1
- import { schedule } from "node-cron" ;
2
1
import { v4 as createUUID } from "uuid" ;
2
+ import { loadCache , saveCache } from "./cache" ;
3
3
import type { Point } from "./messages" ;
4
4
import { publish , subscribe } from "./rabbitmq" ;
5
5
6
- const players = new Map < string , Point > ( ) ;
6
+ const players = loadCache ( ) ;
7
+ const goals = new Map < string , Point > ( ) ;
8
+ const speeds = new Map < string , number > ( ) ;
7
9
8
10
const MAX_PLAYERS = 20 ;
9
11
@@ -12,56 +14,110 @@ console.log("simulator started");
12
14
function addPlayer ( ) {
13
15
const uuid = createUUID ( ) ;
14
16
console . log ( `${ uuid } joined the game (${ players . size } /${ MAX_PLAYERS } )` ) ;
15
- players . set ( uuid , {
16
- x : Math . floor ( Math . random ( ) * 200 - 100 ) ,
17
- y : Math . floor ( Math . random ( ) * 40 + 100 ) ,
18
- z : Math . floor ( Math . random ( ) * 200 - 100 ) ,
19
- world : "overworld" ,
20
- } ) ;
17
+ const pos = randomPos ( ) ;
18
+ players . set ( uuid , pos ) ;
19
+ publish ( "PlayerMoveMessage" , { pos, player : uuid } ) ;
21
20
}
22
21
23
- function removePlayer ( ) {
24
- const index = Math . floor ( Math . random ( ) * players . size ) ;
25
- const uuid = [ ...players . keys ( ) ] [ index ] ;
22
+ function removePlayer ( uuid : string ) {
26
23
console . log ( `${ uuid } left the game (${ players . size } /${ MAX_PLAYERS } )` ) ;
27
24
players . delete ( uuid ) ;
28
25
publish ( "PlayerDisconnectMessage" , { player : uuid } ) ;
29
26
}
30
27
31
- for ( let i = 0 ; i < MAX_PLAYERS / 2 ; i ++ ) {
32
- addPlayer ( ) ;
28
+ if ( players . size === 0 ) {
29
+ for ( let i = 0 ; i < MAX_PLAYERS / 2 ; i ++ ) {
30
+ addPlayer ( ) ;
31
+ }
33
32
}
34
33
35
- const interval = process . env . INTERVAL || "*/2 * * * * *" ;
34
+ let interval = Number . parseInt ( process . env . INTERVAL ?? "" ) ;
35
+ if ( isNaN ( interval ) ) interval = 100 ;
36
+
37
+ function randomPos ( ) : Point {
38
+ const x = Math . random ( ) * 1000 - 500 ;
39
+ const z = Math . random ( ) * 1000 - 500 ;
40
+ const y = 80 ;
41
+ const world = "overworld" ;
42
+ return { x, y, z, world } ;
43
+ }
44
+
45
+ function step ( pos : Point , uuid : string ) {
46
+ let goal = goals . get ( uuid ) ;
47
+
48
+ if ( ! goal ) {
49
+ if ( Math . random ( ) < 0.1 ) return removePlayer ( uuid ) ;
50
+ goal = randomPos ( ) ;
51
+ goals . set ( uuid , goal ) ;
52
+ }
53
+
54
+ if ( Math . random ( ) > 0.2 ) return ;
55
+
56
+ const diff = {
57
+ x : goal . x - pos . x ,
58
+ z : goal . z - pos . z ,
59
+ } ;
60
+
61
+ const dist = Math . sqrt ( Math . pow ( diff . x , 2 ) + Math . pow ( diff . x , 2 ) ) ;
62
+
63
+ const speed = speeds . get ( uuid ) ?? 0.1 ;
64
+
65
+ if ( Math . random ( ) < 0.2 ) {
66
+ speeds . set ( uuid , Math . max ( 0 , speed - 0.1 ) ) ;
67
+ } else if ( Math . random ( ) > 0.5 ) {
68
+ speeds . set ( uuid , Math . min ( 3 , speed + 0.1 ) ) ;
69
+ }
36
70
37
- schedule (
38
- interval ,
39
- ( ) => {
40
- if ( players . size > 0 && Math . random ( ) < 0.5 ) {
41
- removePlayer ( ) ;
42
- }
71
+ const vec = {
72
+ x : ( diff . x / dist ) * speed ,
73
+ z : ( diff . z / dist ) * speed ,
74
+ } ;
43
75
44
- players . forEach ( ( pos , key ) => {
45
- if ( Math . random ( ) > 0.2 ) return ;
76
+ function add ( a : number , b : number ) {
77
+ if ( b < 0 ) return Math . floor ( a + b ) ;
78
+ return Math . ceil ( a + b ) ;
79
+ }
46
80
47
- const dx = Math . floor ( Math . random ( ) * 24 - 12 ) ;
48
- const dz = Math . floor ( Math . random ( ) * 24 - 12 ) ;
49
- players . set ( key , { ...pos , x : pos . x + dx , z : pos . z + dz } ) ;
50
- } ) ;
81
+ const newPos : Point = {
82
+ ...pos ,
83
+ x : add ( pos . x , vec . x ) ,
84
+ z : add ( pos . z , vec . z ) ,
85
+ } ;
51
86
52
- if ( players . size < MAX_PLAYERS && Math . random ( ) < 0.5 ) {
53
- addPlayer ( ) ;
54
- }
87
+ publish ( "PlayerMoveMessage" , { pos : newPos , player : uuid } ) ;
55
88
56
- players . forEach ( ( pos , player ) => {
57
- publish ( "PlayerMoveMessage" , { pos, player } ) ;
58
- } ) ;
59
- } ,
60
- { runOnInit : true }
61
- ) ;
89
+ players . set ( uuid , newPos ) ;
90
+ }
91
+
92
+ setInterval ( ( ) => {
93
+ players . forEach ( step ) ;
94
+
95
+ if ( players . size < MAX_PLAYERS && Math . random ( ) < 0.5 ) {
96
+ addPlayer ( ) ;
97
+ }
98
+
99
+ saveCache ( players ) ;
100
+ } , interval ) ;
62
101
63
102
publish ( "ServerStatusMessage" , { status : "STARTED" } ) ;
64
103
65
104
subscribe ( "RestartMessage" , ( ) => {
66
105
publish ( "ServerStatusMessage" , { status : "RECOVERED" } ) ;
67
106
} ) ;
107
+
108
+ function shutdown ( ) {
109
+ console . log ( "shutting down simulator" ) ;
110
+
111
+ players . forEach ( ( _ , player ) => {
112
+ publish ( "PlayerDisconnectMessage" , { player } ) ;
113
+ } ) ;
114
+
115
+ publish ( "ServerStatusMessage" , { status : "STOPPED" } ) ;
116
+ }
117
+
118
+ process . on ( "exit" , shutdown ) ;
119
+ process . on ( "SIGINT" , shutdown ) ;
120
+ process . on ( "SIGUSR1" , shutdown ) ;
121
+ process . on ( "SIGUSR2" , shutdown ) ;
122
+ process . on ( "SIGTERM" , shutdown ) ;
123
+ process . on ( "SIGBREAK" , shutdown ) ;
0 commit comments