From 034cd43363324f899de5d29cc8e575ddd45d3f60 Mon Sep 17 00:00:00 2001 From: Romain Beaumont Date: Wed, 26 Aug 2015 03:27:12 +0200 Subject: [PATCH] complete reorganization : client plugins, server plugins --- .gitignore | 22 -- app.js | 304 +-------------------------- docs/API.md | 0 index.js | 54 +++++ lib/clientPlugins/login.js | 229 ++++++++++++++++++++ lib/clientPlugins/updatePositions.js | 35 +++ lib/serverPlugins/communication.js | 16 ++ lib/serverPlugins/log.js | 24 +++ lib/serverPlugins/players.js | 8 + lib/serverPlugins/world.js | 17 ++ package.json | 3 +- 11 files changed, 388 insertions(+), 324 deletions(-) create mode 100644 docs/API.md create mode 100644 index.js create mode 100644 lib/clientPlugins/login.js create mode 100644 lib/clientPlugins/updatePositions.js create mode 100644 lib/serverPlugins/communication.js create mode 100644 lib/serverPlugins/log.js create mode 100644 lib/serverPlugins/players.js create mode 100644 lib/serverPlugins/world.js diff --git a/.gitignore b/.gitignore index f2fb3f0..3c3629e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1 @@ -# Runtime data -pids -*.pid -*.seed - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage - -# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (http://nodejs.org/api/addons.html) -build/Release - -# Dependency directory -# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules diff --git a/app.js b/app.js index ccfa169..1bfee67 100644 --- a/app.js +++ b/app.js @@ -1,19 +1,6 @@ -var mc = require('minecraft-protocol'); -var states = mc.states; +var mcServer=require("./index"); + var settings = require('./config/settings'); -var World = require('prismarine-chunk'); -var fs = require('fs'); -var timeStarted = Math.floor(new Date() / 1000).toString(); -var playersConnected = []; -var uuidToPlayer = {}; -var vec3 = require("vec3"); - - -function toFixedPosition(p) -{ - return new vec3(Math.floor(p.x*32),Math.floor(p.y*32),Math.floor(p.z*32)) -} - var options = { motd: settings.motd, @@ -22,291 +9,6 @@ var options = { 'online-mode': settings.onlineMode }; -var world = new World(); -for (var x = 0; x < 16;x++) { - for (var z = 0; z < 16; z++) { - world.setBlockType(x, 50, z, 2); - for (var y = 0; y < 256; y++) { - world.setSkyLight(x, y, z, 15); - } - } -} - -var entityMaxId=0; - -var server = mc.createServer(options); - - if(settings.logging == true) { - createLog(); - } - -function transformUuid(s) -{ - return s.split("-").map(function(item) { return parseInt(item, 16); }); -} - -server.on('login', function(client) { - if(uuidToPlayer[client.uuid]) - { - client.end("You are already connected"); - return; - } - entityMaxId++; - client.id=entityMaxId; - playersConnected.push(client); - uuidToPlayer[client.uuid]=client; +mcServer.createMCServer(options); - // send init data so client will start rendering world - client.write('login', { - entityId: client.id, - levelType: 'default', - gameMode: 0, - dimension: 0, - difficulty: 0, - reducedDebugInfo: false, - maxPlayers: server.maxPlayers - }); - - var packetData = { - x: 0, - z: 0, - groundUp: true, - bitMap: 0xffff, - chunkData: world.dump() - }; - - client.write('map_chunk', packetData); - - client.write('position', { - x: 6, - y: 53, - z: 6, - yaw: 0, - pitch: 0, - flags: 0x00 - }); - - console.log("[INFO]: position written, player spawning..."); - log("[INFO]: position written, player spawning..."); - - client.write('update_time', { - age: [0,0], - time: [0,1] - }); - - client.write('game_state_change', { - reason: 3, - gameMode: 0 - }); - - client.on('chat', function(data) { - var message = '<'+client.username+'>' + ' ' + data.message; - playerChat(message, client.username); - console.log("[INFO] " + message); - log("[INFO] " + message); - }); - - client.on('packet', function(packet) { - // we don't really need to see the server pass an object every 10nth of a second so I will just disable this - //console.log("[INFO] " + packet); - //log("[INFO] " + packet); - }); - - function getOtherClients() - { - return playersConnected.filter(function(otherClient){return otherClient!=client}); - } - - var otherClients=getOtherClients(); - - otherClients.forEach(function(otherClient) { - otherClient.write('player_info', { - action: 0, - data: [{ - UUID: transformUuid(client.uuid), - name: client.username, - properties: [], - gamemode: 0, - ping: 1, - hasDisplayName: true, - displayName: client.username - }] - }); - }); - - client.write('player_info', { - action: 0, - data: playersConnected - .map(function(otherClient) { - return { - UUID: transformUuid(otherClient.uuid), - name: otherClient.username, - properties: [], - gamemode: 0, - ping: 1, - hasDisplayName: true, - displayName: otherClient.username - }; - }) - }); - - - otherClients.forEach(function(otherClient) { - var pos=uuidToPlayer[otherClient.uuid].position; - client.write('named_entity_spawn', { - entityId: otherClient.id, - playerUUID: transformUuid(otherClient.uuid), - x: pos ? pos.x : 6*32, - y: pos ? pos.y : 53*32, - z: pos ? pos.z : 6*32, - yaw: 0, - pitch: 0, - currentItem: 0, - metadata: [] - }); - otherClient.write('named_entity_spawn', { - entityId: client.id, - playerUUID: transformUuid(client.uuid), - x: 6*32, - y: 53*32, - z: 6*32, - yaw: 0, - pitch: 0, - currentItem: 0, - metadata: [] - }); - }); - - broadcast(client.username + ' joined the game.', "yellow"); - var addr = client.socket.remoteAddress + ':' + client.socket.remotePort; - console.log("[INFO]: " + client.username + ' connected', '(' + addr + ')'); - log("[INFO]: " + client.username + ' connected', '(' + addr + ')'); - - client.on('end', function() { - broadcast(client.username + ' joined the game.', "yellow"); - console.log("[INFO]: " + client.username+' disconnected', '('+addr+')'); - log("[INFO]: " + client.username+' disconnected', '('+addr+')'); - }); - - client.on('position', function(packet) { - var position = new vec3(packet.x,packet.y,packet.z); - var onGround=packet.onGround; - sendRelativePositionChange(client,toFixedPosition(position),onGround); - }); - - - function writeOthers(packetName,packetFields) - { - getOtherClients().forEach(function (otherClient) { - otherClient.write(packetName, packetFields); - }); - } - - - function sendRelativePositionChange(client,newPosition,onGround) { - if (uuidToPlayer[client.uuid].position) { - var diff = newPosition.minus(uuidToPlayer[client.uuid].position); - if (diff.distanceTo(new vec3(0, 0, 0)) != 0) { - - writeOthers('rel_entity_move', { - entityId: client.id, - dX: diff.x, - dY: diff.y, - dZ: diff.z, - onGround: onGround - }); - } - } - uuidToPlayer[client.uuid].position = newPosition; - uuidToPlayer[client.uuid].onGround=onGround; - } - - client.on('error', function(error) { - console.log('[ERR] ' + error.stack); - log('[ERR]: Client: ' + error.stack); - }); - -}); - -server.on('error', function(error) { - console.log('[ERR] ', error.stack); - log('[ERR]: Server:', error.stack); -}); - -server.on('listening', function() { - console.log('[INFO]: Server listening on port', server.socketServer.address().port); - log('[INFO]: Server listening on port', server.socketServer.address().port); -}); - -// function broadcast(message, username) { -// var client, translate; -// translate = username ? 'chat.type.announcement' : 'chat.type.text'; -// username = username || 'Server'; -// for (var clientId in server.clients) { -// if (!server.clients.hasOwnProperty(clientId)) continue; - -// client = server.clients[clientId]; -// var msg = { -// translate: translate, -// "with": [ -// username -// ] -// }; -// if (typeof message === "string") { -// msg["with"].push(message); -// } else { -// Object.keys(message).forEach(function(key) { -// if (key === "text") { -// msg["with"].push(message[key]); -// } else { -// msg[key] = message[key]; -// } -// }); -// } - -// client.write('success', { message: JSON.stringify(msg) }); -// } - -function createLog() { - fs.writeFile("logs/" + timeStarted + ".log", "[INFO]: Started logging...\n", function(err, data) { - if (err) return console.log(err); - }); -} - -function log(message) { - if(settings.logging == true) { - fs.appendFile("logs/" + timeStarted + ".log", message + "\n", function (err) { }); - } -} - -function playerChat(message, exclude, username) { - var client; - //translate = username ? 'chat.type.text' : 'chat.type.text'; - username = username || ''; - for(var clientId in server.clients) { - if(!server.clients.hasOwnProperty(clientId)) continue; - - client = server.clients[clientId]; - if(client !== exclude) { - var msg = { - "text": username + message - }; - client.write('chat', { message: JSON.stringify(msg), position: 0 }); - } - } -} - -function broadcast(message, color) { - var client; - for(var clientId in server.clients) { - if(!server.clients.hasOwnProperty(clientId)) continue; - - client = server.clients[clientId]; - var msg = { - "text": message, - "color": color - }; - client.write('chat', { message: JSON.stringify(msg), position: 0 }); - } -} diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..e69de29 diff --git a/index.js b/index.js new file mode 100644 index 0000000..ed2676f --- /dev/null +++ b/index.js @@ -0,0 +1,54 @@ +var mc = require('minecraft-protocol'); +var EventEmitter = require('events').EventEmitter; +var util = require('util'); +var path = require('path'); +var requireIndex = require('requireindex'); +var serverPlugins = requireIndex(path.join(__dirname, 'lib', 'serverPlugins')); +var clientPlugins = requireIndex(path.join(__dirname, 'lib', 'clientPlugins')); + +module.exports = { + createMCServer:createMCServer +}; + +function createMCServer(options) { + options = options || {}; + var mcServer = new MCServer(); + mcServer.connect(options); + return mcServer; +} + +function MCServer() { + EventEmitter.call(this); + this._server = null; +} +util.inherits(MCServer, EventEmitter); + +MCServer.prototype.connect = function(options) { + var self = this; + self._server = mc.createServer(options); + + for(var pluginName in serverPlugins) { + serverPlugins[pluginName](self, options); + } + + if(options.logging == true) { + self.createLog(); + } + + self._server.on('error', function(error) { + console.log('[ERR] ', error.stack); + self.log('[ERR]: Server:', error.stack); + }); + + self._server.on('listening', function() { + console.log('[INFO]: Server listening on port', self._server.socketServer.address().port); + self.log('[INFO]: Server listening on port', self._server.socketServer.address().port); + }); + + self._server.on('login', function (client) { + for(var pluginName in clientPlugins) { + clientPlugins[pluginName](self, client, options); + } + self.login(client); + }); +}; diff --git a/lib/clientPlugins/login.js b/lib/clientPlugins/login.js new file mode 100644 index 0000000..31b5ae6 --- /dev/null +++ b/lib/clientPlugins/login.js @@ -0,0 +1,229 @@ +var log = require("../serverPlugins/log").log; + +module.exports=inject; + +function transformUuid(s) +{ + return s.split("-").map(function(item) { return parseInt(item, 16); }); +} + + +function inject(serv,client) +{ + serv.login=login; + + function addPlayer() + { + serv.entityMaxId++; + client.id = serv.entityMaxId; + serv.playersConnected.push(client); + console.log(client.uuid); + serv.uuidToPlayer[client.uuid] = client; + } + + function sendLogin() + { + // send init data so client will start rendering world + client.write('login', { + entityId: client.id, + levelType: 'default', + gameMode: 0, + dimension: 0, + difficulty: 0, + reducedDebugInfo: false, + maxPlayers: serv._server.maxPlayers + }); + } + function sendMap() + { + client.write('map_chunk', { + x: 0, + z: 0, + groundUp: true, + bitMap: 0xffff, + chunkData: serv.world.dump() + }); + } + + function sendInitialPosition() + { + client.write('position', { + x: 6, + y: 53, + z: 6, + yaw: 0, + pitch: 0, + flags: 0x00 + }); + } + + function updateTime() + { + client.write('update_time', { + age: [0, 0], + time: [0, 1] + }); + } + + function updateGameState() + { + client.write('game_state_change', { + reason: 3, + gameMode: 0 + }); + } + + function announceLogin() + { + client.on('chat', function (data) { + var message = '<' + client.username + '>' + ' ' + data.message; + playerChat(message, client.username); + console.log("[INFO] " + message); + serv.log("[INFO] " + message); + }); + } + + function fillTabList() + { + serv.otherClients(client).forEach(function (otherClient) { + otherClient.write('player_info', { + action: 0, + data: [{ + UUID: transformUuid(client.uuid), + name: client.username, + properties: [], + gamemode: 0, + ping: 1, + hasDisplayName: true, + displayName: client.username + }] + }); + }); + + client.write('player_info', { + action: 0, + data: serv.playersConnected + .map(function (otherClient) { + return { + UUID: transformUuid(otherClient.uuid), + name: otherClient.username, + properties: [], + gamemode: 0, + ping: 1, + hasDisplayName: true, + displayName: otherClient.username + }; + }) + }); + } + + function spawn() + { + serv.otherClients(client).forEach(function (otherClient) { + var pos = serv.uuidToPlayer[otherClient.uuid].position; + client.write('named_entity_spawn', { + entityId: otherClient.id, + playerUUID: transformUuid(otherClient.uuid), + x: pos ? pos.x : 6 * 32, + y: pos ? pos.y : 53 * 32, + z: pos ? pos.z : 6 * 32, + yaw: 0, + pitch: 0, + currentItem: 0, + metadata: [] + }); + otherClient.write('named_entity_spawn', { + entityId: client.id, + playerUUID: transformUuid(client.uuid), + x: 6 * 32, + y: 53 * 32, + z: 6 * 32, + yaw: 0, + pitch: 0, + currentItem: 0, + metadata: [] + }); + }); + } + + function announceJoin() + { + broadcast(client.username + ' joined the game.', "yellow"); + var addr = client.socket.remoteAddress + ':' + client.socket.remotePort; + console.log("[INFO]: " + client.username + ' connected', '(' + addr + ')'); + serv.log("[INFO]: " + client.username + ' connected', '(' + addr + ')'); + } + + + function playerChat(message, exclude, username) { + var client; + //translate = username ? 'chat.type.text' : 'chat.type.text'; + username = username || ''; + for(var clientId in server.clients) { + if(!serv._server.clients.hasOwnProperty(clientId)) continue; + + client = serv._server.clients[clientId]; + if(client !== exclude) { + var msg = { + "text": username + message + }; + client.write('chat', { message: JSON.stringify(msg), position: 0 }); + } + } + } + + + + + function broadcast(message, color) { + var client; + for(var clientId in serv._server.clients) { + if(!serv._server.clients.hasOwnProperty(clientId)) continue; + + client = serv._server.clients[clientId]; + var msg = { + "text": message, + "color": color + }; + client.write('chat', { message: JSON.stringify(msg), position: 0 }); + } + } + + + function login() + { + if (serv.uuidToPlayer[client.uuid]) { + client.end("You are already connected"); + return; + } + addPlayer(); + sendLogin(); + sendMap(); + sendInitialPosition(); + + console.log("[INFO]: position written, player spawning..."); + serv.log("[INFO]: position written, player spawning..."); + + updateTime(); + updateGameState(); + announceLogin(); + fillTabList(); + spawn(); + + announceJoin(); + + + var addr = client.socket.remoteAddress + ':' + client.socket.remotePort; + client.on('end', function () { + broadcast(client.username + ' joined the game.', "yellow"); + console.log("[INFO]: " + client.username + ' disconnected', '(' + addr + ')'); + serv.log("[INFO]: " + client.username + ' disconnected', '(' + addr + ')'); + }); + + + client.on('error', function (error) { + console.log('[ERR] ' + error.stack); + serv.log('[ERR]: Client: ' + error.stack); + }); + } +} \ No newline at end of file diff --git a/lib/clientPlugins/updatePositions.js b/lib/clientPlugins/updatePositions.js new file mode 100644 index 0000000..6292332 --- /dev/null +++ b/lib/clientPlugins/updatePositions.js @@ -0,0 +1,35 @@ +var vec3 = require("vec3"); + +module.exports=inject; + +function toFixedPosition(p) +{ + return new vec3(Math.floor(p.x*32),Math.floor(p.y*32),Math.floor(p.z*32)) +} + +function inject(serv,client) +{ + client.on('position', function (packet) { + var position = new vec3(packet.x, packet.y, packet.z); + var onGround = packet.onGround; + sendRelativePositionChange(client, toFixedPosition(position), onGround); + }); + + function sendRelativePositionChange(client, newPosition, onGround) { + if (serv.uuidToPlayer[client.uuid].position) { + var diff = newPosition.minus(serv.uuidToPlayer[client.uuid].position); + if (diff.distanceTo(new vec3(0, 0, 0)) != 0) { + + serv.writeOthers(client,'rel_entity_move', { + entityId: client.id, + dX: diff.x, + dY: diff.y, + dZ: diff.z, + onGround: onGround + }); + } + } + serv.uuidToPlayer[client.uuid].position = newPosition; + serv.uuidToPlayer[client.uuid].onGround = onGround; + } +} \ No newline at end of file diff --git a/lib/serverPlugins/communication.js b/lib/serverPlugins/communication.js new file mode 100644 index 0000000..67b249c --- /dev/null +++ b/lib/serverPlugins/communication.js @@ -0,0 +1,16 @@ +module.exports=inject; + +function inject(serv) +{ + serv.writeOthers=function(client,packetName, packetFields) { + serv.otherClients(client).forEach(function (otherClient) { + otherClient.write(packetName, packetFields); + }); + }; + + serv.otherClients = function(client) { + return serv.playersConnected.filter(function (otherClient) { + return otherClient != client + }); + }; +} \ No newline at end of file diff --git a/lib/serverPlugins/log.js b/lib/serverPlugins/log.js new file mode 100644 index 0000000..9da4bcf --- /dev/null +++ b/lib/serverPlugins/log.js @@ -0,0 +1,24 @@ +module.exports=inject; + +var fs = require('fs'); +var timeStarted = Math.floor(new Date() / 1000).toString(); + +function inject(serv,settings) { + + serv.log=log; + serv.createLog=createLog; + + function log(message) { + if (settings.logging == true) { + fs.appendFile("logs/" + timeStarted + ".log", message + "\n", function (err) { + }); + } + } + + + function createLog() { + fs.writeFile("logs/" + timeStarted + ".log", "[INFO]: Started logging...\n", function (err, data) { + if (err) return console.log(err); + }); + } +} diff --git a/lib/serverPlugins/players.js b/lib/serverPlugins/players.js new file mode 100644 index 0000000..9b58872 --- /dev/null +++ b/lib/serverPlugins/players.js @@ -0,0 +1,8 @@ +module.exports=inject; + +function inject(serv) +{ + serv.entityMaxId=0; + serv.playersConnected=[]; + serv.uuidToPlayer={}; +} \ No newline at end of file diff --git a/lib/serverPlugins/world.js b/lib/serverPlugins/world.js new file mode 100644 index 0000000..0762581 --- /dev/null +++ b/lib/serverPlugins/world.js @@ -0,0 +1,17 @@ +var World = require('prismarine-chunk'); + +module.exports=inject; + +function inject(serv) { + + serv.world=new World(); + + for (var x = 0; x < 16;x++) { + for (var z = 0; z < 16; z++) { + serv.world.setBlockType(x, 50, z, 2); + for (var y = 0; y < 256; y++) { + serv.world.setSkyLight(x, y, z, 15); + } + } + } +} \ No newline at end of file diff --git a/package.json b/package.json index 456cc39..ca755fb 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "mocha": "~2.2.5", "chai": "~3.2.0", "prismarine-chunk": "~0.1.0", - "vec3":"0.1.3" + "vec3":"0.1.3", + "requireindex": "~1.0.0" }, "repository": { "type": "git",