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

add redis

parent 61c5fdb7
......@@ -5,7 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
const v2_1 = __importDefault(require("./v2"));
class User {
constructor(token, id) {
constructor(user) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
this.id = '';
this.token = '';
this.towerNumber = 0;
......@@ -21,9 +22,21 @@ class User {
this.timeStart = 0;
this.isStartGame = true;
this.history = [];
this.token = token;
this.id = id;
this.reset();
this.token = user.token;
this.id = user.id;
this.towerNumber = user.towerNumber || 0;
this.direction = user.direction || 1;
this.distance2Tower = user.distance2Tower || 0;
this.score = user.score || 0;
this.totalScore = user.totalScore || 0;
this.combo = user.combo || 0;
this.timeStart = user.timeStart || 0;
this.isStartGame = user.isStartGame || true;
this.history = ((_a = user.history) === null || _a === void 0 ? void 0 : _a.map(h => new History(h.timeStart, h.timePlayed, h.totalScore, h.tower, h.score))) || [];
this.curBlock = new v2_1.default(((_b = user.curBlock) === null || _b === void 0 ? void 0 : _b.x) || 0, ((_c = user.curBlock) === null || _c === void 0 ? void 0 : _c.y) || 0);
this.nextBlock = new v2_1.default(((_d = user.nextBlock) === null || _d === void 0 ? void 0 : _d.x) || 0, ((_e = user.nextBlock) === null || _e === void 0 ? void 0 : _e.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);
}
reset() {
this.score = 0;
......@@ -46,12 +59,17 @@ class User {
}
}
class History {
constructor() {
constructor(timeStart, timePlayed, totalScore, tower, score) {
this.timeStart = 0;
this.timePlayed = 0;
this.totalScore = 0;
this.tower = 0;
this.score = 0;
this.timeStart = timeStart;
this.timePlayed = timePlayed;
this.totalScore = totalScore;
this.tower = tower;
this.score = score;
}
}
exports.default = User;
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.redis = void 0;
const server_1 = __importDefault(require("./server"));
const http_1 = __importDefault(require("http"));
const socket_io_1 = require("socket.io");
const socket_1 = require("./socket");
const redis_adapter_1 = require("@socket.io/redis-adapter");
const redis_1 = require("redis");
const PORT = 3001;
const REDIS_URL = "redis://127.0.0.1:6379";
exports.redis = (0, redis_1.createClient)({ url: REDIS_URL });
function initRedis() {
return __awaiter(this, void 0, void 0, function* () {
yield exports.redis.connect();
console.log("✅ Redis connected");
});
}
initRedis().catch(err => {
console.error("Redis error:", err);
});
const server = http_1.default.createServer(server_1.default);
const io = new socket_io_1.Server(server, {
path: '/socket',
......@@ -18,21 +41,14 @@ const io = new socket_io_1.Server(server, {
"https://dev.gamee.vn",
"http://localhost:7456"
]
// origin: (origin, callback) => {
// const allowed = [
// "https://play.gamee.vn",
// "https://dev.gamee.vn",
// "http://localhost:7456"
// ];
// if (!origin || allowed.includes(origin)) {
// callback(null, origin);
// } else {
// callback(new Error("Not allowed by CORS"));
// }
// },
// credentials: true,
}
});
const pubClient = (0, redis_1.createClient)({ url: REDIS_URL });
const subClient = pubClient.duplicate();
Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
io.adapter((0, redis_adapter_1.createAdapter)(pubClient, subClient));
console.log("✅ Socket.IO Redis adapter connected");
});
(0, socket_1.setupSocket)(io);
server.listen(PORT, () => {
console.log(`Server is running on port ${PORT}!`);
......
......@@ -16,6 +16,7 @@ exports.setupSocket = setupSocket;
const User_1 = __importDefault(require("./Model/User"));
const v2_1 = __importDefault(require("./Model/v2"));
const networkCtrl_1 = require("./Controller/networkCtrl");
const _1 = require(".");
const users = new Map();
const Y_RADIO = 0.5560472;
const HALF_SIZE_TOWER = 56.43580423808985;
......@@ -36,36 +37,47 @@ const CONFIG = {
REQUEST_HISTORY: '603',
}
};
const TIME_EX = 24 * 60 * 60;
function setupSocket(io) {
try {
io.use((socket, next) => {
const token = String(socket.handshake.query.token);
const userId = String(socket.handshake.query.userId);
if (!token || !userId) {
return;
}
if (!users.has(userId)) {
users.set(userId, new User_1.default(token, userId));
}
next();
});
io.on("connection", (socket) => {
console.log(`🟢 Client connected: ${socket.id}`);
socket.on(CONFIG.EVT.START_GAME, (data) => startGame(socket, data));
socket.on(CONFIG.EVT.PASS_TOWER, (data) => __awaiter(this, void 0, void 0, function* () { return yield passTower(socket, data); }));
socket.on(CONFIG.EVT.HISTORY, () => __awaiter(this, void 0, void 0, function* () { return getHistory(socket); }));
socket.on(CONFIG.EVT.END_GAME, () => endGame(socket));
socket.on("disconnect", () => onDisconnect(socket));
});
}
catch (error) {
console.log('error', error);
}
return __awaiter(this, void 0, void 0, function* () {
try {
io.use((socket, next) => __awaiter(this, void 0, void 0, function* () {
const token = String(socket.handshake.query.token);
const userId = String(socket.handshake.query.userId);
if (!token || !userId) {
return;
}
const keys = yield _1.redis.keys("user:*");
console.log('keys', keys);
const userJson = yield _1.redis.get(`user:${userId}`);
const user = userJson ? JSON.parse(userJson) : null;
const newUser = new User_1.default(user || { token, id: userId });
users.set(userId, newUser);
if (!user) {
yield _1.redis.set(`user:${userId}`, JSON.stringify(user));
yield _1.redis.expire(`user:${user.id}`, TIME_EX);
}
next();
}));
io.on("connection", (socket) => {
console.log(`🟢 Client connected: ${socket.id}`);
const userId = String(socket.handshake.query.userId);
const user = users.get(userId);
socket.on(CONFIG.EVT.START_GAME, (data) => startGame(socket, data, user));
socket.on(CONFIG.EVT.PASS_TOWER, (data) => __awaiter(this, void 0, void 0, function* () { return yield passTower(socket, data, user); }));
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("disconnect", () => __awaiter(this, void 0, void 0, function* () { return yield onDisconnect(socket, user); }));
});
}
catch (error) {
console.log('error', error);
}
});
}
function endGame(socket) {
function endGame(socket, user) {
return __awaiter(this, void 0, void 0, function* () {
try {
const user = getUserBySocket(socket);
yield (0, networkCtrl_1.endGameApi)(user);
user.reset();
}
......@@ -74,20 +86,27 @@ function endGame(socket) {
}
});
}
function onDisconnect(socket) {
console.log(`🔴 Client disconnected: ${socket.id}`);
// endGame(socket);
function onDisconnect(socket, user) {
return __awaiter(this, void 0, void 0, function* () {
try {
console.log(`🔴 Client disconnected: ${socket.id}`);
yield _1.redis.set(`user:${user.id}`, JSON.stringify(user));
yield _1.redis.expire(`user:${user.id}`, TIME_EX);
users.delete(user.id);
}
catch (error) {
console.log('error', error);
}
});
}
function getHistory(socket) {
function getHistory(socket, user) {
return __awaiter(this, void 0, void 0, function* () {
const user = getUserBySocket(socket);
socket.emit(CONFIG.EVT.REQUEST_HISTORY, user.history);
});
}
function startGame(socket, data) {
function startGame(socket, data, user) {
return __awaiter(this, void 0, void 0, function* () {
try {
const user = getUserBySocket(socket);
const result = yield (0, networkCtrl_1.startGameApi)(data);
if (result && user.isStartGame) {
user.reset();
......@@ -95,7 +114,7 @@ function startGame(socket, data) {
if (result) {
socket.emit(CONFIG.EVT.REQUEST_START_GAME, true);
user.isStartGame = true;
addBlock(socket);
addBlock(socket, user);
}
else {
console.log('start game fail');
......@@ -107,10 +126,9 @@ function startGame(socket, data) {
}
});
}
function passTower(socket, data) {
function passTower(socket, data, user) {
return __awaiter(this, void 0, void 0, function* () {
try {
const user = getUserBySocket(socket);
let xDistance_ = 0;
const { direction, nextBlock, towerNumber, position } = user;
if (towerNumber > MAX_TOWER) {
......@@ -174,7 +192,7 @@ function passTower(socket, data) {
user.position = target;
socket.emit(CONFIG.EVT.REQUEST_PASS_TOWER, dataSocket);
if (score > 0) {
addBlock(socket);
addBlock(socket, user);
}
else {
socket.emit(CONFIG.EVT.REQUEST_HISTORY, user.history);
......@@ -187,10 +205,9 @@ function passTower(socket, data) {
}
});
}
function addBlock(socket) {
function addBlock(socket, user) {
return __awaiter(this, void 0, void 0, function* () {
try {
const user = getUserBySocket(socket);
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;
......@@ -201,12 +218,9 @@ function addBlock(socket) {
user.nextBlock.x = user.curBlock.x + (xDistance * user.direction);
user.nextBlock.y = user.curBlock.y + yDistance;
user.distance2Tower = user.nextBlock.distanceTo(user.curBlock);
user.screenPos = user.nextBlock.add(user.curBlock).mul(0.5).sub(new v2_1.default(0, 100));
const data = {
nextBlock: user.nextBlock,
screenPos: user.screenPos,
direction: user.direction,
distance2Tower: user.distance2Tower,
towerNumber: user.towerNumber,
};
user.timeStart = Date.now();
......@@ -217,7 +231,3 @@ function addBlock(socket) {
}
});
}
function getUserBySocket(socket) {
const userId = String(socket.handshake.query.userId);
return users.get(userId);
}
{
"name": "game-sever-flip-jump",
"version": "1.0.0",
"version": "1.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "game-sever-flip-jump",
"version": "1.0.0",
"version": "1.0.1",
"license": "ISC",
"dependencies": {
"@socket.io/redis-adapter": "^8.3.0",
"@types/express": "^5.0.3",
"cors": "^2.8.5",
"express": "^5.1.0",
"http": "^0.0.1-security",
"nodemon": "^3.1.10",
"redis": "^5.8.1",
"socket.io": "^4.8.1",
"socket.io-redis": "^6.1.1",
"ts-node": "^10.9.2",
"typescript": "^5.9.2"
}
......@@ -52,11 +55,111 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@redis/bloom": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.8.1.tgz",
"integrity": "sha512-hJOJr/yX6BttnyZ+nxD3Ddiu2lPig4XJjyAK1v7OSHOJNUTfn3RHBryB9wgnBMBdkg9glVh2AjItxIXmr600MA==",
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.8.1"
}
},
"node_modules/@redis/client": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@redis/client/-/client-5.8.1.tgz",
"integrity": "sha512-hD5Tvv7G0t8b3w8ao3kQ4jEPUmUUC6pqA18c8ciYF5xZGfUGBg0olQHW46v6qSt4O5bxOuB3uV7pM6H5wEjBwA==",
"dependencies": {
"cluster-key-slot": "1.1.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@redis/json": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@redis/json/-/json-5.8.1.tgz",
"integrity": "sha512-kyvM8Vn+WjJI++nRsIoI9TbdfCs1/TgD0Hp7Z7GiG6W4IEBzkXGQakli+R5BoJzUfgh7gED2fkncYy1NLprMNg==",
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.8.1"
}
},
"node_modules/@redis/search": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@redis/search/-/search-5.8.1.tgz",
"integrity": "sha512-CzuKNTInTNQkxqehSn7QiYcM+th+fhjQn5ilTvksP1wPjpxqK0qWt92oYg3XZc3tO2WuXkqDvTujc4D7kb6r/A==",
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.8.1"
}
},
"node_modules/@redis/time-series": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.8.1.tgz",
"integrity": "sha512-klvdR96U9oSOyqvcectoAGhYlMOnMS3I5UWUOgdBn1buMODiwM/E4Eds7gxldKmtowe4rLJSF1CyIqyZTjy8Ow==",
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.8.1"
}
},
"node_modules/@socket.io/component-emitter": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="
},
"node_modules/@socket.io/redis-adapter": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/@socket.io/redis-adapter/-/redis-adapter-8.3.0.tgz",
"integrity": "sha512-ly0cra+48hDmChxmIpnESKrc94LjRL80TEmZVscuQ/WWkRP81nNj8W8cCGMqbI4L6NCuAaPRSzZF1a9GlAxxnA==",
"dependencies": {
"debug": "~4.3.1",
"notepack.io": "~3.0.1",
"uid2": "1.0.0"
},
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"socket.io-adapter": "^2.5.4"
}
},
"node_modules/@socket.io/redis-adapter/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/@socket.io/redis-adapter/node_modules/notepack.io": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-3.0.1.tgz",
"integrity": "sha512-TKC/8zH5pXIAMVQio2TvVDTtPRX+DJPHDqjRbxogtFiByHyzKmy96RA0JtCQJ+WouyyL4A10xomQzgbUT+1jCg=="
},
"node_modules/@socket.io/redis-adapter/node_modules/uid2": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-1.0.0.tgz",
"integrity": "sha512-+I6aJUv63YAcY9n4mQreLUt0d4lvwkkopDNmpomkAUz0fAkEMV9pRWxN0EjhW1YfRhcuyHg2v3mwddCDW1+LFQ==",
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
......@@ -342,6 +445,14 @@
"fsevents": "~2.3.2"
}
},
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
......@@ -415,6 +526,14 @@
}
}
},
"node_modules/denque": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.5.1.tgz",
"integrity": "sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw==",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
......@@ -981,6 +1100,11 @@
"node": ">=0.10.0"
}
},
"node_modules/notepack.io": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-2.2.0.tgz",
"integrity": "sha512-9b5w3t5VSH6ZPosoYnyDONnUTF8o0UkBw7JLA6eBlYJWyGT1Q3vQa8Hmuj1/X6RYvHjjygBDgw6fJhe0JEojfw=="
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
......@@ -1110,6 +1234,45 @@
"node": ">=8.10.0"
}
},
"node_modules/redis": {
"version": "5.8.1",
"resolved": "https://registry.npmjs.org/redis/-/redis-5.8.1.tgz",
"integrity": "sha512-RZjBKYX/qFF809x6vDcE5VA6L3MmiuT+BkbXbIyyyeU0lPD47V4z8qTzN+Z/kKFwpojwCItOfaItYuAjNs8pTQ==",
"dependencies": {
"@redis/bloom": "5.8.1",
"@redis/client": "5.8.1",
"@redis/json": "5.8.1",
"@redis/search": "5.8.1",
"@redis/time-series": "5.8.1"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/redis-commands": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.7.0.tgz",
"integrity": "sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ=="
},
"node_modules/redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==",
"engines": {
"node": ">=4"
}
},
"node_modules/redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==",
"dependencies": {
"redis-errors": "^1.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/router": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
......@@ -1349,6 +1512,61 @@
}
}
},
"node_modules/socket.io-redis": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/socket.io-redis/-/socket.io-redis-6.1.1.tgz",
"integrity": "sha512-jeaXe3TGKC20GMSlPHEdwTUIWUpay/L7m5+S9TQcOf22p9Llx44/RkpJV08+buXTZ8E+aivOotj2RdeFJJWJJQ==",
"deprecated": "This package has been renamed to '@socket.io/redis-adapter', please see the migration guide here: https://socket.io/docs/v4/redis-adapter/#migrating-from-socketio-redis",
"dependencies": {
"debug": "~4.3.1",
"notepack.io": "~2.2.0",
"redis": "^3.0.0",
"socket.io-adapter": "~2.2.0",
"uid2": "0.0.3"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/socket.io-redis/node_modules/debug": {
"version": "4.3.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/socket.io-redis/node_modules/redis": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.1.2.tgz",
"integrity": "sha512-grn5KoZLr/qrRQVwoSkmzdbw6pwF+/rwODtrOr6vuBRiR/f3rjSTGupbF90Zpqm2oenix8Do6RV7pYEkGwlKkw==",
"dependencies": {
"denque": "^1.5.0",
"redis-commands": "^1.7.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/node-redis"
}
},
"node_modules/socket.io-redis/node_modules/socket.io-adapter": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.2.0.tgz",
"integrity": "sha512-rG49L+FwaVEwuAdeBRq49M97YI3ElVabJPzvHT9S6a2CWhDKnjSFasvwAwSYPRhQzfn4NtDIbCaGYgOCOU/rlg=="
},
"node_modules/socket.io/node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
......@@ -1517,6 +1735,11 @@
"node": ">=14.17"
}
},
"node_modules/uid2": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
"integrity": "sha512-5gSP1liv10Gjp8cMEnFd6shzkL/D6W1uhXSFNCxDC+YI8+L8wkCYCbJ7n77Ezb4wE/xzMogecE+DtamEe9PZjg=="
},
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
......
......@@ -12,12 +12,15 @@
"author": "",
"license": "ISC",
"dependencies": {
"@socket.io/redis-adapter": "^8.3.0",
"@types/express": "^5.0.3",
"cors": "^2.8.5",
"express": "^5.1.0",
"http": "^0.0.1-security",
"nodemon": "^3.1.10",
"redis": "^5.8.1",
"socket.io": "^4.8.1",
"socket.io-redis": "^6.1.1",
"ts-node": "^10.9.2",
"typescript": "^5.9.2"
}
......
......@@ -20,10 +20,25 @@ class User {
public isStartGame: boolean = true;
public history: History[] = [];
constructor(token: string, id: string) {
this.token = token;
this.id = id;
this.reset();
constructor(user: User) {
this.token = user.token;
this.id = user.id;
this.towerNumber = user.towerNumber || 0;
this.direction = user.direction || 1;
this.distance2Tower = user.distance2Tower || 0;
this.score = user.score || 0;
this.totalScore = user.totalScore || 0;
this.combo = user.combo || 0;
this.timeStart = user.timeStart || 0;
this.isStartGame = user.isStartGame || true;
this.history = user.history?.map(h => new History(h.timeStart, h.timePlayed, h.totalScore, h.tower, h.score)) || [];
this.curBlock = new v2(user.curBlock?.x || 0, user.curBlock?.y || 0);
this.nextBlock = new v2(user.nextBlock?.x || 0, user.nextBlock?.y || 0);
this.screenPos = new v2(user.screenPos?.x || 0, user.screenPos?.y || 0);
this.position = new v2(user.position?.x || 0, user.position?.y || 0);
}
public reset() {
......@@ -53,6 +68,14 @@ class History {
public totalScore: number = 0;
public tower: number = 0;
public score: number = 0;
constructor(timeStart: number, timePlayed: number, totalScore: number, tower: number, score: number) {
this.timeStart = timeStart;
this.timePlayed = timePlayed;
this.totalScore = totalScore;
this.tower = tower;
this.score = score;
}
}
export interface IDataPassTower {
......@@ -65,9 +88,7 @@ export interface IDataPassTower {
export interface IDataSpawnTower {
nextBlock: v2,
screenPos: v2,
direction: number,
distance2Tower: number,
towerNumber: number,
}
......
......@@ -2,36 +2,44 @@ import app from "./server";
import http from 'http';
import { Server } from 'socket.io';
import { setupSocket } from "./socket";
import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis";
const PORT = 3001;
const REDIS_URL = "redis://127.0.0.1:6379";
export const redis = createClient({ url: REDIS_URL });
async function initRedis() {
await redis.connect();
console.log("✅ Redis connected");
}
initRedis().catch(err => {
console.error("Redis error:", err);
});
const server = http.createServer(app);
const io = new Server(server, {
path: '/socket',
cors: {
methods: ["GET", "POST"],
origin:[
"https://play.gamee.vn",
"https://dev.gamee.vn",
"http://localhost:7456"
]
// origin: (origin, callback) => {
// const allowed = [
// "https://play.gamee.vn",
// "https://dev.gamee.vn",
// "http://localhost:7456"
// ];
// if (!origin || allowed.includes(origin)) {
// callback(null, origin);
// } else {
// callback(new Error("Not allowed by CORS"));
// }
// },
// credentials: true,
origin: [
"https://play.gamee.vn",
"https://dev.gamee.vn",
"http://localhost:7456"
]
}
});
const pubClient = createClient({ url: REDIS_URL });
const subClient = pubClient.duplicate();
Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
io.adapter(createAdapter(pubClient, subClient));
console.log("✅ Socket.IO Redis adapter connected");
});
setupSocket(io);
......
......@@ -2,8 +2,10 @@ import { Server, Socket } from "socket.io";
import User, { IDataPassTower, IDataSpawnTower, IRequestPassTower } from "./Model/User";
import v2 from "./Model/v2";
import { endGameApi, startGameApi } from "./Controller/networkCtrl";
import { redis } from ".";
const users: Map<string, User> = new Map<string, User>();
const Y_RADIO: number = 0.5560472;
const HALF_SIZE_TOWER: number = 56.43580423808985;
const INIT_SPEED: number = 300;
......@@ -25,39 +27,52 @@ const CONFIG = {
}
};
export function setupSocket(io: Server) {
const TIME_EX = 24 * 60 * 60;
export async function setupSocket(io: Server) {
try {
io.use((socket: Socket, next) => {
io.use(async (socket: Socket, next) => {
const token = String(socket.handshake.query.token);
const userId = String(socket.handshake.query.userId);
if (!token || !userId) {
return;
}
if (!users.has(userId)) {
users.set(userId, new User(token, userId));
const keys = await redis.keys("user:*");
console.log('keys', keys)
const userJson = await redis.get(`user:${userId}`);
const user = userJson ? JSON.parse(userJson) : null;
const newUser = new User(user || { token, id: userId } as User);
users.set(userId, newUser);
if (!user) {
await redis.set(`user:${userId}`, JSON.stringify(user));
await redis.expire(`user:${user.id}`, TIME_EX);
}
next();
});
io.on("connection", (socket: Socket) => {
console.log(`🟢 Client connected: ${socket.id}`);
const userId = String(socket.handshake.query.userId);
const user = users.get(userId)!;
socket.on(CONFIG.EVT.START_GAME, (data: string) => startGame(socket, data));
socket.on(CONFIG.EVT.PASS_TOWER, async (data: IRequestPassTower) => await passTower(socket, data));
socket.on(CONFIG.EVT.HISTORY, async () => getHistory(socket));
socket.on(CONFIG.EVT.START_GAME, (data: string) => startGame(socket, data, user));
socket.on(CONFIG.EVT.PASS_TOWER, async (data: IRequestPassTower) => await passTower(socket, data, user));
socket.on(CONFIG.EVT.HISTORY, async () => await getHistory(socket, user));
socket.on(CONFIG.EVT.END_GAME, () => endGame(socket));
socket.on(CONFIG.EVT.END_GAME, () => endGame(socket, user));
socket.on("disconnect", () => onDisconnect(socket));
socket.on("disconnect", async () => await onDisconnect(socket, user));
});
} catch (error) {
console.log('error', error)
}
}
async function endGame(socket: Socket) {
async function endGame(socket: Socket, user: User) {
try {
const user = getUserBySocket(socket);
await endGameApi(user);
user.reset();
} catch (error) {
......@@ -65,19 +80,24 @@ async function endGame(socket: Socket) {
}
}
function onDisconnect(socket: Socket): void {
console.log(`🔴 Client disconnected: ${socket.id}`)
// endGame(socket);
async function onDisconnect(socket: Socket, user: User) {
try {
console.log(`🔴 Client disconnected: ${socket.id}`)
await redis.set(`user:${user.id}`, JSON.stringify(user));
await redis.expire(`user:${user.id}`, TIME_EX);
users.delete(user.id);
} catch (error) {
console.log('error', error)
}
}
async function getHistory(socket: Socket) {
const user = getUserBySocket(socket);
async function getHistory(socket: Socket, user: User) {
socket.emit(CONFIG.EVT.REQUEST_HISTORY, user.history);
}
async function startGame(socket: Socket, data: any) {
async function startGame(socket: Socket, data: any, user: User) {
try {
const user = getUserBySocket(socket);
const result = await startGameApi(data);
if (result && user.isStartGame) {
......@@ -87,7 +107,7 @@ async function startGame(socket: Socket, data: any) {
if (result) {
socket.emit(CONFIG.EVT.REQUEST_START_GAME, true);
user.isStartGame = true;
addBlock(socket);
addBlock(socket, user);
} else {
console.log('start game fail');
socket.emit(CONFIG.EVT.REQUEST_START_GAME, false);
......@@ -97,13 +117,12 @@ async function startGame(socket: Socket, data: any) {
}
}
async function passTower(socket: Socket, data: IRequestPassTower) {
async function passTower(socket: Socket, data: IRequestPassTower, user: User) {
try {
const user = getUserBySocket(socket);
let xDistance_ = 0;
const { direction, nextBlock, towerNumber, position } = user;
if (towerNumber > MAX_TOWER) {
let speed = INIT_SPEED;
// data.step = Math.min(data.step, 150);
......@@ -176,7 +195,7 @@ async function passTower(socket: Socket, data: IRequestPassTower) {
socket.emit(CONFIG.EVT.REQUEST_PASS_TOWER, dataSocket);
if (score > 0) {
addBlock(socket);
addBlock(socket, user);
} else {
socket.emit(CONFIG.EVT.REQUEST_HISTORY, user.history);
await endGameApi(user);
......@@ -187,9 +206,8 @@ async function passTower(socket: Socket, data: IRequestPassTower) {
}
}
async function addBlock(socket: Socket) {
async function addBlock(socket: Socket, user: User) {
try {
const user = getUserBySocket(socket);
const towerNumber = ++user.towerNumber;
const xDistance = ((Math.min(towerNumber, 100) / 100 + 1) + Math.random() * (towerNumber <= 10 ? 0.5 : 1)) * 150;
......@@ -202,13 +220,10 @@ async function addBlock(socket: Socket) {
user.nextBlock.x = user.curBlock.x + (xDistance * user.direction);
user.nextBlock.y = user.curBlock.y + yDistance;
user.distance2Tower = user.nextBlock.distanceTo(user.curBlock);
user.screenPos = user.nextBlock.add(user.curBlock).mul(0.5).sub(new v2(0, 100));
const data: IDataSpawnTower = {
nextBlock: user.nextBlock,
screenPos: user.screenPos,
direction: user.direction,
distance2Tower: user.distance2Tower,
towerNumber: user.towerNumber,
};
user.timeStart = Date.now();
......@@ -216,9 +231,4 @@ async function addBlock(socket: Socket) {
} catch (error) {
console.log('error', error)
}
}
function getUserBySocket(socket: Socket) {
const userId = String(socket.handshake.query.userId);
return users.get(userId)!;
}
}
\ No newline at end of file
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