├── README.md ├── package.json ├── app ├── auth.js └── socket.js ├── app.js └── godot └── Socket.gd /README.md: -------------------------------------------------------------------------------- 1 | Godot Engine Node.JS Game Server 2 | ====================================== 3 | Simple game server using NodeJS, using very simple authentication. 4 | 5 | Authors: 6 | - rafinha (salsa2k - freenode) 7 | - thc202 (freenode) 8 | 9 | Running: 10 | node app.js 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Gameserver", 3 | "version": "0.0.1", 4 | "description": "Gameserver", 5 | "main": "app.js", 6 | "dependencies": { 7 | "net": "latest", 8 | "underscore": "latest" 9 | }, 10 | "devDependencies": { 11 | }, 12 | "scripts": { 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "author": "Rafael Noronha", 16 | "license": "BSD-2-Clause" 17 | } 18 | -------------------------------------------------------------------------------- /app/auth.js: -------------------------------------------------------------------------------- 1 | function Auth() { 2 | var $this = this; 3 | 4 | this.handleCommand = function (client, data) { 5 | switch (data.action) { 6 | case 'login': 7 | $this.login(client, data); 8 | break; 9 | } 10 | }; 11 | 12 | this.login = function (client, args) { 13 | if (args.username === 'test' && args.password === 'test') { 14 | SOCKET.send(client, {'action': 'authorized'}); 15 | } else { 16 | SOCKET.disconnect(client); 17 | } 18 | }; 19 | } 20 | 21 | module.exports = new Auth(); 22 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | //---------------------------------------------------------------------------------------------------------------------- 2 | // Config 3 | //---------------------------------------------------------------------------------------------------------------------- 4 | _ = require('underscore'), 5 | NET = require('net'), 6 | SOCKET = require('./app/socket.js'), 7 | AUTH = require('./app/auth.js'), 8 | PORT = 7200, 9 | clients = 0; 10 | 11 | //---------------------------------------------------------------------------------------------------------------------- 12 | // Create server 13 | //---------------------------------------------------------------------------------------------------------------------- 14 | var server = NET.createServer(function (client) { 15 | clients++; 16 | var clientId = clients; 17 | //------------------------------------------------------------------------------------------------------------------ 18 | // Client connected 19 | //------------------------------------------------------------------------------------------------------------------ 20 | SOCKET.debug('Client connected: ' + clientId); 21 | 22 | //------------------------------------------------------------------------------------------------------------------ 23 | // Ask for authentication 24 | //------------------------------------------------------------------------------------------------------------------ 25 | SOCKET.send(client, {'action': 'welcome'}); 26 | 27 | //------------------------------------------------------------------------------------------------------------------ 28 | // On error event 29 | // - Avoid node crash 30 | //------------------------------------------------------------------------------------------------------------------ 31 | client.on('error', function (err) { 32 | SOCKET.debug('Client disconnected: ' + clientId); 33 | }); 34 | 35 | //------------------------------------------------------------------------------------------------------------------ 36 | // On client disconnect 37 | //------------------------------------------------------------------------------------------------------------------ 38 | client.on('end', function () { 39 | SOCKET.debug('Client disconnected: ' + clientId); 40 | }); 41 | 42 | //------------------------------------------------------------------------------------------------------------------ 43 | // On receive data 44 | //------------------------------------------------------------------------------------------------------------------ 45 | client.on('data', function (data) { 46 | SOCKET.data(client, data); 47 | }); 48 | 49 | client.pipe(client); 50 | }); 51 | 52 | //---------------------------------------------------------------------------------------------------------------------- 53 | // Initialize server 54 | //---------------------------------------------------------------------------------------------------------------------- 55 | server.listen(PORT, function () { 56 | SOCKET.debug('Server started on port ' + PORT); 57 | }); 58 | -------------------------------------------------------------------------------- /godot/Socket.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | #################################################################################################### 4 | # Config 5 | #################################################################################################### 6 | const port = 7200 7 | var ip = "127.0.0.1" 8 | var debug = true; 9 | 10 | #################################################################################################### 11 | # Socket 12 | #################################################################################################### 13 | var connection = null 14 | var peerstream = null 15 | var connected = false 16 | var timeout = 5 17 | var timer 18 | 19 | #################################################################################################### 20 | # Debug 21 | #################################################################################################### 22 | function _Debug(msg): 23 | if (debug): 24 | print("[SOCKET]: " + msg) 25 | 26 | #################################################################################################### 27 | # Socket is connected? 28 | #################################################################################################### 29 | func IsConnected(): 30 | return connected 31 | 32 | #################################################################################################### 33 | # Connect 34 | #################################################################################################### 35 | func Connect(args): 36 | # Verify if have arguments 37 | if (args.empty()): 38 | Disconnect() 39 | 40 | # Connect 41 | connection = StreamPeerTCP.new() 42 | connection.connect(ip, port) 43 | 44 | # Timeout 45 | timer = Timer.new() 46 | add_child(timer) 47 | timer.set_wait_time(timeout) 48 | timer.set_one_shot(true) 49 | timer.connect("timeout", self, "_TimeoutDisconnect") 50 | timer.start() 51 | 52 | # Proccess 53 | set_process(true) 54 | 55 | # Dialog 56 | get_node("/root/Dialog").Show({ 57 | "width": 150, 58 | "height": 80, 59 | "label": "Trying to connect ..." 60 | }) 61 | 62 | # OnClient 63 | func OnClient(): 64 | # Connected 65 | if (!connected && connection.get_status() == connection.STATUS_CONNECTED): 66 | connected = true 67 | 68 | peerstream = PacketPeerStream.new() 69 | peerstream.set_stream_peer(connection) 70 | 71 | # Disconnected 72 | if (connection.get_status() == connection.STATUS_NONE or connection.get_status() == connection.STATUS_ERROR): 73 | set_process(false) 74 | Disconnect() 75 | 76 | # Connected and receiving data 77 | if (connected): 78 | if (peerstream.get_available_packet_count() > 0): 79 | var data = peerstream.get_var() 80 | OnData(data) 81 | 82 | func OnData(data): 83 | if (data.action == "welcome"): 84 | Send({ 85 | "type": "AUTH", 86 | "action": "login", 87 | "username": get_node("/root/EditUsername").get_text(), 88 | "password": get_node("/root/EditPassword").get_text() 89 | }) 90 | 91 | # Load lobby 92 | if (data.action == "authorized"): 93 | get_node("/root/SceneManager").JumpTo("lobby") 94 | 95 | #################################################################################################### 96 | # Disconnect 97 | #################################################################################################### 98 | func Disconnect(): 99 | if (connected): 100 | connection.disconnect() 101 | connected = false 102 | 103 | get_node("/root/SceneManager").JumpTo("login") 104 | get_node("/root/Dialog").Hide() 105 | 106 | get_node("/root/EditUsername").set_text("") 107 | get_node("/root/EditPassword").set_text("") 108 | 109 | #################################################################################################### 110 | # Timeout disconnect 111 | #################################################################################################### 112 | func _TimeoutDisconnect(): 113 | timer.stop() 114 | remove_child(timer) 115 | 116 | if (!connected): 117 | Disconnect() 118 | 119 | #################################################################################################### 120 | # Process 121 | #################################################################################################### 122 | func _process(delta): 123 | OnClient() 124 | 125 | #################################################################################################### 126 | # Send data 127 | #################################################################################################### 128 | func Send(data): 129 | if (data != ""): 130 | peerstream.put_var(data) 131 | -------------------------------------------------------------------------------- /app/socket.js: -------------------------------------------------------------------------------- 1 | function Socket() { 2 | "use strict"; 3 | 4 | var $this = this, 5 | _debug = true; 6 | 7 | /** 8 | * Process data 9 | * @param client 10 | * @param buf 11 | */ 12 | this.data = function (client, buf) { 13 | buf = buf.slice(4); 14 | var data = $this.decode(buf).value; 15 | 16 | //-------------------------------------------------------------------------------------------------------------- 17 | // Handle data 18 | // 20 = Dictionary 19 | //-------------------------------------------------------------------------------------------------------------- 20 | if (data !== null) { 21 | $this.handleData(client, data); 22 | } 23 | }; 24 | 25 | /** 26 | * Decode data 27 | * @param buf 28 | * @returns {*} 29 | */ 30 | this.decode = function (buf) { 31 | buf = new Buffer(buf); 32 | 33 | var type = buf.readUInt32LE(0), 34 | data; 35 | 36 | switch (type) { 37 | case 1: 38 | data = $this.decodeBoolean(buf); 39 | break; 40 | case 2: 41 | data = $this.decodeInteger(buf); 42 | break; 43 | case 3: 44 | data = $this.decodeFloat(buf); 45 | break; 46 | case 4: 47 | data = $this.decodeString(buf); 48 | break; 49 | case 20: 50 | data = $this.decodeDictionary(buf); 51 | break; 52 | case 0: 53 | default: 54 | data = { 55 | "value": null, 56 | "length": 4 57 | }; 58 | break; 59 | } 60 | 61 | //-------------------------------------------------------------------------------------------------------------- 62 | // Debug type info 63 | //-------------------------------------------------------------------------------------------------------------- 64 | //$this.debug('Received [' + type + '] (' + typeof(buf) + '): ' + buf); 65 | 66 | return data; 67 | }; 68 | 69 | /** 70 | * Decode boolean 71 | * @param buf 72 | * @returns {boolean} 73 | */ 74 | this.decodeBoolean = function (buf) { 75 | //-------------------------------------------------------------------------------------------------------------- 76 | // Read the integer value and check if it's 1 (true) 77 | //-------------------------------------------------------------------------------------------------------------- 78 | return { 79 | "value": buf.readUInt32LE(4) === 1, 80 | "length": 8 81 | }; 82 | }; 83 | 84 | /** 85 | * Decode integer 86 | * @param buf 87 | * @returns {*} 88 | */ 89 | this.decodeInteger = function (buf) { 90 | //-------------------------------------------------------------------------------------------------------------- 91 | // Read the signed integer 92 | //-------------------------------------------------------------------------------------------------------------- 93 | return { 94 | "value": buf.readInt32LE(4), 95 | "length": 8 96 | }; 97 | }; 98 | 99 | /** 100 | * Decode float 101 | * @param buf 102 | * @returns {*} 103 | */ 104 | this.decodeFloat = function (buf) { 105 | //-------------------------------------------------------------------------------------------------------------- 106 | // Read the IEE 754 32-Bits Float 107 | //-------------------------------------------------------------------------------------------------------------- 108 | return { 109 | // Read the IEE 754 32-Bits Float 110 | "value": buf.readFloatLE(4), 111 | "length": 8 112 | }; 113 | }; 114 | 115 | /** 116 | * Decode string 117 | * @param buf 118 | * @returns {string} 119 | */ 120 | this.decodeString = function (buf) { 121 | //-------------------------------------------------------------------------------------------------------------- 122 | // Read the length of the string, in bytes 123 | //-------------------------------------------------------------------------------------------------------------- 124 | var len = buf.readUInt32LE(4); 125 | var pad = len % 4 === 0 ? 0 : 4 - len % 4; 126 | 127 | //-------------------------------------------------------------------------------------------------------------- 128 | // Read the bytes of the string (utf8) 129 | //-------------------------------------------------------------------------------------------------------------- 130 | return { 131 | "value": buf.toString('utf8', 8, 8 + len), 132 | "length": 8 + len + pad 133 | }; 134 | }; 135 | 136 | /** 137 | * Decode dictionary 138 | * @param buf 139 | * @returns {{value: {}, length: number}} 140 | */ 141 | this.decodeDictionary = function (buf) { 142 | //-------------------------------------------------------------------------------------------------------------- 143 | // Read the number of entries 144 | //-------------------------------------------------------------------------------------------------------------- 145 | var nrEntries = buf.readUInt32LE(4) & 0x7FFFFFFF; 146 | 147 | var dict = {}, 148 | bufPos = 8; 149 | 150 | for (var i = 0; i < nrEntries; i++) { 151 | var decodedKey = $this.decode(buf.slice(bufPos)); 152 | bufPos += decodedKey.length; 153 | 154 | var decodedValue = $this.decode(buf.slice(bufPos)); 155 | bufPos += decodedValue.length; 156 | 157 | dict[decodedKey.value] = decodedValue.value; 158 | } 159 | 160 | return { 161 | "value": dict, 162 | "length": bufPos 163 | }; 164 | }; 165 | 166 | /** 167 | * Send packet 168 | * @param client 169 | * @param value 170 | */ 171 | this.send = function (client, value) { 172 | //-------------------------------------------------------------------------------------------------------------- 173 | // Packet 174 | //-------------------------------------------------------------------------------------------------------------- 175 | var packet = function (data) { 176 | var buf = new Buffer(data.length + 4); 177 | 178 | //---------------------------------------------------------------------------------------------------------- 179 | // Writes length of the "packet" (variable) 180 | //---------------------------------------------------------------------------------------------------------- 181 | buf.writeUInt32LE(data.length, 0); 182 | 183 | //---------------------------------------------------------------------------------------------------------- 184 | // Write "variable" into packet 185 | //---------------------------------------------------------------------------------------------------------- 186 | data.value.copy(buf, 4); 187 | 188 | return buf; 189 | }; 190 | 191 | //-------------------------------------------------------------------------------------------------------------- 192 | // Socket write 193 | //-------------------------------------------------------------------------------------------------------------- 194 | client.write(packet($this.prepareSend(value))); 195 | }; 196 | 197 | /** 198 | * Prepare command message 199 | * @param value 200 | * @returns {*} 201 | */ 202 | this.prepareSend = function (value) { 203 | var data; 204 | 205 | //-------------------------------------------------------------------------------------------------------------- 206 | // Encode process 207 | //-------------------------------------------------------------------------------------------------------------- 208 | switch (typeof value) { 209 | case 'undefined': 210 | break; 211 | 212 | case 'object': 213 | if (_.isObject(value)) { 214 | var encode = []; 215 | 216 | _.each(value, function (v, i) { 217 | encode.push($this.prepareSend(i)); 218 | encode.push($this.prepareSend(v)); 219 | }); 220 | 221 | data = $this.encodeDictionary(encode); 222 | } 223 | break; 224 | 225 | case 'boolean': 226 | data = $this.encodeBoolean(value); 227 | break; 228 | 229 | case 'number': 230 | //------------------------------------------------------------------------------------------------------ 231 | // Integer 232 | //------------------------------------------------------------------------------------------------------ 233 | if (Number(value) === value && value % 1 === 0) { 234 | data = $this.encodeInteger(value); 235 | } 236 | 237 | //------------------------------------------------------------------------------------------------------ 238 | // Float 239 | //------------------------------------------------------------------------------------------------------ 240 | if (value === Number(value) && value % 1 !== 0) { 241 | data = $this.encodeFloat(value); 242 | } 243 | break; 244 | 245 | case 'string': 246 | data = $this.encodeString(value); 247 | break; 248 | } 249 | 250 | return data; 251 | }; 252 | 253 | /** 254 | * Write type 255 | * @param buf 256 | * @param type 257 | */ 258 | this.writeType = function (buf, type) { 259 | buf.writeUInt32LE(type, 0); 260 | }; 261 | 262 | /** 263 | * Encode null 264 | * @returns {{value: Buffer, length: Number}} 265 | */ 266 | this.encodeNull = function () { 267 | var buf = new Buffer(4); 268 | 269 | $this.writeType(buf, 0); 270 | 271 | return { 272 | "value": buf, 273 | "length": buf.length 274 | }; 275 | }; 276 | 277 | /** 278 | * Encode boolean 279 | * @param value 280 | * @returns {{value: Buffer, length: Number}} 281 | */ 282 | this.encodeBoolean = function (value) { 283 | var buf = new Buffer(8); 284 | 285 | $this.writeType(buf, 1); 286 | buf.writeUInt32LE(value ? 1 : 0, 4); 287 | 288 | return { 289 | "value": buf, 290 | "length": buf.length 291 | }; 292 | }; 293 | 294 | /** 295 | * Encode integer 296 | * @param value 297 | * @returns {{value: Buffer, length: Number}} 298 | */ 299 | this.encodeInteger = function (value) { 300 | var buf = new Buffer(8); 301 | 302 | $this.writeType(buf, 2); 303 | buf.writeInt32LE(value, 4); 304 | 305 | return { 306 | "value": buf, 307 | "length": buf.length 308 | }; 309 | }; 310 | 311 | /** 312 | * Encode float 313 | * @param value 314 | * @returns {{value: Buffer, length: Number}} 315 | */ 316 | this.encodeFloat = function (value) { 317 | var buf = new Buffer(8); 318 | 319 | $this.writeType(buf, 3); 320 | buf.writeFloatLE(value, 4); 321 | 322 | return { 323 | "value": buf, 324 | "length": buf.length 325 | }; 326 | }; 327 | 328 | /** 329 | * Encode string 330 | * @param value 331 | * @returns {{value: Buffer, length: Number}} 332 | */ 333 | this.encodeString = function (value) { 334 | var len = Buffer.byteLength(value); 335 | 336 | //-------------------------------------------------------------------------------------------------------------- 337 | // Calculate the padding 338 | //-------------------------------------------------------------------------------------------------------------- 339 | var pad = len % 4 == 0 ? 0 : 4 - len % 4; 340 | 341 | //-------------------------------------------------------------------------------------------------------------- 342 | // See below for more details on why 8 343 | //-------------------------------------------------------------------------------------------------------------- 344 | var buf = new Buffer(8 + len + pad); 345 | 346 | $this.writeType(buf, 4); 347 | 348 | //-------------------------------------------------------------------------------------------------------------- 349 | // Writes the length of the string, in bytes 350 | //-------------------------------------------------------------------------------------------------------------- 351 | buf.writeUInt32LE(len, 4); 352 | 353 | //-------------------------------------------------------------------------------------------------------------- 354 | // Writes the bytes of the string (utf8) 355 | //-------------------------------------------------------------------------------------------------------------- 356 | buf.write(value, 8); 357 | 358 | //-------------------------------------------------------------------------------------------------------------- 359 | // Add some bytes to meet the padding of 4 bytes 360 | //-------------------------------------------------------------------------------------------------------------- 361 | if (pad !== 0) { 362 | var pos = 8 + len; 363 | 364 | for (var i = 0; i < pad; i++) { 365 | buf.write('\0', i + pos); 366 | } 367 | } 368 | 369 | return { 370 | "value": buf, 371 | "length": buf.length 372 | }; 373 | }; 374 | 375 | /** 376 | * Encode dictionary 377 | * @param encoded 378 | * @returns {{value: Buffer, length: Number}} 379 | */ 380 | this.encodeDictionary = function (encoded) { 381 | var len = 8; 382 | 383 | for (var i in encoded) { 384 | len += encoded[i].length; 385 | } 386 | 387 | var buf = new Buffer(len); 388 | 389 | $this.writeType(buf, 20); 390 | buf.writeUInt32LE((encoded.length / 2) & 0x7FFFFFFF, 4); 391 | 392 | var bufPos = 8; 393 | 394 | //-------------------------------------------------------------------------------------------------------------- 395 | // Add key/value to buffer 396 | //-------------------------------------------------------------------------------------------------------------- 397 | for (var i in encoded) { 398 | encoded[i].value.copy(buf, bufPos); 399 | bufPos += encoded[i].length; 400 | } 401 | 402 | return { 403 | "value": buf, 404 | "length": buf.length 405 | }; 406 | }; 407 | 408 | /** 409 | * Debug message 410 | * @param msg 411 | */ 412 | this.debug = function (msg) { 413 | if (_debug) { 414 | console.log('[SOCKET]:', msg); 415 | } 416 | }; 417 | 418 | /** 419 | * Disconnect client 420 | * @param client 421 | */ 422 | this.disconnect = function (client) { 423 | client.destroy(); 424 | }; 425 | 426 | /** 427 | * Handle data commands 428 | * @param client 429 | * @param data 430 | */ 431 | this.handleData = function (client, data) { 432 | switch (data.type) { 433 | case 'AUTH': 434 | AUTH.handleCommand(client, data); 435 | break; 436 | } 437 | }; 438 | } 439 | 440 | module.exports = new Socket(); 441 | --------------------------------------------------------------------------------