Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
game-server-flip-jump
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Gia Vương
game-server-flip-jump
Commits
a2d20591
Commit
a2d20591
authored
Aug 22, 2025
by
Vũ Gia Vương
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add gameCtrl
parent
857a1a59
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
218 additions
and
228 deletions
+218
-228
.gitignore
.gitignore
+0
-6
config.ts
src/Config/config.ts
+5
-44
gameCtrl.ts
src/Controller/gameCtrl.ts
+161
-0
networkCtrl.ts
src/Controller/networkCtrl.ts
+2
-2
statisticalCtrl.ts
src/Controller/statisticalCtrl.ts
+37
-0
index.ts
src/index.ts
+3
-3
socket.ts
src/socket.ts
+10
-173
No files found.
.gitignore
View file @
a2d20591
...
...
@@ -2,12 +2,6 @@
# Fireball Projects
#/////////////////////////////////////////////////////////////////////////////
/library/
/temp/
/local/
/build/
package-lock.json
node_modules/
# dist/
build/
...
...
src/Config/config.ts
View file @
a2d20591
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
src/Controller/gameCtrl.ts
0 → 100644
View file @
a2d20591
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
src/Controller/networkCtrl.ts
View file @
a2d20591
...
...
@@ -14,7 +14,7 @@ let userId = 0;
let
matchId
=
''
;
let
eventId
:
string
|
null
=
null
;
export
async
function
startGameApi
(
data
:
any
)
{
export
async
function
startGameApi
Ctrl
(
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
endGameApi
Ctrl
(
user
:
User
)
{
try
{
const
playedSeconds
=
(
new
Date
().
getTime
()
-
timeStart
)
/
1
e3
;
const
details
=
user
.
history
.
map
(
h
=>
({
...
...
src/Controller/statisticalCtrl.ts
0 → 100644
View file @
a2d20591
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
src/index.ts
View file @
a2d20591
...
...
@@ -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
:
"*"
,
}
});
...
...
src/socket.ts
View file @
a2d20591
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
)
=>
startGame
Ctrl
(
socket
,
data
,
user
));
socket
.
on
(
CONFIG
.
EVT
.
PASS_TOWER
,
async
(
data
:
IRequestPassTower
)
=>
await
passTower
Ctrl
(
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
,
()
=>
endGame
Ctrl
(
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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment