-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDotBots-server.js
132 lines (118 loc) · 3.65 KB
/
DotBots-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
"use strict";
String.prototype.format = function () {
var args = arguments;
return this.replace(/\{(\d+)\}/g, function (m, n) { return args[n]; });
};
const byte = function(byte)
{
if(byte instanceof ArrayBuffer)
return (new Uint8Array(byte))[0]
//TODO: cache
return new Uint8Array([byte])
}
const string = function(str)
{
return str.toString();
}
let play = {main : ()=>null };//require("server-play")
const dbglog = console.log;
var assert = require('assert')
dbglog("All systems go...")
/*
Modes/Lifecycle of a player:
Registering (Give your name, synch timestamps)
=> Preparing (Spawn location decided, etc, preparations made by server)
=> Playing (Player is playing right now)
=> Dead (Player has died, and camera is lingering)
=> Preparing
*/
{
const nameCheck = /^[a-z_0-9]{1,10}$/i
const PlayerTable = new Map(); //WebSocket => Player Details
const NamePool = new Set();
//PlayerTable is sent to other modules as context, and is thus const.
const PlayerFSM = function* ()
{
const [player, ws, FSM] = yield; //Bind to these variables, given on connection
const FSMcall = FSM.next.bind(FSM, FSM);
assert((yield) == "FSM:getName")
{ //Get name
dbglog("Getting name for "+ws.upgradeReq.connection.remoteAddress)
let name = yield
while (NamePool.has(name) || !nameCheck.test(name)) //Input name
{
dbglog(ws.upgradeReq.connection.remoteAddress+" gave an invalid name: "+name)
ws.send(byte(1)) //Name invalid
name = yield
}
NamePool.add(name)
player.name = name;
ws.send(byte(0))
}
assert((yield) == "FSM:ts")
{ //Synch timestamps
let min = 9999; //a 10 second max lag is a very reasonable assumption.
let minstart = 0
let minnum = -1
const id = new Buffer([0])
//const pongHandler = (data) => { if(data[0]===id[0]) FSMcall() }
//ws.on("pong", pongHandler)
for(let i=0; i<10; i++)
{
let start = Date.now()
id[0] = Math.floor(Math.random()*256);
ws.send(id)
//We need to have a mechanism so yield's source is trusted.
//This mechanism is making FSMcall created yields return the FSM
//In the future we will have async/await to deal with this stuff
//TODO: Change the generator mechanism to use asynch/await with nodejs7
//assert(byte(yield) === id[0]); //Wait for pong to callback
yield
let lag = Date.now() - start;
dbglog("Detected lag of: ", lag); //DEBUG:
if(lag < min)
{
min = lag;
minstart = start;
minnum = i;
}
}
//ws.removeListener("pong", pongHandler)
dbglog("Minimum latency: ", min)
//min is the time it takes to send a message to the client
ws.send(string(minstart+min/2)); //This is the time at server side when it reached client.
ws.send(byte(minnum))
}
while(true)
{
assert((yield) == "FSM:spawning")
ws.send(Math.random(256)) //TODO: Avoid id collisions
while(true)
{
assert((yield) == "FSM:playing")
}
}
}
let messageHandler = function(message)
{
let details = PlayerTable.get(this)
dbglog(this.upgradeReq.connection.remoteAddress+" sent "+message)
details.onMessage.next(message)
}
let connectPlayer = function(ws)
{
let FSM = PlayerFSM();
let details = {onMessage : FSM};
PlayerTable.set(ws, details)
FSM.next();
FSM.next([details, ws, FSM])
ws.on("message", messageHandler)
//ws.on("close", disconnectHandler)
}
let main = function()
{
play.main({PlayerTable : PlayerTable});
exports.connectPlayer = connectPlayer; //Start accepting connections
}
exports.main = main;
}