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

add gameCtrl

parent 857a1a59
......@@ -2,12 +2,6 @@
# Fireball Projects
#/////////////////////////////////////////////////////////////////////////////
/library/
/temp/
/local/
/build/
package-lock.json
node_modules/
# dist/
build/
......
const CONFIG = {
GAMEID: {
HUNGQUA: 1000,
},
STATE: {
WATING: 'WATING',
PLAYING: 'PLAYING',
END: 'END',
SELECT: 'SELECT',
},
TIME: {
COUNTDOWN: 10,
SELECT_PLAYER: 5,
PLAY: 60
},
EVT: {
CREATE_ROOM: '100',
JOIN_ROOM: '101',
LEAVE_ROOM: '103',
ROOM_MESSAGE: '104',
START_GAME: '200',
UPDATE_STATE: '201',
UPDATE_SCORE: '202',
UPDATE_TIME_REMAINING: '203',
END_GAME: '204',
SPAWN_TOWER: '205',
CLOSE_QR_VIEW: '206',
SPAWN_ITEM: '205',
PASS_TOWER: '207',
HISTORY: '208',
GAME_MESSAGE: '300',
RECONNECT: '500',
REQUEST_START_GAME: '600',
REQUEST_PASS_TOWER: '601',
REQUEST_SPAWN_TOWER: '602',
REQUEST_HISTORY: '603',
},
HTTP_RESPONSE: {
SUCCESS: {
code: 200,
message: "Success!",
},
ERROR: {
code: 500,
message: "Error!",
}
},
}
};
export default CONFIG;
\ No newline at end of file
import { Socket } from "socket.io";
import User, { IDataPassTower, IDataSpawnTower, IRequestPassTower } from "../Model/User";
import { endGameApiCtrl, startGameApiCtrl } from "./networkCtrl";
import v2 from "../Model/v2";
import CONFIG from "../Config/config";
// 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;
const A_POWER: number = 300;
const DT: number = 0.016;
const MAX_TOWER: number = 50;
export async function endGameCtrl(socket: Socket, user: User) {
try {
await endGameApiCtrl(user);
user.reset();
} catch (error) {
console.log('error', error)
}
}
export async function startGameCtrl(socket: Socket, data: any, user: User) {
try {
const result = await startGameApiCtrl(data);
if (result && user.isStartGame) {
user.reset();
}
if (result) {
socket.emit(CONFIG.EVT.REQUEST_START_GAME, true);
user.isStartGame = true;
addBlockCtrl(socket, user);
} else {
console.log('start game fail');
socket.emit(CONFIG.EVT.REQUEST_START_GAME, false);
}
} catch (error) {
console.log('error', error)
}
}
export async function passTowerCtrl(socket: Socket, data: IRequestPassTower, user: User) {
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(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(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: IDataPassTower = {
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) {
addBlockCtrl(socket, user);
} else {
socket.emit(CONFIG.EVT.REQUEST_HISTORY, user.history);
await endGameApiCtrl(user);
user.reset();
}
} catch (error) {
console.log('error', error)
}
}
export async function addBlockCtrl(socket: Socket, user: User) {
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(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: IDataSpawnTower = {
nextBlock: user.nextBlock,
direction: user.direction,
towerNumber: user.towerNumber,
};
user.timeStart = Date.now();
socket.emit(CONFIG.EVT.REQUEST_SPAWN_TOWER, data);
} catch (error) {
console.log('error', error)
}
}
\ No newline at end of file
......@@ -14,7 +14,7 @@ let userId = 0;
let matchId = '';
let eventId: string | null = null;
export async function startGameApi(data: any) {
export async function startGameApiCtrl(data: any) {
try {
timeStart = new Date().getTime();
userId = (Math.random() * 100 >> 0) + 10;
......@@ -31,7 +31,7 @@ export async function startGameApi(data: any) {
}
}
export async function endGameApi(user: User) {
export async function endGameApiCtrl(user: User) {
try {
const playedSeconds = (new Date().getTime() - timeStart) / 1e3;
const details = user.history.map(h => ({
......
import { redis } from "..";
export const ONLINE_KEY = 'socket:online';
export const VISITS_KEY = 'socket:visits';
export function incrUserCtrl() {
redis.incr(ONLINE_KEY);
const dayKey = getDayKey();
redis.incr(dayKey);
const hourKey = getHourKey();
redis.incr(hourKey);
}
export function decrUserCtrl() {
redis.decr(ONLINE_KEY);
const dayKey = getDayKey();
redis.decr(dayKey);
const hourKey = getHourKey();
redis.decr(hourKey);
}
function getHourKey() {
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
const hour = new Date().getHours() + 1; // 0-23
const hourKey = `${VISITS_KEY}:${today}:${hour}`;
return hourKey;
}
function getDayKey() {
const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
const dayKey = `${VISITS_KEY}:${today}`;
return dayKey;
}
\ No newline at end of file
......@@ -6,8 +6,8 @@ import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis";
const PORT = process.env.PORT || 6636;
const REDIS_URL = "redis://default:Myreview123@123a@127.0.0.1:6379";
// const REDIS_URL = "redis://127.0.0.1:6379";
// const REDIS_URL = "redis://default:Myreview123@123a@127.0.0.1:6379";
const REDIS_URL = "redis://127.0.0.1:6379";
const PATH = '/socketws/socket.io';
export const redis = createClient({ url: REDIS_URL });
......@@ -27,7 +27,7 @@ const io = new Server(server, {
path: PATH,
cors: {
methods: ["GET", "POST"],
origin: "*"
origin: "*",
}
});
......
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 User, { IRequestPassTower } from "./Model/User";
import { redis } from ".";
import { decrUserCtrl, incrUserCtrl } from "./Controller/statisticalCtrl";
import { endGameCtrl, passTowerCtrl, startGameCtrl } from "./Controller/gameCtrl";
import CONFIG from "./Config/config";
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;
const A_POWER: number = 300;
const DT: number = 0.016;
const MAX_TOWER: number = 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;
export async function setupSocket(io: Server) {
......@@ -60,14 +40,16 @@ export async function setupSocket(io: Server) {
io.on("connection", (socket: Socket) => {
try {
console.log(`🟢 Client connected: ${socket.id}`);
incrUserCtrl();
const userId = String(socket.handshake.query.userId);
const user = users.get(userId)!;
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.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.HISTORY, async () => await getHistory(socket, user));
socket.on(CONFIG.EVT.END_GAME, () => endGame(socket, user));
socket.on(CONFIG.EVT.END_GAME, () => endGameCtrl(socket, user));
socket.on("disconnect", async () => await onDisconnect(socket, user));
} catch (error) {
......@@ -77,18 +59,10 @@ export async function setupSocket(io: Server) {
}
async function endGame(socket: Socket, user: User) {
try {
await endGameApi(user);
user.reset();
} catch (error) {
console.log('error', error)
}
}
async function onDisconnect(socket: Socket, user: User) {
try {
console.log(`🔴 Client disconnected: ${socket.id}`)
decrUserCtrl();
await redis.set(`user:${user.id}`, JSON.stringify(user));
await redis.expire(`user:${user.id}`, TIME_EX);
......@@ -101,140 +75,3 @@ async function onDisconnect(socket: Socket, user: User) {
async function getHistory(socket: Socket, user: User) {
socket.emit(CONFIG.EVT.REQUEST_HISTORY, user.history);
}
async function startGame(socket: Socket, data: any, user: User) {
try {
const result = await 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)
}
}
async function passTower(socket: Socket, data: IRequestPassTower, user: User) {
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(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(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: IDataPassTower = {
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);
await endGameApi(user);
user.reset();
}
} catch (error) {
console.log('error', error)
}
}
async function addBlock(socket: Socket, user: User) {
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(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: IDataSpawnTower = {
nextBlock: user.nextBlock,
direction: user.direction,
towerNumber: user.towerNumber,
};
user.timeStart = Date.now();
socket.emit(CONFIG.EVT.REQUEST_SPAWN_TOWER, data);
} catch (error) {
console.log('error', error)
}
}
\ 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