snakeia-server 1.1.5 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.drone.yml +2 -1
- package/GameEngineMultithreading.js +97 -44
- package/GameEngineMultithreadingController.js +136 -105
- package/Player.js +132 -0
- package/README.md +34 -4
- package/assets/css/dark-theme.css +103 -0
- package/config/default.json +4 -1
- package/package.json +6 -6
- package/server.js +159 -193
- package/views/admin.html +7 -0
- package/views/authentication.html +7 -0
- package/views/banned.html +7 -0
- package/views/index.html +7 -0
package/.drone.yml
CHANGED
|
@@ -23,20 +23,57 @@ const Position = snakeia.Position;
|
|
|
23
23
|
const Grid = snakeia.Grid;
|
|
24
24
|
const Snake = snakeia.Snake;
|
|
25
25
|
const GameEngine = snakeia.GameEngine;
|
|
26
|
-
const seedrandom = require("seedrandom");
|
|
27
26
|
|
|
28
27
|
let game;
|
|
29
28
|
|
|
30
29
|
function copySnakes(snakes) {
|
|
31
|
-
|
|
30
|
+
const snakesCopy = [];
|
|
32
31
|
|
|
33
|
-
if(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
if(snakes) {
|
|
33
|
+
snakes.forEach(snake => {
|
|
34
|
+
if(snake) {
|
|
35
|
+
const snakeCopy = new Snake();
|
|
36
|
+
|
|
37
|
+
snakeCopy.color = snake.color;
|
|
38
|
+
snakeCopy.direction = snake.direction;
|
|
39
|
+
snakeCopy.errorInit = snake.errorInit;
|
|
40
|
+
snakeCopy.gameOver = snake.gameOver;
|
|
41
|
+
snakeCopy.autoRetry = snake.autoRetry;
|
|
42
|
+
snakeCopy.aiLevel = snake.aiLevel;
|
|
43
|
+
|
|
44
|
+
if(snake.lastTail) {
|
|
45
|
+
snakeCopy.lastTail = JSON.parse(JSON.stringify(snake.lastTail));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if(snake.lastHead) {
|
|
49
|
+
snakeCopy.lastHead = JSON.parse(JSON.stringify(snake.lastHead));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
snakeCopy.lastTailMoved = snake.lastTailMoved;
|
|
53
|
+
snakeCopy.lastHeadMoved = snake.lastHeadMoved;
|
|
54
|
+
snakeCopy.name = snake.name;
|
|
55
|
+
snakeCopy.player = snake.player;
|
|
56
|
+
|
|
57
|
+
if(snake.queue) {
|
|
58
|
+
snakeCopy.queue = JSON.parse(JSON.stringify(snake.queue));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
snakeCopy.score = snake.score;
|
|
62
|
+
snakeCopy.scoreMax = snake.scoreMax;
|
|
63
|
+
snakeCopy.ticksDead = snake.ticksDead;
|
|
64
|
+
snakeCopy.ticksWithoutAction = snake.ticksWithoutAction;
|
|
65
|
+
snakeCopy.grid = null;
|
|
66
|
+
|
|
67
|
+
if(snake.snakeAI && snake.snakeAI.aiLevelText) {
|
|
68
|
+
snakeCopy.snakeAI.aiLevelText = snake.snakeAI.aiLevelText;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
snakesCopy.push(snakeCopy);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
37
74
|
}
|
|
38
75
|
|
|
39
|
-
return
|
|
76
|
+
return snakesCopy;
|
|
40
77
|
}
|
|
41
78
|
|
|
42
79
|
function copyGrid(grid) {
|
|
@@ -51,35 +88,22 @@ function copyGrid(grid) {
|
|
|
51
88
|
}
|
|
52
89
|
|
|
53
90
|
function parseSnakes(snakes, grid) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
grid = Object.assign(new Grid(), grid);
|
|
91
|
+
let gridCopy = game ? (grid ?? game.grid) : grid;
|
|
92
|
+
gridCopy = Object.assign(new Grid(), gridCopy);
|
|
59
93
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
for(var j = 0; j < snakes[i].queue.length; j++) {
|
|
69
|
-
snakes[i].queue[j] = Object.assign(new Position(), snakes[i].queue[j]);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
snakes[i].initAI();
|
|
73
|
-
}
|
|
94
|
+
snakes = snakes ?? game?.snakes;
|
|
95
|
+
const snakesCopy = (Array.isArray(snakes) ? snakes : [snakes]).map(snake => {
|
|
96
|
+
const newSnake = Object.assign(new Snake(), snake);
|
|
97
|
+
newSnake.grid = gridCopy;
|
|
98
|
+
newSnake.queue = newSnake.queue.map(pos => Object.assign(new Position(), pos));
|
|
99
|
+
return newSnake;
|
|
100
|
+
});
|
|
74
101
|
|
|
75
|
-
return {
|
|
76
|
-
grid: grid,
|
|
77
|
-
snakes: snakes
|
|
78
|
-
};
|
|
102
|
+
return { grid: gridCopy, snakes: snakesCopy };
|
|
79
103
|
}
|
|
80
104
|
|
|
81
105
|
if(!isMainThread) {
|
|
82
|
-
parentPort.on("message",
|
|
106
|
+
parentPort.on("message", async data => {
|
|
83
107
|
const type = data.type;
|
|
84
108
|
const keys = Object.keys(data);
|
|
85
109
|
|
|
@@ -89,8 +113,17 @@ if(!isMainThread) {
|
|
|
89
113
|
const snakes = parsed["snakes"];
|
|
90
114
|
|
|
91
115
|
if(!game) {
|
|
92
|
-
|
|
93
|
-
|
|
116
|
+
try {
|
|
117
|
+
game = new GameEngine(grid, snakes, data.speed, data.enablePause, data.enableRetry, data.progressiveSpeed, data.aiStuckLimit, data.disableStuckAIDetection, data.aiUltraModelSettings);
|
|
118
|
+
await game.init();
|
|
119
|
+
} catch(e) {
|
|
120
|
+
console.error(e);
|
|
121
|
+
|
|
122
|
+
parentPort.postMessage({
|
|
123
|
+
type: "init",
|
|
124
|
+
errorOccurred: true
|
|
125
|
+
});
|
|
126
|
+
}
|
|
94
127
|
|
|
95
128
|
parentPort.postMessage({
|
|
96
129
|
type: "init",
|
|
@@ -100,7 +133,8 @@ if(!isMainThread) {
|
|
|
100
133
|
"enableRetry": game.enableRetry,
|
|
101
134
|
"progressiveSpeed": game.progressiveSpeed,
|
|
102
135
|
"offsetFrame": game.speed * GameConstants.Setting.TIME_MULTIPLIER,
|
|
103
|
-
"errorOccurred": game.errorOccurred
|
|
136
|
+
"errorOccurred": game.errorOccurred,
|
|
137
|
+
"engineLoading": game.engineLoading
|
|
104
138
|
});
|
|
105
139
|
|
|
106
140
|
game.onReset(() => {
|
|
@@ -127,7 +161,8 @@ if(!isMainThread) {
|
|
|
127
161
|
"getInfosGame": false,
|
|
128
162
|
"errorOccurred": game.errorOccurred,
|
|
129
163
|
"aiStuck": game.aiStuck,
|
|
130
|
-
"precAiStuck": false
|
|
164
|
+
"precAiStuck": false,
|
|
165
|
+
"engineLoading": game.engineLoading
|
|
131
166
|
});
|
|
132
167
|
});
|
|
133
168
|
|
|
@@ -144,7 +179,8 @@ if(!isMainThread) {
|
|
|
144
179
|
"confirmExit": false,
|
|
145
180
|
"getInfos": false,
|
|
146
181
|
"getInfosGame": false,
|
|
147
|
-
"errorOccurred": game.errorOccurred
|
|
182
|
+
"errorOccurred": game.errorOccurred,
|
|
183
|
+
"engineLoading": game.engineLoading
|
|
148
184
|
});
|
|
149
185
|
});
|
|
150
186
|
|
|
@@ -156,7 +192,8 @@ if(!isMainThread) {
|
|
|
156
192
|
"confirmExit": false,
|
|
157
193
|
"getInfos": false,
|
|
158
194
|
"getInfosGame": false,
|
|
159
|
-
"errorOccurred": game.errorOccurred
|
|
195
|
+
"errorOccurred": game.errorOccurred,
|
|
196
|
+
"engineLoading": game.engineLoading
|
|
160
197
|
});
|
|
161
198
|
});
|
|
162
199
|
|
|
@@ -167,7 +204,8 @@ if(!isMainThread) {
|
|
|
167
204
|
"confirmExit": false,
|
|
168
205
|
"getInfos": false,
|
|
169
206
|
"getInfosGame": false,
|
|
170
|
-
"errorOccurred": game.errorOccurred
|
|
207
|
+
"errorOccurred": game.errorOccurred,
|
|
208
|
+
"engineLoading": game.engineLoading
|
|
171
209
|
});
|
|
172
210
|
});
|
|
173
211
|
|
|
@@ -182,7 +220,8 @@ if(!isMainThread) {
|
|
|
182
220
|
"confirmExit": false,
|
|
183
221
|
"getInfos": false,
|
|
184
222
|
"getInfosGame": false,
|
|
185
|
-
"errorOccurred": game.errorOccurred
|
|
223
|
+
"errorOccurred": game.errorOccurred,
|
|
224
|
+
"engineLoading": game.engineLoading
|
|
186
225
|
});
|
|
187
226
|
});
|
|
188
227
|
|
|
@@ -197,7 +236,8 @@ if(!isMainThread) {
|
|
|
197
236
|
"confirmExit": false,
|
|
198
237
|
"getInfos": false,
|
|
199
238
|
"getInfosGame": false,
|
|
200
|
-
"errorOccurred": game.errorOccurred
|
|
239
|
+
"errorOccurred": game.errorOccurred,
|
|
240
|
+
"engineLoading": game.engineLoading
|
|
201
241
|
});
|
|
202
242
|
});
|
|
203
243
|
|
|
@@ -214,7 +254,8 @@ if(!isMainThread) {
|
|
|
214
254
|
"confirmExit": false,
|
|
215
255
|
"getInfos": false,
|
|
216
256
|
"getInfosGame": false,
|
|
217
|
-
"errorOccurred": game.errorOccurred
|
|
257
|
+
"errorOccurred": game.errorOccurred,
|
|
258
|
+
"engineLoading": game.engineLoading
|
|
218
259
|
});
|
|
219
260
|
});
|
|
220
261
|
|
|
@@ -243,7 +284,8 @@ if(!isMainThread) {
|
|
|
243
284
|
"numFruit": game.numFruit,
|
|
244
285
|
"offsetFrame": 0,
|
|
245
286
|
"errorOccurred": game.errorOccurred,
|
|
246
|
-
"aiStuck": game.aiStuck
|
|
287
|
+
"aiStuck": game.aiStuck,
|
|
288
|
+
"engineLoading": game.engineLoading
|
|
247
289
|
});
|
|
248
290
|
});
|
|
249
291
|
|
|
@@ -266,14 +308,25 @@ if(!isMainThread) {
|
|
|
266
308
|
"speed": game.speed,
|
|
267
309
|
"countBeforePlay": game.countBeforePlay,
|
|
268
310
|
"numFruit": game.numFruit,
|
|
269
|
-
"errorOccurred": game.errorOccurred
|
|
311
|
+
"errorOccurred": game.errorOccurred,
|
|
312
|
+
"engineLoading": game.engineLoading
|
|
270
313
|
});
|
|
271
314
|
});
|
|
272
315
|
} else {
|
|
273
316
|
game.snakes = snakes;
|
|
274
317
|
game.grid = grid;
|
|
275
318
|
game.countBeforePlay = 3;
|
|
276
|
-
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
await game.init();
|
|
322
|
+
} catch(e) {
|
|
323
|
+
console.error(e);
|
|
324
|
+
|
|
325
|
+
parentPort.postMessage({
|
|
326
|
+
type: "init",
|
|
327
|
+
errorOccurred: true
|
|
328
|
+
});
|
|
329
|
+
}
|
|
277
330
|
}
|
|
278
331
|
} else if(game) {
|
|
279
332
|
switch(type) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (C) 2020 Eliastik (eliastiksofts.com)
|
|
2
|
+
* Copyright (C) 2020-2025 Eliastik (eliastiksofts.com)
|
|
3
3
|
*
|
|
4
4
|
* This file is part of "SnakeIA Server".
|
|
5
5
|
*
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* You should have received a copy of the GNU General Public License
|
|
17
17
|
* along with "SnakeIA Server". If not, see <http://www.gnu.org/licenses/>.
|
|
18
18
|
*/
|
|
19
|
-
const { Worker } = require(
|
|
19
|
+
const { Worker } = require("worker_threads");
|
|
20
20
|
const snakeia = require("snakeia");
|
|
21
21
|
const GameEngine = snakeia.GameEngine;
|
|
22
22
|
const Grid = snakeia.Grid;
|
|
@@ -25,132 +25,145 @@ const Position = snakeia.Position;
|
|
|
25
25
|
let logger;
|
|
26
26
|
|
|
27
27
|
class GameEngineMultithreadingController extends GameEngine {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
|
|
29
|
+
constructor(grid, snakes, speed, enablePause, enableRetry, progressiveSpeed, aiStuckLimit, disableStuckAIDetection, aiUltraModelSettings) {
|
|
30
|
+
super(grid, snakes, speed, enablePause, enableRetry, progressiveSpeed, aiStuckLimit, disableStuckAIDetection, aiUltraModelSettings);
|
|
30
31
|
this.worker = new Worker("./GameEngineMultithreading.js");
|
|
32
|
+
this.workerReady = false;
|
|
33
|
+
this.messageQueue = []; // Queue of message if the worker is still loading
|
|
31
34
|
this.eventsInit = false;
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
init() {
|
|
35
|
-
|
|
36
|
-
this.
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
async init() {
|
|
38
|
+
return new Promise(resolve => {
|
|
39
|
+
if(!this.eventsInit) {
|
|
40
|
+
this.worker.on("message", (data) => {
|
|
41
|
+
const type = data.type;
|
|
42
|
+
const dataKeys = Object.keys(data);
|
|
39
43
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
if(dataKeys.length > 1) {
|
|
45
|
+
let grid = this.grid;
|
|
46
|
+
let snakes = this.snakes;
|
|
43
47
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if(data.snakes) {
|
|
50
|
-
for(let i = 0; i < data.snakes.length; i++) {
|
|
51
|
-
data.snakes[i].grid = grid;
|
|
52
|
-
data.snakes[i] = Object.assign(new Snake(), data.snakes[i]);
|
|
53
|
-
|
|
54
|
-
for(let j = 0; j < data.snakes[i].queue.length; j++) {
|
|
55
|
-
data.snakes[i].queue[j] = Object.assign(new Position(), data.snakes[i].queue[j]);
|
|
56
|
-
}
|
|
48
|
+
if(data.grid) {
|
|
49
|
+
grid = Object.assign(new Grid(), data.grid);
|
|
50
|
+
data.grid = grid;
|
|
57
51
|
}
|
|
52
|
+
|
|
53
|
+
if(data.snakes) {
|
|
54
|
+
for(let i = 0; i < data.snakes.length; i++) {
|
|
55
|
+
data.snakes[i].grid = grid;
|
|
56
|
+
data.snakes[i] = Object.assign(new Snake(), data.snakes[i]);
|
|
57
|
+
|
|
58
|
+
for(let j = 0; j < data.snakes[i].queue.length; j++) {
|
|
59
|
+
data.snakes[i].queue[j] = Object.assign(new Position(), data.snakes[i].queue[j]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
snakes = data.snakes;
|
|
64
|
+
}
|
|
61
65
|
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
this.snakes = snakes;
|
|
67
|
+
this.grid = grid;
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
for(let i = 0; i < dataKeys.length; i++) {
|
|
70
|
+
if(dataKeys[i] != "snakes" && dataKeys[i] != "grid") {
|
|
71
|
+
this[dataKeys[i]] = data[dataKeys[i]];
|
|
72
|
+
}
|
|
68
73
|
}
|
|
69
74
|
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
switch(type) {
|
|
73
|
-
case "reset":
|
|
74
|
-
this.reactor.dispatchEvent("onReset");
|
|
75
|
-
break;
|
|
76
|
-
case "start":
|
|
77
|
-
this.reactor.dispatchEvent("onStart");
|
|
78
|
-
break;
|
|
79
|
-
case "pause":
|
|
80
|
-
this.reactor.dispatchEvent("onPause");
|
|
81
|
-
break;
|
|
82
|
-
case "continue":
|
|
83
|
-
this.reactor.dispatchEvent("onContinue");
|
|
84
|
-
break;
|
|
85
|
-
case "stop":
|
|
86
|
-
this.reactor.dispatchEvent("onStop");
|
|
87
|
-
break;
|
|
88
|
-
case "exit":
|
|
89
|
-
this.reactor.dispatchEvent("onExit");
|
|
90
|
-
break;
|
|
91
|
-
case "kill":
|
|
92
|
-
this.reactor.dispatchEvent("onKill");
|
|
93
|
-
break;
|
|
94
|
-
case "scoreIncreased":
|
|
95
|
-
this.reactor.dispatchEvent("onScoreIncreased");
|
|
96
|
-
break;
|
|
97
|
-
case "update":
|
|
98
|
-
this.reactor.dispatchEvent("onUpdate");
|
|
99
|
-
break;
|
|
100
|
-
case "updateCounter":
|
|
101
|
-
this.reactor.dispatchEvent("onUpdateCounter");
|
|
102
|
-
break;
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
this.worker.on("error", (error) => {
|
|
107
|
-
if(logger) logger.error("Error in GameEngineMultithreading", error);
|
|
108
|
-
this.errorOccurred = true;
|
|
109
|
-
this.reactor.dispatchEvent("onStop");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
this.worker.on("exit", () => {
|
|
113
|
-
this.gameFinished = true;
|
|
114
|
-
this.reactor.dispatchEvent("onStop");
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
this.eventsInit = true;
|
|
118
|
-
}
|
|
119
75
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
76
|
+
switch(type) {
|
|
77
|
+
case "init":
|
|
78
|
+
this.workerReady = true;
|
|
79
|
+
this.passQueuedMessages();
|
|
80
|
+
resolve();
|
|
81
|
+
break;
|
|
82
|
+
case "reset":
|
|
83
|
+
this.reactor.dispatchEvent("onReset");
|
|
84
|
+
break;
|
|
85
|
+
case "start":
|
|
86
|
+
this.reactor.dispatchEvent("onStart");
|
|
87
|
+
break;
|
|
88
|
+
case "pause":
|
|
89
|
+
this.reactor.dispatchEvent("onPause");
|
|
90
|
+
break;
|
|
91
|
+
case "continue":
|
|
92
|
+
this.reactor.dispatchEvent("onContinue");
|
|
93
|
+
break;
|
|
94
|
+
case "stop":
|
|
95
|
+
this.reactor.dispatchEvent("onStop");
|
|
96
|
+
break;
|
|
97
|
+
case "exit":
|
|
98
|
+
this.reactor.dispatchEvent("onExit");
|
|
99
|
+
break;
|
|
100
|
+
case "kill":
|
|
101
|
+
this.reactor.dispatchEvent("onKill");
|
|
102
|
+
break;
|
|
103
|
+
case "scoreIncreased":
|
|
104
|
+
this.reactor.dispatchEvent("onScoreIncreased");
|
|
105
|
+
break;
|
|
106
|
+
case "update":
|
|
107
|
+
this.reactor.dispatchEvent("onUpdate");
|
|
108
|
+
break;
|
|
109
|
+
case "updateCounter":
|
|
110
|
+
this.reactor.dispatchEvent("onUpdateCounter");
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
this.worker.on("error", (error) => {
|
|
116
|
+
if(logger) logger.error("Error in GameEngineMultithreading", error);
|
|
117
|
+
this.errorOccurred = true;
|
|
118
|
+
this.reactor.dispatchEvent("onStop");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
this.worker.on("exit", () => {
|
|
122
|
+
this.gameFinished = true;
|
|
123
|
+
this.reactor.dispatchEvent("onStop");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
this.eventsInit = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if(this.grid) {
|
|
130
|
+
this.grid.rngGrid = null;
|
|
131
|
+
this.grid.rngGame = null;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.worker.postMessage({
|
|
135
|
+
type: "init",
|
|
136
|
+
grid: this.grid,
|
|
137
|
+
snakes: this.snakes,
|
|
138
|
+
speed: this.speed,
|
|
139
|
+
enablePause: this.enablePause,
|
|
140
|
+
enableRetry: this.enableRetry,
|
|
141
|
+
progressiveSpeed: this.progressiveSpeed,
|
|
142
|
+
aiStuckLimit: this.aiStuckLimit,
|
|
143
|
+
disableStuckAIDetection: this.disableStuckAIDetection,
|
|
144
|
+
aiUltraModelSettings: this.aiUltraModelSettings
|
|
145
|
+
});
|
|
133
146
|
});
|
|
134
147
|
}
|
|
135
148
|
|
|
136
149
|
reset() {
|
|
137
|
-
|
|
150
|
+
this.passMessage({ type: "reset" });
|
|
138
151
|
}
|
|
139
152
|
|
|
140
153
|
start() {
|
|
141
|
-
|
|
154
|
+
this.passMessage({ type: "start" });
|
|
142
155
|
}
|
|
143
156
|
|
|
144
157
|
stop(finish) {
|
|
145
|
-
|
|
158
|
+
this.passMessage({ type: finish ? "finish" : "stop" });
|
|
146
159
|
}
|
|
147
160
|
|
|
148
161
|
finish(finish) {
|
|
149
|
-
|
|
162
|
+
this.passMessage({ type: finish ? "finish" : "stop" });
|
|
150
163
|
}
|
|
151
164
|
|
|
152
165
|
pause() {
|
|
153
|
-
|
|
166
|
+
this.passMessage({ type: "pause" });
|
|
154
167
|
}
|
|
155
168
|
|
|
156
169
|
kill() {
|
|
@@ -162,27 +175,27 @@ class GameEngineMultithreadingController extends GameEngine {
|
|
|
162
175
|
}
|
|
163
176
|
|
|
164
177
|
tick() {
|
|
165
|
-
|
|
178
|
+
this.passMessage({ type: "tick" });
|
|
166
179
|
}
|
|
167
180
|
|
|
168
181
|
exit() {
|
|
169
|
-
|
|
182
|
+
this.passMessage({ type: "exit" });
|
|
170
183
|
}
|
|
171
184
|
|
|
172
185
|
key(key, numSnake) {
|
|
173
|
-
|
|
186
|
+
this.passMessage({ type: "key", key: key, numSnake: numSnake });
|
|
174
187
|
}
|
|
175
188
|
|
|
176
189
|
setGameOver(numSnake) {
|
|
177
|
-
|
|
190
|
+
this.passMessage({ type: "setGameOver", numSnake: numSnake });
|
|
178
191
|
}
|
|
179
192
|
|
|
180
193
|
forceStart() {
|
|
181
|
-
|
|
194
|
+
this.passMessage({ type: "forceStart" });
|
|
182
195
|
}
|
|
183
196
|
|
|
184
197
|
updateEngine(key, data) {
|
|
185
|
-
|
|
198
|
+
this.passMessage({ type: "update", key: key, data: data });
|
|
186
199
|
}
|
|
187
200
|
|
|
188
201
|
onReset(callback) {
|
|
@@ -224,6 +237,24 @@ class GameEngineMultithreadingController extends GameEngine {
|
|
|
224
237
|
onUpdateCounter(callback) {
|
|
225
238
|
this.reactor.addEventListener("onUpdateCounter", callback);
|
|
226
239
|
}
|
|
240
|
+
|
|
241
|
+
passMessage(message) {
|
|
242
|
+
if(this.workerReady && this.worker) {
|
|
243
|
+
this.worker.postMessage(message);
|
|
244
|
+
} else {
|
|
245
|
+
this.messageQueue.push(message);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
passQueuedMessages() {
|
|
250
|
+
if(this.workerReady && this.worker) {
|
|
251
|
+
this.messageQueue.forEach(message => {
|
|
252
|
+
this.worker.postMessage(message);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
this.messageQueue = [];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
227
258
|
}
|
|
228
259
|
|
|
229
260
|
module.exports = function(l) {
|
package/Player.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2020-2025 Eliastik (eliastiksofts.com)
|
|
3
|
+
*
|
|
4
|
+
* This file is part of "SnakeIA Server".
|
|
5
|
+
*
|
|
6
|
+
* "SnakeIA Server" is free software: you can redistribute it and/or modify
|
|
7
|
+
* it under the terms of the GNU General Public License as published by
|
|
8
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
* (at your option) any later version.
|
|
10
|
+
*
|
|
11
|
+
* "SnakeIA Server" is distributed in the hope that it will be useful,
|
|
12
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
* GNU General Public License for more details.
|
|
15
|
+
*
|
|
16
|
+
* You should have received a copy of the GNU General Public License
|
|
17
|
+
* along with "SnakeIA Server". If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
class Player {
|
|
20
|
+
constructor(token, id, snake, ready, version) {
|
|
21
|
+
this.token = token;
|
|
22
|
+
this.id = id;
|
|
23
|
+
this.snake = snake;
|
|
24
|
+
this.ready = ready;
|
|
25
|
+
this.version = version;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get username() {
|
|
29
|
+
return Player.getUsername(this);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static getPlayer(array, id) {
|
|
33
|
+
for (let i = 0; i < array.length; i++) {
|
|
34
|
+
if (array[i] != null && array[i].id == id) {
|
|
35
|
+
return array[i];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static getPlayerAllGames(id, games) {
|
|
43
|
+
const keys = Object.keys(games);
|
|
44
|
+
|
|
45
|
+
for (let i = 0; i < keys.length; i++) {
|
|
46
|
+
const game = games[keys[i]];
|
|
47
|
+
|
|
48
|
+
if (game) {
|
|
49
|
+
const p = this.getPlayer(game.players, id);
|
|
50
|
+
const p2 = this.getPlayer(game.spectators, id);
|
|
51
|
+
if (p) return p;
|
|
52
|
+
if (p2) return p2;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
static getPlayerToken(array, token) {
|
|
60
|
+
if (!token) return null;
|
|
61
|
+
for (let i = 0; i < array.length; i++) {
|
|
62
|
+
if (array[i] != null && array[i].token == token) {
|
|
63
|
+
return array[i];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static getPlayerAllGamesToken(token, games) {
|
|
71
|
+
if (!token) return null;
|
|
72
|
+
const keys = Object.keys(games);
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < keys.length; i++) {
|
|
75
|
+
const game = games[keys[i]];
|
|
76
|
+
|
|
77
|
+
if (game) {
|
|
78
|
+
const p = this.getPlayerToken(game.players, token);
|
|
79
|
+
const p2 = this.getPlayerToken(game.spectators, token);
|
|
80
|
+
if (p) return p;
|
|
81
|
+
if (p2) return p2;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static containsId(array, id) {
|
|
89
|
+
return Player.getPlayer(array, id) != null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
static containsToken(array, token) {
|
|
93
|
+
return Player.getPlayerToken(array, token) != null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
static containsIdAllGames(id, games) {
|
|
97
|
+
return Player.getPlayerAllGames(id, games) != null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static containsTokenAllGames(token, games) {
|
|
101
|
+
return Player.getPlayerAllGamesToken(token, games) != null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
static getUsername(player) {
|
|
105
|
+
try {
|
|
106
|
+
const decoded_token = jwt.verify(player.token, jsonWebTokenSecretKey);
|
|
107
|
+
return decoded_token && decoded_token.username
|
|
108
|
+
? decoded_token.username
|
|
109
|
+
: null;
|
|
110
|
+
} catch (e) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static getUsernameSocket(socket) {
|
|
116
|
+
try {
|
|
117
|
+
const decoded_token = jwt.verify(
|
|
118
|
+
socket.handshake.auth.token ||
|
|
119
|
+
socket.handshake.query.token ||
|
|
120
|
+
socket.request.cookies.token,
|
|
121
|
+
jsonWebTokenSecretKey
|
|
122
|
+
);
|
|
123
|
+
return decoded_token && decoded_token.username
|
|
124
|
+
? decoded_token.username
|
|
125
|
+
: null;
|
|
126
|
+
} catch (e) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = Player;
|