-
Notifications
You must be signed in to change notification settings - Fork 0
/
server.js
491 lines (379 loc) · 14.3 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
// Enable "require" usage
import { createRequire } from "module";
const require = createRequire(import.meta.url);
// Node.js Dependencies
const http = require("http");
const express = require("express");
const { Server } = require("socket.io");
// Enable __dirname usage
import { fileURLToPath } from 'url';
const path = require("path");
const __dirname = path.dirname(fileURLToPath(import.meta.url))
// Import Utils
import { Random } from "./src/utils/Random.js";
import { Time } from "./src/utils/Time.js";
// Game Datas
const STARTING_MONEY = 100;
import JSON_ITEMS from "./src/server/datas/ItemList.json" assert {type: "json"};
// Customers spawn rate
const CUSTOMER_SPAWN_RATE = {
MEAN : Time.getSeconds(0, 2), // 2 min
STD_DEV : Time.getSeconds(30, 0) // 30 sec
}
// Customers despawn rate
const CUSTOMER_SHOPING_RATE = {
MEAN : Time.getSeconds(30), // 30 sec
STD_DEV : Time.getSeconds(5) // 5 sec
}
// Customers despawn rate
const CUSTOMER_DESPAWN_RATE = {
MEAN : Time.getSeconds(0, 5), // 5 min
STD_DEV : Time.getSeconds(0, 1) // 1 min
}
// Wholesale spawn rate
const WHOLESALE_SPAWN_RATE = {
MEAN : Time.getSeconds(0, 2), // 2 min
STD_DEV : Time.getSeconds(30, 0) // 30 sec
}
// Wholesale despawn rate
const WHOLESALE_DESPAWN_RATE = {
MEAN : Time.getSeconds(0, 3), // 3 min
STD_DEV : Time.getSeconds(30, 0) // 30 sec
}
// Import Game Modules
import {
Item,
Player,
Customer,
Wholesale,
Category,
Theme
} from "./src/server/index.js";
// GLOBAL VARIABLES
const PORT = 5000;
//============================================================= Init Server ========================================================
// Init
const app = express();
const server = http.createServer(app);
const io = new Server(server);
// Setup
app.use("/client", express.static(__dirname + "/src/client"));
app.use("/utils", express.static(__dirname + "/src/utils"));
// Routing
app.get("/", function(request, response){
response.sendFile(path.join(__dirname, "/src/client/html/index.html"));
});
// Server Start
server.listen(PORT, function(){
console.log("Starting server on port " + PORT);
});
//============================================================= Init Game State ========================================================
let theme_list = {};
// List of Categories
let category_list = {};
// List of Items
let item_list = {};
// Read Item List from JSON Data file
let theme_id = 0
let category_id = 0;
let item_id = 0;
for(let item_theme of JSON_ITEMS){
theme_list[theme_id] = (new Theme(theme_id, item_theme.name));
for(let item_category of item_theme.category){
category_list[category_id] = (new Category(category_id, item_category.name));
for(let item of item_category.items){
item_list[item_id] = (new Item(item_id, item.name, theme_list[theme_id], category_list[category_id]));
//category_list[category_id].addItem(item_list[item_id]);
theme_list[theme_id].addItem(item_list[item_id], category_list[category_id]);
item_id++;
}
category_id++;
}
theme_id++;
}
// List of Players
let player_list = {};
// List of Customers
let customer_list = [];
// === FOR DEBUG ONLY ===
// // Generate random shopping rate
// let shopping_timer = Random.normal(CUSTOMER_SHOPING_RATE.MEAN, CUSTOMER_SHOPING_RATE.STD_DEV);
// // Generate random despawn rate
// let despawn_timer = Random.normal(CUSTOMER_DESPAWN_RATE.MEAN, CUSTOMER_DESPAWN_RATE.STD_DEV);
// // Generate new Customer
// let new_customer = new Customer(Random.uniformInt(50, 100), shopping_timer, despawn_timer);
// // Generate random wishlist
// new_customer.generateRandomWishlist(item_list);
// // Add new Customer to Customer List
// customer_list.push(new_customer);
// ======================
// List of Wholesales
let wholesale_list = [];
// Generate 2 initals Wholesales
for(let i = 0; i < 2; i++){
// Generate deswpan rate
let despawn_timer = Random.normal(WHOLESALE_DESPAWN_RATE.MEAN / (2 - i), WHOLESALE_DESPAWN_RATE.STD_DEV / (2 - i));
// Create new Wholesale and push it to Wholesale List
wholesale_list.push(Wholesale.generateRandomWholesale(i, theme_list, despawn_timer));
}
//============================================================= Player Interactions ========================================================
// New Client connection, set up Player-Server intaractions
io.on("connection", (socket) => {
//=========================== New Player ==================================
socket.on("New Player", (pseudo) => {
// Send back Player ID
socket.emit("id", socket.id);
// Create new Player
let new_player = new Player(socket, pseudo, STARTING_MONEY);
// Add new player to Player List
player_list[socket.id] = new_player
// === FOR DEBUG ONLY ===
//for(let i in item_list)
new_player.inventory.addItem(item_list[0], 10, 10);
new_player.inventory.addItem(item_list[2], 10, 10);
new_player.inventory.addItem(item_list[4], 10, 10);
new_player.inventory.addItem(item_list[8], 10, 10);
new_player.inventory.addItem(item_list[20], 10, 10);
new_player.update();
// ======================
// // Update the LeaderBoard
// updateLeaderBoard();
// Update the Customers
updateCustomers();
// Update Wholesale
updateWholesales();
});
//=========================== Player Inputs ==================================
// Update Prices
socket.on("Player Change Item Price", (item_id, new_price) => {
// Get Player
let player = player_list[socket.id];
// Get Item
let item = item_list[item_id];
// Get New Price
new_price = parseFloat(new_price);
// Check if New Price is a number
if(!isNaN(new_price)){
// Round New Price into an Integer
new_price = Math.round(new_price);
// Check that New Price >= 0
if(new_price < 0)
new_price = 0;
// Change Item Price for New Price
player.changePrice(item, new_price);
} else {
// Update Player
player.update();
}
});
// Player Bid
socket.on("Player Bid", (wholesale_id, bid) => {
// Get Player
let player = player_list[socket.id];
// Get Wholesale
let wholesale = wholesale_list.find((x) => {return x.id == wholesale_id});
// Get Bid
bid = parseFloat(bid);
// Check if Bid is a number
if(!isNaN(bid)){
// Round Bid into an Integer
bid = Math.round(bid);
// Check that Bid >= 0
if(bid < 0)
bid = 0;
// I Wholesale exist : Add Bid
if(wholesale != undefined)
wholesale.addBid(player, bid);
}
// Update the Player
player.update();
// // Update the LeaderBoard
// updateLeaderBoard();
// Update the Wholesale List
updateWholesales();
});
//=========================== Player Disconnection ==================================
socket.on('disconnect', () => {
// If Player ID exist
if(player_list[socket.id]){
// Remove Player from Player List
delete player_list[socket.id];
// Remove Player from Wholesales Bids
// For each Wholesale
for(let wholesale of wholesale_list){
// For each Bid
for(let i in wholesale.bid_list){
// If Bid is from Player
if(wholesale.bid_list[i].player.getID() == socket.id){
// Remove Bid
wholesale.bid_list.splice(i, 1);
}
}
}
}
// // Update the LeaderBoard
// updateLeaderBoard();
// Update the Wholesale List
updateWholesales();
});
});
//============================================================= Server Interactions ========================================================
// Update LeaderBoard
// function updateLeaderBoard(){
// // Init new LeaderBoard
// let leader_board = {};
// // Add every players (Score = Money, TBD)
// for(let id in player_list){
// leader_board[id] = {
// pseudo: player_list[id].pseudo,
// score: player_list[id].money
// }
// }
// // Broadcast LeaderBoard
// io.emit("Update LeaderBoard", leader_board);
// }
// Update Customers
function updateCustomers(){
// Timers need to be removed to awoid Cheating
// Init an empty array of Sendable Customers
let customer_list_sendable = [];
// For each Customer in Customers List
for(let customer of customer_list){
// Add the Sendable Clone to the new List
customer_list_sendable.push(customer.getSendable());
}
io.emit("Update Customers", customer_list_sendable);
}
// Update Wholesale
function updateWholesales(){
// Player Object (in Bid List) contain a Socket and can't be send as-is to the Clients
// Timers need to be removed to awoid Cheating
// Init an empty array of Sendable Wholesales
let wholesale_list_sendable = [];
// For each Wholesale in Wholesale List
for(let wholesale of wholesale_list){
// Add the Sendable Clone to the new List
wholesale_list_sendable.push(wholesale.getSendable());
}
// Update the Wholesales infos
io.emit("Update Wholesales", wholesale_list_sendable);
}
function updateWholesalesTimers(){
let wholesale_list_sendable = [];
// For each Wholesale in Wholesale List
for(let wholesale of wholesale_list){
// Add the Sendable Clone to the new List
wholesale_list_sendable.push(wholesale.getSendable());
}
io.emit("Update Wholesales Timers", wholesale_list_sendable);
}
//============================================================= Main Loop ========================================================
// Init First Spawn Cooldowns
let customer_spawn_cooldown = Random.normal(CUSTOMER_SPAWN_RATE.MEAN, CUSTOMER_SPAWN_RATE.STD_DEV);
let wholesale_spawn_cooldown = Random.normal(WHOLESALE_SPAWN_RATE.MEAN, WHOLESALE_SPAWN_RATE.STD_DEV);
// Every seconds
setInterval(() => {
//=============================================================
// Customer spawn
// If Customer Spawn Timer has ended
if(customer_spawn_cooldown <= 0){
// Generate random shopping rate
let shopping_timer = Random.normal(CUSTOMER_SHOPING_RATE.MEAN, CUSTOMER_SHOPING_RATE.STD_DEV);
// Generate random despawn rate
let despawn_timer = Random.normal(CUSTOMER_DESPAWN_RATE.MEAN, CUSTOMER_DESPAWN_RATE.STD_DEV);
// Generate new Customer
let new_customer = new Customer(Random.uniformInt(50, 100), shopping_timer, despawn_timer);
// Generate random wishlist
new_customer.generateRandomWishlist(item_list);
// Add new Customer to Customer List
customer_list.push(new_customer);
// Update Customers
updateCustomers();
// Server Log
console.log("Customer - NEW");
// Reset Cooldown
customer_spawn_cooldown = Random.normal(CUSTOMER_SPAWN_RATE.MEAN, CUSTOMER_SPAWN_RATE.STD_DEV);
} else {
// Decrement Spawn Timer
customer_spawn_cooldown--;
}
// Handle Customer Actions (Shopping and Despawn)
// For each Customer
for(let i in customer_list){
// Shopping
// If Shopping Timer has ended
if(customer_list[i].shopping_timer <= 0){
// Customer Shop
customer_list[i].shop(player_list);
// Update Customers
updateCustomers();
// Server Log
console.log("Customer - SHOPPING");
// Reset Shoping Timer
customer_list[i].shopping_timer = Random.normal(CUSTOMER_SHOPING_RATE.MEAN, CUSTOMER_SHOPING_RATE.STD_DEV);
} else {
// Decrement Shoping Timer
customer_list[i].shopping_timer--;
}
// Despawn
// If Despawn Timer has ended OR no more Items in Wishlist
if(customer_list[i].despawn_timer <= 0 || Object.keys(customer_list[i].wishlist).length == 0){
// Remove Customer from List
customer_list.splice(i, 1);
// Update Customers
updateCustomers();
// Server Log
console.log("Customer - LEAVE");
} else {
// Decrement Despawn Timer
customer_list[i].despawn_timer--;
}
}
//=============================================================
// Wholesale Spawn
// If Wholesale Spawn Timer has ended
if(wholesale_spawn_cooldown <= 0){
// Generate Wholesale ID
let id = new Date().getTime();
// Generate deswpan rate
let despawn_timer = Random.normal(WHOLESALE_DESPAWN_RATE.MEAN, WHOLESALE_DESPAWN_RATE.STD_DEV);
// Create new Wholesale and push it to Wholesale List
wholesale_list.push(Wholesale.generateRandomWholesale(id, theme_list, despawn_timer));
// Update the Wholesale List
updateWholesales();
// Server Log
console.log("Wholesale - NEW");
// Reset Cooldown
wholesale_spawn_cooldown = Random.normal(WHOLESALE_SPAWN_RATE.MEAN, WHOLESALE_SPAWN_RATE.STD_DEV);
} else {
// Decrement Spawn Timer
wholesale_spawn_cooldown--;
}
// Despawn ended Wholesales
// For each Wholesale
for(let i in wholesale_list){
// If Despawn Timer has ended
if(wholesale_list[i].despawn_timer <= 0){
// End the Bidding
wholesale_list[i].endBid();
// Remove the Wholesale
wholesale_list.splice(i, 1);
// Update all players
for(let i in player_list){
player_list[i].update();
}
// Update the Wholesale List
updateWholesales();
// Server Log
console.log("Wholesale - END");
} else {
// Decrement the Despawn Timer
wholesale_list[i].despawn_timer--;
}
}
//=============================================================
// // Update Leader Board
// updateLeaderBoard();
// Update timers
updateWholesalesTimers();
}, 1000);