Commit 505cc4ca authored by Vũ Gia Vương's avatar Vũ Gia Vương

add new end game api

parent 6f08f5af
...@@ -3,12 +3,13 @@ ...@@ -3,12 +3,13 @@
#///////////////////////////////////////////////////////////////////////////// #/////////////////////////////////////////////////////////////////////////////
node_modules/ node_modules/
# dist/ dist/
build/ build/
library/ library/
temp/ temp/
local/ local/
package-lock.json package-lock.json
.env
#///////////////////////////////////////////////////////////////////////////// #/////////////////////////////////////////////////////////////////////////////
# npm files # npm files
......
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const CONFIG = { const CONFIG = {
GAMEID: {
HUNGQUA: 1000,
},
STATE: {
WATING: 'WATING',
PLAYING: 'PLAYING',
END: 'END',
SELECT: 'SELECT',
},
TIME: {
COUNTDOWN: 10,
SELECT_PLAYER: 5,
PLAY: 60
},
EVT: { EVT: {
CREATE_ROOM: '100',
JOIN_ROOM: '101',
LEAVE_ROOM: '103',
ROOM_MESSAGE: '104',
START_GAME: '200', START_GAME: '200',
UPDATE_STATE: '201',
UPDATE_SCORE: '202',
UPDATE_TIME_REMAINING: '203',
END_GAME: '204', END_GAME: '204',
SPAWN_TOWER: '205', SPAWN_ITEM: '205',
CLOSE_QR_VIEW: '206',
PASS_TOWER: '207', PASS_TOWER: '207',
HISTORY: '208', HISTORY: '208',
GAME_MESSAGE: '300',
RECONNECT: '500',
REQUEST_START_GAME: '600', REQUEST_START_GAME: '600',
REQUEST_PASS_TOWER: '601', REQUEST_PASS_TOWER: '601',
REQUEST_SPAWN_TOWER: '602', REQUEST_SPAWN_TOWER: '602',
REQUEST_HISTORY: '603', REQUEST_HISTORY: '603',
}, }
HTTP_RESPONSE: {
SUCCESS: {
code: 200,
message: "Success!",
},
ERROR: {
code: 500,
message: "Error!",
}
},
}; };
exports.default = CONFIG; exports.default = CONFIG;
...@@ -9,13 +9,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge ...@@ -9,13 +9,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.startGameApi = startGameApi; exports.startGameApiCtrl = startGameApiCtrl;
exports.endGameApi = endGameApi; exports.endGameApiCtrl = endGameApiCtrl;
const URL = 'https://dev.gamee.vn/api/web'; const URL = process.env.URL || 'https://dev.gamee.vn/api/web';
const URL_START_GAME = `${URL}/game/start`; const URL_START_GAME = `${URL}/game/start`;
const URL_END_GAME = `${URL}/game/end`; const URL_END_GAME = `${URL}/game/end`;
const GAME_CODE = 'flip-jump'; const GAME_CODE = 'flip-jump';
let token = 'eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTI1LCJ1c2VybmFtZSI6IlFIU18wMUpEVkZHOE5DUEdBNEtFRUhLWDA4RUJWOCIsImRpc3BsYXlOYW1lIjoiVFJBTiBEVUMgS0hBTkgiLCJlbmFibGVkM' let tokenTest = 'eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTI1LCJ1c2VybmFtZSI6IlFIU18wMUpEVkZHOE5DUEdBNEtFRUhLWDA4RUJWOCIsImRpc3BsYXlOYW1lIjoiVFJBTiBEVUMgS0hBTkgiLCJlbmFibGVkM'
+ 'kZBIjpmYWxzZSwibGFzdExvZ2luVGltZSI6IjIwMjUtMDgtMDhUMTQ6MTc6MDcuNzczMTEyMjUzIiwicmVmZXJlbmNlQ29kZSI6IklRVEtUQzRkbGk4NiIsImdlbmRlciI6Ik1BTEUiLCJ2dG1BY2NvdW50' + 'kZBIjpmYWxzZSwibGFzdExvZ2luVGltZSI6IjIwMjUtMDgtMDhUMTQ6MTc6MDcuNzczMTEyMjUzIiwicmVmZXJlbmNlQ29kZSI6IklRVEtUQzRkbGk4NiIsImdlbmRlciI6Ik1BTEUiLCJ2dG1BY2NvdW50'
+ 'SWQiOiIwMUpEVkZHOE5DUEdBNEtFRUhLWDA4RUJWOCIsImRvYiI6IjE5OTktMDItMTciLCJjb2RlIjoiMTIzNDU2NzYiLCJzdWIiOiIxMjUiLCJpYXQiOjE3NTQ2Mzc0MjcsImV4cCI6MTc2MzI3NzQyN30.' + 'SWQiOiIwMUpEVkZHOE5DUEdBNEtFRUhLWDA4RUJWOCIsImRvYiI6IjE5OTktMDItMTciLCJjb2RlIjoiMTIzNDU2NzYiLCJzdWIiOiIxMjUiLCJpYXQiOjE3NTQ2Mzc0MjcsImV4cCI6MTc2MzI3NzQyN30.'
+ 'Wf7PDX2rgqRPajkTL8oHHeYFwZrLpTKmuBO8Va9OGMs'; + 'Wf7PDX2rgqRPajkTL8oHHeYFwZrLpTKmuBO8Va9OGMs';
...@@ -23,7 +23,7 @@ let timeStart = 0; ...@@ -23,7 +23,7 @@ let timeStart = 0;
let userId = 0; let userId = 0;
let matchId = ''; let matchId = '';
let eventId = null; let eventId = null;
function startGameApi(data) { function startGameApiCtrl(data, user) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
timeStart = new Date().getTime(); timeStart = new Date().getTime();
...@@ -33,7 +33,7 @@ function startGameApi(data) { ...@@ -33,7 +33,7 @@ function startGameApi(data) {
matchId, matchId,
gameCode: GAME_CODE, gameCode: GAME_CODE,
}; };
const result = yield callApi(URL_START_GAME, 'POST', data); const result = yield callApi(URL_START_GAME, 'POST', data, user.token);
return result; return result;
} }
catch (error) { catch (error) {
...@@ -41,7 +41,7 @@ function startGameApi(data) { ...@@ -41,7 +41,7 @@ function startGameApi(data) {
} }
}); });
} }
function endGameApi(user) { function endGameApiCtrl(user) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
const playedSeconds = (new Date().getTime() - timeStart) / 1e3; const playedSeconds = (new Date().getTime() - timeStart) / 1e3;
...@@ -60,7 +60,7 @@ function endGameApi(user) { ...@@ -60,7 +60,7 @@ function endGameApi(user) {
gameLevel: user.towerNumber - 1, gameLevel: user.towerNumber - 1,
details details
}; };
const res = yield callApi(URL_END_GAME, 'POST', params); const res = yield callApi(URL_END_GAME, 'POST', params, user.token);
return res; return res;
} }
catch (error) { catch (error) {
...@@ -68,16 +68,18 @@ function endGameApi(user) { ...@@ -68,16 +68,18 @@ function endGameApi(user) {
} }
}); });
} }
function callApi(url, method, data) { function callApi(url, method, data, token) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
const body = JSON.stringify(data);
let res = yield fetch(url, { let res = yield fetch(url, {
method: method, method: method,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`, 'Authorization': `Bearer ${token}`,
'X-INTERNAL-KEY': 'X7pL9qW3zT2mK8vR5nY4bJ6hF1cD0aE'
}, },
body: JSON.stringify(data), body,
}); });
const result = yield res.json(); const result = yield res.json();
return (result === null || result === void 0 ? void 0 : result.code) == 'success'; return (result === null || result === void 0 ? void 0 : result.code) == 'success';
......
...@@ -22,6 +22,8 @@ class User { ...@@ -22,6 +22,8 @@ class User {
this.timeStart = 0; this.timeStart = 0;
this.isStartGame = true; this.isStartGame = true;
this.history = []; this.history = [];
this.isBlacklist = false;
this.connectionTime = new Date();
this.token = user.token; this.token = user.token;
this.id = user.id; this.id = user.id;
this.towerNumber = user.towerNumber || 0; this.towerNumber = user.towerNumber || 0;
...@@ -38,6 +40,13 @@ class User { ...@@ -38,6 +40,13 @@ class User {
this.screenPos = new v2_1.default(((_f = user.screenPos) === null || _f === void 0 ? void 0 : _f.x) || 0, ((_g = user.screenPos) === null || _g === void 0 ? void 0 : _g.y) || 0); this.screenPos = new v2_1.default(((_f = user.screenPos) === null || _f === void 0 ? void 0 : _f.x) || 0, ((_g = user.screenPos) === null || _g === void 0 ? void 0 : _g.y) || 0);
this.position = new v2_1.default(((_h = user.position) === null || _h === void 0 ? void 0 : _h.x) || 0, ((_j = user.position) === null || _j === void 0 ? void 0 : _j.y) || 0); this.position = new v2_1.default(((_h = user.position) === null || _h === void 0 ? void 0 : _h.x) || 0, ((_j = user.position) === null || _j === void 0 ? void 0 : _j.y) || 0);
} }
isSameHour() {
const now = new Date();
return this.connectionTime.getFullYear() === now.getFullYear() &&
this.connectionTime.getMonth() === now.getMonth() &&
this.connectionTime.getDate() === now.getDate() &&
this.connectionTime.getHours() === now.getHours();
}
reset() { reset() {
this.score = 0; this.score = 0;
this.totalScore = 0; this.totalScore = 0;
......
...@@ -13,15 +13,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) { ...@@ -13,15 +13,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.redis = void 0; exports.redis = void 0;
const server_1 = __importDefault(require("./server"));
const http_1 = __importDefault(require("http")); const http_1 = __importDefault(require("http"));
const socket_io_1 = require("socket.io"); const socket_io_1 = require("socket.io");
const socket_1 = require("./socket");
const redis_adapter_1 = require("@socket.io/redis-adapter"); const redis_adapter_1 = require("@socket.io/redis-adapter");
const redis_1 = require("redis"); const redis_1 = require("redis");
const admin_ui_1 = require("@socket.io/admin-ui");
require('dotenv').config();
const server_1 = __importDefault(require("./server"));
const socket_1 = require("./socket");
const PORT = process.env.PORT || 6636; const PORT = process.env.PORT || 6636;
const REDIS_URL = "redis://default:Myreview123@123a@127.0.0.1:6379"; const REDIS_URL = process.env.REDIS_URL || "redis://127.0.0.1:6379";
// const REDIS_URL = "redis://127.0.0.1:6379";
const PATH = '/socketws/socket.io'; const PATH = '/socketws/socket.io';
exports.redis = (0, redis_1.createClient)({ url: REDIS_URL }); exports.redis = (0, redis_1.createClient)({ url: REDIS_URL });
function initRedis() { function initRedis() {
...@@ -38,7 +39,28 @@ const io = new socket_io_1.Server(server, { ...@@ -38,7 +39,28 @@ const io = new socket_io_1.Server(server, {
path: PATH, path: PATH,
cors: { cors: {
methods: ["GET", "POST"], methods: ["GET", "POST"],
origin: "*" // origin: "*",
// origin: [
// "https://admin.socket.io",
// "https://play.gamee.vn",
// "https://dev.gamee.vn",
// "http://localhost:7456"
// ],
origin: (origin, callback) => {
const allowedOrigins = [
"https://admin.socket.io",
"https://play.gamee.vn",
"https://dev.gamee.vn",
"http://localhost:7456"
];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
}
else {
callback(new Error("Not allowed by CORS"));
}
},
credentials: true,
} }
}); });
const pubClient = (0, redis_1.createClient)({ url: REDIS_URL }); const pubClient = (0, redis_1.createClient)({ url: REDIS_URL });
...@@ -46,6 +68,11 @@ const subClient = pubClient.duplicate(); ...@@ -46,6 +68,11 @@ const subClient = pubClient.duplicate();
Promise.all([pubClient.connect(), subClient.connect()]).then(() => { Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
io.adapter((0, redis_adapter_1.createAdapter)(pubClient, subClient)); io.adapter((0, redis_adapter_1.createAdapter)(pubClient, subClient));
console.log("✅ Socket.IO Redis adapter connected"); console.log("✅ Socket.IO Redis adapter connected");
// http://localhost:6636/admin
(0, admin_ui_1.instrument)(io, {
auth: false,
readonly: true,
});
}); });
(0, socket_1.setupSocket)(io); (0, socket_1.setupSocket)(io);
server.listen(PORT, () => { server.listen(PORT, () => {
......
...@@ -11,7 +11,10 @@ app.use(express_1.default.json()); ...@@ -11,7 +11,10 @@ app.use(express_1.default.json());
app.get('/', (req, res, next) => { app.get('/', (req, res, next) => {
res.send('Hello World'); res.send('Hello World');
}); });
app.get('/socketws/dev', (req, res, next) => { app.get('/socketws/online', (req, res, next) => {
res.send('dev'); // const data = getDataStatistical();
// const result = Array.from(data.values());
// res.json(result);
res.send('online');
}); });
exports.default = app; exports.default = app;
...@@ -12,31 +12,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) { ...@@ -12,31 +12,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod }; return (mod && mod.__esModule) ? mod : { "default": mod };
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.users = void 0;
exports.setupSocket = setupSocket; exports.setupSocket = setupSocket;
const User_1 = __importDefault(require("./Model/User")); const User_1 = __importDefault(require("./Model/User"));
const v2_1 = __importDefault(require("./Model/v2"));
const networkCtrl_1 = require("./Controller/networkCtrl");
const _1 = require("."); const _1 = require(".");
const users = new Map(); const gameCtrl_1 = require("./Controller/gameCtrl");
const Y_RADIO = 0.5560472; const config_1 = __importDefault(require("./Config/config"));
const HALF_SIZE_TOWER = 56.43580423808985; const statisticalCtrl_1 = require("./Controller/statisticalCtrl");
const INIT_SPEED = 300; exports.users = new Map();
const A_POWER = 300;
const DT = 0.016;
const MAX_TOWER = 50;
const CONFIG = {
EVT: {
START_GAME: '200',
END_GAME: '204',
SPAWN_ITEM: '205',
PASS_TOWER: '207',
HISTORY: '208',
REQUEST_START_GAME: '600',
REQUEST_PASS_TOWER: '601',
REQUEST_SPAWN_TOWER: '602',
REQUEST_HISTORY: '603',
}
};
const TIME_EX = 24 * 60 * 60; const TIME_EX = 24 * 60 * 60;
function setupSocket(io) { function setupSocket(io) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
...@@ -44,6 +27,7 @@ function setupSocket(io) { ...@@ -44,6 +27,7 @@ function setupSocket(io) {
try { try {
const token = String(socket.handshake.query.token); const token = String(socket.handshake.query.token);
const userId = String(socket.handshake.query.userId); const userId = String(socket.handshake.query.userId);
const isBlacklist = Boolean(socket.handshake.query.isBlacklist);
if (!token || !userId) { if (!token || !userId) {
return; return;
} }
...@@ -52,9 +36,10 @@ function setupSocket(io) { ...@@ -52,9 +36,10 @@ function setupSocket(io) {
const userJson = yield _1.redis.get(`user:${userId}`); const userJson = yield _1.redis.get(`user:${userId}`);
const user = userJson ? JSON.parse(userJson) : null; const user = userJson ? JSON.parse(userJson) : null;
const newUser = new User_1.default(user || { token, id: userId }); const newUser = new User_1.default(user || { token, id: userId });
users.set(userId, newUser); newUser.isBlacklist = isBlacklist;
exports.users.set(userId, newUser);
if (!user) { if (!user) {
yield _1.redis.set(`user:${userId}`, JSON.stringify(user)); yield _1.redis.set(`user:${userId}`, JSON.stringify(newUser));
yield _1.redis.expire(`user:${userId}`, TIME_EX); yield _1.redis.expire(`user:${userId}`, TIME_EX);
} }
next(); next();
...@@ -67,11 +52,14 @@ function setupSocket(io) { ...@@ -67,11 +52,14 @@ function setupSocket(io) {
try { try {
console.log(`🟢 Client connected: ${socket.id}`); console.log(`🟢 Client connected: ${socket.id}`);
const userId = String(socket.handshake.query.userId); const userId = String(socket.handshake.query.userId);
const user = users.get(userId); const user = exports.users.get(userId);
socket.on(CONFIG.EVT.START_GAME, (data) => startGame(socket, data, user)); if (!user.isSameHour()) {
socket.on(CONFIG.EVT.PASS_TOWER, (data) => __awaiter(this, void 0, void 0, function* () { return yield passTower(socket, data, user); })); (0, statisticalCtrl_1.connectedUserCtrl)();
socket.on(CONFIG.EVT.HISTORY, () => __awaiter(this, void 0, void 0, function* () { return yield getHistory(socket, user); })); }
socket.on(CONFIG.EVT.END_GAME, () => endGame(socket, user)); socket.on(config_1.default.EVT.START_GAME, (data) => (0, gameCtrl_1.startGameCtrl)(socket, data, user));
socket.on(config_1.default.EVT.PASS_TOWER, (data) => __awaiter(this, void 0, void 0, function* () { return yield (0, gameCtrl_1.passTowerCtrl)(socket, data, user); }));
socket.on(config_1.default.EVT.HISTORY, () => __awaiter(this, void 0, void 0, function* () { return yield (0, gameCtrl_1.getHistory)(socket, user); }));
socket.on(config_1.default.EVT.END_GAME, () => (0, gameCtrl_1.endGameCtrl)(socket, user));
socket.on("disconnect", () => __awaiter(this, void 0, void 0, function* () { return yield onDisconnect(socket, user); })); socket.on("disconnect", () => __awaiter(this, void 0, void 0, function* () { return yield onDisconnect(socket, user); }));
} }
catch (error) { catch (error) {
...@@ -80,156 +68,16 @@ function setupSocket(io) { ...@@ -80,156 +68,16 @@ function setupSocket(io) {
}); });
}); });
} }
function endGame(socket, user) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield (0, networkCtrl_1.endGameApi)(user);
user.reset();
}
catch (error) {
console.log('error', error);
}
});
}
function onDisconnect(socket, user) { function onDisconnect(socket, user) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
try { try {
console.log(`🔴 Client disconnected: ${socket.id}`); console.log(`🔴 Client disconnected: ${socket.id}`);
if (!user.isSameHour()) {
(0, statisticalCtrl_1.disconnectUserCtrl)();
}
yield _1.redis.set(`user:${user.id}`, JSON.stringify(user)); yield _1.redis.set(`user:${user.id}`, JSON.stringify(user));
yield _1.redis.expire(`user:${user.id}`, TIME_EX); yield _1.redis.expire(`user:${user.id}`, TIME_EX);
users.delete(user.id); exports.users.delete(user.id);
}
catch (error) {
console.log('error', error);
}
});
}
function getHistory(socket, user) {
return __awaiter(this, void 0, void 0, function* () {
socket.emit(CONFIG.EVT.REQUEST_HISTORY, user.history);
});
}
function startGame(socket, data, user) {
return __awaiter(this, void 0, void 0, function* () {
try {
const result = yield (0, networkCtrl_1.startGameApi)(data);
if (result && user.isStartGame) {
user.reset();
}
if (result) {
socket.emit(CONFIG.EVT.REQUEST_START_GAME, true);
user.isStartGame = true;
addBlock(socket, user);
}
else {
console.log('start game fail');
socket.emit(CONFIG.EVT.REQUEST_START_GAME, false);
}
}
catch (error) {
console.log('error', error);
}
});
}
function passTower(socket, data, user) {
return __awaiter(this, void 0, void 0, function* () {
try {
let xDistance_ = 0;
const { direction, nextBlock, towerNumber, position } = user;
if (towerNumber > MAX_TOWER) {
let speed = INIT_SPEED;
// data.step = Math.min(data.step, 150);
// for (let i = 0; i < data.step; i++) {
// speed += (Math.min(towerNumber - MAX_TOWER, A_POWER) * DT);
// xDistance_ += speed * DT;
// }
const acceleration = Math.min(towerNumber - MAX_TOWER, A_POWER) * DT;
speed += data.step * acceleration;
xDistance_ += INIT_SPEED * data.step * DT + acceleration * DT * (data.step * (data.step - 1)) / 2;
}
else {
xDistance_ = data.step * INIT_SPEED * DT;
}
const point = new v2_1.default(xDistance_ * direction, xDistance_ * Y_RADIO);
const nextTowerPos = nextBlock;
const distancePlayer2NextTower = nextTowerPos.clone().sub(position).mag();
const minDistance = Math.abs(xDistance_ - distancePlayer2NextTower);
const isHead = distancePlayer2NextTower > xDistance_;
let score = -1;
const pointEdge = new v2_1.default(HALF_SIZE_TOWER * direction, HALF_SIZE_TOWER * Y_RADIO);
let target = position.clone().add(point);
if (minDistance < HALF_SIZE_TOWER * 1.5) {
target = nextTowerPos.clone().sub(isHead ? pointEdge : pointEdge.clone().mul(-1));
score = 0;
}
if (minDistance < HALF_SIZE_TOWER * 0.8) {
target = nextTowerPos.clone().sub(pointEdge.clone().mul(0.5));
score = 1;
}
if (minDistance < HALF_SIZE_TOWER * 0.3) {
target = nextTowerPos.clone();
score = 2;
}
const heightJump = user.distance2Tower * 0.7;
if (score == 2) {
user.combo = Math.min(user.combo + 1, 2);
score *= user.combo;
}
else {
user.combo = 0;
}
user.totalScore += Math.max(score, 0);
const dataSocket = {
target,
score,
isHead,
heightJump,
totalScore: user.totalScore,
};
const history = {
totalScore: user.totalScore,
tower: towerNumber,
score: Math.max(score, 0),
timeStart: user.timeStart,
timePlayed: Date.now() - user.timeStart,
};
user.history.unshift(history);
user.position = target;
socket.emit(CONFIG.EVT.REQUEST_PASS_TOWER, dataSocket);
if (score > 0) {
addBlock(socket, user);
}
else {
socket.emit(CONFIG.EVT.REQUEST_HISTORY, user.history);
yield (0, networkCtrl_1.endGameApi)(user);
user.reset();
}
}
catch (error) {
console.log('error', error);
}
});
}
function addBlock(socket, user) {
return __awaiter(this, void 0, void 0, function* () {
try {
const towerNumber = ++user.towerNumber;
const xDistance = ((Math.min(towerNumber, 100) / 100 + 1) + Math.random() * (towerNumber <= 10 ? 0.5 : 1)) * 150;
const yDistance = xDistance * Y_RADIO;
user.curBlock = new v2_1.default(user.nextBlock.x, user.nextBlock.y);
if (towerNumber > 1) {
user.direction = (Math.random() < 0.5) ? -1 : 1;
}
user.nextBlock.x = user.curBlock.x + (xDistance * user.direction);
user.nextBlock.y = user.curBlock.y + yDistance;
user.distance2Tower = user.nextBlock.distanceTo(user.curBlock);
const data = {
nextBlock: user.nextBlock,
direction: user.direction,
towerNumber: user.towerNumber,
};
user.timeStart = Date.now();
socket.emit(CONFIG.EVT.REQUEST_SPAWN_TOWER, data);
} }
catch (error) { catch (error) {
console.log('error', error); console.log('error', error);
......
...@@ -23,10 +23,9 @@ export async function endGameCtrl(socket: Socket, user: User) { ...@@ -23,10 +23,9 @@ export async function endGameCtrl(socket: Socket, user: User) {
} }
} }
export async function startGameCtrl(socket: Socket, data: any, user: User) { export async function startGameCtrl(socket: Socket, data: any, user: User) {
try { try {
const result = await startGameApiCtrl(data); const result = await startGameApiCtrl(data, user);
if (result && user.isStartGame) { if (result && user.isStartGame) {
user.reset(); user.reset();
......
import User from "../Model/User"; import User from "../Model/User";
const URL = process.env.URL || 'https://dev.gamee.vn/api/web';
const URL = 'https://dev.gamee.vn/api/web';
const URL_START_GAME = `${URL}/game/start`; const URL_START_GAME = `${URL}/game/start`;
const URL_END_GAME = `${URL}/game/end`; const URL_END_GAME = `${URL}/game/end`;
const GAME_CODE = 'flip-jump'; const GAME_CODE = 'flip-jump';
let token = 'eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTI1LCJ1c2VybmFtZSI6IlFIU18wMUpEVkZHOE5DUEdBNEtFRUhLWDA4RUJWOCIsImRpc3BsYXlOYW1lIjoiVFJBTiBEVUMgS0hBTkgiLCJlbmFibGVkM' let tokenTest = 'eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MTI1LCJ1c2VybmFtZSI6IlFIU18wMUpEVkZHOE5DUEdBNEtFRUhLWDA4RUJWOCIsImRpc3BsYXlOYW1lIjoiVFJBTiBEVUMgS0hBTkgiLCJlbmFibGVkM'
+ 'kZBIjpmYWxzZSwibGFzdExvZ2luVGltZSI6IjIwMjUtMDgtMDhUMTQ6MTc6MDcuNzczMTEyMjUzIiwicmVmZXJlbmNlQ29kZSI6IklRVEtUQzRkbGk4NiIsImdlbmRlciI6Ik1BTEUiLCJ2dG1BY2NvdW50' + 'kZBIjpmYWxzZSwibGFzdExvZ2luVGltZSI6IjIwMjUtMDgtMDhUMTQ6MTc6MDcuNzczMTEyMjUzIiwicmVmZXJlbmNlQ29kZSI6IklRVEtUQzRkbGk4NiIsImdlbmRlciI6Ik1BTEUiLCJ2dG1BY2NvdW50'
+ 'SWQiOiIwMUpEVkZHOE5DUEdBNEtFRUhLWDA4RUJWOCIsImRvYiI6IjE5OTktMDItMTciLCJjb2RlIjoiMTIzNDU2NzYiLCJzdWIiOiIxMjUiLCJpYXQiOjE3NTQ2Mzc0MjcsImV4cCI6MTc2MzI3NzQyN30.' + 'SWQiOiIwMUpEVkZHOE5DUEdBNEtFRUhLWDA4RUJWOCIsImRvYiI6IjE5OTktMDItMTciLCJjb2RlIjoiMTIzNDU2NzYiLCJzdWIiOiIxMjUiLCJpYXQiOjE3NTQ2Mzc0MjcsImV4cCI6MTc2MzI3NzQyN30.'
+ 'Wf7PDX2rgqRPajkTL8oHHeYFwZrLpTKmuBO8Va9OGMs'; + 'Wf7PDX2rgqRPajkTL8oHHeYFwZrLpTKmuBO8Va9OGMs';
...@@ -14,7 +13,7 @@ let userId = 0; ...@@ -14,7 +13,7 @@ let userId = 0;
let matchId = ''; let matchId = '';
let eventId: string | null = null; let eventId: string | null = null;
export async function startGameApiCtrl(data: any) { export async function startGameApiCtrl(data: any, user: User) {
try { try {
timeStart = new Date().getTime(); timeStart = new Date().getTime();
userId = (Math.random() * 100 >> 0) + 10; userId = (Math.random() * 100 >> 0) + 10;
...@@ -24,7 +23,7 @@ export async function startGameApiCtrl(data: any) { ...@@ -24,7 +23,7 @@ export async function startGameApiCtrl(data: any) {
matchId, matchId,
gameCode: GAME_CODE, gameCode: GAME_CODE,
}; };
const result = await callApi(URL_START_GAME, 'POST', data); const result = await callApi(URL_START_GAME, 'POST', data, user.token);
return result; return result;
} catch (error) { } catch (error) {
console.log('error', error) console.log('error', error)
...@@ -51,22 +50,24 @@ export async function endGameApiCtrl(user: User) { ...@@ -51,22 +50,24 @@ export async function endGameApiCtrl(user: User) {
details details
} }
const res = await callApi(URL_END_GAME, 'POST', params); const res = await callApi(URL_END_GAME, 'POST', params, user.token);
return res; return res;
} catch (error) { } catch (error) {
console.log('error', error) console.log('error', error)
} }
} }
async function callApi(url: string, method: string, data: any) { async function callApi(url: string, method: string, data: any, token: string) {
try { try {
const body = JSON.stringify(data);
let res = await fetch(url, { let res = await fetch(url, {
method: method, method: method,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`, 'Authorization': `Bearer ${token}`,
'X-INTERNAL-KEY': 'X7pL9qW3zT2mK8vR5nY4bJ6hF1cD0aE'
}, },
body: JSON.stringify(data), body,
}); });
const result = await res.json(); const result = await res.json();
return result?.code == 'success'; return result?.code == 'success';
......
...@@ -4,9 +4,9 @@ import { users } from "../socket"; ...@@ -4,9 +4,9 @@ import { users } from "../socket";
const ONLINE_KEY = 'socket:online'; const ONLINE_KEY = 'socket:online';
const VISITS_KEY = 'socket:visits'; const VISITS_KEY = 'socket:visits';
const CONNECT = 'connect'; const CONNECT = 'socket:connect';
const DISCONNECT = 'disconnect'; const DISCONNECT = 'socket:disconnect';
const FIVE_MINUTE_EX = 2 * 60 * 60; const HOUR_EX = 2 * 60 * 60;
export function setDataStatistical() { export function setDataStatistical() {
...@@ -17,20 +17,63 @@ export function getDataStatistical() { ...@@ -17,20 +17,63 @@ export function getDataStatistical() {
}; };
export function connectedUserCtrl() { export function connectedUserCtrl() {
const {today, hour, minute} = getTime5Minute(); const { today, hour } = getTime();
const connected5MinuteKey = `${CONNECT}:${today}:${hour}:${minute}`; const connectedInHourKey = `${CONNECT}:${today}:${hour}`;
redis.incr(connected5MinuteKey); const onlineKey = `${ONLINE_KEY}:${today}:${hour}`;
redis.incr(connectedInHourKey);
redis.incr(onlineKey);
} }
export function disconnectUserCtrl() { export function disconnectUserCtrl() {
const {today, hour, minute} = getTime5Minute(); const { today, hour } = getTime();
const disconnected5MinuteKey = `${CONNECT}:${today}:${hour}:${minute}`; const disconnectedInHourKey = `${DISCONNECT}:${today}:${hour}`;
redis.incr(disconnected5MinuteKey); const onlineKey = `${ONLINE_KEY}:${today}:${hour}`;
redis.incr(disconnectedInHourKey);
redis.decr(onlineKey);
} }
function getTime5Minute() { function getTime() {
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
const hour = new Date().getHours() + 1; // 1-24 const hour = new Date().getHours() + 1; // 1-24
const minute = Math.floor(new Date().getMinutes() / 5) * 5; // 0-59 return { today, hour };
return { today, hour, minute }; }
function startAtNextHour() {
console.log('startAtNextHour', 1)
const now = new Date();
// mốc giờ tiếp theo
const nextHour = new Date(now);
nextHour.setHours(now.getHours() + 1, 0, 0, 0);
// số ms còn lại tới đầu giờ tiếp theo
const delay = nextHour.getTime() - now.getTime();
console.log(`Sẽ chạy lần đầu sau ${delay / 1000}s`);
// mốc phút tiếp theo
const nextMinute = new Date(now);
nextMinute.setSeconds(0, 0); // reset giây & ms
nextMinute.setMinutes(now.getMinutes() + 1);
// số ms còn lại tới đầu phút tiếp theo
const delayMinite = nextMinute.getTime() - now.getTime();
setTimeout(() => {
runJob(); // chạy ngay tại đầu giờ
// sau đó lặp lại mỗi 1 giờ (3600000 ms)
setInterval(runJob, 60 * 60 * 1000);
}, delayMinite);
} }
function runJob() {
const now = new Date();
console.log("Run job at:", now.toISOString());
// TODO: logic của bạn ở đây
}
// gọi khởi động
// startAtNextHour();
\ No newline at end of file
...@@ -20,6 +20,7 @@ class User { ...@@ -20,6 +20,7 @@ class User {
public isStartGame: boolean = true; public isStartGame: boolean = true;
public history: History[] = []; public history: History[] = [];
public isBlacklist: boolean = false; public isBlacklist: boolean = false;
public connectionTime: Date = new Date();
constructor(user: User) { constructor(user: User) {
this.token = user.token; this.token = user.token;
...@@ -42,6 +43,14 @@ class User { ...@@ -42,6 +43,14 @@ class User {
this.position = new v2(user.position?.x || 0, user.position?.y || 0); this.position = new v2(user.position?.x || 0, user.position?.y || 0);
} }
public isSameHour() {
const now = new Date();
return this.connectionTime.getFullYear() === now.getFullYear() &&
this.connectionTime.getMonth() === now.getMonth() &&
this.connectionTime.getDate() === now.getDate() &&
this.connectionTime.getHours() === now.getHours();
}
public reset() { public reset() {
this.score = 0; this.score = 0;
this.totalScore = 0; this.totalScore = 0;
......
import app from "./server";
import http from 'http'; import http from 'http';
import { Server } from 'socket.io'; import { Server } from 'socket.io';
import { setupSocket } from "./socket";
import { createAdapter } from "@socket.io/redis-adapter"; import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis"; import { createClient } from "redis";
import { instrument } from "@socket.io/admin-ui"; import { instrument } from "@socket.io/admin-ui";
require('dotenv').config();
import app from "./server";
import { setupSocket } from "./socket";
const PORT = process.env.PORT || 6636; const PORT = process.env.PORT || 6636;
// const REDIS_URL = "redis://default:Myreview123@123a@127.0.0.1:6379"; const REDIS_URL = process.env.REDIS_URL || "redis://127.0.0.1:6379";
const REDIS_URL = "redis://127.0.0.1:6379";
const PATH = '/socketws/socket.io'; const PATH = '/socketws/socket.io';
export const redis = createClient({ url: REDIS_URL }); export const redis = createClient({ url: REDIS_URL });
...@@ -28,8 +29,13 @@ const io = new Server(server, { ...@@ -28,8 +29,13 @@ const io = new Server(server, {
path: PATH, path: PATH,
cors: { cors: {
methods: ["GET", "POST"], methods: ["GET", "POST"],
origin: "*", // origin: "*",
// origin: ["https://admin.socket.io", "http://localhost:8080"], origin: [
"https://admin.socket.io",
"https://play.gamee.vn",
"https://dev.gamee.vn",
"http://localhost:7456"
],
credentials: true, credentials: true,
} }
}); });
...@@ -41,6 +47,7 @@ Promise.all([pubClient.connect(), subClient.connect()]).then(() => { ...@@ -41,6 +47,7 @@ Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
io.adapter(createAdapter(pubClient, subClient)); io.adapter(createAdapter(pubClient, subClient));
console.log("✅ Socket.IO Redis adapter connected"); console.log("✅ Socket.IO Redis adapter connected");
// http://localhost:6636/admin
instrument(io, { instrument(io, {
auth: false, auth: false,
readonly: true, readonly: true,
......
import express, { Request, Response, NextFunction } from 'express' import express, { Request, Response, NextFunction } from 'express'
import cors from 'cors'; import cors from 'cors';
import { getDataStatistical } from './Controller/statisticalCtrl';
const app = express(); const app = express();
app.use(cors()); app.use(cors());
...@@ -14,7 +13,7 @@ app.get('/socketws/online', (req: Request, res: Response, next: NextFunction) => ...@@ -14,7 +13,7 @@ app.get('/socketws/online', (req: Request, res: Response, next: NextFunction) =>
// const data = getDataStatistical(); // const data = getDataStatistical();
// const result = Array.from(data.values()); // const result = Array.from(data.values());
// res.json(result); // res.json(result);
res.send('online'); res.send('online');
}) })
......
...@@ -3,6 +3,7 @@ import User, { IRequestPassTower } from "./Model/User"; ...@@ -3,6 +3,7 @@ import User, { IRequestPassTower } from "./Model/User";
import { redis } from "."; import { redis } from ".";
import { endGameCtrl, getHistory, passTowerCtrl, startGameCtrl } from "./Controller/gameCtrl"; import { endGameCtrl, getHistory, passTowerCtrl, startGameCtrl } from "./Controller/gameCtrl";
import CONFIG from "./Config/config"; import CONFIG from "./Config/config";
import { connectedUserCtrl, disconnectUserCtrl } from "./Controller/statisticalCtrl";
export const users: Map<string, User> = new Map<string, User>(); export const users: Map<string, User> = new Map<string, User>();
...@@ -24,12 +25,12 @@ export async function setupSocket(io: Server) { ...@@ -24,12 +25,12 @@ export async function setupSocket(io: Server) {
const userJson = await redis.get(`user:${userId}`); const userJson = await redis.get(`user:${userId}`);
const user = userJson ? JSON.parse(userJson) : null; const user = userJson ? JSON.parse(userJson) : null;
const newUser = new User(user || { token, id: userId, isBlacklist} as User); const newUser = new User(user || { token, id: userId } as User);
newUser.isBlacklist = isBlacklist; newUser.isBlacklist = isBlacklist;
users.set(userId, newUser); users.set(userId, newUser);
if (!user) { if (!user) {
await redis.set(`user:${userId}`, JSON.stringify(newUser)); await redis.set(`user:${userId}`, JSON.stringify(newUser));
await redis.expire(`user:${userId}`, TIME_EX); await redis.expire(`user:${userId}`, TIME_EX);
} }
next(); next();
...@@ -44,11 +45,13 @@ export async function setupSocket(io: Server) { ...@@ -44,11 +45,13 @@ export async function setupSocket(io: Server) {
const userId = String(socket.handshake.query.userId); const userId = String(socket.handshake.query.userId);
const user = users.get(userId)!; const user = users.get(userId)!;
if (!user.isSameHour()) {
connectedUserCtrl();
}
socket.on(CONFIG.EVT.START_GAME, (data: string) => startGameCtrl(socket, data, user)); socket.on(CONFIG.EVT.START_GAME, (data: string) => startGameCtrl(socket, data, user));
socket.on(CONFIG.EVT.PASS_TOWER, async (data: IRequestPassTower) => await passTowerCtrl(socket, data, user)); socket.on(CONFIG.EVT.PASS_TOWER, async (data: IRequestPassTower) => await passTowerCtrl(socket, data, user));
socket.on(CONFIG.EVT.HISTORY, async () => await getHistory(socket, user)); socket.on(CONFIG.EVT.HISTORY, async () => await getHistory(socket, user));
socket.on(CONFIG.EVT.END_GAME, () => endGameCtrl(socket, user)); socket.on(CONFIG.EVT.END_GAME, () => endGameCtrl(socket, user));
socket.on("disconnect", async () => await onDisconnect(socket, user)); socket.on("disconnect", async () => await onDisconnect(socket, user));
...@@ -61,7 +64,9 @@ export async function setupSocket(io: Server) { ...@@ -61,7 +64,9 @@ export async function setupSocket(io: Server) {
async function onDisconnect(socket: Socket, user: User) { async function onDisconnect(socket: Socket, user: User) {
try { try {
console.log(`🔴 Client disconnected: ${socket.id}`) console.log(`🔴 Client disconnected: ${socket.id}`)
if (!user.isSameHour()) {
disconnectUserCtrl();
}
await redis.set(`user:${user.id}`, JSON.stringify(user)); await redis.set(`user:${user.id}`, JSON.stringify(user));
await redis.expire(`user:${user.id}`, TIME_EX); await redis.expire(`user:${user.id}`, TIME_EX);
users.delete(user.id); users.delete(user.id);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment