From ca18d76193654063a34b3958800fee4050e17475 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 16:26:53 +0200 Subject: [PATCH 001/417] Add files via upload --- src/GameServer.js | 2060 +++++++++++++++---------------- src/PacketHandler.js | 299 +++-- src/PlayerTracker.js | 939 +++++++------- src/entity/Cell.js | 43 +- src/entity/PlayerCell.js | 3 +- src/entity/Virus.js | 8 +- src/gamemodes/Experimental.js | 28 +- src/gameserver.ini | 4 - src/modules/CommandList.js | 26 +- src/packet/AddNode.js | 29 +- src/packet/ClearNodes.js | 23 +- src/packet/DrawLine.js | 31 +- src/packet/SetBorder.js | 55 +- src/packet/UpdateLeaderboard.js | 188 ++- src/packet/UpdateNodes.js | 365 ++++-- src/packet/UpdatePosition.js | 40 +- 16 files changed, 2125 insertions(+), 2016 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index b8782e252..d016621c9 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1,1070 +1,990 @@ -// Library imports -var WebSocket = require('ws'); -var http = require('http'); -var fs = require("fs"); -var ini = require('./modules/ini.js'); - -// Project imports -var Packet = require('./packet'); -var PlayerTracker = require('./PlayerTracker'); -var PacketHandler = require('./PacketHandler'); -var Entity = require('./entity'); -var Gamemode = require('./gamemodes'); -var BotLoader = require('./ai/BotLoader'); -var Logger = require('./modules/log'); - -// GameServer implementation -function GameServer() { - // Startup - this.run = true; - this.lastNodeId = 1; - this.lastPlayerId = 1; - this.clients = []; - this.largestClient; // Required for spectators - - this.nodes = []; - this.nonPlayerNodes = []; // All nodes except player nodes - this.nodesVirus = []; // Virus nodes - this.nodesEjected = []; // Ejected mass nodes - this.nodesPlayer = []; // Nodes controlled by players - - this.currentFood = 0; - this.leaderboard = []; - - this.bots = new BotLoader(this); - this.log = new Logger(); - this.commands; // Command handler - - // Main loop tick - this.time = +new Date; - this.startTime = this.time; - this.tick = 0; // 1 ms, 25 ms - collision update, next time all update - this.fullTick = 0; // 2 = all update - this.tickMain = 0; // 50 ms ticks, 20 of these = 1 leaderboard update - this.tickSpawn = 0; // Used with spawning food - - // Config - this.config = { // Border - Right: X increases, Down: Y increases (as of 2015-05-20) - serverMaxConnections: 64, // Maximum amount of connections to the server. - serverPort: 443, // Server port - serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams - serverBots: 0, // Amount of player bots to spawn - serverViewBaseX: 1024, // Base view distance of players. Warning: high values may cause lag - serverViewBaseY: 592, - serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. - serverStatsUpdate: 60, // Amount of seconds per update for the server stats - serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections - serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. - serverScrambleMinimaps: 1, // Toggles scrambling of borders to render maps unusable. 0 = No scrambling, 1 = scrambling. Default is 1. - serverTeamingAllowed: 1, // Toggles anti-teaming. 0 = Anti-team enabled, 1 = Anti-team disabled - serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. - borderLeft: 0, // Left border of map (Vanilla value: 0) - borderRight: 6000, // Right border of map (Vanilla value: 14142.135623730952) - borderTop: 0, // Top border of map (Vanilla value: 0) - borderBottom: 6000, // Bottom border of map (Vanilla value: 14142.135623730952) - spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) - foodSpawnAmount: 10, // The amount of food to spawn per interval - foodStartAmount: 100, // The starting amount of food in the map - foodMaxAmount: 500, // Maximum food cells on the map - foodMass: 1, // Starting food size (In mass) - foodMassGrow: 1, // Enable food mass grow ? - foodMassGrowPossiblity: 50, // Chance for a food to has the ability to be self growing - foodMassLimit: 5, // Maximum mass for a food can grow - foodMassTimeout: 120, // The amount of interval for a food to grow its mass (in seconds) - virusMinAmount: 10, // Minimum amount of viruses on the map. - virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. - virusStartMass: 100, // Starting virus size (In mass) - virusFeedAmount: 7, // Amount of times you need to feed a virus to shoot it - ejectMass: 13, // Mass of ejected cells - ejectMassCooldown: 100, // Time until a player can eject mass again - ejectMassLoss: 15, // Mass lost when ejecting cells - ejectSpeed: 100, // Base speed of ejected cells - ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass - playerStartMass: 10, // Starting mass of the player cell. - playerBotGrowEnabled: 1, // If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass - playerMaxMass: 22500, // Maximum mass a player can have - playerMinMassEject: 32, // Mass required to eject a cell - playerMinMassSplit: 36, // Mass required to split - playerMaxCells: 16, // Max cells the player is allowed to have - playerRecombineTime: 30, // Base amount of seconds before a cell is allowed to recombine - playerMassAbsorbed: 1.0, // Fraction of player cell's mass gained upon eating - playerMassDecayRate: .002, // Amount of mass lost per second - playerMinMassDecay: 9, // Minimum mass for decay to occur - playerMaxNickLength: 15, // Maximum nick length - playerSpeed: 30, // Player base speed - playerDisconnectTime: 60, // The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) - tourneyMaxPlayers: 12, // Maximum amount of participants for tournament style game modes - tourneyPrepTime: 10, // Amount of ticks to wait after all players are ready (1 tick = 1000 ms) - tourneyEndTime: 30, // Amount of ticks to wait after a player wins (1 tick = 1000 ms) - tourneyTimeLimit: 20, // Time limit of the game, in minutes. - tourneyAutoFill: 0, // If set to a value higher than 0, the tournament match will automatically fill up with bots after this amount of seconds - tourneyAutoFillPlayers: 1, // The timer for filling the server with bots will not count down unless there is this amount of real players - }; - // Parse config - this.loadConfig(); - - // Gamemodes - this.gameMode = Gamemode.get(this.config.serverGamemode); -} - -module.exports = GameServer; - -GameServer.prototype.start = function() { - // Logging - this.log.setup(this); - - // Gamemode configurations - this.gameMode.onServerInit(this); - - // Start the server - this.socketServer = new WebSocket.Server({ - port: this.config.serverPort, - perMessageDeflate: false - }, function() { - // Spawn starting food - this.startingFood(); - - // Start Main Loop - setInterval(this.mainLoop.bind(this), 1); - - // Done - console.log("[Game] Listening on port " + this.config.serverPort); - console.log("[Game] Current game mode is " + this.gameMode.name); - - // Player bots (Experimental) - if (this.config.serverBots > 0) { - for (var i = 0; i < this.config.serverBots; i++) { - this.bots.addBot(); - } - console.log("[Game] Loaded " + this.config.serverBots + " player bots"); - } - - }.bind(this)); - - this.socketServer.on('connection', connectionEstablished.bind(this)); - - // Properly handle errors because some people are too lazy to read the readme - this.socketServer.on('error', function err(e) { - switch (e.code) { - case "EADDRINUSE": - console.log("[Error] Server could not bind to port! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); - break; - case "EACCES": - console.log("[Error] Please make sure you are running Ogar with root privileges."); - break; - default: - console.log("[Error] Unhandled error code: " + e.code); - break; - } - process.exit(1); // Exits the program - }); - - function connectionEstablished(ws) { - if (this.clients.length >= this.config.serverMaxConnections) { // Server full - ws.close(); - return; - } - - // ----- Client authenticity check code ----- - // !!!!! WARNING !!!!! - // THE BELOW SECTION OF CODE CHECKS TO ENSURE THAT CONNECTIONS ARE COMING - // FROM THE OFFICIAL AGAR.IO CLIENT. IF YOU REMOVE OR MODIFY THE BELOW - // SECTION OF CODE TO ALLOW CONNECTIONS FROM A CLIENT ON A DIFFERENT DOMAIN, - // YOU MAY BE COMMITTING COPYRIGHT INFRINGEMENT AND LEGAL ACTION MAY BE TAKEN - // AGAINST YOU. THIS SECTION OF CODE WAS ADDED ON JULY 9, 2015 AT THE REQUEST - // OF THE AGAR.IO DEVELOPERS. - var origin = ws.upgradeReq.headers.origin; - if (origin != 'http://agar.io' && - origin != 'https://agar.io' && - origin != 'http://localhost' && - origin != 'https://localhost' && - origin != 'http://127.0.0.1' && - origin != 'https://127.0.0.1') { - - ws.close(); - return; - } - // -----/Client authenticity check code ----- - - function close(error) { - // Log disconnections - this.server.log.onDisconnect(this.socket.remoteAddress); - - var client = this.socket.playerTracker; - var len = this.socket.playerTracker.cells.length; - for (var i = 0; i < len; i++) { - var cell = this.socket.playerTracker.cells[i]; - - if (!cell) { - continue; - } - - cell.calcMove = function() { - return; - }; // Clear function so that the cell cant move - //this.server.removeNode(cell); - } - - client.disconnect = this.server.config.playerDisconnectTime * 20; - this.socket.sendPacket = function() { - return; - }; // Clear function so no packets are sent - } - - ws.remoteAddress = ws._socket.remoteAddress; - ws.remotePort = ws._socket.remotePort; - this.log.onConnect(ws.remoteAddress); // Log connections - - ws.playerTracker = new PlayerTracker(this, ws); - ws.packetHandler = new PacketHandler(this, ws); - ws.on('message', ws.packetHandler.handleMessage.bind(ws.packetHandler)); - - var bindObject = { - server: this, - socket: ws - }; - ws.on('error', close.bind(bindObject)); - ws.on('close', close.bind(bindObject)); - this.clients.push(ws); - } - - this.startStatsServer(this.config.serverStatsPort); -}; - -GameServer.prototype.getMode = function() { - return this.gameMode; -}; - -GameServer.prototype.getNextNodeId = function() { - // Resets integer - if (this.lastNodeId > 2147483647) { - this.lastNodeId = 1; - } - return this.lastNodeId++; -}; - -GameServer.prototype.getNewPlayerID = function() { - // Resets integer - if (this.lastPlayerId > 2147483647) { - this.lastPlayerId = 1; - } - return this.lastPlayerId++; -}; - -GameServer.prototype.getRandomPosition = function() { - var xSum = this.config.borderRight + this.config.borderLeft; - var ySum = this.config.borderBottom + this.config.borderTop; - return { - x: Math.floor(Math.random() * xSum - this.config.borderLeft), - y: Math.floor(Math.random() * ySum - this.config.borderTop) - }; -}; - -GameServer.prototype.getRandomSpawn = function(mass) { - // Random and secure spawns for players and viruses - var pos = this.getRandomPosition(); - var unsafe = this.willCollide(mass, pos, mass == this.config.virusStartMass); - var attempt = 1; - - // Prevent stack overflow by counting attempts - while (true) { - if (!unsafe || attempt >= 15) break; - pos = this.getRandomPosition(); - unsafe = this.willCollide(mass, pos, mass == this.config.virusStartMass); - attempt++; - } - - // If it reached attempt 15, warn the user - if (attempt >= 14) { - console.log("[Server] Entity was force spawned near viruses/playercells after 15 attempts."); - console.log("[Server] If this message keeps appearing, check your config, especially start masses for players and viruses."); - } - - return pos; -}; - -GameServer.prototype.getRandomColor = function() { - var colorRGB = [0xFF, 0x07, (Math.random() * 256) >> 0]; - colorRGB.sort(function() { - return 0.5 - Math.random(); - }); - return { - r: colorRGB[0], - g: colorRGB[1], - b: colorRGB[2] - }; -}; - -GameServer.prototype.addNode = function(node) { - this.nodes.push(node); - if (node.cellType != 0) this.nonPlayerNodes.push(node); - - // Adds to the owning player's screen excluding ejected cells - if (node.owner && node.cellType != 3) { - node.setColor(node.owner.color); - node.owner.cells.push(node); - node.owner.socket.sendPacket(new Packet.AddNode(node)); - } - - // Special on-add actions - node.onAdd(this); - - // Add to visible nodes - for (var i = 0; i < this.clients.length; i++) { - var client = this.clients[i].playerTracker; - if (!client) { - continue; - } - - // client.nodeAdditionQueue is only used by human players, not bots - // for bots it just gets collected forever, using ever-increasing amounts of memory - if ('_socket' in client.socket && node.visibleCheck(client.viewBox, client.centerPos, client.cells)) { - client.nodeAdditionQueue.push(node); - } - } -}; - -GameServer.prototype.removeNode = function(node) { - // Remove from main nodes list - var index = this.nodes.indexOf(node); - if (index != -1) { - this.nodes.splice(index, 1); - } - - if (node.cellType != 0) { - // Remove from non-player node list - index = this.nonPlayerNodes.indexOf(node); - if (index != -1) { - this.nonPlayerNodes.splice(index, 1); - } - } - - // Special on-remove actions - node.onRemove(this); - - // Animation when eating - for (var i = 0; i < this.clients.length; i++) { - var client = this.clients[i].playerTracker; - if (!client) continue; - - // Remove from client - client.nodeDestroyQueue.push(node); - } -}; - -GameServer.prototype.moveTick = function() { - // Move cells - this.updateMoveEngine(); -}; - -GameServer.prototype.spawnTick = function() { - // Spawn food - this.tickSpawn++; - if (this.tickSpawn >= this.config.spawnInterval) { - this.updateFood(); // Spawn food - this.virusCheck(); // Spawn viruses - - this.tickSpawn = 0; // Reset - } -}; - -GameServer.prototype.gamemodeTick = function() { - // Gamemode tick - this.gameMode.onTick(this); -}; - -GameServer.prototype.cellUpdateTick = function() { - // Update cells - this.updateCells(); -}; - -GameServer.prototype.mainLoop = function() { - // Timer - var local = new Date(); - this.tick += (local - this.time); - this.time = local; - - if (!this.run) return; - - // The node & client updating mechanism is perfomance overhauled - // Nodes & clients will update periodically and never all on 25ms/50ms - // PlayerTracker and all Cell types have own internal update timer which is measured - setTimeout(this.updateClients.bind(this), 0); - setTimeout(this.moveTick.bind(this), 0); - - - if (this.tick >= 25) { - this.fullTick++; - - if (this.fullTick >= 2) { - // Loop main functions - setTimeout(this.spawnTick.bind(this), 0); - setTimeout(this.gamemodeTick.bind(this), 0); - setTimeout(this.cellUpdateTick.bind(this), 0); - - // Update cells/leaderboard loop - this.tickMain++; - if (this.tickMain >= 4) { // 250 milliseconds - // Update leaderboard with the gamemode's method - this.leaderboard = []; - this.gameMode.updateLB(this); - - if (!this.gameMode.specByLeaderboard) { - // Get client with largest score if gamemode doesn't have a leaderboard - var clients = this.clients.valueOf(); - - // Use sort function - clients.sort(function(a, b) { - return b.playerTracker.getScore(true) - a.playerTracker.getScore(true); - }); - this.largestClient = clients[0].playerTracker; - } else this.largestClient = this.gameMode.rankOne; - - this.tickMain = 0; // Reset - } - this.fullTick = 0; // Reset - } - - // Debug - //console.log(this.tick - 25); - - // Reset - this.tick = 0; - } -}; - -GameServer.prototype.updateClients = function() { - // The node & client updating mechanism is perfomance overhauled - // Nodes & clients will update periodically and never all on 25ms/50ms - // PlayerTracker and all Cell types have own internal update timer which is measured - - var updatedClients = []; - - var len = this.clients.length; - for (var i = 0; i < len; i++) { - var client = this.clients[i].playerTracker; - if (!client) continue; - - client.ticksLeft--; - if (client.ticksLeft > 0) continue; - updatedClients.push(client); - - client.ticksLeft = 40; // Reset timer - - client.update(); - client.antiTeamTick(); - } - - // Very experimental and currently will freeze the server. - // If there are too many updated clients at once, update them a few ticks later - /*var maxOnTick = Math.ceil(this.clients.length / 50); - if (updatedClients.length > maxOnTick) { - for (var i = 0; i < updatedClients.length - maxOnTick; i) { - updatedClients[i].ticksLeft += i + 1; - } - }*/ -}; - -GameServer.prototype.startingFood = function() { - // Spawns the starting amount of food cells - for (var i = 0; i < this.config.foodStartAmount; i++) { - this.spawnFood(); - } -}; - -GameServer.prototype.updateFood = function() { - var toSpawn = Math.min(this.config.foodSpawnAmount, (this.config.foodMaxAmount - this.currentFood)); - for (var i = 0; i < toSpawn; i++) { - this.spawnFood(); - } -}; - -GameServer.prototype.spawnFood = function() { - var f = new Entity.Food(this.getNextNodeId(), null, this.getRandomPosition(), this.config.foodMass, this); - f.setColor(this.getRandomColor()); - - this.addNode(f); - this.currentFood++; -}; - -GameServer.prototype.spawnPlayer = function(player, pos, mass) { - if (mass == null) { // Get starting mass - mass = this.config.playerStartMass; - } - if (pos == null) { // Get random pos - pos = this.getRandomSpawn(mass); - } - - // Spawn player and add to world - var cell = new Entity.PlayerCell(this.getNextNodeId(), player, pos, mass, this); - this.addNode(cell); - - // Set initial mouse coords - player.mouse = { - x: pos.x, - y: pos.y - }; -}; - -GameServer.prototype.virusCheck = function() { - // Checks if there are enough viruses on the map - if (this.nodesVirus.length < this.config.virusMinAmount) { - // Spawns a virus - var pos = this.getRandomSpawn(this.config.virusStartMass); - - var v = new Entity.Virus(this.getNextNodeId(), null, pos, this.config.virusStartMass, this); - this.addNode(v); - } -}; - -GameServer.prototype.willCollide = function(mass, pos, isVirus) { - // Look if there will be any collision with the current nodes - var size = Math.sqrt(mass * 100) >> 0; - - for (var i = 0; i < this.nodesPlayer.length; i++) { - var check = this.nodesPlayer[i]; - if (!check) continue; - - // Eating range - var xs = check.position.x - pos.x, - ys = check.position.y - pos.y, - sqDist = xs * xs + ys * ys, - dist = Math.sqrt(sqDist); - - if (check.getSize() > size) { // Check only if the player cell is larger than imaginary cell - if (dist + size <= check.getSize()) return true; // Collided - } - } - - if (isVirus) return false; // Don't check for viruses if the new cell will be virus - - for (var i = 0; i < this.nodesVirus.length; i++) { - var check = this.nodesVirus[i]; - if (!check) continue; - - // Eating range - var xs = check.position.x - pos.x, - ys = check.position.y - pos.y, - sqDist = xs * xs + ys * ys, - dist = Math.sqrt(sqDist); - - if (check.getSize() > size) { // Check only if the virus cell is larger than imaginary cell - if (dist + size <= check.getSize()) return true; // Collided - } - } - return false; -}; - -GameServer.prototype.getDist = function(x1, y1, x2, y2) { // Use Pythagoras theorem - var deltaX = x1 - x2; - var deltaY = y1 - y2; - return Math.sqrt(deltaX * deltaX + deltaY * deltaY); -}; - -GameServer.prototype.abs = function(x) { // Because Math.abs is slow - return x < 0 ? -x : x; -}; - -GameServer.prototype.checkCellCollision = function(cell, check) { - // Returns object which contains info about cell's collisions. You can use this in the future. - - // Check the two cells for collision - var collisionDist = cell.getSize() + check.getSize(); // Minimum distance between the two cells - - var dY = cell.position.y - check.position.y; - var dX = cell.position.x - check.position.x; - var angle = Math.atan2(dX, dY); - var dist = Math.sqrt(dX * dX + dY * dY); - - return ({ - cellDist: dist, - collideDist: collisionDist, - cellMult: (Math.sqrt(check.getSize() * 100) / Math.sqrt(cell.getSize() * 100)) / 3, - cellAngle: angle, - collided: (dist < collisionDist) - }); -}; - -GameServer.prototype.cellCollision = function(cell, check, calcInfo) { - if (!calcInfo) calcInfo = this.checkCellCollision(cell, check); // Unedefined calc info - - // Check collision - if (calcInfo.collided) { // Collided - // The moving cell pushes the colliding cell - - var dist = calcInfo.cellDist; - var collisionDist = calcInfo.collideDist; - var mult = calcInfo.cellMult; - var angle = calcInfo.cellAngle; - - var move = (collisionDist - dist) * mult; - - cell.position.x += move * Math.sin(angle); - cell.position.y += move * Math.cos(angle); - } -}; - -GameServer.prototype.updateMoveEngine = function() { - // Move player cells - var len = this.nodesPlayer.length; - - for (var i in this.clients) { - var client = this.clients[i].playerTracker; - - client.cellUpdateTick--; - if (client.cellUpdateTick > 0) continue; - client.cellUpdateTick = 18; - - // Sort client's cells by ascending mass - var sorted = []; - for (var i = 0; i < client.cells.length; i++) sorted.push(client.cells[i]); - - sorted.sort(function(a, b) { - return b.mass - a.mass; - }); - - // Go cell by cell - for (var i = 0; i < sorted.length; i++) { - var cell = sorted[i]; - - // Do not move cells that have already been eaten - if (!cell) { - continue; - } - - // First move the cell - cell.calcMovePhys(this.config); - - // Now move it to the mouse - cell.calcMove(client.mouse.x, client.mouse.y, this); - - // Collision with own cells - cell.collision(this); - - // Cell eating - this.cellEating(cell); - } - } - - - // A system to move cells not controlled by players (ex. viruses, ejected mass) - len = this.nonPlayerNodes.length; - for (var i = 0; i < len; i++) { - var node = this.nonPlayerNodes[i]; - if (!node) continue; - - node.updateTicks--; - if (node.updateTicks > 0) continue; - node.updateTicks = 25; - - if (node.moveEngineSpeed <= 0) continue; // No speed - node.calcMovePhys(this.config); - node.onAutoMove(this); - } -}; - -GameServer.prototype.cellEating = function(cell) { - // Check if cells nearby - var list = this.getCellsInRange(cell); - for (var j = 0; j < list.length; j++) { - var check = list[j]; - - // Consume effect - check.onConsume(cell, this); - - // Remove cell - check.setKiller(cell); - this.removeNode(check); - } -}; - -GameServer.prototype.splitCells = function(client) { - var len = client.cells.length; - var splitCells = 0; // How many cells have been split - for (var i = 0; i < len; i++) { - var cell = client.cells[i]; - - var deltaY = client.mouse.y - cell.position.y; - var deltaX = client.mouse.x - cell.position.x; - var angle = Math.atan2(deltaX, deltaY); - if (angle == 0) angle = Math.PI / 2; - - if (this.createPlayerCell(client, cell, angle, cell.mass / 2) == true) splitCells++; - } - if (splitCells > 0) client.applyTeaming(1, 2); // Account anti-teaming -}; - -GameServer.prototype.createPlayerCell = function(client, parent, angle, mass) { - // Returns boolean whether a cell has been split or not. You can use this in the future. - - if (client.cells.length >= this.config.playerMaxCells) { - // Player cell limit - return false; - } - - if (parent.mass < this.config.playerMinMassSplit) { - // Minimum mass to split - return false; - } - - // Calculate customized speed for splitting cells - var t = Math.PI * Math.PI; - var modifier = 3 + Math.log(1 + mass) / (10 + Math.log(1 + mass)); - var splitSpeed = this.config.playerSpeed * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); - - // Calculate new position - var newPos = { - x: parent.position.x, - y: parent.position.y - }; - - // Create cell - var newCell = new Entity.PlayerCell(this.getNextNodeId(), client, newPos, mass, this); - newCell.setAngle(angle); - newCell.setMoveEngineData(splitSpeed, 0.88); - // Cells won't collide immediately - newCell.collisionRestoreTicks = 12; - parent.collisionRestoreTicks = 12; - newCell.calcMergeTime(this.config.playerRecombineTime); - parent.mass -= mass; // Remove mass from parent cell - - // Add to node list - this.addNode(newCell); - return true; -}; - -GameServer.prototype.canEjectMass = function(client) { - if (typeof client.lastEject == 'undefined' || this.time - client.lastEject >= this.config.ejectMassCooldown) { - client.lastEject = this.time; - return true; - } else - return false; -}; - -GameServer.prototype.ejectMass = function(client) { - if (!this.canEjectMass(client)) - return; - for (var i = 0; i < client.cells.length; i++) { - var cell = client.cells[i]; - - if (!cell) { - continue; - } - - if (cell.mass < this.config.playerMinMassEject) { - continue; - } - - var deltaY = client.mouse.y - cell.position.y; - var deltaX = client.mouse.x - cell.position.x; - var angle = Math.atan2(deltaX, deltaY); - - // Randomize angle - angle += (Math.random() * 0.1) - 0.05; - - // Get starting position - var size = cell.getSize() + 0.2; - var startPos = { - x: cell.position.x + ((size + this.config.ejectMass) * Math.sin(angle)), - y: cell.position.y + ((size + this.config.ejectMass) * Math.cos(angle)) - }; - - // Remove mass from parent cell - cell.mass -= this.config.ejectMassLoss; - - // Randomize angle - angle += (Math.random() * 0.6) - 0.3; - - // Create cell - var ejected = new Entity.EjectedMass(this.getNextNodeId(), client, startPos, this.config.ejectMass, this); - ejected.setAngle(angle); - ejected.setMoveEngineData(this.config.ejectSpeed, 0.88); - ejected.setColor(cell.getColor()); - - this.nodesEjected.push(ejected); - this.addNode(ejected); - } -}; - -GameServer.prototype.shootVirus = function(parent) { - var parentPos = { - x: parent.position.x, - y: parent.position.y, - }; - - var newVirus = new Entity.Virus(this.getNextNodeId(), null, parentPos, this.config.virusStartMass, this); - newVirus.setAngle(parent.getAngle()); - newVirus.setMoveEngineData(115, 0.9); - - // Add to moving cells list - this.addNode(newVirus); -}; - -GameServer.prototype.getCellsInRange = function(cell) { - var list = []; - var squareR = cell.getSquareSize(); // Get cell squared radius - - // Loop through all cells that are colliding with the player's cells - var len = cell.owner.collidingNodes.length; - for (var i = 0; i < len; i++) { - var check = cell.owner.collidingNodes[i]; - - if (typeof check === 'undefined') { - continue; - } - - // if something already collided with this cell, don't check for other collisions - if (check.inRange) { - continue; - } - - // Can't eat itself - if (cell.nodeId == check.nodeId) { - continue; - } - - // Can't eat cells that have collision turned off - if ((cell.owner == check.owner) && (cell.collisionRestoreTicks != 0) && (check.cellType == 0)) { - continue; - } - - // Eating range - var xs = cell.position.x - check.position.x, - ys = cell.position.y - check.position.y, - sqDist = xs * xs + ys * ys, - dist = Math.sqrt(sqDist); - - // Use a more reliant version for pellets - // Might be a bit slower but it can be eaten with any mass - if (check.cellType == 1) { - if (dist + check.getSize() / 3.14 > cell.getSize()) continue; // Too far away - else { - // Add to list of cells nearby - list.push(check); - - // Something is about to eat this cell; no need to check for other collisions with it - check.inRange = true; - continue; // No need to look for type and calculate if eaten again - } - } - - // Cell type check - Cell must be bigger than this number times the mass of the cell being eaten - var multiplier = 1.3; - - switch (check.getType()) { - case 0: // Players - // Can't eat self if it's not time to recombine yet - if (check.owner == cell.owner) { - // If one of cells can't merge - if (!cell.shouldRecombine || !check.shouldRecombine) { - // Check if merge command was triggered on this client - if (!cell.owner.mergeOverride) continue; - } - - multiplier = 1.0; - } - - // Can't eat team members - if (this.gameMode.haveTeams) { - if (!check.owner) { // Error check - continue; - } - - if ((check.owner != cell.owner) && (check.owner.getTeam() == cell.owner.getTeam())) { - continue; - } - } - break; - default: - break; - } - - // Make sure the cell is big enough to be eaten. - if ((check.mass * multiplier) > cell.mass) { - continue; - } - - // Eating range = radius of eating cell / 2 - 31% of the radius of the cell being eaten - var eatingRange = cell.getSize() - check.getEatingRange(); - - if (dist < eatingRange) { - // Add to list of cells nearby - list.push(check); - - // Something is about to eat this cell; no need to check for other collisions with it - check.inRange = true; - } else { - // Not in eating range - continue; - } - } - return list; -}; - -GameServer.prototype.getNearestVirus = function(cell) { - // More like getNearbyVirus - var virus = null; - var r = 100; // Checking radius - - var topY = cell.position.y - r; - var bottomY = cell.position.y + r; - - var leftX = cell.position.x - r; - var rightX = cell.position.x + r; - - // Loop through all viruses on the map. There is probably a more efficient way of doing this but whatever - var len = this.nodesVirus.length; - for (var i = 0; i < len; i++) { - var check = this.nodesVirus[i]; - - if (typeof check === 'undefined') { - continue; - } - - if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { - continue; - } - - // Add to list of cells nearby - virus = check; - break; // stop checking when a virus found - } - return virus; -}; - -GameServer.prototype.updateCells = function() { - if (!this.run) { - // Server is paused - return; - } - - // Loop through all player cells - var massDecay = 1 - (this.config.playerMassDecayRate * this.gameMode.decayMod * 0.05); - for (var i = 0; i < this.nodesPlayer.length; i++) { - var cell = this.nodesPlayer[i]; - - if (!cell) { - continue; - } - - // Recombining - if (cell.owner.cells.length > 1) { - cell.recombineTicks += 0.05; - cell.calcMergeTime(this.config.playerRecombineTime); - } else if (cell.owner.cells.length == 1 && cell.recombineTicks > 0) { - cell.recombineTicks = 0; - cell.shouldRecombine = false; - cell.owner.mergeOverride = false; - cell.owner.mergeOverrideDuration = 0; - } - // Collision - if (cell.collisionRestoreTicks > 0) { - cell.collisionRestoreTicks--; - } - - // Mass decay - if (cell.mass >= this.config.playerMinMassDecay) { - var client = cell.owner; - if (this.config.serverTeamingAllowed == 0) { - var teamMult = (client.massDecayMult - 1) / 1111 + 1; // Calculate anti-teaming multiplier for decay - var thisDecay = 1 - massDecay * (1 / teamMult); // Reverse mass decay and apply anti-teaming multiplier - cell.mass *= (1 - thisDecay); - } else { - // No anti-team - cell.mass *= massDecay; - } - } - } -}; - -GameServer.prototype.loadConfig = function() { - try { - // Load the contents of the config file - var load = ini.parse(fs.readFileSync('./gameserver.ini', 'utf-8')); - - // Replace all the default config's values with the loaded config's values - for (var obj in load) { - this.config[obj] = load[obj]; - } - } catch (err) { - // No config - console.log("[Game] Config not found... Generating new config"); - - // Create a new config - fs.writeFileSync('./gameserver.ini', ini.stringify(this.config)); - } -}; - -// Stats server - -GameServer.prototype.startStatsServer = function(port) { - // Do not start the server if the port is negative - if (port < 1) { - return; - } - - // Create stats - this.stats = "Test"; - this.getStats(); - - // Show stats - this.httpServer = http.createServer(function(req, res) { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.writeHead(200); - res.end(this.stats); - }.bind(this)); - - this.httpServer.listen(port, function() { - // Stats server - console.log("[Game] Loaded stats server on port " + port); - setInterval(this.getStats.bind(this), this.config.serverStatsUpdate * 1000); - }.bind(this)); -}; - -GameServer.prototype.getStats = function() { - var players = 0; - this.clients.forEach(function(client) { - if (client.playerTracker && client.playerTracker.cells.length > 0) - players++; - }); - var s = { - 'current_players': this.clients.length, - 'alive': players, - 'spectators': this.clients.length - players, - 'max_players': this.config.serverMaxConnections, - 'gamemode': this.gameMode.name, - 'uptime': Math.round((new Date().getTime() - this.startTime)/1000/60)+" m", - 'start_time': this.startTime - }; - this.stats = JSON.stringify(s); -}; - -// Custom prototype functions -WebSocket.prototype.sendPacket = function(packet) { - function getBuf(data) { - var array = new Uint8Array(data.buffer || data); - var l = data.byteLength || data.length; - var o = data.byteOffset || 0; - var buffer = new Buffer(l); - - for (var i = 0; i < l; i++) { - buffer[i] = array[o + i]; - } - - return buffer; - } - - //if (this.readyState == WebSocket.OPEN && (this._socket.bufferSize == 0) && packet.build) { - if (this.readyState == WebSocket.OPEN && packet.build) { - var buf = packet.build(); - this.send(getBuf(buf), { - binary: true - }); - } else if (!packet.build) { - // Do nothing - } else { - this.readyState = WebSocket.CLOSED; - this.emit('close'); - this.removeAllListeners(); - } -}; +// Library imports +var WebSocket = require('ws'); +var http = require('http'); +var fs = require("fs"); +var ini = require('./modules/ini.js'); + +// Project imports +var Packet = require('./packet'); +var PlayerTracker = require('./PlayerTracker'); +var PacketHandler = require('./PacketHandler'); +var Entity = require('./entity'); +var Gamemode = require('./gamemodes'); +var BotLoader = require('./ai/BotLoader'); +var Logger = require('./modules/log'); + +// GameServer implementation +function GameServer() { + // Startup + this.run = true; + this.lastNodeId = 1; + this.lastPlayerId = 1; + this.clients = []; + this.largestClient; // Required for spectators + this.nodes = []; + this.nodesVirus = []; // Virus nodes + this.nodesEjected = []; // Ejected mass nodes + this.nodesPlayer = []; // Nodes controlled by players + + this.currentFood = 0; + this.movingNodes = []; // For move engine + this.leaderboard = []; + this.lb_packet = null; // Leaderboard packet for protocol 5 + + this.bots = new BotLoader(this); + this.log = new Logger(); + this.commands; // Command handler + + // Main loop tick + this.startTime = +new Date; + this.tickCounter = 0; + this.tickSpawn = 0; // Used with spawning food + + + // Config + this.config = { // Border - Right: X increases, Down: Y increases (as of 2015-05-20) + serverMaxConnections: 64, // Maximum amount of connections to the server. + serverPort: 443, // Server port + serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams + serverBots: 0, // Amount of player bots to spawn + serverViewBaseX: 1024, // Base view distance of players. Warning: high values may cause lag + serverViewBaseY: 592, + serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. + serverStatsUpdate: 60, // Amount of seconds per update for the server stats + serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections + serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. + serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. + borderLeft: 0, // Left border of map (Vanilla value: 0) + borderRight: 6000, // Right border of map (Vanilla value: 14142.135623730952) + borderTop: 0, // Top border of map (Vanilla value: 0) + borderBottom: 6000, // Bottom border of map (Vanilla value: 14142.135623730952) + spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) + foodSpawnAmount: 10, // The amount of food to spawn per interval + foodStartAmount: 100, // The starting amount of food in the map + foodMaxAmount: 500, // Maximum food cells on the map + foodMass: 1, // Starting food size (In mass) + foodMassGrow: 1, // Enable food mass grow ? + foodMassGrowPossiblity: 50, // Chance for a food to has the ability to be self growing + foodMassLimit: 5, // Maximum mass for a food can grow + foodMassTimeout: 120, // The amount of interval for a food to grow its mass (in seconds) + virusMinAmount: 10, // Minimum amount of viruses on the map. + virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. + virusStartMass: 100, // Starting virus size (In mass) + virusFeedAmount: 7, // Amount of times you need to feed a virus to shoot it + ejectMass: 13, // Mass of ejected cells + ejectMassCooldown: 100, // Time until a player can eject mass again + ejectMassLoss: 15, // Mass lost when ejecting cells + ejectSpeed: 100, // Base speed of ejected cells + ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass + playerStartMass: 10, // Starting mass of the player cell. + playerBotGrowEnabled: 1, // If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass + playerMaxMass: 22500, // Maximum mass a player can have + playerMinMassEject: 32, // Mass required to eject a cell + playerMinMassSplit: 36, // Mass required to split + playerMaxCells: 16, // Max cells the player is allowed to have + playerRecombineTime: 30, // Base amount of seconds before a cell is allowed to recombine + playerMassAbsorbed: 1.0, // Fraction of player cell's mass gained upon eating + playerMassDecayRate: .002, // Amount of mass lost per second + playerMinMassDecay: 9, // Minimum mass for decay to occur + playerMaxNickLength: 15, // Maximum nick length + playerSpeed: 30, // Player base speed + playerDisconnectTime: 60, // The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) + tourneyMaxPlayers: 12, // Maximum amount of participants for tournament style game modes + tourneyPrepTime: 10, // Amount of ticks to wait after all players are ready (1 tick = 1000 ms) + tourneyEndTime: 30, // Amount of ticks to wait after a player wins (1 tick = 1000 ms) + tourneyTimeLimit: 20, // Time limit of the game, in minutes. + tourneyAutoFill: 0, // If set to a value higher than 0, the tournament match will automatically fill up with bots after this amount of seconds + tourneyAutoFillPlayers: 1, // The timer for filling the server with bots will not count down unless there is this amount of real players + }; + // Parse config + this.loadConfig(); + + // Gamemodes + this.gameMode = Gamemode.get(this.config.serverGamemode); +} + +module.exports = GameServer; + +GameServer.prototype.start = function() { + // Logging + this.log.setup(this); + + // Gamemode configurations + this.gameMode.onServerInit(this); + + // Start the server + this.socketServer = new WebSocket.Server({ + port: this.config.serverPort, + perMessageDeflate: false + }, function() { + // Spawn starting food + this.startingFood(); + + // Start Main Loop + setInterval(this.mainLoop.bind(this), 40); + + // Done + console.log("[Game] Listening on port " + this.config.serverPort); + console.log("[Game] Current game mode is " + this.gameMode.name); + + // Player bots (Experimental) + if (this.config.serverBots > 0) { + for (var i = 0; i < this.config.serverBots; i++) { + this.bots.addBot(); + } + console.log("[Game] Loaded " + this.config.serverBots + " player bots"); + } + + }.bind(this)); + + this.socketServer.on('connection', connectionEstablished.bind(this)); + + // Properly handle errors because some people are too lazy to read the readme + this.socketServer.on('error', function err(e) { + switch (e.code) { + case "EADDRINUSE": + console.log("[Error] Server could not bind to port! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); + break; + case "EACCES": + console.log("[Error] Please make sure you are running Ogar with root privileges."); + break; + default: + console.log("[Error] Unhandled error code: " + e.code); + break; + } + process.exit(1); // Exits the program + }); + + function connectionEstablished(ws) { + if (this.clients.length >= this.config.serverMaxConnections) { // Server full + ws.close(); + return; + } + + function close(error) { + // Log disconnections + this.server.log.onDisconnect(this.socket.remoteAddress); + + var client = this.socket.playerTracker; + var len = this.socket.playerTracker.cells.length; + for (var i = 0; i < len; i++) { + var cell = this.socket.playerTracker.cells[i]; + + if (!cell) { + continue; + } + + cell.calcMove = function() { + return; + }; // Clear function so that the cell cant move + //this.server.removeNode(cell); + } + + client.disconnect = this.server.config.playerDisconnectTime * 20; + this.socket.sendPacket = function() { + return; + }; // Clear function so no packets are sent + } + + ws.remoteAddress = ws._socket.remoteAddress; + ws.remotePort = ws._socket.remotePort; + this.log.onConnect(ws.remoteAddress); // Log connections + + ws.playerTracker = new PlayerTracker(this, ws); + ws.packetHandler = new PacketHandler(this, ws); + ws.on('message', ws.packetHandler.handleMessage.bind(ws.packetHandler)); + + var bindObject = { + server: this, + socket: ws + }; + ws.on('error', close.bind(bindObject)); + ws.on('close', close.bind(bindObject)); + this.clients.push(ws); + } + + this.startStatsServer(this.config.serverStatsPort); +}; + +GameServer.prototype.getMode = function() { + return this.gameMode; +}; + +GameServer.prototype.getNextNodeId = function() { + // Resets integer + if (this.lastNodeId > 2147483647) { + this.lastNodeId = 1; + } + return this.lastNodeId++; +}; + +GameServer.prototype.getNewPlayerID = function() { + // Resets integer + if (this.lastPlayerId > 2147483647) { + this.lastPlayerId = 1; + } + return this.lastPlayerId++; +}; + +GameServer.prototype.getRandomPosition = function() { + var xSum = this.config.borderRight + this.config.borderLeft; + var ySum = this.config.borderBottom + this.config.borderTop; + return { + x: Math.floor(Math.random() * xSum - this.config.borderLeft), + y: Math.floor(Math.random() * ySum - this.config.borderTop) + }; +}; + +GameServer.prototype.getRandomSpawn = function(mass) { + // Random and secure spawns for players and viruses + var pos = this.getRandomPosition(); + var unsafe = this.willCollide(mass, pos, mass == this.config.virusStartMass); + var attempt = 1; + + // Prevent stack overflow by counting attempts + while (true) { + if (!unsafe || attempt >= 15) break; + pos = this.getRandomPosition(); + unsafe = this.willCollide(mass, pos, mass == this.config.virusStartMass); + attempt++; + } + + // If it reached attempt 15, warn the user + if (attempt >= 14) { + console.log("[Server] Entity was force spawned near viruses/playercells after 15 attempts."); + console.log("[Server] If this message keeps appearing, check your config, especially start masses for players and viruses."); + } + + return pos; +}; + +GameServer.prototype.getRandomColor = function() { + var colorRGB = [0xFF, 0x07, (Math.random() * 256) >> 0]; + colorRGB.sort(function() { + return 0.5 - Math.random(); + }); + return { + r: colorRGB[0], + g: colorRGB[1], + b: colorRGB[2] + }; +}; + +GameServer.prototype.addNode = function(node) { + this.nodes.push(node); + + // Adds to the owning player's screen excluding ejected cells + if (node.owner && node.cellType != 3) { + node.setColor(node.owner.color); + node.owner.cells.push(node); + node.owner.socket.sendPacket(new Packet.AddNode(node)); + } + + // Special on-add actions + node.onAdd(this); + + // Add to visible nodes + for (var i = 0; i < this.clients.length; i++) { + var client = this.clients[i].playerTracker; + if (!client) { + continue; + } + + // client.nodeAdditionQueue is only used by human players, not bots + // for bots it just gets collected forever, using ever-increasing amounts of memory + if ('_socket' in client.socket && node.visibleCheck(client.viewBox, client.centerPos, client.cells)) { + client.nodeAdditionQueue.push(node); + } + } +}; + +GameServer.prototype.removeNode = function(node) { + // Remove from main nodes list + var index = this.nodes.indexOf(node); + if (index != -1) { + this.nodes.splice(index, 1); + } + + // Remove from moving cells list + index = this.movingNodes.indexOf(node); + if (index != -1) { + this.movingNodes.splice(index, 1); + } + + // Special on-remove actions + node.onRemove(this); + + // Animation when eating + for (var i = 0; i < this.clients.length; i++) { + var client = this.clients[i].playerTracker; + if (!client) { + continue; + } + + // Remove from client + client.nodeDestroyQueue.push(node); + } +}; + +GameServer.prototype.spawnTick = function() { + // Spawn food + this.tickSpawn++; + if (this.tickSpawn >= this.config.spawnInterval) { + this.updateFood(); // Spawn food + this.virusCheck(); // Spawn viruses + + this.tickSpawn = 0; // Reset + } +}; + +GameServer.prototype.gamemodeTick = function() { + // Gamemode tick + this.gameMode.onTick(this); +}; + +GameServer.prototype.cellUpdateTick = function() { + // Update cells + this.updateCells(); +}; + +GameServer.prototype.mainLoop = function() { + // Loop main functions + this.updateMoveEngine(); // Move cells + this.spawnTick(); + this.gamemodeTick(); + this.cellUpdateTick(); + this.updateClients(); + + // Update leaderboard with the gamemode's method + if ((this.tickCounter % 40) == 0) { + this.leaderboard = []; + this.gameMode.updateLB(this); + this.lb_packet = new Packet.UpdateLeaderboard(this.leaderboard, this.gameMode.packetLB); + + if (!this.gameMode.specByLeaderboard) { + // Get client with largest score if gamemode doesn't have a leaderboard + var clients = this.clients.valueOf(); + + // Use sort function + clients.sort(function (a, b) { + return b.playerTracker.getScore(true) - a.playerTracker.getScore(true); + }); + this.largestClient = clients[0].playerTracker; + } else this.largestClient = this.gameMode.rankOne; + } + + this.tickCounter++; +}; + +GameServer.prototype.updateClients = function() { + for (var i = 0; i < this.clients.length; i++) { + if (typeof this.clients[i] == "undefined") { + continue; + } + this.clients[i].playerTracker.update(); + } +}; + +GameServer.prototype.startingFood = function() { + // Spawns the starting amount of food cells + for (var i = 0; i < this.config.foodStartAmount; i++) { + this.spawnFood(); + } +}; + +GameServer.prototype.updateFood = function() { + var toSpawn = Math.min(this.config.foodSpawnAmount, (this.config.foodMaxAmount - this.currentFood)); + for (var i = 0; i < toSpawn; i++) { + this.spawnFood(); + } +}; + +GameServer.prototype.spawnFood = function() { + var f = new Entity.Food(this.getNextNodeId(), null, this.getRandomPosition(), this.config.foodMass, this); + f.setColor(this.getRandomColor()); + + this.addNode(f); + this.currentFood++; +}; + +GameServer.prototype.spawnPlayer = function(player, pos, mass) { + if (mass == null) { // Get starting mass + mass = this.config.playerStartMass; + } + if (pos == null) { // Get random pos + pos = this.getRandomSpawn(mass); + } + + // Spawn player and add to world + var cell = new Entity.PlayerCell(this.getNextNodeId(), player, pos, mass, this); + this.addNode(cell); + + // Set initial mouse coords + player.mouse = { + x: pos.x, + y: pos.y + }; +}; + +GameServer.prototype.virusCheck = function() { + // Checks if there are enough viruses on the map + if (this.nodesVirus.length < this.config.virusMinAmount) { + // Spawns a virus + var pos = this.getRandomSpawn(this.config.virusStartMass); + + var v = new Entity.Virus(this.getNextNodeId(), null, pos, this.config.virusStartMass, this); + this.addNode(v); + } +}; + +GameServer.prototype.willCollide = function(mass, pos, isVirus) { + // Look if there will be any collision with the current nodes + var size = Math.sqrt(mass * 100) >> 0; + + for (var i = 0; i < this.nodesPlayer.length; i++) { + var check = this.nodesPlayer[i]; + if (!check) continue; + + // Eating range + var xs = check.position.x - pos.x, + ys = check.position.y - pos.y, + sqDist = xs * xs + ys * ys, + dist = Math.sqrt(sqDist); + + if (check.getSize() > size) { // Check only if the player cell is larger than imaginary cell + if (dist + size <= check.getSize()) return true; // Collided + } + } + + if (isVirus) return false; // Don't check for viruses if the new cell will be virus + + for (var i = 0; i < this.nodesVirus.length; i++) { + var check = this.nodesVirus[i]; + if (!check) continue; + + // Eating range + var xs = check.position.x - pos.x, + ys = check.position.y - pos.y, + sqDist = xs * xs + ys * ys, + dist = Math.sqrt(sqDist); + + if (check.getSize() > size) { // Check only if the virus cell is larger than imaginary cell + if (dist + size <= check.getSize()) return true; // Collided + } + } + return false; +}; + +GameServer.prototype.getDist = function(x1, y1, x2, y2) { // Use Pythagoras theorem + var deltaX = this.abs(x1 - x2); + var deltaY = this.abs(y1 - y2); + return Math.sqrt(deltaX * deltaX + deltaY * deltaY); +}; + +GameServer.prototype.abs = function(x) { // Because Math.abs is slow + return x < 0 ? -x : x; +}; + +GameServer.prototype.checkCellCollision = function(cell, check) { + // Returns object which contains info about cell's collisions. You can use this in the future. + + // Check the two cells for collision + var collisionDist = cell.getSize() + check.getSize(); // Minimum distance between the two cells + var dist = this.getDist(cell.position.x, cell.position.y, + check.position.x, check.position.y); // Distance between these two cells + + var dY = cell.position.y - check.position.y; + var dX = cell.position.x - check.position.x; + var angle = Math.atan2(dX, dY); + + return ({ + cellDist: dist, + collideDist: collisionDist, + cellMult: (Math.sqrt(check.getSize() * 100) / Math.sqrt(cell.getSize() * 100)) / 3, + cellAngle: angle, + collided: (dist < collisionDist) + }); +}; + +GameServer.prototype.cellCollision = function(cell, check, calcInfo) { + if (!calcInfo) calcInfo = this.checkCellCollision(cell, check); // Unedefined calc info + + // Check collision + if (calcInfo.collided) { // Collided + // The moving cell pushes the colliding cell + + var dist = calcInfo.cellDist; + var collisionDist = calcInfo.collideDist; + var mult = calcInfo.cellMult; + var angle = calcInfo.cellAngle; + + var move = (collisionDist - dist) * mult; + + cell.position.x += move * Math.sin(angle); + cell.position.y += move * Math.cos(angle); + } +}; + +GameServer.prototype.updateMoveEngine = function() { + // Move player cells + var len = this.nodesPlayer.length; + + for (var i in this.clients) { + var client = this.clients[i].playerTracker; + + // Sort client's cells by ascending mass + var sorted = []; + for (var i = 0; i < client.cells.length; i++) sorted.push(client.cells[i]); + + sorted.sort(function(a, b) { + return b.mass - a.mass; + }); + + // Go cell by cell + for (var i = 0; i < sorted.length; i++) { + var cell = sorted[i]; + + // Do not move cells that have already been eaten + if (!cell) { + continue; + } + + // First move the cell + cell.calcMovePhys(this.config); + + // Now move it to the mouse + cell.calcMove(client.mouse.x, client.mouse.y, this); + + // Collision with own cells + cell.collision(this); + + // Cell eating + this.cellEating(cell); + } + } + + + // A system to move cells not controlled by players (ex. viruses, ejected mass) + len = this.movingNodes.length; + for (var i = 0; i < len; i++) { + var check = this.movingNodes[i]; + + // Recycle unused nodes + while ((typeof check == "undefined") && (i < this.movingNodes.length)) { + // Remove moving cells that are undefined + this.movingNodes.splice(i, 1); + check = this.movingNodes[i]; + } + + if (i >= this.movingNodes.length) { + continue; + } + + if (check.moveEngineTicks > 0) { + check.onAutoMove(this); + // If the cell has enough move ticks, then move it + check.calcMovePhys(this.config); + } else { + // Auto move is done + check.moveDone(this); + // Remove cell from list + var index = this.movingNodes.indexOf(check); + if (index != -1) { + this.movingNodes.splice(index, 1); + } + } + } +}; + +GameServer.prototype.cellEating = function(cell) { + // Check if cells nearby + var list = this.getCellsInRange(cell); + for (var j = 0; j < list.length; j++) { + var check = list[j]; + + // Consume effect + check.onConsume(cell, this); + + // Remove cell + check.setKiller(cell); + this.removeNode(check); + } +}; + +GameServer.prototype.setAsMovingNode = function(node) { + if (this.movingNodes.indexOf(node) == -1) this.movingNodes.push(node); +}; + +GameServer.prototype.splitCells = function(client) { + var len = client.cells.length; + var splitCells = 0; // How many cells have been split + for (var i = 0; i < len; i++) { + var cell = client.cells[i]; + + var deltaY = client.mouse.y - cell.position.y; + var deltaX = client.mouse.x - cell.position.x; + var angle = Math.atan2(deltaX, deltaY); + if (angle == 0) angle = Math.PI / 2; + + if (this.createPlayerCell(client, cell, angle, cell.mass / 2) == true) splitCells++; + } +}; + +GameServer.prototype.createPlayerCell = function(client, parent, angle, mass) { + // Returns boolean whether a cell has been split or not. You can use this in the future. + + if (client.cells.length >= this.config.playerMaxCells) { + // Player cell limit + return false; + } + + if (parent.mass < this.config.playerMinMassSplit) { + // Minimum mass to split + return false; + } + + // Calculate customized speed for splitting cells + var t = Math.PI * Math.PI; + var modifier = 3 + Math.log(1 + mass) / (10 + Math.log(1 + mass)); + var splitSpeed = this.config.playerSpeed * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); + + // Calculate new position + var newPos = { + x: parent.position.x, + y: parent.position.y + }; + + // Create cell + var newCell = new Entity.PlayerCell(this.getNextNodeId(), client, newPos, mass, this); + newCell.setAngle(angle); + newCell.setMoveEngineData(splitSpeed, 12, 0.88); + // Cells won't collide immediately + newCell.collisionRestoreTicks = 12; + parent.collisionRestoreTicks = 12; + newCell.calcMergeTime(this.config.playerRecombineTime); + parent.mass -= mass; // Remove mass from parent cell + + // Add to node list + this.addNode(newCell); + return true; +}; + +GameServer.prototype.canEjectMass = function(client) { + if (typeof client.lastEject == 'undefined' || this.tickCounter - client.lastEject >= this.config.ejectMassCooldown) { + client.lastEject = this.tickCounter; + return true; + } else + return false; +}; + +GameServer.prototype.ejectMass = function(client) { + if (!this.canEjectMass(client)) + return; + for (var i = 0; i < client.cells.length; i++) { + var cell = client.cells[i]; + + if (!cell) { + continue; + } + + if (cell.mass < this.config.playerMinMassEject) { + continue; + } + + var deltaY = client.mouse.y - cell.position.y; + var deltaX = client.mouse.x - cell.position.x; + var angle = Math.atan2(deltaX, deltaY); + + // Randomize angle + angle += (Math.random() * 0.1) - 0.05; + + // Get starting position + var size = cell.getSize() + 0.2; + var startPos = { + x: cell.position.x + ((size + this.config.ejectMass) * Math.sin(angle)), + y: cell.position.y + ((size + this.config.ejectMass) * Math.cos(angle)) + }; + + // Remove mass from parent cell + cell.mass -= this.config.ejectMassLoss; + + // Randomize angle + angle += (Math.random() * 0.6) - 0.3; + + // Create cell + var ejected = new Entity.EjectedMass(this.getNextNodeId(), client, startPos, this.config.ejectMass, this); + ejected.setAngle(angle); + ejected.setMoveEngineData(this.config.ejectSpeed, 20, 0.88); + ejected.setColor(cell.getColor()); + + this.nodesEjected.push(ejected); + this.addNode(ejected); + this.setAsMovingNode(ejected); + } +}; + +GameServer.prototype.shootVirus = function(parent) { + var parentPos = { + x: parent.position.x, + y: parent.position.y, + }; + + var newVirus = new Entity.Virus(this.getNextNodeId(), null, parentPos, this.config.virusStartMass, this); + newVirus.setAngle(parent.getAngle()); + newVirus.setMoveEngineData(135, 20, 0.85); + + // Add to moving cells list + this.addNode(newVirus); + this.setAsMovingNode(newVirus); +}; + +GameServer.prototype.getCellsInRange = function(cell) { + var list = []; + var squareR = cell.getSquareSize(); // Get cell squared radius + + // Loop through all cells that are colliding with the player's cells + var len = cell.owner.collidingNodes.length; + for (var i = 0; i < len; i++) { + var check = cell.owner.collidingNodes[i]; + + if (typeof check === 'undefined') { + continue; + } + + // if something already collided with this cell, don't check for other collisions + if (check.inRange) { + continue; + } + + // Can't eat itself + if (cell.nodeId == check.nodeId) { + continue; + } + + // Can't eat cells that have collision turned off + if ((cell.owner == check.owner) && (cell.collisionRestoreTicks != 0) && (check.cellType == 0)) { + continue; + } + + // Eating range + var xs = cell.position.x - check.position.x, + ys = cell.position.y - check.position.y, + sqDist = xs * xs + ys * ys, + dist = Math.sqrt(sqDist); + + // Use a more reliant version for pellets + // Might be a bit slower but it can be eaten with any mass + if (check.cellType == 1) { + if (dist + check.getSize() / 3.14 > cell.getSize()) continue; // Too far away + else { + // Add to list of cells nearby + list.push(check); + + // Something is about to eat this cell; no need to check for other collisions with it + check.inRange = true; + continue; // No need to look for type and calculate if eaten again + } + } + + // Cell type check - Cell must be bigger than this number times the mass of the cell being eaten + var multiplier = 1.3; + + switch (check.getType()) { + case 1: // Food cell + list.push(check); + check.inRange = true; // skip future collision checks for this food + continue; + case 0: // Players + // Can't eat self if it's not time to recombine yet + if (check.owner == cell.owner) { + // If one of cells can't merge + if (!cell.shouldRecombine || !check.shouldRecombine) { + // Check if merge command was triggered on this client + if (!cell.owner.mergeOverride) continue; + } + + multiplier = 1.00; + } + + // Can't eat team members + if (this.gameMode.haveTeams) { + if (!check.owner) { // Error check + continue; + } + + if ((check.owner != cell.owner) && (check.owner.getTeam() == cell.owner.getTeam())) { + continue; + } + } + break; + default: + break; + } + + // Make sure the cell is big enough to be eaten. + if ((check.mass * multiplier) > cell.mass) { + continue; + } + + // Eating range = radius of eating cell / 2 - 31% of the radius of the cell being eaten + var eatingRange = cell.getSize() - check.getEatingRange(); + + if (dist < eatingRange) { + // Add to list of cells nearby + list.push(check); + + // Something is about to eat this cell; no need to check for other collisions with it + check.inRange = true; + } else { + // Not in eating range + continue; + } + } + return list; +}; + +GameServer.prototype.getNearestVirus = function(cell) { + // More like getNearbyVirus + var virus = null; + var r = 100; // Checking radius + + var topY = cell.position.y - r; + var bottomY = cell.position.y + r; + + var leftX = cell.position.x - r; + var rightX = cell.position.x + r; + + // Loop through all viruses on the map. There is probably a more efficient way of doing this but whatever + var len = this.nodesVirus.length; + for (var i = 0; i < len; i++) { + var check = this.nodesVirus[i]; + + if (typeof check === 'undefined') { + continue; + } + + if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { + continue; + } + + // Add to list of cells nearby + virus = check; + break; // stop checking when a virus found + } + return virus; +}; + +GameServer.prototype.updateCells = function() { + if (!this.run) { + // Server is paused + return; + } + + // Loop through all player cells + var massDecay = 1 - (this.config.playerMassDecayRate * this.gameMode.decayMod * 0.05); + for (var i = 0; i < this.nodesPlayer.length; i++) { + var cell = this.nodesPlayer[i]; + + if (!cell) { + continue; + } + + // Recombining + if (cell.owner.cells.length > 1) { + cell.recombineTicks += 0.05; + cell.calcMergeTime(this.config.playerRecombineTime); + } else if (cell.owner.cells.length == 1 && cell.recombineTicks > 0) { + cell.recombineTicks = 0; + cell.shouldRecombine = false; + cell.owner.mergeOverride = false; + cell.owner.mergeOverrideDuration = 0; + } + // Collision + if (cell.collisionRestoreTicks > 0) { + cell.collisionRestoreTicks--; + } + + // Mass decay + if (cell.mass >= this.config.playerMinMassDecay) { + cell.mass *= massDecay; + } + } +}; + +GameServer.prototype.loadConfig = function() { + try { + // Load the contents of the config file + var load = ini.parse(fs.readFileSync('./gameserver.ini', 'utf-8')); + + // Replace all the default config's values with the loaded config's values + for (var obj in load) { + this.config[obj] = load[obj]; + } + } catch (err) { + // No config + console.log("[Game] Config not found... Generating new config"); + + // Create a new config + fs.writeFileSync('./gameserver.ini', ini.stringify(this.config)); + } +}; + +// Stats server + +GameServer.prototype.startStatsServer = function(port) { + // Do not start the server if the port is negative + if (port < 1) { + return; + } + + // Create stats + this.stats = "Test"; + this.getStats(); + + // Show stats + this.httpServer = http.createServer(function(req, res) { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.writeHead(200); + res.end(this.stats); + }.bind(this)); + + this.httpServer.listen(port, function() { + // Stats server + console.log("[Game] Loaded stats server on port " + port); + setInterval(this.getStats.bind(this), this.config.serverStatsUpdate * 1000); + }.bind(this)); +}; + +GameServer.prototype.getStats = function() { + var players = 0; + this.clients.forEach(function(client) { + if (client.playerTracker && client.playerTracker.cells.length > 0) + players++; + }); + var s = { + 'current_players': this.clients.length, + 'alive': players, + 'spectators': this.clients.length - players, + 'max_players': this.config.serverMaxConnections, + 'gamemode': this.gameMode.name, + 'uptime': Math.round((new Date().getTime() - this.startTime)/1000/60)+" m", + 'start_time': this.startTime + }; + this.stats = JSON.stringify(s); +}; + +// Custom prototype functions +WebSocket.prototype.sendPacket = function(packet) { + + //if (this.readyState == WebSocket.OPEN && (this._socket.bufferSize == 0) && packet.build) { + if (this.readyState == WebSocket.OPEN) { + var buffer = packet.build(this.playerTracker.socket.packetHandler.protocol); + if (buffer != null) { + this.send(buffer, { binary: true }); + } + } else { + this.readyState = WebSocket.CLOSED; + this.emit('close'); + this.removeAllListeners(); + } +}; diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 17301a31f..1bc99010a 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -1,129 +1,170 @@ -var Packet = require('./packet'); - -function PacketHandler(gameServer, socket) { - this.gameServer = gameServer; - this.socket = socket; - // Detect protocol version - we can do something about it later - this.protocolVersion = 0; - - this.pressQ = false; - this.pressW = false; - this.pressSpace = false; -} - -module.exports = PacketHandler; - -PacketHandler.prototype.handleMessage = function(message) { - function stobuf(buf) { - var length = buf.length; - var arrayBuf = new ArrayBuffer(length); - var view = new Uint8Array(arrayBuf); - - for (var i = 0; i < length; i++) { - view[i] = buf[i]; - } - - return view.buffer; - } - - // Discard empty messages - if (message.length == 0) { - return; - } - - var buffer = stobuf(message); - var view = new DataView(buffer); - var packetId = view.getUint8(0, true); - - switch (packetId) { - case 0: - // Set Nickname - if (this.protocolVersion == 5) { - // Check for invalid packets - if ((view.byteLength + 1) % 2 == 1) { - break; - } - var nick = ""; - var maxLen = this.gameServer.config.playerMaxNickLength * 2; // 2 bytes per char - for (var i = 1; i < view.byteLength && i <= maxLen; i += 2) { - var charCode = view.getUint16(i, true); - if (charCode == 0) { - break; - } - - nick += String.fromCharCode(charCode); - } - this.setNickname(nick); - } else { - var name = message.slice(1, message.length - 1).toString().substr(0, this.gameServer.config.playerMaxNickLength); - this.setNickname(name); - } - break; - case 1: - // Spectate mode - if (this.socket.playerTracker.cells.length <= 0) { - // Make sure client has no cells - this.socket.playerTracker.spectate = true; - } - break; - case 16: - // Set Target - if (view.byteLength == 13) { - var client = this.socket.playerTracker; - client.mouse.x = view.getInt32(1, true) - client.scrambleX; - client.mouse.y = view.getInt32(5, true) - client.scrambleY; - } - - client.movePacketTriggered = true; - break; - case 17: - // Space Press - Split cell - this.pressSpace = true; - break; - case 18: - // Q Key Pressed - this.pressQ = true; - break; - case 19: - // Q Key Released - break; - case 21: - // W Press - Eject mass - this.pressW = true; - break; - case 254: - // Connection Start - if (view.byteLength == 5) { - this.protocolVersion = view.getUint32(1, true); - // Send on connection packets - this.socket.sendPacket(new Packet.ClearNodes(this.protocolVersion)); - var c = this.gameServer.config; - this.socket.sendPacket(new Packet.SetBorder( - c.borderLeft + this.socket.playerTracker.scrambleX, - c.borderRight + this.socket.playerTracker.scrambleX, - c.borderTop + this.socket.playerTracker.scrambleY, - c.borderBottom + this.socket.playerTracker.scrambleY - )); - } - break; - default: - break; - } -}; - -PacketHandler.prototype.setNickname = function(newNick) { - var client = this.socket.playerTracker; - if (client.cells.length < 1) { - // Set name first - client.setName(newNick); - - // Clear client's nodes - this.socket.sendPacket(new Packet.ClearNodes()); - - // If client has no cells... then spawn a player - this.gameServer.gameMode.onPlayerSpawn(this.gameServer, client); - - // Turn off spectate mode - client.spectate = false; - } -}; +var Packet = require('./packet'); + +function PacketHandler(gameServer, socket) { + this.gameServer = gameServer; + this.socket = socket; + // Detect protocol version - we can do something about it later + this.protocol = 0; + + this.pressQ = false; + this.pressW = false; + this.pressSpace = false; +} + +module.exports = PacketHandler; + +PacketHandler.prototype.handleMessage = function(message) { + function stobuf(buf) { + var length = buf.length; + var arrayBuf = new ArrayBuffer(length); + var view = new Uint8Array(arrayBuf); + + for (var i = 0; i < length; i++) { + view[i] = buf[i]; + } + + return view.buffer; + } + + // Discard empty messages + if (message.length == 0) { + return; + } + + var buffer = stobuf(message); + var view = new DataView(buffer); + var packetId = view.getUint8(0, true); + + switch (packetId) { + case 0: + var bufferName = new Buffer(message); + bufferName = bufferName.slice(1); + + var text = ""; + if (bufferName.length > 0) { + if (this.protocol <= 5) { + text = this.readStringUnicode(bufferName); + } else { + text = this.readStringUtf8(bufferName); + } + if (text.length > this.gameServer.config.playerMaxNickLength) { + text = text.substring(0, this.gameServer.config.playerMaxNickLength); + } + } + this.setNickname(text); + break; + + case 1: + // Spectate mode + if (this.socket.playerTracker.cells.length <= 0) { + // Make sure client has no cells + this.socket.playerTracker.spectate = true; + } + break; + case 16: + // Set Target + var client = this.socket.playerTracker; + if (view.byteLength == 13) { // protocol 5,6,7 + client.mouse.x = view.getInt32(1, true) - client.scrambleX; + client.mouse.y = view.getInt32(5, true) - client.scrambleY; + client.movePacketTriggered = true; + } else if (view.byteLength == 9) { // early protocol 5 + client.mouse.x = view.getInt16(1, true) - client.scrambleX; + client.mouse.y = view.getInt16(3, true) - client.scrambleY; + client.movePacketTriggered = true; + } else if (view.byteLength == 21) { // protocol 4 + client.mouse.x = view.getFloat64(1, true) - client.scrambleX; + client.mouse.y = view.getFloat64(9, true) - client.scrambleY; + client.movePacketTriggered = true; + } + break; + case 17: + // Space Press - Split cell + this.pressSpace = true; + break; + case 18: + // Q Key Pressed + this.pressQ = true; + break; + case 19: + // Q Key Released + break; + case 21: + // W Press - Eject mass + this.pressW = true; + break; + case 254: + // Connection Start + if (view.byteLength == 5) { + this.protocol = view.getUint32(1, true); + // Send Clear & SetBorder packet first + this.socket.sendPacket(new Packet.ClearNodes()); + var c = this.gameServer.config; + this.socket.sendPacket(new Packet.SetBorder( + c.borderLeft + this.socket.playerTracker.scrambleX, + c.borderRight + this.socket.playerTracker.scrambleX, + c.borderTop + this.socket.playerTracker.scrambleY, + c.borderBottom + this.socket.playerTracker.scrambleY, + 0, + "OgarMulti")); + } + break; + default: + break; + } +}; + +PacketHandler.prototype.readStringUnicode = function (dataBuffer) { + if (dataBuffer.length > 256) return ""; + + var buffer = new Buffer(dataBuffer); + var text = ""; + var maxLength = (buffer.length / 2) >> 0; + if (maxLength * 2 > buffer.length) maxLength--; + if (maxLength < 0) maxLength = 0; + for (var i = 0; i < maxLength; i++) { + var charCode = buffer.readUInt16LE(i * 2); + if (charCode == 0) { + break; + } + text += String.fromCharCode(charCode); + } + return text; +} + +PacketHandler.prototype.readStringUtf8 = function (dataBuffer) { + if (dataBuffer.length > 256) return ""; + + var buffer = new Buffer(dataBuffer); + var maxLen = buffer.length; + for (var i = 0; i < maxLen; i++) { + if (buffer[i] == 0) { + maxLen = i; + break; + } + } + var text = buffer.toString('utf8', 0, maxLen); // utf8 => string + return text; +} + +PacketHandler.prototype.setNickname = function(newNick) { + var client = this.socket.playerTracker; + if (client.cells.length < 1) { + // Set name first + client.setName(newNick); + + var c = this.gameServer.config; + this.socket.sendPacket(new Packet.ClearNodes()); + this.socket.sendPacket(new Packet.SetBorder( + c.borderLeft + this.socket.playerTracker.scrambleX, + c.borderRight + this.socket.playerTracker.scrambleX, + c.borderTop + this.socket.playerTracker.scrambleY, + c.borderBottom + this.socket.playerTracker.scrambleY)); + + // If client has no cells... then spawn a player + this.gameServer.gameMode.onPlayerSpawn(this.gameServer, client); + + // Turn off spectate mode + client.spectate = false; + } +}; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 3972b6844..6f59d0005 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -1,503 +1,436 @@ -var Packet = require('./packet'); -var GameServer = require('./GameServer'); - -function PlayerTracker(gameServer, socket) { - this.pID = -1; - this.disconnect = -1; // Disconnection - this.name = ""; - this.gameServer = gameServer; - this.socket = socket; - this.nodeAdditionQueue = []; - this.nodeDestroyQueue = []; - this.visibleNodes = []; - this.collidingNodes = []; // Perfomance save; all nodes colliding with player's cells - this.cells = []; - this.mergeOverride = false; // Triggered by console command - this.score = 0; // Needed for leaderboard - - this.mouse = { - x: 0, - y: 0 - }; - this.shouldMoveCells = true; // False if the mouse packet wasn't triggered - this.notMoved = false; // If one of cells have been moved after splitting this is triggered - this.movePacketTriggered = false; - this.ignoreNextMoveTick = false; // Screen mouse matches old screen mouse - this.mouseCells = []; // For individual cell movement - this.tickLeaderboard = 0; - this.tickViewBox = 0; - this.ticksLeft = 0; // Individual updates - this.cellTicksLeft = 0; // Individual updates for cells - - this.team = 0; - this.spectate = false; - this.freeRoam = false; // Free-roam mode enables player to move in spectate mode - - // Anti-teaming - this.massDecayMult = 1; // Anti-teaming multiplier - this.Wmult = 0; // W press multiplier, which will also account on duration of effect - this.checkForWMult = false; // Prevent oveload with W multiplier - this.virusMult = 0; // Virus explosion multiplier - this.splittingMult = 0; // Splitting multiplier - - // Viewing box - this.sightRangeX = 0; - this.sightRangeY = 0; - this.centerPos = { // Center of map - x: 3000, - y: 3000 - }; - this.viewBox = { - topY: 0, - bottomY: 0, - leftX: 0, - rightX: 0, - width: 0, // Half-width - height: 0 // Half-height - }; - - // Scramble the coordinate system for anti-raga - this.scrambleX = 0; - this.scrambleY = 0; - - // Gamemode function - if (gameServer) { - // Find center - this.centerPos.x = (gameServer.config.borderLeft - gameServer.config.borderRight) / 2; - this.centerPos.y = (gameServer.config.borderTop - gameServer.config.borderBottom) / 2; - // Player id - this.pID = gameServer.getNewPlayerID(); - // Gamemode function - gameServer.gameMode.onPlayerInit(this); - // Only scramble if enabled in config - if (gameServer.config.serverScrambleCoords == 1) { - this.scrambleX = Math.floor((1 << 15) * Math.random()); - this.scrambleY = Math.floor((1 << 15) * Math.random()); - } - } -} - -module.exports = PlayerTracker; - -// Setters/Getters - -PlayerTracker.prototype.setName = function(name) { - this.name = name; -}; - -PlayerTracker.prototype.getName = function() { - return this.name; -}; - -PlayerTracker.prototype.getScore = function(reCalcScore) { - if (reCalcScore) { - var s = 0; - for (var i = 0; i < this.cells.length; i++) { - if (!this.cells[i]) return; // Error - s += this.cells[i].mass; - this.score = s; - } - } - return this.score >> 0; -}; - -PlayerTracker.prototype.setColor = function(color) { - this.color.r = color.r; - this.color.g = color.g; - this.color.b = color.b; -}; - -PlayerTracker.prototype.getTeam = function() { - return this.team; -}; - -// Functions - -PlayerTracker.prototype.update = function() { - // Async update, perfomance reasons - setTimeout(function() { - // Don't send any messages if client didn't respond with protocol version - if (this.socket.packetHandler.protocolVersion == 0) return; - - // First reset colliding nodes - this.collidingNodes = []; - - // Move packet update - if (this.movePacketTriggered) { - this.movePacketTriggered = false; - this.shouldMoveCells = true; - } else { - this.shouldMoveCells = false; - } - // Actions buffer (So that people cant spam packets) - if (this.socket.packetHandler.pressSpace) { // Split cell - if (!this.mergeOverride) this.gameServer.gameMode.pressSpace(this.gameServer, this); - this.socket.packetHandler.pressSpace = false; - } - - if (this.socket.packetHandler.pressW) { // Eject mass - this.gameServer.gameMode.pressW(this.gameServer, this); - this.socket.packetHandler.pressW = false; - this.checkForWMult = true; - } - - if (this.socket.packetHandler.pressQ) { // Q Press - this.gameServer.gameMode.pressQ(this.gameServer, this); - this.socket.packetHandler.pressQ = false; - } - - var updateNodes = []; // Nodes that need to be updated via packet - - // Remove nodes from visible nodes if possible - var d = 0; - while (d < this.nodeDestroyQueue.length) { - var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[d]); - if (index > -1) { - this.visibleNodes.splice(index, 1); - d++; // Increment - } else { - // Node was never visible anyways - this.nodeDestroyQueue.splice(d, 1); - } - } - - // Get visible nodes every 400 ms - var nonVisibleNodes = []; // Nodes that are not visible - if (this.tickViewBox <= 0) { - var newVisible = this.calcViewBox(); - try { // Add a try block in any case - - // Compare and destroy nodes that are not seen - for (var i = 0; i < this.visibleNodes.length; i++) { - var index = newVisible.indexOf(this.visibleNodes[i]); - if (index == -1) { - // Not seen by the client anymore - nonVisibleNodes.push(this.visibleNodes[i]); - } - } - - // Add nodes to client's screen if client has not seen it already - for (var i = 0; i < newVisible.length; i++) { - var index = this.visibleNodes.indexOf(newVisible[i]); - if (index == -1) { - updateNodes.push(newVisible[i]); - } - } - } catch(err) { - console.error(err); - } - - this.visibleNodes = newVisible; - // Reset Ticks - this.tickViewBox = 0; - } else { - this.tickViewBox--; - // Add nodes to screen - for (var i = 0; i < this.nodeAdditionQueue.length; i++) { - var node = this.nodeAdditionQueue[i]; - this.visibleNodes.push(node); - updateNodes.push(node); - } - } - - // Update moving nodes - for (var i = 0; i < this.visibleNodes.length; i++) { - var node = this.visibleNodes[i]; - if (node.sendUpdate()) { - // Sends an update if cell is moving - updateNodes.push(node); - } - } - - // Send packet - this.socket.sendPacket(new Packet.UpdateNodes( - this.nodeDestroyQueue, - updateNodes, - nonVisibleNodes, - this.scrambleX, - this.scrambleY - )); - - this.nodeDestroyQueue = []; // Reset destroy queue - this.nodeAdditionQueue = []; // Reset addition queue - - // Update leaderboard - if (this.tickLeaderboard <= 0) { - this.socket.sendPacket(new Packet.UpdateLeaderboard( - this.gameServer.leaderboard, - this.gameServer.gameMode.packetLB, - this.protocolVersion, - this.pID - )); - this.tickLeaderboard = 10; // 20 ticks = 1 second - } else { - this.tickLeaderboard--; - } - - // Map obfuscation - var width = this.viewBox.width; - var height = this.viewBox.height; - - if (this.cells.length == 0 && this.gameServer.config.serverScrambleMinimaps >= 1) { - // Update map, it may have changed - this.socket.sendPacket(new Packet.SetBorder( - -this.gameServer.config.borderLeft + this.scrambleX, - this.gameServer.config.borderRight + this.scrambleX, - -this.gameServer.config.borderTop + this.scrambleY, - this.gameServer.config.borderBottom + this.scrambleY - )); - } else { - // Send a border packet to fake the map size - this.socket.sendPacket(new Packet.SetBorder( - Math.max(this.centerPos.x + this.scrambleX - width, -this.gameServer.config.borderLeft + this.scrambleX), - Math.min(this.centerPos.x + this.scrambleX + width, this.gameServer.config.borderRight + this.scrambleX), - Math.max(this.centerPos.y + this.scrambleY - height, -this.gameServer.config.borderTop + this.scrambleY), - Math.min(this.centerPos.y + this.scrambleY + height, this.gameServer.config.borderBottom + this.scrambleY) - )); - } - - // Handles disconnections - if (this.disconnect > -1) { - // Player has disconnected... remove it when the timer hits -1 - this.disconnect--; - // Also remove it when its cells are completely eaten not to back up dead clients - if (this.disconnect == -1 || this.cells.length == 0) { - // Remove all client cells - var len = this.cells.length; - for (var i = 0; i < len; i++) { - var cell = this.socket.playerTracker.cells[0]; - - if (!cell) { - continue; - } - - this.gameServer.removeNode(cell); - } - - // Remove from client list - var index = this.gameServer.clients.indexOf(this.socket); - if (index != -1) { - this.gameServer.clients.splice(index, 1); - } - } - } - }.bind(this), 0); -}; - -PlayerTracker.prototype.antiTeamTick = function() { - // ANTI-TEAMING DECAY - // Calculated even if anti-teaming is disabled. - var effectSum = this.Wmult + this.virusMult + this.splittingMult; - if (this.Wmult - 0.00028 > 0) this.Wmult -= 0.00028; - this.virusMult *= 0.999; - this.splittingMult *= 0.9982; - // Apply anti-teaming if required - if (effectSum > 2) this.massDecayMult = Math.min(effectSum / 2, 3.14); - else this.massDecayMult = 1; -}; - -PlayerTracker.prototype.applyTeaming = function(x, type) { - // Called when player does an action which increases anti-teaming - var effectSum = this.Wmult + this.virusMult + this.splittingMult; - - // Applied anti-teaming is 1.5x smaller if over the threshold - var n = effectSum > 1.5 ? x : x / 1.5; - - switch (type) { - case 0: // Ejected cell - this.Wmult += n; - break; - case 1: // Virus explosion - this.virusMult += n; - break; - case 2: // Splitting - this.splittingMult += n; - break; - } -}; - -// Viewing box - -PlayerTracker.prototype.updateSightRange = function() { // For view distance - var totalSize = 1.0; - var len = this.cells.length; - - for (var i = 0; i < len; i++) { - if (!this.cells[i]) { - continue; - } - - totalSize += this.cells[i].getSize(); - } - - var factor = Math.pow(Math.min(64.0 / totalSize, 1), 0.4); - this.sightRangeX = this.gameServer.config.serverViewBaseX / factor; - this.sightRangeY = this.gameServer.config.serverViewBaseY / factor; -}; - -PlayerTracker.prototype.updateCenter = function() { // Get center of cells - var len = this.cells.length; - - if (len <= 0) return; - - var X = 0; - var Y = 0; - for (var i = 0; i < len; i++) { - // Error check - if (!this.cells[i]) { - len--; - continue; - } - var cell = this.cells[i]; - - X += cell.position.x; - Y += cell.position.y; - } - - this.centerPos.x = X / len; - this.centerPos.y = Y / len; -}; - -PlayerTracker.prototype.calcViewBox = function() { - if (this.spectate) { - // Spectate mode - return this.getSpectateNodes(); - } - - // Main function - this.updateSightRange(); - this.updateCenter(); - - // Box - this.viewBox.topY = this.centerPos.y - this.sightRangeY; - this.viewBox.bottomY = this.centerPos.y + this.sightRangeY; - this.viewBox.leftX = this.centerPos.x - this.sightRangeX; - this.viewBox.rightX = this.centerPos.x + this.sightRangeX; - this.viewBox.width = this.sightRangeX; - this.viewBox.height = this.sightRangeY; - - var newVisible = this.calcVisibleNodes(); - - return newVisible; -}; - -PlayerTracker.prototype.getSpectateNodes = function() { - var specPlayer = this.gameServer.largestClient; - - if (!this.freeRoam) { - - if (!specPlayer) return this.moveInFreeRoam(); // There are probably no players - - // Get spectate player's location and calculate zoom amount - var specZoom = Math.min(Math.sqrt(100 * specPlayer.getScore(false)), 555); - specZoom = Math.pow(Math.min(40.5 / specZoom, 1.0), 0.4); - - this.setCenterPos(specPlayer.centerPos.x, specPlayer.centerPos.y); - this.sendPosPacket(specZoom); - - return specPlayer.visibleNodes.slice(0); - } - // Behave like client is in free-roam as function didn't return nodes - return this.moveInFreeRoam(); -}; - -PlayerTracker.prototype.moveInFreeRoam = function() { - // User is in free roam - // To mimic agar.io, get distance from center to mouse and apply a part of the distance - - var dist = this.gameServer.getDist(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); - var angle = this.getAngle(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); - var speed = Math.min(dist / 10, 70); // Not to break laws of universe by going faster than light speed - - this.centerPos.x += speed * Math.sin(angle); - this.centerPos.y += speed * Math.cos(angle); - - // Check if went away from borders - this.checkBorderPass(); - - // Now that we've updated center pos, get nearby cells - // We're going to use config's view base times 2.5 - - var mult = 3.5; // To simplify multiplier, in case this needs editing later on - var baseX = this.gameServer.config.serverViewBaseX; - var baseY = this.gameServer.config.serverViewBaseY; - - this.viewBox.topY = this.centerPos.y - baseY * mult; - this.viewBox.bottomY = this.centerPos.y + baseY * mult; - this.viewBox.leftX = this.centerPos.x - baseX * mult; - this.viewBox.rightX = this.centerPos.x + baseX * mult; - this.viewBox.width = baseX * mult; - this.viewBox.height = baseY * mult; - - // Use calcViewBox's way of looking for nodes - var newVisible = this.calcVisibleNodes(); - var specZoom = 222; - specZoom = Math.pow(Math.min(40.5 / specZoom, 1.0), 0.4) * 0.6; // Constant zoom - this.sendPosPacket(specZoom); - return newVisible; -}; - -PlayerTracker.prototype.calcVisibleNodes = function() { - var newVisible = []; - for (var i = 0; i < this.gameServer.nodes.length; i++) { - var node = this.gameServer.nodes[i]; - if (!node) { - continue; - } - - var check = node.visibleCheck(this.viewBox, this.centerPos, this.cells); - if (check > 0 || node.owner == this) { - // Cell is in range of viewBox - newVisible.push(node); - // Check if it's colliding with one of player's cells - if (check == 2) this.collidingNodes.push(node); - } - } - return newVisible; -}; - -PlayerTracker.prototype.setCenterPos = function(x, y) { - this.centerPos.x = x; - this.centerPos.y = y; - if (this.freeRoam) this.checkBorderPass(); -}; - -PlayerTracker.prototype.checkBorderPass = function() { - // A check while in free-roam mode to avoid player going into nothingness - if (this.centerPos.x < -this.gameServer.config.borderLeft) { - this.centerPos.x = this.gameServer.config.borderLeft; - } - if (this.centerPos.x > this.gameServer.config.borderRight) { - this.centerPos.x = this.gameServer.config.borderRight; - } - if (this.centerPos.y < -this.gameServer.config.borderTop) { - this.centerPos.y = this.gameServer.config.borderTop; - } - if (this.centerPos.y > this.gameServer.config.borderBottom) { - this.centerPos.y = this.gameServer.config.borderBottom; - } -}; - -PlayerTracker.prototype.sendPosPacket = function(specZoom) { - // TODO: Send packet elsewhere so it is sent more often - this.socket.sendPacket(new Packet.UpdatePosition( - this.centerPos.x + this.scrambleX, - this.centerPos.y + this.scrambleY, - specZoom - )); -}; - -PlayerTracker.prototype.sendCustomPosPacket = function(x, y, specZoom) { - // TODO: Send packet elsewhere so it is sent more often - this.socket.sendPacket(new Packet.UpdatePosition( - x + this.scrambleX, - y + this.scrambleY, - specZoom - )); -}; - -PlayerTracker.prototype.getAngle = function(x1, y1, x2, y2) { - var deltaY = y1 - y2; - var deltaX = x1 - x2; - return Math.atan2(deltaX, deltaY); -}; +var Packet = require('./packet'); +var GameServer = require('./GameServer'); + +function PlayerTracker(gameServer, socket) { + this.pID = -1; + this.disconnect = -1; // Disconnection + this.name = ""; + this.gameServer = gameServer; + this.socket = socket; + this.nodeAdditionQueue = []; + this.nodeDestroyQueue = []; + this.visibleNodes = []; + this.collidingNodes = []; // Perfomance save; all nodes colliding with player's cells + this.cells = []; + this.mergeOverride = false; // Triggered by console command + this.score = 0; // Needed for leaderboard + + this.mouse = { + x: 0, + y: 0 + }; + this.shouldMoveCells = true; // False if the mouse packet wasn't triggered + this.notMoved = false; // If one of cells have been moved after splitting this is triggered + this.movePacketTriggered = false; + this.mouseCells = []; // For individual cell movement + this.tickLeaderboard = 0; + this.tickViewBox = 0; + + this.team = 0; + this.spectate = false; + this.freeRoam = false; // Free-roam mode enables player to move in spectate mode + + // Viewing box + this.sightRangeX = 0; + this.sightRangeY = 0; + this.centerPos = { // Center of map + x: 3000, + y: 3000 + }; + this.viewBox = { + topY: 0, + bottomY: 0, + leftX: 0, + rightX: 0, + width: 0, // Half-width + height: 0 // Half-height + }; + + // Scramble the coordinate system for anti-raga + this.scrambleX = 0; + this.scrambleY = 0; + + // Gamemode function + if (gameServer) { + // Find center + this.centerPos.x = (gameServer.config.borderLeft - gameServer.config.borderRight) / 2; + this.centerPos.y = (gameServer.config.borderTop - gameServer.config.borderBottom) / 2; + // Player id + this.pID = gameServer.getNewPlayerID(); + // Gamemode function + gameServer.gameMode.onPlayerInit(this); + // Only scramble if enabled in config + if (gameServer.config.serverScrambleCoords == 1) { + this.scrambleX = Math.floor((1 << 15) * Math.random()); + this.scrambleY = Math.floor((1 << 15) * Math.random()); + } + } +} + +module.exports = PlayerTracker; + +// Setters/Getters + +PlayerTracker.prototype.setName = function(name) { + this.name = name; +}; + +PlayerTracker.prototype.getName = function() { + return this.name; +}; + +PlayerTracker.prototype.getScore = function(reCalcScore) { + if (reCalcScore) { + var s = 0; + for (var i = 0; i < this.cells.length; i++) { + if (!this.cells[i]) return; // Error + s += this.cells[i].mass; + this.score = s; + } + } + return this.score >> 0; +}; + +PlayerTracker.prototype.setColor = function(color) { + this.color.r = color.r; + this.color.g = color.g; + this.color.b = color.b; +}; + +PlayerTracker.prototype.getTeam = function() { + return this.team; +}; + +// Functions + +PlayerTracker.prototype.update = function() { + // if initialization is not complete yet then do not update + if (this.socket.packetHandler.protocol == 0) + return; + + //// Async update, perfomance reasons + //setTimeout(function() { + // First reset colliding nodes + this.collidingNodes = []; + + // Move packet update + if (this.movePacketTriggered) { + this.movePacketTriggered = false; + this.shouldMoveCells = true; + } else { + this.shouldMoveCells = false; + } + // Actions buffer (So that people cant spam packets) + if (this.socket.packetHandler.pressSpace) { // Split cell + if (!this.mergeOverride) this.gameServer.gameMode.pressSpace(this.gameServer, this); + this.socket.packetHandler.pressSpace = false; + } + + if (this.socket.packetHandler.pressW) { // Eject mass + this.gameServer.gameMode.pressW(this.gameServer, this); + this.socket.packetHandler.pressW = false; + } + + if (this.socket.packetHandler.pressQ) { // Q Press + this.gameServer.gameMode.pressQ(this.gameServer, this); + this.socket.packetHandler.pressQ = false; + } + + var updateNodes = []; // Nodes that need to be updated via packet + + // Remove nodes from visible nodes if possible + var d = 0; + while (d < this.nodeDestroyQueue.length) { + var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[d]); + if (index > -1) { + this.visibleNodes.splice(index, 1); + d++; // Increment + } else { + // Node was never visible anyways + this.nodeDestroyQueue.splice(d, 1); + } + } + + // Get visible nodes every 400 ms + var nonVisibleNodes = []; // Nodes that are not visible + if (this.tickViewBox <= 0) { + var newVisible = this.calcViewBox(); + try { // Add a try block in any case + + // Compare and destroy nodes that are not seen + for (var i = 0; i < this.visibleNodes.length; i++) { + var index = newVisible.indexOf(this.visibleNodes[i]); + if (index == -1) { + // Not seen by the client anymore + nonVisibleNodes.push(this.visibleNodes[i]); + } + } + + // Add nodes to client's screen if client has not seen it already + for (var i = 0; i < newVisible.length; i++) { + var index = this.visibleNodes.indexOf(newVisible[i]); + if (index == -1) { + updateNodes.push(newVisible[i]); + } + } + } catch(err) { + console.error(err); + } + + this.visibleNodes = newVisible; + // Reset Ticks + this.tickViewBox = 0; + } else { + this.tickViewBox--; + // Add nodes to screen + for (var i = 0; i < this.nodeAdditionQueue.length; i++) { + var node = this.nodeAdditionQueue[i]; + this.visibleNodes.push(node); + updateNodes.push(node); + } + } + + // Update moving nodes + for (var i = 0; i < this.visibleNodes.length; i++) { + var node = this.visibleNodes[i]; + if (node.sendUpdate()) { + // Sends an update if cell is moving + updateNodes.push(node); + } + } + + // Send packet + this.socket.sendPacket(new Packet.UpdateNodes( + this.nodeDestroyQueue, + updateNodes, + nonVisibleNodes, + this.scrambleX, + this.scrambleY + )); + + this.nodeDestroyQueue = []; // Reset destroy queue + this.nodeAdditionQueue = []; // Reset addition queue + + // Update leaderboard + if (this.tickLeaderboard <= 0) { + if (this.gameServer.lb_packet != null) + this.socket.sendPacket(this.gameServer.lb_packet); + this.tickLeaderboard = 10; // 20 ticks = 1 second + } else { + this.tickLeaderboard--; + } + + // Handles disconnections + if (this.disconnect > -1) { + // Player has disconnected... remove it when the timer hits -1 + this.disconnect--; + // Also remove it when its cells are completely eaten not to back up dead clients + if (this.disconnect == -1 || this.cells.length == 0) { + // Remove all client cells + var len = this.cells.length; + for (var i = 0; i < len; i++) { + var cell = this.socket.playerTracker.cells[0]; + + if (!cell) { + continue; + } + + this.gameServer.removeNode(cell); + } + + // Remove from client list + var index = this.gameServer.clients.indexOf(this.socket); + if (index != -1) { + this.gameServer.clients.splice(index, 1); + } + } + } + //}.bind(this), 0); +}; + +// Viewing box + +PlayerTracker.prototype.updateSightRange = function() { // For view distance + var totalSize = 1.0; + var len = this.cells.length; + + for (var i = 0; i < len; i++) { + if (!this.cells[i]) { + continue; + } + + totalSize += this.cells[i].getSize(); + } + + var factor = Math.pow(Math.min(64.0 / totalSize, 1), 0.4); + this.sightRangeX = this.gameServer.config.serverViewBaseX / factor; + this.sightRangeY = this.gameServer.config.serverViewBaseY / factor; +}; + +PlayerTracker.prototype.updateCenter = function() { // Get center of cells + var len = this.cells.length; + + if (len <= 0) { + return; // End the function if no cells exist + } + + var X = 0; + var Y = 0; + var allSize = 0; // Focus larger cells to near the center and smaller away + for (var i = 0; i < len; i++) { + // Error check + if (!this.cells[i]) continue; + var cell = this.cells[i]; + + X += cell.position.x * cell.mass; + Y += cell.position.y * cell.mass; + allSize += cell.mass; + } + + this.centerPos.x = X / allSize; + this.centerPos.y = Y / allSize; +}; + +PlayerTracker.prototype.calcViewBox = function() { + if (this.spectate) { + // Spectate mode + return this.getSpectateNodes(); + } + + // Main function + this.updateSightRange(); + this.updateCenter(); + + // Box + this.viewBox.topY = this.centerPos.y - this.sightRangeY; + this.viewBox.bottomY = this.centerPos.y + this.sightRangeY; + this.viewBox.leftX = this.centerPos.x - this.sightRangeX; + this.viewBox.rightX = this.centerPos.x + this.sightRangeX; + this.viewBox.width = this.sightRangeX; + this.viewBox.height = this.sightRangeY; + + var newVisible = this.calcVisibleNodes(); + + return newVisible; +}; + +PlayerTracker.prototype.getSpectateNodes = function() { + var specPlayer = this.gameServer.largestClient; + + if (!this.freeRoam) { + + if (!specPlayer) return this.moveInFreeRoam(); // There are probably no players + + // Get spectate player's location and calculate zoom amount + var specZoom = Math.min(Math.sqrt(100 * specPlayer.getScore(false)), 555); + specZoom = Math.pow(Math.min(40.5 / specZoom, 1.0), 0.4) * 0.8; + + this.setCenterPos(specPlayer.centerPos.x, specPlayer.centerPos.y); + this.sendPosPacket(specZoom); + + return specPlayer.visibleNodes.slice(0); + } + // Behave like client is in free-roam as function didn't return nodes + return this.moveInFreeRoam(); +}; + +PlayerTracker.prototype.moveInFreeRoam = function() { + // User is in free roam + // To mimic agar.io, get distance from center to mouse and apply a part of the distance + + var dist = this.gameServer.getDist(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); + var angle = this.getAngle(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); + var speed = Math.min(dist / 10, 70); // Not to break laws of universe by going faster than light speed + + this.centerPos.x += speed * Math.sin(angle); + this.centerPos.y += speed * Math.cos(angle); + + // Check if went away from borders + this.checkBorderPass(); + + // Now that we've updated center pos, get nearby cells + // We're going to use config's view base times 2.5 + + var mult = 3.5; // To simplify multiplier, in case this needs editing later on + var baseX = this.gameServer.config.serverViewBaseX; + var baseY = this.gameServer.config.serverViewBaseY; + + this.viewBox.topY = this.centerPos.y - baseY * mult; + this.viewBox.bottomY = this.centerPos.y + baseY * mult; + this.viewBox.leftX = this.centerPos.x - baseX * mult; + this.viewBox.rightX = this.centerPos.x + baseX * mult; + this.viewBox.width = baseX * mult; + this.viewBox.height = baseY * mult; + + // Use calcViewBox's way of looking for nodes + var newVisible = this.calcVisibleNodes(); + var specZoom = 222; + specZoom = Math.pow(Math.min(40.5 / specZoom, 1.0), 0.4) * 0.6; // Constant zoom + this.sendPosPacket(specZoom); + return newVisible; +}; + +PlayerTracker.prototype.calcVisibleNodes = function() { + var newVisible = []; + for (var i = 0; i < this.gameServer.nodes.length; i++) { + var node = this.gameServer.nodes[i]; + if (!node) { + continue; + } + + var check = node.visibleCheck(this.viewBox, this.centerPos, this.cells); + if (check > 0 || node.owner == this) { + // Cell is in range of viewBox + newVisible.push(node); + // Check if it's colliding with one of player's cells + if (check == 2) this.collidingNodes.push(node); + } + } + return newVisible; +}; + +PlayerTracker.prototype.setCenterPos = function(x, y) { + this.centerPos.x = x; + this.centerPos.y = y; + if (this.freeRoam) this.checkBorderPass(); +}; + +PlayerTracker.prototype.checkBorderPass = function() { + // A check while in free-roam mode to avoid player going into nothingness + if (this.centerPos.x < -this.gameServer.config.borderLeft) { + this.centerPos.x = this.gameServer.config.borderLeft; + } + if (this.centerPos.x > this.gameServer.config.borderRight) { + this.centerPos.x = this.gameServer.config.borderRight; + } + if (this.centerPos.y < -this.gameServer.config.borderTop) { + this.centerPos.y = this.gameServer.config.borderTop; + } + if (this.centerPos.y > this.gameServer.config.borderBottom) { + this.centerPos.y = this.gameServer.config.borderBottom; + } +}; + +PlayerTracker.prototype.sendPosPacket = function(specZoom) { + // TODO: Send packet elsewhere so it is sent more often + this.socket.sendPacket(new Packet.UpdatePosition( + this.centerPos.x + this.scrambleX, + this.centerPos.y + this.scrambleY, + specZoom + )); +}; + +PlayerTracker.prototype.sendCustomPosPacket = function(x, y, specZoom) { + // TODO: Send packet elsewhere so it is sent more often + this.socket.sendPacket(new Packet.UpdatePosition( + x + this.scrambleX, + y + this.scrambleY, + specZoom + )); +}; + +PlayerTracker.prototype.getAngle = function(x1, y1, x2, y2) { + var deltaY = y1 - y2; + var deltaX = x1 - x2; + return Math.atan2(deltaX, deltaY); +}; diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 738362322..587f43bf9 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -1,7 +1,6 @@ function Cell(nodeId, owner, position, mass, gameServer) { this.nodeId = nodeId; this.owner = owner; // playerTracker that owns this cell - this.ticksLeft = 0; // Individual updates this.color = { r: 0, g: 255, @@ -15,6 +14,7 @@ function Cell(nodeId, owner, position, mass, gameServer) { this.killedBy; // Cell that ate this cell this.gameServer = gameServer; + this.moveEngineTicks = 0; // Amount of times to loop the movement function this.moveEngineSpeed = 0; this.moveDecay = 0.85; this.angle = 0; // Angle of movement @@ -26,11 +26,8 @@ module.exports = Cell; // Fields not defined by the constructor are considered private and need a getter/setter to access from a different class Cell.prototype.getName = function() { - if (this.owner) { - return this.owner.name; - } else { + if (this.owner) return this.owner.name; return ""; - } }; Cell.prototype.setColor = function(color) { @@ -70,13 +67,9 @@ Cell.prototype.addMass = function(n) { }; Cell.prototype.getSpeed = function() { - // Based on 50ms ticks. If updateMoveEngine interval changes, change 50 to new value - // (should possibly have a config value for this?) - - // Old formulas: - // return 5 + (20 * (1 - (this.mass/(70+this.mass)))); - // return this.gameServer.config.playerSpeed * Math.pow(this.mass, -0.22) * 50 / 40; - return this.gameServer.config.playerSpeed * Math.pow(this.mass, -Math.PI / (Math.PI * Math.PI) / 1.5); + var t = Math.PI * Math.PI; + return this.gameServer.config.playerSpeed * + Math.pow(this.getSize(), -0.01 / (Math.PI * Math.PI * Math.PI)); //-Math.PI / t);//1.5); }; Cell.prototype.setAngle = function(radians) { @@ -87,8 +80,9 @@ Cell.prototype.getAngle = function() { return this.angle; }; -Cell.prototype.setMoveEngineData = function(speed, decay) { +Cell.prototype.setMoveEngineData = function(speed, ticks, decay) { this.moveEngineSpeed = speed; + this.moveEngineTicks = ticks; this.moveDecay = isNaN(decay) ? 0.75 : decay; }; @@ -157,13 +151,10 @@ Cell.prototype.visibleCheck = function(box, centerPos, cells) { var cell = cells[i]; if (!cell) continue; - var xs = this.position.x - cell.position.x; - var ys = this.position.y - cell.position.y; - var sqDist = xs * xs + ys * ys; - - var collideDist = cell.getSquareSize() + this.getSquareSize(); + var dist = this.getDist(this.position.x, this.position.y, cell.position.x, cell.position.y); + var collideDist = cell.getSize() + this.getSize(); - if (sqDist < collideDist) { + if (dist < collideDist) { return 2; }// Colliding with one } @@ -174,12 +165,14 @@ Cell.prototype.visibleCheck = function(box, centerPos, cells) { Cell.prototype.calcMovePhys = function(config) { // Move, twice as slower - var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle)); - var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle)); + var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0); + var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0); // Movement engine + if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) this.moveEngineSpeed = 0; var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay; this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower + if (this.moveEngineTicks >= 0.5) this.moveEngineTicks -= 0.5; // Ticks passing twice as slower // Ejected cell collision if (this.cellType == 3) { @@ -196,6 +189,10 @@ Cell.prototype.calcMovePhys = function(config) { var deltaX = this.position.x - check.position.x; var deltaY = this.position.y - check.position.y; var angle = Math.atan2(deltaX, deltaY); + + this.gameServer.setAsMovingNode(check); + check.moveEngineTicks += 1; + this.moveEngineTicks += 1; var move = allowDist - dist; @@ -229,8 +226,8 @@ Cell.prototype.calcMovePhys = function(config) { } // Set position - this.position.x = X; - this.position.y = Y; + this.position.x = X >> 0; + this.position.y = Y >> 0; }; // Override these diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 7653e7e73..e24d7911f 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -28,7 +28,8 @@ PlayerCell.prototype.calcMergeTime = function(base) { // Instant recombine in config or merge command was triggered for this client r = true; } else { - var rec = Math.floor(base + ((0.02 * this.mass))); // base seconds + 0.02% of mass + var rec = Math.max(30, 0.02 * this.getSize()); + rec = Math.max(base, rec); if (this.recombineTicks > rec) r = true; // Can combine with other cells } this.shouldRecombine = r; diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 96072feba..9256c10d3 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -15,10 +15,9 @@ Virus.prototype = new Cell(); Virus.prototype.calcMove = null; // Only for player controlled movement Virus.prototype.feed = function(feeder, gameServer) { - if (this.moveEngineSpeed <= 1) this.setAngle(feeder.getAngle()); // Set direction if the virus explodes + if (this.moveEngineTicks == 0) this.setAngle(feeder.getAngle()); // Set direction if the virus explodes this.mass += feeder.mass; this.fed++; // Increase feed count - feeder.setKiller(this); gameServer.removeNode(feeder); // Check if the virus is going to explode @@ -79,19 +78,18 @@ Virus.prototype.onConsume = function(consumer, gameServer) { numSplits -= bigSplits.length; for (var k = 0; k < bigSplits.length; k++) { - var angle = Math.random() * 6.28; // Random directions + angle = Math.random() * 6.28; // Random directions gameServer.createPlayerCell(client, consumer, angle, bigSplits[k]); } // Splitting for (var k = 0; k < numSplits; k++) { - var angle = Math.random() * 6.28; // Random directions + angle = Math.random() * 6.28; // Random directions gameServer.createPlayerCell(client, consumer, angle, splitMass); } // Prevent consumer cell from merging with other cells consumer.calcMergeTime(gameServer.config.playerRecombineTime); - client.applyTeaming(1.2, 1); // Apply anti-teaming }; Virus.prototype.onAdd = function(gameServer) { diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 9e073f9b7..f80c4fc61 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -98,14 +98,15 @@ Experimental.prototype.onServerInit = function(gameServer) { Virus.prototype.feed = function(feeder, gameServer) { gameServer.removeNode(feeder); // Pushes the virus - // Effect on angle is smaller with larger move engine speed - var angle = this.getAngle() + feeder.getAngle(), - effect = Math.sqrt(this.moveEngineSpeed + 1); - angle += feeder.getAngle() / effect; - angle /= (1 + effect); - this.setAngle(angle); // Set direction if the virus explodes - this.moveEngineSpeed += 10; - this.moveDecay = 0.9; + this.setAngle(feeder.getAngle()); // Set direction if the virus explodes + this.moveEngineTicks += 20; // Amount of times to loop the movement function + this.moveEngineSpeed += 16; + this.moveDecay = 0.875; + + var index = gameServer.movingNodes.indexOf(this); + if (index == -1) { + gameServer.movingNodes.push(this); + } }; // Override this @@ -197,9 +198,9 @@ MotherCell.prototype.checkEat = function(gameServer) { this.checkEatCell(check, safeMass, gameServer); } - // Check ejected cells - for (var i in gameServer.nodesEjected) { - var check = gameServer.nodesEjected[i]; + // Check moving nodes + for (var i in gameServer.movingNodes) { + var check = gameServer.movingNodes[i]; this.checkEatCell(check, safeMass, gameServer); } }; @@ -215,7 +216,6 @@ MotherCell.prototype.checkEatCell = function(check, safeMass, gameServer) { var allowDist = this.getSize() - check.getEatingRange(); if (dist < allowDist) { // Eat it - check.setKiller(this); gameServer.removeNode(check); this.mass += check.mass; } @@ -245,7 +245,9 @@ MotherCell.prototype.spawnFood = function(gameServer) { // Move engine f.angle = angle; var dist = (Math.random() * 8) + 8; // Random distance - f.setMoveEngineData(dist, 0.9); + f.setMoveEngineData(dist, 20, 0.85); + + gameServer.setAsMovingNode(f); }; MotherCell.prototype.onConsume = Virus.prototype.onConsume; // Copies the virus prototype function diff --git a/src/gameserver.ini b/src/gameserver.ini index f8613d15b..8cbe313c3 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -9,8 +9,6 @@ // serverStatsUpdate: Amount of seconds per update for server stats // serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections // serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. -// serverScrambleMinimaps: Toggles scrambling of borders to render maps unusable. 0 = No scrambling, 1 = scrambling. Default is 1. -// serverTeamingAllowed: Toggles anti-teaming. 0 = Anti-team enabled, 1 = Anti-team disabled. Default is 1. // serverMaxLB: Controls the maximum players displayed on the leaderboard. serverMaxConnections = 64 serverPort = 443 @@ -22,8 +20,6 @@ serverStatsPort = 88 serverStatsUpdate = 60 serverLogLevel = 1 serverScrambleCoords = 1 -serverScrambleMinimaps = 1 -serverTeamingAllowed = 1 serverMaxLB = 10 // [Border] diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 41fbc3ede..e49a5a3b3 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -46,7 +46,6 @@ Commands.list = { console.log("[Console] playerlist : get list of players and bots"); console.log("[Console] pause : pause game , freeze all cells"); console.log("[Console] reload : reload config"); - console.log("[Console] resetantiteam [PlayerID] : reset anti-team effect on client"); console.log("[Console] status : get server status"); console.log("[Console] tp [PlayerID] [X] [Y] : teleport player to specified location"); console.log("[Console] virus [X] [Y] [mass] : spawn virus at a specified Location"); @@ -61,7 +60,7 @@ Commands.list = { } for (var i = 0; i < add; i++) { - setTimeout(gameServer.bots.addBot.bind(gameServer.bots), i); + gameServer.bots.addBot(); } console.log("[Console] Added " + add + " player bots"); }, @@ -436,29 +435,6 @@ Commands.list = { gameServer.loadConfig(); console.log("[Console] Reloaded the config file successfully"); }, - resetantiteam: function(gameServer, split) { - // Validation checks - var id = parseInt(split[1]); - if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID!"); - return; - } - - for (var i in gameServer.clients) { - var client = gameServer.clients[i]; - if (!client) continue; // Nonexistent - - if (client.playerTracker.pID == id) { - // Found client - client.playerTracker.massDecayMult = 1; - client.playerTracker.Wmult = 0; - client.playerTracker.virusMult = 0; - client.playerTracker.splittingMult = 0; - console.log("[Console] Successfully reset client's anti-team effect"); - return; - } - } - }, status: function(gameServer, split) { // Get amount of humans/bots var humans = 0, diff --git a/src/packet/AddNode.js b/src/packet/AddNode.js index e49a24bbd..5689f34ea 100644 --- a/src/packet/AddNode.js +++ b/src/packet/AddNode.js @@ -1,16 +1,13 @@ -function AddNode(item) { - this.item = item; -} - -module.exports = AddNode; - -AddNode.prototype.build = function() { - // Only add player controlled cells with this packet or it will bug the camera - var buf = new ArrayBuffer(5); - var view = new DataView(buf); - - view.setUint8(0, 32, true); - view.setUint32(1, this.item.nodeId, true); - - return buf; -}; +function AddNode(item) { + this.item = item; +} + +module.exports = AddNode; + +AddNode.prototype.build = function(protocol) { + // Only add player controlled cells with this packet or it will bug the camera + var buffer = new Buffer(5); + buffer.writeUInt8(0x20, 0); // Packet ID + buffer.writeUInt32LE(this.item.nodeId, 1); + return buffer; +}; diff --git a/src/packet/ClearNodes.js b/src/packet/ClearNodes.js index dece8501f..2018b3deb 100644 --- a/src/packet/ClearNodes.js +++ b/src/packet/ClearNodes.js @@ -1,14 +1,9 @@ -function ClearNodes(protocolVersion) { - this.protocolVersion = protocolVersion; -} - -module.exports = ClearNodes; - -ClearNodes.prototype.build = function() { - var buf = new ArrayBuffer(1); - var view = new DataView(buf); - - view.setUint8(0, this.protocolVersion == 5 ? 20 : 18); - - return buf; -}; +function ClearNodes() { } + +module.exports = ClearNodes; + +ClearNodes.prototype.build = function (protocol) { + var buffer = new Buffer(1); + buffer.writeUInt8(protocol>=6 ? 0x12 : 0x14, 0); + return buffer; +}; \ No newline at end of file diff --git a/src/packet/DrawLine.js b/src/packet/DrawLine.js index 90d7260b8..3e00b2183 100644 --- a/src/packet/DrawLine.js +++ b/src/packet/DrawLine.js @@ -1,17 +1,14 @@ -function DrawLine(x, y) { - this.x = x; - this.y = y; -} - -module.exports = DrawLine; - -DrawLine.prototype.build = function() { - var buf = new ArrayBuffer(5); - var view = new DataView(buf); - - view.setUint8(0, 21, true); - view.setUint16(1, this.x, true); - view.setUint16(3, this.y, true); - - return buf; -}; +function DrawLine(x, y) { + this.x = x; + this.y = y; +} + +module.exports = DrawLine; + +DrawLine.prototype.build = function(protocol) { + var buffer = new Buffer(5); + buffer.writeUInt8(0x15, 0); + buffer.writeInt16LE(this.x, 1); + buffer.writeInt16LE(this.y, 3); + return buffer; +}; diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index 08ed36b39..acd4bad4f 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -1,21 +1,34 @@ -function SetBorder(left, right, top, bottom) { - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; -} - -module.exports = SetBorder; - -SetBorder.prototype.build = function() { - var buf = new ArrayBuffer(33); - var view = new DataView(buf); - - view.setUint8(0, 64, true); - view.setFloat64(1, this.left, true); - view.setFloat64(9, this.top, true); - view.setFloat64(17, this.right, true); - view.setFloat64(25, this.bottom, true); - - return buf; -}; +// Import +var ByteBuffer = require("bytebuffer"); + + +function SetBorder(left, right, top, bottom, gameType, serverName) { + this.left = left; + this.right = right; + this.top = top; + this.bottom = bottom; + this.gameType = gameType; + this.serverName = serverName; +} + +module.exports = SetBorder; + +SetBorder.prototype.build = function(protocol) { + var buffer = new ByteBuffer(); + buffer.LE(true); + buffer.writeUInt8(0x40); + buffer.writeDouble(this.left); + buffer.writeDouble(this.top); + buffer.writeDouble(this.right); + buffer.writeDouble(this.bottom); + if (typeof this.gameType != "undefined") { + buffer.writeUInt32(this.gameType >> 0); + var name = this.serverName; + if (typeof name == "undefined" || name == null) { + name = ""; + } + buffer.writeUTF8String(name); + buffer.writeByte(0); + } + return buffer.buffer.slice(0, buffer.offset); +}; diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index 60982ea90..9397bb69a 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -1,66 +1,122 @@ -var DynamicBuffer = require('./DynamicBuffer'); - -function UpdateLeaderboard(leaderboard, packetLB, protocolVersion, sendingUser) { - this.leaderboard = leaderboard; - this.packetLB = packetLB; - this.protocolVersion = protocolVersion; - this.sendingUser = sendingUser; -} - -module.exports = UpdateLeaderboard; - -UpdateLeaderboard.prototype.build = function() { - var buffer = new DynamicBuffer(true); - - switch (this.packetLB) { - case 48: - // Custom text list - buffer.setUint8(48); // Packet ID - buffer.setUint32(this.leaderboard.length); // String amount - - for (var i = 0; i < this.leaderboard.length; i++) { - if (this.protocolVersion != 5) { - buffer.setStringUTF8( // UTF-8 string - this.leaderboard[i] ? this.leaderboard[i] : ""); - buffer.setUint8(0); // UTF-8 null terminator - } else { - buffer.setStringUnicode( // Unicode string - this.leaderboard[i] ? this.leaderboard[i] : ""); - buffer.setUint16(0); // Unicode null terminator - } - } - break; - case 49: - // FFA leaderboard list - buffer.setUint8(49); // Packet ID - buffer.setUint32(this.leaderboard.length); // Player amount - for (var i = 0; i < this.leaderboard.length; i++) { - var player = this.leaderboard[i]; - var name = player.getName(); - name = name ? name : ""; - if (this.protocolVersion != 5) { - var isMe = player.pID == this.sendingUser ? 1 : 0; - buffer.setUint32(isMe); // If to display red color text - buffer.setStringUTF8(name); // UTF-8 string - buffer.setUint8(0); // UTF-8 null terminator - } else { - if (player.cells[0]) - buffer.setUint32(player.cells[0].nodeId); // First cell node ID - else buffer.setUint32(0); // In case of error - buffer.setStringUnicode(name); // Unicode string - buffer.setUint16(0); // Unicode null terminator - } - } - break; - case 50: - // Pie chart - buffer.setUint8(50); // Packet ID - buffer.setUint32(this.leaderboard.length); // Color amount - for (var i = 0; i < this.leaderboard.length; i++) { - buffer.setFloat32(this.leaderboard[i]); // A color's size - } - break; - } - - return buffer.build(); -}; +function UpdateLeaderboard(leaderboard, packetLB) { + this.leaderboard = leaderboard; + this.packetLB = packetLB; +} + +module.exports = UpdateLeaderboard; + +UpdateLeaderboard.prototype.build = function (protocol) { + var lb = this.leaderboard; + switch (this.packetLB) { + case 48:// Custom Text List? + { + var offset = 0; + var buffer = new Buffer(0x10000); + + + buffer.writeUInt8(48, offset); + offset++; + + var countOffset = offset; + offset += 4; + + + var count = 0; + + for (var i = 0; i < lb.length; i++) { + if (typeof lb[i] == "undefined") + continue; + var item = lb[i]; + + var name = item; + name = name ? name : ""; + + buffer.writeUInt32LE(0, offset); + offset += 4; + + if (protocol <= 5) + offset += buffer.write(name, offset, 'ucs2'); // string => unicode + else + offset += buffer.write(name, offset); // string => utf8 + buffer.writeUInt8(0, offset); // string zero terminator + offset++; + + count++; + } + buffer.writeUInt32LE(count, countOffset); // Number of elements + return buffer.slice(0, offset); + } + break; + + case 49:// FFA + { + var offset = 0; + var buffer = new Buffer(0x10000); + + buffer.writeUInt8(49, offset); // Packet ID + offset += 1; + + + var countOffset = offset; + offset += 4; + + var count = 0; + for (var i = 0; i < lb.length; i++) { + if (typeof lb[i] == "undefined") + continue; + var item = lb[i]; + + var isMe = false; // 1 for red color (current player), 0 for white color (other players) + var name = item.getName(); + name = name ? name : ""; + + // Write record + buffer.writeUInt32LE(isMe ? 1:0, offset); // isMe flag (previously cell ID) + offset += 4; + + if (protocol <= 5) + offset += buffer.write(name, offset, 'ucs2'); // string => unicode + else + offset += buffer.write(name, offset); // string => utf8 + buffer.writeUInt8(0, offset); // string zero terminator + offset += 1; + + count++; + } + buffer.writeUInt32LE(count, countOffset); // Number of elements + return buffer.slice(0, offset); + } + break; + + case 50:// (Team) Leaderboard Update + { + var offset = 0; + var buffer = new Buffer(0x10000); + + buffer.writeUInt8(50, offset); // Packet ID + offset++; + + var countOffset = offset; + offset += 4; + + var count = 0; + for (var i = 0; i < lb.length; i++) { + + var value = lb[i]; + + // little validation + value = value < 0 ? 0 : value; + value = value > 1 ? 1 : value; + + buffer.writeFloatLE(0, offset); // string zero terminator + offset += 4; + } + buffer.writeUInt32LE(count, countOffset); // Number of elements + return buffer.slice(0, offset); + } + break; + + default: + break; + } +}; \ No newline at end of file diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index 4c10804a0..d6394956d 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -1,90 +1,275 @@ -var DynamicBuffer = require('./DynamicBuffer'); - -function UpdateNodes(destroyQueue, nodes, nonVisibleNodes, scrambleX, scrambleY, protocolVersion) { - this.destroyQueue = destroyQueue; - this.nodes = nodes; - this.nonVisibleNodes = nonVisibleNodes; - this.scrambleX = scrambleX; - this.scrambleY = scrambleY; - this.protocolVersion = protocolVersion; -} - -module.exports = UpdateNodes; - -UpdateNodes.prototype.build = function() { - var buffer = new DynamicBuffer(true); // Little endian included - - buffer.setUint8(16); // Packet ID - - // Check for invalid nodes in any case - var deadCells = []; - for (var i = 0; i < this.destroyQueue.length; i++) { - deadCells.push(this.destroyQueue[i]); - } - - buffer.setUint16(deadCells.length); // Eat actions length - for (var i = 0; i < deadCells.length; i++) { - var node = deadCells[i]; - var id = 0; - if (node.getKiller()) id = node.getKiller().nodeId; - buffer.setUint32(id); // Eaten ID - buffer.setUint32(node.nodeId); // Eater ID - } - - for (var i = 0; i < this.nodes.length; i++) { // Update nodes - var node = this.nodes[i]; - - if (node.nodeId == 0) continue; // Error! - buffer.setUint32(node.nodeId); // Node ID - buffer.setInt32(node.position.x + this.scrambleX); // Node's X pos - buffer.setInt32(node.position.y + this.scrambleY); // Node's Y pos - buffer.setUint16(node.getSize()); // Node size - - var flags = 0; - - if (this.protocolVersion != 5) { - // Flags - if (node.getName() != null && node.getName() != "") flags += 8; - flags += 2; - if (node.spiked) flags += 1; - - buffer.setUint8(flags); // Node's update flags - buffer.setUint8(node.color.r); // Node's R color - buffer.setUint8(node.color.g); // Node's G color - buffer.setUint8(node.color.b); // Node's B color - if (node.getName() != null && node.getName() != "") { - buffer.setStringUTF8(node.getName()); // Node's name - buffer.setUint8(0); // Node name terminator - } - } else { - // Flags - if (node.spiked) flags += 1; - - buffer.setUint8(node.color.r); // Node's R color - buffer.setUint8(node.color.g); // Node's G color - buffer.setUint8(node.color.b); // Node's B color - buffer.setUint8(flags); // Node's update flags - if (node.getName() != null && node.getName() != "") { - buffer.setStringUnicode(node.getName()); // Node's name - } - buffer.setUint8(0); // Node name terminator - } - } - buffer.setUint32(0); // Update nodes end - - // Add non-visible cells to the "dead cells" list - for (var i = 0; i < this.nonVisibleNodes.length; i++) { - deadCells.push(this.nonVisibleNodes[i]); - } - - if (this.protocolVersion != 5) { - buffer.setUint16(deadCells.length); // Remove actions length - } else { - buffer.setUint32(deadCells.length); // Remove actions length - } - for (var i = 0; i < deadCells.length; i++) { - buffer.setUint32(deadCells[i].nodeId); // Removing node's ID - } - - return buffer.build(); -}; +// Import +var ByteBuffer = require("bytebuffer"); + + +function UpdateNodes(destroyQueue, nodes, nonVisibleNodes, scrambleX, scrambleY) { + this.destroyQueue = destroyQueue; + this.nodes = nodes; + this.nonVisibleNodes = nonVisibleNodes; + this.scrambleX = scrambleX; + this.scrambleY = scrambleY; +} + +module.exports = UpdateNodes; + +UpdateNodes.prototype.build = function (protocol) { + if (!protocol) return null; + switch (protocol) { + case 4: return this.build4(); + case 5: return this.build5(); + case 6: + case 7: return this.build6(); + } + return null; +} + +// protocol 4 +UpdateNodes.prototype.build4 = function () { + var buffer = new ByteBuffer(); + buffer.LE(true); + + var deadCells = []; + for (var i = 0; i < this.destroyQueue.length; i++) { + var node = this.destroyQueue[i]; + if (!node) continue; + deadCells.push(node); + } + + buffer.writeUInt8(0x10); + buffer.writeUInt16(deadCells.length); // EatRecordCount + for (var i = 0; i < deadCells.length; i++) { + var node = deadCells[i]; + var hunterId = 0; + if (node.getKiller()) { + hunterId = node.getKiller().nodeId; + } + buffer.writeUInt32(hunterId); // Hunter ID + buffer.writeUInt32(node.nodeId); // Prey ID + } + + for (var i = 0; i < this.nodes.length; i++) { + var node = this.nodes[i]; + if (typeof node == "undefined" || !node || node.nodeId == 0) + continue; + + var cellX = node.position.x + this.scrambleX; + var cellY = node.position.y + this.scrambleY; + var cellSize = node.getSize(); + var cellName = node.getName(); + if (!cellName) cellName = ""; + + + var isVirus = (node.spiked & 0x01) != 0; + var isAgitated = false; // true = high wave amplitude on a cell outline + var isEject = node.cellType == 3; + + // Write update record + buffer.writeUInt32(node.nodeId); // Cell ID + buffer.writeInt16(cellX >> 0); // Coordinate X + buffer.writeInt16(cellY >> 0); // Coordinate Y + buffer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + buffer.writeUInt8(node.color.r >> 0); // Color R + buffer.writeUInt8(node.color.g >> 0); // Color G + buffer.writeUInt8(node.color.b >> 0); // Color B + + var flags = 0; + if (isVirus) + flags |= 0x01; + if (isAgitated) + flags |= 0x10; + if (isEject) + flags |= 0x20; + buffer.writeUInt8(flags >> 0); // Flags + + for (var j = 0; j < cellName.length && cellName.charCodeAt(j)!=0; j++) { + buffer.writeUInt16(cellName.charCodeAt(j) >> 0); // Cell Name in Unicode + } + buffer.writeUInt16(0); // Zero-terminator + } + buffer.writeUInt32(0); // Cell Update record terminator + + for (var i = 0; i < this.nonVisibleNodes.length; i++) { + var node = this.nonVisibleNodes[i]; + if (!node) continue; + deadCells.push(node); + } + + buffer.writeUInt32(deadCells.length); // RemoveRecordCount + for (var i = 0; i < deadCells.length; i++) { + var node = deadCells[i]; + buffer.writeUInt32(node.nodeId); // Cell ID + } + return buffer.buffer.slice(0, buffer.offset); +} + +// protocol 5 +UpdateNodes.prototype.build5 = function () { + var buffer = new ByteBuffer(); + buffer.LE(true); + + var deadCells = []; + for (var i = 0; i < this.destroyQueue.length; i++) { + var node = this.destroyQueue[i]; + if (!node) continue; + deadCells.push(node); + } + + buffer.writeUInt8(0x10); + buffer.writeUInt16(deadCells.length); // EatRecordCount + for (var i = 0; i < deadCells.length; i++) { + var node = deadCells[i]; + var hunterId = 0; + if (node.getKiller()) { + hunterId = node.getKiller().nodeId; + } + buffer.writeUInt32(hunterId); // Hunter ID + buffer.writeUInt32(node.nodeId); // Prey ID + } + + for (var i = 0; i < this.nodes.length; i++) { + var node = this.nodes[i]; + if (typeof node == "undefined" || !node || node.nodeId == 0) + continue; + + var cellX = node.position.x + this.scrambleX; + var cellY = node.position.y + this.scrambleY; + var cellSize = node.getSize(); + var cellName = node.getName(); + if (!cellName) cellName = ""; + + + var isVirus = (node.spiked & 0x01) != 0; + var isAgitated = false; // true = high wave amplitude on a cell outline + var isEject = node.cellType == 3; + + // Write update record + buffer.writeUInt32(node.nodeId); // Cell ID + buffer.writeInt32(cellX >> 0); // Coordinate X + buffer.writeInt32(cellY >> 0); // Coordinate Y + buffer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + buffer.writeUInt8(node.color.r >> 0); // Color R + buffer.writeUInt8(node.color.g >> 0); // Color G + buffer.writeUInt8(node.color.b >> 0); // Color B + + var flags = 0; + if (isVirus) + flags |= 0x01; + if (isAgitated) + flags |= 0x10; + if (isEject) + flags |= 0x20; + buffer.writeUInt8(flags >> 0); // Flags + + for (var j = 0; j < cellName.length && cellName.charCodeAt(j) != 0; j++) { + buffer.writeUInt16(cellName.charCodeAt(j) >> 0); // Cell Name in Unicode + } + buffer.writeUInt16(0); // Zero-terminator + } + buffer.writeUInt32(0); // Cell Update record terminator + + for (var i = 0; i < this.nonVisibleNodes.length; i++) { + var node = this.nonVisibleNodes[i]; + if (!node) continue; + deadCells.push(node); + } + + buffer.writeUInt32(deadCells.length); // RemoveRecordCount + for (var i = 0; i < deadCells.length; i++) { + var node = deadCells[i]; + buffer.writeUInt32(node.nodeId); // Cell ID + } + return buffer.buffer.slice(0, buffer.offset); +} + +// protocol 6 +UpdateNodes.prototype.build6 = function () { + var buffer = new ByteBuffer(); + buffer.LE(true); + + var deadCells = []; + for (var i = 0; i < this.destroyQueue.length; i++) { + var node = this.destroyQueue[i]; + if (!node) continue; + deadCells.push(node); + } + + buffer.writeUInt8(0x10); + buffer.writeUInt16(deadCells.length); // EatRecordCount + for (var i = 0; i < deadCells.length; i++) { + var node = deadCells[i]; + var hunterId = 0; + if (node.getKiller()) { + hunterId = node.getKiller().nodeId; + } + buffer.writeUInt32(hunterId); // Hunter ID + buffer.writeUInt32(node.nodeId); // Prey ID + } + + for (var i = 0; i < this.nodes.length; i++) { + var node = this.nodes[i]; + if (typeof node == "undefined" || !node || node.nodeId == 0) + continue; + + var cellX = node.position.x + this.scrambleX; + var cellY = node.position.y + this.scrambleY; + var cellSize = node.getSize(); + var skinName = null; + var cellName = node.getName(); + + var isVirus = (node.spiked & 0x01) != 0; + var isColorPresent = true; // we always include color + var isSkinPresent = !isVirus && skinName != null && skinName.length > 0; + var isNamePresent = !isVirus && cellName != null && cellName.length > 0; + var isAgitated = false; // true = high wave amplitude on a cell outline + var isEject = node.cellType==3; + + // Write update record + buffer.writeUInt32(node.nodeId); // Cell ID + buffer.writeInt32(cellX >> 0); // Coordinate X + buffer.writeInt32(cellY >> 0); // Coordinate Y + buffer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + + var flags = 0; + if (isVirus) + flags |= 0x01; + if (isColorPresent) + flags |= 0x02; + if (isSkinPresent) + flags |= 0x04; + if (isNamePresent) + flags |= 0x08; + if (isAgitated) + flags |= 0x10; + if (isEject) + flags |= 0x20; + buffer.writeUInt8(flags >> 0); // Flags + + if (isColorPresent) { + buffer.writeUInt8(node.color.r >> 0); // Color R + buffer.writeUInt8(node.color.g >> 0); // Color G + buffer.writeUInt8(node.color.b >> 0); // Color B + } + if (isSkinPresent) { + buffer.writeUTF8String(skinName); // Skin Name in UTF8 + buffer.writeUInt8(0); // Zero-terminator + } + if (isNamePresent) { + buffer.writeUTF8String(cellName); // Cell Name in UTF8 + buffer.writeUInt8(0); // Zero-terminator + } + } + buffer.writeUInt32(0); // Cell Update record terminator + + for (var i = 0; i < this.nonVisibleNodes.length; i++) { + var node = this.nonVisibleNodes[i]; + if (!node) continue; + deadCells.push(node); + } + + buffer.writeUInt16(deadCells.length); // RemoveRecordCount + for (var i = 0; i < deadCells.length; i++) { + var node = deadCells[i]; + buffer.writeUInt32(node.nodeId); // Cell ID + } + return buffer.buffer.slice(0, buffer.offset); +} diff --git a/src/packet/UpdatePosition.js b/src/packet/UpdatePosition.js index e7b49efa0..43cf03877 100644 --- a/src/packet/UpdatePosition.js +++ b/src/packet/UpdatePosition.js @@ -1,19 +1,21 @@ -function UpdatePosition(x, y, size) { - this.x = x; - this.y = y; - this.size = size; -} - -module.exports = UpdatePosition; - -UpdatePosition.prototype.build = function() { - var buf = new ArrayBuffer(13); - var view = new DataView(buf); - - view.setUint8(0, 17, true); - view.setFloat32(1, this.x, true); - view.setFloat32(5, this.y, true); - view.setFloat32(9, this.size, true); - - return buf; -}; +function UpdatePosition(x, y, size) { + this.x = x; + this.y = y; + this.size = size; +} + +module.exports = UpdatePosition; + +UpdatePosition.prototype.build = function(protocol) { + var buffer = new Buffer(13); + var offset = 0; + buffer.writeUInt8(0x11, offset); + offset += 1; + buffer.writeFloatLE(this.x, offset); + offset += 4; + buffer.writeFloatLE(this.y, offset); + offset += 4; + buffer.writeFloatLE(this.size, offset); + offset += 4; + return buffer; +}; From 8115869a218965007bcf8f1c1344c6cd916b0449 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 16:27:55 +0200 Subject: [PATCH 002/417] Add files via upload --- package.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e3bc3883c..23cff3c35 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,9 @@ "description": "Open source Agar.io server", "main": "src/index.js", "dependencies": { - "ws": "latest", - "vector2-node": "latest" + "bytebuffer": "^5.0.1", + "vector2-node": "latest", + "ws": "latest" }, "devDependencies": {}, "scripts": { From 9dae4532fae97563af561cbe02f4c5af83c27071 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 18:37:09 +0200 Subject: [PATCH 003/417] Add check for null in sendPacket --- src/GameServer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index d016621c9..d494c7981 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -29,7 +29,7 @@ function GameServer() { this.currentFood = 0; this.movingNodes = []; // For move engine this.leaderboard = []; - this.lb_packet = null; // Leaderboard packet for protocol 5 + this.lb_packet = null; // Leaderboard packet this.bots = new BotLoader(this); this.log = new Logger(); @@ -975,6 +975,7 @@ GameServer.prototype.getStats = function() { // Custom prototype functions WebSocket.prototype.sendPacket = function(packet) { + if (packet == null) return; //if (this.readyState == WebSocket.OPEN && (this._socket.bufferSize == 0) && packet.build) { if (this.readyState == WebSocket.OPEN) { From b05e83eb0109160fe311365a6e20a455736b1e5b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 19:25:59 +0200 Subject: [PATCH 004/417] Add disconnection reason message --- src/modules/CommandList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index e49a5a3b3..c6ed2fe3d 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -223,7 +223,7 @@ Commands.list = { for (var j = 0; j < len; j++) { gameServer.removeNode(client.cells[0]); } - client.socket.close(); + client.socket.close(1000, "Kicked from server"); console.log("[Console] Kicked " + client.name); break; } @@ -236,7 +236,7 @@ Commands.list = { for (var j = 0; j < len; j++) { gameServer.removeNode(client.cells[0]); } - client.socket.close(); + client.socket.close(1000, "Kicked from server"); console.log("[Console] Kicked " + client.name); } }, From 670f3ad93a961b5f19bb36f331be01396d11a2d8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 19:53:55 +0200 Subject: [PATCH 005/417] Update readme --- LICENSE.md | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++--- README.md | 117 +++---------------------- 2 files changed, 245 insertions(+), 116 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 80864c5c3..6c1686b64 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,13 +1,231 @@ -Copyright 2015 Devin Ryan - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this project except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + Devin Ryan (https://github.com/forairan) saught out to have his license + listed. So this license can be found at the following location. + + Copyright 2015 Devin Ryan + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this project except in compliance with the License. + + See https://github.com/OgarProject/Ogar/blob/master/LICENSE.md + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + * INTERSTELLAR POLICE WARNING * + + As provided by the Galactic Treaty of 4410, this computer software product is + hereby declared the Interllectual Property of the human author, Barbosik. + All rights are henceforth reserved in space and time. + + Provision for the protection of Interlllectual Property is covered under + Section 8.9.1A-f of the Intengible Property Act of 4506, ratified by all + beings except the Gazurtoids of planet Gazuria. + + WARNING: Any being caught with an unautorized copy or version of this software + product will be punished by Interstellar Corporate Police. Punishment may + include the annihilation of the offending beings planet. + +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- diff --git a/README.md b/README.md index 1968ae18d..0fd9aca22 100644 --- a/README.md +++ b/README.md @@ -1,107 +1,18 @@ -# Ogar -A fully functional open source Agar.io server implementation, written in Node.js. Ogar is designed to be used with the latest Agar.io client. +# OgarMulti -### Official Website -The official website for the Ogar Project is [ogarproject.com](http://ogarproject.com). You can register on our forums to chat with other Ogar users, get support, advertise your server, and more. +## Project Info +![Language](https://img.shields.io/badge/language-Java-yellow.svg) +[![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/Barbosik/OgarMulti/LICENSE) + +## [![Language](https://img.shields.io/badge/Ogar-Node-red.svg)](https://github.com/OgarProject/Ogar) Ogar +Copy of Ogar that I heavily modified, and will continue to update. +The [OgarProject](https://ogarproject.com) owns Ogar, and I do not claim it as mine! +Original Ogar found [here](https://github.com/OgarProject/Ogar) -### Purchased Ogar? -If you've purchased a copy of Ogar, you've probably been ripped off. [This post on our website explains why.](http://ogarproject.com/threads/psa-if-you-purchased-ogar-youve-been-ripped-off.6/) +The goal is to cleanup the code, fix the bugs and improve physics. -## Obtaining and Using -If you are on Windows, you can download the latest binary build of Ogar [from this page](http://dl.ogarproject.com/). The binary is the easiest way to get started running an Ogar server. If you'd like to tinker with the source code, you can follow the instructions below (and slightly modify them) to run the source on Windows. -As Ogar is written in Node.js, you must have Node.js, its "ws" and "vector2-node" modules installed to use it (unless you are using the Windows binary). You can usually download Node using your distribution's package manager (for *nix-like systems), or from [the Node website](http://nodejs.org). To install the "ws" module that is required, you can: -- for Windows, run `Install Dependecies.bat` located in the folder where this file is. -- for Mac, open your terminal, go to this directory with `cd` and type in `npm install`. -- for Linux, you can use the install script which would also automatically install node.js and ws. - -Manual: -```sh -~$ git clone git://github.com/OgarProject/Ogar.git Ogar -~$ npm install -~$ node Ogar -``` -Using the install script: -```sh -~$ sudo ogar-linux-script.sh install /your/preferred/directory -~$ sudo -u ogar -H /bin/sh -c "cd; /bin/node src/index.js" -``` -Using ```sudo -u ogar -H /bin/sh -c "cd; /bin/node src/index.js" ``` to launch the server increases security by running the process as an unprivileged, dedicated user with a limited shell and it is recommended to do so. - -Currently, Ogar listens on the following addresses and ports: -* *:80 - for the master server -* *:443 - for the game server - -Please note that on some systems, you may have to run the process as root or otherwise elevate your privileges to allow the process to listen on the needed ports. **If you are getting an EADDRINUSE error, it means that the port required to run Ogar is being used. Usually, Skype is the culprit. To solve this, either close out skype, or change the serverPort value in gameserver.ini to a different port. You will have to change your connection ip to "127.0.0.1:PORT"** - -Once the game server is running, you can connect (locally) by typing `agar.io/?ip=127.0.0.1:443` into your browser's address bar. - -## Configuring Ogar -Use "gameserver.ini" to modify Ogar's configurations field. Player bots are currently basic and for testing purposes. To use them, change "serverBots" to a value higher than zero in the configuration file. To add/remove bot names, edit the file named "botnames.txt" which is in the same folder as "gameserver.ini". Names should be separated by using the enter key. - -## Custom Game modes -Ogar has support for custom game modes. To switch between game modes, change the value of "serverGamemode" in the configurations file to the selected game mode id and restart the server. The current supported game modes are: - -Id | Name ------|-------------- -0 | Free For All -1 | Teams -2 | Experimental (As of 6/13/15) -10 | Tournament -11 | Hunger Games -12 | Zombie Mode -13 | Team Z -14 | Team X -20 | Rainbow FFA - Hint: Use with "setAcid(true)" - -## Console Commands -The current available console commands are listed here. Command names are not case sensitive, but player names are. - - - Addbot [Number] - * Adds [Number] of bots to the server. If an amount is not specified, 1 bot will be added. - - Board [String 1] [String 2] [String 3] ... - * Replaces the text on the leaderboard with the string text. - - Boardreset - * Resets the leaderboard to display the proper data for the current gamemode - - Change [Config setting] [Value] - * Changes a config setting to a value. Ex. "change serverMaxConnections 32" will change the variable serverMaxConnections to 32. Note that some config values (Like serverGamemode) are parsed before the server starts so changing them mid game will have no effect. - - Clear - * Clears the console output - - Color [Player ID] [Red] [Green] [Blue] - * Replaces the color of the specified player with this color. - - Exit - * Closes the server. - - Food [X position] [Y position] [Mass] - * Spawns a food cell at those coordinates. If a mass value is not specified, then the server will default to "foodStartMass" in the config. - - Gamemode [Id] - * Changes the gamemode of the server. Warning - This can cause problems. - - Help - * Shows List Of Commands - - Kick [Player ID] - * Kicks the specified player or bot from the server. - - Kill [Player ID] - * Kills all cells belonging to the specified player. - - Killall - * Kills all player cells on the map. - - Mass [Player ID] [Number] - * Sets the mass of all cells belonging to the specified player to [Number]. - - Name [Player ID] [New Name] - * Changes the name of the player with the specified id with [New Name]. - - Playerlist - * Shows a list of connected players, their IP, player ID, the amount of cells they have, total mass, and their position. - - Pause - * Pauses/Unpauses the game. - - Reload - * Reloads the config file used by the server. However, the following values are not affected: serverPort, serverGamemode, serverBots, serverStatsPort, serverStatsUpdate. - - Status - * Shows the amount of players currently connected, time elapsed, memory usage (memory used/memory allocated), and the current gamemode. - - Tp [Player ID] [X position] [Y position] - * Teleports the specified player to the specified coordinates. - - Virus [X position] [Y position] [Mass] - * Spawns a virus cell at those coordinates. If a mass value is not specified, then the server will default to "virusStartMass" in the config. - -## Contributing -Please see [CONTRIBUTING.md](https://github.com/OgarProject/Ogar/blob/master/CONTRIBUTING.md) for contribution guidelines. - -## License -Please see [LICENSE.md](https://github.com/OgarProject/Ogar/blob/master/LICENSE.md). +## The changes +* Fix protocol issues +* Cleanup the code (in progress) +* Improved physics (in progress) From 08ae1e4cbfb4582700fd6bda610881361162e6bf Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 20:01:11 +0200 Subject: [PATCH 006/417] Update readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0fd9aca22..c7fb0eff6 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@ # OgarMulti ## Project Info -![Language](https://img.shields.io/badge/language-Java-yellow.svg) -[![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/Barbosik/OgarMulti/LICENSE) +![Language](https://img.shields.io/badge/https://img.shields.io/badge/language-Node.js-yellow.svg) +[![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/Barbosik/OgarMulti/blob/master/LICENSE.md) ## [![Language](https://img.shields.io/badge/Ogar-Node-red.svg)](https://github.com/OgarProject/Ogar) Ogar Copy of Ogar that I heavily modified, and will continue to update. The [OgarProject](https://ogarproject.com) owns Ogar, and I do not claim it as mine! Original Ogar found [here](https://github.com/OgarProject/Ogar) + The goal is to cleanup the code, fix the bugs and improve physics. From 600c5dfe9867154754518b02704a03f3f8fd79d9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 20:02:03 +0200 Subject: [PATCH 007/417] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7fb0eff6..dd288e5a5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # OgarMulti ## Project Info -![Language](https://img.shields.io/badge/https://img.shields.io/badge/language-Node.js-yellow.svg) +![Language](https://img.shields.io/badge/language-node.js-yellow.svg) [![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/Barbosik/OgarMulti/blob/master/LICENSE.md) ## [![Language](https://img.shields.io/badge/Ogar-Node-red.svg)](https://github.com/OgarProject/Ogar) Ogar From 91944e8c02a7eb7a05a56c107c58b3132d3212d9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 20:18:15 +0200 Subject: [PATCH 008/417] Add disconnection reason message --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index d494c7981..47d4e6547 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -157,7 +157,7 @@ GameServer.prototype.start = function() { function connectionEstablished(ws) { if (this.clients.length >= this.config.serverMaxConnections) { // Server full - ws.close(); + ws.close(1000, "No slots"); return; } From e1daa3ae189917709c895883bd5d74324ed7660d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 20:42:34 +0200 Subject: [PATCH 009/417] Replace color generator with HSV --- src/GameServer.js | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 47d4e6547..b0dbdb782 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -259,14 +259,38 @@ GameServer.prototype.getRandomSpawn = function(mass) { }; GameServer.prototype.getRandomColor = function() { - var colorRGB = [0xFF, 0x07, (Math.random() * 256) >> 0]; - colorRGB.sort(function() { - return 0.5 - Math.random(); - }); + var h = 360 * Math.random(); + var s = 248 / 255; + var v = 1; + + // hsv to rgb + var rgb = { r: v, g: v, b: v }; // achromatic (grey) + if (s > 0) { + h /= 60; // sector 0 to 5 + var i = Math.floor(h) >> 0; + var f = h - i; // factorial part of h + var p = v * (1 - s); + var q = v * (1 - s * f); + var t = v * (1 - s * (1 - f)); + switch (i) { + case 0: rgb = { r: v, g: t, b: p }; break + case 1: rgb = { r: q, g: v, b: p }; break + case 2: rgb = { r: p, g: v, b: t }; break + case 3: rgb = { r: p, g: q, b: v }; break + case 4: rgb = { r: t, g: p, b: v }; break + default: rgb = { r: v, g: p, b: q }; break + } + } + rgb.r = Math.max(rgb.r, 0); + rgb.g = Math.max(rgb.g, 0); + rgb.b = Math.max(rgb.b, 0); + rgb.r = Math.min(rgb.r, 1); + rgb.g = Math.min(rgb.g, 1); + rgb.b = Math.min(rgb.b, 1); return { - r: colorRGB[0], - g: colorRGB[1], - b: colorRGB[2] + r: rgb.r * 255 >> 0, + g: rgb.g * 255 >> 0, + b: rgb.b * 255 >> 0 }; }; From 8ade7108a65f3d4cc5a7175c70c9cbec7e66ba62 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 21:28:40 +0200 Subject: [PATCH 010/417] cleanup mainLoop --- src/GameServer.js | 50 +++++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index b0dbdb782..bac50f8df 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -350,35 +350,27 @@ GameServer.prototype.removeNode = function(node) { } }; -GameServer.prototype.spawnTick = function() { +GameServer.prototype.updateSpawn = function() { // Spawn food this.tickSpawn++; if (this.tickSpawn >= this.config.spawnInterval) { + this.tickSpawn = 0; // Reset + this.updateFood(); // Spawn food this.virusCheck(); // Spawn viruses - - this.tickSpawn = 0; // Reset } }; -GameServer.prototype.gamemodeTick = function() { - // Gamemode tick - this.gameMode.onTick(this); -}; - -GameServer.prototype.cellUpdateTick = function() { - // Update cells - this.updateCells(); +GameServer.prototype.updateClients = function () { + for (var i = 0; i < this.clients.length; i++) { + if (typeof this.clients[i] == "undefined") { + continue; + } + this.clients[i].playerTracker.update(); + } }; -GameServer.prototype.mainLoop = function() { - // Loop main functions - this.updateMoveEngine(); // Move cells - this.spawnTick(); - this.gamemodeTick(); - this.cellUpdateTick(); - this.updateClients(); - +GameServer.prototype.updateLeaderboard = function () { // Update leaderboard with the gamemode's method if ((this.tickCounter % 40) == 0) { this.leaderboard = []; @@ -396,17 +388,19 @@ GameServer.prototype.mainLoop = function() { this.largestClient = clients[0].playerTracker; } else this.largestClient = this.gameMode.rankOne; } - - this.tickCounter++; }; -GameServer.prototype.updateClients = function() { - for (var i = 0; i < this.clients.length; i++) { - if (typeof this.clients[i] == "undefined") { - continue; - } - this.clients[i].playerTracker.update(); - } + +GameServer.prototype.mainLoop = function() { + // Loop main functions + this.updateMoveEngine(); + this.updateSpawn(); + this.gameMode.onTick(this); + this.updateCells(); + this.updateClients(); + this.updateLeaderboard(); + + this.tickCounter++; }; GameServer.prototype.startingFood = function() { From 96b8840b6201d0f8fe26e0be19257661102a016f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 21:47:00 +0200 Subject: [PATCH 011/417] Fix issue #1 (leaderboard strings for protocol 5) https://github.com/Barbosik/OgarMulti/issues/1#issue-158439542 --- src/packet/UpdateLeaderboard.js | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index 9397bb69a..c9cb0db33 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -34,12 +34,16 @@ UpdateLeaderboard.prototype.build = function (protocol) { buffer.writeUInt32LE(0, offset); offset += 4; - if (protocol <= 5) + if (protocol <= 5) { offset += buffer.write(name, offset, 'ucs2'); // string => unicode - else + buffer.writeUInt16LE(0, offset); + offset += 2; + } + else { offset += buffer.write(name, offset); // string => utf8 - buffer.writeUInt8(0, offset); // string zero terminator - offset++; + buffer.writeUInt8(0, offset); // string zero terminator + offset += 1; + } count++; } @@ -74,12 +78,16 @@ UpdateLeaderboard.prototype.build = function (protocol) { buffer.writeUInt32LE(isMe ? 1:0, offset); // isMe flag (previously cell ID) offset += 4; - if (protocol <= 5) + if (protocol <= 5) { offset += buffer.write(name, offset, 'ucs2'); // string => unicode - else + buffer.writeUInt16LE(0, offset); + offset += 2; + } + else { offset += buffer.write(name, offset); // string => utf8 - buffer.writeUInt8(0, offset); // string zero terminator - offset += 1; + buffer.writeUInt8(0, offset); // string zero terminator + offset += 1; + } count++; } From 21e8930a8feadf5bcbebec3f116ab03ce281b011 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 21:56:38 +0200 Subject: [PATCH 012/417] remove unused file --- src/packet/DynamicBuffer.js | 114 ------------------------------------ 1 file changed, 114 deletions(-) delete mode 100644 src/packet/DynamicBuffer.js diff --git a/src/packet/DynamicBuffer.js b/src/packet/DynamicBuffer.js deleted file mode 100644 index 95aa8f692..000000000 --- a/src/packet/DynamicBuffer.js +++ /dev/null @@ -1,114 +0,0 @@ -function DynamicBuffer(littleEndian) { - this.littleEndian = littleEndian == null ? false : littleEndian; - this.bytes = []; - - // Temp buffers to calculate bytes because I'm too lazy to do it - this.tempArrayBuffer = new ArrayBuffer(8); - this.tempDataView = new DataView(this.tempArrayBuffer); -} - -module.exports = DynamicBuffer; - -// Setters - -DynamicBuffer.prototype.setStringUTF8 = function(value) { - var utf8 = unescape(encodeURIComponent(value)); - for (var i = 0; i < utf8.length; i++) { - this.tempDataView.setUint8(0, utf8.charCodeAt(i), false); - this.moveTemp(1); - } -}; - -DynamicBuffer.prototype.setStringUnicode = function(value) { - for (var i = 0; i < value.length; i++) { - this.tempDataView.setUint16(0, value.charCodeAt(i), false); - this.moveTemp(2); - } -}; - -DynamicBuffer.prototype.setBoolean = function(value) { - this.tempDataView.setUint8(0, value ? 1 : 0, false); - this.moveTemp(1); -}; - -DynamicBuffer.prototype.setUint8 = function(value) { - this.tempDataView.setUint8(0, value, false); - this.moveTemp(1); -}; - -DynamicBuffer.prototype.setInt8 = function(value) { - this.tempDataView.setInt8(0, value, false); - this.moveTemp(1); -}; - -DynamicBuffer.prototype.setUint16 = function(value) { - this.tempDataView.setUint16(0, value, false); - this.moveTemp(2); -}; - -DynamicBuffer.prototype.setInt16 = function(value) { - this.tempDataView.setInt16(0, value, false); - this.moveTemp(2); -}; - -DynamicBuffer.prototype.setUint32 = function(value) { - this.tempDataView.setUint32(0, value, false); - this.moveTemp(4); -}; - -DynamicBuffer.prototype.setInt32 = function(value) { - this.tempDataView.setInt32(0, value, false); - this.moveTemp(4); -}; - -DynamicBuffer.prototype.setUint64 = function(value) { - this.tempDataView.setUint64(0, value, false); - this.moveTemp(8); -}; - -DynamicBuffer.prototype.setInt64 = function(value) { - this.tempDataView.setInt64(0, value, false); - this.moveTemp(8); -}; - -DynamicBuffer.prototype.setFloat32 = function(value) { - this.tempDataView.setFloat32(0, value, false); - this.moveTemp(4); -}; - -DynamicBuffer.prototype.setFloat64 = function(value) { - this.tempDataView.setFloat64(0, value, false); - this.moveTemp(8); -}; - -// Lib - -DynamicBuffer.prototype.moveTemp = function(amount) { - // Moves from temp to calculated bytes and flushes temp - if (this.littleEndian) { - for (var i = amount - 1; i >= 0; i--) { - this.bytes.push(this.tempDataView.getUint8(i)); - } - } else { - for (var i = 0; i < amount; i++) { - this.bytes.push(this.tempDataView.getUint8(i)); - } - } - this.flush(); -}; - -DynamicBuffer.prototype.flush = function() { - // Readies temp buffer for use - this.tempArrayBuffer = new ArrayBuffer(8); - this.tempDataView = new DataView(this.tempArrayBuffer); -}; - -DynamicBuffer.prototype.build = function() { - // Builds fixed array buffer from dynamic byte array - var buf = new ArrayBuffer(this.bytes.length); - var view = new DataView(buf); - for (var i = 0; i < this.bytes.length; i++) { - view.setUint8(i, this.bytes[i], false); - } - return buf; -}; From 8bee8063bac0e903664794abc15ca280989ae058 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 22:40:33 +0200 Subject: [PATCH 013/417] Fix issue #3 (Player speed) https://github.com/Barbosik/OgarMulti/issues/3#issue-158444929 --- src/BotPlayer.js | 201 ++++++++++++++++++++++++++++++++ src/Cell.js | 283 +++++++++++++++++++++++++++++++++++++++++++++ src/GameServer.js | 6 +- src/gameserver.ini | 4 +- 4 files changed, 489 insertions(+), 5 deletions(-) create mode 100644 src/BotPlayer.js create mode 100644 src/Cell.js diff --git a/src/BotPlayer.js b/src/BotPlayer.js new file mode 100644 index 000000000..2fabf8ce7 --- /dev/null +++ b/src/BotPlayer.js @@ -0,0 +1,201 @@ +var PlayerTracker = require('../PlayerTracker'); +var gameServer = require('../GameServer'); +var Vector = require('vector2-node'); + +function BotPlayer() { + PlayerTracker.apply(this, Array.prototype.slice.call(arguments)); + //this.color = gameServer.getRandomColor(); + + this.splitCooldown = 0; +} + +module.exports = BotPlayer; +BotPlayer.prototype = new PlayerTracker(); + +// Functions + +BotPlayer.prototype.getLowestCell = function() { + // Gets the cell with the lowest mass + if (this.cells.length <= 0) { + return null; // Error! + } + + // Sort the cells by Array.sort() function to avoid errors + var sorted = this.cells.valueOf(); + sorted.sort(function(a, b) { + return b.mass - a.mass; + }); + + return sorted[0]; +}; + +BotPlayer.prototype.update = function() { // Overrides the update function from player tracker + // Remove nodes from visible nodes if possible + for (var i = 0; i < this.nodeDestroyQueue.length; i++) { + var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[i]); + if (index > -1) { + this.visibleNodes.splice(index, 1); + } + } + + // Respawn if bot is dead + if (this.cells.length <= 0) { + this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); + if (this.cells.length == 0) { + // If the bot cannot spawn any cells, then disconnect it + this.socket.close(); + return; + } + } + + if (this.splitCooldown > 0) this.splitCooldown--; + + setTimeout(function() { + // Calculate nodes + this.visibleNodes = this.calcViewBox(); + + // Calc predators/prey + var cell = this.getLowestCell(); + + // Action + this.decide(cell); + + // Reset queues + this.nodeDestroyQueue = []; + this.nodeAdditionQueue = []; + }.bind(this), 0); +}; + +// Custom +BotPlayer.prototype.decide = function(cell) { + if (!cell) return; // Cell was eaten, check in the next tick (I'm too lazy) + + var cellPos = cell.position; + var result = new Vector(0, 0); + // Splitting + var split = false, + splitTarget = null, + threats = []; + + for (var i = 0; i < this.visibleNodes.length; i++) { + var check = this.visibleNodes[i]; + + // Get attraction of the cells - avoid larger cells, viruses and same team cells + var influence = 0; + if (check.cellType == 0) { + // Player cell + if (this.gameServer.gameMode.haveTeams && (cell.owner.team == check.owner.team)) influence = 0; // Same team cell + else if (cell.mass / 1.3 > check.mass) influence = check.getSize() * 2.5; // Can eat it + else if (check.mass / 1.3 > cell.mass) influence = -check.getSize(); // Can eat me + } else if (check.cellType == 1) { + // Food + influence = 1; + } else if (check.cellType == 2) { + // Virus + if (cell.mass / 1.3 > check.mass) { + // Can eat it + if (this.cells.length == this.gameServer.config.playerMaxCells) influence = check.getSize() * 2.5; // Won't explode + else influence = -1; // Can explode + } + } else if (check.cellType == 3) { + // Ejected mass + if (cell.mass / 1.3 > check.mass) influence = check.getSize(); + } else { + influence = check.getSize(); // Might be TeamZ + } + + // Apply influence if it isn't 0 or my cell + if (influence == 0 || cell.owner == check.owner) continue; + + // Calculate separation between cell and check + var checkPos = check.position; + var displacement = new Vector(checkPos.x - cellPos.x, checkPos.y - cellPos.y); + + // Figure out distance between cells + var distance = displacement.length(); + if (influence < 0) { + // Get edge distance + distance -= cell.getSize() + check.getSize(); + if (check.cellType == 0) threats.push(check); + } + + // The farther they are the smaller influnce it is + if (distance < 1) distance = 1; // Avoid NaN and positive influence with negative distance & attraction + influence /= distance; + + // Produce force vector exerted by this entity on the cell + var force = displacement.normalize().scale(influence); + + // Splitting conditions + if (check.cellType == 0 && cell.mass / 2.6 > check.mass && cell.mass / 5 < check.mass && + (!split) && this.splitCooldown == 0 && this.cells.length < 3) { + + var endDist = Math.max(this.splitDistance(cell), cell.getSize() * 4); + + if (distance < endDist - cell.getSize() - check.getSize()) { + splitTarget = check; + split = true; + } + } else { + // Add up forces on the entity + result.add(force); + } + } + + // Normalize the resulting vector + result.normalize(); + + // Check for splitkilling and threats + if (split) { + // Can be shortened but I'm too lazy + if (threats.length > 0) { + if (this.largest(threats).mass / 2.6 > cell.mass) { // ??? but works + // Splitkill the target + this.mouse = { + x: splitTarget.position.x, + y: splitTarget.position.y + }; + this.splitCooldown = 16; + this.gameServer.splitCells(this); + return; + } + } + else { + // Still splitkill the target + this.mouse = { + x: splitTarget.position.x, + y: splitTarget.position.y + }; + this.splitCooldown = 16; + this.gameServer.splitCells(this); + return; + } + } + this.mouse = { + x: cellPos.x + result.x * 800, + y: cellPos.y + result.y * 800 + }; +}; + +// Subfunctions + +BotPlayer.prototype.largest = function(list) { + // Sort the cells by Array.sort() function to avoid errors + var sorted = list.valueOf(); + sorted.sort(function(a, b) { + return b.mass - a.mass; + }); + + return sorted[0]; +}; + +BotPlayer.prototype.splitDistance = function(cell) { + // Calculate split distance and check if it is larger than the raw distance + var mass = cell.mass; + var t = Math.PI * Math.PI; + var modifier = 3 + Math.log(1 + mass) / 10; + var splitSpeed = cell.owner.gameServer.config.playerSpeed * 30 * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); + var endDist = Math.max(splitSpeed * 12.8, cell.getSize() * 2); // Checked via C#, final distance is near 6.512x splitSpeed + + return endDist; +}; diff --git a/src/Cell.js b/src/Cell.js new file mode 100644 index 000000000..97f1abb19 --- /dev/null +++ b/src/Cell.js @@ -0,0 +1,283 @@ +function Cell(nodeId, owner, position, mass, gameServer) { + this.nodeId = nodeId; + this.owner = owner; // playerTracker that owns this cell + this.color = { + r: 0, + g: 255, + b: 0 + }; + this.position = position; + this.mass = mass; // Starting mass of the cell + this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass + this.spiked = 0; // If 1, then this cell has spikes around it + + this.killedBy; // Cell that ate this cell + this.gameServer = gameServer; + + this.moveEngineTicks = 0; // Amount of times to loop the movement function + this.moveEngineSpeed = 0; + this.moveDecay = 0.85; + this.angle = 0; // Angle of movement + this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells +} + +module.exports = Cell; + +// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class + +Cell.prototype.getName = function() { + if (this.owner) return this.owner.name; + return ""; +}; + +Cell.prototype.setColor = function(color) { + this.color.r = color.r; + this.color.g = color.g; + this.color.b = color.b; +}; + +Cell.prototype.getColor = function() { + return this.color; +}; + +Cell.prototype.getType = function() { + return this.cellType; +}; + +Cell.prototype.getSize = function() { + // Calculates radius based on cell mass + return Math.ceil(Math.sqrt(100 * this.mass)); +}; + +Cell.prototype.getSquareSize = function() { + // R * R + return (100 * this.mass) >> 0; +}; + +Cell.prototype.addMass = function(n) { + // Check if the cell needs to autosplit before adding mass + if (this.mass > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) { + var splitMass = this.mass / 2; + var randomAngle = Math.random() * 6.28; // Get random angle + this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass); + } else { + this.mass = Math.min(this.mass, this.gameServer.config.playerMaxMass); + } + this.mass += n; +}; + +Cell.prototype.getSpeed = function() { + //var t = Math.PI * Math.PI; + //return this.gameServer.config.playerSpeed * 30 * + // Math.pow(this.getSize(), -0.01 / (Math.PI * Math.PI * Math.PI)); //-Math.PI / t);//1.5); + + var k = 100 / 32; // TODO: implement mouse pos dependency + + var speed = this.getMaxSpeed(this.getSize()) * k; + return speed * (1 / 0.04) * this.gameServer.config.playerSpeed; +}; + +Cell.prototype.getMaxSpeed = function (size) { + return (2.1106 / Math.pow(size, 0.449)); +} + +Cell.prototype.setAngle = function(radians) { + this.angle = radians; +}; + +Cell.prototype.getAngle = function() { + return this.angle; +}; + +Cell.prototype.setMoveEngineData = function(speed, ticks, decay) { + this.moveEngineSpeed = speed; + this.moveEngineTicks = ticks; + this.moveDecay = isNaN(decay) ? 0.75 : decay; +}; + +Cell.prototype.getEatingRange = function() { + return 0; // 0 for ejected cells +}; + +Cell.prototype.getKiller = function() { + return this.killedBy; +}; + +Cell.prototype.setKiller = function(cell) { + this.killedBy = cell; +}; + +// Functions + +Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) { + // Collision checking + if (this.position.y > bottomY) { + return false; + } + + if (this.position.y < topY) { + return false; + } + + if (this.position.x > rightX) { + return false; + } + + if (this.position.x < leftX) { + return false; + } + + return true; +}; + +// This collision checking function is based on CIRCLE shape +Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) { + // IF (O1O2 + r <= R) THEN collided. (O1O2: distance b/w 2 centers of cells) + // (O1O2 + r)^2 <= R^2 + // approximately, remove 2*O1O2*r because it requires sqrt(): O1O2^2 + r^2 <= R^2 + + var dx = this.position.x - objectPosition.x; + var dy = this.position.y - objectPosition.y; + + return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize); +}; + +Cell.prototype.visibleCheck = function(box, centerPos, cells) { + // Checks if this cell is visible to the player + var isThere = false; + if (this.mass < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); + else { + var cellSize = this.getSize(); + var lenX = cellSize + box.width >> 0; // Width of cell + width of the box (Int) + var lenY = cellSize + box.height >> 0; // Height of cell + height of the box (Int) + + isThere = (this.abs(this.position.x - centerPos.x) < lenX) && (this.abs(this.position.y - centerPos.y) < lenY); + } + if (isThere) { + // It is + // To save perfomance, check if any client's cell collides with this cell + for (var i = 0; i < cells.length; i++) { + var cell = cells[i]; + if (!cell) continue; + + var dist = this.getDist(this.position.x, this.position.y, cell.position.x, cell.position.y); + var collideDist = cell.getSize() + this.getSize(); + + if (dist < collideDist) { + return 2; + }// Colliding with one + } + return 1; // Not colliding with any + } + else return 0; +}; + +Cell.prototype.calcMovePhys = function(config) { + // Move, twice as slower + var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0); + var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0); + + // Movement engine + if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) this.moveEngineSpeed = 0; + var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay; + this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower + if (this.moveEngineTicks >= 0.5) this.moveEngineTicks -= 0.5; // Ticks passing twice as slower + + // Ejected cell collision + if (this.cellType == 3) { + for (var i = 0; i < this.gameServer.nodesEjected.length; i++) { + var check = this.gameServer.nodesEjected[i]; + + if (check.nodeId == this.nodeId) continue; // Don't check for yourself + + var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y); + var allowDist = this.getSize() + check.getSize(); // Allow cells to get in themselves a bit + + if (dist < allowDist) { + // Two ejected cells collided + var deltaX = this.position.x - check.position.x; + var deltaY = this.position.y - check.position.y; + var angle = Math.atan2(deltaX, deltaY); + + this.gameServer.setAsMovingNode(check); + check.moveEngineTicks += 1; + this.moveEngineTicks += 1; + + var move = allowDist - dist; + + X += Math.sin(angle) * move / 2; + Y += Math.cos(angle) * move / 2; + } + } + } + + // Border check - Bouncy physics + var radius = 40; + if ((this.position.x - radius) < -config.borderLeft) { + // Flip angle horizontally - Left side + this.angle = 6.28 - this.angle; + X = -config.borderLeft + radius; + } + if ((this.position.x + radius) > config.borderRight) { + // Flip angle horizontally - Right side + this.angle = 6.28 - this.angle; + X = config.borderRight - radius; + } + if ((this.position.y - radius) < -config.borderTop) { + // Flip angle vertically - Top side + this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; + Y = -config.borderTop + radius; + } + if ((this.position.y + radius) > config.borderBottom) { + // Flip angle vertically - Bottom side + this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; + Y = config.borderBottom - radius; + } + + // Set position + this.position.x = X >> 0; + this.position.y = Y >> 0; +}; + +// Override these + +Cell.prototype.sendUpdate = function() { + // Whether or not to include this cell in the update packet + return true; +}; + +Cell.prototype.onConsume = function(consumer, gameServer) { + // Called when the cell is consumed +}; + +Cell.prototype.onAdd = function(gameServer) { + // Called when this cell is added to the world +}; + +Cell.prototype.onRemove = function(gameServer) { + // Called when this cell is removed +}; + +Cell.prototype.onAutoMove = function(gameServer) { + // Called on each auto move engine tick +}; + +Cell.prototype.moveDone = function(gameServer) { + // Called when this cell finished moving with the auto move engine +}; + +// Lib + +Cell.prototype.abs = function(x) { + return x < 0 ? -x : x; +}; + +Cell.prototype.getDist = function(x1, y1, x2, y2) { + var xs = x2 - x1; + xs = xs * xs; + + var ys = y2 - y1; + ys = ys * ys; + + return Math.sqrt(xs + ys); +}; diff --git a/src/GameServer.js b/src/GameServer.js index bac50f8df..d7c866c2f 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -72,7 +72,7 @@ function GameServer() { virusStartMass: 100, // Starting virus size (In mass) virusFeedAmount: 7, // Amount of times you need to feed a virus to shoot it ejectMass: 13, // Mass of ejected cells - ejectMassCooldown: 100, // Time until a player can eject mass again + ejectMassCooldown: 3, // min ticks between ejects ejectMassLoss: 15, // Mass lost when ejecting cells ejectSpeed: 100, // Base speed of ejected cells ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass @@ -87,7 +87,7 @@ function GameServer() { playerMassDecayRate: .002, // Amount of mass lost per second playerMinMassDecay: 9, // Minimum mass for decay to occur playerMaxNickLength: 15, // Maximum nick length - playerSpeed: 30, // Player base speed + playerSpeed: 1, // Player speed multiplier playerDisconnectTime: 60, // The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) tourneyMaxPlayers: 12, // Maximum amount of participants for tournament style game modes tourneyPrepTime: 10, // Amount of ticks to wait after all players are ready (1 tick = 1000 ms) @@ -664,7 +664,7 @@ GameServer.prototype.createPlayerCell = function(client, parent, angle, mass) { // Calculate customized speed for splitting cells var t = Math.PI * Math.PI; var modifier = 3 + Math.log(1 + mass) / (10 + Math.log(1 + mass)); - var splitSpeed = this.config.playerSpeed * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); + var splitSpeed = this.config.playerSpeed * 30 * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); // Calculate new position var newPos = { diff --git a/src/gameserver.ini b/src/gameserver.ini index 8cbe313c3..735f6a110 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -52,7 +52,7 @@ virusFeedAmount = 7 // ejectSpeed: Base speed of ejected cells // ejectSpawnPlayer: Chance for a player to spawn from ejected mass ejectMass = 13 -ejectMassCooldown = 100 +ejectMassCooldown = 3 ejectMassLoss = 15 ejectSpeed = 100 ejectSpawnPlayer = 50 @@ -75,7 +75,7 @@ playerMassAbsorbed = 1.0 playerMassDecayRate = .002 playerMinMassDecay = 9 playerMaxNickLength = 15 -playerSpeed = 35 +playerSpeed = 1 playerDisconnectTime = 60 // [Gamemode] From eeb81bd7ae72d673a4306c1d059068545fa0a607 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 22:53:48 +0200 Subject: [PATCH 014/417] remove (incorrect commit path) --- src/Cell.js | 283 ---------------------------------------------------- 1 file changed, 283 deletions(-) delete mode 100644 src/Cell.js diff --git a/src/Cell.js b/src/Cell.js deleted file mode 100644 index 97f1abb19..000000000 --- a/src/Cell.js +++ /dev/null @@ -1,283 +0,0 @@ -function Cell(nodeId, owner, position, mass, gameServer) { - this.nodeId = nodeId; - this.owner = owner; // playerTracker that owns this cell - this.color = { - r: 0, - g: 255, - b: 0 - }; - this.position = position; - this.mass = mass; // Starting mass of the cell - this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass - this.spiked = 0; // If 1, then this cell has spikes around it - - this.killedBy; // Cell that ate this cell - this.gameServer = gameServer; - - this.moveEngineTicks = 0; // Amount of times to loop the movement function - this.moveEngineSpeed = 0; - this.moveDecay = 0.85; - this.angle = 0; // Angle of movement - this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells -} - -module.exports = Cell; - -// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class - -Cell.prototype.getName = function() { - if (this.owner) return this.owner.name; - return ""; -}; - -Cell.prototype.setColor = function(color) { - this.color.r = color.r; - this.color.g = color.g; - this.color.b = color.b; -}; - -Cell.prototype.getColor = function() { - return this.color; -}; - -Cell.prototype.getType = function() { - return this.cellType; -}; - -Cell.prototype.getSize = function() { - // Calculates radius based on cell mass - return Math.ceil(Math.sqrt(100 * this.mass)); -}; - -Cell.prototype.getSquareSize = function() { - // R * R - return (100 * this.mass) >> 0; -}; - -Cell.prototype.addMass = function(n) { - // Check if the cell needs to autosplit before adding mass - if (this.mass > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) { - var splitMass = this.mass / 2; - var randomAngle = Math.random() * 6.28; // Get random angle - this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass); - } else { - this.mass = Math.min(this.mass, this.gameServer.config.playerMaxMass); - } - this.mass += n; -}; - -Cell.prototype.getSpeed = function() { - //var t = Math.PI * Math.PI; - //return this.gameServer.config.playerSpeed * 30 * - // Math.pow(this.getSize(), -0.01 / (Math.PI * Math.PI * Math.PI)); //-Math.PI / t);//1.5); - - var k = 100 / 32; // TODO: implement mouse pos dependency - - var speed = this.getMaxSpeed(this.getSize()) * k; - return speed * (1 / 0.04) * this.gameServer.config.playerSpeed; -}; - -Cell.prototype.getMaxSpeed = function (size) { - return (2.1106 / Math.pow(size, 0.449)); -} - -Cell.prototype.setAngle = function(radians) { - this.angle = radians; -}; - -Cell.prototype.getAngle = function() { - return this.angle; -}; - -Cell.prototype.setMoveEngineData = function(speed, ticks, decay) { - this.moveEngineSpeed = speed; - this.moveEngineTicks = ticks; - this.moveDecay = isNaN(decay) ? 0.75 : decay; -}; - -Cell.prototype.getEatingRange = function() { - return 0; // 0 for ejected cells -}; - -Cell.prototype.getKiller = function() { - return this.killedBy; -}; - -Cell.prototype.setKiller = function(cell) { - this.killedBy = cell; -}; - -// Functions - -Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) { - // Collision checking - if (this.position.y > bottomY) { - return false; - } - - if (this.position.y < topY) { - return false; - } - - if (this.position.x > rightX) { - return false; - } - - if (this.position.x < leftX) { - return false; - } - - return true; -}; - -// This collision checking function is based on CIRCLE shape -Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) { - // IF (O1O2 + r <= R) THEN collided. (O1O2: distance b/w 2 centers of cells) - // (O1O2 + r)^2 <= R^2 - // approximately, remove 2*O1O2*r because it requires sqrt(): O1O2^2 + r^2 <= R^2 - - var dx = this.position.x - objectPosition.x; - var dy = this.position.y - objectPosition.y; - - return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize); -}; - -Cell.prototype.visibleCheck = function(box, centerPos, cells) { - // Checks if this cell is visible to the player - var isThere = false; - if (this.mass < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); - else { - var cellSize = this.getSize(); - var lenX = cellSize + box.width >> 0; // Width of cell + width of the box (Int) - var lenY = cellSize + box.height >> 0; // Height of cell + height of the box (Int) - - isThere = (this.abs(this.position.x - centerPos.x) < lenX) && (this.abs(this.position.y - centerPos.y) < lenY); - } - if (isThere) { - // It is - // To save perfomance, check if any client's cell collides with this cell - for (var i = 0; i < cells.length; i++) { - var cell = cells[i]; - if (!cell) continue; - - var dist = this.getDist(this.position.x, this.position.y, cell.position.x, cell.position.y); - var collideDist = cell.getSize() + this.getSize(); - - if (dist < collideDist) { - return 2; - }// Colliding with one - } - return 1; // Not colliding with any - } - else return 0; -}; - -Cell.prototype.calcMovePhys = function(config) { - // Move, twice as slower - var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0); - var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0); - - // Movement engine - if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) this.moveEngineSpeed = 0; - var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay; - this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower - if (this.moveEngineTicks >= 0.5) this.moveEngineTicks -= 0.5; // Ticks passing twice as slower - - // Ejected cell collision - if (this.cellType == 3) { - for (var i = 0; i < this.gameServer.nodesEjected.length; i++) { - var check = this.gameServer.nodesEjected[i]; - - if (check.nodeId == this.nodeId) continue; // Don't check for yourself - - var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y); - var allowDist = this.getSize() + check.getSize(); // Allow cells to get in themselves a bit - - if (dist < allowDist) { - // Two ejected cells collided - var deltaX = this.position.x - check.position.x; - var deltaY = this.position.y - check.position.y; - var angle = Math.atan2(deltaX, deltaY); - - this.gameServer.setAsMovingNode(check); - check.moveEngineTicks += 1; - this.moveEngineTicks += 1; - - var move = allowDist - dist; - - X += Math.sin(angle) * move / 2; - Y += Math.cos(angle) * move / 2; - } - } - } - - // Border check - Bouncy physics - var radius = 40; - if ((this.position.x - radius) < -config.borderLeft) { - // Flip angle horizontally - Left side - this.angle = 6.28 - this.angle; - X = -config.borderLeft + radius; - } - if ((this.position.x + radius) > config.borderRight) { - // Flip angle horizontally - Right side - this.angle = 6.28 - this.angle; - X = config.borderRight - radius; - } - if ((this.position.y - radius) < -config.borderTop) { - // Flip angle vertically - Top side - this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - Y = -config.borderTop + radius; - } - if ((this.position.y + radius) > config.borderBottom) { - // Flip angle vertically - Bottom side - this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - Y = config.borderBottom - radius; - } - - // Set position - this.position.x = X >> 0; - this.position.y = Y >> 0; -}; - -// Override these - -Cell.prototype.sendUpdate = function() { - // Whether or not to include this cell in the update packet - return true; -}; - -Cell.prototype.onConsume = function(consumer, gameServer) { - // Called when the cell is consumed -}; - -Cell.prototype.onAdd = function(gameServer) { - // Called when this cell is added to the world -}; - -Cell.prototype.onRemove = function(gameServer) { - // Called when this cell is removed -}; - -Cell.prototype.onAutoMove = function(gameServer) { - // Called on each auto move engine tick -}; - -Cell.prototype.moveDone = function(gameServer) { - // Called when this cell finished moving with the auto move engine -}; - -// Lib - -Cell.prototype.abs = function(x) { - return x < 0 ? -x : x; -}; - -Cell.prototype.getDist = function(x1, y1, x2, y2) { - var xs = x2 - x1; - xs = xs * xs; - - var ys = y2 - y1; - ys = ys * ys; - - return Math.sqrt(xs + ys); -}; From 00307d7296ea94984f3742f89e90c53f53b0673d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 22:54:12 +0200 Subject: [PATCH 015/417] remove (incorrect commit path) --- src/BotPlayer.js | 201 ----------------------------------------------- 1 file changed, 201 deletions(-) delete mode 100644 src/BotPlayer.js diff --git a/src/BotPlayer.js b/src/BotPlayer.js deleted file mode 100644 index 2fabf8ce7..000000000 --- a/src/BotPlayer.js +++ /dev/null @@ -1,201 +0,0 @@ -var PlayerTracker = require('../PlayerTracker'); -var gameServer = require('../GameServer'); -var Vector = require('vector2-node'); - -function BotPlayer() { - PlayerTracker.apply(this, Array.prototype.slice.call(arguments)); - //this.color = gameServer.getRandomColor(); - - this.splitCooldown = 0; -} - -module.exports = BotPlayer; -BotPlayer.prototype = new PlayerTracker(); - -// Functions - -BotPlayer.prototype.getLowestCell = function() { - // Gets the cell with the lowest mass - if (this.cells.length <= 0) { - return null; // Error! - } - - // Sort the cells by Array.sort() function to avoid errors - var sorted = this.cells.valueOf(); - sorted.sort(function(a, b) { - return b.mass - a.mass; - }); - - return sorted[0]; -}; - -BotPlayer.prototype.update = function() { // Overrides the update function from player tracker - // Remove nodes from visible nodes if possible - for (var i = 0; i < this.nodeDestroyQueue.length; i++) { - var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[i]); - if (index > -1) { - this.visibleNodes.splice(index, 1); - } - } - - // Respawn if bot is dead - if (this.cells.length <= 0) { - this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); - if (this.cells.length == 0) { - // If the bot cannot spawn any cells, then disconnect it - this.socket.close(); - return; - } - } - - if (this.splitCooldown > 0) this.splitCooldown--; - - setTimeout(function() { - // Calculate nodes - this.visibleNodes = this.calcViewBox(); - - // Calc predators/prey - var cell = this.getLowestCell(); - - // Action - this.decide(cell); - - // Reset queues - this.nodeDestroyQueue = []; - this.nodeAdditionQueue = []; - }.bind(this), 0); -}; - -// Custom -BotPlayer.prototype.decide = function(cell) { - if (!cell) return; // Cell was eaten, check in the next tick (I'm too lazy) - - var cellPos = cell.position; - var result = new Vector(0, 0); - // Splitting - var split = false, - splitTarget = null, - threats = []; - - for (var i = 0; i < this.visibleNodes.length; i++) { - var check = this.visibleNodes[i]; - - // Get attraction of the cells - avoid larger cells, viruses and same team cells - var influence = 0; - if (check.cellType == 0) { - // Player cell - if (this.gameServer.gameMode.haveTeams && (cell.owner.team == check.owner.team)) influence = 0; // Same team cell - else if (cell.mass / 1.3 > check.mass) influence = check.getSize() * 2.5; // Can eat it - else if (check.mass / 1.3 > cell.mass) influence = -check.getSize(); // Can eat me - } else if (check.cellType == 1) { - // Food - influence = 1; - } else if (check.cellType == 2) { - // Virus - if (cell.mass / 1.3 > check.mass) { - // Can eat it - if (this.cells.length == this.gameServer.config.playerMaxCells) influence = check.getSize() * 2.5; // Won't explode - else influence = -1; // Can explode - } - } else if (check.cellType == 3) { - // Ejected mass - if (cell.mass / 1.3 > check.mass) influence = check.getSize(); - } else { - influence = check.getSize(); // Might be TeamZ - } - - // Apply influence if it isn't 0 or my cell - if (influence == 0 || cell.owner == check.owner) continue; - - // Calculate separation between cell and check - var checkPos = check.position; - var displacement = new Vector(checkPos.x - cellPos.x, checkPos.y - cellPos.y); - - // Figure out distance between cells - var distance = displacement.length(); - if (influence < 0) { - // Get edge distance - distance -= cell.getSize() + check.getSize(); - if (check.cellType == 0) threats.push(check); - } - - // The farther they are the smaller influnce it is - if (distance < 1) distance = 1; // Avoid NaN and positive influence with negative distance & attraction - influence /= distance; - - // Produce force vector exerted by this entity on the cell - var force = displacement.normalize().scale(influence); - - // Splitting conditions - if (check.cellType == 0 && cell.mass / 2.6 > check.mass && cell.mass / 5 < check.mass && - (!split) && this.splitCooldown == 0 && this.cells.length < 3) { - - var endDist = Math.max(this.splitDistance(cell), cell.getSize() * 4); - - if (distance < endDist - cell.getSize() - check.getSize()) { - splitTarget = check; - split = true; - } - } else { - // Add up forces on the entity - result.add(force); - } - } - - // Normalize the resulting vector - result.normalize(); - - // Check for splitkilling and threats - if (split) { - // Can be shortened but I'm too lazy - if (threats.length > 0) { - if (this.largest(threats).mass / 2.6 > cell.mass) { // ??? but works - // Splitkill the target - this.mouse = { - x: splitTarget.position.x, - y: splitTarget.position.y - }; - this.splitCooldown = 16; - this.gameServer.splitCells(this); - return; - } - } - else { - // Still splitkill the target - this.mouse = { - x: splitTarget.position.x, - y: splitTarget.position.y - }; - this.splitCooldown = 16; - this.gameServer.splitCells(this); - return; - } - } - this.mouse = { - x: cellPos.x + result.x * 800, - y: cellPos.y + result.y * 800 - }; -}; - -// Subfunctions - -BotPlayer.prototype.largest = function(list) { - // Sort the cells by Array.sort() function to avoid errors - var sorted = list.valueOf(); - sorted.sort(function(a, b) { - return b.mass - a.mass; - }); - - return sorted[0]; -}; - -BotPlayer.prototype.splitDistance = function(cell) { - // Calculate split distance and check if it is larger than the raw distance - var mass = cell.mass; - var t = Math.PI * Math.PI; - var modifier = 3 + Math.log(1 + mass) / 10; - var splitSpeed = cell.owner.gameServer.config.playerSpeed * 30 * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); - var endDist = Math.max(splitSpeed * 12.8, cell.getSize() * 2); // Checked via C#, final distance is near 6.512x splitSpeed - - return endDist; -}; From 94ae71c700f19b7201ec5dc2ea5dd8277e9e4dec Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 22:55:00 +0200 Subject: [PATCH 016/417] Fix issue #3 (Player speed) https://github.com/Barbosik/OgarMulti/issues/3#issue-158444929 --- src/ai/BotPlayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index d229b7dfa..2fabf8ce7 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -194,7 +194,7 @@ BotPlayer.prototype.splitDistance = function(cell) { var mass = cell.mass; var t = Math.PI * Math.PI; var modifier = 3 + Math.log(1 + mass) / 10; - var splitSpeed = cell.owner.gameServer.config.playerSpeed * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); + var splitSpeed = cell.owner.gameServer.config.playerSpeed * 30 * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); var endDist = Math.max(splitSpeed * 12.8, cell.getSize() * 2); // Checked via C#, final distance is near 6.512x splitSpeed return endDist; From f060de3ae0c55ad674fee310a52b31fbc038832a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 3 Jun 2016 22:55:36 +0200 Subject: [PATCH 017/417] Fix issue #3 (Player speed) https://github.com/Barbosik/OgarMulti/issues/3#issue-158444929 --- src/entity/Cell.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 587f43bf9..97f1abb19 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -67,11 +67,20 @@ Cell.prototype.addMass = function(n) { }; Cell.prototype.getSpeed = function() { - var t = Math.PI * Math.PI; - return this.gameServer.config.playerSpeed * - Math.pow(this.getSize(), -0.01 / (Math.PI * Math.PI * Math.PI)); //-Math.PI / t);//1.5); + //var t = Math.PI * Math.PI; + //return this.gameServer.config.playerSpeed * 30 * + // Math.pow(this.getSize(), -0.01 / (Math.PI * Math.PI * Math.PI)); //-Math.PI / t);//1.5); + + var k = 100 / 32; // TODO: implement mouse pos dependency + + var speed = this.getMaxSpeed(this.getSize()) * k; + return speed * (1 / 0.04) * this.gameServer.config.playerSpeed; }; +Cell.prototype.getMaxSpeed = function (size) { + return (2.1106 / Math.pow(size, 0.449)); +} + Cell.prototype.setAngle = function(radians) { this.angle = radians; }; From 4535baf62b0406d9733c641350165f454869ee6d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 00:25:59 +0200 Subject: [PATCH 018/417] Add skin support https://github.com/Barbosik/OgarMulti/issues/4#issue-158453099 https://github.com/OgarProject/Ogar/issues/422#issuecomment-182826493 --- src/entity/Cell.js | 561 +++++++++++++++++++++++---------------------- 1 file changed, 284 insertions(+), 277 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 97f1abb19..9c53ff8b0 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -1,283 +1,290 @@ -function Cell(nodeId, owner, position, mass, gameServer) { - this.nodeId = nodeId; - this.owner = owner; // playerTracker that owns this cell - this.color = { - r: 0, - g: 255, - b: 0 - }; - this.position = position; - this.mass = mass; // Starting mass of the cell - this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass - this.spiked = 0; // If 1, then this cell has spikes around it - - this.killedBy; // Cell that ate this cell - this.gameServer = gameServer; - - this.moveEngineTicks = 0; // Amount of times to loop the movement function - this.moveEngineSpeed = 0; - this.moveDecay = 0.85; - this.angle = 0; // Angle of movement - this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells -} - -module.exports = Cell; - -// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class - -Cell.prototype.getName = function() { - if (this.owner) return this.owner.name; - return ""; -}; - -Cell.prototype.setColor = function(color) { - this.color.r = color.r; - this.color.g = color.g; - this.color.b = color.b; -}; - -Cell.prototype.getColor = function() { - return this.color; -}; - -Cell.prototype.getType = function() { - return this.cellType; -}; - -Cell.prototype.getSize = function() { - // Calculates radius based on cell mass - return Math.ceil(Math.sqrt(100 * this.mass)); -}; - -Cell.prototype.getSquareSize = function() { - // R * R - return (100 * this.mass) >> 0; -}; - -Cell.prototype.addMass = function(n) { - // Check if the cell needs to autosplit before adding mass - if (this.mass > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) { - var splitMass = this.mass / 2; - var randomAngle = Math.random() * 6.28; // Get random angle - this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass); - } else { - this.mass = Math.min(this.mass, this.gameServer.config.playerMaxMass); - } - this.mass += n; -}; - -Cell.prototype.getSpeed = function() { - //var t = Math.PI * Math.PI; - //return this.gameServer.config.playerSpeed * 30 * - // Math.pow(this.getSize(), -0.01 / (Math.PI * Math.PI * Math.PI)); //-Math.PI / t);//1.5); +function Cell(nodeId, owner, position, mass, gameServer) { + this.nodeId = nodeId; + this.owner = owner; // playerTracker that owns this cell + this.color = { + r: 0, + g: 255, + b: 0 + }; + this.position = position; + this.mass = mass; // Starting mass of the cell + this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass + this.spiked = 0; // If 1, then this cell has spikes around it + + this.killedBy; // Cell that ate this cell + this.gameServer = gameServer; + + this.moveEngineTicks = 0; // Amount of times to loop the movement function + this.moveEngineSpeed = 0; + this.moveDecay = 0.85; + this.angle = 0; // Angle of movement + this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells +} + +module.exports = Cell; + +// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class + +Cell.prototype.getName = function() { + if (this.owner) + return this.owner.name; + return ""; +}; + +Cell.prototype.getSkin = function () { + if (this.owner) + return this.owner.skin; + return ""; +}; + +Cell.prototype.setColor = function(color) { + this.color.r = color.r; + this.color.g = color.g; + this.color.b = color.b; +}; + +Cell.prototype.getColor = function() { + return this.color; +}; + +Cell.prototype.getType = function() { + return this.cellType; +}; + +Cell.prototype.getSize = function() { + // Calculates radius based on cell mass + return Math.ceil(Math.sqrt(100 * this.mass)); +}; + +Cell.prototype.getSquareSize = function() { + // R * R + return (100 * this.mass) >> 0; +}; + +Cell.prototype.addMass = function(n) { + // Check if the cell needs to autosplit before adding mass + if (this.mass > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) { + var splitMass = this.mass / 2; + var randomAngle = Math.random() * 6.28; // Get random angle + this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass); + } else { + this.mass = Math.min(this.mass, this.gameServer.config.playerMaxMass); + } + this.mass += n; +}; + +Cell.prototype.getSpeed = function() { + //var t = Math.PI * Math.PI; + //return this.gameServer.config.playerSpeed * 30 * + // Math.pow(this.getSize(), -0.01 / (Math.PI * Math.PI * Math.PI)); //-Math.PI / t);//1.5); var k = 100 / 32; // TODO: implement mouse pos dependency var speed = this.getMaxSpeed(this.getSize()) * k; return speed * (1 / 0.04) * this.gameServer.config.playerSpeed; -}; - -Cell.prototype.getMaxSpeed = function (size) { +}; + +Cell.prototype.getMaxSpeed = function (size) { return (2.1106 / Math.pow(size, 0.449)); -} - -Cell.prototype.setAngle = function(radians) { - this.angle = radians; -}; - -Cell.prototype.getAngle = function() { - return this.angle; -}; - -Cell.prototype.setMoveEngineData = function(speed, ticks, decay) { - this.moveEngineSpeed = speed; - this.moveEngineTicks = ticks; - this.moveDecay = isNaN(decay) ? 0.75 : decay; -}; - -Cell.prototype.getEatingRange = function() { - return 0; // 0 for ejected cells -}; - -Cell.prototype.getKiller = function() { - return this.killedBy; -}; - -Cell.prototype.setKiller = function(cell) { - this.killedBy = cell; -}; - -// Functions - -Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) { - // Collision checking - if (this.position.y > bottomY) { - return false; - } - - if (this.position.y < topY) { - return false; - } - - if (this.position.x > rightX) { - return false; - } - - if (this.position.x < leftX) { - return false; - } - - return true; -}; - -// This collision checking function is based on CIRCLE shape -Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) { - // IF (O1O2 + r <= R) THEN collided. (O1O2: distance b/w 2 centers of cells) - // (O1O2 + r)^2 <= R^2 - // approximately, remove 2*O1O2*r because it requires sqrt(): O1O2^2 + r^2 <= R^2 - - var dx = this.position.x - objectPosition.x; - var dy = this.position.y - objectPosition.y; - - return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize); -}; - -Cell.prototype.visibleCheck = function(box, centerPos, cells) { - // Checks if this cell is visible to the player - var isThere = false; - if (this.mass < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); - else { - var cellSize = this.getSize(); - var lenX = cellSize + box.width >> 0; // Width of cell + width of the box (Int) - var lenY = cellSize + box.height >> 0; // Height of cell + height of the box (Int) - - isThere = (this.abs(this.position.x - centerPos.x) < lenX) && (this.abs(this.position.y - centerPos.y) < lenY); - } - if (isThere) { - // It is - // To save perfomance, check if any client's cell collides with this cell - for (var i = 0; i < cells.length; i++) { - var cell = cells[i]; - if (!cell) continue; - - var dist = this.getDist(this.position.x, this.position.y, cell.position.x, cell.position.y); - var collideDist = cell.getSize() + this.getSize(); - - if (dist < collideDist) { - return 2; - }// Colliding with one - } - return 1; // Not colliding with any - } - else return 0; -}; - -Cell.prototype.calcMovePhys = function(config) { - // Move, twice as slower - var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0); - var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0); - - // Movement engine - if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) this.moveEngineSpeed = 0; - var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay; - this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower - if (this.moveEngineTicks >= 0.5) this.moveEngineTicks -= 0.5; // Ticks passing twice as slower - - // Ejected cell collision - if (this.cellType == 3) { - for (var i = 0; i < this.gameServer.nodesEjected.length; i++) { - var check = this.gameServer.nodesEjected[i]; - - if (check.nodeId == this.nodeId) continue; // Don't check for yourself - - var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y); - var allowDist = this.getSize() + check.getSize(); // Allow cells to get in themselves a bit - - if (dist < allowDist) { - // Two ejected cells collided - var deltaX = this.position.x - check.position.x; - var deltaY = this.position.y - check.position.y; - var angle = Math.atan2(deltaX, deltaY); - - this.gameServer.setAsMovingNode(check); - check.moveEngineTicks += 1; - this.moveEngineTicks += 1; - - var move = allowDist - dist; - - X += Math.sin(angle) * move / 2; - Y += Math.cos(angle) * move / 2; - } - } - } - - // Border check - Bouncy physics - var radius = 40; - if ((this.position.x - radius) < -config.borderLeft) { - // Flip angle horizontally - Left side - this.angle = 6.28 - this.angle; - X = -config.borderLeft + radius; - } - if ((this.position.x + radius) > config.borderRight) { - // Flip angle horizontally - Right side - this.angle = 6.28 - this.angle; - X = config.borderRight - radius; - } - if ((this.position.y - radius) < -config.borderTop) { - // Flip angle vertically - Top side - this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - Y = -config.borderTop + radius; - } - if ((this.position.y + radius) > config.borderBottom) { - // Flip angle vertically - Bottom side - this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - Y = config.borderBottom - radius; - } - - // Set position - this.position.x = X >> 0; - this.position.y = Y >> 0; -}; - -// Override these - -Cell.prototype.sendUpdate = function() { - // Whether or not to include this cell in the update packet - return true; -}; - -Cell.prototype.onConsume = function(consumer, gameServer) { - // Called when the cell is consumed -}; - -Cell.prototype.onAdd = function(gameServer) { - // Called when this cell is added to the world -}; - -Cell.prototype.onRemove = function(gameServer) { - // Called when this cell is removed -}; - -Cell.prototype.onAutoMove = function(gameServer) { - // Called on each auto move engine tick -}; - -Cell.prototype.moveDone = function(gameServer) { - // Called when this cell finished moving with the auto move engine -}; - -// Lib - -Cell.prototype.abs = function(x) { - return x < 0 ? -x : x; -}; - -Cell.prototype.getDist = function(x1, y1, x2, y2) { - var xs = x2 - x1; - xs = xs * xs; - - var ys = y2 - y1; - ys = ys * ys; - - return Math.sqrt(xs + ys); -}; +} + +Cell.prototype.setAngle = function(radians) { + this.angle = radians; +}; + +Cell.prototype.getAngle = function() { + return this.angle; +}; + +Cell.prototype.setMoveEngineData = function(speed, ticks, decay) { + this.moveEngineSpeed = speed; + this.moveEngineTicks = ticks; + this.moveDecay = isNaN(decay) ? 0.75 : decay; +}; + +Cell.prototype.getEatingRange = function() { + return 0; // 0 for ejected cells +}; + +Cell.prototype.getKiller = function() { + return this.killedBy; +}; + +Cell.prototype.setKiller = function(cell) { + this.killedBy = cell; +}; + +// Functions + +Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) { + // Collision checking + if (this.position.y > bottomY) { + return false; + } + + if (this.position.y < topY) { + return false; + } + + if (this.position.x > rightX) { + return false; + } + + if (this.position.x < leftX) { + return false; + } + + return true; +}; + +// This collision checking function is based on CIRCLE shape +Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) { + // IF (O1O2 + r <= R) THEN collided. (O1O2: distance b/w 2 centers of cells) + // (O1O2 + r)^2 <= R^2 + // approximately, remove 2*O1O2*r because it requires sqrt(): O1O2^2 + r^2 <= R^2 + + var dx = this.position.x - objectPosition.x; + var dy = this.position.y - objectPosition.y; + + return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize); +}; + +Cell.prototype.visibleCheck = function(box, centerPos, cells) { + // Checks if this cell is visible to the player + var isThere = false; + if (this.mass < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); + else { + var cellSize = this.getSize(); + var lenX = cellSize + box.width >> 0; // Width of cell + width of the box (Int) + var lenY = cellSize + box.height >> 0; // Height of cell + height of the box (Int) + + isThere = (this.abs(this.position.x - centerPos.x) < lenX) && (this.abs(this.position.y - centerPos.y) < lenY); + } + if (isThere) { + // It is + // To save perfomance, check if any client's cell collides with this cell + for (var i = 0; i < cells.length; i++) { + var cell = cells[i]; + if (!cell) continue; + + var dist = this.getDist(this.position.x, this.position.y, cell.position.x, cell.position.y); + var collideDist = cell.getSize() + this.getSize(); + + if (dist < collideDist) { + return 2; + }// Colliding with one + } + return 1; // Not colliding with any + } + else return 0; +}; + +Cell.prototype.calcMovePhys = function(config) { + // Move, twice as slower + var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0); + var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0); + + // Movement engine + if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) this.moveEngineSpeed = 0; + var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay; + this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower + if (this.moveEngineTicks >= 0.5) this.moveEngineTicks -= 0.5; // Ticks passing twice as slower + + // Ejected cell collision + if (this.cellType == 3) { + for (var i = 0; i < this.gameServer.nodesEjected.length; i++) { + var check = this.gameServer.nodesEjected[i]; + + if (check.nodeId == this.nodeId) continue; // Don't check for yourself + + var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y); + var allowDist = this.getSize() + check.getSize(); // Allow cells to get in themselves a bit + + if (dist < allowDist) { + // Two ejected cells collided + var deltaX = this.position.x - check.position.x; + var deltaY = this.position.y - check.position.y; + var angle = Math.atan2(deltaX, deltaY); + + this.gameServer.setAsMovingNode(check); + check.moveEngineTicks += 1; + this.moveEngineTicks += 1; + + var move = allowDist - dist; + + X += Math.sin(angle) * move / 2; + Y += Math.cos(angle) * move / 2; + } + } + } + + // Border check - Bouncy physics + var radius = 40; + if ((this.position.x - radius) < -config.borderLeft) { + // Flip angle horizontally - Left side + this.angle = 6.28 - this.angle; + X = -config.borderLeft + radius; + } + if ((this.position.x + radius) > config.borderRight) { + // Flip angle horizontally - Right side + this.angle = 6.28 - this.angle; + X = config.borderRight - radius; + } + if ((this.position.y - radius) < -config.borderTop) { + // Flip angle vertically - Top side + this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; + Y = -config.borderTop + radius; + } + if ((this.position.y + radius) > config.borderBottom) { + // Flip angle vertically - Bottom side + this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; + Y = config.borderBottom - radius; + } + + // Set position + this.position.x = X >> 0; + this.position.y = Y >> 0; +}; + +// Override these + +Cell.prototype.sendUpdate = function() { + // Whether or not to include this cell in the update packet + return true; +}; + +Cell.prototype.onConsume = function(consumer, gameServer) { + // Called when the cell is consumed +}; + +Cell.prototype.onAdd = function(gameServer) { + // Called when this cell is added to the world +}; + +Cell.prototype.onRemove = function(gameServer) { + // Called when this cell is removed +}; + +Cell.prototype.onAutoMove = function(gameServer) { + // Called on each auto move engine tick +}; + +Cell.prototype.moveDone = function(gameServer) { + // Called when this cell finished moving with the auto move engine +}; + +// Lib + +Cell.prototype.abs = function(x) { + return x < 0 ? -x : x; +}; + +Cell.prototype.getDist = function(x1, y1, x2, y2) { + var xs = x2 - x1; + xs = xs * xs; + + var ys = y2 - y1; + ys = ys * ys; + + return Math.sqrt(xs + ys); +}; From 820251fc82c5ca857c4bdc279667503baa118429 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 00:26:58 +0200 Subject: [PATCH 019/417] Add skin support https://github.com/Barbosik/OgarMulti/issues/4#issue-158453099 https://github.com/OgarProject/Ogar/issues/422#issuecomment-182826493 --- src/packet/UpdateNodes.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index d6394956d..4a2379e85 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -134,11 +134,14 @@ UpdateNodes.prototype.build5 = function () { var cellX = node.position.x + this.scrambleX; var cellY = node.position.y + this.scrambleY; var cellSize = node.getSize(); + var skinName = node.getSkin(); var cellName = node.getName(); + if (!skinName) skinName = ""; if (!cellName) cellName = ""; var isVirus = (node.spiked & 0x01) != 0; + var isSkinPresent = !isVirus && skinName != null && skinName.length > 0; var isAgitated = false; // true = high wave amplitude on a cell outline var isEject = node.cellType == 3; @@ -154,12 +157,19 @@ UpdateNodes.prototype.build5 = function () { var flags = 0; if (isVirus) flags |= 0x01; + if (isSkinPresent) + flags |= 0x04; if (isAgitated) flags |= 0x10; if (isEject) flags |= 0x20; buffer.writeUInt8(flags >> 0); // Flags + if (isSkinPresent) { + buffer.writeUTF8String(skinName); // Skin Name in UTF8 + buffer.writeUInt8(0); // Zero-terminator + } + for (var j = 0; j < cellName.length && cellName.charCodeAt(j) != 0; j++) { buffer.writeUInt16(cellName.charCodeAt(j) >> 0); // Cell Name in Unicode } @@ -213,7 +223,7 @@ UpdateNodes.prototype.build6 = function () { var cellX = node.position.x + this.scrambleX; var cellY = node.position.y + this.scrambleY; var cellSize = node.getSize(); - var skinName = null; + var skinName = node.getSkin(); var cellName = node.getName(); var isVirus = (node.spiked & 0x01) != 0; From f8bce1a6a57a25572da2b5a17871279afd8199a8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 00:27:53 +0200 Subject: [PATCH 020/417] Add skin support https://github.com/Barbosik/OgarMulti/issues/4#issue-158453099 https://github.com/OgarProject/Ogar/issues/422#issuecomment-182826493 --- src/PacketHandler.js | 30 ++++++++++++++++++++++-------- src/PlayerTracker.js | 9 +++++++++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 1bc99010a..83ae838d1 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -47,9 +47,6 @@ PacketHandler.prototype.handleMessage = function(message) { } else { text = this.readStringUtf8(bufferName); } - if (text.length > this.gameServer.config.playerMaxNickLength) { - text = text.substring(0, this.gameServer.config.playerMaxNickLength); - } } this.setNickname(text); break; @@ -115,7 +112,7 @@ PacketHandler.prototype.handleMessage = function(message) { }; PacketHandler.prototype.readStringUnicode = function (dataBuffer) { - if (dataBuffer.length > 256) return ""; + if (dataBuffer.length > 512) return ""; var buffer = new Buffer(dataBuffer); var text = ""; @@ -133,7 +130,7 @@ PacketHandler.prototype.readStringUnicode = function (dataBuffer) { } PacketHandler.prototype.readStringUtf8 = function (dataBuffer) { - if (dataBuffer.length > 256) return ""; + if (dataBuffer.length > 512) return ""; var buffer = new Buffer(dataBuffer); var maxLen = buffer.length; @@ -147,11 +144,28 @@ PacketHandler.prototype.readStringUtf8 = function (dataBuffer) { return text; } -PacketHandler.prototype.setNickname = function(newNick) { +PacketHandler.prototype.setNickname = function(text) { var client = this.socket.playerTracker; if (client.cells.length < 1) { - // Set name first - client.setName(newNick); + var name = ""; + var skin = ""; + if (text != null && text.length != 0) { + var n = -1; + if (text.charAt(0) == '<' && (n = text.indexOf('>', 1)) >= 0) { + skin = "%" + text.slice(1, n); + name = text.slice(n + 1); + //} else if (text[0] == "|" && (n = text.indexOf("|", 1)) >= 0) { + // skin = ":http://i.imgur.com/" + text.slice(1, n); + // name = text.slice(n + 1); + } else { + name = text; + } + } + if (name.length > this.gameServer.config.playerMaxNickLength) { + name = name.substring(0, this.gameServer.config.playerMaxNickLength); + } + client.setName(name); + client.setSkin(skin); var c = this.gameServer.config; this.socket.sendPacket(new Packet.ClearNodes()); diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 6f59d0005..a4d0809fd 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -5,6 +5,7 @@ function PlayerTracker(gameServer, socket) { this.pID = -1; this.disconnect = -1; // Disconnection this.name = ""; + this.skin = ""; this.gameServer = gameServer; this.socket = socket; this.nodeAdditionQueue = []; @@ -79,6 +80,14 @@ PlayerTracker.prototype.getName = function() { return this.name; }; +PlayerTracker.prototype.setSkin = function (skin) { + this.skin = skin; +}; + +PlayerTracker.prototype.getSkin = function () { + return this.skin; +}; + PlayerTracker.prototype.getScore = function(reCalcScore) { if (reCalcScore) { var s = 0; From fc695b2fa1d70d944283c241b436ba222d9be73f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 00:46:36 +0200 Subject: [PATCH 021/417] refactoring --- src/PlayerTracker.js | 233 +++++++++++++++++++++---------------------- 1 file changed, 115 insertions(+), 118 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index a4d0809fd..f26e8bd64 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -112,149 +112,146 @@ PlayerTracker.prototype.getTeam = function() { // Functions -PlayerTracker.prototype.update = function() { +PlayerTracker.prototype.update = function () { // if initialization is not complete yet then do not update - if (this.socket.packetHandler.protocol == 0) - return; + if (this.socket.packetHandler.protocol == 0) + return; - //// Async update, perfomance reasons - //setTimeout(function() { - // First reset colliding nodes - this.collidingNodes = []; - - // Move packet update - if (this.movePacketTriggered) { - this.movePacketTriggered = false; - this.shouldMoveCells = true; - } else { - this.shouldMoveCells = false; - } - // Actions buffer (So that people cant spam packets) - if (this.socket.packetHandler.pressSpace) { // Split cell - if (!this.mergeOverride) this.gameServer.gameMode.pressSpace(this.gameServer, this); - this.socket.packetHandler.pressSpace = false; - } - - if (this.socket.packetHandler.pressW) { // Eject mass - this.gameServer.gameMode.pressW(this.gameServer, this); - this.socket.packetHandler.pressW = false; - } + // First reset colliding nodes + this.collidingNodes = []; - if (this.socket.packetHandler.pressQ) { // Q Press - this.gameServer.gameMode.pressQ(this.gameServer, this); - this.socket.packetHandler.pressQ = false; - } + // Move packet update + if (this.movePacketTriggered) { + this.movePacketTriggered = false; + this.shouldMoveCells = true; + } else { + this.shouldMoveCells = false; + } + // Actions buffer (So that people cant spam packets) + if (this.socket.packetHandler.pressSpace) { // Split cell + if (!this.mergeOverride) this.gameServer.gameMode.pressSpace(this.gameServer, this); + this.socket.packetHandler.pressSpace = false; + } - var updateNodes = []; // Nodes that need to be updated via packet + if (this.socket.packetHandler.pressW) { // Eject mass + this.gameServer.gameMode.pressW(this.gameServer, this); + this.socket.packetHandler.pressW = false; + } - // Remove nodes from visible nodes if possible - var d = 0; - while (d < this.nodeDestroyQueue.length) { - var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[d]); - if (index > -1) { - this.visibleNodes.splice(index, 1); - d++; // Increment - } else { - // Node was never visible anyways - this.nodeDestroyQueue.splice(d, 1); - } - } + if (this.socket.packetHandler.pressQ) { // Q Press + this.gameServer.gameMode.pressQ(this.gameServer, this); + this.socket.packetHandler.pressQ = false; + } - // Get visible nodes every 400 ms - var nonVisibleNodes = []; // Nodes that are not visible - if (this.tickViewBox <= 0) { - var newVisible = this.calcViewBox(); - try { // Add a try block in any case + var updateNodes = []; // Nodes that need to be updated via packet - // Compare and destroy nodes that are not seen - for (var i = 0; i < this.visibleNodes.length; i++) { - var index = newVisible.indexOf(this.visibleNodes[i]); - if (index == -1) { - // Not seen by the client anymore - nonVisibleNodes.push(this.visibleNodes[i]); - } - } + // Remove nodes from visible nodes if possible + var d = 0; + while (d < this.nodeDestroyQueue.length) { + var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[d]); + if (index > -1) { + this.visibleNodes.splice(index, 1); + d++; // Increment + } else { + // Node was never visible anyways + this.nodeDestroyQueue.splice(d, 1); + } + } - // Add nodes to client's screen if client has not seen it already - for (var i = 0; i < newVisible.length; i++) { - var index = this.visibleNodes.indexOf(newVisible[i]); - if (index == -1) { - updateNodes.push(newVisible[i]); - } + // Get visible nodes every 400 ms + var nonVisibleNodes = []; // Nodes that are not visible + if (this.tickViewBox <= 0) { + var newVisible = this.calcViewBox(); + try { // Add a try block in any case + + // Compare and destroy nodes that are not seen + for (var i = 0; i < this.visibleNodes.length; i++) { + var index = newVisible.indexOf(this.visibleNodes[i]); + if (index == -1) { + // Not seen by the client anymore + nonVisibleNodes.push(this.visibleNodes[i]); } - } catch(err) { - console.error(err); } - - this.visibleNodes = newVisible; - // Reset Ticks - this.tickViewBox = 0; - } else { - this.tickViewBox--; - // Add nodes to screen - for (var i = 0; i < this.nodeAdditionQueue.length; i++) { - var node = this.nodeAdditionQueue[i]; - this.visibleNodes.push(node); - updateNodes.push(node); + + // Add nodes to client's screen if client has not seen it already + for (var i = 0; i < newVisible.length; i++) { + var index = this.visibleNodes.indexOf(newVisible[i]); + if (index == -1) { + updateNodes.push(newVisible[i]); + } } + } catch (err) { + console.error(err); + } + + this.visibleNodes = newVisible; + // Reset Ticks + this.tickViewBox = 0; + } else { + this.tickViewBox--; + // Add nodes to screen + for (var i = 0; i < this.nodeAdditionQueue.length; i++) { + var node = this.nodeAdditionQueue[i]; + this.visibleNodes.push(node); + updateNodes.push(node); } + } - // Update moving nodes - for (var i = 0; i < this.visibleNodes.length; i++) { - var node = this.visibleNodes[i]; - if (node.sendUpdate()) { - // Sends an update if cell is moving - updateNodes.push(node); - } + // Update moving nodes + for (var i = 0; i < this.visibleNodes.length; i++) { + var node = this.visibleNodes[i]; + if (node.sendUpdate()) { + // Sends an update if cell is moving + updateNodes.push(node); } + } - // Send packet - this.socket.sendPacket(new Packet.UpdateNodes( - this.nodeDestroyQueue, + // Send packet + this.socket.sendPacket(new Packet.UpdateNodes( + this.nodeDestroyQueue, updateNodes, nonVisibleNodes, this.scrambleX, this.scrambleY - )); - - this.nodeDestroyQueue = []; // Reset destroy queue - this.nodeAdditionQueue = []; // Reset addition queue - - // Update leaderboard - if (this.tickLeaderboard <= 0) { - if (this.gameServer.lb_packet != null) - this.socket.sendPacket(this.gameServer.lb_packet); - this.tickLeaderboard = 10; // 20 ticks = 1 second - } else { - this.tickLeaderboard--; - } - - // Handles disconnections - if (this.disconnect > -1) { - // Player has disconnected... remove it when the timer hits -1 - this.disconnect--; - // Also remove it when its cells are completely eaten not to back up dead clients - if (this.disconnect == -1 || this.cells.length == 0) { - // Remove all client cells - var len = this.cells.length; - for (var i = 0; i < len; i++) { - var cell = this.socket.playerTracker.cells[0]; + )); - if (!cell) { - continue; - } + this.nodeDestroyQueue = []; // Reset destroy queue + this.nodeAdditionQueue = []; // Reset addition queue - this.gameServer.removeNode(cell); - } + // Update leaderboard + if (this.tickLeaderboard <= 0) { + if (this.gameServer.lb_packet != null) + this.socket.sendPacket(this.gameServer.lb_packet); + this.tickLeaderboard = 10; // 20 ticks = 1 second + } else { + this.tickLeaderboard--; + } - // Remove from client list - var index = this.gameServer.clients.indexOf(this.socket); - if (index != -1) { - this.gameServer.clients.splice(index, 1); + // Handles disconnections + if (this.disconnect > -1) { + // Player has disconnected... remove it when the timer hits -1 + this.disconnect--; + // Also remove it when its cells are completely eaten not to back up dead clients + if (this.disconnect == -1 || this.cells.length == 0) { + // Remove all client cells + var len = this.cells.length; + for (var i = 0; i < len; i++) { + var cell = this.socket.playerTracker.cells[0]; + + if (!cell) { + continue; } + + this.gameServer.removeNode(cell); + } + + // Remove from client list + var index = this.gameServer.clients.indexOf(this.socket); + if (index != -1) { + this.gameServer.clients.splice(index, 1); } } - //}.bind(this), 0); + } }; // Viewing box From f4a80d30108643555df287d3d45f7a181cad28e3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 00:58:49 +0200 Subject: [PATCH 022/417] Add some checks for SetBorder --- src/packet/SetBorder.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index acd4bad4f..1f7566bf4 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -21,7 +21,7 @@ SetBorder.prototype.build = function(protocol) { buffer.writeDouble(this.top); buffer.writeDouble(this.right); buffer.writeDouble(this.bottom); - if (typeof this.gameType != "undefined") { + if (typeof this.gameType != "undefined" && this.gameType != null) { buffer.writeUInt32(this.gameType >> 0); var name = this.serverName; if (typeof name == "undefined" || name == null) { From 931c17ee85fcb0731383507f23f0a865b64de8bf Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 01:47:21 +0200 Subject: [PATCH 023/417] decrease leaderboard update rate --- src/GameServer.js | 2 +- src/PlayerTracker.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index d7c866c2f..1783b1a75 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -372,7 +372,7 @@ GameServer.prototype.updateClients = function () { GameServer.prototype.updateLeaderboard = function () { // Update leaderboard with the gamemode's method - if ((this.tickCounter % 40) == 0) { + if ((this.tickCounter % 25) == 0) { this.leaderboard = []; this.gameMode.updateLB(this); this.lb_packet = new Packet.UpdateLeaderboard(this.leaderboard, this.gameMode.packetLB); diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index f26e8bd64..a2732c4ba 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -222,7 +222,7 @@ PlayerTracker.prototype.update = function () { if (this.tickLeaderboard <= 0) { if (this.gameServer.lb_packet != null) this.socket.sendPacket(this.gameServer.lb_packet); - this.tickLeaderboard = 10; // 20 ticks = 1 second + this.tickLeaderboard = 50; } else { this.tickLeaderboard--; } From 938fed27c17776e81cabbe740de520e9dcc68e3e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 02:37:38 +0200 Subject: [PATCH 024/417] Fix movement physics --- src/entity/Cell.js | 14 ++------------ src/entity/PlayerCell.js | 10 ++++++++-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 9c53ff8b0..94b53d35c 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -74,20 +74,10 @@ Cell.prototype.addMass = function(n) { }; Cell.prototype.getSpeed = function() { - //var t = Math.PI * Math.PI; - //return this.gameServer.config.playerSpeed * 30 * - // Math.pow(this.getSize(), -0.01 / (Math.PI * Math.PI * Math.PI)); //-Math.PI / t);//1.5); - - var k = 100 / 32; // TODO: implement mouse pos dependency - - var speed = this.getMaxSpeed(this.getSize()) * k; - return speed * (1 / 0.04) * this.gameServer.config.playerSpeed; + var speed = 2.1106 / Math.pow(this.getSize(), 0.449); + return speed * (1 / 0.04) * this.gameServer.config.playerSpeed * 2; // have no idea why it twice slower }; -Cell.prototype.getMaxSpeed = function (size) { - return (2.1106 / Math.pow(size, 0.449)); -} - Cell.prototype.setAngle = function(radians) { this.angle = radians; }; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index e24d7911f..5e8461ea1 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -49,8 +49,14 @@ PlayerCell.prototype.calcMove = function(x2, y2, gameServer) { return; } - var dist = this.getDist(this.position.x, this.position.y, x2, y2); - var speed = Math.min(this.getSpeed(), dist) / 2; // Twice as slower + //var dist = this.getDist(this.position.x, this.position.y, x2, y2); + //var speed = Math.min(this.getSpeed(), dist) / 2; // Twice as slower + var dist = Math.sqrt(deltaX*deltaX + deltaY*deltaY); + dist = Math.min(dist, 32); + var speed = 0; + if (dist >= 1) { + speed = this.getSpeed() * dist / 32; + } // Move cell this.position.x += Math.sin(angle) * speed; From 22fe56d091616c37827ff529144a7b98b890e894 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 04:37:54 +0200 Subject: [PATCH 025/417] Perfrormance optimization (size) --- src/GameServer.js | 18 +++++++++--------- src/PlayerTracker.js | 8 ++++---- src/ai/BotPlayer.js | 18 +++++++++--------- src/entity/Cell.js | 31 ++++++++++++++++++++----------- src/entity/EjectedMass.js | 14 +------------- src/entity/Food.js | 18 +++--------------- src/entity/PlayerCell.js | 4 ++-- src/entity/Virus.js | 12 ++++++------ src/gamemodes/Experimental.js | 12 ++++++------ src/gamemodes/FFA.js | 2 +- src/gamemodes/TeamX.js | 18 +++++++++--------- src/gamemodes/TeamZ.js | 32 ++++++++++++++++---------------- src/gamemodes/Teams.js | 4 ++-- src/modules/CommandList.js | 2 +- 14 files changed, 89 insertions(+), 104 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 1783b1a75..63fd50d67 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -555,7 +555,7 @@ GameServer.prototype.updateMoveEngine = function() { for (var i = 0; i < client.cells.length; i++) sorted.push(client.cells[i]); sorted.sort(function(a, b) { - return b.mass - a.mass; + return b.getMass() - a.getMass(); }); // Go cell by cell @@ -644,7 +644,7 @@ GameServer.prototype.splitCells = function(client) { var angle = Math.atan2(deltaX, deltaY); if (angle == 0) angle = Math.PI / 2; - if (this.createPlayerCell(client, cell, angle, cell.mass / 2) == true) splitCells++; + if (this.createPlayerCell(client, cell, angle, cell.getMass() / 2) == true) splitCells++; } }; @@ -656,7 +656,7 @@ GameServer.prototype.createPlayerCell = function(client, parent, angle, mass) { return false; } - if (parent.mass < this.config.playerMinMassSplit) { + if (parent.getMass() < this.config.playerMinMassSplit) { // Minimum mass to split return false; } @@ -680,7 +680,7 @@ GameServer.prototype.createPlayerCell = function(client, parent, angle, mass) { newCell.collisionRestoreTicks = 12; parent.collisionRestoreTicks = 12; newCell.calcMergeTime(this.config.playerRecombineTime); - parent.mass -= mass; // Remove mass from parent cell + parent.setMass(parent.getMass() - mass); // Remove mass from parent cell // Add to node list this.addNode(newCell); @@ -705,7 +705,7 @@ GameServer.prototype.ejectMass = function(client) { continue; } - if (cell.mass < this.config.playerMinMassEject) { + if (cell.getMass() < this.config.playerMinMassEject) { continue; } @@ -724,7 +724,7 @@ GameServer.prototype.ejectMass = function(client) { }; // Remove mass from parent cell - cell.mass -= this.config.ejectMassLoss; + cell.setMass(cell.getMass() - this.config.ejectMassLoss); // Randomize angle angle += (Math.random() * 0.6) - 0.3; @@ -840,7 +840,7 @@ GameServer.prototype.getCellsInRange = function(cell) { } // Make sure the cell is big enough to be eaten. - if ((check.mass * multiplier) > cell.mass) { + if ((check.getMass() * multiplier) > cell.getMass()) { continue; } @@ -923,8 +923,8 @@ GameServer.prototype.updateCells = function() { } // Mass decay - if (cell.mass >= this.config.playerMinMassDecay) { - cell.mass *= massDecay; + if (cell.getMass() >= this.config.playerMinMassDecay) { + cell.setMass(cell.getMass() * massDecay); } } }; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index a2732c4ba..95e15f7e6 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -93,7 +93,7 @@ PlayerTracker.prototype.getScore = function(reCalcScore) { var s = 0; for (var i = 0; i < this.cells.length; i++) { if (!this.cells[i]) return; // Error - s += this.cells[i].mass; + s += this.cells[i].getMass(); this.score = s; } } @@ -288,9 +288,9 @@ PlayerTracker.prototype.updateCenter = function() { // Get center of cells if (!this.cells[i]) continue; var cell = this.cells[i]; - X += cell.position.x * cell.mass; - Y += cell.position.y * cell.mass; - allSize += cell.mass; + X += cell.position.x * cell.getMass(); + Y += cell.position.y * cell.getMass(); + allSize += cell.getMass(); } this.centerPos.x = X / allSize; diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index 2fabf8ce7..12bf5b40c 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -23,7 +23,7 @@ BotPlayer.prototype.getLowestCell = function() { // Sort the cells by Array.sort() function to avoid errors var sorted = this.cells.valueOf(); sorted.sort(function(a, b) { - return b.mass - a.mass; + return b.getMass() - a.getMass(); }); return sorted[0]; @@ -85,21 +85,21 @@ BotPlayer.prototype.decide = function(cell) { if (check.cellType == 0) { // Player cell if (this.gameServer.gameMode.haveTeams && (cell.owner.team == check.owner.team)) influence = 0; // Same team cell - else if (cell.mass / 1.3 > check.mass) influence = check.getSize() * 2.5; // Can eat it - else if (check.mass / 1.3 > cell.mass) influence = -check.getSize(); // Can eat me + else if (cell.getMass() / 1.3 > check.getMass()) influence = check.getSize() * 2.5; // Can eat it + else if (check.getMass() / 1.3 > cell.getMass()) influence = -check.getSize(); // Can eat me } else if (check.cellType == 1) { // Food influence = 1; } else if (check.cellType == 2) { // Virus - if (cell.mass / 1.3 > check.mass) { + if (cell.getMass() / 1.3 > check.getMass()) { // Can eat it if (this.cells.length == this.gameServer.config.playerMaxCells) influence = check.getSize() * 2.5; // Won't explode else influence = -1; // Can explode } } else if (check.cellType == 3) { // Ejected mass - if (cell.mass / 1.3 > check.mass) influence = check.getSize(); + if (cell.getMass() / 1.3 > check.getMass()) influence = check.getSize(); } else { influence = check.getSize(); // Might be TeamZ } @@ -127,7 +127,7 @@ BotPlayer.prototype.decide = function(cell) { var force = displacement.normalize().scale(influence); // Splitting conditions - if (check.cellType == 0 && cell.mass / 2.6 > check.mass && cell.mass / 5 < check.mass && + if (check.cellType == 0 && cell.getMass() / 2.6 > check.getMass() && cell.getMass() / 5 < check.getMass() && (!split) && this.splitCooldown == 0 && this.cells.length < 3) { var endDist = Math.max(this.splitDistance(cell), cell.getSize() * 4); @@ -149,7 +149,7 @@ BotPlayer.prototype.decide = function(cell) { if (split) { // Can be shortened but I'm too lazy if (threats.length > 0) { - if (this.largest(threats).mass / 2.6 > cell.mass) { // ??? but works + if (this.largest(threats).getMass() / 2.6 > cell.getMass()) { // ??? but works // Splitkill the target this.mouse = { x: splitTarget.position.x, @@ -183,7 +183,7 @@ BotPlayer.prototype.largest = function(list) { // Sort the cells by Array.sort() function to avoid errors var sorted = list.valueOf(); sorted.sort(function(a, b) { - return b.mass - a.mass; + return b.getMass() - a.getMass(); }); return sorted[0]; @@ -191,7 +191,7 @@ BotPlayer.prototype.largest = function(list) { BotPlayer.prototype.splitDistance = function(cell) { // Calculate split distance and check if it is larger than the raw distance - var mass = cell.mass; + var mass = cell.getMass(); var t = Math.PI * Math.PI; var modifier = 3 + Math.log(1 + mass) / 10; var splitSpeed = cell.owner.gameServer.config.playerSpeed * 30 * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 94b53d35c..88b359d64 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -7,9 +7,11 @@ function Cell(nodeId, owner, position, mass, gameServer) { b: 0 }; this.position = position; - this.mass = mass; // Starting mass of the cell + this._size = 0; + this._mass = 0; + this.setMass(mass); // Starting mass of the cell this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass - this.spiked = 0; // If 1, then this cell has spikes around it + this.spiked = 0; // If 1, then this cell has spikes around it this.killedBy; // Cell that ate this cell this.gameServer = gameServer; @@ -52,25 +54,32 @@ Cell.prototype.getType = function() { }; Cell.prototype.getSize = function() { - // Calculates radius based on cell mass - return Math.ceil(Math.sqrt(100 * this.mass)); + return this._size; +}; + +Cell.prototype.getMass = function () { + return this._mass; +}; + +Cell.prototype.setMass = function (mass) { + this._mass = mass; + this._size = Math.ceil(Math.sqrt(100 * mass)); }; Cell.prototype.getSquareSize = function() { - // R * R - return (100 * this.mass) >> 0; + return this._size * this._size; }; Cell.prototype.addMass = function(n) { // Check if the cell needs to autosplit before adding mass - if (this.mass > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) { - var splitMass = this.mass / 2; + if (this.getMass() > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) { + var splitMass = this.getMass() / 2; var randomAngle = Math.random() * 6.28; // Get random angle this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass); } else { - this.mass = Math.min(this.mass, this.gameServer.config.playerMaxMass); + this.setMass(Math.min(this.getMass(), this.gameServer.config.playerMaxMass)); } - this.mass += n; + this.setMass(this.getMass() + n); }; Cell.prototype.getSpeed = function() { @@ -142,7 +151,7 @@ Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) { Cell.prototype.visibleCheck = function(box, centerPos, cells) { // Checks if this cell is visible to the player var isThere = false; - if (this.mass < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); + if (this.getMass() < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); else { var cellSize = this.getSize(); var lenX = cellSize + box.width >> 0; // Width of cell + width of the box (Int) diff --git a/src/entity/EjectedMass.js b/src/entity/EjectedMass.js index e28468574..aae3b7574 100644 --- a/src/entity/EjectedMass.js +++ b/src/entity/EjectedMass.js @@ -4,8 +4,6 @@ function EjectedMass() { Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = 3; - this.size = Math.ceil(Math.sqrt(100 * this.mass)); - this.squareSize = (100 * this.mass) >> 0; // not being decayed -> calculate one time this.addedAntiTeam = false; // Not to affect anti-teaming two times } @@ -21,16 +19,6 @@ EjectedMass.prototype.addMass = function(n) { return; // Do nothing, this is an ejected cell }; - -// Cell-specific functions -EjectedMass.prototype.getSize = function() { - return this.size; -}; - -EjectedMass.prototype.getSquareSize = function() { - return this.squareSize; -}; - EjectedMass.prototype.calcMove = null; // Only for player controlled movement // Main Functions @@ -67,7 +55,7 @@ EjectedMass.prototype.onRemove = function(gameServer) { EjectedMass.prototype.onConsume = function(consumer, gameServer) { // Adds mass to consumer - consumer.addMass(this.mass); + consumer.addMass(this.getMass()); }; EjectedMass.prototype.onAutoMove = function(gameServer) { diff --git a/src/entity/Food.js b/src/entity/Food.js index fd8d4a4a2..7fbc9cfcc 100644 --- a/src/entity/Food.js +++ b/src/entity/Food.js @@ -4,8 +4,6 @@ function Food() { Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = 1; - this.size = Math.ceil(Math.sqrt(100 * this.mass)); - this.squareSize = (100 * this.mass) >> 0; // not being decayed -> calculate one time this.shouldSendUpdate = false; if (this.gameServer.config.foodMassGrow && @@ -17,26 +15,16 @@ function Food() { module.exports = Food; Food.prototype = new Cell(); -Food.prototype.getSize = function() { - return this.size; -}; - -Food.prototype.getSquareSize = function() { - return this.squareSize; -}; - Food.prototype.calcMove = null; // Food has no need to move // Main Functions Food.prototype.grow = function() { setTimeout(function() { - this.mass++; // food mass increased, we need to recalculate its size and squareSize, and send update to client side - this.size = Math.ceil(Math.sqrt(100 * this.mass)); - this.squareSize = (100 * this.mass) >> 0; + this.setMass(this.getMass() + 1); // food mass increased, we need to recalculate its size and squareSize, and send update to client side this.shouldSendUpdate = true; - if (this.mass < this.gameServer.config.foodMassLimit) { + if (this.getMass() < this.gameServer.config.foodMassLimit) { this.grow(); } }.bind(this), this.gameServer.config.foodMassTimeout * 1000); @@ -59,5 +47,5 @@ Food.prototype.onRemove = function(gameServer) { }; Food.prototype.onConsume = function(consumer, gameServer) { - consumer.addMass(this.mass); + consumer.addMass(this.getMass()); }; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 5e8461ea1..942ebbd8a 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -117,8 +117,8 @@ PlayerCell.prototype.onConsume = function(consumer, gameServer) { // Add an inefficiency for eating other players' cells var factor = ( consumer.owner === this.owner ? 1 : gameServer.config.playerMassAbsorbed ); // Anti-bot measure - factor = (consumer.mass >= 625 && this.mass <= 17 && gameServer.config.playerBotGrowEnabled == 1) ? 0 : factor; - consumer.addMass(factor * this.mass); + factor = (consumer.getMass() >= 625 && this.getMass() <= 17 && gameServer.config.playerBotGrowEnabled == 1) ? 0 : factor; + consumer.addMass(factor * this.getMass()); }; PlayerCell.prototype.onAdd = function(gameServer) { diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 9256c10d3..10a3aa77d 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -16,13 +16,13 @@ Virus.prototype.calcMove = null; // Only for player controlled movement Virus.prototype.feed = function(feeder, gameServer) { if (this.moveEngineTicks == 0) this.setAngle(feeder.getAngle()); // Set direction if the virus explodes - this.mass += feeder.mass; + this.setMass(this.getMass() + feeder.getMass()); this.fed++; // Increase feed count gameServer.removeNode(feeder); // Check if the virus is going to explode if (this.fed >= gameServer.config.virusFeedAmount) { - this.mass = gameServer.config.virusStartMass; // Reset mass + this.setMass(gameServer.config.virusStartMass); // Reset mass this.fed = 0; gameServer.shootVirus(this); } @@ -39,19 +39,19 @@ Virus.prototype.onConsume = function(consumer, gameServer) { var client = consumer.owner; // Cell consumes mass before any calculation - consumer.addMass(this.mass); + consumer.addMass(this.getMass()); - var maxSplits = Math.floor(consumer.mass / 16) - 1; // Maximum amount of splits + var maxSplits = Math.floor(consumer.getMass() / 16) - 1; // Maximum amount of splits var numSplits = gameServer.config.playerMaxCells - client.cells.length; // Get number of splits numSplits = Math.min(numSplits, maxSplits); - var splitMass = Math.min(consumer.mass / (numSplits + 1), 24); // Maximum size of new splits + var splitMass = Math.min(consumer.getMass() / (numSplits + 1), 24); // Maximum size of new splits // Cell cannot split any further if (numSplits <= 0) { return; } - var mass = consumer.mass; // Mass of the consumer + var mass = consumer.getMass(); // Mass of the consumer var bigSplits = []; // Big splits // Big cells will split into cells larger than 24 mass diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index f80c4fc61..85f010347 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -171,20 +171,20 @@ MotherCell.prototype.update = function(gameServer) { i++; } } - if (this.mass > 222) { + if (this.getMass() > 222) { // Always spawn food if the mother cell is larger than 222 var cellSize = gameServer.config.foodMass; - var remaining = this.mass - 222; + var remaining = this.getMass() - 222; var maxAmount = Math.min(Math.floor(remaining / cellSize), 2); for (var i = 0; i < maxAmount; i++) { this.spawnFood(gameServer); - this.mass -= cellSize; + this.setMass(this.getMass() - cellSize); } } }; MotherCell.prototype.checkEat = function(gameServer) { - var safeMass = this.mass * .78; + var safeMass = this.getMass() * .78; // Loop for potential prey for (var i in gameServer.nodesPlayer) { @@ -206,7 +206,7 @@ MotherCell.prototype.checkEat = function(gameServer) { }; MotherCell.prototype.checkEatCell = function(check, safeMass, gameServer) { - if ((check.getType() == 1) || (check.mass > safeMass)) { + if ((check.getType() == 1) || (check.getMass() > safeMass)) { // Too big to be consumed or check is a food cell return; } @@ -217,7 +217,7 @@ MotherCell.prototype.checkEatCell = function(check, safeMass, gameServer) { if (dist < allowDist) { // Eat it gameServer.removeNode(check); - this.mass += check.mass; + this.setMass(this.getMass() + check.getMass()); } }; diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index 35250a2cf..5ab8c5963 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -60,7 +60,7 @@ FFA.prototype.onPlayerSpawn = function(gameServer, player) { x: e.position.x, y: e.position.y }; - startMass = Math.max(e.mass, gameServer.config.playerStartMass); + startMass = Math.max(e.getMass(), gameServer.config.playerStartMass); var color = e.getColor(); player.setColor({ diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 1391fea2e..a9671566e 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -228,7 +228,7 @@ TeamX.prototype.onServerInit = function(gameServer) { } // Make sure the cell is big enough to be eaten. - if ((check.mass * multiplier) > cell.mass) { + if ((check.getMass() * multiplier) > cell.getMass()) { continue; } @@ -344,32 +344,32 @@ MotherCell.prototype.getEatingRange = function() { MotherCell.prototype.update = function(gameServer) { // Add mass - this.mass += .25; + this.setMass(this.getMass() + .25); // Spawn food var maxFood = 10; // Max food spawned per tick var i = 0; // Food spawn counter - while ((this.mass > gameServer.gameMode.motherCellMass) && (i < maxFood)) { + while ((this.getMass() > gameServer.gameMode.motherCellMass) && (i < maxFood)) { // Only spawn if food cap hasn been reached if (gameServer.currentFood < gameServer.config.foodMaxAmount) { this.spawnFood(gameServer); } // Incrementers - this.mass--; + this.setMass(this.getMass() - 1); i++; } }; MotherCell.prototype.checkEat = function(gameServer) { - var safeMass = this.mass * .9; + var safeMass = this.getMass() * .9; var r = this.getSize(); // The box area that the checked cell needs to be in to be considered eaten // Loop for potential prey for (var i in gameServer.nodesPlayer) { var check = gameServer.nodesPlayer[i]; - if (check.mass > safeMass) { + if (check.getMass() > safeMass) { // Too big to be consumed continue; } @@ -379,13 +379,13 @@ MotherCell.prototype.checkEat = function(gameServer) { if ((this.abs(this.position.x - check.position.x) < len) && (this.abs(this.position.y - check.position.y) < len)) { // Eats the cell gameServer.removeNode(check); - this.mass += check.mass; + this.setMass(this.getMass() + check.getMass()); } } for (var i in gameServer.movingNodes) { var check = gameServer.movingNodes[i]; - if ((check.getType() == 1) || (check.mass > safeMass)) { + if ((check.getType() == 1) || (check.getMass() > safeMass)) { // Too big to be consumed/ No player cells continue; } @@ -395,7 +395,7 @@ MotherCell.prototype.checkEat = function(gameServer) { if ((this.abs(this.position.x - check.position.x) < len) && (this.abs(this.position.y - check.position.y) < len)) { // Eat the cell gameServer.removeNode(check); - this.mass += check.mass; + this.setMass(this.getMass() + check.getMass()); } } }; diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 70ea79cf8..91265bbed 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -246,7 +246,7 @@ TeamZ.prototype.startGame = function(gameServer) { var cell = client.cells[j]; if (cell) { cell.setColor(client.color); - cell.mass = gameServer.config.playerStartMass; + cell.setMass(gameServer.config.playerStartMass); this.resetSpeedCell(cell); } } @@ -486,7 +486,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { } // Make sure the cell is big enough to be eaten. - if ((check.mass * multiplier) > cell.mass) { + if ((check.getMass() * multiplier) > cell.getMass()) { continue; } @@ -524,7 +524,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { continue; } - if (cell.mass < this.config.playerMinMassSplit) { + if (cell.getMass() < this.config.playerMinMassSplit) { continue; } @@ -541,8 +541,8 @@ TeamZ.prototype.onServerInit = function(gameServer) { }; // Calculate mass and speed of splitting cell var splitSpeed = cell.getSpeed() * 6; - var newMass = cell.mass / 2; - cell.mass = newMass; + var newMass = cell.getMass() / 2; + cell.setMass(newMass); // Create cell var split = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, newMass); split.setAngle(angle); @@ -598,13 +598,13 @@ TeamZ.prototype.onServerInit = function(gameServer) { Virus.prototype.onConsume = function(consumer, gameServer) { var client = consumer.owner; - var maxSplits = Math.floor(consumer.mass / 16) - 1; // Maximum amount of splits + var maxSplits = Math.floor(consumer.getMass() / 16) - 1; // Maximum amount of splits var numSplits = gameServer.config.playerMaxCells - client.cells.length; // Get number of splits numSplits = Math.min(numSplits, maxSplits); - var splitMass = Math.min(consumer.mass / (numSplits + 1), 36); // Maximum size of new splits + var splitMass = Math.min(consumer.getMass() / (numSplits + 1), 36); // Maximum size of new splits // Cell consumes mass before splitting - consumer.addMass(this.mass); + consumer.addMass(this.getMass()); // Cell cannot split any further if (numSplits <= 0) { @@ -613,7 +613,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { // Big cells will split into cells larger than 36 mass (1/4 of their mass) var bigSplits = 0; - var endMass = consumer.mass - (numSplits * splitMass); + var endMass = consumer.getMass() - (numSplits * splitMass); if ((endMass > 300) && (numSplits > 0)) { bigSplits++; numSplits--; @@ -632,14 +632,14 @@ TeamZ.prototype.onServerInit = function(gameServer) { for (var k = 0; k < numSplits; k++) { angle += 6 / numSplits; // Get directions of splitting cells gameServer.newCellVirused(client, consumer, angle, splitMass, 150); - consumer.mass -= splitMass; + consumer.setMass(consumer.getMass() - splitMass); } for (var k = 0; k < bigSplits; k++) { angle = Math.random() * 6.28; // Random directions - splitMass = consumer.mass / 4; + splitMass = consumer.getMass() / 4; gameServer.newCellVirused(client, consumer, angle, splitMass, 20); - consumer.mass -= splitMass; + consumer.setMass(consumer.getMass() - splitMass); } if (gameServer.gameMode.hasEatenHero(client)) @@ -1052,7 +1052,7 @@ function Hero() { g: 255, b: 7 }; - this.mass = 60; + this.setMass(60); } Hero.prototype = new Cell(); @@ -1092,7 +1092,7 @@ Hero.prototype.feed = function(feeder, gameServer) { Hero.prototype.onConsume = function(consumer, gameServer) { // Called when the cell is consumed var client = consumer.owner; - consumer.addMass(this.mass); // delicious + consumer.addMass(this.getMass()); // delicious if (gameServer.gameMode.isCrazy(client)) { // Neutralize the Zombie effect @@ -1123,7 +1123,7 @@ function Brain() { g: 7, b: 255 }; - this.mass = 60; + this.setMass(60); } Brain.prototype = new Cell(); @@ -1163,7 +1163,7 @@ Brain.prototype.feed = function(feeder, gameServer) { Brain.prototype.onConsume = function(consumer, gameServer) { // Called when the cell is consumed var client = consumer.owner; - consumer.addMass(this.mass); // yummy! + consumer.addMass(this.getMass()); // yummy! client.eatenBrainTimer = gameServer.gameMode.brainEffectDuration; diff --git a/src/gamemodes/Teams.js b/src/gamemodes/Teams.js index 8362525e3..bc274db45 100644 --- a/src/gamemodes/Teams.js +++ b/src/gamemodes/Teams.js @@ -138,8 +138,8 @@ Teams.prototype.updateLB = function(gameServer) { continue; } - teamMass[i] += cell.mass; - total += cell.mass; + teamMass[i] += cell.getMass(); + total += cell.getMass(); } } // Calc percentage diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index c6ed2fe3d..5f4a5269f 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -290,7 +290,7 @@ Commands.list = { if (gameServer.clients[i].playerTracker.pID == id) { var client = gameServer.clients[i].playerTracker; for (var j in client.cells) { - client.cells[j].mass = amount; + client.cells[j].setMass(amount); } console.log("[Console] Set mass of " + client.name + " to " + amount); From 207432a30d9c3164ada7d99d78c83837c291ce3a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 05:04:18 +0200 Subject: [PATCH 026/417] Fix remerge time --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index 63fd50d67..3b233b5a2 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -909,7 +909,7 @@ GameServer.prototype.updateCells = function() { // Recombining if (cell.owner.cells.length > 1) { - cell.recombineTicks += 0.05; + cell.recombineTicks += 0.04; cell.calcMergeTime(this.config.playerRecombineTime); } else if (cell.owner.cells.length == 1 && cell.recombineTicks > 0) { cell.recombineTicks = 0; From 2420b454694336dee0e1ae676c374b1b7e17bc83 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 05:37:20 +0200 Subject: [PATCH 027/417] rename project --- src/PacketHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 83ae838d1..afc08807d 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -103,7 +103,7 @@ PacketHandler.prototype.handleMessage = function(message) { c.borderTop + this.socket.playerTracker.scrambleY, c.borderBottom + this.socket.playerTracker.scrambleY, 0, - "OgarMulti")); + "MultiOgar 1.0")); } break; default: From eaccc27c13fcb1e62628a471b267b4bcdb59213b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 05:37:48 +0200 Subject: [PATCH 028/417] rename project --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index dd288e5a5..0039c1759 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ -# OgarMulti - +# MultiOgar + ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) [![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/Barbosik/OgarMulti/blob/master/LICENSE.md) ## [![Language](https://img.shields.io/badge/Ogar-Node-red.svg)](https://github.com/OgarProject/Ogar) Ogar -Copy of Ogar that I heavily modified, and will continue to update. -The [OgarProject](https://ogarproject.com) owns Ogar, and I do not claim it as mine! -Original Ogar found [here](https://github.com/OgarProject/Ogar) - - -The goal is to cleanup the code, fix the bugs and improve physics. - - +Copy of Ogar that I heavily modified, and will continue to update. +The [OgarProject](https://ogarproject.com) owns Ogar, and I do not claim it as mine! +Original Ogar found [here](https://github.com/OgarProject/Ogar) + + +The goal is to cleanup the code, fix the bugs and improve physics. + + ## The changes -* Fix protocol issues -* Cleanup the code (in progress) -* Improved physics (in progress) +* Fix protocol issues +* Cleanup the code (in progress) +* Improved physics (in progress) From 734c11f6d346eb78ba21ec7fae5ab93b13536bf2 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 15:28:37 +0200 Subject: [PATCH 029/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0039c1759..a36c4fc9c 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,4 @@ The goal is to cleanup the code, fix the bugs and improve physics. ## The changes * Fix protocol issues * Cleanup the code (in progress) -* Improved physics (in progress) +* Improve physics (in progress) From e44b0129c08ada005e1267681b7ff3ae1217fc32 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 16:13:38 +0200 Subject: [PATCH 030/417] test change --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a36c4fc9c..a9200fa4d 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,5 @@ The goal is to cleanup the code, fix the bugs and improve physics. * Fix protocol issues * Cleanup the code (in progress) * Improve physics (in progress) + +test#1 From 2254fad1d3cb9d39b4e5aeee885d10a02d36c1c8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 16:21:56 +0200 Subject: [PATCH 031/417] test 2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a9200fa4d..bf1532b46 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,4 @@ The goal is to cleanup the code, fix the bugs and improve physics. * Cleanup the code (in progress) * Improve physics (in progress) -test#1 +test#2 From d81eb93fc5d719481e08f19d0334e782f4435684 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 16:25:07 +0200 Subject: [PATCH 032/417] test 3 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bf1532b46..0ca70a390 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,4 @@ The goal is to cleanup the code, fix the bugs and improve physics. * Cleanup the code (in progress) * Improve physics (in progress) -test#2 +test#3 From 10e47c82ea7542c5a8297d246e6076f5438ecc35 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 16:28:50 +0200 Subject: [PATCH 033/417] test 4 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ca70a390..ab6c4b2b1 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,4 @@ The goal is to cleanup the code, fix the bugs and improve physics. * Cleanup the code (in progress) * Improve physics (in progress) -test#3 +test#4 From ab0f83d48dac2740193d1540dff60a509e1683b0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 16:44:25 +0200 Subject: [PATCH 034/417] update readme --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index ab6c4b2b1..a36c4fc9c 100644 --- a/README.md +++ b/README.md @@ -17,5 +17,3 @@ The goal is to cleanup the code, fix the bugs and improve physics. * Fix protocol issues * Cleanup the code (in progress) * Improve physics (in progress) - -test#4 From d9f4bcb7bd5de928bf1826ca3506216dfdf26502 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 16:52:45 +0200 Subject: [PATCH 035/417] fix base resolution --- src/GameServer.js | 4 ++-- src/gameserver.ini | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 3b233b5a2..4e9988b46 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -47,8 +47,8 @@ function GameServer() { serverPort: 443, // Server port serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams serverBots: 0, // Amount of player bots to spawn - serverViewBaseX: 1024, // Base view distance of players. Warning: high values may cause lag - serverViewBaseY: 592, + serverViewBaseX: 1920, // Base client screen resolution. Used to calculate view area. Warning: high values may cause lag + serverViewBaseY: 1080, serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Amount of seconds per update for the server stats serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections diff --git a/src/gameserver.ini b/src/gameserver.ini index 735f6a110..4bb71579b 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -11,11 +11,11 @@ // serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. // serverMaxLB: Controls the maximum players displayed on the leaderboard. serverMaxConnections = 64 -serverPort = 443 +serverPort = 50000 serverGamemode = 0 serverBots = 0 -serverViewBaseX = 1024 -serverViewBaseY = 592 +serverViewBaseX = 1920 +serverViewBaseY = 1080 serverStatsPort = 88 serverStatsUpdate = 60 serverLogLevel = 1 @@ -47,7 +47,7 @@ virusFeedAmount = 7 // [Ejected Mass] // ejectMass: Mass of ejected cells -// ejectMassCooldown: Time until a player can eject mass again (ms) +// ejectMassCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) // ejectMassLoss: Mass lost when ejecting cells // ejectSpeed: Base speed of ejected cells // ejectSpawnPlayer: Chance for a player to spawn from ejected mass @@ -64,6 +64,7 @@ ejectSpawnPlayer = 50 // playerMassDecayRate: Amount of mass lost per tick (Multiplier) (1 tick = 1000 milliseconds) // playerMinMassDecay: Minimum mass for decay to occur // playerDisconnectTime: The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) +// playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) playerStartMass = 10 playerBotGrowEnabled = 1 playerMaxMass = 22500 From e69b3b4021a5361b37a75df9fb3aff8784505965 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 18:54:16 +0200 Subject: [PATCH 036/417] fix calculations (player center, sight area, scale) for gameplay mode; fix collision detector --- src/PlayerTracker.js | 99 +++++++++++++++++++++++--------------------- src/entity/Cell.js | 46 ++++++++++---------- 2 files changed, 73 insertions(+), 72 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 95e15f7e6..f56984c48 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -15,6 +15,8 @@ function PlayerTracker(gameServer, socket) { this.cells = []; this.mergeOverride = false; // Triggered by console command this.score = 0; // Needed for leaderboard + this.scale = 1; + this.isMassChanged = true; this.mouse = { x: 0, @@ -89,17 +91,39 @@ PlayerTracker.prototype.getSkin = function () { }; PlayerTracker.prototype.getScore = function(reCalcScore) { - if (reCalcScore) { - var s = 0; - for (var i = 0; i < this.cells.length; i++) { - if (!this.cells[i]) return; // Error - s += this.cells[i].getMass(); - this.score = s; - } - } + if (this.isMassChanged) + this.updateMass(); return this.score >> 0; }; +PlayerTracker.prototype.getScale = function () { + if (this.isMassChanged) + this.updateMass(); + return this.scale; +}; + +PlayerTracker.prototype.updateMass = function () { + var totalSize = 0; + for (var i = 0; i < this.cells.length; i++) { + var node = this.cells[i]; + if (!node) continue; + totalSize += node.getSize(); + } + if (totalSize == 0) { + //this.scale = 1; + this.score = 0; + } else { + this.score = totalSize * totalSize / 100; + this.scale = Math.pow(Math.min(64 / totalSize, 1), 0.4); + } + this.isMassChanged = false; +}; + +PlayerTracker.prototype.massChanged = function () { + this.isMassChanged = true; +}; + + PlayerTracker.prototype.setColor = function(color) { this.color.r = color.r; this.color.g = color.g; @@ -257,44 +281,27 @@ PlayerTracker.prototype.update = function () { // Viewing box PlayerTracker.prototype.updateSightRange = function() { // For view distance - var totalSize = 1.0; - var len = this.cells.length; - - for (var i = 0; i < len; i++) { - if (!this.cells[i]) { - continue; - } - - totalSize += this.cells[i].getSize(); - } - - var factor = Math.pow(Math.min(64.0 / totalSize, 1), 0.4); - this.sightRangeX = this.gameServer.config.serverViewBaseX / factor; - this.sightRangeY = this.gameServer.config.serverViewBaseY / factor; + var scale = this.getScale(); + this.sightRangeX = (this.gameServer.config.serverViewBaseX + 100) / scale; + this.sightRangeY = (this.gameServer.config.serverViewBaseY + 100) / scale; }; PlayerTracker.prototype.updateCenter = function() { // Get center of cells var len = this.cells.length; - - if (len <= 0) { - return; // End the function if no cells exist - } - - var X = 0; - var Y = 0; - var allSize = 0; // Focus larger cells to near the center and smaller away + if (len <= 0) return; + var cx = 0; + var cy = 0; + var count = 0; for (var i = 0; i < len; i++) { - // Error check - if (!this.cells[i]) continue; - var cell = this.cells[i]; - - X += cell.position.x * cell.getMass(); - Y += cell.position.y * cell.getMass(); - allSize += cell.getMass(); + var node = this.cells[i]; + if (!node) continue; + cx += node.position.x; + cy += node.position.y; + count++; } - - this.centerPos.x = X / allSize; - this.centerPos.y = Y / allSize; + if (count == 0) return; + this.centerPos.x = cx / count; + this.centerPos.y = cy / count; }; PlayerTracker.prototype.calcViewBox = function() { @@ -308,10 +315,10 @@ PlayerTracker.prototype.calcViewBox = function() { this.updateCenter(); // Box - this.viewBox.topY = this.centerPos.y - this.sightRangeY; - this.viewBox.bottomY = this.centerPos.y + this.sightRangeY; - this.viewBox.leftX = this.centerPos.x - this.sightRangeX; - this.viewBox.rightX = this.centerPos.x + this.sightRangeX; + this.viewBox.topY = this.centerPos.y - this.sightRangeY / 2; + this.viewBox.bottomY = this.centerPos.y + this.sightRangeY / 2; + this.viewBox.leftX = this.centerPos.x - this.sightRangeX / 2; + this.viewBox.rightX = this.centerPos.x + this.sightRangeX / 2; this.viewBox.width = this.sightRangeX; this.viewBox.height = this.sightRangeY; @@ -380,9 +387,7 @@ PlayerTracker.prototype.calcVisibleNodes = function() { var newVisible = []; for (var i = 0; i < this.gameServer.nodes.length; i++) { var node = this.gameServer.nodes[i]; - if (!node) { - continue; - } + if (!node) continue; var check = node.visibleCheck(this.viewBox, this.centerPos, this.cells); if (check > 0 || node.owner == this) { diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 88b359d64..e428c2c03 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -9,6 +9,7 @@ function Cell(nodeId, owner, position, mass, gameServer) { this.position = position; this._size = 0; this._mass = 0; + this._squareSize = 0; this.setMass(mass); // Starting mass of the cell this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass this.spiked = 0; // If 1, then this cell has spikes around it @@ -64,10 +65,13 @@ Cell.prototype.getMass = function () { Cell.prototype.setMass = function (mass) { this._mass = mass; this._size = Math.ceil(Math.sqrt(100 * mass)); + this._squareSize = this._size * this._size; + if (this.owner) + this.owner.massChanged(); }; Cell.prototype.getSquareSize = function() { - return this._size * this._size; + return this._squareSize; }; Cell.prototype.addMass = function(n) { @@ -116,24 +120,10 @@ Cell.prototype.setKiller = function(cell) { // Functions Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) { - // Collision checking - if (this.position.y > bottomY) { - return false; - } - - if (this.position.y < topY) { - return false; - } - - if (this.position.x > rightX) { - return false; - } - - if (this.position.x < leftX) { - return false; - } - - return true; + return this.position.x > leftX && + this.position.x < rightX && + this.position.y > topY && + this.position.y < bottomY; }; // This collision checking function is based on CIRCLE shape @@ -151,13 +141,19 @@ Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) { Cell.prototype.visibleCheck = function(box, centerPos, cells) { // Checks if this cell is visible to the player var isThere = false; - if (this.getMass() < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); - else { + if (this.cellType == 1) { + isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); + } else { var cellSize = this.getSize(); - var lenX = cellSize + box.width >> 0; // Width of cell + width of the box (Int) - var lenY = cellSize + box.height >> 0; // Height of cell + height of the box (Int) - - isThere = (this.abs(this.position.x - centerPos.x) < lenX) && (this.abs(this.position.y - centerPos.y) < lenY); + var minx = this.position.x - cellSize; + var miny = this.position.y - cellSize; + var maxx = this.position.x + cellSize; + var maxy = this.position.y + cellSize; + var d1x = box.leftX - maxx; + var d1y = box.topY - maxy; + var d2x = minx - box.rightX; + var d2y = miny - box.bottomY; + isThere = d1x < 0 && d1y < 0 && d2x < 0 && d2y < 0; } if (isThere) { // It is From 0f26bf97541c998cac890f1ebf2d4ed7d233caea Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 19:48:46 +0200 Subject: [PATCH 037/417] replace circle collision detector with optimized one --- src/entity/Cell.js | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index e428c2c03..9f4f79c34 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -138,12 +138,14 @@ Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) { return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize); }; -Cell.prototype.visibleCheck = function(box, centerPos, cells) { +Cell.prototype.visibleCheck = function (box, centerPos, cells) { // Checks if this cell is visible to the player var isThere = false; if (this.cellType == 1) { + // dot collision detector isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); } else { + // rectangle collision detector var cellSize = this.getSize(); var minx = this.position.x - cellSize; var miny = this.position.y - cellSize; @@ -155,23 +157,24 @@ Cell.prototype.visibleCheck = function(box, centerPos, cells) { var d2y = miny - box.bottomY; isThere = d1x < 0 && d1y < 0 && d2x < 0 && d2y < 0; } - if (isThere) { - // It is - // To save perfomance, check if any client's cell collides with this cell - for (var i = 0; i < cells.length; i++) { - var cell = cells[i]; - if (!cell) continue; - - var dist = this.getDist(this.position.x, this.position.y, cell.position.x, cell.position.y); - var collideDist = cell.getSize() + this.getSize(); - - if (dist < collideDist) { - return 2; - }// Colliding with one + if (!isThere) return 0; + + // To save perfomance, check if any client's cell collides with this cell + for (var i = 0; i < cells.length; i++) { + var cell = cells[i]; + if (!cell) continue; + + // circle collision detector + var dx = cell.position.x - this.position.x; + var dy = cell.position.y - this.position.y; + var r = cell.getSize() + this.getSize(); + if (dx * dx + dy * dy < r * r) { + // circle collision detected + return 2; } - return 1; // Not colliding with any } - else return 0; + // Not colliding with any + return 1; }; Cell.prototype.calcMovePhys = function(config) { From 12617a30685b1c34d76b66908c562a7405ae8146 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 21:43:36 +0200 Subject: [PATCH 038/417] fix border calculations; fix border bouncy physics --- src/GameServer.js | 8 +- src/PlayerTracker.js | 10 +- src/entity/Cell.js | 54 ++++-- src/entity/PlayerCell.js | 320 +++++++++++++++++----------------- src/gamemodes/Experimental.js | 7 +- 5 files changed, 217 insertions(+), 182 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 4e9988b46..05dba510c 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -227,11 +227,11 @@ GameServer.prototype.getNewPlayerID = function() { }; GameServer.prototype.getRandomPosition = function() { - var xSum = this.config.borderRight + this.config.borderLeft; - var ySum = this.config.borderBottom + this.config.borderTop; + var width = this.config.borderRight - this.config.borderLeft; + var height = this.config.borderBottom - this.config.borderTop; return { - x: Math.floor(Math.random() * xSum - this.config.borderLeft), - y: Math.floor(Math.random() * ySum - this.config.borderTop) + x: Math.floor(this.config.borderLeft + width * Math.random()), + y: Math.floor(this.config.borderTop + height * Math.random()) }; }; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index f56984c48..c52c6a41d 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -56,8 +56,10 @@ function PlayerTracker(gameServer, socket) { // Gamemode function if (gameServer) { // Find center - this.centerPos.x = (gameServer.config.borderLeft - gameServer.config.borderRight) / 2; - this.centerPos.y = (gameServer.config.borderTop - gameServer.config.borderBottom) / 2; + var width = gameServer.config.borderRight - gameServer.config.borderLeft; + var height = gameServer.config.borderBottom - gameServer.config.borderTop; + this.centerPos.x = gameServer.config.borderLeft + width / 2; + this.centerPos.y = gameServer.config.borderTop + height / 2; // Player id this.pID = gameServer.getNewPlayerID(); // Gamemode function @@ -408,13 +410,13 @@ PlayerTracker.prototype.setCenterPos = function(x, y) { PlayerTracker.prototype.checkBorderPass = function() { // A check while in free-roam mode to avoid player going into nothingness - if (this.centerPos.x < -this.gameServer.config.borderLeft) { + if (this.centerPos.x < this.gameServer.config.borderLeft) { this.centerPos.x = this.gameServer.config.borderLeft; } if (this.centerPos.x > this.gameServer.config.borderRight) { this.centerPos.x = this.gameServer.config.borderRight; } - if (this.centerPos.y < -this.gameServer.config.borderTop) { + if (this.centerPos.y < this.gameServer.config.borderTop) { this.centerPos.y = this.gameServer.config.borderTop; } if (this.centerPos.y > this.gameServer.config.borderBottom) { diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 9f4f79c34..242874c44 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -216,34 +216,66 @@ Cell.prototype.calcMovePhys = function(config) { } } - // Border check - Bouncy physics + //// Border check - Bouncy physics var radius = 40; - if ((this.position.x - radius) < -config.borderLeft) { + if (X < config.borderLeft && this.position.x != X) { // Flip angle horizontally - Left side this.angle = 6.28 - this.angle; - X = -config.borderLeft + radius; + var p = this.getLineIntersect( + this.position.x, this.position.y, X, Y, + config.borderLeft, config.borderBottom, + config.borderLeft, config.borderTop); + X = p.x; + Y = p.y; } - if ((this.position.x + radius) > config.borderRight) { + if (X > config.borderRight && this.position.y != X) { // Flip angle horizontally - Right side this.angle = 6.28 - this.angle; - X = config.borderRight - radius; + var p = this.getLineIntersect( + this.position.x, this.position.y, X, Y, + config.borderRight, config.borderBottom, + config.borderRight, config.borderTop); + X = p.x; + Y = p.y; } - if ((this.position.y - radius) < -config.borderTop) { + if (Y < config.borderTop && this.position.y != Y) { // Flip angle vertically - Top side this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - Y = -config.borderTop + radius; + var p = this.getLineIntersect( + this.position.x, this.position.y, X, Y, + config.borderRight, config.borderTop, + config.borderLeft, config.borderTop); + X = p.x; + Y = p.y; } - if ((this.position.y + radius) > config.borderBottom) { + if (Y > config.borderBottom && this.position.y != Y) { // Flip angle vertically - Bottom side this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - Y = config.borderBottom - radius; + var p = this.getLineIntersect( + this.position.x, this.position.y, X, Y, + config.borderRight, config.borderBottom, + config.borderLeft, config.borderBottom); + X = p.x; + Y = p.y; } // Set position - this.position.x = X >> 0; - this.position.y = Y >> 0; + this.position.x = X; + this.position.y = Y; }; +Cell.prototype.getLineIntersect = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { + var z1 = p1x - p0x; + var z2 = p3x - p2x; + var w1 = p1y - p0y; + var w2 = p3y - p2y; + var k2 = (z1 * (p2y - p0y) + w1 * (p0x - p2x)) / (w1 * z2 - z1 * w2); + return { + x: p2x + z2 * k2, + y: p2y + w2 * k2 + }; +} + // Override these Cell.prototype.sendUpdate = function() { diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 942ebbd8a..7176ce44d 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -1,165 +1,165 @@ -var Cell = require('./Cell'); - -function PlayerCell() { - Cell.apply(this, Array.prototype.slice.call(arguments)); - - this.cellType = 0; - this.recombineTicks = 0; // Ticks passed after the cell has split - this.shouldRecombine = false; // Should the cell combine. If true, collision with own cells happens -} - -module.exports = PlayerCell; -PlayerCell.prototype = new Cell(); - -// Main Functions - -PlayerCell.prototype.simpleCollide = function(check, d) { - // Simple collision check - var len = 2 * d >> 0; // Width of cell + width of the box (Int) - - return (this.abs(this.position.x - check.x) < len) && - (this.abs(this.position.y - check.y) < len); -}; - -PlayerCell.prototype.calcMergeTime = function(base) { - // Check for merging time - var r = false; - if (base == 0 || this.owner.mergeOverride) { - // Instant recombine in config or merge command was triggered for this client - r = true; - } else { - var rec = Math.max(30, 0.02 * this.getSize()); - rec = Math.max(base, rec); - if (this.recombineTicks > rec) r = true; // Can combine with other cells - } - this.shouldRecombine = r; -}; - -// Movement - -PlayerCell.prototype.calcMove = function(x2, y2, gameServer) { - if (!this.owner.shouldMoveCells && this.owner.notMoved) return; // Mouse is in one place - - // Get angle of mouse - var deltaY = y2 - this.position.y; - var deltaX = x2 - this.position.x; - var angle = Math.atan2(deltaX, deltaY); - - if (isNaN(angle)) { - return; - } - - //var dist = this.getDist(this.position.x, this.position.y, x2, y2); - //var speed = Math.min(this.getSpeed(), dist) / 2; // Twice as slower - var dist = Math.sqrt(deltaX*deltaX + deltaY*deltaY); +var Cell = require('./Cell'); + +function PlayerCell() { + Cell.apply(this, Array.prototype.slice.call(arguments)); + + this.cellType = 0; + this.recombineTicks = 0; // Ticks passed after the cell has split + this.shouldRecombine = false; // Should the cell combine. If true, collision with own cells happens +} + +module.exports = PlayerCell; +PlayerCell.prototype = new Cell(); + +// Main Functions + +PlayerCell.prototype.simpleCollide = function(check, d) { + // Simple collision check + var len = 2 * d >> 0; // Width of cell + width of the box (Int) + + return (this.abs(this.position.x - check.x) < len) && + (this.abs(this.position.y - check.y) < len); +}; + +PlayerCell.prototype.calcMergeTime = function(base) { + // Check for merging time + var r = false; + if (base == 0 || this.owner.mergeOverride) { + // Instant recombine in config or merge command was triggered for this client + r = true; + } else { + var rec = Math.max(30, 0.02 * this.getSize()); + rec = Math.max(base, rec); + if (this.recombineTicks > rec) r = true; // Can combine with other cells + } + this.shouldRecombine = r; +}; + +// Movement + +PlayerCell.prototype.calcMove = function(x2, y2, gameServer) { + if (!this.owner.shouldMoveCells && this.owner.notMoved) return; // Mouse is in one place + + // Get angle of mouse + var deltaY = y2 - this.position.y; + var deltaX = x2 - this.position.x; + var angle = Math.atan2(deltaX, deltaY); + + if (isNaN(angle)) { + return; + } + + //var dist = this.getDist(this.position.x, this.position.y, x2, y2); + //var speed = Math.min(this.getSpeed(), dist) / 2; // Twice as slower + var dist = Math.sqrt(deltaX*deltaX + deltaY*deltaY); dist = Math.min(dist, 32); var speed = 0; if (dist >= 1) { speed = this.getSpeed() * dist / 32; } - - // Move cell - this.position.x += Math.sin(angle) * speed; - this.position.y += Math.cos(angle) * speed; - this.owner.notMoved = false; -}; - -PlayerCell.prototype.collision = function(gameServer) { - var config = gameServer.config; - var r = this.getSize(); // Cell radius - - // Collision check for other cells - for (var i = 0; i < this.owner.cells.length; i++) { - var cell = this.owner.cells[i]; - - if (!cell) continue; // Error - if (this.nodeId == cell.nodeId) continue; - - if ((!cell.shouldRecombine) || (!this.shouldRecombine)) { - // Cannot recombine - Collision with your own cells - var calcInfo = gameServer.checkCellCollision(this, cell); // Calculation info - - // Further calculations - if (calcInfo.collided) { // Collided - // Cell with collision restore ticks on should not collide - if (this.collisionRestoreTicks > 0 || cell.collisionRestoreTicks > 0) continue; - - // Call gameserver's function to collide cells - gameServer.cellCollision(this, cell, calcInfo); - } - } - } - - gameServer.gameMode.onCellMove(this, gameServer); - - // Check to ensure we're not passing the world border (shouldn't get closer than a quarter of the cell's diameter) - if (this.position.x < -config.borderLeft + r / 2) { - this.position.x = -config.borderLeft + r / 2; - } - if (this.position.x > config.borderRight - r / 2) { - this.position.x = config.borderRight - r / 2; - } - if (this.position.y < -config.borderTop + r / 2) { - this.position.y = -config.borderTop + r / 2; - } - if (this.position.y > config.borderBottom - r / 2) { - this.position.y = config.borderBottom - r / 2; - } -}; - -// Override - -PlayerCell.prototype.getEatingRange = function() { - return this.getSize() / 3.14; -}; - -PlayerCell.prototype.onConsume = function(consumer, gameServer) { - // Add an inefficiency for eating other players' cells - var factor = ( consumer.owner === this.owner ? 1 : gameServer.config.playerMassAbsorbed ); - // Anti-bot measure - factor = (consumer.getMass() >= 625 && this.getMass() <= 17 && gameServer.config.playerBotGrowEnabled == 1) ? 0 : factor; - consumer.addMass(factor * this.getMass()); -}; - -PlayerCell.prototype.onAdd = function(gameServer) { - // Add to special player node list - gameServer.nodesPlayer.push(this); - // Gamemode actions - gameServer.gameMode.onCellAdd(this); -}; - -PlayerCell.prototype.onRemove = function(gameServer) { - var index; - // Remove from player cell list - index = this.owner.cells.indexOf(this); - if (index != -1) { - this.owner.cells.splice(index, 1); - } - // Remove from special player controlled node list - index = gameServer.nodesPlayer.indexOf(this); - if (index != -1) { - gameServer.nodesPlayer.splice(index, 1); - } - // Gamemode actions - gameServer.gameMode.onCellRemove(this); -}; - -PlayerCell.prototype.moveDone = function(gameServer) { - // Well, nothing. -}; - -// Lib - -PlayerCell.prototype.abs = function(x) { - return x < 0 ? -x : x; -}; - -PlayerCell.prototype.getDist = function(x1, y1, x2, y2) { - var xs = x2 - x1; - xs = xs * xs; - - var ys = y2 - y1; - ys = ys * ys; - - return Math.sqrt(xs + ys); -}; + + // Move cell + this.position.x += Math.sin(angle) * speed; + this.position.y += Math.cos(angle) * speed; + this.owner.notMoved = false; +}; + +PlayerCell.prototype.collision = function(gameServer) { + var config = gameServer.config; + var r = this.getSize(); // Cell radius + + // Collision check for other cells + for (var i = 0; i < this.owner.cells.length; i++) { + var cell = this.owner.cells[i]; + + if (!cell) continue; // Error + if (this.nodeId == cell.nodeId) continue; + + if ((!cell.shouldRecombine) || (!this.shouldRecombine)) { + // Cannot recombine - Collision with your own cells + var calcInfo = gameServer.checkCellCollision(this, cell); // Calculation info + + // Further calculations + if (calcInfo.collided) { // Collided + // Cell with collision restore ticks on should not collide + if (this.collisionRestoreTicks > 0 || cell.collisionRestoreTicks > 0) continue; + + // Call gameserver's function to collide cells + gameServer.cellCollision(this, cell, calcInfo); + } + } + } + + gameServer.gameMode.onCellMove(this, gameServer); + + // Check to ensure we're not passing the world border (shouldn't get closer than a quarter of the cell's diameter) + if (this.position.x < config.borderLeft + r / 2) { + this.position.x = config.borderLeft + r / 2; + } + if (this.position.x > config.borderRight - r / 2) { + this.position.x = config.borderRight - r / 2; + } + if (this.position.y < config.borderTop + r / 2) { + this.position.y = config.borderTop + r / 2; + } + if (this.position.y > config.borderBottom - r / 2) { + this.position.y = config.borderBottom - r / 2; + } +}; + +// Override + +PlayerCell.prototype.getEatingRange = function() { + return this.getSize() / 3.14; +}; + +PlayerCell.prototype.onConsume = function(consumer, gameServer) { + // Add an inefficiency for eating other players' cells + var factor = ( consumer.owner === this.owner ? 1 : gameServer.config.playerMassAbsorbed ); + // Anti-bot measure + factor = (consumer.getMass() >= 625 && this.getMass() <= 17 && gameServer.config.playerBotGrowEnabled == 1) ? 0 : factor; + consumer.addMass(factor * this.getMass()); +}; + +PlayerCell.prototype.onAdd = function(gameServer) { + // Add to special player node list + gameServer.nodesPlayer.push(this); + // Gamemode actions + gameServer.gameMode.onCellAdd(this); +}; + +PlayerCell.prototype.onRemove = function(gameServer) { + var index; + // Remove from player cell list + index = this.owner.cells.indexOf(this); + if (index != -1) { + this.owner.cells.splice(index, 1); + } + // Remove from special player controlled node list + index = gameServer.nodesPlayer.indexOf(this); + if (index != -1) { + gameServer.nodesPlayer.splice(index, 1); + } + // Gamemode actions + gameServer.gameMode.onCellRemove(this); +}; + +PlayerCell.prototype.moveDone = function(gameServer) { + // Well, nothing. +}; + +// Lib + +PlayerCell.prototype.abs = function(x) { + return x < 0 ? -x : x; +}; + +PlayerCell.prototype.getDist = function(x1, y1, x2, y2) { + var xs = x2 - x1; + xs = xs * xs; + + var ys = y2 - y1; + ys = ys * ys; + + return Math.sqrt(xs + ys); +}; diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 85f010347..e5192ac9f 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -89,10 +89,11 @@ Experimental.prototype.onServerInit = function(gameServer) { // Called when the server starts gameServer.run = true; - var mapSize = gameServer.config.borderLeft + gameServer.config.borderRight + - gameServer.config.borderTop + gameServer.config.borderRight; + var mapWidth = gameServer.config.borderRight - gameServer.config.borderLeft; + var mapHeight = gameServer.config.borderBottom - gameServer.config.borderTop; + var mapSize = Math.max(mapWidth, mapHeight); - this.motherMinAmount = Math.ceil(mapSize / 3194.382825); // 7 mother cells for agar.io map size + this.motherMinAmount = Math.ceil(mapSize / 2000); // 7 mother cells for agar.io map size // Special virus mechanics Virus.prototype.feed = function(feeder, gameServer) { From a352e318343f71b10cfa58d9f0ab6032a53c8436 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 22:09:12 +0200 Subject: [PATCH 039/417] update readme --- README.md | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a36c4fc9c..6930c8a02 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,14 @@ Original Ogar found [here](https://github.com/OgarProject/Ogar) The goal is to cleanup the code, fix the bugs and improve physics. -## The changes -* Fix protocol issues -* Cleanup the code (in progress) -* Improve physics (in progress) +## What's new: +* Player speed - replaced with formula approximated to vanilla physics; +* Recombine time - replaced with formula approximated to vanilla physics; +* Cell collision - rectangle collision and circle collision algorithms were fixed and replaced with more fast; +* View area - fixed and replaced with formula approximated to vanilla physics; +* Mouse control and cell movements - fixed and replaced with physics approximated to vanilla physics; +* Border calculations - fixed +* Border bouncy physics - fixed and improved +* mainLoop - cleaned +* added support for different protocols +* color generator replaced with hsv model From 1e9bfb782a72793dd47a74fb2a07fbd7d705ce26 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 23:20:57 +0200 Subject: [PATCH 040/417] fix player speed --- src/entity/Cell.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 242874c44..9e1ffc0cc 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -88,7 +88,8 @@ Cell.prototype.addMass = function(n) { Cell.prototype.getSpeed = function() { var speed = 2.1106 / Math.pow(this.getSize(), 0.449); - return speed * (1 / 0.04) * this.gameServer.config.playerSpeed * 2; // have no idea why it twice slower + // tickStep=40ms + return speed * 40 * this.gameServer.config.playerSpeed; }; Cell.prototype.setAngle = function(radians) { From cc286e5e8c5dd2a23287b761aaac95309a8e3ae7 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 4 Jun 2016 23:55:03 +0200 Subject: [PATCH 041/417] fix sight area and scale for spectate --- src/PlayerTracker.js | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index c52c6a41d..7897df42b 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -337,11 +337,9 @@ PlayerTracker.prototype.getSpectateNodes = function() { if (!specPlayer) return this.moveInFreeRoam(); // There are probably no players // Get spectate player's location and calculate zoom amount - var specZoom = Math.min(Math.sqrt(100 * specPlayer.getScore(false)), 555); - specZoom = Math.pow(Math.min(40.5 / specZoom, 1.0), 0.4) * 0.8; - + this.scale = specPlayer.getScale(); this.setCenterPos(specPlayer.centerPos.x, specPlayer.centerPos.y); - this.sendPosPacket(specZoom); + this.sendPosPacket(this.scale); return specPlayer.visibleNodes.slice(0); } @@ -364,24 +362,21 @@ PlayerTracker.prototype.moveInFreeRoam = function() { this.checkBorderPass(); // Now that we've updated center pos, get nearby cells - // We're going to use config's view base times 2.5 - - var mult = 3.5; // To simplify multiplier, in case this needs editing later on - var baseX = this.gameServer.config.serverViewBaseX; - var baseY = this.gameServer.config.serverViewBaseY; - this.viewBox.topY = this.centerPos.y - baseY * mult; - this.viewBox.bottomY = this.centerPos.y + baseY * mult; - this.viewBox.leftX = this.centerPos.x - baseX * mult; - this.viewBox.rightX = this.centerPos.x + baseX * mult; - this.viewBox.width = baseX * mult; - this.viewBox.height = baseY * mult; + this.scale = 0.25; + var baseX = (this.gameServer.config.serverViewBaseX + 100) / this.scale; + var baseY = (this.gameServer.config.serverViewBaseY + 100) / this.scale; + + this.viewBox.leftX = this.centerPos.x - baseX / 2; + this.viewBox.topY = this.centerPos.y - baseY / 2; + this.viewBox.rightX = this.centerPos.x + baseX / 2; + this.viewBox.bottomY = this.centerPos.y + baseY / 2; + this.viewBox.width = baseX; + this.viewBox.height = baseY; // Use calcViewBox's way of looking for nodes var newVisible = this.calcVisibleNodes(); - var specZoom = 222; - specZoom = Math.pow(Math.min(40.5 / specZoom, 1.0), 0.4) * 0.6; // Constant zoom - this.sendPosPacket(specZoom); + this.sendPosPacket(this.scale); return newVisible; }; From df5b1ba67008e9ff18e1dceaf0c76c0ae77c3ce4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 5 Jun 2016 00:19:42 +0200 Subject: [PATCH 042/417] fix skin support for team mode; remove unused code --- src/GameServer.js | 7 +++++-- src/PacketHandler.js | 3 +++ src/entity/EjectedMass.js | 21 --------------------- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 05dba510c..27fb6146b 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -385,7 +385,10 @@ GameServer.prototype.updateLeaderboard = function () { clients.sort(function (a, b) { return b.playerTracker.getScore(true) - a.playerTracker.getScore(true); }); - this.largestClient = clients[0].playerTracker; + //this.largestClient = clients[0].playerTracker; + this.largestClient = null; + if (clients[0] != null) + this.largestClient = clients[0].playerTracker; } else this.largestClient = this.gameMode.rankOne; } }; @@ -730,7 +733,7 @@ GameServer.prototype.ejectMass = function(client) { angle += (Math.random() * 0.6) - 0.3; // Create cell - var ejected = new Entity.EjectedMass(this.getNextNodeId(), client, startPos, this.config.ejectMass, this); + var ejected = new Entity.EjectedMass(this.getNextNodeId(), null, startPos, this.config.ejectMass, this); ejected.setAngle(angle); ejected.setMoveEngineData(this.config.ejectSpeed, 20, 0.88); ejected.setColor(cell.getColor()); diff --git a/src/PacketHandler.js b/src/PacketHandler.js index afc08807d..6d9401578 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -164,6 +164,9 @@ PacketHandler.prototype.setNickname = function(text) { if (name.length > this.gameServer.config.playerMaxNickLength) { name = name.substring(0, this.gameServer.config.playerMaxNickLength); } + if (this.gameServer.gameMode.haveTeams) { + skin = ""; + } client.setName(name); client.setSkin(skin); diff --git a/src/entity/EjectedMass.js b/src/entity/EjectedMass.js index aae3b7574..ec1f34a5e 100644 --- a/src/entity/EjectedMass.js +++ b/src/entity/EjectedMass.js @@ -4,7 +4,6 @@ function EjectedMass() { Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = 3; - this.addedAntiTeam = false; // Not to affect anti-teaming two times } module.exports = EjectedMass; @@ -30,22 +29,6 @@ EjectedMass.prototype.sendUpdate = function() { }; EjectedMass.prototype.onRemove = function(gameServer) { - // Check for teaming and apply anti-teaming if required - if (!this.addedAntiTeam && this.owner.checkForWMult) { - try { - if (this.gameServer.gameMode.teamAmount > 0) { - // Apply teaming EXCEPT when exchanging mass to same team member - if (this.owner.team != this.killedBy.owner.team || this.owner == this.killedBy.owner) { - this.owner.Wmult += 0.02; - this.owner.checkForWMult = false; - }; - } else { - // Always apply anti-teaming if there are no teams - this.owner.Wmult += 0.02; - this.owner.checkForWMult = false; - }; - } catch(ex) { } // Dont do anything whatever the error is - } // Remove from list of ejected mass var index = gameServer.nodesEjected.indexOf(this); if (index != -1) { @@ -70,8 +53,4 @@ EjectedMass.prototype.onAutoMove = function(gameServer) { }; EjectedMass.prototype.moveDone = function(gameServer) { - // Always apply anti-teaming - this.owner.actionMult += 0.02; - this.addedAntiTeam = true; - this.owner.checkForWMult = false; }; From cb08df68f1912555ab2777c00456466bf17a0fa8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 5 Jun 2016 03:31:40 +0200 Subject: [PATCH 043/417] fix leaderboard for teams mode --- src/gamemodes/Teams.js | 12 +- src/packet/UpdateLeaderboard.js | 227 ++++++++++++++++---------------- 2 files changed, 118 insertions(+), 121 deletions(-) diff --git a/src/gamemodes/Teams.js b/src/gamemodes/Teams.js index bc274db45..655c184ca 100644 --- a/src/gamemodes/Teams.js +++ b/src/gamemodes/Teams.js @@ -142,13 +142,15 @@ Teams.prototype.updateLB = function(gameServer) { total += cell.getMass(); } } + // No players + if (total <= 0) { + for (var i = 0; i < this.teamAmount; i++) { + gameServer.leaderboard[i] = 0; + } + return + } // Calc percentage for (var i = 0; i < this.teamAmount; i++) { - // No players - if (total <= 0) { - continue; - } - gameServer.leaderboard[i] = teamMass[i] / total; } }; diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index c9cb0db33..d43124aa6 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -6,125 +6,120 @@ function UpdateLeaderboard(leaderboard, packetLB) { module.exports = UpdateLeaderboard; UpdateLeaderboard.prototype.build = function (protocol) { - var lb = this.leaderboard; switch (this.packetLB) { - case 48:// Custom Text List? - { - var offset = 0; - var buffer = new Buffer(0x10000); - - - buffer.writeUInt8(48, offset); - offset++; - - var countOffset = offset; - offset += 4; - - - var count = 0; - - for (var i = 0; i < lb.length; i++) { - if (typeof lb[i] == "undefined") - continue; - var item = lb[i]; - - var name = item; - name = name ? name : ""; - - buffer.writeUInt32LE(0, offset); - offset += 4; - - if (protocol <= 5) { - offset += buffer.write(name, offset, 'ucs2'); // string => unicode - buffer.writeUInt16LE(0, offset); - offset += 2; - } - else { - offset += buffer.write(name, offset); // string => utf8 - buffer.writeUInt8(0, offset); // string zero terminator - offset += 1; - } - - count++; - } - buffer.writeUInt32LE(count, countOffset); // Number of elements - return buffer.slice(0, offset); - } - break; + case 48: return this.build48(protocol); // ? + case 49: return this.build49(protocol); // FFA + case 50: return this.build50(protocol); // Team + default: return null; + } +} - case 49:// FFA - { - var offset = 0; - var buffer = new Buffer(0x10000); - - buffer.writeUInt8(49, offset); // Packet ID - offset += 1; - - - var countOffset = offset; - offset += 4; - - var count = 0; - for (var i = 0; i < lb.length; i++) { - if (typeof lb[i] == "undefined") - continue; - var item = lb[i]; - - var isMe = false; // 1 for red color (current player), 0 for white color (other players) - var name = item.getName(); - name = name ? name : ""; - - // Write record - buffer.writeUInt32LE(isMe ? 1:0, offset); // isMe flag (previously cell ID) - offset += 4; - - if (protocol <= 5) { - offset += buffer.write(name, offset, 'ucs2'); // string => unicode - buffer.writeUInt16LE(0, offset); - offset += 2; - } - else { - offset += buffer.write(name, offset); // string => utf8 - buffer.writeUInt8(0, offset); // string zero terminator - offset += 1; - } - - count++; - } - buffer.writeUInt32LE(count, countOffset); // Number of elements - return buffer.slice(0, offset); - } - break; +// Custom Text List? WTF? +UpdateLeaderboard.prototype.build48 = function (protocol) { + var offset = 0; + var buffer = new Buffer(0x10000); + + buffer.writeUInt8(48, offset); + offset++; + + var countOffset = offset; + offset += 4; + + var count = 0; + for (var i = 0; i < this.leaderboard.length; i++) { + var item = this.leaderboard[i]; + if (typeof item == "undefined" || item==null) + continue; + + var name = item; + name = name ? name : ""; + + buffer.writeUInt32LE(0, offset); + offset += 4; + + if (protocol <= 5) { + offset += buffer.write(name, offset, 'ucs2'); // string => unicode + buffer.writeUInt16LE(0, offset); + offset += 2; + } + else { + offset += buffer.write(name, offset); // string => utf8 + buffer.writeUInt8(0, offset); // string zero terminator + offset += 1; + } + count++; + } + buffer.writeUInt32LE(count, countOffset); // Number of elements + return buffer.slice(0, offset); +}; - case 50:// (Team) Leaderboard Update - { - var offset = 0; - var buffer = new Buffer(0x10000); - - buffer.writeUInt8(50, offset); // Packet ID - offset++; - - var countOffset = offset; - offset += 4; - - var count = 0; - for (var i = 0; i < lb.length; i++) { - - var value = lb[i]; - - // little validation - value = value < 0 ? 0 : value; - value = value > 1 ? 1 : value; - - buffer.writeFloatLE(0, offset); // string zero terminator - offset += 4; - } - buffer.writeUInt32LE(count, countOffset); // Number of elements - return buffer.slice(0, offset); - } - break; +// (FFA) Leaderboard Update +UpdateLeaderboard.prototype.build49 = function (protocol) { + var offset = 0; + var buffer = new Buffer(0x10000); + + buffer.writeUInt8(49, offset); // Packet ID + offset += 1; + + var countOffset = offset; + offset += 4; + + var count = 0; + for (var i = 0; i < this.leaderboard.length; i++) { + var item = this.leaderboard[i]; + if (typeof item == "undefined" || item==null) + continue; + + var isMe = false; // 1 for red color (current player), 0 for white color (other players) + var name = item.getName(); + name = name ? name : ""; + var id = isMe ? 1:0; + + // Write record + buffer.writeUInt32LE(id>>0, offset); // isMe flag (previously cell ID) + offset += 4; + + if (protocol <= 5) { + offset += buffer.write(name, offset, 'ucs2'); // string => unicode + buffer.writeUInt16LE(0, offset); + offset += 2; + } + else { + offset += buffer.write(name, offset); // string => utf8 + buffer.writeUInt8(0, offset); // string zero terminator + offset += 1; + } + count++; + } + buffer.writeUInt32LE(count, countOffset); // Number of elements + return buffer.slice(0, offset); +}; - default: - break; +// (Team) Leaderboard Update +UpdateLeaderboard.prototype.build50 = function (protocol) { + var offset = 0; + var buffer = new Buffer(0x10000); + + buffer.writeUInt8(50, offset); // Packet ID + offset++; + + var countOffset = offset; + offset += 4; + + var count = 0; + for (var i = 0; i < this.leaderboard.length; i++) { + var value = this.leaderboard[i]; + if (value==null) continue; + + // little validation + value = value < 0 ? 0 : value; + value = value > 1 ? 1 : value; + + buffer.writeFloatLE(value, offset); // string zero terminator + offset += 4; + + count++; } + buffer.writeUInt32LE(count, countOffset); // Number of elements + return buffer.slice(0, offset); }; \ No newline at end of file From bddc1aba34fdaaf442fcd8ae3d0f68ab237149af Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 5 Jun 2016 15:41:42 +0200 Subject: [PATCH 044/417] fix custom text leaderboard packet --- src/packet/UpdateLeaderboard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index d43124aa6..60391fed0 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -14,12 +14,12 @@ UpdateLeaderboard.prototype.build = function (protocol) { } } -// Custom Text List? WTF? +// Custom Text List UpdateLeaderboard.prototype.build48 = function (protocol) { var offset = 0; var buffer = new Buffer(0x10000); - buffer.writeUInt8(48, offset); + buffer.writeUInt8(49, offset); offset++; var countOffset = offset; From 0c390b65b64c1396bfae2ec316a14a71cbe6ec0a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 5 Jun 2016 20:20:01 +0200 Subject: [PATCH 045/417] fix mass/size conversion issues; replace remerge physics code with new physics (cell age based) --- src/GameServer.js | 23 ++++++++++------------ src/entity/Cell.js | 19 +++++++++++++------ src/entity/PlayerCell.js | 41 ++++++++++++++++++++++------------------ src/entity/Virus.js | 5 +++-- src/gamemodes/TeamX.js | 2 +- src/gamemodes/TeamZ.js | 27 +++++++++++++------------- 6 files changed, 64 insertions(+), 53 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 27fb6146b..9abcd3850 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -206,7 +206,11 @@ GameServer.prototype.start = function() { this.startStatsServer(this.config.serverStatsPort); }; -GameServer.prototype.getMode = function() { +GameServer.prototype.getTick = function () { + return this.tickCounter; +}; + +GameServer.prototype.getMode = function () { return this.gameMode; }; @@ -682,7 +686,6 @@ GameServer.prototype.createPlayerCell = function(client, parent, angle, mass) { // Cells won't collide immediately newCell.collisionRestoreTicks = 12; parent.collisionRestoreTicks = 12; - newCell.calcMergeTime(this.config.playerRecombineTime); parent.setMass(parent.getMass() - mass); // Remove mass from parent cell // Add to node list @@ -819,7 +822,7 @@ GameServer.prototype.getCellsInRange = function(cell) { // Can't eat self if it's not time to recombine yet if (check.owner == cell.owner) { // If one of cells can't merge - if (!cell.shouldRecombine || !check.shouldRecombine) { + if (!cell.canRemerge() || !check.canRemerge()) { // Check if merge command was triggered on this client if (!cell.owner.mergeOverride) continue; } @@ -905,21 +908,15 @@ GameServer.prototype.updateCells = function() { var massDecay = 1 - (this.config.playerMassDecayRate * this.gameMode.decayMod * 0.05); for (var i = 0; i < this.nodesPlayer.length; i++) { var cell = this.nodesPlayer[i]; - - if (!cell) { - continue; - } + if (!cell) continue; // Recombining - if (cell.owner.cells.length > 1) { - cell.recombineTicks += 0.04; - cell.calcMergeTime(this.config.playerRecombineTime); - } else if (cell.owner.cells.length == 1 && cell.recombineTicks > 0) { - cell.recombineTicks = 0; - cell.shouldRecombine = false; + if (cell.owner.cells.length == 1 && cell.recombineTicks > 0) { cell.owner.mergeOverride = false; cell.owner.mergeOverrideDuration = 0; } + cell.updateRemerge(this); + // Collision if (cell.collisionRestoreTicks > 0) { cell.collisionRestoreTicks--; diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 9e1ffc0cc..4fa89b8e0 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -1,6 +1,8 @@ function Cell(nodeId, owner, position, mass, gameServer) { this.nodeId = nodeId; this.owner = owner; // playerTracker that owns this cell + if (gameServer != null) + this.tickOfBirth = gameServer.getTick(); this.color = { r: 0, g: 255, @@ -62,18 +64,18 @@ Cell.prototype.getMass = function () { return this._mass; }; +Cell.prototype.getSquareSize = function () { + return this._squareSize; +}; + Cell.prototype.setMass = function (mass) { - this._mass = mass; - this._size = Math.ceil(Math.sqrt(100 * mass)); + this._size = Math.sqrt(mass * 100); this._squareSize = this._size * this._size; + this._mass = this._squareSize / 100; if (this.owner) this.owner.massChanged(); }; -Cell.prototype.getSquareSize = function() { - return this._squareSize; -}; - Cell.prototype.addMass = function(n) { // Check if the cell needs to autosplit before adding mass if (this.getMass() > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) { @@ -100,6 +102,11 @@ Cell.prototype.getAngle = function() { return this.angle; }; +Cell.prototype.getAgeTicks = function (tick) { + if (this.tickOfBirth == null) return 0; + return Math.max(0, tick - this.tickOfBirth); +} + Cell.prototype.setMoveEngineData = function(speed, ticks, decay) { this.moveEngineSpeed = speed; this.moveEngineTicks = ticks; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 7176ce44d..82920cd94 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -2,10 +2,9 @@ var Cell = require('./Cell'); function PlayerCell() { Cell.apply(this, Array.prototype.slice.call(arguments)); - + this.cellType = 0; - this.recombineTicks = 0; // Ticks passed after the cell has split - this.shouldRecombine = false; // Should the cell combine. If true, collision with own cells happens + this._canRemerge = false; } module.exports = PlayerCell; @@ -21,18 +20,26 @@ PlayerCell.prototype.simpleCollide = function(check, d) { (this.abs(this.position.y - check.y) < len); }; -PlayerCell.prototype.calcMergeTime = function(base) { - // Check for merging time - var r = false; - if (base == 0 || this.owner.mergeOverride) { - // Instant recombine in config or merge command was triggered for this client - r = true; - } else { - var rec = Math.max(30, 0.02 * this.getSize()); - rec = Math.max(base, rec); - if (this.recombineTicks > rec) r = true; // Can combine with other cells +PlayerCell.prototype.updateRemerge = function (gameServer) { + if (this.owner == null) { + this._canRemerge = false; + return; + } + var tick = gameServer.getTick(); + var baseTtr = gameServer.config.playerRecombineTime; // default baseTtr = 30 + var threshold = 3; // do not remerge if cell age is smaller than 3 ticks + + if (baseTtr == 0 || this.owner.mergeOverride) { + this._canRemerge = this.getAgeTicks(tick) > threshold; + return; } - this.shouldRecombine = r; + var ttr = Math.max(baseTtr, (this.getSize() * 0.2) >> 0); // seconds + // tickStep = 0.040 sec + this._canRemerge = this.getAgeTicks(tick) > (threshold + ttr / 0.040); +} + +PlayerCell.prototype.canRemerge = function () { + return this._canRemerge; }; // Movement @@ -49,8 +56,6 @@ PlayerCell.prototype.calcMove = function(x2, y2, gameServer) { return; } - //var dist = this.getDist(this.position.x, this.position.y, x2, y2); - //var speed = Math.min(this.getSpeed(), dist) / 2; // Twice as slower var dist = Math.sqrt(deltaX*deltaX + deltaY*deltaY); dist = Math.min(dist, 32); var speed = 0; @@ -75,8 +80,8 @@ PlayerCell.prototype.collision = function(gameServer) { if (!cell) continue; // Error if (this.nodeId == cell.nodeId) continue; - if ((!cell.shouldRecombine) || (!this.shouldRecombine)) { - // Cannot recombine - Collision with your own cells + if (!cell.canRemerge() || !this.canRemerge()) { + // Cannot remerge - Collision with your own cells var calcInfo = gameServer.checkCellCollision(this, cell); // Calculation info // Further calculations diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 10a3aa77d..ab9ab7c45 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -88,8 +88,9 @@ Virus.prototype.onConsume = function(consumer, gameServer) { gameServer.createPlayerCell(client, consumer, angle, splitMass); } - // Prevent consumer cell from merging with other cells - consumer.calcMergeTime(gameServer.config.playerRecombineTime); + //// Prevent consumer cell from merging with other cells + //consumer.calcMergeTime(gameServer.getTick(), gameServer.config.playerRecombineTime); + // TODO: ttr fix? }; Virus.prototype.onAdd = function(gameServer) { diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index a9671566e..49afa25cf 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -205,7 +205,7 @@ TeamX.prototype.onServerInit = function(gameServer) { case 0: // Players // Can't eat self if it's not time to recombine yet if (check.owner == cell.owner) { - if ((cell.recombineTicks > 0) || (check.recombineTicks > 0)) { + if (!cell.canRemerge() || !check.canRemerge()) { continue; } diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 91265bbed..2f8a13e88 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -463,7 +463,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { case 0: // Players // Can't eat self if it's not time to recombine yet if (check.owner == cell.owner) { - if ((cell.recombineTicks > 0) || (check.recombineTicks > 0)) { + if (!cell.canRemrege() || !check.canRemerge()) { continue; } @@ -547,7 +547,6 @@ TeamZ.prototype.onServerInit = function(gameServer) { var split = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, newMass); split.setAngle(angle); split.setMoveEngineData(splitSpeed, 32, 0.85); - split.calcMergeTime(this.config.playerRecombineTime); // boost speed if zombie eats brain if (this.gameMode.hasEatenBrain(client) || this.gameMode.isCrazy(client)) { @@ -556,7 +555,8 @@ TeamZ.prototype.onServerInit = function(gameServer) { // gain effect if human eat hero else if (this.gameMode.hasEatenHero(client)) { // fix "unable to split" bug: cell can be merged after finish moving (2nd param in setMoveEngineData) - split.recombineTicks = 2; // main-ticks, 1 main-tick = 1 s + //split.recombineTicks = 2; // main-ticks, 1 main-tick = 1 s + //TODO: fix? } // Add to moving cells list @@ -577,7 +577,6 @@ TeamZ.prototype.onServerInit = function(gameServer) { newCell = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, mass); newCell.setAngle(angle); newCell.setMoveEngineData(speed, 10); - newCell.calcMergeTime(this.config.playerRecombineTime); newCell.ignoreCollision = true; // Turn off collision // boost speed if zombie eats brain @@ -587,7 +586,8 @@ TeamZ.prototype.onServerInit = function(gameServer) { // gain effect if human eat hero else if (this.gameMode.hasEatenHero(client)) { // fix "unable to split" bug - newCell.recombineTicks = 1; + //newCell.recombineTicks = 1; + // TODO: fix? } // Add to moving cells list @@ -642,10 +642,10 @@ TeamZ.prototype.onServerInit = function(gameServer) { consumer.setMass(consumer.getMass() - splitMass); } - if (gameServer.gameMode.hasEatenHero(client)) - consumer.recombineTicks = 0; - else - consumer.calcMergeTime(gameServer.config.playerRecombineTime); + if (gameServer.gameMode.hasEatenHero(client)) { + //consumer.recombineTicks = 0; + // TODO: fix? + } }; // Handle "gamemode" command: @@ -1103,10 +1103,11 @@ Hero.prototype.onConsume = function(consumer, gameServer) { client.heroColorFactor = 0; // Merge immediately - for (var i = 0; i < client.cells.length; i++) { - var cell = client.cells[i]; - cell.recombineTicks = 0; - } + //for (var i = 0; i < client.cells.length; i++) { + // var cell = client.cells[i]; + // cell.recombineTicks = 0; + //} + // TODO: fix? } }; From 01fa80070022c5bb05f70643e6fb07d4d096c63c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 5 Jun 2016 21:05:22 +0200 Subject: [PATCH 046/417] a little remerge calculations optimization --- src/entity/PlayerCell.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 82920cd94..d670a403f 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -26,16 +26,17 @@ PlayerCell.prototype.updateRemerge = function (gameServer) { return; } var tick = gameServer.getTick(); - var baseTtr = gameServer.config.playerRecombineTime; // default baseTtr = 30 - var threshold = 3; // do not remerge if cell age is smaller than 3 ticks - - if (baseTtr == 0 || this.owner.mergeOverride) { - this._canRemerge = this.getAgeTicks(tick) > threshold; + var age = this.getAgeTicks(tick); + if (age < 3 || this.owner.mergeOverride) { + // do not remerge if cell age is smaller than 3 ticks + this._canRemerge = false; return; } - var ttr = Math.max(baseTtr, (this.getSize() * 0.2) >> 0); // seconds - // tickStep = 0.040 sec - this._canRemerge = this.getAgeTicks(tick) > (threshold + ttr / 0.040); + var baseTtr = gameServer.config.playerRecombineTime; // default baseTtr = 30 + var ttr = Math.max(baseTtr, (this.getSize() * 0.2) >> 0); // ttr in seconds + // seconds to ticks (tickStep = 0.040 sec) + ttr /= 0.040; + this._canRemerge = age >= ttr; } PlayerCell.prototype.canRemerge = function () { From f712d95442757152c2aeec737cde2f5b3486067b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 5 Jun 2016 21:39:30 +0200 Subject: [PATCH 047/417] update package --- package.json | 15 ++++++++------- src/PlayerTracker.js | 2 +- src/gamemodes/Experimental.js | 2 +- src/gamemodes/Mode.js | 2 +- src/gamemodes/TeamZ.js | 1 - src/index.js | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 23cff3c35..b6f01d062 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,14 @@ { - "name": "Ogar", + "name": "MultiOgar", "version": "1.0.0", - "description": "Open source Agar.io server", + "description": "Open source Ogar server", + "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", + "homepage": "https://github.com/Barbosik/MultiOgar" + "license": "Apache License, Version 2.0" "main": "src/index.js", "dependencies": { "bytebuffer": "^5.0.1", + "simple-quadtree": "^0.1.3", "vector2-node": "latest", "ws": "latest" }, @@ -14,12 +18,9 @@ }, "repository": { "type": "git", - "url": "https://github.com/OgarProject/Ogar" + "url": "https://github.com/Barbosik/MultiOgar" }, - "author": "Devin Ryan (http://devin.codes/)", - "license": "Apache License 2.0", "bugs": { - "url": "https://github.com/OgarProject/Ogar/issues" + "url": "https://github.com/Barbosik/MultiOgar/issues" }, - "homepage": "https://github.com/OgarProject/Ogar" } diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 7897df42b..b71efdb94 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -349,7 +349,7 @@ PlayerTracker.prototype.getSpectateNodes = function() { PlayerTracker.prototype.moveInFreeRoam = function() { // User is in free roam - // To mimic agar.io, get distance from center to mouse and apply a part of the distance + // To mimic vanilla, get distance from center to mouse and apply a part of the distance var dist = this.gameServer.getDist(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); var angle = this.getAngle(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index e5192ac9f..443df25ed 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -93,7 +93,7 @@ Experimental.prototype.onServerInit = function(gameServer) { var mapHeight = gameServer.config.borderBottom - gameServer.config.borderTop; var mapSize = Math.max(mapWidth, mapHeight); - this.motherMinAmount = Math.ceil(mapSize / 2000); // 7 mother cells for agar.io map size + this.motherMinAmount = Math.ceil(mapSize / 2000); // 7 mother cells for vanilla map size // Special virus mechanics Virus.prototype.feed = function(feeder, gameServer) { diff --git a/src/gamemodes/Mode.js b/src/gamemodes/Mode.js index 53ecce9a9..c9b638587 100644 --- a/src/gamemodes/Mode.js +++ b/src/gamemodes/Mode.js @@ -61,7 +61,7 @@ Mode.prototype.onCellRemove = function(cell) { // Called when a player cell is removed }; -Mode.prototype.onCellMove = function(x1, y1, cell) { +Mode.prototype.onCellMove = function(cell, gameServer) { // Called when a player cell is moved }; diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 2f8a13e88..110ce57ba 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -324,7 +324,6 @@ TeamZ.prototype.onServerInit = function(gameServer) { //OVERWRITE GLOBAL FUNCTIONs to adapt Zombie Team mode - // Change to AGARIO colorful scheme GameServer.prototype.getRandomColor = function() { var colorRGB = [0xFF, 0x07, (Math.random() * 256) >> 0]; colorRGB.sort(function() { diff --git a/src/index.js b/src/index.js index b12016ac3..10e5f1627 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,7 @@ var GameServer = require('./GameServer'); var showConsole = true; // Start msg -console.log("[Game] Ogar - An open source Agar.io server implementation"); +console.log("[Game] MultiOgar - An open source multi-protocol ogar server"); // Handle arguments process.argv.forEach(function(val) { From a54c787d4c3542df94e8e251e267a3420e26fb20 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 00:19:48 +0200 Subject: [PATCH 048/417] protocol code refactoring; remove bytebuffer dependency --- package.json | 1 - src/packet/SetBorder.js | 28 +++--- src/packet/UpdateLeaderboard.js | 118 ++++++++----------------- src/packet/UpdateNodes.js | 148 ++++++++++++++------------------ src/packet/index.js | 1 + 5 files changed, 114 insertions(+), 182 deletions(-) diff --git a/package.json b/package.json index b6f01d062..e2ca1a9ab 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "license": "Apache License, Version 2.0" "main": "src/index.js", "dependencies": { - "bytebuffer": "^5.0.1", "simple-quadtree": "^0.1.3", "vector2-node": "latest", "ws": "latest" diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index 1f7566bf4..861c32bcf 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -1,5 +1,5 @@ // Import -var ByteBuffer = require("bytebuffer"); +var BinaryWriter = require("./BinaryWriter"); function SetBorder(left, right, top, bottom, gameType, serverName) { @@ -14,21 +14,17 @@ function SetBorder(left, right, top, bottom, gameType, serverName) { module.exports = SetBorder; SetBorder.prototype.build = function(protocol) { - var buffer = new ByteBuffer(); - buffer.LE(true); - buffer.writeUInt8(0x40); - buffer.writeDouble(this.left); - buffer.writeDouble(this.top); - buffer.writeDouble(this.right); - buffer.writeDouble(this.bottom); - if (typeof this.gameType != "undefined" && this.gameType != null) { - buffer.writeUInt32(this.gameType >> 0); + var writer = new BinaryWriter(); + writer.writeUInt8(0x40); // Packet ID + writer.writeDouble(this.left); + writer.writeDouble(this.top); + writer.writeDouble(this.right); + writer.writeDouble(this.bottom); + if (this.gameType != null) { + writer.writeUInt32(this.gameType >> 0); var name = this.serverName; - if (typeof name == "undefined" || name == null) { - name = ""; - } - buffer.writeUTF8String(name); - buffer.writeByte(0); + if (name == null) name = ""; + writer.writeStringZeroUtf8(name); } - return buffer.buffer.slice(0, buffer.offset); + return writer.ToBuffer(); }; diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index 60391fed0..863f2300f 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -1,3 +1,7 @@ +// Import +var BinaryWriter = require("./BinaryWriter"); + + function UpdateLeaderboard(leaderboard, packetLB) { this.leaderboard = leaderboard; this.packetLB = packetLB; @@ -16,110 +20,64 @@ UpdateLeaderboard.prototype.build = function (protocol) { // Custom Text List UpdateLeaderboard.prototype.build48 = function (protocol) { - var offset = 0; - var buffer = new Buffer(0x10000); - - buffer.writeUInt8(49, offset); - offset++; - - var countOffset = offset; - offset += 4; - - var count = 0; + var writer = new BinaryWriter(); + writer.writeUInt8(0x31); // Packet ID + writer.writeUInt32(this.leaderboard.length >> 0); // Number of elements for (var i = 0; i < this.leaderboard.length; i++) { var item = this.leaderboard[i]; - if (typeof item == "undefined" || item==null) - continue; + if (item == null) return null; // bad leaderboardm just don't send it var name = item; name = name ? name : ""; + var id = 0; - buffer.writeUInt32LE(0, offset); - offset += 4; - - if (protocol <= 5) { - offset += buffer.write(name, offset, 'ucs2'); // string => unicode - buffer.writeUInt16LE(0, offset); - offset += 2; - } - else { - offset += buffer.write(name, offset); // string => utf8 - buffer.writeUInt8(0, offset); // string zero terminator - offset += 1; - } - count++; + writer.writeUInt32(id >> 0); // isMe flag (previously cell ID) + if (protocol <= 5) + writer.writeStringZeroUnicode(name); + else + writer.writeStringZeroUtf8(name); } - buffer.writeUInt32LE(count, countOffset); // Number of elements - return buffer.slice(0, offset); + return writer.ToBuffer(); }; // (FFA) Leaderboard Update UpdateLeaderboard.prototype.build49 = function (protocol) { - var offset = 0; - var buffer = new Buffer(0x10000); - - buffer.writeUInt8(49, offset); // Packet ID - offset += 1; - - var countOffset = offset; - offset += 4; - - var count = 0; + var writer = new BinaryWriter(); + writer.writeUInt8(0x31); // Packet ID + writer.writeUInt32(this.leaderboard.length >> 0); // Number of elements for (var i = 0; i < this.leaderboard.length; i++) { var item = this.leaderboard[i]; - if (typeof item == "undefined" || item==null) - continue; - - var isMe = false; // 1 for red color (current player), 0 for white color (other players) + if (item == null) return null; // bad leaderboardm just don't send it + + var isMe = false; // true for red color (current player), false for white color (other players) var name = item.getName(); - name = name ? name : ""; + name = name != null ? name : ""; var id = isMe ? 1:0; - - // Write record - buffer.writeUInt32LE(id>>0, offset); // isMe flag (previously cell ID) - offset += 4; - - if (protocol <= 5) { - offset += buffer.write(name, offset, 'ucs2'); // string => unicode - buffer.writeUInt16LE(0, offset); - offset += 2; - } - else { - offset += buffer.write(name, offset); // string => utf8 - buffer.writeUInt8(0, offset); // string zero terminator - offset += 1; - } - count++; + + writer.writeUInt32(id >> 0); // isMe flag (previously cell ID) + if (protocol <= 5) + writer.writeStringZeroUnicode(name); + else + writer.writeStringZeroUtf8(name); } - buffer.writeUInt32LE(count, countOffset); // Number of elements - return buffer.slice(0, offset); + return writer.ToBuffer(); }; // (Team) Leaderboard Update UpdateLeaderboard.prototype.build50 = function (protocol) { - var offset = 0; - var buffer = new Buffer(0x10000); - - buffer.writeUInt8(50, offset); // Packet ID - offset++; - - var countOffset = offset; - offset += 4; - - var count = 0; + var writer = new BinaryWriter(); + writer.writeUInt8(0x32); // Packet ID + writer.writeUInt32(this.leaderboard.length >> 0); // Number of elements for (var i = 0; i < this.leaderboard.length; i++) { - var value = this.leaderboard[i]; - if (value==null) continue; + var item = this.leaderboard[i]; + if (item == null) return null; // bad leaderboardm just don't send it - // little validation + var value = item; + if (isNaN(value)) value = 0; value = value < 0 ? 0 : value; value = value > 1 ? 1 : value; - buffer.writeFloatLE(value, offset); // string zero terminator - offset += 4; - - count++; + writer.writeFloat(value); // isMe flag (previously cell ID) } - buffer.writeUInt32LE(count, countOffset); // Number of elements - return buffer.slice(0, offset); + return writer.ToBuffer(); }; \ No newline at end of file diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index 4a2379e85..a6c78dcf8 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -1,5 +1,5 @@ // Import -var ByteBuffer = require("bytebuffer"); +var BinaryWriter = require("./BinaryWriter"); function UpdateNodes(destroyQueue, nodes, nonVisibleNodes, scrambleX, scrambleY) { @@ -25,8 +25,8 @@ UpdateNodes.prototype.build = function (protocol) { // protocol 4 UpdateNodes.prototype.build4 = function () { - var buffer = new ByteBuffer(); - buffer.LE(true); + var writer = new BinaryWriter(); + writer.writeUInt8(0x10); // Packet ID var deadCells = []; for (var i = 0; i < this.destroyQueue.length; i++) { @@ -34,22 +34,20 @@ UpdateNodes.prototype.build4 = function () { if (!node) continue; deadCells.push(node); } - - buffer.writeUInt8(0x10); - buffer.writeUInt16(deadCells.length); // EatRecordCount + writer.writeUInt16(deadCells.length >> 0); // EatRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; var hunterId = 0; if (node.getKiller()) { hunterId = node.getKiller().nodeId; } - buffer.writeUInt32(hunterId); // Hunter ID - buffer.writeUInt32(node.nodeId); // Prey ID + writer.writeUInt32(hunterId >> 0); // Hunter ID + writer.writeUInt32(node.nodeId >> 0); // Prey ID } for (var i = 0; i < this.nodes.length; i++) { var node = this.nodes[i]; - if (typeof node == "undefined" || !node || node.nodeId == 0) + if (node==null || node.nodeId == 0) continue; var cellX = node.position.x + this.scrambleX; @@ -64,13 +62,13 @@ UpdateNodes.prototype.build4 = function () { var isEject = node.cellType == 3; // Write update record - buffer.writeUInt32(node.nodeId); // Cell ID - buffer.writeInt16(cellX >> 0); // Coordinate X - buffer.writeInt16(cellY >> 0); // Coordinate Y - buffer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) - buffer.writeUInt8(node.color.r >> 0); // Color R - buffer.writeUInt8(node.color.g >> 0); // Color G - buffer.writeUInt8(node.color.b >> 0); // Color B + writer.writeUInt32(node.nodeId >> 0); // Cell ID + writer.writeInt16(cellX >> 0); // Coordinate X + writer.writeInt16(cellY >> 0); // Coordinate Y + writer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + writer.writeUInt8(node.color.r >> 0); // Color R + writer.writeUInt8(node.color.g >> 0); // Color G + writer.writeUInt8(node.color.b >> 0); // Color B var flags = 0; if (isVirus) @@ -79,33 +77,28 @@ UpdateNodes.prototype.build4 = function () { flags |= 0x10; if (isEject) flags |= 0x20; - buffer.writeUInt8(flags >> 0); // Flags - - for (var j = 0; j < cellName.length && cellName.charCodeAt(j)!=0; j++) { - buffer.writeUInt16(cellName.charCodeAt(j) >> 0); // Cell Name in Unicode - } - buffer.writeUInt16(0); // Zero-terminator + writer.writeUInt8(flags >> 0); // Flags + writer.writeStringZeroUnicode(cellName); // Name } - buffer.writeUInt32(0); // Cell Update record terminator + writer.writeUInt32(0); // Cell Update record terminator for (var i = 0; i < this.nonVisibleNodes.length; i++) { var node = this.nonVisibleNodes[i]; if (!node) continue; deadCells.push(node); } - - buffer.writeUInt32(deadCells.length); // RemoveRecordCount + writer.writeUInt32(deadCells.length >> 0); // RemoveRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; - buffer.writeUInt32(node.nodeId); // Cell ID + writer.writeUInt32(node.nodeId); // Cell ID } - return buffer.buffer.slice(0, buffer.offset); + return writer.ToBuffer(); } // protocol 5 UpdateNodes.prototype.build5 = function () { - var buffer = new ByteBuffer(); - buffer.LE(true); + var writer = new BinaryWriter(); + writer.writeUInt8(0x10); // Packet ID var deadCells = []; for (var i = 0; i < this.destroyQueue.length; i++) { @@ -113,22 +106,20 @@ UpdateNodes.prototype.build5 = function () { if (!node) continue; deadCells.push(node); } - - buffer.writeUInt8(0x10); - buffer.writeUInt16(deadCells.length); // EatRecordCount + writer.writeUInt16(deadCells.length >> 0); // EatRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; var hunterId = 0; if (node.getKiller()) { hunterId = node.getKiller().nodeId; } - buffer.writeUInt32(hunterId); // Hunter ID - buffer.writeUInt32(node.nodeId); // Prey ID + writer.writeUInt32(hunterId >> 0); // Hunter ID + writer.writeUInt32(node.nodeId >> 0); // Prey ID } for (var i = 0; i < this.nodes.length; i++) { var node = this.nodes[i]; - if (typeof node == "undefined" || !node || node.nodeId == 0) + if (node==null || node.nodeId == 0) continue; var cellX = node.position.x + this.scrambleX; @@ -146,13 +137,13 @@ UpdateNodes.prototype.build5 = function () { var isEject = node.cellType == 3; // Write update record - buffer.writeUInt32(node.nodeId); // Cell ID - buffer.writeInt32(cellX >> 0); // Coordinate X - buffer.writeInt32(cellY >> 0); // Coordinate Y - buffer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) - buffer.writeUInt8(node.color.r >> 0); // Color R - buffer.writeUInt8(node.color.g >> 0); // Color G - buffer.writeUInt8(node.color.b >> 0); // Color B + writer.writeUInt32(node.nodeId >> 0); // Cell ID + writer.writeInt32(cellX >> 0); // Coordinate X + writer.writeInt32(cellY >> 0); // Coordinate Y + writer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + writer.writeUInt8(node.color.r >> 0); // Color R + writer.writeUInt8(node.color.g >> 0); // Color G + writer.writeUInt8(node.color.b >> 0); // Color B var flags = 0; if (isVirus) @@ -163,38 +154,32 @@ UpdateNodes.prototype.build5 = function () { flags |= 0x10; if (isEject) flags |= 0x20; - buffer.writeUInt8(flags >> 0); // Flags + writer.writeUInt8(flags >> 0); // Flags - if (isSkinPresent) { - buffer.writeUTF8String(skinName); // Skin Name in UTF8 - buffer.writeUInt8(0); // Zero-terminator - } + if (isSkinPresent) + writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 - for (var j = 0; j < cellName.length && cellName.charCodeAt(j) != 0; j++) { - buffer.writeUInt16(cellName.charCodeAt(j) >> 0); // Cell Name in Unicode - } - buffer.writeUInt16(0); // Zero-terminator + writer.writeStringZeroUnicode(cellName); // Cell Name } - buffer.writeUInt32(0); // Cell Update record terminator + writer.writeUInt32(0 >> 0); // Cell Update record terminator for (var i = 0; i < this.nonVisibleNodes.length; i++) { var node = this.nonVisibleNodes[i]; if (!node) continue; deadCells.push(node); } - - buffer.writeUInt32(deadCells.length); // RemoveRecordCount + writer.writeUInt32(deadCells.length >> 0); // RemoveRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; - buffer.writeUInt32(node.nodeId); // Cell ID + writer.writeUInt32(node.nodeId >> 0); // Cell ID } - return buffer.buffer.slice(0, buffer.offset); + return writer.ToBuffer(); } // protocol 6 UpdateNodes.prototype.build6 = function () { - var buffer = new ByteBuffer(); - buffer.LE(true); + var writer = new BinaryWriter(); + writer.writeUInt8(0x10); // Packet ID var deadCells = []; for (var i = 0; i < this.destroyQueue.length; i++) { @@ -202,22 +187,20 @@ UpdateNodes.prototype.build6 = function () { if (!node) continue; deadCells.push(node); } - - buffer.writeUInt8(0x10); - buffer.writeUInt16(deadCells.length); // EatRecordCount + writer.writeUInt16(deadCells.length >> 0); // EatRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; var hunterId = 0; if (node.getKiller()) { hunterId = node.getKiller().nodeId; } - buffer.writeUInt32(hunterId); // Hunter ID - buffer.writeUInt32(node.nodeId); // Prey ID + writer.writeUInt32(hunterId >> 0); // Hunter ID + writer.writeUInt32(node.nodeId >> 0); // Prey ID } for (var i = 0; i < this.nodes.length; i++) { var node = this.nodes[i]; - if (typeof node == "undefined" || !node || node.nodeId == 0) + if (node==null || node.nodeId == 0) continue; var cellX = node.position.x + this.scrambleX; @@ -234,10 +217,10 @@ UpdateNodes.prototype.build6 = function () { var isEject = node.cellType==3; // Write update record - buffer.writeUInt32(node.nodeId); // Cell ID - buffer.writeInt32(cellX >> 0); // Coordinate X - buffer.writeInt32(cellY >> 0); // Coordinate Y - buffer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + writer.writeUInt32(node.nodeId >> 0); // Cell ID + writer.writeInt32(cellX >> 0); // Coordinate X + writer.writeInt32(cellY >> 0); // Coordinate Y + writer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) var flags = 0; if (isVirus) @@ -252,34 +235,29 @@ UpdateNodes.prototype.build6 = function () { flags |= 0x10; if (isEject) flags |= 0x20; - buffer.writeUInt8(flags >> 0); // Flags + writer.writeUInt8(flags >> 0); // Flags if (isColorPresent) { - buffer.writeUInt8(node.color.r >> 0); // Color R - buffer.writeUInt8(node.color.g >> 0); // Color G - buffer.writeUInt8(node.color.b >> 0); // Color B - } - if (isSkinPresent) { - buffer.writeUTF8String(skinName); // Skin Name in UTF8 - buffer.writeUInt8(0); // Zero-terminator - } - if (isNamePresent) { - buffer.writeUTF8String(cellName); // Cell Name in UTF8 - buffer.writeUInt8(0); // Zero-terminator + writer.writeUInt8(node.color.r >> 0); // Color R + writer.writeUInt8(node.color.g >> 0); // Color G + writer.writeUInt8(node.color.b >> 0); // Color B } + if (isSkinPresent) + writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 + if (isNamePresent) + writer.writeStringZeroUtf8(cellName); // Cell Name in UTF8 } - buffer.writeUInt32(0); // Cell Update record terminator + writer.writeUInt32(0); // Cell Update record terminator for (var i = 0; i < this.nonVisibleNodes.length; i++) { var node = this.nonVisibleNodes[i]; if (!node) continue; deadCells.push(node); } - - buffer.writeUInt16(deadCells.length); // RemoveRecordCount + writer.writeUInt16(deadCells.length >> 0); // RemoveRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; - buffer.writeUInt32(node.nodeId); // Cell ID + writer.writeUInt32(node.nodeId >> 0); // Cell ID } - return buffer.buffer.slice(0, buffer.offset); + return writer.ToBuffer(); } diff --git a/src/packet/index.js b/src/packet/index.js index 9e44c4627..46bbdc6e7 100644 --- a/src/packet/index.js +++ b/src/packet/index.js @@ -1,4 +1,5 @@ module.exports = { + BinaryWriter: require('./BinaryWriter'), AddNode: require('./AddNode'), ClearNodes: require('./ClearNodes'), UpdatePosition: require('./UpdatePosition'), From f4f61b1618f16cd1d36bc46732d5993649ca4845 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 00:20:48 +0200 Subject: [PATCH 049/417] protocol code refactoring; remove bytebuffer dependency (add missing file) --- src/packet/BinaryWriter.js | 121 +++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 src/packet/BinaryWriter.js diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js new file mode 100644 index 000000000..a58416857 --- /dev/null +++ b/src/packet/BinaryWriter.js @@ -0,0 +1,121 @@ +/* +* Simple BinaryWriter is a minimal tool to write binary stream with unpredictable size. +* Useful for binary serialization. +* +* Copyright (c) 2016 Barbosik https://github.com/Barbosik +* License: Apache License, Version 2.0 +*/ +function BinaryWriter() { + this._offset = 0; + this._buffer = null; +} + +module.exports = BinaryWriter; + +BinaryWriter.prototype.writeUInt8 = function (value) { + this.allocCheck(1); + this._buffer.writeUInt8(value, this._offset); + this._offset += 1; +}; + +BinaryWriter.prototype.writeInt8 = function (value) { + this.allocCheck(1); + this._buffer.writeInt8(value, this._offset); + this._offset += 1; +}; + +BinaryWriter.prototype.writeUInt16 = function (value) { + this.allocCheck(2); + this._buffer.writeUInt16LE(value, this._offset); + this._offset += 2; +}; + +BinaryWriter.prototype.writeInt16 = function (value) { + this.allocCheck(2); + this._buffer.writeInt16LE(value, this._offset); + this._offset += 2; +}; + +BinaryWriter.prototype.writeUInt32 = function (value) { + this.allocCheck(4); + this._buffer.writeUInt32LE(value, this._offset); + this._offset += 4; +}; + +BinaryWriter.prototype.writeInt32 = function (value) { + this.allocCheck(4); + this._buffer.writeInt32LE(value, this._offset); + this._offset += 4; +}; + +BinaryWriter.prototype.writeFloat = function (value) { + this.allocCheck(4); + this._buffer.writeFloatLE(value, this._offset); + this._offset += 4; +}; + +BinaryWriter.prototype.writeDouble = function (value) { + this.allocCheck(8); + this._buffer.writeDoubleLE(value, this._offset); + this._offset += 8; +}; + +BinaryWriter.prototype.writeDouble = function (value) { + this.allocCheck(8); + this._buffer.writeDoubleLE(value, this._offset); + this._offset += 8; +}; + +BinaryWriter.prototype.writeStringUtf8 = function (value) { + var length = Buffer.byteLength(value, 'utf8') + this.allocCheck(length); + this._buffer.write(value, this._offset, 'utf8'); + this._offset += length; +}; + +BinaryWriter.prototype.writeStringZeroUtf8 = function (value) { + this.writeStringUtf8(value); + this.writeUInt8(0); +}; + +BinaryWriter.prototype.writeStringUnicode = function (value) { + var length = Buffer.byteLength(value, 'ucs2') + this.allocCheck(length); + this._buffer.write(value, this._offset, 'ucs2'); + this._offset += length; +}; + +BinaryWriter.prototype.writeStringZeroUnicode = function (value) { + this.writeStringUnicode(value); + this.writeUInt16(0); +}; + + +BinaryWriter.prototype.allocCheck = function (size) { + if (this._buffer == null) { + var allocSize = size + Buffer.poolSize - (size % Buffer.poolSize); + this._buffer = this.allocBuffer(allocSize); + } + var needed = this._offset + size; + if (needed <= this._buffer.length) + return; + var addSize = needed - this._buffer.length; + if (addSize < Buffer.poolSize) { + addSize = Buffer.poolSize; + } else { + addSize += Buffer.poolSize - (addSize % Buffer.poolSize); + } + var buffer2 = this.allocBuffer(addSize); + this._buffer = Buffer.concat([this._buffer, buffer2], this._buffer.length + buffer2.length); +} + +BinaryWriter.prototype.allocBuffer = function (size) { + if (Buffer.allocUnsafe == null) // node.js < v6? + return new Buffer(size); + return Buffer.allocUnsafe(poolSize); +} + +BinaryWriter.prototype.ToBuffer = function () { + if (this._buffer == null) return new Buffer(0); + return this._buffer.slice(0, this._offset); +}; From 3a6be5593bef75d2db423bad68c5172f95875864 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 01:20:42 +0200 Subject: [PATCH 050/417] fix timer lag issues --- src/GameServer.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index 9abcd3850..43bb56736 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -38,6 +38,7 @@ function GameServer() { // Main loop tick this.startTime = +new Date; this.tickCounter = 0; + this.timeStamp = 0; this.tickSpawn = 0; // Used with spawning food @@ -121,7 +122,8 @@ GameServer.prototype.start = function() { this.startingFood(); // Start Main Loop - setInterval(this.mainLoop.bind(this), 40); + //setInterval(this.mainLoop.bind(this), 40); + setInterval(this.timerLoop.bind(this), 1); // Done console.log("[Game] Listening on port " + this.config.serverPort); @@ -206,6 +208,17 @@ GameServer.prototype.start = function() { this.startStatsServer(this.config.serverStatsPort); }; +GameServer.prototype.timerLoop = function () { + var ts = new Date().getTime(); + if (ts - this.timeStamp < 40) + return; + if (this.timeStamp == 0) + this.timeStamp = ts; + this.timeStamp += 40; + + setTimeout(this.mainLoop.bind(this), 0); +}; + GameServer.prototype.getTick = function () { return this.tickCounter; }; From 22b39e52383493f31af1297ca132b100936630dc Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 02:42:41 +0200 Subject: [PATCH 051/417] timerLoop fix --- src/GameServer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index 43bb56736..3791b6f9b 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -215,7 +215,10 @@ GameServer.prototype.timerLoop = function () { if (this.timeStamp == 0) this.timeStamp = ts; this.timeStamp += 40; - + if (this.timeStamp + 400 < ts) { + // high lag detected, resynchronize + this.timeStamp = ts - 80; + } setTimeout(this.mainLoop.bind(this), 0); }; From 68439513d79ae9b5d04acceb5c292f5686aca472 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 04:17:08 +0200 Subject: [PATCH 052/417] protocol code refactoring --- src/PacketHandler.js | 52 +++-------------- src/packet/BinaryReader.js | 113 +++++++++++++++++++++++++++++++++++++ src/packet/BinaryWriter.js | 17 ++++-- src/packet/index.js | 1 + 4 files changed, 134 insertions(+), 49 deletions(-) create mode 100644 src/packet/BinaryReader.js diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 6d9401578..1fbe7f9ca 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -1,4 +1,5 @@ var Packet = require('./packet'); +var BinaryReader = require('./packet/BinaryReader'); function PacketHandler(gameServer, socket) { this.gameServer = gameServer; @@ -37,17 +38,13 @@ PacketHandler.prototype.handleMessage = function(message) { switch (packetId) { case 0: - var bufferName = new Buffer(message); - bufferName = bufferName.slice(1); - - var text = ""; - if (bufferName.length > 0) { - if (this.protocol <= 5) { - text = this.readStringUnicode(bufferName); - } else { - text = this.readStringUtf8(bufferName); - } - } + var reader = new BinaryReader(message); + reader.readUInt8(); + var text = null; + if (this.protocol <= 5) + text = reader.readStringZeroUnicode(); + else + text = reader.readStringZeroUtf8(); this.setNickname(text); break; @@ -111,39 +108,6 @@ PacketHandler.prototype.handleMessage = function(message) { } }; -PacketHandler.prototype.readStringUnicode = function (dataBuffer) { - if (dataBuffer.length > 512) return ""; - - var buffer = new Buffer(dataBuffer); - var text = ""; - var maxLength = (buffer.length / 2) >> 0; - if (maxLength * 2 > buffer.length) maxLength--; - if (maxLength < 0) maxLength = 0; - for (var i = 0; i < maxLength; i++) { - var charCode = buffer.readUInt16LE(i * 2); - if (charCode == 0) { - break; - } - text += String.fromCharCode(charCode); - } - return text; -} - -PacketHandler.prototype.readStringUtf8 = function (dataBuffer) { - if (dataBuffer.length > 512) return ""; - - var buffer = new Buffer(dataBuffer); - var maxLen = buffer.length; - for (var i = 0; i < maxLen; i++) { - if (buffer[i] == 0) { - maxLen = i; - break; - } - } - var text = buffer.toString('utf8', 0, maxLen); // utf8 => string - return text; -} - PacketHandler.prototype.setNickname = function(text) { var client = this.socket.playerTracker; if (client.cells.length < 1) { diff --git a/src/packet/BinaryReader.js b/src/packet/BinaryReader.js new file mode 100644 index 000000000..5c0bd5ead --- /dev/null +++ b/src/packet/BinaryReader.js @@ -0,0 +1,113 @@ +/* +* Simple BinaryReader is a minimal tool to read binary stream. +* Useful for binary deserialization. +* +* Copyright (c) 2016 Barbosik https://github.com/Barbosik +* License: Apache License, Version 2.0 +*/ +function BinaryReader(buffer) { + this._offset = 0; + this._buffer = new Buffer(buffer); +} + +module.exports = BinaryReader; + +BinaryReader.prototype.readUInt8 = function () { + var value = this._buffer.readUInt8(this._offset); + this._offset += 1; + return value; +}; + +BinaryReader.prototype.readInt8 = function () { + var value = this._buffer.readInt8(this._offset); + this._offset += 1; + return value; +}; + +BinaryReader.prototype.readUInt16 = function () { + var value = this._buffer.readUInt16LE(this._offset); + this._offset += 2; + return value; +}; + +BinaryReader.prototype.readInt16 = function () { + var value = this._buffer.readInt16LE(this._offset); + this._offset += 2; + return value; +}; + +BinaryReader.prototype.readUInt32 = function () { + var value = this._buffer.readUInt32LE(this._offset); + this._offset += 4; + return value; +}; + +BinaryReader.prototype.readInt32 = function () { + var value = this._buffer.readInt32LE(this._offset); + this._offset += 4; + return value; +}; + +BinaryReader.prototype.readFloat = function () { + var value = this._buffer.readFloatLE(this._offset); + this._offset += 4; + return value; +}; + +BinaryReader.prototype.readDouble = function () { + var value = this._buffer.readDoubleLE(this._offset); + this._offset += 8; + return value; +}; + +BinaryReader.prototype.readBytes = function (length) { + return this._buffer.slice(this._offset, this._offset + length); +}; + +BinaryReader.prototype.readStringUtf8 = function (length) { + if (length == null) length = this._buffer.length - this._offset; + length = Math.max(0, length); + var value = this._buffer.toString('utf8', this._offset, this._offset + length); + this._offset += length; + return value; +}; + +BinaryReader.prototype.readStringUnicode = function (length) { + if (length == null) length = this._buffer.length - this._offset; + length = Math.max(0, length); + var safeLength = length - (length % 2); + safeLength = Math.max(0, safeLength); + var value = this._buffer.toString('ucs2', this._offset, this._offset + safeLength); + this._offset += length; + return value; +}; + +BinaryReader.prototype.readStringZeroUtf8 = function () { + var length = 0; + var terminatorLength = 0; + for (var i = this._offset; i < this._buffer.length; i++) { + if (this._buffer.readUInt8(i) == 0) { + terminatorLength = 1; + break; + } + length++; + } + var value = this.readStringUtf8(length); + this._offset += terminatorLength; + return value; +}; + +BinaryReader.prototype.readStringZeroUnicode = function () { + var length = 0; + var terminatorLength = ((this._buffer.length - this._offset) & 1) != 0 ? 1 : 0; + for (var i = this._offset; i+1 < this._buffer.length; i+=2) { + if (this._buffer.readUInt16LE(i) == 0) { + terminatorLength = 2; + break; + } + length+=2; + } + var value = this.readStringUnicode(length); + this._offset += terminatorLength; + return value; +}; diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index a58416857..dc5edd1f9 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -66,6 +66,13 @@ BinaryWriter.prototype.writeDouble = function (value) { this._offset += 8; }; +BinaryWriter.prototype.writeBytes = function (data) { + var length = data.length; + this.allocCheck(length); + data.copy(this._buffer, this._offset, 0, length); + this._offset += length; +}; + BinaryWriter.prototype.writeStringUtf8 = function (value) { var length = Buffer.byteLength(value, 'utf8') this.allocCheck(length); @@ -73,11 +80,6 @@ BinaryWriter.prototype.writeStringUtf8 = function (value) { this._offset += length; }; -BinaryWriter.prototype.writeStringZeroUtf8 = function (value) { - this.writeStringUtf8(value); - this.writeUInt8(0); -}; - BinaryWriter.prototype.writeStringUnicode = function (value) { var length = Buffer.byteLength(value, 'ucs2') this.allocCheck(length); @@ -85,6 +87,11 @@ BinaryWriter.prototype.writeStringUnicode = function (value) { this._offset += length; }; +BinaryWriter.prototype.writeStringZeroUtf8 = function (value) { + this.writeStringUtf8(value); + this.writeUInt8(0); +}; + BinaryWriter.prototype.writeStringZeroUnicode = function (value) { this.writeStringUnicode(value); this.writeUInt16(0); diff --git a/src/packet/index.js b/src/packet/index.js index 46bbdc6e7..74486d0ea 100644 --- a/src/packet/index.js +++ b/src/packet/index.js @@ -1,5 +1,6 @@ module.exports = { BinaryWriter: require('./BinaryWriter'), + BinaryReader: require('./BinaryReader'), AddNode: require('./AddNode'), ClearNodes: require('./ClearNodes'), UpdatePosition: require('./UpdatePosition'), From cbdd10d1c574e9663ccba79e6caead4abbe06a5c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 16:18:23 +0200 Subject: [PATCH 053/417] code refactoring --- src/entity/Cell.js | 92 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 47 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 4fa89b8e0..0748a1ebf 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -187,14 +187,16 @@ Cell.prototype.visibleCheck = function (box, centerPos, cells) { Cell.prototype.calcMovePhys = function(config) { // Move, twice as slower - var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0); - var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0); + var x = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0); + var y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0); // Movement engine - if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) this.moveEngineSpeed = 0; + if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) + this.moveEngineSpeed = 0; var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay; this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower - if (this.moveEngineTicks >= 0.5) this.moveEngineTicks -= 0.5; // Ticks passing twice as slower + if (this.moveEngineTicks >= 0.5) + this.moveEngineTicks -= 0.5; // Ticks passing twice as slower // Ejected cell collision if (this.cellType == 3) { @@ -218,72 +220,60 @@ Cell.prototype.calcMovePhys = function(config) { var move = allowDist - dist; - X += Math.sin(angle) * move / 2; - Y += Math.cos(angle) * move / 2; + x += Math.sin(angle) * move / 2; + y += Math.cos(angle) * move / 2; } } } //// Border check - Bouncy physics var radius = 40; - if (X < config.borderLeft && this.position.x != X) { + if (x < config.borderLeft && this.position.x != x) { // Flip angle horizontally - Left side this.angle = 6.28 - this.angle; - var p = this.getLineIntersect( - this.position.x, this.position.y, X, Y, + var p = this.getLineIntersection( + this.position.x, this.position.y, x, y, config.borderLeft, config.borderBottom, config.borderLeft, config.borderTop); - X = p.x; - Y = p.y; + x = p.x; + y = p.y; } - if (X > config.borderRight && this.position.y != X) { + if (x > config.borderRight && this.position.y != x) { // Flip angle horizontally - Right side this.angle = 6.28 - this.angle; - var p = this.getLineIntersect( - this.position.x, this.position.y, X, Y, + var p = this.getLineIntersection( + this.position.x, this.position.y, x, y, config.borderRight, config.borderBottom, config.borderRight, config.borderTop); - X = p.x; - Y = p.y; + x = p.x; + y = p.y; } - if (Y < config.borderTop && this.position.y != Y) { + if (y < config.borderTop && this.position.y != y) { // Flip angle vertically - Top side this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - var p = this.getLineIntersect( - this.position.x, this.position.y, X, Y, + var p = this.getLineIntersection( + this.position.x, this.position.y, x, y, config.borderRight, config.borderTop, config.borderLeft, config.borderTop); - X = p.x; - Y = p.y; + x = p.x; + y = p.y; } - if (Y > config.borderBottom && this.position.y != Y) { + if (y > config.borderBottom && this.position.y != y) { // Flip angle vertically - Bottom side this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - var p = this.getLineIntersect( - this.position.x, this.position.y, X, Y, + var p = this.getLineIntersection( + this.position.x, this.position.y, x, y, config.borderRight, config.borderBottom, config.borderLeft, config.borderBottom); - X = p.x; - Y = p.y; + x = p.x; + y = p.y; } // Set position - this.position.x = X; - this.position.y = Y; + this.position.x = x; + this.position.y = y; }; -Cell.prototype.getLineIntersect = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { - var z1 = p1x - p0x; - var z2 = p3x - p2x; - var w1 = p1y - p0y; - var w2 = p3y - p2y; - var k2 = (z1 * (p2y - p0y) + w1 * (p0x - p2x)) / (w1 * z2 - z1 * w2); - return { - x: p2x + z2 * k2, - y: p2y + w2 * k2 - }; -} - // Override these Cell.prototype.sendUpdate = function() { @@ -313,16 +303,24 @@ Cell.prototype.moveDone = function(gameServer) { // Lib +Cell.prototype.getLineIntersection = function (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { + var z1 = p1x - p0x; + var z2 = p3x - p2x; + var w1 = p1y - p0y; + var w2 = p3y - p2y; + var k2 = (z1 * (p2y - p0y) + w1 * (p0x - p2x)) / (w1 * z2 - z1 * w2); + return { + x: p2x + z2 * k2, + y: p2y + w2 * k2 + }; +} + Cell.prototype.abs = function(x) { return x < 0 ? -x : x; }; Cell.prototype.getDist = function(x1, y1, x2, y2) { - var xs = x2 - x1; - xs = xs * xs; - - var ys = y2 - y1; - ys = ys * ys; - - return Math.sqrt(xs + ys); + var vx = x2 - x1; + var vy = y2 - x1; + return Math.sqrt(vx * vx + vy * vy); }; From 69f490d0a1f507e55aa1f3288a48b010b5696c7f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 17:32:12 +0200 Subject: [PATCH 054/417] fix leaderboard (highlight for the current player) --- src/GameServer.js | 6 +++--- src/PlayerTracker.js | 6 ++++-- src/gamemodes/FFA.js | 1 + src/gamemodes/Mode.js | 3 ++- src/gamemodes/TeamZ.js | 1 + src/gamemodes/Teams.js | 1 + src/gamemodes/Tournament.js | 1 + src/gamemodes/Zombie.js | 1 + src/modules/CommandList.js | 1 + src/packet/UpdateLeaderboard.js | 13 +++++++------ 10 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 3791b6f9b..a00fa7e5f 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -29,7 +29,7 @@ function GameServer() { this.currentFood = 0; this.movingNodes = []; // For move engine this.leaderboard = []; - this.lb_packet = null; // Leaderboard packet + this.leaderboardType = -1; // no type this.bots = new BotLoader(this); this.log = new Logger(); @@ -394,9 +394,9 @@ GameServer.prototype.updateLeaderboard = function () { // Update leaderboard with the gamemode's method if ((this.tickCounter % 25) == 0) { this.leaderboard = []; + this.leaderboardType = -1; this.gameMode.updateLB(this); - this.lb_packet = new Packet.UpdateLeaderboard(this.leaderboard, this.gameMode.packetLB); - + if (!this.gameMode.specByLeaderboard) { // Get client with largest score if gamemode doesn't have a leaderboard var clients = this.clients.valueOf(); diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index b71efdb94..c6f678316 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -246,8 +246,10 @@ PlayerTracker.prototype.update = function () { // Update leaderboard if (this.tickLeaderboard <= 0) { - if (this.gameServer.lb_packet != null) - this.socket.sendPacket(this.gameServer.lb_packet); + if (this.gameServer.leaderboardType >= 0) { + var packet = new Packet.UpdateLeaderboard(this, this.gameServer.leaderboard, this.gameServer.leaderboardType); + this.socket.sendPacket(packet); + } this.tickLeaderboard = 50; } else { this.tickLeaderboard--; diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index 5ab8c5963..d0d22f505 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -76,6 +76,7 @@ FFA.prototype.onPlayerSpawn = function(gameServer, player) { }; FFA.prototype.updateLB = function(gameServer) { + gameServer.leaderboardType = this.packetLB; var lb = gameServer.leaderboard; // Loop through all clients for (var i = 0; i < gameServer.clients.length; i++) { diff --git a/src/gamemodes/Mode.js b/src/gamemodes/Mode.js index c9b638587..fc0cf76f6 100644 --- a/src/gamemodes/Mode.js +++ b/src/gamemodes/Mode.js @@ -65,6 +65,7 @@ Mode.prototype.onCellMove = function(cell, gameServer) { // Called when a player cell is moved }; -Mode.prototype.updateLB = function(gameServer) { +Mode.prototype.updateLB = function (gameServer) { + gameServer.leaderboardType = this.packetLB; // Called when the leaderboard update function is called }; diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 110ce57ba..56ef74591 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -968,6 +968,7 @@ TeamZ.prototype.onCellMove = function(x1, y1, cell) { }; TeamZ.prototype.updateLB = function(gameServer) { + gameServer.leaderboardType = this.packetLB; var lb = gameServer.leaderboard; if (this.winTeam == 0) { diff --git a/src/gamemodes/Teams.js b/src/gamemodes/Teams.js index 655c184ca..f122e6d03 100644 --- a/src/gamemodes/Teams.js +++ b/src/gamemodes/Teams.js @@ -123,6 +123,7 @@ Teams.prototype.onCellMove = function(cell, gameServer) { }; Teams.prototype.updateLB = function(gameServer) { + gameServer.leaderboardType = this.packetLB; var total = 0; var teamMass = []; // Get mass diff --git a/src/gamemodes/Tournament.js b/src/gamemodes/Tournament.js index c04f7d06e..a8cf64dc3 100644 --- a/src/gamemodes/Tournament.js +++ b/src/gamemodes/Tournament.js @@ -176,6 +176,7 @@ Tournament.prototype.onCellRemove = function(cell) { }; Tournament.prototype.updateLB = function(gameServer) { + gameServer.leaderboardType = this.packetLB; var lb = gameServer.leaderboard; switch (this.gamePhase) { diff --git a/src/gamemodes/Zombie.js b/src/gamemodes/Zombie.js index 59d8f9ea3..5e64851db 100755 --- a/src/gamemodes/Zombie.js +++ b/src/gamemodes/Zombie.js @@ -145,6 +145,7 @@ Zombie.prototype.onCellMove = function(x1, y1, cell) { }; Zombie.prototype.updateLB = function(gameServer) { + gameServer.leaderboardType = this.packetLB; var lb = gameServer.leaderboard; // Loop through all clients for (var i = 0; i < gameServer.clients.length; i++) { diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 5f4a5269f..342917ddd 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -106,6 +106,7 @@ Commands.list = { gameServer.gameMode.specByLeaderboard = false; gameServer.gameMode.updateLB = function(gameServer) { gameServer.leaderboard = newLB; + gameServer.leaderboardType = 48; }; console.log("[Console] Successfully changed leaderboard values"); }, diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index 863f2300f..1423efb5e 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -2,23 +2,24 @@ var BinaryWriter = require("./BinaryWriter"); -function UpdateLeaderboard(leaderboard, packetLB) { +function UpdateLeaderboard(playerTracker, leaderboard, leaderboardType) { + this.playerTracker = playerTracker; this.leaderboard = leaderboard; - this.packetLB = packetLB; + this.leaderboardType = leaderboardType; } module.exports = UpdateLeaderboard; UpdateLeaderboard.prototype.build = function (protocol) { - switch (this.packetLB) { - case 48: return this.build48(protocol); // ? + switch (this.leaderboardType) { + case 48: return this.build48(protocol); // UserText case 49: return this.build49(protocol); // FFA case 50: return this.build50(protocol); // Team default: return null; } } -// Custom Text List +// UserText UpdateLeaderboard.prototype.build48 = function (protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x31); // Packet ID @@ -49,7 +50,7 @@ UpdateLeaderboard.prototype.build49 = function (protocol) { var item = this.leaderboard[i]; if (item == null) return null; // bad leaderboardm just don't send it - var isMe = false; // true for red color (current player), false for white color (other players) + var isMe = item == this.playerTracker; // true for red color (current player), false for white color (other players) var name = item.getName(); name = name != null ? name : ""; var id = isMe ? 1:0; From e6827f7623290060e4d2363ebd1593ea8c35e658 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 18:07:19 +0200 Subject: [PATCH 055/417] fix leaderboard highlight for protocol 4 and 5 --- src/packet/UpdateLeaderboard.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index 1423efb5e..a52fe63b5 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -32,7 +32,7 @@ UpdateLeaderboard.prototype.build48 = function (protocol) { name = name ? name : ""; var id = 0; - writer.writeUInt32(id >> 0); // isMe flag (previously cell ID) + writer.writeUInt32(id >> 0); // isMe flag/cell ID if (protocol <= 5) writer.writeStringZeroUnicode(name); else @@ -50,12 +50,14 @@ UpdateLeaderboard.prototype.build49 = function (protocol) { var item = this.leaderboard[i]; if (item == null) return null; // bad leaderboardm just don't send it - var isMe = item == this.playerTracker; // true for red color (current player), false for white color (other players) var name = item.getName(); name = name != null ? name : ""; - var id = isMe ? 1:0; + var id = item == this.playerTracker ? 1 : 0; // protocol 6+ uses isMe flag + if (protocol <= 5 && item.cells != null && item.cells.length > 0) { + id = item.cells[0].nodeId; // protocol 5- uses player cellId instead of isMe flag + } - writer.writeUInt32(id >> 0); // isMe flag (previously cell ID) + writer.writeUInt32(id >> 0); // isMe flag/cell ID if (protocol <= 5) writer.writeStringZeroUnicode(name); else From e7a285c585173482475f4457b37d08e82379ceab Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 19:37:44 +0200 Subject: [PATCH 056/417] code refactoring; increase leaderboard update rate to 1 second --- src/GameServer.js | 2 +- src/PlayerTracker.js | 9 +- src/gamemodes/FFA.js | 6 +- src/gamemodes/TeamZ.js | 2344 ++++++++++++++++++------------------ src/gamemodes/Zombie.js | 354 +++--- src/modules/CommandList.js | 2 +- 6 files changed, 1359 insertions(+), 1358 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index a00fa7e5f..9140973a1 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -403,7 +403,7 @@ GameServer.prototype.updateLeaderboard = function () { // Use sort function clients.sort(function (a, b) { - return b.playerTracker.getScore(true) - a.playerTracker.getScore(true); + return b.playerTracker.getScore() - a.playerTracker.getScore(); }); //this.largestClient = clients[0].playerTracker; this.largestClient = null; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index c6f678316..211b5e19b 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -92,10 +92,10 @@ PlayerTracker.prototype.getSkin = function () { return this.skin; }; -PlayerTracker.prototype.getScore = function(reCalcScore) { +PlayerTracker.prototype.getScore = function () { if (this.isMassChanged) this.updateMass(); - return this.score >> 0; + return this.score; }; PlayerTracker.prototype.getScale = function () { @@ -108,7 +108,7 @@ PlayerTracker.prototype.updateMass = function () { var totalSize = 0; for (var i = 0; i < this.cells.length; i++) { var node = this.cells[i]; - if (!node) continue; + if (node == null) continue; totalSize += node.getSize(); } if (totalSize == 0) { @@ -250,7 +250,8 @@ PlayerTracker.prototype.update = function () { var packet = new Packet.UpdateLeaderboard(this, this.gameServer.leaderboard, this.gameServer.leaderboardType); this.socket.sendPacket(packet); } - this.tickLeaderboard = 50; + // 1 / 0.040 = 25 (once per second) + this.tickLeaderboard = 25; } else { this.tickLeaderboard--; } diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index d0d22f505..8a6dc38d0 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -19,7 +19,7 @@ FFA.prototype.leaderboardAddSort = function(player, leaderboard) { var loop = true; while ((len >= 0) && (loop)) { // Start from the bottom of the leaderboard - if (player.getScore(false) <= leaderboard[len].getScore(false)) { + if (player.getScore() <= leaderboard[len].getScore()) { leaderboard.splice(len + 1, 0, player); loop = false; // End the loop if a spot is found } @@ -86,7 +86,7 @@ FFA.prototype.updateLB = function(gameServer) { var player = gameServer.clients[i].playerTracker; if (player.disconnect > -1) continue; // Don't add disconnected players to list - var playerScore = player.getScore(true); + var playerScore = player.getScore(); if (player.cells.length <= 0) { continue; } @@ -99,7 +99,7 @@ FFA.prototype.updateLB = function(gameServer) { this.leaderboardAddSort(player, lb); } else { // 10 in leaderboard already - if (playerScore > lb[gameServer.config.serverMaxLB - 1].getScore(false)) { + if (playerScore > lb[gameServer.config.serverMaxLB - 1].getScore()) { lb.pop(); this.leaderboardAddSort(player, lb); } diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 56ef74591..a25f21b38 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -1,1173 +1,1173 @@ -var Mode = require('./Mode.js'); -var Cell = require('../entity/Cell.js'); -var Entity = require('../entity'); -var Virus = require('../entity/Virus.js'); - -var GameServer = null; // represent GameServer Type -var GS_getRandomColor = null; // backup getRandomColor function of GameServer type -var GS_getNearestVirus = null; -var GS_getCellsInRange = null; -var GS_splitCells = null; -var GS_newCellVirused = null; -var Virus_onConsume = Virus.prototype.onConsume; - -var GameState = { - WF_PLAYERS: 0, - WF_START: 1, - IN_PROGRESS: 2 -}; - -// new Cell Type IDs of HERO and BRAIN are calculated based on Game Mode ID -var CellType = { - PLAYER: 0, - FOOD: 1, - VIRUS: 2, - EJECTED_MASS: 3, - HERO: 130, - BRAIN: 131 -}; - -var localLB = []; - -function TeamZ() { - Mode.apply(this, Array.prototype.slice.call(arguments)); - - this.ID = 13; - this.name = 'Zombie Team'; - this.packetLB = 48; - this.haveTeams = true; - - // configurations: - this.minPlayer = 2; // game is auto started if there are at least 2 players - this.gameDuration = 18000; // ticks, 1 tick = 50 ms (20 ticks = 1 s) - this.warmUpDuration = 600; // ticks, time to wait between games - this.crazyDuration = 200; // ticks - this.heroEffectDuration = 1000; // ticks - this.brainEffectDuration = 200; // ticks - this.spawnBrainInterval = 1200; // ticks - this.spawnHeroInterval = 600; // ticks - this.defaultColor = { - r: 0x9b, - g: 0x30, - b: 0xff - }; - - this.colorFactorStep = 5; - this.colorLower = 50; // Min 0 - this.colorUpper = 225; // Max 255 - this.maxBrain = -1; // set this param to any negative number to keep the number of brains not exceed number of humans - this.maxHero = 4; // set this param to any negative number to keep the number of heroes not exceed number of zombies - - // game mode data: - this.state = GameState.WF_PLAYERS; - this.winTeam = -1; - this.gameTimer = 0; - this.zombies = []; // the clients of zombie players - this.humans = []; // the clients of human players - - this.heroes = []; - this.brains = []; - - this.spawnHeroTimer = 0; - this.spawnBrainTimer = 0; -} - -module.exports = TeamZ; -TeamZ.prototype = new Mode(); - -// Gamemode Specific Functions - -TeamZ.prototype.createZColorFactor = function(client) { - client.zColorFactor = (Math.random() * (this.colorUpper - this.colorLower + 1)) >> 0 + this.colorLower; - client.zColorIncr = true; // color will be increased if TRUE - otherwise it will be decreased. -}; - -TeamZ.prototype.nextZColorFactor = function(client) { - if (client.zColorIncr == true) { - if (client.zColorFactor + this.colorFactorStep >= this.colorUpper) { - client.zColorFactor = this.colorUpper; - client.zColorIncr = false; - } else { - client.zColorFactor += this.colorFactorStep; - } - } else { - if (client.zColorFactor - this.colorFactorStep <= this.colorLower) { - client.zColorFactor = this.colorLower; - client.zColorIncr = true; - } else { - client.zColorFactor -= this.colorFactorStep; - } - } -}; - -TeamZ.prototype.updateZColor = function(client, mask) { - var color = { - r: (mask & 0x4) > 0 ? client.zColorFactor : 7, - g: (mask & 0x2) > 0 ? client.zColorFactor : 7, - b: (mask & 0x1) > 0 ? client.zColorFactor : 7 - }; - client.color = { - r: color.r, - g: color.g, - b: color.b - }; - for (var i = 0; i < client.cells.length; i++) { - var cell = client.cells[i]; - cell.setColor(color); - } -}; - -TeamZ.prototype.isCrazy = function(client) { - return (typeof(client.crazyTimer) != 'undefined' && client.crazyTimer > 0 && client.team > 0); -}; - -TeamZ.prototype.hasEatenHero = function(client) { - return (typeof(client.eatenHeroTimer) != 'undefined' && client.eatenHeroTimer > 0); -}; - -TeamZ.prototype.hasEatenBrain = function(client) { - return (typeof(client.eatenBrainTimer) != 'undefined' && client.eatenBrainTimer > 0); -}; - -TeamZ.prototype.spawnDrug = function(gameServer, cell) { // spawn HERO or BRAIN - var max = 0; - var proceedNext = false; - if (cell.getType() == CellType.HERO) { - max = this.maxHero < 0 ? this.zombies.length : this.maxHero; - proceedNext = this.heroes.length < max; - } else if (cell.getType() == CellType.BRAIN) { - max = this.maxBrain < 0 ? this.humans.length : this.maxBrain; - proceedNext = this.brains.length < max; - } - if (proceedNext) { - var pos = gameServer.getRandomPosition(); - - // Check for players - var collided = false; - for (var i = 0; i < gameServer.nodesPlayer.length; i++) { - var check = gameServer.nodesPlayer[i]; - var r = check.getSize(); // Radius of checking player cell - - // Collision box - var topY = check.position.y - r; - var bottomY = check.position.y + r; - var leftX = check.position.x - r; - var rightX = check.position.x + r; - - // Check for collisions - if (pos.y > bottomY) { - continue; - } - if (pos.y < topY) { - continue; - } - if (pos.x > rightX) { - continue; - } - if (pos.x < leftX) { - continue; - } - - // Collided - collided = true; - break; - } - - // Spawn if no cells are colliding - if (!collided) { - cell.position = pos; - gameServer.addNode(cell); - return true; // SUCCESS with spawn - } - return false; // FAILED because of collision - } - return true; // SUCCESS without spawn -}; - -// Call to change a human client to a zombie -TeamZ.prototype.turnToZombie = function(client) { - client.team = 0; // team Z - this.createZColorFactor(client); - this.updateZColor(client, 0x7); // Gray - - // remove from human list - var index = this.humans.indexOf(client); - if (index >= 0) { - this.humans.splice(index, 1); - } - - // add to zombie list - this.zombies.push(client); -}; - -TeamZ.prototype.boostSpeedCell = function(cell) { - if (typeof cell.originalSpeed == 'undefined' || cell.originalSpeed == null) { - cell.originalSpeed = cell.getSpeed; - cell.getSpeed = function() { - return 2 * this.originalSpeed(); - }; - } -}; - -TeamZ.prototype.boostSpeed = function(client) { - for (var i = 0; i < client.cells.length; i++) { - var cell = client.cells[i]; - if (typeof cell == 'undefined') - continue; - this.boostSpeedCell(cell); - } -}; - -TeamZ.prototype.resetSpeedCell = function(cell) { - if (typeof cell.originalSpeed != 'undefined' && cell.originalSpeed != null) { - cell.getSpeed = cell.originalSpeed; - cell.originalSpeed = null; - } -}; - -TeamZ.prototype.resetSpeed = function(client) { - for (var i = 0; i < client.cells.length; i++) { - var cell = client.cells[i]; - if (typeof cell == 'undefined') - continue; - this.resetSpeedCell(cell); - } -}; - -TeamZ.prototype.startGame = function(gameServer) { - for (var i = 0; i < this.humans.length; i++) { - var client = this.humans[i]; - client.team = client.pID; - client.crazyTimer = 0; - client.eatenHeroTimer = 0; - client.eatenBrainTimer = 0; - client.color = gameServer.getRandomColor(); - for (var j = 0; j < client.cells.length; j++) { - var cell = client.cells[j]; - if (cell) { - cell.setColor(client.color); - cell.setMass(gameServer.config.playerStartMass); - this.resetSpeedCell(cell); - } - } - } - - // Select random human to be the zombie - var zombie = this.humans[(Math.random() * this.humans.length) >> 0]; - this.turnToZombie(zombie); - - this.winTeam = -1; - this.state = GameState.IN_PROGRESS; - this.gameTimer = this.gameDuration; -}; - -TeamZ.prototype.endGame = function(gameServer) { - // reset game - for (var i = 0; i < this.zombies.length; i++) { - var client = this.zombies[i]; - var index = this.humans.indexOf(client); - if (index < 0) { - this.humans.push(client); - } - } - this.zombies = []; - this.spawnHeroTimer = 0; - this.spawnBrainTimer = 0; - localLB = []; // reset leader board - - for (var i = 0; i < this.humans.length; i++) { - var client = this.humans[i]; - client.color = this.defaultColor; - client.team = 1; - for (var j = 0; j < client.cells.length; j++) { - var cell = client.cells[j]; - cell.setColor(this.defaultColor); - } - } - - this.state = GameState.WF_PLAYERS; - this.gameTimer = 0; -}; - -TeamZ.prototype.leaderboardAddSort = function(player, leaderboard) { - // Adds the player and sorts the leaderboard - var len = leaderboard.length - 1; - var loop = true; - - while ((len >= 0) && (loop)) { - // Start from the bottom of the leaderboard - if (player.getScore(false) <= leaderboard[len].getScore(false)) { - leaderboard.splice(len + 1, 0, player); - loop = false; // End the loop if a spot is found - } - len--; - } - if (loop) { - // Add to top of the list because no spots were found - leaderboard.splice(0, 0, player); - } -}; - -// Override - -TeamZ.prototype.onServerInit = function(gameServer) { - // Called when the server starts - gameServer.run = true; - - // Overwrite some server functions: - GameServer = require('../GameServer.js'); - GS_getRandomColor = GameServer.prototype.getRandomColor; // backup - GS_getNearestVirus = GameServer.prototype.getNearestVirus; - GS_getCellsInRange = GameServer.prototype.getCellsInRange; - GS_splitCells = GameServer.prototype.splitCells; - GS_newCellVirused = GameServer.prototype.newCellVirused; - - //OVERWRITE GLOBAL FUNCTIONs to adapt Zombie Team mode - - GameServer.prototype.getRandomColor = function() { - var colorRGB = [0xFF, 0x07, (Math.random() * 256) >> 0]; - colorRGB.sort(function() { - return 0.5 - Math.random(); - }); - return { - r: colorRGB[0], - b: colorRGB[1], - g: colorRGB[2] - }; - }; - - GameServer.prototype.getNearestVirus = function(cell) { - // More like getNearbyVirus - var virus = null; - var r = 100; // Checking radius - - var topY = cell.position.y - r; - var bottomY = cell.position.y + r; - - var leftX = cell.position.x - r; - var rightX = cell.position.x + r; - - // loop through all heroes - for (var i = 0; i < this.gameMode.heroes.length; i++) { - var check = this.gameMode.heroes[i]; - if (typeof check === 'undefined') { - continue; - } - if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { - continue; - } - virus = check; - break; - } - if (virus != null) - return virus; - - // loop through all brains - for (var i = 0; i < this.gameMode.brains.length; i++) { - var check = this.gameMode.brains[i]; - if (typeof check === 'undefined') { - continue; - } - if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { - continue; - } - virus = check; - break; - } - - if (virus != null) - return virus; - - // Call base: - // Loop through all viruses on the map. There is probably a more efficient way of doing this but whatever - var len = this.nodesVirus.length; - for (var i = 0; i < len; i++) { - var check = this.nodesVirus[i]; - - if (typeof check === 'undefined') { - continue; - } - - if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { - continue; - } - - // Add to list of cells nearby - virus = check; - break; // stop checking when a virus found - } - return virus; - }; - - // this is almost same to the legacy function - GameServer.prototype.getCellsInRange = function(cell) { - var list = new Array(); - - if (this.gameMode.state != GameState.IN_PROGRESS) - return list; - - var squareR = cell.getSquareSize(); // Get cell squared radius - - // Loop through all cells that are visible to the cell. There is probably a more efficient way of doing this but whatever - var len = cell.owner.visibleNodes.length; - for (var i = 0; i < len; i++) { - var check = cell.owner.visibleNodes[i]; - - if (typeof check === 'undefined') { - continue; - } - - // if something already collided with this cell, don't check for other collisions - if (check.inRange) { - continue; - } - - // HERO and BRAIN checking - if (cell.owner.getTeam() == 0) { - // Z team - if (check.getType() == CellType.HERO) - continue; - } else { - // H team - if (check.getType() == CellType.BRAIN) - continue; - } - - // Can't eat itself - if (cell.nodeId == check.nodeId) { - continue; - } - - // Can't eat cells that have collision turned off - if ((cell.owner == check.owner) && (cell.ignoreCollision)) { - continue; - } - - // AABB Collision - if (!check.collisionCheck2(squareR, cell.position)) { - continue; - } - - // Cell type check - Cell must be bigger than this number times the mass of the cell being eaten - var multiplier = 1.25; - - switch (check.getType()) { - case 1: // Food cell - list.push(check); - check.inRange = true; // skip future collision checks for this food - continue; - case 2: // Virus - multiplier = 1.33; - break; - case 0: // Players - // Can't eat self if it's not time to recombine yet - if (check.owner == cell.owner) { - if (!cell.canRemrege() || !check.canRemerge()) { - continue; - } - - multiplier = 1.00; - } - - // Can't eat team members - if (this.gameMode.haveTeams) { - if (!check.owner) { // Error check - continue; - } - - if ((check.owner != cell.owner) && (check.owner.getTeam() == cell.owner.getTeam())) { - continue; - } - } - break; - default: - break; - } - - // Make sure the cell is big enough to be eaten. - if ((check.getMass() * multiplier) > cell.getMass()) { - continue; - } - - // Eating range - var xs = Math.pow(check.position.x - cell.position.x, 2); - var ys = Math.pow(check.position.y - cell.position.y, 2); - var dist = Math.sqrt(xs + ys); - - var eatingRange = cell.getSize() - check.getEatingRange(); // Eating range = radius of eating cell + 40% of the radius of the cell being eaten - if (dist > eatingRange) { - // Not in eating range - continue; - } - - // Add to list of cells nearby - list.push(check); - - // Something is about to eat this cell; no need to check for other collisions with it - check.inRange = true; - } - return list; - }; - - // this is almost same to the legacy function - GameServer.prototype.splitCells = function(client) { - var len = client.cells.length; - for (var i = 0; i < len; i++) { - if (client.cells.length >= this.config.playerMaxCells) { - // Player cell limit - continue; - } - - var cell = client.cells[i]; - if (!cell) { - continue; - } - - if (cell.getMass() < this.config.playerMinMassSplit) { - continue; - } - - // Get angle - var deltaY = client.mouse.y - cell.position.y; - var deltaX = client.mouse.x - cell.position.x; - var angle = Math.atan2(deltaX, deltaY); - - // Get starting position - var size = cell.getSize() / 2; - var startPos = { - x: cell.position.x + (size * Math.sin(angle)), - y: cell.position.y + (size * Math.cos(angle)) - }; - // Calculate mass and speed of splitting cell - var splitSpeed = cell.getSpeed() * 6; - var newMass = cell.getMass() / 2; - cell.setMass(newMass); - // Create cell - var split = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, newMass); - split.setAngle(angle); - split.setMoveEngineData(splitSpeed, 32, 0.85); - - // boost speed if zombie eats brain - if (this.gameMode.hasEatenBrain(client) || this.gameMode.isCrazy(client)) { - this.gameMode.boostSpeedCell(split); - } - // gain effect if human eat hero - else if (this.gameMode.hasEatenHero(client)) { - // fix "unable to split" bug: cell can be merged after finish moving (2nd param in setMoveEngineData) - //split.recombineTicks = 2; // main-ticks, 1 main-tick = 1 s - //TODO: fix? - } - - // Add to moving cells list - this.setAsMovingNode(split); - this.addNode(split); - } - }; - - // this function is almost same to the legacy - GameServer.prototype.newCellVirused = function(client, parent, angle, mass, speed) { - // Starting position - var startPos = { - x: parent.position.x, - y: parent.position.y - }; - - // Create cell - newCell = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, mass); - newCell.setAngle(angle); - newCell.setMoveEngineData(speed, 10); - newCell.ignoreCollision = true; // Turn off collision - - // boost speed if zombie eats brain - if (this.gameMode.hasEatenBrain(client) || this.gameMode.isCrazy(client)) { - this.gameMode.boostSpeedCell(newCell); - } - // gain effect if human eat hero - else if (this.gameMode.hasEatenHero(client)) { - // fix "unable to split" bug - //newCell.recombineTicks = 1; - // TODO: fix? - } - - // Add to moving cells list - this.addNode(newCell); - this.setAsMovingNode(newCell); - }; - - Virus.prototype.onConsume = function(consumer, gameServer) { - var client = consumer.owner; - - var maxSplits = Math.floor(consumer.getMass() / 16) - 1; // Maximum amount of splits - var numSplits = gameServer.config.playerMaxCells - client.cells.length; // Get number of splits - numSplits = Math.min(numSplits, maxSplits); - var splitMass = Math.min(consumer.getMass() / (numSplits + 1), 36); // Maximum size of new splits - - // Cell consumes mass before splitting - consumer.addMass(this.getMass()); - - // Cell cannot split any further - if (numSplits <= 0) { - return; - } - - // Big cells will split into cells larger than 36 mass (1/4 of their mass) - var bigSplits = 0; - var endMass = consumer.getMass() - (numSplits * splitMass); - if ((endMass > 300) && (numSplits > 0)) { - bigSplits++; - numSplits--; - } - if ((endMass > 1200) && (numSplits > 0)) { - bigSplits++; - numSplits--; - } - if ((endMass > 3000) && (numSplits > 0)) { - bigSplits++; - numSplits--; - } - - // Splitting - var angle = 0; // Starting angle - for (var k = 0; k < numSplits; k++) { - angle += 6 / numSplits; // Get directions of splitting cells - gameServer.newCellVirused(client, consumer, angle, splitMass, 150); - consumer.setMass(consumer.getMass() - splitMass); - } - - for (var k = 0; k < bigSplits; k++) { - angle = Math.random() * 6.28; // Random directions - splitMass = consumer.getMass() / 4; - gameServer.newCellVirused(client, consumer, angle, splitMass, 20); - consumer.setMass(consumer.getMass() - splitMass); - } - - if (gameServer.gameMode.hasEatenHero(client)) { - //consumer.recombineTicks = 0; - // TODO: fix? - } - }; - - // Handle "gamemode" command: - for (var i = 0; i < gameServer.clients.length; i++) { - var client = gameServer.clients[i].playerTracker; - if (!client) - continue; - - if (client.cells.length > 0) { - client.eatenBrainTimer = 0; - client.eatenHeroTimer = 0; - client.crazyTimer = 0; - client.color = this.defaultColor; - client.team = 1; - for (var j = 0; j < client.cells.length; j++) { - var cell = client.cells[j]; - cell.setColor(this.defaultColor); - } - this.humans.push(client); - } - } -}; - -TeamZ.prototype.onChange = function(gameServer) { - // Called when someone changes the gamemode via console commands - // remove Brain and Hero - for (var i = 0; this.brains.length; i++) { - var node = this.brains[i]; - gameServer.removeNode(node); - } - for (var i = 0; this.heroes.length; i++) { - var node = this.heroes[i]; - gameServer.removeNode(node); - } - - // discard all boost: - for (var i = 0; i < this.humans.length; i++) { - var client = this.humans[i]; - if (this.isCrazy(client)) { - this.resetSpeed(client); - } - } - for (var i = 0; i < this.zombies.length; i++) { - var client = this.zombies[i]; - if (this.hasEatenBrain(client)) { - this.resetSpeed(client); - } - } - - // revert to default: - GameServer.prototype.getRandomColor = GS_getRandomColor; - GameServer.prototype.getNearestVirus = GS_getNearestVirus; - GameServer.prototype.getCellsInRange = GS_getCellsInRange; - GameServer.prototype.splitCells = GS_splitCells; - GameServer.prototype.newCellVirused = GS_newCellVirused; - Virus.prototype.onConsume = Virus_onConsume; -}; - -TeamZ.prototype.onTick = function(gameServer) { - // Called on every game tick - - switch (this.state) { - case GameState.WF_PLAYERS: - if (this.humans.length >= this.minPlayer) { - this.state = GameState.WF_START; - this.gameTimer = this.warmUpDuration; - } - break; - case GameState.WF_START: - this.gameTimer--; - if (this.gameTimer == 0) { - if (this.humans.length >= this.minPlayer) { - // proceed: - this.startGame(gameServer); - } else { - // back to previous state: - this.state = GameState.WF_PLAYERS; - } - } - break; - case GameState.IN_PROGRESS: - this.gameTimer--; - if (this.gameTimer == 0) { - // human wins - this.winTeam = 1; - } else { - if (this.humans.length == 0) { // no human left - // zombie wins - this.winTeam = 0; - } else if (this.zombies.length == 0) { // no zombie left - // human wins - this.winTeam = 1; - } - } - - if (this.winTeam >= 0) { - this.endGame(gameServer); - } - - break; - } - - // change color of zombies - for (var i = 0; i < this.zombies.length; i++) { - var client = this.zombies[i]; - this.nextZColorFactor(client); - - if (this.hasEatenBrain(client)) { - client.eatenBrainTimer--; - - if (client.eatenBrainTimer > 0) { - this.updateZColor(client, 0x5); // Pink - continue; - } else { - // reset speed: - this.resetSpeed(client); - } - } - - this.updateZColor(client, 0x7); // Gray - } - - for (var i = 0; i < this.humans.length; i++) { - var client = this.humans[i]; - if (this.isCrazy(client)) { - client.crazyTimer--; - if (client.crazyTimer == 0) { - for (var j = 0; j < client.cells.length; j++) { - var cell = client.cells[j]; - // reset speed: - this.resetSpeedCell(cell); - - // reset color: - if (client.cured == true) - cell.setColor(client.color); - } - - if (client.cured == true) { - client.cured = false; // reset - } else { - // turn player to zombie - this.turnToZombie(client); - continue; - } - } else { - client.colorToggle++; - if (client.colorToggle % 10 == 0) { - var blinkColor = null; - - if (client.colorToggle == 20) { - blinkColor = client.color; - client.colorToggle = 0; - } else { - if (client.cured == true) { - blinkColor = { - r: 255, - g: 255, - b: 7 - }; // Yellow - } else { - blinkColor = { - r: 75, - g: 75, - b: 75 - }; // Gray - } - } - - for (var j = 0; j < client.cells.length; j++) { - var cell = client.cells[j]; - cell.setColor(blinkColor); - } - } - } - } else if (this.hasEatenHero(client)) { - client.eatenHeroTimer--; - var color = null; - if (client.eatenHeroTimer > 0) { - client.heroColorFactor = (client.heroColorFactor + 5) % 401; - if (client.heroColorFactor <= 200) { - color = { - r: 255, - g: 255, - b: client.heroColorFactor - }; // Yellow scheme - } else { - color = { - r: 255, - g: 255, - b: 400 - client.heroColorFactor - }; // Yellow scheme - } - } else { - color = client.color; // reset - } - - for (var j = 0; j < client.cells.length; j++) { - var cell = client.cells[j]; - cell.setColor(color); - } - } - } - - // check timer to spawn Hero: - this.spawnHeroTimer++; - if (this.spawnHeroTimer >= this.spawnHeroInterval) { - this.spawnHeroTimer = 0; - var cell = new Hero(gameServer.getNextNodeId(), null); - while (!this.spawnDrug(gameServer, cell)); // collision detect algorithm needs enhancement - } - - // check timer to spawn Brain: - this.spawnBrainTimer++; - if (this.spawnBrainTimer >= this.spawnBrainInterval) { - this.spawnBrainTimer = 0; - var cell = new Brain(gameServer.getNextNodeId(), null); - while (!this.spawnDrug(gameServer, cell)); // collision detect algorithm needs enhancement - } -}; - -TeamZ.prototype.onCellAdd = function(cell) { - // Called when a player cell is added - var client = cell.owner; - if (client.cells.length == 1) { // first cell - client.team = client.pID; - client.color = { - r: cell.color.r, - g: cell.color.g, - b: cell.color.b - }; - client.eatenBrainTimer = 0; - client.eatenHeroTimer = 0; - client.crazyTimer = 0; - this.humans.push(client); - - if (this.state == GameState.IN_PROGRESS) { - this.turnToZombie(client); - } else { - client.color = this.defaultColor; - cell.setColor(this.defaultColor); - client.team = 1; // game not started yet - } - } -}; - -TeamZ.prototype.onCellRemove = function(cell) { - // Called when a player cell is removed - var client = cell.owner; - if (client.cells.length == 0) { // last cell - if (client.getTeam() == 0) { - // Z team - var index = this.zombies.indexOf(client); - if (index >= 0) - this.zombies.splice(index, 1); - } else { - // H team - var index = this.humans.indexOf(client); - if (index >= 0) - this.humans.splice(index, 1); - } - } -}; - -TeamZ.prototype.onCellMove = function(x1, y1, cell) { - // Called when a player cell is moved - var team = cell.owner.getTeam(); - var r = cell.getSize(); - - // Find team - for (var i = 0; i < cell.owner.visibleNodes.length; i++) { - // Only collide with player cells - var check = cell.owner.visibleNodes[i]; - - if ((check.getType() != 0) || (cell.owner == check.owner)) { - continue; - } - - if ((this.hasEatenHero(check.owner)) || (this.hasEatenHero(cell.owner))) { - continue; - } - - // Collision with zombies - if (check.owner.getTeam() == 0 || team == 0) { - // Check if in collision range - var collisionDist = check.getSize() + r; // Minimum distance between the 2 cells - if (!cell.simpleCollide(x1, y1, check, collisionDist)) { - // Skip - continue; - } - - // First collision check passed... now more precise checking - dist = cell.getDist(cell.position.x, cell.position.y, check.position.x, check.position.y); - - // Calculations - if (dist < collisionDist) { // Collided - var crazyClient = null; - if (check.owner.getTeam() == 0 && team != 0) { - crazyClient = cell.owner; - } else if (team == 0 && check.owner.getTeam() != 0) { - crazyClient = check.owner; - } - - if (crazyClient != null && !this.isCrazy(crazyClient)) { - crazyClient.crazyTimer = this.crazyDuration; - crazyClient.colorToggle = 0; - this.boostSpeed(crazyClient); - } - - // The moving cell pushes the colliding cell - var newDeltaY = check.position.y - y1; - var newDeltaX = check.position.x - x1; - var newAngle = Math.atan2(newDeltaX, newDeltaY); - - var move = collisionDist - dist; - - check.position.x = check.position.x + (move * Math.sin(newAngle)) >> 0; - check.position.y = check.position.y + (move * Math.cos(newAngle)) >> 0; - } - } - } -}; - -TeamZ.prototype.updateLB = function(gameServer) { +var Mode = require('./Mode.js'); +var Cell = require('../entity/Cell.js'); +var Entity = require('../entity'); +var Virus = require('../entity/Virus.js'); + +var GameServer = null; // represent GameServer Type +var GS_getRandomColor = null; // backup getRandomColor function of GameServer type +var GS_getNearestVirus = null; +var GS_getCellsInRange = null; +var GS_splitCells = null; +var GS_newCellVirused = null; +var Virus_onConsume = Virus.prototype.onConsume; + +var GameState = { + WF_PLAYERS: 0, + WF_START: 1, + IN_PROGRESS: 2 +}; + +// new Cell Type IDs of HERO and BRAIN are calculated based on Game Mode ID +var CellType = { + PLAYER: 0, + FOOD: 1, + VIRUS: 2, + EJECTED_MASS: 3, + HERO: 130, + BRAIN: 131 +}; + +var localLB = []; + +function TeamZ() { + Mode.apply(this, Array.prototype.slice.call(arguments)); + + this.ID = 13; + this.name = 'Zombie Team'; + this.packetLB = 48; + this.haveTeams = true; + + // configurations: + this.minPlayer = 2; // game is auto started if there are at least 2 players + this.gameDuration = 18000; // ticks, 1 tick = 50 ms (20 ticks = 1 s) + this.warmUpDuration = 600; // ticks, time to wait between games + this.crazyDuration = 200; // ticks + this.heroEffectDuration = 1000; // ticks + this.brainEffectDuration = 200; // ticks + this.spawnBrainInterval = 1200; // ticks + this.spawnHeroInterval = 600; // ticks + this.defaultColor = { + r: 0x9b, + g: 0x30, + b: 0xff + }; + + this.colorFactorStep = 5; + this.colorLower = 50; // Min 0 + this.colorUpper = 225; // Max 255 + this.maxBrain = -1; // set this param to any negative number to keep the number of brains not exceed number of humans + this.maxHero = 4; // set this param to any negative number to keep the number of heroes not exceed number of zombies + + // game mode data: + this.state = GameState.WF_PLAYERS; + this.winTeam = -1; + this.gameTimer = 0; + this.zombies = []; // the clients of zombie players + this.humans = []; // the clients of human players + + this.heroes = []; + this.brains = []; + + this.spawnHeroTimer = 0; + this.spawnBrainTimer = 0; +} + +module.exports = TeamZ; +TeamZ.prototype = new Mode(); + +// Gamemode Specific Functions + +TeamZ.prototype.createZColorFactor = function(client) { + client.zColorFactor = (Math.random() * (this.colorUpper - this.colorLower + 1)) >> 0 + this.colorLower; + client.zColorIncr = true; // color will be increased if TRUE - otherwise it will be decreased. +}; + +TeamZ.prototype.nextZColorFactor = function(client) { + if (client.zColorIncr == true) { + if (client.zColorFactor + this.colorFactorStep >= this.colorUpper) { + client.zColorFactor = this.colorUpper; + client.zColorIncr = false; + } else { + client.zColorFactor += this.colorFactorStep; + } + } else { + if (client.zColorFactor - this.colorFactorStep <= this.colorLower) { + client.zColorFactor = this.colorLower; + client.zColorIncr = true; + } else { + client.zColorFactor -= this.colorFactorStep; + } + } +}; + +TeamZ.prototype.updateZColor = function(client, mask) { + var color = { + r: (mask & 0x4) > 0 ? client.zColorFactor : 7, + g: (mask & 0x2) > 0 ? client.zColorFactor : 7, + b: (mask & 0x1) > 0 ? client.zColorFactor : 7 + }; + client.color = { + r: color.r, + g: color.g, + b: color.b + }; + for (var i = 0; i < client.cells.length; i++) { + var cell = client.cells[i]; + cell.setColor(color); + } +}; + +TeamZ.prototype.isCrazy = function(client) { + return (typeof(client.crazyTimer) != 'undefined' && client.crazyTimer > 0 && client.team > 0); +}; + +TeamZ.prototype.hasEatenHero = function(client) { + return (typeof(client.eatenHeroTimer) != 'undefined' && client.eatenHeroTimer > 0); +}; + +TeamZ.prototype.hasEatenBrain = function(client) { + return (typeof(client.eatenBrainTimer) != 'undefined' && client.eatenBrainTimer > 0); +}; + +TeamZ.prototype.spawnDrug = function(gameServer, cell) { // spawn HERO or BRAIN + var max = 0; + var proceedNext = false; + if (cell.getType() == CellType.HERO) { + max = this.maxHero < 0 ? this.zombies.length : this.maxHero; + proceedNext = this.heroes.length < max; + } else if (cell.getType() == CellType.BRAIN) { + max = this.maxBrain < 0 ? this.humans.length : this.maxBrain; + proceedNext = this.brains.length < max; + } + if (proceedNext) { + var pos = gameServer.getRandomPosition(); + + // Check for players + var collided = false; + for (var i = 0; i < gameServer.nodesPlayer.length; i++) { + var check = gameServer.nodesPlayer[i]; + var r = check.getSize(); // Radius of checking player cell + + // Collision box + var topY = check.position.y - r; + var bottomY = check.position.y + r; + var leftX = check.position.x - r; + var rightX = check.position.x + r; + + // Check for collisions + if (pos.y > bottomY) { + continue; + } + if (pos.y < topY) { + continue; + } + if (pos.x > rightX) { + continue; + } + if (pos.x < leftX) { + continue; + } + + // Collided + collided = true; + break; + } + + // Spawn if no cells are colliding + if (!collided) { + cell.position = pos; + gameServer.addNode(cell); + return true; // SUCCESS with spawn + } + return false; // FAILED because of collision + } + return true; // SUCCESS without spawn +}; + +// Call to change a human client to a zombie +TeamZ.prototype.turnToZombie = function(client) { + client.team = 0; // team Z + this.createZColorFactor(client); + this.updateZColor(client, 0x7); // Gray + + // remove from human list + var index = this.humans.indexOf(client); + if (index >= 0) { + this.humans.splice(index, 1); + } + + // add to zombie list + this.zombies.push(client); +}; + +TeamZ.prototype.boostSpeedCell = function(cell) { + if (typeof cell.originalSpeed == 'undefined' || cell.originalSpeed == null) { + cell.originalSpeed = cell.getSpeed; + cell.getSpeed = function() { + return 2 * this.originalSpeed(); + }; + } +}; + +TeamZ.prototype.boostSpeed = function(client) { + for (var i = 0; i < client.cells.length; i++) { + var cell = client.cells[i]; + if (typeof cell == 'undefined') + continue; + this.boostSpeedCell(cell); + } +}; + +TeamZ.prototype.resetSpeedCell = function(cell) { + if (typeof cell.originalSpeed != 'undefined' && cell.originalSpeed != null) { + cell.getSpeed = cell.originalSpeed; + cell.originalSpeed = null; + } +}; + +TeamZ.prototype.resetSpeed = function(client) { + for (var i = 0; i < client.cells.length; i++) { + var cell = client.cells[i]; + if (typeof cell == 'undefined') + continue; + this.resetSpeedCell(cell); + } +}; + +TeamZ.prototype.startGame = function(gameServer) { + for (var i = 0; i < this.humans.length; i++) { + var client = this.humans[i]; + client.team = client.pID; + client.crazyTimer = 0; + client.eatenHeroTimer = 0; + client.eatenBrainTimer = 0; + client.color = gameServer.getRandomColor(); + for (var j = 0; j < client.cells.length; j++) { + var cell = client.cells[j]; + if (cell) { + cell.setColor(client.color); + cell.setMass(gameServer.config.playerStartMass); + this.resetSpeedCell(cell); + } + } + } + + // Select random human to be the zombie + var zombie = this.humans[(Math.random() * this.humans.length) >> 0]; + this.turnToZombie(zombie); + + this.winTeam = -1; + this.state = GameState.IN_PROGRESS; + this.gameTimer = this.gameDuration; +}; + +TeamZ.prototype.endGame = function(gameServer) { + // reset game + for (var i = 0; i < this.zombies.length; i++) { + var client = this.zombies[i]; + var index = this.humans.indexOf(client); + if (index < 0) { + this.humans.push(client); + } + } + this.zombies = []; + this.spawnHeroTimer = 0; + this.spawnBrainTimer = 0; + localLB = []; // reset leader board + + for (var i = 0; i < this.humans.length; i++) { + var client = this.humans[i]; + client.color = this.defaultColor; + client.team = 1; + for (var j = 0; j < client.cells.length; j++) { + var cell = client.cells[j]; + cell.setColor(this.defaultColor); + } + } + + this.state = GameState.WF_PLAYERS; + this.gameTimer = 0; +}; + +TeamZ.prototype.leaderboardAddSort = function(player, leaderboard) { + // Adds the player and sorts the leaderboard + var len = leaderboard.length - 1; + var loop = true; + + while ((len >= 0) && (loop)) { + // Start from the bottom of the leaderboard + if (player.getScore() <= leaderboard[len].getScore()) { + leaderboard.splice(len + 1, 0, player); + loop = false; // End the loop if a spot is found + } + len--; + } + if (loop) { + // Add to top of the list because no spots were found + leaderboard.splice(0, 0, player); + } +}; + +// Override + +TeamZ.prototype.onServerInit = function(gameServer) { + // Called when the server starts + gameServer.run = true; + + // Overwrite some server functions: + GameServer = require('../GameServer.js'); + GS_getRandomColor = GameServer.prototype.getRandomColor; // backup + GS_getNearestVirus = GameServer.prototype.getNearestVirus; + GS_getCellsInRange = GameServer.prototype.getCellsInRange; + GS_splitCells = GameServer.prototype.splitCells; + GS_newCellVirused = GameServer.prototype.newCellVirused; + + //OVERWRITE GLOBAL FUNCTIONs to adapt Zombie Team mode + + GameServer.prototype.getRandomColor = function() { + var colorRGB = [0xFF, 0x07, (Math.random() * 256) >> 0]; + colorRGB.sort(function() { + return 0.5 - Math.random(); + }); + return { + r: colorRGB[0], + b: colorRGB[1], + g: colorRGB[2] + }; + }; + + GameServer.prototype.getNearestVirus = function(cell) { + // More like getNearbyVirus + var virus = null; + var r = 100; // Checking radius + + var topY = cell.position.y - r; + var bottomY = cell.position.y + r; + + var leftX = cell.position.x - r; + var rightX = cell.position.x + r; + + // loop through all heroes + for (var i = 0; i < this.gameMode.heroes.length; i++) { + var check = this.gameMode.heroes[i]; + if (typeof check === 'undefined') { + continue; + } + if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { + continue; + } + virus = check; + break; + } + if (virus != null) + return virus; + + // loop through all brains + for (var i = 0; i < this.gameMode.brains.length; i++) { + var check = this.gameMode.brains[i]; + if (typeof check === 'undefined') { + continue; + } + if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { + continue; + } + virus = check; + break; + } + + if (virus != null) + return virus; + + // Call base: + // Loop through all viruses on the map. There is probably a more efficient way of doing this but whatever + var len = this.nodesVirus.length; + for (var i = 0; i < len; i++) { + var check = this.nodesVirus[i]; + + if (typeof check === 'undefined') { + continue; + } + + if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { + continue; + } + + // Add to list of cells nearby + virus = check; + break; // stop checking when a virus found + } + return virus; + }; + + // this is almost same to the legacy function + GameServer.prototype.getCellsInRange = function(cell) { + var list = new Array(); + + if (this.gameMode.state != GameState.IN_PROGRESS) + return list; + + var squareR = cell.getSquareSize(); // Get cell squared radius + + // Loop through all cells that are visible to the cell. There is probably a more efficient way of doing this but whatever + var len = cell.owner.visibleNodes.length; + for (var i = 0; i < len; i++) { + var check = cell.owner.visibleNodes[i]; + + if (typeof check === 'undefined') { + continue; + } + + // if something already collided with this cell, don't check for other collisions + if (check.inRange) { + continue; + } + + // HERO and BRAIN checking + if (cell.owner.getTeam() == 0) { + // Z team + if (check.getType() == CellType.HERO) + continue; + } else { + // H team + if (check.getType() == CellType.BRAIN) + continue; + } + + // Can't eat itself + if (cell.nodeId == check.nodeId) { + continue; + } + + // Can't eat cells that have collision turned off + if ((cell.owner == check.owner) && (cell.ignoreCollision)) { + continue; + } + + // AABB Collision + if (!check.collisionCheck2(squareR, cell.position)) { + continue; + } + + // Cell type check - Cell must be bigger than this number times the mass of the cell being eaten + var multiplier = 1.25; + + switch (check.getType()) { + case 1: // Food cell + list.push(check); + check.inRange = true; // skip future collision checks for this food + continue; + case 2: // Virus + multiplier = 1.33; + break; + case 0: // Players + // Can't eat self if it's not time to recombine yet + if (check.owner == cell.owner) { + if (!cell.canRemrege() || !check.canRemerge()) { + continue; + } + + multiplier = 1.00; + } + + // Can't eat team members + if (this.gameMode.haveTeams) { + if (!check.owner) { // Error check + continue; + } + + if ((check.owner != cell.owner) && (check.owner.getTeam() == cell.owner.getTeam())) { + continue; + } + } + break; + default: + break; + } + + // Make sure the cell is big enough to be eaten. + if ((check.getMass() * multiplier) > cell.getMass()) { + continue; + } + + // Eating range + var xs = Math.pow(check.position.x - cell.position.x, 2); + var ys = Math.pow(check.position.y - cell.position.y, 2); + var dist = Math.sqrt(xs + ys); + + var eatingRange = cell.getSize() - check.getEatingRange(); // Eating range = radius of eating cell + 40% of the radius of the cell being eaten + if (dist > eatingRange) { + // Not in eating range + continue; + } + + // Add to list of cells nearby + list.push(check); + + // Something is about to eat this cell; no need to check for other collisions with it + check.inRange = true; + } + return list; + }; + + // this is almost same to the legacy function + GameServer.prototype.splitCells = function(client) { + var len = client.cells.length; + for (var i = 0; i < len; i++) { + if (client.cells.length >= this.config.playerMaxCells) { + // Player cell limit + continue; + } + + var cell = client.cells[i]; + if (!cell) { + continue; + } + + if (cell.getMass() < this.config.playerMinMassSplit) { + continue; + } + + // Get angle + var deltaY = client.mouse.y - cell.position.y; + var deltaX = client.mouse.x - cell.position.x; + var angle = Math.atan2(deltaX, deltaY); + + // Get starting position + var size = cell.getSize() / 2; + var startPos = { + x: cell.position.x + (size * Math.sin(angle)), + y: cell.position.y + (size * Math.cos(angle)) + }; + // Calculate mass and speed of splitting cell + var splitSpeed = cell.getSpeed() * 6; + var newMass = cell.getMass() / 2; + cell.setMass(newMass); + // Create cell + var split = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, newMass); + split.setAngle(angle); + split.setMoveEngineData(splitSpeed, 32, 0.85); + + // boost speed if zombie eats brain + if (this.gameMode.hasEatenBrain(client) || this.gameMode.isCrazy(client)) { + this.gameMode.boostSpeedCell(split); + } + // gain effect if human eat hero + else if (this.gameMode.hasEatenHero(client)) { + // fix "unable to split" bug: cell can be merged after finish moving (2nd param in setMoveEngineData) + //split.recombineTicks = 2; // main-ticks, 1 main-tick = 1 s + //TODO: fix? + } + + // Add to moving cells list + this.setAsMovingNode(split); + this.addNode(split); + } + }; + + // this function is almost same to the legacy + GameServer.prototype.newCellVirused = function(client, parent, angle, mass, speed) { + // Starting position + var startPos = { + x: parent.position.x, + y: parent.position.y + }; + + // Create cell + newCell = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, mass); + newCell.setAngle(angle); + newCell.setMoveEngineData(speed, 10); + newCell.ignoreCollision = true; // Turn off collision + + // boost speed if zombie eats brain + if (this.gameMode.hasEatenBrain(client) || this.gameMode.isCrazy(client)) { + this.gameMode.boostSpeedCell(newCell); + } + // gain effect if human eat hero + else if (this.gameMode.hasEatenHero(client)) { + // fix "unable to split" bug + //newCell.recombineTicks = 1; + // TODO: fix? + } + + // Add to moving cells list + this.addNode(newCell); + this.setAsMovingNode(newCell); + }; + + Virus.prototype.onConsume = function(consumer, gameServer) { + var client = consumer.owner; + + var maxSplits = Math.floor(consumer.getMass() / 16) - 1; // Maximum amount of splits + var numSplits = gameServer.config.playerMaxCells - client.cells.length; // Get number of splits + numSplits = Math.min(numSplits, maxSplits); + var splitMass = Math.min(consumer.getMass() / (numSplits + 1), 36); // Maximum size of new splits + + // Cell consumes mass before splitting + consumer.addMass(this.getMass()); + + // Cell cannot split any further + if (numSplits <= 0) { + return; + } + + // Big cells will split into cells larger than 36 mass (1/4 of their mass) + var bigSplits = 0; + var endMass = consumer.getMass() - (numSplits * splitMass); + if ((endMass > 300) && (numSplits > 0)) { + bigSplits++; + numSplits--; + } + if ((endMass > 1200) && (numSplits > 0)) { + bigSplits++; + numSplits--; + } + if ((endMass > 3000) && (numSplits > 0)) { + bigSplits++; + numSplits--; + } + + // Splitting + var angle = 0; // Starting angle + for (var k = 0; k < numSplits; k++) { + angle += 6 / numSplits; // Get directions of splitting cells + gameServer.newCellVirused(client, consumer, angle, splitMass, 150); + consumer.setMass(consumer.getMass() - splitMass); + } + + for (var k = 0; k < bigSplits; k++) { + angle = Math.random() * 6.28; // Random directions + splitMass = consumer.getMass() / 4; + gameServer.newCellVirused(client, consumer, angle, splitMass, 20); + consumer.setMass(consumer.getMass() - splitMass); + } + + if (gameServer.gameMode.hasEatenHero(client)) { + //consumer.recombineTicks = 0; + // TODO: fix? + } + }; + + // Handle "gamemode" command: + for (var i = 0; i < gameServer.clients.length; i++) { + var client = gameServer.clients[i].playerTracker; + if (!client) + continue; + + if (client.cells.length > 0) { + client.eatenBrainTimer = 0; + client.eatenHeroTimer = 0; + client.crazyTimer = 0; + client.color = this.defaultColor; + client.team = 1; + for (var j = 0; j < client.cells.length; j++) { + var cell = client.cells[j]; + cell.setColor(this.defaultColor); + } + this.humans.push(client); + } + } +}; + +TeamZ.prototype.onChange = function(gameServer) { + // Called when someone changes the gamemode via console commands + // remove Brain and Hero + for (var i = 0; this.brains.length; i++) { + var node = this.brains[i]; + gameServer.removeNode(node); + } + for (var i = 0; this.heroes.length; i++) { + var node = this.heroes[i]; + gameServer.removeNode(node); + } + + // discard all boost: + for (var i = 0; i < this.humans.length; i++) { + var client = this.humans[i]; + if (this.isCrazy(client)) { + this.resetSpeed(client); + } + } + for (var i = 0; i < this.zombies.length; i++) { + var client = this.zombies[i]; + if (this.hasEatenBrain(client)) { + this.resetSpeed(client); + } + } + + // revert to default: + GameServer.prototype.getRandomColor = GS_getRandomColor; + GameServer.prototype.getNearestVirus = GS_getNearestVirus; + GameServer.prototype.getCellsInRange = GS_getCellsInRange; + GameServer.prototype.splitCells = GS_splitCells; + GameServer.prototype.newCellVirused = GS_newCellVirused; + Virus.prototype.onConsume = Virus_onConsume; +}; + +TeamZ.prototype.onTick = function(gameServer) { + // Called on every game tick + + switch (this.state) { + case GameState.WF_PLAYERS: + if (this.humans.length >= this.minPlayer) { + this.state = GameState.WF_START; + this.gameTimer = this.warmUpDuration; + } + break; + case GameState.WF_START: + this.gameTimer--; + if (this.gameTimer == 0) { + if (this.humans.length >= this.minPlayer) { + // proceed: + this.startGame(gameServer); + } else { + // back to previous state: + this.state = GameState.WF_PLAYERS; + } + } + break; + case GameState.IN_PROGRESS: + this.gameTimer--; + if (this.gameTimer == 0) { + // human wins + this.winTeam = 1; + } else { + if (this.humans.length == 0) { // no human left + // zombie wins + this.winTeam = 0; + } else if (this.zombies.length == 0) { // no zombie left + // human wins + this.winTeam = 1; + } + } + + if (this.winTeam >= 0) { + this.endGame(gameServer); + } + + break; + } + + // change color of zombies + for (var i = 0; i < this.zombies.length; i++) { + var client = this.zombies[i]; + this.nextZColorFactor(client); + + if (this.hasEatenBrain(client)) { + client.eatenBrainTimer--; + + if (client.eatenBrainTimer > 0) { + this.updateZColor(client, 0x5); // Pink + continue; + } else { + // reset speed: + this.resetSpeed(client); + } + } + + this.updateZColor(client, 0x7); // Gray + } + + for (var i = 0; i < this.humans.length; i++) { + var client = this.humans[i]; + if (this.isCrazy(client)) { + client.crazyTimer--; + if (client.crazyTimer == 0) { + for (var j = 0; j < client.cells.length; j++) { + var cell = client.cells[j]; + // reset speed: + this.resetSpeedCell(cell); + + // reset color: + if (client.cured == true) + cell.setColor(client.color); + } + + if (client.cured == true) { + client.cured = false; // reset + } else { + // turn player to zombie + this.turnToZombie(client); + continue; + } + } else { + client.colorToggle++; + if (client.colorToggle % 10 == 0) { + var blinkColor = null; + + if (client.colorToggle == 20) { + blinkColor = client.color; + client.colorToggle = 0; + } else { + if (client.cured == true) { + blinkColor = { + r: 255, + g: 255, + b: 7 + }; // Yellow + } else { + blinkColor = { + r: 75, + g: 75, + b: 75 + }; // Gray + } + } + + for (var j = 0; j < client.cells.length; j++) { + var cell = client.cells[j]; + cell.setColor(blinkColor); + } + } + } + } else if (this.hasEatenHero(client)) { + client.eatenHeroTimer--; + var color = null; + if (client.eatenHeroTimer > 0) { + client.heroColorFactor = (client.heroColorFactor + 5) % 401; + if (client.heroColorFactor <= 200) { + color = { + r: 255, + g: 255, + b: client.heroColorFactor + }; // Yellow scheme + } else { + color = { + r: 255, + g: 255, + b: 400 - client.heroColorFactor + }; // Yellow scheme + } + } else { + color = client.color; // reset + } + + for (var j = 0; j < client.cells.length; j++) { + var cell = client.cells[j]; + cell.setColor(color); + } + } + } + + // check timer to spawn Hero: + this.spawnHeroTimer++; + if (this.spawnHeroTimer >= this.spawnHeroInterval) { + this.spawnHeroTimer = 0; + var cell = new Hero(gameServer.getNextNodeId(), null); + while (!this.spawnDrug(gameServer, cell)); // collision detect algorithm needs enhancement + } + + // check timer to spawn Brain: + this.spawnBrainTimer++; + if (this.spawnBrainTimer >= this.spawnBrainInterval) { + this.spawnBrainTimer = 0; + var cell = new Brain(gameServer.getNextNodeId(), null); + while (!this.spawnDrug(gameServer, cell)); // collision detect algorithm needs enhancement + } +}; + +TeamZ.prototype.onCellAdd = function(cell) { + // Called when a player cell is added + var client = cell.owner; + if (client.cells.length == 1) { // first cell + client.team = client.pID; + client.color = { + r: cell.color.r, + g: cell.color.g, + b: cell.color.b + }; + client.eatenBrainTimer = 0; + client.eatenHeroTimer = 0; + client.crazyTimer = 0; + this.humans.push(client); + + if (this.state == GameState.IN_PROGRESS) { + this.turnToZombie(client); + } else { + client.color = this.defaultColor; + cell.setColor(this.defaultColor); + client.team = 1; // game not started yet + } + } +}; + +TeamZ.prototype.onCellRemove = function(cell) { + // Called when a player cell is removed + var client = cell.owner; + if (client.cells.length == 0) { // last cell + if (client.getTeam() == 0) { + // Z team + var index = this.zombies.indexOf(client); + if (index >= 0) + this.zombies.splice(index, 1); + } else { + // H team + var index = this.humans.indexOf(client); + if (index >= 0) + this.humans.splice(index, 1); + } + } +}; + +TeamZ.prototype.onCellMove = function(x1, y1, cell) { + // Called when a player cell is moved + var team = cell.owner.getTeam(); + var r = cell.getSize(); + + // Find team + for (var i = 0; i < cell.owner.visibleNodes.length; i++) { + // Only collide with player cells + var check = cell.owner.visibleNodes[i]; + + if ((check.getType() != 0) || (cell.owner == check.owner)) { + continue; + } + + if ((this.hasEatenHero(check.owner)) || (this.hasEatenHero(cell.owner))) { + continue; + } + + // Collision with zombies + if (check.owner.getTeam() == 0 || team == 0) { + // Check if in collision range + var collisionDist = check.getSize() + r; // Minimum distance between the 2 cells + if (!cell.simpleCollide(x1, y1, check, collisionDist)) { + // Skip + continue; + } + + // First collision check passed... now more precise checking + dist = cell.getDist(cell.position.x, cell.position.y, check.position.x, check.position.y); + + // Calculations + if (dist < collisionDist) { // Collided + var crazyClient = null; + if (check.owner.getTeam() == 0 && team != 0) { + crazyClient = cell.owner; + } else if (team == 0 && check.owner.getTeam() != 0) { + crazyClient = check.owner; + } + + if (crazyClient != null && !this.isCrazy(crazyClient)) { + crazyClient.crazyTimer = this.crazyDuration; + crazyClient.colorToggle = 0; + this.boostSpeed(crazyClient); + } + + // The moving cell pushes the colliding cell + var newDeltaY = check.position.y - y1; + var newDeltaX = check.position.x - x1; + var newAngle = Math.atan2(newDeltaX, newDeltaY); + + var move = collisionDist - dist; + + check.position.x = check.position.x + (move * Math.sin(newAngle)) >> 0; + check.position.y = check.position.y + (move * Math.cos(newAngle)) >> 0; + } + } + } +}; + +TeamZ.prototype.updateLB = function(gameServer) { gameServer.leaderboardType = this.packetLB; - var lb = gameServer.leaderboard; - - if (this.winTeam == 0) { - lb.push('ZOMBIE WINS'); - lb.push('_______________'); - } else if (this.winTeam > 0) { - lb.push('HUMAN WINS'); - lb.push('_______________'); - } - - switch (this.state) { - case GameState.WF_PLAYERS: - lb.push('WAITING FOR'); - lb.push('PLAYERS...'); - lb.push(this.humans.length + '/' + this.minPlayer); - break; - case GameState.WF_START: - lb.push('GAME STARTS IN:'); - var min = (this.gameTimer / 20 / 60) >> 0; - var sec = ((this.gameTimer / 20) >> 0) % 60; - lb.push((min < 10 ? '0' : '') + min + ':' + (sec < 10 ? '0' : '') + sec); - break; - case GameState.IN_PROGRESS: - var min = (this.gameTimer / 20 / 60) >> 0; - var sec = ((this.gameTimer / 20) >> 0) % 60; - lb.push((min < 10 ? '0' : '') + min + ':' + (sec < 10 ? '0' : '') + sec); - lb.push('HUMAN: ' + this.humans.length); - lb.push('ZOMBIE: ' + this.zombies.length); - lb.push('_______________'); - - // Loop through all clients - localLB = []; - for (var i = 0; i < gameServer.clients.length; i++) { - if (typeof gameServer.clients[i] == 'undefined' || gameServer.clients[i].playerTracker.team == 0) { - continue; - } - - var player = gameServer.clients[i].playerTracker; - if (player.cells.length <= 0) { - continue; - } - var playerScore = player.getScore(true); - - if (localLB.length == 0) { - // Initial player - localLB.push(player); - continue; - } else if (localLB.length < 6) { - this.leaderboardAddSort(player, localLB); - } else { - // 6 in leaderboard already - if (playerScore > localLB[5].getScore(false)) { - localLB.pop(); - this.leaderboardAddSort(player, localLB); - } - } - } - for (var i = 0; i < localLB.length && lb.length < 10; i++) { - lb.push(localLB[i].getName()); - } - - break; - default: - lb.push('ERROR STATE'); - break; - } - -}; - -// ---------------------------------------------------------------------------- -// Game mode entities: - -// HERO POISON CELL: -function Hero() { - Cell.apply(this, Array.prototype.slice.call(arguments)); - - this.cellType = CellType.HERO; - //this.spiked = 1; - this.color = { - r: 255, - g: 255, - b: 7 - }; - this.setMass(60); -} - -Hero.prototype = new Cell(); - -Hero.prototype.getName = function() { - return 'HERO'; -}; - -Hero.prototype.calcMove = null; - -Hero.prototype.onAdd = function(gameServer) { - gameServer.gameMode.heroes.push(this); -}; - -Hero.prototype.onRemove = function(gameServer) { - var index = gameServer.gameMode.heroes.indexOf(this); - if (index != -1) { - gameServer.gameMode.heroes.splice(index, 1); - } else { - console.log('[Warning] Tried to remove a non existing HERO node!'); - } -}; - -Hero.prototype.feed = function(feeder, gameServer) { - gameServer.removeNode(feeder); - - this.setAngle(feeder.getAngle()); - this.moveEngineTicks = 5; // Amount of times to loop the movement function - this.moveEngineSpeed = 60; - - var index = gameServer.movingNodes.indexOf(this); - if (index == -1) { - gameServer.movingNodes.push(this); - } -}; - -Hero.prototype.onConsume = function(consumer, gameServer) { - // Called when the cell is consumed - var client = consumer.owner; - consumer.addMass(this.getMass()); // delicious - - if (gameServer.gameMode.isCrazy(client)) { - // Neutralize the Zombie effect - client.cured = true; - } else { - // Become a hero - client.eatenHeroTimer = gameServer.gameMode.heroEffectDuration; - client.heroColorFactor = 0; - - // Merge immediately - //for (var i = 0; i < client.cells.length; i++) { - // var cell = client.cells[i]; - // cell.recombineTicks = 0; - //} - // TODO: fix? - - } -}; - -// ---------------------------------------------------------------------------- -// BRAIN CELL: -function Brain() { - Cell.apply(this, Array.prototype.slice.call(arguments)); - - this.cellType = CellType.BRAIN; - //this.spiked = 1; - this.color = { - r: 255, - g: 7, - b: 255 - }; - this.setMass(60); -} - -Brain.prototype = new Cell(); - -Brain.prototype.getName = function() { - return 'BRAIN'; -}; - -Brain.prototype.calcMove = null; - -Brain.prototype.onAdd = function(gameServer) { - gameServer.gameMode.brains.push(this); -}; - -Brain.prototype.onRemove = function(gameServer) { - var index = gameServer.gameMode.brains.indexOf(this); - if (index != -1) { - gameServer.gameMode.brains.splice(index, 1); - } else { - console.log('[Warning] Tried to remove a non existing BRAIN node!'); - } -}; - -Brain.prototype.feed = function(feeder, gameServer) { - gameServer.removeNode(feeder); - - this.setAngle(feeder.getAngle()); - this.moveEngineTicks = 5; // Amount of times to loop the movement function - this.moveEngineSpeed = 60; - - var index = gameServer.movingNodes.indexOf(this); - if (index == -1) { - gameServer.movingNodes.push(this); - } -}; - -Brain.prototype.onConsume = function(consumer, gameServer) { - // Called when the cell is consumed - var client = consumer.owner; - consumer.addMass(this.getMass()); // yummy! - - client.eatenBrainTimer = gameServer.gameMode.brainEffectDuration; - - // Boost speed - gameServer.gameMode.boostSpeed(client); -}; + var lb = gameServer.leaderboard; + + if (this.winTeam == 0) { + lb.push('ZOMBIE WINS'); + lb.push('_______________'); + } else if (this.winTeam > 0) { + lb.push('HUMAN WINS'); + lb.push('_______________'); + } + + switch (this.state) { + case GameState.WF_PLAYERS: + lb.push('WAITING FOR'); + lb.push('PLAYERS...'); + lb.push(this.humans.length + '/' + this.minPlayer); + break; + case GameState.WF_START: + lb.push('GAME STARTS IN:'); + var min = (this.gameTimer / 20 / 60) >> 0; + var sec = ((this.gameTimer / 20) >> 0) % 60; + lb.push((min < 10 ? '0' : '') + min + ':' + (sec < 10 ? '0' : '') + sec); + break; + case GameState.IN_PROGRESS: + var min = (this.gameTimer / 20 / 60) >> 0; + var sec = ((this.gameTimer / 20) >> 0) % 60; + lb.push((min < 10 ? '0' : '') + min + ':' + (sec < 10 ? '0' : '') + sec); + lb.push('HUMAN: ' + this.humans.length); + lb.push('ZOMBIE: ' + this.zombies.length); + lb.push('_______________'); + + // Loop through all clients + localLB = []; + for (var i = 0; i < gameServer.clients.length; i++) { + if (typeof gameServer.clients[i] == 'undefined' || gameServer.clients[i].playerTracker.team == 0) { + continue; + } + + var player = gameServer.clients[i].playerTracker; + if (player.cells.length <= 0) { + continue; + } + var playerScore = player.getScore(); + + if (localLB.length == 0) { + // Initial player + localLB.push(player); + continue; + } else if (localLB.length < 6) { + this.leaderboardAddSort(player, localLB); + } else { + // 6 in leaderboard already + if (playerScore > localLB[5].getScore()) { + localLB.pop(); + this.leaderboardAddSort(player, localLB); + } + } + } + for (var i = 0; i < localLB.length && lb.length < 10; i++) { + lb.push(localLB[i].getName()); + } + + break; + default: + lb.push('ERROR STATE'); + break; + } + +}; + +// ---------------------------------------------------------------------------- +// Game mode entities: + +// HERO POISON CELL: +function Hero() { + Cell.apply(this, Array.prototype.slice.call(arguments)); + + this.cellType = CellType.HERO; + //this.spiked = 1; + this.color = { + r: 255, + g: 255, + b: 7 + }; + this.setMass(60); +} + +Hero.prototype = new Cell(); + +Hero.prototype.getName = function() { + return 'HERO'; +}; + +Hero.prototype.calcMove = null; + +Hero.prototype.onAdd = function(gameServer) { + gameServer.gameMode.heroes.push(this); +}; + +Hero.prototype.onRemove = function(gameServer) { + var index = gameServer.gameMode.heroes.indexOf(this); + if (index != -1) { + gameServer.gameMode.heroes.splice(index, 1); + } else { + console.log('[Warning] Tried to remove a non existing HERO node!'); + } +}; + +Hero.prototype.feed = function(feeder, gameServer) { + gameServer.removeNode(feeder); + + this.setAngle(feeder.getAngle()); + this.moveEngineTicks = 5; // Amount of times to loop the movement function + this.moveEngineSpeed = 60; + + var index = gameServer.movingNodes.indexOf(this); + if (index == -1) { + gameServer.movingNodes.push(this); + } +}; + +Hero.prototype.onConsume = function(consumer, gameServer) { + // Called when the cell is consumed + var client = consumer.owner; + consumer.addMass(this.getMass()); // delicious + + if (gameServer.gameMode.isCrazy(client)) { + // Neutralize the Zombie effect + client.cured = true; + } else { + // Become a hero + client.eatenHeroTimer = gameServer.gameMode.heroEffectDuration; + client.heroColorFactor = 0; + + // Merge immediately + //for (var i = 0; i < client.cells.length; i++) { + // var cell = client.cells[i]; + // cell.recombineTicks = 0; + //} + // TODO: fix? + + } +}; + +// ---------------------------------------------------------------------------- +// BRAIN CELL: +function Brain() { + Cell.apply(this, Array.prototype.slice.call(arguments)); + + this.cellType = CellType.BRAIN; + //this.spiked = 1; + this.color = { + r: 255, + g: 7, + b: 255 + }; + this.setMass(60); +} + +Brain.prototype = new Cell(); + +Brain.prototype.getName = function() { + return 'BRAIN'; +}; + +Brain.prototype.calcMove = null; + +Brain.prototype.onAdd = function(gameServer) { + gameServer.gameMode.brains.push(this); +}; + +Brain.prototype.onRemove = function(gameServer) { + var index = gameServer.gameMode.brains.indexOf(this); + if (index != -1) { + gameServer.gameMode.brains.splice(index, 1); + } else { + console.log('[Warning] Tried to remove a non existing BRAIN node!'); + } +}; + +Brain.prototype.feed = function(feeder, gameServer) { + gameServer.removeNode(feeder); + + this.setAngle(feeder.getAngle()); + this.moveEngineTicks = 5; // Amount of times to loop the movement function + this.moveEngineSpeed = 60; + + var index = gameServer.movingNodes.indexOf(this); + if (index == -1) { + gameServer.movingNodes.push(this); + } +}; + +Brain.prototype.onConsume = function(consumer, gameServer) { + // Called when the cell is consumed + var client = consumer.owner; + consumer.addMass(this.getMass()); // yummy! + + client.eatenBrainTimer = gameServer.gameMode.brainEffectDuration; + + // Boost speed + gameServer.gameMode.boostSpeed(client); +}; diff --git a/src/gamemodes/Zombie.js b/src/gamemodes/Zombie.js index 5e64851db..16b0ccce7 100755 --- a/src/gamemodes/Zombie.js +++ b/src/gamemodes/Zombie.js @@ -1,178 +1,178 @@ -var Mode = require('./Mode'); - -function Zombie() { - Mode.apply(this, Array.prototype.slice.call(arguments)); - - this.ID = 12; - this.name = "Zombie FFA"; - this.haveTeams = true; - this.zombieColor = { - 'r': 223, - 'g': 223, - 'b': 223 - }; - this.zombies = []; - this.players = []; -} - -module.exports = Zombie; -Zombie.prototype = new Mode(); - -// Gamemode Specific Functions - -Zombie.prototype.leaderboardAddSort = function(player, leaderboard) { - // Adds the player and sorts the leaderboard - var len = leaderboard.length - 1; - var loop = true; - while ((len >= 0) && (loop)) { - // Start from the bottom of the leaderboard - if (player.getScore(false) <= leaderboard[len].getScore(false)) { - leaderboard.splice(len + 1, 0, player); - loop = false; // End the loop if a spot is found - } - len--; - } - if (loop) { - // Add to top of the list because no spots were found - leaderboard.splice(0, 0, player); - } -}; - -Zombie.prototype.makeZombie = function(player) { - // turns a player into a zombie - player.team = 0; - player.color = this.zombieColor; - for (var i = 0; i < player.cells.length; i++) { - // remove cell from players array - var index = this.players.indexOf(player.cells[i]); - if (index != -1) { - this.players.splice(index, 1); - } - // change color of cell - player.cells[i].color = this.zombieColor; - // add cell to zombie array - this.zombies.push(player.cells[i]); - } -}; - -// Override - -Zombie.prototype.onPlayerSpawn = function(gameServer, player) { - // make player a zombie if there are none - if (this.zombies.length == 0) { - player.team = 0; - player.color = this.zombieColor; - } else { - // use player id as team so that bots are still able to fight (even though they probably turn into zombies very fast) - player.team = player.pID; - player.color = gameServer.getRandomColor(); - } - - // Spawn player - gameServer.spawnPlayer(player); -}; - -Zombie.prototype.onCellAdd = function(cell) { - // Add to team list - if (cell.owner.getTeam() == 0) { - this.zombies.push(cell); - } else { - this.players.push(cell); - } -}; - -Zombie.prototype.onCellRemove = function(cell) { - // Remove from team list - if (cell.owner.getTeam() == 0) { - var index = this.zombies.indexOf(cell); - if (index != -1) { - this.zombies.splice(index, 1); - } - } else { - var index = this.players.indexOf(cell); - if (index != -1) { - this.players.splice(index, 1); - } - } -}; - -Zombie.prototype.onCellMove = function(x1, y1, cell) { - var team = cell.owner.getTeam(); - var r = cell.getSize(); - - // Find team - for (var i = 0; i < cell.owner.visibleNodes.length; i++) { - // Only collide with player cells - var check = cell.owner.visibleNodes[i]; - - if ((check.getType() != 0) || (cell.owner == check.owner)) { - continue; - } - - // Collision with zombies - if (check.owner.getTeam() == team || check.owner.getTeam() == 0 || team == 0) { - // Check if in collision range - var collisionDist = check.getSize() + r; // Minimum distance between the 2 cells - if (!cell.simpleCollide(x1, y1, check, collisionDist)) { - // Skip - continue; - } - - // First collision check passed... now more precise checking - dist = cell.getDist(cell.position.x, cell.position.y, check.position.x, check.position.y); - - // Calculations - if (dist < collisionDist) { // Collided - if (check.owner.getTeam() == 0 && team != 0) { - // turn player into zombie - this.makeZombie(cell.owner); - } else if (team == 0 && check.owner.getTeam() != 0) { - // turn other player into zombie - this.makeZombie(check.owner); - } - // The moving cell pushes the colliding cell - var newDeltaY = check.position.y - y1; - var newDeltaX = check.position.x - x1; - var newAngle = Math.atan2(newDeltaX, newDeltaY); - - var move = collisionDist - dist; - - check.position.x = check.position.x + (move * Math.sin(newAngle)) >> 0; - check.position.y = check.position.y + (move * Math.cos(newAngle)) >> 0; - } - } - } -}; - -Zombie.prototype.updateLB = function(gameServer) { +var Mode = require('./Mode'); + +function Zombie() { + Mode.apply(this, Array.prototype.slice.call(arguments)); + + this.ID = 12; + this.name = "Zombie FFA"; + this.haveTeams = true; + this.zombieColor = { + 'r': 223, + 'g': 223, + 'b': 223 + }; + this.zombies = []; + this.players = []; +} + +module.exports = Zombie; +Zombie.prototype = new Mode(); + +// Gamemode Specific Functions + +Zombie.prototype.leaderboardAddSort = function(player, leaderboard) { + // Adds the player and sorts the leaderboard + var len = leaderboard.length - 1; + var loop = true; + while ((len >= 0) && (loop)) { + // Start from the bottom of the leaderboard + if (player.getScore() <= leaderboard[len].getScore()) { + leaderboard.splice(len + 1, 0, player); + loop = false; // End the loop if a spot is found + } + len--; + } + if (loop) { + // Add to top of the list because no spots were found + leaderboard.splice(0, 0, player); + } +}; + +Zombie.prototype.makeZombie = function(player) { + // turns a player into a zombie + player.team = 0; + player.color = this.zombieColor; + for (var i = 0; i < player.cells.length; i++) { + // remove cell from players array + var index = this.players.indexOf(player.cells[i]); + if (index != -1) { + this.players.splice(index, 1); + } + // change color of cell + player.cells[i].color = this.zombieColor; + // add cell to zombie array + this.zombies.push(player.cells[i]); + } +}; + +// Override + +Zombie.prototype.onPlayerSpawn = function(gameServer, player) { + // make player a zombie if there are none + if (this.zombies.length == 0) { + player.team = 0; + player.color = this.zombieColor; + } else { + // use player id as team so that bots are still able to fight (even though they probably turn into zombies very fast) + player.team = player.pID; + player.color = gameServer.getRandomColor(); + } + + // Spawn player + gameServer.spawnPlayer(player); +}; + +Zombie.prototype.onCellAdd = function(cell) { + // Add to team list + if (cell.owner.getTeam() == 0) { + this.zombies.push(cell); + } else { + this.players.push(cell); + } +}; + +Zombie.prototype.onCellRemove = function(cell) { + // Remove from team list + if (cell.owner.getTeam() == 0) { + var index = this.zombies.indexOf(cell); + if (index != -1) { + this.zombies.splice(index, 1); + } + } else { + var index = this.players.indexOf(cell); + if (index != -1) { + this.players.splice(index, 1); + } + } +}; + +Zombie.prototype.onCellMove = function(x1, y1, cell) { + var team = cell.owner.getTeam(); + var r = cell.getSize(); + + // Find team + for (var i = 0; i < cell.owner.visibleNodes.length; i++) { + // Only collide with player cells + var check = cell.owner.visibleNodes[i]; + + if ((check.getType() != 0) || (cell.owner == check.owner)) { + continue; + } + + // Collision with zombies + if (check.owner.getTeam() == team || check.owner.getTeam() == 0 || team == 0) { + // Check if in collision range + var collisionDist = check.getSize() + r; // Minimum distance between the 2 cells + if (!cell.simpleCollide(x1, y1, check, collisionDist)) { + // Skip + continue; + } + + // First collision check passed... now more precise checking + dist = cell.getDist(cell.position.x, cell.position.y, check.position.x, check.position.y); + + // Calculations + if (dist < collisionDist) { // Collided + if (check.owner.getTeam() == 0 && team != 0) { + // turn player into zombie + this.makeZombie(cell.owner); + } else if (team == 0 && check.owner.getTeam() != 0) { + // turn other player into zombie + this.makeZombie(check.owner); + } + // The moving cell pushes the colliding cell + var newDeltaY = check.position.y - y1; + var newDeltaX = check.position.x - x1; + var newAngle = Math.atan2(newDeltaX, newDeltaY); + + var move = collisionDist - dist; + + check.position.x = check.position.x + (move * Math.sin(newAngle)) >> 0; + check.position.y = check.position.y + (move * Math.cos(newAngle)) >> 0; + } + } + } +}; + +Zombie.prototype.updateLB = function(gameServer) { gameServer.leaderboardType = this.packetLB; - var lb = gameServer.leaderboard; - // Loop through all clients - for (var i = 0; i < gameServer.clients.length; i++) { - if (typeof gameServer.clients[i] == "undefined" || gameServer.clients[i].playerTracker.team == 0) { - continue; - } - - var player = gameServer.clients[i].playerTracker; - var playerScore = player.getScore(true); - if (player.cells.length <= 0) { - continue; - } - - if (lb.length == 0) { - // Initial player - lb.push(player); - continue; - } else if (lb.length < 10) { - this.leaderboardAddSort(player, lb); - } else { - // 10 in leaderboard already - if (playerScore > lb[9].getScore(false)) { - lb.pop(); - this.leaderboardAddSort(player, lb); - } - } - } - - this.rankOne = lb[0]; -}; + var lb = gameServer.leaderboard; + // Loop through all clients + for (var i = 0; i < gameServer.clients.length; i++) { + if (typeof gameServer.clients[i] == "undefined" || gameServer.clients[i].playerTracker.team == 0) { + continue; + } + + var player = gameServer.clients[i].playerTracker; + var playerScore = player.getScore(); + if (player.cells.length <= 0) { + continue; + } + + if (lb.length == 0) { + // Initial player + lb.push(player); + continue; + } else if (lb.length < 10) { + this.leaderboardAddSort(player, lb); + } else { + // 10 in leaderboard already + if (playerScore > lb[9].getScore()) { + lb.pop(); + this.leaderboardAddSort(player, lb); + } + } + } + + this.rankOne = lb[0]; +}; diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 342917ddd..01e1bc737 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -417,7 +417,7 @@ Commands.list = { } else if (client.cells.length > 0) { nick = fillChar((client.name == "") ? "An unnamed cell" : client.name, ' ', gameServer.config.playerMaxNickLength); cells = fillChar(client.cells.length, ' ', 5, true); - score = fillChar(client.getScore(true), ' ', 6, true); + score = fillChar(client.getScore(), ' ', 6, true); position = fillChar(client.centerPos.x >> 0, ' ', 5, true) + ', ' + fillChar(client.centerPos.y >> 0, ' ', 5, true); console.log(" " + id + " | " + ip + " | " + nick + " | " + cells + " | " + score + " | " + position); } else { From d08c0db3e0b00718a852f9173bc2bf655d272d7a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 20:42:03 +0200 Subject: [PATCH 057/417] add chat support --- src/GameServer.js | 17 +++++++++++++++++ src/PacketHandler.js | 23 +++++++++++++++++++++++ src/packet/index.js | 1 + 3 files changed, 41 insertions(+) diff --git a/src/GameServer.js b/src/GameServer.js index 9140973a1..5129d58b7 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -413,6 +413,23 @@ GameServer.prototype.updateLeaderboard = function () { } }; +GameServer.prototype.onChatMessage = function (from, to, message) { + if (message == null) return; + message = message.trim(); + if (message == "") return; + if (message.length > 128) message = message.slice(0, 128); + //console.log("[CHAT] " + (from!=null && from.getName().length>0 ? from.getName() : "Spectator") + ": " + message); + this.sendChatMessage(from, to, message); +}; + +GameServer.prototype.sendChatMessage = function (from, to, message) { + for (var i = 0; i < this.clients.length; i++) { + var client = this.clients[i]; + if (client == null) continue; + if (to == null || to == client.playerTracker) + client.sendPacket(new Packet.ChatMessage(from, message)); + } +}; GameServer.prototype.mainLoop = function() { // Loop main functions diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 1fbe7f9ca..77cd4367a 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -6,6 +6,7 @@ function PacketHandler(gameServer, socket) { this.socket = socket; // Detect protocol version - we can do something about it later this.protocol = 0; + this.lastChatTick = 0; this.pressQ = false; this.pressW = false; @@ -87,6 +88,27 @@ PacketHandler.prototype.handleMessage = function(message) { // W Press - Eject mass this.pressW = true; break; + case 99: + // Chat + var tick = this.gameServer.getTick(); + // antispam: ignore it if the time between two message is smaller than 2 seconds + if (tick - this.lastChatTick < 40 * 2) + break; + this.lastChatTick = tick; + var reader = new BinaryReader(message); + reader.readUInt8(); // message id + var flags = reader.readUInt8(); // flags + if (flags & 2) reader.readBytes(4); + if (flags & 4) reader.readBytes(8); + if (flags & 8) reader.readBytes(16); + var text = null; + if (this.protocol <= 5) + text = reader.readStringZeroUnicode(); + else + text = reader.readStringZeroUtf8(); + this.gameServer.onChatMessage(this.socket.playerTracker, null, text); + break; + case 254: // Connection Start if (view.byteLength == 5) { @@ -101,6 +123,7 @@ PacketHandler.prototype.handleMessage = function(message) { c.borderBottom + this.socket.playerTracker.scrambleY, 0, "MultiOgar 1.0")); + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); } break; default: diff --git a/src/packet/index.js b/src/packet/index.js index 74486d0ea..bbb1bdf27 100644 --- a/src/packet/index.js +++ b/src/packet/index.js @@ -1,6 +1,7 @@ module.exports = { BinaryWriter: require('./BinaryWriter'), BinaryReader: require('./BinaryReader'), + ChatMessage: require('./ChatMessage'), AddNode: require('./AddNode'), ClearNodes: require('./ClearNodes'), UpdatePosition: require('./UpdatePosition'), From 4945327767ef5584d1c893938c7cb96100f4463b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 20:42:32 +0200 Subject: [PATCH 058/417] add chat support (missing file) --- src/packet/ChatMessage.js | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 src/packet/ChatMessage.js diff --git a/src/packet/ChatMessage.js b/src/packet/ChatMessage.js new file mode 100644 index 000000000..ae09727f4 --- /dev/null +++ b/src/packet/ChatMessage.js @@ -0,0 +1,44 @@ +// Import +var BinaryWriter = require("./BinaryWriter"); + + +function ChatMessage(sender, message) { + this.sender = sender; + this.message = message; +} + +module.exports = ChatMessage; + +ChatMessage.prototype.build = function (protocol) { + var text = this.message; + if (text == null) text = ""; + var name = "SERVER"; + var color = { 'r': 0x9B, 'g': 0x9B, 'b': 0x9B }; + if (this.sender != null) { + name = this.sender.getName(); + if (name == null || name.length == 0) { + if (this.sender.cells.length > 0) + name = "An unnamed cell"; + else + name = "Spectator"; + } + if (this.sender.cells.length > 0) { + color = this.sender.cells[0].getColor(); + } + } + + var writer = new BinaryWriter(); + writer.writeUInt8(0x63); // message id (decimal 99) + writer.writeUInt8(0x00); // flags for client; for future use + writer.writeUInt8(color.r >> 0); + writer.writeUInt8(color.g >> 0); + writer.writeUInt8(color.b >> 0); + if (protocol <= 5) { + writer.writeStringZeroUnicode(name); + writer.writeStringZeroUnicode(text); + } else { + writer.writeStringZeroUtf8(name); + writer.writeStringZeroUtf8(text); + } + return writer.ToBuffer(); +}; From b7ee8b4458a84492b5e2bbabcfb20f5532c3e60d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 6 Jun 2016 22:33:20 +0200 Subject: [PATCH 059/417] fix playerlist command output --- src/modules/CommandList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 01e1bc737..b6c6dc21f 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -417,7 +417,7 @@ Commands.list = { } else if (client.cells.length > 0) { nick = fillChar((client.name == "") ? "An unnamed cell" : client.name, ' ', gameServer.config.playerMaxNickLength); cells = fillChar(client.cells.length, ' ', 5, true); - score = fillChar(client.getScore(), ' ', 6, true); + score = fillChar(client.getScore() >> 0, ' ', 6, true); position = fillChar(client.centerPos.x >> 0, ' ', 5, true) + ', ' + fillChar(client.centerPos.y >> 0, ' ', 5, true); console.log(" " + id + " | " + ip + " | " + nick + " | " + cells + " | " + score + " | " + position); } else { From c2fba38a968f2c3f180c978103732f8b62549428 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 00:21:35 +0200 Subject: [PATCH 060/417] protocol optimizations and validation; code refactoring --- src/PacketHandler.js | 139 ++++++++++++++++++------------------------- src/PlayerTracker.js | 18 ++++++ 2 files changed, 75 insertions(+), 82 deletions(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 77cd4367a..65113d087 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -16,31 +16,14 @@ function PacketHandler(gameServer, socket) { module.exports = PacketHandler; PacketHandler.prototype.handleMessage = function(message) { - function stobuf(buf) { - var length = buf.length; - var arrayBuf = new ArrayBuffer(length); - var view = new Uint8Array(arrayBuf); - - for (var i = 0; i < length; i++) { - view[i] = buf[i]; - } - - return view.buffer; - } - // Discard empty messages - if (message.length == 0) { + if (message.length == 0) return; - } - - var buffer = stobuf(message); - var view = new DataView(buffer); - var packetId = view.getUint8(0, true); + var reader = new BinaryReader(message); + var packetId = reader.readUInt8(); switch (packetId) { case 0: - var reader = new BinaryReader(message); - reader.readUInt8(); var text = null; if (this.protocol <= 5) text = reader.readStringZeroUnicode(); @@ -59,17 +42,20 @@ PacketHandler.prototype.handleMessage = function(message) { case 16: // Set Target var client = this.socket.playerTracker; - if (view.byteLength == 13) { // protocol 5,6,7 - client.mouse.x = view.getInt32(1, true) - client.scrambleX; - client.mouse.y = view.getInt32(5, true) - client.scrambleY; + if (message.length == 13) { + // protocol late 5, 6, 7 + client.mouse.x = reader.readInt32() - client.scrambleX; + client.mouse.y = reader.readInt32() - client.scrambleY; client.movePacketTriggered = true; - } else if (view.byteLength == 9) { // early protocol 5 - client.mouse.x = view.getInt16(1, true) - client.scrambleX; - client.mouse.y = view.getInt16(3, true) - client.scrambleY; + } else if (message.length == 9) { + // early protocol 5 + client.mouse.x = reader.readInt16() - client.scrambleX; + client.mouse.y = reader.readInt16() - client.scrambleY; client.movePacketTriggered = true; - } else if (view.byteLength == 21) { // protocol 4 - client.mouse.x = view.getFloat64(1, true) - client.scrambleX; - client.mouse.y = view.getFloat64(9, true) - client.scrambleY; + } else if (message.length == 21) { + // protocol 4 + client.mouse.x = reader.readDouble() - client.scrambleX; + client.mouse.y = reader.readDouble() - client.scrambleY; client.movePacketTriggered = true; } break; @@ -90,17 +76,22 @@ PacketHandler.prototype.handleMessage = function(message) { break; case 99: // Chat - var tick = this.gameServer.getTick(); - // antispam: ignore it if the time between two message is smaller than 2 seconds - if (tick - this.lastChatTick < 40 * 2) + if (message.length < 3) // first validation break; + var tick = this.gameServer.getTick(); + var deltaTick = tick - this.lastChatTick; this.lastChatTick = tick; - var reader = new BinaryReader(message); - reader.readUInt8(); // message id - var flags = reader.readUInt8(); // flags - if (flags & 2) reader.readBytes(4); - if (flags & 4) reader.readBytes(8); - if (flags & 8) reader.readBytes(16); + // antispam: + // ignore if thetime between two messages is smaller than 2 seconds + // The user should stop spamming for at least 2 seconds in order to send next chat message + if (deltaTick < 40 * 2) + break; + + var flags = reader.readUInt8(); // flags + var rvLength = (flags & 2 ? 4:0) + (flags & 4 ? 8:0) + (flags & 8 ? 16:0); + if (message.length < 3 + rvLength) // second validation + break; + reader.readBytes(rvLength); // reserved var text = null; if (this.protocol <= 5) text = reader.readStringZeroUnicode(); @@ -110,19 +101,21 @@ PacketHandler.prototype.handleMessage = function(message) { break; case 254: - // Connection Start - if (view.byteLength == 5) { - this.protocol = view.getUint32(1, true); - // Send Clear & SetBorder packet first + // Handshake request + if (this.protocol != 0) // second handshake request is not allowed + break; + if (message.length == 5) { + this.protocol = reader.readUInt32(); + // Send handshake response this.socket.sendPacket(new Packet.ClearNodes()); - var c = this.gameServer.config; this.socket.sendPacket(new Packet.SetBorder( - c.borderLeft + this.socket.playerTracker.scrambleX, - c.borderRight + this.socket.playerTracker.scrambleX, - c.borderTop + this.socket.playerTracker.scrambleY, - c.borderBottom + this.socket.playerTracker.scrambleY, + this.gameServer.config.borderLeft + this.socket.playerTracker.scrambleX, + this.gameServer.config.borderRight + this.socket.playerTracker.scrambleX, + this.gameServer.config.borderTop + this.socket.playerTracker.scrambleY, + this.gameServer.config.borderBottom + this.socket.playerTracker.scrambleY, 0, "MultiOgar 1.0")); + // Send welcome message this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); } break; @@ -131,44 +124,26 @@ PacketHandler.prototype.handleMessage = function(message) { } }; -PacketHandler.prototype.setNickname = function(text) { - var client = this.socket.playerTracker; - if (client.cells.length < 1) { - var name = ""; - var skin = ""; - if (text != null && text.length != 0) { - var n = -1; - if (text.charAt(0) == '<' && (n = text.indexOf('>', 1)) >= 0) { - skin = "%" + text.slice(1, n); - name = text.slice(n + 1); +PacketHandler.prototype.setNickname = function (text) { + var name = ""; + var skin = ""; + if (text != null && text.length != 0) { + var n = -1; + if (text.charAt(0) == '<' && (n = text.indexOf('>', 1)) >= 0) { + skin = "%" + text.slice(1, n); + name = text.slice(n + 1); //} else if (text[0] == "|" && (n = text.indexOf("|", 1)) >= 0) { // skin = ":http://i.imgur.com/" + text.slice(1, n); // name = text.slice(n + 1); - } else { - name = text; - } - } - if (name.length > this.gameServer.config.playerMaxNickLength) { - name = name.substring(0, this.gameServer.config.playerMaxNickLength); - } - if (this.gameServer.gameMode.haveTeams) { - skin = ""; + } else { + name = text; } - client.setName(name); - client.setSkin(skin); - - var c = this.gameServer.config; - this.socket.sendPacket(new Packet.ClearNodes()); - this.socket.sendPacket(new Packet.SetBorder( - c.borderLeft + this.socket.playerTracker.scrambleX, - c.borderRight + this.socket.playerTracker.scrambleX, - c.borderTop + this.socket.playerTracker.scrambleY, - c.borderBottom + this.socket.playerTracker.scrambleY)); - - // If client has no cells... then spawn a player - this.gameServer.gameMode.onPlayerSpawn(this.gameServer, client); - - // Turn off spectate mode - client.spectate = false; } + if (name.length > this.gameServer.config.playerMaxNickLength) { + name = name.substring(0, this.gameServer.config.playerMaxNickLength); + } + if (this.gameServer.gameMode.haveTeams) { + skin = ""; + } + this.socket.playerTracker.joinGame(name, skin); }; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 211b5e19b..e7550ff64 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -138,6 +138,24 @@ PlayerTracker.prototype.getTeam = function() { // Functions +PlayerTracker.prototype.joinGame = function (name, skin) { + if (this.cells.length > 0) return; + if (name == null) name = ""; + if (skin == null) skin = ""; + this.setName(name); + this.setSkin(skin); + this.spectate = false; + + this.socket.sendPacket(new Packet.ClearNodes()); + this.socket.sendPacket(new Packet.SetBorder( + this.gameServer.config.borderLeft + this.scrambleX, + this.gameServer.config.borderRight + this.scrambleX, + this.gameServer.config.borderTop + this.scrambleY, + this.gameServer.config.borderBottom + this.scrambleY)); + + this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); +}; + PlayerTracker.prototype.update = function () { // if initialization is not complete yet then do not update if (this.socket.packetHandler.protocol == 0) From c653beeb5a59dbc65292464ac4c147828acdf0c2 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 00:43:23 +0200 Subject: [PATCH 061/417] add anti-spam protection --- src/PacketHandler.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 65113d087..54309f68d 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -19,6 +19,11 @@ PacketHandler.prototype.handleMessage = function(message) { // Discard empty messages if (message.length == 0) return; + if (message.length > 2048) { + // anti-spamming + this.socket.close(1000, "Spam"); + return; + } var reader = new BinaryReader(message); var packetId = reader.readUInt8(); From 33822357f365fd44e93a30115f6eeea9eb4869ac Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 00:49:31 +0200 Subject: [PATCH 062/417] fix BinaryReader.readBytes; a little protocol optimization --- src/PacketHandler.js | 2 +- src/packet/BinaryReader.js | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 54309f68d..97b35f221 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -96,7 +96,7 @@ PacketHandler.prototype.handleMessage = function(message) { var rvLength = (flags & 2 ? 4:0) + (flags & 4 ? 8:0) + (flags & 8 ? 16:0); if (message.length < 3 + rvLength) // second validation break; - reader.readBytes(rvLength); // reserved + reader.skipBytes(rvLength); // reserved var text = null; if (this.protocol <= 5) text = reader.readStringZeroUnicode(); diff --git a/src/packet/BinaryReader.js b/src/packet/BinaryReader.js index 5c0bd5ead..7cd2c5d47 100644 --- a/src/packet/BinaryReader.js +++ b/src/packet/BinaryReader.js @@ -62,6 +62,11 @@ BinaryReader.prototype.readDouble = function () { BinaryReader.prototype.readBytes = function (length) { return this._buffer.slice(this._offset, this._offset + length); + this._offset += length; +}; + +BinaryReader.prototype.skipBytes = function (length) { + this._offset += length; }; BinaryReader.prototype.readStringUtf8 = function (length) { From 17a8a53298ea403a22d53db9ec05541127007b30 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 00:52:18 +0200 Subject: [PATCH 063/417] refactoring --- src/PacketHandler.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 97b35f221..ad6d080d4 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -21,8 +21,8 @@ PacketHandler.prototype.handleMessage = function(message) { return; if (message.length > 2048) { // anti-spamming - this.socket.close(1000, "Spam"); - return; + this.socket.close(1000, "Spam"); + return; } var reader = new BinaryReader(message); var packetId = reader.readUInt8(); @@ -83,12 +83,12 @@ PacketHandler.prototype.handleMessage = function(message) { // Chat if (message.length < 3) // first validation break; + // chat anti-spam + // Just ignore if the time between two messages is smaller than 2 seconds + // The user should stop spamming for at least 2 seconds in order to send next chat message var tick = this.gameServer.getTick(); var deltaTick = tick - this.lastChatTick; this.lastChatTick = tick; - // antispam: - // ignore if thetime between two messages is smaller than 2 seconds - // The user should stop spamming for at least 2 seconds in order to send next chat message if (deltaTick < 40 * 2) break; From e9ff11215853199de77bf0fb65f0a72b31ff8bea Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 01:51:27 +0200 Subject: [PATCH 064/417] fix spawn cell update issue; SetBorder refactoring; add scramble minimap template --- src/PacketHandler.js | 14 +++++++------- src/PlayerTracker.js | 22 ++++++++++++++++------ src/packet/SetBorder.js | 15 ++++++--------- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index ad6d080d4..6f947db02 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -113,13 +113,13 @@ PacketHandler.prototype.handleMessage = function(message) { this.protocol = reader.readUInt32(); // Send handshake response this.socket.sendPacket(new Packet.ClearNodes()); - this.socket.sendPacket(new Packet.SetBorder( - this.gameServer.config.borderLeft + this.socket.playerTracker.scrambleX, - this.gameServer.config.borderRight + this.socket.playerTracker.scrambleX, - this.gameServer.config.borderTop + this.socket.playerTracker.scrambleY, - this.gameServer.config.borderBottom + this.socket.playerTracker.scrambleY, - 0, - "MultiOgar 1.0")); + var border = { + left: this.gameServer.config.borderLeft + this.socket.playerTracker.scrambleX, + top: this.gameServer.config.borderTop + this.socket.playerTracker.scrambleY, + right: this.gameServer.config.borderRight + this.socket.playerTracker.scrambleX, + bottom: this.gameServer.config.borderBottom + this.socket.playerTracker.scrambleY + }; + this.socket.sendPacket(new Packet.SetBorder(border, 0, "MultiOgar 1.0")); // Send welcome message this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); } diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index e7550ff64..0cb245448 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -147,12 +147,11 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.spectate = false; this.socket.sendPacket(new Packet.ClearNodes()); - this.socket.sendPacket(new Packet.SetBorder( - this.gameServer.config.borderLeft + this.scrambleX, - this.gameServer.config.borderRight + this.scrambleX, - this.gameServer.config.borderTop + this.scrambleY, - this.gameServer.config.borderBottom + this.scrambleY)); - + this.collidingNodes = []; + this.visibleNodes = []; // Reset visible nodes + this.nodeDestroyQueue = []; // Reset destroy queue + this.nodeAdditionQueue = []; // Reset addition queue + this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); }; @@ -224,6 +223,17 @@ PlayerTracker.prototype.update = function () { updateNodes.push(newVisible[i]); } } + + // Scramble minimap + // TODO: need to send it more rarely (once per second or something like that) + // also need to make sure that we send it before first cell update + //var border = { + // left: Math.max(this.viewBox.leftX - this.viewBox.width / 2, this.gameServer.config.borderLeft) + this.scrambleX, + // top: Math.max(this.viewBox.topY - this.viewBox.height / 2, this.gameServer.config.borderTop) + this.scrambleY, + // right: Math.min(this.viewBox.rightX + this.viewBox.width / 2, this.gameServer.config.borderRight) + this.scrambleX, + // bottom: Math.min(this.viewBox.bottomY + this.viewBox.height / 2, this.gameServer.config.borderBottom) + this.scrambleY + //}; + //this.socket.sendPacket(new Packet.SetBorder(border)); } catch (err) { console.error(err); } diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index 861c32bcf..8164ee253 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -2,11 +2,8 @@ var BinaryWriter = require("./BinaryWriter"); -function SetBorder(left, right, top, bottom, gameType, serverName) { - this.left = left; - this.right = right; - this.top = top; - this.bottom = bottom; +function SetBorder(border, gameType, serverName) { + this.border = border; this.gameType = gameType; this.serverName = serverName; } @@ -16,10 +13,10 @@ module.exports = SetBorder; SetBorder.prototype.build = function(protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x40); // Packet ID - writer.writeDouble(this.left); - writer.writeDouble(this.top); - writer.writeDouble(this.right); - writer.writeDouble(this.bottom); + writer.writeDouble(this.border.left); + writer.writeDouble(this.border.top); + writer.writeDouble(this.border.right); + writer.writeDouble(this.border.bottom); if (this.gameType != null) { writer.writeUInt32(this.gameType >> 0); var name = this.serverName; From e7087fdb3f4ac2c0fe135cf3cda69d6d512ea504 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 04:15:34 +0200 Subject: [PATCH 065/417] collision engine refactoring --- src/GameServer.js | 2 +- src/PlayerTracker.js | 201 ++++++++++++++++------------------------- src/ai/BotPlayer.js | 2 +- src/entity/Cell.js | 20 ++-- src/gamemodes/TeamZ.js | 6 +- 5 files changed, 93 insertions(+), 138 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 5129d58b7..0b16f6c0b 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -920,7 +920,7 @@ GameServer.prototype.getNearestVirus = function(cell) { continue; } - if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { + if (!check.collisionCheck(leftX, topY, rightX, bottomY)) { continue; } diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 0cb245448..e5e57d728 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -34,19 +34,19 @@ function PlayerTracker(gameServer, socket) { this.freeRoam = false; // Free-roam mode enables player to move in spectate mode // Viewing box - this.sightRangeX = 0; - this.sightRangeY = 0; + this.sightWidth = 0; + this.sightHeight = 0; this.centerPos = { // Center of map x: 3000, y: 3000 }; this.viewBox = { - topY: 0, - bottomY: 0, - leftX: 0, - rightX: 0, - width: 0, // Half-width - height: 0 // Half-height + left: 0, + top: 0, + right: 0, + bottom: 0, + halfWidth: 0, + halfHeight: 0 }; // Scramble the coordinate system for anti-raga @@ -66,8 +66,11 @@ function PlayerTracker(gameServer, socket) { gameServer.gameMode.onPlayerInit(this); // Only scramble if enabled in config if (gameServer.config.serverScrambleCoords == 1) { - this.scrambleX = Math.floor((1 << 15) * Math.random()); - this.scrambleY = Math.floor((1 << 15) * Math.random()); + // avoid mouse packet limitations + var maxScrambleX = Math.max(0, 32767 - width / 2); + var maxScrambleY = Math.max(0, 32767 - height / 2); + this.scrambleX = Math.floor(maxScrambleX * Math.random()); + this.scrambleY = Math.floor(maxScrambleY * Math.random()); } } } @@ -204,7 +207,7 @@ PlayerTracker.prototype.update = function () { // Get visible nodes every 400 ms var nonVisibleNodes = []; // Nodes that are not visible if (this.tickViewBox <= 0) { - var newVisible = this.calcViewBox(); + var newVisible = this.getVisibleNodes(); try { // Add a try block in any case // Compare and destroy nodes that are not seen @@ -228,10 +231,10 @@ PlayerTracker.prototype.update = function () { // TODO: need to send it more rarely (once per second or something like that) // also need to make sure that we send it before first cell update //var border = { - // left: Math.max(this.viewBox.leftX - this.viewBox.width / 2, this.gameServer.config.borderLeft) + this.scrambleX, - // top: Math.max(this.viewBox.topY - this.viewBox.height / 2, this.gameServer.config.borderTop) + this.scrambleY, - // right: Math.min(this.viewBox.rightX + this.viewBox.width / 2, this.gameServer.config.borderRight) + this.scrambleX, - // bottom: Math.min(this.viewBox.bottomY + this.viewBox.height / 2, this.gameServer.config.borderBottom) + this.scrambleY + // left: Math.max(this.viewBox.left - this.viewBox.halfWidth, this.gameServer.config.borderLeft) + this.scrambleX, + // top: Math.max(this.viewBox.top - this.viewBox.halfHeight, this.gameServer.config.borderTop) + this.scrambleY, + // right: Math.min(this.viewBox.right + this.viewBox.halfWidth, this.gameServer.config.borderRight) + this.scrambleX, + // bottom: Math.min(this.viewBox.bottom + this.viewBox.halfHeight, this.gameServer.config.borderBottom) + this.scrambleY //}; //this.socket.sendPacket(new Packet.SetBorder(border)); } catch (err) { @@ -263,11 +266,10 @@ PlayerTracker.prototype.update = function () { // Send packet this.socket.sendPacket(new Packet.UpdateNodes( this.nodeDestroyQueue, - updateNodes, - nonVisibleNodes, - this.scrambleX, - this.scrambleY - )); + updateNodes, + nonVisibleNodes, + this.scrambleX, + this.scrambleY)); this.nodeDestroyQueue = []; // Reset destroy queue this.nodeAdditionQueue = []; // Reset addition queue @@ -294,10 +296,7 @@ PlayerTracker.prototype.update = function () { var len = this.cells.length; for (var i = 0; i < len; i++) { var cell = this.socket.playerTracker.cells[0]; - - if (!cell) { - continue; - } + if (cell == null) continue; this.gameServer.removeNode(cell); } @@ -313,13 +312,7 @@ PlayerTracker.prototype.update = function () { // Viewing box -PlayerTracker.prototype.updateSightRange = function() { // For view distance - var scale = this.getScale(); - this.sightRangeX = (this.gameServer.config.serverViewBaseX + 100) / scale; - this.sightRangeY = (this.gameServer.config.serverViewBaseY + 100) / scale; -}; - -PlayerTracker.prototype.updateCenter = function() { // Get center of cells +PlayerTracker.prototype.updateCenterInGame = function() { // Get center of cells var len = this.cells.length; if (len <= 0) return; var cx = 0; @@ -327,95 +320,70 @@ PlayerTracker.prototype.updateCenter = function() { // Get center of cells var count = 0; for (var i = 0; i < len; i++) { var node = this.cells[i]; - if (!node) continue; + if (node == null) continue; cx += node.position.x; cy += node.position.y; count++; } if (count == 0) return; - this.centerPos.x = cx / count; - this.centerPos.y = cy / count; + this.setCenterPos(cx / count, cy / count); }; -PlayerTracker.prototype.calcViewBox = function() { - if (this.spectate) { - // Spectate mode - return this.getSpectateNodes(); - } - - // Main function - this.updateSightRange(); - this.updateCenter(); - - // Box - this.viewBox.topY = this.centerPos.y - this.sightRangeY / 2; - this.viewBox.bottomY = this.centerPos.y + this.sightRangeY / 2; - this.viewBox.leftX = this.centerPos.x - this.sightRangeX / 2; - this.viewBox.rightX = this.centerPos.x + this.sightRangeX / 2; - this.viewBox.width = this.sightRangeX; - this.viewBox.height = this.sightRangeY; - - var newVisible = this.calcVisibleNodes(); - - return newVisible; -}; - -PlayerTracker.prototype.getSpectateNodes = function() { - var specPlayer = this.gameServer.largestClient; - - if (!this.freeRoam) { - - if (!specPlayer) return this.moveInFreeRoam(); // There are probably no players - - // Get spectate player's location and calculate zoom amount - this.scale = specPlayer.getScale(); - this.setCenterPos(specPlayer.centerPos.x, specPlayer.centerPos.y); - this.sendPosPacket(this.scale); - - return specPlayer.visibleNodes.slice(0); - } - // Behave like client is in free-roam as function didn't return nodes - return this.moveInFreeRoam(); -}; - -PlayerTracker.prototype.moveInFreeRoam = function() { - // User is in free roam - // To mimic vanilla, get distance from center to mouse and apply a part of the distance - +PlayerTracker.prototype.updateCenterFreeRoam = function () { var dist = this.gameServer.getDist(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); var angle = this.getAngle(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); - var speed = Math.min(dist / 10, 70); // Not to break laws of universe by going faster than light speed - - this.centerPos.x += speed * Math.sin(angle); - this.centerPos.y += speed * Math.cos(angle); - - // Check if went away from borders - this.checkBorderPass(); + // Not to break laws of universe by going faster than light speed + var speed = Math.min(dist / 10, 70); + var x = this.centerPos.x + speed * Math.sin(angle); + var y = this.centerPos.y + speed * Math.cos(angle); + this.setCenterPos(x, y); +}; - // Now that we've updated center pos, get nearby cells - - this.scale = 0.25; - var baseX = (this.gameServer.config.serverViewBaseX + 100) / this.scale; - var baseY = (this.gameServer.config.serverViewBaseY + 100) / this.scale; - - this.viewBox.leftX = this.centerPos.x - baseX / 2; - this.viewBox.topY = this.centerPos.y - baseY / 2; - this.viewBox.rightX = this.centerPos.x + baseX / 2; - this.viewBox.bottomY = this.centerPos.y + baseY / 2; - this.viewBox.width = baseX; - this.viewBox.height = baseY; - - // Use calcViewBox's way of looking for nodes - var newVisible = this.calcVisibleNodes(); - this.sendPosPacket(this.scale); - return newVisible; +PlayerTracker.prototype.updateViewBox = function () { + var scale = this.getScale(); + this.sightWidth = (this.gameServer.config.serverViewBaseX + 100) / scale; + this.sightHeight = (this.gameServer.config.serverViewBaseY + 100) / scale; + var halfWidth = this.sightWidth / 2; + var halfHeight = this.sightHeight / 2; + this.viewBox = { + left: this.centerPos.x - halfWidth, + top: this.centerPos.y - halfHeight, + right: this.centerPos.x + halfWidth, + bottom: this.centerPos.y + halfHeight, + halfWidth: halfWidth, + halfHeight: halfHeight + }; }; +PlayerTracker.prototype.getVisibleNodes = function () { + if (this.spectate) { + var specPlayer = this.gameServer.largestClient; + if (!this.freeRoam && specPlayer != null) { + // top player spectate + this.setCenterPos(specPlayer.centerPos.x, specPlayer.centerPos.y); + this.scale = specPlayer.getScale(); + this.sendPosPacket(); + this.updateViewBox(); + return specPlayer.visibleNodes.slice(0); + } + // free roam spectate + this.updateCenterFreeRoam(); + this.scale = 0.25; + this.sendPosPacket(); + } else { + // in game + this.updateCenterInGame(); + // scale will be calculated on first call to this.getScale() inside updateViewBox() + } + this.updateViewBox(); + return this.calcVisibleNodes(); +} + PlayerTracker.prototype.calcVisibleNodes = function() { var newVisible = []; for (var i = 0; i < this.gameServer.nodes.length; i++) { var node = this.gameServer.nodes[i]; - if (!node) continue; + if (node == null) continue; var check = node.visibleCheck(this.viewBox, this.centerPos, this.cells); if (check > 0 || node.owner == this) { @@ -436,35 +404,22 @@ PlayerTracker.prototype.setCenterPos = function(x, y) { PlayerTracker.prototype.checkBorderPass = function() { // A check while in free-roam mode to avoid player going into nothingness - if (this.centerPos.x < this.gameServer.config.borderLeft) { + if (this.centerPos.x < this.gameServer.config.borderLeft) this.centerPos.x = this.gameServer.config.borderLeft; - } - if (this.centerPos.x > this.gameServer.config.borderRight) { + if (this.centerPos.x > this.gameServer.config.borderRight) this.centerPos.x = this.gameServer.config.borderRight; - } - if (this.centerPos.y < this.gameServer.config.borderTop) { + if (this.centerPos.y < this.gameServer.config.borderTop) this.centerPos.y = this.gameServer.config.borderTop; - } - if (this.centerPos.y > this.gameServer.config.borderBottom) { + if (this.centerPos.y > this.gameServer.config.borderBottom) this.centerPos.y = this.gameServer.config.borderBottom; - } }; -PlayerTracker.prototype.sendPosPacket = function(specZoom) { +PlayerTracker.prototype.sendPosPacket = function() { // TODO: Send packet elsewhere so it is sent more often this.socket.sendPacket(new Packet.UpdatePosition( this.centerPos.x + this.scrambleX, this.centerPos.y + this.scrambleY, - specZoom - )); -}; - -PlayerTracker.prototype.sendCustomPosPacket = function(x, y, specZoom) { - // TODO: Send packet elsewhere so it is sent more often - this.socket.sendPacket(new Packet.UpdatePosition( - x + this.scrambleX, - y + this.scrambleY, - specZoom + this.getScale() )); }; diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index 12bf5b40c..6fc3d2d92 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -52,7 +52,7 @@ BotPlayer.prototype.update = function() { // Overrides the update function from setTimeout(function() { // Calculate nodes - this.visibleNodes = this.calcViewBox(); + this.visibleNodes = this.getVisibleNodes(); // Calc predators/prey var cell = this.getLowestCell(); diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 0748a1ebf..30f03ec14 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -127,11 +127,11 @@ Cell.prototype.setKiller = function(cell) { // Functions -Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) { - return this.position.x > leftX && - this.position.x < rightX && - this.position.y > topY && - this.position.y < bottomY; +Cell.prototype.collisionCheck = function(left, top, right, bottom) { + return this.position.x > left && + this.position.x < right && + this.position.y > top && + this.position.y < bottom; }; // This collision checking function is based on CIRCLE shape @@ -151,7 +151,7 @@ Cell.prototype.visibleCheck = function (box, centerPos, cells) { var isThere = false; if (this.cellType == 1) { // dot collision detector - isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX); + isThere = this.collisionCheck(box.left, box.top, box.right, box.bottom); } else { // rectangle collision detector var cellSize = this.getSize(); @@ -159,10 +159,10 @@ Cell.prototype.visibleCheck = function (box, centerPos, cells) { var miny = this.position.y - cellSize; var maxx = this.position.x + cellSize; var maxy = this.position.y + cellSize; - var d1x = box.leftX - maxx; - var d1y = box.topY - maxy; - var d2x = minx - box.rightX; - var d2y = miny - box.bottomY; + var d1x = box.left - maxx; + var d1y = box.top - maxy; + var d2x = minx - box.right; + var d2y = miny - box.bottom; isThere = d1x < 0 && d1y < 0 && d2x < 0 && d2y < 0; } if (!isThere) return 0; diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index a25f21b38..9264c71cc 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -353,7 +353,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { if (typeof check === 'undefined') { continue; } - if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { + if (!check.collisionCheck(leftX, topY, rightX, bottomY)) { continue; } virus = check; @@ -368,7 +368,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { if (typeof check === 'undefined') { continue; } - if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { + if (!check.collisionCheck(leftX, topY, rightX, bottomY)) { continue; } virus = check; @@ -388,7 +388,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { continue; } - if (!check.collisionCheck(bottomY, topY, rightX, leftX)) { + if (!check.collisionCheck(leftX, topY, rightX, bottomY)) { continue; } From b115604b9024e2900cc3aba706072d10bb739a7e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 04:52:03 +0200 Subject: [PATCH 066/417] fix mouse issues with protocol 5 and enabled coordinate scrambling --- src/PlayerTracker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index e5e57d728..a5775689d 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -67,8 +67,8 @@ function PlayerTracker(gameServer, socket) { // Only scramble if enabled in config if (gameServer.config.serverScrambleCoords == 1) { // avoid mouse packet limitations - var maxScrambleX = Math.max(0, 32767 - width / 2); - var maxScrambleY = Math.max(0, 32767 - height / 2); + var maxScrambleX = Math.max(0, 32767 - 2000 - width / 2); + var maxScrambleY = Math.max(0, 32767 - 2000 - height / 2); this.scrambleX = Math.floor(maxScrambleX * Math.random()); this.scrambleY = Math.floor(maxScrambleY * Math.random()); } From 4421e95617bfb43cc98dfd83a8de3c9203299249 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 05:32:09 +0200 Subject: [PATCH 067/417] update readme --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6930c8a02..0e8ec1bbd 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,15 @@ The goal is to cleanup the code, fix the bugs and improve physics. ## What's new: -* Player speed - replaced with formula approximated to vanilla physics; -* Recombine time - replaced with formula approximated to vanilla physics; -* Cell collision - rectangle collision and circle collision algorithms were fixed and replaced with more fast; -* View area - fixed and replaced with formula approximated to vanilla physics; -* Mouse control and cell movements - fixed and replaced with physics approximated to vanilla physics; -* Border calculations - fixed +* Player speed - physics rewritten; +* Recombine time - physics rewritten; +* Cell collision - physics rewritten; +* View area - rewritten +* Mouse control and cell movements - physics rewritten; +* Border calculations - rewritten; * Border bouncy physics - fixed and improved * mainLoop - cleaned * added support for different protocols +* added chat support +* added anti-spam protection * color generator replaced with hsv model From bfc2b4388d90324e2204400dd7f29c51f9f51e5b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 05:32:53 +0200 Subject: [PATCH 068/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0e8ec1bbd..6bc7f37dc 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The goal is to cleanup the code, fix the bugs and improve physics. ## What's new: * Player speed - physics rewritten; -* Recombine time - physics rewritten; +* Recombine - physics rewritten; * Cell collision - physics rewritten; * View area - rewritten * Mouse control and cell movements - physics rewritten; From c2ddad0c14457b0e5a9092bb2bc7f571db55a0a6 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 05:43:15 +0200 Subject: [PATCH 069/417] add protocol 4 mouse anti-spoof --- src/PacketHandler.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 6f947db02..476eba439 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -62,6 +62,10 @@ PacketHandler.prototype.handleMessage = function(message) { client.mouse.x = reader.readDouble() - client.scrambleX; client.mouse.y = reader.readDouble() - client.scrambleY; client.movePacketTriggered = true; + if (isNaN(client.mouse.x)) + client.mouse.x = client.centerPos.x; + if (isNaN(client.mouse.y)) + client.mouse.y = client.centerPos.y; } break; case 17: From d4c2f927e50f613fe89b016bc72256241db86b61 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 05:52:55 +0200 Subject: [PATCH 070/417] add protocol validation --- src/PacketHandler.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 476eba439..486622fbc 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -115,6 +115,10 @@ PacketHandler.prototype.handleMessage = function(message) { break; if (message.length == 5) { this.protocol = reader.readUInt32(); + if (this.protocol < 4 || this.protocol > 7) { + this.socket.close(1002, "Not supported protocol"); + break; + } // Send handshake response this.socket.sendPacket(new Packet.ClearNodes()); var border = { From 2cd5ad5bf13a5dc809037aa59cda171ab800c499 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 06:01:08 +0200 Subject: [PATCH 071/417] fix package file --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e2ca1a9ab..618f8f360 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "1.0.0", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", - "homepage": "https://github.com/Barbosik/MultiOgar" - "license": "Apache License, Version 2.0" + "homepage": "https://github.com/Barbosik/MultiOgar", + "license": "Apache License, Version 2.0", "main": "src/index.js", "dependencies": { "simple-quadtree": "^0.1.3", From f711b343d397b9f58ea4f8ac47e471ee0cf9584e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 06:02:31 +0200 Subject: [PATCH 072/417] fix package file --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 618f8f360..ba7679ac8 100644 --- a/package.json +++ b/package.json @@ -21,5 +21,5 @@ }, "bugs": { "url": "https://github.com/Barbosik/MultiOgar/issues" - }, + } } From b564f1c044bbfd0b190ec135bc6c925d5c29139f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 06:25:44 +0200 Subject: [PATCH 073/417] fix BinaryWriter for node.js v6 --- src/packet/BinaryWriter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index dc5edd1f9..045aba79e 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -119,7 +119,7 @@ BinaryWriter.prototype.allocCheck = function (size) { BinaryWriter.prototype.allocBuffer = function (size) { if (Buffer.allocUnsafe == null) // node.js < v6? return new Buffer(size); - return Buffer.allocUnsafe(poolSize); + return Buffer.allocUnsafe(size); } BinaryWriter.prototype.ToBuffer = function () { From d6b278cf7bb71fe1f0c57f987656d1264b0e1d70 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 07:21:19 +0200 Subject: [PATCH 074/417] show protocol in playerlist command --- src/modules/CommandList.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index b6c6dc21f..c9b047381 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -383,8 +383,8 @@ Commands.list = { }, playerlist: function(gameServer, split) { console.log("[Console] Showing " + gameServer.clients.length + " players: "); - console.log(" ID | IP | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space - console.log(fillChar('', '-', ' ID | IP | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); + console.log(" ID | IP | | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space + console.log(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); for (var i = 0; i < gameServer.clients.length; i++) { var client = gameServer.clients[i].playerTracker; @@ -397,6 +397,11 @@ Commands.list = { ip = gameServer.clients[i].remoteAddress; } ip = fillChar(ip, ' ', 15); + var protocol = gameServer.clients[i].packetHandler.protocol; + if (protocol == null) + protocol = "?" + + // Get name and data var nick = '', @@ -413,17 +418,17 @@ Commands.list = { } nick = (nick == "") ? "An unnamed cell" : nick; data = fillChar("SPECTATING: " + nick, '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); - console.log(" " + id + " | " + ip + " | " + data); + console.log(" " + id + " | " + ip + " | " + protocol + " | " + data); } else if (client.cells.length > 0) { nick = fillChar((client.name == "") ? "An unnamed cell" : client.name, ' ', gameServer.config.playerMaxNickLength); cells = fillChar(client.cells.length, ' ', 5, true); score = fillChar(client.getScore() >> 0, ' ', 6, true); position = fillChar(client.centerPos.x >> 0, ' ', 5, true) + ', ' + fillChar(client.centerPos.y >> 0, ' ', 5, true); - console.log(" " + id + " | " + ip + " | " + nick + " | " + cells + " | " + score + " | " + position); + console.log(" " + id + " | " + ip + " | " + protocol + " | " + nick + " | " + cells + " | " + score + " | " + position); } else { // No cells = dead player or in-menu data = fillChar('DEAD OR NOT PLAYING', '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); - console.log(" " + id + " | " + ip + " | " + data); + console.log(" " + id + " | " + ip + " | " + protocol + " | " + data); } } }, From 30ff33b4b57bde4df645242fc2eccc43b674f604 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 07:22:35 +0200 Subject: [PATCH 075/417] add protocol handshake validation --- src/PacketHandler.js | 51 +++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 486622fbc..24f11e31d 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -6,6 +6,7 @@ function PacketHandler(gameServer, socket) { this.socket = socket; // Detect protocol version - we can do something about it later this.protocol = 0; + this.isHandshakePassed = false; this.lastChatTick = 0; this.pressQ = false; @@ -26,6 +27,32 @@ PacketHandler.prototype.handleMessage = function(message) { } var reader = new BinaryReader(message); var packetId = reader.readUInt8(); + + // no handshake? + if (!this.isHandshakePassed) { + if (packetId != 254 || message.length != 5) + return; // wait handshake + + // Handshake request + this.protocol = reader.readUInt32(); + if (this.protocol < 4 || this.protocol > 7) { + this.socket.close(1002, "Not supported protocol"); + return; + } + // Send handshake response + this.socket.sendPacket(new Packet.ClearNodes()); + var border = { + left: this.gameServer.config.borderLeft + this.socket.playerTracker.scrambleX, + top: this.gameServer.config.borderTop + this.socket.playerTracker.scrambleY, + right: this.gameServer.config.borderRight + this.socket.playerTracker.scrambleX, + bottom: this.gameServer.config.borderBottom + this.socket.playerTracker.scrambleY + }; + this.socket.sendPacket(new Packet.SetBorder(border, 0, "MultiOgar 1.0")); + // Send welcome message + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); + this.isHandshakePassed = true; + return; + } switch (packetId) { case 0: @@ -108,30 +135,6 @@ PacketHandler.prototype.handleMessage = function(message) { text = reader.readStringZeroUtf8(); this.gameServer.onChatMessage(this.socket.playerTracker, null, text); break; - - case 254: - // Handshake request - if (this.protocol != 0) // second handshake request is not allowed - break; - if (message.length == 5) { - this.protocol = reader.readUInt32(); - if (this.protocol < 4 || this.protocol > 7) { - this.socket.close(1002, "Not supported protocol"); - break; - } - // Send handshake response - this.socket.sendPacket(new Packet.ClearNodes()); - var border = { - left: this.gameServer.config.borderLeft + this.socket.playerTracker.scrambleX, - top: this.gameServer.config.borderTop + this.socket.playerTracker.scrambleY, - right: this.gameServer.config.borderRight + this.socket.playerTracker.scrambleX, - bottom: this.gameServer.config.borderBottom + this.socket.playerTracker.scrambleY - }; - this.socket.sendPacket(new Packet.SetBorder(border, 0, "MultiOgar 1.0")); - // Send welcome message - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); - } - break; default: break; } From a3eb9dcac430ff939c602a63095ba704bbe547a0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 7 Jun 2016 22:18:02 +0200 Subject: [PATCH 076/417] prepare to protocol 8 --- src/PacketHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 24f11e31d..2f1b48cc0 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -35,7 +35,7 @@ PacketHandler.prototype.handleMessage = function(message) { // Handshake request this.protocol = reader.readUInt32(); - if (this.protocol < 4 || this.protocol > 7) { + if (this.protocol < 4 || this.protocol > 8) { this.socket.close(1002, "Not supported protocol"); return; } From 26f83ca9a6bd0c38ab3434bc0e39aea087533804 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 01:17:09 +0200 Subject: [PATCH 077/417] fix protocol 8 support --- src/packet/UpdateNodes.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index a6c78dcf8..7a3b015d1 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -14,13 +14,10 @@ module.exports = UpdateNodes; UpdateNodes.prototype.build = function (protocol) { if (!protocol) return null; - switch (protocol) { - case 4: return this.build4(); - case 5: return this.build5(); - case 6: - case 7: return this.build6(); - } - return null; + + if (protocol <= 4) return this.build4(); + else if (protocol == 5) return this.build5(); + else return this.build6(); } // protocol 4 From 227f85402dc7e0f0cf33b07a5eb96021a268cc61 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 03:34:23 +0200 Subject: [PATCH 078/417] update readme --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 6bc7f37dc..719520f15 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,25 @@ Original Ogar found [here](https://github.com/OgarProject/Ogar) The goal is to cleanup the code, fix the bugs and improve physics. +## Clients + +Ogar clients and server trackers, that I found on internet + +Ogar server trackers + +URL | Description +--- | --- +http://ogar.mivabe.nl/master | MivaBe, tracks a lot of servers + + +Ogar clients + +URL | Protocol | Description +--- | --- | --- +http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, custom graphics +http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla clone (sends invalid protocol=1) + + ## What's new: * Player speed - physics rewritten; From ad4ae9f20be846e7fc302a158e45a6a8dc1ee158 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 03:37:16 +0200 Subject: [PATCH 079/417] update readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 719520f15..2c7650db4 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,19 @@ The goal is to cleanup the code, fix the bugs and improve physics. Ogar clients and server trackers, that I found on internet -Ogar server trackers +#Ogar server trackers URL | Description --- | --- http://ogar.mivabe.nl/master | MivaBe, tracks a lot of servers -Ogar clients +#Ogar clients +Just replace ip:port in the url to play URL | Protocol | Description --- | --- | --- -http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, custom graphics +http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, custom graphics (anime) http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla clone (sends invalid protocol=1) From 21ca9559139d0f81814d74ff85144ae93a921813 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 03:38:21 +0200 Subject: [PATCH 080/417] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2c7650db4..7e66137d0 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,14 @@ The goal is to cleanup the code, fix the bugs and improve physics. Ogar clients and server trackers, that I found on internet -#Ogar server trackers +###Ogar server trackers URL | Description --- | --- http://ogar.mivabe.nl/master | MivaBe, tracks a lot of servers -#Ogar clients +###Ogar clients Just replace ip:port in the url to play URL | Protocol | Description From c50f2b516347606da6c2b547077afa91c530466e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 04:11:23 +0200 Subject: [PATCH 081/417] update readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7e66137d0..6ddb8fc8b 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,9 @@ Just replace ip:port in the url to play URL | Protocol | Description --- | --- | --- -http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, custom graphics (anime) -http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla clone (sends invalid protocol=1) +http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, pretty smooth, custom graphics (anime) +http://agariogame.club/?ip=127.0.0.1:50000 | 5 | a little laggy, vanilla style +http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends invalid protocol=1) From e66f9a2a6448ef032beee3d5cb9649ef8bcf7a6f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 04:33:13 +0200 Subject: [PATCH 082/417] update readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6ddb8fc8b..74d1fd83c 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,6 @@ Just replace ip:port in the url to play URL | Protocol | Description --- | --- | --- http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, pretty smooth, custom graphics (anime) -http://agariogame.club/?ip=127.0.0.1:50000 | 5 | a little laggy, vanilla style http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends invalid protocol=1) From 5cee10a00e4bc5aa5969e634881cecf7b0a3cc53 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 04:53:58 +0200 Subject: [PATCH 083/417] add ogarul support (it sends invalid version) --- src/PacketHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 2f1b48cc0..411a975fb 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -35,7 +35,7 @@ PacketHandler.prototype.handleMessage = function(message) { // Handshake request this.protocol = reader.readUInt32(); - if (this.protocol < 4 || this.protocol > 8) { + if (this.protocol < 1 || this.protocol > 8) { this.socket.close(1002, "Not supported protocol"); return; } From 2cc7205f4c273695544c759c6492886f36252292 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 04:56:41 +0200 Subject: [PATCH 084/417] comment --- src/PlayerTracker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index a5775689d..82a8ba727 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -115,7 +115,7 @@ PlayerTracker.prototype.updateMass = function () { totalSize += node.getSize(); } if (totalSize == 0) { - //this.scale = 1; + //do not change scale for spectators or not in game players this.score = 0; } else { this.score = totalSize * totalSize / 100; From dce8e2f6b5918a44609a009c01f5c272d8f287fb Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 05:30:21 +0200 Subject: [PATCH 085/417] spawn collision optimization --- src/GameServer.js | 24 ++++++------------------ src/entity/Cell.js | 7 +++++++ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 0b16f6c0b..03d94aeb1 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -257,15 +257,16 @@ GameServer.prototype.getRandomPosition = function() { GameServer.prototype.getRandomSpawn = function(mass) { // Random and secure spawns for players and viruses + var size = Math.sqrt(mass * 100); var pos = this.getRandomPosition(); - var unsafe = this.willCollide(mass, pos, mass == this.config.virusStartMass); + var unsafe = this.willCollide(size, pos, mass == this.config.virusStartMass); var attempt = 1; // Prevent stack overflow by counting attempts while (true) { if (!unsafe || attempt >= 15) break; pos = this.getRandomPosition(); - unsafe = this.willCollide(mass, pos, mass == this.config.virusStartMass); + unsafe = this.willCollide(size, pos, mass == this.config.virusStartMass); attempt++; } @@ -495,22 +496,15 @@ GameServer.prototype.virusCheck = function() { } }; -GameServer.prototype.willCollide = function(mass, pos, isVirus) { +GameServer.prototype.willCollide = function(size, pos, isVirus) { // Look if there will be any collision with the current nodes - var size = Math.sqrt(mass * 100) >> 0; for (var i = 0; i < this.nodesPlayer.length; i++) { var check = this.nodesPlayer[i]; if (!check) continue; - // Eating range - var xs = check.position.x - pos.x, - ys = check.position.y - pos.y, - sqDist = xs * xs + ys * ys, - dist = Math.sqrt(sqDist); - if (check.getSize() > size) { // Check only if the player cell is larger than imaginary cell - if (dist + size <= check.getSize()) return true; // Collided + if (check.collisionCheckCircle(pos.x, pos.y, size+50)) return true; // Collided } } @@ -520,14 +514,8 @@ GameServer.prototype.willCollide = function(mass, pos, isVirus) { var check = this.nodesVirus[i]; if (!check) continue; - // Eating range - var xs = check.position.x - pos.x, - ys = check.position.y - pos.y, - sqDist = xs * xs + ys * ys, - dist = Math.sqrt(sqDist); - if (check.getSize() > size) { // Check only if the virus cell is larger than imaginary cell - if (dist + size <= check.getSize()) return true; // Collided + if (check.collisionCheckCircle(pos.x, pos.y, size + 50)) return true; // Collided } } return false; diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 30f03ec14..3c4a51b8e 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -146,6 +146,13 @@ Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) { return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize); }; +Cell.prototype.collisionCheckCircle = function(x, y, size) { + var dx = this.position.x - x; + var dy = this.position.y - y; + var r = this.getSize() + size; + return dx * dx + dy * dy < r * r; +}; + Cell.prototype.visibleCheck = function (box, centerPos, cells) { // Checks if this cell is visible to the player var isThere = false; From 9855d8bcd4c72e64566314658c413da8df9e0ae9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 05:54:09 +0200 Subject: [PATCH 086/417] fix merge command --- src/GameServer.js | 2 +- src/entity/PlayerCell.js | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 03d94aeb1..52a73134e 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -932,7 +932,7 @@ GameServer.prototype.updateCells = function() { if (!cell) continue; // Recombining - if (cell.owner.cells.length == 1 && cell.recombineTicks > 0) { + if (cell.owner.cells.length == 1) { cell.owner.mergeOverride = false; cell.owner.mergeOverrideDuration = 0; } diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index d670a403f..548c8befa 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -25,9 +25,13 @@ PlayerCell.prototype.updateRemerge = function (gameServer) { this._canRemerge = false; return; } + if (this.owner.mergeOverride) { // force merge from console + this._canRemerge = true; + return; + } var tick = gameServer.getTick(); var age = this.getAgeTicks(tick); - if (age < 3 || this.owner.mergeOverride) { + if (age < 3) { // do not remerge if cell age is smaller than 3 ticks this._canRemerge = false; return; From bc40386df15dad3159622299ef116a9c0accb1fd Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 07:11:17 +0200 Subject: [PATCH 087/417] fix Score is inconsistant #17 --- src/GameServer.js | 1 + src/PlayerTracker.js | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index 52a73134e..12d1658f9 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -944,6 +944,7 @@ GameServer.prototype.updateCells = function() { } // Mass decay + // TODO: needs to be updated rarely if (cell.getMass() >= this.config.playerMinMassDecay) { cell.setMass(cell.getMass() * massDecay); } diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 82a8ba727..fc9399d03 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -109,16 +109,18 @@ PlayerTracker.prototype.getScale = function () { PlayerTracker.prototype.updateMass = function () { var totalSize = 0; + var totalMass = 0; for (var i = 0; i < this.cells.length; i++) { var node = this.cells[i]; if (node == null) continue; totalSize += node.getSize(); + totalMass += node.getMass(); } if (totalSize == 0) { //do not change scale for spectators or not in game players this.score = 0; } else { - this.score = totalSize * totalSize / 100; + this.score = totalMass; this.scale = Math.pow(Math.min(64 / totalSize, 1), 0.4); } this.isMassChanged = false; From ded13d0df986a4a6879f65ab64edb17e039f613a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 8 Jun 2016 23:34:40 +0200 Subject: [PATCH 088/417] fix bouncy physics bug (border) --- src/entity/Cell.js | 70 +++++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 3c4a51b8e..325778565 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -233,47 +233,67 @@ Cell.prototype.calcMovePhys = function(config) { } } - //// Border check - Bouncy physics + // Border check - Bouncy physics var radius = 40; if (x < config.borderLeft && this.position.x != x) { // Flip angle horizontally - Left side this.angle = 6.28 - this.angle; - var p = this.getLineIntersection( - this.position.x, this.position.y, x, y, - config.borderLeft, config.borderBottom, - config.borderLeft, config.borderTop); - x = p.x; - y = p.y; + if (x == this.position.x && y == this.position.y) { + // movement vector is missing + x = config.borderLeft; + } else { + var p = this.getLineIntersection( + this.position.x, this.position.y, x, y, + config.borderLeft, config.borderBottom, + config.borderLeft, config.borderTop); + x = p.x; + y = p.y; + } } if (x > config.borderRight && this.position.y != x) { // Flip angle horizontally - Right side this.angle = 6.28 - this.angle; - var p = this.getLineIntersection( - this.position.x, this.position.y, x, y, - config.borderRight, config.borderBottom, - config.borderRight, config.borderTop); - x = p.x; - y = p.y; + if (x == this.position.x && y == this.position.y) { + // movement vector is missing + x = config.borderRight; + } else { + var p = this.getLineIntersection( + this.position.x, this.position.y, x, y, + config.borderRight, config.borderBottom, + config.borderRight, config.borderTop); + x = p.x; + y = p.y; + } } if (y < config.borderTop && this.position.y != y) { // Flip angle vertically - Top side this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - var p = this.getLineIntersection( - this.position.x, this.position.y, x, y, - config.borderRight, config.borderTop, - config.borderLeft, config.borderTop); - x = p.x; - y = p.y; + if (x == this.position.x && y == this.position.y) { + // movement vector is missing + y = config.borderTop; + } else { + var p = this.getLineIntersection( + this.position.x, this.position.y, x, y, + config.borderRight, config.borderTop, + config.borderLeft, config.borderTop); + x = p.x; + y = p.y; + } } if (y > config.borderBottom && this.position.y != y) { // Flip angle vertically - Bottom side this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; - var p = this.getLineIntersection( - this.position.x, this.position.y, x, y, - config.borderRight, config.borderBottom, - config.borderLeft, config.borderBottom); - x = p.x; - y = p.y; + if (x == this.position.x && y == this.position.y) { + // movement vector is missing + y = config.borderBottom; + } else { + var p = this.getLineIntersection( + this.position.x, this.position.y, x, y, + config.borderRight, config.borderBottom, + config.borderLeft, config.borderBottom); + x = p.x; + y = p.y; + } } // Set position From b99917859840a1dd88ac0e9ba41268ef443835a1 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 00:12:42 +0200 Subject: [PATCH 089/417] replace body collision physics code; fix body impulse; collision resolver optimizations --- src/GameServer.js | 78 ++++++++++++++++++++++------------------ src/entity/PlayerCell.js | 11 +++--- src/gamemodes/Teams.js | 9 ++--- 3 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 12d1658f9..79eddffe9 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -532,43 +532,51 @@ GameServer.prototype.abs = function(x) { // Because Math.abs is slow }; GameServer.prototype.checkCellCollision = function(cell, check) { - // Returns object which contains info about cell's collisions. You can use this in the future. - - // Check the two cells for collision - var collisionDist = cell.getSize() + check.getSize(); // Minimum distance between the two cells - var dist = this.getDist(cell.position.x, cell.position.y, - check.position.x, check.position.y); // Distance between these two cells - - var dY = cell.position.y - check.position.y; - var dX = cell.position.x - check.position.x; - var angle = Math.atan2(dX, dY); - - return ({ - cellDist: dist, - collideDist: collisionDist, - cellMult: (Math.sqrt(check.getSize() * 100) / Math.sqrt(cell.getSize() * 100)) / 3, - cellAngle: angle, - collided: (dist < collisionDist) - }); + // Returns manifold which contains info about cell's collisions. + // Returns null if there is no collision + var r = cell.getSize() + check.getSize(); + var dx = check.position.x - cell.position.x; + var dy = check.position.y - cell.position.y; + var squared = dx * dx + dy * dy; // squared distance from cell to check + if (squared >= r * r) { + // no collision + return null; + } + // create collision manifold + return { + cell1: cell, + cell2: check, + r: r, // radius sum + dx: dx, // delta x from cell1 to cell2 + dy: dy, // delta y from cell1 to cell2 + squared: squared // squared distance from cell1 to cell2 + }; }; -GameServer.prototype.cellCollision = function(cell, check, calcInfo) { - if (!calcInfo) calcInfo = this.checkCellCollision(cell, check); // Unedefined calc info - - // Check collision - if (calcInfo.collided) { // Collided - // The moving cell pushes the colliding cell - - var dist = calcInfo.cellDist; - var collisionDist = calcInfo.collideDist; - var mult = calcInfo.cellMult; - var angle = calcInfo.cellAngle; - - var move = (collisionDist - dist) * mult; - - cell.position.x += move * Math.sin(angle); - cell.position.y += move * Math.cos(angle); - } +GameServer.prototype.resolveCollision = function (manifold) { + // distance from cell1 to cell2 + var d = Math.sqrt(manifold.squared); + if (d <= 0) return; + + // body penetration distance + var penetration = manifold.r - d; + if (penetration <= 0) return; + + // penetration vector = penetration distance * normalized vector + var px = penetration * manifold.dx / d; + var py = penetration * manifold.dy / d; + + // body impulse + var totalMass = manifold.cell1.getMass() + manifold.cell2.getMass(); + if (totalMass <= 0) return; + var impulse1 = manifold.cell2.getMass() / totalMass; + var impulse2 = manifold.cell1.getMass() / totalMass; + + // apply extrusion force + manifold.cell1.position.x -= px * impulse1; + manifold.cell1.position.y -= py * impulse1; + manifold.cell2.position.x += px * impulse2; + manifold.cell2.position.y += py * impulse2; }; GameServer.prototype.updateMoveEngine = function() { diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 548c8befa..a325a85c6 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -87,21 +87,22 @@ PlayerCell.prototype.collision = function(gameServer) { if (!cell.canRemerge() || !this.canRemerge()) { // Cannot remerge - Collision with your own cells - var calcInfo = gameServer.checkCellCollision(this, cell); // Calculation info + var manifold = gameServer.checkCellCollision(this, cell); // Calculation info // Further calculations - if (calcInfo.collided) { // Collided + if (manifold != null) { // Collided // Cell with collision restore ticks on should not collide - if (this.collisionRestoreTicks > 0 || cell.collisionRestoreTicks > 0) continue; + if (this.collisionRestoreTicks > 0 || cell.collisionRestoreTicks > 0) + continue; // Call gameserver's function to collide cells - gameServer.cellCollision(this, cell, calcInfo); + gameServer.resolveCollision(manifold); } } } gameServer.gameMode.onCellMove(this, gameServer); - + // Check to ensure we're not passing the world border (shouldn't get closer than a quarter of the cell's diameter) if (this.position.x < config.borderLeft + r / 2) { this.position.x = config.borderLeft + r / 2; diff --git a/src/gamemodes/Teams.js b/src/gamemodes/Teams.js index f122e6d03..f58cadb1c 100644 --- a/src/gamemodes/Teams.js +++ b/src/gamemodes/Teams.js @@ -108,15 +108,16 @@ Teams.prototype.onCellMove = function(cell, gameServer) { // Collision with teammates if (check.owner.getTeam() == team) { - var calcInfo = gameServer.checkCellCollision(cell, check); // Calculation info + var manifold = gameServer.checkCellCollision(cell, check); // Calculation info // Further calculations - if (calcInfo.collided) { // Collided + if (manifold != null) { // Collided // Cell with collision restore ticks on should not collide - if (cell.collisionRestoreTicks > 0 || check.collisionRestoreTicks > 0) continue; + if (cell.collisionRestoreTicks > 0 || check.collisionRestoreTicks > 0) + continue; // Call gameserver's function to collide cells - gameServer.cellCollision(cell, check, calcInfo); + gameServer.resolveCollision(manifold); } } } From 1583ca8b8e100857e1659cac1285fdc47c3aa59e Mon Sep 17 00:00:00 2001 From: F0RIS Date: Thu, 9 Jun 2016 01:45:49 +0300 Subject: [PATCH 090/417] Fix disaplying of playierlist command --- src/modules/CommandList.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index c9b047381..d973a0454 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -383,13 +383,13 @@ Commands.list = { }, playerlist: function(gameServer, split) { console.log("[Console] Showing " + gameServer.clients.length + " players: "); - console.log(" ID | IP | | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space - console.log(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); + console.log(" ID | IP | P | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space + console.log(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); for (var i = 0; i < gameServer.clients.length; i++) { var client = gameServer.clients[i].playerTracker; // ID with 3 digits length - var id = fillChar((client.pID), ' ', 10, true); + var id = fillChar((client.pID), ' ', 6, true); // Get ip (15 digits length) var ip = "BOT"; From 2329bca4011d587795d3124d5a97b5c166cbca7b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 00:51:08 +0200 Subject: [PATCH 091/417] fix bouncy physics bug (border) --- src/entity/Cell.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 325778565..aab198213 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -250,7 +250,7 @@ Cell.prototype.calcMovePhys = function(config) { y = p.y; } } - if (x > config.borderRight && this.position.y != x) { + if (x > config.borderRight && this.position.x != x) { // Flip angle horizontally - Right side this.angle = 6.28 - this.angle; if (x == this.position.x && y == this.position.y) { From 5adaf8622363869cfadea6f38ff5ee2e1e63d4db Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 02:01:37 +0200 Subject: [PATCH 092/417] code refactoring --- src/GameServer.js | 29 ++++++++++++++++++++++++----- src/entity/PlayerCell.js | 16 ---------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 79eddffe9..dd2060e8a 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -441,6 +441,25 @@ GameServer.prototype.mainLoop = function() { this.updateClients(); this.updateLeaderboard(); + //var t = process.hrtime(); + //this.updateMoveEngine(); + //this.t1 = process.hrtime(t); + //t = process.hrtime(); + //this.updateSpawn(); + //this.t2 = process.hrtime(t); + //t = process.hrtime(); + //this.gameMode.onTick(this); + //this.t3 = process.hrtime(t); + //t = process.hrtime(); + //this.updateCells(); + //this.t4 = process.hrtime(t); + //t = process.hrtime(); + //this.updateClients(); + //this.t5 = process.hrtime(t); + //t = process.hrtime(); + //this.updateLeaderboard(); + //this.t6 = process.hrtime(t); + this.tickCounter++; }; @@ -521,13 +540,13 @@ GameServer.prototype.willCollide = function(size, pos, isVirus) { return false; }; -GameServer.prototype.getDist = function(x1, y1, x2, y2) { // Use Pythagoras theorem - var deltaX = this.abs(x1 - x2); - var deltaY = this.abs(y1 - y2); - return Math.sqrt(deltaX * deltaX + deltaY * deltaY); +GameServer.prototype.getDist = function (x1, y1, x2, y2) { + var dx = x2 - x1; + var dy = y2 - x1; + return Math.sqrt(dx * dx + dy * dy); }; -GameServer.prototype.abs = function(x) { // Because Math.abs is slow +GameServer.prototype.abs = function (x) { return x < 0 ? -x : x; }; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index a325a85c6..bbec5d247 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -158,19 +158,3 @@ PlayerCell.prototype.onRemove = function(gameServer) { PlayerCell.prototype.moveDone = function(gameServer) { // Well, nothing. }; - -// Lib - -PlayerCell.prototype.abs = function(x) { - return x < 0 ? -x : x; -}; - -PlayerCell.prototype.getDist = function(x1, y1, x2, y2) { - var xs = x2 - x1; - xs = xs * xs; - - var ys = y2 - y1; - ys = ys * ys; - - return Math.sqrt(xs + ys); -}; From e6d55c3b9121947736bb46d7547284fb5bd81ffe Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 02:32:55 +0200 Subject: [PATCH 093/417] code refactoring and optimization --- src/entity/PlayerCell.js | 41 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index bbec5d247..46233a0a1 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -49,28 +49,31 @@ PlayerCell.prototype.canRemerge = function () { // Movement -PlayerCell.prototype.calcMove = function(x2, y2, gameServer) { - if (!this.owner.shouldMoveCells && this.owner.notMoved) return; // Mouse is in one place - - // Get angle of mouse - var deltaY = y2 - this.position.y; - var deltaX = x2 - this.position.x; - var angle = Math.atan2(deltaX, deltaY); - - if (isNaN(angle)) { +PlayerCell.prototype.calcMove = function(x, y, gameServer) { + // No mouse update + if (!this.owner.shouldMoveCells && this.owner.notMoved) return; - } - var dist = Math.sqrt(deltaX*deltaX + deltaY*deltaY); - dist = Math.min(dist, 32); - var speed = 0; - if (dist >= 1) { - speed = this.getSpeed() * dist / 32; + var dx = x - this.position.x; + var dy = y - this.position.y; + var squared = dx * dx + dy * dy; + + if (squared >= 1) { // stop threshold + // distance + var d = Math.sqrt(squared); + // normalized distance (0..1) + d = Math.min(d, 32) / 32; + + var speed = this.getSpeed() * d; + if (speed > 0) { + var angle = Math.atan2(dx, dy); + if (isNaN(angle)) return; + // Move cell + this.position.x += speed * Math.sin(angle); + this.position.y += speed * Math.cos(angle); + } } - - // Move cell - this.position.x += Math.sin(angle) * speed; - this.position.y += Math.cos(angle) * speed; + this.owner.notMoved = false; }; From 6d4612f4c4ebd873ab89ff4b751710056aedcec0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 03:11:10 +0200 Subject: [PATCH 094/417] code refactoring and optimizations --- src/PlayerTracker.js | 23 +++++++++++++++++------ src/entity/PlayerCell.js | 34 ++++++++++++++++------------------ 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index fc9399d03..d21f10a9b 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -332,13 +332,24 @@ PlayerTracker.prototype.updateCenterInGame = function() { // Get center of cells }; PlayerTracker.prototype.updateCenterFreeRoam = function () { - var dist = this.gameServer.getDist(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); - var angle = this.getAngle(this.mouse.x, this.mouse.y, this.centerPos.x, this.centerPos.y); + var dx = this.mouse.x - this.centerPos.x; + var dy = this.mouse.y - this.centerPos.x; + var squared = dx * dx + dy * dy; + if (squared < 1) return; // stop threshold + + // distance + var d = Math.sqrt(squared); + // Not to break laws of universe by going faster than light speed - var speed = Math.min(dist / 10, 70); - var x = this.centerPos.x + speed * Math.sin(angle); - var y = this.centerPos.y + speed * Math.cos(angle); - this.setCenterPos(x, y); + var speed = Math.min(d / 10, 70); + if (speed <= 0) return; + + var angle = Math.atan2(dx, dy); + if (isNaN(angle)) return; + + this.setCenterPos( + this.centerPos.x + speed * Math.sin(angle), + this.centerPos.y + speed * Math.cos(angle)); }; PlayerTracker.prototype.updateViewBox = function () { diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 46233a0a1..1c324f68d 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -49,32 +49,30 @@ PlayerCell.prototype.canRemerge = function () { // Movement -PlayerCell.prototype.calcMove = function(x, y, gameServer) { +PlayerCell.prototype.calcMove = function (x, y, gameServer) { // No mouse update if (!this.owner.shouldMoveCells && this.owner.notMoved) return; - + this.owner.notMoved = false; + var dx = x - this.position.x; var dy = y - this.position.y; var squared = dx * dx + dy * dy; + if (squared < 1) return; // stop threshold - if (squared >= 1) { // stop threshold - // distance - var d = Math.sqrt(squared); - // normalized distance (0..1) - d = Math.min(d, 32) / 32; - - var speed = this.getSpeed() * d; - if (speed > 0) { - var angle = Math.atan2(dx, dy); - if (isNaN(angle)) return; - // Move cell - this.position.x += speed * Math.sin(angle); - this.position.y += speed * Math.cos(angle); - } - } + // distance + var d = Math.sqrt(squared); + // normalized distance (0..1) + d = Math.min(d, 32) / 32; - this.owner.notMoved = false; + var speed = this.getSpeed() * d; + if (speed <= 0) return; + + var angle = Math.atan2(dx, dy); + if (isNaN(angle)) return; + // Move cell + this.position.x += speed * Math.sin(angle); + this.position.y += speed * Math.cos(angle); }; PlayerCell.prototype.collision = function(gameServer) { From d244bdcd304c1b2f9c3b84f590e91ec405c168ff Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 03:46:50 +0200 Subject: [PATCH 095/417] fix free roaming spectate --- src/PlayerTracker.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index d21f10a9b..d3db9d5e3 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -333,15 +333,14 @@ PlayerTracker.prototype.updateCenterInGame = function() { // Get center of cells PlayerTracker.prototype.updateCenterFreeRoam = function () { var dx = this.mouse.x - this.centerPos.x; - var dy = this.mouse.y - this.centerPos.x; + var dy = this.mouse.y - this.centerPos.y; var squared = dx * dx + dy * dy; if (squared < 1) return; // stop threshold // distance var d = Math.sqrt(squared); - // Not to break laws of universe by going faster than light speed - var speed = Math.min(d / 10, 70); + var speed = Math.min(d, 32); if (speed <= 0) return; var angle = Math.atan2(dx, dy); From 28f40ccf73dc2167fc26587926be5450d50faf42 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 04:48:27 +0200 Subject: [PATCH 096/417] code refactoring and optimizations --- src/GameServer.js | 87 +++++++---------- src/gamemodes/FFA.js | 219 ++++++++++++++++++++++--------------------- 2 files changed, 146 insertions(+), 160 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index dd2060e8a..a406ef8e9 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -171,10 +171,7 @@ GameServer.prototype.start = function() { var len = this.socket.playerTracker.cells.length; for (var i = 0; i < len; i++) { var cell = this.socket.playerTracker.cells[i]; - - if (!cell) { - continue; - } + if (cell == null) continue; cell.calcMove = function() { return; @@ -331,9 +328,7 @@ GameServer.prototype.addNode = function(node) { // Add to visible nodes for (var i = 0; i < this.clients.length; i++) { var client = this.clients[i].playerTracker; - if (!client) { - continue; - } + if (client == null) continue; // client.nodeAdditionQueue is only used by human players, not bots // for bots it just gets collected forever, using ever-increasing amounts of memory @@ -362,9 +357,7 @@ GameServer.prototype.removeNode = function(node) { // Animation when eating for (var i = 0; i < this.clients.length; i++) { var client = this.clients[i].playerTracker; - if (!client) { - continue; - } + if (client == null) continue; // Remove from client client.nodeDestroyQueue.push(node); @@ -384,10 +377,9 @@ GameServer.prototype.updateSpawn = function() { GameServer.prototype.updateClients = function () { for (var i = 0; i < this.clients.length; i++) { - if (typeof this.clients[i] == "undefined") { - continue; - } - this.clients[i].playerTracker.update(); + var client = this.clients[i]; + if (client == null) continue; + client.playerTracker.update(); } }; @@ -410,7 +402,9 @@ GameServer.prototype.updateLeaderboard = function () { this.largestClient = null; if (clients[0] != null) this.largestClient = clients[0].playerTracker; - } else this.largestClient = this.gameMode.rankOne; + } else { + this.largestClient = this.gameMode.rankOne; + } } }; @@ -520,7 +514,7 @@ GameServer.prototype.willCollide = function(size, pos, isVirus) { for (var i = 0; i < this.nodesPlayer.length; i++) { var check = this.nodesPlayer[i]; - if (!check) continue; + if (check == null) continue; if (check.getSize() > size) { // Check only if the player cell is larger than imaginary cell if (check.collisionCheckCircle(pos.x, pos.y, size+50)) return true; // Collided @@ -531,7 +525,7 @@ GameServer.prototype.willCollide = function(size, pos, isVirus) { for (var i = 0; i < this.nodesVirus.length; i++) { var check = this.nodesVirus[i]; - if (!check) continue; + if (check == null) continue; if (check.getSize() > size) { // Check only if the virus cell is larger than imaginary cell if (check.collisionCheckCircle(pos.x, pos.y, size + 50)) return true; // Collided @@ -600,14 +594,16 @@ GameServer.prototype.resolveCollision = function (manifold) { GameServer.prototype.updateMoveEngine = function() { // Move player cells - var len = this.nodesPlayer.length; - for (var i in this.clients) { var client = this.clients[i].playerTracker; // Sort client's cells by ascending mass var sorted = []; - for (var i = 0; i < client.cells.length; i++) sorted.push(client.cells[i]); + for (var i = 0; i < client.cells.length; i++) { + var node = client.cells[i]; + if (node == null) continue; + sorted.push(node); + } sorted.sort(function(a, b) { return b.getMass() - a.getMass(); @@ -617,11 +613,6 @@ GameServer.prototype.updateMoveEngine = function() { for (var i = 0; i < sorted.length; i++) { var cell = sorted[i]; - // Do not move cells that have already been eaten - if (!cell) { - continue; - } - // First move the cell cell.calcMovePhys(this.config); @@ -638,20 +629,18 @@ GameServer.prototype.updateMoveEngine = function() { // A system to move cells not controlled by players (ex. viruses, ejected mass) - len = this.movingNodes.length; - for (var i = 0; i < len; i++) { + for (var i = 0; i < this.movingNodes.length; i++) { var check = this.movingNodes[i]; // Recycle unused nodes - while ((typeof check == "undefined") && (i < this.movingNodes.length)) { + while (check == null && i < this.movingNodes.length) { // Remove moving cells that are undefined this.movingNodes.splice(i, 1); check = this.movingNodes[i]; } - if (i >= this.movingNodes.length) { + if (i >= this.movingNodes.length) continue; - } if (check.moveEngineTicks > 0) { check.onAutoMove(this); @@ -685,7 +674,8 @@ GameServer.prototype.cellEating = function(cell) { }; GameServer.prototype.setAsMovingNode = function(node) { - if (this.movingNodes.indexOf(node) == -1) this.movingNodes.push(node); + if (this.movingNodes.indexOf(node) == -1) + this.movingNodes.push(node); }; GameServer.prototype.splitCells = function(client) { @@ -699,7 +689,8 @@ GameServer.prototype.splitCells = function(client) { var angle = Math.atan2(deltaX, deltaY); if (angle == 0) angle = Math.PI / 2; - if (this.createPlayerCell(client, cell, angle, cell.getMass() / 2) == true) splitCells++; + if (this.createPlayerCell(client, cell, angle, cell.getMass() / 2) == true) + splitCells++; } }; @@ -742,11 +733,12 @@ GameServer.prototype.createPlayerCell = function(client, parent, angle, mass) { }; GameServer.prototype.canEjectMass = function(client) { - if (typeof client.lastEject == 'undefined' || this.tickCounter - client.lastEject >= this.config.ejectMassCooldown) { + if (client.lastEject == null || this.tickCounter - client.lastEject >= this.config.ejectMassCooldown) { client.lastEject = this.tickCounter; return true; - } else + } else { return false; + } }; GameServer.prototype.ejectMass = function(client) { @@ -818,25 +810,19 @@ GameServer.prototype.getCellsInRange = function(cell) { var len = cell.owner.collidingNodes.length; for (var i = 0; i < len; i++) { var check = cell.owner.collidingNodes[i]; - - if (typeof check === 'undefined') { - continue; - } + if (check === null) continue; // if something already collided with this cell, don't check for other collisions - if (check.inRange) { + if (check.inRange) continue; - } // Can't eat itself - if (cell.nodeId == check.nodeId) { + if (cell.nodeId == check.nodeId) continue; - } // Can't eat cells that have collision turned off - if ((cell.owner == check.owner) && (cell.collisionRestoreTicks != 0) && (check.cellType == 0)) { + if (cell.owner == check.owner && cell.collisionRestoreTicks != 0 && check.cellType == 0) continue; - } // Eating range var xs = cell.position.x - check.position.x, @@ -847,7 +833,10 @@ GameServer.prototype.getCellsInRange = function(cell) { // Use a more reliant version for pellets // Might be a bit slower but it can be eaten with any mass if (check.cellType == 1) { - if (dist + check.getSize() / 3.14 > cell.getSize()) continue; // Too far away + if (dist + check.getSize() / 3.14 > cell.getSize()) { + // Too far away + continue; + } else { // Add to list of cells nearby list.push(check); @@ -930,14 +919,10 @@ GameServer.prototype.getNearestVirus = function(cell) { var len = this.nodesVirus.length; for (var i = 0; i < len; i++) { var check = this.nodesVirus[i]; + if (check === null) continue; - if (typeof check === 'undefined') { + if (!check.collisionCheck(leftX, topY, rightX, bottomY)) continue; - } - - if (!check.collisionCheck(leftX, topY, rightX, bottomY)) { - continue; - } // Add to list of cells nearby virus = check; diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index 8a6dc38d0..1a55a862e 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -1,110 +1,111 @@ -var Mode = require('./Mode'); - -function FFA() { - Mode.apply(this, Array.prototype.slice.call(arguments)); - - this.ID = 0; - this.name = "Free For All"; - this.specByLeaderboard = true; -} - -module.exports = FFA; -FFA.prototype = new Mode(); - -// Gamemode Specific Functions - -FFA.prototype.leaderboardAddSort = function(player, leaderboard) { - // Adds the player and sorts the leaderboard - var len = leaderboard.length - 1; - var loop = true; - while ((len >= 0) && (loop)) { - // Start from the bottom of the leaderboard - if (player.getScore() <= leaderboard[len].getScore()) { - leaderboard.splice(len + 1, 0, player); - loop = false; // End the loop if a spot is found - } - len--; - } - if (loop) { - // Add to top of the list because no spots were found - leaderboard.splice(0, 0, player); - } -}; - -// Override - -FFA.prototype.onPlayerSpawn = function(gameServer, player) { - // Random color - player.color = gameServer.getRandomColor(); - - // Set up variables - var pos, startMass; - - // Check if there are ejected mass in the world. - if (gameServer.nodesEjected.length > 0) { - var index = Math.floor(Math.random() * 100) + 1; - if (index >= gameServer.config.ejectSpawnPlayer) { - // Get ejected cell - index = Math.floor(Math.random() * gameServer.nodesEjected.length); - var e = gameServer.nodesEjected[index]; - if (e.moveEngineTicks > 0) { - // Ejected cell is currently moving - gameServer.spawnPlayer(player, pos, startMass); - } - - // Remove ejected mass - gameServer.removeNode(e); - - // Inherit - pos = { - x: e.position.x, - y: e.position.y - }; - startMass = Math.max(e.getMass(), gameServer.config.playerStartMass); - - var color = e.getColor(); - player.setColor({ - 'r': color.r, - 'g': color.g, - 'b': color.b - }); - } - } - - // Spawn player - gameServer.spawnPlayer(player, pos, startMass); -}; - -FFA.prototype.updateLB = function(gameServer) { +var Mode = require('./Mode'); + +function FFA() { + Mode.apply(this, Array.prototype.slice.call(arguments)); + + this.ID = 0; + this.name = "Free For All"; + this.specByLeaderboard = true; +} + +module.exports = FFA; +FFA.prototype = new Mode(); + +// Gamemode Specific Functions + +FFA.prototype.leaderboardAddSort = function(player, leaderboard) { + // Adds the player and sorts the leaderboard + var len = leaderboard.length - 1; + var loop = true; + while ((len >= 0) && (loop)) { + // Start from the bottom of the leaderboard + if (player.getScore() <= leaderboard[len].getScore()) { + leaderboard.splice(len + 1, 0, player); + loop = false; // End the loop if a spot is found + } + len--; + } + if (loop) { + // Add to top of the list because no spots were found + leaderboard.splice(0, 0, player); + } +}; + +// Override + +FFA.prototype.onPlayerSpawn = function(gameServer, player) { + // Random color + player.color = gameServer.getRandomColor(); + + // Set up variables + var pos, startMass; + + // Check if there are ejected mass in the world. + if (gameServer.nodesEjected.length > 0) { + var index = Math.floor(Math.random() * 100) + 1; + if (index >= gameServer.config.ejectSpawnPlayer) { + // Get ejected cell + index = Math.floor(Math.random() * gameServer.nodesEjected.length); + var e = gameServer.nodesEjected[index]; + if (e.moveEngineTicks > 0) { + // Ejected cell is currently moving + gameServer.spawnPlayer(player, pos, startMass); + } + + // Remove ejected mass + gameServer.removeNode(e); + + // Inherit + pos = { + x: e.position.x, + y: e.position.y + }; + startMass = Math.max(e.getMass(), gameServer.config.playerStartMass); + + var color = e.getColor(); + player.setColor({ + 'r': color.r, + 'g': color.g, + 'b': color.b + }); + } + } + + // Spawn player + gameServer.spawnPlayer(player, pos, startMass); +}; + +FFA.prototype.updateLB = function(gameServer) { gameServer.leaderboardType = this.packetLB; - var lb = gameServer.leaderboard; - // Loop through all clients - for (var i = 0; i < gameServer.clients.length; i++) { - if (typeof gameServer.clients[i] == "undefined") { - continue; - } - - var player = gameServer.clients[i].playerTracker; - if (player.disconnect > -1) continue; // Don't add disconnected players to list - var playerScore = player.getScore(); - if (player.cells.length <= 0) { - continue; - } - - if (lb.length == 0) { - // Initial player - lb.push(player); - continue; - } else if (lb.length < gameServer.config.serverMaxLB) { - this.leaderboardAddSort(player, lb); - } else { - // 10 in leaderboard already - if (playerScore > lb[gameServer.config.serverMaxLB - 1].getScore()) { - lb.pop(); - this.leaderboardAddSort(player, lb); - } - } - } - - this.rankOne = lb[0]; -} + var lb = gameServer.leaderboard; + // Loop through all clients + for (var i = 0; i < gameServer.clients.length; i++) { + var client = gameServer.clients[i]; + if (client == null) continue; + + var player = client.playerTracker; + if (player.disconnect > -1) + continue; // Don't add disconnected players to list + + var playerScore = player.getScore(); + + if (player.cells.length <= 0) + continue; + + if (lb.length == 0) { + // Initial player + lb.push(player); + continue; + } else if (lb.length < gameServer.config.serverMaxLB) { + this.leaderboardAddSort(player, lb); + } else { + // 10 in leaderboard already + if (playerScore > lb[gameServer.config.serverMaxLB - 1].getScore()) { + lb.pop(); + this.leaderboardAddSort(player, lb); + } + } + } + + this.rankOne = lb[0]; +} From 5b20ffff6cab213d1dd1fbdf8704197d33c72b9a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 10:11:03 +0200 Subject: [PATCH 097/417] remove quadtree dependency --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index ba7679ac8..c16f32583 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,6 @@ "license": "Apache License, Version 2.0", "main": "src/index.js", "dependencies": { - "simple-quadtree": "^0.1.3", "vector2-node": "latest", "ws": "latest" }, From c4a33a213f2b79efce51f8301d5229ce8c124096 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 10:41:31 +0200 Subject: [PATCH 098/417] fix buffer allocation --- src/packet/BinaryWriter.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index 045aba79e..c8c6884b9 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -99,18 +99,19 @@ BinaryWriter.prototype.writeStringZeroUnicode = function (value) { BinaryWriter.prototype.allocCheck = function (size) { + var pageSize = Math.max(256, (Buffer.poolSize / 4) >> 0); if (this._buffer == null) { - var allocSize = size + Buffer.poolSize - (size % Buffer.poolSize); + var allocSize = size + pageSize - (size % pageSize); this._buffer = this.allocBuffer(allocSize); } var needed = this._offset + size; if (needed <= this._buffer.length) return; var addSize = needed - this._buffer.length; - if (addSize < Buffer.poolSize) { - addSize = Buffer.poolSize; + if (addSize < pageSize) { + addSize = pageSize; } else { - addSize += Buffer.poolSize - (addSize % Buffer.poolSize); + addSize += pageSize - (addSize % pageSize); } var buffer2 = this.allocBuffer(addSize); this._buffer = Buffer.concat([this._buffer, buffer2], this._buffer.length + buffer2.length); From c5e725d8a58770b0838c4ac2a52fe2ceec00b2b2 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 22:23:56 +0200 Subject: [PATCH 099/417] code refactoring and optimizations --- src/GameServer.js | 2 +- src/PlayerTracker.js | 21 ++++++++++++++++--- src/entity/Cell.js | 49 +++++++++++++++----------------------------- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index a406ef8e9..c411de00a 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -332,7 +332,7 @@ GameServer.prototype.addNode = function(node) { // client.nodeAdditionQueue is only used by human players, not bots // for bots it just gets collected forever, using ever-increasing amounts of memory - if ('_socket' in client.socket && node.visibleCheck(client.viewBox, client.centerPos, client.cells)) { + if ('_socket' in client.socket && node.visibleCheck(client.viewBox)) { client.nodeAdditionQueue.push(node); } } diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index d3db9d5e3..f9985c1a8 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -397,12 +397,27 @@ PlayerTracker.prototype.calcVisibleNodes = function() { var node = this.gameServer.nodes[i]; if (node == null) continue; - var check = node.visibleCheck(this.viewBox, this.centerPos, this.cells); - if (check > 0 || node.owner == this) { + if (node.owner == this || node.visibleCheck(this.viewBox)) { // Cell is in range of viewBox newVisible.push(node); + + // TODO: rewrite that! // Check if it's colliding with one of player's cells - if (check == 2) this.collidingNodes.push(node); + // To save perfomance, check if any client's cell collides with this cell + for (var j = 0; j < this.cells.length; j++) { + var cell = this.cells[j]; + if (cell == null || cell == node) + continue; + + // circle collision detector + var dx = cell.position.x - node.position.x; + var dy = cell.position.y - node.position.y; + var r = cell.getSize() + node.getSize(); + if (dx * dx + dy * dy < r * r) { + // circle collision detected + this.collidingNodes.push(node); + } + } } } return newVisible; diff --git a/src/entity/Cell.js b/src/entity/Cell.js index aab198213..1e1020ec8 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -153,43 +153,26 @@ Cell.prototype.collisionCheckCircle = function(x, y, size) { return dx * dx + dy * dy < r * r; }; -Cell.prototype.visibleCheck = function (box, centerPos, cells) { +Cell.prototype.visibleCheck = function (box) { // Checks if this cell is visible to the player - var isThere = false; if (this.cellType == 1) { // dot collision detector - isThere = this.collisionCheck(box.left, box.top, box.right, box.bottom); - } else { - // rectangle collision detector - var cellSize = this.getSize(); - var minx = this.position.x - cellSize; - var miny = this.position.y - cellSize; - var maxx = this.position.x + cellSize; - var maxy = this.position.y + cellSize; - var d1x = box.left - maxx; - var d1y = box.top - maxy; - var d2x = minx - box.right; - var d2y = miny - box.bottom; - isThere = d1x < 0 && d1y < 0 && d2x < 0 && d2y < 0; - } - if (!isThere) return 0; - - // To save perfomance, check if any client's cell collides with this cell - for (var i = 0; i < cells.length; i++) { - var cell = cells[i]; - if (!cell) continue; - - // circle collision detector - var dx = cell.position.x - this.position.x; - var dy = cell.position.y - this.position.y; - var r = cell.getSize() + this.getSize(); - if (dx * dx + dy * dy < r * r) { - // circle collision detected - return 2; - } + return this.position.x >= box.left && + this.position.x <= box.right && + this.position.y >= box.top && + this.position.y <= box.bottom; } - // Not colliding with any - return 1; + // rectangle collision detector + var cellSize = this.getSize(); + var minx = this.position.x - cellSize; + var miny = this.position.y - cellSize; + var maxx = this.position.x + cellSize; + var maxy = this.position.y + cellSize; + var d1x = box.left - maxx; + var d1y = box.top - maxy; + var d2x = minx - box.right; + var d2y = miny - box.bottom; + return d1x < 0 && d1y < 0 && d2x < 0 && d2y < 0; }; Cell.prototype.calcMovePhys = function(config) { From a2cc9d0c8b46f7e999f818645670a9bab3eea08f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 9 Jun 2016 23:24:33 +0200 Subject: [PATCH 100/417] code refactoring and optimizations --- src/GameServer.js | 3 +-- src/PlayerTracker.js | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index c411de00a..cf8f9571a 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -807,8 +807,7 @@ GameServer.prototype.getCellsInRange = function(cell) { var squareR = cell.getSquareSize(); // Get cell squared radius // Loop through all cells that are colliding with the player's cells - var len = cell.owner.collidingNodes.length; - for (var i = 0; i < len; i++) { + for (var i = 0; i < cell.owner.collidingNodes.length; i++) { var check = cell.owner.collidingNodes[i]; if (check === null) continue; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index f9985c1a8..e3b5fa3cf 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -392,6 +392,7 @@ PlayerTracker.prototype.getVisibleNodes = function () { } PlayerTracker.prototype.calcVisibleNodes = function() { + this.collidingNodes = []; var newVisible = []; for (var i = 0; i < this.gameServer.nodes.length; i++) { var node = this.gameServer.nodes[i]; From 6fbf932e775d76dc79d11dfefccf519b54e9b377 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 10 Jun 2016 00:17:56 +0200 Subject: [PATCH 101/417] BinaryWriter optimizations --- src/packet/BinaryWriter.js | 131 ++++++++++++++++++------------------- 1 file changed, 62 insertions(+), 69 deletions(-) diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index c8c6884b9..a5be611f8 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -6,85 +6,101 @@ * License: Apache License, Version 2.0 */ function BinaryWriter() { - this._offset = 0; - this._buffer = null; + this._writers = []; + this._length = 0; } module.exports = BinaryWriter; BinaryWriter.prototype.writeUInt8 = function (value) { - this.allocCheck(1); - this._buffer.writeUInt8(value, this._offset); - this._offset += 1; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.writeUInt8(value, offset); + }); + this._length += 1; }; BinaryWriter.prototype.writeInt8 = function (value) { - this.allocCheck(1); - this._buffer.writeInt8(value, this._offset); - this._offset += 1; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.writeInt8(value, offset); + }); + this._length += 1; }; BinaryWriter.prototype.writeUInt16 = function (value) { - this.allocCheck(2); - this._buffer.writeUInt16LE(value, this._offset); - this._offset += 2; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.writeUInt16LE(value, offset); + }); + this._length += 2; }; BinaryWriter.prototype.writeInt16 = function (value) { - this.allocCheck(2); - this._buffer.writeInt16LE(value, this._offset); - this._offset += 2; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.writeInt16LE(value, offset); + }); + this._length += 2; }; BinaryWriter.prototype.writeUInt32 = function (value) { - this.allocCheck(4); - this._buffer.writeUInt32LE(value, this._offset); - this._offset += 4; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.writeUInt32LE(value, offset); + }); + this._length += 4; }; BinaryWriter.prototype.writeInt32 = function (value) { - this.allocCheck(4); - this._buffer.writeInt32LE(value, this._offset); - this._offset += 4; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.writeInt32LE(value, offset); + }); + this._length += 4; }; BinaryWriter.prototype.writeFloat = function (value) { - this.allocCheck(4); - this._buffer.writeFloatLE(value, this._offset); - this._offset += 4; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.writeFloatLE(value, offset); + }); + this._length += 4; }; BinaryWriter.prototype.writeDouble = function (value) { - this.allocCheck(8); - this._buffer.writeDoubleLE(value, this._offset); - this._offset += 8; -}; - -BinaryWriter.prototype.writeDouble = function (value) { - this.allocCheck(8); - this._buffer.writeDoubleLE(value, this._offset); - this._offset += 8; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.writeDoubleLE(value, offset); + }); + this._length += 8; }; BinaryWriter.prototype.writeBytes = function (data) { var length = data.length; - this.allocCheck(length); - data.copy(this._buffer, this._offset, 0, length); - this._offset += length; + var offset = this._length; + this._writers.push(function (buffer) { + data.copy(buffer, offset, 0, length); + }); + this._length += length; }; BinaryWriter.prototype.writeStringUtf8 = function (value) { var length = Buffer.byteLength(value, 'utf8') - this.allocCheck(length); - this._buffer.write(value, this._offset, 'utf8'); - this._offset += length; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.write(value, offset, 'utf8'); + }); + this._length += length; }; BinaryWriter.prototype.writeStringUnicode = function (value) { var length = Buffer.byteLength(value, 'ucs2') - this.allocCheck(length); - this._buffer.write(value, this._offset, 'ucs2'); - this._offset += length; + var offset = this._length; + this._writers.push(function (buffer) { + buffer.write(value, offset, 'ucs2'); + }); + this._length += length; }; BinaryWriter.prototype.writeStringZeroUtf8 = function (value) { @@ -97,33 +113,10 @@ BinaryWriter.prototype.writeStringZeroUnicode = function (value) { this.writeUInt16(0); }; - -BinaryWriter.prototype.allocCheck = function (size) { - var pageSize = Math.max(256, (Buffer.poolSize / 4) >> 0); - if (this._buffer == null) { - var allocSize = size + pageSize - (size % pageSize); - this._buffer = this.allocBuffer(allocSize); - } - var needed = this._offset + size; - if (needed <= this._buffer.length) - return; - var addSize = needed - this._buffer.length; - if (addSize < pageSize) { - addSize = pageSize; - } else { - addSize += pageSize - (addSize % pageSize); - } - var buffer2 = this.allocBuffer(addSize); - this._buffer = Buffer.concat([this._buffer, buffer2], this._buffer.length + buffer2.length); -} - -BinaryWriter.prototype.allocBuffer = function (size) { - if (Buffer.allocUnsafe == null) // node.js < v6? - return new Buffer(size); - return Buffer.allocUnsafe(size); -} - BinaryWriter.prototype.ToBuffer = function () { - if (this._buffer == null) return new Buffer(0); - return this._buffer.slice(0, this._offset); + var buffer = new Buffer(this._length); + for (var i = 0; i < this._writers.length; i++) { + this._writers[i](buffer); + } + return buffer; }; From ee2d18fbd244085ad758ba404b4451ce8182fa38 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 00:07:33 +0200 Subject: [PATCH 102/417] replace split/eject physics with new one --- src/GameServer.js | 115 ++++++------- src/entity/Cell.js | 94 ++++------- src/entity/Food.js | 2 +- src/entity/PlayerCell.js | 9 +- src/entity/Virus.js | 3 +- src/gamemodes/Experimental.js | 14 +- src/gamemodes/FFA.js | 2 +- src/gamemodes/TeamX.js | 12 +- src/gamemodes/TeamZ.js | 28 +-- src/gamemodes/Teams.js | 309 +++++++++++++++++----------------- 10 files changed, 281 insertions(+), 307 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index cf8f9571a..a3dfcb209 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -547,6 +547,14 @@ GameServer.prototype.abs = function (x) { GameServer.prototype.checkCellCollision = function(cell, check) { // Returns manifold which contains info about cell's collisions. // Returns null if there is no collision + + // do not affect splitted cell for 1 sec + if (cell.boostDistance > 0 || check.boostDistance > 0) { + var tick = this.getTick(); + if (cell.getAgeTicks(tick) < 25 || check.getAgeTicks(tick) < 25) + return null; + } + var r = cell.getSize() + check.getSize(); var dx = check.position.x - cell.position.x; var dy = check.position.y - cell.position.y; @@ -571,13 +579,17 @@ GameServer.prototype.resolveCollision = function (manifold) { var d = Math.sqrt(manifold.squared); if (d <= 0) return; + // normal + var nx = manifold.dx / d; + var ny = manifold.dy / d; + // body penetration distance var penetration = manifold.r - d; if (penetration <= 0) return; - // penetration vector = penetration distance * normalized vector - var px = penetration * manifold.dx / d; - var py = penetration * manifold.dy / d; + // penetration vector = penetration * normal + var px = penetration * nx; + var py = penetration * ny; // body impulse var totalMass = manifold.cell1.getMass() + manifold.cell2.getMass(); @@ -608,17 +620,17 @@ GameServer.prototype.updateMoveEngine = function() { sorted.sort(function(a, b) { return b.getMass() - a.getMass(); }); - + // Go cell by cell + for (var i = 0; i < sorted.length; i++) { + sorted[i].calcMove(client.mouse.x, client.mouse.y, this); + } + for (var i = 0; i < sorted.length; i++) { + sorted[i].calcMoveBoost(this.config); + } for (var i = 0; i < sorted.length; i++) { var cell = sorted[i]; - - // First move the cell - cell.calcMovePhys(this.config); - - // Now move it to the mouse - cell.calcMove(client.mouse.x, client.mouse.y, this); - + // Collision with own cells cell.collision(this); @@ -642,10 +654,10 @@ GameServer.prototype.updateMoveEngine = function() { if (i >= this.movingNodes.length) continue; - if (check.moveEngineTicks > 0) { + if (check.boostDistance > 0) { check.onAutoMove(this); // If the cell has enough move ticks, then move it - check.calcMovePhys(this.config); + check.calcMoveBoost(this.config); } else { // Auto move is done check.moveDone(this); @@ -684,10 +696,11 @@ GameServer.prototype.splitCells = function(client) { for (var i = 0; i < len; i++) { var cell = client.cells[i]; - var deltaY = client.mouse.y - cell.position.y; - var deltaX = client.mouse.x - cell.position.x; - var angle = Math.atan2(deltaX, deltaY); - if (angle == 0) angle = Math.PI / 2; + var dx = client.mouse.x - cell.position.x; + var dy = client.mouse.y - cell.position.y; + var angle = Math.atan2(dx, dy); + //if (angle == 0) angle = Math.PI / 2; + if (isNaN(angle)) angle = 0; if (this.createPlayerCell(client, cell, angle, cell.getMass() / 2) == true) splitCells++; @@ -706,27 +719,20 @@ GameServer.prototype.createPlayerCell = function(client, parent, angle, mass) { // Minimum mass to split return false; } - - // Calculate customized speed for splitting cells - var t = Math.PI * Math.PI; - var modifier = 3 + Math.log(1 + mass) / (10 + Math.log(1 + mass)); - var splitSpeed = this.config.playerSpeed * 30 * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); - - // Calculate new position - var newPos = { - x: parent.position.x, - y: parent.position.y + + // make a small shift to the cell position to prevent extrusion in wrong direction + var pos = { + x: parent.position.x + 40 * Math.sin(angle), + y: parent.position.y + 40 * Math.cos(angle) }; - - // Create cell - var newCell = new Entity.PlayerCell(this.getNextNodeId(), client, newPos, mass, this); - newCell.setAngle(angle); - newCell.setMoveEngineData(splitSpeed, 12, 0.88); - // Cells won't collide immediately - newCell.collisionRestoreTicks = 12; - parent.collisionRestoreTicks = 12; + parent.setMass(parent.getMass() - mass); // Remove mass from parent cell - + + // Create cell + var newCell = new Entity.PlayerCell(this.getNextNodeId(), client, pos, mass, this); + newCell.ejector = parent; + newCell.setBoost(780, angle); + // Add to node list this.addNode(newCell); return true; @@ -755,31 +761,28 @@ GameServer.prototype.ejectMass = function(client) { continue; } - var deltaY = client.mouse.y - cell.position.y; - var deltaX = client.mouse.x - cell.position.x; - var angle = Math.atan2(deltaX, deltaY); - + var dx = client.mouse.x - cell.position.x; + var dy = client.mouse.y - cell.position.y; + var angle = Math.atan2(dx, dy); + if (isNaN(angle)) angle = 0; + // Randomize angle angle += (Math.random() * 0.1) - 0.05; - + // Get starting position - var size = cell.getSize() + 0.2; var startPos = { - x: cell.position.x + ((size + this.config.ejectMass) * Math.sin(angle)), - y: cell.position.y + ((size + this.config.ejectMass) * Math.cos(angle)) + x: cell.position.x + cell.getSize() * Math.sin(angle), + y: cell.position.y + cell.getSize() * Math.cos(angle) }; - + // Remove mass from parent cell cell.setMass(cell.getMass() - this.config.ejectMassLoss); - // Randomize angle - angle += (Math.random() * 0.6) - 0.3; - // Create cell var ejected = new Entity.EjectedMass(this.getNextNodeId(), null, startPos, this.config.ejectMass, this); - ejected.setAngle(angle); - ejected.setMoveEngineData(this.config.ejectSpeed, 20, 0.88); ejected.setColor(cell.getColor()); + ejected.setBoost(780, angle); + ejected.ejector = cell; this.nodesEjected.push(ejected); this.addNode(ejected); @@ -794,8 +797,7 @@ GameServer.prototype.shootVirus = function(parent) { }; var newVirus = new Entity.Virus(this.getNextNodeId(), null, parentPos, this.config.virusStartMass, this); - newVirus.setAngle(parent.getAngle()); - newVirus.setMoveEngineData(135, 20, 0.85); + newVirus.setBoost(780, parent.getAngle()); // Add to moving cells list this.addNode(newVirus); @@ -819,8 +821,8 @@ GameServer.prototype.getCellsInRange = function(cell) { if (cell.nodeId == check.nodeId) continue; - // Can't eat cells that have collision turned off - if (cell.owner == check.owner && cell.collisionRestoreTicks != 0 && check.cellType == 0) + // Can't eat self ejected mass, because it still in boost mode + if (check.ejector == cell && check.boostDistance > 0) continue; // Eating range @@ -949,11 +951,6 @@ GameServer.prototype.updateCells = function() { } cell.updateRemerge(this); - // Collision - if (cell.collisionRestoreTicks > 0) { - cell.collisionRestoreTicks--; - } - // Mass decay // TODO: needs to be updated rarely if (cell.getMass() >= this.config.playerMinMassDecay) { diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 1e1020ec8..1f30517e3 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -19,10 +19,10 @@ function Cell(nodeId, owner, position, mass, gameServer) { this.killedBy; // Cell that ate this cell this.gameServer = gameServer; - this.moveEngineTicks = 0; // Amount of times to loop the movement function - this.moveEngineSpeed = 0; - this.moveDecay = 0.85; - this.angle = 0; // Angle of movement + this.boostDistance = 0; + this.boostDirection = { x: 1, y: 0, angle: 0 }; + this.ejector = null; + this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells } @@ -94,12 +94,16 @@ Cell.prototype.getSpeed = function() { return speed * 40 * this.gameServer.config.playerSpeed; }; -Cell.prototype.setAngle = function(radians) { - this.angle = radians; +Cell.prototype.setAngle = function(angle) { + this.boostDirection = { + x: Math.sin(angle), + y: Math.cos(angle), + angle: angle + }; }; Cell.prototype.getAngle = function() { - return this.angle; + return this.boostDirection.angle; }; Cell.prototype.getAgeTicks = function (tick) { @@ -107,12 +111,6 @@ Cell.prototype.getAgeTicks = function (tick) { return Math.max(0, tick - this.tickOfBirth); } -Cell.prototype.setMoveEngineData = function(speed, ticks, decay) { - this.moveEngineSpeed = speed; - this.moveEngineTicks = ticks; - this.moveDecay = isNaN(decay) ? 0.75 : decay; -}; - Cell.prototype.getEatingRange = function() { return 0; // 0 for ejected cells }; @@ -175,52 +173,30 @@ Cell.prototype.visibleCheck = function (box) { return d1x < 0 && d1y < 0 && d2x < 0 && d2y < 0; }; -Cell.prototype.calcMovePhys = function(config) { - // Move, twice as slower - var x = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle) >> 0); - var y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle) >> 0); - - // Movement engine - if (this.moveEngineSpeed <= this.moveDecay * 3 && this.cellType == 0) - this.moveEngineSpeed = 0; - var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay; - this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower - if (this.moveEngineTicks >= 0.5) - this.moveEngineTicks -= 0.5; // Ticks passing twice as slower - - // Ejected cell collision - if (this.cellType == 3) { - for (var i = 0; i < this.gameServer.nodesEjected.length; i++) { - var check = this.gameServer.nodesEjected[i]; - - if (check.nodeId == this.nodeId) continue; // Don't check for yourself - - var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y); - var allowDist = this.getSize() + check.getSize(); // Allow cells to get in themselves a bit - - if (dist < allowDist) { - // Two ejected cells collided - var deltaX = this.position.x - check.position.x; - var deltaY = this.position.y - check.position.y; - var angle = Math.atan2(deltaX, deltaY); - - this.gameServer.setAsMovingNode(check); - check.moveEngineTicks += 1; - this.moveEngineTicks += 1; - - var move = allowDist - dist; - - x += Math.sin(angle) * move / 2; - y += Math.cos(angle) * move / 2; - } - } - } +Cell.prototype.setBoost = function (distance, angle) { + if (isNaN(angle)) angle = 0; + + this.boostDistance = distance; + this.setAngle(angle); +}; + +Cell.prototype.calcMoveBoost = function (config) { + if (this.boostDistance <= 0) return; + + //var maxSpeed = 40 * (2.1106 / Math.pow(32, 0.449)); + //var speed = Math.sqrt(this.boostDistance * this.boostDistance / this.getSize()) * 0.8; + var speed = Math.sqrt(this.boostDistance * this.boostDistance / (this.getSize()*2) ); + speed = Math.min(speed, this.boostDistance); + this.boostDistance -= speed; + if (this.boostDistance <= 1) this.boostDistance = 0; + var x = this.position.x + this.boostDirection.x * speed; + var y = this.position.y + this.boostDirection.y * speed; // Border check - Bouncy physics var radius = 40; if (x < config.borderLeft && this.position.x != x) { // Flip angle horizontally - Left side - this.angle = 6.28 - this.angle; + this.setAngle(6.28 - this.getAngle()); if (x == this.position.x && y == this.position.y) { // movement vector is missing x = config.borderLeft; @@ -235,7 +211,7 @@ Cell.prototype.calcMovePhys = function(config) { } if (x > config.borderRight && this.position.x != x) { // Flip angle horizontally - Right side - this.angle = 6.28 - this.angle; + this.setAngle(6.28 - this.getAngle()); if (x == this.position.x && y == this.position.y) { // movement vector is missing x = config.borderRight; @@ -250,7 +226,7 @@ Cell.prototype.calcMovePhys = function(config) { } if (y < config.borderTop && this.position.y != y) { // Flip angle vertically - Top side - this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; + this.setAngle((this.getAngle() <= 3.14) ? 3.14 - this.getAngle() : 9.42 - this.getAngle()); if (x == this.position.x && y == this.position.y) { // movement vector is missing y = config.borderTop; @@ -265,7 +241,7 @@ Cell.prototype.calcMovePhys = function(config) { } if (y > config.borderBottom && this.position.y != y) { // Flip angle vertically - Bottom side - this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle; + this.setAngle((this.getAngle() <= 3.14) ? 3.14 - this.getAngle() : 9.42 - this.getAngle()); if (x == this.position.x && y == this.position.y) { // movement vector is missing y = config.borderBottom; @@ -278,11 +254,9 @@ Cell.prototype.calcMovePhys = function(config) { y = p.y; } } - - // Set position this.position.x = x; this.position.y = y; -}; +} // Override these diff --git a/src/entity/Food.js b/src/entity/Food.js index 7fbc9cfcc..a04e33dec 100644 --- a/src/entity/Food.js +++ b/src/entity/Food.js @@ -32,7 +32,7 @@ Food.prototype.grow = function() { Food.prototype.sendUpdate = function() { // Whether or not to include this cell in the update packet - if (this.moveEngineTicks == 0) { + if (this.boostDistance <= 0) { return false; } if (this.shouldSendUpdate) { diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 1c324f68d..d1c92ab78 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -83,19 +83,14 @@ PlayerCell.prototype.collision = function(gameServer) { for (var i = 0; i < this.owner.cells.length; i++) { var cell = this.owner.cells[i]; - if (!cell) continue; // Error + if (cell == null) continue; // Error if (this.nodeId == cell.nodeId) continue; if (!cell.canRemerge() || !this.canRemerge()) { // Cannot remerge - Collision with your own cells + var manifold = gameServer.checkCellCollision(this, cell); // Calculation info - - // Further calculations if (manifold != null) { // Collided - // Cell with collision restore ticks on should not collide - if (this.collisionRestoreTicks > 0 || cell.collisionRestoreTicks > 0) - continue; - // Call gameserver's function to collide cells gameServer.resolveCollision(manifold); } diff --git a/src/entity/Virus.js b/src/entity/Virus.js index ab9ab7c45..27853c76d 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -15,7 +15,8 @@ Virus.prototype = new Cell(); Virus.prototype.calcMove = null; // Only for player controlled movement Virus.prototype.feed = function(feeder, gameServer) { - if (this.moveEngineTicks == 0) this.setAngle(feeder.getAngle()); // Set direction if the virus explodes + if (this.boostDistance <= 0) + this.setAngle(feeder.getAngle()); // Set direction if the virus explodes this.setMass(this.getMass() + feeder.getMass()); this.fed++; // Increase feed count gameServer.removeNode(feeder); diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 443df25ed..255abe159 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -99,10 +99,12 @@ Experimental.prototype.onServerInit = function(gameServer) { Virus.prototype.feed = function(feeder, gameServer) { gameServer.removeNode(feeder); // Pushes the virus - this.setAngle(feeder.getAngle()); // Set direction if the virus explodes - this.moveEngineTicks += 20; // Amount of times to loop the movement function - this.moveEngineSpeed += 16; - this.moveDecay = 0.875; + // TODO: check distance + this.setBoost(16 * 20, feeder.getAngle()); + //this.setAngle(feeder.getAngle()); // Set direction if the virus explodes + //this.moveEngineTicks += 20; // Amount of times to loop the movement function + //this.moveEngineSpeed += 16; + //this.moveDecay = 0.875; var index = gameServer.movingNodes.indexOf(this); if (index == -1) { @@ -244,9 +246,9 @@ MotherCell.prototype.spawnFood = function(gameServer) { gameServer.currentFood++; // Move engine - f.angle = angle; var dist = (Math.random() * 8) + 8; // Random distance - f.setMoveEngineData(dist, 20, 0.85); + // TODO: check distance + f.setBoost(dist, angle); gameServer.setAsMovingNode(f); }; diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index 1a55a862e..f14c2089f 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -47,7 +47,7 @@ FFA.prototype.onPlayerSpawn = function(gameServer, player) { // Get ejected cell index = Math.floor(Math.random() * gameServer.nodesEjected.length); var e = gameServer.nodesEjected[index]; - if (e.moveEngineTicks > 0) { + if (e.boostDistance > 0) { // Ejected cell is currently moving gameServer.spawnPlayer(player, pos, startMass); } diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 49afa25cf..6790e8500 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -142,9 +142,11 @@ TeamX.prototype.onServerInit = function(gameServer) { Virus.prototype.feed = function(feeder, gameServer) { gameServer.removeNode(feeder); // Pushes the virus - this.setAngle(feeder.getAngle()); // Set direction if the virus explodes - this.moveEngineTicks = 5; // Amount of times to loop the movement function - this.moveEngineSpeed = 30; + // TODO: check distance + this.setBoost(30 * 5, feeder.getAngle()); + //this.setAngle(feeder.getAngle()); // Set direction if the virus explodes + //this.moveEngineTicks = 5; // Amount of times to loop the movement function + //this.moveEngineSpeed = 30; var index = gameServer.movingNodes.indexOf(this); if (index == -1) { @@ -422,9 +424,9 @@ MotherCell.prototype.spawnFood = function(gameServer) { gameServer.currentFood++; // Move engine - f.angle = angle; var dist = (Math.random() * 10) + 22; // Random distance - f.setMoveEngineData(dist, 15); + // TODO: check distance + f.setBoost(dist*15, angle); gameServer.setAsMovingNode(f); }; diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 9264c71cc..368b51a66 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -544,8 +544,10 @@ TeamZ.prototype.onServerInit = function(gameServer) { cell.setMass(newMass); // Create cell var split = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, newMass); - split.setAngle(angle); - split.setMoveEngineData(splitSpeed, 32, 0.85); + // TODO: check distance + split.setBoost(splitSpeed * 32, angle); + //split.setAngle(angle); + //split.setMoveEngineData(splitSpeed, 32, 0.85); // boost speed if zombie eats brain if (this.gameMode.hasEatenBrain(client) || this.gameMode.isCrazy(client)) { @@ -574,8 +576,10 @@ TeamZ.prototype.onServerInit = function(gameServer) { // Create cell newCell = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, mass); - newCell.setAngle(angle); - newCell.setMoveEngineData(speed, 10); + // TODO: check distance + newCell.setBoost(speed * 10, angle); + //newCell.setAngle(angle); + //newCell.setMoveEngineData(speed, 10); newCell.ignoreCollision = true; // Turn off collision // boost speed if zombie eats brain @@ -1079,9 +1083,11 @@ Hero.prototype.onRemove = function(gameServer) { Hero.prototype.feed = function(feeder, gameServer) { gameServer.removeNode(feeder); - this.setAngle(feeder.getAngle()); - this.moveEngineTicks = 5; // Amount of times to loop the movement function - this.moveEngineSpeed = 60; + // TODO: check distance + this.setBoost(60 * 5, feeder.getAngle()); + //this.setAngle(feeder.getAngle()); + //this.moveEngineTicks = 5; // Amount of times to loop the movement function + //this.moveEngineSpeed = 60; var index = gameServer.movingNodes.indexOf(this); if (index == -1) { @@ -1151,9 +1157,11 @@ Brain.prototype.onRemove = function(gameServer) { Brain.prototype.feed = function(feeder, gameServer) { gameServer.removeNode(feeder); - this.setAngle(feeder.getAngle()); - this.moveEngineTicks = 5; // Amount of times to loop the movement function - this.moveEngineSpeed = 60; + // TODO: check distance + this.setBoost(60 * 5, feeder.getAngle()); + //this.setAngle(feeder.getAngle()); + //this.moveEngineTicks = 5; // Amount of times to loop the movement function + //this.moveEngineSpeed = 60; var index = gameServer.movingNodes.indexOf(this); if (index == -1) { diff --git a/src/gamemodes/Teams.js b/src/gamemodes/Teams.js index f58cadb1c..3f7f60837 100644 --- a/src/gamemodes/Teams.js +++ b/src/gamemodes/Teams.js @@ -1,158 +1,153 @@ -var Mode = require('./Mode'); - -function Teams() { - Mode.apply(this, Array.prototype.slice.call(arguments)); - - this.ID = 1; - this.name = "Teams"; - this.decayMod = 1.5; - this.packetLB = 50; - this.haveTeams = true; - this.colorFuzziness = 32; - - // Special - this.teamAmount = 3; // Amount of teams. Having more than 3 teams will cause the leaderboard to work incorrectly (client issue). - this.colors = [{ - 'r': 223, - 'g': 0, - 'b': 0 - }, { - 'r': 0, - 'g': 223, - 'b': 0 - }, { - 'r': 0, - 'g': 0, - 'b': 223 - }, ]; // Make sure you add extra colors here if you wish to increase the team amount [Default colors are: Red, Green, Blue] - this.nodes = []; // Teams -} - -module.exports = Teams; -Teams.prototype = new Mode(); - -//Gamemode Specific Functions - -Teams.prototype.fuzzColorComponent = function(component) { - component += Math.random() * this.colorFuzziness >> 0; - return component; -}; - -Teams.prototype.getTeamColor = function(team) { - var color = this.colors[team]; - return { - r: this.fuzzColorComponent(color.r), - b: this.fuzzColorComponent(color.b), - g: this.fuzzColorComponent(color.g) - }; -}; - -// Override - -Teams.prototype.onPlayerSpawn = function(gameServer, player) { - // Random color based on team - player.color = this.getTeamColor(player.team); - // Spawn player - gameServer.spawnPlayer(player); -}; - -Teams.prototype.onServerInit = function(gameServer) { - // Set up teams - for (var i = 0; i < this.teamAmount; i++) { - this.nodes[i] = []; - } - - // migrate current players to team mode - for (var i = 0; i < gameServer.clients.length; i++) { - var client = gameServer.clients[i].playerTracker; - this.onPlayerInit(client); - client.color = this.getTeamColor(client.team); - for (var j = 0; j < client.cells.length; j++) { - var cell = client.cells[j]; - cell.setColor(client.color); - this.nodes[client.team].push(cell); - } - } -}; - -Teams.prototype.onPlayerInit = function(player) { - // Get random team - player.team = Math.floor(Math.random() * this.teamAmount); -}; - -Teams.prototype.onCellAdd = function(cell) { - // Add to team list - this.nodes[cell.owner.getTeam()].push(cell); -}; - -Teams.prototype.onCellRemove = function(cell) { - // Remove from team list - var index = this.nodes[cell.owner.getTeam()].indexOf(cell); - if (index != -1) { - this.nodes[cell.owner.getTeam()].splice(index, 1); - } -}; - -Teams.prototype.onCellMove = function(cell, gameServer) { - var team = cell.owner.getTeam(); - var r = cell.getSize(); - - // Find team - for (var i = 0; i < cell.owner.visibleNodes.length; i++) { - // Only collide with player cells - var check = cell.owner.visibleNodes[i]; - - if ((check.getType() != 0) || (cell.owner == check.owner)) { - continue; - } - - // Collision with teammates - if (check.owner.getTeam() == team) { - var manifold = gameServer.checkCellCollision(cell, check); // Calculation info - - // Further calculations - if (manifold != null) { // Collided - // Cell with collision restore ticks on should not collide - if (cell.collisionRestoreTicks > 0 || check.collisionRestoreTicks > 0) - continue; - - // Call gameserver's function to collide cells - gameServer.resolveCollision(manifold); - } - } - } -}; - -Teams.prototype.updateLB = function(gameServer) { +var Mode = require('./Mode'); + +function Teams() { + Mode.apply(this, Array.prototype.slice.call(arguments)); + + this.ID = 1; + this.name = "Teams"; + this.decayMod = 1.5; + this.packetLB = 50; + this.haveTeams = true; + this.colorFuzziness = 32; + + // Special + this.teamAmount = 3; // Amount of teams. Having more than 3 teams will cause the leaderboard to work incorrectly (client issue). + this.colors = [{ + 'r': 223, + 'g': 0, + 'b': 0 + }, { + 'r': 0, + 'g': 223, + 'b': 0 + }, { + 'r': 0, + 'g': 0, + 'b': 223 + }, ]; // Make sure you add extra colors here if you wish to increase the team amount [Default colors are: Red, Green, Blue] + this.nodes = []; // Teams +} + +module.exports = Teams; +Teams.prototype = new Mode(); + +//Gamemode Specific Functions + +Teams.prototype.fuzzColorComponent = function(component) { + component += Math.random() * this.colorFuzziness >> 0; + return component; +}; + +Teams.prototype.getTeamColor = function(team) { + var color = this.colors[team]; + return { + r: this.fuzzColorComponent(color.r), + b: this.fuzzColorComponent(color.b), + g: this.fuzzColorComponent(color.g) + }; +}; + +// Override + +Teams.prototype.onPlayerSpawn = function(gameServer, player) { + // Random color based on team + player.color = this.getTeamColor(player.team); + // Spawn player + gameServer.spawnPlayer(player); +}; + +Teams.prototype.onServerInit = function(gameServer) { + // Set up teams + for (var i = 0; i < this.teamAmount; i++) { + this.nodes[i] = []; + } + + // migrate current players to team mode + for (var i = 0; i < gameServer.clients.length; i++) { + var client = gameServer.clients[i].playerTracker; + this.onPlayerInit(client); + client.color = this.getTeamColor(client.team); + for (var j = 0; j < client.cells.length; j++) { + var cell = client.cells[j]; + cell.setColor(client.color); + this.nodes[client.team].push(cell); + } + } +}; + +Teams.prototype.onPlayerInit = function(player) { + // Get random team + player.team = Math.floor(Math.random() * this.teamAmount); +}; + +Teams.prototype.onCellAdd = function(cell) { + // Add to team list + this.nodes[cell.owner.getTeam()].push(cell); +}; + +Teams.prototype.onCellRemove = function(cell) { + // Remove from team list + var index = this.nodes[cell.owner.getTeam()].indexOf(cell); + if (index != -1) { + this.nodes[cell.owner.getTeam()].splice(index, 1); + } +}; + +Teams.prototype.onCellMove = function(cell, gameServer) { + var team = cell.owner.getTeam(); + var r = cell.getSize(); + + // Find team + for (var i = 0; i < cell.owner.visibleNodes.length; i++) { + // Only collide with player cells + var check = cell.owner.visibleNodes[i]; + + if ((check.getType() != 0) || (cell.owner == check.owner)) { + continue; + } + + // Collision with teammates + if (check.owner.getTeam() == team) { + + var manifold = gameServer.checkCellCollision(cell, check); // Calculation info + if (manifold != null) { // Collided + // Call gameserver's function to collide cells + gameServer.resolveCollision(manifold); + } + } + } +}; + +Teams.prototype.updateLB = function(gameServer) { gameServer.leaderboardType = this.packetLB; - var total = 0; - var teamMass = []; - // Get mass - for (var i = 0; i < this.teamAmount; i++) { - // Set starting mass - teamMass[i] = 0; - - // Loop through cells - for (var j = 0; j < this.nodes[i].length; j++) { - var cell = this.nodes[i][j]; - - if (!cell) { - continue; - } - - teamMass[i] += cell.getMass(); - total += cell.getMass(); - } - } - // No players - if (total <= 0) { - for (var i = 0; i < this.teamAmount; i++) { - gameServer.leaderboard[i] = 0; - } - return - } - // Calc percentage - for (var i = 0; i < this.teamAmount; i++) { - gameServer.leaderboard[i] = teamMass[i] / total; - } -}; + var total = 0; + var teamMass = []; + // Get mass + for (var i = 0; i < this.teamAmount; i++) { + // Set starting mass + teamMass[i] = 0; + + // Loop through cells + for (var j = 0; j < this.nodes[i].length; j++) { + var cell = this.nodes[i][j]; + + if (!cell) { + continue; + } + + teamMass[i] += cell.getMass(); + total += cell.getMass(); + } + } + // No players + if (total <= 0) { + for (var i = 0; i < this.teamAmount; i++) { + gameServer.leaderboard[i] = 0; + } + return + } + // Calc percentage + for (var i = 0; i < this.teamAmount; i++) { + gameServer.leaderboard[i] = teamMass[i] / total; + } +}; From 2322ddaaf3fad3d5842f6a7071986af5789e9076 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 00:43:02 +0200 Subject: [PATCH 103/417] add warning for client with invalid protocol --- src/PacketHandler.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 411a975fb..ac7898d5d 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -50,6 +50,11 @@ PacketHandler.prototype.handleMessage = function(message) { this.socket.sendPacket(new Packet.SetBorder(border, 0, "MultiOgar 1.0")); // Send welcome message this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); + if (this.protocol < 4) { + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING Your client has protocol error!"); + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Client sends invalid protocol version "+this.protocol); + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Server assumes it as protocol 4"); + } this.isHandshakePassed = true; return; } From 73d1895ee942eb35c361a9414366dcee5c7e9db1 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 01:21:52 +0200 Subject: [PATCH 104/417] update readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 74d1fd83c..843b6f7ed 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends inv ## What's new: +* Split/Eject - physics rewritten; * Player speed - physics rewritten; * Recombine - physics rewritten; * Cell collision - physics rewritten; @@ -46,3 +47,8 @@ http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends inv * added chat support * added anti-spam protection * color generator replaced with hsv model +* Memory leaks fixed +* Performance improved with optimizations + +Currently most of the physics code from original ogar was replaced with new code. +Now the physics in MultiOgar it pretty close to old vanilla physics. \ No newline at end of file From 688ef6acd3c0b421153e68349079b98afda9a483 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 01:23:19 +0200 Subject: [PATCH 105/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 843b6f7ed..37c297440 100644 --- a/README.md +++ b/README.md @@ -51,4 +51,4 @@ http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends inv * Performance improved with optimizations Currently most of the physics code from original ogar was replaced with new code. -Now the physics in MultiOgar it pretty close to old vanilla physics. \ No newline at end of file +Now the physics engine in MultiOgar is pretty close to old vanilla physics. \ No newline at end of file From e799bb1c3c1269a6de346e9549ffa49446bfe3ea Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 05:06:01 +0200 Subject: [PATCH 106/417] fix split speed --- src/entity/Cell.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 1f30517e3..156cae4b6 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -184,8 +184,7 @@ Cell.prototype.calcMoveBoost = function (config) { if (this.boostDistance <= 0) return; //var maxSpeed = 40 * (2.1106 / Math.pow(32, 0.449)); - //var speed = Math.sqrt(this.boostDistance * this.boostDistance / this.getSize()) * 0.8; - var speed = Math.sqrt(this.boostDistance * this.boostDistance / (this.getSize()*2) ); + var speed = Math.sqrt(this.boostDistance * this.boostDistance / this.getSize()); speed = Math.min(speed, this.boostDistance); this.boostDistance -= speed; if (this.boostDistance <= 1) this.boostDistance = 0; From 9fd36b7f976afbc9786d9b18c8ea8a5b0336ac8f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 06:18:29 +0200 Subject: [PATCH 107/417] fix split speed --- src/entity/Cell.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 156cae4b6..50228cd0c 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -184,7 +184,7 @@ Cell.prototype.calcMoveBoost = function (config) { if (this.boostDistance <= 0) return; //var maxSpeed = 40 * (2.1106 / Math.pow(32, 0.449)); - var speed = Math.sqrt(this.boostDistance * this.boostDistance / this.getSize()); + var speed = Math.sqrt(this.boostDistance * this.boostDistance / 100); speed = Math.min(speed, this.boostDistance); this.boostDistance -= speed; if (this.boostDistance <= 1) this.boostDistance = 0; From 654945c22af94249bb5e7d9d9f93e17e00ee07e3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 06:53:34 +0200 Subject: [PATCH 108/417] fix ghost cells for old clients --- src/PacketHandler.js | 2 +- src/PlayerTracker.js | 11 +++++++---- src/packet/ClearAll.js | 9 +++++++++ src/packet/ClearNodes.js | 9 --------- src/packet/ClearOwned.js | 9 +++++++++ src/packet/index.js | 3 ++- 6 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 src/packet/ClearAll.js delete mode 100644 src/packet/ClearNodes.js create mode 100644 src/packet/ClearOwned.js diff --git a/src/PacketHandler.js b/src/PacketHandler.js index ac7898d5d..c7156c873 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -40,7 +40,7 @@ PacketHandler.prototype.handleMessage = function(message) { return; } // Send handshake response - this.socket.sendPacket(new Packet.ClearNodes()); + this.socket.sendPacket(new Packet.ClearAll()); var border = { left: this.gameServer.config.borderLeft + this.socket.playerTracker.scrambleX, top: this.gameServer.config.borderTop + this.socket.playerTracker.scrambleY, diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index e3b5fa3cf..73da2550d 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -151,11 +151,14 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.setSkin(skin); this.spectate = false; - this.socket.sendPacket(new Packet.ClearNodes()); + this.socket.sendPacket(new Packet.ClearAll()); this.collidingNodes = []; - this.visibleNodes = []; // Reset visible nodes - this.nodeDestroyQueue = []; // Reset destroy queue - this.nodeAdditionQueue = []; // Reset addition queue + // some clients don't understand ClearAll message + // so we will send we will do not perform cleanup + // to allow them to receive remove notifications + //this.visibleNodes = []; // Reset visible nodes + //this.nodeDestroyQueue = []; // Reset destroy queue + //this.nodeAdditionQueue = []; // Reset addition queue this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); }; diff --git a/src/packet/ClearAll.js b/src/packet/ClearAll.js new file mode 100644 index 000000000..d6a50783d --- /dev/null +++ b/src/packet/ClearAll.js @@ -0,0 +1,9 @@ +function ClearAll() { } + +module.exports = ClearAll; + +ClearAll.prototype.build = function (protocol) { + var buffer = new Buffer(1); + buffer.writeUInt8(0x12, 0); + return buffer; +}; \ No newline at end of file diff --git a/src/packet/ClearNodes.js b/src/packet/ClearNodes.js deleted file mode 100644 index 2018b3deb..000000000 --- a/src/packet/ClearNodes.js +++ /dev/null @@ -1,9 +0,0 @@ -function ClearNodes() { } - -module.exports = ClearNodes; - -ClearNodes.prototype.build = function (protocol) { - var buffer = new Buffer(1); - buffer.writeUInt8(protocol>=6 ? 0x12 : 0x14, 0); - return buffer; -}; \ No newline at end of file diff --git a/src/packet/ClearOwned.js b/src/packet/ClearOwned.js new file mode 100644 index 000000000..2ebafd787 --- /dev/null +++ b/src/packet/ClearOwned.js @@ -0,0 +1,9 @@ +function ClearOwned() { } + +module.exports = ClearOwned; + +ClearOwned.prototype.build = function (protocol) { + var buffer = new Buffer(1); + buffer.writeUInt8(0x14, 0); + return buffer; +}; \ No newline at end of file diff --git a/src/packet/index.js b/src/packet/index.js index bbb1bdf27..0c87983de 100644 --- a/src/packet/index.js +++ b/src/packet/index.js @@ -3,7 +3,8 @@ module.exports = { BinaryReader: require('./BinaryReader'), ChatMessage: require('./ChatMessage'), AddNode: require('./AddNode'), - ClearNodes: require('./ClearNodes'), + ClearAll: require('./ClearAll'), + ClearOwned: require('./ClearOwned'), UpdatePosition: require('./UpdatePosition'), SetBorder: require('./SetBorder'), UpdateNodes: require('./UpdateNodes'), From e82179d1cf3011d60ac14a7f7b8a6e8ae41968d4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 07:57:00 +0200 Subject: [PATCH 109/417] fix collision disable time for split --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index a3dfcb209..465c128ae 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -551,7 +551,7 @@ GameServer.prototype.checkCellCollision = function(cell, check) { // do not affect splitted cell for 1 sec if (cell.boostDistance > 0 || check.boostDistance > 0) { var tick = this.getTick(); - if (cell.getAgeTicks(tick) < 25 || check.getAgeTicks(tick) < 25) + if (cell.getAgeTicks(tick) < 12 || check.getAgeTicks(tick) < 12) return null; } From 7f96ce9a769c500fbe057ca2f76b793bf7fe35d0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 09:21:09 +0200 Subject: [PATCH 110/417] fix split/eject collision behavior to vanilla --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index 465c128ae..d044ef318 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -551,7 +551,7 @@ GameServer.prototype.checkCellCollision = function(cell, check) { // do not affect splitted cell for 1 sec if (cell.boostDistance > 0 || check.boostDistance > 0) { var tick = this.getTick(); - if (cell.getAgeTicks(tick) < 12 || check.getAgeTicks(tick) < 12) + if (cell.getAgeTicks(tick) < 15 || check.getAgeTicks(tick) < 15) return null; } From 5308ceccb56fd8b0fb2f721aef92d2e930284996 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 10:03:00 +0200 Subject: [PATCH 111/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 37c297440..bac425d16 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # MultiOgar +Ogar game server with vanilla physics and multi-protocol support. ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) From 1cd383377fee367ed2cd58fa255a4fccc6d5d7f0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 10:09:00 +0200 Subject: [PATCH 112/417] update readme --- README.md | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index bac425d16..ca2319362 100644 --- a/README.md +++ b/README.md @@ -35,21 +35,23 @@ http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends inv ## What's new: -* Split/Eject - physics rewritten; -* Player speed - physics rewritten; -* Recombine - physics rewritten; -* Cell collision - physics rewritten; -* View area - rewritten -* Mouse control and cell movements - physics rewritten; +* Split/Eject - physics code rewritten; +* Player speed - physics code rewritten; +* Recombine - physics code rewritten; +* Cell collision - physics code rewritten; +* View area - code rewritten; +* Spectate - code rewritten; +* Mouse control and cell movements - physics code rewritten; * Border calculations - rewritten; -* Border bouncy physics - fixed and improved -* mainLoop - cleaned -* added support for different protocols -* added chat support -* added anti-spam protection -* color generator replaced with hsv model -* Memory leaks fixed -* Performance improved with optimizations - -Currently most of the physics code from original ogar was replaced with new code. -Now the physics engine in MultiOgar is pretty close to old vanilla physics. \ No newline at end of file +* Border bouncy physics - fixed and improved; +* mainLoop - cleaned; +* Added support for different protocols (4, early 5, late 5, 6, 7, 8); +* Added automatic mouse message type recognition; +* Added chat support; +* Added anti-spam protection; +* Color generator replaced with hsv model; +* Memory leaks fixed; +* Performance improved and optimized + +Most of the physics code from original ogar were rewritten. +The physics engine in MultiOgar is pretty close to the old vanilla physics. \ No newline at end of file From 4f1715a500190268cebf8e835cf001c0601b60c6 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 10:10:04 +0200 Subject: [PATCH 113/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca2319362..a7791d8c8 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends inv ## What's new: * Split/Eject - physics code rewritten; * Player speed - physics code rewritten; -* Recombine - physics code rewritten; +* Cell remerge - physics code rewritten; * Cell collision - physics code rewritten; * View area - code rewritten; * Spectate - code rewritten; From 176f6a3b01b8ac97b056616696628d06f327cd26 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 12:22:46 +0200 Subject: [PATCH 114/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a7791d8c8..c9bfa9089 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends inv * Added automatic mouse message type recognition; * Added chat support; * Added anti-spam protection; +* Added skin support (use name "< shark > Fish", remove space); * Color generator replaced with hsv model; * Memory leaks fixed; * Performance improved and optimized From 035727aa083eecffcf1fab9fdd3ad9c23bae8fd8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 12:46:24 +0200 Subject: [PATCH 115/417] fix ejected direction spread --- src/GameServer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index d044ef318..1c865edc6 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -766,20 +766,20 @@ GameServer.prototype.ejectMass = function(client) { var angle = Math.atan2(dx, dy); if (isNaN(angle)) angle = 0; - // Randomize angle - angle += (Math.random() * 0.1) - 0.05; - // Get starting position - var startPos = { + var pos = { x: cell.position.x + cell.getSize() * Math.sin(angle), y: cell.position.y + cell.getSize() * Math.cos(angle) }; + // Randomize angle + angle += (Math.random() * 0.6) - 0.3; + // Remove mass from parent cell cell.setMass(cell.getMass() - this.config.ejectMassLoss); // Create cell - var ejected = new Entity.EjectedMass(this.getNextNodeId(), null, startPos, this.config.ejectMass, this); + var ejected = new Entity.EjectedMass(this.getNextNodeId(), null, pos, this.config.ejectMass, this); ejected.setColor(cell.getColor()); ejected.setBoost(780, angle); ejected.ejector = cell; From 96b12a5d405f2f780bbac1e803943f9deb38f6e1 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 11 Jun 2016 12:55:14 +0200 Subject: [PATCH 116/417] rename Cell.getAgeTicks to Cell.getAge --- src/GameServer.js | 2 +- src/entity/Cell.js | 3 ++- src/entity/PlayerCell.js | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 1c865edc6..d4a0da88a 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -551,7 +551,7 @@ GameServer.prototype.checkCellCollision = function(cell, check) { // do not affect splitted cell for 1 sec if (cell.boostDistance > 0 || check.boostDistance > 0) { var tick = this.getTick(); - if (cell.getAgeTicks(tick) < 15 || check.getAgeTicks(tick) < 15) + if (cell.getAge(tick) < 15 || check.getAge(tick) < 15) return null; } diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 50228cd0c..9eb504f94 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -106,7 +106,8 @@ Cell.prototype.getAngle = function() { return this.boostDirection.angle; }; -Cell.prototype.getAgeTicks = function (tick) { +// Returns cell age in ticks for specified game tick +Cell.prototype.getAge = function (tick) { if (this.tickOfBirth == null) return 0; return Math.max(0, tick - this.tickOfBirth); } diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index d1c92ab78..d30fd92d8 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -30,7 +30,7 @@ PlayerCell.prototype.updateRemerge = function (gameServer) { return; } var tick = gameServer.getTick(); - var age = this.getAgeTicks(tick); + var age = this.getAge(tick); if (age < 3) { // do not remerge if cell age is smaller than 3 ticks this._canRemerge = false; From 5a2b956eba415da1c85befc45f4abcaf30d309de Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 12 Jun 2016 03:10:19 +0200 Subject: [PATCH 117/417] fix game console handler --- src/index.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index 10e5f1627..e265aa907 100644 --- a/src/index.js +++ b/src/index.js @@ -39,8 +39,11 @@ if (showConsole) { function prompt() { in_.question(">", function(str) { - parseCommands(str); - return prompt(); // Too lazy to learn async + try { + parseCommands(str); + } finally { + process.nextTick(prompt); + } }); } From 564cf0191eaf7025f900ccd407113ca79fa0b473 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 12 Jun 2016 04:12:29 +0200 Subject: [PATCH 118/417] protocol code refactoring --- src/packet/UpdateNodes.js | 117 +++++++++++++++----------------------- 1 file changed, 47 insertions(+), 70 deletions(-) diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index 7a3b015d1..b51017537 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -18,7 +18,7 @@ UpdateNodes.prototype.build = function (protocol) { if (protocol <= 4) return this.build4(); else if (protocol == 5) return this.build5(); else return this.build6(); -} +}; // protocol 4 UpdateNodes.prototype.build4 = function () { @@ -28,7 +28,7 @@ UpdateNodes.prototype.build4 = function () { var deadCells = []; for (var i = 0; i < this.destroyQueue.length; i++) { var node = this.destroyQueue[i]; - if (!node) continue; + if (node == null) continue; deadCells.push(node); } writer.writeUInt16(deadCells.length >> 0); // EatRecordCount @@ -44,44 +44,38 @@ UpdateNodes.prototype.build4 = function () { for (var i = 0; i < this.nodes.length; i++) { var node = this.nodes[i]; - if (node==null || node.nodeId == 0) + if (node == null || node.nodeId == 0) continue; var cellX = node.position.x + this.scrambleX; var cellY = node.position.y + this.scrambleY; - var cellSize = node.getSize(); var cellName = node.getName(); - if (!cellName) cellName = ""; - - - var isVirus = (node.spiked & 0x01) != 0; - var isAgitated = false; // true = high wave amplitude on a cell outline - var isEject = node.cellType == 3; // Write update record writer.writeUInt32(node.nodeId >> 0); // Cell ID writer.writeInt16(cellX >> 0); // Coordinate X writer.writeInt16(cellY >> 0); // Coordinate Y - writer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) writer.writeUInt8(node.color.r >> 0); // Color R writer.writeUInt8(node.color.g >> 0); // Color G writer.writeUInt8(node.color.b >> 0); // Color B var flags = 0; - if (isVirus) - flags |= 0x01; - if (isAgitated) - flags |= 0x10; - if (isEject) - flags |= 0x20; + if (node.spiked & 1) + flags |= 0x01; // isVirus + if (false) + flags |= 0x10; // isAgitated + if (node.cellType == 3) + flags |= 0x20; // isEjected writer.writeUInt8(flags >> 0); // Flags + writer.writeStringZeroUnicode(cellName); // Name } writer.writeUInt32(0); // Cell Update record terminator for (var i = 0; i < this.nonVisibleNodes.length; i++) { var node = this.nonVisibleNodes[i]; - if (!node) continue; + if (node == null) continue; deadCells.push(node); } writer.writeUInt32(deadCells.length >> 0); // RemoveRecordCount @@ -90,7 +84,7 @@ UpdateNodes.prototype.build4 = function () { writer.writeUInt32(node.nodeId); // Cell ID } return writer.ToBuffer(); -} +}; // protocol 5 UpdateNodes.prototype.build5 = function () { @@ -100,7 +94,7 @@ UpdateNodes.prototype.build5 = function () { var deadCells = []; for (var i = 0; i < this.destroyQueue.length; i++) { var node = this.destroyQueue[i]; - if (!node) continue; + if (node == null) continue; deadCells.push(node); } writer.writeUInt16(deadCells.length >> 0); // EatRecordCount @@ -116,44 +110,35 @@ UpdateNodes.prototype.build5 = function () { for (var i = 0; i < this.nodes.length; i++) { var node = this.nodes[i]; - if (node==null || node.nodeId == 0) + if (node == null || node.nodeId == 0) continue; var cellX = node.position.x + this.scrambleX; var cellY = node.position.y + this.scrambleY; - var cellSize = node.getSize(); var skinName = node.getSkin(); var cellName = node.getName(); - if (!skinName) skinName = ""; - if (!cellName) cellName = ""; - - - var isVirus = (node.spiked & 0x01) != 0; - var isSkinPresent = !isVirus && skinName != null && skinName.length > 0; - var isAgitated = false; // true = high wave amplitude on a cell outline - var isEject = node.cellType == 3; // Write update record writer.writeUInt32(node.nodeId >> 0); // Cell ID writer.writeInt32(cellX >> 0); // Coordinate X writer.writeInt32(cellY >> 0); // Coordinate Y - writer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) writer.writeUInt8(node.color.r >> 0); // Color R writer.writeUInt8(node.color.g >> 0); // Color G writer.writeUInt8(node.color.b >> 0); // Color B var flags = 0; - if (isVirus) - flags |= 0x01; - if (isSkinPresent) - flags |= 0x04; - if (isAgitated) - flags |= 0x10; - if (isEject) - flags |= 0x20; + if (node.spiked & 1) + flags |= 0x01; // isVirus + if (!(node.spiked & 1) && skinName != null && skinName.length > 0) + flags |= 0x04; // isSkinPresent + if (false) + flags |= 0x10; // isAgitated + if (node.cellType == 3) + flags |= 0x20; // isEjected writer.writeUInt8(flags >> 0); // Flags - if (isSkinPresent) + if (flags & 0x04) writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 writer.writeStringZeroUnicode(cellName); // Cell Name @@ -162,7 +147,7 @@ UpdateNodes.prototype.build5 = function () { for (var i = 0; i < this.nonVisibleNodes.length; i++) { var node = this.nonVisibleNodes[i]; - if (!node) continue; + if (node == null) continue; deadCells.push(node); } writer.writeUInt32(deadCells.length >> 0); // RemoveRecordCount @@ -171,7 +156,7 @@ UpdateNodes.prototype.build5 = function () { writer.writeUInt32(node.nodeId >> 0); // Cell ID } return writer.ToBuffer(); -} +}; // protocol 6 UpdateNodes.prototype.build6 = function () { @@ -181,7 +166,7 @@ UpdateNodes.prototype.build6 = function () { var deadCells = []; for (var i = 0; i < this.destroyQueue.length; i++) { var node = this.destroyQueue[i]; - if (!node) continue; + if (node == null) continue; deadCells.push(node); } writer.writeUInt16(deadCells.length >> 0); // EatRecordCount @@ -197,58 +182,50 @@ UpdateNodes.prototype.build6 = function () { for (var i = 0; i < this.nodes.length; i++) { var node = this.nodes[i]; - if (node==null || node.nodeId == 0) + if (node == null || node.nodeId == 0) continue; var cellX = node.position.x + this.scrambleX; var cellY = node.position.y + this.scrambleY; - var cellSize = node.getSize(); var skinName = node.getSkin(); var cellName = node.getName(); - var isVirus = (node.spiked & 0x01) != 0; - var isColorPresent = true; // we always include color - var isSkinPresent = !isVirus && skinName != null && skinName.length > 0; - var isNamePresent = !isVirus && cellName != null && cellName.length > 0; - var isAgitated = false; // true = high wave amplitude on a cell outline - var isEject = node.cellType==3; - // Write update record writer.writeUInt32(node.nodeId >> 0); // Cell ID writer.writeInt32(cellX >> 0); // Coordinate X writer.writeInt32(cellY >> 0); // Coordinate Y - writer.writeUInt16(cellSize >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) var flags = 0; - if (isVirus) - flags |= 0x01; - if (isColorPresent) - flags |= 0x02; - if (isSkinPresent) - flags |= 0x04; - if (isNamePresent) - flags |= 0x08; - if (isAgitated) - flags |= 0x10; - if (isEject) - flags |= 0x20; + if (node.spiked & 1) + flags |= 0x01; // isVirus + if (true) + flags |= 0x02; // isColorPresent + if (!(node.spiked & 1) && skinName != null && skinName.length > 1) + flags |= 0x04; // isSkinPresent + if (!(node.spiked & 1) && cellName != null && cellName.length > 1) + flags |= 0x08; // isNamePresent + if (false) + flags |= 0x10; // isAgitated + if (node.cellType == 3) + flags |= 0x20; // isEjected writer.writeUInt8(flags >> 0); // Flags - if (isColorPresent) { + if (flags & 0x02) { writer.writeUInt8(node.color.r >> 0); // Color R writer.writeUInt8(node.color.g >> 0); // Color G writer.writeUInt8(node.color.b >> 0); // Color B } - if (isSkinPresent) + if (flags & 0x04) writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 - if (isNamePresent) + if (flags & 0x08) writer.writeStringZeroUtf8(cellName); // Cell Name in UTF8 } writer.writeUInt32(0); // Cell Update record terminator for (var i = 0; i < this.nonVisibleNodes.length; i++) { var node = this.nonVisibleNodes[i]; - if (!node) continue; + if (node == null) continue; deadCells.push(node); } writer.writeUInt16(deadCells.length >> 0); // RemoveRecordCount @@ -257,4 +234,4 @@ UpdateNodes.prototype.build6 = function () { writer.writeUInt32(node.nodeId >> 0); // Cell ID } return writer.ToBuffer(); -} +}; From 806e4896d00e861f77064351514d7653c73fef31 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 12 Jun 2016 04:59:13 +0200 Subject: [PATCH 119/417] fix performance and memory leaks; rewrite websocket code; realtime loop with adaptive timeStep --- src/GameServer.js | 242 +++++++++++++++++++++++++------------------- src/ai/BotPlayer.js | 30 +++--- src/entity/Food.js | 16 ++- src/gameserver.ini | 1 - 4 files changed, 160 insertions(+), 129 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index d4a0da88a..81dc5c8d8 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -37,8 +37,13 @@ function GameServer() { // Main loop tick this.startTime = +new Date; - this.tickCounter = 0; this.timeStamp = 0; + this.updateTime = 0; + this.updateTimeAvg = 0; + this.timerLoopBind = null; + this.mainLoopBind = null; + + this.tickCounter = 0; this.tickSpawn = 0; // Used with spawning food @@ -67,7 +72,6 @@ function GameServer() { foodMassGrow: 1, // Enable food mass grow ? foodMassGrowPossiblity: 50, // Chance for a food to has the ability to be self growing foodMassLimit: 5, // Maximum mass for a food can grow - foodMassTimeout: 120, // The amount of interval for a food to grow its mass (in seconds) virusMinAmount: 10, // Minimum amount of viruses on the map. virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. virusStartMass: 100, // Starting virus size (In mass) @@ -107,118 +111,114 @@ function GameServer() { module.exports = GameServer; GameServer.prototype.start = function() { + this.timerLoopBind = this.timerLoop.bind(this); + this.mainLoopBind = this.mainLoop.bind(this); + // Logging this.log.setup(this); - + // Gamemode configurations this.gameMode.onServerInit(this); - - // Start the server - this.socketServer = new WebSocket.Server({ + + var options = { port: this.config.serverPort, perMessageDeflate: false - }, function() { - // Spawn starting food - this.startingFood(); - - // Start Main Loop - //setInterval(this.mainLoop.bind(this), 40); - setInterval(this.timerLoop.bind(this), 1); - - // Done - console.log("[Game] Listening on port " + this.config.serverPort); - console.log("[Game] Current game mode is " + this.gameMode.name); - - // Player bots (Experimental) - if (this.config.serverBots > 0) { - for (var i = 0; i < this.config.serverBots; i++) { - this.bots.addBot(); - } - console.log("[Game] Loaded " + this.config.serverBots + " player bots"); - } - - }.bind(this)); - - this.socketServer.on('connection', connectionEstablished.bind(this)); - - // Properly handle errors because some people are too lazy to read the readme - this.socketServer.on('error', function err(e) { - switch (e.code) { - case "EADDRINUSE": - console.log("[Error] Server could not bind to port! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); - break; - case "EACCES": - console.log("[Error] Please make sure you are running Ogar with root privileges."); - break; - default: - console.log("[Error] Unhandled error code: " + e.code); - break; - } - process.exit(1); // Exits the program - }); - - function connectionEstablished(ws) { - if (this.clients.length >= this.config.serverMaxConnections) { // Server full - ws.close(1000, "No slots"); - return; - } + }; - function close(error) { - // Log disconnections - this.server.log.onDisconnect(this.socket.remoteAddress); + // Start the server + this.socketServer = new WebSocket.Server(options, this.onServerSocketOpen.bind(this)); + this.socketServer.on('error', this.onServerSocketError.bind(this)); + this.socketServer.on('connection', this.onClientSocketOpen.bind(this)); - var client = this.socket.playerTracker; - var len = this.socket.playerTracker.cells.length; - for (var i = 0; i < len; i++) { - var cell = this.socket.playerTracker.cells[i]; - if (cell == null) continue; + this.startStatsServer(this.config.serverStatsPort); +}; - cell.calcMove = function() { - return; - }; // Clear function so that the cell cant move - //this.server.removeNode(cell); - } +GameServer.prototype.onServerSocketError = function (error) { + switch (error.code) { + case "EADDRINUSE": + console.log("[Error] Server could not bind to port! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); + break; + case "EACCES": + console.log("[Error] Please make sure you are running Ogar with root privileges."); + break; + default: + console.log("[Error] Unhandled error code: " + error.code); + break; + } + process.exit(1); // Exits the program +}; - client.disconnect = this.server.config.playerDisconnectTime * 20; - this.socket.sendPacket = function() { - return; - }; // Clear function so no packets are sent +GameServer.prototype.onServerSocketOpen = function () { + // Spawn starting food + this.startingFood(); + + // Start Main Loop + setTimeout(this.timerLoopBind, 1); + + // Done + console.log("[Game] Listening on port " + this.config.serverPort); + console.log("[Game] Current game mode is " + this.gameMode.name); + + // Player bots (Experimental) + if (this.config.serverBots > 0) { + for (var i = 0; i < this.config.serverBots; i++) { + this.bots.addBot(); } + console.log("[Game] Loaded " + this.config.serverBots + " player bots"); + } +}; - ws.remoteAddress = ws._socket.remoteAddress; - ws.remotePort = ws._socket.remotePort; - this.log.onConnect(ws.remoteAddress); // Log connections - - ws.playerTracker = new PlayerTracker(this, ws); - ws.packetHandler = new PacketHandler(this, ws); - ws.on('message', ws.packetHandler.handleMessage.bind(ws.packetHandler)); +GameServer.prototype.onClientSocketOpen = function (ws) { + if (this.clients.length >= this.config.serverMaxConnections) { // Server full + ws.close(1000, "No slots"); + return; + } + ws.remoteAddress = ws._socket.remoteAddress; + ws.remotePort = ws._socket.remotePort; + this.log.onConnect(ws.remoteAddress); // Log connections + + ws.playerTracker = new PlayerTracker(this, ws); + ws.packetHandler = new PacketHandler(this, ws); + + var gameServer = this; + var onMessage = function (message) { + gameServer.onClientSocketMessage(ws, message); + }; + var onError = function (error) { + gameServer.onClientSocketError(ws, error); + }; + var onClose = function (reason) { + gameServer.onClientSocketClose(ws, reason); + }; + ws.on('message', onMessage); + ws.on('error', onError); + ws.on('close', onClose); + this.clients.push(ws); +}; - var bindObject = { - server: this, - socket: ws - }; - ws.on('error', close.bind(bindObject)); - ws.on('close', close.bind(bindObject)); - this.clients.push(ws); +GameServer.prototype.onClientSocketClose = function (ws, reason) { + this.log.onDisconnect(ws.remoteAddress); + + ws.sendPacket = function (data) { }; + ws.playerTracker.disconnect = this.config.playerDisconnectTime * 20; + for (var i = 0; i < ws.playerTracker.cells.length; i++) { + var cell = ws.playerTracker.cells[i]; + if (cell == null) continue; + + cell.calcMove = function () { } } +}; - this.startStatsServer(this.config.serverStatsPort); +GameServer.prototype.onClientSocketError = function (ws, error) { + ws.sendPacket = function (data) { }; + ws.close(1002, "Socket error"); }; -GameServer.prototype.timerLoop = function () { - var ts = new Date().getTime(); - if (ts - this.timeStamp < 40) - return; - if (this.timeStamp == 0) - this.timeStamp = ts; - this.timeStamp += 40; - if (this.timeStamp + 400 < ts) { - // high lag detected, resynchronize - this.timeStamp = ts - 80; - } - setTimeout(this.mainLoop.bind(this), 0); +GameServer.prototype.onClientSocketMessage = function (ws, message) { + ws.packetHandler.handleMessage(message); }; + GameServer.prototype.getTick = function () { return this.tickCounter; }; @@ -426,7 +426,37 @@ GameServer.prototype.sendChatMessage = function (from, to, message) { } }; +GameServer.prototype.timerLoop = function () { + var timeStep = this.updateTimeAvg >> 0; + timeStep += 5; + timeStep = Math.max(timeStep, 40); + + var ts = new Date().getTime(); + var dt = ts - this.timeStamp; + if (dt < timeStep - 5) { + setTimeout(this.timerLoopBind, ((timeStep-5) - dt) >> 0); + return; + } + if (dt < timeStep - 1) { + setTimeout(this.timerLoopBind, 0); + return; + } + if (dt < timeStep) { + process.nextTick(this.timerLoopBind); + return; + } + // update average + this.updateTimeAvg += 0.5 * (this.updateTime - this.updateTimeAvg); + // calculate next + if (this.timeStamp == 0) + this.timeStamp = ts; + this.timeStamp += timeStep; + process.nextTick(this.mainLoopBind); + process.nextTick(this.timerLoopBind); +}; + GameServer.prototype.mainLoop = function() { + var tStart = new Date().getTime(); // Loop main functions this.updateMoveEngine(); this.updateSpawn(); @@ -437,24 +467,29 @@ GameServer.prototype.mainLoop = function() { //var t = process.hrtime(); //this.updateMoveEngine(); - //this.t1 = process.hrtime(t); + //this.t1 = toTime(process.hrtime(t)); //t = process.hrtime(); //this.updateSpawn(); - //this.t2 = process.hrtime(t); + //this.t2 = toTime(process.hrtime(t)); //t = process.hrtime(); //this.gameMode.onTick(this); - //this.t3 = process.hrtime(t); + //this.t3 = toTime(process.hrtime(t)); //t = process.hrtime(); //this.updateCells(); - //this.t4 = process.hrtime(t); + //this.t4 = toTime(process.hrtime(t)); //t = process.hrtime(); //this.updateClients(); - //this.t5 = process.hrtime(t); + //this.t5 = toTime(process.hrtime(t)); //t = process.hrtime(); //this.updateLeaderboard(); - //this.t6 = process.hrtime(t); + //this.t6 = toTime(process.hrtime(t)); + //function toTime(tscTicks) { + // return tscTicks[0] * 1000 + tscTicks[1] / 1000000; + //} this.tickCounter++; + var tEnd = new Date().getTime(); + this.updateTime = tEnd - tStart; }; GameServer.prototype.startingFood = function() { @@ -996,10 +1031,11 @@ GameServer.prototype.startStatsServer = function(port) { res.end(this.stats); }.bind(this)); - this.httpServer.listen(port, function() { + var getStatsBind = this.getStats.bind(this); + this.httpServer.listen(port, function () { // Stats server console.log("[Game] Loaded stats server on port " + port); - setInterval(this.getStats.bind(this), this.config.serverStatsUpdate * 1000); + setInterval(getStatsBind, this.config.serverStatsUpdate * 1000); }.bind(this)); }; diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index 6fc3d2d92..d3dcbf8e3 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -29,7 +29,7 @@ BotPlayer.prototype.getLowestCell = function() { return sorted[0]; }; -BotPlayer.prototype.update = function() { // Overrides the update function from player tracker +BotPlayer.prototype.update = function () { // Overrides the update function from player tracker // Remove nodes from visible nodes if possible for (var i = 0; i < this.nodeDestroyQueue.length; i++) { var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[i]); @@ -37,7 +37,7 @@ BotPlayer.prototype.update = function() { // Overrides the update function from this.visibleNodes.splice(index, 1); } } - + // Respawn if bot is dead if (this.cells.length <= 0) { this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); @@ -50,20 +50,18 @@ BotPlayer.prototype.update = function() { // Overrides the update function from if (this.splitCooldown > 0) this.splitCooldown--; - setTimeout(function() { - // Calculate nodes - this.visibleNodes = this.getVisibleNodes(); - - // Calc predators/prey - var cell = this.getLowestCell(); - - // Action - this.decide(cell); - - // Reset queues - this.nodeDestroyQueue = []; - this.nodeAdditionQueue = []; - }.bind(this), 0); + // Calculate nodes + this.visibleNodes = this.getVisibleNodes(); + + // Calc predators/prey + var cell = this.getLowestCell(); + + // Action + this.decide(cell); + + // Reset queues + this.nodeDestroyQueue = []; + this.nodeAdditionQueue = []; }; // Custom diff --git a/src/entity/Food.js b/src/entity/Food.js index a04e33dec..1d7449353 100644 --- a/src/entity/Food.js +++ b/src/entity/Food.js @@ -19,15 +19,13 @@ Food.prototype.calcMove = null; // Food has no need to move // Main Functions -Food.prototype.grow = function() { - setTimeout(function() { - this.setMass(this.getMass() + 1); // food mass increased, we need to recalculate its size and squareSize, and send update to client side - this.shouldSendUpdate = true; - - if (this.getMass() < this.gameServer.config.foodMassLimit) { - this.grow(); - } - }.bind(this), this.gameServer.config.foodMassTimeout * 1000); +Food.prototype.grow = function () { + this.setMass(this.getMass() + 1); // food mass increased, we need to recalculate its size and squareSize, and send update to client side + this.shouldSendUpdate = true; + + if (this.getMass() < this.gameServer.config.foodMassLimit) { + this.grow(); + } }; Food.prototype.sendUpdate = function() { diff --git a/src/gameserver.ini b/src/gameserver.ini index 4bb71579b..4c4d2198d 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -39,7 +39,6 @@ foodMass = 1 foodMassGrow = 1 foodMassGrowPossiblity = 50 foodMassLimit = 5 -foodMassTimeout = 120 virusMinAmount = 10 virusMaxAmount = 50 virusStartMass = 100 From 2c6c485801a8177cded39e2cabb70a5c02a8878c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 12 Jun 2016 05:52:02 +0200 Subject: [PATCH 120/417] add current lag info into status command --- src/modules/CommandList.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index d973a0454..9010e91d6 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -452,12 +452,23 @@ Commands.list = { bots++; } } - // + + var lagMessage = "extreme high lag"; + if (gameServer.updateTimeAvg < 20) + lagMessage = "perfect smooth"; + else if (gameServer.updateTimeAvg < 35) + lagMessage = "good"; + else if (gameServer.updateTimeAvg < 40) + lagMessage = "tiny lag"; + else if (gameServer.updateTimeAvg < 50) + lagMessage = "lag"; + console.log("[Console] Connected players: " + gameServer.clients.length + "/" + gameServer.config.serverMaxConnections); console.log("[Console] Players: " + humans + " - Bots: " + bots); console.log("[Console] Server has been running for " + Math.floor(process.uptime()/60) + " minutes"); console.log("[Console] Current memory usage: " + Math.round(process.memoryUsage().heapUsed / 1048576 * 10)/10 + "/" + Math.round(process.memoryUsage().heapTotal / 1048576 * 10)/10 + " mb"); console.log("[Console] Current game mode: " + gameServer.gameMode.name); + console.log("[Console] Current update time: " + gameServer.updateTimeAvg.toFixed(3) + " [ms] (" + lagMessage + ")"); }, tp: function(gameServer, split) { var id = parseInt(split[1]); From af83c461cb7549303a8719864ce79ba22be473f6 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 12 Jun 2016 06:03:11 +0200 Subject: [PATCH 121/417] fix lag text --- src/modules/CommandList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 9010e91d6..9ed961f41 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -455,7 +455,7 @@ Commands.list = { var lagMessage = "extreme high lag"; if (gameServer.updateTimeAvg < 20) - lagMessage = "perfect smooth"; + lagMessage = "perfectly smooth"; else if (gameServer.updateTimeAvg < 35) lagMessage = "good"; else if (gameServer.updateTimeAvg < 40) From 8f56f3d80e2b3134a185c70739d5c75acd05c5bd Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 12 Jun 2016 08:01:16 +0200 Subject: [PATCH 122/417] fix console --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index e265aa907..3291b04bb 100644 --- a/src/index.js +++ b/src/index.js @@ -42,7 +42,7 @@ function prompt() { try { parseCommands(str); } finally { - process.nextTick(prompt); + setTimeout(prompt, 0); } }); } From e352b3eadeb4bfef2ea57fec16eb4646a120cfad Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 12 Jun 2016 09:53:13 +0200 Subject: [PATCH 123/417] fix "recursive process.nextTick detected" (https://nodejs.org/dist/v4.4.4/node-v4.4.4-linux-x64.tar.xz) --- src/GameServer.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 81dc5c8d8..bd14c23ab 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -442,7 +442,8 @@ GameServer.prototype.timerLoop = function () { return; } if (dt < timeStep) { - process.nextTick(this.timerLoopBind); + //process.nextTick(this.timerLoopBind); + setTimeout(this.timerLoopBind, 0); return; } // update average @@ -451,8 +452,10 @@ GameServer.prototype.timerLoop = function () { if (this.timeStamp == 0) this.timeStamp = ts; this.timeStamp += timeStep; - process.nextTick(this.mainLoopBind); - process.nextTick(this.timerLoopBind); + //process.nextTick(this.mainLoopBind); + //process.nextTick(this.timerLoopBind); + setTimeout(this.mainLoopBind, 0); + setTimeout(this.timerLoopBind, 0); }; GameServer.prototype.mainLoop = function() { From a8d13a479158fa13c1a333fd6ef1b7ae6da58cbf Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 12 Jun 2016 23:28:40 +0200 Subject: [PATCH 124/417] fix to avoid too high eject speed for custom distance --- src/entity/Cell.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 9eb504f94..ad0fc4766 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -184,9 +184,9 @@ Cell.prototype.setBoost = function (distance, angle) { Cell.prototype.calcMoveBoost = function (config) { if (this.boostDistance <= 0) return; - //var maxSpeed = 40 * (2.1106 / Math.pow(32, 0.449)); var speed = Math.sqrt(this.boostDistance * this.boostDistance / 100); - speed = Math.min(speed, this.boostDistance); + var speed = Math.min(speed, 78); // limit max speed with sqrt(780*780/100) + speed = Math.min(speed, this.boostDistance); // avoid overlap 0 this.boostDistance -= speed; if (this.boostDistance <= 1) this.boostDistance = 0; var x = this.position.x + this.boostDirection.x * speed; From 17887f08cb4cbc0b4f20d3c4a116105f879fdbb0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 00:37:31 +0200 Subject: [PATCH 125/417] show connection status in playerlist command --- src/GameServer.js | 2 ++ src/modules/CommandList.js | 13 +++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index bd14c23ab..8051eb6d5 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -173,6 +173,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { ws.close(1000, "No slots"); return; } + ws.isConnected = true; ws.remoteAddress = ws._socket.remoteAddress; ws.remotePort = ws._socket.remotePort; this.log.onConnect(ws.remoteAddress); // Log connections @@ -199,6 +200,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { GameServer.prototype.onClientSocketClose = function (ws, reason) { this.log.onDisconnect(ws.remoteAddress); + ws.isConnected = false; ws.sendPacket = function (data) { }; ws.playerTracker.disconnect = this.config.playerDisconnectTime * 20; for (var i = 0; i < ws.playerTracker.cells.length; i++) { diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 9ed961f41..6c23eaa09 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -386,15 +386,20 @@ Commands.list = { console.log(" ID | IP | P | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space console.log(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); for (var i = 0; i < gameServer.clients.length; i++) { - var client = gameServer.clients[i].playerTracker; + var socket = gameServer.clients[i]; + var client = socket.playerTracker; // ID with 3 digits length var id = fillChar((client.pID), ' ', 6, true); // Get ip (15 digits length) - var ip = "BOT"; - if (typeof gameServer.clients[i].remoteAddress != 'undefined') { - ip = gameServer.clients[i].remoteAddress; + var ip = "[BOT]"; + if (socket.isConnected != null) { + if (socket.isConnected) { + ip = socket.remoteAddress; + } else { + ip = "[DISCONNECTED]"; + } } ip = fillChar(ip, ' ', 15); var protocol = gameServer.clients[i].packetHandler.protocol; From c5e66e31f1ca1bc5273fc590938b848a06619919 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 01:01:35 +0200 Subject: [PATCH 126/417] add IP limit feature --- src/GameServer.js | 117 ++++++++++++++++++++++++++------------------- src/gameserver.ini | 2 + 2 files changed, 69 insertions(+), 50 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 8051eb6d5..27d515001 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -49,57 +49,58 @@ function GameServer() { // Config this.config = { // Border - Right: X increases, Down: Y increases (as of 2015-05-20) - serverMaxConnections: 64, // Maximum amount of connections to the server. - serverPort: 443, // Server port - serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams - serverBots: 0, // Amount of player bots to spawn - serverViewBaseX: 1920, // Base client screen resolution. Used to calculate view area. Warning: high values may cause lag + serverMaxConnections: 64, // Maximum amount of connections to the server. (0 for no limit) + serverIpLimit: 4, // Maximum amount of connections from the same IP (0 for no limit) + serverPort: 443, // Server port + serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams + serverBots: 0, // Amount of player bots to spawn + serverViewBaseX: 1920, // Base client screen resolution. Used to calculate view area. Warning: high values may cause lag serverViewBaseY: 1080, - serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. - serverStatsUpdate: 60, // Amount of seconds per update for the server stats - serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections - serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. - serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. - borderLeft: 0, // Left border of map (Vanilla value: 0) - borderRight: 6000, // Right border of map (Vanilla value: 14142.135623730952) - borderTop: 0, // Top border of map (Vanilla value: 0) - borderBottom: 6000, // Bottom border of map (Vanilla value: 14142.135623730952) - spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) - foodSpawnAmount: 10, // The amount of food to spawn per interval - foodStartAmount: 100, // The starting amount of food in the map - foodMaxAmount: 500, // Maximum food cells on the map - foodMass: 1, // Starting food size (In mass) - foodMassGrow: 1, // Enable food mass grow ? + serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. + serverStatsUpdate: 60, // Amount of seconds per update for the server stats + serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections + serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. + serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. + borderLeft: 0, // Left border of map (Vanilla value: 0) + borderRight: 6000, // Right border of map (Vanilla value: 14142.135623730952) + borderTop: 0, // Top border of map (Vanilla value: 0) + borderBottom: 6000, // Bottom border of map (Vanilla value: 14142.135623730952) + spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) + foodSpawnAmount: 10, // The amount of food to spawn per interval + foodStartAmount: 100, // The starting amount of food in the map + foodMaxAmount: 500, // Maximum food cells on the map + foodMass: 1, // Starting food size (In mass) + foodMassGrow: 1, // Enable food mass grow ? foodMassGrowPossiblity: 50, // Chance for a food to has the ability to be self growing - foodMassLimit: 5, // Maximum mass for a food can grow - virusMinAmount: 10, // Minimum amount of viruses on the map. - virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. - virusStartMass: 100, // Starting virus size (In mass) - virusFeedAmount: 7, // Amount of times you need to feed a virus to shoot it - ejectMass: 13, // Mass of ejected cells - ejectMassCooldown: 3, // min ticks between ejects - ejectMassLoss: 15, // Mass lost when ejecting cells - ejectSpeed: 100, // Base speed of ejected cells - ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass - playerStartMass: 10, // Starting mass of the player cell. - playerBotGrowEnabled: 1, // If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass - playerMaxMass: 22500, // Maximum mass a player can have - playerMinMassEject: 32, // Mass required to eject a cell - playerMinMassSplit: 36, // Mass required to split - playerMaxCells: 16, // Max cells the player is allowed to have - playerRecombineTime: 30, // Base amount of seconds before a cell is allowed to recombine - playerMassAbsorbed: 1.0, // Fraction of player cell's mass gained upon eating - playerMassDecayRate: .002, // Amount of mass lost per second - playerMinMassDecay: 9, // Minimum mass for decay to occur - playerMaxNickLength: 15, // Maximum nick length - playerSpeed: 1, // Player speed multiplier - playerDisconnectTime: 60, // The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) - tourneyMaxPlayers: 12, // Maximum amount of participants for tournament style game modes - tourneyPrepTime: 10, // Amount of ticks to wait after all players are ready (1 tick = 1000 ms) - tourneyEndTime: 30, // Amount of ticks to wait after a player wins (1 tick = 1000 ms) - tourneyTimeLimit: 20, // Time limit of the game, in minutes. - tourneyAutoFill: 0, // If set to a value higher than 0, the tournament match will automatically fill up with bots after this amount of seconds - tourneyAutoFillPlayers: 1, // The timer for filling the server with bots will not count down unless there is this amount of real players + foodMassLimit: 5, // Maximum mass for a food can grow + virusMinAmount: 10, // Minimum amount of viruses on the map. + virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. + virusStartMass: 100, // Starting virus size (In mass) + virusFeedAmount: 7, // Amount of times you need to feed a virus to shoot it + ejectMass: 13, // Mass of ejected cells + ejectMassCooldown: 3, // min ticks between ejects + ejectMassLoss: 15, // Mass lost when ejecting cells + ejectSpeed: 100, // Base speed of ejected cells + ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass + playerStartMass: 10, // Starting mass of the player cell. + playerBotGrowEnabled: 1, // If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass + playerMaxMass: 22500, // Maximum mass a player can have + playerMinMassEject: 32, // Mass required to eject a cell + playerMinMassSplit: 36, // Mass required to split + playerMaxCells: 16, // Max cells the player is allowed to have + playerRecombineTime: 30, // Base amount of seconds before a cell is allowed to recombine + playerMassAbsorbed: 1.0, // Fraction of player cell's mass gained upon eating + playerMassDecayRate: .002, // Amount of mass lost per second + playerMinMassDecay: 9, // Minimum mass for decay to occur + playerMaxNickLength: 15, // Maximum nick length + playerSpeed: 1, // Player speed multiplier + playerDisconnectTime: 60, // The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) + tourneyMaxPlayers: 12, // Maximum amount of participants for tournament style game modes + tourneyPrepTime: 10, // Amount of ticks to wait after all players are ready (1 tick = 1000 ms) + tourneyEndTime: 30, // Amount of ticks to wait after a player wins (1 tick = 1000 ms) + tourneyTimeLimit: 20, // Time limit of the game, in minutes. + tourneyAutoFill: 0, // If set to a value higher than 0, the tournament match will automatically fill up with bots after this amount of seconds + tourneyAutoFillPlayers: 1, // The timer for filling the server with bots will not count down unless there is this amount of real players }; // Parse config this.loadConfig(); @@ -169,10 +170,26 @@ GameServer.prototype.onServerSocketOpen = function () { }; GameServer.prototype.onClientSocketOpen = function (ws) { - if (this.clients.length >= this.config.serverMaxConnections) { // Server full + var totalConnections = 0; + var ipConnections = 0; + for (var i = 0; i < this.clients.length; i++) { + var socket = this.clients[i]; + if (socket == null || socket.isConnected == null) + continue; + totalConnections++; + if (socket.remoteAddress == ws._socket.remoteAddress) + ipConnections++; + } + if (this.config.serverMaxConnections > 0 && totalConnections >= this.config.serverMaxConnections) { + // Server full ws.close(1000, "No slots"); return; } + if (this.config.serverIpLimit > 0 && ipConnections >= this.config.serverIpLimit) { + // IP limit reached + ws.close(1000, "IP limit reached"); + return; + } ws.isConnected = true; ws.remoteAddress = ws._socket.remoteAddress; ws.remotePort = ws._socket.remotePort; diff --git a/src/gameserver.ini b/src/gameserver.ini index 4c4d2198d..3a76f71f2 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -2,6 +2,7 @@ // Lines starting with slashes are comment lines // [Server] +// serverIpLimit: Controls the maximum connection amount from single IP (use 0 to disable) // serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games // serverBots: Amount of player bots to spawn (Experimental) // serverViewBase: Base view distance of players. Warning: high values may cause lag @@ -11,6 +12,7 @@ // serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. // serverMaxLB: Controls the maximum players displayed on the leaderboard. serverMaxConnections = 64 +serverIpLimit = 4 serverPort = 50000 serverGamemode = 0 serverBots = 0 From 73482c11056319396526f537435385be1dba6b9b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 01:53:53 +0200 Subject: [PATCH 127/417] anti-bot measurements (scramble cell id) --- src/GameServer.js | 6 +-- src/PlayerTracker.js | 7 +-- src/packet/AddNode.js | 5 +- src/packet/UpdateLeaderboard.js | 10 ++-- src/packet/UpdateNodes.js | 89 ++++++++++++++++++--------------- 5 files changed, 65 insertions(+), 52 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 27d515001..91671cdaf 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -251,7 +251,7 @@ GameServer.prototype.getNextNodeId = function() { if (this.lastNodeId > 2147483647) { this.lastNodeId = 1; } - return this.lastNodeId++; + return this.lastNodeId++ >>> 0; }; GameServer.prototype.getNewPlayerID = function() { @@ -259,7 +259,7 @@ GameServer.prototype.getNewPlayerID = function() { if (this.lastPlayerId > 2147483647) { this.lastPlayerId = 1; } - return this.lastPlayerId++; + return this.lastPlayerId++ >>> 0; }; GameServer.prototype.getRandomPosition = function() { @@ -338,7 +338,7 @@ GameServer.prototype.addNode = function(node) { if (node.owner && node.cellType != 3) { node.setColor(node.owner.color); node.owner.cells.push(node); - node.owner.socket.sendPacket(new Packet.AddNode(node)); + node.owner.socket.sendPacket(new Packet.AddNode(node.owner, node)); } // Special on-add actions diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 73da2550d..b4343b26c 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -52,6 +52,7 @@ function PlayerTracker(gameServer, socket) { // Scramble the coordinate system for anti-raga this.scrambleX = 0; this.scrambleY = 0; + this.scrambleId = 0; // Gamemode function if (gameServer) { @@ -72,6 +73,7 @@ function PlayerTracker(gameServer, socket) { this.scrambleX = Math.floor(maxScrambleX * Math.random()); this.scrambleY = Math.floor(maxScrambleY * Math.random()); } + this.scrambleId = (Math.random() * 0xFFFFFFFF) >>> 0; } } @@ -270,11 +272,10 @@ PlayerTracker.prototype.update = function () { // Send packet this.socket.sendPacket(new Packet.UpdateNodes( + this, this.nodeDestroyQueue, updateNodes, - nonVisibleNodes, - this.scrambleX, - this.scrambleY)); + nonVisibleNodes)); this.nodeDestroyQueue = []; // Reset destroy queue this.nodeAdditionQueue = []; // Reset addition queue diff --git a/src/packet/AddNode.js b/src/packet/AddNode.js index 5689f34ea..deb8d9ffa 100644 --- a/src/packet/AddNode.js +++ b/src/packet/AddNode.js @@ -1,4 +1,5 @@ -function AddNode(item) { +function AddNode(playerTracker, item) { + this.playerTracker = playerTracker; this.item = item; } @@ -8,6 +9,6 @@ AddNode.prototype.build = function(protocol) { // Only add player controlled cells with this packet or it will bug the camera var buffer = new Buffer(5); buffer.writeUInt8(0x20, 0); // Packet ID - buffer.writeUInt32LE(this.item.nodeId, 1); + buffer.writeUInt32LE((this.item.nodeId ^ this.playerTracker.scrambleId) >>> 0, 1); return buffer; }; diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index a52fe63b5..69806069c 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -23,7 +23,7 @@ UpdateLeaderboard.prototype.build = function (protocol) { UpdateLeaderboard.prototype.build48 = function (protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x31); // Packet ID - writer.writeUInt32(this.leaderboard.length >> 0); // Number of elements + writer.writeUInt32(this.leaderboard.length >>> 0); // Number of elements for (var i = 0; i < this.leaderboard.length; i++) { var item = this.leaderboard[i]; if (item == null) return null; // bad leaderboardm just don't send it @@ -45,7 +45,7 @@ UpdateLeaderboard.prototype.build48 = function (protocol) { UpdateLeaderboard.prototype.build49 = function (protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x31); // Packet ID - writer.writeUInt32(this.leaderboard.length >> 0); // Number of elements + writer.writeUInt32(this.leaderboard.length >>> 0); // Number of elements for (var i = 0; i < this.leaderboard.length; i++) { var item = this.leaderboard[i]; if (item == null) return null; // bad leaderboardm just don't send it @@ -54,10 +54,10 @@ UpdateLeaderboard.prototype.build49 = function (protocol) { name = name != null ? name : ""; var id = item == this.playerTracker ? 1 : 0; // protocol 6+ uses isMe flag if (protocol <= 5 && item.cells != null && item.cells.length > 0) { - id = item.cells[0].nodeId; // protocol 5- uses player cellId instead of isMe flag + id = item.cells[0].nodeId ^ this.playerTracker.scrambleId; // protocol 5- uses player cellId instead of isMe flag } - writer.writeUInt32(id >> 0); // isMe flag/cell ID + writer.writeUInt32(id >>> 0); // isMe flag/cell ID if (protocol <= 5) writer.writeStringZeroUnicode(name); else @@ -70,7 +70,7 @@ UpdateLeaderboard.prototype.build49 = function (protocol) { UpdateLeaderboard.prototype.build50 = function (protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x32); // Packet ID - writer.writeUInt32(this.leaderboard.length >> 0); // Number of elements + writer.writeUInt32(this.leaderboard.length >>> 0); // Number of elements for (var i = 0; i < this.leaderboard.length; i++) { var item = this.leaderboard[i]; if (item == null) return null; // bad leaderboardm just don't send it diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index b51017537..05bc85852 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -2,12 +2,11 @@ var BinaryWriter = require("./BinaryWriter"); -function UpdateNodes(destroyQueue, nodes, nonVisibleNodes, scrambleX, scrambleY) { +function UpdateNodes(playerTracker, destroyQueue, nodes, nonVisibleNodes) { + this.playerTracker = playerTracker; this.destroyQueue = destroyQueue; this.nodes = nodes; this.nonVisibleNodes = nonVisibleNodes; - this.scrambleX = scrambleX; - this.scrambleY = scrambleY; } module.exports = UpdateNodes; @@ -22,6 +21,10 @@ UpdateNodes.prototype.build = function (protocol) { // protocol 4 UpdateNodes.prototype.build4 = function () { + var scrambleX = this.playerTracker.scrambleX; + var scrambleY = this.playerTracker.scrambleY; + var scrambleId = this.playerTracker.scrambleId; + var writer = new BinaryWriter(); writer.writeUInt8(0x10); // Packet ID @@ -31,15 +34,15 @@ UpdateNodes.prototype.build4 = function () { if (node == null) continue; deadCells.push(node); } - writer.writeUInt16(deadCells.length >> 0); // EatRecordCount + writer.writeUInt16(deadCells.length >>> 0); // EatRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; var hunterId = 0; if (node.getKiller()) { hunterId = node.getKiller().nodeId; } - writer.writeUInt32(hunterId >> 0); // Hunter ID - writer.writeUInt32(node.nodeId >> 0); // Prey ID + writer.writeUInt32((hunterId^scrambleId) >>> 0); // Hunter ID + writer.writeUInt32((node.nodeId^scrambleId) >>> 0); // Prey ID } for (var i = 0; i < this.nodes.length; i++) { @@ -47,18 +50,18 @@ UpdateNodes.prototype.build4 = function () { if (node == null || node.nodeId == 0) continue; - var cellX = node.position.x + this.scrambleX; - var cellY = node.position.y + this.scrambleY; + var cellX = node.position.x + scrambleX; + var cellY = node.position.y + scrambleY; var cellName = node.getName(); // Write update record - writer.writeUInt32(node.nodeId >> 0); // Cell ID + writer.writeUInt32((node.nodeId^scrambleId) >>> 0); // Cell ID writer.writeInt16(cellX >> 0); // Coordinate X writer.writeInt16(cellY >> 0); // Coordinate Y writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) - writer.writeUInt8(node.color.r >> 0); // Color R - writer.writeUInt8(node.color.g >> 0); // Color G - writer.writeUInt8(node.color.b >> 0); // Color B + writer.writeUInt8(node.color.r >>> 0); // Color R + writer.writeUInt8(node.color.g >>> 0); // Color G + writer.writeUInt8(node.color.b >>> 0); // Color B var flags = 0; if (node.spiked & 1) @@ -67,7 +70,7 @@ UpdateNodes.prototype.build4 = function () { flags |= 0x10; // isAgitated if (node.cellType == 3) flags |= 0x20; // isEjected - writer.writeUInt8(flags >> 0); // Flags + writer.writeUInt8(flags >>> 0); // Flags writer.writeStringZeroUnicode(cellName); // Name } @@ -78,16 +81,20 @@ UpdateNodes.prototype.build4 = function () { if (node == null) continue; deadCells.push(node); } - writer.writeUInt32(deadCells.length >> 0); // RemoveRecordCount + writer.writeUInt32(deadCells.length >>> 0); // RemoveRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; - writer.writeUInt32(node.nodeId); // Cell ID + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID } return writer.ToBuffer(); }; // protocol 5 UpdateNodes.prototype.build5 = function () { + var scrambleX = this.playerTracker.scrambleX; + var scrambleY = this.playerTracker.scrambleY; + var scrambleId = this.playerTracker.scrambleId; + var writer = new BinaryWriter(); writer.writeUInt8(0x10); // Packet ID @@ -97,15 +104,15 @@ UpdateNodes.prototype.build5 = function () { if (node == null) continue; deadCells.push(node); } - writer.writeUInt16(deadCells.length >> 0); // EatRecordCount + writer.writeUInt16(deadCells.length >>> 0); // EatRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; var hunterId = 0; if (node.getKiller()) { hunterId = node.getKiller().nodeId; } - writer.writeUInt32(hunterId >> 0); // Hunter ID - writer.writeUInt32(node.nodeId >> 0); // Prey ID + writer.writeUInt32((hunterId ^ scrambleId) >>> 0); // Hunter ID + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Prey ID } for (var i = 0; i < this.nodes.length; i++) { @@ -113,19 +120,19 @@ UpdateNodes.prototype.build5 = function () { if (node == null || node.nodeId == 0) continue; - var cellX = node.position.x + this.scrambleX; - var cellY = node.position.y + this.scrambleY; + var cellX = node.position.x + scrambleX; + var cellY = node.position.y + scrambleY; var skinName = node.getSkin(); var cellName = node.getName(); // Write update record - writer.writeUInt32(node.nodeId >> 0); // Cell ID + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID writer.writeInt32(cellX >> 0); // Coordinate X writer.writeInt32(cellY >> 0); // Coordinate Y writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) - writer.writeUInt8(node.color.r >> 0); // Color R - writer.writeUInt8(node.color.g >> 0); // Color G - writer.writeUInt8(node.color.b >> 0); // Color B + writer.writeUInt8(node.color.r >>> 0); // Color R + writer.writeUInt8(node.color.g >>> 0); // Color G + writer.writeUInt8(node.color.b >>> 0); // Color B var flags = 0; if (node.spiked & 1) @@ -136,7 +143,7 @@ UpdateNodes.prototype.build5 = function () { flags |= 0x10; // isAgitated if (node.cellType == 3) flags |= 0x20; // isEjected - writer.writeUInt8(flags >> 0); // Flags + writer.writeUInt8(flags >>> 0); // Flags if (flags & 0x04) writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 @@ -150,16 +157,20 @@ UpdateNodes.prototype.build5 = function () { if (node == null) continue; deadCells.push(node); } - writer.writeUInt32(deadCells.length >> 0); // RemoveRecordCount + writer.writeUInt32(deadCells.length >>> 0); // RemoveRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; - writer.writeUInt32(node.nodeId >> 0); // Cell ID + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID } return writer.ToBuffer(); }; // protocol 6 UpdateNodes.prototype.build6 = function () { + var scrambleX = this.playerTracker.scrambleX; + var scrambleY = this.playerTracker.scrambleY; + var scrambleId = this.playerTracker.scrambleId; + var writer = new BinaryWriter(); writer.writeUInt8(0x10); // Packet ID @@ -169,15 +180,15 @@ UpdateNodes.prototype.build6 = function () { if (node == null) continue; deadCells.push(node); } - writer.writeUInt16(deadCells.length >> 0); // EatRecordCount + writer.writeUInt16(deadCells.length >>> 0); // EatRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; var hunterId = 0; if (node.getKiller()) { hunterId = node.getKiller().nodeId; } - writer.writeUInt32(hunterId >> 0); // Hunter ID - writer.writeUInt32(node.nodeId >> 0); // Prey ID + writer.writeUInt32((hunterId ^ scrambleId) >>> 0); // Hunter ID + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Prey ID } for (var i = 0; i < this.nodes.length; i++) { @@ -185,13 +196,13 @@ UpdateNodes.prototype.build6 = function () { if (node == null || node.nodeId == 0) continue; - var cellX = node.position.x + this.scrambleX; - var cellY = node.position.y + this.scrambleY; + var cellX = node.position.x + scrambleX; + var cellY = node.position.y + scrambleY; var skinName = node.getSkin(); var cellName = node.getName(); // Write update record - writer.writeUInt32(node.nodeId >> 0); // Cell ID + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID writer.writeInt32(cellX >> 0); // Coordinate X writer.writeInt32(cellY >> 0); // Coordinate Y writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) @@ -209,12 +220,12 @@ UpdateNodes.prototype.build6 = function () { flags |= 0x10; // isAgitated if (node.cellType == 3) flags |= 0x20; // isEjected - writer.writeUInt8(flags >> 0); // Flags + writer.writeUInt8(flags >>> 0); // Flags if (flags & 0x02) { - writer.writeUInt8(node.color.r >> 0); // Color R - writer.writeUInt8(node.color.g >> 0); // Color G - writer.writeUInt8(node.color.b >> 0); // Color B + writer.writeUInt8(node.color.r >>> 0); // Color R + writer.writeUInt8(node.color.g >>> 0); // Color G + writer.writeUInt8(node.color.b >>> 0); // Color B } if (flags & 0x04) writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 @@ -228,10 +239,10 @@ UpdateNodes.prototype.build6 = function () { if (node == null) continue; deadCells.push(node); } - writer.writeUInt16(deadCells.length >> 0); // RemoveRecordCount + writer.writeUInt16(deadCells.length >>> 0); // RemoveRecordCount for (var i = 0; i < deadCells.length; i++) { var node = deadCells[i]; - writer.writeUInt32(node.nodeId >> 0); // Cell ID + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID } return writer.ToBuffer(); }; From ee0e0f2f434086ff2dcad8e75a332e2345da1644 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 02:28:19 +0200 Subject: [PATCH 128/417] fix serverGamemode for border message --- src/PacketHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index c7156c873..c7e960857 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -47,7 +47,7 @@ PacketHandler.prototype.handleMessage = function(message) { right: this.gameServer.config.borderRight + this.socket.playerTracker.scrambleX, bottom: this.gameServer.config.borderBottom + this.socket.playerTracker.scrambleY }; - this.socket.sendPacket(new Packet.SetBorder(border, 0, "MultiOgar 1.0")); + this.socket.sendPacket(new Packet.SetBorder(border, this.gameServer.config.serverGamemode, "MultiOgar 1.0")); // Send welcome message this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); if (this.protocol < 4) { From eaf44e154cd4b533ee1b4bdec3ae225e39545c7f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 02:36:59 +0200 Subject: [PATCH 129/417] SetBorder refactoring --- src/PacketHandler.js | 10 +++++----- src/PlayerTracker.js | 10 +++++----- src/packet/SetBorder.js | 11 ++++++----- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index c7e960857..9d94e3cf3 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -42,12 +42,12 @@ PacketHandler.prototype.handleMessage = function(message) { // Send handshake response this.socket.sendPacket(new Packet.ClearAll()); var border = { - left: this.gameServer.config.borderLeft + this.socket.playerTracker.scrambleX, - top: this.gameServer.config.borderTop + this.socket.playerTracker.scrambleY, - right: this.gameServer.config.borderRight + this.socket.playerTracker.scrambleX, - bottom: this.gameServer.config.borderBottom + this.socket.playerTracker.scrambleY + left: this.gameServer.config.borderLeft, + top: this.gameServer.config.borderTop, + right: this.gameServer.config.borderRight, + bottom: this.gameServer.config.borderBottom }; - this.socket.sendPacket(new Packet.SetBorder(border, this.gameServer.config.serverGamemode, "MultiOgar 1.0")); + this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, border, this.gameServer.config.serverGamemode, "MultiOgar 1.0")); // Send welcome message this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); if (this.protocol < 4) { diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index b4343b26c..612e66f6f 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -238,12 +238,12 @@ PlayerTracker.prototype.update = function () { // TODO: need to send it more rarely (once per second or something like that) // also need to make sure that we send it before first cell update //var border = { - // left: Math.max(this.viewBox.left - this.viewBox.halfWidth, this.gameServer.config.borderLeft) + this.scrambleX, - // top: Math.max(this.viewBox.top - this.viewBox.halfHeight, this.gameServer.config.borderTop) + this.scrambleY, - // right: Math.min(this.viewBox.right + this.viewBox.halfWidth, this.gameServer.config.borderRight) + this.scrambleX, - // bottom: Math.min(this.viewBox.bottom + this.viewBox.halfHeight, this.gameServer.config.borderBottom) + this.scrambleY + // left: Math.max(this.viewBox.left - this.viewBox.halfWidth, this.gameServer.config.borderLeft), + // top: Math.max(this.viewBox.top - this.viewBox.halfHeight, this.gameServer.config.borderTop), + // right: Math.min(this.viewBox.right + this.viewBox.halfWidth, this.gameServer.config.borderRight), + // bottom: Math.min(this.viewBox.bottom + this.viewBox.halfHeight, this.gameServer.config.borderBottom) //}; - //this.socket.sendPacket(new Packet.SetBorder(border)); + //this.socket.sendPacket(new Packet.SetBorder(this, border)); } catch (err) { console.error(err); } diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index 8164ee253..ac8bf8ea6 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -2,7 +2,8 @@ var BinaryWriter = require("./BinaryWriter"); -function SetBorder(border, gameType, serverName) { +function SetBorder(playerTracker, border, gameType, serverName) { + this.playerTracker = playerTracker; this.border = border; this.gameType = gameType; this.serverName = serverName; @@ -13,10 +14,10 @@ module.exports = SetBorder; SetBorder.prototype.build = function(protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x40); // Packet ID - writer.writeDouble(this.border.left); - writer.writeDouble(this.border.top); - writer.writeDouble(this.border.right); - writer.writeDouble(this.border.bottom); + writer.writeDouble(this.border.left + this.playerTracker.scrambleX); + writer.writeDouble(this.border.top + this.playerTracker.scrambleY); + writer.writeDouble(this.border.right + this.playerTracker.scrambleX); + writer.writeDouble(this.border.bottom + this.playerTracker.scrambleY); if (this.gameType != null) { writer.writeUInt32(this.gameType >> 0); var name = this.serverName; From cb12ed127af62b65f122b2cee57a436e8f685c23 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 03:06:14 +0200 Subject: [PATCH 130/417] color generator comments --- src/GameServer.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 91671cdaf..0a1750405 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -318,6 +318,7 @@ GameServer.prototype.getRandomColor = function() { default: rgb = { r: v, g: p, b: q }; break } } + // check color range rgb.r = Math.max(rgb.r, 0); rgb.g = Math.max(rgb.g, 0); rgb.b = Math.max(rgb.b, 0); @@ -325,9 +326,9 @@ GameServer.prototype.getRandomColor = function() { rgb.g = Math.min(rgb.g, 1); rgb.b = Math.min(rgb.b, 1); return { - r: rgb.r * 255 >> 0, - g: rgb.g * 255 >> 0, - b: rgb.b * 255 >> 0 + r: (rgb.r * 255) >>> 0, + g: (rgb.g * 255) >>> 0, + b: (rgb.b * 255) >>> 0 }; }; From 9e5bd4922bfcc24964f794d37addf890fe5b37b0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 03:28:30 +0200 Subject: [PATCH 131/417] UpdateCamera message refactoring; reset free roam on join to reduce cpu load from free roam spectators --- src/PlayerTracker.js | 6 ++++-- src/gamemodes/Mode.js | 3 +-- src/packet/UpdatePosition.js | 11 ++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 612e66f6f..955aa89ac 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -152,6 +152,7 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.setName(name); this.setSkin(skin); this.spectate = false; + this.freeRoam = false; this.socket.sendPacket(new Packet.ClearAll()); this.collidingNodes = []; @@ -449,8 +450,9 @@ PlayerTracker.prototype.checkBorderPass = function() { PlayerTracker.prototype.sendPosPacket = function() { // TODO: Send packet elsewhere so it is sent more often this.socket.sendPacket(new Packet.UpdatePosition( - this.centerPos.x + this.scrambleX, - this.centerPos.y + this.scrambleY, + this, + this.centerPos.x, + this.centerPos.y, this.getScale() )); }; diff --git a/src/gamemodes/Mode.js b/src/gamemodes/Mode.js index fc0cf76f6..486adc3b3 100644 --- a/src/gamemodes/Mode.js +++ b/src/gamemodes/Mode.js @@ -38,8 +38,7 @@ Mode.prototype.onPlayerSpawn = function(gameServer, player) { Mode.prototype.pressQ = function(gameServer, player) { // Called when the Q key is pressed if (player.spectate) { - if (!player.freeRoam) player.freeRoam = true; - else player.freeRoam = false; + player.freeRoam = !player.freeRoam; } }; diff --git a/src/packet/UpdatePosition.js b/src/packet/UpdatePosition.js index 43cf03877..3cafc4a81 100644 --- a/src/packet/UpdatePosition.js +++ b/src/packet/UpdatePosition.js @@ -1,7 +1,8 @@ -function UpdatePosition(x, y, size) { +function UpdatePosition(playerTracker, x, y, scale) { + this.playerTracker = playerTracker, this.x = x; this.y = y; - this.size = size; + this.scale = scale; } module.exports = UpdatePosition; @@ -11,11 +12,11 @@ UpdatePosition.prototype.build = function(protocol) { var offset = 0; buffer.writeUInt8(0x11, offset); offset += 1; - buffer.writeFloatLE(this.x, offset); + buffer.writeFloatLE(this.x + this.playerTracker.scrambleX, offset); offset += 4; - buffer.writeFloatLE(this.y, offset); + buffer.writeFloatLE(this.y + this.playerTracker.scrambleY, offset); offset += 4; - buffer.writeFloatLE(this.size, offset); + buffer.writeFloatLE(this.scale, offset); offset += 4; return buffer; }; From 5b37013c3d0aed48be13981673eab334f5f48572 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 03:42:37 +0200 Subject: [PATCH 132/417] remove unused variable --- src/GameServer.js | 1 - src/gameserver.ini | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 0a1750405..e36ec5347 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -80,7 +80,6 @@ function GameServer() { ejectMass: 13, // Mass of ejected cells ejectMassCooldown: 3, // min ticks between ejects ejectMassLoss: 15, // Mass lost when ejecting cells - ejectSpeed: 100, // Base speed of ejected cells ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass playerStartMass: 10, // Starting mass of the player cell. playerBotGrowEnabled: 1, // If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass diff --git a/src/gameserver.ini b/src/gameserver.ini index 3a76f71f2..9e4108c1c 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -50,12 +50,10 @@ virusFeedAmount = 7 // ejectMass: Mass of ejected cells // ejectMassCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) // ejectMassLoss: Mass lost when ejecting cells -// ejectSpeed: Base speed of ejected cells // ejectSpawnPlayer: Chance for a player to spawn from ejected mass ejectMass = 13 ejectMassCooldown = 3 ejectMassLoss = 15 -ejectSpeed = 100 ejectSpawnPlayer = 50 // [Player] From 0f349626d5275188a7cdd89c88b27e83157cdd47 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 04:00:46 +0200 Subject: [PATCH 133/417] fix default ejected mass --- src/GameServer.js | 2 +- src/gameserver.ini | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index e36ec5347..4cf4b1d29 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -77,7 +77,7 @@ function GameServer() { virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. virusStartMass: 100, // Starting virus size (In mass) virusFeedAmount: 7, // Amount of times you need to feed a virus to shoot it - ejectMass: 13, // Mass of ejected cells + ejectMass: 13.69, // Mass of ejected cells ejectMassCooldown: 3, // min ticks between ejects ejectMassLoss: 15, // Mass lost when ejecting cells ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass diff --git a/src/gameserver.ini b/src/gameserver.ini index 9e4108c1c..7e1243d7c 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -47,11 +47,11 @@ virusStartMass = 100 virusFeedAmount = 7 // [Ejected Mass] -// ejectMass: Mass of ejected cells +// ejectMass: Mass of ejected cells (vanilla 13.69) // ejectMassCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) // ejectMassLoss: Mass lost when ejecting cells // ejectSpawnPlayer: Chance for a player to spawn from ejected mass -ejectMass = 13 +ejectMass = 13.69 ejectMassCooldown = 3 ejectMassLoss = 15 ejectSpawnPlayer = 50 From 4275452a1a2d0216c2b554a14b7f8c1a44542ed6 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 04:12:23 +0200 Subject: [PATCH 134/417] fix default start mass and min player mass --- src/GameServer.js | 4 ++-- src/gameserver.ini | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 4cf4b1d29..c201bc600 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -81,7 +81,7 @@ function GameServer() { ejectMassCooldown: 3, // min ticks between ejects ejectMassLoss: 15, // Mass lost when ejecting cells ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass - playerStartMass: 10, // Starting mass of the player cell. + playerStartMass: 10.24, // Starting mass of the player cell. playerBotGrowEnabled: 1, // If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass playerMaxMass: 22500, // Maximum mass a player can have playerMinMassEject: 32, // Mass required to eject a cell @@ -90,7 +90,7 @@ function GameServer() { playerRecombineTime: 30, // Base amount of seconds before a cell is allowed to recombine playerMassAbsorbed: 1.0, // Fraction of player cell's mass gained upon eating playerMassDecayRate: .002, // Amount of mass lost per second - playerMinMassDecay: 9, // Minimum mass for decay to occur + playerMinMassDecay: 10.24, // Minimum mass for decay to occur playerMaxNickLength: 15, // Maximum nick length playerSpeed: 1, // Player speed multiplier playerDisconnectTime: 60, // The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) diff --git a/src/gameserver.ini b/src/gameserver.ini index 7e1243d7c..3bc59bda3 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -47,7 +47,7 @@ virusStartMass = 100 virusFeedAmount = 7 // [Ejected Mass] -// ejectMass: Mass of ejected cells (vanilla 13.69) +// ejectMass: Mass of ejected cells (vanilla mass 13.69, size 37) // ejectMassCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) // ejectMassLoss: Mass lost when ejecting cells // ejectSpawnPlayer: Chance for a player to spawn from ejected mass @@ -57,6 +57,7 @@ ejectMassLoss = 15 ejectSpawnPlayer = 50 // [Player] +// playerStartMass: start mass (vanilla 10.24, size 32 - minimum player size) // playerRecombineTime: Base amount of ticks before a cell is allowed to recombine (1 tick = 1000 milliseconds) // playerBotGrowEnabled: If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass // playerMassAbsorbed: Fraction of a player cell that gets absorbed upon eating (i.e., 1 = 100%, 0.8 = 80%, etc) @@ -64,7 +65,7 @@ ejectSpawnPlayer = 50 // playerMinMassDecay: Minimum mass for decay to occur // playerDisconnectTime: The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) // playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) -playerStartMass = 10 +playerStartMass = 10.24 playerBotGrowEnabled = 1 playerMaxMass = 22500 playerMinMassEject = 32 @@ -73,7 +74,7 @@ playerMaxCells = 16 playerRecombineTime = 30 playerMassAbsorbed = 1.0 playerMassDecayRate = .002 -playerMinMassDecay = 9 +playerMinMassDecay = 10.24 playerMaxNickLength = 15 playerSpeed = 1 playerDisconnectTime = 60 From 91d6bf72fdc9cda490f9980d3d097f3ecab097e2 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 04:38:26 +0200 Subject: [PATCH 135/417] fix food size limit --- src/GameServer.js | 2 +- src/gameserver.ini | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index c201bc600..e8f2cee1e 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -72,7 +72,7 @@ function GameServer() { foodMass: 1, // Starting food size (In mass) foodMassGrow: 1, // Enable food mass grow ? foodMassGrowPossiblity: 50, // Chance for a food to has the ability to be self growing - foodMassLimit: 5, // Maximum mass for a food can grow + foodMassLimit: 4, // Maximum mass for a food can grow virusMinAmount: 10, // Minimum amount of viruses on the map. virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. virusStartMass: 100, // Starting virus size (In mass) diff --git a/src/gameserver.ini b/src/gameserver.ini index 3bc59bda3..a352d06d9 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -33,6 +33,8 @@ borderBottom = 6000 // [Spawn] // Each interval is 1 tick (50 ms) +// foodMass: vanilla 1, size 10 +// foodMassLimit: vanilla 4, size 20 spawnInterval = 20 foodSpawnAmount = 10 foodStartAmount = 100 @@ -40,7 +42,7 @@ foodMaxAmount = 500 foodMass = 1 foodMassGrow = 1 foodMassGrowPossiblity = 50 -foodMassLimit = 5 +foodMassLimit = 4 virusMinAmount = 10 virusMaxAmount = 50 virusStartMass = 100 From cf84b80ec8fef84dc020a9f754a74da66127e601 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 08:02:15 +0200 Subject: [PATCH 136/417] add server tracker support --- src/GameServer.js | 62 ++++++++++++++++++++++++++++++++++++++++++++++ src/gameserver.ini | 2 ++ 2 files changed, 64 insertions(+) diff --git a/src/GameServer.js b/src/GameServer.js index e8f2cee1e..2f03e434a 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -3,6 +3,7 @@ var WebSocket = require('ws'); var http = require('http'); var fs = require("fs"); var ini = require('./modules/ini.js'); +var os = require("os"); // Project imports var Packet = require('./packet'); @@ -52,6 +53,7 @@ function GameServer() { serverMaxConnections: 64, // Maximum amount of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum amount of connections from the same IP (0 for no limit) serverPort: 443, // Server port + serverTracker: 0, // Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams serverBots: 0, // Amount of player bots to spawn serverViewBaseX: 1920, // Base client screen resolution. Used to calculate view area. Warning: high values may cause lag @@ -479,6 +481,7 @@ GameServer.prototype.timerLoop = function () { GameServer.prototype.mainLoop = function() { var tStart = new Date().getTime(); + // Loop main functions this.updateMoveEngine(); this.updateSpawn(); @@ -487,6 +490,11 @@ GameServer.prototype.mainLoop = function() { this.updateClients(); this.updateLeaderboard(); + // ping server tracker + if (this.config.serverTracker && (this.getTick() % (30000/40)) == 0) { + this.pingServerTracker(); + } + //var t = process.hrtime(); //this.updateMoveEngine(); //this.t1 = toTime(process.hrtime(t)); @@ -1095,3 +1103,57 @@ WebSocket.prototype.sendPacket = function(packet) { this.removeAllListeners(); } }; + +// Ping the server tracker. +// To list us on the server tracker located at http://ogar.mivabe.nl/master +// Should be called every 30 seconds +GameServer.prototype.pingServerTracker = function () { + // Get server statistics + var totalPlayers = 0; + var alivePlayers = 0; + var spectatePlayers = 0; + for (var i = 0; i < this.clients.length; i++) { + var socket = this.clients[i]; + if (socket == null || !socket.isConnected) + continue; + totalPlayers++; + if (socket.playerTracker.cells.length > 0) + alivePlayers++; + else + spectatePlayers++; + } + /* Sending Ping */ + // Why don't just to use JSON? + var data = 'current_players=' + totalPlayers + + '&alive=' + alivePlayers + + '&spectators=' + spectatePlayers + + '&max_players=' + this.config.serverMaxConnections + + '&sport=' + this.config.serverPort + + '&gamemode=[*] ' + this.gameMode.name + // we add [*] to indicate that this is multi-server + '&agario=true' + // protocol version + '&name=Unnamed Server' + // we cannot use it, because other value will be used as dns name + '&opp=' + os.platform() + ' ' + os.arch() + // "win32 x64" + '&uptime=' + process.uptime() + // Number of seconds server has been running + '&start_time=' + this.startTime; + var options ={ + host: 'ogar.mivabe.nl', + port: '80', + path: '/master', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(data) + } + }; + var req = http.request(options, function (res) { + if (res.statusCode != 200) { + console.log("\u001B[1m\u001B[31m[Tracker Error] " + res.statusCode + "\u001B[0m"); + } + }); + req.on('error', function (e) { + console.log("\u001B[1m\u001B[31m[Tracker Error] " + e.message + "\u001B[0m"); + }); + req.write(data); + req.end() +}; + diff --git a/src/gameserver.ini b/src/gameserver.ini index a352d06d9..eb769ad1b 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -3,6 +3,7 @@ // [Server] // serverIpLimit: Controls the maximum connection amount from single IP (use 0 to disable) +// serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) // serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games // serverBots: Amount of player bots to spawn (Experimental) // serverViewBase: Base view distance of players. Warning: high values may cause lag @@ -14,6 +15,7 @@ serverMaxConnections = 64 serverIpLimit = 4 serverPort = 50000 +serverTracker = 0 serverGamemode = 0 serverBots = 0 serverViewBaseX = 1920 From fd94123100e8e1bec86f2130204f01b612ad3109 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 08:04:20 +0200 Subject: [PATCH 137/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c9bfa9089..e0997be3d 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends inv * Color generator replaced with hsv model; * Memory leaks fixed; * Performance improved and optimized +* Added support for server tracker ogar.mivabe.nl/master Most of the physics code from original ogar were rewritten. The physics engine in MultiOgar is pretty close to the old vanilla physics. \ No newline at end of file From 089cec4438d7d486f68e89236461ab0c0222db2e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 09:44:03 +0200 Subject: [PATCH 138/417] fix + optimization for mass consume calcualtions --- src/entity/Cell.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index ad0fc4766..5cf2c0550 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -82,10 +82,11 @@ Cell.prototype.addMass = function(n) { var splitMass = this.getMass() / 2; var randomAngle = Math.random() * 6.28; // Get random angle this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass); - } else { - this.setMass(Math.min(this.getMass(), this.gameServer.config.playerMaxMass)); } - this.setMass(this.getMass() + n); + var newMass = this.getMass() + n; + if (newMass > this.gameServer.config.playerMaxMass) + newMass = this.gameServer.config.playerMaxMass; + this.setMass(newMass); }; Cell.prototype.getSpeed = function() { From a3da94e1d3d3b2cdcfd3c281c4521757bc3fbb29 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 10:21:10 +0200 Subject: [PATCH 139/417] fix mass limit behavior; fix mergeOverride bug (continuous merging when mass limit exceeded) --- src/GameServer.js | 18 +++++++++++------- src/entity/Cell.js | 16 ++++++++-------- src/entity/Virus.js | 4 ++-- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 2f03e434a..b60ff99cb 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -740,10 +740,18 @@ GameServer.prototype.cellEating = function(cell) { var list = this.getCellsInRange(cell); for (var j = 0; j < list.length; j++) { var check = list[j]; + + // Disable mergeOverride on the last merging cell + // We need to disable it before onCosume to prevent merging loop + // (onConsume may cause split for big mass) + if (check.owner != null && check.owner.cells.length <= 2) { + cell.owner.mergeOverride = false; + cell.owner.mergeOverrideDuration = 0; + } // Consume effect check.onConsume(cell, this); - + // Remove cell check.setKiller(cell); this.removeNode(check); @@ -767,12 +775,12 @@ GameServer.prototype.splitCells = function(client) { //if (angle == 0) angle = Math.PI / 2; if (isNaN(angle)) angle = 0; - if (this.createPlayerCell(client, cell, angle, cell.getMass() / 2) == true) + if (this.splitPlayerCell(client, cell, angle, cell.getMass() / 2) == true) splitCells++; } }; -GameServer.prototype.createPlayerCell = function(client, parent, angle, mass) { +GameServer.prototype.splitPlayerCell = function(client, parent, angle, mass) { // Returns boolean whether a cell has been split or not. You can use this in the future. if (client.cells.length >= this.config.playerMaxCells) { @@ -1010,10 +1018,6 @@ GameServer.prototype.updateCells = function() { if (!cell) continue; // Recombining - if (cell.owner.cells.length == 1) { - cell.owner.mergeOverride = false; - cell.owner.mergeOverrideDuration = 0; - } cell.updateRemerge(this); // Mass decay diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 5cf2c0550..84cb4b515 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -76,17 +76,17 @@ Cell.prototype.setMass = function (mass) { this.owner.massChanged(); }; -Cell.prototype.addMass = function(n) { +Cell.prototype.addMass = function (n) { // Check if the cell needs to autosplit before adding mass - if (this.getMass() > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) { - var splitMass = this.getMass() / 2; - var randomAngle = Math.random() * 6.28; // Get random angle - this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass); - } var newMass = this.getMass() + n; - if (newMass > this.gameServer.config.playerMaxMass) - newMass = this.gameServer.config.playerMaxMass; this.setMass(newMass); + if (this.getMass() <= this.gameServer.config.playerMaxMass) + return; + if (this.owner.cells.length >= this.gameServer.config.playerMaxCells) + return; + var splitMass = this.getMass() / 2; + var randomAngle = Math.random() * 6.28; // Get random angle + this.gameServer.splitPlayerCell(this.owner, this, randomAngle, splitMass); }; Cell.prototype.getSpeed = function() { diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 27853c76d..48443e296 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -80,13 +80,13 @@ Virus.prototype.onConsume = function(consumer, gameServer) { for (var k = 0; k < bigSplits.length; k++) { angle = Math.random() * 6.28; // Random directions - gameServer.createPlayerCell(client, consumer, angle, bigSplits[k]); + gameServer.splitPlayerCell(client, consumer, angle, bigSplits[k]); } // Splitting for (var k = 0; k < numSplits; k++) { angle = Math.random() * 6.28; // Random directions - gameServer.createPlayerCell(client, consumer, angle, splitMass); + gameServer.splitPlayerCell(client, consumer, angle, splitMass); } //// Prevent consumer cell from merging with other cells From dce6c8f87fecea28cb51a9c330a160b6aae07e6f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 10:44:05 +0200 Subject: [PATCH 140/417] update readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e0997be3d..4707ed271 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ URL | Description --- | --- http://ogar.mivabe.nl/master | MivaBe, tracks a lot of servers +Now you can enable MultiOgar to be listed on server tracker. +Just set serverTracker = 1 in the gameserver.ini and your server will appears +on this page: http://ogar.mivabe.nl/master +If you don't want to include your server to tracker list. +Just set serverTracker = 0 and server will not make ping to server tracker. + ###Ogar clients Just replace ip:port in the url to play From aab771eec85541e0238a160eceb0710e4408e2cc Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 13 Jun 2016 10:47:37 +0200 Subject: [PATCH 141/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4707ed271..48f61d6f5 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Now you can enable MultiOgar to be listed on server tracker. Just set serverTracker = 1 in the gameserver.ini and your server will appears on this page: http://ogar.mivabe.nl/master If you don't want to include your server to tracker list. -Just set serverTracker = 0 and server will not make ping to server tracker. +Just set serverTracker = 0 and server will not ping the server tracker. ###Ogar clients From ba16dcfb3b8f2f9bd70085cbfa5e35d8347a4cf7 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 14 Jun 2016 04:40:19 +0200 Subject: [PATCH 142/417] fix bot AI --- src/ai/BotPlayer.js | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index d3dcbf8e3..e5479b010 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -82,28 +82,45 @@ BotPlayer.prototype.decide = function(cell) { var influence = 0; if (check.cellType == 0) { // Player cell - if (this.gameServer.gameMode.haveTeams && (cell.owner.team == check.owner.team)) influence = 0; // Same team cell - else if (cell.getMass() / 1.3 > check.getMass()) influence = check.getSize() * 2.5; // Can eat it - else if (check.getMass() / 1.3 > cell.getMass()) influence = -check.getSize(); // Can eat me + if (this.gameServer.gameMode.haveTeams && (cell.owner.team == check.owner.team)) { + // Same team cell + influence = 0; + } + else if (cell.getSize() > check.getSize() * 1.15) {//cell.getMass() / 1.3 > check.getMass()) { + // Can eat it + influence = check.getSize() * 2.5; + } + else if (check.getSize() >= cell.getSize() * 1.15) {//check.getMass() / 1.3 > cell.getMass()) { + // Can eat me + influence = -check.getSize(); + } } else if (check.cellType == 1) { // Food influence = 1; } else if (check.cellType == 2) { // Virus - if (cell.getMass() / 1.3 > check.getMass()) { + if (cell.getSize() > check.getSize() * 1.15) {//cell.getMass() / 1.3 > check.getMass()) { // Can eat it - if (this.cells.length == this.gameServer.config.playerMaxCells) influence = check.getSize() * 2.5; // Won't explode - else influence = -1; // Can explode + if (this.cells.length == this.gameServer.config.playerMaxCells) { + // Won't explode + influence = check.getSize() * 2.5; + } + else { + // Can explode + influence = -1; + } } } else if (check.cellType == 3) { // Ejected mass - if (cell.getMass() / 1.3 > check.getMass()) influence = check.getSize(); + if (cell.getSize() > check.getSize() * 1.15)// cell.getMass() / 1.3 > check.getMass()) + influence = check.getSize(); } else { influence = check.getSize(); // Might be TeamZ } // Apply influence if it isn't 0 or my cell - if (influence == 0 || cell.owner == check.owner) continue; + if (influence == 0 || cell.owner == check.owner) + continue; // Calculate separation between cell and check var checkPos = check.position; @@ -125,8 +142,12 @@ BotPlayer.prototype.decide = function(cell) { var force = displacement.normalize().scale(influence); // Splitting conditions - if (check.cellType == 0 && cell.getMass() / 2.6 > check.getMass() && cell.getMass() / 5 < check.getMass() && - (!split) && this.splitCooldown == 0 && this.cells.length < 3) { + if (check.cellType == 0 && + cell.getMass() / 2.6 > check.getMass() && + cell.getMass() / 5 < check.getMass() && + (!split) && + this.splitCooldown == 0 && + this.cells.length < 3) { var endDist = Math.max(this.splitDistance(cell), cell.getSize() * 4); From 382153cae980db0d3b20b1258921c4a3c83730d6 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 14 Jun 2016 17:27:37 +0200 Subject: [PATCH 143/417] reworked collision code; improve physics; fix experimental; small optimizations & checks --- src/GameServer.js | 305 ++++++++++++++++------------------ src/PacketHandler.js | 3 - src/PlayerTracker.js | 15 +- src/entity/Cell.js | 169 ++++++++++--------- src/entity/PlayerCell.js | 73 ++------ src/entity/Virus.js | 4 - src/gamemodes/Experimental.js | 34 ++-- src/gamemodes/TeamX.js | 6 +- src/gamemodes/TeamZ.js | 2 +- 9 files changed, 273 insertions(+), 338 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index b60ff99cb..e0b681e67 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -349,11 +349,11 @@ GameServer.prototype.addNode = function(node) { // Add to visible nodes for (var i = 0; i < this.clients.length; i++) { var client = this.clients[i].playerTracker; - if (client == null) continue; + if (client == null || !client.socket.isConnected) continue; // client.nodeAdditionQueue is only used by human players, not bots // for bots it just gets collected forever, using ever-increasing amounts of memory - if ('_socket' in client.socket && node.visibleCheck(client.viewBox)) { + if (node.visibleCheck(client.viewBox)) { client.nodeAdditionQueue.push(node); } } @@ -612,14 +612,6 @@ GameServer.prototype.abs = function (x) { GameServer.prototype.checkCellCollision = function(cell, check) { // Returns manifold which contains info about cell's collisions. // Returns null if there is no collision - - // do not affect splitted cell for 1 sec - if (cell.boostDistance > 0 || check.boostDistance > 0) { - var tick = this.getTick(); - if (cell.getAge(tick) < 15 || check.getAge(tick) < 15) - return null; - } - var r = cell.getSize() + check.getSize(); var dx = check.position.x - cell.position.x; var dy = check.position.y - cell.position.y; @@ -643,10 +635,11 @@ GameServer.prototype.resolveCollision = function (manifold) { // distance from cell1 to cell2 var d = Math.sqrt(manifold.squared); if (d <= 0) return; + var invd = 1 / d; // normal - var nx = manifold.dx / d; - var ny = manifold.dy / d; + var nx = manifold.dx * invd; + var ny = manifold.dy * invd; // body penetration distance var penetration = manifold.r - d; @@ -659,8 +652,9 @@ GameServer.prototype.resolveCollision = function (manifold) { // body impulse var totalMass = manifold.cell1.getMass() + manifold.cell2.getMass(); if (totalMass <= 0) return; - var impulse1 = manifold.cell2.getMass() / totalMass; - var impulse2 = manifold.cell1.getMass() / totalMass; + var invTotalMass = 1 / totalMass; + var impulse1 = manifold.cell2.getMass() * invTotalMass; + var impulse2 = manifold.cell1.getMass() * invTotalMass; // apply extrusion force manifold.cell1.position.x -= px * impulse1; @@ -669,60 +663,165 @@ GameServer.prototype.resolveCollision = function (manifold) { manifold.cell2.position.y += py * impulse2; }; +GameServer.prototype.processCollision = function (manifold) { + var minCell = manifold.cell1; + var maxCell = manifold.cell2; + if (minCell.getSize() > maxCell.getSize()) { + minCell = manifold.cell2; + maxCell = manifold.cell1; + } + // check if any cell already eaten + if (minCell.inRange) return; + if (maxCell.inRange) return; + + if (minCell.owner && minCell.owner == maxCell.owner) { + // collision owned/owned => ignore or resolve or remerge + + var tick = this.getTick(); + if ((minCell.boostDistance > 0 && minCell.getAge(tick) < 15) || + (maxCell.boostDistance > 0 && maxCell.getAge(tick) < 15)) { + // just splited => ignore + return; + } + if (!minCell.owner.mergeOverride) { + // not force remerge => check if can remerge + if (!minCell.canRemerge() || !maxCell.canRemerge()) { + // cannot remerge => resolve + this.resolveCollision(manifold); + return; + } + } + } else { + // collision owned/enemy => check if can eat + + // Can't eat self ejected mass because it still in boost mode (vanilla) + //if (minCell.ejector == maxCell && minCell.boostDistance > 0) + // return; + + // Team check + if (this.gameMode.haveTeams) { + if (minCell.owner && maxCell.owner && minCell.owner.getTeam() == maxCell.owner.getTeam()) { + // cannot eat team member + this.resolveCollision(manifold); + return; + } + } + // Size check + if (minCell.getSize() * 1.15 > maxCell.getSize()) { + // too large => can't eat + return; + } + } + // check distance + var eatingRange = maxCell.getSize() - minCell.getSize() / Math.PI; + //var d = Math.sqrt(manifold.squared); // distance + //if (d >= eatingRange) { + if (manifold.squared >= eatingRange * eatingRange) { + // too far => can't eat + return; + } + // Now maxCell can eat minCell + minCell.inRange = true; + + // Disable mergeOverride on the last merging cell + // We need to disable it before onCosume to prevent merging loop + // (onConsume may cause split for big mass) + if (minCell.owner != null && minCell.owner.cells.length <= 2) { + minCell.owner.mergeOverride = false; + } + + // Consume effect + minCell.onConsume(maxCell, this); + + // Remove cell + minCell.setKiller(maxCell); + this.removeNode(minCell); +}; + GameServer.prototype.updateMoveEngine = function() { + var border = { + left: this.config.borderLeft, + top: this.config.borderTop, + right: this.config.borderRight, + bottom: this.config.borderBottom + }; // Move player cells for (var i in this.clients) { var client = this.clients[i].playerTracker; - // Sort client's cells by ascending mass - var sorted = []; - for (var i = 0; i < client.cells.length; i++) { - var node = client.cells[i]; - if (node == null) continue; - sorted.push(node); - } - - sorted.sort(function(a, b) { - return b.getMass() - a.getMass(); + client.cells.sort(function (a, b) { + return a.getSize() - b.getSize(); }); + var sorted = client.cells; - // Go cell by cell + // Move for (var i = 0; i < sorted.length; i++) { - sorted[i].calcMove(client.mouse.x, client.mouse.y, this); - } - for (var i = 0; i < sorted.length; i++) { - sorted[i].calcMoveBoost(this.config); + var cell = sorted[i]; + cell.calcMove(client.mouse.x, client.mouse.y, this); + cell.checkBorder(border); + cell.calcMoveBoost(border); } + + // Scan for collision and process it for (var i = 0; i < sorted.length; i++) { - var cell = sorted[i]; - - // Collision with own cells - cell.collision(this); - - // Cell eating - this.cellEating(cell); + var cell1 = sorted[i]; + // check collision owned/owned + for (var j = 0; j < client.cells.length; j++) { + var cell2 = client.cells[j]; + if (cell2 == null || cell2.inRange || cell1 == cell2) + continue; + + var manifold = this.checkCellCollision(cell1, cell2); + if (manifold == null) continue; + this.processCollision(manifold); + } + // check collision owned/enemy + for (var j = 0; j < client.collidingNodes.length; j++) { + var cell2 = client.collidingNodes[j]; + if (cell2 == null || cell2.inRange || cell1 == cell2) + continue; + + // TODO: remove + // Added skip to comply with oldstyle collision check + // If we remove it virus can eat player cell, so it should be fixed + if (cell1.getSize() < cell2.getSize()) continue; + + var manifold = this.checkCellCollision(cell1, cell2); + if (manifold == null) continue; + this.processCollision(manifold); + } + this.gameMode.onCellMove(cell1, this); } } - // A system to move cells not controlled by players (ex. viruses, ejected mass) for (var i = 0; i < this.movingNodes.length; i++) { var check = this.movingNodes[i]; - + // Recycle unused nodes while (check == null && i < this.movingNodes.length) { // Remove moving cells that are undefined this.movingNodes.splice(i, 1); check = this.movingNodes[i]; } - + if (i >= this.movingNodes.length) continue; - + if (check.boostDistance > 0) { check.onAutoMove(this); // If the cell has enough move ticks, then move it - check.calcMoveBoost(this.config); + check.calcMoveBoost(border); + //var cell1 = check; + //// check collision for ejected/ejected + //for (var j = 0; j < this.movingNodes.length; j++) { + // var cell2 = this.movingNodes[j]; + // if (cell2 == null) continue; + // if (cell1 == cell2) continue; // ignore self + // var manifold = this.checkCellCollision(cell1, cell2); + // if (manifold == null) continue; + // this.resolveCollision(manifold); + //} } else { // Auto move is done check.moveDone(this); @@ -735,29 +834,6 @@ GameServer.prototype.updateMoveEngine = function() { } }; -GameServer.prototype.cellEating = function(cell) { - // Check if cells nearby - var list = this.getCellsInRange(cell); - for (var j = 0; j < list.length; j++) { - var check = list[j]; - - // Disable mergeOverride on the last merging cell - // We need to disable it before onCosume to prevent merging loop - // (onConsume may cause split for big mass) - if (check.owner != null && check.owner.cells.length <= 2) { - cell.owner.mergeOverride = false; - cell.owner.mergeOverrideDuration = 0; - } - - // Consume effect - check.onConsume(cell, this); - - // Remove cell - check.setKiller(cell); - this.removeNode(check); - } -}; - GameServer.prototype.setAsMovingNode = function(node) { if (this.movingNodes.indexOf(node) == -1) this.movingNodes.push(node); @@ -877,107 +953,6 @@ GameServer.prototype.shootVirus = function(parent) { this.setAsMovingNode(newVirus); }; -GameServer.prototype.getCellsInRange = function(cell) { - var list = []; - var squareR = cell.getSquareSize(); // Get cell squared radius - - // Loop through all cells that are colliding with the player's cells - for (var i = 0; i < cell.owner.collidingNodes.length; i++) { - var check = cell.owner.collidingNodes[i]; - if (check === null) continue; - - // if something already collided with this cell, don't check for other collisions - if (check.inRange) - continue; - - // Can't eat itself - if (cell.nodeId == check.nodeId) - continue; - - // Can't eat self ejected mass, because it still in boost mode - if (check.ejector == cell && check.boostDistance > 0) - continue; - - // Eating range - var xs = cell.position.x - check.position.x, - ys = cell.position.y - check.position.y, - sqDist = xs * xs + ys * ys, - dist = Math.sqrt(sqDist); - - // Use a more reliant version for pellets - // Might be a bit slower but it can be eaten with any mass - if (check.cellType == 1) { - if (dist + check.getSize() / 3.14 > cell.getSize()) { - // Too far away - continue; - } - else { - // Add to list of cells nearby - list.push(check); - - // Something is about to eat this cell; no need to check for other collisions with it - check.inRange = true; - continue; // No need to look for type and calculate if eaten again - } - } - - // Cell type check - Cell must be bigger than this number times the mass of the cell being eaten - var multiplier = 1.3; - - switch (check.getType()) { - case 1: // Food cell - list.push(check); - check.inRange = true; // skip future collision checks for this food - continue; - case 0: // Players - // Can't eat self if it's not time to recombine yet - if (check.owner == cell.owner) { - // If one of cells can't merge - if (!cell.canRemerge() || !check.canRemerge()) { - // Check if merge command was triggered on this client - if (!cell.owner.mergeOverride) continue; - } - - multiplier = 1.00; - } - - // Can't eat team members - if (this.gameMode.haveTeams) { - if (!check.owner) { // Error check - continue; - } - - if ((check.owner != cell.owner) && (check.owner.getTeam() == cell.owner.getTeam())) { - continue; - } - } - break; - default: - break; - } - - // Make sure the cell is big enough to be eaten. - if ((check.getMass() * multiplier) > cell.getMass()) { - continue; - } - - // Eating range = radius of eating cell / 2 - 31% of the radius of the cell being eaten - var eatingRange = cell.getSize() - check.getEatingRange(); - - if (dist < eatingRange) { - // Add to list of cells nearby - list.push(check); - - // Something is about to eat this cell; no need to check for other collisions with it - check.inRange = true; - } else { - // Not in eating range - continue; - } - } - return list; -}; - GameServer.prototype.getNearestVirus = function(cell) { // More like getNearbyVirus var virus = null; diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 9d94e3cf3..d5a403672 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -83,17 +83,14 @@ PacketHandler.prototype.handleMessage = function(message) { // protocol late 5, 6, 7 client.mouse.x = reader.readInt32() - client.scrambleX; client.mouse.y = reader.readInt32() - client.scrambleY; - client.movePacketTriggered = true; } else if (message.length == 9) { // early protocol 5 client.mouse.x = reader.readInt16() - client.scrambleX; client.mouse.y = reader.readInt16() - client.scrambleY; - client.movePacketTriggered = true; } else if (message.length == 21) { // protocol 4 client.mouse.x = reader.readDouble() - client.scrambleX; client.mouse.y = reader.readDouble() - client.scrambleY; - client.movePacketTriggered = true; if (isNaN(client.mouse.x)) client.mouse.x = client.centerPos.x; if (isNaN(client.mouse.y)) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 955aa89ac..eb7aa0d7d 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -22,10 +22,6 @@ function PlayerTracker(gameServer, socket) { x: 0, y: 0 }; - this.shouldMoveCells = true; // False if the mouse packet wasn't triggered - this.notMoved = false; // If one of cells have been moved after splitting this is triggered - this.movePacketTriggered = false; - this.mouseCells = []; // For individual cell movement this.tickLeaderboard = 0; this.tickViewBox = 0; @@ -174,13 +170,6 @@ PlayerTracker.prototype.update = function () { // First reset colliding nodes this.collidingNodes = []; - // Move packet update - if (this.movePacketTriggered) { - this.movePacketTriggered = false; - this.shouldMoveCells = true; - } else { - this.shouldMoveCells = false; - } // Actions buffer (So that people cant spam packets) if (this.socket.packetHandler.pressSpace) { // Split cell if (!this.mergeOverride) this.gameServer.gameMode.pressSpace(this.gameServer, this); @@ -439,11 +428,11 @@ PlayerTracker.prototype.checkBorderPass = function() { // A check while in free-roam mode to avoid player going into nothingness if (this.centerPos.x < this.gameServer.config.borderLeft) this.centerPos.x = this.gameServer.config.borderLeft; - if (this.centerPos.x > this.gameServer.config.borderRight) + else if (this.centerPos.x > this.gameServer.config.borderRight) this.centerPos.x = this.gameServer.config.borderRight; if (this.centerPos.y < this.gameServer.config.borderTop) this.centerPos.y = this.gameServer.config.borderTop; - if (this.centerPos.y > this.gameServer.config.borderBottom) + else if (this.centerPos.y > this.gameServer.config.borderBottom) this.centerPos.y = this.gameServer.config.borderBottom; }; diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 84cb4b515..e1bf762ed 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -113,10 +113,6 @@ Cell.prototype.getAge = function (tick) { return Math.max(0, tick - this.tickOfBirth); } -Cell.prototype.getEatingRange = function() { - return 0; // 0 for ejected cells -}; - Cell.prototype.getKiller = function() { return this.killedBy; }; @@ -182,7 +178,7 @@ Cell.prototype.setBoost = function (distance, angle) { this.setAngle(angle); }; -Cell.prototype.calcMoveBoost = function (config) { +Cell.prototype.calcMoveBoost = function (border) { if (this.boostDistance <= 0) return; var speed = Math.sqrt(this.boostDistance * this.boostDistance / 100); @@ -190,74 +186,97 @@ Cell.prototype.calcMoveBoost = function (config) { speed = Math.min(speed, this.boostDistance); // avoid overlap 0 this.boostDistance -= speed; if (this.boostDistance <= 1) this.boostDistance = 0; - var x = this.position.x + this.boostDirection.x * speed; - var y = this.position.y + this.boostDirection.y * speed; - - // Border check - Bouncy physics - var radius = 40; - if (x < config.borderLeft && this.position.x != x) { - // Flip angle horizontally - Left side - this.setAngle(6.28 - this.getAngle()); - if (x == this.position.x && y == this.position.y) { - // movement vector is missing - x = config.borderLeft; - } else { - var p = this.getLineIntersection( - this.position.x, this.position.y, x, y, - config.borderLeft, config.borderBottom, - config.borderLeft, config.borderTop); - x = p.x; - y = p.y; - } + var dx = this.boostDirection.x * speed; + var dy = this.boostDirection.y * speed; + + // Border bouncy physics + var r = this.getSize() / 2; + var borderCell = { + left: border.left + r, + top: border.top + r, + right: border.right - r, + bottom: border.bottom - r + }; + this.moveReflected(dx, dy, borderCell); +} + +Cell.prototype.moveReflected = function (dx, dy, border) { + var cx = this.position.x; + var cy = this.position.y; + var x = cx + dx; + var y = cy + dy; + if (isNaN(x) || isNaN(y)) { + // something is going wrong + // TODO: border center? + console.log("\u001B[1m\u001B[31m[Error] Cell.moveReflected failed (NaN)"); + return; } - if (x > config.borderRight && this.position.x != x) { - // Flip angle horizontally - Right side - this.setAngle(6.28 - this.getAngle()); - if (x == this.position.x && y == this.position.y) { - // movement vector is missing - x = config.borderRight; - } else { - var p = this.getLineIntersection( - this.position.x, this.position.y, x, y, - config.borderRight, config.borderBottom, - config.borderRight, config.borderTop); - x = p.x; - y = p.y; - } + if (dx == 0 && dy == 0) return; // zero move :) + + var pleft = x >= border.left ? null : this.getLineIntersection( + cx, cy, x, y, + border.left, border.top, border.left, border.bottom); + var pright = x <= border.right ? null : this.getLineIntersection( + cx, cy, x, y, + border.right, border.top, border.right, border.bottom); + var ptop = y >= border.top ? null : this.getLineIntersection( + cx, cy, x, y, + border.left, border.top, border.right, border.top); + var pbottom = y <= border.bottom ? null : this.getLineIntersection( + cx, cy, x, y, + border.left, border.bottom, border.right, border.bottom); + var ph = pleft != null ? pleft : pright; + var pv = ptop != null ? ptop : pbottom; + var p = ph != null ? ph : pv; + if (p == null) { + // inside border + this.position.x = x; + this.position.y = y; + return; } - if (y < config.borderTop && this.position.y != y) { - // Flip angle vertically - Top side - this.setAngle((this.getAngle() <= 3.14) ? 3.14 - this.getAngle() : 9.42 - this.getAngle()); - if (x == this.position.x && y == this.position.y) { - // movement vector is missing - y = config.borderTop; - } else { - var p = this.getLineIntersection( - this.position.x, this.position.y, x, y, - config.borderRight, config.borderTop, - config.borderLeft, config.borderTop); - x = p.x; - y = p.y; - } + if (ph && pv) { + // two border lines intersection => get nearest point + var hdx = ph.x - cx; + var hdy = ph.y - cx; + var vdx = pv.x - cx; + var vdy = pv.y - cx; + if (hdx * hdx + hdy * hdy < vdx * vdx + vdy * vdy) + p = ph; + else + p = pv; } - if (y > config.borderBottom && this.position.y != y) { - // Flip angle vertically - Bottom side - this.setAngle((this.getAngle() <= 3.14) ? 3.14 - this.getAngle() : 9.42 - this.getAngle()); - if (x == this.position.x && y == this.position.y) { - // movement vector is missing - y = config.borderBottom; - } else { - var p = this.getLineIntersection( - this.position.x, this.position.y, x, y, - config.borderRight, config.borderBottom, - config.borderLeft, config.borderBottom); - x = p.x; - y = p.y; - } + var angle = this.getAngle(); + if (p == ph) { + // left/right border reflection + angle = 2 * Math.PI - angle; + } else { + // top/bottom border reflection + angle = angle <= Math.PI ? Math.PI - angle : 3 * Math.PI - angle; } - this.position.x = x; - this.position.y = y; -} + this.setAngle(angle); + this.position.x = p.x; + this.position.y = p.y; + // calculate rest of distance + var lx = p.x - cx; + var ly = p.y - cy; + var ldx = dx - lx; + var ldy = dy - ly; + var l = Math.sqrt(ldx * ldx + ldy * ldy); + this.boostDistance += l; +}; + +Cell.prototype.checkBorder = function (border) { + var r = this.getSize() / 2; + if (this.position.x < border.left + r) + this.position.x = border.left + r; + else if (this.position.x > border.right - r) + this.position.x = border.right - r; + if (this.position.y < border.top + r) + this.position.y = border.top + r; + else if (this.position.y > border.bottom - r) + this.position.y = border.bottom - r; +}; + // Override these @@ -293,11 +312,13 @@ Cell.prototype.getLineIntersection = function (p0x, p0y, p1x, p1y, p2x, p2y, p3x var z2 = p3x - p2x; var w1 = p1y - p0y; var w2 = p3y - p2y; - var k2 = (z1 * (p2y - p0y) + w1 * (p0x - p2x)) / (w1 * z2 - z1 * w2); - return { - x: p2x + z2 * k2, - y: p2y + w2 * k2 - }; + var k1 = w1 * z2 - z1 * w2; + if (k1 == 0) return null; + var k2 = (z1 * (p2y - p0y) + w1 * (p0x - p2x)) / k1; + var px = p2x + z2 * k2; + var py = p2y + w2 * k2; + if (isNaN(px) || isNaN(py)) return null; + return { x: px, y: py }; } Cell.prototype.abs = function(x) { diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index d30fd92d8..ddde1bb38 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -25,10 +25,6 @@ PlayerCell.prototype.updateRemerge = function (gameServer) { this._canRemerge = false; return; } - if (this.owner.mergeOverride) { // force merge from console - this._canRemerge = true; - return; - } var tick = gameServer.getTick(); var age = this.getAge(tick); if (age < 3) { @@ -37,6 +33,11 @@ PlayerCell.prototype.updateRemerge = function (gameServer) { return; } var baseTtr = gameServer.config.playerRecombineTime; // default baseTtr = 30 + if (baseTtr == 0) { + // instant merge + this._canRemerge = true; + return; + } var ttr = Math.max(baseTtr, (this.getSize() * 0.2) >> 0); // ttr in seconds // seconds to ticks (tickStep = 0.040 sec) ttr /= 0.040; @@ -50,76 +51,30 @@ PlayerCell.prototype.canRemerge = function () { // Movement PlayerCell.prototype.calcMove = function (x, y, gameServer) { - // No mouse update - if (!this.owner.shouldMoveCells && this.owner.notMoved) - return; - this.owner.notMoved = false; - var dx = x - this.position.x; var dy = y - this.position.y; var squared = dx * dx + dy * dy; - if (squared < 1) return; // stop threshold + if (squared == 0) return; // distance var d = Math.sqrt(squared); + + // normal + var invd = 1 / d; + var nx = dx * invd; + var ny = dy * invd; + // normalized distance (0..1) d = Math.min(d, 32) / 32; - var speed = this.getSpeed() * d; if (speed <= 0) return; - var angle = Math.atan2(dx, dy); - if (isNaN(angle)) return; - // Move cell - this.position.x += speed * Math.sin(angle); - this.position.y += speed * Math.cos(angle); -}; - -PlayerCell.prototype.collision = function(gameServer) { - var config = gameServer.config; - var r = this.getSize(); // Cell radius - - // Collision check for other cells - for (var i = 0; i < this.owner.cells.length; i++) { - var cell = this.owner.cells[i]; - - if (cell == null) continue; // Error - if (this.nodeId == cell.nodeId) continue; - - if (!cell.canRemerge() || !this.canRemerge()) { - // Cannot remerge - Collision with your own cells - - var manifold = gameServer.checkCellCollision(this, cell); // Calculation info - if (manifold != null) { // Collided - // Call gameserver's function to collide cells - gameServer.resolveCollision(manifold); - } - } - } - - gameServer.gameMode.onCellMove(this, gameServer); - - // Check to ensure we're not passing the world border (shouldn't get closer than a quarter of the cell's diameter) - if (this.position.x < config.borderLeft + r / 2) { - this.position.x = config.borderLeft + r / 2; - } - if (this.position.x > config.borderRight - r / 2) { - this.position.x = config.borderRight - r / 2; - } - if (this.position.y < config.borderTop + r / 2) { - this.position.y = config.borderTop + r / 2; - } - if (this.position.y > config.borderBottom - r / 2) { - this.position.y = config.borderBottom - r / 2; - } + this.position.x += nx * speed; + this.position.y += ny * speed; }; // Override -PlayerCell.prototype.getEatingRange = function() { - return this.getSize() / 3.14; -}; - PlayerCell.prototype.onConsume = function(consumer, gameServer) { // Add an inefficiency for eating other players' cells var factor = ( consumer.owner === this.owner ? 1 : gameServer.config.playerMassAbsorbed ); diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 48443e296..088fdada0 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -32,10 +32,6 @@ Virus.prototype.feed = function(feeder, gameServer) { // Main Functions -Virus.prototype.getEatingRange = function() { - return this.getSize() / 3.14; // 0 for ejected cells -}; - Virus.prototype.onConsume = function(consumer, gameServer) { var client = consumer.owner; diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 255abe159..41e65f8f4 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -78,7 +78,7 @@ Experimental.prototype.spawnMotherCell = function(gameServer) { } // Spawn if no cells are colliding - var m = new MotherCell(gameServer.getNextNodeId(), null, pos, this.motherCellMass); + var m = new MotherCell(gameServer.getNextNodeId(), null, pos, this.motherCellMass, gameServer); gameServer.addNode(m); } }; @@ -156,10 +156,6 @@ function MotherCell() { // Temporary - Will be in its own file if Zeach decides MotherCell.prototype = new Cell(); // Base -MotherCell.prototype.getEatingRange = function() { - return this.getSize() / 3.14; -}; - MotherCell.prototype.update = function(gameServer) { if (Math.random() * 100 > 97) { var maxFood = Math.random() * 2; // Max food spawned per tick @@ -208,20 +204,30 @@ MotherCell.prototype.checkEat = function(gameServer) { } }; -MotherCell.prototype.checkEatCell = function(check, safeMass, gameServer) { +MotherCell.prototype.checkEatCell = function (check, safeMass, gameServer) { if ((check.getType() == 1) || (check.getMass() > safeMass)) { // Too big to be consumed or check is a food cell return; } - + // Very simple yet very powerful - var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y); - var allowDist = this.getSize() - check.getEatingRange(); - if (dist < allowDist) { - // Eat it - gameServer.removeNode(check); - this.setMass(this.getMass() + check.getMass()); - } + //var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y); + //var allowDist = this.getSize() - check.getEatingRange(); + //if (dist < allowDist) { + // // Eat it + // gameServer.removeNode(check); + // this.setMass(this.getMass() + check.getMass()); + //} + if (this.getSize() <= check.getSize() * 1.15) + return; + var eatingRange = this.getSize() - check.getSize() / Math.PI; + var dx = this.position.x - check.position.x; + var dy = this.position.y - check.position.y; + if (dx * dx + dy * dy >= eatingRange * eatingRange) + return + // Eat it + gameServer.removeNode(check); + this.setMass(this.getMass() + check.getMass()); }; MotherCell.prototype.abs = function(n) { diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 6790e8500..24e7d7e95 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -239,7 +239,7 @@ TeamX.prototype.onServerInit = function(gameServer) { var ys = Math.pow(check.position.y - cell.position.y, 2); var dist = Math.sqrt(xs + ys); - var eatingRange = cell.getSize() - check.getEatingRange(); // Eating range = radius of eating cell + 40% of the radius of the cell being eaten + var eatingRange = cell.getSize() - check.getSize() / Math.PI; // Eating range = radius of eating cell + 40% of the radius of the cell being eaten if (dist > eatingRange) { // Not in eating range continue; @@ -340,10 +340,6 @@ function MotherCell() { // Temporary - Will be in its own file if Zeach decides MotherCell.prototype = new Cell(); // Base -MotherCell.prototype.getEatingRange = function() { - return this.getSize() * .5; -}; - MotherCell.prototype.update = function(gameServer) { // Add mass this.setMass(this.getMass() + .25); diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 368b51a66..46b27c397 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -494,7 +494,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { var ys = Math.pow(check.position.y - cell.position.y, 2); var dist = Math.sqrt(xs + ys); - var eatingRange = cell.getSize() - check.getEatingRange(); // Eating range = radius of eating cell + 40% of the radius of the cell being eaten + var eatingRange = cell.getSize() - check.getSize() / Math.PI; // Eating range = radius of eating cell + 40% of the radius of the cell being eaten if (dist > eatingRange) { // Not in eating range continue; From f2bfb37b904ee01e9cc906b70af0f736e834ca86 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 16 Jun 2016 14:01:20 +0200 Subject: [PATCH 144/417] use strict --- src/packet/BinaryReader.js | 16 +++++++++------- src/packet/BinaryWriter.js | 16 +++++++++------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/packet/BinaryReader.js b/src/packet/BinaryReader.js index 7cd2c5d47..43567514e 100644 --- a/src/packet/BinaryReader.js +++ b/src/packet/BinaryReader.js @@ -1,10 +1,12 @@ -/* -* Simple BinaryReader is a minimal tool to read binary stream. -* Useful for binary deserialization. -* -* Copyright (c) 2016 Barbosik https://github.com/Barbosik -* License: Apache License, Version 2.0 -*/ +'use strict'; +/* + * Simple BinaryReader is a minimal tool to read binary stream. + * Useful for binary deserialization. + * + * Copyright (c) 2016 Barbosik https://github.com/Barbosik + * License: Apache License, Version 2.0 + */ + function BinaryReader(buffer) { this._offset = 0; this._buffer = new Buffer(buffer); diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index a5be611f8..a7cf3140e 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -1,10 +1,12 @@ -/* -* Simple BinaryWriter is a minimal tool to write binary stream with unpredictable size. -* Useful for binary serialization. -* -* Copyright (c) 2016 Barbosik https://github.com/Barbosik -* License: Apache License, Version 2.0 -*/ +'use strict'; +/* + * Simple BinaryWriter is a minimal tool to write binary stream with unpredictable size. + * Useful for binary serialization. + * + * Copyright (c) 2016 Barbosik https://github.com/Barbosik + * License: Apache License, Version 2.0 + */ + function BinaryWriter() { this._writers = []; this._length = 0; From 94f982befeb558e4ec7725e1a793aa043078472f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 16 Jun 2016 17:54:03 +0200 Subject: [PATCH 145/417] new physics engine; quad-tree search; deep performance optimizations --- src/GameServer.js | 396 +++++++++++++++++++++------------- src/PlayerTracker.js | 188 ++++++---------- src/ai/BotPlayer.js | 12 -- src/entity/Cell.js | 144 +++++++------ src/entity/EjectedMass.js | 19 +- src/entity/PlayerCell.js | 20 +- src/entity/Virus.js | 10 +- src/gamemodes/Experimental.js | 58 +---- src/gamemodes/TeamX.js | 9 +- src/gamemodes/TeamZ.js | 14 +- src/gamemodes/Zombie.js | 7 +- src/modules/CommandList.js | 4 +- 12 files changed, 441 insertions(+), 440 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index e0b681e67..ee6bf1942 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -4,6 +4,7 @@ var http = require('http'); var fs = require("fs"); var ini = require('./modules/ini.js'); var os = require("os"); +var QuadNode = require('./QuadNode.js'); // Project imports var Packet = require('./packet'); @@ -26,6 +27,7 @@ function GameServer() { this.nodesVirus = []; // Virus nodes this.nodesEjected = []; // Ejected mass nodes this.nodesPlayer = []; // Nodes controlled by players + this.quadTree = null; this.currentFood = 0; this.movingNodes = []; // For move engine @@ -151,6 +153,14 @@ GameServer.prototype.onServerSocketError = function (error) { }; GameServer.prototype.onServerSocketOpen = function () { + var bound = { + minx: this.config.borderLeft, + miny: this.config.borderTop, + maxx: this.config.borderRight, + maxy: this.config.borderBottom + }; + this.quadTree = new QuadNode(bound, 4, 1024); + // Spawn starting food this.startingFood(); @@ -277,21 +287,31 @@ GameServer.prototype.getRandomSpawn = function(mass) { var size = Math.sqrt(mass * 100); var pos = this.getRandomPosition(); var unsafe = this.willCollide(size, pos, mass == this.config.virusStartMass); - var attempt = 1; + if (!unsafe) return pos; - // Prevent stack overflow by counting attempts - while (true) { - if (!unsafe || attempt >= 15) break; - pos = this.getRandomPosition(); + // just shift offset and try again + var attempt = 1; + var maxAttempt = 4; + var w = this.config.borderRight - this.config.borderLeft; + var h = this.config.borderBottom - this.config.borderTop; + var cx = this.config.borderLeft + w / 2; + var cy = this.config.borderTop + w / 2; + var dirx = pos.x < cx ? 1 : -1; + var diry = pos.y < cy ? 1 : -1; + var stepx = w / (2 * maxAttempt); + var stepy = h / (2 * maxAttempt); + while (unsafe && attempt < maxAttempt) { + pos.x += stepx * dirx; + pos.y += stepy * diry; unsafe = this.willCollide(size, pos, mass == this.config.virusStartMass); attempt++; } - // If it reached attempt 15, warn the user - if (attempt >= 14) { - console.log("[Server] Entity was force spawned near viruses/playercells after 15 attempts."); - console.log("[Server] If this message keeps appearing, check your config, especially start masses for players and viruses."); - } + // If it reached attempt maxAttempt, warn the user + //if (unsafe) { + // console.log("[Server] Entity was force spawned near viruses/playercells after "+attempt+" attempts."); + // console.log("[Server] If this message keeps appearing, check your config, especially start masses for players and viruses."); + //} return pos; }; @@ -333,7 +353,44 @@ GameServer.prototype.getRandomColor = function() { }; }; +GameServer.prototype.updateNodeQuad = function (node) { + var quadItem = node.quadItem; + // check for change + if (node.position.x == quadItem.x && + node.position.y == quadItem.y && + node.getSize() == quadItem.size) { + // no change + return; + } + // update quadTree + quadItem.x = node.position.x; + quadItem.y = node.position.y; + quadItem.size = node.getSize(); + quadItem.bound = { + minx: node.quadItem.x - node.quadItem.size, + miny: node.quadItem.y - node.quadItem.size, + maxx: node.quadItem.x + node.quadItem.size, + maxy: node.quadItem.y + node.quadItem.size + }; + this.quadTree.update(quadItem); +}; + + GameServer.prototype.addNode = function(node) { + node.quadItem = { + cell: node, + x: node.position.x, + y: node.position.y, + size: node.getSize() + }; + node.quadItem.bound = { + minx: node.quadItem.x - node.quadItem.size, + miny: node.quadItem.y - node.quadItem.size, + maxx: node.quadItem.x + node.quadItem.size, + maxy: node.quadItem.y + node.quadItem.size + }; + this.quadTree.insert(node.quadItem); + this.nodes.push(node); // Adds to the owning player's screen excluding ejected cells @@ -345,21 +402,16 @@ GameServer.prototype.addNode = function(node) { // Special on-add actions node.onAdd(this); - - // Add to visible nodes - for (var i = 0; i < this.clients.length; i++) { - var client = this.clients[i].playerTracker; - if (client == null || !client.socket.isConnected) continue; - - // client.nodeAdditionQueue is only used by human players, not bots - // for bots it just gets collected forever, using ever-increasing amounts of memory - if (node.visibleCheck(client.viewBox)) { - client.nodeAdditionQueue.push(node); - } - } }; GameServer.prototype.removeNode = function(node) { + if (node.quadItem == null) { + throw new TypeError("GameServer.removeNode: attempt to remove invalid node!"); + } + node.isRemoved = true; + this.quadTree.remove(node.quadItem); + node.quadItem = null; + // Remove from main nodes list var index = this.nodes.indexOf(node); if (index != -1) { @@ -374,15 +426,6 @@ GameServer.prototype.removeNode = function(node) { // Special on-remove actions node.onRemove(this); - - // Animation when eating - for (var i = 0; i < this.clients.length; i++) { - var client = this.clients[i].playerTracker; - if (client == null) continue; - - // Remove from client - client.nodeDestroyQueue.push(node); - } }; GameServer.prototype.updateSpawn = function() { @@ -495,6 +538,8 @@ GameServer.prototype.mainLoop = function() { this.pingServerTracker(); } + //this.tt = 0; + //this.tc = 0; //var t = process.hrtime(); //this.updateMoveEngine(); //this.t1 = toTime(process.hrtime(t)); @@ -574,29 +619,19 @@ GameServer.prototype.virusCheck = function() { } }; -GameServer.prototype.willCollide = function(size, pos, isVirus) { +GameServer.prototype.willCollide = function (size, pos, isVirus) { // Look if there will be any collision with the current nodes - - for (var i = 0; i < this.nodesPlayer.length; i++) { - var check = this.nodesPlayer[i]; - if (check == null) continue; - - if (check.getSize() > size) { // Check only if the player cell is larger than imaginary cell - if (check.collisionCheckCircle(pos.x, pos.y, size+50)) return true; // Collided - } - } - - if (isVirus) return false; // Don't check for viruses if the new cell will be virus - - for (var i = 0; i < this.nodesVirus.length; i++) { - var check = this.nodesVirus[i]; - if (check == null) continue; - - if (check.getSize() > size) { // Check only if the virus cell is larger than imaginary cell - if (check.collisionCheckCircle(pos.x, pos.y, size + 50)) return true; // Collided - } - } - return false; + var bound = { + minx: pos.x - size - 10, + miny: pos.y - size - 10, + maxx: pos.x + size + 10, + maxy: pos.y + size + 10 + }; + return this.quadTree.any( + bound, + function (item) { + return item.cell.cellType != 1; // ignore food + }); }; GameServer.prototype.getDist = function (x1, y1, x2, y2) { @@ -609,14 +644,14 @@ GameServer.prototype.abs = function (x) { return x < 0 ? -x : x; }; +// Checks cells for collision. +// Returns collision manifold or null if there is no collision GameServer.prototype.checkCellCollision = function(cell, check) { - // Returns manifold which contains info about cell's collisions. - // Returns null if there is no collision var r = cell.getSize() + check.getSize(); var dx = check.position.x - cell.position.x; var dy = check.position.y - cell.position.y; var squared = dx * dx + dy * dy; // squared distance from cell to check - if (squared >= r * r) { + if (squared > r * r) { // no collision return null; } @@ -631,7 +666,8 @@ GameServer.prototype.checkCellCollision = function(cell, check) { }; }; -GameServer.prototype.resolveCollision = function (manifold) { +// Resolves rigid body collision +GameServer.prototype.resolveRigidCollision = function (manifold, border) { // distance from cell1 to cell2 var d = Math.sqrt(manifold.squared); if (d <= 0) return; @@ -661,48 +697,87 @@ GameServer.prototype.resolveCollision = function (manifold) { manifold.cell1.position.y -= py * impulse1; manifold.cell2.position.x += px * impulse2; manifold.cell2.position.y += py * impulse2; + // clip to border bounds + manifold.cell1.checkBorder(border); + manifold.cell2.checkBorder(border); + // update quadTree + this.updateNodeQuad(manifold.cell1); + this.updateNodeQuad(manifold.cell2); +}; + +// Checks if collision is rigid body collision +GameServer.prototype.checkRigidCollision = function (manifold) { + if (!manifold.cell1.owner || !manifold.cell2.owner) + return false; + // The same owner + if (manifold.cell1.owner == manifold.cell2.owner) { + var tick = this.getTick(); + if ((manifold.cell1.boostDistance > 0 && manifold.cell1.getAge(tick) < 15) || + (manifold.cell2.boostDistance > 0 && manifold.cell2.getAge(tick) < 15)) { + // just splited => ignore + return false; + } + if (manifold.cell1.owner.mergeOverride) + return false; + // not force remerge => check if can remerge + if (!manifold.cell1.canRemerge() || !manifold.cell2.canRemerge()) { + // cannot remerge => rigid + return true; + } + return false; + } + // Different owners + if (this.gameMode.haveTeams) { + // Team check + if (manifold.cell1.owner.getTeam() == manifold.cell2.owner.getTeam()) { + // cannot eat team member => rigid + return true; + } + } + return false; }; -GameServer.prototype.processCollision = function (manifold) { +// Resolves non-rigid body collision +GameServer.prototype.resolveCollision = function (manifold) { var minCell = manifold.cell1; var maxCell = manifold.cell2; + // check if any cell already eaten + if (minCell.isRemoved || maxCell.isRemoved) + return; if (minCell.getSize() > maxCell.getSize()) { minCell = manifold.cell2; maxCell = manifold.cell1; } - // check if any cell already eaten - if (minCell.inRange) return; - if (maxCell.inRange) return; + + // check distance + var eatDistance = maxCell.getSize() - minCell.getSize() / Math.PI; + if (manifold.squared >= eatDistance * eatDistance) { + // too far => can't eat + return; + } if (minCell.owner && minCell.owner == maxCell.owner) { // collision owned/owned => ignore or resolve or remerge var tick = this.getTick(); - if ((minCell.boostDistance > 0 && minCell.getAge(tick) < 15) || - (maxCell.boostDistance > 0 && maxCell.getAge(tick) < 15)) { + if (minCell.getAge(tick) < 15 || maxCell.getAge(tick) < 15) { // just splited => ignore return; } if (!minCell.owner.mergeOverride) { // not force remerge => check if can remerge if (!minCell.canRemerge() || !maxCell.canRemerge()) { - // cannot remerge => resolve - this.resolveCollision(manifold); + // cannot remerge return; } } } else { // collision owned/enemy => check if can eat - // Can't eat self ejected mass because it still in boost mode (vanilla) - //if (minCell.ejector == maxCell && minCell.boostDistance > 0) - // return; - // Team check - if (this.gameMode.haveTeams) { - if (minCell.owner && maxCell.owner && minCell.owner.getTeam() == maxCell.owner.getTeam()) { + if (this.gameMode.haveTeams && minCell.owner && maxCell.owner) { + if (minCell.owner.getTeam() == maxCell.owner.getTeam()) { // cannot eat team member - this.resolveCollision(manifold); return; } } @@ -712,21 +787,17 @@ GameServer.prototype.processCollision = function (manifold) { return; } } - // check distance - var eatingRange = maxCell.getSize() - minCell.getSize() / Math.PI; - //var d = Math.sqrt(manifold.squared); // distance - //if (d >= eatingRange) { - if (manifold.squared >= eatingRange * eatingRange) { - // too far => can't eat + if (!maxCell.canEat(minCell)) { + // maxCell don't want to eat return; } // Now maxCell can eat minCell - minCell.inRange = true; + minCell.isRemoved = true; // Disable mergeOverride on the last merging cell // We need to disable it before onCosume to prevent merging loop // (onConsume may cause split for big mass) - if (minCell.owner != null && minCell.owner.cells.length <= 2) { + if (minCell.owner && minCell.owner.cells.length <= 2) { minCell.owner.mergeOverride = false; } @@ -738,7 +809,7 @@ GameServer.prototype.processCollision = function (manifold) { this.removeNode(minCell); }; -GameServer.prototype.updateMoveEngine = function() { +GameServer.prototype.updateMoveEngine = function () { var border = { left: this.config.borderLeft, top: this.config.borderTop, @@ -748,90 +819,127 @@ GameServer.prototype.updateMoveEngine = function() { // Move player cells for (var i in this.clients) { var client = this.clients[i].playerTracker; - + + // sort by size ascending client.cells.sort(function (a, b) { return a.getSize() - b.getSize(); }); - var sorted = client.cells; // Move - for (var i = 0; i < sorted.length; i++) { - var cell = sorted[i]; - cell.calcMove(client.mouse.x, client.mouse.y, this); - cell.checkBorder(border); - cell.calcMoveBoost(border); + for (var j = 0; j < client.cells.length; j++) { + var cell1 = client.cells[j]; + if (cell1 == null || cell1.isRemoved) + continue; + cell1.moveUser(border); + cell1.move(border); + this.updateNodeQuad(cell1); } - - // Scan for collision and process it - for (var i = 0; i < sorted.length; i++) { - var cell1 = sorted[i]; - // check collision owned/owned - for (var j = 0; j < client.cells.length; j++) { - var cell2 = client.cells[j]; - if (cell2 == null || cell2.inRange || cell1 == cell2) - continue; - - var manifold = this.checkCellCollision(cell1, cell2); - if (manifold == null) continue; - this.processCollision(manifold); - } - // check collision owned/enemy - for (var j = 0; j < client.collidingNodes.length; j++) { - var cell2 = client.collidingNodes[j]; - if (cell2 == null || cell2.inRange || cell1 == cell2) - continue; - - // TODO: remove - // Added skip to comply with oldstyle collision check - // If we remove it virus can eat player cell, so it should be fixed - if (cell1.getSize() < cell2.getSize()) continue; - - var manifold = this.checkCellCollision(cell1, cell2); - if (manifold == null) continue; - this.processCollision(manifold); - } - this.gameMode.onCellMove(cell1, this); + } + + // Scan for player cells collisions + var self = this; + var rigidCollisions = []; + var eatCollisions = []; + for (var i in this.clients) { + var client = this.clients[i].playerTracker; + for (var j = 0; j < client.cells.length; j++) { + var cell1 = client.cells[j]; + if (cell1 == null) continue; + this.quadTree.find(cell1.quadItem.bound, function (item) { + var cell2 = item.cell; + if (cell2 == cell1) return; + var manifold = self.checkCellCollision(cell1, cell2); + if (manifold == null) return; + if (self.checkRigidCollision(manifold)) + rigidCollisions.push({ cell1: cell1, cell2: cell2 }); + else + eatCollisions.push({ cell1: cell1, cell2: cell2 }); + }); } } - - // A system to move cells not controlled by players (ex. viruses, ejected mass) + + // resolve rigid body collisions + //for (var z = 0; z < 4; z++) { // loop for better rigid body resolution quality (slow) + for (var k = 0; k < rigidCollisions.length; k++) { + var c = rigidCollisions[k]; + var manifold = this.checkCellCollision(c.cell1, c.cell2); + if (manifold == null) continue; + this.resolveRigidCollision(manifold, border); + } + //} + rigidCollisions = null; + + // resolve eat collisions + for (var k = 0; k < eatCollisions.length; k++) { + var c = eatCollisions[k]; + var manifold = this.checkCellCollision(c.cell1, c.cell2); + if (manifold == null) continue; + this.resolveCollision(manifold); + } + eatCollisions = null; + + //this.gameMode.onCellMove(cell1, this); + + // Recycle unused moving nodes for (var i = 0; i < this.movingNodes.length; i++) { var check = this.movingNodes[i]; - - // Recycle unused nodes while (check == null && i < this.movingNodes.length) { // Remove moving cells that are undefined this.movingNodes.splice(i, 1); check = this.movingNodes[i]; } - - if (i >= this.movingNodes.length) - continue; - - if (check.boostDistance > 0) { - check.onAutoMove(this); - // If the cell has enough move ticks, then move it - check.calcMoveBoost(border); - //var cell1 = check; - //// check collision for ejected/ejected - //for (var j = 0; j < this.movingNodes.length; j++) { - // var cell2 = this.movingNodes[j]; - // if (cell2 == null) continue; - // if (cell1 == cell2) continue; // ignore self - // var manifold = this.checkCellCollision(cell1, cell2); - // if (manifold == null) continue; - // this.resolveCollision(manifold); - //} - } else { - // Auto move is done - check.moveDone(this); + if (check.boostDistance <= 0) { // Remove cell from list - var index = this.movingNodes.indexOf(check); + var index = this.movingNodes.indexOf(cell1); if (index != -1) { this.movingNodes.splice(index, 1); } } } + + // Move moving cells + for (var i = 0; i < this.movingNodes.length; i++) { + var cell1 = this.movingNodes[i]; + if (cell1 == null || cell1.isRemoved) continue; + cell1.move(border); + this.updateNodeQuad(cell1); + } + + // Scan for ejected cell collisions (scan for ejected or virus only) + rigidCollisions = []; + eatCollisions = []; + for (var i = 0; i < this.movingNodes.length; i++) { + var cell1 = this.movingNodes[i]; + if (cell1 == null || cell1.isRemoved || cell1.cellType != 3) continue; + this.quadTree.find(cell1.quadItem.bound, function (item) { + var cell2 = item.cell; + if (cell2 == cell1 || (cell2.cellType != 3 && cell2.cellType!=2)) + return; + var manifold = self.checkCellCollision(cell1, cell2); + if (manifold == null) return; + if (cell1.cellType==3 && cell2.cellType==3) // ejected/ejected + rigidCollisions.push({ cell1: cell1, cell2: cell2 }); + else + eatCollisions.push({ cell1: cell1, cell2: cell2 }); + }); + } + + // resolve rigid body collisions + for (var k = 0; k < rigidCollisions.length; k++) { + var c = rigidCollisions[k]; + var manifold = this.checkCellCollision(c.cell1, c.cell2); + if (manifold == null) continue; + this.resolveRigidCollision(manifold, border); + } + rigidCollisions = null; + + // resolve eat collisions + for (var k = 0; k < eatCollisions.length; k++) { + var c = eatCollisions[k]; + var manifold = this.checkCellCollision(c.cell1, c.cell2); + if (manifold == null) continue; + this.resolveCollision(manifold); + } }; GameServer.prototype.setAsMovingNode = function(node) { diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index eb7aa0d7d..b02b45453 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -8,10 +8,7 @@ function PlayerTracker(gameServer, socket) { this.skin = ""; this.gameServer = gameServer; this.socket = socket; - this.nodeAdditionQueue = []; - this.nodeDestroyQueue = []; this.visibleNodes = []; - this.collidingNodes = []; // Perfomance save; all nodes colliding with player's cells this.cells = []; this.mergeOverride = false; // Triggered by console command this.score = 0; // Needed for leaderboard @@ -150,14 +147,13 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.spectate = false; this.freeRoam = false; - this.socket.sendPacket(new Packet.ClearAll()); - this.collidingNodes = []; - // some clients don't understand ClearAll message - // so we will send we will do not perform cleanup + // some old clients don't understand ClearAll message + // so we will not perform cleanup // to allow them to receive remove notifications - //this.visibleNodes = []; // Reset visible nodes - //this.nodeDestroyQueue = []; // Reset destroy queue - //this.nodeAdditionQueue = []; // Reset addition queue + if (this.socket.packetHandler.protocol >= 6) { + this.socket.sendPacket(new Packet.ClearAll()); + this.visibleNodes = []; + } this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); }; @@ -167,9 +163,6 @@ PlayerTracker.prototype.update = function () { if (this.socket.packetHandler.protocol == 0) return; - // First reset colliding nodes - this.collidingNodes = []; - // Actions buffer (So that people cant spam packets) if (this.socket.packetHandler.pressSpace) { // Split cell if (!this.mergeOverride) this.gameServer.gameMode.pressSpace(this.gameServer, this); @@ -186,89 +179,42 @@ PlayerTracker.prototype.update = function () { this.socket.packetHandler.pressQ = false; } - var updateNodes = []; // Nodes that need to be updated via packet - - // Remove nodes from visible nodes if possible - var d = 0; - while (d < this.nodeDestroyQueue.length) { - var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[d]); - if (index > -1) { - this.visibleNodes.splice(index, 1); - d++; // Increment - } else { - // Node was never visible anyways - this.nodeDestroyQueue.splice(d, 1); - } - } - - // Get visible nodes every 400 ms - var nonVisibleNodes = []; // Nodes that are not visible - if (this.tickViewBox <= 0) { - var newVisible = this.getVisibleNodes(); - try { // Add a try block in any case - - // Compare and destroy nodes that are not seen - for (var i = 0; i < this.visibleNodes.length; i++) { - var index = newVisible.indexOf(this.visibleNodes[i]); - if (index == -1) { - // Not seen by the client anymore - nonVisibleNodes.push(this.visibleNodes[i]); - } - } - - // Add nodes to client's screen if client has not seen it already - for (var i = 0; i < newVisible.length; i++) { - var index = this.visibleNodes.indexOf(newVisible[i]); - if (index == -1) { - updateNodes.push(newVisible[i]); - } - } - - // Scramble minimap - // TODO: need to send it more rarely (once per second or something like that) - // also need to make sure that we send it before first cell update - //var border = { - // left: Math.max(this.viewBox.left - this.viewBox.halfWidth, this.gameServer.config.borderLeft), - // top: Math.max(this.viewBox.top - this.viewBox.halfHeight, this.gameServer.config.borderTop), - // right: Math.min(this.viewBox.right + this.viewBox.halfWidth, this.gameServer.config.borderRight), - // bottom: Math.min(this.viewBox.bottom + this.viewBox.halfHeight, this.gameServer.config.borderBottom) - //}; - //this.socket.sendPacket(new Packet.SetBorder(this, border)); - } catch (err) { - console.error(err); + var newVisible = this.getVisibleNodes(); + newVisible.sort(function (a, b) { return a.nodeId - b.nodeId; }); + var deleteNodes = []; + var eatNodes = []; + var newIndex = 0; + var oldIndex = 0; + for (; newIndex < newVisible.length && oldIndex < this.visibleNodes.length;) { + if (newVisible[newIndex].nodeId < this.visibleNodes[oldIndex].nodeId) { + newIndex++; + continue; } - - this.visibleNodes = newVisible; - // Reset Ticks - this.tickViewBox = 0; - } else { - this.tickViewBox--; - // Add nodes to screen - for (var i = 0; i < this.nodeAdditionQueue.length; i++) { - var node = this.nodeAdditionQueue[i]; - this.visibleNodes.push(node); - updateNodes.push(node); + if (newVisible[newIndex].nodeId > this.visibleNodes[oldIndex].nodeId) { + if (this.visibleNodes[oldIndex].isRemoved) + eatNodes.push(this.visibleNodes[oldIndex]); + else + deleteNodes.push(this.visibleNodes[oldIndex]); + oldIndex++; + continue; } + newIndex++; + oldIndex++; } - - // Update moving nodes - for (var i = 0; i < this.visibleNodes.length; i++) { - var node = this.visibleNodes[i]; - if (node.sendUpdate()) { - // Sends an update if cell is moving - updateNodes.push(node); - } + for (; oldIndex < this.visibleNodes.length; oldIndex++) { + if (this.visibleNodes[oldIndex].isRemoved) + eatNodes.push(this.visibleNodes[oldIndex]); + else + deleteNodes.push(this.visibleNodes[oldIndex]); } - + this.visibleNodes = newVisible; + // Send packet this.socket.sendPacket(new Packet.UpdateNodes( this, - this.nodeDestroyQueue, - updateNodes, - nonVisibleNodes)); - - this.nodeDestroyQueue = []; // Reset destroy queue - this.nodeAdditionQueue = []; // Reset addition queue + eatNodes, + this.visibleNodes, + deleteNodes)); // Update leaderboard if (this.tickLeaderboard <= 0) { @@ -329,20 +275,30 @@ PlayerTracker.prototype.updateCenterFreeRoam = function () { var dx = this.mouse.x - this.centerPos.x; var dy = this.mouse.y - this.centerPos.y; var squared = dx * dx + dy * dy; - if (squared < 1) return; // stop threshold + if (squared == 0) return; // stop threshold // distance var d = Math.sqrt(squared); + var invd = 1 / d; + var nx = dx * invd; + var ny = dy * invd; + var speed = Math.min(d, 32); if (speed <= 0) return; - var angle = Math.atan2(dx, dy); - if (isNaN(angle)) return; - - this.setCenterPos( - this.centerPos.x + speed * Math.sin(angle), - this.centerPos.y + speed * Math.cos(angle)); + var x = this.centerPos.x + nx * speed; + var y = this.centerPos.y + ny * speed; + // check border + if (x < this.gameServer.config.borderLeft) + x = this.gameServer.config.borderLeft; + else if (y > this.gameServer.config.borderRight) + x = this.gameServer.config.borderRight; + if (y < this.gameServer.config.borderTop) + y = this.gameServer.config.borderTop; + else if (y > this.gameServer.config.borderBottom) + y = this.gameServer.config.borderBottom; + this.setCenterPos(x, y); }; PlayerTracker.prototype.updateViewBox = function () { @@ -386,34 +342,20 @@ PlayerTracker.prototype.getVisibleNodes = function () { } PlayerTracker.prototype.calcVisibleNodes = function() { - this.collidingNodes = []; var newVisible = []; - for (var i = 0; i < this.gameServer.nodes.length; i++) { - var node = this.gameServer.nodes[i]; - if (node == null) continue; - - if (node.owner == this || node.visibleCheck(this.viewBox)) { - // Cell is in range of viewBox - newVisible.push(node); - - // TODO: rewrite that! - // Check if it's colliding with one of player's cells - // To save perfomance, check if any client's cell collides with this cell - for (var j = 0; j < this.cells.length; j++) { - var cell = this.cells[j]; - if (cell == null || cell == node) - continue; - - // circle collision detector - var dx = cell.position.x - node.position.x; - var dy = cell.position.y - node.position.y; - var r = cell.getSize() + node.getSize(); - if (dx * dx + dy * dy < r * r) { - // circle collision detected - this.collidingNodes.push(node); - } - } - } + var bound = { + minx: this.viewBox.left, + miny: this.viewBox.top, + maxx: this.viewBox.right, + maxy: this.viewBox.bottom + }; + this.gameServer.quadTree.find(bound, function (quadItem) { + newVisible.push(quadItem.cell); + }); + // make sure that all owned is included + for (var i = 0; i < this.cells.length; i++) { + if (newVisible.indexOf(this.cells[i]) < 0) + newVisible.push(this.cells[i]); } return newVisible; }; diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index e5479b010..24459a08f 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -30,14 +30,6 @@ BotPlayer.prototype.getLowestCell = function() { }; BotPlayer.prototype.update = function () { // Overrides the update function from player tracker - // Remove nodes from visible nodes if possible - for (var i = 0; i < this.nodeDestroyQueue.length; i++) { - var index = this.visibleNodes.indexOf(this.nodeDestroyQueue[i]); - if (index > -1) { - this.visibleNodes.splice(index, 1); - } - } - // Respawn if bot is dead if (this.cells.length <= 0) { this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); @@ -58,10 +50,6 @@ BotPlayer.prototype.update = function () { // Overrides the update function from // Action this.decide(cell); - - // Reset queues - this.nodeDestroyQueue = []; - this.nodeAdditionQueue = []; }; // Custom diff --git a/src/entity/Cell.js b/src/entity/Cell.js index e1bf762ed..c2c530065 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -121,6 +121,14 @@ Cell.prototype.setKiller = function(cell) { this.killedBy = cell; }; +Cell.prototype.setPosition = function (x, y) { + if (isNaN(x) || isNaN(y)) { + console.log("[ERROR] Cell.setPosition: NaN"); + return; + } + this.position = { x: x, y: y }; +}; + // Functions Cell.prototype.collisionCheck = function(left, top, right, bottom) { @@ -178,7 +186,7 @@ Cell.prototype.setBoost = function (distance, angle) { this.setAngle(angle); }; -Cell.prototype.calcMoveBoost = function (border) { +Cell.prototype.move = function (border) { if (this.boostDistance <= 0) return; var speed = Math.sqrt(this.boostDistance * this.boostDistance / 100); @@ -186,65 +194,64 @@ Cell.prototype.calcMoveBoost = function (border) { speed = Math.min(speed, this.boostDistance); // avoid overlap 0 this.boostDistance -= speed; if (this.boostDistance <= 1) this.boostDistance = 0; - var dx = this.boostDirection.x * speed; - var dy = this.boostDirection.y * speed; + + var v = this.clipVelocity( + { x: this.boostDirection.x * speed, y: this.boostDirection.y * speed }, + border); + this.position.x += v.x; + this.position.y += v.y; + this.checkBorder(border); +} - // Border bouncy physics +Cell.prototype.clipVelocity = function (v, border) { + if (isNaN(v.x) || isNaN(v.y)) { + throw new TypeError("Cell.clipVelocity: NaN"); + }; + if (v.x == 0 && v.y == 0) + return v; // zero move, no calculations :) var r = this.getSize() / 2; - var borderCell = { - left: border.left + r, - top: border.top + r, - right: border.right - r, - bottom: border.bottom - r + var bound = { + minx: border.left + r, + miny: border.top + r, + maxx: border.right - r, + maxy: border.bottom - r }; - this.moveReflected(dx, dy, borderCell); -} - -Cell.prototype.moveReflected = function (dx, dy, border) { - var cx = this.position.x; - var cy = this.position.y; - var x = cx + dx; - var y = cy + dy; - if (isNaN(x) || isNaN(y)) { - // something is going wrong - // TODO: border center? - console.log("\u001B[1m\u001B[31m[Error] Cell.moveReflected failed (NaN)"); - return; - } - if (dx == 0 && dy == 0) return; // zero move :) - - var pleft = x >= border.left ? null : this.getLineIntersection( - cx, cy, x, y, - border.left, border.top, border.left, border.bottom); - var pright = x <= border.right ? null : this.getLineIntersection( - cx, cy, x, y, - border.right, border.top, border.right, border.bottom); + var x = this.position.x + v.x; + var y = this.position.y + v.y; + // border check + var pleft = x >= bound.minx ? null : this.getLineIntersection( + this.position.x, this.position.y, x, y, + bound.minx, bound.miny, bound.minx, bound.maxy); + var pright = x <= bound.maxx ? null : this.getLineIntersection( + this.position.x, this.position.y, x, y, + bound.maxx, bound.miny, bound.maxx, bound.maxy); var ptop = y >= border.top ? null : this.getLineIntersection( - cx, cy, x, y, - border.left, border.top, border.right, border.top); - var pbottom = y <= border.bottom ? null : this.getLineIntersection( - cx, cy, x, y, - border.left, border.bottom, border.right, border.bottom); + this.position.x, this.position.y, x, y, + bound.minx, bound.miny, bound.maxx, bound.miny); + var pbottom = y <= bound.maxy ? null : this.getLineIntersection( + this.position.x, this.position.y, x, y, + bound.minx, bound.maxy, bound.maxx, bound.maxy); var ph = pleft != null ? pleft : pright; var pv = ptop != null ? ptop : pbottom; var p = ph != null ? ph : pv; if (p == null) { // inside border - this.position.x = x; - this.position.y = y; - return; + return v; } if (ph && pv) { // two border lines intersection => get nearest point - var hdx = ph.x - cx; - var hdy = ph.y - cx; - var vdx = pv.x - cx; - var vdy = pv.y - cx; + var hdx = ph.x - this.position.x; + var hdy = ph.y - this.position.y; + var vdx = pv.x - this.position.x; + var vdy = pv.y - this.position.y; if (hdx * hdx + hdy * hdy < vdx * vdx + vdy * vdy) p = ph; else p = pv; } + // p - stop point on the border + + // reflect angle var angle = this.getAngle(); if (p == ph) { // left/right border reflection @@ -254,27 +261,33 @@ Cell.prototype.moveReflected = function (dx, dy, border) { angle = angle <= Math.PI ? Math.PI - angle : 3 * Math.PI - angle; } this.setAngle(angle); - this.position.x = p.x; - this.position.y = p.y; - // calculate rest of distance - var lx = p.x - cx; - var ly = p.y - cy; - var ldx = dx - lx; - var ldy = dy - ly; - var l = Math.sqrt(ldx * ldx + ldy * ldy); - this.boostDistance += l; + // new velocity + var lx = p.x - this.position.x; + var ly = p.y - this.position.y; + // calculate rest of velocity + var ldx = v.x - lx; + var ldy = v.y - ly; + // update velocity and add rest to the boostDistance + v.x = lx; + v.y = ly; + this.boostDistance += Math.sqrt(ldx * ldx + ldy * ldy); + return v; }; Cell.prototype.checkBorder = function (border) { var r = this.getSize() / 2; - if (this.position.x < border.left + r) - this.position.x = border.left + r; - else if (this.position.x > border.right - r) - this.position.x = border.right - r; - if (this.position.y < border.top + r) - this.position.y = border.top + r; + var x = this.position.x; + var y = this.position.y; + if (x < border.left + r) + x = border.left + r; + else if (x > border.right - r) + x = border.right - r; + if (y < border.top + r) + y = border.top + r; else if (this.position.y > border.bottom - r) - this.position.y = border.bottom - r; + y = border.bottom - r; + if (x != this.position.x || y != this.position.y) + this.setPosition(x, y); }; @@ -285,6 +298,11 @@ Cell.prototype.sendUpdate = function() { return true; }; +Cell.prototype.canEat = function (cell) { + // by default cell cannot eat anyone + return false; +}; + Cell.prototype.onConsume = function(consumer, gameServer) { // Called when the cell is consumed }; @@ -297,14 +315,6 @@ Cell.prototype.onRemove = function(gameServer) { // Called when this cell is removed }; -Cell.prototype.onAutoMove = function(gameServer) { - // Called on each auto move engine tick -}; - -Cell.prototype.moveDone = function(gameServer) { - // Called when this cell finished moving with the auto move engine -}; - // Lib Cell.prototype.getLineIntersection = function (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { diff --git a/src/entity/EjectedMass.js b/src/entity/EjectedMass.js index ec1f34a5e..cf6c1a296 100644 --- a/src/entity/EjectedMass.js +++ b/src/entity/EjectedMass.js @@ -38,19 +38,8 @@ EjectedMass.prototype.onRemove = function(gameServer) { EjectedMass.prototype.onConsume = function(consumer, gameServer) { // Adds mass to consumer - consumer.addMass(this.getMass()); -}; - -EjectedMass.prototype.onAutoMove = function(gameServer) { - if (gameServer.nodesVirus.length < gameServer.config.virusMaxAmount) { - // Check for viruses - var v = gameServer.getNearestVirus(this); - if (v) { // Feeds the virus if it exists - v.feed(this, gameServer); - return true; - } - } -}; - -EjectedMass.prototype.moveDone = function(gameServer) { + if (consumer.cellType == 2 && consumer.feed != null) // virus + consumer.feed(this, gameServer); + else + consumer.addMass(this.getMass()); }; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index ddde1bb38..20d1d7a89 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -48,9 +48,22 @@ PlayerCell.prototype.canRemerge = function () { return this._canRemerge; }; +PlayerCell.prototype.canEat = function (cell) { + // player cell can eat anyone + return true; +}; + // Movement -PlayerCell.prototype.calcMove = function (x, y, gameServer) { +PlayerCell.prototype.moveUser = function (border) { + if (this.owner == null) { + return; + } + var x = this.owner.mouse.x; + var y = this.owner.mouse.y; + if (isNaN(x) || isNaN(y)) { + return; + } var dx = x - this.position.x; var dy = y - this.position.y; var squared = dx * dx + dy * dy; @@ -71,6 +84,7 @@ PlayerCell.prototype.calcMove = function (x, y, gameServer) { this.position.x += nx * speed; this.position.y += ny * speed; + this.checkBorder(border); }; // Override @@ -105,7 +119,3 @@ PlayerCell.prototype.onRemove = function(gameServer) { // Gamemode actions gameServer.gameMode.onCellRemove(this); }; - -PlayerCell.prototype.moveDone = function(gameServer) { - // Well, nothing. -}; diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 088fdada0..e79efabb9 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -19,7 +19,6 @@ Virus.prototype.feed = function(feeder, gameServer) { this.setAngle(feeder.getAngle()); // Set direction if the virus explodes this.setMass(this.getMass() + feeder.getMass()); this.fed++; // Increase feed count - gameServer.removeNode(feeder); // Check if the virus is going to explode if (this.fed >= gameServer.config.virusFeedAmount) { @@ -32,6 +31,11 @@ Virus.prototype.feed = function(feeder, gameServer) { // Main Functions +Virus.prototype.canEat = function (cell) { + return cell.cellType == 3; // virus can eat ejected mass only +}; + + Virus.prototype.onConsume = function(consumer, gameServer) { var client = consumer.owner; @@ -84,10 +88,6 @@ Virus.prototype.onConsume = function(consumer, gameServer) { angle = Math.random() * 6.28; // Random directions gameServer.splitPlayerCell(client, consumer, angle, splitMass); } - - //// Prevent consumer cell from merging with other cells - //consumer.calcMergeTime(gameServer.getTick(), gameServer.config.playerRecombineTime); - // TODO: ttr fix? }; Virus.prototype.onAdd = function(gameServer) { diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 41e65f8f4..6745fcb4c 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -34,7 +34,6 @@ Experimental.prototype.updateMotherCells = function(gameServer) { // Checks mother.update(gameServer); - mother.checkEat(gameServer); } }; @@ -97,7 +96,6 @@ Experimental.prototype.onServerInit = function(gameServer) { // Special virus mechanics Virus.prototype.feed = function(feeder, gameServer) { - gameServer.removeNode(feeder); // Pushes the virus // TODO: check distance this.setBoost(16 * 20, feeder.getAngle()); @@ -182,57 +180,9 @@ MotherCell.prototype.update = function(gameServer) { } }; -MotherCell.prototype.checkEat = function(gameServer) { - var safeMass = this.getMass() * .78; - - // Loop for potential prey - for (var i in gameServer.nodesPlayer) { - var check = gameServer.nodesPlayer[i]; - this.checkEatCell(check, safeMass, gameServer); - } - - // Viruses might be literally in the mother cell when it becomes large. Prevent this - for (var i in gameServer.nodesVirus) { - var check = gameServer.nodesVirus[i]; - this.checkEatCell(check, safeMass, gameServer); - } - - // Check moving nodes - for (var i in gameServer.movingNodes) { - var check = gameServer.movingNodes[i]; - this.checkEatCell(check, safeMass, gameServer); - } -}; - -MotherCell.prototype.checkEatCell = function (check, safeMass, gameServer) { - if ((check.getType() == 1) || (check.getMass() > safeMass)) { - // Too big to be consumed or check is a food cell - return; - } - - // Very simple yet very powerful - //var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y); - //var allowDist = this.getSize() - check.getEatingRange(); - //if (dist < allowDist) { - // // Eat it - // gameServer.removeNode(check); - // this.setMass(this.getMass() + check.getMass()); - //} - if (this.getSize() <= check.getSize() * 1.15) - return; - var eatingRange = this.getSize() - check.getSize() / Math.PI; - var dx = this.position.x - check.position.x; - var dy = this.position.y - check.position.y; - if (dx * dx + dy * dy >= eatingRange * eatingRange) - return - // Eat it - gameServer.removeNode(check); - this.setMass(this.getMass() + check.getMass()); -}; - -MotherCell.prototype.abs = function(n) { - // Because Math.abs is slow - return (n < 0) ? -n : n; +MotherCell.prototype.canEat = function (cell) { + return cell.cellType == 0 || // can eat player cell + cell.cellType == 3; // can eat ejected mass }; MotherCell.prototype.spawnFood = function(gameServer) { @@ -252,7 +202,7 @@ MotherCell.prototype.spawnFood = function(gameServer) { gameServer.currentFood++; // Move engine - var dist = (Math.random() * 8) + 8; // Random distance + var dist = (Math.random() * 25) + 25; // Random distance // TODO: check distance f.setBoost(dist, angle); diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 24e7d7e95..28d3b5c80 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -1,3 +1,4 @@ +// TODO: fix this game mode has outdated code and probably will not works var Teams = require('./Teams.js'); var Cell = require('../entity/Cell.js'); var Food = require('../entity/Food.js'); @@ -106,7 +107,7 @@ TeamX.prototype.countNotInRange = function(client) { var count = 0; for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; - if (!(cell.inRange === true)) { + if (!(cell.isRemoved === true)) { count++; } } @@ -174,7 +175,7 @@ TeamX.prototype.onServerInit = function(gameServer) { } // if something already collided with this cell, don't check for other collisions - if (check.inRange) { + if (check.isRemoved) { continue; } @@ -199,7 +200,7 @@ TeamX.prototype.onServerInit = function(gameServer) { switch (check.getType()) { case 1: // Food cell list.push(check); - check.inRange = true; // skip future collision checks for this food + check.isRemoved = true; // skip future collision checks for this food continue; case 2: // Virus multiplier = 1.33; @@ -249,7 +250,7 @@ TeamX.prototype.onServerInit = function(gameServer) { list.push(check); // Something is about to eat this cell; no need to check for other collisions with it - check.inRange = true; + check.isRemoved = true; } return list; }; diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 46b27c397..95f691e51 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -1,3 +1,4 @@ +// TODO: fix this game mode has outdated code and probably will not works var Mode = require('./Mode.js'); var Cell = require('../entity/Cell.js'); var Entity = require('../entity'); @@ -175,7 +176,7 @@ TeamZ.prototype.spawnDrug = function(gameServer, cell) { // spawn HERO or BRAIN // Spawn if no cells are colliding if (!collided) { - cell.position = pos; + cell.setPosition(pos.x, pos.y); gameServer.addNode(cell); return true; // SUCCESS with spawn } @@ -418,7 +419,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { } // if something already collided with this cell, don't check for other collisions - if (check.inRange) { + if (check.isRemoved) { continue; } @@ -454,7 +455,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { switch (check.getType()) { case 1: // Food cell list.push(check); - check.inRange = true; // skip future collision checks for this food + check.isRemoved = true; // skip future collision checks for this food continue; case 2: // Virus multiplier = 1.33; @@ -504,7 +505,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { list.push(check); // Something is about to eat this cell; no need to check for other collisions with it - check.inRange = true; + check.isRemoved = true; } return list; }; @@ -964,8 +965,9 @@ TeamZ.prototype.onCellMove = function(x1, y1, cell) { var move = collisionDist - dist; - check.position.x = check.position.x + (move * Math.sin(newAngle)) >> 0; - check.position.y = check.position.y + (move * Math.cos(newAngle)) >> 0; + check.setPosition( + check.position.x + (move * Math.sin(newAngle)) >> 0, + check.position.y + (move * Math.cos(newAngle)) >> 0); } } } diff --git a/src/gamemodes/Zombie.js b/src/gamemodes/Zombie.js index 16b0ccce7..35c011efa 100755 --- a/src/gamemodes/Zombie.js +++ b/src/gamemodes/Zombie.js @@ -136,9 +136,10 @@ Zombie.prototype.onCellMove = function(x1, y1, cell) { var newAngle = Math.atan2(newDeltaX, newDeltaY); var move = collisionDist - dist; - - check.position.x = check.position.x + (move * Math.sin(newAngle)) >> 0; - check.position.y = check.position.y + (move * Math.cos(newAngle)) >> 0; + + check.setPosition( + check.position.x + (move * Math.sin(newAngle)) >> 0, + check.position.y + (move * Math.cos(newAngle)) >> 0); } } } diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 6c23eaa09..bf1627cee 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -497,8 +497,8 @@ Commands.list = { if (gameServer.clients[i].playerTracker.pID == id) { var client = gameServer.clients[i].playerTracker; for (var j in client.cells) { - client.cells[j].position.x = pos.x; - client.cells[j].position.y = pos.y; + client.cells[j].setPosition(pos.x, pos.y); + gameServer.updateNodeQuad(client.cells[j]); } console.log("[Console] Teleported " + client.name + " to (" + pos.x + " , " + pos.y + ")"); From 1c035e3fb73c7eca8cf703a4860bdbdb2ca4b400 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 16 Jun 2016 17:55:39 +0200 Subject: [PATCH 146/417] add quad-tree implementation --- src/QuadNode.js | 195 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 src/QuadNode.js diff --git a/src/QuadNode.js b/src/QuadNode.js new file mode 100644 index 000000000..3b5e300d8 --- /dev/null +++ b/src/QuadNode.js @@ -0,0 +1,195 @@ +'use strict'; +/* + * Fast and easy Quad-Tree implementation written by Barbosik. + * Useful for quick object search in the area specified with bounds. + * + * Copyright (c) 2016 Barbosik https://github.com/Barbosik + * License: Apache License, Version 2.0 + * + */ + +function QuadNode(bound, maxChildren, maxLevel, level, parent) { + if (!level) level = 0; + if (!parent) parent = null; + var width = bound.maxx - bound.minx; + var height = bound.maxy - bound.miny; + + this.level = level; + this.parent = parent; + this.bound = { + minx: bound.minx, + miny: bound.miny, + maxx: bound.maxx, + maxy: bound.maxy, + width: width, + height: height, + cx: bound.minx + width / 2, + cy: bound.miny + height / 2 + }; + this.maxChildren = maxChildren; + this.maxLevel = maxLevel; + this.childNodes = []; + this.items = []; +} + +module.exports = QuadNode; + +QuadNode.prototype.insert = function (item) { + if (item._quadNode != null) { + throw new TypeError("QuadNode.insert: cannot insert item which already belong to another QuadNode!"); + } + if (this.childNodes.length != 0) { + var quad = this.getQuad(item.bound); + if (quad != -1) { + this.childNodes[quad].insert(item); + return; + } + } + this.items.push(item); + item._quadNode = this; // attached field, used for quick search quad node by item + + // check if rebalance needed + if (this.childNodes.length != 0 || this.level >= this.maxLevel || this.items.length < this.maxChildren) + return; + // split and rebalance current node + if (this.childNodes.length == 0) { + // split + var w = this.bound.width / 2; + var h = this.bound.height / 2; + var x0 = this.bound.minx + w; + var y0 = this.bound.miny; + var x1 = this.bound.minx; + var y1 = this.bound.miny; + var x2 = this.bound.minx; + var y2 = this.bound.miny + h; + var x3 = this.bound.minx + w; + var y3 = this.bound.miny + h; + var b0 = { minx: x0, miny: y0, maxx: x0 + w, maxy: y0 + h }; + var b1 = { minx: x1, miny: y1, maxx: x1 + w, maxy: y1 + h }; + var b2 = { minx: x2, miny: y2, maxx: x2 + w, maxy: y2 + h }; + var b3 = { minx: x3, miny: y3, maxx: x3 + w, maxy: y3 + h }; + this.childNodes.push(new QuadNode(b0, this.maxChildren, this.maxLevel, this.level + 1, this)); + this.childNodes.push(new QuadNode(b1, this.maxChildren, this.maxLevel, this.level + 1, this)); + this.childNodes.push(new QuadNode(b2, this.maxChildren, this.maxLevel, this.level + 1, this)); + this.childNodes.push(new QuadNode(b3, this.maxChildren, this.maxLevel, this.level + 1, this)); + } + // rebalance + for (var i = 0; i < this.items.length; ) { + var qitem = this.items[i]; + var quad = this.getQuad(qitem.bound); + if (quad != -1) { + this.items.splice(i, 1); + qitem._quadNode = null; + this.childNodes[quad].insert(qitem); + } + else i++; + } +}; + +QuadNode.prototype.remove = function (item) { + if (item._quadNode != this) { + item._quadNode.remove(item); + return; + } + var index = this.items.indexOf(item); + if (index < 0) { + throw new TypeError("QuadNode.remove: item not found!"); + } + this.items.splice(index, 1); + item._quadNode = null; + // TODO: collapse node if empty +}; + +QuadNode.prototype.update = function (item) { + this.remove(item); + this.insert(item); +}; + +QuadNode.prototype.clear = function () { + for (var i = 0; i < this.items.length; i++) + this.items[i]._quadNode = null; + this.items = []; + for (var i = 0; i < this.childNodes.length; i++) + this.childNodes[i].clear(); + this.childNodes = []; +}; + +QuadNode.prototype.contains = function (item) { + if (item._quadNode == null) + return false; + if (item._quadNode != this) { + return item._quadNode.contains(item); + } + return this.items.indexOf(item) >= 0; +}; + +QuadNode.prototype.find = function (bound, callback) { + if (this.childNodes.length != 0) { + var quad = this.getQuad(bound); + if (quad != -1) { + this.childNodes[quad].find(bound, callback); + } else { + for (var i = 0; i < this.childNodes.length; i++) { + var node = this.childNodes[i]; + if (checkBoundIntersection(node.bound, bound)) + node.find(bound, callback); + } + } + } + for (var i = 0; i < this.items.length; i++) { + var item = this.items[i]; + if (checkBoundIntersection(item.bound, bound)) + callback(item); + } +}; + +QuadNode.prototype.any = function (bound, predicate) { + if (this.childNodes.length != 0) { + var quad = this.getQuad(bound); + if (quad != -1) { + if (this.childNodes[quad].any(bound, predicate)) + return true; + } else { + for (var i = 0; i < this.childNodes.length; i++) { + var node = this.childNodes[i]; + if (checkBoundIntersection(node.bound, bound)) + if (node.any(bound, predicate)) + return true; + } + } + } + for (var i = 0; i < this.items.length; i++) { + var item = this.items[i]; + if (checkBoundIntersection(item.bound, bound)) { + if (predicate == null || predicate(item)) + return true; + } + } + return false; +}; + +// Returns quadrant for the bound. +// Returns -1 if bound cannot completely fit within a child node +QuadNode.prototype.getQuad = function (bound) { + var isTop = bound.miny < this.bound.cy && bound.maxy < this.bound.cy; + var isLeft = bound.minx < this.bound.cx && bound.maxx < this.bound.cx; + if (isLeft) { + if (isTop) return 1; + else if (bound.miny > this.bound.cy) return 2; // isBottom + } + else if (bound.minx > this.bound.cx) // isRight + { + if (isTop) return 0; + else if (bound.miny > this.bound.cy) return 3; // isBottom + } + return -1; // cannot fit (too large size) +}; + +function checkBoundIntersection(bound1, bound2) { + var notIntersect = + bound2.minx >= bound1.maxx || + bound2.maxx <= bound1.minx || + bound2.miny >= bound1.maxy || + bound2.maxy <= bound1.miny; + return !notIntersect; +}; From 78f2b24bbb9ba3a84ad947bf8bf17e948cdd1a8d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 16 Jun 2016 20:03:33 +0200 Subject: [PATCH 147/417] protocol refactoring & optimizations --- src/PlayerTracker.js | 27 +++-- src/packet/BinaryWriter.js | 2 +- src/packet/ChatMessage.js | 2 +- src/packet/SetBorder.js | 2 +- src/packet/UpdateLeaderboard.js | 6 +- src/packet/UpdateNodes.js | 170 +++++++++++--------------------- 6 files changed, 75 insertions(+), 134 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index b02b45453..cb2b3cbb0 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -148,7 +148,7 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.freeRoam = false; // some old clients don't understand ClearAll message - // so we will not perform cleanup + // so we will not perform cleanup for old protocol // to allow them to receive remove notifications if (this.socket.packetHandler.protocol >= 6) { this.socket.sendPacket(new Packet.ClearAll()); @@ -191,21 +191,24 @@ PlayerTracker.prototype.update = function () { continue; } if (newVisible[newIndex].nodeId > this.visibleNodes[oldIndex].nodeId) { - if (this.visibleNodes[oldIndex].isRemoved) - eatNodes.push(this.visibleNodes[oldIndex]); + var node = this.visibleNodes[oldIndex]; + if (node.isRemoved) + eatNodes.push(node); else - deleteNodes.push(this.visibleNodes[oldIndex]); + deleteNodes.push(node); oldIndex++; continue; } newIndex++; oldIndex++; } - for (; oldIndex < this.visibleNodes.length; oldIndex++) { - if (this.visibleNodes[oldIndex].isRemoved) - eatNodes.push(this.visibleNodes[oldIndex]); - else - deleteNodes.push(this.visibleNodes[oldIndex]); + for (; oldIndex < this.visibleNodes.length; ) { + var node = this.visibleNodes[oldIndex]; + if (node.isRemoved) + eatNodes.push(node); + else + deleteNodes.push(node); + oldIndex++; } this.visibleNodes = newVisible; @@ -387,9 +390,3 @@ PlayerTracker.prototype.sendPosPacket = function() { this.getScale() )); }; - -PlayerTracker.prototype.getAngle = function(x1, y1, x2, y2) { - var deltaY = y1 - y2; - var deltaX = x1 - x2; - return Math.atan2(deltaX, deltaY); -}; diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index a7cf3140e..a5753b13d 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -115,7 +115,7 @@ BinaryWriter.prototype.writeStringZeroUnicode = function (value) { this.writeUInt16(0); }; -BinaryWriter.prototype.ToBuffer = function () { +BinaryWriter.prototype.toBuffer = function () { var buffer = new Buffer(this._length); for (var i = 0; i < this._writers.length; i++) { this._writers[i](buffer); diff --git a/src/packet/ChatMessage.js b/src/packet/ChatMessage.js index ae09727f4..9c6e08899 100644 --- a/src/packet/ChatMessage.js +++ b/src/packet/ChatMessage.js @@ -40,5 +40,5 @@ ChatMessage.prototype.build = function (protocol) { writer.writeStringZeroUtf8(name); writer.writeStringZeroUtf8(text); } - return writer.ToBuffer(); + return writer.toBuffer(); }; diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index ac8bf8ea6..69412eca2 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -24,5 +24,5 @@ SetBorder.prototype.build = function(protocol) { if (name == null) name = ""; writer.writeStringZeroUtf8(name); } - return writer.ToBuffer(); + return writer.toBuffer(); }; diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index 69806069c..ca372a2ff 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -38,7 +38,7 @@ UpdateLeaderboard.prototype.build48 = function (protocol) { else writer.writeStringZeroUtf8(name); } - return writer.ToBuffer(); + return writer.toBuffer(); }; // (FFA) Leaderboard Update @@ -63,7 +63,7 @@ UpdateLeaderboard.prototype.build49 = function (protocol) { else writer.writeStringZeroUtf8(name); } - return writer.ToBuffer(); + return writer.toBuffer(); }; // (Team) Leaderboard Update @@ -82,5 +82,5 @@ UpdateLeaderboard.prototype.build50 = function (protocol) { writer.writeFloat(value); // isMe flag (previously cell ID) } - return writer.ToBuffer(); + return writer.toBuffer(); }; \ No newline at end of file diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index 05bc85852..dd26f1c48 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -2,11 +2,11 @@ var BinaryWriter = require("./BinaryWriter"); -function UpdateNodes(playerTracker, destroyQueue, nodes, nonVisibleNodes) { +function UpdateNodes(playerTracker, eatNodes, updateNodes, removeNodes) { this.playerTracker = playerTracker; - this.destroyQueue = destroyQueue; - this.nodes = nodes; - this.nonVisibleNodes = nonVisibleNodes; + this.eatNodes = eatNodes; + this.updateNodes = updateNodes; + this.removeNodes = removeNodes; } module.exports = UpdateNodes; @@ -14,40 +14,27 @@ module.exports = UpdateNodes; UpdateNodes.prototype.build = function (protocol) { if (!protocol) return null; - if (protocol <= 4) return this.build4(); - else if (protocol == 5) return this.build5(); - else return this.build6(); + var writer = new BinaryWriter(); + writer.writeUInt8(0x10); // Packet ID + this.writeEatItems(writer); + + if (protocol < 5) this.writeUpdateItems4(writer); + else if (protocol == 5) this.writeUpdateItems5(writer); + else this.writeUpdateItems6(writer); + + this.writeRemoveItems(writer, protocol); + return writer.toBuffer(); }; // protocol 4 -UpdateNodes.prototype.build4 = function () { +UpdateNodes.prototype.writeUpdateItems4 = function (writer) { var scrambleX = this.playerTracker.scrambleX; var scrambleY = this.playerTracker.scrambleY; var scrambleId = this.playerTracker.scrambleId; - var writer = new BinaryWriter(); - writer.writeUInt8(0x10); // Packet ID - - var deadCells = []; - for (var i = 0; i < this.destroyQueue.length; i++) { - var node = this.destroyQueue[i]; - if (node == null) continue; - deadCells.push(node); - } - writer.writeUInt16(deadCells.length >>> 0); // EatRecordCount - for (var i = 0; i < deadCells.length; i++) { - var node = deadCells[i]; - var hunterId = 0; - if (node.getKiller()) { - hunterId = node.getKiller().nodeId; - } - writer.writeUInt32((hunterId^scrambleId) >>> 0); // Hunter ID - writer.writeUInt32((node.nodeId^scrambleId) >>> 0); // Prey ID - } - - for (var i = 0; i < this.nodes.length; i++) { - var node = this.nodes[i]; - if (node == null || node.nodeId == 0) + for (var i = 0; i < this.updateNodes.length; i++) { + var node = this.updateNodes[i]; + if (node.nodeId == 0) continue; var cellX = node.position.x + scrambleX; @@ -55,7 +42,7 @@ UpdateNodes.prototype.build4 = function () { var cellName = node.getName(); // Write update record - writer.writeUInt32((node.nodeId^scrambleId) >>> 0); // Cell ID + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID writer.writeInt16(cellX >> 0); // Coordinate X writer.writeInt16(cellY >> 0); // Coordinate Y writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) @@ -75,49 +62,17 @@ UpdateNodes.prototype.build4 = function () { writer.writeStringZeroUnicode(cellName); // Name } writer.writeUInt32(0); // Cell Update record terminator - - for (var i = 0; i < this.nonVisibleNodes.length; i++) { - var node = this.nonVisibleNodes[i]; - if (node == null) continue; - deadCells.push(node); - } - writer.writeUInt32(deadCells.length >>> 0); // RemoveRecordCount - for (var i = 0; i < deadCells.length; i++) { - var node = deadCells[i]; - writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID - } - return writer.ToBuffer(); }; // protocol 5 -UpdateNodes.prototype.build5 = function () { +UpdateNodes.prototype.writeUpdateItems5 = function (writer) { var scrambleX = this.playerTracker.scrambleX; var scrambleY = this.playerTracker.scrambleY; var scrambleId = this.playerTracker.scrambleId; - var writer = new BinaryWriter(); - writer.writeUInt8(0x10); // Packet ID - - var deadCells = []; - for (var i = 0; i < this.destroyQueue.length; i++) { - var node = this.destroyQueue[i]; - if (node == null) continue; - deadCells.push(node); - } - writer.writeUInt16(deadCells.length >>> 0); // EatRecordCount - for (var i = 0; i < deadCells.length; i++) { - var node = deadCells[i]; - var hunterId = 0; - if (node.getKiller()) { - hunterId = node.getKiller().nodeId; - } - writer.writeUInt32((hunterId ^ scrambleId) >>> 0); // Hunter ID - writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Prey ID - } - - for (var i = 0; i < this.nodes.length; i++) { - var node = this.nodes[i]; - if (node == null || node.nodeId == 0) + for (var i = 0; i < this.updateNodes.length; i++) { + var node = this.updateNodes[i]; + if (node.nodeId == 0) continue; var cellX = node.position.x + scrambleX; @@ -151,49 +106,17 @@ UpdateNodes.prototype.build5 = function () { writer.writeStringZeroUnicode(cellName); // Cell Name } writer.writeUInt32(0 >> 0); // Cell Update record terminator - - for (var i = 0; i < this.nonVisibleNodes.length; i++) { - var node = this.nonVisibleNodes[i]; - if (node == null) continue; - deadCells.push(node); - } - writer.writeUInt32(deadCells.length >>> 0); // RemoveRecordCount - for (var i = 0; i < deadCells.length; i++) { - var node = deadCells[i]; - writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID - } - return writer.ToBuffer(); }; // protocol 6 -UpdateNodes.prototype.build6 = function () { +UpdateNodes.prototype.writeUpdateItems6 = function (writer) { var scrambleX = this.playerTracker.scrambleX; var scrambleY = this.playerTracker.scrambleY; var scrambleId = this.playerTracker.scrambleId; - var writer = new BinaryWriter(); - writer.writeUInt8(0x10); // Packet ID - - var deadCells = []; - for (var i = 0; i < this.destroyQueue.length; i++) { - var node = this.destroyQueue[i]; - if (node == null) continue; - deadCells.push(node); - } - writer.writeUInt16(deadCells.length >>> 0); // EatRecordCount - for (var i = 0; i < deadCells.length; i++) { - var node = deadCells[i]; - var hunterId = 0; - if (node.getKiller()) { - hunterId = node.getKiller().nodeId; - } - writer.writeUInt32((hunterId ^ scrambleId) >>> 0); // Hunter ID - writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Prey ID - } - - for (var i = 0; i < this.nodes.length; i++) { - var node = this.nodes[i]; - if (node == null || node.nodeId == 0) + for (var i = 0; i < this.updateNodes.length; i++) { + var node = this.updateNodes[i]; + if (node.nodeId == 0) continue; var cellX = node.position.x + scrambleX; @@ -233,16 +156,37 @@ UpdateNodes.prototype.build6 = function () { writer.writeStringZeroUtf8(cellName); // Cell Name in UTF8 } writer.writeUInt32(0); // Cell Update record terminator +}; + +UpdateNodes.prototype.writeEatItems = function (writer) { + var scrambleId = this.playerTracker.scrambleId; + + writer.writeUInt16(this.eatNodes.length >>> 0); // EatRecordCount + for (var i = 0; i < this.eatNodes.length; i++) { + var node = this.eatNodes[i]; + var hunterId = 0; + if (node.getKiller()) { + hunterId = node.getKiller().nodeId; + } + writer.writeUInt32((hunterId ^ scrambleId) >>> 0); // Hunter ID + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Prey ID + } +}; + +UpdateNodes.prototype.writeRemoveItems = function (writer, protocol) { + var scrambleId = this.playerTracker.scrambleId; - for (var i = 0; i < this.nonVisibleNodes.length; i++) { - var node = this.nonVisibleNodes[i]; - if (node == null) continue; - deadCells.push(node); + var length = this.eatNodes.length + this.removeNodes.length; + if (protocol < 6) + writer.writeUInt32(length >>> 0); // RemoveRecordCount + else + writer.writeUInt16(length >>> 0); // RemoveRecordCount + for (var i = 0; i < this.eatNodes.length; i++) { + var node = this.eatNodes[i]; + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID } - writer.writeUInt16(deadCells.length >>> 0); // RemoveRecordCount - for (var i = 0; i < deadCells.length; i++) { - var node = deadCells[i]; - writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID + for (var i = 0; i < this.removeNodes.length; i++) { + var node = this.removeNodes[i]; + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID } - return writer.ToBuffer(); }; From 082caa47a56360c8887077f5613be2f54f907bc4 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Thu, 16 Jun 2016 18:58:32 -0400 Subject: [PATCH 148/417] Add Preliminary Ban Feature + Chat Toggle --- .gitignore | 3 ++ src/GameServer.js | 27 ++++++++++++- src/PacketHandler.js | 4 +- src/blacklist.txt | 0 src/gameserver.ini | 4 ++ src/modules/CommandList.js | 82 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 src/blacklist.txt diff --git a/.gitignore b/.gitignore index 9e610f2ce..45db5c493 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ src/.settings/org.eclipse.wst.jsdt.ui.superType.name # Linux start script warning file src/.readwarning + +# Possible IP address leak +#src/blacklist.txt \ No newline at end of file diff --git a/src/GameServer.js b/src/GameServer.js index ee6bf1942..56c25d94c 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -64,7 +64,9 @@ function GameServer() { serverStatsUpdate: 60, // Amount of seconds per update for the server stats serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. - serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. + serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. + serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. + serverUseBlacklist: 0, // Set to 1 to use blacklist; 0 to not use a blacklist. borderLeft: 0, // Left border of map (Vanilla value: 0) borderRight: 6000, // Right border of map (Vanilla value: 14142.135623730952) borderTop: 0, // Top border of map (Vanilla value: 0) @@ -105,6 +107,9 @@ function GameServer() { tourneyAutoFill: 0, // If set to a value higher than 0, the tournament match will automatically fill up with bots after this amount of seconds tourneyAutoFillPlayers: 1, // The timer for filling the server with bots will not count down unless there is this amount of real players }; + + this.blacklist = []; + // Parse config this.loadConfig(); @@ -181,6 +186,12 @@ GameServer.prototype.onServerSocketOpen = function () { }; GameServer.prototype.onClientSocketOpen = function (ws) { + // Check blacklist first (if enabled). + if (this.config.serverUseBlacklist == 1 && this.blacklist.indexOf(ws._socket.remoteAddress) > -1) { + // IP banned + ws.close(1000, "IP banned"); + return; + } var totalConnections = 0; var ipConnections = 0; for (var i = 0; i < this.clients.length; i++) { @@ -1127,6 +1138,20 @@ GameServer.prototype.loadConfig = function() { // Create a new config fs.writeFileSync('./gameserver.ini', ini.stringify(this.config)); } + + // Sorry for hijacking this function to add banning feature... + try { + // Load and input the contents of the blacklist file + this.blacklist = fs.readFileSync("./blacklist.txt", "utf8").split(/[\r\n]+/).filter(function(x) { + return x != ''; // filter empty lines + }); + } catch (err) { + // Only if blacklist toggle has been turned on + if (this.config.serverUseBlacklist != 0) { + // No blacklist file + console.log("[Game] Info: Blacklist enabled but file not found."); + } + } }; // Stats server diff --git a/src/PacketHandler.js b/src/PacketHandler.js index d5a403672..0170a5b3f 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -50,6 +50,8 @@ PacketHandler.prototype.handleMessage = function(message) { this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, border, this.gameServer.config.serverGamemode, "MultiOgar 1.0")); // Send welcome message this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); + if (this.gameServer.config.serverChat == 0) + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "This server chat is disabled."); if (this.protocol < 4) { this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING Your client has protocol error!"); this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Client sends invalid protocol version "+this.protocol); @@ -114,7 +116,7 @@ PacketHandler.prototype.handleMessage = function(message) { break; case 99: // Chat - if (message.length < 3) // first validation + if (this.gameServer.config.serverChat == 0 || message.length < 3) // first validation break; // chat anti-spam // Just ignore if the time between two messages is smaller than 2 seconds diff --git a/src/blacklist.txt b/src/blacklist.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/gameserver.ini b/src/gameserver.ini index eb769ad1b..37039cb71 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -12,6 +12,8 @@ // serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections // serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. // serverMaxLB: Controls the maximum players displayed on the leaderboard. +// serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. +// serverUseBlacklist: Allows usage of banning feature. 0 = no ban, 1 = use ban. serverMaxConnections = 64 serverIpLimit = 4 serverPort = 50000 @@ -25,6 +27,8 @@ serverStatsUpdate = 60 serverLogLevel = 1 serverScrambleCoords = 1 serverMaxLB = 10 +serverChat = 1 +serverUseBlacklist = 0 // [Border] // Border values of the map (Vanilla values are left/top = 0, right/bottom = 14142.135623730952) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index bf1627cee..2a68b6132 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -28,6 +28,7 @@ Commands.list = { console.log("[Console] ======================== HELP ======================"); console.log("[Console] addbot [number] : add bot to the server"); console.log("[Console] kickbot [number] : kick an amount of bots"); + console.log("[Console] ban [PlayerID | IP] : bans a(n) (player's) IP"); console.log("[Console] board [string] [string] ... : set scoreboard text"); console.log("[Console] boardreset : reset scoreboard text"); console.log("[Console] change [setting] [value] : change specified settings"); @@ -43,6 +44,7 @@ Commands.list = { console.log("[Console] mass [PlayerID] [mass] : set cell(s) mass by client ID"); console.log("[Console] merge [PlayerID] : merge all client's cells once"); console.log("[Console] name [PlayerID] [name] : change cell(s) name by client ID"); + console.log("[Console] pardon [IP] : pardons (unbans) an IP"); console.log("[Console] playerlist : get list of players and bots"); console.log("[Console] pause : pause game , freeze all cells"); console.log("[Console] reload : reload config"); @@ -64,6 +66,53 @@ Commands.list = { } console.log("[Console] Added " + add + " player bots"); }, + ban: function(gameServer, split) { + if (split[1].indexOf(".") > -1) { + var ip = split[1]; + // If input is an IP address + if (ip.split(".").length != 4) { + // an IP without 3 decimals + console.log("[Console] Please specify a valid player ID or IP address!"); + return; + } + gameServer.blacklist.push(ip); + console.log("[Console] Banned IP " + ip); + } + else { + // if input is a Player ID + var id = parseInt(split[1]); + if (isNaN(id)) { + console.log("[Console] Please specify a valid player ID or IP address!"); + return; + } + for (var i in gameServer.clients) { + if (gameServer.clients[i].playerTracker.pID == id) { + var client = gameServer.clients[i].playerTracker; + var len = client.cells.length; + for (var j = 0; j < len; j++) { + gameServer.removeNode(client.cells[0]); + } + + // Push banned IP address + var ip = gameServer.clients[i]._socket.remoteAddress; + gameServer.blacklist.push(ip); + client.socket.close(1000, "Banned from server"); + console.log("[Console] Banned " + client.name + " with IP " + ip); + break; + } + } + } + // TODO: Make it so players with the same IP are kicked. + // Get filestream + var fs = require("fs"); + var blFile = fs.createWriteStream('./blacklist.txt'); + + // Sort the blacklist and write. + gameServer.blacklist.forEach(function (v) { + blFile.write(v + '\n'); + }); + blFile.end(); + }, kickbot: function(gameServer, split) { var toRemove = parseInt(split[1]); if (isNaN(toRemove)) { @@ -170,6 +219,19 @@ Commands.list = { } }, exit: function(gameServer, split) { + if (gameServer.config.serverUseBlacklist != 0) { + console.log("[Console] Writing blacklist..."); + + // Get filestream + var fs = require("fs"); + var blFile = fs.createWriteStream('./blacklist.txt'); + + // Sort the blacklist and write. + gameServer.blacklist.sort().forEach(function (v) { + blFile.write(v + '\n'); + }); + blFile.end(); + } console.log("[Console] Closing server..."); gameServer.socketServer.close(); process.exit(1); @@ -381,6 +443,26 @@ Commands.list = { // Error console.log("[Console] Player " + id + " was not found"); }, + pardon: function(gameServer, split) { + var ip = split[1], + index = gameServer.blacklist.indexOf(ip); + if (index > -1) { + gameServer.blacklist.splice(index, 1); + console.log("[Console] Pardoned IP " + ip) + + // Get filestream + var fs = require("fs"); + var blFile = fs.createWriteStream('./blacklist.txt'); + + // Sort the blacklist and write. + gameServer.blacklist.forEach(function (v) { + blFile.write(v + '\n'); + }); + blFile.end(); + } + else + console.log("[Console] IP " + ip + " was not in the blacklist"); + }, playerlist: function(gameServer, split) { console.log("[Console] Showing " + gameServer.clients.length + " players: "); console.log(" ID | IP | P | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space From 1a067624f4b993b6f5a26f64169bbc3691d73817 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Thu, 16 Jun 2016 19:41:10 -0400 Subject: [PATCH 149/417] Spelling fixes + Linux Install Script Link update The ogar-linux-script still pointed towards the original Ogar. --- ogar-linux-script.sh | 4 ++-- src/PacketHandler.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ogar-linux-script.sh b/ogar-linux-script.sh index fb4160072..48dac8db5 100644 --- a/ogar-linux-script.sh +++ b/ogar-linux-script.sh @@ -9,11 +9,11 @@ download_and_extract () { if [ ! -d Ogar-master ]; then if [ ! -f master.tar.gz ]; then echo "No local master.tar.gz found, downloading with curl." - curl -O -L https://github.com/forairan/Ogar/archive/master.tar.gz + curl -O -L https://github.com/Barbosik/MultiOgar/archive/master.tar.gz fi if [ ! -f master.tar.gz ]; then echo "curl failed to download master.tar.gz, trying wget." - wget https://github.com/forairan/Ogar/archive/master.tar.gz + wget https://github.com/Barbosik/MultiOgar/archive/master.tar.gz if [ ! -f master.tar.gz ]; then echo "wget failed as well. Aborting!" exit 1 diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 0170a5b3f..8dd6149a5 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -51,9 +51,9 @@ PacketHandler.prototype.handleMessage = function(message) { // Send welcome message this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); if (this.gameServer.config.serverChat == 0) - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "This server chat is disabled."); + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "This server's chat is disabled."); if (this.protocol < 4) { - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING Your client has protocol error!"); + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING: Your client has protocol error!"); this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Client sends invalid protocol version "+this.protocol); this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Server assumes it as protocol 4"); } From 00786fc91e51c76c748f2086f3695a87ad5cad68 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 17 Jun 2016 07:30:15 +0200 Subject: [PATCH 150/417] fix ban/kick commands; improve playerlist command --- src/GameServer.js | 117 +++++++++++++++++++++++++---- src/PlayerTracker.js | 9 +++ src/gameserver.ini | 4 +- src/modules/CommandList.js | 147 +++++++++++-------------------------- 4 files changed, 156 insertions(+), 121 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 56c25d94c..502ea86c2 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -54,6 +54,7 @@ function GameServer() { this.config = { // Border - Right: X increases, Down: Y increases (as of 2015-05-20) serverMaxConnections: 64, // Maximum amount of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum amount of connections from the same IP (0 for no limit) + serverIpBanList: 0, // Set to 1 to use IP ban list; 0 to not use a ban list. serverPort: 443, // Server port serverTracker: 0, // Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams @@ -66,7 +67,6 @@ function GameServer() { serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. - serverUseBlacklist: 0, // Set to 1 to use blacklist; 0 to not use a blacklist. borderLeft: 0, // Left border of map (Vanilla value: 0) borderRight: 6000, // Right border of map (Vanilla value: 14142.135623730952) borderTop: 0, // Top border of map (Vanilla value: 0) @@ -108,10 +108,11 @@ function GameServer() { tourneyAutoFillPlayers: 1, // The timer for filling the server with bots will not count down unless there is this amount of real players }; - this.blacklist = []; + this.ipBanList = []; // Parse config this.loadConfig(); + this.loadIpBanList(); // Gamemodes this.gameMode = Gamemode.get(this.config.serverGamemode); @@ -187,7 +188,7 @@ GameServer.prototype.onServerSocketOpen = function () { GameServer.prototype.onClientSocketOpen = function (ws) { // Check blacklist first (if enabled). - if (this.config.serverUseBlacklist == 1 && this.blacklist.indexOf(ws._socket.remoteAddress) > -1) { + if (this.ipBanList && this.ipBanList.length > 0 && this.ipBanList.indexOf(ws._socket.remoteAddress) >= 0) { // IP banned ws.close(1000, "IP banned"); return; @@ -199,7 +200,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { if (socket == null || socket.isConnected == null) continue; totalConnections++; - if (socket.remoteAddress == ws._socket.remoteAddress) + if (socket.isConnected && socket.remoteAddress == ws._socket.remoteAddress) ipConnections++; } if (this.config.serverMaxConnections > 0 && totalConnections >= this.config.serverMaxConnections) { @@ -236,11 +237,13 @@ GameServer.prototype.onClientSocketOpen = function (ws) { this.clients.push(ws); }; -GameServer.prototype.onClientSocketClose = function (ws, reason) { +GameServer.prototype.onClientSocketClose = function (ws, code) { this.log.onDisconnect(ws.remoteAddress); ws.isConnected = false; ws.sendPacket = function (data) { }; + ws.closeReason = { code: ws._closeCode, message: ws._closeMessage }; + ws.playerTracker.disconnect = this.config.playerDisconnectTime * 20; for (var i = 0; i < ws.playerTracker.cells.length; i++) { var cell = ws.playerTracker.cells[i]; @@ -1138,20 +1141,102 @@ GameServer.prototype.loadConfig = function() { // Create a new config fs.writeFileSync('./gameserver.ini', ini.stringify(this.config)); } - - // Sorry for hijacking this function to add banning feature... +}; + +GameServer.prototype.loadIpBanList = function () { + if (!this.config.serverIpBanList) + return; + var fileName = "./ipbanlist.txt"; try { - // Load and input the contents of the blacklist file - this.blacklist = fs.readFileSync("./blacklist.txt", "utf8").split(/[\r\n]+/).filter(function(x) { - return x != ''; // filter empty lines - }); - } catch (err) { - // Only if blacklist toggle has been turned on - if (this.config.serverUseBlacklist != 0) { - // No blacklist file - console.log("[Game] Info: Blacklist enabled but file not found."); + if (fs.existsSync(fileName)) { + // Load and input the contents of the ipbanlist file + this.ipBanList = fs.readFileSync(fileName, "utf8").split(/[\r\n]+/).filter(function (x) { + return x != ''; // filter empty lines + }); + console.log("[Game] " + this.ipBanList.length + " IP ban records loaded."); + } else { + console.log("[Game] " + fileName + " is missing."); } + } catch (err) { + console.log("[Game] Failed to load " + fileName + ": " + err.message); + } +}; + +GameServer.prototype.saveIpBanList = function () { + if (!this.config.serverIpBanList) + return; + var fileName = "./ipbanlist.txt"; + try { + var blFile = fs.createWriteStream(fileName); + // Sort the blacklist and write. + this.ipBanList.sort().forEach(function (v) { + blFile.write(v + '\n'); + }); + blFile.end(); + console.log("[Game] " + this.ipBanList.length + " IP ban records saved."); + } catch (err) { + console.log("[Game] Failed to save " + fileName + ": " + err.message); + } +}; + +GameServer.prototype.banIp = function (ip) { + if (this.ipBanList.indexOf(ip) >= 0) { + console.log("[Game] " + ip + " already in the ban list!"); + return; + } + this.ipBanList.push(ip); + this.clients.forEach(function (socket) { + if (socket == null || !socket.isConnected) + return; + // remove player cells + socket.playerTracker.cells.forEach(function (cell) { + this.removeNode(cell); + }, this); + // disconnect + socket.close(1000, "Banned from server"); + var name = socket.playerTracker.getFriendlyName(); + console.log("[Game] Banned \"" + name + "\" with IP " + ip); + this.sendChatMessage(null, null, "Banned \"" + name + "\""); // notify to don't confuse with server bug + }, this); + this.saveIpBanList(); +}; + +GameServer.prototype.unbanIp = function (ip) { + var index = this.ipBanList.indexOf(ip); + if (index < 0) { + console.log("[Game] IP " + ip + " is not in the banlist"); + return; } + this.ipBanList.splice(index, 1); + console.log("[Game] Unbanned IP: " + ip); + this.saveIpBanList(); +}; + +// Kick player by ID. Use ID = 0 to kick all players +GameServer.prototype.kickId = function (id) { + var count = 0; + this.clients.forEach(function (socket) { + if (socket.isConnected == false) + return; + if (id != 0 && socket.playerTracker.pID != id) + return; + // remove player cells + socket.playerTracker.cells.forEach(function (cell) { + this.removeNode(cell); + }, this); + // disconnect + socket.close(1000, "Kicked from server"); + var name = socket.playerTracker.getFriendlyName(); + console.log("[Game] Kicked \"" + name + "\""); + this.sendChatMessage(null, null, "Kicked \"" + name + "\""); // notify to don't confuse with server bug + count++; + }, this); + if (count > 0) + return; + if (id == 0) + console.log("[Game] No players to kick!"); + else + console.log("[Game] Player with ID="+id+" not found!"); }; // Stats server diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index cb2b3cbb0..b10ac6803 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -74,6 +74,15 @@ module.exports = PlayerTracker; // Setters/Getters +PlayerTracker.prototype.getFriendlyName = function () { + var name = this.getName(); + if (!name) name = ""; + name = name.trim(); + if (name.length == 0) + name = "An unnamed cell"; + return name; +}; + PlayerTracker.prototype.setName = function(name) { this.name = name; }; diff --git a/src/gameserver.ini b/src/gameserver.ini index 37039cb71..49c5dcafa 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -3,6 +3,7 @@ // [Server] // serverIpLimit: Controls the maximum connection amount from single IP (use 0 to disable) +// serverIpBanList: Allows usage of banning feature. 0 = no ban, 1 = use ban. // serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) // serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games // serverBots: Amount of player bots to spawn (Experimental) @@ -13,9 +14,9 @@ // serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. // serverMaxLB: Controls the maximum players displayed on the leaderboard. // serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. -// serverUseBlacklist: Allows usage of banning feature. 0 = no ban, 1 = use ban. serverMaxConnections = 64 serverIpLimit = 4 +serverIpBanList = 0 serverPort = 50000 serverTracker = 0 serverGamemode = 0 @@ -28,7 +29,6 @@ serverLogLevel = 1 serverScrambleCoords = 1 serverMaxLB = 10 serverChat = 1 -serverUseBlacklist = 0 // [Border] // Border values of the map (Vanilla values are left/top = 0, right/bottom = 14142.135623730952) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 2a68b6132..1c6f5d84f 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -44,12 +44,12 @@ Commands.list = { console.log("[Console] mass [PlayerID] [mass] : set cell(s) mass by client ID"); console.log("[Console] merge [PlayerID] : merge all client's cells once"); console.log("[Console] name [PlayerID] [name] : change cell(s) name by client ID"); - console.log("[Console] pardon [IP] : pardons (unbans) an IP"); console.log("[Console] playerlist : get list of players and bots"); console.log("[Console] pause : pause game , freeze all cells"); console.log("[Console] reload : reload config"); console.log("[Console] status : get server status"); console.log("[Console] tp [PlayerID] [X] [Y] : teleport player to specified location"); + console.log("[Console] unban [IP] : unban an IP"); console.log("[Console] virus [X] [Y] [mass] : spawn virus at a specified Location"); console.log("[Console] pl : alias for playerlist"); console.log("[Console] st : alias for status"); @@ -66,8 +66,8 @@ Commands.list = { } console.log("[Console] Added " + add + " player bots"); }, - ban: function(gameServer, split) { - if (split[1].indexOf(".") > -1) { + ban: function (gameServer, split) { + if (split[1].indexOf(".") >= 0) { var ip = split[1]; // If input is an IP address if (ip.split(".").length != 4) { @@ -75,43 +75,29 @@ Commands.list = { console.log("[Console] Please specify a valid player ID or IP address!"); return; } - gameServer.blacklist.push(ip); - console.log("[Console] Banned IP " + ip); + gameServer.banIp(ip); + return; } - else { - // if input is a Player ID - var id = parseInt(split[1]); - if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID or IP address!"); - return; - } - for (var i in gameServer.clients) { - if (gameServer.clients[i].playerTracker.pID == id) { - var client = gameServer.clients[i].playerTracker; - var len = client.cells.length; - for (var j = 0; j < len; j++) { - gameServer.removeNode(client.cells[0]); - } - - // Push banned IP address - var ip = gameServer.clients[i]._socket.remoteAddress; - gameServer.blacklist.push(ip); - client.socket.close(1000, "Banned from server"); - console.log("[Console] Banned " + client.name + " with IP " + ip); - break; - } + // if input is a Player ID + var id = parseInt(split[1]); + if (isNaN(id)) { + console.log("[Console] Please specify a valid player ID or IP address!"); + return; + } + var ip = null; + for (var i in gameServer.clients) { + var client = gameServer.clients[i]; + if (client == null || !client.isConnected) + continue; + if (client.playerTracker.pID == id) { + ip = client._socket.remoteAddress; + break; } } - // TODO: Make it so players with the same IP are kicked. - // Get filestream - var fs = require("fs"); - var blFile = fs.createWriteStream('./blacklist.txt'); - - // Sort the blacklist and write. - gameServer.blacklist.forEach(function (v) { - blFile.write(v + '\n'); - }); - blFile.end(); + if (ip) + gameServer.banIp(ip); + else + console.log("[Console] Player ID " + id + " not found!"); }, kickbot: function(gameServer, split) { var toRemove = parseInt(split[1]); @@ -219,19 +205,6 @@ Commands.list = { } }, exit: function(gameServer, split) { - if (gameServer.config.serverUseBlacklist != 0) { - console.log("[Console] Writing blacklist..."); - - // Get filestream - var fs = require("fs"); - var blFile = fs.createWriteStream('./blacklist.txt'); - - // Sort the blacklist and write. - gameServer.blacklist.sort().forEach(function (v) { - blFile.write(v + '\n'); - }); - blFile.end(); - } console.log("[Console] Closing server..."); gameServer.socketServer.close(); process.exit(1); @@ -278,30 +251,10 @@ Commands.list = { console.log("[Console] Please specify a valid player ID!"); return; } - - for (var i in gameServer.clients) { - if (gameServer.clients[i].playerTracker.pID == id) { - var client = gameServer.clients[i].playerTracker; - var len = client.cells.length; - for (var j = 0; j < len; j++) { - gameServer.removeNode(client.cells[0]); - } - client.socket.close(1000, "Kicked from server"); - console.log("[Console] Kicked " + client.name); - break; - } - } + gameServer.kickId(id); }, - kickall: function(gameServer, split) { - for (var i in gameServer.clients) { - var client = gameServer.clients[i].playerTracker; - var len = client.cells.length; - for (var j = 0; j < len; j++) { - gameServer.removeNode(client.cells[0]); - } - client.socket.close(1000, "Kicked from server"); - console.log("[Console] Kicked " + client.name); - } + kickall: function (gameServer, split) { + gameServer.kickId(0); }, kill: function(gameServer, split) { var id = parseInt(split[1]); @@ -443,25 +396,12 @@ Commands.list = { // Error console.log("[Console] Player " + id + " was not found"); }, - pardon: function(gameServer, split) { - var ip = split[1], - index = gameServer.blacklist.indexOf(ip); - if (index > -1) { - gameServer.blacklist.splice(index, 1); - console.log("[Console] Pardoned IP " + ip) - - // Get filestream - var fs = require("fs"); - var blFile = fs.createWriteStream('./blacklist.txt'); - - // Sort the blacklist and write. - gameServer.blacklist.forEach(function (v) { - blFile.write(v + '\n'); - }); - blFile.end(); + unban: function(gameServer, split) { + if (split.length < 2 || split[1] == null || split[1].trim().length < 1) { + console.log("[Console] Please specify a valid IP!"); + return; } - else - console.log("[Console] IP " + ip + " was not in the blacklist"); + gameServer.unbanIp(split[1].trim()); }, playerlist: function(gameServer, split) { console.log("[Console] Showing " + gameServer.clients.length + " players: "); @@ -477,37 +417,37 @@ Commands.list = { // Get ip (15 digits length) var ip = "[BOT]"; if (socket.isConnected != null) { - if (socket.isConnected) { - ip = socket.remoteAddress; - } else { - ip = "[DISCONNECTED]"; - } + ip = socket.remoteAddress; } ip = fillChar(ip, ' ', 15); var protocol = gameServer.clients[i].packetHandler.protocol; if (protocol == null) protocol = "?" - - - // Get name and data var nick = '', cells = '', score = '', position = '', data = ''; - if (client.spectate) { + if (socket.closeReason != null) { + // Disconnected + var reason = "[DISCONNECTED] "; + if (socket.closeReason.code) + reason += "[" + socket.closeReason.code + "] "; + if (socket.closeReason.message) + reason += socket.closeReason.message; + console.log(" " + id + " | " + ip + " | " + protocol + " | " + reason); + } else if (client.spectate) { try { - nick = gameServer.largestClient.name; + nick = gameServer.largestClient.getFriendlyName(); } catch (e) { // Specating in free-roam mode nick = "in free-roam"; } - nick = (nick == "") ? "An unnamed cell" : nick; data = fillChar("SPECTATING: " + nick, '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); console.log(" " + id + " | " + ip + " | " + protocol + " | " + data); } else if (client.cells.length > 0) { - nick = fillChar((client.name == "") ? "An unnamed cell" : client.name, ' ', gameServer.config.playerMaxNickLength); + nick = fillChar(client.getFriendlyName(), ' ', gameServer.config.playerMaxNickLength); cells = fillChar(client.cells.length, ' ', 5, true); score = fillChar(client.getScore() >> 0, ' ', 6, true); position = fillChar(client.centerPos.x >> 0, ' ', 5, true) + ', ' + fillChar(client.centerPos.y >> 0, ' ', 5, true); @@ -526,6 +466,7 @@ Commands.list = { }, reload: function(gameServer) { gameServer.loadConfig(); + gameServer.loadIpBanList(); console.log("[Console] Reloaded the config file successfully"); }, status: function(gameServer, split) { From 1aa2ed7a711c3534d2782a52c9b7df474e910899 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 17 Jun 2016 08:00:04 +0200 Subject: [PATCH 151/417] more detailed connection status for playerlist --- src/modules/CommandList.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 1c6f5d84f..6c245241c 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -437,7 +437,9 @@ Commands.list = { if (socket.closeReason.message) reason += socket.closeReason.message; console.log(" " + id + " | " + ip + " | " + protocol + " | " + reason); - } else if (client.spectate) { + } if (!socket.packetHandler.protocol) { + console.log(" " + id + " | " + ip + " | " + protocol + " | " + "[CONNECTING]"); + }else if (client.spectate) { try { nick = gameServer.largestClient.getFriendlyName(); } catch (e) { From 4ab3760b651742aca338a907c40b844b9d63b3ff Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 17 Jun 2016 09:55:32 +0200 Subject: [PATCH 152/417] fix player disconnection --- src/GameServer.js | 18 +++++++++--- src/PacketHandler.js | 3 +- src/PlayerTracker.js | 59 +++++++++++++++++++++----------------- src/gamemodes/FFA.js | 2 +- src/gameserver.ini | 2 ++ src/modules/CommandList.js | 2 +- 6 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 502ea86c2..fd852831e 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -52,6 +52,7 @@ function GameServer() { // Config this.config = { // Border - Right: X increases, Down: Y increases (as of 2015-05-20) + serverTimeout: 30, // Seconds to keep connection alive for non-responding client serverMaxConnections: 64, // Maximum amount of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum amount of connections from the same IP (0 for no limit) serverIpBanList: 0, // Set to 1 to use IP ban list; 0 to not use a ban list. @@ -216,6 +217,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { ws.isConnected = true; ws.remoteAddress = ws._socket.remoteAddress; ws.remotePort = ws._socket.remotePort; + ws.lastAliveTime = +new Date; this.log.onConnect(ws.remoteAddress); // Log connections ws.playerTracker = new PlayerTracker(this, ws); @@ -243,8 +245,8 @@ GameServer.prototype.onClientSocketClose = function (ws, code) { ws.isConnected = false; ws.sendPacket = function (data) { }; ws.closeReason = { code: ws._closeCode, message: ws._closeMessage }; + ws.closeTime = +new Date; - ws.playerTracker.disconnect = this.config.playerDisconnectTime * 20; for (var i = 0; i < ws.playerTracker.cells.length; i++) { var cell = ws.playerTracker.cells[i]; if (cell == null) continue; @@ -455,9 +457,17 @@ GameServer.prototype.updateSpawn = function() { GameServer.prototype.updateClients = function () { for (var i = 0; i < this.clients.length; i++) { - var client = this.clients[i]; - if (client == null) continue; - client.playerTracker.update(); + var socket = this.clients[i]; + socket.playerTracker.update(); + } + // remove dead clients + for (var i = 0; i < this.clients.length; ) { + var socket = this.clients[i]; + if (socket.playerTracker.isRemoved) { + this.clients.splice(i, 1); + } else { + i++; + } } }; diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 8dd6149a5..381550be4 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -22,7 +22,7 @@ PacketHandler.prototype.handleMessage = function(message) { return; if (message.length > 2048) { // anti-spamming - this.socket.close(1000, "Spam"); + this.socket.close(1009, "Spam"); return; } var reader = new BinaryReader(message); @@ -60,6 +60,7 @@ PacketHandler.prototype.handleMessage = function(message) { this.isHandshakePassed = true; return; } + this.socket.lastAliveTime = +new Date; switch (packetId) { case 0: diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index b10ac6803..d3f7302d8 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -3,7 +3,8 @@ var GameServer = require('./GameServer'); function PlayerTracker(gameServer, socket) { this.pID = -1; - this.disconnect = -1; // Disconnection + this.isRemoved = false; + this.isCloseRequested = false; this.name = ""; this.skin = ""; this.gameServer = gameServer; @@ -168,8 +169,37 @@ PlayerTracker.prototype.joinGame = function (name, skin) { }; PlayerTracker.prototype.update = function () { - // if initialization is not complete yet then do not update - if (this.socket.packetHandler.protocol == 0) + // Handles disconnection + var time = +new Date; + if (!this.socket.isConnected) { + // wait for playerDisconnectTime + var dt = (time - this.socket.closeTime) / 1000; + if (this.cells.length == 0 || dt >= this.gameServer.config.playerDisconnectTime) { + // Remove all client cells + for (var i = 0; i < this.cells.length; i++) { + var cell = this.cells[0]; + if (cell == null) continue; + this.gameServer.removeNode(cell); + } + // Mark to remove + this.isRemoved = true; + } + return; + } + if (this.isCloseRequested) + return; + // Check timeout + if (this.gameServer.config.serverTimeout) { + var dt = (time - this.socket.lastAliveTime) / 1000; + if (dt >= this.gameServer.config.serverTimeout) { + this.socket.close(1000, "Connection timeout"); + this.isCloseRequested = true; + return; + } + } + + // if initialization is not complete yet then do not send update + if (!this.socket.packetHandler.protocol) return; // Actions buffer (So that people cant spam packets) @@ -239,29 +269,6 @@ PlayerTracker.prototype.update = function () { } else { this.tickLeaderboard--; } - - // Handles disconnections - if (this.disconnect > -1) { - // Player has disconnected... remove it when the timer hits -1 - this.disconnect--; - // Also remove it when its cells are completely eaten not to back up dead clients - if (this.disconnect == -1 || this.cells.length == 0) { - // Remove all client cells - var len = this.cells.length; - for (var i = 0; i < len; i++) { - var cell = this.socket.playerTracker.cells[0]; - if (cell == null) continue; - - this.gameServer.removeNode(cell); - } - - // Remove from client list - var index = this.gameServer.clients.indexOf(this.socket); - if (index != -1) { - this.gameServer.clients.splice(index, 1); - } - } - } }; // Viewing box diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index f14c2089f..136aeaaa6 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -84,7 +84,7 @@ FFA.prototype.updateLB = function(gameServer) { if (client == null) continue; var player = client.playerTracker; - if (player.disconnect > -1) + if (player.isRemoved) continue; // Don't add disconnected players to list var playerScore = player.getScore(); diff --git a/src/gameserver.ini b/src/gameserver.ini index 49c5dcafa..0ec7bb74e 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -2,6 +2,7 @@ // Lines starting with slashes are comment lines // [Server] +// serverTimeout: Seconds to keep connection alive for non-responding client // serverIpLimit: Controls the maximum connection amount from single IP (use 0 to disable) // serverIpBanList: Allows usage of banning feature. 0 = no ban, 1 = use ban. // serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) @@ -14,6 +15,7 @@ // serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. // serverMaxLB: Controls the maximum players displayed on the leaderboard. // serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. +serverTimeout = 30 serverMaxConnections = 64 serverIpLimit = 4 serverIpBanList = 0 diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 6c245241c..698283a2f 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -437,7 +437,7 @@ Commands.list = { if (socket.closeReason.message) reason += socket.closeReason.message; console.log(" " + id + " | " + ip + " | " + protocol + " | " + reason); - } if (!socket.packetHandler.protocol) { + } else if (!socket.packetHandler.protocol) { console.log(" " + id + " | " + ip + " | " + protocol + " | " + "[CONNECTING]"); }else if (client.spectate) { try { From a3ec066da31de4168183360faa40152e2f9a7d81 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 17 Jun 2016 10:15:40 +0200 Subject: [PATCH 153/417] fix ban by ip --- src/GameServer.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/GameServer.js b/src/GameServer.js index fd852831e..b7ce8decc 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1198,6 +1198,8 @@ GameServer.prototype.banIp = function (ip) { this.clients.forEach(function (socket) { if (socket == null || !socket.isConnected) return; + if (socket.remoteAddress != ip) + return; // remove player cells socket.playerTracker.cells.forEach(function (cell) { this.removeNode(cell); From 195aeef8111c44d06488795da86624b9409a1283 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 17 Jun 2016 11:29:07 +0200 Subject: [PATCH 154/417] fix display of connection status --- src/modules/CommandList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 698283a2f..936664325 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -437,7 +437,7 @@ Commands.list = { if (socket.closeReason.message) reason += socket.closeReason.message; console.log(" " + id + " | " + ip + " | " + protocol + " | " + reason); - } else if (!socket.packetHandler.protocol) { + } else if (!socket.packetHandler.protocol && socket.isConnected) { console.log(" " + id + " | " + ip + " | " + protocol + " | " + "[CONNECTING]"); }else if (client.spectate) { try { From 06527ebb6c640c3c2923a93300431e39c36c3d4d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 17 Jun 2016 14:29:30 +0200 Subject: [PATCH 155/417] fix spectating for disconnected player; add disconnected effect --- src/GameServer.js | 20 ++++++++++++++------ src/PlayerTracker.js | 23 +++++++++++++++-------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index b7ce8decc..058b7e8eb 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -247,12 +247,11 @@ GameServer.prototype.onClientSocketClose = function (ws, code) { ws.closeReason = { code: ws._closeCode, message: ws._closeMessage }; ws.closeTime = +new Date; - for (var i = 0; i < ws.playerTracker.cells.length; i++) { - var cell = ws.playerTracker.cells[i]; - if (cell == null) continue; - - cell.calcMove = function () { } - } + this.getGrayColor + // disconnected effect + ws.playerTracker.cells.forEach(function (cell) { + cell.setColor(this.getGrayColor(cell.getColor())); + }, this); }; GameServer.prototype.onClientSocketError = function (ws, error) { @@ -332,6 +331,15 @@ GameServer.prototype.getRandomSpawn = function(mass) { return pos; }; +GameServer.prototype.getGrayColor = function (rgb) { + var luminance = Math.min(255, (rgb.r * 0.2125 + rgb.g * 0.7154 + rgb.b * 0.0721)) >>> 0; + return { + r: luminance, + g: luminance, + b: luminance + }; +}; + GameServer.prototype.getRandomColor = function() { var h = 360 * Math.random(); var s = 248 / 255; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index d3f7302d8..cb368bf27 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -169,6 +169,7 @@ PlayerTracker.prototype.joinGame = function (name, skin) { }; PlayerTracker.prototype.update = function () { + if (this.isRemoved) return; // Handles disconnection var time = +new Date; if (!this.socket.isConnected) { @@ -176,25 +177,31 @@ PlayerTracker.prototype.update = function () { var dt = (time - this.socket.closeTime) / 1000; if (this.cells.length == 0 || dt >= this.gameServer.config.playerDisconnectTime) { // Remove all client cells - for (var i = 0; i < this.cells.length; i++) { - var cell = this.cells[0]; - if (cell == null) continue; - this.gameServer.removeNode(cell); + var cells = this.cells; + this.cells = []; + for (var i = 0; i < cells.length; i++) { + this.gameServer.removeNode(cells[i]); } // Mark to remove this.isRemoved = true; } + // update visible nodes/mouse (for spectators, if any) + var nodes = this.getVisibleNodes(); + nodes.sort(function (a, b) { return a.nodeId - b.nodeId; }); + this.visibleNodes = nodes; + this.mouse.x = this.centerPos.x; + this.mouse.y = this.centerPos.y; + this.socket.packetHandler.pressSpace = false; + this.socket.packetHandler.pressW = false; + this.socket.packetHandler.pressQ = false; return; } - if (this.isCloseRequested) - return; // Check timeout - if (this.gameServer.config.serverTimeout) { + if (!this.isCloseRequested && this.gameServer.config.serverTimeout) { var dt = (time - this.socket.lastAliveTime) / 1000; if (dt >= this.gameServer.config.serverTimeout) { this.socket.close(1000, "Connection timeout"); this.isCloseRequested = true; - return; } } From 4f36438879ac6f74f2e03635934e94502464e795 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 17 Jun 2016 19:21:00 +0200 Subject: [PATCH 156/417] improve rigid body physics quality and performance --- src/GameServer.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 058b7e8eb..9eb837637 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -733,8 +733,9 @@ GameServer.prototype.resolveRigidCollision = function (manifold, border) { manifold.cell1.checkBorder(border); manifold.cell2.checkBorder(border); // update quadTree - this.updateNodeQuad(manifold.cell1); - this.updateNodeQuad(manifold.cell2); + // it's too heavy operation we will update it when resolution will be completed + //this.updateNodeQuad(manifold.cell1); + //this.updateNodeQuad(manifold.cell2); }; // Checks if collision is rigid body collision @@ -891,14 +892,21 @@ GameServer.prototype.updateMoveEngine = function () { } // resolve rigid body collisions - //for (var z = 0; z < 4; z++) { // loop for better rigid body resolution quality (slow) + for (var z = 0; z < 2; z++) { // loop for better rigid body resolution quality (slow) for (var k = 0; k < rigidCollisions.length; k++) { var c = rigidCollisions[k]; var manifold = this.checkCellCollision(c.cell1, c.cell2); if (manifold == null) continue; this.resolveRigidCollision(manifold, border); + // position changed! don't forgot to update quad-tree } - //} + } + // Update quad tree + for (var k = 0; k < rigidCollisions.length; k++) { + var c = rigidCollisions[k]; + this.updateNodeQuad(c.cell1); + this.updateNodeQuad(c.cell2); + } rigidCollisions = null; // resolve eat collisions @@ -962,6 +970,13 @@ GameServer.prototype.updateMoveEngine = function () { var manifold = this.checkCellCollision(c.cell1, c.cell2); if (manifold == null) continue; this.resolveRigidCollision(manifold, border); + // position changed! don't forgot to update quad-tree + } + // Update quad tree + for (var k = 0; k < rigidCollisions.length; k++) { + var c = rigidCollisions[k]; + this.updateNodeQuad(c.cell1); + this.updateNodeQuad(c.cell2); } rigidCollisions = null; From 6465042a5919a02269b60e2b698307dad6f0f8c3 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 17 Jun 2016 21:35:40 -0400 Subject: [PATCH 157/417] Fix Ban With no Argument Error + Remove serverIpBanList Option + minor grammarical stuff serverIpBanList was only controlling the writing of ipbanlist.txt, which everyone who utilizes the ban command will want to keep track of. --- src/GameServer.js | 24 +++++++-------- src/gameserver.ini | 2 -- src/{blacklist.txt => ipbanlist.txt} | 0 src/modules/CommandList.js | 44 ++++++++++++++++++++++++---- 4 files changed, 50 insertions(+), 20 deletions(-) rename src/{blacklist.txt => ipbanlist.txt} (100%) diff --git a/src/GameServer.js b/src/GameServer.js index 9eb837637..a1d4b4776 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -55,7 +55,6 @@ function GameServer() { serverTimeout: 30, // Seconds to keep connection alive for non-responding client serverMaxConnections: 64, // Maximum amount of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum amount of connections from the same IP (0 for no limit) - serverIpBanList: 0, // Set to 1 to use IP ban list; 0 to not use a ban list. serverPort: 443, // Server port serverTracker: 0, // Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams @@ -147,7 +146,7 @@ GameServer.prototype.start = function() { GameServer.prototype.onServerSocketError = function (error) { switch (error.code) { case "EADDRINUSE": - console.log("[Error] Server could not bind to port! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); + console.log("[Error] Server could not bind to port " + this.config.serverPort + "! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); break; case "EACCES": console.log("[Error] Please make sure you are running Ogar with root privileges."); @@ -1177,8 +1176,6 @@ GameServer.prototype.loadConfig = function() { }; GameServer.prototype.loadIpBanList = function () { - if (!this.config.serverIpBanList) - return; var fileName = "./ipbanlist.txt"; try { if (fs.existsSync(fileName)) { @@ -1196,8 +1193,6 @@ GameServer.prototype.loadIpBanList = function () { }; GameServer.prototype.saveIpBanList = function () { - if (!this.config.serverIpBanList) - return; var fileName = "./ipbanlist.txt"; try { var blFile = fs.createWriteStream(fileName); @@ -1214,23 +1209,25 @@ GameServer.prototype.saveIpBanList = function () { GameServer.prototype.banIp = function (ip) { if (this.ipBanList.indexOf(ip) >= 0) { - console.log("[Game] " + ip + " already in the ban list!"); + console.log("[Game] " + ip + " is already in the ban list!"); return; } this.ipBanList.push(ip); + console.log("[Game] The IP " + ip + " has been banned"); this.clients.forEach(function (socket) { - if (socket == null || !socket.isConnected) - return; - if (socket.remoteAddress != ip) + // If already disconnected or the ip does not match + if (socket == null || !socket.isConnected || socket.remoteAddress != ip) return; + // remove player cells socket.playerTracker.cells.forEach(function (cell) { this.removeNode(cell); }, this); + // disconnect socket.close(1000, "Banned from server"); var name = socket.playerTracker.getFriendlyName(); - console.log("[Game] Banned \"" + name + "\" with IP " + ip); + console.log("[Game] Banned: \"" + name + "\" with Player ID " + socket.playerTracker.pID); // Redacted "with IP #.#.#.#" since it'll already be logged above this.sendChatMessage(null, null, "Banned \"" + name + "\""); // notify to don't confuse with server bug }, this); this.saveIpBanList(); @@ -1239,7 +1236,7 @@ GameServer.prototype.banIp = function (ip) { GameServer.prototype.unbanIp = function (ip) { var index = this.ipBanList.indexOf(ip); if (index < 0) { - console.log("[Game] IP " + ip + " is not in the banlist"); + console.log("[Game] IP " + ip + " is not in the ban list!"); return; } this.ipBanList.splice(index, 1); @@ -1271,7 +1268,7 @@ GameServer.prototype.kickId = function (id) { if (id == 0) console.log("[Game] No players to kick!"); else - console.log("[Game] Player with ID="+id+" not found!"); + console.log("[Game] Player with ID "+id+" not found!"); }; // Stats server @@ -1294,6 +1291,7 @@ GameServer.prototype.startStatsServer = function(port) { }.bind(this)); var getStatsBind = this.getStats.bind(this); + // TODO: This causes error if something else already uses this port. Catch the error. this.httpServer.listen(port, function () { // Stats server console.log("[Game] Loaded stats server on port " + port); diff --git a/src/gameserver.ini b/src/gameserver.ini index 0ec7bb74e..981eaca06 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -4,7 +4,6 @@ // [Server] // serverTimeout: Seconds to keep connection alive for non-responding client // serverIpLimit: Controls the maximum connection amount from single IP (use 0 to disable) -// serverIpBanList: Allows usage of banning feature. 0 = no ban, 1 = use ban. // serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) // serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games // serverBots: Amount of player bots to spawn (Experimental) @@ -18,7 +17,6 @@ serverTimeout = 30 serverMaxConnections = 64 serverIpLimit = 4 -serverIpBanList = 0 serverPort = 50000 serverTracker = 0 serverGamemode = 0 diff --git a/src/blacklist.txt b/src/ipbanlist.txt similarity index 100% rename from src/blacklist.txt rename to src/ipbanlist.txt diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 936664325..fe4579f84 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -27,8 +27,9 @@ Commands.list = { help: function(gameServer, split) { console.log("[Console] ======================== HELP ======================"); console.log("[Console] addbot [number] : add bot to the server"); - console.log("[Console] kickbot [number] : kick an amount of bots"); + console.log("[Console] kickbot [number] : kick a number of bots"); console.log("[Console] ban [PlayerID | IP] : bans a(n) (player's) IP"); + console.log("[Console] banlist : get list of banned IPs."); console.log("[Console] board [string] [string] ... : set scoreboard text"); console.log("[Console] boardreset : reset scoreboard text"); console.log("[Console] change [setting] [value] : change specified settings"); @@ -67,21 +68,44 @@ Commands.list = { console.log("[Console] Added " + add + " player bots"); }, ban: function (gameServer, split) { + // Error message + var logInvalid = "[Console] Please specify a valid player ID or IP address!"; + + if (split[1] == null) { + // If no input is given; added to avoid error + console.log(logInvalid); + return; + } + if (split[1].indexOf(".") >= 0) { - var ip = split[1]; // If input is an IP address - if (ip.split(".").length != 4) { + var ip = split[1]; + var ipParts = ip.split("."); + + // Check for invalid decimal numbers of the IP address + for (var i in ipParts) { + // If not numerical or if it's not between 0 and 255 + // TODO: Catch string "e" as it means "10^". + if (isNaN(ipParts[i]) || ipParts[i] < 0 || ipParts[i] >= 256) { + console.log(logInvalid); + return; + } + } + + if (ipParts.length != 4) { // an IP without 3 decimals - console.log("[Console] Please specify a valid player ID or IP address!"); + console.log(logInvalid); return; } + gameServer.banIp(ip); return; } // if input is a Player ID var id = parseInt(split[1]); if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID or IP address!"); + // If not numerical + console.log(logInvalid); return; } var ip = null; @@ -99,6 +123,16 @@ Commands.list = { else console.log("[Console] Player ID " + id + " not found!"); }, + banlist: function(gameServer, split) { + console.log("[Console] Showing " + gameServer.ipBanList.length + " banned IPs: "); + console.log(" IP | IP "); + console.log("-----------------------------------"); + for (var i = 0; i < gameServer.ipBanList.length; i += 2) { + console.log(" " + fillChar(gameServer.ipBanList[i], " ", 15) + " | " + + (gameServer.ipBanList.length === i+1 ? "" : gameServer.ipBanList[i+1] ) + ); + } + }, kickbot: function(gameServer, split) { var toRemove = parseInt(split[1]); if (isNaN(toRemove)) { From 18df4fcfd03e133be12c4aee231abb5e8d8b6cfa Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 17 Jun 2016 21:37:33 -0400 Subject: [PATCH 158/417] Update .gitignore git now ignores this file when committing. --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 45db5c493..c9dc1264c 100644 --- a/.gitignore +++ b/.gitignore @@ -36,5 +36,5 @@ src/.settings/org.eclipse.wst.jsdt.ui.superType.name # Linux start script warning file src/.readwarning -# Possible IP address leak -#src/blacklist.txt \ No newline at end of file +# Prevent possible banned IP address leak +src/ipbanlist.txt From a29b122b2b4b3d397ac584fcc322c8f713cacb8e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 18 Jun 2016 09:49:22 +0200 Subject: [PATCH 159/417] fix split behavior --- src/GameServer.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index a1d4b4776..309764143 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -994,15 +994,25 @@ GameServer.prototype.setAsMovingNode = function(node) { }; GameServer.prototype.splitCells = function(client) { - var len = client.cells.length; - var splitCells = 0; // How many cells have been split - for (var i = 0; i < len; i++) { + // sort by size descending + client.cells.sort(function (a, b) { + return b.getSize() - a.getSize(); + }); + var cellToSplit = []; + for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; - + if (cell.getMass() < this.config.playerMinMassSplit) + continue; + cellToSplit.push(cell); + if (cellToSplit.length + client.cells.length >= this.config.playerMaxCells) + break; + } + var splitCells = 0; // How many cells have been split + for (var i = 0; i < cellToSplit.length; i++) { + var cell = cellToSplit[i]; var dx = client.mouse.x - cell.position.x; var dy = client.mouse.y - cell.position.y; var angle = Math.atan2(dx, dy); - //if (angle == 0) angle = Math.PI / 2; if (isNaN(angle)) angle = 0; if (this.splitPlayerCell(client, cell, angle, cell.getMass() / 2) == true) From 087b38d13b444e0f43f88120f7720b70985dbae0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 18 Jun 2016 10:11:13 +0200 Subject: [PATCH 160/417] performance improvement --- src/GameServer.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 309764143..2565b007d 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -851,13 +851,6 @@ GameServer.prototype.updateMoveEngine = function () { // Move player cells for (var i in this.clients) { var client = this.clients[i].playerTracker; - - // sort by size ascending - client.cells.sort(function (a, b) { - return a.getSize() - b.getSize(); - }); - - // Move for (var j = 0; j < client.cells.length; j++) { var cell1 = client.cells[j]; if (cell1 == null || cell1.isRemoved) From 2f121c690ea8393479e22bac74ccaa4fdaab412b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 18 Jun 2016 10:26:56 +0200 Subject: [PATCH 161/417] update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 48f61d6f5..922113883 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,9 @@ http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends inv ## What's new: +* Split behavior - fixed; +* Protocol code optimized; +* Massive performance improvement with quad-tree lookup; * Split/Eject - physics code rewritten; * Player speed - physics code rewritten; * Cell remerge - physics code rewritten; From c081994e51e602eb9f096fda608b34646ef97682 Mon Sep 17 00:00:00 2001 From: ItzLevvie Date: Sat, 18 Jun 2016 13:22:31 +0100 Subject: [PATCH 162/417] Update Virus.js --- src/entity/Virus.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/entity/Virus.js b/src/entity/Virus.js index e79efabb9..1d3fb0d7e 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -66,11 +66,11 @@ Virus.prototype.onConsume = function(consumer, gameServer) { var m = endMass, i = 0; if (m > 466) { // Threshold - // While can split into an even smaller cell (1000 => 333, 167, etc) - var mult = 3.33; + // While can split into an even smaller cell (10000 => 2500, 1000, etc) + var mult = 4; while (m / mult > 24) { m /= mult; - mult = 2; // First mult 3.33, the next ones 2 + mult = 2.5; // First mult 4, the next ones 2.5 bigSplits.push(m >> 0); i++; } From fbf579e725ddfb03a35f132cb7157f02d83e8ed6 Mon Sep 17 00:00:00 2001 From: F0RIS Date: Sat, 18 Jun 2016 18:18:59 +0300 Subject: [PATCH 163/417] Show lag message in stats --- src/GameServer.js | 1 + src/modules/CommandList.js | 13 ++----------- src/modules/ini.js | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 2565b007d..f97946823 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1314,6 +1314,7 @@ GameServer.prototype.getStats = function() { 'spectators': this.clients.length - players, 'max_players': this.config.serverMaxConnections, 'gamemode': this.gameMode.name, + 'update_time':this.updateTimeAvg.toFixed(3) + " [ms] (" + ini.getLagMessage(this.updateTimeAvg) + ")", 'uptime': Math.round((new Date().getTime() - this.startTime)/1000/60)+" m", 'start_time': this.startTime }; diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index fe4579f84..d9d67357f 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -1,6 +1,7 @@ // Imports var GameMode = require('../gamemodes'); var Entity = require('../entity'); +var ini = require('./ini.js'); function Commands() { this.list = {}; // Empty @@ -517,22 +518,12 @@ Commands.list = { } } - var lagMessage = "extreme high lag"; - if (gameServer.updateTimeAvg < 20) - lagMessage = "perfectly smooth"; - else if (gameServer.updateTimeAvg < 35) - lagMessage = "good"; - else if (gameServer.updateTimeAvg < 40) - lagMessage = "tiny lag"; - else if (gameServer.updateTimeAvg < 50) - lagMessage = "lag"; - console.log("[Console] Connected players: " + gameServer.clients.length + "/" + gameServer.config.serverMaxConnections); console.log("[Console] Players: " + humans + " - Bots: " + bots); console.log("[Console] Server has been running for " + Math.floor(process.uptime()/60) + " minutes"); console.log("[Console] Current memory usage: " + Math.round(process.memoryUsage().heapUsed / 1048576 * 10)/10 + "/" + Math.round(process.memoryUsage().heapTotal / 1048576 * 10)/10 + " mb"); console.log("[Console] Current game mode: " + gameServer.gameMode.name); - console.log("[Console] Current update time: " + gameServer.updateTimeAvg.toFixed(3) + " [ms] (" + lagMessage + ")"); + console.log("[Console] Current update time: " + gameServer.updateTimeAvg.toFixed(3) + " [ms] (" + ini.getLagMessage(gameServer.updateTimeAvg) + ")"); }, tp: function(gameServer, split) { var id = parseInt(split[1]); diff --git a/src/modules/ini.js b/src/modules/ini.js index c01ddb7a2..5d79643a5 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -3,6 +3,7 @@ exports.stringify = exports.encode = encode; exports.safe = safe; exports.unsafe = unsafe; +exports.getLagMessage = getLagMessage; var eol = process.platform === "win32" ? "\r\n" : "\n"; @@ -188,3 +189,17 @@ function unsafe(val, doUnesc) { var isInt = function(n) { return parseInt(n) === n; }; + +function getLagMessage(updateTimeAvg){ + var lagMessage = "extreme high lag"; + if (updateTimeAvg < 20) + lagMessage = "perfectly smooth"; + else if (updateTimeAvg < 35) + lagMessage = "good"; + else if (updateTimeAvg < 40) + lagMessage = "tiny lag"; + else if (updateTimeAvg < 50) + lagMessage = "lag"; + + return lagMessage; +} From c52e7d2a4aa52c0cbe6fb2bd17e1cbe7eb92da3d Mon Sep 17 00:00:00 2001 From: F0RIS Date: Sat, 18 Jun 2016 18:23:01 +0300 Subject: [PATCH 164/417] Fix indent --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index f97946823..68cf84ea6 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1314,7 +1314,7 @@ GameServer.prototype.getStats = function() { 'spectators': this.clients.length - players, 'max_players': this.config.serverMaxConnections, 'gamemode': this.gameMode.name, - 'update_time':this.updateTimeAvg.toFixed(3) + " [ms] (" + ini.getLagMessage(this.updateTimeAvg) + ")", + 'update_time':this.updateTimeAvg.toFixed(3) + " [ms] (" + ini.getLagMessage(this.updateTimeAvg) + ")", 'uptime': Math.round((new Date().getTime() - this.startTime)/1000/60)+" m", 'start_time': this.startTime }; From 3d2d7261decb8da0d14e39cd516949fa8cec3098 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 18 Jun 2016 19:23:40 +0200 Subject: [PATCH 165/417] protocol optimization and performance improvement --- src/PlayerTracker.js | 20 +++++-- src/packet/UpdateNodes.js | 120 ++++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 23 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index cb368bf27..ef552abf3 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -227,12 +227,15 @@ PlayerTracker.prototype.update = function () { var newVisible = this.getVisibleNodes(); newVisible.sort(function (a, b) { return a.nodeId - b.nodeId; }); - var deleteNodes = []; + var delNodes = []; var eatNodes = []; + var addNodes = []; + var updNodes = []; var newIndex = 0; var oldIndex = 0; for (; newIndex < newVisible.length && oldIndex < this.visibleNodes.length;) { if (newVisible[newIndex].nodeId < this.visibleNodes[oldIndex].nodeId) { + addNodes.push(newVisible[newIndex]); newIndex++; continue; } @@ -241,19 +244,25 @@ PlayerTracker.prototype.update = function () { if (node.isRemoved) eatNodes.push(node); else - deleteNodes.push(node); + delNodes.push(node); oldIndex++; continue; } + updNodes.push(newVisible[newIndex]); newIndex++; oldIndex++; } + for (; newIndex < newVisible.length; ) { + var node = newVisible[newIndex]; + addNodes.push(newVisible[newIndex]); + newIndex++; + } for (; oldIndex < this.visibleNodes.length; ) { var node = this.visibleNodes[oldIndex]; if (node.isRemoved) eatNodes.push(node); else - deleteNodes.push(node); + delNodes.push(node); oldIndex++; } this.visibleNodes = newVisible; @@ -261,9 +270,10 @@ PlayerTracker.prototype.update = function () { // Send packet this.socket.sendPacket(new Packet.UpdateNodes( this, + addNodes, + updNodes, eatNodes, - this.visibleNodes, - deleteNodes)); + delNodes)); // Update leaderboard if (this.tickLeaderboard <= 0) { diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index dd26f1c48..19919377c 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -2,11 +2,12 @@ var BinaryWriter = require("./BinaryWriter"); -function UpdateNodes(playerTracker, eatNodes, updateNodes, removeNodes) { +function UpdateNodes(playerTracker, addNodes, updNodes, eatNodes, delNodes) { this.playerTracker = playerTracker; + this.addNodes = addNodes; + this.updNodes = updNodes; this.eatNodes = eatNodes; - this.updateNodes = updateNodes; - this.removeNodes = removeNodes; + this.delNodes = delNodes; } module.exports = UpdateNodes; @@ -32,11 +33,37 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { var scrambleY = this.playerTracker.scrambleY; var scrambleId = this.playerTracker.scrambleId; - for (var i = 0; i < this.updateNodes.length; i++) { - var node = this.updateNodes[i]; + for (var i = 0; i < this.updNodes.length; i++) { + var node = this.updNodes[i]; if (node.nodeId == 0) continue; + var cellX = node.position.x + scrambleX; + var cellY = node.position.y + scrambleY; + + // Write update record + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID + writer.writeInt16(cellX >> 0); // Coordinate X + writer.writeInt16(cellY >> 0); // Coordinate Y + writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + writer.writeUInt8(node.color.r >>> 0); // Color R + writer.writeUInt8(node.color.g >>> 0); // Color G + writer.writeUInt8(node.color.b >>> 0); // Color B + + var flags = 0; + if (node.spiked) + flags |= 0x01; // isVirus + if (false) + flags |= 0x10; // isAgitated + if (node.cellType == 3) + flags |= 0x20; // isEjected + writer.writeUInt8(flags >>> 0); // Flags + writer.writeUInt16(0); // Name + } + for (var i = 0; i < this.addNodes.length; i++) { + var node = this.addNodes[i]; + if (node.nodeId == 0) + continue; var cellX = node.position.x + scrambleX; var cellY = node.position.y + scrambleY; var cellName = node.getName(); @@ -51,7 +78,7 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { writer.writeUInt8(node.color.b >>> 0); // Color B var flags = 0; - if (node.spiked & 1) + if (node.spiked) flags |= 0x01; // isVirus if (false) flags |= 0x10; // isAgitated @@ -70,8 +97,35 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { var scrambleY = this.playerTracker.scrambleY; var scrambleId = this.playerTracker.scrambleId; - for (var i = 0; i < this.updateNodes.length; i++) { - var node = this.updateNodes[i]; + for (var i = 0; i < this.updNodes.length; i++) { + var node = this.updNodes[i]; + if (node.nodeId == 0) + continue; + var cellX = node.position.x + scrambleX; + var cellY = node.position.y + scrambleY; + + // Write update record + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID + writer.writeInt32(cellX >> 0); // Coordinate X + writer.writeInt32(cellY >> 0); // Coordinate Y + writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + writer.writeUInt8(node.color.r >>> 0); // Color R + writer.writeUInt8(node.color.g >>> 0); // Color G + writer.writeUInt8(node.color.b >>> 0); // Color B + + var flags = 0; + if (node.spiked) + flags |= 0x01; // isVirus + if (false) + flags |= 0x10; // isAgitated + if (node.cellType == 3) + flags |= 0x20; // isEjected + writer.writeUInt8(flags >>> 0); // Flags + + writer.writeUInt16(0); // Cell Name + } + for (var i = 0; i < this.addNodes.length; i++) { + var node = this.addNodes[i]; if (node.nodeId == 0) continue; @@ -90,9 +144,9 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { writer.writeUInt8(node.color.b >>> 0); // Color B var flags = 0; - if (node.spiked & 1) + if (node.spiked) flags |= 0x01; // isVirus - if (!(node.spiked & 1) && skinName != null && skinName.length > 0) + if (!node.spiked && skinName != null && skinName.length > 0) flags |= 0x04; // isSkinPresent if (false) flags |= 0x10; // isAgitated @@ -113,9 +167,39 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { var scrambleX = this.playerTracker.scrambleX; var scrambleY = this.playerTracker.scrambleY; var scrambleId = this.playerTracker.scrambleId; - - for (var i = 0; i < this.updateNodes.length; i++) { - var node = this.updateNodes[i]; + for (var i = 0; i < this.updNodes.length; i++) { + var node = this.updNodes[i]; + if (node.nodeId == 0) + continue; + + var cellX = node.position.x + scrambleX; + var cellY = node.position.y + scrambleY; + + // Write update record + writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID + writer.writeInt32(cellX >> 0); // Coordinate X + writer.writeInt32(cellY >> 0); // Coordinate Y + writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) + + var flags = 0; + if (node.spiked) + flags |= 0x01; // isVirus + if (node.cellType != 1 && node.cellType != 3) + flags |= 0x02; // isColorPresent (do not update for food and ejected mass) + if (false) + flags |= 0x10; // isAgitated + if (node.cellType == 3) + flags |= 0x20; // isEjected + writer.writeUInt8(flags >>> 0); // Flags + + if (flags & 0x02) { + writer.writeUInt8(node.color.r >>> 0); // Color R + writer.writeUInt8(node.color.g >>> 0); // Color G + writer.writeUInt8(node.color.b >>> 0); // Color B + } + } + for (var i = 0; i < this.addNodes.length; i++) { + var node = this.addNodes[i]; if (node.nodeId == 0) continue; @@ -131,10 +215,10 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) var flags = 0; - if (node.spiked & 1) + if (node.spiked) flags |= 0x01; // isVirus if (true) - flags |= 0x02; // isColorPresent + flags |= 0x02; // isColorPresent (always for added) if (!(node.spiked & 1) && skinName != null && skinName.length > 1) flags |= 0x04; // isSkinPresent if (!(node.spiked & 1) && cellName != null && cellName.length > 1) @@ -176,7 +260,7 @@ UpdateNodes.prototype.writeEatItems = function (writer) { UpdateNodes.prototype.writeRemoveItems = function (writer, protocol) { var scrambleId = this.playerTracker.scrambleId; - var length = this.eatNodes.length + this.removeNodes.length; + var length = this.eatNodes.length + this.delNodes.length; if (protocol < 6) writer.writeUInt32(length >>> 0); // RemoveRecordCount else @@ -185,8 +269,8 @@ UpdateNodes.prototype.writeRemoveItems = function (writer, protocol) { var node = this.eatNodes[i]; writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID } - for (var i = 0; i < this.removeNodes.length; i++) { - var node = this.removeNodes[i]; + for (var i = 0; i < this.delNodes.length; i++) { + var node = this.delNodes[i]; writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID } }; From 80a5e956af0ada38e392c317ac0013d8aa91b43b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 18 Jun 2016 21:15:57 +0200 Subject: [PATCH 166/417] short text for warning --- src/PacketHandler.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 381550be4..3e4726ba8 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -53,9 +53,7 @@ PacketHandler.prototype.handleMessage = function(message) { if (this.gameServer.config.serverChat == 0) this.gameServer.sendChatMessage(null, this.socket.playerTracker, "This server's chat is disabled."); if (this.protocol < 4) { - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING: Your client has protocol error!"); - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Client sends invalid protocol version "+this.protocol); - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Server assumes it as protocol 4"); + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING: Protocol " + this.protocol + " assumed as 4!"); } this.isHandshakePassed = true; return; From acd09e5622e574176b412481b5243f5ae3779509 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 19 Jun 2016 03:16:34 +0200 Subject: [PATCH 167/417] fix line-split behavior --- src/GameServer.js | 27 +++++++++++++++++++++------ src/entity/Cell.js | 3 ++- src/entity/PlayerCell.js | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 68cf84ea6..dea6cd368 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1005,8 +1005,13 @@ GameServer.prototype.splitCells = function(client) { var cell = cellToSplit[i]; var dx = client.mouse.x - cell.position.x; var dy = client.mouse.y - cell.position.y; + var dl = dx * dx + dy * dy; + if (dl < 1) { + dx = 1; + dy = 0; + } var angle = Math.atan2(dx, dy); - if (isNaN(angle)) angle = 0; + if (isNaN(angle)) angle = Math.PI / 2; if (this.splitPlayerCell(client, cell, angle, cell.getMass() / 2) == true) splitCells++; @@ -1069,15 +1074,25 @@ GameServer.prototype.ejectMass = function(client) { var dx = client.mouse.x - cell.position.x; var dy = client.mouse.y - cell.position.y; - var angle = Math.atan2(dx, dy); - if (isNaN(angle)) angle = 0; - + var dl = dx * dx + dy * dy; + if (dl < 1) { + dx = 1; + dy = 0; + } else { + dl = Math.sqrt(dl); + dx /= dl; + dy /= dl; + } + // Get starting position var pos = { - x: cell.position.x + cell.getSize() * Math.sin(angle), - y: cell.position.y + cell.getSize() * Math.cos(angle) + x: cell.position.x + dx * cell.getSize(), + y: cell.position.y + dy * cell.getSize() }; + var angle = Math.atan2(dx, dy); + if (isNaN(angle)) angle = Math.PI / 2; + // Randomize angle angle += (Math.random() * 0.6) - 0.3; diff --git a/src/entity/Cell.js b/src/entity/Cell.js index c2c530065..7d10c025e 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -193,7 +193,7 @@ Cell.prototype.move = function (border) { var speed = Math.min(speed, 78); // limit max speed with sqrt(780*780/100) speed = Math.min(speed, this.boostDistance); // avoid overlap 0 this.boostDistance -= speed; - if (this.boostDistance <= 1) this.boostDistance = 0; + if (this.boostDistance < 1) this.boostDistance = 0; var v = this.clipVelocity( { x: this.boostDirection.x * speed, y: this.boostDirection.y * speed }, @@ -271,6 +271,7 @@ Cell.prototype.clipVelocity = function (v, border) { v.x = lx; v.y = ly; this.boostDistance += Math.sqrt(ldx * ldx + ldy * ldy); + if (this.boostDistance < 1) this.boostDistance = 0; return v; }; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 20d1d7a89..8cd5da181 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -67,7 +67,7 @@ PlayerCell.prototype.moveUser = function (border) { var dx = x - this.position.x; var dy = y - this.position.y; var squared = dx * dx + dy * dy; - if (squared == 0) return; + if (squared < 1) return; // distance var d = Math.sqrt(squared); From c46369c2038b8de842e7f5b471de56e471976a82 Mon Sep 17 00:00:00 2001 From: NatsuTheGreat Date: Sun, 19 Jun 2016 00:20:55 -0500 Subject: [PATCH 168/417] Update FFA.js --- src/gamemodes/FFA.js | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index 136aeaaa6..c56a04330 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -47,27 +47,28 @@ FFA.prototype.onPlayerSpawn = function(gameServer, player) { // Get ejected cell index = Math.floor(Math.random() * gameServer.nodesEjected.length); var e = gameServer.nodesEjected[index]; - if (e.boostDistance > 0) { + if (e.boostDistance === 0) { // Ejected cell is currently moving gameServer.spawnPlayer(player, pos, startMass); + + + // Remove ejected mass + gameServer.removeNode(e); + + // Inherit + pos = { + x: e.position.x, + y: e.position.y + }; + startMass = Math.max(e.getMass(), gameServer.config.playerStartMass); + + var color = e.getColor(); + player.setColor({ + 'r': color.r, + 'g': color.g, + 'b': color.b + }); } - - // Remove ejected mass - gameServer.removeNode(e); - - // Inherit - pos = { - x: e.position.x, - y: e.position.y - }; - startMass = Math.max(e.getMass(), gameServer.config.playerStartMass); - - var color = e.getColor(); - player.setColor({ - 'r': color.r, - 'g': color.g, - 'b': color.b - }); } } From a04311701becdf2e58410b90b3bf5d7a206748ea Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 19 Jun 2016 11:38:25 +0200 Subject: [PATCH 169/417] fix player spawn issue; fix color property --- src/GameServer.js | 41 +++++++++++++++++++++++++--------- src/PlayerTracker.js | 34 +++++++++++++++------------- src/ai/BotPlayer.js | 2 +- src/entity/Cell.js | 34 +++++++++++++++------------- src/entity/EjectedMass.js | 5 +++++ src/entity/Virus.js | 1 + src/gamemodes/Experimental.js | 6 +---- src/gamemodes/FFA.js | 41 ++-------------------------------- src/gamemodes/HungerGames.js | 2 +- src/gamemodes/Mode.js | 2 +- src/gamemodes/Rainbow.js | 2 +- src/gamemodes/TeamX.js | 10 +++------ src/gamemodes/TeamZ.js | 38 +++++++++---------------------- src/gamemodes/Teams.js | 6 ++--- src/gamemodes/Tournament.js | 2 +- src/gamemodes/Zombie.js | 8 +++---- src/packet/UpdateNodes.js | 42 ++++++++++++++++++++--------------- 17 files changed, 126 insertions(+), 150 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index dea6cd368..79d3b02ff 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -246,10 +246,11 @@ GameServer.prototype.onClientSocketClose = function (ws, code) { ws.closeReason = { code: ws._closeCode, message: ws._closeMessage }; ws.closeTime = +new Date; - this.getGrayColor + var color = this.getGrayColor(ws.playerTracker.getColor()); + ws.playerTracker.setColor(color); // disconnected effect ws.playerTracker.cells.forEach(function (cell) { - cell.setColor(this.getGrayColor(cell.getColor())); + cell.setColor(color); }, this); }; @@ -416,9 +417,9 @@ GameServer.prototype.addNode = function(node) { this.nodes.push(node); - // Adds to the owning player's screen excluding ejected cells - if (node.owner && node.cellType != 3) { - node.setColor(node.owner.color); + // Adds to the owning player's screen + if (node.owner) { + node.setColor(node.owner.getColor()); node.owner.cells.push(node); node.owner.socket.sendPacket(new Packet.AddNode(node.owner, node)); } @@ -621,12 +622,32 @@ GameServer.prototype.spawnFood = function() { }; GameServer.prototype.spawnPlayer = function(player, pos, mass) { - if (mass == null) { // Get starting mass - mass = this.config.playerStartMass; + // Check if there are ejected mass in the world. + if (this.nodesEjected.length > 0) { + var index = Math.floor(Math.random() * 100) + 1; + if (index >= this.config.ejectSpawnPlayer) { + // Get ejected cell + index = Math.floor(Math.random() * this.nodesEjected.length); + var e = this.nodesEjected[index]; + if (e.boostDistance == 0) { + // Remove ejected mass + this.removeNode(e); + // Inherit + pos = { + x: e.position.x, + y: e.position.y + }; + } + } } - if (pos == null) { // Get random pos + if (pos == null) { + // Get random pos pos = this.getRandomSpawn(mass); } + if (mass == null) { + // Get starting mass + mass = this.config.playerStartMass; + } // Spawn player and add to world var cell = new Entity.PlayerCell(this.getNextNodeId(), player, pos, mass, this); @@ -1041,7 +1062,6 @@ GameServer.prototype.splitPlayerCell = function(client, parent, angle, mass) { // Create cell var newCell = new Entity.PlayerCell(this.getNextNodeId(), client, pos, mass, this); - newCell.ejector = parent; newCell.setBoost(780, angle); // Add to node list @@ -1101,11 +1121,10 @@ GameServer.prototype.ejectMass = function(client) { // Create cell var ejected = new Entity.EjectedMass(this.getNextNodeId(), null, pos, this.config.ejectMass, this); + ejected.ejector = cell; ejected.setColor(cell.getColor()); ejected.setBoost(780, angle); - ejected.ejector = cell; - this.nodesEjected.push(ejected); this.addNode(ejected); this.setAsMovingNode(ejected); } diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index ef552abf3..0b0b55909 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -2,13 +2,14 @@ var Packet = require('./packet'); var GameServer = require('./GameServer'); function PlayerTracker(gameServer, socket) { + this.gameServer = gameServer; + this.socket = socket; this.pID = -1; this.isRemoved = false; this.isCloseRequested = false; this.name = ""; this.skin = ""; - this.gameServer = gameServer; - this.socket = socket; + this.color = { r: 0, g: 0, b: 0 }; this.visibleNodes = []; this.cells = []; this.mergeOverride = false; // Triggered by console command @@ -31,8 +32,8 @@ function PlayerTracker(gameServer, socket) { this.sightWidth = 0; this.sightHeight = 0; this.centerPos = { // Center of map - x: 3000, - y: 3000 + x: 0, + y: 0 }; this.viewBox = { left: 0, @@ -100,6 +101,20 @@ PlayerTracker.prototype.getSkin = function () { return this.skin; }; +PlayerTracker.prototype.getColor = function (color) { + return this.color; +}; + +PlayerTracker.prototype.setColor = function (color) { + this.color.r = color.r; + this.color.g = color.g; + this.color.b = color.b; +}; + +PlayerTracker.prototype.getTeam = function () { + return this.team; +}; + PlayerTracker.prototype.getScore = function () { if (this.isMassChanged) this.updateMass(); @@ -135,17 +150,6 @@ PlayerTracker.prototype.massChanged = function () { this.isMassChanged = true; }; - -PlayerTracker.prototype.setColor = function(color) { - this.color.r = color.r; - this.color.g = color.g; - this.color.b = color.b; -}; - -PlayerTracker.prototype.getTeam = function() { - return this.team; -}; - // Functions PlayerTracker.prototype.joinGame = function (name, skin) { diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index 24459a08f..91f1e090f 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -4,7 +4,7 @@ var Vector = require('vector2-node'); function BotPlayer() { PlayerTracker.apply(this, Array.prototype.slice.call(arguments)); - //this.color = gameServer.getRandomColor(); + //this.setColor(gameServer.getRandomColor()); this.splitCooldown = 0; } diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 7d10c025e..e536531e4 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -1,29 +1,28 @@ function Cell(nodeId, owner, position, mass, gameServer) { this.nodeId = nodeId; this.owner = owner; // playerTracker that owns this cell - if (gameServer != null) - this.tickOfBirth = gameServer.getTick(); - this.color = { - r: 0, - g: 255, - b: 0 - }; - this.position = position; + this.gameServer = gameServer; + + this.tickOfBirth = 0; + this.color = { r: 0, g: 0, b: 0 }; + this.position = { x: 0, y: 0 }; this._size = 0; this._mass = 0; this._squareSize = 0; - this.setMass(mass); // Starting mass of the cell - this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass - this.spiked = 0; // If 1, then this cell has spikes around it - - this.killedBy; // Cell that ate this cell - this.gameServer = gameServer; + this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass + this.spiked = 0; // If 1, then this cell has spikes around it + this.killedBy = null; // Cell that ate this cell this.boostDistance = 0; this.boostDirection = { x: 1, y: 0, angle: 0 }; this.ejector = null; - this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells + if (gameServer != null) + this.tickOfBirth = gameServer.getTick(); + if (mass != null) + this.setMass(mass); + if (position != null) + this.setPosition(position.x, position.y); } module.exports = Cell; @@ -187,7 +186,10 @@ Cell.prototype.setBoost = function (distance, angle) { }; Cell.prototype.move = function (border) { - if (this.boostDistance <= 0) return; + if (this.boostDistance <= 0) { + this.boostDistance = 0; + return; + } var speed = Math.sqrt(this.boostDistance * this.boostDistance / 100); var speed = Math.min(speed, 78); // limit max speed with sqrt(780*780/100) diff --git a/src/entity/EjectedMass.js b/src/entity/EjectedMass.js index cf6c1a296..4fd2fdef3 100644 --- a/src/entity/EjectedMass.js +++ b/src/entity/EjectedMass.js @@ -28,6 +28,11 @@ EjectedMass.prototype.sendUpdate = function() { return true; }; +EjectedMass.prototype.onAdd = function (gameServer) { + // Add to list of ejected mass + gameServer.nodesEjected.push(this); +}; + EjectedMass.prototype.onRemove = function(gameServer) { // Remove from list of ejected mass var index = gameServer.nodesEjected.indexOf(this); diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 1d3fb0d7e..4f568ebd7 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -7,6 +7,7 @@ function Virus() { this.spiked = 1; this.fed = 0; this.isMotherCell = false; // Not to confuse bots + this.setColor({ r: 0, g: 255, b: 0 }); } module.exports = Virus; diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 6745fcb4c..2d41e1950 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -143,11 +143,7 @@ function MotherCell() { // Temporary - Will be in its own file if Zeach decides Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = 2; // Copies virus cell - this.color = { - r: 205, - g: 85, - b: 100 - }; + this.setColor({ r: 205, g: 85, b: 100 }); this.spiked = 1; this.isMotherCell = true; // Not to confuse bots } diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index c56a04330..ca6795e97 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -34,46 +34,9 @@ FFA.prototype.leaderboardAddSort = function(player, leaderboard) { // Override FFA.prototype.onPlayerSpawn = function(gameServer, player) { - // Random color - player.color = gameServer.getRandomColor(); - - // Set up variables - var pos, startMass; - - // Check if there are ejected mass in the world. - if (gameServer.nodesEjected.length > 0) { - var index = Math.floor(Math.random() * 100) + 1; - if (index >= gameServer.config.ejectSpawnPlayer) { - // Get ejected cell - index = Math.floor(Math.random() * gameServer.nodesEjected.length); - var e = gameServer.nodesEjected[index]; - if (e.boostDistance === 0) { - // Ejected cell is currently moving - gameServer.spawnPlayer(player, pos, startMass); - - - // Remove ejected mass - gameServer.removeNode(e); - - // Inherit - pos = { - x: e.position.x, - y: e.position.y - }; - startMass = Math.max(e.getMass(), gameServer.config.playerStartMass); - - var color = e.getColor(); - player.setColor({ - 'r': color.r, - 'g': color.g, - 'b': color.b - }); - } - } - } - + player.setColor(gameServer.getRandomColor()); // Spawn player - gameServer.spawnPlayer(player, pos, startMass); + gameServer.spawnPlayer(player); }; FFA.prototype.updateLB = function(gameServer) { diff --git a/src/gamemodes/HungerGames.js b/src/gamemodes/HungerGames.js index 32be736f7..180a5c882 100644 --- a/src/gamemodes/HungerGames.js +++ b/src/gamemodes/HungerGames.js @@ -296,7 +296,7 @@ HungerGames.prototype.onServerInit = function(gameServer) { HungerGames.prototype.onPlayerSpawn = function(gameServer, player) { // Only spawn players if the game hasnt started yet if ((this.gamePhase == 0) && (this.contenders.length < this.maxContenders)) { - player.color = gameServer.getRandomColor(); // Random color + player.setColor(gameServer.getRandomColor()); // Random color this.contenders.push(player); // Add to contenders list gameServer.spawnPlayer(player, this.getPos()); diff --git a/src/gamemodes/Mode.js b/src/gamemodes/Mode.js index 486adc3b3..b7555e55b 100644 --- a/src/gamemodes/Mode.js +++ b/src/gamemodes/Mode.js @@ -31,7 +31,7 @@ Mode.prototype.onPlayerInit = function(player) { Mode.prototype.onPlayerSpawn = function(gameServer, player) { // Called when a player is spawned - player.color = gameServer.getRandomColor(); // Random color + player.setColor(gameServer.getRandomColor()); // Random color gameServer.spawnPlayer(player); }; diff --git a/src/gamemodes/Rainbow.js b/src/gamemodes/Rainbow.js index 49b94154f..437b1809c 100644 --- a/src/gamemodes/Rainbow.js +++ b/src/gamemodes/Rainbow.js @@ -148,7 +148,7 @@ Rainbow.prototype.changeColor = function(node) { node.rainbow = 0; } - node.color = this.colors[node.rainbow]; + node.setColor(this.colors[node.rainbow]); node.rainbow += this.speed; }; diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 28d3b5c80..075134f06 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -279,10 +279,10 @@ TeamX.prototype.onServerInit = function(gameServer) { for (var i = 0; i < gameServer.clients.length; i++) { var client = gameServer.clients[i].playerTracker; this.onPlayerInit(client); - client.color = this.getTeamColor(client.team); + client.setColor(this.getTeamColor(client.team)); for (var j = 0; j < client.cells.length; j++) { var cell = client.cells[j]; - cell.setColor(client.color); + cell.setColor(client.getColor()); this.nodes[client.team].push(cell); } } @@ -331,11 +331,7 @@ function MotherCell() { // Temporary - Will be in its own file if Zeach decides Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = 2; // Copies virus cell - this.color = { - r: 205, - g: 85, - b: 100 - }; + this.setColor({ r: 205, g: 85, b: 100 }); this.spiked = 1; } diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 95f691e51..80955f4c4 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -107,11 +107,7 @@ TeamZ.prototype.updateZColor = function(client, mask) { g: (mask & 0x2) > 0 ? client.zColorFactor : 7, b: (mask & 0x1) > 0 ? client.zColorFactor : 7 }; - client.color = { - r: color.r, - g: color.g, - b: color.b - }; + client.setColor(color); for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; cell.setColor(color); @@ -242,11 +238,11 @@ TeamZ.prototype.startGame = function(gameServer) { client.crazyTimer = 0; client.eatenHeroTimer = 0; client.eatenBrainTimer = 0; - client.color = gameServer.getRandomColor(); + client.setColor(gameServer.getRandomColor()); for (var j = 0; j < client.cells.length; j++) { var cell = client.cells[j]; if (cell) { - cell.setColor(client.color); + cell.setColor(client.getColor()); cell.setMass(gameServer.config.playerStartMass); this.resetSpeedCell(cell); } @@ -662,7 +658,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { client.eatenBrainTimer = 0; client.eatenHeroTimer = 0; client.crazyTimer = 0; - client.color = this.defaultColor; + client.setColor(this.defaultColor); client.team = 1; for (var j = 0; j < client.cells.length; j++) { var cell = client.cells[j]; @@ -784,7 +780,7 @@ TeamZ.prototype.onTick = function(gameServer) { // reset color: if (client.cured == true) - cell.setColor(client.color); + cell.setColor(client.getColor()); } if (client.cured == true) { @@ -800,7 +796,7 @@ TeamZ.prototype.onTick = function(gameServer) { var blinkColor = null; if (client.colorToggle == 20) { - blinkColor = client.color; + blinkColor = client.getColor(); client.colorToggle = 0; } else { if (client.cured == true) { @@ -843,7 +839,7 @@ TeamZ.prototype.onTick = function(gameServer) { }; // Yellow scheme } } else { - color = client.color; // reset + color = client.getColor(); // reset } for (var j = 0; j < client.cells.length; j++) { @@ -875,11 +871,7 @@ TeamZ.prototype.onCellAdd = function(cell) { var client = cell.owner; if (client.cells.length == 1) { // first cell client.team = client.pID; - client.color = { - r: cell.color.r, - g: cell.color.g, - b: cell.color.b - }; + client.setColor(cell.getColor()); client.eatenBrainTimer = 0; client.eatenHeroTimer = 0; client.crazyTimer = 0; @@ -888,7 +880,7 @@ TeamZ.prototype.onCellAdd = function(cell) { if (this.state == GameState.IN_PROGRESS) { this.turnToZombie(client); } else { - client.color = this.defaultColor; + client.setColor(this.defaultColor); cell.setColor(this.defaultColor); client.team = 1; // game not started yet } @@ -1053,11 +1045,7 @@ function Hero() { this.cellType = CellType.HERO; //this.spiked = 1; - this.color = { - r: 255, - g: 255, - b: 7 - }; + this.setColor({ r: 255, g: 255, b: 7 }); this.setMass(60); } @@ -1127,11 +1115,7 @@ function Brain() { this.cellType = CellType.BRAIN; //this.spiked = 1; - this.color = { - r: 255, - g: 7, - b: 255 - }; + this.setColor({ r: 255, g: 7, b: 255 }); this.setMass(60); } diff --git a/src/gamemodes/Teams.js b/src/gamemodes/Teams.js index 3f7f60837..eb71c74f8 100644 --- a/src/gamemodes/Teams.js +++ b/src/gamemodes/Teams.js @@ -51,7 +51,7 @@ Teams.prototype.getTeamColor = function(team) { Teams.prototype.onPlayerSpawn = function(gameServer, player) { // Random color based on team - player.color = this.getTeamColor(player.team); + player.setColor(this.getTeamColor(player.team)); // Spawn player gameServer.spawnPlayer(player); }; @@ -66,10 +66,10 @@ Teams.prototype.onServerInit = function(gameServer) { for (var i = 0; i < gameServer.clients.length; i++) { var client = gameServer.clients[i].playerTracker; this.onPlayerInit(client); - client.color = this.getTeamColor(client.team); + client.setColor(this.getTeamColor(client.team)); for (var j = 0; j < client.cells.length; j++) { var cell = client.cells[j]; - cell.setColor(client.color); + cell.setColor(client.getColor()); this.nodes[client.team].push(cell); } } diff --git a/src/gamemodes/Tournament.js b/src/gamemodes/Tournament.js index a8cf64dc3..241441ac0 100644 --- a/src/gamemodes/Tournament.js +++ b/src/gamemodes/Tournament.js @@ -129,7 +129,7 @@ Tournament.prototype.onServerInit = function(gameServer) { Tournament.prototype.onPlayerSpawn = function(gameServer, player) { // Only spawn players if the game hasnt started yet if ((this.gamePhase == 0) && (this.contenders.length < this.maxContenders)) { - player.color = gameServer.getRandomColor(); // Random color + player.setColor(gameServer.getRandomColor()); // Random color this.contenders.push(player); // Add to contenders list gameServer.spawnPlayer(player); diff --git a/src/gamemodes/Zombie.js b/src/gamemodes/Zombie.js index 35c011efa..428ba6562 100755 --- a/src/gamemodes/Zombie.js +++ b/src/gamemodes/Zombie.js @@ -41,7 +41,7 @@ Zombie.prototype.leaderboardAddSort = function(player, leaderboard) { Zombie.prototype.makeZombie = function(player) { // turns a player into a zombie player.team = 0; - player.color = this.zombieColor; + player.setColor(this.zombieColor); for (var i = 0; i < player.cells.length; i++) { // remove cell from players array var index = this.players.indexOf(player.cells[i]); @@ -49,7 +49,7 @@ Zombie.prototype.makeZombie = function(player) { this.players.splice(index, 1); } // change color of cell - player.cells[i].color = this.zombieColor; + player.cells[i].setColor(this.zombieColor); // add cell to zombie array this.zombies.push(player.cells[i]); } @@ -61,11 +61,11 @@ Zombie.prototype.onPlayerSpawn = function(gameServer, player) { // make player a zombie if there are none if (this.zombies.length == 0) { player.team = 0; - player.color = this.zombieColor; + player.setColor(this.zombieColor); } else { // use player id as team so that bots are still able to fight (even though they probably turn into zombies very fast) player.team = player.pID; - player.color = gameServer.getRandomColor(); + player.setColor(gameServer.getRandomColor()); } // Spawn player diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index 19919377c..865747cc0 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -45,9 +45,10 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { writer.writeInt16(cellX >> 0); // Coordinate X writer.writeInt16(cellY >> 0); // Coordinate Y writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) - writer.writeUInt8(node.color.r >>> 0); // Color R - writer.writeUInt8(node.color.g >>> 0); // Color G - writer.writeUInt8(node.color.b >>> 0); // Color B + var color = node.getColor(); + writer.writeUInt8(color.r >>> 0); // Color R + writer.writeUInt8(color.g >>> 0); // Color G + writer.writeUInt8(color.b >>> 0); // Color B var flags = 0; if (node.spiked) @@ -73,9 +74,10 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { writer.writeInt16(cellX >> 0); // Coordinate X writer.writeInt16(cellY >> 0); // Coordinate Y writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) - writer.writeUInt8(node.color.r >>> 0); // Color R - writer.writeUInt8(node.color.g >>> 0); // Color G - writer.writeUInt8(node.color.b >>> 0); // Color B + var color = node.getColor(); + writer.writeUInt8(color.r >>> 0); // Color R + writer.writeUInt8(color.g >>> 0); // Color G + writer.writeUInt8(color.b >>> 0); // Color B var flags = 0; if (node.spiked) @@ -109,9 +111,10 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { writer.writeInt32(cellX >> 0); // Coordinate X writer.writeInt32(cellY >> 0); // Coordinate Y writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) - writer.writeUInt8(node.color.r >>> 0); // Color R - writer.writeUInt8(node.color.g >>> 0); // Color G - writer.writeUInt8(node.color.b >>> 0); // Color B + var color = node.getColor(); + writer.writeUInt8(color.r >>> 0); // Color R + writer.writeUInt8(color.g >>> 0); // Color G + writer.writeUInt8(color.b >>> 0); // Color B var flags = 0; if (node.spiked) @@ -139,9 +142,10 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { writer.writeInt32(cellX >> 0); // Coordinate X writer.writeInt32(cellY >> 0); // Coordinate Y writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) - writer.writeUInt8(node.color.r >>> 0); // Color R - writer.writeUInt8(node.color.g >>> 0); // Color G - writer.writeUInt8(node.color.b >>> 0); // Color B + var color = node.getColor(); + writer.writeUInt8(color.r >>> 0); // Color R + writer.writeUInt8(color.g >>> 0); // Color G + writer.writeUInt8(color.b >>> 0); // Color B var flags = 0; if (node.spiked) @@ -193,9 +197,10 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { writer.writeUInt8(flags >>> 0); // Flags if (flags & 0x02) { - writer.writeUInt8(node.color.r >>> 0); // Color R - writer.writeUInt8(node.color.g >>> 0); // Color G - writer.writeUInt8(node.color.b >>> 0); // Color B + var color = node.getColor(); + writer.writeUInt8(color.r >>> 0); // Color R + writer.writeUInt8(color.g >>> 0); // Color G + writer.writeUInt8(color.b >>> 0); // Color B } } for (var i = 0; i < this.addNodes.length; i++) { @@ -230,9 +235,10 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { writer.writeUInt8(flags >>> 0); // Flags if (flags & 0x02) { - writer.writeUInt8(node.color.r >>> 0); // Color R - writer.writeUInt8(node.color.g >>> 0); // Color G - writer.writeUInt8(node.color.b >>> 0); // Color B + var color = node.getColor(); + writer.writeUInt8(color.r >>> 0); // Color R + writer.writeUInt8(color.g >>> 0); // Color G + writer.writeUInt8(color.b >>> 0); // Color B } if (flags & 0x04) writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 From 7163201e2968e6f27a74736b15ba7b4968e7015b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 19 Jun 2016 12:22:04 +0200 Subject: [PATCH 170/417] rename Cell.spiked to isSpiked, use boolean; fix protocol for spiked player cell --- src/entity/Cell.js | 3 ++- src/entity/Virus.js | 2 +- src/gamemodes/Experimental.js | 2 +- src/gamemodes/TeamX.js | 2 +- src/gamemodes/TeamZ.js | 4 +-- src/packet/UpdateNodes.js | 46 ++++++++++++++++++++--------------- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index e536531e4..bf3c84082 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -10,7 +10,8 @@ function Cell(nodeId, owner, position, mass, gameServer) { this._mass = 0; this._squareSize = 0; this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass - this.spiked = 0; // If 1, then this cell has spikes around it + this.isSpiked = false; // If true, then this cell has spikes around it + this.isAgitated = false;// If true, then this cell has waves on it's outline this.killedBy = null; // Cell that ate this cell this.boostDistance = 0; diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 4f568ebd7..ad5167029 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -4,7 +4,7 @@ function Virus() { Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = 2; - this.spiked = 1; + this.isSpiked = true; this.fed = 0; this.isMotherCell = false; // Not to confuse bots this.setColor({ r: 0, g: 255, b: 0 }); diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 2d41e1950..6e08c1aae 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -144,7 +144,7 @@ function MotherCell() { // Temporary - Will be in its own file if Zeach decides this.cellType = 2; // Copies virus cell this.setColor({ r: 205, g: 85, b: 100 }); - this.spiked = 1; + this.isSpiked = true; this.isMotherCell = true; // Not to confuse bots } diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 075134f06..786d0be59 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -332,7 +332,7 @@ function MotherCell() { // Temporary - Will be in its own file if Zeach decides this.cellType = 2; // Copies virus cell this.setColor({ r: 205, g: 85, b: 100 }); - this.spiked = 1; + this.isSpiked = true; } MotherCell.prototype = new Cell(); // Base diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 80955f4c4..162f59bdd 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -1044,7 +1044,7 @@ function Hero() { Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = CellType.HERO; - //this.spiked = 1; + //this.isSpiked = true; this.setColor({ r: 255, g: 255, b: 7 }); this.setMass(60); } @@ -1114,7 +1114,7 @@ function Brain() { Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = CellType.BRAIN; - //this.spiked = 1; + //this.isSpiked = true; this.setColor({ r: 255, g: 7, b: 255 }); this.setMass(60); } diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index 865747cc0..abafeccec 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -51,9 +51,9 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { writer.writeUInt8(color.b >>> 0); // Color B var flags = 0; - if (node.spiked) + if (node.isSpiked) flags |= 0x01; // isVirus - if (false) + if (node.isAgitated) flags |= 0x10; // isAgitated if (node.cellType == 3) flags |= 0x20; // isEjected @@ -80,15 +80,18 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { writer.writeUInt8(color.b >>> 0); // Color B var flags = 0; - if (node.spiked) + if (node.isSpiked) flags |= 0x01; // isVirus - if (false) + if (node.isAgitated) flags |= 0x10; // isAgitated if (node.cellType == 3) flags |= 0x20; // isEjected writer.writeUInt8(flags >>> 0); // Flags - writer.writeStringZeroUnicode(cellName); // Name + if (node.owner != null && cellName != null && cellName.length > 0) + writer.writeStringZeroUnicode(cellName); // Name + else + writer.writeUInt16(0); // Name } writer.writeUInt32(0); // Cell Update record terminator }; @@ -117,9 +120,9 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { writer.writeUInt8(color.b >>> 0); // Color B var flags = 0; - if (node.spiked) + if (node.isSpiked) flags |= 0x01; // isVirus - if (false) + if (node.isAgitated) flags |= 0x10; // isAgitated if (node.cellType == 3) flags |= 0x20; // isEjected @@ -148,11 +151,11 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { writer.writeUInt8(color.b >>> 0); // Color B var flags = 0; - if (node.spiked) + if (node.isSpiked) flags |= 0x01; // isVirus - if (!node.spiked && skinName != null && skinName.length > 0) + if (node.owner != null && skinName != null && skinName.length > 0) flags |= 0x04; // isSkinPresent - if (false) + if (node.isAgitated) flags |= 0x10; // isAgitated if (node.cellType == 3) flags |= 0x20; // isEjected @@ -161,7 +164,10 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { if (flags & 0x04) writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 - writer.writeStringZeroUnicode(cellName); // Cell Name + if (node.owner != null && cellName != null && cellName.length > 0) + writer.writeStringZeroUnicode(cellName); // Name + else + writer.writeUInt16(0); // Name } writer.writeUInt32(0 >> 0); // Cell Update record terminator }; @@ -186,11 +192,11 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) var flags = 0; - if (node.spiked) + if (node.isSpiked) flags |= 0x01; // isVirus if (node.cellType != 1 && node.cellType != 3) flags |= 0x02; // isColorPresent (do not update for food and ejected mass) - if (false) + if (node.isAgitated) flags |= 0x10; // isAgitated if (node.cellType == 3) flags |= 0x20; // isEjected @@ -220,15 +226,17 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { writer.writeUInt16(node.getSize() >>> 0); // Cell Size (not to be confused with mass, because mass = size*size/100) var flags = 0; - if (node.spiked) + if (node.isSpiked) flags |= 0x01; // isVirus if (true) flags |= 0x02; // isColorPresent (always for added) - if (!(node.spiked & 1) && skinName != null && skinName.length > 1) - flags |= 0x04; // isSkinPresent - if (!(node.spiked & 1) && cellName != null && cellName.length > 1) - flags |= 0x08; // isNamePresent - if (false) + if (node.owner != null) { + if (skinName != null && skinName.length > 1) + flags |= 0x04; // isSkinPresent + if (cellName != null && cellName.length > 1) + flags |= 0x08; // isNamePresent + } + if (node.isAgitated) flags |= 0x10; // isAgitated if (node.cellType == 3) flags |= 0x20; // isEjected From 8eac215eabe5a2a857e48a8f82d0dbd163cc3159 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 19 Jun 2016 12:32:22 +0200 Subject: [PATCH 171/417] refactoring: Cell.setPosition --- src/entity/Cell.js | 11 ++++++----- src/gamemodes/TeamZ.js | 9 +++++---- src/gamemodes/Zombie.js | 7 ++++--- src/modules/CommandList.js | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index bf3c84082..6f1675d6b 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -23,7 +23,7 @@ function Cell(nodeId, owner, position, mass, gameServer) { if (mass != null) this.setMass(mass); if (position != null) - this.setPosition(position.x, position.y); + this.setPosition(position); } module.exports = Cell; @@ -121,12 +121,13 @@ Cell.prototype.setKiller = function(cell) { this.killedBy = cell; }; -Cell.prototype.setPosition = function (x, y) { - if (isNaN(x) || isNaN(y)) { +Cell.prototype.setPosition = function (pos) { + if (isNaN(pos.x) || isNaN(pos.y)) { console.log("[ERROR] Cell.setPosition: NaN"); return; } - this.position = { x: x, y: y }; + this.position.x = pos.x; + this.position.y = pos.y; }; // Functions @@ -291,7 +292,7 @@ Cell.prototype.checkBorder = function (border) { else if (this.position.y > border.bottom - r) y = border.bottom - r; if (x != this.position.x || y != this.position.y) - this.setPosition(x, y); + this.setPosition({ x: x, y: y }); }; diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 162f59bdd..dcb0adb09 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -172,7 +172,7 @@ TeamZ.prototype.spawnDrug = function(gameServer, cell) { // spawn HERO or BRAIN // Spawn if no cells are colliding if (!collided) { - cell.setPosition(pos.x, pos.y); + cell.setPosition(pos); gameServer.addNode(cell); return true; // SUCCESS with spawn } @@ -957,9 +957,10 @@ TeamZ.prototype.onCellMove = function(x1, y1, cell) { var move = collisionDist - dist; - check.setPosition( - check.position.x + (move * Math.sin(newAngle)) >> 0, - check.position.y + (move * Math.cos(newAngle)) >> 0); + check.setPosition({ + x: check.position.x + (move * Math.sin(newAngle)) >> 0, + y: check.position.y + (move * Math.cos(newAngle)) >> 0 + }); } } } diff --git a/src/gamemodes/Zombie.js b/src/gamemodes/Zombie.js index 428ba6562..33c888fa0 100755 --- a/src/gamemodes/Zombie.js +++ b/src/gamemodes/Zombie.js @@ -137,9 +137,10 @@ Zombie.prototype.onCellMove = function(x1, y1, cell) { var move = collisionDist - dist; - check.setPosition( - check.position.x + (move * Math.sin(newAngle)) >> 0, - check.position.y + (move * Math.cos(newAngle)) >> 0); + check.setPosition({ + x: check.position.x + (move * Math.sin(newAngle)) >> 0, + y: check.position.y + (move * Math.cos(newAngle)) >> 0 + }); } } } diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index d9d67357f..327f42059 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -547,7 +547,7 @@ Commands.list = { if (gameServer.clients[i].playerTracker.pID == id) { var client = gameServer.clients[i].playerTracker; for (var j in client.cells) { - client.cells[j].setPosition(pos.x, pos.y); + client.cells[j].setPosition(pos); gameServer.updateNodeQuad(client.cells[j]); } From f386d0db9dbad08e637d07d2950edead8aa75378 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 19 Jun 2016 12:52:10 +0200 Subject: [PATCH 172/417] refactoring - remove redundant functions --- src/GameServer.js | 23 +++----------------- src/entity/Cell.js | 48 ------------------------------------------ src/gamemodes/TeamX.js | 2 +- src/gamemodes/TeamZ.js | 15 ++++--------- 4 files changed, 8 insertions(+), 80 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 79d3b02ff..c76c87e34 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1145,30 +1145,13 @@ GameServer.prototype.shootVirus = function(parent) { }; GameServer.prototype.getNearestVirus = function(cell) { - // More like getNearbyVirus - var virus = null; - var r = 100; // Checking radius - - var topY = cell.position.y - r; - var bottomY = cell.position.y + r; - - var leftX = cell.position.x - r; - var rightX = cell.position.x + r; - // Loop through all viruses on the map. There is probably a more efficient way of doing this but whatever - var len = this.nodesVirus.length; - for (var i = 0; i < len; i++) { + for (var i = 0; i < this.nodesVirus.length; i++) { var check = this.nodesVirus[i]; if (check === null) continue; - - if (!check.collisionCheck(leftX, topY, rightX, bottomY)) - continue; - - // Add to list of cells nearby - virus = check; - break; // stop checking when a virus found + if (this.checkCellCollision(cell, check) != null) + return check; } - return virus; }; GameServer.prototype.updateCells = function() { diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 6f1675d6b..70474bb22 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -132,54 +132,6 @@ Cell.prototype.setPosition = function (pos) { // Functions -Cell.prototype.collisionCheck = function(left, top, right, bottom) { - return this.position.x > left && - this.position.x < right && - this.position.y > top && - this.position.y < bottom; -}; - -// This collision checking function is based on CIRCLE shape -Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) { - // IF (O1O2 + r <= R) THEN collided. (O1O2: distance b/w 2 centers of cells) - // (O1O2 + r)^2 <= R^2 - // approximately, remove 2*O1O2*r because it requires sqrt(): O1O2^2 + r^2 <= R^2 - - var dx = this.position.x - objectPosition.x; - var dy = this.position.y - objectPosition.y; - - return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize); -}; - -Cell.prototype.collisionCheckCircle = function(x, y, size) { - var dx = this.position.x - x; - var dy = this.position.y - y; - var r = this.getSize() + size; - return dx * dx + dy * dy < r * r; -}; - -Cell.prototype.visibleCheck = function (box) { - // Checks if this cell is visible to the player - if (this.cellType == 1) { - // dot collision detector - return this.position.x >= box.left && - this.position.x <= box.right && - this.position.y >= box.top && - this.position.y <= box.bottom; - } - // rectangle collision detector - var cellSize = this.getSize(); - var minx = this.position.x - cellSize; - var miny = this.position.y - cellSize; - var maxx = this.position.x + cellSize; - var maxy = this.position.y + cellSize; - var d1x = box.left - maxx; - var d1y = box.top - maxy; - var d2x = minx - box.right; - var d2y = miny - box.bottom; - return d1x < 0 && d1y < 0 && d2x < 0 && d2y < 0; -}; - Cell.prototype.setBoost = function (distance, angle) { if (isNaN(angle)) angle = 0; diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 786d0be59..33ce107c7 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -190,7 +190,7 @@ TeamX.prototype.onServerInit = function(gameServer) { } // AABB Collision - if (!check.collisionCheck2(squareR, cell.position)) { + if (gameServer.checkCellCollision(cell, check) == null) { continue; } diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index dcb0adb09..0d87ded71 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -336,13 +336,6 @@ TeamZ.prototype.onServerInit = function(gameServer) { GameServer.prototype.getNearestVirus = function(cell) { // More like getNearbyVirus var virus = null; - var r = 100; // Checking radius - - var topY = cell.position.y - r; - var bottomY = cell.position.y + r; - - var leftX = cell.position.x - r; - var rightX = cell.position.x + r; // loop through all heroes for (var i = 0; i < this.gameMode.heroes.length; i++) { @@ -350,7 +343,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { if (typeof check === 'undefined') { continue; } - if (!check.collisionCheck(leftX, topY, rightX, bottomY)) { + if (this.checkCellCollision(cell, check) == null) { continue; } virus = check; @@ -365,7 +358,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { if (typeof check === 'undefined') { continue; } - if (!check.collisionCheck(leftX, topY, rightX, bottomY)) { + if (this.checkCellCollision(cell, check) == null) { continue; } virus = check; @@ -385,7 +378,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { continue; } - if (!check.collisionCheck(leftX, topY, rightX, bottomY)) { + if (this.checkCellCollision(cell, check) == null) { continue; } @@ -441,7 +434,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { } // AABB Collision - if (!check.collisionCheck2(squareR, cell.position)) { + if (gameServer.checkCellCollision(cell, check)==null) { continue; } From 80be17e5f7fe0e97ba42bab287c750ae2a9f08f7 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 19 Jun 2016 14:37:45 +0200 Subject: [PATCH 173/417] refactoring - border structure --- src/GameServer.js | 68 +++++++++++++++++------------------ src/PacketHandler.js | 8 +---- src/PlayerTracker.js | 35 +++++++----------- src/entity/Cell.js | 25 ++++++------- src/gamemodes/Experimental.js | 7 ++-- src/gamemodes/HungerGames.js | 26 ++++++-------- src/gameserver.ini | 8 ++--- src/packet/SetBorder.js | 8 ++--- 8 files changed, 77 insertions(+), 108 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index c76c87e34..6cf51535c 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -48,10 +48,11 @@ function GameServer() { this.tickCounter = 0; this.tickSpawn = 0; // Used with spawning food - + + this.setBorder(10000, 10000); // Config - this.config = { // Border - Right: X increases, Down: Y increases (as of 2015-05-20) + this.config = { serverTimeout: 30, // Seconds to keep connection alive for non-responding client serverMaxConnections: 64, // Maximum amount of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum amount of connections from the same IP (0 for no limit) @@ -67,10 +68,8 @@ function GameServer() { serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. - borderLeft: 0, // Left border of map (Vanilla value: 0) - borderRight: 6000, // Right border of map (Vanilla value: 14142.135623730952) - borderTop: 0, // Top border of map (Vanilla value: 0) - borderBottom: 6000, // Bottom border of map (Vanilla value: 14142.135623730952) + borderWidth: 14142, // Map border size (Vanilla value: 14142) + borderHeight: 14142, // Map border size (Vanilla value: 14142) spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) foodSpawnAmount: 10, // The amount of food to spawn per interval foodStartAmount: 100, // The starting amount of food in the map @@ -159,13 +158,8 @@ GameServer.prototype.onServerSocketError = function (error) { }; GameServer.prototype.onServerSocketOpen = function () { - var bound = { - minx: this.config.borderLeft, - miny: this.config.borderTop, - maxx: this.config.borderRight, - maxy: this.config.borderBottom - }; - this.quadTree = new QuadNode(bound, 4, 1024); + this.setBorder(this.config.borderWidth, this.config.borderHeight); + this.quadTree = new QuadNode(this.border, 4, 100); // Spawn starting food this.startingFood(); @@ -263,6 +257,20 @@ GameServer.prototype.onClientSocketMessage = function (ws, message) { ws.packetHandler.handleMessage(message); }; +GameServer.prototype.setBorder = function(width, height) { + var hw = width / 2; + var hh = height / 2; + this.border = { + minx: -hw, + miny: -hh, + maxx: hw, + maxy: hh, + width: width, + height: height, + centerx: 0, + centery: 0 + }; +}; GameServer.prototype.getTick = function () { return this.tickCounter; @@ -289,11 +297,9 @@ GameServer.prototype.getNewPlayerID = function() { }; GameServer.prototype.getRandomPosition = function() { - var width = this.config.borderRight - this.config.borderLeft; - var height = this.config.borderBottom - this.config.borderTop; return { - x: Math.floor(this.config.borderLeft + width * Math.random()), - y: Math.floor(this.config.borderTop + height * Math.random()) + x: Math.floor(this.border.minx + this.border.width * Math.random()), + y: Math.floor(this.border.miny + this.border.height * Math.random()) }; }; @@ -307,14 +313,10 @@ GameServer.prototype.getRandomSpawn = function(mass) { // just shift offset and try again var attempt = 1; var maxAttempt = 4; - var w = this.config.borderRight - this.config.borderLeft; - var h = this.config.borderBottom - this.config.borderTop; - var cx = this.config.borderLeft + w / 2; - var cy = this.config.borderTop + w / 2; - var dirx = pos.x < cx ? 1 : -1; - var diry = pos.y < cy ? 1 : -1; - var stepx = w / (2 * maxAttempt); - var stepy = h / (2 * maxAttempt); + var dirx = pos.x < this.border.centerx ? 1 : -1; + var diry = pos.y < this.border.centery ? 1 : -1; + var stepx = this.border.width / (2 * maxAttempt); + var stepy = this.border.height / (2 * maxAttempt); while (unsafe && attempt < maxAttempt) { pos.x += stepx * dirx; pos.y += stepy * diry; @@ -863,12 +865,6 @@ GameServer.prototype.resolveCollision = function (manifold) { }; GameServer.prototype.updateMoveEngine = function () { - var border = { - left: this.config.borderLeft, - top: this.config.borderTop, - right: this.config.borderRight, - bottom: this.config.borderBottom - }; // Move player cells for (var i in this.clients) { var client = this.clients[i].playerTracker; @@ -876,8 +872,8 @@ GameServer.prototype.updateMoveEngine = function () { var cell1 = client.cells[j]; if (cell1 == null || cell1.isRemoved) continue; - cell1.moveUser(border); - cell1.move(border); + cell1.moveUser(this.border); + cell1.move(this.border); this.updateNodeQuad(cell1); } } @@ -910,7 +906,7 @@ GameServer.prototype.updateMoveEngine = function () { var c = rigidCollisions[k]; var manifold = this.checkCellCollision(c.cell1, c.cell2); if (manifold == null) continue; - this.resolveRigidCollision(manifold, border); + this.resolveRigidCollision(manifold, this.border); // position changed! don't forgot to update quad-tree } } @@ -954,7 +950,7 @@ GameServer.prototype.updateMoveEngine = function () { for (var i = 0; i < this.movingNodes.length; i++) { var cell1 = this.movingNodes[i]; if (cell1 == null || cell1.isRemoved) continue; - cell1.move(border); + cell1.move(this.border); this.updateNodeQuad(cell1); } @@ -982,7 +978,7 @@ GameServer.prototype.updateMoveEngine = function () { var c = rigidCollisions[k]; var manifold = this.checkCellCollision(c.cell1, c.cell2); if (manifold == null) continue; - this.resolveRigidCollision(manifold, border); + this.resolveRigidCollision(manifold, this.border); // position changed! don't forgot to update quad-tree } // Update quad tree diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 3e4726ba8..5a25202ed 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -41,13 +41,7 @@ PacketHandler.prototype.handleMessage = function(message) { } // Send handshake response this.socket.sendPacket(new Packet.ClearAll()); - var border = { - left: this.gameServer.config.borderLeft, - top: this.gameServer.config.borderTop, - right: this.gameServer.config.borderRight, - bottom: this.gameServer.config.borderBottom - }; - this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, border, this.gameServer.config.serverGamemode, "MultiOgar 1.0")); + this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, this.gameServer.border, this.gameServer.config.serverGamemode, "MultiOgar")); // Send welcome message this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); if (this.gameServer.config.serverChat == 0) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 0b0b55909..2659304df 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -51,11 +51,8 @@ function PlayerTracker(gameServer, socket) { // Gamemode function if (gameServer) { - // Find center - var width = gameServer.config.borderRight - gameServer.config.borderLeft; - var height = gameServer.config.borderBottom - gameServer.config.borderTop; - this.centerPos.x = gameServer.config.borderLeft + width / 2; - this.centerPos.y = gameServer.config.borderTop + height / 2; + this.centerPos.x = gameServer.border.centerx; + this.centerPos.y = gameServer.border.centery; // Player id this.pID = gameServer.getNewPlayerID(); // Gamemode function @@ -63,8 +60,8 @@ function PlayerTracker(gameServer, socket) { // Only scramble if enabled in config if (gameServer.config.serverScrambleCoords == 1) { // avoid mouse packet limitations - var maxScrambleX = Math.max(0, 32767 - 2000 - width / 2); - var maxScrambleY = Math.max(0, 32767 - 2000 - height / 2); + var maxScrambleX = Math.max(0, 32767 - 1000 - gameServer.border.width / 2); + var maxScrambleY = Math.max(0, 32767 - 1000 - gameServer.border.height / 2); this.scrambleX = Math.floor(maxScrambleX * Math.random()); this.scrambleY = Math.floor(maxScrambleY * Math.random()); } @@ -330,14 +327,10 @@ PlayerTracker.prototype.updateCenterFreeRoam = function () { var x = this.centerPos.x + nx * speed; var y = this.centerPos.y + ny * speed; // check border - if (x < this.gameServer.config.borderLeft) - x = this.gameServer.config.borderLeft; - else if (y > this.gameServer.config.borderRight) - x = this.gameServer.config.borderRight; - if (y < this.gameServer.config.borderTop) - y = this.gameServer.config.borderTop; - else if (y > this.gameServer.config.borderBottom) - y = this.gameServer.config.borderBottom; + x = Math.max(x, this.gameServer.border.minx); + y = Math.max(y, this.gameServer.border.miny); + x = Math.min(x, this.gameServer.border.maxx); + y = Math.min(y, this.gameServer.border.maxy); this.setCenterPos(x, y); }; @@ -408,14 +401,10 @@ PlayerTracker.prototype.setCenterPos = function(x, y) { PlayerTracker.prototype.checkBorderPass = function() { // A check while in free-roam mode to avoid player going into nothingness - if (this.centerPos.x < this.gameServer.config.borderLeft) - this.centerPos.x = this.gameServer.config.borderLeft; - else if (this.centerPos.x > this.gameServer.config.borderRight) - this.centerPos.x = this.gameServer.config.borderRight; - if (this.centerPos.y < this.gameServer.config.borderTop) - this.centerPos.y = this.gameServer.config.borderTop; - else if (this.centerPos.y > this.gameServer.config.borderBottom) - this.centerPos.y = this.gameServer.config.borderBottom; + this.centerPos.x = Math.max(this.centerPos.x, this.gameServer.border.minx); + this.centerPos.y = Math.max(this.centerPos.y, this.gameServer.border.miny); + this.centerPos.x = Math.min(this.centerPos.x, this.gameServer.border.maxx); + this.centerPos.y = Math.min(this.centerPos.y, this.gameServer.border.maxy); }; PlayerTracker.prototype.sendPosPacket = function() { diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 70474bb22..f834a55b8 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -167,10 +167,10 @@ Cell.prototype.clipVelocity = function (v, border) { return v; // zero move, no calculations :) var r = this.getSize() / 2; var bound = { - minx: border.left + r, - miny: border.top + r, - maxx: border.right - r, - maxy: border.bottom - r + minx: border.minx + r, + miny: border.miny + r, + maxx: border.maxx - r, + maxy: border.maxy - r }; var x = this.position.x + v.x; var y = this.position.y + v.y; @@ -181,7 +181,7 @@ Cell.prototype.clipVelocity = function (v, border) { var pright = x <= bound.maxx ? null : this.getLineIntersection( this.position.x, this.position.y, x, y, bound.maxx, bound.miny, bound.maxx, bound.maxy); - var ptop = y >= border.top ? null : this.getLineIntersection( + var ptop = y >= bound.miny ? null : this.getLineIntersection( this.position.x, this.position.y, x, y, bound.minx, bound.miny, bound.maxx, bound.miny); var pbottom = y <= bound.maxy ? null : this.getLineIntersection( @@ -235,16 +235,13 @@ Cell.prototype.checkBorder = function (border) { var r = this.getSize() / 2; var x = this.position.x; var y = this.position.y; - if (x < border.left + r) - x = border.left + r; - else if (x > border.right - r) - x = border.right - r; - if (y < border.top + r) - y = border.top + r; - else if (this.position.y > border.bottom - r) - y = border.bottom - r; - if (x != this.position.x || y != this.position.y) + x = Math.max(x, border.minx + r); + y = Math.max(y, border.miny + r); + x = Math.min(x, border.maxx - r); + y = Math.min(y, border.maxy - r); + if (x != this.position.x || y != this.position.y) { this.setPosition({ x: x, y: y }); + } }; diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 6e08c1aae..12ffe859a 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -88,11 +88,10 @@ Experimental.prototype.onServerInit = function(gameServer) { // Called when the server starts gameServer.run = true; - var mapWidth = gameServer.config.borderRight - gameServer.config.borderLeft; - var mapHeight = gameServer.config.borderBottom - gameServer.config.borderTop; - var mapSize = Math.max(mapWidth, mapHeight); + var mapSize = Math.max(gameServer.border.width, gameServer.border.height); - this.motherMinAmount = Math.ceil(mapSize / 2000); // 7 mother cells for vanilla map size + // 7 mother cells for vanilla map size + this.motherMinAmount = Math.ceil(mapSize / 2000); // Special virus mechanics Virus.prototype.feed = function(feeder, gameServer) { diff --git a/src/gamemodes/HungerGames.js b/src/gamemodes/HungerGames.js index 180a5c882..2de67d099 100644 --- a/src/gamemodes/HungerGames.js +++ b/src/gamemodes/HungerGames.js @@ -92,11 +92,9 @@ HungerGames.prototype.spawnVirus = function(gameServer, pos) { }; HungerGames.prototype.onPlayerDeath = function(gameServer) { - var config = gameServer.config; - config.borderLeft += this.borderDec; - config.borderRight -= this.borderDec; - config.borderTop += this.borderDec; - config.borderBottom -= this.borderDec; + gameServer.setBorder( + gameServer.border.width - this.borderDec * 2, + gameServer.border.height - this.borderDec * 2); // Remove all cells var len = gameServer.nodes.length; @@ -108,16 +106,16 @@ HungerGames.prototype.onPlayerDeath = function(gameServer) { } // Move - if (node.position.x < config.borderLeft) { + if (node.position.x < gameServer.border.minx) { gameServer.removeNode(node); i--; - } else if (node.position.x > config.borderRight) { + } else if (node.position.x > gameServer.border.maxx) { gameServer.removeNode(node); i--; - } else if (node.position.y < config.borderTop) { + } else if (node.position.y < gameServer.border.miny) { gameServer.removeNode(node); i--; - } else if (node.position.y > config.borderBottom) { + } else if (node.position.y > gameServer.border.maxy) { gameServer.removeNode(node); i--; } @@ -139,10 +137,8 @@ HungerGames.prototype.onServerInit = function(gameServer) { gameServer.config.serverBots = this.maxContenders; } gameServer.config.spawnInterval = 20; - gameServer.config.borderLeft = 0; - gameServer.config.borderRight = 6400; - gameServer.config.borderTop = 0; - gameServer.config.borderBottom = 6400; + gameServer.config.borderWidth = 3200; + gameServer.config.borderHeight = 3200; gameServer.config.foodSpawnAmount = 5; // This is hunger games gameServer.config.foodStartAmount = 100; gameServer.config.foodMaxAmount = 200; @@ -153,8 +149,8 @@ HungerGames.prototype.onServerInit = function(gameServer) { gameServer.config.playerDisconnectTime = 10; // So that people dont disconnect and stall the game for too long // Spawn Initial Virus/Large food - var mapWidth = gameServer.config.borderRight - gameServer.config.borderLeft; - var mapHeight = gameServer.config.borderBottom - gameServer.config.borderTop; + var mapWidth = gameServer.border.width; + var mapHeight = gameServer.border.height; // Food this.spawnFood(gameServer, 200, { diff --git a/src/gameserver.ini b/src/gameserver.ini index 981eaca06..38a15c0be 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -31,11 +31,9 @@ serverMaxLB = 10 serverChat = 1 // [Border] -// Border values of the map (Vanilla values are left/top = 0, right/bottom = 14142.135623730952) -borderLeft = 0 -borderRight = 6000 -borderTop = 0 -borderBottom = 6000 +// Border size (vanilla values are 14142.135623730952) +borderWidth = 6000 +borderHeight = 6000 // [Spawn] // Each interval is 1 tick (50 ms) diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index 69412eca2..c33638216 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -14,10 +14,10 @@ module.exports = SetBorder; SetBorder.prototype.build = function(protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x40); // Packet ID - writer.writeDouble(this.border.left + this.playerTracker.scrambleX); - writer.writeDouble(this.border.top + this.playerTracker.scrambleY); - writer.writeDouble(this.border.right + this.playerTracker.scrambleX); - writer.writeDouble(this.border.bottom + this.playerTracker.scrambleY); + writer.writeDouble(this.border.minx + this.playerTracker.scrambleX); + writer.writeDouble(this.border.miny + this.playerTracker.scrambleY); + writer.writeDouble(this.border.maxx + this.playerTracker.scrambleX); + writer.writeDouble(this.border.maxy + this.playerTracker.scrambleY); if (this.gameType != null) { writer.writeUInt32(this.gameType >> 0); var name = this.serverName; From 3ef02624da3a53c152d44ff2bfc6fbb4b5f1d04a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 19 Jun 2016 19:16:39 +0200 Subject: [PATCH 174/417] collisions refactoring --- src/GameServer.js | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 6cf51535c..cf35fc72c 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -764,32 +764,20 @@ GameServer.prototype.resolveRigidCollision = function (manifold, border) { GameServer.prototype.checkRigidCollision = function (manifold) { if (!manifold.cell1.owner || !manifold.cell2.owner) return false; + if (manifold.cell1.owner != manifold.cell2.owner) { + // Different owners + return this.gameMode.haveTeams && + manifold.cell1.owner.getTeam() == manifold.cell2.owner.getTeam(); + } // The same owner - if (manifold.cell1.owner == manifold.cell2.owner) { - var tick = this.getTick(); - if ((manifold.cell1.boostDistance > 0 && manifold.cell1.getAge(tick) < 15) || - (manifold.cell2.boostDistance > 0 && manifold.cell2.getAge(tick) < 15)) { - // just splited => ignore - return false; - } - if (manifold.cell1.owner.mergeOverride) - return false; - // not force remerge => check if can remerge - if (!manifold.cell1.canRemerge() || !manifold.cell2.canRemerge()) { - // cannot remerge => rigid - return true; - } + if (manifold.cell1.owner.mergeOverride) + return false; + var tick = this.getTick(); + if (manifold.cell1.getAge(tick) < 15 || manifold.cell2.getAge(tick) < 15) { + // just splited => ignore return false; } - // Different owners - if (this.gameMode.haveTeams) { - // Team check - if (manifold.cell1.owner.getTeam() == manifold.cell2.owner.getTeam()) { - // cannot eat team member => rigid - return true; - } - } - return false; + return !manifold.cell1.canRemerge() || !manifold.cell2.canRemerge(); }; // Resolves non-rigid body collision From 7ace4cb5e8e41db9f208eec5126623f32ff8065b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 10:09:19 +0200 Subject: [PATCH 175/417] fix mergeOverride split loop --- src/entity/Cell.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index f834a55b8..b9da657a8 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -80,10 +80,12 @@ Cell.prototype.addMass = function (n) { // Check if the cell needs to autosplit before adding mass var newMass = this.getMass() + n; this.setMass(newMass); - if (this.getMass() <= this.gameServer.config.playerMaxMass) + if (!this.owner || this.owner.mergeOverride) return; if (this.owner.cells.length >= this.gameServer.config.playerMaxCells) return; + if (this.getMass() <= this.gameServer.config.playerMaxMass) + return; var splitMass = this.getMass() / 2; var randomAngle = Math.random() * 6.28; // Get random angle this.gameServer.splitPlayerCell(this.owner, this, randomAngle, splitMass); From c2a8f74ce2cbbfb5d6504c3e06a71115d8943d00 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 10:22:58 +0200 Subject: [PATCH 176/417] fix mass limit for playerMaxCells --- src/entity/Cell.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index b9da657a8..92845e2c2 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -82,10 +82,12 @@ Cell.prototype.addMass = function (n) { this.setMass(newMass); if (!this.owner || this.owner.mergeOverride) return; - if (this.owner.cells.length >= this.gameServer.config.playerMaxCells) - return; if (this.getMass() <= this.gameServer.config.playerMaxMass) return; + if (this.owner.cells.length >= this.gameServer.config.playerMaxCells) { + this.setMass(this.gameServer.config.playerMaxMass); + return; + } var splitMass = this.getMass() / 2; var randomAngle = Math.random() * 6.28; // Get random angle this.gameServer.splitPlayerCell(this.owner, this, randomAngle, splitMass); From 96bff9fff48cd9089dfe76367ffb271714ff32f8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 16:51:07 +0200 Subject: [PATCH 177/417] fix mass calculations; fix movement behavior; fix virus behavior; refactoring move/collisions logic --- src/GameServer.js | 64 ++++++++++++++++------------------- src/entity/Cell.js | 43 ++++++++++++----------- src/entity/EjectedMass.js | 14 -------- src/entity/Food.js | 6 ---- src/entity/PlayerCell.js | 22 ++++++++---- src/entity/Virus.js | 41 ++++++++++------------ src/gamemodes/Experimental.js | 60 +++++--------------------------- src/gamemodes/HungerGames.js | 2 +- src/gamemodes/TeamX.js | 4 +-- src/gamemodes/TeamZ.js | 44 +++++++++++------------- src/gameserver.ini | 4 +-- src/modules/CommandList.js | 2 +- 12 files changed, 117 insertions(+), 189 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index cf35fc72c..51c26fd27 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -80,8 +80,8 @@ function GameServer() { foodMassLimit: 4, // Maximum mass for a food can grow virusMinAmount: 10, // Minimum amount of viruses on the map. virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. - virusStartMass: 100, // Starting virus size (In mass) - virusFeedAmount: 7, // Amount of times you need to feed a virus to shoot it + virusStartMass: 100, // Starting virus mass + virusMaxMass: 200, // Maximum virus mass ejectMass: 13.69, // Mass of ejected cells ejectMassCooldown: 3, // min ticks between ejects ejectMassLoss: 15, // Mass lost when ejecting cells @@ -93,7 +93,6 @@ function GameServer() { playerMinMassSplit: 36, // Mass required to split playerMaxCells: 16, // Max cells the player is allowed to have playerRecombineTime: 30, // Base amount of seconds before a cell is allowed to recombine - playerMassAbsorbed: 1.0, // Fraction of player cell's mass gained upon eating playerMassDecayRate: .002, // Amount of mass lost per second playerMinMassDecay: 10.24, // Minimum mass for decay to occur playerMaxNickLength: 15, // Maximum nick length @@ -845,7 +844,8 @@ GameServer.prototype.resolveCollision = function (manifold) { } // Consume effect - minCell.onConsume(maxCell, this); + maxCell.onEat(minCell); + minCell.onEaten(maxCell); // Remove cell minCell.setKiller(maxCell); @@ -858,7 +858,7 @@ GameServer.prototype.updateMoveEngine = function () { var client = this.clients[i].playerTracker; for (var j = 0; j < client.cells.length; j++) { var cell1 = client.cells[j]; - if (cell1 == null || cell1.isRemoved) + if (cell1.isRemoved) continue; cell1.moveUser(this.border); cell1.move(this.border); @@ -917,47 +917,48 @@ GameServer.prototype.updateMoveEngine = function () { //this.gameMode.onCellMove(cell1, this); - // Recycle unused moving nodes - for (var i = 0; i < this.movingNodes.length; i++) { - var check = this.movingNodes[i]; - while (check == null && i < this.movingNodes.length) { - // Remove moving cells that are undefined - this.movingNodes.splice(i, 1); - check = this.movingNodes[i]; - } - if (check.boostDistance <= 0) { - // Remove cell from list - var index = this.movingNodes.indexOf(cell1); - if (index != -1) { - this.movingNodes.splice(index, 1); - } - } - } - // Move moving cells - for (var i = 0; i < this.movingNodes.length; i++) { + for (var i = 0; i < this.movingNodes.length; ) { var cell1 = this.movingNodes[i]; - if (cell1 == null || cell1.isRemoved) continue; + if (cell1.isRemoved) + continue; cell1.move(this.border); this.updateNodeQuad(cell1); + if (!cell1.isMoving) + this.movingNodes.splice(i, 1); + else + i++; } // Scan for ejected cell collisions (scan for ejected or virus only) rigidCollisions = []; eatCollisions = []; + var self = this; for (var i = 0; i < this.movingNodes.length; i++) { var cell1 = this.movingNodes[i]; - if (cell1 == null || cell1.isRemoved || cell1.cellType != 3) continue; + if (cell1.isRemoved) continue; this.quadTree.find(cell1.quadItem.bound, function (item) { var cell2 = item.cell; - if (cell2 == cell1 || (cell2.cellType != 3 && cell2.cellType!=2)) + if (cell2 == cell1) return; var manifold = self.checkCellCollision(cell1, cell2); if (manifold == null) return; - if (cell1.cellType==3 && cell2.cellType==3) // ejected/ejected + if (cell1.cellType == 3 && cell2.cellType == 3) { + // ejected/ejected rigidCollisions.push({ cell1: cell1, cell2: cell2 }); - else + // add to moving nodes if needed + if (!cell1.isMoving) { + cell1.isMoving = true + self.movingNodes.push(cell1); + } + if (!cell2.isMoving) { + cell2.isMoving = true + self.movingNodes.push(cell2); + } + } + else { eatCollisions.push({ cell1: cell1, cell2: cell2 }); + } }); } @@ -986,11 +987,6 @@ GameServer.prototype.updateMoveEngine = function () { } }; -GameServer.prototype.setAsMovingNode = function(node) { - if (this.movingNodes.indexOf(node) == -1) - this.movingNodes.push(node); -}; - GameServer.prototype.splitCells = function(client) { // sort by size descending client.cells.sort(function (a, b) { @@ -1110,7 +1106,6 @@ GameServer.prototype.ejectMass = function(client) { ejected.setBoost(780, angle); this.addNode(ejected); - this.setAsMovingNode(ejected); } }; @@ -1125,7 +1120,6 @@ GameServer.prototype.shootVirus = function(parent) { // Add to moving cells list this.addNode(newVirus); - this.setAsMovingNode(newVirus); }; GameServer.prototype.getNearestVirus = function(cell) { diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 92845e2c2..acb8ed000 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -13,6 +13,7 @@ function Cell(nodeId, owner, position, mass, gameServer) { this.isSpiked = false; // If true, then this cell has spikes around it this.isAgitated = false;// If true, then this cell has waves on it's outline this.killedBy = null; // Cell that ate this cell + this.isMoving = false; // Indicate that cell is in boosted mode this.boostDistance = 0; this.boostDirection = { x: 1, y: 0, angle: 0 }; @@ -76,21 +77,12 @@ Cell.prototype.setMass = function (mass) { this.owner.massChanged(); }; -Cell.prototype.addMass = function (n) { - // Check if the cell needs to autosplit before adding mass - var newMass = this.getMass() + n; - this.setMass(newMass); - if (!this.owner || this.owner.mergeOverride) - return; - if (this.getMass() <= this.gameServer.config.playerMaxMass) - return; - if (this.owner.cells.length >= this.gameServer.config.playerMaxCells) { - this.setMass(this.gameServer.config.playerMaxMass); - return; - } - var splitMass = this.getMass() / 2; - var randomAngle = Math.random() * 6.28; // Get random angle - this.gameServer.splitPlayerCell(this.owner, this, randomAngle, splitMass); +Cell.prototype.setSize = function (size) { + this._size = size; + this._squareSize = size * size; + this._mass = this._squareSize / 100; + if (this.owner) + this.owner.massChanged(); }; Cell.prototype.getSpeed = function() { @@ -141,14 +133,20 @@ Cell.prototype.setBoost = function (distance, angle) { this.boostDistance = distance; this.setAngle(angle); + this.isMoving = true; + if (!this.owner) { + var index = this.gameServer.movingNodes.indexOf(this); + if (index < 0) + this.gameServer.movingNodes.push(this); + } }; Cell.prototype.move = function (border) { - if (this.boostDistance <= 0) { + if (this.isMoving && this.boostDistance <= 0) { this.boostDistance = 0; + this.isMoving = false; return; } - var speed = Math.sqrt(this.boostDistance * this.boostDistance / 100); var speed = Math.min(speed, 78); // limit max speed with sqrt(780*780/100) speed = Math.min(speed, this.boostDistance); // avoid overlap 0 @@ -232,6 +230,7 @@ Cell.prototype.clipVelocity = function (v, border) { v.y = ly; this.boostDistance += Math.sqrt(ldx * ldx + ldy * ldy); if (this.boostDistance < 1) this.boostDistance = 0; + this.isMoving = true; return v; }; @@ -261,8 +260,14 @@ Cell.prototype.canEat = function (cell) { return false; }; -Cell.prototype.onConsume = function(consumer, gameServer) { - // Called when the cell is consumed +Cell.prototype.onEat = function (prey) { + // Called to eat prey cell + var size1 = this.getSize(); + var size2 = prey.getSize() + 1; + this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); +}; + +Cell.prototype.onEaten = function (hunter) { }; Cell.prototype.onAdd = function(gameServer) { diff --git a/src/entity/EjectedMass.js b/src/entity/EjectedMass.js index 4fd2fdef3..e927088ac 100644 --- a/src/entity/EjectedMass.js +++ b/src/entity/EjectedMass.js @@ -14,12 +14,6 @@ EjectedMass.prototype.getName = function() { return ""; }; -EjectedMass.prototype.addMass = function(n) { - return; // Do nothing, this is an ejected cell -}; - -EjectedMass.prototype.calcMove = null; // Only for player controlled movement - // Main Functions EjectedMass.prototype.sendUpdate = function() { @@ -40,11 +34,3 @@ EjectedMass.prototype.onRemove = function(gameServer) { gameServer.nodesEjected.splice(index, 1); } }; - -EjectedMass.prototype.onConsume = function(consumer, gameServer) { - // Adds mass to consumer - if (consumer.cellType == 2 && consumer.feed != null) // virus - consumer.feed(this, gameServer); - else - consumer.addMass(this.getMass()); -}; diff --git a/src/entity/Food.js b/src/entity/Food.js index 1d7449353..71520b4e7 100644 --- a/src/entity/Food.js +++ b/src/entity/Food.js @@ -15,8 +15,6 @@ function Food() { module.exports = Food; Food.prototype = new Cell(); -Food.prototype.calcMove = null; // Food has no need to move - // Main Functions Food.prototype.grow = function () { @@ -43,7 +41,3 @@ Food.prototype.sendUpdate = function() { Food.prototype.onRemove = function(gameServer) { gameServer.currentFood--; }; - -Food.prototype.onConsume = function(consumer, gameServer) { - consumer.addMass(this.getMass()); -}; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 8cd5da181..9018a6e31 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -89,12 +89,22 @@ PlayerCell.prototype.moveUser = function (border) { // Override -PlayerCell.prototype.onConsume = function(consumer, gameServer) { - // Add an inefficiency for eating other players' cells - var factor = ( consumer.owner === this.owner ? 1 : gameServer.config.playerMassAbsorbed ); - // Anti-bot measure - factor = (consumer.getMass() >= 625 && this.getMass() <= 17 && gameServer.config.playerBotGrowEnabled == 1) ? 0 : factor; - consumer.addMass(factor * this.getMass()); +PlayerCell.prototype.onEat = function (prey) { + var size1 = this.getSize(); + var size2 = prey.getSize() + 1; + this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); + + if (this.owner.mergeOverride) + return; + if (this.getMass() <= this.gameServer.config.playerMaxMass) + return; + if (this.owner.cells.length >= this.gameServer.config.playerMaxCells) { + this.setMass(this.gameServer.config.playerMaxMass); + return; + } + var splitMass = this.getMass() / 2; + var randomAngle = Math.random() * 6.28; // Get random angle + this.gameServer.splitPlayerCell(this.owner, this, randomAngle, splitMass); }; PlayerCell.prototype.onAdd = function(gameServer) { diff --git a/src/entity/Virus.js b/src/entity/Virus.js index ad5167029..013252691 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -13,38 +13,31 @@ function Virus() { module.exports = Virus; Virus.prototype = new Cell(); -Virus.prototype.calcMove = null; // Only for player controlled movement - -Virus.prototype.feed = function(feeder, gameServer) { - if (this.boostDistance <= 0) - this.setAngle(feeder.getAngle()); // Set direction if the virus explodes - this.setMass(this.getMass() + feeder.getMass()); - this.fed++; // Increase feed count - - // Check if the virus is going to explode - if (this.fed >= gameServer.config.virusFeedAmount) { - this.setMass(gameServer.config.virusStartMass); // Reset mass - this.fed = 0; - gameServer.shootVirus(this); - } - -}; - // Main Functions Virus.prototype.canEat = function (cell) { return cell.cellType == 3; // virus can eat ejected mass only }; +Virus.prototype.onEat = function (prey) { + // Called to eat prey cell + var size1 = this.getSize(); + var size2 = prey.getSize() + 1; + this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); + if (prey.isMoving) + this.setAngle(prey.getAngle()); + if (this.getMass() >= this.gameServer.config.virusMaxMass) { + this.setMass(this.gameServer.config.virusStartMass); // Reset mass + this.gameServer.shootVirus(this); + } +}; -Virus.prototype.onConsume = function(consumer, gameServer) { +Virus.prototype.onEaten = function(consumer) { var client = consumer.owner; - - // Cell consumes mass before any calculation - consumer.addMass(this.getMass()); + if (client == null) return; var maxSplits = Math.floor(consumer.getMass() / 16) - 1; // Maximum amount of splits - var numSplits = gameServer.config.playerMaxCells - client.cells.length; // Get number of splits + var numSplits = this.gameServer.config.playerMaxCells - client.cells.length; // Get number of splits numSplits = Math.min(numSplits, maxSplits); var splitMass = Math.min(consumer.getMass() / (numSplits + 1), 24); // Maximum size of new splits @@ -81,13 +74,13 @@ Virus.prototype.onConsume = function(consumer, gameServer) { for (var k = 0; k < bigSplits.length; k++) { angle = Math.random() * 6.28; // Random directions - gameServer.splitPlayerCell(client, consumer, angle, bigSplits[k]); + this.gameServer.splitPlayerCell(client, consumer, angle, bigSplits[k]); } // Splitting for (var k = 0; k < numSplits; k++) { angle = Math.random() * 6.28; // Random directions - gameServer.splitPlayerCell(client, consumer, angle, splitMass); + this.gameServer.splitPlayerCell(client, consumer, angle, splitMass); } }; diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 12ffe859a..637f23166 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -2,7 +2,6 @@ var FFA = require('./FFA'); // Base gamemode var Cell = require('../entity/Cell'); var Food = require('../entity/Food'); var Virus = require('../entity/Virus'); -var VirusFeed = require('../entity/Virus').prototype.feed; function Experimental() { FFA.apply(this, Array.prototype.slice.call(arguments)); @@ -17,7 +16,8 @@ function Experimental() { this.tickMotherS = 0; // Config - this.motherCellMass = 222; + var motherSize = 149; // mass=222.01, size=149 (vanilla) + this.motherCellMass = motherSize * motherSize / 100; this.motherUpdateInterval = 5; // How many ticks it takes to update the mother cell (1 tick = 50 ms) this.motherSpawnInterval = 100; // How many ticks it takes to spawn another mother cell - Currently 5 seconds this.motherMinAmount = 5; @@ -41,40 +41,8 @@ Experimental.prototype.spawnMotherCell = function(gameServer) { // Checks if there are enough mother cells on the map if (this.nodesMother.length < this.motherMinAmount) { // Spawns a mother cell - var pos = gameServer.getRandomPosition(); - - // Check for players - for (var i = 0; i < gameServer.nodesPlayer.length; i++) { - var check = gameServer.nodesPlayer[i]; - - var r = check.getSize(); // Radius of checking player cell - - // Collision box - var topY = check.position.y - r; - var bottomY = check.position.y + r; - var leftX = check.position.x - r; - var rightX = check.position.x + r; - - // Check for collisions - if (pos.y > bottomY) { - continue; - } - - if (pos.y < topY) { - continue; - } - - if (pos.x > rightX) { - continue; - } - - if (pos.x < leftX) { - continue; - } - - // Collided - return; - } + var pos = gameServer.getRandomSpawn(this.motherCellMass); + if (pos == null) return; // Spawn if no cells are colliding var m = new MotherCell(gameServer.getNextNodeId(), null, pos, this.motherCellMass, gameServer); @@ -94,19 +62,10 @@ Experimental.prototype.onServerInit = function(gameServer) { this.motherMinAmount = Math.ceil(mapSize / 2000); // Special virus mechanics - Virus.prototype.feed = function(feeder, gameServer) { + Virus.prototype.onEat = function(prey) { // Pushes the virus - // TODO: check distance - this.setBoost(16 * 20, feeder.getAngle()); - //this.setAngle(feeder.getAngle()); // Set direction if the virus explodes - //this.moveEngineTicks += 20; // Amount of times to loop the movement function - //this.moveEngineSpeed += 16; - //this.moveDecay = 0.875; - - var index = gameServer.movingNodes.indexOf(this); - if (index == -1) { - gameServer.movingNodes.push(this); - } + var angle = prey.isMoving ? prey.getAngle() : this.getAngle(); + this.setBoost(16 * 20, angle); }; // Override this @@ -132,7 +91,6 @@ Experimental.prototype.onChange = function(gameServer) { gameServer.removeNode(this.nodesMother[i]); } // Add back default functions - Virus.prototype.feed = VirusFeed; gameServer.getRandomSpawn = require('../GameServer').prototype.getRandomSpawn; }; @@ -200,11 +158,9 @@ MotherCell.prototype.spawnFood = function(gameServer) { var dist = (Math.random() * 25) + 25; // Random distance // TODO: check distance f.setBoost(dist, angle); - - gameServer.setAsMovingNode(f); }; -MotherCell.prototype.onConsume = Virus.prototype.onConsume; // Copies the virus prototype function +MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function MotherCell.prototype.onAdd = function(gameServer) { gameServer.gameMode.nodesMother.push(this); // Temporary diff --git a/src/gamemodes/HungerGames.js b/src/gamemodes/HungerGames.js index 2de67d099..ab77ecaf7 100644 --- a/src/gamemodes/HungerGames.js +++ b/src/gamemodes/HungerGames.js @@ -87,7 +87,7 @@ HungerGames.prototype.spawnFood = function(gameServer, mass, pos) { }; HungerGames.prototype.spawnVirus = function(gameServer, pos) { - var v = new Entity.Virus(gameServer.getNextNodeId(), null, pos, gameServer.config.virusStartMass); + var v = new Entity.Virus(gameServer.getNextNodeId(), null, pos, gameServer.config.virusStartMass, gameServer); gameServer.addNode(v); }; diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 33ce107c7..101a0cbd8 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -420,11 +420,9 @@ MotherCell.prototype.spawnFood = function(gameServer) { var dist = (Math.random() * 10) + 22; // Random distance // TODO: check distance f.setBoost(dist*15, angle); - - gameServer.setAsMovingNode(f); }; -MotherCell.prototype.onConsume = Virus.prototype.onConsume; // Copies the virus prototype function +MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function MotherCell.prototype.onAdd = function(gameServer) { gameServer.gameMode.nodesMother.push(this); // Temporary diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 0d87ded71..1c56ad1bb 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -10,7 +10,7 @@ var GS_getNearestVirus = null; var GS_getCellsInRange = null; var GS_splitCells = null; var GS_newCellVirused = null; -var Virus_onConsume = Virus.prototype.onConsume; +var Virus_onEaten = Virus.prototype.onEaten; var GameState = { WF_PLAYERS: 0, @@ -549,9 +549,6 @@ TeamZ.prototype.onServerInit = function(gameServer) { //split.recombineTicks = 2; // main-ticks, 1 main-tick = 1 s //TODO: fix? } - - // Add to moving cells list - this.setAsMovingNode(split); this.addNode(split); } }; @@ -585,20 +582,15 @@ TeamZ.prototype.onServerInit = function(gameServer) { // Add to moving cells list this.addNode(newCell); - this.setAsMovingNode(newCell); }; - Virus.prototype.onConsume = function(consumer, gameServer) { + Virus.prototype.onEaten = function(consumer) { var client = consumer.owner; - var maxSplits = Math.floor(consumer.getMass() / 16) - 1; // Maximum amount of splits - var numSplits = gameServer.config.playerMaxCells - client.cells.length; // Get number of splits + var numSplits = this.gameServer.config.playerMaxCells - client.cells.length; // Get number of splits numSplits = Math.min(numSplits, maxSplits); var splitMass = Math.min(consumer.getMass() / (numSplits + 1), 36); // Maximum size of new splits - // Cell consumes mass before splitting - consumer.addMass(this.getMass()); - // Cell cannot split any further if (numSplits <= 0) { return; @@ -624,26 +616,26 @@ TeamZ.prototype.onServerInit = function(gameServer) { var angle = 0; // Starting angle for (var k = 0; k < numSplits; k++) { angle += 6 / numSplits; // Get directions of splitting cells - gameServer.newCellVirused(client, consumer, angle, splitMass, 150); + this.gameServer.newCellVirused(client, consumer, angle, splitMass, 150); consumer.setMass(consumer.getMass() - splitMass); } for (var k = 0; k < bigSplits; k++) { angle = Math.random() * 6.28; // Random directions splitMass = consumer.getMass() / 4; - gameServer.newCellVirused(client, consumer, angle, splitMass, 20); + this.gameServer.newCellVirused(client, consumer, angle, splitMass, 20); consumer.setMass(consumer.getMass() - splitMass); } - if (gameServer.gameMode.hasEatenHero(client)) { + if (this.gameServer.gameMode.hasEatenHero(client)) { //consumer.recombineTicks = 0; // TODO: fix? } }; // Handle "gamemode" command: - for (var i = 0; i < gameServer.clients.length; i++) { - var client = gameServer.clients[i].playerTracker; + for (var i = 0; i < this.gameServer.clients.length; i++) { + var client = this.gameServer.clients[i].playerTracker; if (!client) continue; @@ -694,7 +686,7 @@ TeamZ.prototype.onChange = function(gameServer) { GameServer.prototype.getCellsInRange = GS_getCellsInRange; GameServer.prototype.splitCells = GS_splitCells; GameServer.prototype.newCellVirused = GS_newCellVirused; - Virus.prototype.onConsume = Virus_onConsume; + Virus.prototype.onEaten = Virus_onEaten; }; TeamZ.prototype.onTick = function(gameServer) { @@ -1079,17 +1071,18 @@ Hero.prototype.feed = function(feeder, gameServer) { } }; -Hero.prototype.onConsume = function(consumer, gameServer) { +Hero.prototype.onEaten = function(consumer) { // Called when the cell is consumed var client = consumer.owner; - consumer.addMass(this.getMass()); // delicious + + // delicious - if (gameServer.gameMode.isCrazy(client)) { + if (this.gameServer.gameMode.isCrazy(client)) { // Neutralize the Zombie effect client.cured = true; } else { // Become a hero - client.eatenHeroTimer = gameServer.gameMode.heroEffectDuration; + client.eatenHeroTimer = this.gameServer.gameMode.heroEffectDuration; client.heroColorFactor = 0; // Merge immediately @@ -1149,13 +1142,14 @@ Brain.prototype.feed = function(feeder, gameServer) { } }; -Brain.prototype.onConsume = function(consumer, gameServer) { +Brain.prototype.onEaten = function(consumer) { // Called when the cell is consumed var client = consumer.owner; - consumer.addMass(this.getMass()); // yummy! + + // yummy! - client.eatenBrainTimer = gameServer.gameMode.brainEffectDuration; + client.eatenBrainTimer = this.gameServer.gameMode.brainEffectDuration; // Boost speed - gameServer.gameMode.boostSpeed(client); + this.gameServer.gameMode.boostSpeed(client); }; diff --git a/src/gameserver.ini b/src/gameserver.ini index 38a15c0be..5000c6a6c 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -50,7 +50,7 @@ foodMassLimit = 4 virusMinAmount = 10 virusMaxAmount = 50 virusStartMass = 100 -virusFeedAmount = 7 +virusMaxMass = 200 // [Ejected Mass] // ejectMass: Mass of ejected cells (vanilla mass 13.69, size 37) @@ -66,7 +66,6 @@ ejectSpawnPlayer = 50 // playerStartMass: start mass (vanilla 10.24, size 32 - minimum player size) // playerRecombineTime: Base amount of ticks before a cell is allowed to recombine (1 tick = 1000 milliseconds) // playerBotGrowEnabled: If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass -// playerMassAbsorbed: Fraction of a player cell that gets absorbed upon eating (i.e., 1 = 100%, 0.8 = 80%, etc) // playerMassDecayRate: Amount of mass lost per tick (Multiplier) (1 tick = 1000 milliseconds) // playerMinMassDecay: Minimum mass for decay to occur // playerDisconnectTime: The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) @@ -78,7 +77,6 @@ playerMinMassEject = 32 playerMinMassSplit = 36 playerMaxCells = 16 playerRecombineTime = 30 -playerMassAbsorbed = 1.0 playerMassDecayRate = .002 playerMinMassDecay = 10.24 playerMaxNickLength = 15 diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 327f42059..eb7f36b22 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -573,7 +573,7 @@ Commands.list = { } // Spawn - var v = new Entity.Virus(gameServer.getNextNodeId(), null, pos, mass); + var v = new Entity.Virus(gameServer.getNextNodeId(), null, pos, mass, gameServer); gameServer.addNode(v); console.log("[Console] Spawned 1 virus at (" + pos.x + " , " + pos.y + ")"); }, From 395f202993c68a840d9b32ad43c9760c8b4dc64a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 17:18:33 +0200 Subject: [PATCH 178/417] fix virus size bug --- src/GameServer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/GameServer.js b/src/GameServer.js index 51c26fd27..a03591afc 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -847,6 +847,9 @@ GameServer.prototype.resolveCollision = function (manifold) { maxCell.onEat(minCell); minCell.onEaten(maxCell); + // update bounds + this.updateNodeQuad(maxCell); + // Remove cell minCell.setKiller(maxCell); this.removeNode(minCell); From 115a44d9513eab617d97bf8673b661347e2b746a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 17:50:00 +0200 Subject: [PATCH 179/417] fix motherCell size bug --- src/gamemodes/Experimental.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 637f23166..e97dfe54a 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -129,6 +129,8 @@ MotherCell.prototype.update = function(gameServer) { for (var i = 0; i < maxAmount; i++) { this.spawnFood(gameServer); this.setMass(this.getMass() - cellSize); + // update bounds + this.gameServer.updateNodeQuad(this); } } }; From aa2f357858a993d3db23ece3661df7cbcf948c79 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 19:00:06 +0200 Subject: [PATCH 180/417] disable mouse move for disconnected player --- src/GameServer.js | 1 + src/entity/PlayerCell.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index a03591afc..6e55b7f4d 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -241,6 +241,7 @@ GameServer.prototype.onClientSocketClose = function (ws, code) { var color = this.getGrayColor(ws.playerTracker.getColor()); ws.playerTracker.setColor(color); + ws.playerTracker.setSkin(""); // disconnected effect ws.playerTracker.cells.forEach(function (cell) { cell.setColor(color); diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 9018a6e31..57bcf2f53 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -56,7 +56,7 @@ PlayerCell.prototype.canEat = function (cell) { // Movement PlayerCell.prototype.moveUser = function (border) { - if (this.owner == null) { + if (this.owner == null || !this.owner.socket.isConnected) { return; } var x = this.owner.mouse.x; From fb04539a7d1ccea8f4b85e0fc712b12f76169d27 Mon Sep 17 00:00:00 2001 From: ItzLevvie Date: Mon, 20 Jun 2016 18:07:16 +0100 Subject: [PATCH 181/417] Now there's no need to append ".png" to the name --- src/PacketHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 5a25202ed..e751c1c60 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -146,7 +146,7 @@ PacketHandler.prototype.setNickname = function (text) { skin = "%" + text.slice(1, n); name = text.slice(n + 1); //} else if (text[0] == "|" && (n = text.indexOf("|", 1)) >= 0) { - // skin = ":http://i.imgur.com/" + text.slice(1, n); + // skin = ":http://i.imgur.com/" + text.slice(1, n) + ".png"; // name = text.slice(n + 1); } else { name = text; From b47d0c3d778c649a526394cb9e201a8850a7c1e0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 19:17:32 +0200 Subject: [PATCH 182/417] fix bot movement --- src/entity/PlayerCell.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 57bcf2f53..14578f404 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -56,7 +56,7 @@ PlayerCell.prototype.canEat = function (cell) { // Movement PlayerCell.prototype.moveUser = function (border) { - if (this.owner == null || !this.owner.socket.isConnected) { + if (this.owner == null || this.owner.socket.isConnected === false) { return; } var x = this.owner.mouse.x; From e87f43839dadb3fa5b9e821a7714b2514b144dbe Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 20:09:01 +0200 Subject: [PATCH 183/417] performance improvement & reduce network traffic --- src/PlayerTracker.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 2659304df..2b1b245fa 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -249,7 +249,10 @@ PlayerTracker.prototype.update = function () { oldIndex++; continue; } - updNodes.push(newVisible[newIndex]); + var node = newVisible[newIndex]; + // skip food & eject if no moving + if (node.isMoving || (node.cellType != 1 && node.cellType != 3)) + updNodes.push(node); newIndex++; oldIndex++; } From 391c510c337de8309fcfb2edc6ba1eb9fac9b8af Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 20:16:02 +0200 Subject: [PATCH 184/417] update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 922113883..679c2d12c 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,12 @@ URL | Protocol | Description --- | --- | --- http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, pretty smooth, custom graphics (anime) http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends invalid protocol=1) +http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style ## What's new: +* Massive perfromance improvement & reduce network traffic * Split behavior - fixed; * Protocol code optimized; * Massive performance improvement with quad-tree lookup; From f0de8e6b45d1b5a7e985f210718b4db606227677 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 22:34:06 +0200 Subject: [PATCH 185/417] add anti-minion protection --- src/PlayerTracker.js | 54 ++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 2b1b245fa..d21916271 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -16,6 +16,7 @@ function PlayerTracker(gameServer, socket) { this.score = 0; // Needed for leaderboard this.scale = 1; this.isMassChanged = true; + this.borderCounter = 0; this.mouse = { x: 0, @@ -58,14 +59,7 @@ function PlayerTracker(gameServer, socket) { // Gamemode function gameServer.gameMode.onPlayerInit(this); // Only scramble if enabled in config - if (gameServer.config.serverScrambleCoords == 1) { - // avoid mouse packet limitations - var maxScrambleX = Math.max(0, 32767 - 1000 - gameServer.border.width / 2); - var maxScrambleY = Math.max(0, 32767 - 1000 - gameServer.border.height / 2); - this.scrambleX = Math.floor(maxScrambleX * Math.random()); - this.scrambleY = Math.floor(maxScrambleY * Math.random()); - } - this.scrambleId = (Math.random() * 0xFFFFFFFF) >>> 0; + this.scramble(); } } @@ -73,6 +67,23 @@ module.exports = PlayerTracker; // Setters/Getters +PlayerTracker.prototype.scramble = function () { + this.scrambleId = (Math.random() * 0xFFFFFFFF) >>> 0; + + if (!this.gameServer.config.serverScrambleCoords) + return; + // avoid mouse packet limitations + var maxx = Math.max(0, 32767 - 1000 - this.gameServer.border.width); + var maxy = Math.max(0, 32767 - 1000 - this.gameServer.border.height); + var x = maxx * Math.random(); + var y = maxy * Math.random(); + if (Math.random() >= 0.5) x = -x; + if (Math.random() >= 0.5) y = -y; + this.scrambleX = x; + this.scrambleY = y; + this.borderCounter = 0; +}; + PlayerTracker.prototype.getFriendlyName = function () { var name = this.getName(); if (!name) name = ""; @@ -159,13 +170,13 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.freeRoam = false; // some old clients don't understand ClearAll message - // so we will not perform cleanup for old protocol - // to allow them to receive remove notifications - if (this.socket.packetHandler.protocol >= 6) { - this.socket.sendPacket(new Packet.ClearAll()); - this.visibleNodes = []; + // so we will send update for them + if (this.socket.packetHandler.protocol < 6) { + this.socket.sendPacket(new Packet.UpdateNodes(this, [], [], [], this.visibleNodes)); } - + this.socket.sendPacket(new Packet.ClearAll()); + this.visibleNodes = []; + this.scramble(); this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); }; @@ -270,6 +281,21 @@ PlayerTracker.prototype.update = function () { oldIndex++; } this.visibleNodes = newVisible; + + if (this.gameServer.config.serverScrambleCoords) { + if (this.borderCounter == 0) { + var bound = { + minx: Math.max(this.gameServer.border.minx, this.viewBox.left - this.viewBox.halfWidth), + miny: Math.max(this.gameServer.border.miny, this.viewBox.top - this.viewBox.halfHeight), + maxx: Math.min(this.gameServer.border.maxx, this.viewBox.right + this.viewBox.halfWidth), + maxy: Math.min(this.gameServer.border.maxy, this.viewBox.bottom + this.viewBox.halfHeight) + }; + this.socket.sendPacket(new Packet.SetBorder(this, bound)); + } + this.borderCounter++; + if (this.borderCounter >= 10) + this.borderCounter = 0; + } // Send packet this.socket.sendPacket(new Packet.UpdateNodes( From f277acd79cc006c28d36755bac9e819d7806714d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 22:45:30 +0200 Subject: [PATCH 186/417] anti-minion protection (decrease border update interval) --- src/PlayerTracker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index d21916271..cd8634fc3 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -293,7 +293,7 @@ PlayerTracker.prototype.update = function () { this.socket.sendPacket(new Packet.SetBorder(this, bound)); } this.borderCounter++; - if (this.borderCounter >= 10) + if (this.borderCounter >= 20) this.borderCounter = 0; } From 230fe05a9489e5780cb3c0a9dbbb2f29559b936e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 23:01:11 +0200 Subject: [PATCH 187/417] config check for min player size --- src/GameServer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index 6e55b7f4d..b6c9d1842 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -111,7 +111,7 @@ function GameServer() { // Parse config this.loadConfig(); this.loadIpBanList(); - + // Gamemodes this.gameMode = Gamemode.get(this.config.serverGamemode); } @@ -1175,6 +1175,9 @@ GameServer.prototype.loadConfig = function() { // Create a new config fs.writeFileSync('./gameserver.ini', ini.stringify(this.config)); } + // check config (min player size = 32 => mass = 10.24) + this.config.playerStartMass = Math.max(10.24, this.config.playerStartMass); + this.config.playerMinMassDecay = Math.max(10.24, this.config.playerMinMassDecay); }; GameServer.prototype.loadIpBanList = function () { From c0fbe102011b335d597206f6fee51ff791d3e2b0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 20 Jun 2016 23:51:04 +0200 Subject: [PATCH 188/417] disable scrambling by default (for old clients) --- src/GameServer.js | 2 +- src/gameserver.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index b6c9d1842..f3546c344 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -65,7 +65,7 @@ function GameServer() { serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Amount of seconds per update for the server stats serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections - serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. + serverScrambleCoords: 0, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. borderWidth: 14142, // Map border size (Vanilla value: 14142) diff --git a/src/gameserver.ini b/src/gameserver.ini index 5000c6a6c..446079de0 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -26,7 +26,7 @@ serverViewBaseY = 1080 serverStatsPort = 88 serverStatsUpdate = 60 serverLogLevel = 1 -serverScrambleCoords = 1 +serverScrambleCoords = 0 serverMaxLB = 10 serverChat = 1 From 5decf99875e896f17fb0ae5feca468af37c5cd25 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 00:17:31 +0200 Subject: [PATCH 189/417] fix pause command --- src/GameServer.js | 14 +++++++++----- src/gamemodes/Mode.js | 2 ++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index f3546c344..542d66aff 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -560,10 +560,12 @@ GameServer.prototype.mainLoop = function() { var tStart = new Date().getTime(); // Loop main functions - this.updateMoveEngine(); - this.updateSpawn(); - this.gameMode.onTick(this); - this.updateCells(); + if (this.run) { + this.updateMoveEngine(); + this.updateSpawn(); + this.gameMode.onTick(this); + this.updateCells(); + } this.updateClients(); this.updateLeaderboard(); @@ -596,7 +598,9 @@ GameServer.prototype.mainLoop = function() { // return tscTicks[0] * 1000 + tscTicks[1] / 1000000; //} - this.tickCounter++; + if (this.run) { + this.tickCounter++; + } var tEnd = new Date().getTime(); this.updateTime = tEnd - tStart; }; diff --git a/src/gamemodes/Mode.js b/src/gamemodes/Mode.js index b7555e55b..eab928b44 100644 --- a/src/gamemodes/Mode.js +++ b/src/gamemodes/Mode.js @@ -43,11 +43,13 @@ Mode.prototype.pressQ = function(gameServer, player) { }; Mode.prototype.pressW = function(gameServer, player) { + if (!gameServer.run) return; // Called when the W key is pressed gameServer.ejectMass(player); }; Mode.prototype.pressSpace = function(gameServer, player) { + if (!gameServer.run) return; // Called when the Space bar is pressed gameServer.splitCells(player); }; From bd04b6f358eb5a2810fa7b7b0d81910ca5c95181 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 02:13:07 +0200 Subject: [PATCH 190/417] perfromance optimization --- src/PlayerTracker.js | 61 +++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index cd8634fc3..12413acaf 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -23,24 +23,22 @@ function PlayerTracker(gameServer, socket) { y: 0 }; this.tickLeaderboard = 0; - this.tickViewBox = 0; this.team = 0; this.spectate = false; this.freeRoam = false; // Free-roam mode enables player to move in spectate mode - // Viewing box - this.sightWidth = 0; - this.sightHeight = 0; - this.centerPos = { // Center of map + this.centerPos = { x: 0, y: 0 }; this.viewBox = { - left: 0, - top: 0, - right: 0, - bottom: 0, + minx: 0, + miny: 0, + maxx: 0, + maxy: 0, + width: 0, + height: 0, halfWidth: 0, halfHeight: 0 }; @@ -285,10 +283,10 @@ PlayerTracker.prototype.update = function () { if (this.gameServer.config.serverScrambleCoords) { if (this.borderCounter == 0) { var bound = { - minx: Math.max(this.gameServer.border.minx, this.viewBox.left - this.viewBox.halfWidth), - miny: Math.max(this.gameServer.border.miny, this.viewBox.top - this.viewBox.halfHeight), - maxx: Math.min(this.gameServer.border.maxx, this.viewBox.right + this.viewBox.halfWidth), - maxy: Math.min(this.gameServer.border.maxy, this.viewBox.bottom + this.viewBox.halfHeight) + minx: Math.max(this.gameServer.border.minx, this.viewBox.minx - this.viewBox.halfWidth), + miny: Math.max(this.gameServer.border.miny, this.viewBox.miny - this.viewBox.halfHeight), + maxx: Math.min(this.gameServer.border.maxx, this.viewBox.maxx + this.viewBox.halfWidth), + maxy: Math.min(this.gameServer.border.maxy, this.viewBox.maxy + this.viewBox.halfHeight) }; this.socket.sendPacket(new Packet.SetBorder(this, bound)); } @@ -365,15 +363,17 @@ PlayerTracker.prototype.updateCenterFreeRoam = function () { PlayerTracker.prototype.updateViewBox = function () { var scale = this.getScale(); - this.sightWidth = (this.gameServer.config.serverViewBaseX + 100) / scale; - this.sightHeight = (this.gameServer.config.serverViewBaseY + 100) / scale; - var halfWidth = this.sightWidth / 2; - var halfHeight = this.sightHeight / 2; + var width = (this.gameServer.config.serverViewBaseX + 100) / scale; + var height = (this.gameServer.config.serverViewBaseY + 100) / scale; + var halfWidth = width / 2; + var halfHeight = height / 2; this.viewBox = { - left: this.centerPos.x - halfWidth, - top: this.centerPos.y - halfHeight, - right: this.centerPos.x + halfWidth, - bottom: this.centerPos.y + halfHeight, + minx: this.centerPos.x - halfWidth, + miny: this.centerPos.y - halfHeight, + maxx: this.centerPos.x + halfWidth, + maxy: this.centerPos.y + halfHeight, + width: width, + height: height, halfWidth: halfWidth, halfHeight: halfHeight }; @@ -405,21 +405,12 @@ PlayerTracker.prototype.getVisibleNodes = function () { PlayerTracker.prototype.calcVisibleNodes = function() { var newVisible = []; - var bound = { - minx: this.viewBox.left, - miny: this.viewBox.top, - maxx: this.viewBox.right, - maxy: this.viewBox.bottom - }; - this.gameServer.quadTree.find(bound, function (quadItem) { - newVisible.push(quadItem.cell); + var self = this; + this.gameServer.quadTree.find(this.viewBox, function (quadItem) { + if (quadItem.cell.owner != self) + newVisible.push(quadItem.cell); }); - // make sure that all owned is included - for (var i = 0; i < this.cells.length; i++) { - if (newVisible.indexOf(this.cells[i]) < 0) - newVisible.push(this.cells[i]); - } - return newVisible; + return newVisible.concat(this.cells); }; PlayerTracker.prototype.setCenterPos = function(x, y) { From ef7b22b209da06fb414ca0f02ad09f9114b02897 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 12:18:27 +0200 Subject: [PATCH 191/417] configurable spectator scale (to reduce lags from spectators) --- src/GameServer.js | 1 + src/PlayerTracker.js | 2 +- src/gameserver.ini | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index 542d66aff..dd5c8f1c0 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -62,6 +62,7 @@ function GameServer() { serverBots: 0, // Amount of player bots to spawn serverViewBaseX: 1920, // Base client screen resolution. Used to calculate view area. Warning: high values may cause lag serverViewBaseY: 1080, + serverSpectatorScale: 0.4, // Scale used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Amount of seconds per update for the server stats serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 12413acaf..89433c85a 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -392,7 +392,7 @@ PlayerTracker.prototype.getVisibleNodes = function () { } // free roam spectate this.updateCenterFreeRoam(); - this.scale = 0.25; + this.scale = this.gameServer.config.serverSpectatorScale;//0.25; this.sendPosPacket(); } else { // in game diff --git a/src/gameserver.ini b/src/gameserver.ini index 446079de0..3ad7b69be 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -8,6 +8,7 @@ // serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games // serverBots: Amount of player bots to spawn (Experimental) // serverViewBase: Base view distance of players. Warning: high values may cause lag +// serverSpectatorScale: Scale used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) // serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. // serverStatsUpdate: Amount of seconds per update for server stats // serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections @@ -23,6 +24,7 @@ serverGamemode = 0 serverBots = 0 serverViewBaseX = 1920 serverViewBaseY = 1080 +serverSpectatorScale = 0.4 serverStatsPort = 88 serverStatsUpdate = 60 serverLogLevel = 1 From 33031ff42da62cd3d76805e74132130ed6815910 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 12:25:22 +0200 Subject: [PATCH 192/417] comment --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index dd5c8f1c0..259ddc8ee 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -62,7 +62,7 @@ function GameServer() { serverBots: 0, // Amount of player bots to spawn serverViewBaseX: 1920, // Base client screen resolution. Used to calculate view area. Warning: high values may cause lag serverViewBaseY: 1080, - serverSpectatorScale: 0.4, // Scale used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) + serverSpectatorScale: 0.4, // Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Amount of seconds per update for the server stats serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections From 821b247e7a860cb407bf1a5d8d634bf367291672 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 12:27:55 +0200 Subject: [PATCH 193/417] comment --- src/GameServer.js | 2 +- src/gameserver.ini | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 259ddc8ee..ec1c04e03 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -66,7 +66,7 @@ function GameServer() { serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Amount of seconds per update for the server stats serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections - serverScrambleCoords: 0, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. + serverScrambleCoords: 0, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Some clients doesn't support it. serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. borderWidth: 14142, // Map border size (Vanilla value: 14142) diff --git a/src/gameserver.ini b/src/gameserver.ini index 3ad7b69be..cfcfbe021 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -8,11 +8,11 @@ // serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games // serverBots: Amount of player bots to spawn (Experimental) // serverViewBase: Base view distance of players. Warning: high values may cause lag -// serverSpectatorScale: Scale used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) +// serverSpectatorScale: Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) // serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. // serverStatsUpdate: Amount of seconds per update for server stats // serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections -// serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Default is 1. +// serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Some clients doesn't support it. // serverMaxLB: Controls the maximum players displayed on the leaderboard. // serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. serverTimeout = 30 From 1054e157ec5cbccfee5dbe8df75eb649d86c29e3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 13:20:37 +0200 Subject: [PATCH 194/417] performance optimization --- src/GameServer.js | 26 +++++++++++--------------- src/entity/PlayerCell.js | 19 +++++++------------ 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index ec1c04e03..30e97c1b2 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -565,7 +565,10 @@ GameServer.prototype.mainLoop = function() { this.updateMoveEngine(); this.updateSpawn(); this.gameMode.onTick(this); - this.updateCells(); + if ((this.getTick() % (1000 / 40)) == 0) { + // once per second + this.updateMassDecay(); + } } this.updateClients(); this.updateLeaderboard(); @@ -587,7 +590,7 @@ GameServer.prototype.mainLoop = function() { //this.gameMode.onTick(this); //this.t3 = toTime(process.hrtime(t)); //t = process.hrtime(); - //this.updateCells(); + //this.updateMassDecay(); //this.t4 = toTime(process.hrtime(t)); //t = process.hrtime(); //this.updateClients(); @@ -869,6 +872,7 @@ GameServer.prototype.updateMoveEngine = function () { var cell1 = client.cells[j]; if (cell1.isRemoved) continue; + cell1.updateRemerge(this); cell1.moveUser(this.border); cell1.move(this.border); this.updateNodeQuad(cell1); @@ -1141,25 +1145,17 @@ GameServer.prototype.getNearestVirus = function(cell) { } }; -GameServer.prototype.updateCells = function() { - if (!this.run) { - // Server is paused - return; - } - +GameServer.prototype.updateMassDecay = function() { // Loop through all player cells - var massDecay = 1 - (this.config.playerMassDecayRate * this.gameMode.decayMod * 0.05); + var massDecay = 1 - (this.config.playerMassDecayRate * this.gameMode.decayMod); for (var i = 0; i < this.nodesPlayer.length; i++) { var cell = this.nodesPlayer[i]; if (!cell) continue; - // Recombining - cell.updateRemerge(this); - // Mass decay - // TODO: needs to be updated rarely - if (cell.getMass() >= this.config.playerMinMassDecay) { - cell.setMass(cell.getMass() * massDecay); + if (cell.getMass() > this.config.playerMinMassDecay) { + var mass = Math.max(cell.getMass() * massDecay, this.config.playerMinMassDecay); + cell.setMass(mass); } } }; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 14578f404..591774d1a 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -20,27 +20,22 @@ PlayerCell.prototype.simpleCollide = function(check, d) { (this.abs(this.position.y - check.y) < len); }; -PlayerCell.prototype.updateRemerge = function (gameServer) { - if (this.owner == null) { +PlayerCell.prototype.updateRemerge = function () { + var age = this.getAge(this.gameServer.getTick()); + if (age < 15) { + // do not remerge if cell age is smaller than 15 ticks this._canRemerge = false; return; } - var tick = gameServer.getTick(); - var age = this.getAge(tick); - if (age < 3) { - // do not remerge if cell age is smaller than 3 ticks - this._canRemerge = false; - return; - } - var baseTtr = gameServer.config.playerRecombineTime; // default baseTtr = 30 + var baseTtr = this.gameServer.config.playerRecombineTime; // default baseTtr = 30 if (baseTtr == 0) { // instant merge this._canRemerge = true; return; } var ttr = Math.max(baseTtr, (this.getSize() * 0.2) >> 0); // ttr in seconds - // seconds to ticks (tickStep = 0.040 sec) - ttr /= 0.040; + // seconds to ticks (tickStep = 0.040 sec => 1 / 0.040 = 25) + ttr *= 25; this._canRemerge = age >= ttr; } From 7814db8333e5b99afdded76b9df9b1173f22b835 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 13:36:13 +0200 Subject: [PATCH 195/417] comments --- src/GameServer.js | 2 +- src/gameserver.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 30e97c1b2..b834ce95b 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -61,7 +61,7 @@ function GameServer() { serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams serverBots: 0, // Amount of player bots to spawn serverViewBaseX: 1920, // Base client screen resolution. Used to calculate view area. Warning: high values may cause lag - serverViewBaseY: 1080, + serverViewBaseY: 1080, // min value is 1920x1080 serverSpectatorScale: 0.4, // Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Amount of seconds per update for the server stats diff --git a/src/gameserver.ini b/src/gameserver.ini index cfcfbe021..8d6f75ab6 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -7,7 +7,7 @@ // serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) // serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games // serverBots: Amount of player bots to spawn (Experimental) -// serverViewBase: Base view distance of players. Warning: high values may cause lag +// serverViewBase: Base view distance of players. Warning: high values may cause lag! Min value is 1920x1080 // serverSpectatorScale: Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) // serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. // serverStatsUpdate: Amount of seconds per update for server stats From 8835b53844fb702cded933b7995cf24feadc2c44 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 13:48:10 +0200 Subject: [PATCH 196/417] fix eject distance --- src/GameServer.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index b834ce95b..394793736 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1045,14 +1045,15 @@ GameServer.prototype.splitPlayerCell = function(client, parent, angle, mass) { return false; } + // Remove mass from parent cell first + parent.setMass(parent.getMass() - mass); + // make a small shift to the cell position to prevent extrusion in wrong direction var pos = { x: parent.position.x + 40 * Math.sin(angle), y: parent.position.y + 40 * Math.cos(angle) }; - parent.setMass(parent.getMass() - mass); // Remove mass from parent cell - // Create cell var newCell = new Entity.PlayerCell(this.getNextNodeId(), client, pos, mass, this); newCell.setBoost(780, angle); @@ -1096,6 +1097,9 @@ GameServer.prototype.ejectMass = function(client) { dx /= dl; dy /= dl; } + + // Remove mass from parent cell first + cell.setMass(cell.getMass() - this.config.ejectMassLoss); // Get starting position var pos = { @@ -1109,9 +1113,6 @@ GameServer.prototype.ejectMass = function(client) { // Randomize angle angle += (Math.random() * 0.6) - 0.3; - // Remove mass from parent cell - cell.setMass(cell.getMass() - this.config.ejectMassLoss); - // Create cell var ejected = new Entity.EjectedMass(this.getNextNodeId(), null, pos, this.config.ejectMass, this); ejected.ejector = cell; From f34a45fcc868724b4ebddbb2faf9d87e420b175d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 14:12:34 +0200 Subject: [PATCH 197/417] fix collision for moving cells --- src/GameServer.js | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 394793736..e474d828b 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -878,6 +878,20 @@ GameServer.prototype.updateMoveEngine = function () { this.updateNodeQuad(cell1); } } + // Move moving cells + for (var i = 0; i < this.movingNodes.length; ) { + var cell1 = this.movingNodes[i]; + if (cell1.isRemoved) + continue; + cell1.move(this.border); + this.updateNodeQuad(cell1); + if (!cell1.isMoving) + this.movingNodes.splice(i, 1); + else + i++; + } + + // === check for collisions === // Scan for player cells collisions var self = this; @@ -930,19 +944,6 @@ GameServer.prototype.updateMoveEngine = function () { //this.gameMode.onCellMove(cell1, this); - // Move moving cells - for (var i = 0; i < this.movingNodes.length; ) { - var cell1 = this.movingNodes[i]; - if (cell1.isRemoved) - continue; - cell1.move(this.border); - this.updateNodeQuad(cell1); - if (!cell1.isMoving) - this.movingNodes.splice(i, 1); - else - i++; - } - // Scan for ejected cell collisions (scan for ejected or virus only) rigidCollisions = []; eatCollisions = []; From ab98cf30c53bcf5f37a16e6a8d76d8a922257d29 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 14:35:26 +0200 Subject: [PATCH 198/417] fix FFA virus behavior --- src/GameServer.js | 4 ++-- src/entity/Virus.js | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index e474d828b..4da4a407e 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1124,14 +1124,14 @@ GameServer.prototype.ejectMass = function(client) { } }; -GameServer.prototype.shootVirus = function(parent) { +GameServer.prototype.shootVirus = function(parent, angle) { var parentPos = { x: parent.position.x, y: parent.position.y, }; var newVirus = new Entity.Virus(this.getNextNodeId(), null, parentPos, this.config.virusStartMass, this); - newVirus.setBoost(780, parent.getAngle()); + newVirus.setBoost(780, angle); // Add to moving cells list this.addNode(newVirus); diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 013252691..aed00fea5 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -24,11 +24,9 @@ Virus.prototype.onEat = function (prey) { var size1 = this.getSize(); var size2 = prey.getSize() + 1; this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); - if (prey.isMoving) - this.setAngle(prey.getAngle()); if (this.getMass() >= this.gameServer.config.virusMaxMass) { this.setMass(this.gameServer.config.virusStartMass); // Reset mass - this.gameServer.shootVirus(this); + this.gameServer.shootVirus(this, prey.getAngle()); } }; From 02b8186539ee30a96f3921c75a405209ca8dbaef Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 16:42:24 +0200 Subject: [PATCH 199/417] code refactoring --- src/GameServer.js | 17 +++++++++-------- src/modules/CommandList.js | 10 ++++++---- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 4da4a407e..873bb5281 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1150,14 +1150,15 @@ GameServer.prototype.getNearestVirus = function(cell) { GameServer.prototype.updateMassDecay = function() { // Loop through all player cells var massDecay = 1 - (this.config.playerMassDecayRate * this.gameMode.decayMod); - for (var i = 0; i < this.nodesPlayer.length; i++) { - var cell = this.nodesPlayer[i]; - if (!cell) continue; - - // Mass decay - if (cell.getMass() > this.config.playerMinMassDecay) { - var mass = Math.max(cell.getMass() * massDecay, this.config.playerMinMassDecay); - cell.setMass(mass); + for (var i = 0; i < this.clients.length; i++) { + var playerTracker = this.clients[i].playerTracker; + for (var j = 0; j < playerTracker.cells.length; j++) { + var cell = playerTracker.cells[j]; + // Mass decay + if (cell.getMass() > this.config.playerMinMassDecay) { + var mass = Math.max(cell.getMass() * massDecay, this.config.playerMinMassDecay); + cell.setMass(mass); + } } } }; diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index eb7f36b22..e88fdbf32 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -315,10 +315,12 @@ Commands.list = { }, killall: function(gameServer, split) { var count = 0; - var len = gameServer.nodesPlayer.length; - for (var i = 0; i < len; i++) { - gameServer.removeNode(gameServer.nodesPlayer[0]); - count++; + for (var i = 0; i < gameServer.clients.length; i++) { + var playerTracker = gameServer.clients[i].playerTracker; + while (playerTracker.cells.length > 0) { + gameServer.removeNode(playerTracker.cells[0]); + count++; + } } console.log("[Console] Removed " + count + " cells"); }, From fd86269c109e23d1b15762b79a72c7ae2d3147bf Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 17:28:12 +0200 Subject: [PATCH 200/417] performance optimization --- src/GameServer.js | 1 - src/entity/PlayerCell.js | 7 ---- src/gamemodes/TeamX.js | 82 ++++------------------------------------ src/gamemodes/TeamZ.js | 51 +++++++------------------ 4 files changed, 22 insertions(+), 119 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 873bb5281..add28d6ca 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -26,7 +26,6 @@ function GameServer() { this.nodes = []; this.nodesVirus = []; // Virus nodes this.nodesEjected = []; // Ejected mass nodes - this.nodesPlayer = []; // Nodes controlled by players this.quadTree = null; this.currentFood = 0; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 591774d1a..3253f5ac2 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -103,8 +103,6 @@ PlayerCell.prototype.onEat = function (prey) { }; PlayerCell.prototype.onAdd = function(gameServer) { - // Add to special player node list - gameServer.nodesPlayer.push(this); // Gamemode actions gameServer.gameMode.onCellAdd(this); }; @@ -116,11 +114,6 @@ PlayerCell.prototype.onRemove = function(gameServer) { if (index != -1) { this.owner.cells.splice(index, 1); } - // Remove from special player controlled node list - index = gameServer.nodesPlayer.indexOf(this); - if (index != -1) { - gameServer.nodesPlayer.splice(index, 1); - } // Gamemode actions gameServer.gameMode.onCellRemove(this); }; diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 101a0cbd8..4ddbc1757 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -65,38 +65,16 @@ TeamX.prototype.spawnMotherCell = function(gameServer) { var pos = gameServer.getRandomPosition(); // Check for players - for (var i = 0; i < gameServer.nodesPlayer.length; i++) { - var check = gameServer.nodesPlayer[i]; - - var r = check.getSize(); // Radius of checking player cell - - // Collision box - var topY = check.position.y - r; - var bottomY = check.position.y + r; - var leftX = check.position.x - r; - var rightX = check.position.x + r; - - // Check for collisions - if (pos.y > bottomY) { - continue; - } - - if (pos.y < topY) { - continue; - } - - if (pos.x > rightX) { - continue; - } - - if (pos.x < leftX) { - continue; - } - - // Collided + var size = Math.sqrt(this.motherCellMass * 100); + var bound = { + minx: pos.x - size, + miny: pos.y - size, + maxx: pos.x + size, + maxy: pos.y + size + }; + if (gameServer.quadTree.any(bound, function (item) { return item.cell.cellType == 0; })) { return; } - // Spawn if no cells are colliding var m = new MotherCell(gameServer.getNextNodeId(), null, pos, this.motherCellMass); gameServer.addNode(m); @@ -356,50 +334,6 @@ MotherCell.prototype.update = function(gameServer) { } }; -MotherCell.prototype.checkEat = function(gameServer) { - var safeMass = this.getMass() * .9; - var r = this.getSize(); // The box area that the checked cell needs to be in to be considered eaten - - // Loop for potential prey - for (var i in gameServer.nodesPlayer) { - var check = gameServer.nodesPlayer[i]; - - if (check.getMass() > safeMass) { - // Too big to be consumed - continue; - } - - // Calculations - var len = r - (check.getSize() / 2) >> 0; - if ((this.abs(this.position.x - check.position.x) < len) && (this.abs(this.position.y - check.position.y) < len)) { - // Eats the cell - gameServer.removeNode(check); - this.setMass(this.getMass() + check.getMass()); - } - } - for (var i in gameServer.movingNodes) { - var check = gameServer.movingNodes[i]; - - if ((check.getType() == 1) || (check.getMass() > safeMass)) { - // Too big to be consumed/ No player cells - continue; - } - - // Calculations - var len = r >> 0; - if ((this.abs(this.position.x - check.position.x) < len) && (this.abs(this.position.y - check.position.y) < len)) { - // Eat the cell - gameServer.removeNode(check); - this.setMass(this.getMass() + check.getMass()); - } - } -}; - -MotherCell.prototype.abs = function(n) { - // Because Math.abs is slow - return (n < 0) ? -n : n; -}; - MotherCell.prototype.spawnFood = function(gameServer) { // Get starting position var angle = Math.random() * 6.28; // (Math.PI * 2) ??? Precision is not our greatest concern here diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 1c56ad1bb..3db2d6b86 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -140,43 +140,20 @@ TeamZ.prototype.spawnDrug = function(gameServer, cell) { // spawn HERO or BRAIN var pos = gameServer.getRandomPosition(); // Check for players - var collided = false; - for (var i = 0; i < gameServer.nodesPlayer.length; i++) { - var check = gameServer.nodesPlayer[i]; - var r = check.getSize(); // Radius of checking player cell - - // Collision box - var topY = check.position.y - r; - var bottomY = check.position.y + r; - var leftX = check.position.x - r; - var rightX = check.position.x + r; - - // Check for collisions - if (pos.y > bottomY) { - continue; - } - if (pos.y < topY) { - continue; - } - if (pos.x > rightX) { - continue; - } - if (pos.x < leftX) { - continue; - } - - // Collided - collided = true; - break; - } - - // Spawn if no cells are colliding - if (!collided) { - cell.setPosition(pos); - gameServer.addNode(cell); - return true; // SUCCESS with spawn - } - return false; // FAILED because of collision + var size = cell.getSize(); + var bound = { + minx: pos.x - size, + miny: pos.y - size, + maxx: pos.x + size, + maxy: pos.y + size + }; + if (gameServer.quadTree.any(bound, function (item) { return item.cell.cellType == 0; })) { + // FAILED because of collision + return false; + } + cell.setPosition(pos); + gameServer.addNode(cell); + return true; // SUCCESS with spawn } return true; // SUCCESS without spawn }; From b9e5a80625a5a36bbfa17b8f02854f1aa1d32e08 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 17:41:58 +0200 Subject: [PATCH 201/417] update readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 679c2d12c..91634ac0a 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,10 @@ http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, pretty smooth, cus http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends invalid protocol=1) http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style +###MultiOgar Servers + +IP | Links +ws://164.132.48.230:600 | http://c0nsume.me/private4.php?ip=164.132.48.230:600 http://ogar.mivabe.nl/?ip=164.132.48.230:600 http://play.ogarul.tk/?ip=164.132.48.230:600 ## What's new: From 8e1dbd49407a4de897dfc58f1449534103926d0e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 17:43:05 +0200 Subject: [PATCH 202/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 91634ac0a..c0baa3d88 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style ###MultiOgar Servers IP | Links +--- | --- ws://164.132.48.230:600 | http://c0nsume.me/private4.php?ip=164.132.48.230:600 http://ogar.mivabe.nl/?ip=164.132.48.230:600 http://play.ogarul.tk/?ip=164.132.48.230:600 From 5460b019f729676cb5204494573374c54e4295ab Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 17:44:38 +0200 Subject: [PATCH 203/417] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c0baa3d88..63c956c05 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,9 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style ###MultiOgar Servers -IP | Links +IP | Location --- | --- -ws://164.132.48.230:600 | http://c0nsume.me/private4.php?ip=164.132.48.230:600 http://ogar.mivabe.nl/?ip=164.132.48.230:600 http://play.ogarul.tk/?ip=164.132.48.230:600 +ws://164.132.48.230:600 | France ## What's new: From c7e54b1e0f25a3f42b4fcef68fb8ca56c3baf4d3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 17:46:35 +0200 Subject: [PATCH 204/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 63c956c05..9f074abcb 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style IP | Location --- | --- -ws://164.132.48.230:600 | France +164.132.48.230:600 | France ## What's new: From 5c13b93b38fc8324b122649fb172ebea9332aeb3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 17:53:09 +0200 Subject: [PATCH 205/417] update readme --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9f074abcb..9e8a04d45 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,10 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style ###MultiOgar Servers -IP | Location ---- | --- -164.132.48.230:600 | France +IP | Location | Game Mode | Web Site +--- | --- | --- | --- +vps.simonorj.com:24270 | USA | Instant Merge | https://www.reddit.com/r/Agario/comments/4mufge +164.132.48.230:600 | France | FFA | http://c0nsume.me/private4.php?ip=164.132.48.230:600 ## What's new: From 567874a8698c3e07520770e611e2c4c3f19ab3cc Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 18:36:46 +0200 Subject: [PATCH 206/417] code refactoring --- src/GameServer.js | 7 +++---- src/entity/Cell.js | 9 --------- src/entity/EjectedMass.js | 11 ----------- src/entity/Food.js | 30 +++++------------------------- src/entity/PlayerCell.js | 10 +++++----- src/gamemodes/Experimental.js | 4 ++-- src/gamemodes/HungerGames.js | 2 +- src/gamemodes/Rainbow.js | 7 ------- src/gamemodes/TeamX.js | 2 +- src/gamemodes/TeamZ.js | 3 ++- src/gamemodes/Zombie.js | 3 ++- src/gameserver.ini | 10 +++++----- 12 files changed, 26 insertions(+), 72 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index add28d6ca..517d0547c 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -74,10 +74,9 @@ function GameServer() { foodSpawnAmount: 10, // The amount of food to spawn per interval foodStartAmount: 100, // The starting amount of food in the map foodMaxAmount: 500, // Maximum food cells on the map - foodMass: 1, // Starting food size (In mass) + foodMinMass: 1, // Minimum food mass + foodMaxMass: 4, // Maximum food mass foodMassGrow: 1, // Enable food mass grow ? - foodMassGrowPossiblity: 50, // Chance for a food to has the ability to be self growing - foodMassLimit: 4, // Maximum mass for a food can grow virusMinAmount: 10, // Minimum amount of viruses on the map. virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. virusStartMass: 100, // Starting virus mass @@ -623,7 +622,7 @@ GameServer.prototype.updateFood = function() { }; GameServer.prototype.spawnFood = function() { - var f = new Entity.Food(this.getNextNodeId(), null, this.getRandomPosition(), this.config.foodMass, this); + var f = new Entity.Food(this.getNextNodeId(), null, this.getRandomPosition(), this.config.foodMinMass, this); f.setColor(this.getRandomColor()); this.addNode(f); diff --git a/src/entity/Cell.js b/src/entity/Cell.js index acb8ed000..596072212 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -32,14 +32,10 @@ module.exports = Cell; // Fields not defined by the constructor are considered private and need a getter/setter to access from a different class Cell.prototype.getName = function() { - if (this.owner) - return this.owner.name; return ""; }; Cell.prototype.getSkin = function () { - if (this.owner) - return this.owner.skin; return ""; }; @@ -250,11 +246,6 @@ Cell.prototype.checkBorder = function (border) { // Override these -Cell.prototype.sendUpdate = function() { - // Whether or not to include this cell in the update packet - return true; -}; - Cell.prototype.canEat = function (cell) { // by default cell cannot eat anyone return false; diff --git a/src/entity/EjectedMass.js b/src/entity/EjectedMass.js index e927088ac..2d7e831ba 100644 --- a/src/entity/EjectedMass.js +++ b/src/entity/EjectedMass.js @@ -9,19 +9,8 @@ function EjectedMass() { module.exports = EjectedMass; EjectedMass.prototype = new Cell(); -// Override functions that use 'owner' variable -EjectedMass.prototype.getName = function() { - return ""; -}; - // Main Functions -EjectedMass.prototype.sendUpdate = function() { - // Whether or not to include this cell in the update packet - // Always true since ejected cells can collide with themselves - return true; -}; - EjectedMass.prototype.onAdd = function (gameServer) { // Add to list of ejected mass gameServer.nodesEjected.push(this); diff --git a/src/entity/Food.js b/src/entity/Food.js index 71520b4e7..4f5a73303 100644 --- a/src/entity/Food.js +++ b/src/entity/Food.js @@ -4,11 +4,12 @@ function Food() { Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = 1; - this.shouldSendUpdate = false; - if (this.gameServer.config.foodMassGrow && - this.gameServer.config.foodMassGrowPossiblity > Math.floor(Math.random() * 101)) { - this.grow(); + if (this.gameServer.config.foodMassGrow) { + var mass = this.getMass(); + var maxGrow = this.gameServer.config.foodMaxMass - mass; + mass += maxGrow * Math.random(); + this.setMass(mass); } } @@ -17,27 +18,6 @@ Food.prototype = new Cell(); // Main Functions -Food.prototype.grow = function () { - this.setMass(this.getMass() + 1); // food mass increased, we need to recalculate its size and squareSize, and send update to client side - this.shouldSendUpdate = true; - - if (this.getMass() < this.gameServer.config.foodMassLimit) { - this.grow(); - } -}; - -Food.prototype.sendUpdate = function() { - // Whether or not to include this cell in the update packet - if (this.boostDistance <= 0) { - return false; - } - if (this.shouldSendUpdate) { - this.shouldSendUpdate = false; - return true; - } - return true; -}; - Food.prototype.onRemove = function(gameServer) { gameServer.currentFood--; }; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 3253f5ac2..f661cbe21 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -12,12 +12,12 @@ PlayerCell.prototype = new Cell(); // Main Functions -PlayerCell.prototype.simpleCollide = function(check, d) { - // Simple collision check - var len = 2 * d >> 0; // Width of cell + width of the box (Int) +PlayerCell.prototype.getName = function () { + return this.owner.name; +}; - return (this.abs(this.position.x - check.x) < len) && - (this.abs(this.position.y - check.y) < len); +PlayerCell.prototype.getSkin = function () { + return this.owner.skin; }; PlayerCell.prototype.updateRemerge = function () { diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index e97dfe54a..dafdb525f 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -123,7 +123,7 @@ MotherCell.prototype.update = function(gameServer) { } if (this.getMass() > 222) { // Always spawn food if the mother cell is larger than 222 - var cellSize = gameServer.config.foodMass; + var cellSize = gameServer.config.foodMinMass; var remaining = this.getMass() - 222; var maxAmount = Math.min(Math.floor(remaining / cellSize), 2); for (var i = 0; i < maxAmount; i++) { @@ -150,7 +150,7 @@ MotherCell.prototype.spawnFood = function(gameServer) { }; // Spawn food - var f = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMass, gameServer); + var f = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinMass, gameServer); f.setColor(gameServer.getRandomColor()); gameServer.addNode(f); diff --git a/src/gamemodes/HungerGames.js b/src/gamemodes/HungerGames.js index ab77ecaf7..8f694b0d5 100644 --- a/src/gamemodes/HungerGames.js +++ b/src/gamemodes/HungerGames.js @@ -142,7 +142,7 @@ HungerGames.prototype.onServerInit = function(gameServer) { gameServer.config.foodSpawnAmount = 5; // This is hunger games gameServer.config.foodStartAmount = 100; gameServer.config.foodMaxAmount = 200; - gameServer.config.foodMass = 2; // Food is scarce, but its worth more + gameServer.config.foodMinMass = 2; // Food is scarce, but its worth more gameServer.config.virusMinAmount = 10; // We need to spawn some viruses in case someone eats them all gameServer.config.virusMaxAmount = 100; gameServer.config.ejectSpawnPlayer = 0; diff --git a/src/gamemodes/Rainbow.js b/src/gamemodes/Rainbow.js index 437b1809c..0e3e10f2b 100644 --- a/src/gamemodes/Rainbow.js +++ b/src/gamemodes/Rainbow.js @@ -1,6 +1,5 @@ var FFA = require('./FFA'); // Base gamemode var Food = require('../entity/Food'); -var FoodUp = require('../entity/Food').prototype.sendUpdate; function Rainbow() { FFA.apply(this, Array.prototype.slice.call(arguments)); @@ -155,15 +154,9 @@ Rainbow.prototype.changeColor = function(node) { // Override Rainbow.prototype.onServerInit = function() { - // Overrides the update function - Food.prototype.sendUpdate = function() { - return true; - }; }; Rainbow.prototype.onChange = function() { - // Reset - Food.prototype.sendUpdate = FoodUp; }; Rainbow.prototype.onTick = function(gameServer) { diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 4ddbc1757..f73a864d6 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -344,7 +344,7 @@ MotherCell.prototype.spawnFood = function(gameServer) { }; // Spawn food - var f = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMass, gameServer); + var f = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinMass, gameServer); f.setColor(gameServer.getRandomColor()); gameServer.addNode(f); diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 3db2d6b86..fd815cb93 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -867,7 +867,8 @@ TeamZ.prototype.onCellRemove = function(cell) { } }; -TeamZ.prototype.onCellMove = function(x1, y1, cell) { +// TODO: remove it (move physics is managed by GameServer) +TeamZ.prototype.onCellMove = function (x1, y1, cell) { // Called when a player cell is moved var team = cell.owner.getTeam(); var r = cell.getSize(); diff --git a/src/gamemodes/Zombie.js b/src/gamemodes/Zombie.js index 33c888fa0..af1e8b688 100755 --- a/src/gamemodes/Zombie.js +++ b/src/gamemodes/Zombie.js @@ -96,7 +96,8 @@ Zombie.prototype.onCellRemove = function(cell) { } }; -Zombie.prototype.onCellMove = function(x1, y1, cell) { +// TODO: remove it (move physics is managed by GameServer) +Zombie.prototype.onCellMove = function (x1, y1, cell) { var team = cell.owner.getTeam(); var r = cell.getSize(); diff --git a/src/gameserver.ini b/src/gameserver.ini index 8d6f75ab6..29ae16c9a 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -39,16 +39,16 @@ borderHeight = 6000 // [Spawn] // Each interval is 1 tick (50 ms) -// foodMass: vanilla 1, size 10 -// foodMassLimit: vanilla 4, size 20 +// foodMinMass: vanilla 1, size 10 +// foodMaxMass: vanilla 4, size 20 spawnInterval = 20 foodSpawnAmount = 10 foodStartAmount = 100 foodMaxAmount = 500 -foodMass = 1 +foodMinMass = 1 +foodMaxMass = 4 foodMassGrow = 1 -foodMassGrowPossiblity = 50 -foodMassLimit = 4 + virusMinAmount = 10 virusMaxAmount = 50 virusStartMass = 100 From 3018da945becd997be25e55b8cdf561c627325d4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 19:09:43 +0200 Subject: [PATCH 207/417] code refactoring --- src/GameServer.js | 14 +++++++++----- src/entity/Cell.js | 1 + src/entity/Food.js | 11 ++++------- src/gamemodes/Experimental.js | 10 ++++------ src/gamemodes/HungerGames.js | 8 +++----- src/gamemodes/TeamX.js | 9 ++++----- src/modules/CommandList.js | 7 +++---- 7 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 517d0547c..4ffa297d0 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -622,11 +622,15 @@ GameServer.prototype.updateFood = function() { }; GameServer.prototype.spawnFood = function() { - var f = new Entity.Food(this.getNextNodeId(), null, this.getRandomPosition(), this.config.foodMinMass, this); - f.setColor(this.getRandomColor()); - - this.addNode(f); - this.currentFood++; + var cell = new Entity.Food(this.getNextNodeId(), null, this.getRandomPosition(), this.config.foodMinMass, this); + if (this.config.foodMassGrow) { + var mass = cell.getMass(); + var maxGrow = this.config.foodMaxMass - mass; + mass += maxGrow * Math.random(); + cell.setMass(mass); + } + cell.setColor(this.getRandomColor()); + this.addNode(cell); }; GameServer.prototype.spawnPlayer = function(player, pos, mass) { diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 596072212..db28d5ac8 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -29,6 +29,7 @@ function Cell(nodeId, owner, position, mass, gameServer) { module.exports = Cell; + // Fields not defined by the constructor are considered private and need a getter/setter to access from a different class Cell.prototype.getName = function() { diff --git a/src/entity/Food.js b/src/entity/Food.js index 4f5a73303..a83c216b2 100644 --- a/src/entity/Food.js +++ b/src/entity/Food.js @@ -4,13 +4,6 @@ function Food() { Cell.apply(this, Array.prototype.slice.call(arguments)); this.cellType = 1; - - if (this.gameServer.config.foodMassGrow) { - var mass = this.getMass(); - var maxGrow = this.gameServer.config.foodMaxMass - mass; - mass += maxGrow * Math.random(); - this.setMass(mass); - } } module.exports = Food; @@ -18,6 +11,10 @@ Food.prototype = new Cell(); // Main Functions +Food.prototype.onAdd = function (gameServer) { + gameServer.currentFood++; +}; + Food.prototype.onRemove = function(gameServer) { gameServer.currentFood--; }; diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index dafdb525f..4da7b3e08 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -150,16 +150,14 @@ MotherCell.prototype.spawnFood = function(gameServer) { }; // Spawn food - var f = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinMass, gameServer); - f.setColor(gameServer.getRandomColor()); + var cell = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinMass, gameServer); + cell.setColor(gameServer.getRandomColor()); - gameServer.addNode(f); - gameServer.currentFood++; + gameServer.addNode(cell); // Move engine var dist = (Math.random() * 25) + 25; // Random distance - // TODO: check distance - f.setBoost(dist, angle); + cell.setBoost(dist, angle); }; MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function diff --git a/src/gamemodes/HungerGames.js b/src/gamemodes/HungerGames.js index 8f694b0d5..ce7fe80d0 100644 --- a/src/gamemodes/HungerGames.js +++ b/src/gamemodes/HungerGames.js @@ -79,11 +79,9 @@ HungerGames.prototype.getPos = function() { }; HungerGames.prototype.spawnFood = function(gameServer, mass, pos) { - var f = new Entity.Food(gameServer.getNextNodeId(), null, pos, mass, gameServer); - f.setColor(gameServer.getRandomColor()); - - gameServer.addNode(f); - gameServer.currentFood++; + var cell = new Entity.Food(gameServer.getNextNodeId(), null, pos, mass, gameServer); + cell.setColor(gameServer.getRandomColor()); + gameServer.addNode(cell); }; HungerGames.prototype.spawnVirus = function(gameServer, pos) { diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index f73a864d6..2338ca280 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -344,16 +344,15 @@ MotherCell.prototype.spawnFood = function(gameServer) { }; // Spawn food - var f = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinMass, gameServer); - f.setColor(gameServer.getRandomColor()); + var cell = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinMass, gameServer); + cell.setColor(gameServer.getRandomColor()); - gameServer.addNode(f); - gameServer.currentFood++; + gameServer.addNode(cell); // Move engine var dist = (Math.random() * 10) + 22; // Random distance // TODO: check distance - f.setBoost(dist*15, angle); + cell.setBoost(dist*15, angle); }; MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index e88fdbf32..eeac7fa57 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -262,10 +262,9 @@ Commands.list = { } // Spawn - var f = new Entity.Food(gameServer.getNextNodeId(), null, pos, mass, gameServer); - f.setColor(gameServer.getRandomColor()); - gameServer.addNode(f); - gameServer.currentFood++; + var cell = new Entity.Food(gameServer.getNextNodeId(), null, pos, mass, gameServer); + cell.setColor(gameServer.getRandomColor()); + gameServer.addNode(cell); console.log("[Console] Spawned 1 food cell at (" + pos.x + " , " + pos.y + ")"); }, gamemode: function(gameServer, split) { From cf524471d62af7ae8de48ca6cfdf79ae4df80572 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 20:37:15 +0200 Subject: [PATCH 208/417] add lightweight scramble mode --- src/GameServer.js | 2 +- src/PlayerTracker.js | 32 ++++++++++++++++++++------------ src/gameserver.ini | 4 ++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 4ffa297d0..1069e1686 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -65,7 +65,7 @@ function GameServer() { serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Amount of seconds per update for the server stats serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections - serverScrambleCoords: 0, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Some clients doesn't support it. + serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. borderWidth: 14142, // Map border size (Vanilla value: 14142) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 89433c85a..1952476b1 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -68,17 +68,20 @@ module.exports = PlayerTracker; PlayerTracker.prototype.scramble = function () { this.scrambleId = (Math.random() * 0xFFFFFFFF) >>> 0; - if (!this.gameServer.config.serverScrambleCoords) - return; - // avoid mouse packet limitations - var maxx = Math.max(0, 32767 - 1000 - this.gameServer.border.width); - var maxy = Math.max(0, 32767 - 1000 - this.gameServer.border.height); - var x = maxx * Math.random(); - var y = maxy * Math.random(); - if (Math.random() >= 0.5) x = -x; - if (Math.random() >= 0.5) y = -y; - this.scrambleX = x; - this.scrambleY = y; + if (!this.gameServer.config.serverScrambleCoords) { + this.scrambleX = 0; + this.scrambleY = 0; + } else { + // avoid mouse packet limitations + var maxx = Math.max(0, 32767 - 1000 - this.gameServer.border.width); + var maxy = Math.max(0, 32767 - 1000 - this.gameServer.border.height); + var x = maxx * Math.random(); + var y = maxy * Math.random(); + if (Math.random() >= 0.5) x = -x; + if (Math.random() >= 0.5) y = -y; + this.scrambleX = x; + this.scrambleY = y; + } this.borderCounter = 0; }; @@ -175,6 +178,10 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.socket.sendPacket(new Packet.ClearAll()); this.visibleNodes = []; this.scramble(); + if (this.gameServer.config.serverScrambleCoords < 2) { + // no scramble / lightweight scramble + this.socket.sendPacket(new Packet.SetBorder(this, this.gameServer.border)); + } this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); }; @@ -280,7 +287,8 @@ PlayerTracker.prototype.update = function () { } this.visibleNodes = newVisible; - if (this.gameServer.config.serverScrambleCoords) { + if (this.gameServer.config.serverScrambleCoords == 2) { + // moving border scramble if (this.borderCounter == 0) { var bound = { minx: Math.max(this.gameServer.border.minx, this.viewBox.minx - this.viewBox.halfWidth), diff --git a/src/gameserver.ini b/src/gameserver.ini index 29ae16c9a..1c89a1f39 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -12,7 +12,7 @@ // serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. // serverStatsUpdate: Amount of seconds per update for server stats // serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections -// serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = scrambling. Some clients doesn't support it. +// serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) // serverMaxLB: Controls the maximum players displayed on the leaderboard. // serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. serverTimeout = 30 @@ -28,7 +28,7 @@ serverSpectatorScale = 0.4 serverStatsPort = 88 serverStatsUpdate = 60 serverLogLevel = 1 -serverScrambleCoords = 0 +serverScrambleCoords = 1 serverMaxLB = 10 serverChat = 1 From c55e473e49d7c7690745c5d5a81ba0183a192344 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 22:53:24 +0200 Subject: [PATCH 209/417] protocol optimizations --- src/packet/BinaryWriter.js | 16 ++++++++-------- src/packet/UpdateNodes.js | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index a5753b13d..91363072c 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -17,7 +17,7 @@ module.exports = BinaryWriter; BinaryWriter.prototype.writeUInt8 = function (value) { var offset = this._length; this._writers.push(function (buffer) { - buffer.writeUInt8(value, offset); + buffer.writeUInt8(value, offset, true); }); this._length += 1; }; @@ -25,7 +25,7 @@ BinaryWriter.prototype.writeUInt8 = function (value) { BinaryWriter.prototype.writeInt8 = function (value) { var offset = this._length; this._writers.push(function (buffer) { - buffer.writeInt8(value, offset); + buffer.writeInt8(value, offset, true); }); this._length += 1; }; @@ -33,7 +33,7 @@ BinaryWriter.prototype.writeInt8 = function (value) { BinaryWriter.prototype.writeUInt16 = function (value) { var offset = this._length; this._writers.push(function (buffer) { - buffer.writeUInt16LE(value, offset); + buffer.writeUInt16LE(value, offset, true); }); this._length += 2; }; @@ -41,7 +41,7 @@ BinaryWriter.prototype.writeUInt16 = function (value) { BinaryWriter.prototype.writeInt16 = function (value) { var offset = this._length; this._writers.push(function (buffer) { - buffer.writeInt16LE(value, offset); + buffer.writeInt16LE(value, offset, true); }); this._length += 2; }; @@ -49,7 +49,7 @@ BinaryWriter.prototype.writeInt16 = function (value) { BinaryWriter.prototype.writeUInt32 = function (value) { var offset = this._length; this._writers.push(function (buffer) { - buffer.writeUInt32LE(value, offset); + buffer.writeUInt32LE(value, offset, true); }); this._length += 4; }; @@ -57,7 +57,7 @@ BinaryWriter.prototype.writeUInt32 = function (value) { BinaryWriter.prototype.writeInt32 = function (value) { var offset = this._length; this._writers.push(function (buffer) { - buffer.writeInt32LE(value, offset); + buffer.writeInt32LE(value, offset, true); }); this._length += 4; }; @@ -65,7 +65,7 @@ BinaryWriter.prototype.writeInt32 = function (value) { BinaryWriter.prototype.writeFloat = function (value) { var offset = this._length; this._writers.push(function (buffer) { - buffer.writeFloatLE(value, offset); + buffer.writeFloatLE(value, offset, true); }); this._length += 4; }; @@ -73,7 +73,7 @@ BinaryWriter.prototype.writeFloat = function (value) { BinaryWriter.prototype.writeDouble = function (value) { var offset = this._length; this._writers.push(function (buffer) { - buffer.writeDoubleLE(value, offset); + buffer.writeDoubleLE(value, offset, true); }); this._length += 8; }; diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index abafeccec..aa81b9d12 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -194,8 +194,8 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { var flags = 0; if (node.isSpiked) flags |= 0x01; // isVirus - if (node.cellType != 1 && node.cellType != 3) - flags |= 0x02; // isColorPresent (do not update for food and ejected mass) + if (node.cellType == 0) + flags |= 0x02; // isColorPresent (for players only) if (node.isAgitated) flags |= 0x10; // isAgitated if (node.cellType == 3) From 468b8ab835f56ec08e386bd646953838aa164963 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 21 Jun 2016 23:32:42 +0200 Subject: [PATCH 210/417] fix bot AI --- src/ai/BotPlayer.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index 91f1e090f..53bfbbba8 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -74,11 +74,11 @@ BotPlayer.prototype.decide = function(cell) { // Same team cell influence = 0; } - else if (cell.getSize() > check.getSize() * 1.15) {//cell.getMass() / 1.3 > check.getMass()) { + else if (cell.getSize() > (check.getSize() + 4) * 1.15) {//cell.getMass() / 1.3 > check.getMass()) { // Can eat it influence = check.getSize() * 2.5; } - else if (check.getSize() >= cell.getSize() * 1.15) {//check.getMass() / 1.3 > cell.getMass()) { + else if (check.getSize() + 4 > cell.getSize() * 1.15) {//check.getMass() / 1.3 > cell.getMass()) { // Can eat me influence = -check.getSize(); } @@ -87,7 +87,7 @@ BotPlayer.prototype.decide = function(cell) { influence = 1; } else if (check.cellType == 2) { // Virus - if (cell.getSize() > check.getSize() * 1.15) {//cell.getMass() / 1.3 > check.getMass()) { + if (cell.getSize() > check.getSize() * 1.15) { // Can eat it if (this.cells.length == this.gameServer.config.playerMaxCells) { // Won't explode @@ -97,10 +97,14 @@ BotPlayer.prototype.decide = function(cell) { // Can explode influence = -1; } + } else if (check.isMotherCell && check.getSize() > cell.getSize() * 1.15) { + // can eat me + influence = -1; } } else if (check.cellType == 3) { // Ejected mass - if (cell.getSize() > check.getSize() * 1.15)// cell.getMass() / 1.3 > check.getMass()) + if (cell.getSize() > check.getSize() * 1.15) + // can eat influence = check.getSize(); } else { influence = check.getSize(); // Might be TeamZ @@ -131,7 +135,7 @@ BotPlayer.prototype.decide = function(cell) { // Splitting conditions if (check.cellType == 0 && - cell.getMass() / 2.6 > check.getMass() && + cell.getSize() > (check.getSize() + 4) * 1.15 && cell.getMass() / 5 < check.getMass() && (!split) && this.splitCooldown == 0 && From f1b8fc34bd31f0cc2f4a8afd1da1fe417c00e371 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Tue, 21 Jun 2016 23:24:40 -0400 Subject: [PATCH 211/417] Add debug command + Proofread (parts of) README.md --- README.md | 20 ++++++++++---------- src/modules/CommandList.js | 17 +++++++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 9e8a04d45..2a35aae2e 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ The goal is to cleanup the code, fix the bugs and improve physics. ## Clients -Ogar clients and server trackers, that I found on internet +This lists Ogar clients and server trackers that I found on internet. ###Ogar server trackers @@ -23,15 +23,15 @@ URL | Description --- | --- http://ogar.mivabe.nl/master | MivaBe, tracks a lot of servers -Now you can enable MultiOgar to be listed on server tracker. -Just set serverTracker = 1 in the gameserver.ini and your server will appears +Now you can allow MultiOgar to be listed on a server tracker. +Just set `serverTracker = 1` in the gameserver.ini, and your server will appear on this page: http://ogar.mivabe.nl/master -If you don't want to include your server to tracker list. -Just set serverTracker = 0 and server will not ping the server tracker. +If you don't want to include your server to tracker list, +just set `serverTracker = 0` and the server will not ping the server tracker. ###Ogar clients -Just replace ip:port in the url to play +Just replace `127.0.0.1:50000` in the url to the server IP and port to play. URL | Protocol | Description --- | --- | --- @@ -43,14 +43,14 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style IP | Location | Game Mode | Web Site --- | --- | --- | --- -vps.simonorj.com:24270 | USA | Instant Merge | https://www.reddit.com/r/Agario/comments/4mufge +vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge 164.132.48.230:600 | France | FFA | http://c0nsume.me/private4.php?ip=164.132.48.230:600 ## What's new: * Massive perfromance improvement & reduce network traffic * Split behavior - fixed; -* Protocol code optimized; +* Protocol code - optimized; * Massive performance improvement with quad-tree lookup; * Split/Eject - physics code rewritten; * Player speed - physics code rewritten; @@ -68,9 +68,9 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://www.reddit.com/r/Agario/c * Added anti-spam protection; * Added skin support (use name "< shark > Fish", remove space); * Color generator replaced with hsv model; -* Memory leaks fixed; +* Memory leaks - fixed; * Performance improved and optimized * Added support for server tracker ogar.mivabe.nl/master -Most of the physics code from original ogar were rewritten. +Most of the physics code from the original Ogar were rewritten. The physics engine in MultiOgar is pretty close to the old vanilla physics. \ No newline at end of file diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index eeac7fa57..b7f0979cf 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -57,6 +57,23 @@ Commands.list = { console.log("[Console] st : alias for status"); console.log("[Console] ===================================================="); }, + debug: function(gameServer, split) { + // Used for checking node lengths + + // Calculate client cells + var clientCells = 0; + for (var i in gameServer.clients) { + clientCells += gameServer.clients[i].playerTracker.cells.length; + } + + console.log("[Console] Clients: " + gameServer.clients.length); + console.log("[Console] Total nodes: " + gameServer.nodes.length); + console.log("[Console] - Client cells: " + clientCells); + console.log("[Console] - Foods: " + gameServer.currentFood); + console.log("[Console] - Ejected cells: " + gameServer.nodesEjected.length); + console.log("[Console] - Viruses: " + gameServer.nodesVirus.length); + console.log("[Console] Moving nodes: " + gameServer.movingNodes.length); + }, addbot: function(gameServer, split) { var add = parseInt(split[1]); if (isNaN(add)) { From 5fe30fbc572b89022ed68ee0d364cf1f220f0f4e Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Wed, 22 Jun 2016 00:02:22 -0400 Subject: [PATCH 212/417] Add "maximum" possible values which are defined by gameserver.ini --- src/modules/CommandList.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index b7f0979cf..34ac16488 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -58,21 +58,21 @@ Commands.list = { console.log("[Console] ===================================================="); }, debug: function(gameServer, split) { - // Used for checking node lengths + // Used for checking node lengths (for now) - // Calculate client cells + // Count client cells var clientCells = 0; for (var i in gameServer.clients) { clientCells += gameServer.clients[i].playerTracker.cells.length; } - - console.log("[Console] Clients: " + gameServer.clients.length); - console.log("[Console] Total nodes: " + gameServer.nodes.length); - console.log("[Console] - Client cells: " + clientCells); - console.log("[Console] - Foods: " + gameServer.currentFood); - console.log("[Console] - Ejected cells: " + gameServer.nodesEjected.length); - console.log("[Console] - Viruses: " + gameServer.nodesVirus.length); - console.log("[Console] Moving nodes: " + gameServer.movingNodes.length); + // Output node information + console.log("[Console] Clients: " + fillChar(gameServer.clients.length, " ", 4, true) + " / " + gameServer.config.serverMaxConnections + " + bots"); + console.log("[Console] Total nodes:" + fillChar(gameServer.nodes.length, " ", 8, true)); + console.log("[Console] - Client cells: " + fillChar(clientCells, " ", 4, true) + " / " + (gameServer.clients.length * gameServer.config.playerMaxCells)); + console.log("[Console] - Ejected cells:" + fillChar(gameServer.nodesEjected.length, " ", 4, true)); + console.log("[Console] - Foods: " + fillChar(gameServer.currentFood, " ", 4, true) + " / " + gameServer.config.foodMaxAmount); + console.log("[Console] - Viruses: " + fillChar(gameServer.nodesVirus.length, " ", 4, true) + " / " + gameServer.config.virusMaxAmount); + console.log("[Console] Moving nodes: " + fillChar(gameServer.movingNodes.length, " ", 4, true)); }, addbot: function(gameServer, split) { var add = parseInt(split[1]); From 3efd7fdb287be5e7d53dd29680d38069afbb68f9 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Wed, 22 Jun 2016 00:09:12 -0400 Subject: [PATCH 213/417] Improve ini.getLagMessage(updateTimeAvg) with minor grammarical correction --- src/modules/ini.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/modules/ini.js b/src/modules/ini.js index 5d79643a5..7ab40e0fd 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -191,15 +191,13 @@ var isInt = function(n) { }; function getLagMessage(updateTimeAvg){ - var lagMessage = "extreme high lag"; if (updateTimeAvg < 20) - lagMessage = "perfectly smooth"; - else if (updateTimeAvg < 35) - lagMessage = "good"; - else if (updateTimeAvg < 40) - lagMessage = "tiny lag"; - else if (updateTimeAvg < 50) - lagMessage = "lag"; - - return lagMessage; + return "perfectly smooth"; + if (updateTimeAvg < 35) + return "good"; + if (updateTimeAvg < 40) + return "tiny lag"; + if (updateTimeAvg < 50) + return "lag"; + return "extremely high lag"; } From ee594966cb3da854f3c23640f159afc86b19de6c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 22 Jun 2016 11:38:27 +0200 Subject: [PATCH 214/417] show quad-tree state in debug command --- src/modules/CommandList.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 34ac16488..999404a74 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -73,6 +73,15 @@ Commands.list = { console.log("[Console] - Foods: " + fillChar(gameServer.currentFood, " ", 4, true) + " / " + gameServer.config.foodMaxAmount); console.log("[Console] - Viruses: " + fillChar(gameServer.nodesVirus.length, " ", 4, true) + " / " + gameServer.config.virusMaxAmount); console.log("[Console] Moving nodes: " + fillChar(gameServer.movingNodes.length, " ", 4, true)); + + function getQuadCount(quadNode) { + var count = 0; + for (var i = 0; i < quadNode.childNodes.length; i++) + count += getQuadCount(quadNode.childNodes[i]); + return 1 + count; + } + var quadCount = getQuadCount(gameServer.quadTree); + console.log("[Console] Quad nodes: " + fillChar(quadCount, " ", 4, true)); }, addbot: function(gameServer, split) { var add = parseInt(split[1]); From 6a1271ffb1b6587a286e5350135681c2b40918a3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 22 Jun 2016 14:19:55 +0200 Subject: [PATCH 215/417] performance improvement; fix lags --- src/QuadNode.js | 29 ++++++++++++++++++++++++++++- src/modules/CommandList.js | 11 ++--------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/QuadNode.js b/src/QuadNode.js index 3b5e300d8..20d7a7314 100644 --- a/src/QuadNode.js +++ b/src/QuadNode.js @@ -97,7 +97,18 @@ QuadNode.prototype.remove = function (item) { } this.items.splice(index, 1); item._quadNode = null; - // TODO: collapse node if empty + cleanup(this); +}; + +function cleanup (node) { + if (node.parent==null || node.items.length > 0) return; + for (var i = 0; i < node.childNodes.length; i++) { + var child = node.childNodes[i]; + if (child.childNodes.length > 0 || child.items.length > 0) + return; + } + node.childNodes = []; + cleanup(node.parent); }; QuadNode.prototype.update = function (item) { @@ -168,6 +179,22 @@ QuadNode.prototype.any = function (bound, predicate) { return false; }; +QuadNode.prototype.scanNodeCount = function () { + var count = 0; + for (var i = 0; i < this.childNodes.length; i++) { + count += this.childNodes[i].scanNodeCount(); + } + return 1 + count; +}; + +QuadNode.prototype.scanItemCount = function () { + var count = 0; + for (var i = 0; i < this.childNodes.length; i++) { + count += this.childNodes[i].scanItemCount(); + } + return this.items.length + count; +}; + // Returns quadrant for the bound. // Returns -1 if bound cannot completely fit within a child node QuadNode.prototype.getQuad = function (bound) { diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 999404a74..c05cd6048 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -73,15 +73,8 @@ Commands.list = { console.log("[Console] - Foods: " + fillChar(gameServer.currentFood, " ", 4, true) + " / " + gameServer.config.foodMaxAmount); console.log("[Console] - Viruses: " + fillChar(gameServer.nodesVirus.length, " ", 4, true) + " / " + gameServer.config.virusMaxAmount); console.log("[Console] Moving nodes: " + fillChar(gameServer.movingNodes.length, " ", 4, true)); - - function getQuadCount(quadNode) { - var count = 0; - for (var i = 0; i < quadNode.childNodes.length; i++) - count += getQuadCount(quadNode.childNodes[i]); - return 1 + count; - } - var quadCount = getQuadCount(gameServer.quadTree); - console.log("[Console] Quad nodes: " + fillChar(quadCount, " ", 4, true)); + console.log("[Console] Quad nodes: " + fillChar(gameServer.quadTree.scanNodeCount(), " ", 4, true)); + console.log("[Console] Quad items: " + fillChar(gameServer.quadTree.scanItemCount(), " ", 4, true)); }, addbot: function(gameServer, split) { var add = parseInt(split[1]); From 4cbdfa136764a9d3282eee25169afb8a9c4140a4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 22 Jun 2016 20:31:38 +0200 Subject: [PATCH 216/417] fix server stats --- src/GameServer.js | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 1069e1686..de24b63eb 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1311,20 +1311,32 @@ GameServer.prototype.startStatsServer = function(port) { }; GameServer.prototype.getStats = function() { - var players = 0; - this.clients.forEach(function(client) { - if (client.playerTracker && client.playerTracker.cells.length > 0) - players++; - }); + // Get server statistics + var totalPlayers = 0; + var alivePlayers = 0; + var spectatePlayers = 0; + for (var i = 0; i < this.clients.length; i++) { + var socket = this.clients[i]; + if (socket == null || !socket.isConnected) + continue; + totalPlayers++; + if (socket.playerTracker.cells.length > 0) + alivePlayers++; + else + spectatePlayers++; + } var s = { - 'current_players': this.clients.length, - 'alive': players, - 'spectators': this.clients.length - players, + 'current_players': totalPlayers, + 'alive': alivePlayers, + 'spectators': spectatePlayers, 'max_players': this.config.serverMaxConnections, 'gamemode': this.gameMode.name, - 'update_time':this.updateTimeAvg.toFixed(3) + " [ms] (" + ini.getLagMessage(this.updateTimeAvg) + ")", - 'uptime': Math.round((new Date().getTime() - this.startTime)/1000/60)+" m", - 'start_time': this.startTime + 'update_time': this.updateTimeAvg.toFixed(3), + 'uptime': Math.round((new Date().getTime() - this.startTime)/1000/60), + 'start_time': this.startTime, + 'border_width': this.border.width, + 'border_height': this.border.height, + 'chat_enabled': this.config.serverChat ? "true" : "false" }; this.stats = JSON.stringify(s); }; From 6af4f027f98c8738f5c5261fad34e9495762ce5c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 22 Jun 2016 21:16:31 +0200 Subject: [PATCH 217/417] configurable welcome message; add serverName; fix server stat init --- src/GameServer.js | 22 +++++++++++++--------- src/PacketHandler.js | 8 +++++--- src/gameserver.ini | 6 ++++++ src/modules/ini.js | 6 ++++-- 4 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index de24b63eb..f84bd723a 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -68,6 +68,9 @@ function GameServer() { serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. + serverName: 'MultiOgar #1', // Server name + serverWelcome1: 'Welcome to MultiOgar server!', // First server welcome message + serverWelcome2: '', // Second server welcome message (for info, etc) borderWidth: 14142, // Map border size (Vanilla value: 14142) borderHeight: 14142, // Map border size (Vanilla value: 14142) spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) @@ -111,6 +114,9 @@ function GameServer() { this.loadConfig(); this.loadIpBanList(); + this.setBorder(this.config.borderWidth, this.config.borderHeight); + this.quadTree = new QuadNode(this.border, 4, 100); + // Gamemodes this.gameMode = Gamemode.get(this.config.serverGamemode); } @@ -156,9 +162,6 @@ GameServer.prototype.onServerSocketError = function (error) { }; GameServer.prototype.onServerSocketOpen = function () { - this.setBorder(this.config.borderWidth, this.config.borderHeight); - this.quadTree = new QuadNode(this.border, 4, 100); - // Spawn starting food this.startingFood(); @@ -1326,17 +1329,18 @@ GameServer.prototype.getStats = function() { spectatePlayers++; } var s = { + 'server_name': this.config.serverName, + 'server_chat': this.config.serverChat ? "true" : "false", + 'border_width': this.border.width, + 'border_height': this.border.height, + 'gamemode': this.gameMode.name, + 'max_players': this.config.serverMaxConnections, 'current_players': totalPlayers, 'alive': alivePlayers, 'spectators': spectatePlayers, - 'max_players': this.config.serverMaxConnections, - 'gamemode': this.gameMode.name, 'update_time': this.updateTimeAvg.toFixed(3), 'uptime': Math.round((new Date().getTime() - this.startTime)/1000/60), - 'start_time': this.startTime, - 'border_width': this.border.width, - 'border_height': this.border.height, - 'chat_enabled': this.config.serverChat ? "true" : "false" + 'start_time': this.startTime }; this.stats = JSON.stringify(s); }; diff --git a/src/PacketHandler.js b/src/PacketHandler.js index e751c1c60..d26350998 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -43,12 +43,14 @@ PacketHandler.prototype.handleMessage = function(message) { this.socket.sendPacket(new Packet.ClearAll()); this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, this.gameServer.border, this.gameServer.config.serverGamemode, "MultiOgar")); // Send welcome message - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "Welcome to MultiOgar server!"); + if (this.gameServer.config.serverWelcome1) + this.gameServer.sendChatMessage(null, this.socket.playerTracker, this.gameServer.config.serverWelcome1); + if (this.gameServer.config.serverWelcome2) + this.gameServer.sendChatMessage(null, this.socket.playerTracker, this.gameServer.config.serverWelcome2); if (this.gameServer.config.serverChat == 0) this.gameServer.sendChatMessage(null, this.socket.playerTracker, "This server's chat is disabled."); - if (this.protocol < 4) { + if (this.protocol < 4) this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING: Protocol " + this.protocol + " assumed as 4!"); - } this.isHandshakePassed = true; return; } diff --git a/src/gameserver.ini b/src/gameserver.ini index 1c89a1f39..e68640576 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -15,6 +15,9 @@ // serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) // serverMaxLB: Controls the maximum players displayed on the leaderboard. // serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. +// serverName: Server name, for example "My great server" +// serverWelcome1: First server welcome message +// serverWelcome2: Second server welcome message (optional, for info, etc) serverTimeout = 30 serverMaxConnections = 64 serverIpLimit = 4 @@ -31,6 +34,9 @@ serverLogLevel = 1 serverScrambleCoords = 1 serverMaxLB = 10 serverChat = 1 +serverName = "MultiOgar #1" +serverWelcome1 = "Welcome to MultiOgar server!" +serverWelcome2 = "" // [Border] // Border size (vanilla values are 14142.135623730952) diff --git a/src/modules/ini.js b/src/modules/ini.js index 7ab40e0fd..ee49cf477 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -106,7 +106,9 @@ function decode(str) { // safeguard against resetting a previously defined // array by accidentally forgetting the brackets - if (isInt(value)) { + if (isNaN(value)) { + p[key] = value; + } else if (isInt(value)) { p[key] = parseInt(value); } else { p[key] = parseFloat(value); @@ -187,7 +189,7 @@ function unsafe(val, doUnesc) { } var isInt = function(n) { - return parseInt(n) === n; + return parseInt(n) == n; }; function getLagMessage(updateTimeAvg){ From aefdedd501da2d2a92ff65f56110808f17650c69 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 22 Jun 2016 21:35:14 +0200 Subject: [PATCH 218/417] fix disabled screamble mode --- src/PlayerTracker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 1952476b1..0c170eb44 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -66,12 +66,12 @@ module.exports = PlayerTracker; // Setters/Getters PlayerTracker.prototype.scramble = function () { - this.scrambleId = (Math.random() * 0xFFFFFFFF) >>> 0; - if (!this.gameServer.config.serverScrambleCoords) { + this.scrambleId = 0; this.scrambleX = 0; this.scrambleY = 0; } else { + this.scrambleId = (Math.random() * 0xFFFFFFFF) >>> 0; // avoid mouse packet limitations var maxx = Math.max(0, 32767 - 1000 - this.gameServer.border.width); var maxy = Math.max(0, 32767 - 1000 - this.gameServer.border.height); From 20fd5fb773f320879d34fcd1b54c1dd976719649 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 22 Jun 2016 23:11:54 +0200 Subject: [PATCH 219/417] add player commands (skin, self kill) --- src/GameServer.js | 12 +++++++ src/PacketHandler.js | 7 ++-- src/PlayerTracker.js | 7 ++-- src/modules/PlayerCommand.js | 68 ++++++++++++++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 src/modules/PlayerCommand.js diff --git a/src/GameServer.js b/src/GameServer.js index f84bd723a..b0f6a471d 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -5,6 +5,7 @@ var fs = require("fs"); var ini = require('./modules/ini.js'); var os = require("os"); var QuadNode = require('./QuadNode.js'); +var PlayerCommand = require('./modules/PlayerCommand'); // Project imports var Packet = require('./packet'); @@ -216,6 +217,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { ws.playerTracker = new PlayerTracker(this, ws); ws.packetHandler = new PacketHandler(this, ws); + ws.playerCommand = new PlayerCommand(this, ws.playerTracker); var gameServer = this; var onMessage = function (message) { @@ -512,6 +514,16 @@ GameServer.prototype.onChatMessage = function (from, to, message) { if (message == null) return; message = message.trim(); if (message == "") return; + if (from && message.length > 0 && message[0] == '/') { + // player command + message = message.slice(1, message.length); + from.socket.playerCommand.executeCommandLine(message); + return; + } + if (!this.config.serverChat) { + // chat is disabled + return; + } if (message.length > 128) message = message.slice(0, 128); //console.log("[CHAT] " + (from!=null && from.getName().length>0 ? from.getName() : "Spectator") + ": " + message); this.sendChatMessage(from, to, message); diff --git a/src/PacketHandler.js b/src/PacketHandler.js index d26350998..cd9271852 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -111,7 +111,7 @@ PacketHandler.prototype.handleMessage = function(message) { break; case 99: // Chat - if (this.gameServer.config.serverChat == 0 || message.length < 3) // first validation + if (message.length < 3) // first validation break; // chat anti-spam // Just ignore if the time between two messages is smaller than 2 seconds @@ -141,7 +141,7 @@ PacketHandler.prototype.handleMessage = function(message) { PacketHandler.prototype.setNickname = function (text) { var name = ""; - var skin = ""; + var skin = null; if (text != null && text.length != 0) { var n = -1; if (text.charAt(0) == '<' && (n = text.indexOf('>', 1)) >= 0) { @@ -157,8 +157,5 @@ PacketHandler.prototype.setNickname = function (text) { if (name.length > this.gameServer.config.playerMaxNickLength) { name = name.substring(0, this.gameServer.config.playerMaxNickLength); } - if (this.gameServer.gameMode.haveTeams) { - skin = ""; - } this.socket.playerTracker.joinGame(name, skin); }; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 0c170eb44..5ba57448b 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -107,6 +107,9 @@ PlayerTracker.prototype.setSkin = function (skin) { }; PlayerTracker.prototype.getSkin = function () { + if (this.gameServer.gameMode.haveTeams) { + return ""; + } return this.skin; }; @@ -164,9 +167,9 @@ PlayerTracker.prototype.massChanged = function () { PlayerTracker.prototype.joinGame = function (name, skin) { if (this.cells.length > 0) return; if (name == null) name = ""; - if (skin == null) skin = ""; this.setName(name); - this.setSkin(skin); + if (skin != null) + this.setSkin(skin); this.spectate = false; this.freeRoam = false; diff --git a/src/modules/PlayerCommand.js b/src/modules/PlayerCommand.js new file mode 100644 index 000000000..afde47f64 --- /dev/null +++ b/src/modules/PlayerCommand.js @@ -0,0 +1,68 @@ +var Entity = require('../entity'); + +function PlayerCommand(gameServer, playerTracker) { + this.gameServer = gameServer; + this.playerTracker = playerTracker; +} + +module.exports = PlayerCommand; + +PlayerCommand.prototype.writeLine = function (text) { + this.gameServer.sendChatMessage(null, this.playerTracker, text); +}; + +PlayerCommand.prototype.executeCommandLine = function (commandLine) { + if (!commandLine) return; + var command = commandLine; + var args = ""; + var index = commandLine.indexOf(' '); + if (index >= 0) { + command = commandLine.slice(0, index); + args = commandLine.slice(index+1, commandLine.length); + } + command = command.trim().toLowerCase(); + var execute = playerCommands[command]; + if (typeof execute == 'function') { + execute.bind(this)(args); + } else { + this.writeLine("Unknown command, type /help for command list"); + } +}; + +var playerCommands = { + help: function (args) { + this.writeLine("/skin %shark - change skin"); + this.writeLine("/kill - self kill"); + this.writeLine("/help - this command list"); + }, + skin: function (args) { + if (this.playerTracker.cells.length > 0) { + this.writeLine("Cannot change skin while player in game!"); + return; + } + var skinName = ""; + if (args) skinName = args.trim(); + this.playerTracker.setSkin(skinName); + if (skinName == "") + this.writeLine("Your skin was removed"); + else + this.writeLine("Your skin set to " + skinName); + }, + kill: function (args) { + if (this.playerTracker.cells.length < 1) { + this.writeLine("You cannot kill yourself, because you're still not joined to the game!"); + return; + } + while (this.playerTracker.cells.length > 0) { + var cell = this.playerTracker.cells[0]; + this.gameServer.removeNode(cell); + // replace with food + var food = new Entity.Food(this.gameServer.getNextNodeId(), null, cell.position, this.gameServer.config.playerStartMass, this.gameServer); + food.setColor(this.gameServer.getGrayColor(cell.getColor())); + this.gameServer.addNode(food); + } + this.writeLine("You killed yourself"); + } +}; + + From 28dc94bd5694cf2c8640fa33930ad8f5cba1fd32 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 22 Jun 2016 23:46:42 +0200 Subject: [PATCH 220/417] set skinName limit --- src/modules/PlayerCommand.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/PlayerCommand.js b/src/modules/PlayerCommand.js index afde47f64..59242dc36 100644 --- a/src/modules/PlayerCommand.js +++ b/src/modules/PlayerCommand.js @@ -42,6 +42,8 @@ var playerCommands = { } var skinName = ""; if (args) skinName = args.trim(); + if (skinName.length > 16) + skinName = skinName.slice(0, 16); this.playerTracker.setSkin(skinName); if (skinName == "") this.writeLine("Your skin was removed"); From 98c4db4f0db0426ab784baf2aa822393aafc0b24 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Wed, 22 Jun 2016 20:36:24 -0400 Subject: [PATCH 221/417] Added official client to the list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2a35aae2e..1d464c22e 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Just replace `127.0.0.1:50000` in the url to the server IP and port to play. URL | Protocol | Description --- | --- | --- +http://agar.io/?ip=127.0.0.1:50000 | 8 | Official Client http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, pretty smooth, custom graphics (anime) http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends invalid protocol=1) http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style From 80820693883f49db5f3d286308f8ab7532b1c61f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 23 Jun 2016 09:22:54 +0200 Subject: [PATCH 222/417] fix ini parsing for commented lines --- src/modules/ini.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/ini.js b/src/modules/ini.js index ee49cf477..e0dd5dac5 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -75,6 +75,11 @@ function decode(str) { section = null; lines.forEach(function(line, _, __) { + var testLine = line.trim(); + if (testLine.length >= 2 && testLine[0] == '/' && testLine[1] == '/') { + // skip commented lines + return; + } if (!line || line.match(/^\s*[;#]/)) { return; } From 5e1a2080b40f183d5929151457c42b244b2cbd51 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 23 Jun 2016 09:26:44 +0200 Subject: [PATCH 223/417] fix ini parsing for empty lines --- src/modules/ini.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/modules/ini.js b/src/modules/ini.js index e0dd5dac5..c7375ecd5 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -76,6 +76,10 @@ function decode(str) { lines.forEach(function(line, _, __) { var testLine = line.trim(); + if (testLine.length == 0) { + // skip empty lines + return; + } if (testLine.length >= 2 && testLine[0] == '/' && testLine[1] == '/') { // skip commented lines return; From 1909dc5a57fba423417714c9310dcbc98695a080 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 23 Jun 2016 18:48:42 +0200 Subject: [PATCH 224/417] perfromance optimization (eliminate redundant mass to size conversion); add MotherCell --- src/GameServer.js | 192 +++++++++++++++++++--------------- src/entity/Cell.js | 128 ++++++++++------------- src/entity/PlayerCell.js | 17 +-- src/entity/Virus.js | 8 +- src/entity/index.js | 1 + src/gamemodes/Experimental.js | 178 ++++++++++--------------------- src/gamemodes/HungerGames.js | 8 +- src/gamemodes/TeamX.js | 121 ++------------------- src/gamemodes/TeamZ.js | 23 ++-- src/gamemodes/Tournament.js | 1 - src/gameserver.ini | 57 +++++----- src/modules/CommandList.js | 19 ++-- src/modules/PlayerCommand.js | 2 +- 13 files changed, 298 insertions(+), 457 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index b0f6a471d..a58036342 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -25,7 +25,7 @@ function GameServer() { this.clients = []; this.largestClient; // Required for spectators this.nodes = []; - this.nodesVirus = []; // Virus nodes + this.nodesVirus = []; // Virus nodes this.nodesEjected = []; // Ejected mass nodes this.quadTree = null; @@ -72,35 +72,36 @@ function GameServer() { serverName: 'MultiOgar #1', // Server name serverWelcome1: 'Welcome to MultiOgar server!', // First server welcome message serverWelcome2: '', // Second server welcome message (for info, etc) + borderWidth: 14142, // Map border size (Vanilla value: 14142) borderHeight: 14142, // Map border size (Vanilla value: 14142) - spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) - foodSpawnAmount: 10, // The amount of food to spawn per interval - foodStartAmount: 100, // The starting amount of food in the map + + foodMinSize: 10, // Minimum food size (vanilla 10) + foodMaxSize: 20, // Maximum food size (vanilla 20) + foodMinAmount: 100, // Minimum food cells on the map foodMaxAmount: 500, // Maximum food cells on the map - foodMinMass: 1, // Minimum food mass - foodMaxMass: 4, // Maximum food mass + foodSpawnAmount: 10, // The amount of food to spawn per interval foodMassGrow: 1, // Enable food mass grow ? + spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) + + virusMinSize: 100, // Minimum virus size (vanilla 100) + virusMaxSize: 140, // Maximum virus size (vanilla 140) virusMinAmount: 10, // Minimum amount of viruses on the map. virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. - virusStartMass: 100, // Starting virus mass - virusMaxMass: 200, // Maximum virus mass - ejectMass: 13.69, // Mass of ejected cells - ejectMassCooldown: 3, // min ticks between ejects - ejectMassLoss: 15, // Mass lost when ejecting cells + + ejectSize: 37, // Size of ejected cells (vanilla 37) + ejectCooldown: 3, // min ticks between ejects ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass - playerStartMass: 10.24, // Starting mass of the player cell. - playerBotGrowEnabled: 1, // If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass - playerMaxMass: 22500, // Maximum mass a player can have - playerMinMassEject: 32, // Mass required to eject a cell - playerMinMassSplit: 36, // Mass required to split + + playerMinSize: 32, // Minimym size of the player cell (mass = 32*32/100 = 10.24) + playerMaxSize: 1500, // Maximum size of the player cell (mass = 1500*1500/100 = 22500) playerMaxCells: 16, // Max cells the player is allowed to have + playerSpeed: 1, // Player speed multiplier + playerDecayRate: .002, // Amount of size lost per second playerRecombineTime: 30, // Base amount of seconds before a cell is allowed to recombine - playerMassDecayRate: .002, // Amount of mass lost per second - playerMinMassDecay: 10.24, // Minimum mass for decay to occur playerMaxNickLength: 15, // Maximum nick length - playerSpeed: 1, // Player speed multiplier playerDisconnectTime: 60, // The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) + tourneyMaxPlayers: 12, // Maximum amount of participants for tournament style game modes tourneyPrepTime: 10, // Amount of ticks to wait after all players are ready (1 tick = 1000 ms) tourneyEndTime: 30, // Amount of ticks to wait after a player wins (1 tick = 1000 ms) @@ -307,11 +308,10 @@ GameServer.prototype.getRandomPosition = function() { }; }; -GameServer.prototype.getRandomSpawn = function(mass) { +GameServer.prototype.getRandomSpawn = function(size) { // Random and secure spawns for players and viruses - var size = Math.sqrt(mass * 100); var pos = this.getRandomPosition(); - var unsafe = this.willCollide(size, pos, mass == this.config.virusStartMass); + var unsafe = this.willCollide(pos, size); if (!unsafe) return pos; // just shift offset and try again @@ -324,17 +324,11 @@ GameServer.prototype.getRandomSpawn = function(mass) { while (unsafe && attempt < maxAttempt) { pos.x += stepx * dirx; pos.y += stepy * diry; - unsafe = this.willCollide(size, pos, mass == this.config.virusStartMass); + unsafe = this.willCollide(pos, size); attempt++; } - - // If it reached attempt maxAttempt, warn the user - //if (unsafe) { - // console.log("[Server] Entity was force spawned near viruses/playercells after "+attempt+" attempts."); - // console.log("[Server] If this message keeps appearing, check your config, especially start masses for players and viruses."); - //} - - return pos; + // failed to find safe position + return null; }; GameServer.prototype.getGrayColor = function (rgb) { @@ -464,8 +458,8 @@ GameServer.prototype.updateSpawn = function() { if (this.tickSpawn >= this.config.spawnInterval) { this.tickSpawn = 0; // Reset - this.updateFood(); // Spawn food - this.virusCheck(); // Spawn viruses + this.updateFood(); // Spawn food + this.updateVirus(); // Spawn viruses } }; @@ -624,31 +618,51 @@ GameServer.prototype.mainLoop = function() { GameServer.prototype.startingFood = function() { // Spawns the starting amount of food cells - for (var i = 0; i < this.config.foodStartAmount; i++) { + for (var i = 0; i < this.config.foodMinAmount; i++) { this.spawnFood(); } }; GameServer.prototype.updateFood = function() { - var toSpawn = Math.min(this.config.foodSpawnAmount, (this.config.foodMaxAmount - this.currentFood)); - for (var i = 0; i < toSpawn; i++) { + var maxCount = this.config.foodMaxAmount - this.currentFood; + var spawnCount = Math.min(maxCount, this.config.foodSpawnAmount); + for (var i = 0; i < spawnCount; i++) { this.spawnFood(); } }; +GameServer.prototype.updateVirus = function () { + var maxCount = this.config.virusMaxAmount - this.nodesVirus.length; + var spawnCount = Math.min(maxCount, 2); + for (var i = 0; i < spawnCount; i++) { + this.spawnVirus(); + } +}; + GameServer.prototype.spawnFood = function() { - var cell = new Entity.Food(this.getNextNodeId(), null, this.getRandomPosition(), this.config.foodMinMass, this); + var cell = new Entity.Food(this, null, this.getRandomPosition(), this.config.foodMinSize); if (this.config.foodMassGrow) { - var mass = cell.getMass(); - var maxGrow = this.config.foodMaxMass - mass; - mass += maxGrow * Math.random(); - cell.setMass(mass); + var size = cell.getSize(); + var maxGrow = this.config.foodMaxSize - size; + size += maxGrow * Math.random(); + cell.setSize(size); } cell.setColor(this.getRandomColor()); this.addNode(cell); }; -GameServer.prototype.spawnPlayer = function(player, pos, mass) { +GameServer.prototype.spawnVirus = function () { + // Spawns a virus + var pos = this.getRandomSpawn(this.config.virusMinSize); + if (pos == null) { + // cannot find safe position => do not spawn + return; + } + var v = new Entity.Virus(this, null, pos, this.config.virusMinSize); + this.addNode(v); +}; + +GameServer.prototype.spawnPlayer = function(player, pos, size) { // Check if there are ejected mass in the world. if (this.nodesEjected.length > 0) { var index = Math.floor(Math.random() * 100) + 1; @@ -669,15 +683,19 @@ GameServer.prototype.spawnPlayer = function(player, pos, mass) { } if (pos == null) { // Get random pos - pos = this.getRandomSpawn(mass); + pos = this.getRandomSpawn(this.config.playerMinSize); + if (pos == null) { + // cannot find safe position => spawn anyway at random position + pos = this.getRandomPosition(); + } } - if (mass == null) { + if (size == null) { // Get starting mass - mass = this.config.playerStartMass; + size = this.config.playerMinSize; } // Spawn player and add to world - var cell = new Entity.PlayerCell(this.getNextNodeId(), player, pos, mass, this); + var cell = new Entity.PlayerCell(this, player, pos, size); this.addNode(cell); // Set initial mouse coords @@ -687,18 +705,7 @@ GameServer.prototype.spawnPlayer = function(player, pos, mass) { }; }; -GameServer.prototype.virusCheck = function() { - // Checks if there are enough viruses on the map - if (this.nodesVirus.length < this.config.virusMinAmount) { - // Spawns a virus - var pos = this.getRandomSpawn(this.config.virusStartMass); - - var v = new Entity.Virus(this.getNextNodeId(), null, pos, this.config.virusStartMass, this); - this.addNode(v); - } -}; - -GameServer.prototype.willCollide = function (size, pos, isVirus) { +GameServer.prototype.willCollide = function (pos, size) { // Look if there will be any collision with the current nodes var bound = { minx: pos.x - size - 10, @@ -779,10 +786,6 @@ GameServer.prototype.resolveRigidCollision = function (manifold, border) { // clip to border bounds manifold.cell1.checkBorder(border); manifold.cell2.checkBorder(border); - // update quadTree - // it's too heavy operation we will update it when resolution will be completed - //this.updateNodeQuad(manifold.cell1); - //this.updateNodeQuad(manifold.cell2); }; // Checks if collision is rigid body collision @@ -1026,8 +1029,9 @@ GameServer.prototype.splitCells = function(client) { var cellToSplit = []; for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; - if (cell.getMass() < this.config.playerMinMassSplit) + if (cell.getSplitSize() < this.config.playerMinSize) { continue; + } cellToSplit.push(cell); if (cellToSplit.length + client.cells.length >= this.config.playerMaxCells) break; @@ -1045,12 +1049,14 @@ GameServer.prototype.splitCells = function(client) { var angle = Math.atan2(dx, dy); if (isNaN(angle)) angle = Math.PI / 2; - if (this.splitPlayerCell(client, cell, angle, cell.getMass() / 2) == true) + if (this.splitPlayerCell(client, cell, angle, null)) { splitCells++; + } } }; -GameServer.prototype.splitPlayerCell = function(client, parent, angle, mass) { +// TODO: replace mass with size (Virus) +GameServer.prototype.splitPlayerCell = function (client, parent, angle, mass) { // Returns boolean whether a cell has been split or not. You can use this in the future. if (client.cells.length >= this.config.playerMaxCells) { @@ -1058,13 +1064,18 @@ GameServer.prototype.splitPlayerCell = function(client, parent, angle, mass) { return false; } - if (parent.getMass() < this.config.playerMinMassSplit) { - // Minimum mass to split - return false; + var size1 = 0; + var size2 = 0; + if (mass == null) { + size1 = parent.getSplitSize(); + size2 = size1; + } else { + size2 = Math.sqrt(mass * 100); + size1 = Math.sqrt(parent.getSize() * parent.getSize() - size2 * size2); } // Remove mass from parent cell first - parent.setMass(parent.getMass() - mass); + parent.setSize(size1); // make a small shift to the cell position to prevent extrusion in wrong direction var pos = { @@ -1073,7 +1084,7 @@ GameServer.prototype.splitPlayerCell = function(client, parent, angle, mass) { }; // Create cell - var newCell = new Entity.PlayerCell(this.getNextNodeId(), client, pos, mass, this); + var newCell = new Entity.PlayerCell(this, client, pos, size2); newCell.setBoost(780, angle); // Add to node list @@ -1082,12 +1093,19 @@ GameServer.prototype.splitPlayerCell = function(client, parent, angle, mass) { }; GameServer.prototype.canEjectMass = function(client) { - if (client.lastEject == null || this.tickCounter - client.lastEject >= this.config.ejectMassCooldown) { - client.lastEject = this.tickCounter; + var tick = this.getTick(); + if (client.lastEject == null) { + // first eject + client.lastEject = tick; return true; - } else { + } + var dt = tick - client.lastEject; + if (dt < this.config.ejectCooldown) { + // reject (cooldown) return false; } + client.lastEject = tick; + return true; }; GameServer.prototype.ejectMass = function(client) { @@ -1100,9 +1118,12 @@ GameServer.prototype.ejectMass = function(client) { continue; } - if (cell.getMass() < this.config.playerMinMassEject) { + var size2 = this.config.ejectSize; + var sizeSquared = cell.getSquareSize() - size2 * size2; + if (sizeSquared < this.config.playerMinSize * this.config.playerMinSize) { continue; } + var size1 = Math.sqrt(sizeSquared); var dx = client.mouse.x - cell.position.x; var dy = client.mouse.y - cell.position.y; @@ -1117,7 +1138,7 @@ GameServer.prototype.ejectMass = function(client) { } // Remove mass from parent cell first - cell.setMass(cell.getMass() - this.config.ejectMassLoss); + cell.setSize(size1); // Get starting position var pos = { @@ -1132,7 +1153,7 @@ GameServer.prototype.ejectMass = function(client) { angle += (Math.random() * 0.6) - 0.3; // Create cell - var ejected = new Entity.EjectedMass(this.getNextNodeId(), null, pos, this.config.ejectMass, this); + var ejected = new Entity.EjectedMass(this, null, pos, size2); ejected.ejector = cell; ejected.setColor(cell.getColor()); ejected.setBoost(780, angle); @@ -1147,7 +1168,7 @@ GameServer.prototype.shootVirus = function(parent, angle) { y: parent.position.y, }; - var newVirus = new Entity.Virus(this.getNextNodeId(), null, parentPos, this.config.virusStartMass, this); + var newVirus = new Entity.Virus(this, null, parentPos, this.config.virusMinSize); newVirus.setBoost(780, angle); // Add to moving cells list @@ -1165,16 +1186,20 @@ GameServer.prototype.getNearestVirus = function(cell) { }; GameServer.prototype.updateMassDecay = function() { + var decay = 1 - (this.config.playerDecayRate * this.gameMode.decayMod); + if (decay == 0) { + return; + } // Loop through all player cells - var massDecay = 1 - (this.config.playerMassDecayRate * this.gameMode.decayMod); for (var i = 0; i < this.clients.length; i++) { var playerTracker = this.clients[i].playerTracker; for (var j = 0; j < playerTracker.cells.length; j++) { var cell = playerTracker.cells[j]; - // Mass decay - if (cell.getMass() > this.config.playerMinMassDecay) { - var mass = Math.max(cell.getMass() * massDecay, this.config.playerMinMassDecay); - cell.setMass(mass); + // TODO: check if non linear will be better + var size = cell.getSize() * decay; + size = Math.max(size, this.config.playerMinSize); + if (size != cell.getSize()) { + cell.setSize(size); } } } @@ -1197,8 +1222,7 @@ GameServer.prototype.loadConfig = function() { fs.writeFileSync('./gameserver.ini', ini.stringify(this.config)); } // check config (min player size = 32 => mass = 10.24) - this.config.playerStartMass = Math.max(10.24, this.config.playerStartMass); - this.config.playerMinMassDecay = Math.max(10.24, this.config.playerMinMassDecay); + this.config.playerMinSize = Math.max(32, this.config.playerMinSize); }; GameServer.prototype.loadIpBanList = function () { diff --git a/src/entity/Cell.js b/src/entity/Cell.js index db28d5ac8..a2eca8546 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -1,7 +1,6 @@ -function Cell(nodeId, owner, position, mass, gameServer) { - this.nodeId = nodeId; - this.owner = owner; // playerTracker that owns this cell +function Cell(gameServer, owner, position, size) { this.gameServer = gameServer; + this.owner = owner; // playerTracker that owns this cell this.tickOfBirth = 0; this.color = { r: 0, g: 0, b: 0 }; @@ -16,15 +15,19 @@ function Cell(nodeId, owner, position, mass, gameServer) { this.isMoving = false; // Indicate that cell is in boosted mode this.boostDistance = 0; - this.boostDirection = { x: 1, y: 0, angle: 0 }; + this.boostDirection = { x: 1, y: 0, angle: Math.PI / 2 }; this.ejector = null; - if (gameServer != null) - this.tickOfBirth = gameServer.getTick(); - if (mass != null) - this.setMass(mass); - if (position != null) - this.setPosition(position); + if (this.gameServer != null) { + this.nodeId = this.gameServer.getNextNodeId(); + this.tickOfBirth = this.gameServer.getTick(); + if (size != null) { + this.setSize(size); + } + if (position != null) { + this.setPosition(position); + } + } } module.exports = Cell; @@ -54,6 +57,17 @@ Cell.prototype.getType = function() { return this.cellType; }; +Cell.prototype.setSize = function (size) { + if (isNaN(size)) { + throw new TypeError("Cell.setSize: size is NaN"); + } + this._size = size; + this._squareSize = size * size; + this._mass = this._squareSize / 100; + if (this.owner) + this.owner.massChanged(); +}; + Cell.prototype.getSize = function() { return this._size; }; @@ -66,22 +80,6 @@ Cell.prototype.getSquareSize = function () { return this._squareSize; }; -Cell.prototype.setMass = function (mass) { - this._size = Math.sqrt(mass * 100); - this._squareSize = this._size * this._size; - this._mass = this._squareSize / 100; - if (this.owner) - this.owner.massChanged(); -}; - -Cell.prototype.setSize = function (size) { - this._size = size; - this._squareSize = size * size; - this._mass = this._squareSize / 100; - if (this.owner) - this.owner.massChanged(); -}; - Cell.prototype.getSpeed = function() { var speed = 2.1106 / Math.pow(this.getSize(), 0.449); // tickStep=40ms @@ -106,12 +104,12 @@ Cell.prototype.getAge = function (tick) { return Math.max(0, tick - this.tickOfBirth); } -Cell.prototype.getKiller = function() { - return this.killedBy; +Cell.prototype.setKiller = function (cell) { + this.killedBy = cell; }; -Cell.prototype.setKiller = function(cell) { - this.killedBy = cell; +Cell.prototype.getKiller = function () { + return this.killedBy; }; Cell.prototype.setPosition = function (pos) { @@ -123,6 +121,31 @@ Cell.prototype.setPosition = function (pos) { this.position.y = pos.y; }; +// Virtual + +Cell.prototype.canEat = function (cell) { + // by default cell cannot eat anyone + return false; +}; + +Cell.prototype.onEat = function (prey) { + // Called to eat prey cell + var size1 = this.getSize(); + var size2 = prey.getSize(); + this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); +}; + +Cell.prototype.onEaten = function (hunter) { +}; + +Cell.prototype.onAdd = function (gameServer) { + // Called when this cell is added to the world +}; + +Cell.prototype.onRemove = function (gameServer) { + // Called when this cell is removed +}; + // Functions Cell.prototype.setBoost = function (distance, angle) { @@ -174,16 +197,16 @@ Cell.prototype.clipVelocity = function (v, border) { var x = this.position.x + v.x; var y = this.position.y + v.y; // border check - var pleft = x >= bound.minx ? null : this.getLineIntersection( + var pleft = x >= bound.minx ? null : findLineIntersection( this.position.x, this.position.y, x, y, bound.minx, bound.miny, bound.minx, bound.maxy); - var pright = x <= bound.maxx ? null : this.getLineIntersection( + var pright = x <= bound.maxx ? null : findLineIntersection( this.position.x, this.position.y, x, y, bound.maxx, bound.miny, bound.maxx, bound.maxy); - var ptop = y >= bound.miny ? null : this.getLineIntersection( + var ptop = y >= bound.miny ? null : findLineIntersection( this.position.x, this.position.y, x, y, bound.minx, bound.miny, bound.maxx, bound.miny); - var pbottom = y <= bound.maxy ? null : this.getLineIntersection( + var pbottom = y <= bound.maxy ? null : findLineIntersection( this.position.x, this.position.y, x, y, bound.minx, bound.maxy, bound.maxx, bound.maxy); var ph = pleft != null ? pleft : pright; @@ -245,34 +268,9 @@ Cell.prototype.checkBorder = function (border) { }; -// Override these - -Cell.prototype.canEat = function (cell) { - // by default cell cannot eat anyone - return false; -}; - -Cell.prototype.onEat = function (prey) { - // Called to eat prey cell - var size1 = this.getSize(); - var size2 = prey.getSize() + 1; - this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); -}; - -Cell.prototype.onEaten = function (hunter) { -}; - -Cell.prototype.onAdd = function(gameServer) { - // Called when this cell is added to the world -}; - -Cell.prototype.onRemove = function(gameServer) { - // Called when this cell is removed -}; - // Lib -Cell.prototype.getLineIntersection = function (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { +function findLineIntersection (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { var z1 = p1x - p0x; var z2 = p3x - p2x; var w1 = p1y - p0y; @@ -285,13 +283,3 @@ Cell.prototype.getLineIntersection = function (p0x, p0y, p1x, p1y, p2x, p2y, p3x if (isNaN(px) || isNaN(py)) return null; return { x: px, y: py }; } - -Cell.prototype.abs = function(x) { - return x < 0 ? -x : x; -}; - -Cell.prototype.getDist = function(x1, y1, x2, y2) { - var vx = x2 - x1; - var vy = y2 - x1; - return Math.sqrt(vx * vx + vy * vy); -}; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index f661cbe21..ebbfcf521 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -48,6 +48,12 @@ PlayerCell.prototype.canEat = function (cell) { return true; }; +PlayerCell.prototype.getSplitSize = function () { + return this.getSize() * splitMultiplier; +}; + +var splitMultiplier = 1 / Math.sqrt(2); + // Movement PlayerCell.prototype.moveUser = function (border) { @@ -86,20 +92,19 @@ PlayerCell.prototype.moveUser = function (border) { PlayerCell.prototype.onEat = function (prey) { var size1 = this.getSize(); - var size2 = prey.getSize() + 1; + var size2 = prey.getSize(); this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); if (this.owner.mergeOverride) return; - if (this.getMass() <= this.gameServer.config.playerMaxMass) + if (this.getSize() <= this.gameServer.config.playerMaxSize) return; if (this.owner.cells.length >= this.gameServer.config.playerMaxCells) { - this.setMass(this.gameServer.config.playerMaxMass); + this.setSize(this.gameServer.config.playerMaxSize); return; } - var splitMass = this.getMass() / 2; - var randomAngle = Math.random() * 6.28; // Get random angle - this.gameServer.splitPlayerCell(this.owner, this, randomAngle, splitMass); + var randomAngle = Math.random() * 2 * Math.PI; // Get random angle??? + this.gameServer.splitPlayerCell(this.owner, this, randomAngle, null); }; PlayerCell.prototype.onAdd = function(gameServer) { diff --git a/src/entity/Virus.js b/src/entity/Virus.js index aed00fea5..b94aa124d 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -24,8 +24,8 @@ Virus.prototype.onEat = function (prey) { var size1 = this.getSize(); var size2 = prey.getSize() + 1; this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); - if (this.getMass() >= this.gameServer.config.virusMaxMass) { - this.setMass(this.gameServer.config.virusStartMass); // Reset mass + if (this.getSize() >= this.gameServer.config.virusMaxSize) { + this.setSize(this.gameServer.config.virusMinSize); // Reset mass this.gameServer.shootVirus(this, prey.getAngle()); } }; @@ -71,13 +71,13 @@ Virus.prototype.onEaten = function(consumer) { numSplits -= bigSplits.length; for (var k = 0; k < bigSplits.length; k++) { - angle = Math.random() * 6.28; // Random directions + angle = Math.random() * 2 * Math.PI; // Random directions this.gameServer.splitPlayerCell(client, consumer, angle, bigSplits[k]); } // Splitting for (var k = 0; k < numSplits; k++) { - angle = Math.random() * 6.28; // Random directions + angle = Math.random() * 2 * Math.PI; // Random directions this.gameServer.splitPlayerCell(client, consumer, angle, splitMass); } }; diff --git a/src/entity/index.js b/src/entity/index.js index ae4ddedbb..4913f6e2b 100644 --- a/src/entity/index.js +++ b/src/entity/index.js @@ -3,5 +3,6 @@ module.exports = { PlayerCell: require('./PlayerCell'), Food: require('./Food'), Virus: require('./Virus'), + MotherCell: require('./MotherCell'), EjectedMass: require('./EjectedMass'), }; diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 4da7b3e08..c663df5e2 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -1,7 +1,5 @@ var FFA = require('./FFA'); // Base gamemode -var Cell = require('../entity/Cell'); -var Food = require('../entity/Food'); -var Virus = require('../entity/Virus'); +var Entity = require('../entity'); function Experimental() { FFA.apply(this, Array.prototype.slice.call(arguments)); @@ -12,15 +10,14 @@ function Experimental() { // Gamemode Specific Variables this.nodesMother = []; - this.tickMother = 0; - this.tickMotherS = 0; + this.tickMotherSpawn = 0; + this.tickMotherUpdate = 0; // Config - var motherSize = 149; // mass=222.01, size=149 (vanilla) - this.motherCellMass = motherSize * motherSize / 100; - this.motherUpdateInterval = 5; // How many ticks it takes to update the mother cell (1 tick = 50 ms) - this.motherSpawnInterval = 100; // How many ticks it takes to spawn another mother cell - Currently 5 seconds - this.motherMinAmount = 5; + this.motherSpawnInterval = 25 * 5; // How many ticks it takes to spawn another mother cell (5 seconds) + this.motherUpdateInterval = 5; // How many ticks it takes to spawn mother food (1 second) + this.motherMinAmount = 20; + this.motherMaxAmount = 30; } module.exports = Experimental; @@ -28,26 +25,21 @@ Experimental.prototype = new FFA(); // Gamemode Specific Functions -Experimental.prototype.updateMotherCells = function(gameServer) { - for (var i in this.nodesMother) { - var mother = this.nodesMother[i]; - - // Checks - mother.update(gameServer); - } -}; - -Experimental.prototype.spawnMotherCell = function(gameServer) { +Experimental.prototype.spawnMotherCell = function (gameServer) { // Checks if there are enough mother cells on the map - if (this.nodesMother.length < this.motherMinAmount) { - // Spawns a mother cell - var pos = gameServer.getRandomSpawn(this.motherCellMass); - if (pos == null) return; - - // Spawn if no cells are colliding - var m = new MotherCell(gameServer.getNextNodeId(), null, pos, this.motherCellMass, gameServer); - gameServer.addNode(m); + if (this.nodesMother.length >= this.motherMinAmount) { + return; } + // Spawns a mother cell + var pos = gameServer.getRandomSpawn(149); + if (pos == null) { + // cannot find safe position => do not spawn + return; + } + // Spawn if no cells are colliding + var mother = new Entity.MotherCell(gameServer, null, pos, null); + gameServer.addNode(mother); + this.nodesMother.push(mother); }; // Override @@ -59,116 +51,60 @@ Experimental.prototype.onServerInit = function(gameServer) { var mapSize = Math.max(gameServer.border.width, gameServer.border.height); // 7 mother cells for vanilla map size - this.motherMinAmount = Math.ceil(mapSize / 2000); + //this.motherMinAmount = Math.ceil(mapSize / 2000); + //this.motherMaxAmount = this.motherMinAmount * 2; + var self = this; + // Override + // Special virus mechanics - Virus.prototype.onEat = function(prey) { + Entity.Virus.prototype.onEat = function(prey) { // Pushes the virus var angle = prey.isMoving ? prey.getAngle() : this.getAngle(); this.setBoost(16 * 20, angle); }; - - // Override this - gameServer.getRandomSpawn = gameServer.getRandomPosition; -}; - -Experimental.prototype.onTick = function(gameServer) { - // Mother Cell updates - this.updateMotherCells(gameServer); - - // Mother Cell Spawning - if (this.tickMotherS >= this.motherSpawnInterval) { - this.spawnMotherCell(gameServer); - this.tickMotherS = 0; - } else { - this.tickMotherS++; - } + Entity.MotherCell.prototype.onAdd = function () { + self.nodesMother.push(this); + }; + Entity.MotherCell.prototype.onRemove = function () { + var index = self.nodesMother.indexOf(this); + if (index != -1) { + self.nodesMother.splice(index, 1); + } else { + console.log("[Warning] Tried to remove a non existing MotherCell!"); + } + }; + //gameServer.getRandomSpawn = gameServer.getRandomPosition; }; -Experimental.prototype.onChange = function(gameServer) { +Experimental.prototype.onChange = function (gameServer) { // Remove all mother cells for (var i in this.nodesMother) { gameServer.removeNode(this.nodesMother[i]); } + this.nodesMother = []; // Add back default functions - gameServer.getRandomSpawn = require('../GameServer').prototype.getRandomSpawn; + Entity.Virus.prototype.onEat = require('../Entity/Virus').prototype.onEat; + Entity.MotherCell.prototype.onAdd = require('../Entity/MotherCell').prototype.onAdd; + Entity.MotherCell.prototype.onRemove = require('../Entity/MotherCell').prototype.onRemove; + //gameServer.getRandomSpawn = require('../GameServer').prototype.getRandomSpawn; }; -// New cell type - -function MotherCell() { // Temporary - Will be in its own file if Zeach decides to add this to vanilla - Cell.apply(this, Array.prototype.slice.call(arguments)); - - this.cellType = 2; // Copies virus cell - this.setColor({ r: 205, g: 85, b: 100 }); - this.isSpiked = true; - this.isMotherCell = true; // Not to confuse bots -} - -MotherCell.prototype = new Cell(); // Base - -MotherCell.prototype.update = function(gameServer) { - if (Math.random() * 100 > 97) { - var maxFood = Math.random() * 2; // Max food spawned per tick - var i = 0; // Food spawn counter - while (i < maxFood) { - // Only spawn if food cap hasn't been reached - if (gameServer.currentFood < gameServer.config.foodMaxAmount * 1.5) { - this.spawnFood(gameServer); - } - - // Increment - i++; - } +Experimental.prototype.onTick = function(gameServer) { + // Mother Cell Spawning + if (this.tickMotherSpawn >= this.motherSpawnInterval) { + this.tickMotherSpawn = 0; + this.spawnMotherCell(gameServer); + } else { + this.tickMotherSpawn++; } - if (this.getMass() > 222) { - // Always spawn food if the mother cell is larger than 222 - var cellSize = gameServer.config.foodMinMass; - var remaining = this.getMass() - 222; - var maxAmount = Math.min(Math.floor(remaining / cellSize), 2); - for (var i = 0; i < maxAmount; i++) { - this.spawnFood(gameServer); - this.setMass(this.getMass() - cellSize); - // update bounds - this.gameServer.updateNodeQuad(this); + if (this.tickMotherUpdate >= this.motherUpdateInterval) { + this.tickMotherUpdate = 0; + for (var i = 0; i < this.nodesMother.length; i++) { + this.nodesMother[i].onUpdate(); } + } else { + this.tickMotherUpdate++; } }; -MotherCell.prototype.canEat = function (cell) { - return cell.cellType == 0 || // can eat player cell - cell.cellType == 3; // can eat ejected mass -}; - -MotherCell.prototype.spawnFood = function(gameServer) { - // Get starting position - var angle = Math.random() * 6.28; - var r = this.getSize(); - var pos = { - x: this.position.x + (r * Math.sin(angle)), - y: this.position.y + (r * Math.cos(angle)) - }; - - // Spawn food - var cell = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinMass, gameServer); - cell.setColor(gameServer.getRandomColor()); - - gameServer.addNode(cell); - - // Move engine - var dist = (Math.random() * 25) + 25; // Random distance - cell.setBoost(dist, angle); -}; - -MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function - -MotherCell.prototype.onAdd = function(gameServer) { - gameServer.gameMode.nodesMother.push(this); // Temporary -}; - -MotherCell.prototype.onRemove = function(gameServer) { - var index = gameServer.gameMode.nodesMother.indexOf(this); - if (index != -1) { - gameServer.gameMode.nodesMother.splice(index, 1); - } -}; diff --git a/src/gamemodes/HungerGames.js b/src/gamemodes/HungerGames.js index ce7fe80d0..a8d81804b 100644 --- a/src/gamemodes/HungerGames.js +++ b/src/gamemodes/HungerGames.js @@ -79,13 +79,13 @@ HungerGames.prototype.getPos = function() { }; HungerGames.prototype.spawnFood = function(gameServer, mass, pos) { - var cell = new Entity.Food(gameServer.getNextNodeId(), null, pos, mass, gameServer); + var cell = new Entity.Food(gameServer, null, pos, mass); cell.setColor(gameServer.getRandomColor()); gameServer.addNode(cell); }; HungerGames.prototype.spawnVirus = function(gameServer, pos) { - var v = new Entity.Virus(gameServer.getNextNodeId(), null, pos, gameServer.config.virusStartMass, gameServer); + var v = new Entity.Virus(gameServer, null, pos, gameServer.config.virusMinSize); gameServer.addNode(v); }; @@ -138,9 +138,9 @@ HungerGames.prototype.onServerInit = function(gameServer) { gameServer.config.borderWidth = 3200; gameServer.config.borderHeight = 3200; gameServer.config.foodSpawnAmount = 5; // This is hunger games - gameServer.config.foodStartAmount = 100; + gameServer.config.foodMinAmount = 100; gameServer.config.foodMaxAmount = 200; - gameServer.config.foodMinMass = 2; // Food is scarce, but its worth more + gameServer.config.foodMinSize = 10; // Food is scarce, but its worth more gameServer.config.virusMinAmount = 10; // We need to spawn some viruses in case someone eats them all gameServer.config.virusMaxAmount = 100; gameServer.config.ejectSpawnPlayer = 0; diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 2338ca280..7f6554aeb 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -118,119 +118,10 @@ TeamX.prototype.onServerInit = function(gameServer) { // Special virus mechanics if (this.pushVirus) { - Virus.prototype.feed = function(feeder, gameServer) { - gameServer.removeNode(feeder); + Virus.prototype.onEat = function (prey) { // Pushes the virus - // TODO: check distance - this.setBoost(30 * 5, feeder.getAngle()); - //this.setAngle(feeder.getAngle()); // Set direction if the virus explodes - //this.moveEngineTicks = 5; // Amount of times to loop the movement function - //this.moveEngineSpeed = 30; - - var index = gameServer.movingNodes.indexOf(this); - if (index == -1) { - gameServer.movingNodes.push(this); - } - }; - } - - if (!this.teamCollision) { - this.onCellMove = function(x1, y1, cell) {}; // does nothing - if (GS_getCellsInRange == null) - GS_getCellsInRange = gameServer.getCellsInRange; - - gameServer.getCellsInRange = function(cell) { - var list = new Array(); - var squareR = cell.getSquareSize(); // Get cell squared radius - - // Loop through all cells that are visible to the cell. There is probably a more efficient way of doing this but whatever - var len = cell.owner.visibleNodes.length; - for (var i = 0; i < len; i++) { - var check = cell.owner.visibleNodes[i]; - - if (typeof check === 'undefined') { - continue; - } - - // if something already collided with this cell, don't check for other collisions - if (check.isRemoved) { - continue; - } - - // Can't eat itself - if (cell.nodeId == check.nodeId) { - continue; - } - - // Can't eat cells that have collision turned off - if ((cell.owner == check.owner) && (cell.ignoreCollision)) { - continue; - } - - // AABB Collision - if (gameServer.checkCellCollision(cell, check) == null) { - continue; - } - - // Cell type check - Cell must be bigger than this number times the mass of the cell being eaten - var multiplier = 1.25; - - switch (check.getType()) { - case 1: // Food cell - list.push(check); - check.isRemoved = true; // skip future collision checks for this food - continue; - case 2: // Virus - multiplier = 1.33; - break; - case 0: // Players - // Can't eat self if it's not time to recombine yet - if (check.owner == cell.owner) { - if (!cell.canRemerge() || !check.canRemerge()) { - continue; - } - - multiplier = 1.00; - } - - // Can't eat team members - if (this.gameMode.haveTeams) { - if (!check.owner) { // Error check - continue; - } - - if ((check.owner != cell.owner) && (check.owner.getTeam() == cell.owner.getTeam()) && this.gameMode.countNotInRange(check.owner) == 1) { - continue; - } - } - break; - default: - break; - } - - // Make sure the cell is big enough to be eaten. - if ((check.getMass() * multiplier) > cell.getMass()) { - continue; - } - - // Eating range - var xs = Math.pow(check.position.x - cell.position.x, 2); - var ys = Math.pow(check.position.y - cell.position.y, 2); - var dist = Math.sqrt(xs + ys); - - var eatingRange = cell.getSize() - check.getSize() / Math.PI; // Eating range = radius of eating cell + 40% of the radius of the cell being eaten - if (dist > eatingRange) { - // Not in eating range - continue; - } - - // Add to list of cells nearby - list.push(check); - - // Something is about to eat this cell; no need to check for other collisions with it - check.isRemoved = true; - } - return list; + var angle = prey.isMoving ? prey.getAngle() : this.getAngle(); + this.setBoost(16 * 20, angle); }; } @@ -317,7 +208,7 @@ MotherCell.prototype = new Cell(); // Base MotherCell.prototype.update = function(gameServer) { // Add mass - this.setMass(this.getMass() + .25); + this.setSize(Math.sqrt(this.getSize() * this.getSize() + 0.25 * 0.25)); // Spawn food var maxFood = 10; // Max food spawned per tick @@ -329,7 +220,7 @@ MotherCell.prototype.update = function(gameServer) { } // Incrementers - this.setMass(this.getMass() - 1); + this.setSize(Math.sqrt(this.getSize() * this.getSize() - 1)); i++; } }; @@ -344,7 +235,7 @@ MotherCell.prototype.spawnFood = function(gameServer) { }; // Spawn food - var cell = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinMass, gameServer); + var cell = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinSize, gameServer); cell.setColor(gameServer.getRandomColor()); gameServer.addNode(cell); diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index fd815cb93..3ad8360be 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -220,7 +220,7 @@ TeamZ.prototype.startGame = function(gameServer) { var cell = client.cells[j]; if (cell) { cell.setColor(client.getColor()); - cell.setMass(gameServer.config.playerStartMass); + cell.setSize(gameServer.config.playerMinSize); this.resetSpeedCell(cell); } } @@ -490,7 +490,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { continue; } - if (cell.getMass() < this.config.playerMinMassSplit) { + if (cell.getSplitSize() < this.config.playerMinSize) { continue; } @@ -507,14 +507,12 @@ TeamZ.prototype.onServerInit = function(gameServer) { }; // Calculate mass and speed of splitting cell var splitSpeed = cell.getSpeed() * 6; - var newMass = cell.getMass() / 2; - cell.setMass(newMass); + var newSize = cell.getSplitSize(); + cell.setSize(newSize); // Create cell - var split = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, newMass); + var split = new Entity.PlayerCell(this, client, startPos, newSize); // TODO: check distance split.setBoost(splitSpeed * 32, angle); - //split.setAngle(angle); - //split.setMoveEngineData(splitSpeed, 32, 0.85); // boost speed if zombie eats brain if (this.gameMode.hasEatenBrain(client) || this.gameMode.isCrazy(client)) { @@ -538,8 +536,9 @@ TeamZ.prototype.onServerInit = function(gameServer) { y: parent.position.y }; + var size = Math.sqrt(mass); // Create cell - newCell = new Entity.PlayerCell(this.getNextNodeId(), client, startPos, mass); + newCell = new Entity.PlayerCell(this, client, startPos, size); // TODO: check distance newCell.setBoost(speed * 10, angle); //newCell.setAngle(angle); @@ -594,14 +593,14 @@ TeamZ.prototype.onServerInit = function(gameServer) { for (var k = 0; k < numSplits; k++) { angle += 6 / numSplits; // Get directions of splitting cells this.gameServer.newCellVirused(client, consumer, angle, splitMass, 150); - consumer.setMass(consumer.getMass() - splitMass); + consumer.setSize(Math.sqrt(consumer.getSize() * consumer.getSize() - splitMass*splitMass)); } for (var k = 0; k < bigSplits; k++) { angle = Math.random() * 6.28; // Random directions splitMass = consumer.getMass() / 4; this.gameServer.newCellVirused(client, consumer, angle, splitMass, 20); - consumer.setMass(consumer.getMass() - splitMass); + consumer.setSize(Math.sqrt(consumer.getSize() * consumer.getSize() - splitMass * splitMass)); } if (this.gameServer.gameMode.hasEatenHero(client)) { @@ -1010,7 +1009,7 @@ function Hero() { this.cellType = CellType.HERO; //this.isSpiked = true; this.setColor({ r: 255, g: 255, b: 7 }); - this.setMass(60); + this.setSize(78); } Hero.prototype = new Cell(); @@ -1081,7 +1080,7 @@ function Brain() { this.cellType = CellType.BRAIN; //this.isSpiked = true; this.setColor({ r: 255, g: 7, b: 255 }); - this.setMass(60); + this.setSize(78); } Brain.prototype = new Cell(); diff --git a/src/gamemodes/Tournament.js b/src/gamemodes/Tournament.js index 241441ac0..1b622dbfb 100644 --- a/src/gamemodes/Tournament.js +++ b/src/gamemodes/Tournament.js @@ -95,7 +95,6 @@ Tournament.prototype.prepare = function(gameServer) { // Handles disconnections this.dcTime = gameServer.config.playerDisconnectTime; gameServer.config.playerDisconnectTime = 0; - gameServer.config.playerMinMassDecay = gameServer.config.playerStartMass; this.prepTime = gameServer.config.tourneyPrepTime; this.endTime = gameServer.config.tourneyEndTime; diff --git a/src/gameserver.ini b/src/gameserver.ini index e68640576..a0c582a0f 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -39,56 +39,51 @@ serverWelcome1 = "Welcome to MultiOgar server!" serverWelcome2 = "" // [Border] -// Border size (vanilla values are 14142.135623730952) +// Border size (vanilla 14142.135623730952) borderWidth = 6000 borderHeight = 6000 // [Spawn] -// Each interval is 1 tick (50 ms) -// foodMinMass: vanilla 1, size 10 -// foodMaxMass: vanilla 4, size 20 -spawnInterval = 20 -foodSpawnAmount = 10 -foodStartAmount = 100 +// Each interval is 1 tick (40 ms) +// foodMinSize: vanilla 10 (mass = 10*10/100 = 1) +// foodMaxSize: vanilla 20 (mass = 20*20/100 = 4) +foodMinSize = 10 +foodMaxSize = 20 +foodMinAmount = 100 foodMaxAmount = 500 -foodMinMass = 1 -foodMaxMass = 4 +foodSpawnAmount = 10 foodMassGrow = 1 +spawnInterval = 20 +// virusMinSize: vanilla 100 (mass = 100*100/100 = 100) +// virusMaxSize: vanilla 140 (mass = 140*140/100 = 196) +virusMinSize = 100 +virusMaxSize = 140 virusMinAmount = 10 virusMaxAmount = 50 -virusStartMass = 100 -virusMaxMass = 200 // [Ejected Mass] -// ejectMass: Mass of ejected cells (vanilla mass 13.69, size 37) -// ejectMassCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) -// ejectMassLoss: Mass lost when ejecting cells +// ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) +// ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) // ejectSpawnPlayer: Chance for a player to spawn from ejected mass -ejectMass = 13.69 -ejectMassCooldown = 3 -ejectMassLoss = 15 +ejectSize = 37 +ejectCooldown = 3 ejectSpawnPlayer = 50 // [Player] -// playerStartMass: start mass (vanilla 10.24, size 32 - minimum player size) -// playerRecombineTime: Base amount of ticks before a cell is allowed to recombine (1 tick = 1000 milliseconds) -// playerBotGrowEnabled: If 0, eating a cell with less than 17 mass while cell has over 625 wont gain any mass -// playerMassDecayRate: Amount of mass lost per tick (Multiplier) (1 tick = 1000 milliseconds) -// playerMinMassDecay: Minimum mass for decay to occur -// playerDisconnectTime: The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) +// playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) +// playerMaxSize: vanilla 1500 (mass = 1500*1500/100 = 22500) // playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) -playerStartMass = 10.24 -playerBotGrowEnabled = 1 -playerMaxMass = 22500 -playerMinMassEject = 32 -playerMinMassSplit = 36 +// playerRecombineTime: Base time in seconds before a cell is allowed to recombine +// playerDecayRate: Amount of size lost per second +// playerDisconnectTime: The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) +playerMinSize = 32 +playerMaxSize = 1500 playerMaxCells = 16 +playerSpeed = 1 +playerDecayRate = .002 playerRecombineTime = 30 -playerMassDecayRate = .002 -playerMinMassDecay = 10.24 playerMaxNickLength = 15 -playerSpeed = 1 playerDisconnectTime = 60 // [Gamemode] diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index c05cd6048..9c8c417a3 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -276,12 +276,13 @@ Commands.list = { return; } - if (isNaN(mass)) { - mass = gameServer.config.foodStartMass; + var size = gameServer.config.foodMinMass; + if (!isNaN(mass)) { + size = Math.sqrt(mass * 100); } // Spawn - var cell = new Entity.Food(gameServer.getNextNodeId(), null, pos, mass, gameServer); + var cell = new Entity.Food(gameServer, null, pos, size); cell.setColor(gameServer.getRandomColor()); gameServer.addNode(cell); console.log("[Console] Spawned 1 food cell at (" + pos.x + " , " + pos.y + ")"); @@ -355,16 +356,17 @@ Commands.list = { console.log("[Console] Please specify a valid number"); return; } + var size = Math.sqrt(amount * 100); // Sets mass to the specified amount for (var i in gameServer.clients) { if (gameServer.clients[i].playerTracker.pID == id) { var client = gameServer.clients[i].playerTracker; for (var j in client.cells) { - client.cells[j].setMass(amount); + client.cells[j].setSize(size); } - console.log("[Console] Set mass of " + client.name + " to " + amount); + console.log("[Console] Set mass of " + client.name + " to " + (size*size/100).toFixed(3)); break; } } @@ -588,12 +590,13 @@ Commands.list = { console.log("[Console] Invalid coordinates"); return; } - if (isNaN(mass)) { - mass = gameServer.config.virusStartMass; + var size = gameServer.config.virusMinSize; + if (!isNaN(mass)) { + size = Math.sqrt(mass * 100); } // Spawn - var v = new Entity.Virus(gameServer.getNextNodeId(), null, pos, mass, gameServer); + var v = new Entity.Virus(gameServer, null, pos, size); gameServer.addNode(v); console.log("[Console] Spawned 1 virus at (" + pos.x + " , " + pos.y + ")"); }, diff --git a/src/modules/PlayerCommand.js b/src/modules/PlayerCommand.js index 59242dc36..a0b8a0a4d 100644 --- a/src/modules/PlayerCommand.js +++ b/src/modules/PlayerCommand.js @@ -59,7 +59,7 @@ var playerCommands = { var cell = this.playerTracker.cells[0]; this.gameServer.removeNode(cell); // replace with food - var food = new Entity.Food(this.gameServer.getNextNodeId(), null, cell.position, this.gameServer.config.playerStartMass, this.gameServer); + var food = new Entity.Food(this.gameServer, null, cell.position, this.gameServer.config.playerMinSize); food.setColor(this.gameServer.getGrayColor(cell.getColor())); this.gameServer.addNode(food); } From a0bba6a063781870ae5113b1482910098a02c77b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 23 Jun 2016 18:49:11 +0200 Subject: [PATCH 225/417] add MotherCell --- src/entity/MotherCell.js | 73 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/entity/MotherCell.js diff --git a/src/entity/MotherCell.js b/src/entity/MotherCell.js new file mode 100644 index 000000000..1f11246bb --- /dev/null +++ b/src/entity/MotherCell.js @@ -0,0 +1,73 @@ +var Cell = require('./Cell'); +var Food = require('./Food'); +var Virus = require('./Virus'); + +function MotherCell() { + Cell.apply(this, Array.prototype.slice.call(arguments)); + + this.cellType = 2; + this.isSpiked = true; + this.isMotherCell = true; // Not to confuse bots + this.setColor({ r: 205, g: 85, b: 100 }); + this.motherCellMinSize = 149; // vanilla 149 (mass = 149*149/100 = 222.01) + this.motherCellSpawnAmount = 2; + if (this.getSize() == 0) { + this.setSize(this.motherCellMinSize); + } +} + +module.exports = MotherCell; +MotherCell.prototype = new Cell(); + +// Main Functions + +MotherCell.prototype.canEat = function (cell) { + return cell.cellType == 0 || // can eat player cell + cell.cellType == 3; // can eat ejected mass +}; + +MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function + +MotherCell.prototype.onUpdate = function () { + if (this.getSize() <= this.motherCellMinSize) { + return; + } + var maxFood = this.gameServer.config.foodMaxAmount * 1.5; + if (this.gameServer.currentFood >= maxFood) { + return; + } + var size1 = this.getSize(); + var size2 = this.gameServer.config.foodMinSize; + for (var i = 0; i < this.motherCellSpawnAmount; i++) { + size1 = Math.sqrt(size1 * size1 - size2 * size2); + size1 = Math.max(size1, this.motherCellMinSize); + this.setSize(size1); + + // Spawn food with size2 + var angle = Math.random() * 2 * Math.PI; + var r = this.getSize(); + var pos = { + x: this.position.x + r * Math.sin(angle), + y: this.position.y + r * Math.cos(angle) + }; + + // Spawn food + var food = new Food(this.gameServer, null, pos, size2); + food.setColor(this.gameServer.getRandomColor()); + this.gameServer.addNode(food); + + // Eject to random distance + food.setBoost(32 + 32 * Math.random(), angle); + + if (this.gameServer.currentFood >= maxFood || size1 <= this.motherCellMinSize) { + break; + } + } + this.gameServer.updateNodeQuad(this); +}; + +MotherCell.prototype.onAdd = function () { +}; + +MotherCell.prototype.onRemove = function () { +}; From 0bc0ba530de0094d463e1f44ea9e29c16cdf407c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 23 Jun 2016 18:50:58 +0200 Subject: [PATCH 226/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2a35aae2e..113494fdc 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* NOTE: there is major gameserver.ini change, previous version is incompatible! * Massive perfromance improvement & reduce network traffic * Split behavior - fixed; * Protocol code - optimized; From 7c30d51ce5ae4fa10e3995196b0873956873f2c4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 23 Jun 2016 19:55:10 +0200 Subject: [PATCH 227/417] fix MotherCell spawn issue --- src/GameServer.js | 3 +++ src/entity/Cell.js | 5 ++--- src/gamemodes/Experimental.js | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index a58036342..ebefbd743 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -379,6 +379,9 @@ GameServer.prototype.getRandomColor = function() { GameServer.prototype.updateNodeQuad = function (node) { var quadItem = node.quadItem; + if (quadItem == null) { + throw new TypeError("GameServer.updateNodeQuad: quadItem is null!"); + } // check for change if (node.position.x == quadItem.x && node.position.y == quadItem.y && diff --git a/src/entity/Cell.js b/src/entity/Cell.js index a2eca8546..ecc1ace40 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -113,9 +113,8 @@ Cell.prototype.getKiller = function () { }; Cell.prototype.setPosition = function (pos) { - if (isNaN(pos.x) || isNaN(pos.y)) { - console.log("[ERROR] Cell.setPosition: NaN"); - return; + if (pos == null || isNaN(pos.x) || isNaN(pos.y)) { + throw new TypeError("Cell.setPosition: position is NaN"); } this.position.x = pos.x; this.position.y = pos.y; diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index c663df5e2..51e58d9fb 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -39,7 +39,6 @@ Experimental.prototype.spawnMotherCell = function (gameServer) { // Spawn if no cells are colliding var mother = new Entity.MotherCell(gameServer, null, pos, null); gameServer.addNode(mother); - this.nodesMother.push(mother); }; // Override From 3acbb4aa967d54b2071888eca33f79d264d48899 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 23 Jun 2016 22:56:24 +0200 Subject: [PATCH 228/417] add scramble level 3 (anti-bot/anti-minimap) --- src/PlayerTracker.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 5ba57448b..4dc5f30cb 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -185,6 +185,13 @@ PlayerTracker.prototype.joinGame = function (name, skin) { // no scramble / lightweight scramble this.socket.sendPacket(new Packet.SetBorder(this, this.gameServer.border)); } + if (this.gameServer.config.serverScrambleCoords == 3) { + // Scramble level 3 (no border) + // Unsupported on some clients! (include vanilla) + // ogar.mivabe.nl works ok + // Ruins most known minimaps + this.socket.sendPacket(new Packet.SetBorder(this, {minx:1/0,miny:1/0,maxx:1/0,maxy:1/0})); + } this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); }; From 8d890765c4dce7c5e2009ec555ab83a0d557a0cd Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 23 Jun 2016 22:59:50 +0200 Subject: [PATCH 229/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 113494fdc..42004da35 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Added scramble level 3 (anti-bot/anti-minimap protection), unsupported on some clients (unfortunately include vanilla, ogar.mivabe.nl works ok) * NOTE: there is major gameserver.ini change, previous version is incompatible! * Massive perfromance improvement & reduce network traffic * Split behavior - fixed; From f4bdfca78e4b18039e5a1a3de0da0399f16b5703 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 01:47:01 +0200 Subject: [PATCH 230/417] fix split cell order --- src/GameServer.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index ebefbd743..69ffa421d 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1025,10 +1025,11 @@ GameServer.prototype.updateMoveEngine = function () { }; GameServer.prototype.splitCells = function(client) { - // sort by size descending - client.cells.sort(function (a, b) { - return b.getSize() - a.getSize(); - }); + // it seems that vanilla uses order by cell age + //// sort by size descending + //client.cells.sort(function (a, b) { + // return b.getSize() - a.getSize(); + //}); var cellToSplit = []; for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; From 1008930f0172a7686e6461bb83513fa987410da4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 12:09:14 +0200 Subject: [PATCH 231/417] add comments for gameserver.ini --- src/gameserver.ini | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/gameserver.ini b/src/gameserver.ini index a0c582a0f..6fb07fabd 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -1,6 +1,14 @@ -// Ogar configurations file +// MultiOgar configurations file // Lines starting with slashes are comment lines +// NOTE: MultiOgar using cell size instead of cell mass to improve performance! +// In order to set player start mass you need to calculate size for new mass. +// size = SQRT( mass * 100) +// For example, to set start mass = 43: +// size = SQRT( 43 * 100 ) = SQRT( 4300 ) = 65.57 +// Just set playerMinSize = 66 + + // [Server] // serverTimeout: Seconds to keep connection alive for non-responding client // serverIpLimit: Controls the maximum connection amount from single IP (use 0 to disable) @@ -71,6 +79,7 @@ ejectCooldown = 3 ejectSpawnPlayer = 50 // [Player] +// NOTE: MultiOgar using cell size instead of mass! // playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) // playerMaxSize: vanilla 1500 (mass = 1500*1500/100 = 22500) // playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) From eef425e941976469f92a64d1856104185ed4c715 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 12:12:02 +0200 Subject: [PATCH 232/417] add comments for gameserver.ini --- src/gameserver.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gameserver.ini b/src/gameserver.ini index 6fb07fabd..82ebf4a1a 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -80,6 +80,8 @@ ejectSpawnPlayer = 50 // [Player] // NOTE: MultiOgar using cell size instead of mass! +// playerStartMass replaced with playerMinSize +// // playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) // playerMaxSize: vanilla 1500 (mass = 1500*1500/100 = 22500) // playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) From e995e2d10171cb7cbdf527df9ecaeae796d9c2f5 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 12:24:25 +0200 Subject: [PATCH 233/417] fix player cannot split issue --- src/GameServer.js | 3 ++- src/gameserver.ini | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 69ffa421d..ad0b3cf84 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -95,6 +95,7 @@ function GameServer() { playerMinSize: 32, // Minimym size of the player cell (mass = 32*32/100 = 10.24) playerMaxSize: 1500, // Maximum size of the player cell (mass = 1500*1500/100 = 22500) + playerStartSize: 64, // Start size of the player cell (mass = 64*64/100 = 41) playerMaxCells: 16, // Max cells the player is allowed to have playerSpeed: 1, // Player speed multiplier playerDecayRate: .002, // Amount of size lost per second @@ -694,7 +695,7 @@ GameServer.prototype.spawnPlayer = function(player, pos, size) { } if (size == null) { // Get starting mass - size = this.config.playerMinSize; + size = this.config.playerStartSize; } // Spawn player and add to world diff --git a/src/gameserver.ini b/src/gameserver.ini index 82ebf4a1a..51cf918aa 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -6,7 +6,7 @@ // size = SQRT( mass * 100) // For example, to set start mass = 43: // size = SQRT( 43 * 100 ) = SQRT( 4300 ) = 65.57 -// Just set playerMinSize = 66 +// Just set playerStartSize = 66 // [Server] @@ -80,16 +80,18 @@ ejectSpawnPlayer = 50 // [Player] // NOTE: MultiOgar using cell size instead of mass! -// playerStartMass replaced with playerMinSize +// playerStartMass replaced with playerStartSize // // playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) // playerMaxSize: vanilla 1500 (mass = 1500*1500/100 = 22500) +// playerStartSize: Start size of the player cell (mass = 64*64/100 = 41) // playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) // playerRecombineTime: Base time in seconds before a cell is allowed to recombine // playerDecayRate: Amount of size lost per second // playerDisconnectTime: The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) playerMinSize = 32 playerMaxSize = 1500 +playerStartSize = 64 playerMaxCells = 16 playerSpeed = 1 playerDecayRate = .002 From 371358fa806fab0bef7cc1621214ece5f0c80c99 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 13:12:38 +0200 Subject: [PATCH 234/417] fix virus spawn amount --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index ad0b3cf84..57e46ea3e 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -636,7 +636,7 @@ GameServer.prototype.updateFood = function() { }; GameServer.prototype.updateVirus = function () { - var maxCount = this.config.virusMaxAmount - this.nodesVirus.length; + var maxCount = this.config.virusMinAmount - this.nodesVirus.length; var spawnCount = Math.min(maxCount, 2); for (var i = 0; i < spawnCount; i++) { this.spawnVirus(); From bf650a36aea196d988b6ca7251fb3c3fc1f1233c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 13:37:42 +0200 Subject: [PATCH 235/417] update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 42004da35..8df70e133 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,9 @@ IP | Location | Game Mode | Web Site --- | --- | --- | --- vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge 164.132.48.230:600 | France | FFA | http://c0nsume.me/private4.php?ip=164.132.48.230:600 +149.202.87.51:443 | Paris | FFA | +134.119.17.230:443 | Germany | FFA | +192.34.61.57:443 | New York | FFA | ## What's new: From db31f3798d2810e94e2bef4ddcc32b7a726674b4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 13:58:06 +0200 Subject: [PATCH 236/417] fix default map size in gameserver.ini --- src/gameserver.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gameserver.ini b/src/gameserver.ini index 51cf918aa..79f850c94 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -48,8 +48,8 @@ serverWelcome2 = "" // [Border] // Border size (vanilla 14142.135623730952) -borderWidth = 6000 -borderHeight = 6000 +borderWidth = 14142 +borderHeight = 14142 // [Spawn] // Each interval is 1 tick (40 ms) From 5afd81a9270e27f70445ce60941abccfbe63b704 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 14:06:44 +0200 Subject: [PATCH 237/417] update readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8df70e133..8854b0014 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,9 @@ IP | Location | Game Mode | Web Site --- | --- | --- | --- vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge 164.132.48.230:600 | France | FFA | http://c0nsume.me/private4.php?ip=164.132.48.230:600 -149.202.87.51:443 | Paris | FFA | -134.119.17.230:443 | Germany | FFA | -192.34.61.57:443 | New York | FFA | +149.202.87.51:443 | Paris | FFA | http://agarlist.com/ +134.119.17.230:443 | Germany | FFA | http://agarlist.com/ +192.34.61.57:443 | New York | FFA | http://agarlist.com/ ## What's new: From 8e535b5ac15055a0555ec8d288f68bc3c5910674 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 14:30:39 +0200 Subject: [PATCH 238/417] Update README.md --- README.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8854b0014..1ad1678e2 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,22 @@ # MultiOgar -Ogar game server with vanilla physics and multi-protocol support. +Ogar game server with fast and smooth vanilla physics and multi-protocol support. ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) [![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/Barbosik/OgarMulti/blob/master/LICENSE.md) -## [![Language](https://img.shields.io/badge/Ogar-Node-red.svg)](https://github.com/OgarProject/Ogar) Ogar -Copy of Ogar that I heavily modified, and will continue to update. +MultiOgar code based on Ogar code that I heavily modified, and will continue to update. +Almost all physics and protocol code were rewritten and optimized. The [OgarProject](https://ogarproject.com) owns Ogar, and I do not claim it as mine! Original Ogar found [here](https://github.com/OgarProject/Ogar) -The goal is to cleanup the code, fix the bugs and improve physics. +The goal is to make good and smooth physics and cleanup the code. + +## Screenshot + +Map 6000x6000, 300 bots, 5000 food, 10 viruses - works pretty smooth with no lags: +![Screenshot](http://i.imgur.com/4Wg8s9b.png) ## Clients @@ -78,4 +83,4 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge * Added support for server tracker ogar.mivabe.nl/master Most of the physics code from the original Ogar were rewritten. -The physics engine in MultiOgar is pretty close to the old vanilla physics. \ No newline at end of file +The physics engine in MultiOgar is pretty close to the old vanilla physics. From afd4e44ed7a986bef0a38b7cf72f96364d6e7469 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 17:11:26 +0200 Subject: [PATCH 239/417] code refactoring --- src/PlayerTracker.js | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 4dc5f30cb..3a65341ab 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -357,7 +357,7 @@ PlayerTracker.prototype.updateCenterFreeRoam = function () { var dx = this.mouse.x - this.centerPos.x; var dy = this.mouse.y - this.centerPos.y; var squared = dx * dx + dy * dy; - if (squared == 0) return; // stop threshold + if (squared < 1) return; // stop threshold // distance var d = Math.sqrt(squared); @@ -371,11 +371,6 @@ PlayerTracker.prototype.updateCenterFreeRoam = function () { var x = this.centerPos.x + nx * speed; var y = this.centerPos.y + ny * speed; - // check border - x = Math.max(x, this.gameServer.border.minx); - y = Math.max(y, this.gameServer.border.miny); - x = Math.min(x, this.gameServer.border.maxx); - y = Math.min(y, this.gameServer.border.maxy); this.setCenterPos(x, y); }; @@ -404,14 +399,14 @@ PlayerTracker.prototype.getVisibleNodes = function () { // top player spectate this.setCenterPos(specPlayer.centerPos.x, specPlayer.centerPos.y); this.scale = specPlayer.getScale(); - this.sendPosPacket(); + this.sendCameraPacket(); this.updateViewBox(); return specPlayer.visibleNodes.slice(0); } // free roam spectate this.updateCenterFreeRoam(); this.scale = this.gameServer.config.serverSpectatorScale;//0.25; - this.sendPosPacket(); + this.sendCameraPacket(); } else { // in game this.updateCenterInGame(); @@ -432,21 +427,18 @@ PlayerTracker.prototype.calcVisibleNodes = function() { }; PlayerTracker.prototype.setCenterPos = function(x, y) { + if (isNaN(x) || isNaN(y)) { + throw new TypeError("PlayerTracker.setCenterPos: NaN"); + } + x = Math.max(x, this.gameServer.border.minx); + y = Math.max(y, this.gameServer.border.miny); + x = Math.min(x, this.gameServer.border.maxx); + y = Math.min(y, this.gameServer.border.maxy); this.centerPos.x = x; this.centerPos.y = y; - if (this.freeRoam) this.checkBorderPass(); -}; - -PlayerTracker.prototype.checkBorderPass = function() { - // A check while in free-roam mode to avoid player going into nothingness - this.centerPos.x = Math.max(this.centerPos.x, this.gameServer.border.minx); - this.centerPos.y = Math.max(this.centerPos.y, this.gameServer.border.miny); - this.centerPos.x = Math.min(this.centerPos.x, this.gameServer.border.maxx); - this.centerPos.y = Math.min(this.centerPos.y, this.gameServer.border.maxy); }; -PlayerTracker.prototype.sendPosPacket = function() { - // TODO: Send packet elsewhere so it is sent more often +PlayerTracker.prototype.sendCameraPacket = function() { this.socket.sendPacket(new Packet.UpdatePosition( this, this.centerPos.x, From 5618702c8c08379e6639bcf359eb69993bb4ef8e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 19:22:21 +0200 Subject: [PATCH 240/417] fix mass limit behavior --- src/GameServer.js | 24 ++++++++++++++++++++++++ src/entity/PlayerCell.js | 11 ----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 57e46ea3e..e46a2689a 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -889,9 +889,11 @@ GameServer.prototype.resolveCollision = function (manifold) { }; GameServer.prototype.updateMoveEngine = function () { + var tick = this.getTick(); // Move player cells for (var i in this.clients) { var client = this.clients[i].playerTracker; + var checkSize = !client.mergeOverride || client.cells.length == 1; for (var j = 0; j < client.cells.length; j++) { var cell1 = client.cells[j]; if (cell1.isRemoved) @@ -899,6 +901,28 @@ GameServer.prototype.updateMoveEngine = function () { cell1.updateRemerge(this); cell1.moveUser(this.border); cell1.move(this.border); + + // check size limit + if (checkSize && cell1.getSize() > this.config.playerMaxSize && cell1.getAge(tick) >= 15) { + if (client.cells.length >= this.config.playerMaxCells) { + // cannot split => just limit + cell1.setSize(this.config.playerMaxSize); + } else { + // split + var maxSplit = this.config.playerMaxCells - client.cells.length; + var maxMass = this.config.playerMaxSize * this.config.playerMaxSize / 100; + var count = (cell1.getMass() / maxMass) >> 0; + var count = Math.min(count, maxSplit); + var splitSize = cell1.getSize() / Math.sqrt(count + 1); + var splitMass = splitSize * splitSize / 100; + var angle = Math.random() * 2 * Math.PI; + var step = 2 * Math.PI / count; + for (var k = 0; k < count; k++) { + this.splitPlayerCell(client, cell1, angle, splitMass); + angle += step; + } + } + } this.updateNodeQuad(cell1); } } diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index ebbfcf521..6baf533f8 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -94,17 +94,6 @@ PlayerCell.prototype.onEat = function (prey) { var size1 = this.getSize(); var size2 = prey.getSize(); this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); - - if (this.owner.mergeOverride) - return; - if (this.getSize() <= this.gameServer.config.playerMaxSize) - return; - if (this.owner.cells.length >= this.gameServer.config.playerMaxCells) { - this.setSize(this.gameServer.config.playerMaxSize); - return; - } - var randomAngle = Math.random() * 2 * Math.PI; // Get random angle??? - this.gameServer.splitPlayerCell(this.owner, this, randomAngle, null); }; PlayerCell.prototype.onAdd = function(gameServer) { From e6593824eb9502af9be607696a54c00826cdf7d3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 20:58:17 +0200 Subject: [PATCH 241/417] fix eat distance --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index e46a2689a..9b92f790c 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -825,7 +825,7 @@ GameServer.prototype.resolveCollision = function (manifold) { } // check distance - var eatDistance = maxCell.getSize() - minCell.getSize() / Math.PI; + var eatDistance = maxCell.getSize() - minCell.getSize() / 3; if (manifold.squared >= eatDistance * eatDistance) { // too far => can't eat return; From a67fef5f14de52a3be7d98e71b5c6afd76f4227d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 21:09:54 +0200 Subject: [PATCH 242/417] fix eat size --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index 9b92f790c..9a3bf949c 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -857,7 +857,7 @@ GameServer.prototype.resolveCollision = function (manifold) { } } // Size check - if (minCell.getSize() * 1.15 > maxCell.getSize()) { + if (maxCell.getSize() <= minCell.getSize() * 1.15) { // too large => can't eat return; } From bea4d2414dfb9461421bf2adf7bee8f72c55bef2 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 24 Jun 2016 15:54:39 -0400 Subject: [PATCH 243/417] Mass to Size demo ... with some grammarical corrections: amount: for things you cannot count (e.g. amount of water, amount of mass, amount of money) number: for things you can count (e.g. number of lollipop, number of cells, number of coins) --- src/GameServer.js | 24 +++++++++++++++++++----- src/gameserver.ini | 24 ++++++++++++++++-------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 9a3bf949c..9e518a928 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -78,16 +78,16 @@ function GameServer() { foodMinSize: 10, // Minimum food size (vanilla 10) foodMaxSize: 20, // Maximum food size (vanilla 20) - foodMinAmount: 100, // Minimum food cells on the map - foodMaxAmount: 500, // Maximum food cells on the map + foodMinAmount: 100, // Minimum number of food cells on the map + foodMaxAmount: 500, // Maximum number of food cells on the map foodSpawnAmount: 10, // The amount of food to spawn per interval foodMassGrow: 1, // Enable food mass grow ? spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) virusMinSize: 100, // Minimum virus size (vanilla 100) virusMaxSize: 140, // Maximum virus size (vanilla 140) - virusMinAmount: 10, // Minimum amount of viruses on the map. - virusMaxAmount: 50, // Maximum amount of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. + virusMinAmount: 10, // Minimum number of viruses on the map. + virusMaxAmount: 50, // Maximum number of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. ejectSize: 37, // Size of ejected cells (vanilla 37) ejectCooldown: 3, // min ticks between ejects @@ -1241,7 +1241,21 @@ GameServer.prototype.loadConfig = function() { // Replace all the default config's values with the loaded config's values for (var obj in load) { - this.config[obj] = load[obj]; + // Mass to size conversion + if (obj.endsWith("Mass")) { + var obj2 = obj.replace("Mass","Size"); + + // if Not a number + if(isNaN(load[obj])) continue; + // Rounding for ridding of float values + var size = Math.sqrt(load[obj]*100); + + this.config[obj2] = size; + } + // Normal replacement + else { + this.config[obj] = load[obj]; + } } } catch (err) { // No config diff --git a/src/gameserver.ini b/src/gameserver.ini index 79f850c94..a0fa2fd5a 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -55,8 +55,10 @@ borderHeight = 14142 // Each interval is 1 tick (40 ms) // foodMinSize: vanilla 10 (mass = 10*10/100 = 1) // foodMaxSize: vanilla 20 (mass = 20*20/100 = 4) -foodMinSize = 10 -foodMaxSize = 20 +//foodMinSize = 10 +foodMinMass = 1 +//foodMaxSize = 20 +foodMaxMass = 4 foodMinAmount = 100 foodMaxAmount = 500 foodSpawnAmount = 10 @@ -65,8 +67,10 @@ spawnInterval = 20 // virusMinSize: vanilla 100 (mass = 100*100/100 = 100) // virusMaxSize: vanilla 140 (mass = 140*140/100 = 196) -virusMinSize = 100 -virusMaxSize = 140 +//virusMinSize = 100 +virusMinMass = 100 +//virusMaxSize = 140 +virusMaxMass = 196 virusMinAmount = 10 virusMaxAmount = 50 @@ -74,7 +78,8 @@ virusMaxAmount = 50 // ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) // ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) // ejectSpawnPlayer: Chance for a player to spawn from ejected mass -ejectSize = 37 +//ejectSize = 37 +ejectMass = 2000 ejectCooldown = 3 ejectSpawnPlayer = 50 @@ -89,9 +94,12 @@ ejectSpawnPlayer = 50 // playerRecombineTime: Base time in seconds before a cell is allowed to recombine // playerDecayRate: Amount of size lost per second // playerDisconnectTime: The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) -playerMinSize = 32 -playerMaxSize = 1500 -playerStartSize = 64 +//playerMinSize = 32 +playerMinMass = 10.24 +//playerMaxSize = 1500 +playerMaxMass = 22500 +//playerStartSize = 64 +playerStartMass = 41 playerMaxCells = 16 playerSpeed = 1 playerDecayRate = .002 From 3b55f12a2ea33ecd9f3ad07a7ab57b6d695524b8 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 24 Jun 2016 15:59:36 -0400 Subject: [PATCH 244/417] ejectMass correction 2000 -> 13.69 --- src/gameserver.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gameserver.ini b/src/gameserver.ini index a0fa2fd5a..50bc19d8c 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -79,7 +79,7 @@ virusMaxAmount = 50 // ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) // ejectSpawnPlayer: Chance for a player to spawn from ejected mass //ejectSize = 37 -ejectMass = 2000 +ejectMass = 13.69 ejectCooldown = 3 ejectSpawnPlayer = 50 From 79beff91d8009ff0df773e12df1d4673d0f57cbd Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 24 Jun 2016 16:53:40 -0400 Subject: [PATCH 245/417] Duplicate check With more proofreading... --- src/GameServer.js | 20 +++++++++++++++++++- src/gameserver.ini | 6 +++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 9e518a928..940b9b5d9 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1240,20 +1240,38 @@ GameServer.prototype.loadConfig = function() { var load = ini.parse(fs.readFileSync('./gameserver.ini', 'utf-8')); // Replace all the default config's values with the loaded config's values + var sizeMass = []; for (var obj in load) { // Mass to size conversion if (obj.endsWith("Mass")) { var obj2 = obj.replace("Mass","Size"); + // Duplication check + if (sizeMass.indexOf(obj2) >= 0) { + console.log("[Game] Duplicate Size/Mass found:" + obj); + console.log("[Game] Using " + obj + " = " + load[obj]); + } + else + sizeMass.push(obj2); + + // if Not a number if(isNaN(load[obj])) continue; - // Rounding for ridding of float values var size = Math.sqrt(load[obj]*100); this.config[obj2] = size; } // Normal replacement else { + if (obj.endsWith("Size")) { + // Duplication check + if (sizeMass.indexOf(obj) >= 0) { + console.log("[Game] Duplicate Size/Mass found:" + obj); + console.log("[Game] Using " + obj + " = " + load[obj]); + } + else + sizeMass.push(obj); + } this.config[obj] = load[obj]; } } diff --git a/src/gameserver.ini b/src/gameserver.ini index 50bc19d8c..f3690e0e3 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -1,7 +1,7 @@ // MultiOgar configurations file -// Lines starting with slashes are comment lines +// Lines starting with slashes are comments -// NOTE: MultiOgar using cell size instead of cell mass to improve performance! +// NOTE: MultiOgar uses cell size instead of cell mass to improve performance! // In order to set player start mass you need to calculate size for new mass. // size = SQRT( mass * 100) // For example, to set start mass = 43: @@ -84,7 +84,7 @@ ejectCooldown = 3 ejectSpawnPlayer = 50 // [Player] -// NOTE: MultiOgar using cell size instead of mass! +// NOTE: MultiOgar uses cell size instead of mass! // playerStartMass replaced with playerStartSize // // playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) From 9c44d354896cd86b787c6368f72b9359401ceb9e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 24 Jun 2016 23:59:05 +0200 Subject: [PATCH 246/417] fix min split size --- src/GameServer.js | 10 +++++----- src/gameserver.ini | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 9a3bf949c..98bc8676c 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -95,6 +95,7 @@ function GameServer() { playerMinSize: 32, // Minimym size of the player cell (mass = 32*32/100 = 10.24) playerMaxSize: 1500, // Maximum size of the player cell (mass = 1500*1500/100 = 22500) + playerMinSplitSize: 60, // Minimum player cell size allowed to split (mass = 60*60/100 = 36) playerStartSize: 64, // Start size of the player cell (mass = 64*64/100 = 41) playerMaxCells: 16, // Max cells the player is allowed to have playerSpeed: 1, // Player speed multiplier @@ -1051,14 +1052,10 @@ GameServer.prototype.updateMoveEngine = function () { GameServer.prototype.splitCells = function(client) { // it seems that vanilla uses order by cell age - //// sort by size descending - //client.cells.sort(function (a, b) { - // return b.getSize() - a.getSize(); - //}); var cellToSplit = []; for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; - if (cell.getSplitSize() < this.config.playerMinSize) { + if (cell.getSize() < this.config.playerMinSplitSize) { continue; } cellToSplit.push(cell); @@ -1147,6 +1144,9 @@ GameServer.prototype.ejectMass = function(client) { continue; } + if (cell.getSize() < this.config.playerMinSplitSize) { + continue; + } var size2 = this.config.ejectSize; var sizeSquared = cell.getSquareSize() - size2 * size2; if (sizeSquared < this.config.playerMinSize * this.config.playerMinSize) { diff --git a/src/gameserver.ini b/src/gameserver.ini index 79f850c94..4b7926fb3 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -84,6 +84,7 @@ ejectSpawnPlayer = 50 // // playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) // playerMaxSize: vanilla 1500 (mass = 1500*1500/100 = 22500) +// playerMinSplitSize: vanilla 60 (mass = 60*60/100 = 36) // playerStartSize: Start size of the player cell (mass = 64*64/100 = 41) // playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) // playerRecombineTime: Base time in seconds before a cell is allowed to recombine @@ -91,6 +92,7 @@ ejectSpawnPlayer = 50 // playerDisconnectTime: The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) playerMinSize = 32 playerMaxSize = 1500 +playerMinSplitSize = 60 playerStartSize = 64 playerMaxCells = 16 playerSpeed = 1 From 42ae618168f93096fd6e07d2bef0d0c773f411be Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 00:03:48 +0200 Subject: [PATCH 247/417] fix min split size --- src/gamemodes/TeamZ.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 3ad8360be..e8b069be8 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -490,7 +490,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { continue; } - if (cell.getSplitSize() < this.config.playerMinSize) { + if (cell.getSize() < this.config.playerMinSplitSize) { continue; } From 69b0e37d9dcf8768635759fee2951e54cc43aacf Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 24 Jun 2016 18:50:13 -0400 Subject: [PATCH 248/417] Rollback bea4d2414dfb9461421bf2adf7bee8f72c55bef2 but keep the grammarical corrections... --- src/GameServer.js | 61 +++++++++++-------------------------------- src/Start-windows.bat | 2 +- src/gameserver.ini | 33 +++++++++-------------- 3 files changed, 29 insertions(+), 67 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 940b9b5d9..c5f412ed6 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -54,17 +54,17 @@ function GameServer() { // Config this.config = { serverTimeout: 30, // Seconds to keep connection alive for non-responding client - serverMaxConnections: 64, // Maximum amount of connections to the server. (0 for no limit) - serverIpLimit: 4, // Maximum amount of connections from the same IP (0 for no limit) + serverMaxConnections: 64, // Maximum number of connections to the server. (0 for no limit) + serverIpLimit: 4, // Maximum number of connections from the same IP (0 for no limit) serverPort: 443, // Server port serverTracker: 0, // Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams - serverBots: 0, // Amount of player bots to spawn + serverBots: 0, // Number of player bots to spawn serverViewBaseX: 1920, // Base client screen resolution. Used to calculate view area. Warning: high values may cause lag serverViewBaseY: 1080, // min value is 1920x1080 serverSpectatorScale: 0.4, // Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. - serverStatsUpdate: 60, // Amount of seconds per update for the server stats + serverStatsUpdate: 60, // Update interval of server stats in seconds serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. @@ -78,16 +78,16 @@ function GameServer() { foodMinSize: 10, // Minimum food size (vanilla 10) foodMaxSize: 20, // Maximum food size (vanilla 20) - foodMinAmount: 100, // Minimum number of food cells on the map - foodMaxAmount: 500, // Maximum number of food cells on the map - foodSpawnAmount: 10, // The amount of food to spawn per interval + foodMinAmount: 100, // Minimum food cells on the map + foodMaxAmount: 500, // Maximum food cells on the map + foodSpawnAmount: 10, // The number of food to spawn per interval foodMassGrow: 1, // Enable food mass grow ? spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) virusMinSize: 100, // Minimum virus size (vanilla 100) virusMaxSize: 140, // Maximum virus size (vanilla 140) virusMinAmount: 10, // Minimum number of viruses on the map. - virusMaxAmount: 50, // Maximum number of viruses on the map. If this amount is reached, then ejected cells will pass through viruses. + virusMaxAmount: 50, // Maximum number of viruses on the map. If this number is reached, then ejected cells will pass through viruses. ejectSize: 37, // Size of ejected cells (vanilla 37) ejectCooldown: 3, // min ticks between ejects @@ -98,14 +98,14 @@ function GameServer() { playerStartSize: 64, // Start size of the player cell (mass = 64*64/100 = 41) playerMaxCells: 16, // Max cells the player is allowed to have playerSpeed: 1, // Player speed multiplier - playerDecayRate: .002, // Amount of size lost per second - playerRecombineTime: 30, // Base amount of seconds before a cell is allowed to recombine + playerDecayRate: .002, // Amount of player cell size lost per second + playerRecombineTime: 30, // Base time in seconds before a cell is allowed to recombine playerMaxNickLength: 15, // Maximum nick length - playerDisconnectTime: 60, // The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) + playerDisconnectTime: 60, // The time in seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) - tourneyMaxPlayers: 12, // Maximum amount of participants for tournament style game modes - tourneyPrepTime: 10, // Amount of ticks to wait after all players are ready (1 tick = 1000 ms) - tourneyEndTime: 30, // Amount of ticks to wait after a player wins (1 tick = 1000 ms) + tourneyMaxPlayers: 12, // Maximum number of participants for tournament style game modes + tourneyPrepTime: 10, // Number of ticks to wait after all players are ready (1 tick = 1000 ms) + tourneyEndTime: 30, // Number of ticks to wait after a player wins (1 tick = 1000 ms) tourneyTimeLimit: 20, // Time limit of the game, in minutes. tourneyAutoFill: 0, // If set to a value higher than 0, the tournament match will automatically fill up with bots after this amount of seconds tourneyAutoFillPlayers: 1, // The timer for filling the server with bots will not count down unless there is this amount of real players @@ -1242,38 +1242,7 @@ GameServer.prototype.loadConfig = function() { // Replace all the default config's values with the loaded config's values var sizeMass = []; for (var obj in load) { - // Mass to size conversion - if (obj.endsWith("Mass")) { - var obj2 = obj.replace("Mass","Size"); - - // Duplication check - if (sizeMass.indexOf(obj2) >= 0) { - console.log("[Game] Duplicate Size/Mass found:" + obj); - console.log("[Game] Using " + obj + " = " + load[obj]); - } - else - sizeMass.push(obj2); - - - // if Not a number - if(isNaN(load[obj])) continue; - var size = Math.sqrt(load[obj]*100); - - this.config[obj2] = size; - } - // Normal replacement - else { - if (obj.endsWith("Size")) { - // Duplication check - if (sizeMass.indexOf(obj) >= 0) { - console.log("[Game] Duplicate Size/Mass found:" + obj); - console.log("[Game] Using " + obj + " = " + load[obj]); - } - else - sizeMass.push(obj); - } - this.config[obj] = load[obj]; - } + this.config[obj] = load[obj]; } } catch (err) { // No config diff --git a/src/Start-windows.bat b/src/Start-windows.bat index e8059e059..42946183f 100644 --- a/src/Start-windows.bat +++ b/src/Start-windows.bat @@ -1,2 +1,2 @@ -node index.js +node --debug index.js pause \ No newline at end of file diff --git a/src/gameserver.ini b/src/gameserver.ini index f3690e0e3..fc36656eb 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -11,14 +11,14 @@ // [Server] // serverTimeout: Seconds to keep connection alive for non-responding client -// serverIpLimit: Controls the maximum connection amount from single IP (use 0 to disable) +// serverIpLimit: Controls the maximum connections from single IP (use 0 to disable) // serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) // serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games -// serverBots: Amount of player bots to spawn (Experimental) +// serverBots: Number of player bots to spawn (Experimental) // serverViewBase: Base view distance of players. Warning: high values may cause lag! Min value is 1920x1080 // serverSpectatorScale: Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) // serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. -// serverStatsUpdate: Amount of seconds per update for server stats +// serverStatsUpdate: Update interval of server stats in seconds // serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections // serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) // serverMaxLB: Controls the maximum players displayed on the leaderboard. @@ -55,10 +55,8 @@ borderHeight = 14142 // Each interval is 1 tick (40 ms) // foodMinSize: vanilla 10 (mass = 10*10/100 = 1) // foodMaxSize: vanilla 20 (mass = 20*20/100 = 4) -//foodMinSize = 10 -foodMinMass = 1 -//foodMaxSize = 20 -foodMaxMass = 4 +foodMinSize = 10 +foodMaxSize = 20 foodMinAmount = 100 foodMaxAmount = 500 foodSpawnAmount = 10 @@ -67,10 +65,8 @@ spawnInterval = 20 // virusMinSize: vanilla 100 (mass = 100*100/100 = 100) // virusMaxSize: vanilla 140 (mass = 140*140/100 = 196) -//virusMinSize = 100 -virusMinMass = 100 -//virusMaxSize = 140 -virusMaxMass = 196 +virusMinSize = 100 +virusMaxSize = 140 virusMinAmount = 10 virusMaxAmount = 50 @@ -78,8 +74,7 @@ virusMaxAmount = 50 // ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) // ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) // ejectSpawnPlayer: Chance for a player to spawn from ejected mass -//ejectSize = 37 -ejectMass = 13.69 +ejectSize = 37 ejectCooldown = 3 ejectSpawnPlayer = 50 @@ -93,13 +88,11 @@ ejectSpawnPlayer = 50 // playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) // playerRecombineTime: Base time in seconds before a cell is allowed to recombine // playerDecayRate: Amount of size lost per second -// playerDisconnectTime: The amount of seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) -//playerMinSize = 32 -playerMinMass = 10.24 -//playerMaxSize = 1500 -playerMaxMass = 22500 -//playerStartSize = 64 -playerStartMass = 41 +// playerDisconnectTime: Seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) +// playerDisconnectTime: Time in seconds before a disconnected player's cell is removed (Set to -1 to never remove) +playerMinSize = 32 +playerMaxSize = 1500 +playerStartSize = 64 playerMaxCells = 16 playerSpeed = 1 playerDecayRate = .002 From a92273cca9f5b5694940b5eff325c6557da9575d Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 24 Jun 2016 18:56:45 -0400 Subject: [PATCH 249/417] Cleanup Oops --- src/Start-windows.bat | 2 +- src/gameserver.ini | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Start-windows.bat b/src/Start-windows.bat index 42946183f..e8059e059 100644 --- a/src/Start-windows.bat +++ b/src/Start-windows.bat @@ -1,2 +1,2 @@ -node --debug index.js +node index.js pause \ No newline at end of file diff --git a/src/gameserver.ini b/src/gameserver.ini index 2ab1756ec..39cc17b5e 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -89,7 +89,6 @@ ejectSpawnPlayer = 50 // playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) // playerRecombineTime: Base time in seconds before a cell is allowed to recombine // playerDecayRate: Amount of size lost per second -// playerDisconnectTime: Seconds it takes for a player cell to be removed after disconnection (If set to -1, cells are never removed) // playerDisconnectTime: Time in seconds before a disconnected player's cell is removed (Set to -1 to never remove) playerMinSize = 32 playerMaxSize = 1500 From 2314f6bf99d1eb491f99b0b4129da8cdc7b1a20a Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 24 Jun 2016 19:15:50 -0400 Subject: [PATCH 250/417] Use proper .ini commenting format Proper way to comment is by using # or ; at the beginning of the line. Also, removed invalid comment checking. Useful for people using editors that parse code with colors --- src/gameserver.ini | 108 ++++++++++++++++++++++----------------------- src/modules/ini.js | 11 ++--- 2 files changed, 57 insertions(+), 62 deletions(-) diff --git a/src/gameserver.ini b/src/gameserver.ini index 39cc17b5e..6b129e10a 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -1,31 +1,31 @@ -// MultiOgar configurations file -// Lines starting with slashes are comments +# MultiOgar configurations file +# Lines starting with slashes are comments -// NOTE: MultiOgar uses cell size instead of cell mass to improve performance! -// In order to set player start mass you need to calculate size for new mass. -// size = SQRT( mass * 100) -// For example, to set start mass = 43: -// size = SQRT( 43 * 100 ) = SQRT( 4300 ) = 65.57 -// Just set playerStartSize = 66 +# NOTE: MultiOgar uses cell size instead of cell mass to improve performance! +# In order to set player start mass you need to calculate size for new mass. +# size = SQRT( mass * 100) +# For example, to set start mass = 43: +# size = SQRT( 43 * 100 ) = SQRT( 4300 ) = 65.57 +# Just set playerStartSize = 66 -// [Server] -// serverTimeout: Seconds to keep connection alive for non-responding client -// serverIpLimit: Controls the maximum connections from single IP (use 0 to disable) -// serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) -// serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games -// serverBots: Number of player bots to spawn (Experimental) -// serverViewBase: Base view distance of players. Warning: high values may cause lag! Min value is 1920x1080 -// serverSpectatorScale: Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) -// serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. -// serverStatsUpdate: Update interval of server stats in seconds -// serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections -// serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) -// serverMaxLB: Controls the maximum players displayed on the leaderboard. -// serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. -// serverName: Server name, for example "My great server" -// serverWelcome1: First server welcome message -// serverWelcome2: Second server welcome message (optional, for info, etc) +# [Server] +# serverTimeout: Seconds to keep connection alive for non-responding client +# serverIpLimit: Controls the maximum connections from single IP (use 0 to disable) +# serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) +# serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games +# serverBots: Number of player bots to spawn (Experimental) +# serverViewBase: Base view distance of players. Warning: high values may cause lag! Min value is 1920x1080 +# serverSpectatorScale: Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) +# serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. +# serverStatsUpdate: Update interval of server stats in seconds +# serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections +# serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) +# serverMaxLB: Controls the maximum players displayed on the leaderboard. +# serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. +# serverName: Server name, for example "My great server" +# serverWelcome1: First server welcome message +# serverWelcome2: Second server welcome message (optional, for info, etc) serverTimeout = 30 serverMaxConnections = 64 serverIpLimit = 4 @@ -46,15 +46,15 @@ serverName = "MultiOgar #1" serverWelcome1 = "Welcome to MultiOgar server!" serverWelcome2 = "" -// [Border] -// Border size (vanilla 14142.135623730952) +# [Border] +# Border size (vanilla 14142.135623730952) borderWidth = 14142 borderHeight = 14142 -// [Spawn] -// Each interval is 1 tick (40 ms) -// foodMinSize: vanilla 10 (mass = 10*10/100 = 1) -// foodMaxSize: vanilla 20 (mass = 20*20/100 = 4) +# [Spawn] +# Each interval is 1 tick (40 ms) +# foodMinSize: vanilla 10 (mass = 10*10/100 = 1) +# foodMaxSize: vanilla 20 (mass = 20*20/100 = 4) foodMinSize = 10 foodMaxSize = 20 foodMinAmount = 100 @@ -63,33 +63,33 @@ foodSpawnAmount = 10 foodMassGrow = 1 spawnInterval = 20 -// virusMinSize: vanilla 100 (mass = 100*100/100 = 100) -// virusMaxSize: vanilla 140 (mass = 140*140/100 = 196) +# virusMinSize: vanilla 100 (mass = 100*100/100 = 100) +# virusMaxSize: vanilla 140 (mass = 140*140/100 = 196) virusMinSize = 100 virusMaxSize = 140 virusMinAmount = 10 virusMaxAmount = 50 -// [Ejected Mass] -// ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) -// ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) -// ejectSpawnPlayer: Chance for a player to spawn from ejected mass +# [Ejected Mass] +# ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) +# ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) +# ejectSpawnPlayer: Chance for a player to spawn from ejected mass ejectSize = 37 ejectCooldown = 3 ejectSpawnPlayer = 50 -// [Player] -// NOTE: MultiOgar uses cell size instead of mass! -// playerStartMass replaced with playerStartSize -// -// playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) -// playerMaxSize: vanilla 1500 (mass = 1500*1500/100 = 22500) -// playerMinSplitSize: vanilla 60 (mass = 60*60/100 = 36) -// playerStartSize: Start size of the player cell (mass = 64*64/100 = 41) -// playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) -// playerRecombineTime: Base time in seconds before a cell is allowed to recombine -// playerDecayRate: Amount of size lost per second -// playerDisconnectTime: Time in seconds before a disconnected player's cell is removed (Set to -1 to never remove) +# [Player] +# NOTE: MultiOgar uses cell size instead of mass! +# playerStartMass replaced with playerStartSize +# +# playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) +# playerMaxSize: vanilla 1500 (mass = 1500*1500/100 = 22500) +# playerMinSplitSize: vanilla 60 (mass = 60*60/100 = 36) +# playerStartSize: Start size of the player cell (mass = 64*64/100 = 41) +# playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) +# playerRecombineTime: Base time in seconds before a cell is allowed to recombine +# playerDecayRate: Amount of size lost per second +# playerDisconnectTime: Time in seconds before a disconnected player's cell is removed (Set to -1 to never remove) playerMinSize = 32 playerMaxSize = 1500 playerMinSplitSize = 60 @@ -101,11 +101,11 @@ playerRecombineTime = 30 playerMaxNickLength = 15 playerDisconnectTime = 60 -// [Gamemode] -// Custom gamemode settings -// tourneyTimeLimit: Time limit of the game, in minutes. -// tourneyAutoFill: If set to a value higher than 0, the tournament match will automatically fill up with bots after value seconds -// tourneyAutoFillPlayers: The timer for filling the server with bots will not count down unless there is this amount of real players +# [Gamemode] +# Custom gamemode settings +# tourneyTimeLimit: Time limit of the game, in minutes. +# tourneyAutoFill: If set to a value higher than 0, the tournament match will automatically fill up with bots after value seconds +# tourneyAutoFillPlayers: The timer for filling the server with bots will not count down unless there is this amount of real players tourneyMaxPlayers = 12 tourneyPrepTime = 10 tourneyEndTime = 30 diff --git a/src/modules/ini.js b/src/modules/ini.js index c7375ecd5..5bf27024d 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -76,15 +76,10 @@ function decode(str) { lines.forEach(function(line, _, __) { var testLine = line.trim(); - if (testLine.length == 0) { - // skip empty lines - return; - } - if (testLine.length >= 2 && testLine[0] == '/' && testLine[1] == '/') { - // skip commented lines - return; - } + + // skip empty lines or commented lines if (!line || line.match(/^\s*[;#]/)) { + // skip commented lines return; } From 72fbb89fef0b950878c5c1559118db55485931d7 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 24 Jun 2016 22:34:06 -0400 Subject: [PATCH 251/417] Slashes -> number sign --- src/gameserver.ini | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/gameserver.ini b/src/gameserver.ini index 6b129e10a..99577f521 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -1,12 +1,14 @@ # MultiOgar configurations file -# Lines starting with slashes are comments +# Lines starting with number sign (#) are comments -# NOTE: MultiOgar uses cell size instead of cell mass to improve performance! -# In order to set player start mass you need to calculate size for new mass. -# size = SQRT( mass * 100) +# [NOTE] +# MultiOgar uses cell size instead of cell mass to improve performance! +# In order to get the cell size from mass value, you need to calculate using this formula: +# size = SQRT( mass * 100 ) +# # For example, to set start mass = 43: # size = SQRT( 43 * 100 ) = SQRT( 4300 ) = 65.57 -# Just set playerStartSize = 66 +# Set playerStartSize = 66 # [Server] @@ -79,7 +81,7 @@ ejectCooldown = 3 ejectSpawnPlayer = 50 # [Player] -# NOTE: MultiOgar uses cell size instead of mass! +# Reminder: MultiOgar uses cell size instead of mass! # playerStartMass replaced with playerStartSize # # playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) From 166eddfb3d4e7cb93cc12ec2196fb0c49a2fb0b5 Mon Sep 17 00:00:00 2001 From: SimonOrJ Date: Fri, 24 Jun 2016 23:11:23 -0400 Subject: [PATCH 252/417] Add "massToSize" https://github.com/Barbosik/MultiOgar/issues/74 --- src/modules/ini.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/modules/ini.js b/src/modules/ini.js index 5bf27024d..d932f5665 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -82,7 +82,8 @@ function decode(str) { // skip commented lines return; } - + // E.g. serverTimeout = 30 + // Returns ["serverTimeout = 30", undefined, "serverTimeout ", "= 30", "30"] var match = line.match(re); if (!match) { @@ -108,6 +109,13 @@ function decode(str) { } } + // Mass to Size function catcher + if (value.startsWith("massToSize(") && value.endsWith(")")) { + // 11: length of "massToSize(" + value = Math.sqrt(parseFloat(value.slice(11, value.length - 1)) * 100); + + } + // safeguard against resetting a previously defined // array by accidentally forgetting the brackets if (isNaN(value)) { From 2c97f67ddfdab2d109c9f7190360ec1ebadd49d1 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 09:02:50 +0200 Subject: [PATCH 253/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 60abdebf0..af6aa24ef 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Just replace `127.0.0.1:50000` in the url to the server IP and port to play. URL | Protocol | Description --- | --- | --- -http://agar.io/?ip=127.0.0.1:50000 | 8 | Official Client +http://agar.io/?ip=127.0.0.1:50000 | 8 | Vanilla http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, pretty smooth, custom graphics (anime) http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends invalid protocol=1) http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style From 782043d050f9307ddf3322be8a76202ae69df9b4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 15:38:13 +0200 Subject: [PATCH 254/417] add logger; fix loadConfig --- src/GameServer.js | 90 +++++++++-------- src/entity/Virus.js | 3 +- src/gamemodes/Experimental.js | 3 +- src/index.js | 8 +- src/modules/CommandList.js | 183 +++++++++++++++++----------------- src/modules/Logger.js | 144 ++++++++++++++++++++++++++ src/modules/log.js | 77 -------------- 7 files changed, 291 insertions(+), 217 deletions(-) create mode 100644 src/modules/Logger.js delete mode 100644 src/modules/log.js diff --git a/src/GameServer.js b/src/GameServer.js index 33a9f5291..a9d59e599 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -14,7 +14,7 @@ var PacketHandler = require('./PacketHandler'); var Entity = require('./entity'); var Gamemode = require('./gamemodes'); var BotLoader = require('./ai/BotLoader'); -var Logger = require('./modules/log'); +var Logger = require('./modules/Logger'); // GameServer implementation function GameServer() { @@ -35,7 +35,6 @@ function GameServer() { this.leaderboardType = -1; // no type this.bots = new BotLoader(this); - this.log = new Logger(); this.commands; // Command handler // Main loop tick @@ -131,9 +130,6 @@ GameServer.prototype.start = function() { this.timerLoopBind = this.timerLoop.bind(this); this.mainLoopBind = this.mainLoop.bind(this); - // Logging - this.log.setup(this); - // Gamemode configurations this.gameMode.onServerInit(this); @@ -153,13 +149,13 @@ GameServer.prototype.start = function() { GameServer.prototype.onServerSocketError = function (error) { switch (error.code) { case "EADDRINUSE": - console.log("[Error] Server could not bind to port " + this.config.serverPort + "! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); + Logger.error("Server could not bind to port " + this.config.serverPort + "! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); break; case "EACCES": - console.log("[Error] Please make sure you are running Ogar with root privileges."); + Logger.error("Please make sure you are running Ogar with root privileges."); break; default: - console.log("[Error] Unhandled error code: " + error.code); + Logger.error(error.code + ": " + error.message); break; } process.exit(1); // Exits the program @@ -173,15 +169,15 @@ GameServer.prototype.onServerSocketOpen = function () { setTimeout(this.timerLoopBind, 1); // Done - console.log("[Game] Listening on port " + this.config.serverPort); - console.log("[Game] Current game mode is " + this.gameMode.name); + Logger.info("Listening on port " + this.config.serverPort); + Logger.info("Current game mode is " + this.gameMode.name); // Player bots (Experimental) if (this.config.serverBots > 0) { for (var i = 0; i < this.config.serverBots; i++) { this.bots.addBot(); } - console.log("[Game] Loaded " + this.config.serverBots + " player bots"); + Logger.info("Added " + this.config.serverBots + " player bots"); } }; @@ -216,7 +212,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { ws.remoteAddress = ws._socket.remoteAddress; ws.remotePort = ws._socket.remotePort; ws.lastAliveTime = +new Date; - this.log.onConnect(ws.remoteAddress); // Log connections + Logger.write("CONNECTED " + ws.remoteAddress + ":" + ws.remotePort + ", origin: \"" + ws.upgradeReq.headers.origin + "\""); ws.playerTracker = new PlayerTracker(this, ws); ws.packetHandler = new PacketHandler(this, ws); @@ -239,12 +235,11 @@ GameServer.prototype.onClientSocketOpen = function (ws) { }; GameServer.prototype.onClientSocketClose = function (ws, code) { - this.log.onDisconnect(ws.remoteAddress); - ws.isConnected = false; ws.sendPacket = function (data) { }; ws.closeReason = { code: ws._closeCode, message: ws._closeMessage }; ws.closeTime = +new Date; + Logger.write("DISCONNECTED " + ws.remoteAddress + ":" + ws.remotePort + ", code: " + ws._closeCode + ", reason: \"" + ws._closeMessage + "\", name: \""+ws.playerTracker.getName()+"\""); var color = this.getGrayColor(ws.playerTracker.getColor()); ws.playerTracker.setColor(color); @@ -524,7 +519,7 @@ GameServer.prototype.onChatMessage = function (from, to, message) { return; } if (message.length > 128) message = message.slice(0, 128); - //console.log("[CHAT] " + (from!=null && from.getName().length>0 ? from.getName() : "Spectator") + ": " + message); + //Logger.debug("[CHAT] " + (from!=null && from.getName().length>0 ? from.getName() : "Spectator") + ": " + message); this.sendChatMessage(from, to, message); }; @@ -1234,22 +1229,29 @@ GameServer.prototype.updateMassDecay = function() { } }; -GameServer.prototype.loadConfig = function() { - try { - // Load the contents of the config file - var load = ini.parse(fs.readFileSync('./gameserver.ini', 'utf-8')); +var configFileName = './gameserver.ini'; - // Replace all the default config's values with the loaded config's values - var sizeMass = []; - for (var obj in load) { - this.config[obj] = load[obj]; +GameServer.prototype.loadConfig = function () { + try { + if (!fs.existsSync(configFileName)) { + // No config + Logger.warn("Config not found... Generating new config"); + // Create a new config + fs.writeFileSync(configFileName, ini.stringify(this.config), 'utf-8'); + } else { + // Load the contents of the config file + var load = ini.parse(fs.readFileSync(configFileName, 'utf-8')); + // Replace all the default config's values with the loaded config's values + for (var key in load) { + if (this.config.hasOwnProperty(key)) { + this.config[key] = load[key]; + } else { + Logger.error("Unknown gameserver.ini value: " + key); + } + } } } catch (err) { - // No config - console.log("[Game] Config not found... Generating new config"); - - // Create a new config - fs.writeFileSync('./gameserver.ini', ini.stringify(this.config)); + Logger.error("Failed to load " + configFileName + ": " + err.message); } // check config (min player size = 32 => mass = 10.24) this.config.playerMinSize = Math.max(32, this.config.playerMinSize); @@ -1263,12 +1265,12 @@ GameServer.prototype.loadIpBanList = function () { this.ipBanList = fs.readFileSync(fileName, "utf8").split(/[\r\n]+/).filter(function (x) { return x != ''; // filter empty lines }); - console.log("[Game] " + this.ipBanList.length + " IP ban records loaded."); + Logger.info(this.ipBanList.length + " IP ban records loaded."); } else { - console.log("[Game] " + fileName + " is missing."); + Logger.warn(fileName + " is missing."); } } catch (err) { - console.log("[Game] Failed to load " + fileName + ": " + err.message); + Logger.error("Failed to load " + fileName + ": " + err.message); } }; @@ -1281,19 +1283,19 @@ GameServer.prototype.saveIpBanList = function () { blFile.write(v + '\n'); }); blFile.end(); - console.log("[Game] " + this.ipBanList.length + " IP ban records saved."); + Logger.info(this.ipBanList.length + " IP ban records saved."); } catch (err) { - console.log("[Game] Failed to save " + fileName + ": " + err.message); + Logger.error("Failed to save " + fileName + ": " + err.message); } }; GameServer.prototype.banIp = function (ip) { if (this.ipBanList.indexOf(ip) >= 0) { - console.log("[Game] " + ip + " is already in the ban list!"); + Logger.warn(ip + " is already in the ban list!"); return; } this.ipBanList.push(ip); - console.log("[Game] The IP " + ip + " has been banned"); + Logger.info("The IP " + ip + " has been banned"); this.clients.forEach(function (socket) { // If already disconnected or the ip does not match if (socket == null || !socket.isConnected || socket.remoteAddress != ip) @@ -1307,7 +1309,7 @@ GameServer.prototype.banIp = function (ip) { // disconnect socket.close(1000, "Banned from server"); var name = socket.playerTracker.getFriendlyName(); - console.log("[Game] Banned: \"" + name + "\" with Player ID " + socket.playerTracker.pID); // Redacted "with IP #.#.#.#" since it'll already be logged above + Logger.info("Banned: \"" + name + "\" with Player ID " + socket.playerTracker.pID); // Redacted "with IP #.#.#.#" since it'll already be logged above this.sendChatMessage(null, null, "Banned \"" + name + "\""); // notify to don't confuse with server bug }, this); this.saveIpBanList(); @@ -1316,11 +1318,11 @@ GameServer.prototype.banIp = function (ip) { GameServer.prototype.unbanIp = function (ip) { var index = this.ipBanList.indexOf(ip); if (index < 0) { - console.log("[Game] IP " + ip + " is not in the ban list!"); + Logger.warn("IP " + ip + " is not in the ban list!"); return; } this.ipBanList.splice(index, 1); - console.log("[Game] Unbanned IP: " + ip); + Logger.info("Unbanned IP: " + ip); this.saveIpBanList(); }; @@ -1339,16 +1341,16 @@ GameServer.prototype.kickId = function (id) { // disconnect socket.close(1000, "Kicked from server"); var name = socket.playerTracker.getFriendlyName(); - console.log("[Game] Kicked \"" + name + "\""); + Logger.info("Kicked \"" + name + "\""); this.sendChatMessage(null, null, "Kicked \"" + name + "\""); // notify to don't confuse with server bug count++; }, this); if (count > 0) return; if (id == 0) - console.log("[Game] No players to kick!"); + Logger.warn("No players to kick!"); else - console.log("[Game] Player with ID "+id+" not found!"); + Logger.warn("Player with ID "+id+" not found!"); }; // Stats server @@ -1374,7 +1376,7 @@ GameServer.prototype.startStatsServer = function(port) { // TODO: This causes error if something else already uses this port. Catch the error. this.httpServer.listen(port, function () { // Stats server - console.log("[Game] Loaded stats server on port " + port); + Logger.info("Started stats server on port " + port); setInterval(getStatsBind, this.config.serverStatsUpdate * 1000); }.bind(this)); }; @@ -1471,11 +1473,11 @@ GameServer.prototype.pingServerTracker = function () { }; var req = http.request(options, function (res) { if (res.statusCode != 200) { - console.log("\u001B[1m\u001B[31m[Tracker Error] " + res.statusCode + "\u001B[0m"); + Logger.error("Tracker Error: " + res.statusCode); } }); req.on('error', function (e) { - console.log("\u001B[1m\u001B[31m[Tracker Error] " + e.message + "\u001B[0m"); + Logger.error("Tracker Error: " + e.message); }); req.write(data); req.end() diff --git a/src/entity/Virus.js b/src/entity/Virus.js index b94aa124d..70840973a 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -1,4 +1,5 @@ var Cell = require('./Cell'); +var Logger = require('../modules/Logger'); function Virus() { Cell.apply(this, Array.prototype.slice.call(arguments)); @@ -91,6 +92,6 @@ Virus.prototype.onRemove = function(gameServer) { if (index != -1) { gameServer.nodesVirus.splice(index, 1); } else { - console.log("[Warning] Tried to remove a non existing virus!"); + Logger.error("Virus.onRemove: Tried to remove a non existing virus!"); } }; diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 51e58d9fb..7567cb92f 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -1,5 +1,6 @@ var FFA = require('./FFA'); // Base gamemode var Entity = require('../entity'); +var Logger = require('../modules/Logger'); function Experimental() { FFA.apply(this, Array.prototype.slice.call(arguments)); @@ -70,7 +71,7 @@ Experimental.prototype.onServerInit = function(gameServer) { if (index != -1) { self.nodesMother.splice(index, 1); } else { - console.log("[Warning] Tried to remove a non existing MotherCell!"); + Logger.error("Experimental.onServerInit.MotherVirus.onRemove: Tried to remove a non existing virus!"); } }; //gameServer.getRandomSpawn = gameServer.getRandomPosition; diff --git a/src/index.js b/src/index.js index 3291b04bb..979b8b003 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ // Imports +var Logger = require('./modules/Logger'); var Commands = require('./modules/CommandList'); var GameServer = require('./GameServer'); @@ -6,7 +7,8 @@ var GameServer = require('./GameServer'); var showConsole = true; // Start msg -console.log("[Game] MultiOgar - An open source multi-protocol ogar server"); +Logger.start(); +Logger.info("\u001B[1m\u001B[32mMultiOgar\u001B[37m - An open source multi-protocol ogar server\u001B[0m"); // Handle arguments process.argv.forEach(function(val) { @@ -49,7 +51,7 @@ function prompt() { function parseCommands(str) { // Log the string - gameServer.log.onCommand(str); + Logger.write(">" + str); // Don't process ENTER if (str === '') @@ -66,6 +68,6 @@ function parseCommands(str) { if (typeof execute != 'undefined') { execute(gameServer, split); } else { - console.log("[Console] Invalid Command!"); + Logger.warn("Invalid Command!"); } } diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 9c8c417a3..09618bc3b 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -2,6 +2,7 @@ var GameMode = require('../gamemodes'); var Entity = require('../entity'); var ini = require('./ini.js'); +var Logger = require('./Logger'); function Commands() { this.list = {}; // Empty @@ -26,36 +27,36 @@ var fillChar = function(data, char, fieldLength, rTL) { Commands.list = { help: function(gameServer, split) { - console.log("[Console] ======================== HELP ======================"); - console.log("[Console] addbot [number] : add bot to the server"); - console.log("[Console] kickbot [number] : kick a number of bots"); - console.log("[Console] ban [PlayerID | IP] : bans a(n) (player's) IP"); - console.log("[Console] banlist : get list of banned IPs."); - console.log("[Console] board [string] [string] ... : set scoreboard text"); - console.log("[Console] boardreset : reset scoreboard text"); - console.log("[Console] change [setting] [value] : change specified settings"); - console.log("[Console] clear : clear console output"); - console.log("[Console] color [PlayerID] [R] [G] [B] : set cell(s) color by client ID"); - console.log("[Console] exit : stop the server"); - console.log("[Console] food [X] [Y] [mass] : spawn food at specified Location"); - console.log("[Console] gamemode [id] : change server gamemode"); - console.log("[Console] kick [PlayerID] : kick player or bot by client ID"); - console.log("[Console] kickall : kick all players and bots"); - console.log("[Console] kill [PlayerID] : kill cell(s) by client ID"); - console.log("[Console] killall : kill everyone"); - console.log("[Console] mass [PlayerID] [mass] : set cell(s) mass by client ID"); - console.log("[Console] merge [PlayerID] : merge all client's cells once"); - console.log("[Console] name [PlayerID] [name] : change cell(s) name by client ID"); - console.log("[Console] playerlist : get list of players and bots"); - console.log("[Console] pause : pause game , freeze all cells"); - console.log("[Console] reload : reload config"); - console.log("[Console] status : get server status"); - console.log("[Console] tp [PlayerID] [X] [Y] : teleport player to specified location"); - console.log("[Console] unban [IP] : unban an IP"); - console.log("[Console] virus [X] [Y] [mass] : spawn virus at a specified Location"); - console.log("[Console] pl : alias for playerlist"); - console.log("[Console] st : alias for status"); - console.log("[Console] ===================================================="); + console.log("======================== HELP ======================"); + console.log("addbot [number] : add bot to the server"); + console.log("kickbot [number] : kick a number of bots"); + console.log("ban [PlayerID | IP] : bans a(n) (player's) IP"); + console.log("banlist : get list of banned IPs."); + console.log("board [string] [string] ... : set scoreboard text"); + console.log("boardreset : reset scoreboard text"); + console.log("change [setting] [value] : change specified settings"); + console.log("clear : clear console output"); + console.log("color [PlayerID] [R] [G] [B] : set cell(s) color by client ID"); + console.log("exit : stop the server"); + console.log("food [X] [Y] [mass] : spawn food at specified Location"); + console.log("gamemode [id] : change server gamemode"); + console.log("kick [PlayerID] : kick player or bot by client ID"); + console.log("kickall : kick all players and bots"); + console.log("kill [PlayerID] : kill cell(s) by client ID"); + console.log("killall : kill everyone"); + console.log("mass [PlayerID] [mass] : set cell(s) mass by client ID"); + console.log("merge [PlayerID] : merge all client's cells once"); + console.log("name [PlayerID] [name] : change cell(s) name by client ID"); + console.log("playerlist : get list of players and bots"); + console.log("pause : pause game , freeze all cells"); + console.log("reload : reload config"); + console.log("status : get server status"); + console.log("tp [PlayerID] [X] [Y] : teleport player to specified location"); + console.log("unban [IP] : unban an IP"); + console.log("virus [X] [Y] [mass] : spawn virus at a specified Location"); + console.log("pl : alias for playerlist"); + console.log("st : alias for status"); + console.log("===================================================="); }, debug: function(gameServer, split) { // Used for checking node lengths (for now) @@ -66,15 +67,15 @@ Commands.list = { clientCells += gameServer.clients[i].playerTracker.cells.length; } // Output node information - console.log("[Console] Clients: " + fillChar(gameServer.clients.length, " ", 4, true) + " / " + gameServer.config.serverMaxConnections + " + bots"); - console.log("[Console] Total nodes:" + fillChar(gameServer.nodes.length, " ", 8, true)); - console.log("[Console] - Client cells: " + fillChar(clientCells, " ", 4, true) + " / " + (gameServer.clients.length * gameServer.config.playerMaxCells)); - console.log("[Console] - Ejected cells:" + fillChar(gameServer.nodesEjected.length, " ", 4, true)); - console.log("[Console] - Foods: " + fillChar(gameServer.currentFood, " ", 4, true) + " / " + gameServer.config.foodMaxAmount); - console.log("[Console] - Viruses: " + fillChar(gameServer.nodesVirus.length, " ", 4, true) + " / " + gameServer.config.virusMaxAmount); - console.log("[Console] Moving nodes: " + fillChar(gameServer.movingNodes.length, " ", 4, true)); - console.log("[Console] Quad nodes: " + fillChar(gameServer.quadTree.scanNodeCount(), " ", 4, true)); - console.log("[Console] Quad items: " + fillChar(gameServer.quadTree.scanItemCount(), " ", 4, true)); + console.log("Clients: " + fillChar(gameServer.clients.length, " ", 4, true) + " / " + gameServer.config.serverMaxConnections + " + bots"); + console.log("Total nodes:" + fillChar(gameServer.nodes.length, " ", 8, true)); + console.log("- Client cells: " + fillChar(clientCells, " ", 4, true) + " / " + (gameServer.clients.length * gameServer.config.playerMaxCells)); + console.log("- Ejected cells:" + fillChar(gameServer.nodesEjected.length, " ", 4, true)); + console.log("- Foods: " + fillChar(gameServer.currentFood, " ", 4, true) + " / " + gameServer.config.foodMaxAmount); + console.log("- Viruses: " + fillChar(gameServer.nodesVirus.length, " ", 4, true) + " / " + gameServer.config.virusMaxAmount); + console.log("Moving nodes: " + fillChar(gameServer.movingNodes.length, " ", 4, true)); + console.log("Quad nodes: " + fillChar(gameServer.quadTree.scanNodeCount(), " ", 4, true)); + console.log("Quad items: " + fillChar(gameServer.quadTree.scanItemCount(), " ", 4, true)); }, addbot: function(gameServer, split) { var add = parseInt(split[1]); @@ -85,15 +86,15 @@ Commands.list = { for (var i = 0; i < add; i++) { gameServer.bots.addBot(); } - console.log("[Console] Added " + add + " player bots"); + console.log("Added " + add + " player bots"); }, ban: function (gameServer, split) { // Error message - var logInvalid = "[Console] Please specify a valid player ID or IP address!"; + var logInvalid = "Please specify a valid player ID or IP address!"; if (split[1] == null) { // If no input is given; added to avoid error - console.log(logInvalid); + Logger.warn(logInvalid); return; } @@ -107,14 +108,14 @@ Commands.list = { // If not numerical or if it's not between 0 and 255 // TODO: Catch string "e" as it means "10^". if (isNaN(ipParts[i]) || ipParts[i] < 0 || ipParts[i] >= 256) { - console.log(logInvalid); + Logger.warn(logInvalid); return; } } if (ipParts.length != 4) { // an IP without 3 decimals - console.log(logInvalid); + Logger.warn(logInvalid); return; } @@ -125,7 +126,7 @@ Commands.list = { var id = parseInt(split[1]); if (isNaN(id)) { // If not numerical - console.log(logInvalid); + Logger.warn(logInvalid); return; } var ip = null; @@ -141,10 +142,10 @@ Commands.list = { if (ip) gameServer.banIp(ip); else - console.log("[Console] Player ID " + id + " not found!"); + Logger.warn("Player ID " + id + " not found!"); }, banlist: function(gameServer, split) { - console.log("[Console] Showing " + gameServer.ipBanList.length + " banned IPs: "); + Logger.info("Showing " + gameServer.ipBanList.length + " banned IPs: "); console.log(" IP | IP "); console.log("-----------------------------------"); for (var i = 0; i < gameServer.ipBanList.length; i += 2) { @@ -174,11 +175,11 @@ Commands.list = { i++; } if (toRemove == -1) - console.log("[Console] Kicked all bots (" + removed + ")"); + console.log("Kicked all bots (" + removed + ")"); else if (toRemove == removed) - console.log("[Console] Kicked " + toRemove + " bots"); + console.log("Kicked " + toRemove + " bots"); else - console.log("[Console] Only " + removed + " bots could be kicked"); + console.log("Only " + removed + " bots could be kicked"); }, board: function(gameServer, split) { var newLB = []; @@ -197,7 +198,7 @@ Commands.list = { gameServer.leaderboard = newLB; gameServer.leaderboardType = 48; }; - console.log("[Console] Successfully changed leaderboard values"); + console.log("Successfully changed leaderboard values"); }, boardreset: function(gameServer) { // Gets the current gamemode @@ -206,7 +207,7 @@ Commands.list = { // Replace functions gameServer.gameMode.packetLB = gm.packetLB; gameServer.gameMode.updateLB = gm.updateLB; - console.log("[Console] Successfully reset leaderboard"); + console.log("Successfully reset leaderboard"); }, change: function(gameServer, split) { var key = split[1]; @@ -221,9 +222,9 @@ Commands.list = { if (typeof gameServer.config[key] != 'undefined') { gameServer.config[key] = value; - console.log("[Console] Set " + key + " to " + value); + console.log("Set " + key + " to " + value); } else { - console.log("[Console] Invalid config value"); + console.log("Invalid config value"); } }, clear: function() { @@ -233,7 +234,7 @@ Commands.list = { // Validation checks var id = parseInt(split[1]); if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID!"); + Logger.warn("Please specify a valid player ID!"); return; } @@ -259,7 +260,7 @@ Commands.list = { } }, exit: function(gameServer, split) { - console.log("[Console] Closing server..."); + Logger.warn("Closing server..."); gameServer.socketServer.close(); process.exit(1); }, @@ -272,7 +273,7 @@ Commands.list = { // Make sure the input values are numbers if (isNaN(pos.x) || isNaN(pos.y)) { - console.log("[Console] Invalid coordinates"); + Logger.warn("Invalid coordinates"); return; } @@ -285,7 +286,7 @@ Commands.list = { var cell = new Entity.Food(gameServer, null, pos, size); cell.setColor(gameServer.getRandomColor()); gameServer.addNode(cell); - console.log("[Console] Spawned 1 food cell at (" + pos.x + " , " + pos.y + ")"); + console.log("Spawned 1 food cell at (" + pos.x + " , " + pos.y + ")"); }, gamemode: function(gameServer, split) { try { @@ -294,15 +295,15 @@ Commands.list = { gameServer.gameMode.onChange(gameServer); // Reverts the changes of the old gamemode gameServer.gameMode = gm; // Apply new gamemode gameServer.gameMode.onServerInit(gameServer); // Resets the server - console.log("[Game] Changed game mode to " + gameServer.gameMode.name); + console.log("Changed game mode to " + gameServer.gameMode.name); } catch (e) { - console.log("[Console] Invalid game mode selected"); + Logger.error("Invalid game mode selected"); } }, kick: function(gameServer, split) { var id = parseInt(split[1]); if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID!"); + Logger.warn("Please specify a valid player ID!"); return; } gameServer.kickId(id); @@ -313,7 +314,7 @@ Commands.list = { kill: function(gameServer, split) { var id = parseInt(split[1]); if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID!"); + Logger.warn("Please specify a valid player ID!"); return; } @@ -327,7 +328,7 @@ Commands.list = { count++; } - console.log("[Console] Removed " + count + " cells"); + console.log("Removed " + count + " cells"); break; } } @@ -341,19 +342,19 @@ Commands.list = { count++; } } - console.log("[Console] Removed " + count + " cells"); + console.log("Removed " + count + " cells"); }, mass: function(gameServer, split) { // Validation checks var id = parseInt(split[1]); if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID!"); + Logger.warn("Please specify a valid player ID!"); return; } var amount = Math.max(parseInt(split[2]), 9); if (isNaN(amount)) { - console.log("[Console] Please specify a valid number"); + Logger.warn("Please specify a valid number"); return; } var size = Math.sqrt(amount * 100); @@ -366,7 +367,7 @@ Commands.list = { client.cells[j].setSize(size); } - console.log("[Console] Set mass of " + client.name + " to " + (size*size/100).toFixed(3)); + console.log("Set mass of " + client.name + " to " + (size*size/100).toFixed(3)); break; } } @@ -376,7 +377,7 @@ Commands.list = { var id = parseInt(split[1]); var set = split[2]; if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID!"); + Logger.warn("Please specify a valid player ID!"); return; } @@ -390,12 +391,12 @@ Commands.list = { } if (!client) { - console.log("[Console] Client is nonexistent!"); + Logger.warn("Client is nonexistent!"); return; } if (client.cells.length == 1) { - console.log("[Console] Client already has one cell!"); + Logger.warn("Client already has one cell!"); return; } @@ -422,20 +423,20 @@ Commands.list = { } // Log - if (state) console.log("[Console] Player " + id + " is now force merging"); - else console.log("[Console] Player " + id + " isn't force merging anymore"); + if (state) console.log("Player " + id + " is now force merging"); + else console.log("Player " + id + " isn't force merging anymore"); }, name: function(gameServer, split) { // Validation checks var id = parseInt(split[1]); if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID!"); + Logger.warn("Please specify a valid player ID!"); return; } var name = split.slice(2, split.length).join(' '); if (typeof name == 'undefined') { - console.log("[Console] Please type a valid name"); + Logger.warn("Please type a valid name"); return; } @@ -444,24 +445,24 @@ Commands.list = { var client = gameServer.clients[i].playerTracker; if (client.pID == id) { - console.log("[Console] Changing " + client.name + " to " + name); + console.log("Changing " + client.name + " to " + name); client.name = name; return; } } // Error - console.log("[Console] Player " + id + " was not found"); + Logger.warn("Player " + id + " was not found"); }, unban: function(gameServer, split) { if (split.length < 2 || split[1] == null || split[1].trim().length < 1) { - console.log("[Console] Please specify a valid IP!"); + Logger.warn("Please specify a valid IP!"); return; } gameServer.unbanIp(split[1].trim()); }, playerlist: function(gameServer, split) { - console.log("[Console] Showing " + gameServer.clients.length + " players: "); + Logger.info("Showing " + gameServer.clients.length + " players: "); console.log(" ID | IP | P | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space console.log(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); for (var i = 0; i < gameServer.clients.length; i++) { @@ -521,12 +522,12 @@ Commands.list = { pause: function(gameServer, split) { gameServer.run = !gameServer.run; // Switches the pause state var s = gameServer.run ? "Unpaused" : "Paused"; - console.log("[Console] " + s + " the game."); + console.log(s + " the game."); }, reload: function(gameServer) { gameServer.loadConfig(); gameServer.loadIpBanList(); - console.log("[Console] Reloaded the config file successfully"); + console.log("Reloaded the config file successfully"); }, status: function(gameServer, split) { // Get amount of humans/bots @@ -540,17 +541,17 @@ Commands.list = { } } - console.log("[Console] Connected players: " + gameServer.clients.length + "/" + gameServer.config.serverMaxConnections); - console.log("[Console] Players: " + humans + " - Bots: " + bots); - console.log("[Console] Server has been running for " + Math.floor(process.uptime()/60) + " minutes"); - console.log("[Console] Current memory usage: " + Math.round(process.memoryUsage().heapUsed / 1048576 * 10)/10 + "/" + Math.round(process.memoryUsage().heapTotal / 1048576 * 10)/10 + " mb"); - console.log("[Console] Current game mode: " + gameServer.gameMode.name); - console.log("[Console] Current update time: " + gameServer.updateTimeAvg.toFixed(3) + " [ms] (" + ini.getLagMessage(gameServer.updateTimeAvg) + ")"); + console.log("Connected players: " + gameServer.clients.length + "/" + gameServer.config.serverMaxConnections); + console.log("Players: " + humans + " - Bots: " + bots); + console.log("Server has been running for " + Math.floor(process.uptime()/60) + " minutes"); + console.log("Current memory usage: " + Math.round(process.memoryUsage().heapUsed / 1048576 * 10)/10 + "/" + Math.round(process.memoryUsage().heapTotal / 1048576 * 10)/10 + " mb"); + console.log("Current game mode: " + gameServer.gameMode.name); + console.log("Current update time: " + gameServer.updateTimeAvg.toFixed(3) + " [ms] (" + ini.getLagMessage(gameServer.updateTimeAvg) + ")"); }, tp: function(gameServer, split) { var id = parseInt(split[1]); if (isNaN(id)) { - console.log("[Console] Please specify a valid player ID!"); + Logger.warn("Please specify a valid player ID!"); return; } @@ -560,7 +561,7 @@ Commands.list = { y: parseInt(split[3]) }; if (isNaN(pos.x) || isNaN(pos.y)) { - console.log("[Console] Invalid coordinates"); + Logger.warn("Invalid coordinates"); return; } @@ -573,7 +574,7 @@ Commands.list = { gameServer.updateNodeQuad(client.cells[j]); } - console.log("[Console] Teleported " + client.name + " to (" + pos.x + " , " + pos.y + ")"); + console.log("Teleported " + client.name + " to (" + pos.x + " , " + pos.y + ")"); break; } } @@ -587,7 +588,7 @@ Commands.list = { // Make sure the input values are numbers if (isNaN(pos.x) || isNaN(pos.y)) { - console.log("[Console] Invalid coordinates"); + Logger.warn("Invalid coordinates"); return; } var size = gameServer.config.virusMinSize; @@ -598,7 +599,7 @@ Commands.list = { // Spawn var v = new Entity.Virus(gameServer, null, pos, size); gameServer.addNode(v); - console.log("[Console] Spawned 1 virus at (" + pos.x + " , " + pos.y + ")"); + console.log("Spawned 1 virus at (" + pos.x + " , " + pos.y + ")"); }, //Aliases st: function (gameServer, split) { diff --git a/src/modules/Logger.js b/src/modules/Logger.js new file mode 100644 index 000000000..fcdf555ab --- /dev/null +++ b/src/modules/Logger.js @@ -0,0 +1,144 @@ +'use strict'; +/* + * Simple logger. + * + * Copyright (c) 2016 Barbosik https://github.com/Barbosik + * License: Apache License, Version 2.0 + * + */ + +var fs = require("fs"); +var util = require('util'); +var EOL = require('os').EOL; + + +module.exports.debug = debug; +module.exports.info = info; +module.exports.warn = warn; +module.exports.error = error; +module.exports.fatal = fatal; +module.exports.print = print; +module.exports.write = write; +module.exports.start = start; + +function debug(message) { + message = util.format(message); + writeCon(colorWhite, "[DEBUG] " + message); + writeLog("[DEBUG][" + getTimeString() + "] " + message); +}; + +function info(message) { + message = util.format(message); + writeCon(colorWhite + colorBright, "[INFO ] " + message); + writeLog("[INFO ][" + getTimeString() + "] " + message); +}; + +function warn(message) { + message = util.format(message); + writeCon(colorYellow + colorBright, "[WARN ] " + message); + writeLog("[WARN ][" + getTimeString() + "] " + message); +}; + +function error(message) { + message = util.format(message); + writeCon(colorRed + colorBright, "[ERROR] " + message); + writeLog("[ERROR][" + getTimeString() + "] " + message); +}; + +function fatal(message) { + message = util.format(message); + writeCon(colorRed + colorBright, "[FATAL] " + message); + writeLog("[FATAL][" + getTimeString() + "] " + message); +}; + +function print(message) { + message = util.format(message); + writeCon(colorWhite, message); + writeLog("[NONE ][" + getTimeString() + "] " + message); +}; + +function write(message) { + message = util.format(message); + writeLog("[NONE ][" + getTimeString() + "] " + message); +}; + + +// --- utils --- + +function getDateTimeString() { + var date = new Date(); + var dy = date.getFullYear(); + var dm = date.getMonth(); + var dd = date.getDay(); + var th = date.getHours(); + var tm = date.getMinutes(); + var ts = date.getSeconds(); + var tz = date.getMilliseconds(); + dy = ("0000" + dy).slice(-4); + dm = ("00" + dm).slice(-2); + dd = ("00" + dd).slice(-2); + th = ("00" + th).slice(-2); + tm = ("00" + tm).slice(-2); + ts = ("00" + ts).slice(-2); + tz = ("000" + tz).slice(-3); + return dy + "-" + dm + "-" + dd + "T" + th + "-" + tm + "-" + ts + "-" + tz; +}; + +function getTimeString() { + var date = new Date(); + var th = date.getHours(); + var tm = date.getMinutes(); + var ts = date.getSeconds(); + th = ("00" + th).slice(-2); + tm = ("00" + tm).slice(-2); + ts = ("00" + ts).slice(-2); + return th + "-" + tm + "-" + ts; +}; + +function writeCon(color, message) { + process.stdout.write(color + message + "\u001B[0m" + EOL); +}; + +function writeLog(message) { + if (consoleLog == null) + return; + consoleLog.write(message + EOL); +}; + +function start() { + if (consoleLog != null) + return; + var timeString = getDateTimeString(); + var fileName = logFolder + "/" + logFileName + ".log"; + var fileName2 = logBackupFolder + "/" + logFileName + "-" + timeString + ".log"; + + if (!fs.existsSync(logFolder)) { + // Make log folder + fs.mkdirSync(logFolder); + } else if (fs.existsSync(fileName)) { + if (!fs.existsSync(logBackupFolder)) { + // Make log backup folder + fs.mkdirSync(logBackupFolder); + } + // Backup previous log + fs.renameSync(fileName, fileName2); + } + consoleLog = fs.createWriteStream(fileName, { flags: 'w' }); + console.log = function (message) { print(message); }; + writeLog("=== Started " + timeString + " ==="); +} + +var logFolder = "./logs"; +var logBackupFolder = "./logs/LogBackup"; +var logFileName = "MultiOgar"; + +var consoleLog = null; +var colorBlack = "\u001B[30m"; +var colorRed = "\u001B[31m"; +var colorGreen = "\u001B[32m"; +var colorYellow = "\u001B[33m"; +var colorBlue = "\u001B[34m"; +var colorMagenta = "\u001B[35m"; +var colorCyan = "\u001B[36m"; +var colorWhite = "\u001B[37m"; +var colorBright = "\u001B[1m"; diff --git a/src/modules/log.js b/src/modules/log.js deleted file mode 100644 index ec0ebdf4d..000000000 --- a/src/modules/log.js +++ /dev/null @@ -1,77 +0,0 @@ -var fs = require("fs"); -var util = require('util'); -var EOL = require('os').EOL; - -function Log() { - // Nothing -} - -module.exports = Log; - -Log.prototype.setup = function(gameServer) { - if (!fs.existsSync('./logs')) { - // Make log folder - fs.mkdir('./logs'); - } - - switch (gameServer.config.serverLogLevel) { - case 2: - ip_log = fs.createWriteStream('./logs/ip.log', { - flags: 'w' - }); - - // Override - this.onConnect = function(ip) { - ip_log.write("[" + this.formatTime() + "] Connect: " + ip + EOL); - }; - - this.onDisconnect = function(ip) { - ip_log.write("[" + this.formatTime() + "] Disconnect: " + ip + EOL); - }; - case 1: - console_log = fs.createWriteStream('./logs/console.log', { - flags: 'w' - }); - - console.log = function(d) { // - console_log.write(util.format(d) + EOL); - process.stdout.write(util.format(d) + EOL); - }; - - // - this.onCommand = function(command) { - console_log.write(">" + command + EOL); - }; - case 0: - // Prevent crashes - process.on('uncaughtException', function(err) { - console.log(err.stack); - }); - default: - break; - } -}; - -Log.prototype.onConnect = function(ip) { - // Nothing -}; - -Log.prototype.onDisconnect = function(ip) { - // Nothing -}; - -Log.prototype.onCommand = function(command) { - // Nothing -}; - -Log.prototype.formatTime = function() { - var date = new Date(); - - var hour = date.getHours(); - hour = (hour < 10 ? "0" : "") + hour; - - var min = date.getMinutes(); - min = (min < 10 ? "0" : "") + min; - - return hour + ":" + min; -}; From ba172375d0e662e4f3312177c48e0480cd016b20 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 15:52:19 +0200 Subject: [PATCH 255/417] fix exception handling --- src/GameServer.js | 3 +++ src/ai/BotLoader.js | 3 ++- src/modules/CommandList.js | 6 ++++-- src/modules/ini.js | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index a9d59e599..1d85baa81 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1251,6 +1251,7 @@ GameServer.prototype.loadConfig = function () { } } } catch (err) { + Logger.error(err.stack); Logger.error("Failed to load " + configFileName + ": " + err.message); } // check config (min player size = 32 => mass = 10.24) @@ -1270,6 +1271,7 @@ GameServer.prototype.loadIpBanList = function () { Logger.warn(fileName + " is missing."); } } catch (err) { + Logger.error(err.stack); Logger.error("Failed to load " + fileName + ": " + err.message); } }; @@ -1285,6 +1287,7 @@ GameServer.prototype.saveIpBanList = function () { blFile.end(); Logger.info(this.ipBanList.length + " IP ban records saved."); } catch (err) { + Logger.error(err.stack); Logger.error("Failed to save " + fileName + ": " + err.message); } }; diff --git a/src/ai/BotLoader.js b/src/ai/BotLoader.js index e0b5349b0..e0bfc6869 100644 --- a/src/ai/BotLoader.js +++ b/src/ai/BotLoader.js @@ -36,7 +36,8 @@ BotLoader.prototype.loadNames = function() { this.randomNames = fs.readFileSync("./botnames.txt", "utf8").split(/[\r\n]+/).filter(function(x) { return x != ''; // filter empty names }); - } catch (e) { + } catch (err) { + Logger.error(err.stack); // Nothing, use the default names } diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 09618bc3b..1900b3264 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -296,7 +296,8 @@ Commands.list = { gameServer.gameMode = gm; // Apply new gamemode gameServer.gameMode.onServerInit(gameServer); // Resets the server console.log("Changed game mode to " + gameServer.gameMode.name); - } catch (e) { + } catch (err) { + Logger.error(err.stack); Logger.error("Invalid game mode selected"); } }, @@ -500,7 +501,8 @@ Commands.list = { }else if (client.spectate) { try { nick = gameServer.largestClient.getFriendlyName(); - } catch (e) { + } catch (err) { + Logger.error(err.stack); // Specating in free-roam mode nick = "in free-roam"; } diff --git a/src/modules/ini.js b/src/modules/ini.js index d932f5665..681ef5e27 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -172,7 +172,9 @@ function unsafe(val, doUnesc) { } try { val = JSON.parse(val); - } catch (_) {} + } catch (err) { + Logger.error(err.stack); + } } else { // walk the val to find the first not-escaped ; character var esc = false; From 6a2d07c141af153c5f909d44294cdbbc0538df53 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 16:22:06 +0200 Subject: [PATCH 256/417] fix massToSize (undefined not a function error) --- src/modules/ini.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/modules/ini.js b/src/modules/ini.js index 681ef5e27..0e2e1a86f 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -109,12 +109,20 @@ function decode(str) { } } - // Mass to Size function catcher - if (value.startsWith("massToSize(") && value.endsWith(")")) { + //// Mass to Size function catcher + if (startsWith(value, "massToSize(") && endsWith(value, ")")) { // 11: length of "massToSize(" - value = Math.sqrt(parseFloat(value.slice(11, value.length - 1)) * 100); - + var strValue = value.slice(11, value.length - 1).trim(); + value = Math.sqrt(parseFloat(strValue) * 100) + 0.5; } + function startsWith(value, pattern) { + return value.length >= pattern.length && + value.indexOf(pattern) === 0; + }; + function endsWith(value, pattern) { + return value.length >= pattern.length && + value.lastIndexOf(pattern) === value.length - pattern.length; + }; // safeguard against resetting a previously defined // array by accidentally forgetting the brackets From 28b9ddfda2686c8b34d5051e9a80fbf74564df4e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 16:39:23 +0200 Subject: [PATCH 257/417] fix food spawn amount --- src/GameServer.js | 6 +++--- src/gameserver.ini | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 1d85baa81..4cd33e579 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -77,8 +77,8 @@ function GameServer() { foodMinSize: 10, // Minimum food size (vanilla 10) foodMaxSize: 20, // Maximum food size (vanilla 20) - foodMinAmount: 100, // Minimum food cells on the map - foodMaxAmount: 500, // Maximum food cells on the map + foodMinAmount: 1000, // Minimum food cells on the map + foodMaxAmount: 2000, // Maximum food cells on the map foodSpawnAmount: 10, // The number of food to spawn per interval foodMassGrow: 1, // Enable food mass grow ? spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) @@ -624,7 +624,7 @@ GameServer.prototype.startingFood = function() { }; GameServer.prototype.updateFood = function() { - var maxCount = this.config.foodMaxAmount - this.currentFood; + var maxCount = this.config.foodMinAmount - this.currentFood; var spawnCount = Math.min(maxCount, this.config.foodSpawnAmount); for (var i = 0; i < spawnCount; i++) { this.spawnFood(); diff --git a/src/gameserver.ini b/src/gameserver.ini index 99577f521..3d88a159f 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -59,8 +59,8 @@ borderHeight = 14142 # foodMaxSize: vanilla 20 (mass = 20*20/100 = 4) foodMinSize = 10 foodMaxSize = 20 -foodMinAmount = 100 -foodMaxAmount = 500 +foodMinAmount = 3000 +foodMaxAmount = 4000 foodSpawnAmount = 10 foodMassGrow = 1 spawnInterval = 20 From 0d9b590a036892a10eb675b15ff7b3be9a40a160 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 16:41:01 +0200 Subject: [PATCH 258/417] fix MotherCell food spawn amount --- src/entity/MotherCell.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/MotherCell.js b/src/entity/MotherCell.js index 1f11246bb..409d1ba76 100644 --- a/src/entity/MotherCell.js +++ b/src/entity/MotherCell.js @@ -32,7 +32,7 @@ MotherCell.prototype.onUpdate = function () { if (this.getSize() <= this.motherCellMinSize) { return; } - var maxFood = this.gameServer.config.foodMaxAmount * 1.5; + var maxFood = this.gameServer.config.foodMaxAmount; if (this.gameServer.currentFood >= maxFood) { return; } From 5c022d21e918e4bd2f944e36d91baa4fa96a7cf6 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 17:21:46 +0200 Subject: [PATCH 259/417] update readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index af6aa24ef..d03e300bc 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,10 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Fixed cell-split order, now split-run works ok +* A little performance improvement for split/eject +* Fixed min mass to split/eject +* Fixed mass-limit behavior * Added scramble level 3 (anti-bot/anti-minimap protection), unsupported on some clients (unfortunately include vanilla, ogar.mivabe.nl works ok) * NOTE: there is major gameserver.ini change, previous version is incompatible! * Massive perfromance improvement & reduce network traffic From a7226b87e64d4220122f3888ba4ddc6735d91ba9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 18:00:42 +0200 Subject: [PATCH 260/417] add comments about mass/size for gameserver.ini --- src/gameserver.ini | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/gameserver.ini b/src/gameserver.ini index 3d88a159f..58e6d4285 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -9,6 +9,10 @@ # For example, to set start mass = 43: # size = SQRT( 43 * 100 ) = SQRT( 4300 ) = 65.57 # Set playerStartSize = 66 +# +# Also, you can use the following syntax to specify mass: +# playerStartSize = massToSize(43) +# It will be automatically converted to 66 # [Server] @@ -31,7 +35,7 @@ serverTimeout = 30 serverMaxConnections = 64 serverIpLimit = 4 -serverPort = 50000 +serverPort = 443 serverTracker = 0 serverGamemode = 0 serverBots = 0 From ada6bfd5a8da153635ac696dd0671b637ac60fff Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 19:25:33 +0200 Subject: [PATCH 261/417] update readme --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index d03e300bc..0b013bc02 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,41 @@ The goal is to make good and smooth physics and cleanup the code. Map 6000x6000, 300 bots, 5000 food, 10 viruses - works pretty smooth with no lags: ![Screenshot](http://i.imgur.com/4Wg8s9b.png) +## Install + +#### Windows: +1) Download and install node.js: https://nodejs.org/en/download/ (64-bit recommended) +2) Download MultiOgar code: https://github.com/Barbosik/MultiOgar/archive/master.zip +3) Unzip MultiOgar code into some folder +4) Start command line and select folder with MultiOgar +5) Execute: +``` +npm install +``` +and then: +``` +node src/index.js +``` + +#### Linux: +1) Install node.js: +``` +sudo apt-get update +sudo apt-get install nodejs +sudo apt-get install npm +``` +2) Download MultiOgar code: https://github.com/Barbosik/MultiOgar/archive/master.zip +3) Unzip MultiOgar code into some folder +4) Execute from MultiOgar folder: +``` +npm install +``` +and then: +``` +node src/index.js +``` + + ## Clients This lists Ogar clients and server trackers that I found on internet. From 65b231b7b0fc9c1e1357f9b20041c8da17fec7c8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 19:27:00 +0200 Subject: [PATCH 262/417] update readme --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0b013bc02..84c91be4c 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,11 @@ Map 6000x6000, 300 bots, 5000 food, 10 viruses - works pretty smooth with no lag ## Install #### Windows: -1) Download and install node.js: https://nodejs.org/en/download/ (64-bit recommended) -2) Download MultiOgar code: https://github.com/Barbosik/MultiOgar/archive/master.zip -3) Unzip MultiOgar code into some folder -4) Start command line and select folder with MultiOgar -5) Execute: +* Download and install node.js: https://nodejs.org/en/download/ (64-bit recommended) +* Download MultiOgar code: https://github.com/Barbosik/MultiOgar/archive/master.zip +* Unzip MultiOgar code into some folder +* Start command line and select folder with MultiOgar +* Execute: ``` npm install ``` @@ -35,15 +35,15 @@ node src/index.js ``` #### Linux: -1) Install node.js: +* Install node.js: ``` sudo apt-get update sudo apt-get install nodejs sudo apt-get install npm ``` -2) Download MultiOgar code: https://github.com/Barbosik/MultiOgar/archive/master.zip -3) Unzip MultiOgar code into some folder -4) Execute from MultiOgar folder: +* Download MultiOgar code: https://github.com/Barbosik/MultiOgar/archive/master.zip +* Unzip MultiOgar code into some folder +* Execute from MultiOgar folder: ``` npm install ``` From fc4d13bee6a24bba27bcbc58960c2a12a53969fd Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 19:28:37 +0200 Subject: [PATCH 263/417] update readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 84c91be4c..fe29404fb 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,7 @@ Map 6000x6000, 300 bots, 5000 food, 10 viruses - works pretty smooth with no lag * Download and install node.js: https://nodejs.org/en/download/ (64-bit recommended) * Download MultiOgar code: https://github.com/Barbosik/MultiOgar/archive/master.zip * Unzip MultiOgar code into some folder -* Start command line and select folder with MultiOgar -* Execute: +* Start command line and execute from MultiOgar folder ``` npm install ``` From 51e2dc4b68ab84fa08798f62529534d156860b73 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 19:48:46 +0200 Subject: [PATCH 264/417] update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index fe29404fb..c0bd8375d 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ The goal is to make good and smooth physics and cleanup the code. ## Screenshot +MultiOgar console: +![Screenshot](http://i.imgur.com/pmECJCH.png) + Map 6000x6000, 300 bots, 5000 food, 10 viruses - works pretty smooth with no lags: ![Screenshot](http://i.imgur.com/4Wg8s9b.png) From e888b285521f39f98c60d41a3891179108fba94a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 19:49:46 +0200 Subject: [PATCH 265/417] update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c0bd8375d..84b97c66a 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,12 @@ The goal is to make good and smooth physics and cleanup the code. ## Screenshot MultiOgar console: + ![Screenshot](http://i.imgur.com/pmECJCH.png) + Map 6000x6000, 300 bots, 5000 food, 10 viruses - works pretty smooth with no lags: + ![Screenshot](http://i.imgur.com/4Wg8s9b.png) ## Install From e95ad38827d509b02e9f2344deccbf624bafacda Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 19:53:26 +0200 Subject: [PATCH 266/417] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 84b97c66a..811d3a585 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Ogar game server with fast and smooth vanilla physics and multi-protocol support ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) -[![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/Barbosik/OgarMulti/blob/master/LICENSE.md) +![License](https://img.shields.io/badge/license-APACHE2-blue.svg)(https://github.com/Barbosik/OgarMulti/blob/master/LICENSE.md) MultiOgar code based on Ogar code that I heavily modified, and will continue to update. Almost all physics and protocol code were rewritten and optimized. From 01110181fa07b858b24e8de70f3b7a6d23c798f5 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 19:54:03 +0200 Subject: [PATCH 267/417] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 811d3a585..84b97c66a 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Ogar game server with fast and smooth vanilla physics and multi-protocol support ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) -![License](https://img.shields.io/badge/license-APACHE2-blue.svg)(https://github.com/Barbosik/OgarMulti/blob/master/LICENSE.md) +[![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/Barbosik/OgarMulti/blob/master/LICENSE.md) MultiOgar code based on Ogar code that I heavily modified, and will continue to update. Almost all physics and protocol code were rewritten and optimized. From bb34e6bbc4712215d8bc46a7ccc405f1251fb45c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 25 Jun 2016 21:56:07 +0200 Subject: [PATCH 268/417] protocol optimizations & reduce network traffic --- src/PlayerTracker.js | 6 +++--- src/gameserver.ini | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 3a65341ab..996694711 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -268,7 +268,7 @@ PlayerTracker.prototype.update = function () { } if (newVisible[newIndex].nodeId > this.visibleNodes[oldIndex].nodeId) { var node = this.visibleNodes[oldIndex]; - if (node.isRemoved) + if (node.isRemoved && node.getKiller() != null && node.owner != node.getKiller().owner) eatNodes.push(node); else delNodes.push(node); @@ -289,9 +289,9 @@ PlayerTracker.prototype.update = function () { } for (; oldIndex < this.visibleNodes.length; ) { var node = this.visibleNodes[oldIndex]; - if (node.isRemoved) + if (node.isRemoved && node.getKiller() != null && node.owner != node.getKiller().owner) eatNodes.push(node); - else + else delNodes.push(node); oldIndex++; } diff --git a/src/gameserver.ini b/src/gameserver.ini index 58e6d4285..12633df5c 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -63,8 +63,8 @@ borderHeight = 14142 # foodMaxSize: vanilla 20 (mass = 20*20/100 = 4) foodMinSize = 10 foodMaxSize = 20 -foodMinAmount = 3000 -foodMaxAmount = 4000 +foodMinAmount = 1000 +foodMaxAmount = 2000 foodSpawnAmount = 10 foodMassGrow = 1 spawnInterval = 20 From b7e02a0352886b9fbc167172470505a38c75230a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 26 Jun 2016 10:09:09 +0200 Subject: [PATCH 269/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 84b97c66a..30de3a458 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge * A little performance improvement for split/eject * Fixed min mass to split/eject * Fixed mass-limit behavior +* Added chat player commands /skin and /kill (to change skin, just type /skin %shark in the chat) * Added scramble level 3 (anti-bot/anti-minimap protection), unsupported on some clients (unfortunately include vanilla, ogar.mivabe.nl works ok) * NOTE: there is major gameserver.ini change, previous version is incompatible! * Massive perfromance improvement & reduce network traffic From f199c370cbb01a81a492092da8cba781256cf3c4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 26 Jun 2016 12:55:16 +0200 Subject: [PATCH 270/417] add spectate walk through feature (space) + small protocol optimization --- src/PlayerTracker.js | 117 +++++++++++++++++++++++++++----- src/gamemodes/Debug.js | 60 ---------------- src/gamemodes/Mode.js | 19 ------ src/gamemodes/index.js | 4 -- src/packet/UpdateLeaderboard.js | 11 ++- 5 files changed, 108 insertions(+), 103 deletions(-) delete mode 100644 src/gamemodes/Debug.js diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 996694711..2fad4bead 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -26,7 +26,9 @@ function PlayerTracker(gameServer, socket) { this.team = 0; this.spectate = false; - this.freeRoam = false; // Free-roam mode enables player to move in spectate mode + this.freeRoam = false; // Free-roam mode enables player to move in spectate mode + this.spectateTarget = null; // Spectate target, null for largest player + this.lastSpectateSwitchTick = 0; this.centerPos = { x: 0, @@ -172,6 +174,7 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.setSkin(skin); this.spectate = false; this.freeRoam = false; + this.spectateTarget = null; // some old clients don't understand ClearAll message // so we will send update for them @@ -238,17 +241,17 @@ PlayerTracker.prototype.update = function () { // Actions buffer (So that people cant spam packets) if (this.socket.packetHandler.pressSpace) { // Split cell - if (!this.mergeOverride) this.gameServer.gameMode.pressSpace(this.gameServer, this); + this.pressSpace(); this.socket.packetHandler.pressSpace = false; } if (this.socket.packetHandler.pressW) { // Eject mass - this.gameServer.gameMode.pressW(this.gameServer, this); + this.pressW(); this.socket.packetHandler.pressW = false; } if (this.socket.packetHandler.pressQ) { // Q Press - this.gameServer.gameMode.pressQ(this.gameServer, this); + this.pressQ(); this.socket.packetHandler.pressQ = false; } @@ -322,15 +325,13 @@ PlayerTracker.prototype.update = function () { delNodes)); // Update leaderboard - if (this.tickLeaderboard <= 0) { + if (++this.tickLeaderboard > 25) { + // 1 / 0.040 = 25 (once per second) + this.tickLeaderboard = 0; if (this.gameServer.leaderboardType >= 0) { var packet = new Packet.UpdateLeaderboard(this, this.gameServer.leaderboard, this.gameServer.leaderboardType); this.socket.sendPacket(packet); } - // 1 / 0.040 = 25 (once per second) - this.tickLeaderboard = 25; - } else { - this.tickLeaderboard--; } }; @@ -392,16 +393,98 @@ PlayerTracker.prototype.updateViewBox = function () { }; }; +PlayerTracker.prototype.pressQ = function () { + if (this.spectate) { + // Check for spam first (to prevent too many add/del updates) + var tick = this.gameServer.getTick(); + if (tick - this.lastSpectateSwitchTick < 40) + return; + this.lastSpectateSwitchTick = tick; + + if (this.spectateTarget == null) { + this.freeRoam = !this.freeRoam; + } + this.spectateTarget = null; + } +}; + +PlayerTracker.prototype.pressW = function () { + if (this.spectate) { + return; + } + else if (this.gameServer.run) { + this.gameServer.ejectMass(this); + } +}; + +PlayerTracker.prototype.pressSpace = function () { + if (this.spectate) { + // Check for spam first (to prevent too many add/del updates) + var tick = this.gameServer.getTick(); + if (tick - this.lastSpectateSwitchTick < 40) + return; + this.lastSpectateSwitchTick = tick; + + // Space doesn't work for freeRoam mode + if (this.freeRoam) + return; + this.nextSpectateTarget(); + } else if (this.gameServer.run) { + if (this.mergeOverride) + return; + this.gameServer.splitCells(this); + } +}; + +PlayerTracker.prototype.nextSpectateTarget = function () { + if (this.spectateTarget == null) { + this.spectateTarget = this.gameServer.largestClient; + return; + } + // lookup for next spectate target + var index = this.gameServer.clients.indexOf(this.spectateTarget.socket); + if (index < 0) { + this.spectateTarget = this.gameServer.largestClient; + return; + } + // find next + for (var i = index + 1; i < this.gameServer.clients.length; i++) { + var player = this.gameServer.clients[i].playerTracker; + if (player.cells.length > 0) { + this.spectateTarget = player; + return; + } + } + for (var i = 0; i <= index; i++) { + var player = this.gameServer.clients[i].playerTracker; + if (player.cells.length > 0) { + this.spectateTarget = player; + return; + } + } + // no alive players + this.spectateTarget = null; +}; + +PlayerTracker.prototype.getSpectateTarget = function () { + if (this.spectateTarget == null || this.spectateTarget.isRemoved) { + this.spectateTarget = null; + return this.gameServer.largestClient; + } + return this.spectateTarget; +}; + PlayerTracker.prototype.getVisibleNodes = function () { if (this.spectate) { - var specPlayer = this.gameServer.largestClient; - if (!this.freeRoam && specPlayer != null) { - // top player spectate - this.setCenterPos(specPlayer.centerPos.x, specPlayer.centerPos.y); - this.scale = specPlayer.getScale(); - this.sendCameraPacket(); - this.updateViewBox(); - return specPlayer.visibleNodes.slice(0); + if (!this.freeRoam) { + var player = this.getSpectateTarget(); + if (player != null) { + this.setCenterPos(player.centerPos.x, player.centerPos.y); + this.scale = player.getScale(); + this.sendCameraPacket(); + this.updateViewBox(); + return player.visibleNodes.slice(0); + } } // free roam spectate this.updateCenterFreeRoam(); diff --git a/src/gamemodes/Debug.js b/src/gamemodes/Debug.js deleted file mode 100644 index 57c5524ae..000000000 --- a/src/gamemodes/Debug.js +++ /dev/null @@ -1,60 +0,0 @@ -var FFA = require('./FFA'); // Base gamemode -var Packet = require('../packet'); - -function Debug() { - FFA.apply(this, Array.prototype.slice.call(arguments)); - - this.ID = 21; - this.name = "Debug Mode"; - this.specByLeaderboard = false; -} - -module.exports = Debug; -Debug.prototype = new FFA(); - -// Gamemode Specific Functions - -Debug.prototype.testPath = function(gameServer, player) { - var cell = player.cells[0]; - var check = gameServer.nodesVirus[0]; - - var v1 = Math.atan2(cell.position.x - player.mouse.x, cell.position.y - player.mouse.y); - - // Get angle of vector (cell -> virus) - var v2 = this.getAngle(cell, check); - var dist = this.getDist(cell, check); - console.log(v1); - console.log(v2); - - var inRange = Math.atan((2 * cell.getSize()) / dist); // Opposite/adjacent - console.log(inRange); - if ((v1 <= (v2 + inRange)) && (v1 >= (v2 - inRange))) { - console.log("Collided!"); - } -}; - -Debug.prototype.getAngle = function(c1, c2) { - var deltaY = c1.position.y - c2.position.y; - var deltaX = c1.position.x - c2.position.x; - return Math.atan2(deltaX, deltaY); -}; - -Debug.prototype.getDist = function(cell, check) { - // Fastest distance - I have a crappy computer to test with :( - var xd = (check.position.x - cell.position.x); - xd = xd < 0 ? xd * -1 : xd; // Math.abs is slow - - var yd = (check.position.y - cell.position.y); - yd = yd < 0 ? yd * -1 : yd; // Math.abs is slow - - return (xd + yd); -}; - -// Override - -Debug.prototype.pressW = function(gameServer, player) { - // Called when the Q key is pressed - console.log("Test:"); - this.testPath(gameServer, player); - player.socket.sendPacket(new Packet.DrawLine(player.mouse.x, player.mouse.y)); -}; diff --git a/src/gamemodes/Mode.js b/src/gamemodes/Mode.js index eab928b44..c45dcdcd2 100644 --- a/src/gamemodes/Mode.js +++ b/src/gamemodes/Mode.js @@ -35,25 +35,6 @@ Mode.prototype.onPlayerSpawn = function(gameServer, player) { gameServer.spawnPlayer(player); }; -Mode.prototype.pressQ = function(gameServer, player) { - // Called when the Q key is pressed - if (player.spectate) { - player.freeRoam = !player.freeRoam; - } -}; - -Mode.prototype.pressW = function(gameServer, player) { - if (!gameServer.run) return; - // Called when the W key is pressed - gameServer.ejectMass(player); -}; - -Mode.prototype.pressSpace = function(gameServer, player) { - if (!gameServer.run) return; - // Called when the Space bar is pressed - gameServer.splitCells(player); -}; - Mode.prototype.onCellAdd = function(cell) { // Called when a player cell is added }; diff --git a/src/gamemodes/index.js b/src/gamemodes/index.js index 11d89e0d0..39758a9b4 100755 --- a/src/gamemodes/index.js +++ b/src/gamemodes/index.js @@ -6,7 +6,6 @@ module.exports = { Tournament: require('./Tournament'), HungerGames: require('./HungerGames'), Rainbow: require('./Rainbow'), - Debug: require('./Debug'), Zombie: require('./Zombie'), TeamZ: require('./TeamZ.js'), TeamX: require('./TeamX.js') @@ -39,9 +38,6 @@ var get = function(id) { case 20: // Rainbow mode = new module.exports.Rainbow(); break; - case 21: // Debug - mode = new module.exports.Debug(); - break; default: // FFA is default mode = new module.exports.FFA(); break; diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index ca372a2ff..d677ef524 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -43,6 +43,10 @@ UpdateLeaderboard.prototype.build48 = function (protocol) { // (FFA) Leaderboard Update UpdateLeaderboard.prototype.build49 = function (protocol) { + var player = this.playerTracker; + if (player.spectate && player.spectateTarget != null) { + player = player.spectateTarget; + } var writer = new BinaryWriter(); writer.writeUInt8(0x31); // Packet ID writer.writeUInt32(this.leaderboard.length >>> 0); // Number of elements @@ -52,9 +56,10 @@ UpdateLeaderboard.prototype.build49 = function (protocol) { var name = item.getName(); name = name != null ? name : ""; - var id = item == this.playerTracker ? 1 : 0; // protocol 6+ uses isMe flag - if (protocol <= 5 && item.cells != null && item.cells.length > 0) { - id = item.cells[0].nodeId ^ this.playerTracker.scrambleId; // protocol 5- uses player cellId instead of isMe flag + var id = item == player ? 1 : 0; + if (id && protocol < 6 && item.cells.length > 0) { + // protocol 5- uses player cellId instead of isMe flag + id = item.cells[0].nodeId ^ this.playerTracker.scrambleId; } writer.writeUInt32(id >>> 0); // isMe flag/cell ID From 7dfc4928207bfd51ea5dbbdf36a6b525ba7453fe Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 26 Jun 2016 13:53:25 +0200 Subject: [PATCH 271/417] update readme for linux installation --- README.md | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 30de3a458..0608920bd 100644 --- a/README.md +++ b/README.md @@ -40,21 +40,32 @@ node src/index.js ``` #### Linux: -* Install node.js: +* First update your packages: ``` sudo apt-get update -sudo apt-get install nodejs +``` +* Install git: +``` +sudo apt-get install git +``` +* Install node.js: +``` +sudo apt-get install nodejs-legacy sudo apt-get install npm ``` -* Download MultiOgar code: https://github.com/Barbosik/MultiOgar/archive/master.zip -* Unzip MultiOgar code into some folder -* Execute from MultiOgar folder: +* Clone MultiOgar: +``` +git clone git://github.com/Barbosik/MultiOgar.git ``` +* Install dependencies: +``` +cd MultiOgar npm install ``` -and then: +* Run the server: ``` -node src/index.js +cd src +node index.js ``` From ea816e9606ce7a5f6eef94c4a8c6e567c1711fa9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 26 Jun 2016 14:30:00 +0200 Subject: [PATCH 272/417] update readme --- README.md | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 0608920bd..694200bd8 100644 --- a/README.md +++ b/README.md @@ -34,36 +34,32 @@ Map 6000x6000, 300 bots, 5000 food, 10 viruses - works pretty smooth with no lag ``` npm install ``` -and then: +and run the server: ``` -node src/index.js +cd src +node index.js ``` #### Linux: -* First update your packages: ``` +# First update your packages: sudo apt-get update -``` -* Install git: -``` + +# Install git: sudo apt-get install git -``` -* Install node.js: -``` + +# Install node.js: sudo apt-get install nodejs-legacy sudo apt-get install npm -``` -* Clone MultiOgar: -``` + +# Clone MultiOgar: git clone git://github.com/Barbosik/MultiOgar.git -``` -* Install dependencies: -``` + +# Install dependencies: cd MultiOgar npm install -``` -* Run the server: -``` + +# Run the server: cd src node index.js ``` @@ -108,6 +104,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Added spectate walk through feature (use Space key in spectate mode to lock the current player or switch to the next one) * Fixed cell-split order, now split-run works ok * A little performance improvement for split/eject * Fixed min mass to split/eject From 82735b51735178aee789ae28938661cce5e1b1d8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 26 Jun 2016 14:32:19 +0200 Subject: [PATCH 273/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 694200bd8..10e588290 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: -* Added spectate walk through feature (use Space key in spectate mode to lock the current player or switch to the next one) +* Added spectate walk through feature (use Space key in spectate mode to lock the current player or switch to the next one. Use key Q to reset into the normal mode) * Fixed cell-split order, now split-run works ok * A little performance improvement for split/eject * Fixed min mass to split/eject From b8c8e67fea096571d79c168540de34fb47ce071a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 26 Jun 2016 14:34:00 +0200 Subject: [PATCH 274/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 10e588290..48bf1b0fb 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: -* Added spectate walk through feature (use Space key in spectate mode to lock the current player or switch to the next one. Use key Q to reset into the normal mode) +* Added spectate walk through feature (use Space key in spectate mode to lock the current player or switch to the next one. Use key Q to reset into the normal mode. Locked player is highlighted on leaderboard) * Fixed cell-split order, now split-run works ok * A little performance improvement for split/eject * Fixed min mass to split/eject From 5137cc44141fb8a64b47a47d7c62ed2920b1684e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 26 Jun 2016 14:39:09 +0200 Subject: [PATCH 275/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48bf1b0fb..52d708475 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: -* Added spectate walk through feature (use Space key in spectate mode to lock the current player or switch to the next one. Use key Q to reset into the normal mode. Locked player is highlighted on leaderboard) +* Added spectate walk through feature (use Space key in spectate mode to lock the current player or to lock the next one. Use key Q to reset into the normal mode. Locked player is highlighted on leaderboard) * Fixed cell-split order, now split-run works ok * A little performance improvement for split/eject * Fixed min mass to split/eject From c7c4ab8375784fa01f5c7619750404ab928f7b12 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 26 Jun 2016 16:32:02 +0200 Subject: [PATCH 276/417] a small spectate fix --- src/PlayerTracker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 2fad4bead..b1427fffa 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -426,7 +426,7 @@ PlayerTracker.prototype.pressSpace = function () { this.lastSpectateSwitchTick = tick; // Space doesn't work for freeRoam mode - if (this.freeRoam) + if (this.freeRoam || this.gameServer.largestClient==null) return; this.nextSpectateTarget(); } else if (this.gameServer.run) { @@ -467,7 +467,7 @@ PlayerTracker.prototype.nextSpectateTarget = function () { }; PlayerTracker.prototype.getSpectateTarget = function () { - if (this.spectateTarget == null || this.spectateTarget.isRemoved) { + if (this.spectateTarget == null || this.spectateTarget.isRemoved || this.spectateTarget.cells.length < 1) { this.spectateTarget = null; return this.gameServer.largestClient; } From 34a97f6633bda4ce37364e554a9c6db329d79cac Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 27 Jun 2016 17:20:07 +0200 Subject: [PATCH 277/417] fix eject size --- src/gameserver.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gameserver.ini b/src/gameserver.ini index 12633df5c..ab7830c36 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -80,7 +80,7 @@ virusMaxAmount = 50 # ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) # ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) # ejectSpawnPlayer: Chance for a player to spawn from ejected mass -ejectSize = 37 +ejectSize = 38 ejectCooldown = 3 ejectSpawnPlayer = 50 From c494561f911ab3586ee43a8c6b83ff3d60bb331f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 27 Jun 2016 17:37:44 +0200 Subject: [PATCH 278/417] fix mass calculations; fix eject size --- src/GameServer.js | 2 +- src/entity/PlayerCell.js | 6 ------ src/entity/Virus.js | 3 ++- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 4cd33e579..97b3f0148 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -88,7 +88,7 @@ function GameServer() { virusMinAmount: 10, // Minimum number of viruses on the map. virusMaxAmount: 50, // Maximum number of viruses on the map. If this number is reached, then ejected cells will pass through viruses. - ejectSize: 37, // Size of ejected cells (vanilla 37) + ejectSize: 38, // Size of ejected cells (vanilla 38) ejectCooldown: 3, // min ticks between ejects ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 6baf533f8..331cf137a 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -90,12 +90,6 @@ PlayerCell.prototype.moveUser = function (border) { // Override -PlayerCell.prototype.onEat = function (prey) { - var size1 = this.getSize(); - var size2 = prey.getSize(); - this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); -}; - PlayerCell.prototype.onAdd = function(gameServer) { // Gamemode actions gameServer.gameMode.onCellAdd(this); diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 70840973a..61d1a0b52 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -23,8 +23,9 @@ Virus.prototype.canEat = function (cell) { Virus.prototype.onEat = function (prey) { // Called to eat prey cell var size1 = this.getSize(); - var size2 = prey.getSize() + 1; + var size2 = prey.getSize(); this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); + if (this.getSize() >= this.gameServer.config.virusMaxSize) { this.setSize(this.gameServer.config.virusMinSize); // Reset mass this.gameServer.shootVirus(this, prey.getAngle()); From e19443afe0db3d0ab91f689150eb474797f5feef Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 27 Jun 2016 17:53:11 +0200 Subject: [PATCH 279/417] a little optimization --- src/GameServer.js | 2 +- src/entity/Cell.js | 14 ++++++-------- src/entity/Virus.js | 4 +--- src/gamemodes/TeamZ.js | 2 +- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 97b3f0148..9da0bf299 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1143,7 +1143,7 @@ GameServer.prototype.ejectMass = function(client) { continue; } var size2 = this.config.ejectSize; - var sizeSquared = cell.getSquareSize() - size2 * size2; + var sizeSquared = cell.getSizeSquared() - size2 * size2; if (sizeSquared < this.config.playerMinSize * this.config.playerMinSize) { continue; } diff --git a/src/entity/Cell.js b/src/entity/Cell.js index ecc1ace40..08e50099b 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -7,7 +7,7 @@ function Cell(gameServer, owner, position, size) { this.position = { x: 0, y: 0 }; this._size = 0; this._mass = 0; - this._squareSize = 0; + this._sizeSquared = 0; this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass this.isSpiked = false; // If true, then this cell has spikes around it this.isAgitated = false;// If true, then this cell has waves on it's outline @@ -62,8 +62,8 @@ Cell.prototype.setSize = function (size) { throw new TypeError("Cell.setSize: size is NaN"); } this._size = size; - this._squareSize = size * size; - this._mass = this._squareSize / 100; + this._sizeSquared = size * size; + this._mass = this._sizeSquared / 100; if (this.owner) this.owner.massChanged(); }; @@ -76,8 +76,8 @@ Cell.prototype.getMass = function () { return this._mass; }; -Cell.prototype.getSquareSize = function () { - return this._squareSize; +Cell.prototype.getSizeSquared = function () { + return this._sizeSquared; }; Cell.prototype.getSpeed = function() { @@ -129,9 +129,7 @@ Cell.prototype.canEat = function (cell) { Cell.prototype.onEat = function (prey) { // Called to eat prey cell - var size1 = this.getSize(); - var size2 = prey.getSize(); - this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); + this.setSize(Math.sqrt(this.getSizeSquared() + prey.getSizeSquared())); }; Cell.prototype.onEaten = function (hunter) { diff --git a/src/entity/Virus.js b/src/entity/Virus.js index 61d1a0b52..a8e713e5c 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -22,9 +22,7 @@ Virus.prototype.canEat = function (cell) { Virus.prototype.onEat = function (prey) { // Called to eat prey cell - var size1 = this.getSize(); - var size2 = prey.getSize(); - this.setSize(Math.sqrt(size1 * size1 + size2 * size2)); + this.setSize(Math.sqrt(this.getSizeSquared() + prey.getSizeSquared())); if (this.getSize() >= this.gameServer.config.virusMaxSize) { this.setSize(this.gameServer.config.virusMinSize); // Reset mass diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index e8b069be8..1b7befaf3 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -373,7 +373,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { if (this.gameMode.state != GameState.IN_PROGRESS) return list; - var squareR = cell.getSquareSize(); // Get cell squared radius + var squareR = cell.getSizeSquared(); // Get cell squared radius // Loop through all cells that are visible to the cell. There is probably a more efficient way of doing this but whatever var len = cell.owner.visibleNodes.length; From b7f596df9559a5ab9cd34abfd197804784cd2812 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 27 Jun 2016 18:29:24 +0200 Subject: [PATCH 280/417] fix spawn from ejected mass --- src/GameServer.js | 25 +++++++++++-------------- src/gameserver.ini | 4 ++-- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 9da0bf299..2e81c3173 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -90,7 +90,7 @@ function GameServer() { ejectSize: 38, // Size of ejected cells (vanilla 38) ejectCooldown: 3, // min ticks between ejects - ejectSpawnPlayer: 50, // Chance for a player to spawn from ejected mass + ejectSpawnPlayer: 1, // if 1 then player may be spawned from ejected mass playerMinSize: 32, // Minimym size of the player cell (mass = 32*32/100 = 10.24) playerMaxSize: 1500, // Maximum size of the player cell (mass = 1500*1500/100 = 22500) @@ -663,20 +663,17 @@ GameServer.prototype.spawnVirus = function () { }; GameServer.prototype.spawnPlayer = function(player, pos, size) { - // Check if there are ejected mass in the world. - if (this.nodesEjected.length > 0) { - var index = Math.floor(Math.random() * 100) + 1; - if (index >= this.config.ejectSpawnPlayer) { - // Get ejected cell - index = Math.floor(Math.random() * this.nodesEjected.length); - var e = this.nodesEjected[index]; - if (e.boostDistance == 0) { - // Remove ejected mass - this.removeNode(e); - // Inherit + // Check if can spawn from ejected mass + if (!pos && this.config.ejectSpawnPlayer && this.nodesEjected.length > 0) { + if (Math.random() >= 0.5) { + // Spawn from ejected mass + var index = (this.nodesEjected.length - 1) * Math.random() >>> 0; + var eject = this.nodesEjected[index]; + if (!eject.isRemoved) { + this.removeNode(eject); pos = { - x: e.position.x, - y: e.position.y + x: eject.position.x, + y: eject.position.y }; } } diff --git a/src/gameserver.ini b/src/gameserver.ini index ab7830c36..517835a13 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -79,10 +79,10 @@ virusMaxAmount = 50 # [Ejected Mass] # ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) # ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) -# ejectSpawnPlayer: Chance for a player to spawn from ejected mass +# ejectSpawnPlayer: if 1 then player may be spawned from ejected mass ejectSize = 38 ejectCooldown = 3 -ejectSpawnPlayer = 50 +ejectSpawnPlayer = 1 # [Player] # Reminder: MultiOgar uses cell size instead of mass! From 6c323399976ee187c6a0117fb1e6d822bfe01513 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 27 Jun 2016 18:37:37 +0200 Subject: [PATCH 281/417] do not decrease ejected cell size on spawn player --- src/GameServer.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/GameServer.js b/src/GameServer.js index 2e81c3173..acedede99 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -675,6 +675,9 @@ GameServer.prototype.spawnPlayer = function(player, pos, size) { x: eject.position.x, y: eject.position.y }; + if (!size) { + size = Math.max(eject.getSize(), this.config.playerStartSize); + } } } } From f09541954419f5bc6e6f9ef000446fdc1c36913d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 08:30:44 +0200 Subject: [PATCH 282/417] new virus split --- src/GameServer.js | 102 ++++++++++++++++++++++++++++++++++++++++++++ src/entity/Virus.js | 74 ++++++++++++-------------------- 2 files changed, 129 insertions(+), 47 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index acedede99..32fd95931 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1045,6 +1045,105 @@ GameServer.prototype.updateMoveEngine = function () { } }; +// Returns masses in descending order +GameServer.prototype.splitMass = function (mass, count) { + // min throw size (vanilla 44) + var throwSize = this.config.playerMinSize + 12; + var throwMass = throwSize * throwSize / 100; + + // check maxCount + var maxCount = count; + var curMass = mass; + while (maxCount > 1 && curMass / (maxCount-1) < throwMass) { + maxCount = maxCount / 2 >>> 0; + } + if (maxCount < 2) { + return [mass]; + } + + // calculate mass + var minMass = this.config.playerMinSize * this.config.playerMinSize / 100; + var splitMass = curMass / maxCount; + if (splitMass < minMass) { + return [mass]; + } + var masses = []; + if (maxCount < 3 || maxCount < count || curMass / throwMass <= 30) { + // Monotone blow up + for (var i = 0; i < maxCount; i++) { + masses.push(splitMass); + } + } else { + // Diverse blow up + // Barbosik: draft version + var restCount = maxCount; + while (restCount > 2) { + var splitMass = curMass / 2; + if (splitMass <= throwMass) { + break; + } + var max = curMass - throwMass * (restCount - 1); + if (max <= throwMass || splitMass >= max) { + break; + } + masses.push(splitMass); + curMass -= splitMass; + restCount--; + } + var splitMass = curMass / 4; + if (splitMass > throwMass) { + while (restCount > 2) { + var max = curMass - throwMass * (restCount - 1); + if (max <= throwMass || splitMass >= max) { + break; + } + masses.push(splitMass); + curMass -= splitMass; + restCount--; + } + } + var splitMass = curMass / 8; + if (splitMass > throwMass) { + while (restCount > 2) { + var max = curMass - throwMass * (restCount - 1); + if (max <= throwMass || splitMass >= max) { + break; + } + masses.push(splitMass); + curMass -= splitMass; + restCount--; + } + } + if (restCount > 1) { + splitMass = curMass - throwMass * (restCount - 1); + if (splitMass > throwMass) { + masses.push(splitMass); + curMass -= splitMass; + restCount--; + } + } + if (restCount > 0) { + splitMass = curMass / restCount; + if (splitMass < throwMass-0.001) { + Logger.warn("GameServer.splitMass: throwMass-splitMass = "+(throwMass-splitMass).toFixed(3)+" (" + mass.toFixed(4) + ", " + count + ")"); + } + while (restCount > 0) { + masses.push(splitMass); + restCount--; + } + } + } + //Logger.debug("===GameServer.splitMass==="); + //Logger.debug("mass = " + mass.toFixed(3) + " | " + Math.sqrt(mass * 100).toFixed(3)); + //var sum = 0; + //for (var i = 0; i < masses.length; i++) { + // Logger.debug("mass[" + i + "] = " + masses[i].toFixed(3) + " | " + Math.sqrt(masses[i] * 100).toFixed(3)); + // sum += masses[i] + //} + //Logger.debug("sum = " + sum.toFixed(3) + " | " + Math.sqrt(sum * 100).toFixed(3)); + return masses; +}; + GameServer.prototype.splitCells = function(client) { // it seems that vanilla uses order by cell age var cellToSplit = []; @@ -1094,6 +1193,9 @@ GameServer.prototype.splitPlayerCell = function (client, parent, angle, mass) { size2 = Math.sqrt(mass * 100); size1 = Math.sqrt(parent.getSize() * parent.getSize() - size2 * size2); } + if (isNaN(size1) || size1 < this.config.playerMinSize) { + return false; + } // Remove mass from parent cell first parent.setSize(size1); diff --git a/src/entity/Virus.js b/src/entity/Virus.js index a8e713e5c..ff303d5ba 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -33,53 +33,33 @@ Virus.prototype.onEat = function (prey) { Virus.prototype.onEaten = function(consumer) { var client = consumer.owner; if (client == null) return; - - var maxSplits = Math.floor(consumer.getMass() / 16) - 1; // Maximum amount of splits - var numSplits = this.gameServer.config.playerMaxCells - client.cells.length; // Get number of splits - numSplits = Math.min(numSplits, maxSplits); - var splitMass = Math.min(consumer.getMass() / (numSplits + 1), 24); // Maximum size of new splits - - // Cell cannot split any further - if (numSplits <= 0) { - return; - } - - var mass = consumer.getMass(); // Mass of the consumer - var bigSplits = []; // Big splits - - // Big cells will split into cells larger than 24 mass - // won't do the regular way unless it can split more than 4 times - if (numSplits == 1) bigSplits = [mass / 2]; - else if (numSplits == 2) bigSplits = [mass / 4, mass / 4]; - else if (numSplits == 3) bigSplits = [mass / 4, mass / 4, mass / 7]; - else if (numSplits == 4) bigSplits = [mass / 5, mass / 7, mass / 8, mass / 10]; - else { - var endMass = mass - numSplits * splitMass; - var m = endMass, - i = 0; - if (m > 466) { // Threshold - // While can split into an even smaller cell (10000 => 2500, 1000, etc) - var mult = 4; - while (m / mult > 24) { - m /= mult; - mult = 2.5; // First mult 4, the next ones 2.5 - bigSplits.push(m >> 0); - i++; - } - } - } - numSplits -= bigSplits.length; - - for (var k = 0; k < bigSplits.length; k++) { - angle = Math.random() * 2 * Math.PI; // Random directions - this.gameServer.splitPlayerCell(client, consumer, angle, bigSplits[k]); - } - - // Splitting - for (var k = 0; k < numSplits; k++) { - angle = Math.random() * 2 * Math.PI; // Random directions - this.gameServer.splitPlayerCell(client, consumer, angle, splitMass); - } + + var maxSplit = this.gameServer.config.playerMaxCells - consumer.owner.cells.length; + var masses = this.gameServer.splitMass(consumer.getMass(), maxSplit + 1); + if (masses.length < 2) { + return; + } + + // Balance mass around center & skip first mass (==consumer mass) + var massesMix = []; + for (var i = 1; i < masses.length; i += 2) + massesMix.push(masses[i]); + for (var i = 2; i < masses.length; i += 2) + massesMix.push(masses[i]); + masses = massesMix; + + // Blow up the cell... + var angle = 2 * Math.PI * Math.random(); + var step = 2 * Math.PI / masses.length; + for (var i = 0; i < masses.length; i++) { + if (!this.gameServer.splitPlayerCell(client, consumer, angle, masses[i])) { + break; + } + angle += step; + if (angle >= 2 * Math.PI) { + angle -= 2 * Math.PI; + } + } }; Virus.prototype.onAdd = function(gameServer) { From 514e0cab6d33e09b4b4dc24d0a2b756fadcc4bb4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 14:53:08 +0200 Subject: [PATCH 283/417] fix default virus amount --- src/GameServer.js | 4 ++-- src/gameserver.ini | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 32fd95931..2744f3e4d 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -85,8 +85,8 @@ function GameServer() { virusMinSize: 100, // Minimum virus size (vanilla 100) virusMaxSize: 140, // Maximum virus size (vanilla 140) - virusMinAmount: 10, // Minimum number of viruses on the map. - virusMaxAmount: 50, // Maximum number of viruses on the map. If this number is reached, then ejected cells will pass through viruses. + virusMinAmount: 70, // Minimum number of viruses on the map. + virusMaxAmount: 100, // Maximum number of viruses on the map. If this number is reached, then ejected cells will pass through viruses. ejectSize: 38, // Size of ejected cells (vanilla 38) ejectCooldown: 3, // min ticks between ejects diff --git a/src/gameserver.ini b/src/gameserver.ini index 517835a13..4e8ad238b 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -73,8 +73,8 @@ spawnInterval = 20 # virusMaxSize: vanilla 140 (mass = 140*140/100 = 196) virusMinSize = 100 virusMaxSize = 140 -virusMinAmount = 10 -virusMaxAmount = 50 +virusMinAmount = 70 +virusMaxAmount = 100 # [Ejected Mass] # ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) From 0e3e6e8d9a6cd25bb42baec1a66cf6e27d464ff2 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 15:23:43 +0200 Subject: [PATCH 284/417] update readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 52d708475..7fda47339 100644 --- a/README.md +++ b/README.md @@ -87,10 +87,10 @@ Just replace `127.0.0.1:50000` in the url to the server IP and port to play. URL | Protocol | Description --- | --- | --- -http://agar.io/?ip=127.0.0.1:50000 | 8 | Vanilla -http://ogar.mivabe.nl/?ip=127.0.0.1:50000 | early 5 | MivaBe, pretty smooth, custom graphics (anime) -http://play.ogarul.tk/?ip=127.0.0.1:50000 | 4 | OgarUL, vanilla style (sends invalid protocol=1) -http://c0nsume.me/private4.php?ip=127.0.0.1:50000 | 5 | vanilla style +http://agar.io/?ip=127.0.0.1:443 | 8 | Vanilla +http://ogar.mivabe.nl/?ip=127.0.0.1:443 | early 5 | MivaBe, pretty smooth, custom graphics (anime) +http://play.ogarul.tk/?ip=127.0.0.1:443 | 4 | OgarUL, vanilla style (sends invalid protocol=1) +http://c0nsume.me/private4.php?ip=127.0.0.1:443 | 5 | vanilla style ###MultiOgar Servers From fd87888ad6ca9560ed1db9042a28d03113827901 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 15:48:54 +0200 Subject: [PATCH 285/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7fda47339..86bb8b90b 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Fixed pop-split behavior * Added spectate walk through feature (use Space key in spectate mode to lock the current player or to lock the next one. Use key Q to reset into the normal mode. Locked player is highlighted on leaderboard) * Fixed cell-split order, now split-run works ok * A little performance improvement for split/eject From cd717446a447f16c5410069553817e075b0447f1 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 17:32:59 +0200 Subject: [PATCH 286/417] code refactoring & optimizations --- src/GameServer.js | 81 +++++++++++------------------------ src/PacketHandler.js | 2 +- src/PlayerTracker.js | 3 +- src/gamemodes/Experimental.js | 6 +-- 4 files changed, 29 insertions(+), 63 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 2744f3e4d..c0dff9723 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -305,29 +305,6 @@ GameServer.prototype.getRandomPosition = function() { }; }; -GameServer.prototype.getRandomSpawn = function(size) { - // Random and secure spawns for players and viruses - var pos = this.getRandomPosition(); - var unsafe = this.willCollide(pos, size); - if (!unsafe) return pos; - - // just shift offset and try again - var attempt = 1; - var maxAttempt = 4; - var dirx = pos.x < this.border.centerx ? 1 : -1; - var diry = pos.y < this.border.centery ? 1 : -1; - var stepx = this.border.width / (2 * maxAttempt); - var stepy = this.border.height / (2 * maxAttempt); - while (unsafe && attempt < maxAttempt) { - pos.x += stepx * dirx; - pos.y += stepy * diry; - unsafe = this.willCollide(pos, size); - attempt++; - } - // failed to find safe position - return null; -}; - GameServer.prototype.getGrayColor = function (rgb) { var luminance = Math.min(255, (rgb.r * 0.2125 + rgb.g * 0.7154 + rgb.b * 0.0721)) >>> 0; return { @@ -653,8 +630,8 @@ GameServer.prototype.spawnFood = function() { GameServer.prototype.spawnVirus = function () { // Spawns a virus - var pos = this.getRandomSpawn(this.config.virusMinSize); - if (pos == null) { + var pos = this.getRandomPosition(); + if (this.willCollide(pos, this.config.virusMinSize)) { // cannot find safe position => do not spawn return; } @@ -683,9 +660,9 @@ GameServer.prototype.spawnPlayer = function(player, pos, size) { } if (pos == null) { // Get random pos - pos = this.getRandomSpawn(this.config.playerMinSize); - if (pos == null) { - // cannot find safe position => spawn anyway at random position + var pos = this.getRandomPosition(); + // 10 attempts to find safe position + for (var i = 0; i < 10 && this.willCollide(pos, this.config.playerMinSize); i++) { pos = this.getRandomPosition(); } } @@ -708,28 +685,18 @@ GameServer.prototype.spawnPlayer = function(player, pos, size) { GameServer.prototype.willCollide = function (pos, size) { // Look if there will be any collision with the current nodes var bound = { - minx: pos.x - size - 10, - miny: pos.y - size - 10, - maxx: pos.x + size + 10, - maxy: pos.y + size + 10 + minx: pos.x - size, + miny: pos.y - size, + maxx: pos.x + size, + maxy: pos.y + size }; return this.quadTree.any( bound, function (item) { - return item.cell.cellType != 1; // ignore food + return item.cell.cellType == 0; // check players only }); }; -GameServer.prototype.getDist = function (x1, y1, x2, y2) { - var dx = x2 - x1; - var dy = y2 - x1; - return Math.sqrt(dx * dx + dy * dy); -}; - -GameServer.prototype.abs = function (x) { - return x < 0 ? -x : x; -}; - // Checks cells for collision. // Returns collision manifold or null if there is no collision GameServer.prototype.checkCellCollision = function(cell, check) { @@ -1306,8 +1273,9 @@ GameServer.prototype.getNearestVirus = function(cell) { for (var i = 0; i < this.nodesVirus.length; i++) { var check = this.nodesVirus[i]; if (check === null) continue; - if (this.checkCellCollision(cell, check) != null) + if (this.checkCellCollision(cell, check) != null) { return check; + } } }; @@ -1331,18 +1299,19 @@ GameServer.prototype.updateMassDecay = function() { } }; -var configFileName = './gameserver.ini'; +var fileNameConfig = './gameserver.ini'; +var fileNameIpBan = './ipbanlist.txt'; GameServer.prototype.loadConfig = function () { try { - if (!fs.existsSync(configFileName)) { + if (!fs.existsSync(fileNameConfig)) { // No config Logger.warn("Config not found... Generating new config"); // Create a new config - fs.writeFileSync(configFileName, ini.stringify(this.config), 'utf-8'); + fs.writeFileSync(fileNameConfig, ini.stringify(this.config), 'utf-8'); } else { // Load the contents of the config file - var load = ini.parse(fs.readFileSync(configFileName, 'utf-8')); + var load = ini.parse(fs.readFileSync(fileNameConfig, 'utf-8')); // Replace all the default config's values with the loaded config's values for (var key in load) { if (this.config.hasOwnProperty(key)) { @@ -1354,34 +1323,32 @@ GameServer.prototype.loadConfig = function () { } } catch (err) { Logger.error(err.stack); - Logger.error("Failed to load " + configFileName + ": " + err.message); + Logger.error("Failed to load " + fileNameConfig + ": " + err.message); } // check config (min player size = 32 => mass = 10.24) this.config.playerMinSize = Math.max(32, this.config.playerMinSize); }; GameServer.prototype.loadIpBanList = function () { - var fileName = "./ipbanlist.txt"; try { - if (fs.existsSync(fileName)) { + if (fs.existsSync(fileNameIpBan)) { // Load and input the contents of the ipbanlist file - this.ipBanList = fs.readFileSync(fileName, "utf8").split(/[\r\n]+/).filter(function (x) { + this.ipBanList = fs.readFileSync(fileNameIpBan, "utf8").split(/[\r\n]+/).filter(function (x) { return x != ''; // filter empty lines }); Logger.info(this.ipBanList.length + " IP ban records loaded."); } else { - Logger.warn(fileName + " is missing."); + Logger.warn(fileNameIpBan + " is missing."); } } catch (err) { Logger.error(err.stack); - Logger.error("Failed to load " + fileName + ": " + err.message); + Logger.error("Failed to load " + fileNameIpBan + ": " + err.message); } }; GameServer.prototype.saveIpBanList = function () { - var fileName = "./ipbanlist.txt"; try { - var blFile = fs.createWriteStream(fileName); + var blFile = fs.createWriteStream(fileNameIpBan); // Sort the blacklist and write. this.ipBanList.sort().forEach(function (v) { blFile.write(v + '\n'); @@ -1390,7 +1357,7 @@ GameServer.prototype.saveIpBanList = function () { Logger.info(this.ipBanList.length + " IP ban records saved."); } catch (err) { Logger.error(err.stack); - Logger.error("Failed to save " + fileName + ": " + err.message); + Logger.error("Failed to save " + fileNameIpBan + ": " + err.message); } }; diff --git a/src/PacketHandler.js b/src/PacketHandler.js index cd9271852..d680a0340 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -128,7 +128,7 @@ PacketHandler.prototype.handleMessage = function(message) { break; reader.skipBytes(rvLength); // reserved var text = null; - if (this.protocol <= 5) + if (this.protocol < 6) text = reader.readStringZeroUnicode(); else text = reader.readStringZeroUtf8(); diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index b1427fffa..7d6efe97a 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -201,9 +201,9 @@ PlayerTracker.prototype.joinGame = function (name, skin) { PlayerTracker.prototype.update = function () { if (this.isRemoved) return; // Handles disconnection - var time = +new Date; if (!this.socket.isConnected) { // wait for playerDisconnectTime + var time = +new Date; var dt = (time - this.socket.closeTime) / 1000; if (this.cells.length == 0 || dt >= this.gameServer.config.playerDisconnectTime) { // Remove all client cells @@ -228,6 +228,7 @@ PlayerTracker.prototype.update = function () { } // Check timeout if (!this.isCloseRequested && this.gameServer.config.serverTimeout) { + var time = +new Date; var dt = (time - this.socket.lastAliveTime) / 1000; if (dt >= this.gameServer.config.serverTimeout) { this.socket.close(1000, "Connection timeout"); diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 7567cb92f..80a4cfeeb 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -32,8 +32,8 @@ Experimental.prototype.spawnMotherCell = function (gameServer) { return; } // Spawns a mother cell - var pos = gameServer.getRandomSpawn(149); - if (pos == null) { + var pos = gameServer.getRandomPosition(); + if (gameServer.willCollide(pos, 149)) { // cannot find safe position => do not spawn return; } @@ -74,7 +74,6 @@ Experimental.prototype.onServerInit = function(gameServer) { Logger.error("Experimental.onServerInit.MotherVirus.onRemove: Tried to remove a non existing virus!"); } }; - //gameServer.getRandomSpawn = gameServer.getRandomPosition; }; Experimental.prototype.onChange = function (gameServer) { @@ -87,7 +86,6 @@ Experimental.prototype.onChange = function (gameServer) { Entity.Virus.prototype.onEat = require('../Entity/Virus').prototype.onEat; Entity.MotherCell.prototype.onAdd = require('../Entity/MotherCell').prototype.onAdd; Entity.MotherCell.prototype.onRemove = require('../Entity/MotherCell').prototype.onRemove; - //gameServer.getRandomSpawn = require('../GameServer').prototype.getRandomSpawn; }; Experimental.prototype.onTick = function(gameServer) { From 0800b3376f62dc2438ce07ebe5c869b7cd8fc828 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 18:37:57 +0200 Subject: [PATCH 287/417] code refactoring --- src/GameServer.js | 24 ++++++++---------------- src/gameserver.ini | 4 ++-- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index c0dff9723..1276285e4 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -77,15 +77,15 @@ function GameServer() { foodMinSize: 10, // Minimum food size (vanilla 10) foodMaxSize: 20, // Maximum food size (vanilla 20) - foodMinAmount: 1000, // Minimum food cells on the map + foodMinAmount: 1000, // Minimum food cells on the map foodMaxAmount: 2000, // Maximum food cells on the map - foodSpawnAmount: 10, // The number of food to spawn per interval + foodSpawnAmount: 30, // The number of food to spawn per interval foodMassGrow: 1, // Enable food mass grow ? spawnInterval: 20, // The interval between each food cell spawn in ticks (1 tick = 50 ms) virusMinSize: 100, // Minimum virus size (vanilla 100) virusMaxSize: 140, // Maximum virus size (vanilla 140) - virusMinAmount: 70, // Minimum number of viruses on the map. + virusMinAmount: 50, // Minimum number of viruses on the map. virusMaxAmount: 100, // Maximum number of viruses on the map. If this number is reached, then ejected cells will pass through viruses. ejectSize: 38, // Size of ejected cells (vanilla 38) @@ -429,17 +429,6 @@ GameServer.prototype.removeNode = function(node) { node.onRemove(this); }; -GameServer.prototype.updateSpawn = function() { - // Spawn food - this.tickSpawn++; - if (this.tickSpawn >= this.config.spawnInterval) { - this.tickSpawn = 0; // Reset - - this.updateFood(); // Spawn food - this.updateVirus(); // Spawn viruses - } -}; - GameServer.prototype.updateClients = function () { for (var i = 0; i < this.clients.length; i++) { var socket = this.clients[i]; @@ -547,9 +536,12 @@ GameServer.prototype.mainLoop = function() { // Loop main functions if (this.run) { this.updateMoveEngine(); - this.updateSpawn(); + if ((this.getTick() % this.config.spawnInterval) == 0) { + this.updateFood(); // Spawn food + this.updateVirus(); // Spawn viruses + } this.gameMode.onTick(this); - if ((this.getTick() % (1000 / 40)) == 0) { + if (((this.getTick()+3) % (1000 / 40)) == 0) { // once per second this.updateMassDecay(); } diff --git a/src/gameserver.ini b/src/gameserver.ini index 4e8ad238b..f29e10592 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -65,7 +65,7 @@ foodMinSize = 10 foodMaxSize = 20 foodMinAmount = 1000 foodMaxAmount = 2000 -foodSpawnAmount = 10 +foodSpawnAmount = 30 foodMassGrow = 1 spawnInterval = 20 @@ -73,7 +73,7 @@ spawnInterval = 20 # virusMaxSize: vanilla 140 (mass = 140*140/100 = 196) virusMinSize = 100 virusMaxSize = 140 -virusMinAmount = 70 +virusMinAmount = 50 virusMaxAmount = 100 # [Ejected Mass] From 60a21006806e20c6f32fc0259c4ec667c264a68f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 18:44:27 +0200 Subject: [PATCH 288/417] code refactoring --- src/GameServer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index 1276285e4..1742d1eaf 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -46,7 +46,6 @@ function GameServer() { this.mainLoopBind = null; this.tickCounter = 0; - this.tickSpawn = 0; // Used with spawning food this.setBorder(10000, 10000); From e5a24e0710a4310e79fbc462d96a2dab443824fc Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 18:53:29 +0200 Subject: [PATCH 289/417] code refactoring --- src/PlayerTracker.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 7d6efe97a..3ecc643d6 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -287,7 +287,6 @@ PlayerTracker.prototype.update = function () { oldIndex++; } for (; newIndex < newVisible.length; ) { - var node = newVisible[newIndex]; addNodes.push(newVisible[newIndex]); newIndex++; } From 7b3862447b495ee428b8e35bb1bbfea95cabf367 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 19:41:43 +0200 Subject: [PATCH 290/417] fix change command error handling --- src/modules/CommandList.js | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 1900b3264..b16025325 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -209,23 +209,30 @@ Commands.list = { gameServer.gameMode.updateLB = gm.updateLB; console.log("Successfully reset leaderboard"); }, - change: function(gameServer, split) { + change: function (gameServer, split) { + if (split.length < 3) { + Logger.warn("Invalid command arguments"); + return; + } var key = split[1]; var value = split[2]; - + // Check if int/float if (value.indexOf('.') != -1) { value = parseFloat(value); } else { value = parseInt(value); } - - if (typeof gameServer.config[key] != 'undefined') { - gameServer.config[key] = value; - console.log("Set " + key + " to " + value); - } else { - console.log("Invalid config value"); + if (value == null || isNaN(value)) { + Logger.warn("Invalid value: " + split[2]); + return; + } + if (!gameServer.config.hasOwnProperty(key)) { + Logger.warn("Unknown config value: " + key); + return; } + gameServer.config[key] = value; + console.log("Set " + key + " to " + value); }, clear: function() { process.stdout.write("\u001b[2J\u001b[0;0H"); From 4f63d9fa44dd8b27df340e1cd4d39907c3581a14 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 20:13:07 +0200 Subject: [PATCH 291/417] increase default connection timeout to 5 minutes --- src/GameServer.js | 2 +- src/gameserver.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 1742d1eaf..84f720125 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -51,7 +51,7 @@ function GameServer() { // Config this.config = { - serverTimeout: 30, // Seconds to keep connection alive for non-responding client + serverTimeout: 300, // Seconds to keep connection alive for non-responding client serverMaxConnections: 64, // Maximum number of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum number of connections from the same IP (0 for no limit) serverPort: 443, // Server port diff --git a/src/gameserver.ini b/src/gameserver.ini index f29e10592..7761df7dc 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -32,7 +32,7 @@ # serverName: Server name, for example "My great server" # serverWelcome1: First server welcome message # serverWelcome2: Second server welcome message (optional, for info, etc) -serverTimeout = 30 +serverTimeout = 300 serverMaxConnections = 64 serverIpLimit = 4 serverPort = 443 From adc0ebb443dce5a5b76d801a65ba40915d9aaddb Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 28 Jun 2016 22:13:04 +0200 Subject: [PATCH 292/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86bb8b90b..586c61779 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ npm install # Run the server: cd src -node index.js +sudo node index.js ``` From 915daf416fdfc16a8f8467cd4b23db62ae7a0f45 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 00:01:16 +0200 Subject: [PATCH 293/417] add ServerStat message --- src/PacketHandler.js | 9 +++++++++ src/packet/ServerStat.js | 42 ++++++++++++++++++++++++++++++++++++++++ src/packet/index.js | 1 + 3 files changed, 52 insertions(+) create mode 100644 src/packet/ServerStat.js diff --git a/src/PacketHandler.js b/src/PacketHandler.js index d680a0340..ee6bea58f 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -12,6 +12,7 @@ function PacketHandler(gameServer, socket) { this.pressQ = false; this.pressW = false; this.pressSpace = false; + this.lastStatTime = +new Date; } module.exports = PacketHandler; @@ -134,6 +135,14 @@ PacketHandler.prototype.handleMessage = function(message) { text = reader.readStringZeroUtf8(); this.gameServer.onChatMessage(this.socket.playerTracker, null, text); break; + case 254: + // Server stat + var time = +new Date; + var dt = time - this.lastStatTime; + this.lastStatTime = time; + if (dt < 1000) break; + this.socket.sendPacket(new Packet.ServerStat(this.socket.playerTracker)); + break; default: break; } diff --git a/src/packet/ServerStat.js b/src/packet/ServerStat.js new file mode 100644 index 000000000..02370ff46 --- /dev/null +++ b/src/packet/ServerStat.js @@ -0,0 +1,42 @@ +// Import +var BinaryWriter = require("./BinaryWriter"); + +function ServerStat(playerTracker) { + this.playerTracker = playerTracker; +}; + +module.exports = ServerStat; + +ServerStat.prototype.build = function (protocol) { + var gameServer = this.playerTracker.gameServer; + // Get server statistics + var totalPlayers = 0; + var alivePlayers = 0; + var spectPlayers = 0; + for (var i = 0; i < gameServer.clients.length; i++) { + var socket = gameServer.clients[i]; + if (socket == null || !socket.isConnected) + continue; + totalPlayers++; + if (socket.playerTracker.cells.length > 0) + alivePlayers++; + else + spectPlayers++; + } + var obj = { + 'name': gameServer.config.serverName, + 'mode': gameServer.gameMode.name, + 'uptime': process.uptime() >>> 0, + 'update': gameServer.updateTimeAvg.toFixed(3), + 'playersTotal': totalPlayers, + 'playersAlive': alivePlayers, + 'playersSpect': spectPlayers, + 'playersLimit': gameServer.config.serverMaxConnections + }; + var json = JSON.stringify(obj); + // Serialize + var writer = new BinaryWriter(); + writer.writeUInt8(254); // Message Id + writer.writeStringZeroUtf8(json); // JSON + return writer.toBuffer(); +}; diff --git a/src/packet/index.js b/src/packet/index.js index 0c87983de..a78ad28f5 100644 --- a/src/packet/index.js +++ b/src/packet/index.js @@ -6,6 +6,7 @@ module.exports = { ClearAll: require('./ClearAll'), ClearOwned: require('./ClearOwned'), UpdatePosition: require('./UpdatePosition'), + ServerStat: require('./ServerStat'), SetBorder: require('./SetBorder'), UpdateNodes: require('./UpdateNodes'), UpdateLeaderboard: require('./UpdateLeaderboard'), From 356bc4526e6862c45ef6b6b79d247f5551b70553 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 03:28:15 +0200 Subject: [PATCH 294/417] protocol optimizations --- src/packet/AddNode.js | 5 ++--- src/packet/ClearAll.js | 2 +- src/packet/ClearOwned.js | 2 +- src/packet/DrawLine.js | 4 ++-- src/packet/UpdatePosition.js | 8 ++++---- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/packet/AddNode.js b/src/packet/AddNode.js index deb8d9ffa..a8539d7c1 100644 --- a/src/packet/AddNode.js +++ b/src/packet/AddNode.js @@ -6,9 +6,8 @@ function AddNode(playerTracker, item) { module.exports = AddNode; AddNode.prototype.build = function(protocol) { - // Only add player controlled cells with this packet or it will bug the camera var buffer = new Buffer(5); - buffer.writeUInt8(0x20, 0); // Packet ID - buffer.writeUInt32LE((this.item.nodeId ^ this.playerTracker.scrambleId) >>> 0, 1); + buffer.writeUInt8(0x20, 0, true); // Packet ID + buffer.writeUInt32LE((this.item.nodeId ^ this.playerTracker.scrambleId) >>> 0, 1, true); return buffer; }; diff --git a/src/packet/ClearAll.js b/src/packet/ClearAll.js index d6a50783d..59c4f742f 100644 --- a/src/packet/ClearAll.js +++ b/src/packet/ClearAll.js @@ -4,6 +4,6 @@ module.exports = ClearAll; ClearAll.prototype.build = function (protocol) { var buffer = new Buffer(1); - buffer.writeUInt8(0x12, 0); + buffer.writeUInt8(0x12, 0, true); return buffer; }; \ No newline at end of file diff --git a/src/packet/ClearOwned.js b/src/packet/ClearOwned.js index 2ebafd787..010e84255 100644 --- a/src/packet/ClearOwned.js +++ b/src/packet/ClearOwned.js @@ -4,6 +4,6 @@ module.exports = ClearOwned; ClearOwned.prototype.build = function (protocol) { var buffer = new Buffer(1); - buffer.writeUInt8(0x14, 0); + buffer.writeUInt8(0x14, 0, true); return buffer; }; \ No newline at end of file diff --git a/src/packet/DrawLine.js b/src/packet/DrawLine.js index 3e00b2183..e78091c33 100644 --- a/src/packet/DrawLine.js +++ b/src/packet/DrawLine.js @@ -8,7 +8,7 @@ module.exports = DrawLine; DrawLine.prototype.build = function(protocol) { var buffer = new Buffer(5); buffer.writeUInt8(0x15, 0); - buffer.writeInt16LE(this.x, 1); - buffer.writeInt16LE(this.y, 3); + buffer.writeInt16LE(this.x, 1, true); + buffer.writeInt16LE(this.y, 3, true); return buffer; }; diff --git a/src/packet/UpdatePosition.js b/src/packet/UpdatePosition.js index 3cafc4a81..7a0b4c9b7 100644 --- a/src/packet/UpdatePosition.js +++ b/src/packet/UpdatePosition.js @@ -10,13 +10,13 @@ module.exports = UpdatePosition; UpdatePosition.prototype.build = function(protocol) { var buffer = new Buffer(13); var offset = 0; - buffer.writeUInt8(0x11, offset); + buffer.writeUInt8(0x11, offset, true); offset += 1; - buffer.writeFloatLE(this.x + this.playerTracker.scrambleX, offset); + buffer.writeFloatLE(this.x + this.playerTracker.scrambleX, offset, true); offset += 4; - buffer.writeFloatLE(this.y + this.playerTracker.scrambleY, offset); + buffer.writeFloatLE(this.y + this.playerTracker.scrambleY, offset, true); offset += 4; - buffer.writeFloatLE(this.scale, offset); + buffer.writeFloatLE(this.scale, offset, true); offset += 4; return buffer; }; From 76d71df45504874b7cfff77ad1326238d8fc04e1 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 05:03:53 +0200 Subject: [PATCH 295/417] protocol optimizations --- src/entity/Cell.js | 8 -------- src/entity/PlayerCell.js | 8 -------- src/packet/UpdateNodes.js | 21 ++++++++++++++++----- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 08e50099b..c63dc099b 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -35,14 +35,6 @@ module.exports = Cell; // Fields not defined by the constructor are considered private and need a getter/setter to access from a different class -Cell.prototype.getName = function() { - return ""; -}; - -Cell.prototype.getSkin = function () { - return ""; -}; - Cell.prototype.setColor = function(color) { this.color.r = color.r; this.color.g = color.g; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 331cf137a..7bbc7ceac 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -12,14 +12,6 @@ PlayerCell.prototype = new Cell(); // Main Functions -PlayerCell.prototype.getName = function () { - return this.owner.name; -}; - -PlayerCell.prototype.getSkin = function () { - return this.owner.skin; -}; - PlayerCell.prototype.updateRemerge = function () { var age = this.getAge(this.gameServer.getTick()); if (age < 15) { diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index aa81b9d12..81ff34c5f 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -67,7 +67,10 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { continue; var cellX = node.position.x + scrambleX; var cellY = node.position.y + scrambleY; - var cellName = node.getName(); + var cellName = null; + if (node.owner) { + cellName = node.owner.getName(); + } // Write update record writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID @@ -137,8 +140,12 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { var cellX = node.position.x + scrambleX; var cellY = node.position.y + scrambleY; - var skinName = node.getSkin(); - var cellName = node.getName(); + var skinName = null; + var cellName = null; + if (node.owner) { + skinName = node.owner.getSkin(); + cellName = node.owner.getName(); + } // Write update record writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID @@ -216,8 +223,12 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { var cellX = node.position.x + scrambleX; var cellY = node.position.y + scrambleY; - var skinName = node.getSkin(); - var cellName = node.getName(); + var skinName = null; + var cellName = null; + if (node.owner) { + skinName = node.owner.getSkin(); + cellName = node.owner.getName(); + } // Write update record writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0); // Cell ID From 2033f6ec253f827af4c1566b73b068a52eb53216 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 05:46:47 +0200 Subject: [PATCH 296/417] protocol optimizations --- src/PlayerTracker.js | 47 +++++++++++++++++++++++++++++++++----- src/modules/CommandList.js | 8 +++---- src/packet/UpdateNodes.js | 36 ++++++++++++++--------------- 3 files changed, 62 insertions(+), 29 deletions(-) diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 3ecc643d6..f06b8f898 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -1,5 +1,7 @@ var Packet = require('./packet'); var GameServer = require('./GameServer'); +var BinaryWriter = require("./packet/BinaryWriter"); + function PlayerTracker(gameServer, socket) { this.gameServer = gameServer; @@ -7,8 +9,11 @@ function PlayerTracker(gameServer, socket) { this.pID = -1; this.isRemoved = false; this.isCloseRequested = false; - this.name = ""; - this.skin = ""; + this._name = ""; + this._skin = ""; + this._nameUtf8 = null; + this._nameUnicode = null; + this._skinUtf8 = null; this.color = { r: 0, g: 0, b: 0 }; this.visibleNodes = []; this.cells = []; @@ -97,24 +102,54 @@ PlayerTracker.prototype.getFriendlyName = function () { }; PlayerTracker.prototype.setName = function(name) { - this.name = name; + this._name = name; + if (!name || name.length < 1) { + this._nameUnicode = null; + this._nameUtf8 = null; + return; + } + var writer = new BinaryWriter() + writer.writeStringZeroUnicode(name); + this._nameUnicode = writer.toBuffer(); + writer = new BinaryWriter() + writer.writeStringZeroUtf8(name); + this._nameUtf8 = writer.toBuffer(); }; PlayerTracker.prototype.getName = function() { - return this.name; + return this._name; }; PlayerTracker.prototype.setSkin = function (skin) { - this.skin = skin; + this._skin = skin; + if (!skin || skin.length < 1) { + this._skinUtf8 = null; + return; + } + var writer = new BinaryWriter() + writer.writeStringZeroUtf8(skin); + this._skinUtf8 = writer.toBuffer(); }; PlayerTracker.prototype.getSkin = function () { if (this.gameServer.gameMode.haveTeams) { return ""; } - return this.skin; + return this._skin; }; +PlayerTracker.prototype.getNameUtf8 = function () { + return this._nameUtf8; +} + +PlayerTracker.prototype.getNameUnicode = function () { + return this._nameUnicode; +} + +PlayerTracker.prototype.getSkinUtf8 = function () { + return this._skinUtf8; +} + PlayerTracker.prototype.getColor = function (color) { return this.color; }; diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index b16025325..0d0ff8fcb 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -375,7 +375,7 @@ Commands.list = { client.cells[j].setSize(size); } - console.log("Set mass of " + client.name + " to " + (size*size/100).toFixed(3)); + console.log("Set mass of " + client.getFriendlyName() + " to " + (size*size/100).toFixed(3)); break; } } @@ -453,8 +453,8 @@ Commands.list = { var client = gameServer.clients[i].playerTracker; if (client.pID == id) { - console.log("Changing " + client.name + " to " + name); - client.name = name; + console.log("Changing " + client.getFriendlyName() + " to " + name); + client.setName(name); return; } } @@ -583,7 +583,7 @@ Commands.list = { gameServer.updateNodeQuad(client.cells[j]); } - console.log("Teleported " + client.name + " to (" + pos.x + " , " + pos.y + ")"); + console.log("Teleported " + client.getFriendlyName() + " to (" + pos.x + " , " + pos.y + ")"); break; } } diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index 81ff34c5f..ec155382c 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -69,7 +69,7 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { var cellY = node.position.y + scrambleY; var cellName = null; if (node.owner) { - cellName = node.owner.getName(); + cellName = node.owner.getNameUnicode(); } // Write update record @@ -91,8 +91,8 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { flags |= 0x20; // isEjected writer.writeUInt8(flags >>> 0); // Flags - if (node.owner != null && cellName != null && cellName.length > 0) - writer.writeStringZeroUnicode(cellName); // Name + if (cellName != null) + writer.writeBytes(cellName); // Name else writer.writeUInt16(0); // Name } @@ -143,8 +143,8 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { var skinName = null; var cellName = null; if (node.owner) { - skinName = node.owner.getSkin(); - cellName = node.owner.getName(); + skinName = node.owner.getSkinUtf8(); + cellName = node.owner.getNameUnicode(); } // Write update record @@ -160,7 +160,7 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { var flags = 0; if (node.isSpiked) flags |= 0x01; // isVirus - if (node.owner != null && skinName != null && skinName.length > 0) + if (skinName != null) flags |= 0x04; // isSkinPresent if (node.isAgitated) flags |= 0x10; // isAgitated @@ -169,10 +169,10 @@ UpdateNodes.prototype.writeUpdateItems5 = function (writer) { writer.writeUInt8(flags >>> 0); // Flags if (flags & 0x04) - writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 + writer.writeBytes(skinName); // Skin Name in UTF8 - if (node.owner != null && cellName != null && cellName.length > 0) - writer.writeStringZeroUnicode(cellName); // Name + if (cellName != null) + writer.writeBytes(cellName); // Name else writer.writeUInt16(0); // Name } @@ -226,8 +226,8 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { var skinName = null; var cellName = null; if (node.owner) { - skinName = node.owner.getSkin(); - cellName = node.owner.getName(); + skinName = node.owner.getSkinUtf8(); + cellName = node.owner.getNameUtf8(); } // Write update record @@ -241,12 +241,10 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { flags |= 0x01; // isVirus if (true) flags |= 0x02; // isColorPresent (always for added) - if (node.owner != null) { - if (skinName != null && skinName.length > 1) - flags |= 0x04; // isSkinPresent - if (cellName != null && cellName.length > 1) - flags |= 0x08; // isNamePresent - } + if (skinName != null) + flags |= 0x04; // isSkinPresent + if (cellName != null) + flags |= 0x08; // isNamePresent if (node.isAgitated) flags |= 0x10; // isAgitated if (node.cellType == 3) @@ -260,9 +258,9 @@ UpdateNodes.prototype.writeUpdateItems6 = function (writer) { writer.writeUInt8(color.b >>> 0); // Color B } if (flags & 0x04) - writer.writeStringZeroUtf8(skinName); // Skin Name in UTF8 + writer.writeBytes(skinName); // Skin Name in UTF8 if (flags & 0x08) - writer.writeStringZeroUtf8(cellName); // Cell Name in UTF8 + writer.writeBytes(cellName); // Cell Name in UTF8 } writer.writeUInt32(0); // Cell Update record terminator }; From 3ec2a6b64342094412491ada90ca0e22b500283c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 05:57:18 +0200 Subject: [PATCH 297/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 586c61779..050005549 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Added protocol optimizations to reduce lags on cell multi split * Fixed pop-split behavior * Added spectate walk through feature (use Space key in spectate mode to lock the current player or to lock the next one. Use key Q to reset into the normal mode. Locked player is highlighted on leaderboard) * Fixed cell-split order, now split-run works ok From 2a24138dcf949e757b59ad8594973a61743a36dd Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 15:16:50 +0200 Subject: [PATCH 298/417] reduce lags from new connections when server full --- src/GameServer.js | 52 ++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 84f720125..632c46190 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -23,6 +23,7 @@ function GameServer() { this.lastNodeId = 1; this.lastPlayerId = 1; this.clients = []; + this.socketCount = 0; this.largestClient; // Required for spectators this.nodes = []; this.nodesVirus = []; // Virus nodes @@ -181,31 +182,26 @@ GameServer.prototype.onServerSocketOpen = function () { }; GameServer.prototype.onClientSocketOpen = function (ws) { - // Check blacklist first (if enabled). + if (this.config.serverMaxConnections > 0 && this.socketCount >= this.config.serverMaxConnections) { + ws.close(1000, "No slots"); + return; + } if (this.ipBanList && this.ipBanList.length > 0 && this.ipBanList.indexOf(ws._socket.remoteAddress) >= 0) { - // IP banned ws.close(1000, "IP banned"); return; } - var totalConnections = 0; - var ipConnections = 0; - for (var i = 0; i < this.clients.length; i++) { - var socket = this.clients[i]; - if (socket == null || socket.isConnected == null) - continue; - totalConnections++; - if (socket.isConnected && socket.remoteAddress == ws._socket.remoteAddress) + if (this.config.serverIpLimit > 0) { + var ipConnections = 0; + for (var i = 0; i < this.clients.length; i++) { + var socket = this.clients[i]; + if (!socket.isConnected || socket.remoteAddress != ws._socket.remoteAddress) + continue; ipConnections++; - } - if (this.config.serverMaxConnections > 0 && totalConnections >= this.config.serverMaxConnections) { - // Server full - ws.close(1000, "No slots"); - return; - } - if (this.config.serverIpLimit > 0 && ipConnections >= this.config.serverIpLimit) { - // IP limit reached - ws.close(1000, "IP limit reached"); - return; + } + if (ipConnections >= this.config.serverIpLimit) { + ws.close(1000, "IP limit reached"); + return; + } } ws.isConnected = true; ws.remoteAddress = ws._socket.remoteAddress; @@ -217,33 +213,39 @@ GameServer.prototype.onClientSocketOpen = function (ws) { ws.packetHandler = new PacketHandler(this, ws); ws.playerCommand = new PlayerCommand(this, ws.playerTracker); - var gameServer = this; + var self = this; var onMessage = function (message) { - gameServer.onClientSocketMessage(ws, message); + self.onClientSocketMessage(ws, message); }; var onError = function (error) { - gameServer.onClientSocketError(ws, error); + self.onClientSocketError(ws, error); }; var onClose = function (reason) { - gameServer.onClientSocketClose(ws, reason); + self.onClientSocketClose(ws, reason); }; ws.on('message', onMessage); ws.on('error', onError); ws.on('close', onClose); + this.socketCount++; this.clients.push(ws); }; GameServer.prototype.onClientSocketClose = function (ws, code) { + if (this.socketCount < 1) { + Logger.error("GameServer.onClientSocketClose: socketCount=" + this.socketCount); + } else { + this.socketCount--; + } ws.isConnected = false; ws.sendPacket = function (data) { }; ws.closeReason = { code: ws._closeCode, message: ws._closeMessage }; ws.closeTime = +new Date; Logger.write("DISCONNECTED " + ws.remoteAddress + ":" + ws.remotePort + ", code: " + ws._closeCode + ", reason: \"" + ws._closeMessage + "\", name: \""+ws.playerTracker.getName()+"\""); + // disconnected effect var color = this.getGrayColor(ws.playerTracker.getColor()); ws.playerTracker.setColor(color); ws.playerTracker.setSkin(""); - // disconnected effect ws.playerTracker.cells.forEach(function (cell) { cell.setColor(color); }, this); From 9be0f499ae222fc9c8b7e208753af72a2404e9a2 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 15:34:38 +0200 Subject: [PATCH 299/417] fix high speed after high lag issue --- src/GameServer.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/GameServer.js b/src/GameServer.js index 632c46190..7579efc45 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -519,6 +519,10 @@ GameServer.prototype.timerLoop = function () { setTimeout(this.timerLoopBind, 0); return; } + if (dt > 400) { + // too high lag => resynchronize + this.timeStamp = ts - 40; + } // update average this.updateTimeAvg += 0.5 * (this.updateTime - this.updateTimeAvg); // calculate next From 92826a0964d6d86044bf38b6f0b19212b92dcab7 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 16:03:31 +0200 Subject: [PATCH 300/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 050005549..0630071ff 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Significant performance improvement and more smooth physics * Added protocol optimizations to reduce lags on cell multi split * Fixed pop-split behavior * Added spectate walk through feature (use Space key in spectate mode to lock the current player or to lock the next one. Use key Q to reset into the normal mode. Locked player is highlighted on leaderboard) From c26024a63c59aadb5130d048dc9d460aba5c8ead Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 17:25:38 +0200 Subject: [PATCH 301/417] add server version + update tracker format --- package.json | 2 +- src/GameServer.js | 4 +++- src/PacketHandler.js | 4 +++- src/index.js | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index c16f32583..b4a6ebc74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.0.0", + "version": "1.2.1", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 7579efc45..3d54d66d9 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -2,8 +2,9 @@ var WebSocket = require('ws'); var http = require('http'); var fs = require("fs"); -var ini = require('./modules/ini.js'); var os = require("os"); +var pjson = require('../package.json'); +var ini = require('./modules/ini.js'); var QuadNode = require('./QuadNode.js'); var PlayerCommand = require('./modules/PlayerCommand'); @@ -1529,6 +1530,7 @@ GameServer.prototype.pingServerTracker = function () { '&name=Unnamed Server' + // we cannot use it, because other value will be used as dns name '&opp=' + os.platform() + ' ' + os.arch() + // "win32 x64" '&uptime=' + process.uptime() + // Number of seconds server has been running + '&version=MultiOgar ' + pjson.version + '&start_time=' + this.startTime; var options ={ host: 'ogar.mivabe.nl', diff --git a/src/PacketHandler.js b/src/PacketHandler.js index ee6bea58f..0df676c06 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -1,3 +1,4 @@ +var pjson = require('../package.json'); var Packet = require('./packet'); var BinaryReader = require('./packet/BinaryReader'); @@ -42,8 +43,9 @@ PacketHandler.prototype.handleMessage = function(message) { } // Send handshake response this.socket.sendPacket(new Packet.ClearAll()); - this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, this.gameServer.border, this.gameServer.config.serverGamemode, "MultiOgar")); + this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, this.gameServer.border, this.gameServer.config.serverGamemode, "MultiOgar " + pjson.version)); // Send welcome message + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "MultiOgar " + pjson.version); if (this.gameServer.config.serverWelcome1) this.gameServer.sendChatMessage(null, this.socket.playerTracker, this.gameServer.config.serverWelcome1); if (this.gameServer.config.serverWelcome2) diff --git a/src/index.js b/src/index.js index 979b8b003..9e0f39b02 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,5 @@ // Imports +var pjson = require('../package.json'); var Logger = require('./modules/Logger'); var Commands = require('./modules/CommandList'); var GameServer = require('./GameServer'); @@ -8,7 +9,7 @@ var showConsole = true; // Start msg Logger.start(); -Logger.info("\u001B[1m\u001B[32mMultiOgar\u001B[37m - An open source multi-protocol ogar server\u001B[0m"); +Logger.info("\u001B[1m\u001B[32mMultiOgar "+pjson.version+"\u001B[37m - An open source multi-protocol ogar server\u001B[0m"); // Handle arguments process.argv.forEach(function(val) { From c343f681b2a25b62b18ec1d10d6275dba4f711c9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 17:51:56 +0200 Subject: [PATCH 302/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0630071ff..49f14baee 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Added server version, now you can check if your MultiOgar code is fresh * Significant performance improvement and more smooth physics * Added protocol optimizations to reduce lags on cell multi split * Fixed pop-split behavior From e3b738fda68ee0f3addbf1f93a915da6dce3cc88 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 20:01:09 +0200 Subject: [PATCH 303/417] performance improvement & reduce lags --- package.json | 2 +- src/packet/UpdateLeaderboard.js | 71 +++++++++++++++++++++++---------- 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index b4a6ebc74..b4c883ad7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.1", + "version": "1.2.2", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index d677ef524..530ffcc7d 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -12,15 +12,24 @@ module.exports = UpdateLeaderboard; UpdateLeaderboard.prototype.build = function (protocol) { switch (this.leaderboardType) { - case 48: return this.build48(protocol); // UserText - case 49: return this.build49(protocol); // FFA - case 50: return this.build50(protocol); // Team - default: return null; + case 48: + // UserText + return this.buildUserText(protocol); + case 49: + // FFA + if (protocol < 6) + return this.buildFfa5(); + return this.buildFfa6(); + case 50: + // Team + return this.buildTeam(); + default: + return null; } } // UserText -UpdateLeaderboard.prototype.build48 = function (protocol) { +UpdateLeaderboard.prototype.buildUserText = function (protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x31); // Packet ID writer.writeUInt32(this.leaderboard.length >>> 0); // Number of elements @@ -41,8 +50,8 @@ UpdateLeaderboard.prototype.build48 = function (protocol) { return writer.toBuffer(); }; -// (FFA) Leaderboard Update -UpdateLeaderboard.prototype.build49 = function (protocol) { +// FFA protocol 5 +UpdateLeaderboard.prototype.buildFfa5 = function () { var player = this.playerTracker; if (player.spectate && player.spectateTarget != null) { player = player.spectateTarget; @@ -54,33 +63,55 @@ UpdateLeaderboard.prototype.build49 = function (protocol) { var item = this.leaderboard[i]; if (item == null) return null; // bad leaderboardm just don't send it - var name = item.getName(); - name = name != null ? name : ""; - var id = item == player ? 1 : 0; - if (id && protocol < 6 && item.cells.length > 0) { - // protocol 5- uses player cellId instead of isMe flag + var name = item.getNameUnicode(); + var id = 0; + if (item == player && item.cells.length > 0) { id = item.cells[0].nodeId ^ this.playerTracker.scrambleId; } - writer.writeUInt32(id >>> 0); // isMe flag/cell ID - if (protocol <= 5) - writer.writeStringZeroUnicode(name); + writer.writeUInt32(id >>> 0); // Player cell Id + if (name != null) + writer.writeBytes(name); else - writer.writeStringZeroUtf8(name); + writer.writeUInt16(0); } return writer.toBuffer(); }; -// (Team) Leaderboard Update -UpdateLeaderboard.prototype.build50 = function (protocol) { +// FFA protocol 6 +UpdateLeaderboard.prototype.buildFfa6 = function () { + var player = this.playerTracker; + if (player.spectate && player.spectateTarget != null) { + player = player.spectateTarget; + } var writer = new BinaryWriter(); - writer.writeUInt8(0x32); // Packet ID + writer.writeUInt8(0x31); // Packet ID writer.writeUInt32(this.leaderboard.length >>> 0); // Number of elements for (var i = 0; i < this.leaderboard.length; i++) { var item = this.leaderboard[i]; if (item == null) return null; // bad leaderboardm just don't send it - var value = item; + var name = item.getNameUtf8(); + var id = item == player ? 1 : 0; + + writer.writeUInt32(id >>> 0); // isMe flag + if (name != null) + writer.writeBytes(name); + else + writer.writeUInt8(0); + } + return writer.toBuffer(); +}; + +// Team +UpdateLeaderboard.prototype.buildTeam = function () { + var writer = new BinaryWriter(); + writer.writeUInt8(0x32); // Packet ID + writer.writeUInt32(this.leaderboard.length >>> 0); // Number of elements + for (var i = 0; i < this.leaderboard.length; i++) { + var value = this.leaderboard[i]; + if (value == null) return null; // bad leaderboardm just don't send it + if (isNaN(value)) value = 0; value = value < 0 ? 0 : value; value = value > 1 ? 1 : value; From c66acf138b59ed621cc9d71f186e3000fd158bb7 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 20:59:18 +0200 Subject: [PATCH 304/417] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 49f14baee..ee3165293 100644 --- a/README.md +++ b/README.md @@ -20,9 +20,9 @@ MultiOgar console: ![Screenshot](http://i.imgur.com/pmECJCH.png) -Map 6000x6000, 300 bots, 5000 food, 10 viruses - works pretty smooth with no lags: +1000 bots, 500 viruses, 1000 foods, map 14142x14142 - works pretty smooth: -![Screenshot](http://i.imgur.com/4Wg8s9b.png) +![Screenshot](http://i.imgur.com/ONce8yR.png) ## Install From cdfc8f5cd08c99dc71d27be29def14f5c9910aff Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 21:01:44 +0200 Subject: [PATCH 305/417] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ee3165293..5c6d1a621 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ The goal is to make good and smooth physics and cleanup the code. MultiOgar console: -![Screenshot](http://i.imgur.com/pmECJCH.png) +![Screenshot](https://i.imgur.com/NxQxSXZ.png) 1000 bots, 500 viruses, 1000 foods, map 14142x14142 - works pretty smooth: From 33c5bdea533ded9e7a2f15e5511f774fdcaaf6c8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 21:03:30 +0200 Subject: [PATCH 306/417] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5c6d1a621..8421f8a26 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,12 @@ The goal is to make good and smooth physics and cleanup the code. MultiOgar console: -![Screenshot](https://i.imgur.com/NxQxSXZ.png) - +![Screenshot](http://i.imgur.com/pmECJCH.png) 1000 bots, 500 viruses, 1000 foods, map 14142x14142 - works pretty smooth: -![Screenshot](http://i.imgur.com/ONce8yR.png) +![Screenshot](https://i.imgur.com/NxQxSXZ.png) + ## Install From c32436ca787ae819348ddd7d88cf9a0429799abc Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 23:52:05 +0200 Subject: [PATCH 307/417] fix playerlist error for empty server with spectators --- package.json | 2 +- src/modules/CommandList.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index b4c883ad7..d24bdaa3a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.2", + "version": "1.2.3", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 0d0ff8fcb..d1c575911 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -506,12 +506,12 @@ Commands.list = { } else if (!socket.packetHandler.protocol && socket.isConnected) { console.log(" " + id + " | " + ip + " | " + protocol + " | " + "[CONNECTING]"); }else if (client.spectate) { - try { - nick = gameServer.largestClient.getFriendlyName(); - } catch (err) { - Logger.error(err.stack); - // Specating in free-roam mode - nick = "in free-roam"; + nick = "in free-roam"; + if (!client.freeRoam) { + var target = client.getSpectateTarget(); + if (target != null) { + nick = target.getFriendlyName(); + } } data = fillChar("SPECTATING: " + nick, '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); console.log(" " + id + " | " + ip + " | " + protocol + " | " + data); From 1845c7193ef16a9267d81f54f532e1c97fa01cc0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 29 Jun 2016 23:59:16 +0200 Subject: [PATCH 308/417] add gameserver.ini to .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index c9dc1264c..8b4a89ef5 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,6 @@ src/.readwarning # Prevent possible banned IP address leak src/ipbanlist.txt + +# Prevent modifications of user settings +gameserver.ini \ No newline at end of file From c2ede88bec3baf764c72fc9b3d34b07eec1f407c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 00:04:28 +0200 Subject: [PATCH 309/417] rollback ignore for gameserver.ini --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 8b4a89ef5..6e20e950b 100644 --- a/.gitignore +++ b/.gitignore @@ -39,5 +39,3 @@ src/.readwarning # Prevent possible banned IP address leak src/ipbanlist.txt -# Prevent modifications of user settings -gameserver.ini \ No newline at end of file From 2c5a9c732e7883a4eaadd3b48f0452eb3a72797b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 00:06:52 +0200 Subject: [PATCH 310/417] ignore gameserver.ini --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 6e20e950b..2336c030d 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ src/.readwarning # Prevent possible banned IP address leak src/ipbanlist.txt +# Prevent user settings overwrite +src/gameserver.ini \ No newline at end of file From b537bddff443b8708bdb3a064f7cf2ee5dd4c889 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 00:15:02 +0200 Subject: [PATCH 311/417] fix Logger not found error in BotLoader --- package.json | 2 +- src/ai/BotLoader.js | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index d24bdaa3a..a525dc8e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.3", + "version": "1.2.4", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/ai/BotLoader.js b/src/ai/BotLoader.js index e0bfc6869..f229a5d8c 100644 --- a/src/ai/BotLoader.js +++ b/src/ai/BotLoader.js @@ -1,4 +1,6 @@ // Project imports +var fs = require("fs"); +var Logger = require('../modules/Logger'); var BotPlayer = require('./BotPlayer'); var FakeSocket = require('./FakeSocket'); var PacketHandler = require('../PacketHandler'); @@ -28,19 +30,12 @@ BotLoader.prototype.getName = function() { BotLoader.prototype.loadNames = function() { this.randomNames = []; - // Load names - try { - var fs = require("fs"); // Import the util library - + if (fs.existsSync("./botnames.txt")) { // Read and parse the names - filter out whitespace-only names - this.randomNames = fs.readFileSync("./botnames.txt", "utf8").split(/[\r\n]+/).filter(function(x) { + this.randomNames = fs.readFileSync("./botnames.txt", "utf8").split(/[\r\n]+/).filter(function (x) { return x != ''; // filter empty names }); - } catch (err) { - Logger.error(err.stack); - // Nothing, use the default names } - this.nameIndex = 0; }; From 1da19de107e9bace57dbe0c6ffc085b563bf9efb Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 00:20:20 +0200 Subject: [PATCH 312/417] update readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 8421f8a26..8116304a2 100644 --- a/README.md +++ b/README.md @@ -49,8 +49,7 @@ sudo apt-get update sudo apt-get install git # Install node.js: -sudo apt-get install nodejs-legacy -sudo apt-get install npm +sudo apt-get install nodejs-legacy npm # Clone MultiOgar: git clone git://github.com/Barbosik/MultiOgar.git From 8e098e926f5d33b2105e5efe93f6eaa113453b8b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 00:24:59 +0200 Subject: [PATCH 313/417] update readme (test) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8116304a2..dd28ebb14 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ Original Ogar found [here](https://github.com/OgarProject/Ogar) The goal is to make good and smooth physics and cleanup the code. + ## Screenshot MultiOgar console: From ac87d2cc6f086c3cb9f94af56ddd385233b2bbd0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 01:42:30 +0200 Subject: [PATCH 314/417] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dd28ebb14..86ac189fd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The goal is to make good and smooth physics and cleanup the code. MultiOgar console: -![Screenshot](http://i.imgur.com/pmECJCH.png) +![Screenshot](http://i.imgur.com/TWzFBVu.png) 1000 bots, 500 viruses, 1000 foods, map 14142x14142 - works pretty smooth: From 8ce11b3f5e615cead7e77ec5c3a42420ee561e1a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 01:43:37 +0200 Subject: [PATCH 315/417] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 86ac189fd..88684e68d 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The goal is to make good and smooth physics and cleanup the code. MultiOgar console: -![Screenshot](http://i.imgur.com/TWzFBVu.png) +![Screenshot](https://i.imgur.com/GiJURq0.png) 1000 bots, 500 viruses, 1000 foods, map 14142x14142 - works pretty smooth: From 9b1fe8ca51692449339f490ffd80b28b3af41fa3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 16:31:12 +0200 Subject: [PATCH 316/417] protocol optimizations --- src/packet/SetBorder.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index c33638216..b1b599304 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -11,18 +11,27 @@ function SetBorder(playerTracker, border, gameType, serverName) { module.exports = SetBorder; -SetBorder.prototype.build = function(protocol) { +SetBorder.prototype.build = function (protocol) { + var scrambleX = this.playerTracker.scrambleX; + var scrambleY = this.playerTracker.scrambleY; + if (this.gameType == null) { + var buffer = new Buffer(33); + buffer.writeUInt8(0x40, 0, true); + buffer.writeDoubleLE(this.border.minx + scrambleX, 1, true); + buffer.writeDoubleLE(this.border.miny + scrambleY, 9, true); + buffer.writeDoubleLE(this.border.maxx + scrambleX, 17, true); + buffer.writeDoubleLE(this.border.maxy + scrambleY, 25, true); + return buffer; + } var writer = new BinaryWriter(); writer.writeUInt8(0x40); // Packet ID - writer.writeDouble(this.border.minx + this.playerTracker.scrambleX); - writer.writeDouble(this.border.miny + this.playerTracker.scrambleY); - writer.writeDouble(this.border.maxx + this.playerTracker.scrambleX); - writer.writeDouble(this.border.maxy + this.playerTracker.scrambleY); - if (this.gameType != null) { - writer.writeUInt32(this.gameType >> 0); - var name = this.serverName; - if (name == null) name = ""; - writer.writeStringZeroUtf8(name); - } + writer.writeDouble(this.border.minx + scrambleX); + writer.writeDouble(this.border.miny + scrambleY); + writer.writeDouble(this.border.maxx + scrambleX); + writer.writeDouble(this.border.maxy + scrambleY); + writer.writeUInt32(this.gameType >> 0); + var name = this.serverName; + if (name == null) name = ""; + writer.writeStringZeroUtf8(name); return writer.toBuffer(); }; From c911e42b6ba41a954c7335eef9661c462a3708cb Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 16:31:58 +0200 Subject: [PATCH 317/417] update revision --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a525dc8e6..8d5b7d9ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.4", + "version": "1.2.5", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", From 3e4d79d5d7ae5372d77e4b54e7cd664330d36ec9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 16:37:19 +0200 Subject: [PATCH 318/417] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 88684e68d..fcacba642 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. +Current version: **1.2.5** + ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) [![License](https://img.shields.io/badge/license-APACHE2-blue.svg)](https://github.com/Barbosik/OgarMulti/blob/master/LICENSE.md) From c37fac04dc2f70b2e261c14da31ab100e272d079 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 30 Jun 2016 16:49:55 +0200 Subject: [PATCH 319/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fcacba642..57299374d 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Added a lot of protocol optimizations, now server works with no lags at all even with 64 connected players * Added server version, now you can check if your MultiOgar code is fresh * Significant performance improvement and more smooth physics * Added protocol optimizations to reduce lags on cell multi split From 967c9b5b6c4539ce8c7de962edf93e4a3214cdd2 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 14:51:40 +0200 Subject: [PATCH 320/417] fix cell update bug (cell update were missing for just spawned cells); a small code refactoring --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 71 +++++++++------- src/PlayerTracker.js | 162 +++++++++++++++++++------------------ src/ai/BotPlayer.js | 29 ++++--- src/ai/FakeSocket.js | 18 +---- src/modules/CommandList.js | 32 ++++---- 7 files changed, 162 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index 57299374d..82e4e07e5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.5** +Current version: **1.2.6** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 8d5b7d9ec..eee71f55a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.5", + "version": "1.2.6", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 3d54d66d9..82f119f68 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -432,43 +432,46 @@ GameServer.prototype.removeNode = function(node) { }; GameServer.prototype.updateClients = function () { - for (var i = 0; i < this.clients.length; i++) { - var socket = this.clients[i]; - socket.playerTracker.update(); - } - // remove dead clients + // check dead clients for (var i = 0; i < this.clients.length; ) { - var socket = this.clients[i]; - if (socket.playerTracker.isRemoved) { + var playerTracker = this.clients[i].playerTracker; + playerTracker.checkConnection(); + if (playerTracker.isRemoved) { + // remove dead client this.clients.splice(i, 1); } else { i++; } } + // update + for (var i = 0; i < this.clients.length; i++) { + this.clients[i].playerTracker.updateTick(); + } + for (var i = 0; i < this.clients.length; i++) { + this.clients[i].playerTracker.sendUpdate(); + } }; GameServer.prototype.updateLeaderboard = function () { // Update leaderboard with the gamemode's method - if ((this.tickCounter % 25) == 0) { - this.leaderboard = []; - this.leaderboardType = -1; - this.gameMode.updateLB(this); - - if (!this.gameMode.specByLeaderboard) { - // Get client with largest score if gamemode doesn't have a leaderboard - var clients = this.clients.valueOf(); - - // Use sort function - clients.sort(function (a, b) { - return b.playerTracker.getScore() - a.playerTracker.getScore(); - }); - //this.largestClient = clients[0].playerTracker; - this.largestClient = null; - if (clients[0] != null) - this.largestClient = clients[0].playerTracker; - } else { - this.largestClient = this.gameMode.rankOne; - } + this.leaderboard = []; + this.leaderboardType = -1; + this.gameMode.updateLB(this); + + if (!this.gameMode.specByLeaderboard) { + // Get client with largest score if gamemode doesn't have a leaderboard + var clients = this.clients.valueOf(); + + // Use sort function + clients.sort(function (a, b) { + return b.playerTracker.getScore() - a.playerTracker.getScore(); + }); + //this.largestClient = clients[0].playerTracker; + this.largestClient = null; + if (clients[0] != null) + this.largestClient = clients[0].playerTracker; + } else { + this.largestClient = this.gameMode.rankOne; } }; @@ -537,7 +540,7 @@ GameServer.prototype.timerLoop = function () { }; GameServer.prototype.mainLoop = function() { - var tStart = new Date().getTime(); + var tStart = process.hrtime(); // Loop main functions if (this.run) { @@ -552,11 +555,17 @@ GameServer.prototype.mainLoop = function() { this.updateMassDecay(); } } + this.updateClients(); - this.updateLeaderboard(); + + if (((this.getTick()+7) % (1000 / 40)) == 0) { + // once per second + this.updateLeaderboard(); + } // ping server tracker if (this.config.serverTracker && (this.getTick() % (30000/40)) == 0) { + // once per 30 seconds this.pingServerTracker(); } @@ -587,8 +596,8 @@ GameServer.prototype.mainLoop = function() { if (this.run) { this.tickCounter++; } - var tEnd = new Date().getTime(); - this.updateTime = tEnd - tStart; + var tEnd = process.hrtime(tStart); + this.updateTime = tEnd[0] * 1000 + tEnd[1] / 1000000; }; GameServer.prototype.startingFood = function() { diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index f06b8f898..8d4cf696b 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -15,7 +15,8 @@ function PlayerTracker(gameServer, socket) { this._nameUnicode = null; this._skinUtf8 = null; this.color = { r: 0, g: 0, b: 0 }; - this.visibleNodes = []; + this.viewNodes = []; + this.clientNodes = []; this.cells = []; this.mergeOverride = false; // Triggered by console command this.score = 0; // Needed for leaderboard @@ -214,10 +215,10 @@ PlayerTracker.prototype.joinGame = function (name, skin) { // some old clients don't understand ClearAll message // so we will send update for them if (this.socket.packetHandler.protocol < 6) { - this.socket.sendPacket(new Packet.UpdateNodes(this, [], [], [], this.visibleNodes)); + this.socket.sendPacket(new Packet.UpdateNodes(this, [], [], [], this.clientNodes)); } this.socket.sendPacket(new Packet.ClearAll()); - this.visibleNodes = []; + this.clientNodes = []; this.scramble(); if (this.gameServer.config.serverScrambleCoords < 2) { // no scramble / lightweight scramble @@ -233,9 +234,8 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); }; -PlayerTracker.prototype.update = function () { - if (this.isRemoved) return; - // Handles disconnection +PlayerTracker.prototype.checkConnection = function () { + // Handle disconnection if (!this.socket.isConnected) { // wait for playerDisconnectTime var time = +new Date; @@ -249,11 +249,8 @@ PlayerTracker.prototype.update = function () { } // Mark to remove this.isRemoved = true; + return; } - // update visible nodes/mouse (for spectators, if any) - var nodes = this.getVisibleNodes(); - nodes.sort(function (a, b) { return a.nodeId - b.nodeId; }); - this.visibleNodes = nodes; this.mouse.x = this.centerPos.x; this.mouse.y = this.centerPos.y; this.socket.packetHandler.pressSpace = false; @@ -270,12 +267,9 @@ PlayerTracker.prototype.update = function () { this.isCloseRequested = true; } } +}; - // if initialization is not complete yet then do not send update - if (!this.socket.packetHandler.protocol) - return; - - // Actions buffer (So that people cant spam packets) +PlayerTracker.prototype.updateTick = function () { if (this.socket.packetHandler.pressSpace) { // Split cell this.pressSpace(); this.socket.packetHandler.pressSpace = false; @@ -290,23 +284,75 @@ PlayerTracker.prototype.update = function () { this.pressQ(); this.socket.packetHandler.pressQ = false; } + + if (this.spectate) { + if (this.freeRoam || this.getSpectateTarget() == null) { + // free roam + this.updateCenterFreeRoam(); + this.scale = this.gameServer.config.serverSpectatorScale;//0.25; + } else { + // spectate target + return; + } + } else { + // in game + this.updateCenterInGame(); + } + this.updateViewBox(); + this.updateVisibleNodes(); +}; + +PlayerTracker.prototype.sendUpdate = function () { + if (this.isRemoved || !this.socket.isConnected || !this.socket.packetHandler.protocol) { + // do not send update for disconnected clients + // also do not send if initialization is not complete yet + return; + } + + if (this.spectate) { + if (!this.freeRoam) { + // spectate target + var player = this.getSpectateTarget(); + if (player != null) { + this.setCenterPos(player.centerPos.x, player.centerPos.y); + this.scale = player.getScale(); + this.viewBox = player.viewBox; + this.viewNodes = player.viewNodes; + } + } + this.sendCameraPacket(); + } + + if (this.gameServer.config.serverScrambleCoords == 2) { + // scramble (moving border) + if (this.borderCounter == 0) { + var bound = { + minx: Math.max(this.gameServer.border.minx, this.viewBox.minx - this.viewBox.halfWidth), + miny: Math.max(this.gameServer.border.miny, this.viewBox.miny - this.viewBox.halfHeight), + maxx: Math.min(this.gameServer.border.maxx, this.viewBox.maxx + this.viewBox.halfWidth), + maxy: Math.min(this.gameServer.border.maxy, this.viewBox.maxy + this.viewBox.halfHeight) + }; + this.socket.sendPacket(new Packet.SetBorder(this, bound)); + } + this.borderCounter++; + if (this.borderCounter >= 20) + this.borderCounter = 0; + } - var newVisible = this.getVisibleNodes(); - newVisible.sort(function (a, b) { return a.nodeId - b.nodeId; }); var delNodes = []; var eatNodes = []; var addNodes = []; var updNodes = []; - var newIndex = 0; var oldIndex = 0; - for (; newIndex < newVisible.length && oldIndex < this.visibleNodes.length;) { - if (newVisible[newIndex].nodeId < this.visibleNodes[oldIndex].nodeId) { - addNodes.push(newVisible[newIndex]); + var newIndex = 0; + for (; newIndex < this.viewNodes.length && oldIndex < this.clientNodes.length;) { + if (this.viewNodes[newIndex].nodeId < this.clientNodes[oldIndex].nodeId) { + addNodes.push(this.viewNodes[newIndex]); newIndex++; continue; } - if (newVisible[newIndex].nodeId > this.visibleNodes[oldIndex].nodeId) { - var node = this.visibleNodes[oldIndex]; + if (this.viewNodes[newIndex].nodeId > this.clientNodes[oldIndex].nodeId) { + var node = this.clientNodes[oldIndex]; if (node.isRemoved && node.getKiller() != null && node.owner != node.getKiller().owner) eatNodes.push(node); else @@ -314,49 +360,33 @@ PlayerTracker.prototype.update = function () { oldIndex++; continue; } - var node = newVisible[newIndex]; + var node = this.viewNodes[newIndex]; // skip food & eject if no moving if (node.isMoving || (node.cellType != 1 && node.cellType != 3)) updNodes.push(node); newIndex++; oldIndex++; } - for (; newIndex < newVisible.length; ) { - addNodes.push(newVisible[newIndex]); + for (; newIndex < this.viewNodes.length; ) { + addNodes.push(this.viewNodes[newIndex]); newIndex++; } - for (; oldIndex < this.visibleNodes.length; ) { - var node = this.visibleNodes[oldIndex]; + for (; oldIndex < this.clientNodes.length; ) { + var node = this.clientNodes[oldIndex]; if (node.isRemoved && node.getKiller() != null && node.owner != node.getKiller().owner) eatNodes.push(node); else delNodes.push(node); oldIndex++; } - this.visibleNodes = newVisible; + this.clientNodes = this.viewNodes; - if (this.gameServer.config.serverScrambleCoords == 2) { - // moving border scramble - if (this.borderCounter == 0) { - var bound = { - minx: Math.max(this.gameServer.border.minx, this.viewBox.minx - this.viewBox.halfWidth), - miny: Math.max(this.gameServer.border.miny, this.viewBox.miny - this.viewBox.halfHeight), - maxx: Math.min(this.gameServer.border.maxx, this.viewBox.maxx + this.viewBox.halfWidth), - maxy: Math.min(this.gameServer.border.maxy, this.viewBox.maxy + this.viewBox.halfHeight) - }; - this.socket.sendPacket(new Packet.SetBorder(this, bound)); - } - this.borderCounter++; - if (this.borderCounter >= 20) - this.borderCounter = 0; - } - // Send packet this.socket.sendPacket(new Packet.UpdateNodes( - this, - addNodes, - updNodes, - eatNodes, + this, + addNodes, + updNodes, + eatNodes, delNodes)); // Update leaderboard @@ -509,39 +539,15 @@ PlayerTracker.prototype.getSpectateTarget = function () { return this.spectateTarget; }; -PlayerTracker.prototype.getVisibleNodes = function () { - if (this.spectate) { - if (!this.freeRoam) { - var player = this.getSpectateTarget(); - if (player != null) { - this.setCenterPos(player.centerPos.x, player.centerPos.y); - this.scale = player.getScale(); - this.sendCameraPacket(); - this.updateViewBox(); - return player.visibleNodes.slice(0); - } - } - // free roam spectate - this.updateCenterFreeRoam(); - this.scale = this.gameServer.config.serverSpectatorScale;//0.25; - this.sendCameraPacket(); - } else { - // in game - this.updateCenterInGame(); - // scale will be calculated on first call to this.getScale() inside updateViewBox() - } - this.updateViewBox(); - return this.calcVisibleNodes(); -} - -PlayerTracker.prototype.calcVisibleNodes = function() { - var newVisible = []; +PlayerTracker.prototype.updateVisibleNodes = function() { + this.viewNodes = []; var self = this; this.gameServer.quadTree.find(this.viewBox, function (quadItem) { if (quadItem.cell.owner != self) - newVisible.push(quadItem.cell); + self.viewNodes.push(quadItem.cell); }); - return newVisible.concat(this.cells); + this.viewNodes = this.viewNodes.concat(this.cells); + this.viewNodes.sort(function (a, b) { return a.nodeId - b.nodeId; }); }; PlayerTracker.prototype.setCenterPos = function(x, y) { diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index 53bfbbba8..0fb86505c 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -29,22 +29,28 @@ BotPlayer.prototype.getLowestCell = function() { return sorted[0]; }; -BotPlayer.prototype.update = function () { // Overrides the update function from player tracker +BotPlayer.prototype.checkConnection = function () { + if (this.socket.isCloseRequest) { + while (this.cells.length > 0) { + this.gameServer.removeNode(this.cells[0]); + } + this.isRemoved = true; + return; + } + // Respawn if bot is dead if (this.cells.length <= 0) { this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); if (this.cells.length == 0) { // If the bot cannot spawn any cells, then disconnect it this.socket.close(); - return; } } - +} + +BotPlayer.prototype.sendUpdate = function () { // Overrides the update function from player tracker if (this.splitCooldown > 0) this.splitCooldown--; - // Calculate nodes - this.visibleNodes = this.getVisibleNodes(); - // Calc predators/prey var cell = this.getLowestCell(); @@ -63,8 +69,9 @@ BotPlayer.prototype.decide = function(cell) { splitTarget = null, threats = []; - for (var i = 0; i < this.visibleNodes.length; i++) { - var check = this.visibleNodes[i]; + for (var i = 0; i < this.viewNodes.length; i++) { + var check = this.viewNodes[i]; + if (check.owner == this) continue; // Get attraction of the cells - avoid larger cells, viruses and same team cells var influence = 0; @@ -167,7 +174,8 @@ BotPlayer.prototype.decide = function(cell) { y: splitTarget.position.y }; this.splitCooldown = 16; - this.gameServer.splitCells(this); + this.socket.packetHandler.pressSpace = true; + //this.gameServer.splitCells(this); return; } } @@ -178,7 +186,8 @@ BotPlayer.prototype.decide = function(cell) { y: splitTarget.position.y }; this.splitCooldown = 16; - this.gameServer.splitCells(this); + this.socket.packetHandler.pressSpace = true; + //this.gameServer.splitCells(this); return; } } diff --git a/src/ai/FakeSocket.js b/src/ai/FakeSocket.js index 670d32f26..227348ca7 100644 --- a/src/ai/FakeSocket.js +++ b/src/ai/FakeSocket.js @@ -2,6 +2,7 @@ function FakeSocket(gameServer) { this.server = gameServer; + this.isCloseRequest = false; } module.exports = FakeSocket; @@ -14,20 +15,5 @@ FakeSocket.prototype.sendPacket = function(packet) { }; FakeSocket.prototype.close = function(error) { - // Removes the bot - var len = this.playerTracker.cells.length; - for (var i = 0; i < len; i++) { - var cell = this.playerTracker.cells[0]; - - if (!cell) { - continue; - } - - this.server.removeNode(cell); - } - - var index = this.server.clients.indexOf(this); - if (index != -1) { - this.server.clients.splice(index, 1); - } + this.isCloseRequest = true; }; diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index d1c575911..4d496ed27 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -159,27 +159,25 @@ Commands.list = { if (isNaN(toRemove)) { toRemove = -1; // Kick all bots if user doesnt specify a number } - + if (toRemove < 1) { + Logger.warn("Invalid argument!"); + return; + } var removed = 0; - var i = 0; - while (i < gameServer.clients.length && removed != toRemove) { - if (typeof gameServer.clients[i].remoteAddress == 'undefined') { // if client i is a bot kick him - var client = gameServer.clients[i].playerTracker; - var len = client.cells.length; - for (var j = 0; j < len; j++) { - gameServer.removeNode(client.cells[0]); - } - client.socket.close(); - removed++; - } else - i++; + for (var i = 0; i < gameServer.clients.length; i++) { + var socket = gameServer.clients[i]; + if (socket.isConnected != null) continue; + socket.close(); + removed++; + if (removed >= toRemove) + break; } - if (toRemove == -1) - console.log("Kicked all bots (" + removed + ")"); + if (removed == 0) + Logger.warn("Cannot find any bots"); else if (toRemove == removed) - console.log("Kicked " + toRemove + " bots"); + Logger.warn("Kicked " + removed + " bots"); else - console.log("Only " + removed + " bots could be kicked"); + Logger.warn("Only " + removed + " bots were kicked"); }, board: function(gameServer, split) { var newLB = []; From 20387c17521137d036046ae73cfa9622bce2698a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 15:00:18 +0200 Subject: [PATCH 321/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 82e4e07e5..ea9a50d01 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Fixed bug when some cell split/eject were shown with delay for some clients * Added a lot of protocol optimizations, now server works with no lags at all even with 64 connected players * Added server version, now you can check if your MultiOgar code is fresh * Significant performance improvement and more smooth physics From bd010bd671414de89a065f9ca0f8f354d82d9df4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 15:03:06 +0200 Subject: [PATCH 322/417] remove commented debug code --- src/GameServer.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 82f119f68..9f5330089 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -569,30 +569,6 @@ GameServer.prototype.mainLoop = function() { this.pingServerTracker(); } - //this.tt = 0; - //this.tc = 0; - //var t = process.hrtime(); - //this.updateMoveEngine(); - //this.t1 = toTime(process.hrtime(t)); - //t = process.hrtime(); - //this.updateSpawn(); - //this.t2 = toTime(process.hrtime(t)); - //t = process.hrtime(); - //this.gameMode.onTick(this); - //this.t3 = toTime(process.hrtime(t)); - //t = process.hrtime(); - //this.updateMassDecay(); - //this.t4 = toTime(process.hrtime(t)); - //t = process.hrtime(); - //this.updateClients(); - //this.t5 = toTime(process.hrtime(t)); - //t = process.hrtime(); - //this.updateLeaderboard(); - //this.t6 = toTime(process.hrtime(t)); - //function toTime(tscTicks) { - // return tscTicks[0] * 1000 + tscTicks[1] / 1000000; - //} - if (this.run) { this.tickCounter++; } From 2c779cd69abff78746117e19b737d2dd5c2e021b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 15:46:10 +0200 Subject: [PATCH 323/417] quad-tree optimization --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ea9a50d01..2e53e2fd5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.6** +Current version: **1.2.7** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index eee71f55a..34f093f40 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.6", + "version": "1.2.7", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 9f5330089..5f7e3a9a8 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -119,7 +119,7 @@ function GameServer() { this.loadIpBanList(); this.setBorder(this.config.borderWidth, this.config.borderHeight); - this.quadTree = new QuadNode(this.border, 4, 100); + this.quadTree = new QuadNode(this.border, 16, 100); // Gamemodes this.gameMode = Gamemode.get(this.config.serverGamemode); From 7561397ec96800ad7419a6f216af0411c6da13f2 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 15:58:07 +0200 Subject: [PATCH 324/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 2e53e2fd5..a26d95385 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Added performance optimizations, now up to 700 bots with no lags at all * Fixed bug when some cell split/eject were shown with delay for some clients * Added a lot of protocol optimizations, now server works with no lags at all even with 64 connected players * Added server version, now you can check if your MultiOgar code is fresh From 42edb769a5057909ed8817ccb40b00572f49078c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 16:26:05 +0200 Subject: [PATCH 325/417] fix error handling --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index a26d95385..06fefe6c7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.7** +Current version: **1.2.8** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 34f093f40..7a8a53d24 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.7", + "version": "1.2.8", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 5f7e3a9a8..3ef28be86 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -148,16 +148,15 @@ GameServer.prototype.start = function() { }; GameServer.prototype.onServerSocketError = function (error) { + Logger.error("WebSocket: "+ error.code + " - " + error.message); switch (error.code) { case "EADDRINUSE": - Logger.error("Server could not bind to port " + this.config.serverPort + "! Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); + Logger.error("Server could not bind to port " + this.config.serverPort + "!"); + Logger.error("Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); break; case "EACCES": Logger.error("Please make sure you are running Ogar with root privileges."); break; - default: - Logger.error(error.code + ": " + error.message); - break; } process.exit(1); // Exits the program }; @@ -1426,9 +1425,11 @@ GameServer.prototype.startStatsServer = function(port) { res.writeHead(200); res.end(this.stats); }.bind(this)); + this.httpServer.on('error', function (err) { + Logger.error("Stats Server: " + err.message); + }); var getStatsBind = this.getStats.bind(this); - // TODO: This causes error if something else already uses this port. Catch the error. this.httpServer.listen(port, function () { // Stats server Logger.info("Started stats server on port " + port); From 1d489159430d71d47efc9f1cb04be3e1dba8cbe3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 17:43:14 +0200 Subject: [PATCH 326/417] fix default player limit --- src/gameserver.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gameserver.ini b/src/gameserver.ini index 7761df7dc..1c7ed0a9e 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -33,7 +33,7 @@ # serverWelcome1: First server welcome message # serverWelcome2: Second server welcome message (optional, for info, etc) serverTimeout = 300 -serverMaxConnections = 64 +serverMaxConnections = 128 serverIpLimit = 4 serverPort = 443 serverTracker = 0 From 17308ccef3a1564913bcf929df0ee4ba99999acb Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 18:40:35 +0200 Subject: [PATCH 327/417] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 06fefe6c7..20f9d1070 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ MultiOgar console: ![Screenshot](https://i.imgur.com/GiJURq0.png) -1000 bots, 500 viruses, 1000 foods, map 14142x14142 - works pretty smooth: +Version 1.2.8: 1000 bots, 500 viruses, 1000 foods, map 14142x14142 - works very-very smooth (with a little slower speed, but will not be noticed by user): -![Screenshot](https://i.imgur.com/NxQxSXZ.png) +![Screenshot](http://i.imgur.com/XsXjT0o.png) ## Install From e9277c91b52af02bf03f781c3beb75d03c17f477 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 18:45:10 +0200 Subject: [PATCH 328/417] update readme --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 20f9d1070..7b0250a0f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,11 @@ MultiOgar console: ![Screenshot](https://i.imgur.com/GiJURq0.png) -Version 1.2.8: 1000 bots, 500 viruses, 1000 foods, map 14142x14142 - works very-very smooth (with a little slower speed, but will not be noticed by user): +Version 1.2.8: +1000 bots, 500 viruses, 1000 foods, map 14142x14142 +Works very-very smooth (with a little slower speed, but it will not be noticed by user). +CPU load: 14% (x4 core) +Memory usage: 70 MB ![Screenshot](http://i.imgur.com/XsXjT0o.png) From 4eb5609659747ab21567cf9ab683d36e3f6d6edd Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 18:45:58 +0200 Subject: [PATCH 329/417] update readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7b0250a0f..72e81c87e 100644 --- a/README.md +++ b/README.md @@ -23,10 +23,10 @@ MultiOgar console: ![Screenshot](https://i.imgur.com/GiJURq0.png) Version 1.2.8: -1000 bots, 500 viruses, 1000 foods, map 14142x14142 -Works very-very smooth (with a little slower speed, but it will not be noticed by user). -CPU load: 14% (x4 core) -Memory usage: 70 MB +* 1000 bots, 500 viruses, 1000 foods, map 14142x14142 +* Works very-very smooth (with a little slower speed, but it will not be noticed by user). +* CPU load: 14% (x4 core) +* Memory usage: 70 MB ![Screenshot](http://i.imgur.com/XsXjT0o.png) From f75e48ef7ba3efc9307f64c8c75a380399860961 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 23:01:53 +0200 Subject: [PATCH 330/417] reuse bot names --- src/ai/BotLoader.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ai/BotLoader.js b/src/ai/BotLoader.js index f229a5d8c..418721e12 100644 --- a/src/ai/BotLoader.js +++ b/src/ai/BotLoader.js @@ -17,9 +17,8 @@ BotLoader.prototype.getName = function() { // Picks a random name for the bot if (this.randomNames.length > 0) { - var index = Math.floor(Math.random() * this.randomNames.length); + var index = ((this.randomNames.length-1) * Math.random()) >>> 0; name = this.randomNames[index]; - this.randomNames.splice(index, 1); } else { name = "bot" + ++this.nameIndex; } From 72066a6fc1b43a64d2d7d6993d59ce2c4a24bda8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 23:49:25 +0200 Subject: [PATCH 331/417] add sub-network ban feature --- src/GameServer.js | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 3ef28be86..236c0f39f 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -186,7 +186,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { ws.close(1000, "No slots"); return; } - if (this.ipBanList && this.ipBanList.length > 0 && this.ipBanList.indexOf(ws._socket.remoteAddress) >= 0) { + if (this.checkIpBan(ws._socket.remoteAddress)) { ws.close(1000, "IP banned"); return; } @@ -1343,16 +1343,52 @@ GameServer.prototype.saveIpBanList = function () { } }; +GameServer.prototype.checkIpBan = function (ipAddress) { + if (!this.ipBanList || this.ipBanList.length == 0 || ipAddress == "127.0.0.1") { + return false; + } + if (this.ipBanList.indexOf(ipAddress) >= 0) { + return true; + } + var ipBin = ipAddress.split('.'); + if (ipBin.length != 4) { + // unknown IP format + return false; + } + var subNet2 = ipBin[0] + "." + ipBin[1] + "." + "255.255"; + if (this.ipBanList.indexOf(subNet2) >= 0) { + return true; + } + var subNet1 = ipBin[0] + "." + ipBin[1] + "." + ipBin[2] + ".255"; + if (this.ipBanList.indexOf(subNet1) >= 0) { + return true; + } + return false; +}; + GameServer.prototype.banIp = function (ip) { + var ipBin = ip.split('.'); + if (ipBin.length != 4) { + Logger.warn("Invalid IP format: " + ip); + return; + } + if (ipBin[0] == "127") { + Logger.warn("Cannot ban localhost"); + return; + } if (this.ipBanList.indexOf(ip) >= 0) { Logger.warn(ip + " is already in the ban list!"); return; } this.ipBanList.push(ip); - Logger.info("The IP " + ip + " has been banned"); + if (ipBin[2]=="255" || ipBin[3] == "255") { + Logger.info("The IP sub-net " + ip + " has been banned"); + } else { + Logger.info("The IP " + ip + " has been banned"); + } this.clients.forEach(function (socket) { // If already disconnected or the ip does not match - if (socket == null || !socket.isConnected || socket.remoteAddress != ip) + if (socket == null || !socket.isConnected || !this.checkIpBan(socket.remoteAddress)) return; // remove player cells From 4859b624ca0538cb6d48ac64fe70918ae6b8f0c4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 1 Jul 2016 23:52:33 +0200 Subject: [PATCH 332/417] update revision & readme --- README.md | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 72e81c87e..fb2423a6b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.8** +Current version: **1.2.9** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) @@ -110,6 +110,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Added sub-net ban feature (use ban xx.xx.xx.255 to ban entire sub-network) * Added performance optimizations, now up to 700 bots with no lags at all * Fixed bug when some cell split/eject were shown with delay for some clients * Added a lot of protocol optimizations, now server works with no lags at all even with 64 connected players diff --git a/package.json b/package.json index 7a8a53d24..e37a32228 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.8", + "version": "1.2.9", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", From 03cfa6c8ad1850db4520cc80c2b382c942d9e583 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 2 Jul 2016 03:27:35 +0200 Subject: [PATCH 333/417] use * instead of 255 for sub-net IP mask --- README.md | 4 ++-- package.json | 2 +- src/GameServer.js | 8 ++++---- src/modules/CommandList.js | 4 ++++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index fb2423a6b..41133e095 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.9** +Current version: **1.2.10** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) @@ -110,7 +110,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: -* Added sub-net ban feature (use ban xx.xx.xx.255 to ban entire sub-network) +* Added sub-net ban feature (use ban xx.xx.xx.* or xx.xx.*.* to ban entire sub-network) * Added performance optimizations, now up to 700 bots with no lags at all * Fixed bug when some cell split/eject were shown with delay for some clients * Added a lot of protocol optimizations, now server works with no lags at all even with 64 connected players diff --git a/package.json b/package.json index e37a32228..316dc954f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.9", + "version": "1.2.10", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 236c0f39f..c719361cd 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1355,11 +1355,11 @@ GameServer.prototype.checkIpBan = function (ipAddress) { // unknown IP format return false; } - var subNet2 = ipBin[0] + "." + ipBin[1] + "." + "255.255"; + var subNet2 = ipBin[0] + "." + ipBin[1] + "." + "*.*"; if (this.ipBanList.indexOf(subNet2) >= 0) { return true; } - var subNet1 = ipBin[0] + "." + ipBin[1] + "." + ipBin[2] + ".255"; + var subNet1 = ipBin[0] + "." + ipBin[1] + "." + ipBin[2] + ".*"; if (this.ipBanList.indexOf(subNet1) >= 0) { return true; } @@ -1381,7 +1381,7 @@ GameServer.prototype.banIp = function (ip) { return; } this.ipBanList.push(ip); - if (ipBin[2]=="255" || ipBin[3] == "255") { + if (ipBin[2]=="*" || ipBin[3] == "*") { Logger.info("The IP sub-net " + ip + " has been banned"); } else { Logger.info("The IP " + ip + " has been banned"); @@ -1399,7 +1399,7 @@ GameServer.prototype.banIp = function (ip) { // disconnect socket.close(1000, "Banned from server"); var name = socket.playerTracker.getFriendlyName(); - Logger.info("Banned: \"" + name + "\" with Player ID " + socket.playerTracker.pID); // Redacted "with IP #.#.#.#" since it'll already be logged above + Logger.info("Banned: \"" + name + "\" with Player ID " + socket.playerTracker.pID); this.sendChatMessage(null, null, "Banned \"" + name + "\""); // notify to don't confuse with server bug }, this); this.saveIpBanList(); diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 4d496ed27..e08fbbb3a 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -105,6 +105,10 @@ Commands.list = { // Check for invalid decimal numbers of the IP address for (var i in ipParts) { + if (i > 1 && ipParts[i] == "*") { + // mask for sub-net + continue; + } // If not numerical or if it's not between 0 and 255 // TODO: Catch string "e" as it means "10^". if (isNaN(ipParts[i]) || ipParts[i] < 0 || ipParts[i] >= 256) { From 7b4f553339816dd63954bb52c622431e360b410c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 2 Jul 2016 04:22:20 +0200 Subject: [PATCH 334/417] improve protocol validation --- README.md | 2 +- package.json | 2 +- src/PacketHandler.js | 27 ++++++++++++++++++--------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 41133e095..c41137c5a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.10** +Current version: **1.2.11** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 316dc954f..3c7ea7705 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.10", + "version": "1.2.11", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 0df676c06..4ae02677c 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -5,7 +5,6 @@ var BinaryReader = require('./packet/BinaryReader'); function PacketHandler(gameServer, socket) { this.gameServer = gameServer; this.socket = socket; - // Detect protocol version - we can do something about it later this.protocol = 0; this.isHandshakePassed = false; this.lastChatTick = 0; @@ -19,21 +18,25 @@ function PacketHandler(gameServer, socket) { module.exports = PacketHandler; PacketHandler.prototype.handleMessage = function(message) { - // Discard empty messages - if (message.length == 0) + // Validation + if (message.length == 0) { return; - if (message.length > 2048) { + } + if (message.length > 256) { // anti-spamming this.socket.close(1009, "Spam"); return; } - var reader = new BinaryReader(message); - var packetId = reader.readUInt8(); // no handshake? if (!this.isHandshakePassed) { - if (packetId != 254 || message.length != 5) - return; // wait handshake + if (message[0] != 254 || message.length != 5) { + // wait handshake + return; + } + + var reader = new BinaryReader(message); + reader.skipBytes(1); // Handshake request this.protocol = reader.readUInt32(); @@ -58,9 +61,15 @@ PacketHandler.prototype.handleMessage = function(message) { return; } this.socket.lastAliveTime = +new Date; + + var reader = new BinaryReader(message); + var packetId = reader.readUInt8(); switch (packetId) { case 0: + if (this.socket.playerTracker.cells.length > 0) { + break; + } var text = null; if (this.protocol <= 5) text = reader.readStringZeroUnicode(); @@ -77,7 +86,7 @@ PacketHandler.prototype.handleMessage = function(message) { } break; case 16: - // Set Target + // Mouse var client = this.socket.playerTracker; if (message.length == 13) { // protocol late 5, 6, 7 From b80a352537171b60695867db1c05a1cc4a8bf5ec Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 2 Jul 2016 04:28:58 +0200 Subject: [PATCH 335/417] string fix --- src/GameServer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GameServer.js b/src/GameServer.js index c719361cd..023275fe8 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1355,7 +1355,7 @@ GameServer.prototype.checkIpBan = function (ipAddress) { // unknown IP format return false; } - var subNet2 = ipBin[0] + "." + ipBin[1] + "." + "*.*"; + var subNet2 = ipBin[0] + "." + ipBin[1] + ".*.*"; if (this.ipBanList.indexOf(subNet2) >= 0) { return true; } From f4911badc5e38cfb1ed2a40b2851d81447ed1152 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 2 Jul 2016 04:31:45 +0200 Subject: [PATCH 336/417] update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c41137c5a..47e3c68d6 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ just set `serverTracker = 0` and the server will not ping the server tracker. ###Ogar clients -Just replace `127.0.0.1:50000` in the url to the server IP and port to play. +Just replace `127.0.0.1:443` in the url to the server IP and port to play. URL | Protocol | Description --- | --- | --- @@ -110,7 +110,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: -* Added sub-net ban feature (use ban xx.xx.xx.* or xx.xx.*.* to ban entire sub-network) +* Added sub-net ban feature (use `ban xx.xx.xx.*` or `ban xx.xx.*.*` to ban entire sub-network) * Added performance optimizations, now up to 700 bots with no lags at all * Fixed bug when some cell split/eject were shown with delay for some clients * Added a lot of protocol optimizations, now server works with no lags at all even with 64 connected players From ec2ad5477176a107e6e4aa1b9358a376b1c4eccb Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 2 Jul 2016 05:44:08 +0200 Subject: [PATCH 337/417] fix playerlist & botname generator --- README.md | 2 +- package.json | 2 +- src/ai/BotLoader.js | 2 +- src/modules/CommandList.js | 6 ++++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 47e3c68d6..952c123ab 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.11** +Current version: **1.2.12** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 3c7ea7705..c5dc50965 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.11", + "version": "1.2.12", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/ai/BotLoader.js b/src/ai/BotLoader.js index 418721e12..118c2b538 100644 --- a/src/ai/BotLoader.js +++ b/src/ai/BotLoader.js @@ -17,7 +17,7 @@ BotLoader.prototype.getName = function() { // Picks a random name for the bot if (this.randomNames.length > 0) { - var index = ((this.randomNames.length-1) * Math.random()) >>> 0; + var index = (this.randomNames.length * Math.random()) >>> 0; name = this.randomNames[index]; } else { name = "bot" + ++this.nameIndex; diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index e08fbbb3a..bc806a131 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -475,8 +475,10 @@ Commands.list = { Logger.info("Showing " + gameServer.clients.length + " players: "); console.log(" ID | IP | P | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space console.log(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); - for (var i = 0; i < gameServer.clients.length; i++) { - var socket = gameServer.clients[i]; + var sockets = gameServer.clients.slice(0); + sockets.sort(function (a, b) { return a.playerTracker.pID - b.playerTracker.pID; }); + for (var i = 0; i < sockets.length; i++) { + var socket = sockets[i]; var client = socket.playerTracker; // ID with 3 digits length From 2409ae308c12cd6b0c0f5666cca24bf5f5b2a3b1 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 2 Jul 2016 17:44:43 +0200 Subject: [PATCH 338/417] performance optimization --- README.md | 2 +- package.json | 2 +- src/entity/Cell.js | 26 +++++++++++++++++--------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 952c123ab..9b836e028 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.12** +Current version: **1.2.13** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index c5dc50965..20780277b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.12", + "version": "1.2.13", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/entity/Cell.js b/src/entity/Cell.js index c63dc099b..9eb6da35c 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -6,8 +6,9 @@ function Cell(gameServer, owner, position, size) { this.color = { r: 0, g: 0, b: 0 }; this.position = { x: 0, y: 0 }; this._size = 0; - this._mass = 0; this._sizeSquared = 0; + this._mass = null; + this._speed = null; this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass this.isSpiked = false; // If true, then this cell has spikes around it this.isAgitated = false;// If true, then this cell has waves on it's outline @@ -55,7 +56,8 @@ Cell.prototype.setSize = function (size) { } this._size = size; this._sizeSquared = size * size; - this._mass = this._sizeSquared / 100; + this._mass = null; + this._speed = null; if (this.owner) this.owner.massChanged(); }; @@ -64,18 +66,24 @@ Cell.prototype.getSize = function() { return this._size; }; -Cell.prototype.getMass = function () { - return this._mass; -}; - Cell.prototype.getSizeSquared = function () { return this._sizeSquared; }; +Cell.prototype.getMass = function () { + if (this._mass == null) { + this._mass = this.getSizeSquared() / 100; + } + return this._mass; +}; + Cell.prototype.getSpeed = function() { - var speed = 2.1106 / Math.pow(this.getSize(), 0.449); - // tickStep=40ms - return speed * 40 * this.gameServer.config.playerSpeed; + if (this._speed == null) { + var speed = 2.1106 / Math.pow(this.getSize(), 0.449); + // tickStep=40ms + this._speed = speed * 40 * this.gameServer.config.playerSpeed; + } + return this._speed; }; Cell.prototype.setAngle = function(angle) { From 440e52ef3507743d9beedb14f5a2406dc396c953 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 2 Jul 2016 17:56:19 +0200 Subject: [PATCH 339/417] code refactoring --- README.md | 2 +- package.json | 2 +- src/PlayerTracker.js | 18 +++++++++--------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 9b836e028..2ec2e0d42 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.13** +Current version: **1.2.14** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 20780277b..abad3b044 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.13", + "version": "1.2.14", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 8d4cf696b..1fd5a896f 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -19,8 +19,8 @@ function PlayerTracker(gameServer, socket) { this.clientNodes = []; this.cells = []; this.mergeOverride = false; // Triggered by console command - this.score = 0; // Needed for leaderboard - this.scale = 1; + this._score = 0; // Needed for leaderboard + this._scale = 1; this.isMassChanged = true; this.borderCounter = 0; @@ -168,13 +168,13 @@ PlayerTracker.prototype.getTeam = function () { PlayerTracker.prototype.getScore = function () { if (this.isMassChanged) this.updateMass(); - return this.score; + return this._score; }; PlayerTracker.prototype.getScale = function () { if (this.isMassChanged) this.updateMass(); - return this.scale; + return this._scale; }; PlayerTracker.prototype.updateMass = function () { @@ -188,10 +188,10 @@ PlayerTracker.prototype.updateMass = function () { } if (totalSize == 0) { //do not change scale for spectators or not in game players - this.score = 0; + this._score = 0; } else { - this.score = totalMass; - this.scale = Math.pow(Math.min(64 / totalSize, 1), 0.4); + this._score = totalMass; + this._scale = Math.pow(Math.min(64 / totalSize, 1), 0.4); } this.isMassChanged = false; }; @@ -289,7 +289,7 @@ PlayerTracker.prototype.updateTick = function () { if (this.freeRoam || this.getSpectateTarget() == null) { // free roam this.updateCenterFreeRoam(); - this.scale = this.gameServer.config.serverSpectatorScale;//0.25; + this._scale = this.gameServer.config.serverSpectatorScale;//0.25; } else { // spectate target return; @@ -315,7 +315,7 @@ PlayerTracker.prototype.sendUpdate = function () { var player = this.getSpectateTarget(); if (player != null) { this.setCenterPos(player.centerPos.x, player.centerPos.y); - this.scale = player.getScale(); + this._scale = player.getScale(); this.viewBox = player.viewBox; this.viewNodes = player.viewNodes; } From a999053aef99bf757dd027833a29dd4aa70e57d6 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 2 Jul 2016 18:06:49 +0200 Subject: [PATCH 340/417] performance optimization --- README.md | 2 +- package.json | 2 +- src/entity/Cell.js | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2ec2e0d42..139e63ab2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.14** +Current version: **1.2.15** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index abad3b044..e33df1f85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.14", + "version": "1.2.15", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 9eb6da35c..997c41433 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -5,8 +5,8 @@ function Cell(gameServer, owner, position, size) { this.tickOfBirth = 0; this.color = { r: 0, g: 0, b: 0 }; this.position = { x: 0, y: 0 }; - this._size = 0; - this._sizeSquared = 0; + this._size = null; + this._sizeSquared = null; this._mass = null; this._speed = null; this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass @@ -54,6 +54,7 @@ Cell.prototype.setSize = function (size) { if (isNaN(size)) { throw new TypeError("Cell.setSize: size is NaN"); } + if (this._size === size) return; this._size = size; this._sizeSquared = size * size; this._mass = null; From 99c631b3d1d21cab4b9d4dc3e42389fdb64de03c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 3 Jul 2016 04:46:52 +0200 Subject: [PATCH 341/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 139e63ab2..9414bd13f 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,7 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:443 | 5 | vanilla style IP | Location | Game Mode | Web Site --- | --- | --- | --- +146.185.167.9:443 | Netherlands | FFA | Test server (report issues here) vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge 164.132.48.230:600 | France | FFA | http://c0nsume.me/private4.php?ip=164.132.48.230:600 149.202.87.51:443 | Paris | FFA | http://agarlist.com/ From c161aab7149c78af672a4a1214feb22a47ebd380 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 3 Jul 2016 22:30:08 +0200 Subject: [PATCH 342/417] add c0nsume.me tracker --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 24 ++++++++++++++++++++++-- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9414bd13f..1165b3107 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.15** +Current version: **1.2.16** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index e33df1f85..6cf1afa1d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.15", + "version": "1.2.16", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 023275fe8..ed53691f0 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1566,11 +1566,31 @@ GameServer.prototype.pingServerTracker = function () { }; var req = http.request(options, function (res) { if (res.statusCode != 200) { - Logger.error("Tracker Error: " + res.statusCode); + Logger.error("Tracker Error(ogar.mivabe.nl): " + res.statusCode); } }); req.on('error', function (e) { - Logger.error("Tracker Error: " + e.message); + Logger.error("Tracker Error(ogar.mivabe.nl): " + e.message); + }); + req.write(data); + req.end() + var options = { + host: 'c0nsume.me', + port: '80', + path: '/tracker.php', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(data) + } + }; + var req = http.request(options, function (res) { + if (res.statusCode != 200) { + Logger.error("Tracker Error(c0nsume.me): " + res.statusCode); + } + }); + req.on('error', function (e) { + Logger.error("Tracker Error(c0nsume.me): " + e.message); }); req.write(data); req.end() From fc7426f843e5d79b4d576c91101dfbf97c535c80 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 3 Jul 2016 23:00:10 +0200 Subject: [PATCH 343/417] add ejectSizeLoss --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 4 +++- src/gameserver.ini | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1165b3107..c2934b95d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.16** +Current version: **1.2.17** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 6cf1afa1d..4123ef3ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.16", + "version": "1.2.17", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index ed53691f0..09d472d60 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -90,6 +90,7 @@ function GameServer() { virusMaxAmount: 100, // Maximum number of viruses on the map. If this number is reached, then ejected cells will pass through viruses. ejectSize: 38, // Size of ejected cells (vanilla 38) + ejectSizeLoss: 1, // Player cell size loss on each eject ejectCooldown: 3, // min ticks between ejects ejectSpawnPlayer: 1, // if 1 then player may be spawned from ejected mass @@ -1193,8 +1194,9 @@ GameServer.prototype.ejectMass = function(client) { if (cell.getSize() < this.config.playerMinSplitSize) { continue; } + var size1 = cell.getSize() - this.config.ejectSizeLoss; var size2 = this.config.ejectSize; - var sizeSquared = cell.getSizeSquared() - size2 * size2; + var sizeSquared = size1 * size1 - size2 * size2; if (sizeSquared < this.config.playerMinSize * this.config.playerMinSize) { continue; } diff --git a/src/gameserver.ini b/src/gameserver.ini index 1c7ed0a9e..092811efe 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -78,9 +78,11 @@ virusMaxAmount = 100 # [Ejected Mass] # ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) +# ejectSizeLoss: Player cell size loss on each eject # ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) # ejectSpawnPlayer: if 1 then player may be spawned from ejected mass ejectSize = 38 +ejectSizeLoss = 1 ejectCooldown = 3 ejectSpawnPlayer = 1 From 534bb3e60ace6fd3021ff3448bb1b25ad524a98b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 3 Jul 2016 23:15:43 +0200 Subject: [PATCH 344/417] update readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c2934b95d..45a8fa21b 100644 --- a/README.md +++ b/README.md @@ -80,10 +80,11 @@ This lists Ogar clients and server trackers that I found on internet. URL | Description --- | --- http://ogar.mivabe.nl/master | MivaBe, tracks a lot of servers +http://c0nsume.me/tracker.php | c0nsume.me server tracker Now you can allow MultiOgar to be listed on a server tracker. Just set `serverTracker = 1` in the gameserver.ini, and your server will appear -on this page: http://ogar.mivabe.nl/master +on these pages: http://ogar.mivabe.nl/master , http://c0nsume.me/tracker.php If you don't want to include your server to tracker list, just set `serverTracker = 0` and the server will not ping the server tracker. From f395019c105a533422167181ed120fd438cd616b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 4 Jul 2016 00:38:05 +0200 Subject: [PATCH 345/417] fix mass decay --- README.md | 4 +++- package.json | 2 +- src/GameServer.js | 10 ++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 45a8fa21b..07a5fe632 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.17** +Current version: **1.2.18** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) @@ -112,6 +112,8 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Fixed mass decay +* Added ejectSizeLoss * Added sub-net ban feature (use `ban xx.xx.xx.*` or `ban xx.xx.*.*` to ban entire sub-network) * Added performance optimizations, now up to 700 bots with no lags at all * Fixed bug when some cell split/eject were shown with delay for some clients diff --git a/package.json b/package.json index 4123ef3ea..1357cbb32 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.17", + "version": "1.2.18", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 09d472d60..382cda8ae 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1264,17 +1264,19 @@ GameServer.prototype.getNearestVirus = function(cell) { }; GameServer.prototype.updateMassDecay = function() { - var decay = 1 - (this.config.playerDecayRate * this.gameMode.decayMod); - if (decay == 0) { + if (!this.config.playerDecayRate) { return; } + var decay = 1 - this.config.playerDecayRate * this.gameMode.decayMod; // Loop through all player cells for (var i = 0; i < this.clients.length; i++) { var playerTracker = this.clients[i].playerTracker; for (var j = 0; j < playerTracker.cells.length; j++) { var cell = playerTracker.cells[j]; - // TODO: check if non linear will be better - var size = cell.getSize() * decay; + var size = cell.getSize(); + if (size <= this.config.playerMinSize) + continue; + var size = Math.sqrt(size * size * decay); size = Math.max(size, this.config.playerMinSize); if (size != cell.getSize()) { cell.setSize(size); From 0bd9c66e581edd9868c002dda5485bcbbd55049c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 4 Jul 2016 09:31:11 +0200 Subject: [PATCH 346/417] update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07a5fe632..1937d0cfc 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:443 | 5 | vanilla style IP | Location | Game Mode | Web Site --- | --- | --- | --- -146.185.167.9:443 | Netherlands | FFA | Test server (report issues here) +146.185.167.9:443 | Netherlands | FFA | Test server (report issues here) http://agar.io/?ip=bubble-wars.tk:443 vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge 164.132.48.230:600 | France | FFA | http://c0nsume.me/private4.php?ip=164.132.48.230:600 149.202.87.51:443 | Paris | FFA | http://agarlist.com/ From eb81636bbfca57cc64b603ae870f39d62e32f5f9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 7 Jul 2016 07:53:17 +0200 Subject: [PATCH 347/417] add user login command --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 65 +++++++++++++++++++++++++++++++++++- src/PlayerTracker.js | 4 ++- src/enum/UserRoleEnum.js | 13 ++++++++ src/modules/PlayerCommand.js | 30 +++++++++++++++++ src/userRoles.json | 20 +++++++++++ 7 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 src/enum/UserRoleEnum.js create mode 100644 src/userRoles.json diff --git a/README.md b/README.md index 1937d0cfc..d96337f05 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.18** +Current version: **1.2.19** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 1357cbb32..ac09e010f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.18", + "version": "1.2.19", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 382cda8ae..a10ac4c9b 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -16,6 +16,7 @@ var Entity = require('./entity'); var Gamemode = require('./gamemodes'); var BotLoader = require('./ai/BotLoader'); var Logger = require('./modules/Logger'); +var UserRoleEnum = require('./enum/UserRoleEnum'); // GameServer implementation function GameServer() { @@ -114,10 +115,12 @@ function GameServer() { }; this.ipBanList = []; + this.userList = []; // Parse config this.loadConfig(); this.loadIpBanList(); + this.loadUserList(); this.setBorder(this.config.borderWidth, this.config.borderHeight); this.quadTree = new QuadNode(this.border, 16, 100); @@ -208,7 +211,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { ws.remoteAddress = ws._socket.remoteAddress; ws.remotePort = ws._socket.remotePort; ws.lastAliveTime = +new Date; - Logger.write("CONNECTED " + ws.remoteAddress + ":" + ws.remotePort + ", origin: \"" + ws.upgradeReq.headers.origin + "\""); + Logger.write("CONNECTED " + ws.remoteAddress + ":" + ws.remotePort + ", origin: \"" + ws.upgradeReq.headers.origin + "\""); ws.playerTracker = new PlayerTracker(this, ws); ws.packetHandler = new PacketHandler(this, ws); @@ -1287,6 +1290,7 @@ GameServer.prototype.updateMassDecay = function() { var fileNameConfig = './gameserver.ini'; var fileNameIpBan = './ipbanlist.txt'; +var fileNameUsers = './userRoles.json'; GameServer.prototype.loadConfig = function () { try { @@ -1315,6 +1319,65 @@ GameServer.prototype.loadConfig = function () { this.config.playerMinSize = Math.max(32, this.config.playerMinSize); }; +GameServer.prototype.loadUserList = function () { + try { + this.userList = []; + if (!fs.existsSync(fileNameUsers)) { + Logger.warn(fileNameUsers + " is missing."); + return; + } + var usersJson = fs.readFileSync(fileNameUsers, 'utf-8'); + var list = JSON.parse(usersJson.trim()); + for (var i = 0; i < list.length; ) { + var item = list[i]; + if (!item.hasOwnProperty("ip") || + !item.hasOwnProperty("password") || + !item.hasOwnProperty("role") || + !item.hasOwnProperty("name")) { + list.splice(i, 1); + continue; + } + if (!item.password || !item.password.trim()) { + Logger.warn("User account \"" + item.name + "\" disabled"); + list.splice(i, 1); + continue; + } + if (item.ip) { + item.ip = item.ip.trim(); + } + item.password = item.password.trim(); + if (!UserRoleEnum.hasOwnProperty(item.role)) { + Logger.warn("Unknown user role: " + role); + item.role = UserRoleEnum.USER; + } else { + item.role = UserRoleEnum[item.role]; + } + item.name = (item.name || "").trim(); + i++; + } + this.userList = list; + Logger.info(this.userList.length + " user records loaded."); + } catch (err) { + Logger.error(err.stack); + Logger.error("Failed to load " + fileNameUsers + ": " + err.message); + } +} + +GameServer.prototype.userLogin = function (ip, password) { + if (!password) return null; + password = password.trim(); + if (!password) return null; + for (var i = 0; i < this.userList.length; i++) { + var user = this.userList[i]; + if (user.password != password) + continue; + if (user.ip && user.ip != ip) + continue; + return user; + } + return null; +}; + GameServer.prototype.loadIpBanList = function () { try { if (fs.existsSync(fileNameIpBan)) { diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 1fd5a896f..c1b01790b 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -1,12 +1,14 @@ var Packet = require('./packet'); var GameServer = require('./GameServer'); var BinaryWriter = require("./packet/BinaryWriter"); - +var UserRoleEnum = require("./enum/UserRoleEnum"); function PlayerTracker(gameServer, socket) { this.gameServer = gameServer; this.socket = socket; this.pID = -1; + this.userRole = UserRoleEnum.GUEST; + this.userAuth = null; this.isRemoved = false; this.isCloseRequested = false; this._name = ""; diff --git a/src/enum/UserRoleEnum.js b/src/enum/UserRoleEnum.js new file mode 100644 index 000000000..51e802849 --- /dev/null +++ b/src/enum/UserRoleEnum.js @@ -0,0 +1,13 @@ +function define(name, value) { + Object.defineProperty(exports, name, { + value: value, + enumerable: true, + writable: false, + configurable: false + }); +} + +define("GUEST", 0); +define("USER", 1); +define("MODER", 2); +define("ADMIN", 4); diff --git a/src/modules/PlayerCommand.js b/src/modules/PlayerCommand.js index a0b8a0a4d..3d92976cb 100644 --- a/src/modules/PlayerCommand.js +++ b/src/modules/PlayerCommand.js @@ -1,4 +1,7 @@ var Entity = require('../entity'); +var Logger = require('./Logger'); +var UserRoleEnum = require("../enum/UserRoleEnum"); + function PlayerCommand(gameServer, playerTracker) { this.gameServer = gameServer; @@ -64,6 +67,33 @@ var playerCommands = { this.gameServer.addNode(food); } this.writeLine("You killed yourself"); + }, + login: function (args) { + var password = (args || "").trim(); + if (password.length < 1) { + this.writeLine("ERROR: missing password argument!"); + return; + } + var user = this.gameServer.userLogin(this.playerTracker.socket.remoteAddress, password); + if (!user) { + this.writeLine("ERROR: login failed!"); + return; + } + Logger.write("LOGIN " + this.playerTracker.socket.remoteAddress + ":" + this.playerTracker.socket.remotePort + " as \"" + user.name + "\""); + this.playerTracker.userRole = user.role; + this.playerTracker.userAuth = user.name; + this.writeLine("Login done as \"" + user.name + "\""); + return; + }, + logout: function (args) { + if (this.playerTracker.userRole == UserRoleEnum.GUEST) { + this.writeLine("ERROR: not logged in"); + return; + } + Logger.write("LOGOUT " + this.playerTracker.socket.remoteAddress + ":" + this.playerTracker.socket.remotePort + " as \"" + this.playerTracker.userAuth + "\""); + this.playerTracker.userRole = UserRoleEnum.GUEST; + this.playerTracker.userAuth = null; + this.writeLine("Logout done"); } }; diff --git a/src/userRoles.json b/src/userRoles.json new file mode 100644 index 000000000..cf59cba76 --- /dev/null +++ b/src/userRoles.json @@ -0,0 +1,20 @@ +[ + { + "ip":"127.0.0.1", + "password":"", + "role":"ADMIN", + "name":"Local Administrator" + }, + { + "ip":"127.0.0.1", + "password":"", + "role":"MODER", + "name":"Local Moderator" + }, + { + "ip":"127.0.0.1", + "password":"", + "role":"USER", + "name":"Local User" + } +] \ No newline at end of file From 95da30207383446edef968bb1b0a70b2e2d2b477 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 7 Jul 2016 08:14:43 +0200 Subject: [PATCH 348/417] add chat source flags (server/admin/moder) --- README.md | 2 +- package.json | 2 +- src/packet/ChatMessage.js | 13 ++++++++++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d96337f05..515435378 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.19** +Current version: **1.2.20** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index ac09e010f..d6d42a193 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.19", + "version": "1.2.20", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/packet/ChatMessage.js b/src/packet/ChatMessage.js index 9c6e08899..b5e15a7f1 100644 --- a/src/packet/ChatMessage.js +++ b/src/packet/ChatMessage.js @@ -1,5 +1,6 @@ // Import var BinaryWriter = require("./BinaryWriter"); +var UserRoleEnum = require("../enum/UserRoleEnum"); function ChatMessage(sender, message) { @@ -29,7 +30,17 @@ ChatMessage.prototype.build = function (protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x63); // message id (decimal 99) - writer.writeUInt8(0x00); // flags for client; for future use + + // flags + var flags = 0; + if (this.sender == null) + flags = 0x80; // server message + else if (this.sender.userRole == UserRoleEnum.ADMIN) + flags = 0x40; // admin message + else if (this.sender.userRole == UserRoleEnum.MODER) + flags = 0x20; // moder message + + writer.writeUInt8(flags); writer.writeUInt8(color.r >> 0); writer.writeUInt8(color.g >> 0); writer.writeUInt8(color.b >> 0); From cf1c5dd1fe5024060df49e38b9447ce7105895a1 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 7 Jul 2016 15:15:31 +0200 Subject: [PATCH 349/417] add secure websocket support (TLS) --- src/GameServer.js | 62 +++++++++++++++++++------------ src/HttpsServer.js | 92 ++++++++++++++++++++++++++++++++++++++++++++++ ssl/readme.txt | 7 ++++ 3 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 src/HttpsServer.js create mode 100644 ssl/readme.txt diff --git a/src/GameServer.js b/src/GameServer.js index a10ac4c9b..ab9ea46f4 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -7,6 +7,7 @@ var pjson = require('../package.json'); var ini = require('./modules/ini.js'); var QuadNode = require('./QuadNode.js'); var PlayerCommand = require('./modules/PlayerCommand'); +var HttpsServer = require('./HttpsServer'); // Project imports var Packet = require('./packet'); @@ -20,6 +21,9 @@ var UserRoleEnum = require('./enum/UserRoleEnum'); // GameServer implementation function GameServer() { + this.httpServer = null; + this.wsServer = null; + // Startup this.run = true; this.lastNodeId = 1; @@ -138,34 +142,32 @@ GameServer.prototype.start = function() { // Gamemode configurations this.gameMode.onServerInit(this); - var options = { - port: this.config.serverPort, - perMessageDeflate: false + if (fs.existsSync('./ssl/key.pem') && fs.existsSync('./ssl/cert.pem')) { + // HTTP/TLS + var options = { + key: fs.readFileSync('./ssl/key.pem', 'utf8'), + cert: fs.readFileSync('./ssl/cert.pem', 'utf8') + }; + Logger.info("TLS: supported"); + this.httpServer = HttpsServer.createServer(options); + } else { + // HTTP only + Logger.warn("TLS: not supported (SSL certificate not found!)"); + this.httpServer = http.createServer(); + } + var wsOptions = { + server: this.httpServer, + perMessageDeflate: true }; - - // Start the server - this.socketServer = new WebSocket.Server(options, this.onServerSocketOpen.bind(this)); - this.socketServer.on('error', this.onServerSocketError.bind(this)); - this.socketServer.on('connection', this.onClientSocketOpen.bind(this)); + this.wsServer = new WebSocket.Server(wsOptions); + this.wsServer.on('error', this.onServerSocketError.bind(this)); + this.wsServer.on('connection', this.onClientSocketOpen.bind(this)); + this.httpServer.listen(this.config.serverPort, this.onHttpServerOpen.bind(this)); this.startStatsServer(this.config.serverStatsPort); }; -GameServer.prototype.onServerSocketError = function (error) { - Logger.error("WebSocket: "+ error.code + " - " + error.message); - switch (error.code) { - case "EADDRINUSE": - Logger.error("Server could not bind to port " + this.config.serverPort + "!"); - Logger.error("Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); - break; - case "EACCES": - Logger.error("Please make sure you are running Ogar with root privileges."); - break; - } - process.exit(1); // Exits the program -}; - -GameServer.prototype.onServerSocketOpen = function () { +GameServer.prototype.onHttpServerOpen = function () { // Spawn starting food this.startingFood(); @@ -185,6 +187,20 @@ GameServer.prototype.onServerSocketOpen = function () { } }; +GameServer.prototype.onServerSocketError = function (error) { + Logger.error("WebSocket: " + error.code + " - " + error.message); + switch (error.code) { + case "EADDRINUSE": + Logger.error("Server could not bind to port " + this.config.serverPort + "!"); + Logger.error("Please close out of Skype or change 'serverPort' in gameserver.ini to a different number."); + break; + case "EACCES": + Logger.error("Please make sure you are running Ogar with root privileges."); + break; + } + process.exit(1); // Exits the program +}; + GameServer.prototype.onClientSocketOpen = function (ws) { if (this.config.serverMaxConnections > 0 && this.socketCount >= this.config.serverMaxConnections) { ws.close(1000, "No slots"); diff --git a/src/HttpsServer.js b/src/HttpsServer.js new file mode 100644 index 000000000..8caf6f522 --- /dev/null +++ b/src/HttpsServer.js @@ -0,0 +1,92 @@ +var http = require('http'), + https = require('https'), + inherits = require('util').inherits, + httpSocketHandler = http._connectionListener; + +var isOldNode = /^v0\.10\./.test(process.version); + +function Server(tlsconfig, requestListener) { + if (!(this instanceof Server)) + return new Server(tlsconfig, requestListener); + + if (typeof tlsconfig === 'function') { + requestListener = tlsconfig; + tlsconfig = undefined; + } + + if (typeof tlsconfig === 'object') { + this.removeAllListeners('connection'); + + https.Server.call(this, tlsconfig, requestListener); + + // capture https socket handler, it's not exported like http's socket + // handler + var connev = this._events.connection; + if (typeof connev === 'function') + this._tlsHandler = connev; + else + this._tlsHandler = connev[connev.length - 1]; + this.removeListener('connection', this._tlsHandler); + + this._connListener = connectionListener; + this.on('connection', connectionListener); + + // copy from http.Server + this.timeout = 2 * 60 * 1000; + this.allowHalfOpen = true; + this.httpAllowHalfOpen = false; + } else + http.Server.call(this, requestListener); +} +inherits(Server, https.Server); + +Server.prototype.setTimeout = function (msecs, callback) { + this.timeout = msecs; + if (callback) + this.on('timeout', callback); +}; + +Server.prototype.__httpSocketHandler = httpSocketHandler; + +var connectionListener; +if (isOldNode) { + connectionListener = function (socket) { + var self = this; + socket.ondata = function (d, start, end) { + var firstByte = d[start]; + if (firstByte < 32 || firstByte >= 127) { + // tls/ssl + socket.ondata = null; + self._tlsHandler(socket); + socket.push(d.slice(start, end)); + } else { + self.__httpSocketHandler(socket); + socket.ondata(d, start, end); + } + }; + }; +} else { + connectionListener = function (socket) { + var self = this; + var data = socket.read(1); + if (data === null) { + socket.once('readable', function () { + self._connListener(socket); + }); + } else { + var firstByte = data[0]; + socket.unshift(data); + if (firstByte < 32 || firstByte >= 127) { + // tls/ssl + this._tlsHandler(socket); + } else + this.__httpSocketHandler(socket); + } + }; +} + +exports.Server = Server; + +exports.createServer = function (tlsconfig, requestListener) { + return new Server(tlsconfig, requestListener); +}; \ No newline at end of file diff --git a/ssl/readme.txt b/ssl/readme.txt new file mode 100644 index 000000000..ecc03e9f2 --- /dev/null +++ b/ssl/readme.txt @@ -0,0 +1,7 @@ +Place private key and certificate files here: +key.pem - your private key +cert.pem - your certificate + +You can create it with openssl: + +openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 100 -nodes \ No newline at end of file From 5d43dd863742d796c17602ceacaf962d6d443a5f Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 7 Jul 2016 15:20:05 +0200 Subject: [PATCH 350/417] update revision --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 515435378..2f6393fb5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.20** +Current version: **1.2.21** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index d6d42a193..234e3c16b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.20", + "version": "1.2.21", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", From 0a652f56be2002bc0d5000c0750fbd9524ded958 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 8 Jul 2016 03:37:02 +0200 Subject: [PATCH 351/417] fix logging for tracker errors --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 16 ++++++++-------- src/modules/Logger.js | 8 +++++++- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2f6393fb5..559d14899 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.21** +Current version: **1.2.22** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 234e3c16b..10336344c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.21", + "version": "1.2.22", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index ab9ea46f4..53690839d 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1637,7 +1637,7 @@ GameServer.prototype.pingServerTracker = function () { '&uptime=' + process.uptime() + // Number of seconds server has been running '&version=MultiOgar ' + pjson.version + '&start_time=' + this.startTime; - var options ={ + var options1 = { host: 'ogar.mivabe.nl', port: '80', path: '/master', @@ -1647,17 +1647,17 @@ GameServer.prototype.pingServerTracker = function () { 'Content-Length': Buffer.byteLength(data) } }; - var req = http.request(options, function (res) { + var req = http.request(options1, function (res) { if (res.statusCode != 200) { - Logger.error("Tracker Error(ogar.mivabe.nl): " + res.statusCode); + Logger.writeError("Tracker Error(" + options1.host + "): " + res.statusCode); } }); req.on('error', function (e) { - Logger.error("Tracker Error(ogar.mivabe.nl): " + e.message); + Logger.writeError("Tracker Error(" + options1.host + "): " + e.message); }); req.write(data); req.end() - var options = { + var options2 = { host: 'c0nsume.me', port: '80', path: '/tracker.php', @@ -1667,13 +1667,13 @@ GameServer.prototype.pingServerTracker = function () { 'Content-Length': Buffer.byteLength(data) } }; - var req = http.request(options, function (res) { + var req = http.request(options2, function (res) { if (res.statusCode != 200) { - Logger.error("Tracker Error(c0nsume.me): " + res.statusCode); + Logger.writeError("Tracker Error(" + options2.host + "): " + res.statusCode); } }); req.on('error', function (e) { - Logger.error("Tracker Error(c0nsume.me): " + e.message); + Logger.writeError("Tracker Error(" + options2.host + "): " + e.message); }); req.write(data); req.end() diff --git a/src/modules/Logger.js b/src/modules/Logger.js index fcdf555ab..3e1e71e41 100644 --- a/src/modules/Logger.js +++ b/src/modules/Logger.js @@ -19,6 +19,7 @@ module.exports.error = error; module.exports.fatal = fatal; module.exports.print = print; module.exports.write = write; +module.exports.writeError = writeError; module.exports.start = start; function debug(message) { @@ -62,6 +63,11 @@ function write(message) { writeLog("[NONE ][" + getTimeString() + "] " + message); }; +function writeError(message) { + message = util.format(message); + writeLog("[ERROR][" + getTimeString() + "] " + message); +}; + // --- utils --- @@ -92,7 +98,7 @@ function getTimeString() { th = ("00" + th).slice(-2); tm = ("00" + tm).slice(-2); ts = ("00" + ts).slice(-2); - return th + "-" + tm + "-" + ts; + return th + ":" + tm + ":" + ts; }; function writeCon(color, message) { From 6e9731c30ba4517f2e6157e3bfa51045142b0c6e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 8 Jul 2016 03:42:20 +0200 Subject: [PATCH 352/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 559d14899..bb7037e88 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge ## What's new: +* Added support for secure websocket connections (TLS) * Fixed mass decay * Added ejectSizeLoss * Added sub-net ban feature (use `ban xx.xx.xx.*` or `ban xx.xx.*.*` to ban entire sub-network) From 9d853e86699f3ba6cbd22cd5b3f9c7fb04b98b70 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 8 Jul 2016 07:42:25 +0200 Subject: [PATCH 353/417] fix ejectSizeLoss behavior --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 6 +++--- src/gameserver.ini | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bb7037e88..95ee2f812 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.22** +Current version: **1.2.23** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 10336344c..7f649998e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.22", + "version": "1.2.23", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 53690839d..d21c6d126 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -95,7 +95,7 @@ function GameServer() { virusMaxAmount: 100, // Maximum number of viruses on the map. If this number is reached, then ejected cells will pass through viruses. ejectSize: 38, // Size of ejected cells (vanilla 38) - ejectSizeLoss: 1, // Player cell size loss on each eject + ejectSizeLoss: 43, // Eject size which will be substracted from player cell (vanilla 43?) ejectCooldown: 3, // min ticks between ejects ejectSpawnPlayer: 1, // if 1 then player may be spawned from ejected mass @@ -1213,9 +1213,9 @@ GameServer.prototype.ejectMass = function(client) { if (cell.getSize() < this.config.playerMinSplitSize) { continue; } - var size1 = cell.getSize() - this.config.ejectSizeLoss; var size2 = this.config.ejectSize; - var sizeSquared = size1 * size1 - size2 * size2; + var sizeLoss = this.config.ejectSizeLoss; + var sizeSquared = cell.getSizeSquared() - sizeLoss * sizeLoss; if (sizeSquared < this.config.playerMinSize * this.config.playerMinSize) { continue; } diff --git a/src/gameserver.ini b/src/gameserver.ini index 092811efe..ea30f8754 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -78,11 +78,11 @@ virusMaxAmount = 100 # [Ejected Mass] # ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) -# ejectSizeLoss: Player cell size loss on each eject +# ejectSizeLoss: Eject size which will be substracted from player cell (vanilla 43?) # ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) # ejectSpawnPlayer: if 1 then player may be spawned from ejected mass ejectSize = 38 -ejectSizeLoss = 1 +ejectSizeLoss = 43 ejectCooldown = 3 ejectSpawnPlayer = 1 From d5e55927f0d267b1a4918dc42a683d065d4743db Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 8 Jul 2016 07:52:19 +0200 Subject: [PATCH 354/417] Undefined Variable Fix on Exit #150 --- README.md | 2 +- package.json | 2 +- src/modules/CommandList.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 95ee2f812..a656d9ec4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.23** +Current version: **1.2.24** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 7f649998e..18abe0a0a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.23", + "version": "1.2.24", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index bc806a131..f257ed98b 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -270,7 +270,7 @@ Commands.list = { }, exit: function(gameServer, split) { Logger.warn("Closing server..."); - gameServer.socketServer.close(); + gameServer.wsServer.close(); process.exit(1); }, food: function(gameServer, split) { From 05d7f70b03483d4bfca1b77e2b59ebb3108cafe5 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 8 Jul 2016 08:50:15 +0200 Subject: [PATCH 355/417] fix path for SSL certificate --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 13 +++++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a656d9ec4..ed7a03a00 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.24** +Current version: **1.2.25** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 18abe0a0a..aa1df156a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.24", + "version": "1.2.25", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index d21c6d126..1af7738ca 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -2,7 +2,8 @@ var WebSocket = require('ws'); var http = require('http'); var fs = require("fs"); -var os = require("os"); +var os = require('os'); +var path = require('path'); var pjson = require('../package.json'); var ini = require('./modules/ini.js'); var QuadNode = require('./QuadNode.js'); @@ -142,11 +143,15 @@ GameServer.prototype.start = function() { // Gamemode configurations this.gameMode.onServerInit(this); - if (fs.existsSync('./ssl/key.pem') && fs.existsSync('./ssl/cert.pem')) { + var dirSsl = path.join(path.dirname(module.filename), '../ssl'); + var pathKey = path.join(dirSsl, 'key.pem'); + var pathCert = path.join(dirSsl, 'cert.pem'); + + if (fs.existsSync(pathKey) && fs.existsSync(pathCert)) { // HTTP/TLS var options = { - key: fs.readFileSync('./ssl/key.pem', 'utf8'), - cert: fs.readFileSync('./ssl/cert.pem', 'utf8') + key: fs.readFileSync(pathKey, 'utf8'), + cert: fs.readFileSync(pathCert, 'utf8') }; Logger.info("TLS: supported"); this.httpServer = HttpsServer.createServer(options); From d1ce0bd19709ae880770698e4b704605a9a2613e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 8 Jul 2016 09:26:49 +0200 Subject: [PATCH 356/417] fix network interface binding --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 3 ++- src/gameserver.ini | 3 +++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ed7a03a00..098a51828 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.25** +Current version: **1.2.26** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index aa1df156a..a13894bd0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.25", + "version": "1.2.26", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 1af7738ca..096d47570 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -63,6 +63,7 @@ function GameServer() { serverMaxConnections: 64, // Maximum number of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum number of connections from the same IP (0 for no limit) serverPort: 443, // Server port + serverBind: '0.0.0.0', // Network interface binding serverTracker: 0, // Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master serverGamemode: 0, // Gamemode, 0 = FFA, 1 = Teams serverBots: 0, // Number of player bots to spawn @@ -167,7 +168,7 @@ GameServer.prototype.start = function() { this.wsServer = new WebSocket.Server(wsOptions); this.wsServer.on('error', this.onServerSocketError.bind(this)); this.wsServer.on('connection', this.onClientSocketOpen.bind(this)); - this.httpServer.listen(this.config.serverPort, this.onHttpServerOpen.bind(this)); + this.httpServer.listen(this.config.serverPort, this.config.serverBind, this.onHttpServerOpen.bind(this)); this.startStatsServer(this.config.serverStatsPort); }; diff --git a/src/gameserver.ini b/src/gameserver.ini index ea30f8754..07c8dce5f 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -18,6 +18,8 @@ # [Server] # serverTimeout: Seconds to keep connection alive for non-responding client # serverIpLimit: Controls the maximum connections from single IP (use 0 to disable) +# serverPort: Server port which will be used to listen for incoming connections +# serverBind: Server network interface which will be used to listen for incoming connections (0.0.0.0 for all IPv4 interfaces) # serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) # serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games # serverBots: Number of player bots to spawn (Experimental) @@ -36,6 +38,7 @@ serverTimeout = 300 serverMaxConnections = 128 serverIpLimit = 4 serverPort = 443 +serverBind = "0.0.0.0" serverTracker = 0 serverGamemode = 0 serverBots = 0 From 6d078535b4a120c34c63d2ea408582f16ce1257e Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 8 Jul 2016 10:13:00 +0200 Subject: [PATCH 357/417] fix scramble level 3 to support existing clients --- src/GameServer.js | 2 +- src/PlayerTracker.js | 18 +++++++++++------- src/gameserver.ini | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 096d47570..609a22b62 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -73,7 +73,7 @@ function GameServer() { serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Update interval of server stats in seconds serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections - serverScrambleCoords: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) + serverScrambleLevel: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap); 3 - high scrambling (no border) serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. serverName: 'MultiOgar #1', // Server name diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index c1b01790b..760805b1c 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -76,7 +76,7 @@ module.exports = PlayerTracker; // Setters/Getters PlayerTracker.prototype.scramble = function () { - if (!this.gameServer.config.serverScrambleCoords) { + if (!this.gameServer.config.serverScrambleLevel) { this.scrambleId = 0; this.scrambleX = 0; this.scrambleY = 0; @@ -222,16 +222,20 @@ PlayerTracker.prototype.joinGame = function (name, skin) { this.socket.sendPacket(new Packet.ClearAll()); this.clientNodes = []; this.scramble(); - if (this.gameServer.config.serverScrambleCoords < 2) { + if (this.gameServer.config.serverScrambleLevel < 2) { // no scramble / lightweight scramble this.socket.sendPacket(new Packet.SetBorder(this, this.gameServer.border)); } - if (this.gameServer.config.serverScrambleCoords == 3) { + else if (this.gameServer.config.serverScrambleLevel == 3) { // Scramble level 3 (no border) - // Unsupported on some clients! (include vanilla) - // ogar.mivabe.nl works ok // Ruins most known minimaps - this.socket.sendPacket(new Packet.SetBorder(this, {minx:1/0,miny:1/0,maxx:1/0,maxy:1/0})); + var border = { + minx: this.gameServer.border.minx - (0x10000 + 10000000 * Math.random()), + miny: this.gameServer.border.miny - (0x10000 + 10000000 * Math.random()), + maxx: this.gameServer.border.maxx + (0x10000 + 10000000 * Math.random()), + maxy: this.gameServer.border.maxy + (0x10000 + 10000000 * Math.random()) + }; + this.socket.sendPacket(new Packet.SetBorder(this, border)); } this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); }; @@ -325,7 +329,7 @@ PlayerTracker.prototype.sendUpdate = function () { this.sendCameraPacket(); } - if (this.gameServer.config.serverScrambleCoords == 2) { + if (this.gameServer.config.serverScrambleLevel == 2) { // scramble (moving border) if (this.borderCounter == 0) { var bound = { diff --git a/src/gameserver.ini b/src/gameserver.ini index 07c8dce5f..7e58ceba7 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -28,7 +28,7 @@ # serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. # serverStatsUpdate: Update interval of server stats in seconds # serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections -# serverScrambleCoords: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap, a little slow, some clients may not support it) +# serverScrambleLevel: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap), 3 - high level scrambling (no border) # serverMaxLB: Controls the maximum players displayed on the leaderboard. # serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. # serverName: Server name, for example "My great server" @@ -48,7 +48,7 @@ serverSpectatorScale = 0.4 serverStatsPort = 88 serverStatsUpdate = 60 serverLogLevel = 1 -serverScrambleCoords = 1 +serverScrambleLevel = 1 serverMaxLB = 10 serverChat = 1 serverName = "MultiOgar #1" From 1380ac7ca39bcbda67ede5e0aa8b170ec989c8a4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 8 Jul 2016 10:13:45 +0200 Subject: [PATCH 358/417] update revision --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 098a51828..c4b2bf790 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.26** +Current version: **1.2.27** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index a13894bd0..e12d01a9f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.26", + "version": "1.2.27", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", From a619a6ce217a9f75661496d16794c477c6b26f24 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 8 Jul 2016 04:20:25 -0400 Subject: [PATCH 359/417] Don't add ssl/ to the stage --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2336c030d..99862063f 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,7 @@ src/.readwarning src/ipbanlist.txt # Prevent user settings overwrite -src/gameserver.ini \ No newline at end of file +src/gameserver.ini + +# Prevent certificate leak +ssl/ From 03a40c9d538ffa4dee6c3e01bac6050280fab799 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 9 Jul 2016 06:33:58 +0200 Subject: [PATCH 360/417] fix error logging --- src/GameServer.js | 35 +++++-- src/enum/LogLevelEnum.js | 15 +++ src/gameserver.ini | 7 +- src/index.js | 14 ++- src/modules/CommandList.js | 35 +++---- src/modules/Logger.js | 205 +++++++++++++++++++++++++++++-------- 6 files changed, 235 insertions(+), 76 deletions(-) create mode 100644 src/enum/LogLevelEnum.js diff --git a/src/GameServer.js b/src/GameServer.js index 609a22b62..624fd71f4 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -59,6 +59,9 @@ function GameServer() { // Config this.config = { + logVerbosity: 4, // Console log level (0=NONE; 1=FATAL; 2=ERROR; 3=WARN; 4=INFO; 5=DEBUG) + logFileVerbosity: 5, // File log level + serverTimeout: 300, // Seconds to keep connection alive for non-responding client serverMaxConnections: 64, // Maximum number of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum number of connections from the same IP (0 for no limit) @@ -72,7 +75,6 @@ function GameServer() { serverSpectatorScale: 0.4, // Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Update interval of server stats in seconds - serverLogLevel: 1, // Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections serverScrambleLevel: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap); 3 - high scrambling (no border) serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. @@ -1339,6 +1341,27 @@ GameServer.prototype.loadConfig = function () { } // check config (min player size = 32 => mass = 10.24) this.config.playerMinSize = Math.max(32, this.config.playerMinSize); + Logger.setVerbosity(this.config.logVerbosity); + Logger.setFileVerbosity(this.config.logFileVerbosity); +}; + +GameServer.prototype.changeConfig = function (name, value) { + if (value == null || isNaN(value)) { + Logger.warn("Invalid value: " + value); + return; + } + if (!this.config.hasOwnProperty(name)) { + Logger.warn("Unknown config value: " + name); + return; + } + this.config[name] = value; + + // update/validate + this.config.playerMinSize = Math.max(32, this.config.playerMinSize); + Logger.setVerbosity(this.config.logVerbosity); + Logger.setFileVerbosity(this.config.logFileVerbosity); + + Logger.print("Set " + name + " = " + this.config[name]); }; GameServer.prototype.loadUserList = function () { @@ -1471,9 +1494,9 @@ GameServer.prototype.banIp = function (ip) { } this.ipBanList.push(ip); if (ipBin[2]=="*" || ipBin[3] == "*") { - Logger.info("The IP sub-net " + ip + " has been banned"); + Logger.print("The IP sub-net " + ip + " has been banned"); } else { - Logger.info("The IP " + ip + " has been banned"); + Logger.print("The IP " + ip + " has been banned"); } this.clients.forEach(function (socket) { // If already disconnected or the ip does not match @@ -1488,7 +1511,7 @@ GameServer.prototype.banIp = function (ip) { // disconnect socket.close(1000, "Banned from server"); var name = socket.playerTracker.getFriendlyName(); - Logger.info("Banned: \"" + name + "\" with Player ID " + socket.playerTracker.pID); + Logger.print("Banned: \"" + name + "\" with Player ID " + socket.playerTracker.pID); this.sendChatMessage(null, null, "Banned \"" + name + "\""); // notify to don't confuse with server bug }, this); this.saveIpBanList(); @@ -1501,7 +1524,7 @@ GameServer.prototype.unbanIp = function (ip) { return; } this.ipBanList.splice(index, 1); - Logger.info("Unbanned IP: " + ip); + Logger.print("Unbanned IP: " + ip); this.saveIpBanList(); }; @@ -1520,7 +1543,7 @@ GameServer.prototype.kickId = function (id) { // disconnect socket.close(1000, "Kicked from server"); var name = socket.playerTracker.getFriendlyName(); - Logger.info("Kicked \"" + name + "\""); + Logger.print("Kicked \"" + name + "\""); this.sendChatMessage(null, null, "Kicked \"" + name + "\""); // notify to don't confuse with server bug count++; }, this); diff --git a/src/enum/LogLevelEnum.js b/src/enum/LogLevelEnum.js new file mode 100644 index 000000000..c559b9be6 --- /dev/null +++ b/src/enum/LogLevelEnum.js @@ -0,0 +1,15 @@ +function define(name, value) { + Object.defineProperty(exports, name, { + value: value, + enumerable: true, + writable: false, + configurable: false + }); +} + +define("NONE", 0); +define("FATAL", 1); +define("ERROR", 2); +define("WARN", 3); +define("INFO", 4); +define("DEBUG", 5); diff --git a/src/gameserver.ini b/src/gameserver.ini index 7e58ceba7..4716c8d00 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -14,6 +14,11 @@ # playerStartSize = massToSize(43) # It will be automatically converted to 66 +# [Log] +# logVerbosity: Console log level (0=NONE; 1=FATAL; 2=ERROR; 3=WARN; 4=INFO; 5=DEBUG) +# logFileVerbosity: File log level +logVerbosity = 4 +logFileVerbosity = 5 # [Server] # serverTimeout: Seconds to keep connection alive for non-responding client @@ -27,7 +32,6 @@ # serverSpectatorScale: Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) # serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. # serverStatsUpdate: Update interval of server stats in seconds -# serverLogLevel: Logging level of the server. 0 = No logs, 1 = Logs the console, 2 = Logs console and ip connections # serverScrambleLevel: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap), 3 - high level scrambling (no border) # serverMaxLB: Controls the maximum players displayed on the leaderboard. # serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. @@ -47,7 +51,6 @@ serverViewBaseY = 1080 serverSpectatorScale = 0.4 serverStatsPort = 88 serverStatsUpdate = 60 -serverLogLevel = 1 serverScrambleLevel = 1 serverMaxLB = 10 serverChat = 1 diff --git a/src/index.js b/src/index.js index 9e0f39b02..faf5a51d1 100644 --- a/src/index.js +++ b/src/index.js @@ -9,7 +9,19 @@ var showConsole = true; // Start msg Logger.start(); -Logger.info("\u001B[1m\u001B[32mMultiOgar "+pjson.version+"\u001B[37m - An open source multi-protocol ogar server\u001B[0m"); + +process.on('exit', function (code) { + Logger.debug("process.exit(" + code + ")"); + Logger.shutdown(); +}); + +process.on('uncaughtException', function (err) { + Logger.fatal(err.stack); + process.exit(1); +}); + +Logger.info("\u001B[1m\u001B[32mMultiOgar " + pjson.version + "\u001B[37m - An open source multi-protocol ogar server\u001B[0m"); + // Handle arguments process.argv.forEach(function(val) { diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index f257ed98b..028632281 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -149,11 +149,11 @@ Commands.list = { Logger.warn("Player ID " + id + " not found!"); }, banlist: function(gameServer, split) { - Logger.info("Showing " + gameServer.ipBanList.length + " banned IPs: "); - console.log(" IP | IP "); - console.log("-----------------------------------"); + Logger.print("Showing " + gameServer.ipBanList.length + " banned IPs: "); + Logger.print(" IP | IP "); + Logger.print("-----------------------------------"); for (var i = 0; i < gameServer.ipBanList.length; i += 2) { - console.log(" " + fillChar(gameServer.ipBanList[i], " ", 15) + " | " + Logger.print(" " + fillChar(gameServer.ipBanList[i], " ", 15) + " | " + (gameServer.ipBanList.length === i+1 ? "" : gameServer.ipBanList[i+1] ) ); } @@ -225,16 +225,7 @@ Commands.list = { } else { value = parseInt(value); } - if (value == null || isNaN(value)) { - Logger.warn("Invalid value: " + split[2]); - return; - } - if (!gameServer.config.hasOwnProperty(key)) { - Logger.warn("Unknown config value: " + key); - return; - } - gameServer.config[key] = value; - console.log("Set " + key + " to " + value); + gameServer.changeConfig(key, value); }, clear: function() { process.stdout.write("\u001b[2J\u001b[0;0H"); @@ -472,9 +463,9 @@ Commands.list = { gameServer.unbanIp(split[1].trim()); }, playerlist: function(gameServer, split) { - Logger.info("Showing " + gameServer.clients.length + " players: "); - console.log(" ID | IP | P | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space - console.log(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); + Logger.print("Showing " + gameServer.clients.length + " players: "); + Logger.print(" ID | IP | P | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space + Logger.print(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); var sockets = gameServer.clients.slice(0); sockets.sort(function (a, b) { return a.playerTracker.pID - b.playerTracker.pID; }); for (var i = 0; i < sockets.length; i++) { @@ -506,9 +497,9 @@ Commands.list = { reason += "[" + socket.closeReason.code + "] "; if (socket.closeReason.message) reason += socket.closeReason.message; - console.log(" " + id + " | " + ip + " | " + protocol + " | " + reason); + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + reason); } else if (!socket.packetHandler.protocol && socket.isConnected) { - console.log(" " + id + " | " + ip + " | " + protocol + " | " + "[CONNECTING]"); + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + "[CONNECTING]"); }else if (client.spectate) { nick = "in free-roam"; if (!client.freeRoam) { @@ -518,17 +509,17 @@ Commands.list = { } } data = fillChar("SPECTATING: " + nick, '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); - console.log(" " + id + " | " + ip + " | " + protocol + " | " + data); + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + data); } else if (client.cells.length > 0) { nick = fillChar(client.getFriendlyName(), ' ', gameServer.config.playerMaxNickLength); cells = fillChar(client.cells.length, ' ', 5, true); score = fillChar(client.getScore() >> 0, ' ', 6, true); position = fillChar(client.centerPos.x >> 0, ' ', 5, true) + ', ' + fillChar(client.centerPos.y >> 0, ' ', 5, true); - console.log(" " + id + " | " + ip + " | " + protocol + " | " + nick + " | " + cells + " | " + score + " | " + position); + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + nick + " | " + cells + " | " + score + " | " + position); } else { // No cells = dead player or in-menu data = fillChar('DEAD OR NOT PLAYING', '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); - console.log(" " + id + " | " + ip + " | " + protocol + " | " + data); + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + data); } } }, diff --git a/src/modules/Logger.js b/src/modules/Logger.js index 3e1e71e41..f561530ca 100644 --- a/src/modules/Logger.js +++ b/src/modules/Logger.js @@ -10,8 +10,9 @@ var fs = require("fs"); var util = require('util'); var EOL = require('os').EOL; +var LogLevelEnum = require('../enum/LogLevelEnum'); - + module.exports.debug = debug; module.exports.info = info; module.exports.warn = warn; @@ -19,53 +20,67 @@ module.exports.error = error; module.exports.fatal = fatal; module.exports.print = print; module.exports.write = write; +module.exports.writeDebug = writeDebug; module.exports.writeError = writeError; module.exports.start = start; +module.exports.shutdown = shutdown; +module.exports.setVerbosity = function (level) { + logVerbosity = level; +}; +module.exports.setFileVerbosity = function (level) { + logFileVerbosity = level; +}; +module.exports.getVerbosity = function () { + return logVerbosity; +}; +module.exports.getFileVerbosity = function () { + return logFileVerbosity; +}; + + +var logVerbosity = LogLevelEnum.DEBUG; +var logFileVerbosity = LogLevelEnum.DEBUG; function debug(message) { - message = util.format(message); - writeCon(colorWhite, "[DEBUG] " + message); - writeLog("[DEBUG][" + getTimeString() + "] " + message); + writeCon(colorWhite, LogLevelEnum.DEBUG, message); + writeLog(LogLevelEnum.DEBUG, message); }; function info(message) { - message = util.format(message); - writeCon(colorWhite + colorBright, "[INFO ] " + message); - writeLog("[INFO ][" + getTimeString() + "] " + message); + writeCon(colorWhite + colorBright, LogLevelEnum.INFO, message); + writeLog(LogLevelEnum.INFO, message); }; function warn(message) { - message = util.format(message); - writeCon(colorYellow + colorBright, "[WARN ] " + message); - writeLog("[WARN ][" + getTimeString() + "] " + message); + writeCon(colorYellow + colorBright, LogLevelEnum.WARN, message); + writeLog(LogLevelEnum.WARN, message); }; function error(message) { - message = util.format(message); - writeCon(colorRed + colorBright, "[ERROR] " + message); - writeLog("[ERROR][" + getTimeString() + "] " + message); + writeCon(colorRed + colorBright, LogLevelEnum.ERROR, message); + writeLog(LogLevelEnum.ERROR, message); }; function fatal(message) { - message = util.format(message); - writeCon(colorRed + colorBright, "[FATAL] " + message); - writeLog("[FATAL][" + getTimeString() + "] " + message); + writeCon(colorRed + colorBright, LogLevelEnum.FATAL, message); + writeLog(LogLevelEnum.FATAL, message); }; function print(message) { - message = util.format(message); - writeCon(colorWhite, message); - writeLog("[NONE ][" + getTimeString() + "] " + message); + writeCon(colorWhite, LogLevelEnum.NONE, message); + writeLog(LogLevelEnum.NONE, message); }; function write(message) { - message = util.format(message); - writeLog("[NONE ][" + getTimeString() + "] " + message); + writeLog(LogLevelEnum.NONE, message); +}; + +function writeDebug(message) { + writeLog(LogLevelEnum.DEBUG, message); }; function writeError(message) { - message = util.format(message); - writeLog("[ERROR][" + getTimeString() + "] " + message); + writeLog(LogLevelEnum.ERROR, message); }; @@ -101,38 +116,138 @@ function getTimeString() { return th + ":" + tm + ":" + ts; }; -function writeCon(color, message) { - process.stdout.write(color + message + "\u001B[0m" + EOL); +function writeCon(color, level, message) { + if (level > logVerbosity) return; + message = util.format(message); + var prefix = ""; + if (level == LogLevelEnum.DEBUG) + prefix = "[DEBUG] "; + else if (level == LogLevelEnum.INFO) + prefix = "[INFO ] "; + else if (level == LogLevelEnum.WARN) + prefix = "[WARN ] "; + else if (level == LogLevelEnum.ERROR) + prefix = "[ERROR] "; + else if (level == LogLevelEnum.FATAL) + prefix = "[FATAL] "; + process.stdout.write(color + prefix + message + "\u001B[0m" + EOL); }; -function writeLog(message) { - if (consoleLog == null) +function writeLog(level, message) { + if (level > logFileVerbosity || writeError) return; - consoleLog.write(message + EOL); + message = util.format(message); + var prefix = ""; + if (level == LogLevelEnum.DEBUG) + prefix = "[DEBUG]"; + else if (level == LogLevelEnum.INFO) + prefix = "[INFO ]"; + else if (level == LogLevelEnum.WARN) + prefix = "[WARN ]"; + else if (level == LogLevelEnum.ERROR) + prefix = "[ERROR]"; + else if (level == LogLevelEnum.FATAL) + prefix = "[FATAL]"; + else if (level == LogLevelEnum.NONE) + prefix = "[NONE ]"; + prefix += "[" + getTimeString() + "] "; + + writeQueue.push(prefix + message + EOL); + if (writeShutdown) { + flushSync(); + } else { + if (writeCounter == 0) { + flushAsync(); + } + } +}; + +var writeError = false; +var writeCounter = 0; +var writeShutdown = false; +var writeStarted = false; +var writeQueue = []; + +function flushAsync() { + if (writeShutdown || consoleLog == null || writeQueue.length == 0) + return; + writeCounter++; + consoleLog.write(writeQueue.shift(), function () { writeCounter--; flushAsync(); }); +}; + +function flushSync() { + try { + var tail = ""; + while (writeQueue.length > 0) { + tail += writeQueue.shift(); + } + var fileName = logFolder + "/" + logFileName + ".log"; + fs.appendFileSync(fileName, tail); + } catch (err) { + writeError = true; + writeCon(colorRed + colorBright, LogLevelEnum.ERROR, err.message); + writeCon(colorRed + colorBright, LogLevelEnum.ERROR, "Failed to append log file!"); + } }; function start() { - if (consoleLog != null) + if (writeStarted) return; - var timeString = getDateTimeString(); - var fileName = logFolder + "/" + logFileName + ".log"; - var fileName2 = logBackupFolder + "/" + logFileName + "-" + timeString + ".log"; - - if (!fs.existsSync(logFolder)) { - // Make log folder - fs.mkdirSync(logFolder); - } else if (fs.existsSync(fileName)) { - if (!fs.existsSync(logBackupFolder)) { - // Make log backup folder - fs.mkdirSync(logBackupFolder); + writeStarted = true; + try { + console.log = function (message) { print(message); }; + + var timeString = getDateTimeString(); + var fileName = logFolder + "/" + logFileName + ".log"; + var fileName2 = logBackupFolder + "/" + logFileName + "-" + timeString + ".log"; + + if (!fs.existsSync(logFolder)) { + // Make log folder + fs.mkdirSync(logFolder); + } else if (fs.existsSync(fileName)) { + if (!fs.existsSync(logBackupFolder)) { + // Make log backup folder + fs.mkdirSync(logBackupFolder); + } + // Backup previous log + fs.renameSync(fileName, fileName2); } - // Backup previous log - fs.renameSync(fileName, fileName2); + + fs.writeFileSync(fileName, "=== Started " + timeString + " ===" + EOL); + var file = fs.createWriteStream(fileName, { flags: 'a' }); + file.on('open', function () { + if (writeShutdown) { + file.close(); + return; + } + consoleLog = file; + flushAsync(); + }); + file.on('error', function (err) { + writeError = true; + consoleLog = null; + writeCon(colorRed + colorBright, LogLevelEnum.ERROR, err.message); + }); + } catch (err) { + writeError = true; + consoleLog = null; + writeCon(colorRed + colorBright, LogLevelEnum.ERROR, err.message); } - consoleLog = fs.createWriteStream(fileName, { flags: 'w' }); - console.log = function (message) { print(message); }; - writeLog("=== Started " + timeString + " ==="); } + +function shutdown() { + writeShutdown = true; + if (writeError) return; + if (consoleLog != null) { + consoleLog.end(); + consoleLog.close(); + consoleLog.destroy(); + consoleLog = null; + } + writeQueue.push("=== Shutdown " + getDateTimeString() + " ===" + EOL); + flushSync(); +}; + var logFolder = "./logs"; var logBackupFolder = "./logs/LogBackup"; From 0b1736e57a84aaa8029397203a091f7eb636382b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 9 Jul 2016 06:45:20 +0200 Subject: [PATCH 361/417] update revision --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c4b2bf790..a90b05928 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.27** +Current version: **1.2.28** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index e12d01a9f..8fad0a925 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.27", + "version": "1.2.28", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", From ac1ae03d2281efd014cc6cba0d3e2a6449179f94 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 9 Jul 2016 07:30:40 +0200 Subject: [PATCH 362/417] fix date for logger backup --- README.md | 2 +- package.json | 2 +- src/modules/Logger.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a90b05928..b09968f6e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.28** +Current version: **1.2.29** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 8fad0a925..b027aa5c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.28", + "version": "1.2.29", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/modules/Logger.js b/src/modules/Logger.js index f561530ca..97c058e8e 100644 --- a/src/modules/Logger.js +++ b/src/modules/Logger.js @@ -89,8 +89,8 @@ function writeError(message) { function getDateTimeString() { var date = new Date(); var dy = date.getFullYear(); - var dm = date.getMonth(); - var dd = date.getDay(); + var dm = date.getMonth() + 1; + var dd = date.getDate(); var th = date.getHours(); var tm = date.getMinutes(); var ts = date.getSeconds(); From d17c938664089dfd00910448946efa05d2591151 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 10 Jul 2016 04:28:58 +0200 Subject: [PATCH 363/417] perfromance optimizations --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 10 +++++----- src/PlayerTracker.js | 8 +++----- src/ai/BotPlayer.js | 30 +++++++++++------------------- src/modules/CommandList.js | 2 +- 6 files changed, 22 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index b09968f6e..4cc411974 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.29** +Current version: **1.2.30** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index b027aa5c7..d52da6071 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.29", + "version": "1.2.30", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 624fd71f4..d44a75fd5 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -749,11 +749,11 @@ GameServer.prototype.resolveRigidCollision = function (manifold, border) { var py = penetration * ny; // body impulse - var totalMass = manifold.cell1.getMass() + manifold.cell2.getMass(); + var totalMass = manifold.cell1.getSizeSquared() + manifold.cell2.getSizeSquared(); if (totalMass <= 0) return; var invTotalMass = 1 / totalMass; - var impulse1 = manifold.cell2.getMass() * invTotalMass; - var impulse2 = manifold.cell1.getMass() * invTotalMass; + var impulse1 = manifold.cell2.getSizeSquared() * invTotalMass; + var impulse2 = manifold.cell1.getSizeSquared() * invTotalMass; // apply extrusion force manifold.cell1.position.x -= px * impulse1; @@ -883,8 +883,8 @@ GameServer.prototype.updateMoveEngine = function () { } else { // split var maxSplit = this.config.playerMaxCells - client.cells.length; - var maxMass = this.config.playerMaxSize * this.config.playerMaxSize / 100; - var count = (cell1.getMass() / maxMass) >> 0; + var maxMass = this.config.playerMaxSize * this.config.playerMaxSize; + var count = (cell1.getSizeSquared() / maxMass) >> 0; var count = Math.min(count, maxSplit); var splitSize = cell1.getSize() / Math.sqrt(count + 1); var splitMass = splitSize * splitSize / 100; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 760805b1c..5a26822b8 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -181,18 +181,17 @@ PlayerTracker.prototype.getScale = function () { PlayerTracker.prototype.updateMass = function () { var totalSize = 0; - var totalMass = 0; + var totalScore = 0; for (var i = 0; i < this.cells.length; i++) { var node = this.cells[i]; - if (node == null) continue; totalSize += node.getSize(); - totalMass += node.getMass(); + totalScore += node.getSizeSquared(); } if (totalSize == 0) { //do not change scale for spectators or not in game players this._score = 0; } else { - this._score = totalMass; + this._score = totalScore; this._scale = Math.pow(Math.min(64 / totalSize, 1), 0.4); } this.isMassChanged = false; @@ -416,7 +415,6 @@ PlayerTracker.prototype.updateCenterInGame = function() { // Get center of cells var count = 0; for (var i = 0; i < len; i++) { var node = this.cells[i]; - if (node == null) continue; cx += node.position.x; cy += node.position.y; count++; diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index 0fb86505c..c94fd81f6 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -23,7 +23,7 @@ BotPlayer.prototype.getLowestCell = function() { // Sort the cells by Array.sort() function to avoid errors var sorted = this.cells.valueOf(); sorted.sort(function(a, b) { - return b.getMass() - a.getMass(); + return b.getSize() - a.getSize(); }); return sorted[0]; @@ -81,13 +81,15 @@ BotPlayer.prototype.decide = function(cell) { // Same team cell influence = 0; } - else if (cell.getSize() > (check.getSize() + 4) * 1.15) {//cell.getMass() / 1.3 > check.getMass()) { + else if (cell.getSize() > (check.getSize() + 4) * 1.15) { // Can eat it influence = check.getSize() * 2.5; } - else if (check.getSize() + 4 > cell.getSize() * 1.15) {//check.getMass() / 1.3 > cell.getMass()) { + else if (check.getSize() + 4 > cell.getSize() * 1.15) { // Can eat me influence = -check.getSize(); + } else { + influence = -(check.getSize()/cell.getSize()) / 3; } } else if (check.cellType == 1) { // Food @@ -143,14 +145,14 @@ BotPlayer.prototype.decide = function(cell) { // Splitting conditions if (check.cellType == 0 && cell.getSize() > (check.getSize() + 4) * 1.15 && - cell.getMass() / 5 < check.getMass() && + cell.getSize() < check.getSize() * 5 && (!split) && this.splitCooldown == 0 && this.cells.length < 3) { - var endDist = Math.max(this.splitDistance(cell), cell.getSize() * 4); + var endDist = 780 + 40 - cell.getSize() / 2 - check.getSize(); - if (distance < endDist - cell.getSize() - check.getSize()) { + if (endDist > 0 && distance < endDist) { splitTarget = check; split = true; } @@ -167,7 +169,7 @@ BotPlayer.prototype.decide = function(cell) { if (split) { // Can be shortened but I'm too lazy if (threats.length > 0) { - if (this.largest(threats).getMass() / 2.6 > cell.getMass()) { // ??? but works + if (this.largest(threats).getSize() > cell.getSize() * 1.5) { // Splitkill the target this.mouse = { x: splitTarget.position.x, @@ -197,25 +199,15 @@ BotPlayer.prototype.decide = function(cell) { }; }; + // Subfunctions BotPlayer.prototype.largest = function(list) { // Sort the cells by Array.sort() function to avoid errors var sorted = list.valueOf(); sorted.sort(function(a, b) { - return b.getMass() - a.getMass(); + return b.getSize() - a.getSize(); }); return sorted[0]; }; - -BotPlayer.prototype.splitDistance = function(cell) { - // Calculate split distance and check if it is larger than the raw distance - var mass = cell.getMass(); - var t = Math.PI * Math.PI; - var modifier = 3 + Math.log(1 + mass) / 10; - var splitSpeed = cell.owner.gameServer.config.playerSpeed * 30 * Math.min(Math.pow(mass, -Math.PI / t / 10) * modifier, 150); - var endDist = Math.max(splitSpeed * 12.8, cell.getSize() * 2); // Checked via C#, final distance is near 6.512x splitSpeed - - return endDist; -}; diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 028632281..827b0c734 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -513,7 +513,7 @@ Commands.list = { } else if (client.cells.length > 0) { nick = fillChar(client.getFriendlyName(), ' ', gameServer.config.playerMaxNickLength); cells = fillChar(client.cells.length, ' ', 5, true); - score = fillChar(client.getScore() >> 0, ' ', 6, true); + score = fillChar((client.getScore()/100) >> 0, ' ', 6, true); position = fillChar(client.centerPos.x >> 0, ' ', 5, true) + ', ' + fillChar(client.centerPos.y >> 0, ' ', 5, true); Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + nick + " | " + cells + " | " + score + " | " + position); } else { From 20de1f2124ae49903f91724cb9ac9c302bc93079 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 10 Jul 2016 05:19:11 +0200 Subject: [PATCH 364/417] performance optimization --- README.md | 2 +- package.json | 2 +- src/QuadNode.js | 16 ++++++++-------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4cc411974..66d285448 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.30** +Current version: **1.2.31** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index d52da6071..7bae389dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.30", + "version": "1.2.31", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/QuadNode.js b/src/QuadNode.js index 20d7a7314..3b8a7cd57 100644 --- a/src/QuadNode.js +++ b/src/QuadNode.js @@ -11,8 +11,8 @@ function QuadNode(bound, maxChildren, maxLevel, level, parent) { if (!level) level = 0; if (!parent) parent = null; - var width = bound.maxx - bound.minx; - var height = bound.maxy - bound.miny; + var halfWidth = (bound.maxx - bound.minx) / 2; + var halfHeight = (bound.maxy - bound.miny) / 2; this.level = level; this.parent = parent; @@ -21,10 +21,10 @@ function QuadNode(bound, maxChildren, maxLevel, level, parent) { miny: bound.miny, maxx: bound.maxx, maxy: bound.maxy, - width: width, - height: height, - cx: bound.minx + width / 2, - cy: bound.miny + height / 2 + halfWidth: halfWidth, + halfHeight: halfHeight, + cx: bound.minx + halfWidth, + cy: bound.miny + halfHeight }; this.maxChildren = maxChildren; this.maxLevel = maxLevel; @@ -54,8 +54,8 @@ QuadNode.prototype.insert = function (item) { // split and rebalance current node if (this.childNodes.length == 0) { // split - var w = this.bound.width / 2; - var h = this.bound.height / 2; + var w = this.bound.halfWidth; + var h = this.bound.halfHeight; var x0 = this.bound.minx + w; var y0 = this.bound.miny; var x1 = this.bound.minx; From 2859ed67685d647466a071d07fb57041c0963a67 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 10 Jul 2016 06:30:35 +0200 Subject: [PATCH 365/417] use npm package --- README.md | 2 +- package.json | 3 +- src/GameServer.js | 2 +- src/QuadNode.js | 222 ---------------------------------------------- 4 files changed, 4 insertions(+), 225 deletions(-) delete mode 100644 src/QuadNode.js diff --git a/README.md b/README.md index 66d285448..2b79b0a86 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.31** +Current version: **1.2.32** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 7bae389dc..44735babf 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "name": "MultiOgar", - "version": "1.2.31", + "version": "1.2.32", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", "license": "Apache License, Version 2.0", "main": "src/index.js", "dependencies": { + "quad-node": "^1.0.3", "vector2-node": "latest", "ws": "latest" }, diff --git a/src/GameServer.js b/src/GameServer.js index d44a75fd5..577a3c4bb 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -6,7 +6,7 @@ var os = require('os'); var path = require('path'); var pjson = require('../package.json'); var ini = require('./modules/ini.js'); -var QuadNode = require('./QuadNode.js'); +var QuadNode = require('quad-node'); var PlayerCommand = require('./modules/PlayerCommand'); var HttpsServer = require('./HttpsServer'); diff --git a/src/QuadNode.js b/src/QuadNode.js deleted file mode 100644 index 3b8a7cd57..000000000 --- a/src/QuadNode.js +++ /dev/null @@ -1,222 +0,0 @@ -'use strict'; -/* - * Fast and easy Quad-Tree implementation written by Barbosik. - * Useful for quick object search in the area specified with bounds. - * - * Copyright (c) 2016 Barbosik https://github.com/Barbosik - * License: Apache License, Version 2.0 - * - */ - -function QuadNode(bound, maxChildren, maxLevel, level, parent) { - if (!level) level = 0; - if (!parent) parent = null; - var halfWidth = (bound.maxx - bound.minx) / 2; - var halfHeight = (bound.maxy - bound.miny) / 2; - - this.level = level; - this.parent = parent; - this.bound = { - minx: bound.minx, - miny: bound.miny, - maxx: bound.maxx, - maxy: bound.maxy, - halfWidth: halfWidth, - halfHeight: halfHeight, - cx: bound.minx + halfWidth, - cy: bound.miny + halfHeight - }; - this.maxChildren = maxChildren; - this.maxLevel = maxLevel; - this.childNodes = []; - this.items = []; -} - -module.exports = QuadNode; - -QuadNode.prototype.insert = function (item) { - if (item._quadNode != null) { - throw new TypeError("QuadNode.insert: cannot insert item which already belong to another QuadNode!"); - } - if (this.childNodes.length != 0) { - var quad = this.getQuad(item.bound); - if (quad != -1) { - this.childNodes[quad].insert(item); - return; - } - } - this.items.push(item); - item._quadNode = this; // attached field, used for quick search quad node by item - - // check if rebalance needed - if (this.childNodes.length != 0 || this.level >= this.maxLevel || this.items.length < this.maxChildren) - return; - // split and rebalance current node - if (this.childNodes.length == 0) { - // split - var w = this.bound.halfWidth; - var h = this.bound.halfHeight; - var x0 = this.bound.minx + w; - var y0 = this.bound.miny; - var x1 = this.bound.minx; - var y1 = this.bound.miny; - var x2 = this.bound.minx; - var y2 = this.bound.miny + h; - var x3 = this.bound.minx + w; - var y3 = this.bound.miny + h; - var b0 = { minx: x0, miny: y0, maxx: x0 + w, maxy: y0 + h }; - var b1 = { minx: x1, miny: y1, maxx: x1 + w, maxy: y1 + h }; - var b2 = { minx: x2, miny: y2, maxx: x2 + w, maxy: y2 + h }; - var b3 = { minx: x3, miny: y3, maxx: x3 + w, maxy: y3 + h }; - this.childNodes.push(new QuadNode(b0, this.maxChildren, this.maxLevel, this.level + 1, this)); - this.childNodes.push(new QuadNode(b1, this.maxChildren, this.maxLevel, this.level + 1, this)); - this.childNodes.push(new QuadNode(b2, this.maxChildren, this.maxLevel, this.level + 1, this)); - this.childNodes.push(new QuadNode(b3, this.maxChildren, this.maxLevel, this.level + 1, this)); - } - // rebalance - for (var i = 0; i < this.items.length; ) { - var qitem = this.items[i]; - var quad = this.getQuad(qitem.bound); - if (quad != -1) { - this.items.splice(i, 1); - qitem._quadNode = null; - this.childNodes[quad].insert(qitem); - } - else i++; - } -}; - -QuadNode.prototype.remove = function (item) { - if (item._quadNode != this) { - item._quadNode.remove(item); - return; - } - var index = this.items.indexOf(item); - if (index < 0) { - throw new TypeError("QuadNode.remove: item not found!"); - } - this.items.splice(index, 1); - item._quadNode = null; - cleanup(this); -}; - -function cleanup (node) { - if (node.parent==null || node.items.length > 0) return; - for (var i = 0; i < node.childNodes.length; i++) { - var child = node.childNodes[i]; - if (child.childNodes.length > 0 || child.items.length > 0) - return; - } - node.childNodes = []; - cleanup(node.parent); -}; - -QuadNode.prototype.update = function (item) { - this.remove(item); - this.insert(item); -}; - -QuadNode.prototype.clear = function () { - for (var i = 0; i < this.items.length; i++) - this.items[i]._quadNode = null; - this.items = []; - for (var i = 0; i < this.childNodes.length; i++) - this.childNodes[i].clear(); - this.childNodes = []; -}; - -QuadNode.prototype.contains = function (item) { - if (item._quadNode == null) - return false; - if (item._quadNode != this) { - return item._quadNode.contains(item); - } - return this.items.indexOf(item) >= 0; -}; - -QuadNode.prototype.find = function (bound, callback) { - if (this.childNodes.length != 0) { - var quad = this.getQuad(bound); - if (quad != -1) { - this.childNodes[quad].find(bound, callback); - } else { - for (var i = 0; i < this.childNodes.length; i++) { - var node = this.childNodes[i]; - if (checkBoundIntersection(node.bound, bound)) - node.find(bound, callback); - } - } - } - for (var i = 0; i < this.items.length; i++) { - var item = this.items[i]; - if (checkBoundIntersection(item.bound, bound)) - callback(item); - } -}; - -QuadNode.prototype.any = function (bound, predicate) { - if (this.childNodes.length != 0) { - var quad = this.getQuad(bound); - if (quad != -1) { - if (this.childNodes[quad].any(bound, predicate)) - return true; - } else { - for (var i = 0; i < this.childNodes.length; i++) { - var node = this.childNodes[i]; - if (checkBoundIntersection(node.bound, bound)) - if (node.any(bound, predicate)) - return true; - } - } - } - for (var i = 0; i < this.items.length; i++) { - var item = this.items[i]; - if (checkBoundIntersection(item.bound, bound)) { - if (predicate == null || predicate(item)) - return true; - } - } - return false; -}; - -QuadNode.prototype.scanNodeCount = function () { - var count = 0; - for (var i = 0; i < this.childNodes.length; i++) { - count += this.childNodes[i].scanNodeCount(); - } - return 1 + count; -}; - -QuadNode.prototype.scanItemCount = function () { - var count = 0; - for (var i = 0; i < this.childNodes.length; i++) { - count += this.childNodes[i].scanItemCount(); - } - return this.items.length + count; -}; - -// Returns quadrant for the bound. -// Returns -1 if bound cannot completely fit within a child node -QuadNode.prototype.getQuad = function (bound) { - var isTop = bound.miny < this.bound.cy && bound.maxy < this.bound.cy; - var isLeft = bound.minx < this.bound.cx && bound.maxx < this.bound.cx; - if (isLeft) { - if (isTop) return 1; - else if (bound.miny > this.bound.cy) return 2; // isBottom - } - else if (bound.minx > this.bound.cx) // isRight - { - if (isTop) return 0; - else if (bound.miny > this.bound.cy) return 3; // isBottom - } - return -1; // cannot fit (too large size) -}; - -function checkBoundIntersection(bound1, bound2) { - var notIntersect = - bound2.minx >= bound1.maxx || - bound2.maxx <= bound1.minx || - bound2.miny >= bound1.maxy || - bound2.maxy <= bound1.miny; - return !notIntersect; -}; From 851114f90f958038a94a35d721d3351dc46a3ed3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 10 Jul 2016 06:36:57 +0200 Subject: [PATCH 366/417] use latest version for quad-node --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 44735babf..7b32d6d70 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "Apache License, Version 2.0", "main": "src/index.js", "dependencies": { - "quad-node": "^1.0.3", + "quad-node": "latest", "vector2-node": "latest", "ws": "latest" }, From ea569a9d25acbfa46de2cf31a857af5cd76e7cbc Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 10 Jul 2016 06:44:04 +0200 Subject: [PATCH 367/417] use specific version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b32d6d70..44735babf 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "Apache License, Version 2.0", "main": "src/index.js", "dependencies": { - "quad-node": "latest", + "quad-node": "^1.0.3", "vector2-node": "latest", "ws": "latest" }, From acaeb2e64d5e2545f371d2fad6d3577b2301f5e1 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 10 Jul 2016 01:23:33 -0400 Subject: [PATCH 368/417] Use a valid SPDX License Expression Warning every time `$ npm install` is run: `npm WARN package.json MultiOgar@1.2.32 license should be a valid SPDX license expression` Taken from https://spdx.org/licenses/ => https://spdx.org/licenses/Apache-2.0.html --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 44735babf..e4daab1a3 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", - "license": "Apache License, Version 2.0", + "license": "Apache-2.0", "main": "src/index.js", "dependencies": { "quad-node": "^1.0.3", From 10086e9f9be6636d8e2eff30834437f38705059e Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 10 Jul 2016 01:30:49 -0400 Subject: [PATCH 369/417] Update README.md Actual server location is Montreal, Quebec, Canada. play.ogarul.tk no longer sends Protocol 1 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2b79b0a86..30e5bfa2f 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ URL | Protocol | Description --- | --- | --- http://agar.io/?ip=127.0.0.1:443 | 8 | Vanilla http://ogar.mivabe.nl/?ip=127.0.0.1:443 | early 5 | MivaBe, pretty smooth, custom graphics (anime) -http://play.ogarul.tk/?ip=127.0.0.1:443 | 4 | OgarUL, vanilla style (sends invalid protocol=1) +http://play.ogarul.tk/?ip=127.0.0.1:443 | 4 | OgarUL, vanilla style http://c0nsume.me/private4.php?ip=127.0.0.1:443 | 5 | vanilla style ###MultiOgar Servers @@ -104,7 +104,7 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:443 | 5 | vanilla style IP | Location | Game Mode | Web Site --- | --- | --- | --- 146.185.167.9:443 | Netherlands | FFA | Test server (report issues here) http://agar.io/?ip=bubble-wars.tk:443 -vps.simonorj.com:24270 | USA | Instant Merge | https://redd.it/4mufge +vps.simonorj.com:24270 | Montreal | Instant Merge | https://redd.it/4mufge 164.132.48.230:600 | France | FFA | http://c0nsume.me/private4.php?ip=164.132.48.230:600 149.202.87.51:443 | Paris | FFA | http://agarlist.com/ 134.119.17.230:443 | Germany | FFA | http://agarlist.com/ From d82eb188096b4e930a90a79f6257d4243f7d7dd9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 10 Jul 2016 07:46:57 +0200 Subject: [PATCH 370/417] update quad-node version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 44735babf..047426884 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "license": "Apache License, Version 2.0", "main": "src/index.js", "dependencies": { - "quad-node": "^1.0.3", + "quad-node": "^1.0.5", "vector2-node": "latest", "ws": "latest" }, From a2f439b21afedd0bb63e414064b76c817e63a85c Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 10 Jul 2016 10:21:04 +0200 Subject: [PATCH 371/417] fix socket leak bug --- README.md | 2 +- package.json | 2 +- src/HttpsServer.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 30e5bfa2f..5b936ad09 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.32** +Current version: **1.2.33** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 06e439e2c..91e52f4f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.32", + "version": "1.2.33", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/HttpsServer.js b/src/HttpsServer.js index 8caf6f522..e53e3c14c 100644 --- a/src/HttpsServer.js +++ b/src/HttpsServer.js @@ -33,7 +33,7 @@ function Server(tlsconfig, requestListener) { // copy from http.Server this.timeout = 2 * 60 * 1000; - this.allowHalfOpen = true; + this.allowHalfOpen = false; this.httpAllowHalfOpen = false; } else http.Server.call(this, requestListener); From 57e1e17fefd51d2d4a68be2b3a5390e76d833970 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 10 Jul 2016 13:03:10 +0200 Subject: [PATCH 372/417] fix instant merge; boost move code refactoring --- README.md | 2 +- package.json | 2 +- src/entity/Cell.js | 11 ++++++++--- src/entity/PlayerCell.js | 6 +++++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5b936ad09..4127755f3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.33** +Current version: **1.2.34** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 91e52f4f5..8e18c0e34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.33", + "version": "1.2.34", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 997c41433..3cb6c872e 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -17,6 +17,7 @@ function Cell(gameServer, owner, position, size) { this.boostDistance = 0; this.boostDirection = { x: 1, y: 0, angle: Math.PI / 2 }; + this.boostMaxSpeed = 78; // boost speed limit, sqrt(780*780/100) this.ejector = null; if (this.gameServer != null) { @@ -146,10 +147,14 @@ Cell.prototype.onRemove = function (gameServer) { // Functions -Cell.prototype.setBoost = function (distance, angle) { - if (isNaN(angle)) angle = 0; +// Note: maxSpeed > 78 may leads to bug when cell can fly +// through other cell due to high speed +Cell.prototype.setBoost = function (distance, angle, maxSpeed) { + if (isNaN(angle)) angle = Math.PI / 2; + if (!maxSpeed) maxSpeed = 78; this.boostDistance = distance; + this.boostMaxSpeed = maxSpeed; this.setAngle(angle); this.isMoving = true; if (!this.owner) { @@ -166,7 +171,7 @@ Cell.prototype.move = function (border) { return; } var speed = Math.sqrt(this.boostDistance * this.boostDistance / 100); - var speed = Math.min(speed, 78); // limit max speed with sqrt(780*780/100) + var speed = Math.min(speed, this.boostMaxSpeed);// limit max speed with sqrt(780*780/100) speed = Math.min(speed, this.boostDistance); // avoid overlap 0 this.boostDistance -= speed; if (this.boostDistance < 1) this.boostDistance = 0; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 7bbc7ceac..3004d4e48 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -22,7 +22,11 @@ PlayerCell.prototype.updateRemerge = function () { var baseTtr = this.gameServer.config.playerRecombineTime; // default baseTtr = 30 if (baseTtr == 0) { // instant merge - this._canRemerge = true; + if (this.getSize() >= 780/2) { + this._canRemerge = age > 20; + return; + } + this._canRemerge = this.boostDistance < 100; return; } var ttr = Math.max(baseTtr, (this.getSize() * 0.2) >> 0); // ttr in seconds From e8fd49d45b7c0e0a5ce1a7334c9bbfa69527e8ec Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 10 Jul 2016 15:08:29 +0200 Subject: [PATCH 373/417] update readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4127755f3..932d7cb9d 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,8 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:443 | 5 | vanilla style IP | Location | Game Mode | Web Site --- | --- | --- | --- -146.185.167.9:443 | Netherlands | FFA | Test server (report issues here) http://agar.io/?ip=bubble-wars.tk:443 +bubble-wars.tk:443 | Netherlands | FFA | http://agar.io/?ip=bubble-wars.tk:443 (Test server) +bubble-wars.tk:444 | Netherlands | FFA IM | http://agar.io/?ip=bubble-wars.tk:444 (Test server) vps.simonorj.com:24270 | Montreal | Instant Merge | https://redd.it/4mufge 164.132.48.230:600 | France | FFA | http://c0nsume.me/private4.php?ip=164.132.48.230:600 149.202.87.51:443 | Paris | FFA | http://agarlist.com/ From 418e5a2cb21a68fd3657bf2020a18256b2ab4417 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 12 Jul 2016 13:20:22 +0200 Subject: [PATCH 374/417] fix server crash with ECONNRESET and other connection errors --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 5 ++++- src/HttpsServer.js | 7 +++++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 932d7cb9d..4e2f4b7c9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.34** +Current version: **1.2.35** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 8e18c0e34..dab79f294 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.34", + "version": "1.2.35", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 577a3c4bb..c5e390b15 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -210,6 +210,10 @@ GameServer.prototype.onServerSocketError = function (error) { }; GameServer.prototype.onClientSocketOpen = function (ws) { + var logip = ws._socket.remoteAddress + ":" + ws._socket.remotePort; + ws.on('error', function (err) { + Logger.writeError("[" + logip + "] " + err.stack); + }); if (this.config.serverMaxConnections > 0 && this.socketCount >= this.config.serverMaxConnections) { ws.close(1000, "No slots"); return; @@ -281,7 +285,6 @@ GameServer.prototype.onClientSocketClose = function (ws, code) { GameServer.prototype.onClientSocketError = function (ws, error) { ws.sendPacket = function (data) { }; - ws.close(1002, "Socket error"); }; GameServer.prototype.onClientSocketMessage = function (ws, message) { diff --git a/src/HttpsServer.js b/src/HttpsServer.js index e53e3c14c..a0a6f7415 100644 --- a/src/HttpsServer.js +++ b/src/HttpsServer.js @@ -2,6 +2,7 @@ https = require('https'), inherits = require('util').inherits, httpSocketHandler = http._connectionListener; +var Logger = require('./modules/Logger'); var isOldNode = /^v0\.10\./.test(process.version); @@ -51,6 +52,9 @@ Server.prototype.__httpSocketHandler = httpSocketHandler; var connectionListener; if (isOldNode) { connectionListener = function (socket) { + socket.on('error', function (err) { + Logger.writeError("[" + socket._peername.address + ":" + socket._peername.port + "] " + err.stack); + }); var self = this; socket.ondata = function (d, start, end) { var firstByte = d[start]; @@ -67,6 +71,9 @@ if (isOldNode) { }; } else { connectionListener = function (socket) { + socket.on('error', function (err) { + Logger.writeError("[" + socket._peername.address + ":" + socket._peername.port + "] " + err.stack); + }); var self = this; var data = socket.read(1); if (data === null) { From 703a53c2b1601e089ba554e58bbc190497e5fac4 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 12 Jul 2016 15:32:09 +0200 Subject: [PATCH 375/417] fix cannot read property 'address' --- README.md | 2 +- package.json | 2 +- src/HttpsServer.js | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4e2f4b7c9..828501fbb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.35** +Current version: **1.2.36** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index dab79f294..4e9d918ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.35", + "version": "1.2.36", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/HttpsServer.js b/src/HttpsServer.js index a0a6f7415..ba1fa56a9 100644 --- a/src/HttpsServer.js +++ b/src/HttpsServer.js @@ -52,8 +52,9 @@ Server.prototype.__httpSocketHandler = httpSocketHandler; var connectionListener; if (isOldNode) { connectionListener = function (socket) { + var logip = socket._peername ? socket._peername.address + ":" + socket._peername.port : ""; socket.on('error', function (err) { - Logger.writeError("[" + socket._peername.address + ":" + socket._peername.port + "] " + err.stack); + Logger.writeError("[" + logip + "] " + err.stack); }); var self = this; socket.ondata = function (d, start, end) { @@ -71,8 +72,9 @@ if (isOldNode) { }; } else { connectionListener = function (socket) { + var logip = socket._peername ? socket._peername.address + ":" + socket._peername.port : ""; socket.on('error', function (err) { - Logger.writeError("[" + socket._peername.address + ":" + socket._peername.port + "] " + err.stack); + Logger.writeError("[" + logip + "] " + err.stack); }); var self = this; var data = socket.read(1); From cf6c6c11e91015b350b7e28cb33ed3dc488aac11 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 12 Jul 2016 15:52:42 +0200 Subject: [PATCH 376/417] fix remote address logging --- README.md | 2 +- package.json | 2 +- src/HttpsServer.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 828501fbb..5383c4181 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.36** +Current version: **1.2.37** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 4e9d918ef..51e8bdad2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.36", + "version": "1.2.37", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/HttpsServer.js b/src/HttpsServer.js index ba1fa56a9..3248af2a5 100644 --- a/src/HttpsServer.js +++ b/src/HttpsServer.js @@ -52,7 +52,7 @@ Server.prototype.__httpSocketHandler = httpSocketHandler; var connectionListener; if (isOldNode) { connectionListener = function (socket) { - var logip = socket._peername ? socket._peername.address + ":" + socket._peername.port : ""; + var logip = socket.remoteAddress + ":" + socket.remotePort; socket.on('error', function (err) { Logger.writeError("[" + logip + "] " + err.stack); }); @@ -72,7 +72,7 @@ if (isOldNode) { }; } else { connectionListener = function (socket) { - var logip = socket._peername ? socket._peername.address + ":" + socket._peername.port : ""; + var logip = socket.remoteAddress + ":" + socket.remotePort; socket.on('error', function (err) { Logger.writeError("[" + logip + "] " + err.stack); }); From acbb70c5d3e6aed9d7ab7cdcbcfd0f4549e3c333 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 12 Jul 2016 16:56:17 +0200 Subject: [PATCH 377/417] add anti-minion protection --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 46 ++++++++++++++++++++++++++++++++++++++------ src/PacketHandler.js | 4 ++++ src/PlayerTracker.js | 17 +++++++++++----- src/gamemodes/FFA.js | 2 +- src/gameserver.ini | 4 ++++ 7 files changed, 63 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5383c4181..640b477ac 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.37** +Current version: **1.2.38** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 51e8bdad2..124c480d2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.37", + "version": "1.2.38", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index c5e390b15..783c50880 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -65,6 +65,8 @@ function GameServer() { serverTimeout: 300, // Seconds to keep connection alive for non-responding client serverMaxConnections: 64, // Maximum number of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum number of connections from the same IP (0 for no limit) + serverMinionThreshold: 10, // max connections within serverMinionInterval time period, which will not be marked as minion + serverMinionInterval: 1000, // minion detection interval in milliseconds serverPort: 443, // Server port serverBind: '0.0.0.0', // Network interface binding serverTracker: 0, // Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master @@ -123,6 +125,7 @@ function GameServer() { }; this.ipBanList = []; + this.minionTest = []; this.userList = []; // Parse config @@ -260,6 +263,22 @@ GameServer.prototype.onClientSocketOpen = function (ws) { ws.on('close', onClose); this.socketCount++; this.clients.push(ws); + + // Minion detection + if (this.config.serverMinionThreshold) { + if (this.minionTest.length >= this.config.serverMinionThreshold) { + ws.playerTracker.isMinion = true; + for (var i = 0; i < this.minionTest.length; i++) { + var playerTracker = this.minionTest[i]; + if (!playerTracker.socket.isConnected) continue; + playerTracker.isMinion = true; + } + if (this.minionTest.length) { + this.minionTest.splice(0, 1); + } + } + this.minionTest.push(ws.playerTracker); + } }; GameServer.prototype.onClientSocketClose = function (ws, code) { @@ -462,6 +481,16 @@ GameServer.prototype.removeNode = function(node) { }; GameServer.prototype.updateClients = function () { + // check minions + var dateTime = new Date; + for (var i = 0; i < this.minionTest.length; ) { + var playerTracker = this.minionTest[i]; + if (dateTime - playerTracker.connectedTime > this.config.serverMinionInterval) { + this.minionTest.splice(i, 1); + } else { + i++; + } + } // check dead clients for (var i = 0; i < this.clients.length; ) { var playerTracker = this.clients[i].playerTracker; @@ -842,6 +871,7 @@ GameServer.prototype.resolveCollision = function (manifold) { // maxCell don't want to eat return; } + // Now maxCell can eat minCell minCell.isRemoved = true; @@ -852,12 +882,16 @@ GameServer.prototype.resolveCollision = function (manifold) { minCell.owner.mergeOverride = false; } - // Consume effect - maxCell.onEat(minCell); - minCell.onEaten(maxCell); - - // update bounds - this.updateNodeQuad(maxCell); + var isMinion = (maxCell.owner && maxCell.owner.isMinion) || + (minCell.owner && minCell.owner.isMinion); + if (!isMinion) { + // Consume effect + maxCell.onEat(minCell); + minCell.onEaten(maxCell); + + // update bounds + this.updateNodeQuad(maxCell); + } // Remove cell minCell.setKiller(maxCell); diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 4ae02677c..684e5c298 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -62,6 +62,10 @@ PacketHandler.prototype.handleMessage = function(message) { } this.socket.lastAliveTime = +new Date; + if (this.socket.playerTracker.isMinion && this.socket.playerTracker.spawnCounter >= 2) { + return; + } + var reader = new BinaryReader(message); var packetId = reader.readUInt8(); diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 5a26822b8..4d3474d45 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -57,6 +57,10 @@ function PlayerTracker(gameServer, socket) { this.scrambleX = 0; this.scrambleY = 0; this.scrambleId = 0; + + this.connectedTime = new Date; + this.isMinion = false; + this.spawnCounter = 0; // Gamemode function if (gameServer) { @@ -236,6 +240,7 @@ PlayerTracker.prototype.joinGame = function (name, skin) { }; this.socket.sendPacket(new Packet.SetBorder(this, border)); } + this.spawnCounter++; this.gameServer.gameMode.onPlayerSpawn(this.gameServer, this); }; @@ -545,11 +550,13 @@ PlayerTracker.prototype.getSpectateTarget = function () { PlayerTracker.prototype.updateVisibleNodes = function() { this.viewNodes = []; - var self = this; - this.gameServer.quadTree.find(this.viewBox, function (quadItem) { - if (quadItem.cell.owner != self) - self.viewNodes.push(quadItem.cell); - }); + if (!this.isMinion) { + var self = this; + this.gameServer.quadTree.find(this.viewBox, function (quadItem) { + if (quadItem.cell.owner != self) + self.viewNodes.push(quadItem.cell); + }); + } this.viewNodes = this.viewNodes.concat(this.cells); this.viewNodes.sort(function (a, b) { return a.nodeId - b.nodeId; }); }; diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index ca6795e97..96f24a75e 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -34,7 +34,7 @@ FFA.prototype.leaderboardAddSort = function(player, leaderboard) { // Override FFA.prototype.onPlayerSpawn = function(gameServer, player) { - player.setColor(gameServer.getRandomColor()); + player.setColor(player.isMinion ? {r:240, g:240, b:255} : gameServer.getRandomColor()); // Spawn player gameServer.spawnPlayer(player); }; diff --git a/src/gameserver.ini b/src/gameserver.ini index 4716c8d00..8de6011d0 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -23,6 +23,8 @@ logFileVerbosity = 5 # [Server] # serverTimeout: Seconds to keep connection alive for non-responding client # serverIpLimit: Controls the maximum connections from single IP (use 0 to disable) +# serverMinionThreshold: max connections within serverMinionInterval time period, which will not be marked as minion +# serverMinionInterval: minion detection interval in milliseconds # serverPort: Server port which will be used to listen for incoming connections # serverBind: Server network interface which will be used to listen for incoming connections (0.0.0.0 for all IPv4 interfaces) # serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) @@ -41,6 +43,8 @@ logFileVerbosity = 5 serverTimeout = 300 serverMaxConnections = 128 serverIpLimit = 4 +serverMinionThreshold = 10 +serverMinionInterval = 1000 serverPort = 443 serverBind = "0.0.0.0" serverTracker = 0 From 5986d7eb7ec69cdadacecf28f3a019416bf73e47 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 13 Jul 2016 08:07:15 +0200 Subject: [PATCH 378/417] add serverMinionIgnoreTime to disable minion detection on server startup --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 25 ++++++++++++++----------- src/gameserver.ini | 4 +++- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 640b477ac..64fc25117 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.38** +Current version: **1.2.39** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 124c480d2..6fca61d01 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.38", + "version": "1.2.39", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 783c50880..5c69c6b63 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -65,8 +65,9 @@ function GameServer() { serverTimeout: 300, // Seconds to keep connection alive for non-responding client serverMaxConnections: 64, // Maximum number of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum number of connections from the same IP (0 for no limit) + serverMinionIgnoreTime: 30, // minion detection disable time on server startup [seconds] serverMinionThreshold: 10, // max connections within serverMinionInterval time period, which will not be marked as minion - serverMinionInterval: 1000, // minion detection interval in milliseconds + serverMinionInterval: 1000, // minion detection interval [milliseconds] serverPort: 443, // Server port serverBind: '0.0.0.0', // Network interface binding serverTracker: 0, // Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master @@ -266,18 +267,20 @@ GameServer.prototype.onClientSocketOpen = function (ws) { // Minion detection if (this.config.serverMinionThreshold) { - if (this.minionTest.length >= this.config.serverMinionThreshold) { - ws.playerTracker.isMinion = true; - for (var i = 0; i < this.minionTest.length; i++) { - var playerTracker = this.minionTest[i]; - if (!playerTracker.socket.isConnected) continue; - playerTracker.isMinion = true; - } - if (this.minionTest.length) { - this.minionTest.splice(0, 1); + if ((ws.lastAliveTime - this.startTime)/1000 >= this.config.serverMinionIgnoreTime) { + if (this.minionTest.length >= this.config.serverMinionThreshold) { + ws.playerTracker.isMinion = true; + for (var i = 0; i < this.minionTest.length; i++) { + var playerTracker = this.minionTest[i]; + if (!playerTracker.socket.isConnected) continue; + playerTracker.isMinion = true; + } + if (this.minionTest.length) { + this.minionTest.splice(0, 1); + } } + this.minionTest.push(ws.playerTracker); } - this.minionTest.push(ws.playerTracker); } }; diff --git a/src/gameserver.ini b/src/gameserver.ini index 8de6011d0..a2d04247f 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -23,8 +23,9 @@ logFileVerbosity = 5 # [Server] # serverTimeout: Seconds to keep connection alive for non-responding client # serverIpLimit: Controls the maximum connections from single IP (use 0 to disable) +# serverMinionIgnoreTime: minion detection disable time on server startup [seconds] # serverMinionThreshold: max connections within serverMinionInterval time period, which will not be marked as minion -# serverMinionInterval: minion detection interval in milliseconds +# serverMinionInterval: minion detection interval [milliseconds] # serverPort: Server port which will be used to listen for incoming connections # serverBind: Server network interface which will be used to listen for incoming connections (0.0.0.0 for all IPv4 interfaces) # serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) @@ -43,6 +44,7 @@ logFileVerbosity = 5 serverTimeout = 300 serverMaxConnections = 128 serverIpLimit = 4 +serverMinionIgnoreTime = 30 serverMinionThreshold = 10 serverMinionInterval = 1000 serverPort = 443 From ba0e7d545f91b3948ba394c080e459b2a241849a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 14 Jul 2016 07:15:17 +0200 Subject: [PATCH 379/417] fix MotherCell size bug --- README.md | 2 +- package.json | 2 +- src/entity/MotherCell.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 64fc25117..2b079b589 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.39** +Current version: **1.2.40** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 6fca61d01..bdc9fb401 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.39", + "version": "1.2.40", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/entity/MotherCell.js b/src/entity/MotherCell.js index 409d1ba76..1929b5e12 100644 --- a/src/entity/MotherCell.js +++ b/src/entity/MotherCell.js @@ -11,7 +11,7 @@ function MotherCell() { this.setColor({ r: 205, g: 85, b: 100 }); this.motherCellMinSize = 149; // vanilla 149 (mass = 149*149/100 = 222.01) this.motherCellSpawnAmount = 2; - if (this.getSize() == 0) { + if (!this.getSize()) { this.setSize(this.motherCellMinSize); } } From 4c89c1ad7df03436cdfdcbe01132264a8c08aa3b Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 14 Jul 2016 11:53:49 +0200 Subject: [PATCH 380/417] add censorship for chat and player name; logging for chat messages --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 48 ++++++++++++++++++++++++++++++++++++++++++-- src/PacketHandler.js | 4 ++++ src/badwords.txt | 25 +++++++++++++++++++++++ 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/badwords.txt diff --git a/README.md b/README.md index 2b079b589..461b5a9cd 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.40** +Current version: **1.2.41** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index bdc9fb401..435066cb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.40", + "version": "1.2.41", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 5c69c6b63..f7b566f67 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -128,11 +128,13 @@ function GameServer() { this.ipBanList = []; this.minionTest = []; this.userList = []; + this.badWords = []; // Parse config this.loadConfig(); this.loadIpBanList(); this.loadUserList(); + this.loadBadWords(); this.setBorder(this.config.borderWidth, this.config.borderHeight); this.quadTree = new QuadNode(this.border, 16, 100); @@ -551,8 +553,20 @@ GameServer.prototype.onChatMessage = function (from, to, message) { // chat is disabled return; } - if (message.length > 128) message = message.slice(0, 128); - //Logger.debug("[CHAT] " + (from!=null && from.getName().length>0 ? from.getName() : "Spectator") + ": " + message); + if (message.length > 64) { + message = message.slice(0, 64); + } + if (this.checkBadWord(message)) { + if (from) { + this.sendChatMessage(null, from, "Stop insulting others! Keep calm and be friendly please"); + } + return; + } + if (from) { + Logger.writeDebug("[CHAT] [" + from.getFriendlyName() + "]: " + message); + } else { + Logger.writeDebug("[CHAT] []:" + message); + } this.sendChatMessage(from, to, message); }; @@ -1353,6 +1367,7 @@ GameServer.prototype.updateMassDecay = function() { }; var fileNameConfig = './gameserver.ini'; +var fileNameBadWords = './badwords.txt'; var fileNameIpBan = './ipbanlist.txt'; var fileNameUsers = './userRoles.json'; @@ -1385,6 +1400,35 @@ GameServer.prototype.loadConfig = function () { Logger.setFileVerbosity(this.config.logFileVerbosity); }; +GameServer.prototype.loadBadWords = function () { + try { + if (!fs.existsSync(fileNameBadWords)) { + Logger.warn(fileNameBadWords + " not found"); + } else { + var words = fs.readFileSync(fileNameBadWords, 'utf-8'); + words = words.split(/[\r\n]+/); + words = words.map(function (arg) { return arg.trim().toLowerCase(); }); + words = words.filter(function (arg) { return !!arg; }); + this.badWords = words; + Logger.info(this.badWords.length + " bad words loaded"); + } + } catch (err) { + Logger.error(err.stack); + Logger.error("Failed to load " + fileNameBadWords + ": " + err.message); + } +}; + +GameServer.prototype.checkBadWord = function (value) { + value = value.toLowerCase().trim(); + if (!value) return false; + for (var i = 0; i < this.badWords.length; i++) { + if (value.indexOf(this.badWords[i]) >= 0) { + return true; + } + } + return false; +}; + GameServer.prototype.changeConfig = function (name, value) { if (value == null || isNaN(value)) { Logger.warn("Invalid value: " + value); diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 684e5c298..2571cf8ea 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -181,5 +181,9 @@ PacketHandler.prototype.setNickname = function (text) { if (name.length > this.gameServer.config.playerMaxNickLength) { name = name.substring(0, this.gameServer.config.playerMaxNickLength); } + if (this.gameServer.checkBadWord(name)) { + skin = null; + name = "Hi there!"; + } this.socket.playerTracker.joinGame(name, skin); }; diff --git a/src/badwords.txt b/src/badwords.txt new file mode 100644 index 000000000..56fb10212 --- /dev/null +++ b/src/badwords.txt @@ -0,0 +1,25 @@ +bitch +slut +cunt +dick +fuck +stupid +asshole +nigger +skank +whore +chink +fatass +shemale +gook +kraut +lesbo +lardass +biatch +douchebag +faggot +wetback +wop +shit +idiot +pedo From 43ce8726e40369368ad7e21e2f07b22ddf2e3f59 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 14 Jul 2016 12:06:23 +0200 Subject: [PATCH 381/417] log ip and name for chat --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 461b5a9cd..b02d479e3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.41** +Current version: **1.2.42** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 435066cb7..39848ae10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.41", + "version": "1.2.42", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index f7b566f67..4e870beeb 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -563,9 +563,9 @@ GameServer.prototype.onChatMessage = function (from, to, message) { return; } if (from) { - Logger.writeDebug("[CHAT] [" + from.getFriendlyName() + "]: " + message); + Logger.writeDebug("[CHAT][" + from.socket.remoteAddress + ":" + from.socket.remotePort + "][" + from.getFriendlyName() + "] " + message); } else { - Logger.writeDebug("[CHAT] []:" + message); + Logger.writeDebug("[CHAT][][]: " + message); } this.sendChatMessage(from, to, message); }; From ffd5f07cc9d777077f68a6bb5f67d8ce95886ff4 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Thu, 14 Jul 2016 23:46:19 +0200 Subject: [PATCH 382/417] Disable permessage-deflate, limit payloads to 1kb * Permessage-deflate is very CPU consuming and shouldn't really be used in a game server. Disabling it will increase throughput a lot. * Limiting message payload to 1kb should be enough for all game messages, and better limit sabotage. --- src/GameServer.js | 167 +++++++++++++++++++++++----------------------- 1 file changed, 84 insertions(+), 83 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 4e870beeb..8463dcae0 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -81,7 +81,7 @@ function GameServer() { serverScrambleLevel: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap); 3 - high scrambling (no border) serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. - serverName: 'MultiOgar #1', // Server name + serverName: 'MultiOgar #1', // Server name serverWelcome1: 'Welcome to MultiOgar server!', // First server welcome message serverWelcome2: '', // Second server welcome message (for info, etc) @@ -102,7 +102,7 @@ function GameServer() { virusMaxAmount: 100, // Maximum number of viruses on the map. If this number is reached, then ejected cells will pass through viruses. ejectSize: 38, // Size of ejected cells (vanilla 38) - ejectSizeLoss: 43, // Eject size which will be substracted from player cell (vanilla 43?) + ejectSizeLoss: 43, // Eject size which will be substracted from player cell (vanilla 43?) ejectCooldown: 3, // min ticks between ejects ejectSpawnPlayer: 1, // if 1 then player may be spawned from ejected mass @@ -132,13 +132,13 @@ function GameServer() { // Parse config this.loadConfig(); - this.loadIpBanList(); - this.loadUserList(); - this.loadBadWords(); - + this.loadIpBanList(); + this.loadUserList(); + this.loadBadWords(); + this.setBorder(this.config.borderWidth, this.config.borderHeight); this.quadTree = new QuadNode(this.border, 16, 100); - + // Gamemodes this.gameMode = Gamemode.get(this.config.serverGamemode); } @@ -171,7 +171,8 @@ GameServer.prototype.start = function() { } var wsOptions = { server: this.httpServer, - perMessageDeflate: true + perMessageDeflate: false, + maxPayload: 1024 }; this.wsServer = new WebSocket.Server(wsOptions); this.wsServer.on('error', this.onServerSocketError.bind(this)); @@ -678,11 +679,11 @@ GameServer.prototype.updateVirus = function () { GameServer.prototype.spawnFood = function() { var cell = new Entity.Food(this, null, this.getRandomPosition(), this.config.foodMinSize); if (this.config.foodMassGrow) { - var size = cell.getSize(); - var maxGrow = this.config.foodMaxSize - size; - size += maxGrow * Math.random(); - cell.setSize(size); - } + var size = cell.getSize(); + var maxGrow = this.config.foodMaxSize - size; + size += maxGrow * Math.random(); + cell.setSize(size); + } cell.setColor(this.getRandomColor()); this.addNode(cell); }; @@ -1098,82 +1099,82 @@ GameServer.prototype.splitMass = function (mass, count) { if (splitMass < minMass) { return [mass]; } - var masses = []; + var masses = []; if (maxCount < 3 || maxCount < count || curMass / throwMass <= 30) { - // Monotone blow up + // Monotone blow up for (var i = 0; i < maxCount; i++) { - masses.push(splitMass); + masses.push(splitMass); } } else { // Diverse blow up // Barbosik: draft version var restCount = maxCount; while (restCount > 2) { - var splitMass = curMass / 2; + var splitMass = curMass / 2; if (splitMass <= throwMass) { - break; - } - var max = curMass - throwMass * (restCount - 1); + break; + } + var max = curMass - throwMass * (restCount - 1); if (max <= throwMass || splitMass >= max) { break; } masses.push(splitMass); curMass -= splitMass; restCount--; - } + } var splitMass = curMass / 4; if (splitMass > throwMass) { while (restCount > 2) { - var max = curMass - throwMass * (restCount - 1); + var max = curMass - throwMass * (restCount - 1); if (max <= throwMass || splitMass >= max) { break; } masses.push(splitMass); curMass -= splitMass; restCount--; - } - } + } + } var splitMass = curMass / 8; if (splitMass > throwMass) { while (restCount > 2) { - var max = curMass - throwMass * (restCount - 1); + var max = curMass - throwMass * (restCount - 1); if (max <= throwMass || splitMass >= max) { break; } masses.push(splitMass); curMass -= splitMass; restCount--; - } - } + } + } if (restCount > 1) { - splitMass = curMass - throwMass * (restCount - 1); + splitMass = curMass - throwMass * (restCount - 1); if (splitMass > throwMass) { - masses.push(splitMass); + masses.push(splitMass); curMass -= splitMass; restCount--; } - } + } if (restCount > 0) { - splitMass = curMass / restCount; + splitMass = curMass / restCount; if (splitMass < throwMass-0.001) { - Logger.warn("GameServer.splitMass: throwMass-splitMass = "+(throwMass-splitMass).toFixed(3)+" (" + mass.toFixed(4) + ", " + count + ")"); + Logger.warn("GameServer.splitMass: throwMass-splitMass = "+(throwMass-splitMass).toFixed(3)+" (" + mass.toFixed(4) + ", " + count + ")"); } while (restCount > 0) { - masses.push(splitMass); - restCount--; + masses.push(splitMass); + restCount--; } - } + } } - //Logger.debug("===GameServer.splitMass==="); - //Logger.debug("mass = " + mass.toFixed(3) + " | " + Math.sqrt(mass * 100).toFixed(3)); - //var sum = 0; + //Logger.debug("===GameServer.splitMass==="); + //Logger.debug("mass = " + mass.toFixed(3) + " | " + Math.sqrt(mass * 100).toFixed(3)); + //var sum = 0; //for (var i = 0; i < masses.length; i++) { - // Logger.debug("mass[" + i + "] = " + masses[i].toFixed(3) + " | " + Math.sqrt(masses[i] * 100).toFixed(3)); - // sum += masses[i] - //} - //Logger.debug("sum = " + sum.toFixed(3) + " | " + Math.sqrt(sum * 100).toFixed(3)); + // Logger.debug("mass[" + i + "] = " + masses[i].toFixed(3) + " | " + Math.sqrt(masses[i] * 100).toFixed(3)); + // sum += masses[i] + //} + //Logger.debug("sum = " + sum.toFixed(3) + " | " + Math.sqrt(sum * 100).toFixed(3)); return masses; -}; +}; GameServer.prototype.splitCells = function(client) { // it seems that vanilla uses order by cell age @@ -1351,7 +1352,7 @@ GameServer.prototype.updateMassDecay = function() { var decay = 1 - this.config.playerDecayRate * this.gameMode.decayMod; // Loop through all player cells for (var i = 0; i < this.clients.length; i++) { - var playerTracker = this.clients[i].playerTracker; + var playerTracker = this.clients[i].playerTracker; for (var j = 0; j < playerTracker.cells.length; j++) { var cell = playerTracker.cells[j]; var size = cell.getSize(); @@ -1431,21 +1432,21 @@ GameServer.prototype.checkBadWord = function (value) { GameServer.prototype.changeConfig = function (name, value) { if (value == null || isNaN(value)) { - Logger.warn("Invalid value: " + value); - return; - } + Logger.warn("Invalid value: " + value); + return; + } if (!this.config.hasOwnProperty(name)) { - Logger.warn("Unknown config value: " + name); - return; - } - this.config[name] = value; + Logger.warn("Unknown config value: " + name); + return; + } + this.config[name] = value; // update/validate this.config.playerMinSize = Math.max(32, this.config.playerMinSize); Logger.setVerbosity(this.config.logVerbosity); Logger.setFileVerbosity(this.config.logFileVerbosity); - Logger.print("Set " + name + " = " + this.config[name]); + Logger.print("Set " + name + " = " + this.config[name]); }; GameServer.prototype.loadUserList = function () { @@ -1526,17 +1527,17 @@ GameServer.prototype.loadIpBanList = function () { GameServer.prototype.saveIpBanList = function () { try { - var blFile = fs.createWriteStream(fileNameIpBan); - // Sort the blacklist and write. + var blFile = fs.createWriteStream(fileNameIpBan); + // Sort the blacklist and write. this.ipBanList.sort().forEach(function (v) { - blFile.write(v + '\n'); - }); - blFile.end(); + blFile.write(v + '\n'); + }); + blFile.end(); Logger.info(this.ipBanList.length + " IP ban records saved."); } catch (err) { Logger.error(err.stack); Logger.error("Failed to save " + fileNameIpBan + ": " + err.message); - } + } }; GameServer.prototype.checkIpBan = function (ipAddress) { @@ -1565,18 +1566,18 @@ GameServer.prototype.checkIpBan = function (ipAddress) { GameServer.prototype.banIp = function (ip) { var ipBin = ip.split('.'); if (ipBin.length != 4) { - Logger.warn("Invalid IP format: " + ip); - return; + Logger.warn("Invalid IP format: " + ip); + return; } if (ipBin[0] == "127") { - Logger.warn("Cannot ban localhost"); - return; + Logger.warn("Cannot ban localhost"); + return; } if (this.ipBanList.indexOf(ip) >= 0) { - Logger.warn(ip + " is already in the ban list!"); - return; - } - this.ipBanList.push(ip); + Logger.warn(ip + " is already in the ban list!"); + return; + } + this.ipBanList.push(ip); if (ipBin[2]=="*" || ipBin[3] == "*") { Logger.print("The IP sub-net " + ip + " has been banned"); } else { @@ -1589,13 +1590,13 @@ GameServer.prototype.banIp = function (ip) { // remove player cells socket.playerTracker.cells.forEach(function (cell) { - this.removeNode(cell); + this.removeNode(cell); }, this); // disconnect - socket.close(1000, "Banned from server"); - var name = socket.playerTracker.getFriendlyName(); - Logger.print("Banned: \"" + name + "\" with Player ID " + socket.playerTracker.pID); + socket.close(1000, "Banned from server"); + var name = socket.playerTracker.getFriendlyName(); + Logger.print("Banned: \"" + name + "\" with Player ID " + socket.playerTracker.pID); this.sendChatMessage(null, null, "Banned \"" + name + "\""); // notify to don't confuse with server bug }, this); this.saveIpBanList(); @@ -1604,11 +1605,11 @@ GameServer.prototype.banIp = function (ip) { GameServer.prototype.unbanIp = function (ip) { var index = this.ipBanList.indexOf(ip); if (index < 0) { - Logger.warn("IP " + ip + " is not in the ban list!"); - return; + Logger.warn("IP " + ip + " is not in the ban list!"); + return; } - this.ipBanList.splice(index, 1); - Logger.print("Unbanned IP: " + ip); + this.ipBanList.splice(index, 1); + Logger.print("Unbanned IP: " + ip); this.saveIpBanList(); }; @@ -1622,21 +1623,21 @@ GameServer.prototype.kickId = function (id) { return; // remove player cells socket.playerTracker.cells.forEach(function (cell) { - this.removeNode(cell); + this.removeNode(cell); }, this); // disconnect - socket.close(1000, "Kicked from server"); - var name = socket.playerTracker.getFriendlyName(); - Logger.print("Kicked \"" + name + "\""); + socket.close(1000, "Kicked from server"); + var name = socket.playerTracker.getFriendlyName(); + Logger.print("Kicked \"" + name + "\""); this.sendChatMessage(null, null, "Kicked \"" + name + "\""); // notify to don't confuse with server bug - count++; + count++; }, this); if (count > 0) - return; + return; if (id == 0) - Logger.warn("No players to kick!"); - else - Logger.warn("Player with ID "+id+" not found!"); + Logger.warn("No players to kick!"); + else + Logger.warn("Player with ID "+id+" not found!"); }; // Stats server From 89c4d168c3b84e3510f76f1e3922ea2e49a3bc74 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 15 Jul 2016 08:11:06 +0200 Subject: [PATCH 383/417] fix inconsistent code formatting (use CR-LF line ending; 4x space tab) --- CONTRIBUTING.md | 2 +- README.md | 4 +- package.json | 48 +- src/GameServer.js | 140 ++-- src/PacketHandler.js | 10 +- src/PlayerTracker.js | 58 +- src/ai/BotLoader.js | 100 +-- src/ai/BotPlayer.js | 40 +- src/ai/FakeSocket.js | 6 +- src/ai/Readme.txt | 2 +- src/entity/Cell.js | 26 +- src/entity/EjectedMass.js | 46 +- src/entity/Food.js | 6 +- src/entity/MotherCell.js | 2 +- src/entity/PlayerCell.js | 8 +- src/entity/Virus.js | 106 +-- src/entity/index.js | 2 +- src/gamemodes/Experimental.js | 20 +- src/gamemodes/FFA.js | 18 +- src/gamemodes/HungerGames.js | 40 +- src/gamemodes/Mode.js | 104 +-- src/gamemodes/Rainbow.js | 22 +- src/gamemodes/TeamX.js | 88 +-- src/gamemodes/TeamZ.js | 354 ++++----- src/gamemodes/Teams.js | 62 +- src/gamemodes/Tournament.js | 494 ++++++------- src/gamemodes/Zombie.js | 36 +- src/gamemodes/index.js | 22 +- src/index.js | 170 ++--- src/modules/CommandList.js | 1228 +++++++++++++++---------------- src/modules/Logger.js | 246 +++---- src/modules/PlayerCommand.js | 176 ++--- src/modules/ini.js | 450 +++++------ src/packet/AddNode.js | 4 +- src/packet/BinaryReader.js | 4 +- src/packet/ChatMessage.js | 4 +- src/packet/ClearAll.js | 2 +- src/packet/DrawLine.js | 4 +- src/packet/SetBorder.js | 2 +- src/packet/UpdateLeaderboard.js | 6 +- src/packet/UpdateNodes.js | 10 +- src/packet/UpdatePosition.js | 4 +- src/packet/index.js | 2 +- src/userRoles.json | 38 +- 44 files changed, 2108 insertions(+), 2108 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index de613bdbc..64d6d48b7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,7 +16,7 @@ Contributions are appreciated in the form of pull requests. However, to maintain ... } ``` -* Unix-style line endings should be used (`\n`). +* CR-LF line endings should be used (`\r\n`). * Please leave a blank line at the end of each file. * Conditional/loop statements (`if`, `for`, `while`, etc.) should always use braces, and the opening brace should be placed on the same line as the statement. * There should be a space after a conditional/loop statement and before the condition, as well as a space after the condition and before the brace. Example: diff --git a/README.md b/README.md index b02d479e3..2a34a8d0c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# MultiOgar +# MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.42** +Current version: **1.2.43** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 39848ae10..fa3c5eaa2 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,25 @@ -{ - "name": "MultiOgar", - "version": "1.2.42", - "description": "Open source Ogar server", - "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", - "homepage": "https://github.com/Barbosik/MultiOgar", - "license": "Apache-2.0", - "main": "src/index.js", - "dependencies": { - "quad-node": "^1.0.5", - "vector2-node": "latest", - "ws": "latest" - }, - "devDependencies": {}, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "repository": { - "type": "git", - "url": "https://github.com/Barbosik/MultiOgar" - }, - "bugs": { - "url": "https://github.com/Barbosik/MultiOgar/issues" - } +{ + "name": "MultiOgar", + "version": "1.2.43", + "description": "Open source Ogar server", + "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", + "homepage": "https://github.com/Barbosik/MultiOgar", + "license": "Apache-2.0", + "main": "src/index.js", + "dependencies": { + "quad-node": "^1.0.5", + "vector2-node": "latest", + "ws": "latest" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://github.com/Barbosik/MultiOgar" + }, + "bugs": { + "url": "https://github.com/Barbosik/MultiOgar/issues" + } } diff --git a/src/GameServer.js b/src/GameServer.js index 8463dcae0..c1fd08217 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1,4 +1,4 @@ -// Library imports +// Library imports var WebSocket = require('ws'); var http = require('http'); var fs = require("fs"); @@ -36,15 +36,15 @@ function GameServer() { this.nodesVirus = []; // Virus nodes this.nodesEjected = []; // Ejected mass nodes this.quadTree = null; - + this.currentFood = 0; this.movingNodes = []; // For move engine this.leaderboard = []; this.leaderboardType = -1; // no type - + this.bots = new BotLoader(this); this.commands; // Command handler - + // Main loop tick this.startTime = +new Date; this.timeStamp = 0; @@ -56,7 +56,7 @@ function GameServer() { this.tickCounter = 0; this.setBorder(10000, 10000); - + // Config this.config = { logVerbosity: 4, // Console log level (0=NONE; 1=FATAL; 2=ERROR; 3=WARN; 4=INFO; 5=DEBUG) @@ -145,7 +145,7 @@ function GameServer() { module.exports = GameServer; -GameServer.prototype.start = function() { +GameServer.prototype.start = function () { this.timerLoopBind = this.timerLoop.bind(this); this.mainLoopBind = this.mainLoop.bind(this); @@ -155,7 +155,7 @@ GameServer.prototype.start = function() { var dirSsl = path.join(path.dirname(module.filename), '../ssl'); var pathKey = path.join(dirSsl, 'key.pem'); var pathCert = path.join(dirSsl, 'cert.pem'); - + if (fs.existsSync(pathKey) && fs.existsSync(pathCert)) { // HTTP/TLS var options = { @@ -178,7 +178,7 @@ GameServer.prototype.start = function() { this.wsServer.on('error', this.onServerSocketError.bind(this)); this.wsServer.on('connection', this.onClientSocketOpen.bind(this)); this.httpServer.listen(this.config.serverPort, this.config.serverBind, this.onHttpServerOpen.bind(this)); - + this.startStatsServer(this.config.serverStatsPort); }; @@ -270,7 +270,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { // Minion detection if (this.config.serverMinionThreshold) { - if ((ws.lastAliveTime - this.startTime)/1000 >= this.config.serverMinionIgnoreTime) { + if ((ws.lastAliveTime - this.startTime) / 1000 >= this.config.serverMinionIgnoreTime) { if (this.minionTest.length >= this.config.serverMinionThreshold) { ws.playerTracker.isMinion = true; for (var i = 0; i < this.minionTest.length; i++) { @@ -297,8 +297,8 @@ GameServer.prototype.onClientSocketClose = function (ws, code) { ws.sendPacket = function (data) { }; ws.closeReason = { code: ws._closeCode, message: ws._closeMessage }; ws.closeTime = +new Date; - Logger.write("DISCONNECTED " + ws.remoteAddress + ":" + ws.remotePort + ", code: " + ws._closeCode + ", reason: \"" + ws._closeMessage + "\", name: \""+ws.playerTracker.getName()+"\""); - + Logger.write("DISCONNECTED " + ws.remoteAddress + ":" + ws.remotePort + ", code: " + ws._closeCode + ", reason: \"" + ws._closeMessage + "\", name: \"" + ws.playerTracker.getName() + "\""); + // disconnected effect var color = this.getGrayColor(ws.playerTracker.getColor()); ws.playerTracker.setColor(color); @@ -316,7 +316,7 @@ GameServer.prototype.onClientSocketMessage = function (ws, message) { ws.packetHandler.handleMessage(message); }; -GameServer.prototype.setBorder = function(width, height) { +GameServer.prototype.setBorder = function (width, height) { var hw = width / 2; var hh = height / 2; this.border = { @@ -339,7 +339,7 @@ GameServer.prototype.getMode = function () { return this.gameMode; }; -GameServer.prototype.getNextNodeId = function() { +GameServer.prototype.getNextNodeId = function () { // Resets integer if (this.lastNodeId > 2147483647) { this.lastNodeId = 1; @@ -347,7 +347,7 @@ GameServer.prototype.getNextNodeId = function() { return this.lastNodeId++ >>> 0; }; -GameServer.prototype.getNewPlayerID = function() { +GameServer.prototype.getNewPlayerID = function () { // Resets integer if (this.lastPlayerId > 2147483647) { this.lastPlayerId = 1; @@ -355,7 +355,7 @@ GameServer.prototype.getNewPlayerID = function() { return this.lastPlayerId++ >>> 0; }; -GameServer.prototype.getRandomPosition = function() { +GameServer.prototype.getRandomPosition = function () { return { x: Math.floor(this.border.minx + this.border.width * Math.random()), y: Math.floor(this.border.miny + this.border.height * Math.random()) @@ -371,7 +371,7 @@ GameServer.prototype.getGrayColor = function (rgb) { }; }; -GameServer.prototype.getRandomColor = function() { +GameServer.prototype.getRandomColor = function () { var h = 360 * Math.random(); var s = 248 / 255; var v = 1; @@ -434,7 +434,7 @@ GameServer.prototype.updateNodeQuad = function (node) { }; -GameServer.prototype.addNode = function(node) { +GameServer.prototype.addNode = function (node) { node.quadItem = { cell: node, x: node.position.x, @@ -450,19 +450,19 @@ GameServer.prototype.addNode = function(node) { this.quadTree.insert(node.quadItem); this.nodes.push(node); - + // Adds to the owning player's screen if (node.owner) { node.setColor(node.owner.getColor()); node.owner.cells.push(node); node.owner.socket.sendPacket(new Packet.AddNode(node.owner, node)); } - + // Special on-add actions node.onAdd(this); }; -GameServer.prototype.removeNode = function(node) { +GameServer.prototype.removeNode = function (node) { if (node.quadItem == null) { throw new TypeError("GameServer.removeNode: attempt to remove invalid node!"); } @@ -475,13 +475,13 @@ GameServer.prototype.removeNode = function(node) { if (index != -1) { this.nodes.splice(index, 1); } - + // Remove from moving cells list index = this.movingNodes.indexOf(node); if (index != -1) { this.movingNodes.splice(index, 1); } - + // Special on-remove actions node.onRemove(this); }; @@ -578,7 +578,7 @@ GameServer.prototype.sendChatMessage = function (from, to, message) { if (to == null || to == client.playerTracker) client.sendPacket(new Packet.ChatMessage(from, message)); } -}; +}; GameServer.prototype.timerLoop = function () { var timeStep = this.updateTimeAvg >> 0; @@ -588,7 +588,7 @@ GameServer.prototype.timerLoop = function () { var ts = new Date().getTime(); var dt = ts - this.timeStamp; if (dt < timeStep - 5) { - setTimeout(this.timerLoopBind, ((timeStep-5) - dt) >> 0); + setTimeout(this.timerLoopBind, ((timeStep - 5) - dt) >> 0); return; } if (dt < timeStep - 1) { @@ -616,7 +616,7 @@ GameServer.prototype.timerLoop = function () { setTimeout(this.timerLoopBind, 0); }; -GameServer.prototype.mainLoop = function() { +GameServer.prototype.mainLoop = function () { var tStart = process.hrtime(); // Loop main functions @@ -627,21 +627,21 @@ GameServer.prototype.mainLoop = function() { this.updateVirus(); // Spawn viruses } this.gameMode.onTick(this); - if (((this.getTick()+3) % (1000 / 40)) == 0) { + if (((this.getTick() + 3) % (1000 / 40)) == 0) { // once per second this.updateMassDecay(); } } this.updateClients(); - - if (((this.getTick()+7) % (1000 / 40)) == 0) { + + if (((this.getTick() + 7) % (1000 / 40)) == 0) { // once per second this.updateLeaderboard(); } // ping server tracker - if (this.config.serverTracker && (this.getTick() % (30000/40)) == 0) { + if (this.config.serverTracker && (this.getTick() % (30000 / 40)) == 0) { // once per 30 seconds this.pingServerTracker(); } @@ -653,14 +653,14 @@ GameServer.prototype.mainLoop = function() { this.updateTime = tEnd[0] * 1000 + tEnd[1] / 1000000; }; -GameServer.prototype.startingFood = function() { +GameServer.prototype.startingFood = function () { // Spawns the starting amount of food cells for (var i = 0; i < this.config.foodMinAmount; i++) { this.spawnFood(); } }; -GameServer.prototype.updateFood = function() { +GameServer.prototype.updateFood = function () { var maxCount = this.config.foodMinAmount - this.currentFood; var spawnCount = Math.min(maxCount, this.config.foodSpawnAmount); for (var i = 0; i < spawnCount; i++) { @@ -676,7 +676,7 @@ GameServer.prototype.updateVirus = function () { } }; -GameServer.prototype.spawnFood = function() { +GameServer.prototype.spawnFood = function () { var cell = new Entity.Food(this, null, this.getRandomPosition(), this.config.foodMinSize); if (this.config.foodMassGrow) { var size = cell.getSize(); @@ -699,7 +699,7 @@ GameServer.prototype.spawnVirus = function () { this.addNode(v); }; -GameServer.prototype.spawnPlayer = function(player, pos, size) { +GameServer.prototype.spawnPlayer = function (player, pos, size) { // Check if can spawn from ejected mass if (!pos && this.config.ejectSpawnPlayer && this.nodesEjected.length > 0) { if (Math.random() >= 0.5) { @@ -730,11 +730,11 @@ GameServer.prototype.spawnPlayer = function(player, pos, size) { // Get starting mass size = this.config.playerStartSize; } - + // Spawn player and add to world var cell = new Entity.PlayerCell(this, player, pos, size); this.addNode(cell); - + // Set initial mouse coords player.mouse = { x: pos.x, @@ -759,7 +759,7 @@ GameServer.prototype.willCollide = function (pos, size) { // Checks cells for collision. // Returns collision manifold or null if there is no collision -GameServer.prototype.checkCellCollision = function(cell, check) { +GameServer.prototype.checkCellCollision = function (cell, check) { var r = cell.getSize() + check.getSize(); var dx = check.position.x - cell.position.x; var dy = check.position.y - cell.position.y; @@ -910,7 +910,7 @@ GameServer.prototype.resolveCollision = function (manifold) { // update bounds this.updateNodeQuad(maxCell); } - + // Remove cell minCell.setKiller(maxCell); this.removeNode(minCell); @@ -1051,7 +1051,7 @@ GameServer.prototype.updateMoveEngine = function () { } }); } - + // resolve rigid body collisions for (var k = 0; k < rigidCollisions.length; k++) { var c = rigidCollisions[k]; @@ -1086,7 +1086,7 @@ GameServer.prototype.splitMass = function (mass, count) { // check maxCount var maxCount = count; var curMass = mass; - while (maxCount > 1 && curMass / (maxCount-1) < throwMass) { + while (maxCount > 1 && curMass / (maxCount - 1) < throwMass) { maxCount = maxCount / 2 >>> 0; } if (maxCount < 2) { @@ -1156,8 +1156,8 @@ GameServer.prototype.splitMass = function (mass, count) { } if (restCount > 0) { splitMass = curMass / restCount; - if (splitMass < throwMass-0.001) { - Logger.warn("GameServer.splitMass: throwMass-splitMass = "+(throwMass-splitMass).toFixed(3)+" (" + mass.toFixed(4) + ", " + count + ")"); + if (splitMass < throwMass - 0.001) { + Logger.warn("GameServer.splitMass: throwMass-splitMass = " + (throwMass - splitMass).toFixed(3) + " (" + mass.toFixed(4) + ", " + count + ")"); } while (restCount > 0) { masses.push(splitMass); @@ -1176,7 +1176,7 @@ GameServer.prototype.splitMass = function (mass, count) { return masses; }; -GameServer.prototype.splitCells = function(client) { +GameServer.prototype.splitCells = function (client) { // it seems that vanilla uses order by cell age var cellToSplit = []; for (var i = 0; i < client.cells.length; i++) { @@ -1200,7 +1200,7 @@ GameServer.prototype.splitCells = function(client) { } var angle = Math.atan2(dx, dy); if (isNaN(angle)) angle = Math.PI / 2; - + if (this.splitPlayerCell(client, cell, angle, null)) { splitCells++; } @@ -1210,12 +1210,12 @@ GameServer.prototype.splitCells = function(client) { // TODO: replace mass with size (Virus) GameServer.prototype.splitPlayerCell = function (client, parent, angle, mass) { // Returns boolean whether a cell has been split or not. You can use this in the future. - + if (client.cells.length >= this.config.playerMaxCells) { // Player cell limit return false; } - + var size1 = 0; var size2 = 0; if (mass == null) { @@ -1247,7 +1247,7 @@ GameServer.prototype.splitPlayerCell = function (client, parent, angle, mass) { return true; }; -GameServer.prototype.canEjectMass = function(client) { +GameServer.prototype.canEjectMass = function (client) { var tick = this.getTick(); if (client.lastEject == null) { // first eject @@ -1263,16 +1263,16 @@ GameServer.prototype.canEjectMass = function(client) { return true; }; -GameServer.prototype.ejectMass = function(client) { +GameServer.prototype.ejectMass = function (client) { if (!this.canEjectMass(client)) return; for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; - + if (!cell) { continue; } - + if (cell.getSize() < this.config.playerMinSplitSize) { continue; } @@ -1283,7 +1283,7 @@ GameServer.prototype.ejectMass = function(client) { continue; } var size1 = Math.sqrt(sizeSquared); - + var dx = client.mouse.x - cell.position.x; var dy = client.mouse.y - cell.position.y; var dl = dx * dx + dy * dy; @@ -1298,7 +1298,7 @@ GameServer.prototype.ejectMass = function(client) { // Remove mass from parent cell first cell.setSize(size1); - + // Get starting position var pos = { x: cell.position.x + dx * cell.getSize(), @@ -1310,31 +1310,31 @@ GameServer.prototype.ejectMass = function(client) { // Randomize angle angle += (Math.random() * 0.6) - 0.3; - + // Create cell var ejected = new Entity.EjectedMass(this, null, pos, size2); ejected.ejector = cell; ejected.setColor(cell.getColor()); ejected.setBoost(780, angle); - + this.addNode(ejected); } }; -GameServer.prototype.shootVirus = function(parent, angle) { +GameServer.prototype.shootVirus = function (parent, angle) { var parentPos = { x: parent.position.x, y: parent.position.y, }; - + var newVirus = new Entity.Virus(this, null, parentPos, this.config.virusMinSize); newVirus.setBoost(780, angle); - + // Add to moving cells list this.addNode(newVirus); }; -GameServer.prototype.getNearestVirus = function(cell) { +GameServer.prototype.getNearestVirus = function (cell) { // Loop through all viruses on the map. There is probably a more efficient way of doing this but whatever for (var i = 0; i < this.nodesVirus.length; i++) { var check = this.nodesVirus[i]; @@ -1345,7 +1345,7 @@ GameServer.prototype.getNearestVirus = function(cell) { } }; -GameServer.prototype.updateMassDecay = function() { +GameServer.prototype.updateMassDecay = function () { if (!this.config.playerDecayRate) { return; } @@ -1445,7 +1445,7 @@ GameServer.prototype.changeConfig = function (name, value) { this.config.playerMinSize = Math.max(32, this.config.playerMinSize); Logger.setVerbosity(this.config.logVerbosity); Logger.setFileVerbosity(this.config.logFileVerbosity); - + Logger.print("Set " + name + " = " + this.config[name]); }; @@ -1578,7 +1578,7 @@ GameServer.prototype.banIp = function (ip) { return; } this.ipBanList.push(ip); - if (ipBin[2]=="*" || ipBin[3] == "*") { + if (ipBin[2] == "*" || ipBin[3] == "*") { Logger.print("The IP sub-net " + ip + " has been banned"); } else { Logger.print("The IP " + ip + " has been banned"); @@ -1637,23 +1637,23 @@ GameServer.prototype.kickId = function (id) { if (id == 0) Logger.warn("No players to kick!"); else - Logger.warn("Player with ID "+id+" not found!"); + Logger.warn("Player with ID " + id + " not found!"); }; // Stats server -GameServer.prototype.startStatsServer = function(port) { +GameServer.prototype.startStatsServer = function (port) { // Do not start the server if the port is negative if (port < 1) { return; } - + // Create stats this.stats = "Test"; this.getStats(); - + // Show stats - this.httpServer = http.createServer(function(req, res) { + this.httpServer = http.createServer(function (req, res) { res.setHeader('Access-Control-Allow-Origin', '*'); res.writeHead(200); res.end(this.stats); @@ -1661,7 +1661,7 @@ GameServer.prototype.startStatsServer = function(port) { this.httpServer.on('error', function (err) { Logger.error("Stats Server: " + err.message); }); - + var getStatsBind = this.getStats.bind(this); this.httpServer.listen(port, function () { // Stats server @@ -1670,7 +1670,7 @@ GameServer.prototype.startStatsServer = function(port) { }.bind(this)); }; -GameServer.prototype.getStats = function() { +GameServer.prototype.getStats = function () { // Get server statistics var totalPlayers = 0; var alivePlayers = 0; @@ -1696,16 +1696,16 @@ GameServer.prototype.getStats = function() { 'alive': alivePlayers, 'spectators': spectatePlayers, 'update_time': this.updateTimeAvg.toFixed(3), - 'uptime': Math.round((new Date().getTime() - this.startTime)/1000/60), + 'uptime': Math.round((new Date().getTime() - this.startTime) / 1000 / 60), 'start_time': this.startTime }; this.stats = JSON.stringify(s); }; // Custom prototype functions -WebSocket.prototype.sendPacket = function(packet) { +WebSocket.prototype.sendPacket = function (packet) { if (packet == null) return; - + //if (this.readyState == WebSocket.OPEN && (this._socket.bufferSize == 0) && packet.build) { if (this.readyState == WebSocket.OPEN) { var buffer = packet.build(this.playerTracker.socket.packetHandler.protocol); diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 2571cf8ea..d10221257 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -1,4 +1,4 @@ -var pjson = require('../package.json'); +var pjson = require('../package.json'); var Packet = require('./packet'); var BinaryReader = require('./packet/BinaryReader'); @@ -8,7 +8,7 @@ function PacketHandler(gameServer, socket) { this.protocol = 0; this.isHandshakePassed = false; this.lastChatTick = 0; - + this.pressQ = false; this.pressW = false; this.pressSpace = false; @@ -17,7 +17,7 @@ function PacketHandler(gameServer, socket) { module.exports = PacketHandler; -PacketHandler.prototype.handleMessage = function(message) { +PacketHandler.prototype.handleMessage = function (message) { // Validation if (message.length == 0) { return; @@ -29,7 +29,7 @@ PacketHandler.prototype.handleMessage = function(message) { } // no handshake? - if (!this.isHandshakePassed) { + if (!this.isHandshakePassed) { if (message[0] != 254 || message.length != 5) { // wait handshake return; @@ -68,7 +68,7 @@ PacketHandler.prototype.handleMessage = function(message) { var reader = new BinaryReader(message); var packetId = reader.readUInt8(); - + switch (packetId) { case 0: if (this.socket.playerTracker.cells.length > 0) { diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 4d3474d45..ef5a03e1e 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -1,4 +1,4 @@ -var Packet = require('./packet'); +var Packet = require('./packet'); var GameServer = require('./GameServer'); var BinaryWriter = require("./packet/BinaryWriter"); var UserRoleEnum = require("./enum/UserRoleEnum"); @@ -25,19 +25,19 @@ function PlayerTracker(gameServer, socket) { this._scale = 1; this.isMassChanged = true; this.borderCounter = 0; - + this.mouse = { x: 0, y: 0 }; this.tickLeaderboard = 0; - + this.team = 0; this.spectate = false; this.freeRoam = false; // Free-roam mode enables player to move in spectate mode this.spectateTarget = null; // Spectate target, null for largest player this.lastSpectateSwitchTick = 0; - + this.centerPos = { x: 0, y: 0 @@ -52,7 +52,7 @@ function PlayerTracker(gameServer, socket) { halfWidth: 0, halfHeight: 0 }; - + // Scramble the coordinate system for anti-raga this.scrambleX = 0; this.scrambleY = 0; @@ -61,7 +61,7 @@ function PlayerTracker(gameServer, socket) { this.connectedTime = new Date; this.isMinion = false; this.spawnCounter = 0; - + // Gamemode function if (gameServer) { this.centerPos.x = gameServer.border.centerx; @@ -108,7 +108,7 @@ PlayerTracker.prototype.getFriendlyName = function () { return name; }; -PlayerTracker.prototype.setName = function(name) { +PlayerTracker.prototype.setName = function (name) { this._name = name; if (!name || name.length < 1) { this._nameUnicode = null; @@ -123,7 +123,7 @@ PlayerTracker.prototype.setName = function(name) { this._nameUtf8 = writer.toBuffer(); }; -PlayerTracker.prototype.getName = function() { +PlayerTracker.prototype.getName = function () { return this._name; }; @@ -202,7 +202,7 @@ PlayerTracker.prototype.updateMass = function () { }; PlayerTracker.prototype.massChanged = function () { - this.isMassChanged = true; + this.isMassChanged = true; }; // Functions @@ -211,12 +211,12 @@ PlayerTracker.prototype.joinGame = function (name, skin) { if (this.cells.length > 0) return; if (name == null) name = ""; this.setName(name); - if (skin != null) + if (skin != null) this.setSkin(skin); this.spectate = false; - this.freeRoam = false; - this.spectateTarget = null; - + this.freeRoam = false; + this.spectateTarget = null; + // some old clients don't understand ClearAll message // so we will send update for them if (this.socket.packetHandler.protocol < 6) { @@ -294,7 +294,7 @@ PlayerTracker.prototype.updateTick = function () { this.pressQ(); this.socket.packetHandler.pressQ = false; } - + if (this.spectate) { if (this.freeRoam || this.getSpectateTarget() == null) { // free roam @@ -412,7 +412,7 @@ PlayerTracker.prototype.sendUpdate = function () { // Viewing box -PlayerTracker.prototype.updateCenterInGame = function() { // Get center of cells +PlayerTracker.prototype.updateCenterInGame = function () { // Get center of cells var len = this.cells.length; if (len <= 0) return; var cx = 0; @@ -474,12 +474,12 @@ PlayerTracker.prototype.pressQ = function () { if (tick - this.lastSpectateSwitchTick < 40) return; this.lastSpectateSwitchTick = tick; - + if (this.spectateTarget == null) { - this.freeRoam = !this.freeRoam; - } - this.spectateTarget = null; - } + this.freeRoam = !this.freeRoam; + } + this.spectateTarget = null; + } }; PlayerTracker.prototype.pressW = function () { @@ -487,8 +487,8 @@ PlayerTracker.prototype.pressW = function () { return; } else if (this.gameServer.run) { - this.gameServer.ejectMass(this); - } + this.gameServer.ejectMass(this); + } }; PlayerTracker.prototype.pressSpace = function () { @@ -498,16 +498,16 @@ PlayerTracker.prototype.pressSpace = function () { if (tick - this.lastSpectateSwitchTick < 40) return; this.lastSpectateSwitchTick = tick; - + // Space doesn't work for freeRoam mode - if (this.freeRoam || this.gameServer.largestClient==null) + if (this.freeRoam || this.gameServer.largestClient == null) return; this.nextSpectateTarget(); } else if (this.gameServer.run) { if (this.mergeOverride) return; - this.gameServer.splitCells(this); - } + this.gameServer.splitCells(this); + } }; PlayerTracker.prototype.nextSpectateTarget = function () { @@ -548,7 +548,7 @@ PlayerTracker.prototype.getSpectateTarget = function () { return this.spectateTarget; }; -PlayerTracker.prototype.updateVisibleNodes = function() { +PlayerTracker.prototype.updateVisibleNodes = function () { this.viewNodes = []; if (!this.isMinion) { var self = this; @@ -561,7 +561,7 @@ PlayerTracker.prototype.updateVisibleNodes = function() { this.viewNodes.sort(function (a, b) { return a.nodeId - b.nodeId; }); }; -PlayerTracker.prototype.setCenterPos = function(x, y) { +PlayerTracker.prototype.setCenterPos = function (x, y) { if (isNaN(x) || isNaN(y)) { throw new TypeError("PlayerTracker.setCenterPos: NaN"); } @@ -573,7 +573,7 @@ PlayerTracker.prototype.setCenterPos = function(x, y) { this.centerPos.y = y; }; -PlayerTracker.prototype.sendCameraPacket = function() { +PlayerTracker.prototype.sendCameraPacket = function () { this.socket.sendPacket(new Packet.UpdatePosition( this, this.centerPos.x, diff --git a/src/ai/BotLoader.js b/src/ai/BotLoader.js index 118c2b538..7f9004954 100644 --- a/src/ai/BotLoader.js +++ b/src/ai/BotLoader.js @@ -1,51 +1,51 @@ -// Project imports -var fs = require("fs"); +// Project imports +var fs = require("fs"); var Logger = require('../modules/Logger'); -var BotPlayer = require('./BotPlayer'); -var FakeSocket = require('./FakeSocket'); -var PacketHandler = require('../PacketHandler'); - -function BotLoader(gameServer) { - this.gameServer = gameServer; - this.loadNames(); -} - -module.exports = BotLoader; - -BotLoader.prototype.getName = function() { - var name = ""; - - // Picks a random name for the bot - if (this.randomNames.length > 0) { - var index = (this.randomNames.length * Math.random()) >>> 0; - name = this.randomNames[index]; - } else { - name = "bot" + ++this.nameIndex; - } - - return name; -}; - -BotLoader.prototype.loadNames = function() { - this.randomNames = []; - - if (fs.existsSync("./botnames.txt")) { - // Read and parse the names - filter out whitespace-only names - this.randomNames = fs.readFileSync("./botnames.txt", "utf8").split(/[\r\n]+/).filter(function (x) { - return x != ''; // filter empty names - }); - } - this.nameIndex = 0; -}; - -BotLoader.prototype.addBot = function() { - var s = new FakeSocket(this.gameServer); - s.playerTracker = new BotPlayer(this.gameServer, s); - s.packetHandler = new PacketHandler(this.gameServer, s); - - // Add to client list - this.gameServer.clients.push(s); - - // Add to world - s.packetHandler.setNickname(this.getName()); -}; +var BotPlayer = require('./BotPlayer'); +var FakeSocket = require('./FakeSocket'); +var PacketHandler = require('../PacketHandler'); + +function BotLoader(gameServer) { + this.gameServer = gameServer; + this.loadNames(); +} + +module.exports = BotLoader; + +BotLoader.prototype.getName = function () { + var name = ""; + + // Picks a random name for the bot + if (this.randomNames.length > 0) { + var index = (this.randomNames.length * Math.random()) >>> 0; + name = this.randomNames[index]; + } else { + name = "bot" + ++this.nameIndex; + } + + return name; +}; + +BotLoader.prototype.loadNames = function () { + this.randomNames = []; + + if (fs.existsSync("./botnames.txt")) { + // Read and parse the names - filter out whitespace-only names + this.randomNames = fs.readFileSync("./botnames.txt", "utf8").split(/[\r\n]+/).filter(function (x) { + return x != ''; // filter empty names + }); + } + this.nameIndex = 0; +}; + +BotLoader.prototype.addBot = function () { + var s = new FakeSocket(this.gameServer); + s.playerTracker = new BotPlayer(this.gameServer, s); + s.packetHandler = new PacketHandler(this.gameServer, s); + + // Add to client list + this.gameServer.clients.push(s); + + // Add to world + s.packetHandler.setNickname(this.getName()); +}; diff --git a/src/ai/BotPlayer.js b/src/ai/BotPlayer.js index c94fd81f6..94c86b2cf 100644 --- a/src/ai/BotPlayer.js +++ b/src/ai/BotPlayer.js @@ -1,11 +1,11 @@ -var PlayerTracker = require('../PlayerTracker'); +var PlayerTracker = require('../PlayerTracker'); var gameServer = require('../GameServer'); var Vector = require('vector2-node'); function BotPlayer() { PlayerTracker.apply(this, Array.prototype.slice.call(arguments)); //this.setColor(gameServer.getRandomColor()); - + this.splitCooldown = 0; } @@ -14,18 +14,18 @@ BotPlayer.prototype = new PlayerTracker(); // Functions -BotPlayer.prototype.getLowestCell = function() { +BotPlayer.prototype.getLowestCell = function () { // Gets the cell with the lowest mass if (this.cells.length <= 0) { return null; // Error! } - + // Sort the cells by Array.sort() function to avoid errors var sorted = this.cells.valueOf(); - sorted.sort(function(a, b) { + sorted.sort(function (a, b) { return b.getSize() - a.getSize(); }); - + return sorted[0]; }; @@ -59,7 +59,7 @@ BotPlayer.prototype.sendUpdate = function () { // Overrides the update function }; // Custom -BotPlayer.prototype.decide = function(cell) { +BotPlayer.prototype.decide = function (cell) { if (!cell) return; // Cell was eaten, check in the next tick (I'm too lazy) var cellPos = cell.position; @@ -68,7 +68,7 @@ BotPlayer.prototype.decide = function(cell) { var split = false, splitTarget = null, threats = []; - + for (var i = 0; i < this.viewNodes.length; i++) { var check = this.viewNodes[i]; if (check.owner == this) continue; @@ -89,7 +89,7 @@ BotPlayer.prototype.decide = function(cell) { // Can eat me influence = -check.getSize(); } else { - influence = -(check.getSize()/cell.getSize()) / 3; + influence = -(check.getSize() / cell.getSize()) / 3; } } else if (check.cellType == 1) { // Food @@ -118,7 +118,7 @@ BotPlayer.prototype.decide = function(cell) { } else { influence = check.getSize(); // Might be TeamZ } - + // Apply influence if it isn't 0 or my cell if (influence == 0 || cell.owner == check.owner) continue; @@ -126,7 +126,7 @@ BotPlayer.prototype.decide = function(cell) { // Calculate separation between cell and check var checkPos = check.position; var displacement = new Vector(checkPos.x - cellPos.x, checkPos.y - cellPos.y); - + // Figure out distance between cells var distance = displacement.length(); if (influence < 0) { @@ -134,14 +134,14 @@ BotPlayer.prototype.decide = function(cell) { distance -= cell.getSize() + check.getSize(); if (check.cellType == 0) threats.push(check); } - + // The farther they are the smaller influnce it is if (distance < 1) distance = 1; // Avoid NaN and positive influence with negative distance & attraction influence /= distance; - + // Produce force vector exerted by this entity on the cell var force = displacement.normalize().scale(influence); - + // Splitting conditions if (check.cellType == 0 && cell.getSize() > (check.getSize() + 4) * 1.15 && @@ -149,7 +149,7 @@ BotPlayer.prototype.decide = function(cell) { (!split) && this.splitCooldown == 0 && this.cells.length < 3) { - + var endDist = 780 + 40 - cell.getSize() / 2 - check.getSize(); if (endDist > 0 && distance < endDist) { @@ -161,10 +161,10 @@ BotPlayer.prototype.decide = function(cell) { result.add(force); } } - + // Normalize the resulting vector result.normalize(); - + // Check for splitkilling and threats if (split) { // Can be shortened but I'm too lazy @@ -202,12 +202,12 @@ BotPlayer.prototype.decide = function(cell) { // Subfunctions -BotPlayer.prototype.largest = function(list) { +BotPlayer.prototype.largest = function (list) { // Sort the cells by Array.sort() function to avoid errors var sorted = list.valueOf(); - sorted.sort(function(a, b) { + sorted.sort(function (a, b) { return b.getSize() - a.getSize(); }); - + return sorted[0]; }; diff --git a/src/ai/FakeSocket.js b/src/ai/FakeSocket.js index 227348ca7..6204aee5d 100644 --- a/src/ai/FakeSocket.js +++ b/src/ai/FakeSocket.js @@ -1,4 +1,4 @@ -// A fake socket for bot players +// A fake socket for bot players function FakeSocket(gameServer) { this.server = gameServer; @@ -9,11 +9,11 @@ module.exports = FakeSocket; // Override -FakeSocket.prototype.sendPacket = function(packet) { +FakeSocket.prototype.sendPacket = function (packet) { // Fakes sending a packet return; }; -FakeSocket.prototype.close = function(error) { +FakeSocket.prototype.close = function (error) { this.isCloseRequest = true; }; diff --git a/src/ai/Readme.txt b/src/ai/Readme.txt index 17f605ef7..c2660acf7 100644 --- a/src/ai/Readme.txt +++ b/src/ai/Readme.txt @@ -1,4 +1,4 @@ -[Ogar player bots] +[Ogar player bots] These bots are designed to be used for testing new commits of Ogar. To install this module, set the serverBots config field in gameserver.js to an amount higher than 0 (10 is a good amount), or issue command addBots [number] in console. diff --git a/src/entity/Cell.js b/src/entity/Cell.js index 3cb6c872e..22e5a9357 100644 --- a/src/entity/Cell.js +++ b/src/entity/Cell.js @@ -1,4 +1,4 @@ -function Cell(gameServer, owner, position, size) { +function Cell(gameServer, owner, position, size) { this.gameServer = gameServer; this.owner = owner; // playerTracker that owns this cell @@ -14,7 +14,7 @@ function Cell(gameServer, owner, position, size) { this.isAgitated = false;// If true, then this cell has waves on it's outline this.killedBy = null; // Cell that ate this cell this.isMoving = false; // Indicate that cell is in boosted mode - + this.boostDistance = 0; this.boostDirection = { x: 1, y: 0, angle: Math.PI / 2 }; this.boostMaxSpeed = 78; // boost speed limit, sqrt(780*780/100) @@ -37,17 +37,17 @@ module.exports = Cell; // Fields not defined by the constructor are considered private and need a getter/setter to access from a different class -Cell.prototype.setColor = function(color) { +Cell.prototype.setColor = function (color) { this.color.r = color.r; this.color.g = color.g; this.color.b = color.b; }; -Cell.prototype.getColor = function() { +Cell.prototype.getColor = function () { return this.color; }; -Cell.prototype.getType = function() { +Cell.prototype.getType = function () { return this.cellType; }; @@ -64,7 +64,7 @@ Cell.prototype.setSize = function (size) { this.owner.massChanged(); }; -Cell.prototype.getSize = function() { +Cell.prototype.getSize = function () { return this._size; }; @@ -74,12 +74,12 @@ Cell.prototype.getSizeSquared = function () { Cell.prototype.getMass = function () { if (this._mass == null) { - this._mass = this.getSizeSquared() / 100; - } + this._mass = this.getSizeSquared() / 100; + } return this._mass; }; -Cell.prototype.getSpeed = function() { +Cell.prototype.getSpeed = function () { if (this._speed == null) { var speed = 2.1106 / Math.pow(this.getSize(), 0.449); // tickStep=40ms @@ -88,7 +88,7 @@ Cell.prototype.getSpeed = function() { return this._speed; }; -Cell.prototype.setAngle = function(angle) { +Cell.prototype.setAngle = function (angle) { this.boostDirection = { x: Math.sin(angle), y: Math.cos(angle), @@ -96,7 +96,7 @@ Cell.prototype.setAngle = function(angle) { }; }; -Cell.prototype.getAngle = function() { +Cell.prototype.getAngle = function () { return this.boostDirection.angle; }; @@ -131,7 +131,7 @@ Cell.prototype.canEat = function (cell) { Cell.prototype.onEat = function (prey) { // Called to eat prey cell - this.setSize(Math.sqrt(this.getSizeSquared() + prey.getSizeSquared())); + this.setSize(Math.sqrt(this.getSizeSquared() + prey.getSizeSquared())); }; Cell.prototype.onEaten = function (hunter) { @@ -273,7 +273,7 @@ Cell.prototype.checkBorder = function (border) { // Lib -function findLineIntersection (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { +function findLineIntersection(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { var z1 = p1x - p0x; var z2 = p3x - p2x; var w1 = p1y - p0y; diff --git a/src/entity/EjectedMass.js b/src/entity/EjectedMass.js index 2d7e831ba..b5b480265 100644 --- a/src/entity/EjectedMass.js +++ b/src/entity/EjectedMass.js @@ -1,25 +1,25 @@ -var Cell = require('./Cell'); - -function EjectedMass() { - Cell.apply(this, Array.prototype.slice.call(arguments)); - - this.cellType = 3; -} - -module.exports = EjectedMass; -EjectedMass.prototype = new Cell(); - -// Main Functions - +var Cell = require('./Cell'); + +function EjectedMass() { + Cell.apply(this, Array.prototype.slice.call(arguments)); + + this.cellType = 3; +} + +module.exports = EjectedMass; +EjectedMass.prototype = new Cell(); + +// Main Functions + EjectedMass.prototype.onAdd = function (gameServer) { - // Add to list of ejected mass - gameServer.nodesEjected.push(this); + // Add to list of ejected mass + gameServer.nodesEjected.push(this); +}; + +EjectedMass.prototype.onRemove = function (gameServer) { + // Remove from list of ejected mass + var index = gameServer.nodesEjected.indexOf(this); + if (index != -1) { + gameServer.nodesEjected.splice(index, 1); + } }; - -EjectedMass.prototype.onRemove = function(gameServer) { - // Remove from list of ejected mass - var index = gameServer.nodesEjected.indexOf(this); - if (index != -1) { - gameServer.nodesEjected.splice(index, 1); - } -}; diff --git a/src/entity/Food.js b/src/entity/Food.js index a83c216b2..16fe7e14b 100644 --- a/src/entity/Food.js +++ b/src/entity/Food.js @@ -1,8 +1,8 @@ -var Cell = require('./Cell'); +var Cell = require('./Cell'); function Food() { Cell.apply(this, Array.prototype.slice.call(arguments)); - + this.cellType = 1; } @@ -15,6 +15,6 @@ Food.prototype.onAdd = function (gameServer) { gameServer.currentFood++; }; -Food.prototype.onRemove = function(gameServer) { +Food.prototype.onRemove = function (gameServer) { gameServer.currentFood--; }; diff --git a/src/entity/MotherCell.js b/src/entity/MotherCell.js index 1929b5e12..d16fd8b2f 100644 --- a/src/entity/MotherCell.js +++ b/src/entity/MotherCell.js @@ -29,7 +29,7 @@ MotherCell.prototype.canEat = function (cell) { MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function MotherCell.prototype.onUpdate = function () { - if (this.getSize() <= this.motherCellMinSize) { + if (this.getSize() <= this.motherCellMinSize) { return; } var maxFood = this.gameServer.config.foodMaxAmount; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 3004d4e48..9227ea2b8 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -1,4 +1,4 @@ -var Cell = require('./Cell'); +var Cell = require('./Cell'); function PlayerCell() { Cell.apply(this, Array.prototype.slice.call(arguments)); @@ -22,7 +22,7 @@ PlayerCell.prototype.updateRemerge = function () { var baseTtr = this.gameServer.config.playerRecombineTime; // default baseTtr = 30 if (baseTtr == 0) { // instant merge - if (this.getSize() >= 780/2) { + if (this.getSize() >= 780 / 2) { this._canRemerge = age > 20; return; } @@ -86,12 +86,12 @@ PlayerCell.prototype.moveUser = function (border) { // Override -PlayerCell.prototype.onAdd = function(gameServer) { +PlayerCell.prototype.onAdd = function (gameServer) { // Gamemode actions gameServer.gameMode.onCellAdd(this); }; -PlayerCell.prototype.onRemove = function(gameServer) { +PlayerCell.prototype.onRemove = function (gameServer) { var index; // Remove from player cell list index = this.owner.cells.indexOf(this); diff --git a/src/entity/Virus.js b/src/entity/Virus.js index ff303d5ba..dc66e1c3b 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -1,42 +1,42 @@ -var Cell = require('./Cell'); -var Logger = require('../modules/Logger'); - -function Virus() { - Cell.apply(this, Array.prototype.slice.call(arguments)); - - this.cellType = 2; - this.isSpiked = true; - this.fed = 0; - this.isMotherCell = false; // Not to confuse bots - this.setColor({ r: 0, g: 255, b: 0 }); -} - -module.exports = Virus; -Virus.prototype = new Cell(); - -// Main Functions - -Virus.prototype.canEat = function (cell) { - return cell.cellType == 3; // virus can eat ejected mass only -}; - -Virus.prototype.onEat = function (prey) { +var Cell = require('./Cell'); +var Logger = require('../modules/Logger'); + +function Virus() { + Cell.apply(this, Array.prototype.slice.call(arguments)); + + this.cellType = 2; + this.isSpiked = true; + this.fed = 0; + this.isMotherCell = false; // Not to confuse bots + this.setColor({ r: 0, g: 255, b: 0 }); +} + +module.exports = Virus; +Virus.prototype = new Cell(); + +// Main Functions + +Virus.prototype.canEat = function (cell) { + return cell.cellType == 3; // virus can eat ejected mass only +}; + +Virus.prototype.onEat = function (prey) { // Called to eat prey cell - this.setSize(Math.sqrt(this.getSizeSquared() + prey.getSizeSquared())); - - if (this.getSize() >= this.gameServer.config.virusMaxSize) { - this.setSize(this.gameServer.config.virusMinSize); // Reset mass - this.gameServer.shootVirus(this, prey.getAngle()); - } -}; - -Virus.prototype.onEaten = function(consumer) { - var client = consumer.owner; - if (client == null) return; - + this.setSize(Math.sqrt(this.getSizeSquared() + prey.getSizeSquared())); + + if (this.getSize() >= this.gameServer.config.virusMaxSize) { + this.setSize(this.gameServer.config.virusMinSize); // Reset mass + this.gameServer.shootVirus(this, prey.getAngle()); + } +}; + +Virus.prototype.onEaten = function (consumer) { + var client = consumer.owner; + if (client == null) return; + var maxSplit = this.gameServer.config.playerMaxCells - consumer.owner.cells.length; var masses = this.gameServer.splitMass(consumer.getMass(), maxSplit + 1); - if (masses.length < 2) { + if (masses.length < 2) { return; } @@ -51,26 +51,26 @@ Virus.prototype.onEaten = function(consumer) { // Blow up the cell... var angle = 2 * Math.PI * Math.random(); var step = 2 * Math.PI / masses.length; - for (var i = 0; i < masses.length; i++) { - if (!this.gameServer.splitPlayerCell(client, consumer, angle, masses[i])) { + for (var i = 0; i < masses.length; i++) { + if (!this.gameServer.splitPlayerCell(client, consumer, angle, masses[i])) { break; } angle += step; - if (angle >= 2 * Math.PI) { + if (angle >= 2 * Math.PI) { angle -= 2 * Math.PI; } } -}; - -Virus.prototype.onAdd = function(gameServer) { - gameServer.nodesVirus.push(this); -}; - -Virus.prototype.onRemove = function(gameServer) { - var index = gameServer.nodesVirus.indexOf(this); - if (index != -1) { - gameServer.nodesVirus.splice(index, 1); - } else { - Logger.error("Virus.onRemove: Tried to remove a non existing virus!"); - } -}; +}; + +Virus.prototype.onAdd = function (gameServer) { + gameServer.nodesVirus.push(this); +}; + +Virus.prototype.onRemove = function (gameServer) { + var index = gameServer.nodesVirus.indexOf(this); + if (index != -1) { + gameServer.nodesVirus.splice(index, 1); + } else { + Logger.error("Virus.onRemove: Tried to remove a non existing virus!"); + } +}; diff --git a/src/entity/index.js b/src/entity/index.js index 4913f6e2b..745b68638 100644 --- a/src/entity/index.js +++ b/src/entity/index.js @@ -1,4 +1,4 @@ -module.exports = { +module.exports = { Cell: require('./Cell'), PlayerCell: require('./PlayerCell'), Food: require('./Food'), diff --git a/src/gamemodes/Experimental.js b/src/gamemodes/Experimental.js index 80a4cfeeb..4d9965d97 100644 --- a/src/gamemodes/Experimental.js +++ b/src/gamemodes/Experimental.js @@ -1,19 +1,19 @@ -var FFA = require('./FFA'); // Base gamemode +var FFA = require('./FFA'); // Base gamemode var Entity = require('../entity'); var Logger = require('../modules/Logger'); function Experimental() { FFA.apply(this, Array.prototype.slice.call(arguments)); - + this.ID = 2; this.name = "Experimental"; this.specByLeaderboard = true; - + // Gamemode Specific Variables this.nodesMother = []; this.tickMotherSpawn = 0; this.tickMotherUpdate = 0; - + // Config this.motherSpawnInterval = 25 * 5; // How many ticks it takes to spawn another mother cell (5 seconds) this.motherUpdateInterval = 5; // How many ticks it takes to spawn mother food (1 second) @@ -44,21 +44,21 @@ Experimental.prototype.spawnMotherCell = function (gameServer) { // Override -Experimental.prototype.onServerInit = function(gameServer) { +Experimental.prototype.onServerInit = function (gameServer) { // Called when the server starts gameServer.run = true; - + var mapSize = Math.max(gameServer.border.width, gameServer.border.height); - + // 7 mother cells for vanilla map size //this.motherMinAmount = Math.ceil(mapSize / 2000); //this.motherMaxAmount = this.motherMinAmount * 2; - + var self = this; // Override // Special virus mechanics - Entity.Virus.prototype.onEat = function(prey) { + Entity.Virus.prototype.onEat = function (prey) { // Pushes the virus var angle = prey.isMoving ? prey.getAngle() : this.getAngle(); this.setBoost(16 * 20, angle); @@ -88,7 +88,7 @@ Experimental.prototype.onChange = function (gameServer) { Entity.MotherCell.prototype.onRemove = require('../Entity/MotherCell').prototype.onRemove; }; -Experimental.prototype.onTick = function(gameServer) { +Experimental.prototype.onTick = function (gameServer) { // Mother Cell Spawning if (this.tickMotherSpawn >= this.motherSpawnInterval) { this.tickMotherSpawn = 0; diff --git a/src/gamemodes/FFA.js b/src/gamemodes/FFA.js index 96f24a75e..59163afec 100644 --- a/src/gamemodes/FFA.js +++ b/src/gamemodes/FFA.js @@ -1,8 +1,8 @@ -var Mode = require('./Mode'); +var Mode = require('./Mode'); function FFA() { Mode.apply(this, Array.prototype.slice.call(arguments)); - + this.ID = 0; this.name = "Free For All"; this.specByLeaderboard = true; @@ -13,7 +13,7 @@ FFA.prototype = new Mode(); // Gamemode Specific Functions -FFA.prototype.leaderboardAddSort = function(player, leaderboard) { +FFA.prototype.leaderboardAddSort = function (player, leaderboard) { // Adds the player and sorts the leaderboard var len = leaderboard.length - 1; var loop = true; @@ -33,20 +33,20 @@ FFA.prototype.leaderboardAddSort = function(player, leaderboard) { // Override -FFA.prototype.onPlayerSpawn = function(gameServer, player) { - player.setColor(player.isMinion ? {r:240, g:240, b:255} : gameServer.getRandomColor()); +FFA.prototype.onPlayerSpawn = function (gameServer, player) { + player.setColor(player.isMinion ? { r: 240, g: 240, b: 255 } : gameServer.getRandomColor()); // Spawn player gameServer.spawnPlayer(player); }; -FFA.prototype.updateLB = function(gameServer) { +FFA.prototype.updateLB = function (gameServer) { gameServer.leaderboardType = this.packetLB; var lb = gameServer.leaderboard; // Loop through all clients for (var i = 0; i < gameServer.clients.length; i++) { var client = gameServer.clients[i]; if (client == null) continue; - + var player = client.playerTracker; if (player.isRemoved) continue; // Don't add disconnected players to list @@ -55,7 +55,7 @@ FFA.prototype.updateLB = function(gameServer) { if (player.cells.length <= 0) continue; - + if (lb.length == 0) { // Initial player lb.push(player); @@ -70,6 +70,6 @@ FFA.prototype.updateLB = function(gameServer) { } } } - + this.rankOne = lb[0]; } diff --git a/src/gamemodes/HungerGames.js b/src/gamemodes/HungerGames.js index a8d81804b..8f11345d0 100644 --- a/src/gamemodes/HungerGames.js +++ b/src/gamemodes/HungerGames.js @@ -1,12 +1,12 @@ -var Tournament = require('./Tournament'); +var Tournament = require('./Tournament'); var Entity = require('../entity'); function HungerGames() { Tournament.apply(this, Array.prototype.slice.call(arguments)); - + this.ID = 11; this.name = "Hunger Games"; - + // Gamemode Specific Variables this.maxContenders = 12; this.baseSpawnPoints = [{ @@ -59,50 +59,50 @@ HungerGames.prototype = new Tournament(); // Gamemode Specific Functions -HungerGames.prototype.getPos = function() { +HungerGames.prototype.getPos = function () { var pos = { x: 0, y: 0 }; - + // Random Position if (this.contenderSpawnPoints.length > 0) { var index = Math.floor(Math.random() * this.contenderSpawnPoints.length); pos = this.contenderSpawnPoints[index]; this.contenderSpawnPoints.splice(index, 1); } - + return { x: pos.x, y: pos.y }; }; -HungerGames.prototype.spawnFood = function(gameServer, mass, pos) { +HungerGames.prototype.spawnFood = function (gameServer, mass, pos) { var cell = new Entity.Food(gameServer, null, pos, mass); cell.setColor(gameServer.getRandomColor()); gameServer.addNode(cell); }; -HungerGames.prototype.spawnVirus = function(gameServer, pos) { +HungerGames.prototype.spawnVirus = function (gameServer, pos) { var v = new Entity.Virus(gameServer, null, pos, gameServer.config.virusMinSize); gameServer.addNode(v); }; -HungerGames.prototype.onPlayerDeath = function(gameServer) { +HungerGames.prototype.onPlayerDeath = function (gameServer) { gameServer.setBorder( gameServer.border.width - this.borderDec * 2, gameServer.border.height - this.borderDec * 2); - + // Remove all cells var len = gameServer.nodes.length; for (var i = 0; i < len; i++) { var node = gameServer.nodes[i]; - + if ((!node) || (node.getType() == 0)) { continue; } - + // Move if (node.position.x < gameServer.border.minx) { gameServer.removeNode(node); @@ -122,13 +122,13 @@ HungerGames.prototype.onPlayerDeath = function(gameServer) { // Override -HungerGames.prototype.onServerInit = function(gameServer) { +HungerGames.prototype.onServerInit = function (gameServer) { // Prepare this.prepare(gameServer); - + // Resets spawn points this.contenderSpawnPoints = this.baseSpawnPoints.slice(); - + // Override config values if (gameServer.config.serverBots > this.maxContenders) { // The number of bots cannot exceed the maximum amount of contenders @@ -145,11 +145,11 @@ HungerGames.prototype.onServerInit = function(gameServer) { gameServer.config.virusMaxAmount = 100; gameServer.config.ejectSpawnPlayer = 0; gameServer.config.playerDisconnectTime = 10; // So that people dont disconnect and stall the game for too long - + // Spawn Initial Virus/Large food var mapWidth = gameServer.border.width; var mapHeight = gameServer.border.height; - + // Food this.spawnFood(gameServer, 200, { x: mapWidth * .5, @@ -219,7 +219,7 @@ HungerGames.prototype.onServerInit = function(gameServer) { x: mapWidth * .4, y: mapHeight * .7 }); - + // Virus this.spawnVirus(gameServer, { x: mapWidth * .6, @@ -287,13 +287,13 @@ HungerGames.prototype.onServerInit = function(gameServer) { }); }; -HungerGames.prototype.onPlayerSpawn = function(gameServer, player) { +HungerGames.prototype.onPlayerSpawn = function (gameServer, player) { // Only spawn players if the game hasnt started yet if ((this.gamePhase == 0) && (this.contenders.length < this.maxContenders)) { player.setColor(gameServer.getRandomColor()); // Random color this.contenders.push(player); // Add to contenders list gameServer.spawnPlayer(player, this.getPos()); - + if (this.contenders.length == this.maxContenders) { // Start the game once there is enough players this.startGamePrep(gameServer); diff --git a/src/gamemodes/Mode.js b/src/gamemodes/Mode.js index c45dcdcd2..0c030e786 100644 --- a/src/gamemodes/Mode.js +++ b/src/gamemodes/Mode.js @@ -1,53 +1,53 @@ -function Mode() { - this.ID = -1; - this.name = "Blank"; - this.decayMod = 1.0; // Modifier for decay rate (Multiplier) - this.packetLB = 49; // Packet id for leaderboard packet (48 = Text List, 49 = List, 50 = Pie chart) - this.haveTeams = false; // True = gamemode uses teams, false = gamemode doesnt use teams - - this.specByLeaderboard = false; // false = spectate from player list instead of leaderboard -} - -module.exports = Mode; - -// Override these - -Mode.prototype.onServerInit = function(gameServer) { - // Called when the server starts - gameServer.run = true; -}; - -Mode.prototype.onTick = function(gameServer) { - // Called on every game tick -}; - -Mode.prototype.onChange = function(gameServer) { - // Called when someone changes the gamemode via console commands -}; - -Mode.prototype.onPlayerInit = function(player) { - // Called after a player object is constructed -}; - -Mode.prototype.onPlayerSpawn = function(gameServer, player) { - // Called when a player is spawned - player.setColor(gameServer.getRandomColor()); // Random color - gameServer.spawnPlayer(player); -}; - -Mode.prototype.onCellAdd = function(cell) { - // Called when a player cell is added -}; - -Mode.prototype.onCellRemove = function(cell) { - // Called when a player cell is removed -}; - -Mode.prototype.onCellMove = function(cell, gameServer) { - // Called when a player cell is moved -}; - -Mode.prototype.updateLB = function (gameServer) { +function Mode() { + this.ID = -1; + this.name = "Blank"; + this.decayMod = 1.0; // Modifier for decay rate (Multiplier) + this.packetLB = 49; // Packet id for leaderboard packet (48 = Text List, 49 = List, 50 = Pie chart) + this.haveTeams = false; // True = gamemode uses teams, false = gamemode doesnt use teams + + this.specByLeaderboard = false; // false = spectate from player list instead of leaderboard +} + +module.exports = Mode; + +// Override these + +Mode.prototype.onServerInit = function (gameServer) { + // Called when the server starts + gameServer.run = true; +}; + +Mode.prototype.onTick = function (gameServer) { + // Called on every game tick +}; + +Mode.prototype.onChange = function (gameServer) { + // Called when someone changes the gamemode via console commands +}; + +Mode.prototype.onPlayerInit = function (player) { + // Called after a player object is constructed +}; + +Mode.prototype.onPlayerSpawn = function (gameServer, player) { + // Called when a player is spawned + player.setColor(gameServer.getRandomColor()); // Random color + gameServer.spawnPlayer(player); +}; + +Mode.prototype.onCellAdd = function (cell) { + // Called when a player cell is added +}; + +Mode.prototype.onCellRemove = function (cell) { + // Called when a player cell is removed +}; + +Mode.prototype.onCellMove = function (cell, gameServer) { + // Called when a player cell is moved +}; + +Mode.prototype.updateLB = function (gameServer) { gameServer.leaderboardType = this.packetLB; - // Called when the leaderboard update function is called -}; + // Called when the leaderboard update function is called +}; diff --git a/src/gamemodes/Rainbow.js b/src/gamemodes/Rainbow.js index 0e3e10f2b..d39c56e1d 100644 --- a/src/gamemodes/Rainbow.js +++ b/src/gamemodes/Rainbow.js @@ -1,13 +1,13 @@ -var FFA = require('./FFA'); // Base gamemode +var FFA = require('./FFA'); // Base gamemode var Food = require('../entity/Food'); function Rainbow() { FFA.apply(this, Array.prototype.slice.call(arguments)); - + this.ID = 20; this.name = "Rainbow FFA"; this.specByLeaderboard = true; - + this.colors = [{ 'r': 255, 'g': 0, @@ -138,37 +138,37 @@ Rainbow.prototype = new FFA(); // Gamemode Specific Functions -Rainbow.prototype.changeColor = function(node) { +Rainbow.prototype.changeColor = function (node) { if (typeof node.rainbow == 'undefined') { node.rainbow = Math.floor(Math.random() * this.colors.length); } - + if (node.rainbow >= this.colorsLength) { node.rainbow = 0; } - + node.setColor(this.colors[node.rainbow]); node.rainbow += this.speed; }; // Override -Rainbow.prototype.onServerInit = function() { +Rainbow.prototype.onServerInit = function () { }; -Rainbow.prototype.onChange = function() { +Rainbow.prototype.onChange = function () { }; -Rainbow.prototype.onTick = function(gameServer) { +Rainbow.prototype.onTick = function (gameServer) { var color, node; // Change color for (var i in gameServer.nodes) { node = gameServer.nodes[i]; - + if (!node) { continue; } - + this.changeColor(node); } }; diff --git a/src/gamemodes/TeamX.js b/src/gamemodes/TeamX.js index 7f6554aeb..0455d28bb 100644 --- a/src/gamemodes/TeamX.js +++ b/src/gamemodes/TeamX.js @@ -1,4 +1,4 @@ -// TODO: fix this game mode has outdated code and probably will not works +// TODO: fix this game mode has outdated code and probably will not works var Teams = require('./Teams.js'); var Cell = require('../entity/Cell.js'); var Food = require('../entity/Food.js'); @@ -11,10 +11,10 @@ var GS_getCellsInRange = null; function TeamX() { Teams.apply(this, Array.prototype.slice.call(arguments)); - + this.ID = 14; this.name = 'Experimental Team'; - + // configurations: this.teamCollision = false; // set to true to disable eating teammates this.pushVirus = false; // true: pushing virus, false: splitting virus @@ -23,21 +23,21 @@ function TeamX() { this.motherUpdateInterval = 5; // How many ticks it takes to update the mother cell (1 tick = 50 ms) this.motherSpawnInterval = 100; // How many ticks it takes to spawn another mother cell - Currently 5 seconds this.motherMinAmount = 5; - + // game mode data: this.colors = [{ - 'r': 255, - 'g': 7, - 'b': 7 - }, { - 'r': 7, - 'g': 255, - 'b': 7 - }, { - 'r': 7, - 'g': 7, - 'b': 255 - }, ]; + 'r': 255, + 'g': 7, + 'b': 7 + }, { + 'r': 7, + 'g': 255, + 'b': 7 + }, { + 'r': 7, + 'g': 7, + 'b': 255 + },]; this.nodesMother = []; this.tickMother = 0; this.tickMotherS = 0; @@ -48,22 +48,22 @@ TeamX.prototype = new Teams(); // Gamemode Specific Functions -TeamX.prototype.updateMotherCells = function(gameServer) { +TeamX.prototype.updateMotherCells = function (gameServer) { for (var i in this.nodesMother) { var mother = this.nodesMother[i]; - + // Checks mother.update(gameServer); mother.checkEat(gameServer); } }; -TeamX.prototype.spawnMotherCell = function(gameServer) { +TeamX.prototype.spawnMotherCell = function (gameServer) { // Checks if there are enough mother cells on the map if (this.nodesMother.length < this.motherMinAmount) { // Spawns a mother cell var pos = gameServer.getRandomPosition(); - + // Check for players var size = Math.sqrt(this.motherCellMass * 100); var bound = { @@ -81,7 +81,7 @@ TeamX.prototype.spawnMotherCell = function(gameServer) { } }; -TeamX.prototype.countNotInRange = function(client) { +TeamX.prototype.countNotInRange = function (client) { var count = 0; for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; @@ -94,14 +94,14 @@ TeamX.prototype.countNotInRange = function(client) { // Overwrite: -TeamX.prototype.fuzzColorComponent = function(component) { +TeamX.prototype.fuzzColorComponent = function (component) { if (component != 255) { component = Math.random() * (this.colorFuzziness - 7) + 7; } return component; }; -TeamX.prototype.getTeamColor = function(team) { +TeamX.prototype.getTeamColor = function (team) { var color = this.colors[team]; return { r: this.fuzzColorComponent(color.r), @@ -110,12 +110,12 @@ TeamX.prototype.getTeamColor = function(team) { }; }; -TeamX.prototype.onServerInit = function(gameServer) { +TeamX.prototype.onServerInit = function (gameServer) { // Set up teams for (var i = 0; i < this.teamAmount; i++) { this.nodes[i] = []; } - + // Special virus mechanics if (this.pushVirus) { Virus.prototype.onEat = function (prey) { @@ -124,17 +124,17 @@ TeamX.prototype.onServerInit = function(gameServer) { this.setBoost(16 * 20, angle); }; } - + if (GS_getRandomColor == null) GS_getRandomColor = gameServer.getRandomColor; // backup if (GS_getRandomSpawn == null) GS_getRandomSpawn = gameServer.getRandomSpawn; - + // Override this gameServer.getRandomSpawn = gameServer.getRandomPosition; - gameServer.getRandomColor = function() { + gameServer.getRandomColor = function () { var colorRGB = [0xFF, 0x07, (Math.random() * 256) >> 0]; - colorRGB.sort(function() { + colorRGB.sort(function () { return 0.5 - Math.random(); }); return { @@ -143,7 +143,7 @@ TeamX.prototype.onServerInit = function(gameServer) { g: colorRGB[2] }; }; - + // migrate current players to team mode for (var i = 0; i < gameServer.clients.length; i++) { var client = gameServer.clients[i].playerTracker; @@ -157,7 +157,7 @@ TeamX.prototype.onServerInit = function(gameServer) { } }; -TeamX.prototype.onChange = function(gameServer) { +TeamX.prototype.onChange = function (gameServer) { // Remove all mother cells for (var i in this.nodesMother) { gameServer.removeNode(this.nodesMother[i]); @@ -175,7 +175,7 @@ TeamX.prototype.onChange = function(gameServer) { } }; -TeamX.prototype.onTick = function(gameServer) { +TeamX.prototype.onTick = function (gameServer) { // Mother Cell updates if (this.tickMother >= this.motherUpdateInterval) { this.updateMotherCells(gameServer); @@ -183,7 +183,7 @@ TeamX.prototype.onTick = function(gameServer) { } else { this.tickMother++; } - + // Mother Cell Spawning if (this.tickMotherS >= this.motherSpawnInterval) { this.spawnMotherCell(gameServer); @@ -198,7 +198,7 @@ TeamX.prototype.onTick = function(gameServer) { function MotherCell() { // Temporary - Will be in its own file if Zeach decides to add this to vanilla Cell.apply(this, Array.prototype.slice.call(arguments)); - + this.cellType = 2; // Copies virus cell this.setColor({ r: 205, g: 85, b: 100 }); this.isSpiked = true; @@ -206,10 +206,10 @@ function MotherCell() { // Temporary - Will be in its own file if Zeach decides MotherCell.prototype = new Cell(); // Base -MotherCell.prototype.update = function(gameServer) { +MotherCell.prototype.update = function (gameServer) { // Add mass this.setSize(Math.sqrt(this.getSize() * this.getSize() + 0.25 * 0.25)); - + // Spawn food var maxFood = 10; // Max food spawned per tick var i = 0; // Food spawn counter @@ -218,14 +218,14 @@ MotherCell.prototype.update = function(gameServer) { if (gameServer.currentFood < gameServer.config.foodMaxAmount) { this.spawnFood(gameServer); } - + // Incrementers this.setSize(Math.sqrt(this.getSize() * this.getSize() - 1)); i++; } }; -MotherCell.prototype.spawnFood = function(gameServer) { +MotherCell.prototype.spawnFood = function (gameServer) { // Get starting position var angle = Math.random() * 6.28; // (Math.PI * 2) ??? Precision is not our greatest concern here var r = this.getSize(); @@ -233,26 +233,26 @@ MotherCell.prototype.spawnFood = function(gameServer) { x: this.position.x + (r * Math.sin(angle)), y: this.position.y + (r * Math.cos(angle)) }; - + // Spawn food var cell = new Food(gameServer.getNextNodeId(), null, pos, gameServer.config.foodMinSize, gameServer); cell.setColor(gameServer.getRandomColor()); - + gameServer.addNode(cell); - + // Move engine var dist = (Math.random() * 10) + 22; // Random distance // TODO: check distance - cell.setBoost(dist*15, angle); + cell.setBoost(dist * 15, angle); }; MotherCell.prototype.onEaten = Virus.prototype.onEaten; // Copies the virus prototype function -MotherCell.prototype.onAdd = function(gameServer) { +MotherCell.prototype.onAdd = function (gameServer) { gameServer.gameMode.nodesMother.push(this); // Temporary }; -MotherCell.prototype.onRemove = function(gameServer) { +MotherCell.prototype.onRemove = function (gameServer) { var index = gameServer.gameMode.nodesMother.indexOf(this); if (index != -1) { gameServer.gameMode.nodesMother.splice(index, 1); diff --git a/src/gamemodes/TeamZ.js b/src/gamemodes/TeamZ.js index 1b7befaf3..7da98f48a 100644 --- a/src/gamemodes/TeamZ.js +++ b/src/gamemodes/TeamZ.js @@ -1,4 +1,4 @@ -// TODO: fix this game mode has outdated code and probably will not works +// TODO: fix this game mode has outdated code and probably will not works var Mode = require('./Mode.js'); var Cell = require('../entity/Cell.js'); var Entity = require('../entity'); @@ -32,12 +32,12 @@ var localLB = []; function TeamZ() { Mode.apply(this, Array.prototype.slice.call(arguments)); - + this.ID = 13; this.name = 'Zombie Team'; this.packetLB = 48; this.haveTeams = true; - + // configurations: this.minPlayer = 2; // game is auto started if there are at least 2 players this.gameDuration = 18000; // ticks, 1 tick = 50 ms (20 ticks = 1 s) @@ -52,23 +52,23 @@ function TeamZ() { g: 0x30, b: 0xff }; - + this.colorFactorStep = 5; this.colorLower = 50; // Min 0 this.colorUpper = 225; // Max 255 this.maxBrain = -1; // set this param to any negative number to keep the number of brains not exceed number of humans this.maxHero = 4; // set this param to any negative number to keep the number of heroes not exceed number of zombies - + // game mode data: this.state = GameState.WF_PLAYERS; this.winTeam = -1; this.gameTimer = 0; this.zombies = []; // the clients of zombie players this.humans = []; // the clients of human players - + this.heroes = []; this.brains = []; - + this.spawnHeroTimer = 0; this.spawnBrainTimer = 0; } @@ -78,12 +78,12 @@ TeamZ.prototype = new Mode(); // Gamemode Specific Functions -TeamZ.prototype.createZColorFactor = function(client) { +TeamZ.prototype.createZColorFactor = function (client) { client.zColorFactor = (Math.random() * (this.colorUpper - this.colorLower + 1)) >> 0 + this.colorLower; client.zColorIncr = true; // color will be increased if TRUE - otherwise it will be decreased. }; -TeamZ.prototype.nextZColorFactor = function(client) { +TeamZ.prototype.nextZColorFactor = function (client) { if (client.zColorIncr == true) { if (client.zColorFactor + this.colorFactorStep >= this.colorUpper) { client.zColorFactor = this.colorUpper; @@ -101,7 +101,7 @@ TeamZ.prototype.nextZColorFactor = function(client) { } }; -TeamZ.prototype.updateZColor = function(client, mask) { +TeamZ.prototype.updateZColor = function (client, mask) { var color = { r: (mask & 0x4) > 0 ? client.zColorFactor : 7, g: (mask & 0x2) > 0 ? client.zColorFactor : 7, @@ -114,19 +114,19 @@ TeamZ.prototype.updateZColor = function(client, mask) { } }; -TeamZ.prototype.isCrazy = function(client) { - return (typeof(client.crazyTimer) != 'undefined' && client.crazyTimer > 0 && client.team > 0); +TeamZ.prototype.isCrazy = function (client) { + return (typeof (client.crazyTimer) != 'undefined' && client.crazyTimer > 0 && client.team > 0); }; -TeamZ.prototype.hasEatenHero = function(client) { - return (typeof(client.eatenHeroTimer) != 'undefined' && client.eatenHeroTimer > 0); +TeamZ.prototype.hasEatenHero = function (client) { + return (typeof (client.eatenHeroTimer) != 'undefined' && client.eatenHeroTimer > 0); }; -TeamZ.prototype.hasEatenBrain = function(client) { - return (typeof(client.eatenBrainTimer) != 'undefined' && client.eatenBrainTimer > 0); +TeamZ.prototype.hasEatenBrain = function (client) { + return (typeof (client.eatenBrainTimer) != 'undefined' && client.eatenBrainTimer > 0); }; -TeamZ.prototype.spawnDrug = function(gameServer, cell) { // spawn HERO or BRAIN +TeamZ.prototype.spawnDrug = function (gameServer, cell) { // spawn HERO or BRAIN var max = 0; var proceedNext = false; if (cell.getType() == CellType.HERO) { @@ -138,19 +138,19 @@ TeamZ.prototype.spawnDrug = function(gameServer, cell) { // spawn HERO or BRAIN } if (proceedNext) { var pos = gameServer.getRandomPosition(); - + // Check for players - var size = cell.getSize(); - var bound = { - minx: pos.x - size, - miny: pos.y - size, - maxx: pos.x + size, - maxy: pos.y + size - }; - if (gameServer.quadTree.any(bound, function (item) { return item.cell.cellType == 0; })) { - // FAILED because of collision - return false; - } + var size = cell.getSize(); + var bound = { + minx: pos.x - size, + miny: pos.y - size, + maxx: pos.x + size, + maxy: pos.y + size + }; + if (gameServer.quadTree.any(bound, function (item) { return item.cell.cellType == 0; })) { + // FAILED because of collision + return false; + } cell.setPosition(pos); gameServer.addNode(cell); return true; // SUCCESS with spawn @@ -159,31 +159,31 @@ TeamZ.prototype.spawnDrug = function(gameServer, cell) { // spawn HERO or BRAIN }; // Call to change a human client to a zombie -TeamZ.prototype.turnToZombie = function(client) { +TeamZ.prototype.turnToZombie = function (client) { client.team = 0; // team Z this.createZColorFactor(client); this.updateZColor(client, 0x7); // Gray - + // remove from human list var index = this.humans.indexOf(client); if (index >= 0) { this.humans.splice(index, 1); } - + // add to zombie list this.zombies.push(client); }; -TeamZ.prototype.boostSpeedCell = function(cell) { +TeamZ.prototype.boostSpeedCell = function (cell) { if (typeof cell.originalSpeed == 'undefined' || cell.originalSpeed == null) { cell.originalSpeed = cell.getSpeed; - cell.getSpeed = function() { + cell.getSpeed = function () { return 2 * this.originalSpeed(); }; } }; -TeamZ.prototype.boostSpeed = function(client) { +TeamZ.prototype.boostSpeed = function (client) { for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; if (typeof cell == 'undefined') @@ -192,14 +192,14 @@ TeamZ.prototype.boostSpeed = function(client) { } }; -TeamZ.prototype.resetSpeedCell = function(cell) { +TeamZ.prototype.resetSpeedCell = function (cell) { if (typeof cell.originalSpeed != 'undefined' && cell.originalSpeed != null) { cell.getSpeed = cell.originalSpeed; cell.originalSpeed = null; } }; -TeamZ.prototype.resetSpeed = function(client) { +TeamZ.prototype.resetSpeed = function (client) { for (var i = 0; i < client.cells.length; i++) { var cell = client.cells[i]; if (typeof cell == 'undefined') @@ -208,7 +208,7 @@ TeamZ.prototype.resetSpeed = function(client) { } }; -TeamZ.prototype.startGame = function(gameServer) { +TeamZ.prototype.startGame = function (gameServer) { for (var i = 0; i < this.humans.length; i++) { var client = this.humans[i]; client.team = client.pID; @@ -225,17 +225,17 @@ TeamZ.prototype.startGame = function(gameServer) { } } } - + // Select random human to be the zombie var zombie = this.humans[(Math.random() * this.humans.length) >> 0]; this.turnToZombie(zombie); - + this.winTeam = -1; this.state = GameState.IN_PROGRESS; this.gameTimer = this.gameDuration; }; -TeamZ.prototype.endGame = function(gameServer) { +TeamZ.prototype.endGame = function (gameServer) { // reset game for (var i = 0; i < this.zombies.length; i++) { var client = this.zombies[i]; @@ -248,7 +248,7 @@ TeamZ.prototype.endGame = function(gameServer) { this.spawnHeroTimer = 0; this.spawnBrainTimer = 0; localLB = []; // reset leader board - + for (var i = 0; i < this.humans.length; i++) { var client = this.humans[i]; client.color = this.defaultColor; @@ -258,16 +258,16 @@ TeamZ.prototype.endGame = function(gameServer) { cell.setColor(this.defaultColor); } } - + this.state = GameState.WF_PLAYERS; this.gameTimer = 0; }; -TeamZ.prototype.leaderboardAddSort = function(player, leaderboard) { +TeamZ.prototype.leaderboardAddSort = function (player, leaderboard) { // Adds the player and sorts the leaderboard var len = leaderboard.length - 1; var loop = true; - + while ((len >= 0) && (loop)) { // Start from the bottom of the leaderboard if (player.getScore() <= leaderboard[len].getScore()) { @@ -284,10 +284,10 @@ TeamZ.prototype.leaderboardAddSort = function(player, leaderboard) { // Override -TeamZ.prototype.onServerInit = function(gameServer) { +TeamZ.prototype.onServerInit = function (gameServer) { // Called when the server starts gameServer.run = true; - + // Overwrite some server functions: GameServer = require('../GameServer.js'); GS_getRandomColor = GameServer.prototype.getRandomColor; // backup @@ -295,12 +295,12 @@ TeamZ.prototype.onServerInit = function(gameServer) { GS_getCellsInRange = GameServer.prototype.getCellsInRange; GS_splitCells = GameServer.prototype.splitCells; GS_newCellVirused = GameServer.prototype.newCellVirused; - + //OVERWRITE GLOBAL FUNCTIONs to adapt Zombie Team mode - - GameServer.prototype.getRandomColor = function() { + + GameServer.prototype.getRandomColor = function () { var colorRGB = [0xFF, 0x07, (Math.random() * 256) >> 0]; - colorRGB.sort(function() { + colorRGB.sort(function () { return 0.5 - Math.random(); }); return { @@ -309,11 +309,11 @@ TeamZ.prototype.onServerInit = function(gameServer) { g: colorRGB[2] }; }; - - GameServer.prototype.getNearestVirus = function(cell) { + + GameServer.prototype.getNearestVirus = function (cell) { // More like getNearbyVirus var virus = null; - + // loop through all heroes for (var i = 0; i < this.gameMode.heroes.length; i++) { var check = this.gameMode.heroes[i]; @@ -328,7 +328,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { } if (virus != null) return virus; - + // loop through all brains for (var i = 0; i < this.gameMode.brains.length; i++) { var check = this.gameMode.brains[i]; @@ -341,54 +341,54 @@ TeamZ.prototype.onServerInit = function(gameServer) { virus = check; break; } - + if (virus != null) return virus; - + // Call base: // Loop through all viruses on the map. There is probably a more efficient way of doing this but whatever var len = this.nodesVirus.length; for (var i = 0; i < len; i++) { var check = this.nodesVirus[i]; - + if (typeof check === 'undefined') { continue; } - + if (this.checkCellCollision(cell, check) == null) { continue; } - + // Add to list of cells nearby virus = check; break; // stop checking when a virus found } return virus; }; - + // this is almost same to the legacy function - GameServer.prototype.getCellsInRange = function(cell) { + GameServer.prototype.getCellsInRange = function (cell) { var list = new Array(); - + if (this.gameMode.state != GameState.IN_PROGRESS) return list; - + var squareR = cell.getSizeSquared(); // Get cell squared radius - + // Loop through all cells that are visible to the cell. There is probably a more efficient way of doing this but whatever var len = cell.owner.visibleNodes.length; for (var i = 0; i < len; i++) { var check = cell.owner.visibleNodes[i]; - + if (typeof check === 'undefined') { continue; } - + // if something already collided with this cell, don't check for other collisions if (check.isRemoved) { continue; } - + // HERO and BRAIN checking if (cell.owner.getTeam() == 0) { // Z team @@ -399,49 +399,49 @@ TeamZ.prototype.onServerInit = function(gameServer) { if (check.getType() == CellType.BRAIN) continue; } - + // Can't eat itself if (cell.nodeId == check.nodeId) { continue; } - + // Can't eat cells that have collision turned off if ((cell.owner == check.owner) && (cell.ignoreCollision)) { continue; } - + // AABB Collision - if (gameServer.checkCellCollision(cell, check)==null) { + if (gameServer.checkCellCollision(cell, check) == null) { continue; } - + // Cell type check - Cell must be bigger than this number times the mass of the cell being eaten var multiplier = 1.25; - + switch (check.getType()) { - case 1: // Food cell + case 1:// Food cell list.push(check); check.isRemoved = true; // skip future collision checks for this food continue; - case 2: // Virus + case 2:// Virus multiplier = 1.33; break; - case 0: // Players + case 0:// Players // Can't eat self if it's not time to recombine yet if (check.owner == cell.owner) { if (!cell.canRemrege() || !check.canRemerge()) { continue; } - + multiplier = 1.00; } - + // Can't eat team members if (this.gameMode.haveTeams) { if (!check.owner) { // Error check continue; } - + if ((check.owner != cell.owner) && (check.owner.getTeam() == cell.owner.getTeam())) { continue; } @@ -450,55 +450,55 @@ TeamZ.prototype.onServerInit = function(gameServer) { default: break; } - + // Make sure the cell is big enough to be eaten. if ((check.getMass() * multiplier) > cell.getMass()) { continue; } - + // Eating range var xs = Math.pow(check.position.x - cell.position.x, 2); var ys = Math.pow(check.position.y - cell.position.y, 2); var dist = Math.sqrt(xs + ys); - + var eatingRange = cell.getSize() - check.getSize() / Math.PI; // Eating range = radius of eating cell + 40% of the radius of the cell being eaten if (dist > eatingRange) { // Not in eating range continue; } - + // Add to list of cells nearby list.push(check); - + // Something is about to eat this cell; no need to check for other collisions with it check.isRemoved = true; } return list; }; - + // this is almost same to the legacy function - GameServer.prototype.splitCells = function(client) { + GameServer.prototype.splitCells = function (client) { var len = client.cells.length; for (var i = 0; i < len; i++) { if (client.cells.length >= this.config.playerMaxCells) { // Player cell limit continue; } - + var cell = client.cells[i]; if (!cell) { continue; } - + if (cell.getSize() < this.config.playerMinSplitSize) { continue; } - + // Get angle var deltaY = client.mouse.y - cell.position.y; var deltaX = client.mouse.x - cell.position.x; var angle = Math.atan2(deltaX, deltaY); - + // Get starting position var size = cell.getSize() / 2; var startPos = { @@ -511,9 +511,9 @@ TeamZ.prototype.onServerInit = function(gameServer) { cell.setSize(newSize); // Create cell var split = new Entity.PlayerCell(this, client, startPos, newSize); - // TODO: check distance + // TODO: check distance split.setBoost(splitSpeed * 32, angle); - + // boost speed if zombie eats brain if (this.gameMode.hasEatenBrain(client) || this.gameMode.isCrazy(client)) { this.gameMode.boostSpeedCell(split); @@ -527,24 +527,24 @@ TeamZ.prototype.onServerInit = function(gameServer) { this.addNode(split); } }; - + // this function is almost same to the legacy - GameServer.prototype.newCellVirused = function(client, parent, angle, mass, speed) { + GameServer.prototype.newCellVirused = function (client, parent, angle, mass, speed) { // Starting position var startPos = { x: parent.position.x, y: parent.position.y }; - + var size = Math.sqrt(mass); // Create cell newCell = new Entity.PlayerCell(this, client, startPos, size); - // TODO: check distance + // TODO: check distance newCell.setBoost(speed * 10, angle); //newCell.setAngle(angle); //newCell.setMoveEngineData(speed, 10); newCell.ignoreCollision = true; // Turn off collision - + // boost speed if zombie eats brain if (this.gameMode.hasEatenBrain(client) || this.gameMode.isCrazy(client)) { this.gameMode.boostSpeedCell(newCell); @@ -555,23 +555,23 @@ TeamZ.prototype.onServerInit = function(gameServer) { //newCell.recombineTicks = 1; // TODO: fix? } - + // Add to moving cells list this.addNode(newCell); }; - - Virus.prototype.onEaten = function(consumer) { + + Virus.prototype.onEaten = function (consumer) { var client = consumer.owner; var maxSplits = Math.floor(consumer.getMass() / 16) - 1; // Maximum amount of splits var numSplits = this.gameServer.config.playerMaxCells - client.cells.length; // Get number of splits numSplits = Math.min(numSplits, maxSplits); var splitMass = Math.min(consumer.getMass() / (numSplits + 1), 36); // Maximum size of new splits - + // Cell cannot split any further if (numSplits <= 0) { return; } - + // Big cells will split into cells larger than 36 mass (1/4 of their mass) var bigSplits = 0; var endMass = consumer.getMass() - (numSplits * splitMass); @@ -587,34 +587,34 @@ TeamZ.prototype.onServerInit = function(gameServer) { bigSplits++; numSplits--; } - + // Splitting var angle = 0; // Starting angle for (var k = 0; k < numSplits; k++) { angle += 6 / numSplits; // Get directions of splitting cells this.gameServer.newCellVirused(client, consumer, angle, splitMass, 150); - consumer.setSize(Math.sqrt(consumer.getSize() * consumer.getSize() - splitMass*splitMass)); + consumer.setSize(Math.sqrt(consumer.getSize() * consumer.getSize() - splitMass * splitMass)); } - + for (var k = 0; k < bigSplits; k++) { angle = Math.random() * 6.28; // Random directions splitMass = consumer.getMass() / 4; this.gameServer.newCellVirused(client, consumer, angle, splitMass, 20); - consumer.setSize(Math.sqrt(consumer.getSize() * consumer.getSize() - splitMass * splitMass)); + consumer.setSize(Math.sqrt(consumer.getSize() * consumer.getSize() - splitMass * splitMass)); } - + if (this.gameServer.gameMode.hasEatenHero(client)) { //consumer.recombineTicks = 0; // TODO: fix? } }; - + // Handle "gamemode" command: for (var i = 0; i < this.gameServer.clients.length; i++) { var client = this.gameServer.clients[i].playerTracker; if (!client) continue; - + if (client.cells.length > 0) { client.eatenBrainTimer = 0; client.eatenHeroTimer = 0; @@ -630,7 +630,7 @@ TeamZ.prototype.onServerInit = function(gameServer) { } }; -TeamZ.prototype.onChange = function(gameServer) { +TeamZ.prototype.onChange = function (gameServer) { // Called when someone changes the gamemode via console commands // remove Brain and Hero for (var i = 0; this.brains.length; i++) { @@ -641,7 +641,7 @@ TeamZ.prototype.onChange = function(gameServer) { var node = this.heroes[i]; gameServer.removeNode(node); } - + // discard all boost: for (var i = 0; i < this.humans.length; i++) { var client = this.humans[i]; @@ -655,7 +655,7 @@ TeamZ.prototype.onChange = function(gameServer) { this.resetSpeed(client); } } - + // revert to default: GameServer.prototype.getRandomColor = GS_getRandomColor; GameServer.prototype.getNearestVirus = GS_getNearestVirus; @@ -665,9 +665,9 @@ TeamZ.prototype.onChange = function(gameServer) { Virus.prototype.onEaten = Virus_onEaten; }; -TeamZ.prototype.onTick = function(gameServer) { +TeamZ.prototype.onTick = function (gameServer) { // Called on every game tick - + switch (this.state) { case GameState.WF_PLAYERS: if (this.humans.length >= this.minPlayer) { @@ -701,22 +701,22 @@ TeamZ.prototype.onTick = function(gameServer) { this.winTeam = 1; } } - + if (this.winTeam >= 0) { this.endGame(gameServer); } - + break; } - + // change color of zombies for (var i = 0; i < this.zombies.length; i++) { var client = this.zombies[i]; this.nextZColorFactor(client); - + if (this.hasEatenBrain(client)) { client.eatenBrainTimer--; - + if (client.eatenBrainTimer > 0) { this.updateZColor(client, 0x5); // Pink continue; @@ -725,10 +725,10 @@ TeamZ.prototype.onTick = function(gameServer) { this.resetSpeed(client); } } - + this.updateZColor(client, 0x7); // Gray } - + for (var i = 0; i < this.humans.length; i++) { var client = this.humans[i]; if (this.isCrazy(client)) { @@ -738,12 +738,12 @@ TeamZ.prototype.onTick = function(gameServer) { var cell = client.cells[j]; // reset speed: this.resetSpeedCell(cell); - + // reset color: if (client.cured == true) cell.setColor(client.getColor()); } - + if (client.cured == true) { client.cured = false; // reset } else { @@ -755,7 +755,7 @@ TeamZ.prototype.onTick = function(gameServer) { client.colorToggle++; if (client.colorToggle % 10 == 0) { var blinkColor = null; - + if (client.colorToggle == 20) { blinkColor = client.getColor(); client.colorToggle = 0; @@ -774,7 +774,7 @@ TeamZ.prototype.onTick = function(gameServer) { }; // Gray } } - + for (var j = 0; j < client.cells.length; j++) { var cell = client.cells[j]; cell.setColor(blinkColor); @@ -802,32 +802,32 @@ TeamZ.prototype.onTick = function(gameServer) { } else { color = client.getColor(); // reset } - + for (var j = 0; j < client.cells.length; j++) { var cell = client.cells[j]; cell.setColor(color); } } } - + // check timer to spawn Hero: this.spawnHeroTimer++; if (this.spawnHeroTimer >= this.spawnHeroInterval) { this.spawnHeroTimer = 0; var cell = new Hero(gameServer.getNextNodeId(), null); - while (!this.spawnDrug(gameServer, cell)); // collision detect algorithm needs enhancement + while (!this.spawnDrug(gameServer, cell)) ; // collision detect algorithm needs enhancement } - + // check timer to spawn Brain: this.spawnBrainTimer++; if (this.spawnBrainTimer >= this.spawnBrainInterval) { this.spawnBrainTimer = 0; var cell = new Brain(gameServer.getNextNodeId(), null); - while (!this.spawnDrug(gameServer, cell)); // collision detect algorithm needs enhancement + while (!this.spawnDrug(gameServer, cell)) ; // collision detect algorithm needs enhancement } }; -TeamZ.prototype.onCellAdd = function(cell) { +TeamZ.prototype.onCellAdd = function (cell) { // Called when a player cell is added var client = cell.owner; if (client.cells.length == 1) { // first cell @@ -837,7 +837,7 @@ TeamZ.prototype.onCellAdd = function(cell) { client.eatenHeroTimer = 0; client.crazyTimer = 0; this.humans.push(client); - + if (this.state == GameState.IN_PROGRESS) { this.turnToZombie(client); } else { @@ -848,7 +848,7 @@ TeamZ.prototype.onCellAdd = function(cell) { } }; -TeamZ.prototype.onCellRemove = function(cell) { +TeamZ.prototype.onCellRemove = function (cell) { // Called when a player cell is removed var client = cell.owner; if (client.cells.length == 0) { // last cell @@ -871,20 +871,20 @@ TeamZ.prototype.onCellMove = function (x1, y1, cell) { // Called when a player cell is moved var team = cell.owner.getTeam(); var r = cell.getSize(); - + // Find team for (var i = 0; i < cell.owner.visibleNodes.length; i++) { // Only collide with player cells var check = cell.owner.visibleNodes[i]; - + if ((check.getType() != 0) || (cell.owner == check.owner)) { continue; } - + if ((this.hasEatenHero(check.owner)) || (this.hasEatenHero(cell.owner))) { continue; } - + // Collision with zombies if (check.owner.getTeam() == 0 || team == 0) { // Check if in collision range @@ -893,10 +893,10 @@ TeamZ.prototype.onCellMove = function (x1, y1, cell) { // Skip continue; } - + // First collision check passed... now more precise checking dist = cell.getDist(cell.position.x, cell.position.y, check.position.x, check.position.y); - + // Calculations if (dist < collisionDist) { // Collided var crazyClient = null; @@ -905,33 +905,33 @@ TeamZ.prototype.onCellMove = function (x1, y1, cell) { } else if (team == 0 && check.owner.getTeam() != 0) { crazyClient = check.owner; } - + if (crazyClient != null && !this.isCrazy(crazyClient)) { crazyClient.crazyTimer = this.crazyDuration; crazyClient.colorToggle = 0; this.boostSpeed(crazyClient); } - + // The moving cell pushes the colliding cell var newDeltaY = check.position.y - y1; var newDeltaX = check.position.x - x1; var newAngle = Math.atan2(newDeltaX, newDeltaY); - + var move = collisionDist - dist; - - check.setPosition({ + + check.setPosition({ x: check.position.x + (move * Math.sin(newAngle)) >> 0, - y: check.position.y + (move * Math.cos(newAngle)) >> 0 + y: check.position.y + (move * Math.cos(newAngle)) >> 0 }); } } } }; -TeamZ.prototype.updateLB = function(gameServer) { +TeamZ.prototype.updateLB = function (gameServer) { gameServer.leaderboardType = this.packetLB; var lb = gameServer.leaderboard; - + if (this.winTeam == 0) { lb.push('ZOMBIE WINS'); lb.push('_______________'); @@ -939,7 +939,7 @@ TeamZ.prototype.updateLB = function(gameServer) { lb.push('HUMAN WINS'); lb.push('_______________'); } - + switch (this.state) { case GameState.WF_PLAYERS: lb.push('WAITING FOR'); @@ -959,20 +959,20 @@ TeamZ.prototype.updateLB = function(gameServer) { lb.push('HUMAN: ' + this.humans.length); lb.push('ZOMBIE: ' + this.zombies.length); lb.push('_______________'); - + // Loop through all clients localLB = []; for (var i = 0; i < gameServer.clients.length; i++) { if (typeof gameServer.clients[i] == 'undefined' || gameServer.clients[i].playerTracker.team == 0) { continue; } - + var player = gameServer.clients[i].playerTracker; if (player.cells.length <= 0) { continue; } var playerScore = player.getScore(); - + if (localLB.length == 0) { // Initial player localLB.push(player); @@ -990,7 +990,7 @@ TeamZ.prototype.updateLB = function(gameServer) { for (var i = 0; i < localLB.length && lb.length < 10; i++) { lb.push(localLB[i].getName()); } - + break; default: lb.push('ERROR STATE'); @@ -1005,7 +1005,7 @@ TeamZ.prototype.updateLB = function(gameServer) { // HERO POISON CELL: function Hero() { Cell.apply(this, Array.prototype.slice.call(arguments)); - + this.cellType = CellType.HERO; //this.isSpiked = true; this.setColor({ r: 255, g: 255, b: 7 }); @@ -1014,17 +1014,17 @@ function Hero() { Hero.prototype = new Cell(); -Hero.prototype.getName = function() { +Hero.prototype.getName = function () { return 'HERO'; }; Hero.prototype.calcMove = null; -Hero.prototype.onAdd = function(gameServer) { +Hero.prototype.onAdd = function (gameServer) { gameServer.gameMode.heroes.push(this); }; -Hero.prototype.onRemove = function(gameServer) { +Hero.prototype.onRemove = function (gameServer) { var index = gameServer.gameMode.heroes.indexOf(this); if (index != -1) { gameServer.gameMode.heroes.splice(index, 1); @@ -1033,27 +1033,27 @@ Hero.prototype.onRemove = function(gameServer) { } }; -Hero.prototype.feed = function(feeder, gameServer) { +Hero.prototype.feed = function (feeder, gameServer) { gameServer.removeNode(feeder); - - // TODO: check distance + + // TODO: check distance this.setBoost(60 * 5, feeder.getAngle()); //this.setAngle(feeder.getAngle()); //this.moveEngineTicks = 5; // Amount of times to loop the movement function //this.moveEngineSpeed = 60; - + var index = gameServer.movingNodes.indexOf(this); if (index == -1) { gameServer.movingNodes.push(this); } }; -Hero.prototype.onEaten = function(consumer) { +Hero.prototype.onEaten = function (consumer) { // Called when the cell is consumed var client = consumer.owner; - - // delicious - + + // delicious + if (this.gameServer.gameMode.isCrazy(client)) { // Neutralize the Zombie effect client.cured = true; @@ -1076,7 +1076,7 @@ Hero.prototype.onEaten = function(consumer) { // BRAIN CELL: function Brain() { Cell.apply(this, Array.prototype.slice.call(arguments)); - + this.cellType = CellType.BRAIN; //this.isSpiked = true; this.setColor({ r: 255, g: 7, b: 255 }); @@ -1085,17 +1085,17 @@ function Brain() { Brain.prototype = new Cell(); -Brain.prototype.getName = function() { +Brain.prototype.getName = function () { return 'BRAIN'; }; Brain.prototype.calcMove = null; -Brain.prototype.onAdd = function(gameServer) { +Brain.prototype.onAdd = function (gameServer) { gameServer.gameMode.brains.push(this); }; -Brain.prototype.onRemove = function(gameServer) { +Brain.prototype.onRemove = function (gameServer) { var index = gameServer.gameMode.brains.indexOf(this); if (index != -1) { gameServer.gameMode.brains.splice(index, 1); @@ -1104,29 +1104,29 @@ Brain.prototype.onRemove = function(gameServer) { } }; -Brain.prototype.feed = function(feeder, gameServer) { +Brain.prototype.feed = function (feeder, gameServer) { gameServer.removeNode(feeder); - - // TODO: check distance + + // TODO: check distance this.setBoost(60 * 5, feeder.getAngle()); //this.setAngle(feeder.getAngle()); //this.moveEngineTicks = 5; // Amount of times to loop the movement function //this.moveEngineSpeed = 60; - + var index = gameServer.movingNodes.indexOf(this); if (index == -1) { gameServer.movingNodes.push(this); } }; -Brain.prototype.onEaten = function(consumer) { +Brain.prototype.onEaten = function (consumer) { // Called when the cell is consumed var client = consumer.owner; - // yummy! - + // yummy! + client.eatenBrainTimer = this.gameServer.gameMode.brainEffectDuration; - + // Boost speed this.gameServer.gameMode.boostSpeed(client); }; diff --git a/src/gamemodes/Teams.js b/src/gamemodes/Teams.js index eb71c74f8..816e4fa17 100644 --- a/src/gamemodes/Teams.js +++ b/src/gamemodes/Teams.js @@ -1,30 +1,30 @@ -var Mode = require('./Mode'); +var Mode = require('./Mode'); function Teams() { Mode.apply(this, Array.prototype.slice.call(arguments)); - + this.ID = 1; this.name = "Teams"; this.decayMod = 1.5; this.packetLB = 50; this.haveTeams = true; this.colorFuzziness = 32; - + // Special this.teamAmount = 3; // Amount of teams. Having more than 3 teams will cause the leaderboard to work incorrectly (client issue). this.colors = [{ - 'r': 223, - 'g': 0, - 'b': 0 - }, { - 'r': 0, - 'g': 223, - 'b': 0 - }, { - 'r': 0, - 'g': 0, - 'b': 223 - }, ]; // Make sure you add extra colors here if you wish to increase the team amount [Default colors are: Red, Green, Blue] + 'r': 223, + 'g': 0, + 'b': 0 + }, { + 'r': 0, + 'g': 223, + 'b': 0 + }, { + 'r': 0, + 'g': 0, + 'b': 223 + },]; // Make sure you add extra colors here if you wish to increase the team amount [Default colors are: Red, Green, Blue] this.nodes = []; // Teams } @@ -33,12 +33,12 @@ Teams.prototype = new Mode(); //Gamemode Specific Functions -Teams.prototype.fuzzColorComponent = function(component) { +Teams.prototype.fuzzColorComponent = function (component) { component += Math.random() * this.colorFuzziness >> 0; return component; }; -Teams.prototype.getTeamColor = function(team) { +Teams.prototype.getTeamColor = function (team) { var color = this.colors[team]; return { r: this.fuzzColorComponent(color.r), @@ -49,19 +49,19 @@ Teams.prototype.getTeamColor = function(team) { // Override -Teams.prototype.onPlayerSpawn = function(gameServer, player) { +Teams.prototype.onPlayerSpawn = function (gameServer, player) { // Random color based on team player.setColor(this.getTeamColor(player.team)); // Spawn player gameServer.spawnPlayer(player); }; -Teams.prototype.onServerInit = function(gameServer) { +Teams.prototype.onServerInit = function (gameServer) { // Set up teams for (var i = 0; i < this.teamAmount; i++) { this.nodes[i] = []; } - + // migrate current players to team mode for (var i = 0; i < gameServer.clients.length; i++) { var client = gameServer.clients[i].playerTracker; @@ -75,17 +75,17 @@ Teams.prototype.onServerInit = function(gameServer) { } }; -Teams.prototype.onPlayerInit = function(player) { +Teams.prototype.onPlayerInit = function (player) { // Get random team player.team = Math.floor(Math.random() * this.teamAmount); }; -Teams.prototype.onCellAdd = function(cell) { +Teams.prototype.onCellAdd = function (cell) { // Add to team list this.nodes[cell.owner.getTeam()].push(cell); }; -Teams.prototype.onCellRemove = function(cell) { +Teams.prototype.onCellRemove = function (cell) { // Remove from team list var index = this.nodes[cell.owner.getTeam()].indexOf(cell); if (index != -1) { @@ -93,19 +93,19 @@ Teams.prototype.onCellRemove = function(cell) { } }; -Teams.prototype.onCellMove = function(cell, gameServer) { +Teams.prototype.onCellMove = function (cell, gameServer) { var team = cell.owner.getTeam(); var r = cell.getSize(); - + // Find team for (var i = 0; i < cell.owner.visibleNodes.length; i++) { // Only collide with player cells var check = cell.owner.visibleNodes[i]; - + if ((check.getType() != 0) || (cell.owner == check.owner)) { continue; } - + // Collision with teammates if (check.owner.getTeam() == team) { @@ -118,7 +118,7 @@ Teams.prototype.onCellMove = function(cell, gameServer) { } }; -Teams.prototype.updateLB = function(gameServer) { +Teams.prototype.updateLB = function (gameServer) { gameServer.leaderboardType = this.packetLB; var total = 0; var teamMass = []; @@ -126,15 +126,15 @@ Teams.prototype.updateLB = function(gameServer) { for (var i = 0; i < this.teamAmount; i++) { // Set starting mass teamMass[i] = 0; - + // Loop through cells for (var j = 0; j < this.nodes[i].length; j++) { var cell = this.nodes[i][j]; - + if (!cell) { continue; } - + teamMass[i] += cell.getMass(); total += cell.getMass(); } diff --git a/src/gamemodes/Tournament.js b/src/gamemodes/Tournament.js index 1b622dbfb..8dd49c01c 100644 --- a/src/gamemodes/Tournament.js +++ b/src/gamemodes/Tournament.js @@ -1,248 +1,248 @@ -var Mode = require('./Mode'); - -function Tournament() { - Mode.apply(this, Array.prototype.slice.call(arguments)); - - this.ID = 10; - this.name = "Tournament"; - this.packetLB = 48; - - // Config (1 tick = 1000 ms) - this.prepTime = 5; // Amount of ticks after the server fills up to wait until starting the game - this.endTime = 15; // Amount of ticks after someone wins to restart the game - this.autoFill = false; - this.autoFillPlayers = 1; - this.dcTime = 0; - - // Gamemode Specific Variables - this.gamePhase = 0; // 0 = Waiting for players, 1 = Prepare to start, 2 = Game in progress, 3 = End - this.contenders = []; - this.maxContenders = 12; - - this.winner; - this.timer; - this.timeLimit = 3600; // in seconds -} - -module.exports = Tournament; -Tournament.prototype = new Mode(); - -// Gamemode Specific Functions - -Tournament.prototype.startGamePrep = function(gameServer) { - this.gamePhase = 1; - this.timer = this.prepTime; // 10 seconds -}; - -Tournament.prototype.startGame = function(gameServer) { - gameServer.run = true; - this.gamePhase = 2; - this.getSpectate(); // Gets a random person to spectate - gameServer.config.playerDisconnectTime = this.dcTime; // Reset config -}; - -Tournament.prototype.endGame = function(gameServer) { - this.winner = this.contenders[0]; - this.gamePhase = 3; - this.timer = this.endTime; // 30 Seconds -}; - -Tournament.prototype.endGameTimeout = function(gameServer) { - gameServer.run = false; - this.gamePhase = 4; - this.timer = this.endTime; // 30 Seconds -}; - -Tournament.prototype.fillBots = function(gameServer) { - // Fills the server with bots if there arent enough players - var fill = this.maxContenders - this.contenders.length; - for (var i = 0; i < fill; i++) { - gameServer.bots.addBot(); - } -}; - -Tournament.prototype.getSpectate = function() { - // Finds a random person to spectate - var index = Math.floor(Math.random() * this.contenders.length); - this.rankOne = this.contenders[index]; -}; - -Tournament.prototype.prepare = function(gameServer) { - // Remove all cells - var len = gameServer.nodes.length; - for (var i = 0; i < len; i++) { - var node = gameServer.nodes[0]; - - if (!node) { - continue; - } - - gameServer.removeNode(node); - } - - gameServer.bots.loadNames(); - - // Pauses the server - gameServer.run = false; - this.gamePhase = 0; - - // Get config values - if (gameServer.config.tourneyAutoFill > 0) { - this.timer = gameServer.config.tourneyAutoFill; - this.autoFill = true; - this.autoFillPlayers = gameServer.config.tourneyAutoFillPlayers; - } - // Handles disconnections - this.dcTime = gameServer.config.playerDisconnectTime; - gameServer.config.playerDisconnectTime = 0; - - this.prepTime = gameServer.config.tourneyPrepTime; - this.endTime = gameServer.config.tourneyEndTime; - this.maxContenders = gameServer.config.tourneyMaxPlayers; - - // Time limit - this.timeLimit = gameServer.config.tourneyTimeLimit * 60; // in seconds -}; - -Tournament.prototype.onPlayerDeath = function(gameServer) { - // Nothing -}; - -Tournament.prototype.formatTime = function(time) { - if (time < 0) { - return "0:00"; - } - // Format - var min = Math.floor(this.timeLimit / 60); - var sec = this.timeLimit % 60; - sec = (sec > 9) ? sec : "0" + sec.toString(); - return min + ":" + sec; -}; - -// Override - -Tournament.prototype.onServerInit = function(gameServer) { - this.prepare(gameServer); -}; - -Tournament.prototype.onPlayerSpawn = function(gameServer, player) { - // Only spawn players if the game hasnt started yet - if ((this.gamePhase == 0) && (this.contenders.length < this.maxContenders)) { - player.setColor(gameServer.getRandomColor()); // Random color - this.contenders.push(player); // Add to contenders list - gameServer.spawnPlayer(player); - - if (this.contenders.length == this.maxContenders) { - // Start the game once there is enough players - this.startGamePrep(gameServer); - } - } -}; - -Tournament.prototype.onCellRemove = function(cell) { - var owner = cell.owner, - human_just_died = false; - - if (owner.cells.length <= 0) { - // Remove from contenders list - var index = this.contenders.indexOf(owner); - if (index != -1) { - if ('_socket' in this.contenders[index].socket) { - human_just_died = true; - } - this.contenders.splice(index, 1); - } - - // Victory conditions - var humans = 0; - for (var i = 0; i < this.contenders.length; i++) { - if ('_socket' in this.contenders[i].socket) { - humans++; - } - } - - // the game is over if: - // 1) there is only 1 player left, OR - // 2) all the humans are dead, OR - // 3) the last-but-one human just died - if ((this.contenders.length == 1 || humans == 0 || (humans == 1 && human_just_died)) && this.gamePhase == 2) { - this.endGame(cell.owner.gameServer); - } else { - // Do stuff - this.onPlayerDeath(cell.owner.gameServer); - } - } -}; - -Tournament.prototype.updateLB = function(gameServer) { +var Mode = require('./Mode'); + +function Tournament() { + Mode.apply(this, Array.prototype.slice.call(arguments)); + + this.ID = 10; + this.name = "Tournament"; + this.packetLB = 48; + + // Config (1 tick = 1000 ms) + this.prepTime = 5; // Amount of ticks after the server fills up to wait until starting the game + this.endTime = 15; // Amount of ticks after someone wins to restart the game + this.autoFill = false; + this.autoFillPlayers = 1; + this.dcTime = 0; + + // Gamemode Specific Variables + this.gamePhase = 0; // 0 = Waiting for players, 1 = Prepare to start, 2 = Game in progress, 3 = End + this.contenders = []; + this.maxContenders = 12; + + this.winner; + this.timer; + this.timeLimit = 3600; // in seconds +} + +module.exports = Tournament; +Tournament.prototype = new Mode(); + +// Gamemode Specific Functions + +Tournament.prototype.startGamePrep = function (gameServer) { + this.gamePhase = 1; + this.timer = this.prepTime; // 10 seconds +}; + +Tournament.prototype.startGame = function (gameServer) { + gameServer.run = true; + this.gamePhase = 2; + this.getSpectate(); // Gets a random person to spectate + gameServer.config.playerDisconnectTime = this.dcTime; // Reset config +}; + +Tournament.prototype.endGame = function (gameServer) { + this.winner = this.contenders[0]; + this.gamePhase = 3; + this.timer = this.endTime; // 30 Seconds +}; + +Tournament.prototype.endGameTimeout = function (gameServer) { + gameServer.run = false; + this.gamePhase = 4; + this.timer = this.endTime; // 30 Seconds +}; + +Tournament.prototype.fillBots = function (gameServer) { + // Fills the server with bots if there arent enough players + var fill = this.maxContenders - this.contenders.length; + for (var i = 0; i < fill; i++) { + gameServer.bots.addBot(); + } +}; + +Tournament.prototype.getSpectate = function () { + // Finds a random person to spectate + var index = Math.floor(Math.random() * this.contenders.length); + this.rankOne = this.contenders[index]; +}; + +Tournament.prototype.prepare = function (gameServer) { + // Remove all cells + var len = gameServer.nodes.length; + for (var i = 0; i < len; i++) { + var node = gameServer.nodes[0]; + + if (!node) { + continue; + } + + gameServer.removeNode(node); + } + + gameServer.bots.loadNames(); + + // Pauses the server + gameServer.run = false; + this.gamePhase = 0; + + // Get config values + if (gameServer.config.tourneyAutoFill > 0) { + this.timer = gameServer.config.tourneyAutoFill; + this.autoFill = true; + this.autoFillPlayers = gameServer.config.tourneyAutoFillPlayers; + } + // Handles disconnections + this.dcTime = gameServer.config.playerDisconnectTime; + gameServer.config.playerDisconnectTime = 0; + + this.prepTime = gameServer.config.tourneyPrepTime; + this.endTime = gameServer.config.tourneyEndTime; + this.maxContenders = gameServer.config.tourneyMaxPlayers; + + // Time limit + this.timeLimit = gameServer.config.tourneyTimeLimit * 60; // in seconds +}; + +Tournament.prototype.onPlayerDeath = function (gameServer) { + // Nothing +}; + +Tournament.prototype.formatTime = function (time) { + if (time < 0) { + return "0:00"; + } + // Format + var min = Math.floor(this.timeLimit / 60); + var sec = this.timeLimit % 60; + sec = (sec > 9) ? sec : "0" + sec.toString(); + return min + ":" + sec; +}; + +// Override + +Tournament.prototype.onServerInit = function (gameServer) { + this.prepare(gameServer); +}; + +Tournament.prototype.onPlayerSpawn = function (gameServer, player) { + // Only spawn players if the game hasnt started yet + if ((this.gamePhase == 0) && (this.contenders.length < this.maxContenders)) { + player.setColor(gameServer.getRandomColor()); // Random color + this.contenders.push(player); // Add to contenders list + gameServer.spawnPlayer(player); + + if (this.contenders.length == this.maxContenders) { + // Start the game once there is enough players + this.startGamePrep(gameServer); + } + } +}; + +Tournament.prototype.onCellRemove = function (cell) { + var owner = cell.owner, + human_just_died = false; + + if (owner.cells.length <= 0) { + // Remove from contenders list + var index = this.contenders.indexOf(owner); + if (index != -1) { + if ('_socket' in this.contenders[index].socket) { + human_just_died = true; + } + this.contenders.splice(index, 1); + } + + // Victory conditions + var humans = 0; + for (var i = 0; i < this.contenders.length; i++) { + if ('_socket' in this.contenders[i].socket) { + humans++; + } + } + + // the game is over if: + // 1) there is only 1 player left, OR + // 2) all the humans are dead, OR + // 3) the last-but-one human just died + if ((this.contenders.length == 1 || humans == 0 || (humans == 1 && human_just_died)) && this.gamePhase == 2) { + this.endGame(cell.owner.gameServer); + } else { + // Do stuff + this.onPlayerDeath(cell.owner.gameServer); + } + } +}; + +Tournament.prototype.updateLB = function (gameServer) { gameServer.leaderboardType = this.packetLB; - var lb = gameServer.leaderboard; - - switch (this.gamePhase) { - case 0: - lb[0] = "Waiting for"; - lb[1] = "players: "; - lb[2] = this.contenders.length + "/" + this.maxContenders; - if (this.autoFill) { - if (this.timer <= 0) { - this.fillBots(gameServer); - } else if (this.contenders.length >= this.autoFillPlayers) { - this.timer--; - } - } - break; - case 1: - lb[0] = "Game starting in"; - lb[1] = this.timer.toString(); - lb[2] = "Good luck!"; - if (this.timer <= 0) { - // Reset the game - this.startGame(gameServer); - } else { - this.timer--; - } - break; - case 2: - lb[0] = "Players Remaining"; - lb[1] = this.contenders.length + "/" + this.maxContenders; - lb[2] = "Time Limit:"; - lb[3] = this.formatTime(this.timeLimit); - if (this.timeLimit < 0) { - // Timed out - this.endGameTimeout(gameServer); - } else { - this.timeLimit--; - } - break; - case 3: - lb[0] = "Congratulations"; - lb[1] = this.winner.getName(); - lb[2] = "for winning!"; - if (this.timer <= 0) { - // Reset the game - this.onServerInit(gameServer); - // Respawn starting food - gameServer.startingFood(); - } else { - lb[3] = "Game restarting in"; - lb[4] = this.timer.toString(); - this.timer--; - } - break; - case 4: - lb[0] = "Time Limit"; - lb[1] = "Reached!"; - if (this.timer <= 0) { - // Reset the game - this.onServerInit(gameServer); - // Respawn starting food - gameServer.startingFood(); - } else { - lb[2] = "Game restarting in"; - lb[3] = this.timer.toString(); - this.timer--; - } - default: - break; - } -}; + var lb = gameServer.leaderboard; + + switch (this.gamePhase) { + case 0: + lb[0] = "Waiting for"; + lb[1] = "players: "; + lb[2] = this.contenders.length + "/" + this.maxContenders; + if (this.autoFill) { + if (this.timer <= 0) { + this.fillBots(gameServer); + } else if (this.contenders.length >= this.autoFillPlayers) { + this.timer--; + } + } + break; + case 1: + lb[0] = "Game starting in"; + lb[1] = this.timer.toString(); + lb[2] = "Good luck!"; + if (this.timer <= 0) { + // Reset the game + this.startGame(gameServer); + } else { + this.timer--; + } + break; + case 2: + lb[0] = "Players Remaining"; + lb[1] = this.contenders.length + "/" + this.maxContenders; + lb[2] = "Time Limit:"; + lb[3] = this.formatTime(this.timeLimit); + if (this.timeLimit < 0) { + // Timed out + this.endGameTimeout(gameServer); + } else { + this.timeLimit--; + } + break; + case 3: + lb[0] = "Congratulations"; + lb[1] = this.winner.getName(); + lb[2] = "for winning!"; + if (this.timer <= 0) { + // Reset the game + this.onServerInit(gameServer); + // Respawn starting food + gameServer.startingFood(); + } else { + lb[3] = "Game restarting in"; + lb[4] = this.timer.toString(); + this.timer--; + } + break; + case 4: + lb[0] = "Time Limit"; + lb[1] = "Reached!"; + if (this.timer <= 0) { + // Reset the game + this.onServerInit(gameServer); + // Respawn starting food + gameServer.startingFood(); + } else { + lb[2] = "Game restarting in"; + lb[3] = this.timer.toString(); + this.timer--; + } + default: + break; + } +}; diff --git a/src/gamemodes/Zombie.js b/src/gamemodes/Zombie.js index af1e8b688..bb6a30dbd 100755 --- a/src/gamemodes/Zombie.js +++ b/src/gamemodes/Zombie.js @@ -1,8 +1,8 @@ -var Mode = require('./Mode'); +var Mode = require('./Mode'); function Zombie() { Mode.apply(this, Array.prototype.slice.call(arguments)); - + this.ID = 12; this.name = "Zombie FFA"; this.haveTeams = true; @@ -20,7 +20,7 @@ Zombie.prototype = new Mode(); // Gamemode Specific Functions -Zombie.prototype.leaderboardAddSort = function(player, leaderboard) { +Zombie.prototype.leaderboardAddSort = function (player, leaderboard) { // Adds the player and sorts the leaderboard var len = leaderboard.length - 1; var loop = true; @@ -38,7 +38,7 @@ Zombie.prototype.leaderboardAddSort = function(player, leaderboard) { } }; -Zombie.prototype.makeZombie = function(player) { +Zombie.prototype.makeZombie = function (player) { // turns a player into a zombie player.team = 0; player.setColor(this.zombieColor); @@ -57,7 +57,7 @@ Zombie.prototype.makeZombie = function(player) { // Override -Zombie.prototype.onPlayerSpawn = function(gameServer, player) { +Zombie.prototype.onPlayerSpawn = function (gameServer, player) { // make player a zombie if there are none if (this.zombies.length == 0) { player.team = 0; @@ -67,12 +67,12 @@ Zombie.prototype.onPlayerSpawn = function(gameServer, player) { player.team = player.pID; player.setColor(gameServer.getRandomColor()); } - + // Spawn player gameServer.spawnPlayer(player); }; -Zombie.prototype.onCellAdd = function(cell) { +Zombie.prototype.onCellAdd = function (cell) { // Add to team list if (cell.owner.getTeam() == 0) { this.zombies.push(cell); @@ -81,7 +81,7 @@ Zombie.prototype.onCellAdd = function(cell) { } }; -Zombie.prototype.onCellRemove = function(cell) { +Zombie.prototype.onCellRemove = function (cell) { // Remove from team list if (cell.owner.getTeam() == 0) { var index = this.zombies.indexOf(cell); @@ -100,16 +100,16 @@ Zombie.prototype.onCellRemove = function(cell) { Zombie.prototype.onCellMove = function (x1, y1, cell) { var team = cell.owner.getTeam(); var r = cell.getSize(); - + // Find team for (var i = 0; i < cell.owner.visibleNodes.length; i++) { // Only collide with player cells var check = cell.owner.visibleNodes[i]; - + if ((check.getType() != 0) || (cell.owner == check.owner)) { continue; } - + // Collision with zombies if (check.owner.getTeam() == team || check.owner.getTeam() == 0 || team == 0) { // Check if in collision range @@ -118,10 +118,10 @@ Zombie.prototype.onCellMove = function (x1, y1, cell) { // Skip continue; } - + // First collision check passed... now more precise checking dist = cell.getDist(cell.position.x, cell.position.y, check.position.x, check.position.y); - + // Calculations if (dist < collisionDist) { // Collided if (check.owner.getTeam() == 0 && team != 0) { @@ -135,7 +135,7 @@ Zombie.prototype.onCellMove = function (x1, y1, cell) { var newDeltaY = check.position.y - y1; var newDeltaX = check.position.x - x1; var newAngle = Math.atan2(newDeltaX, newDeltaY); - + var move = collisionDist - dist; check.setPosition({ @@ -147,7 +147,7 @@ Zombie.prototype.onCellMove = function (x1, y1, cell) { } }; -Zombie.prototype.updateLB = function(gameServer) { +Zombie.prototype.updateLB = function (gameServer) { gameServer.leaderboardType = this.packetLB; var lb = gameServer.leaderboard; // Loop through all clients @@ -155,13 +155,13 @@ Zombie.prototype.updateLB = function(gameServer) { if (typeof gameServer.clients[i] == "undefined" || gameServer.clients[i].playerTracker.team == 0) { continue; } - + var player = gameServer.clients[i].playerTracker; var playerScore = player.getScore(); if (player.cells.length <= 0) { continue; } - + if (lb.length == 0) { // Initial player lb.push(player); @@ -176,6 +176,6 @@ Zombie.prototype.updateLB = function(gameServer) { } } } - + this.rankOne = lb[0]; }; diff --git a/src/gamemodes/index.js b/src/gamemodes/index.js index 39758a9b4..25df2bdc3 100755 --- a/src/gamemodes/index.js +++ b/src/gamemodes/index.js @@ -1,4 +1,4 @@ -module.exports = { +module.exports = { Mode: require('./Mode'), FFA: require('./FFA'), Teams: require('./Teams'), @@ -11,34 +11,34 @@ module.exports = { TeamX: require('./TeamX.js') }; -var get = function(id) { +var get = function (id) { var mode; switch (id) { - case 1: // Teams + case 1:// Teams mode = new module.exports.Teams(); break; - case 2: // Experimental + case 2:// Experimental mode = new module.exports.Experimental(); break; - case 10: // Tournament + case 10:// Tournament mode = new module.exports.Tournament(); break; - case 11: // Hunger Games + case 11:// Hunger Games mode = new module.exports.HungerGames(); break; - case 12: // Zombie + case 12:// Zombie mode = new module.exports.Zombie(); break; - case 13: // Zombie Team + case 13:// Zombie Team mode = new module.exports.TeamZ(); break; - case 14: // Experimental Team + case 14:// Experimental Team mode = new module.exports.TeamX(); break; - case 20: // Rainbow + case 20:// Rainbow mode = new module.exports.Rainbow(); break; - default: // FFA is default + default:// FFA is default mode = new module.exports.FFA(); break; } diff --git a/src/index.js b/src/index.js index faf5a51d1..0eb9f5315 100644 --- a/src/index.js +++ b/src/index.js @@ -1,86 +1,86 @@ -// Imports +// Imports var pjson = require('../package.json'); -var Logger = require('./modules/Logger'); -var Commands = require('./modules/CommandList'); -var GameServer = require('./GameServer'); - -// Init variables -var showConsole = true; - -// Start msg -Logger.start(); - -process.on('exit', function (code) { - Logger.debug("process.exit(" + code + ")"); - Logger.shutdown(); -}); - -process.on('uncaughtException', function (err) { - Logger.fatal(err.stack); - process.exit(1); -}); - -Logger.info("\u001B[1m\u001B[32mMultiOgar " + pjson.version + "\u001B[37m - An open source multi-protocol ogar server\u001B[0m"); - - -// Handle arguments -process.argv.forEach(function(val) { - if (val == "--noconsole") { - showConsole = false; - } else if (val == "--help") { - console.log("Proper Usage: node index.js"); - console.log(" --noconsole Disables the console"); - console.log(" --help Help menu."); - console.log(""); - } -}); - -// Run Ogar -var gameServer = new GameServer(); -gameServer.start(); -// Add command handler -gameServer.commands = Commands.list; -// Initialize the server console -if (showConsole) { - var readline = require('readline'); - var in_ = readline.createInterface({ - input: process.stdin, - output: process.stdout - }); - setTimeout(prompt, 100); -} - -// Console functions - -function prompt() { - in_.question(">", function(str) { - try { - parseCommands(str); - } finally { - setTimeout(prompt, 0); - } - }); -} - -function parseCommands(str) { - // Log the string - Logger.write(">" + str); - - // Don't process ENTER - if (str === '') - return; - - // Splits the string - var split = str.split(" "); - - // Process the first string value - var first = split[0].toLowerCase(); - - // Get command function - var execute = gameServer.commands[first]; - if (typeof execute != 'undefined') { - execute(gameServer, split); - } else { - Logger.warn("Invalid Command!"); - } -} +var Logger = require('./modules/Logger'); +var Commands = require('./modules/CommandList'); +var GameServer = require('./GameServer'); + +// Init variables +var showConsole = true; + +// Start msg +Logger.start(); + +process.on('exit', function (code) { + Logger.debug("process.exit(" + code + ")"); + Logger.shutdown(); +}); + +process.on('uncaughtException', function (err) { + Logger.fatal(err.stack); + process.exit(1); +}); + +Logger.info("\u001B[1m\u001B[32mMultiOgar " + pjson.version + "\u001B[37m - An open source multi-protocol ogar server\u001B[0m"); + + +// Handle arguments +process.argv.forEach(function (val) { + if (val == "--noconsole") { + showConsole = false; + } else if (val == "--help") { + console.log("Proper Usage: node index.js"); + console.log(" --noconsole Disables the console"); + console.log(" --help Help menu."); + console.log(""); + } +}); + +// Run Ogar +var gameServer = new GameServer(); +gameServer.start(); +// Add command handler +gameServer.commands = Commands.list; +// Initialize the server console +if (showConsole) { + var readline = require('readline'); + var in_ = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + setTimeout(prompt, 100); +} + +// Console functions + +function prompt() { + in_.question(">", function (str) { + try { + parseCommands(str); + } finally { + setTimeout(prompt, 0); + } + }); +} + +function parseCommands(str) { + // Log the string + Logger.write(">" + str); + + // Don't process ENTER + if (str === '') + return; + + // Splits the string + var split = str.split(" "); + + // Process the first string value + var first = split[0].toLowerCase(); + + // Get command function + var execute = gameServer.commands[first]; + if (typeof execute != 'undefined') { + execute(gameServer, split); + } else { + Logger.warn("Invalid Command!"); + } +} diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 827b0c734..000bc5996 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -1,615 +1,615 @@ -// Imports -var GameMode = require('../gamemodes'); -var Entity = require('../entity'); -var ini = require('./ini.js'); -var Logger = require('./Logger'); - -function Commands() { - this.list = {}; // Empty -} - -module.exports = Commands; - -// Utils -var fillChar = function(data, char, fieldLength, rTL) { - var result = data.toString(); - if (rTL === true) { - for (var i = result.length; i < fieldLength; i++) - result = char.concat(result); - } else { - for (var i = result.length; i < fieldLength; i++) - result = result.concat(char); - } - return result; -}; - -// Commands - -Commands.list = { - help: function(gameServer, split) { - console.log("======================== HELP ======================"); - console.log("addbot [number] : add bot to the server"); - console.log("kickbot [number] : kick a number of bots"); - console.log("ban [PlayerID | IP] : bans a(n) (player's) IP"); - console.log("banlist : get list of banned IPs."); - console.log("board [string] [string] ... : set scoreboard text"); - console.log("boardreset : reset scoreboard text"); - console.log("change [setting] [value] : change specified settings"); - console.log("clear : clear console output"); - console.log("color [PlayerID] [R] [G] [B] : set cell(s) color by client ID"); - console.log("exit : stop the server"); - console.log("food [X] [Y] [mass] : spawn food at specified Location"); - console.log("gamemode [id] : change server gamemode"); - console.log("kick [PlayerID] : kick player or bot by client ID"); - console.log("kickall : kick all players and bots"); - console.log("kill [PlayerID] : kill cell(s) by client ID"); - console.log("killall : kill everyone"); - console.log("mass [PlayerID] [mass] : set cell(s) mass by client ID"); - console.log("merge [PlayerID] : merge all client's cells once"); - console.log("name [PlayerID] [name] : change cell(s) name by client ID"); - console.log("playerlist : get list of players and bots"); - console.log("pause : pause game , freeze all cells"); - console.log("reload : reload config"); - console.log("status : get server status"); - console.log("tp [PlayerID] [X] [Y] : teleport player to specified location"); - console.log("unban [IP] : unban an IP"); - console.log("virus [X] [Y] [mass] : spawn virus at a specified Location"); - console.log("pl : alias for playerlist"); - console.log("st : alias for status"); - console.log("===================================================="); - }, - debug: function(gameServer, split) { - // Used for checking node lengths (for now) - - // Count client cells - var clientCells = 0; - for (var i in gameServer.clients) { - clientCells += gameServer.clients[i].playerTracker.cells.length; - } - // Output node information - console.log("Clients: " + fillChar(gameServer.clients.length, " ", 4, true) + " / " + gameServer.config.serverMaxConnections + " + bots"); - console.log("Total nodes:" + fillChar(gameServer.nodes.length, " ", 8, true)); - console.log("- Client cells: " + fillChar(clientCells, " ", 4, true) + " / " + (gameServer.clients.length * gameServer.config.playerMaxCells)); - console.log("- Ejected cells:" + fillChar(gameServer.nodesEjected.length, " ", 4, true)); - console.log("- Foods: " + fillChar(gameServer.currentFood, " ", 4, true) + " / " + gameServer.config.foodMaxAmount); - console.log("- Viruses: " + fillChar(gameServer.nodesVirus.length, " ", 4, true) + " / " + gameServer.config.virusMaxAmount); - console.log("Moving nodes: " + fillChar(gameServer.movingNodes.length, " ", 4, true)); - console.log("Quad nodes: " + fillChar(gameServer.quadTree.scanNodeCount(), " ", 4, true)); - console.log("Quad items: " + fillChar(gameServer.quadTree.scanItemCount(), " ", 4, true)); - }, - addbot: function(gameServer, split) { - var add = parseInt(split[1]); - if (isNaN(add)) { - add = 1; // Adds 1 bot if user doesnt specify a number - } - - for (var i = 0; i < add; i++) { - gameServer.bots.addBot(); - } - console.log("Added " + add + " player bots"); - }, - ban: function (gameServer, split) { - // Error message - var logInvalid = "Please specify a valid player ID or IP address!"; - - if (split[1] == null) { - // If no input is given; added to avoid error - Logger.warn(logInvalid); - return; - } - - if (split[1].indexOf(".") >= 0) { - // If input is an IP address - var ip = split[1]; - var ipParts = ip.split("."); - - // Check for invalid decimal numbers of the IP address - for (var i in ipParts) { - if (i > 1 && ipParts[i] == "*") { - // mask for sub-net - continue; - } - // If not numerical or if it's not between 0 and 255 - // TODO: Catch string "e" as it means "10^". - if (isNaN(ipParts[i]) || ipParts[i] < 0 || ipParts[i] >= 256) { - Logger.warn(logInvalid); - return; - } - } - - if (ipParts.length != 4) { - // an IP without 3 decimals - Logger.warn(logInvalid); - return; - } - - gameServer.banIp(ip); - return; - } - // if input is a Player ID - var id = parseInt(split[1]); - if (isNaN(id)) { - // If not numerical - Logger.warn(logInvalid); - return; - } - var ip = null; - for (var i in gameServer.clients) { - var client = gameServer.clients[i]; - if (client == null || !client.isConnected) - continue; - if (client.playerTracker.pID == id) { - ip = client._socket.remoteAddress; - break; - } - } - if (ip) - gameServer.banIp(ip); - else - Logger.warn("Player ID " + id + " not found!"); - }, - banlist: function(gameServer, split) { - Logger.print("Showing " + gameServer.ipBanList.length + " banned IPs: "); - Logger.print(" IP | IP "); - Logger.print("-----------------------------------"); - for (var i = 0; i < gameServer.ipBanList.length; i += 2) { - Logger.print(" " + fillChar(gameServer.ipBanList[i], " ", 15) + " | " - + (gameServer.ipBanList.length === i+1 ? "" : gameServer.ipBanList[i+1] ) - ); - } - }, - kickbot: function(gameServer, split) { - var toRemove = parseInt(split[1]); - if (isNaN(toRemove)) { - toRemove = -1; // Kick all bots if user doesnt specify a number - } - if (toRemove < 1) { - Logger.warn("Invalid argument!"); - return; - } - var removed = 0; - for (var i = 0; i < gameServer.clients.length; i++) { - var socket = gameServer.clients[i]; - if (socket.isConnected != null) continue; - socket.close(); - removed++; - if (removed >= toRemove) - break; - } - if (removed == 0) - Logger.warn("Cannot find any bots"); - else if (toRemove == removed) - Logger.warn("Kicked " + removed + " bots"); - else - Logger.warn("Only " + removed + " bots were kicked"); - }, - board: function(gameServer, split) { - var newLB = []; - for (var i = 1; i < split.length; i++) { - if (split[i]) { - newLB[i - 1] = split[i]; - } else { - newLB[i - 1] = " "; - } - } - - // Clears the update leaderboard function and replaces it with our own - gameServer.gameMode.packetLB = 48; - gameServer.gameMode.specByLeaderboard = false; - gameServer.gameMode.updateLB = function(gameServer) { - gameServer.leaderboard = newLB; - gameServer.leaderboardType = 48; - }; - console.log("Successfully changed leaderboard values"); - }, - boardreset: function(gameServer) { - // Gets the current gamemode - var gm = GameMode.get(gameServer.gameMode.ID); - - // Replace functions - gameServer.gameMode.packetLB = gm.packetLB; - gameServer.gameMode.updateLB = gm.updateLB; - console.log("Successfully reset leaderboard"); - }, - change: function (gameServer, split) { - if (split.length < 3) { - Logger.warn("Invalid command arguments"); - return; - } - var key = split[1]; - var value = split[2]; - - // Check if int/float - if (value.indexOf('.') != -1) { - value = parseFloat(value); - } else { - value = parseInt(value); - } - gameServer.changeConfig(key, value); - }, - clear: function() { - process.stdout.write("\u001b[2J\u001b[0;0H"); - }, - color: function(gameServer, split) { - // Validation checks - var id = parseInt(split[1]); - if (isNaN(id)) { - Logger.warn("Please specify a valid player ID!"); - return; - } - - var color = { - r: 0, - g: 0, - b: 0 - }; - color.r = Math.max(Math.min(parseInt(split[2]), 255), 0); - color.g = Math.max(Math.min(parseInt(split[3]), 255), 0); - color.b = Math.max(Math.min(parseInt(split[4]), 255), 0); - - // Sets color to the specified amount - for (var i in gameServer.clients) { - if (gameServer.clients[i].playerTracker.pID == id) { - var client = gameServer.clients[i].playerTracker; - client.setColor(color); // Set color - for (var j in client.cells) { - client.cells[j].setColor(color); - } - break; - } - } - }, - exit: function(gameServer, split) { - Logger.warn("Closing server..."); - gameServer.wsServer.close(); - process.exit(1); - }, - food: function(gameServer, split) { - var pos = { - x: parseInt(split[1]), - y: parseInt(split[2]) - }; - var mass = parseInt(split[3]); - - // Make sure the input values are numbers - if (isNaN(pos.x) || isNaN(pos.y)) { - Logger.warn("Invalid coordinates"); - return; - } - - var size = gameServer.config.foodMinMass; - if (!isNaN(mass)) { - size = Math.sqrt(mass * 100); - } - - // Spawn - var cell = new Entity.Food(gameServer, null, pos, size); - cell.setColor(gameServer.getRandomColor()); - gameServer.addNode(cell); - console.log("Spawned 1 food cell at (" + pos.x + " , " + pos.y + ")"); - }, - gamemode: function(gameServer, split) { - try { - var n = parseInt(split[1]); - var gm = GameMode.get(n); // If there is an invalid gamemode, the function will exit - gameServer.gameMode.onChange(gameServer); // Reverts the changes of the old gamemode - gameServer.gameMode = gm; // Apply new gamemode - gameServer.gameMode.onServerInit(gameServer); // Resets the server - console.log("Changed game mode to " + gameServer.gameMode.name); - } catch (err) { +// Imports +var GameMode = require('../gamemodes'); +var Entity = require('../entity'); +var ini = require('./ini.js'); +var Logger = require('./Logger'); + +function Commands() { + this.list = {}; // Empty +} + +module.exports = Commands; + +// Utils +var fillChar = function (data, char, fieldLength, rTL) { + var result = data.toString(); + if (rTL === true) { + for (var i = result.length; i < fieldLength; i++) + result = char.concat(result); + } else { + for (var i = result.length; i < fieldLength; i++) + result = result.concat(char); + } + return result; +}; + +// Commands + +Commands.list = { + help: function (gameServer, split) { + console.log("======================== HELP ======================"); + console.log("addbot [number] : add bot to the server"); + console.log("kickbot [number] : kick a number of bots"); + console.log("ban [PlayerID | IP] : bans a(n) (player's) IP"); + console.log("banlist : get list of banned IPs."); + console.log("board [string] [string] ... : set scoreboard text"); + console.log("boardreset : reset scoreboard text"); + console.log("change [setting] [value] : change specified settings"); + console.log("clear : clear console output"); + console.log("color [PlayerID] [R] [G] [B] : set cell(s) color by client ID"); + console.log("exit : stop the server"); + console.log("food [X] [Y] [mass] : spawn food at specified Location"); + console.log("gamemode [id] : change server gamemode"); + console.log("kick [PlayerID] : kick player or bot by client ID"); + console.log("kickall : kick all players and bots"); + console.log("kill [PlayerID] : kill cell(s) by client ID"); + console.log("killall : kill everyone"); + console.log("mass [PlayerID] [mass] : set cell(s) mass by client ID"); + console.log("merge [PlayerID] : merge all client's cells once"); + console.log("name [PlayerID] [name] : change cell(s) name by client ID"); + console.log("playerlist : get list of players and bots"); + console.log("pause : pause game , freeze all cells"); + console.log("reload : reload config"); + console.log("status : get server status"); + console.log("tp [PlayerID] [X] [Y] : teleport player to specified location"); + console.log("unban [IP] : unban an IP"); + console.log("virus [X] [Y] [mass] : spawn virus at a specified Location"); + console.log("pl : alias for playerlist"); + console.log("st : alias for status"); + console.log("===================================================="); + }, + debug: function (gameServer, split) { + // Used for checking node lengths (for now) + + // Count client cells + var clientCells = 0; + for (var i in gameServer.clients) { + clientCells += gameServer.clients[i].playerTracker.cells.length; + } + // Output node information + console.log("Clients: " + fillChar(gameServer.clients.length, " ", 4, true) + " / " + gameServer.config.serverMaxConnections + " + bots"); + console.log("Total nodes:" + fillChar(gameServer.nodes.length, " ", 8, true)); + console.log("- Client cells: " + fillChar(clientCells, " ", 4, true) + " / " + (gameServer.clients.length * gameServer.config.playerMaxCells)); + console.log("- Ejected cells:" + fillChar(gameServer.nodesEjected.length, " ", 4, true)); + console.log("- Foods: " + fillChar(gameServer.currentFood, " ", 4, true) + " / " + gameServer.config.foodMaxAmount); + console.log("- Viruses: " + fillChar(gameServer.nodesVirus.length, " ", 4, true) + " / " + gameServer.config.virusMaxAmount); + console.log("Moving nodes: " + fillChar(gameServer.movingNodes.length, " ", 4, true)); + console.log("Quad nodes: " + fillChar(gameServer.quadTree.scanNodeCount(), " ", 4, true)); + console.log("Quad items: " + fillChar(gameServer.quadTree.scanItemCount(), " ", 4, true)); + }, + addbot: function (gameServer, split) { + var add = parseInt(split[1]); + if (isNaN(add)) { + add = 1; // Adds 1 bot if user doesnt specify a number + } + + for (var i = 0; i < add; i++) { + gameServer.bots.addBot(); + } + console.log("Added " + add + " player bots"); + }, + ban: function (gameServer, split) { + // Error message + var logInvalid = "Please specify a valid player ID or IP address!"; + + if (split[1] == null) { + // If no input is given; added to avoid error + Logger.warn(logInvalid); + return; + } + + if (split[1].indexOf(".") >= 0) { + // If input is an IP address + var ip = split[1]; + var ipParts = ip.split("."); + + // Check for invalid decimal numbers of the IP address + for (var i in ipParts) { + if (i > 1 && ipParts[i] == "*") { + // mask for sub-net + continue; + } + // If not numerical or if it's not between 0 and 255 + // TODO: Catch string "e" as it means "10^". + if (isNaN(ipParts[i]) || ipParts[i] < 0 || ipParts[i] >= 256) { + Logger.warn(logInvalid); + return; + } + } + + if (ipParts.length != 4) { + // an IP without 3 decimals + Logger.warn(logInvalid); + return; + } + + gameServer.banIp(ip); + return; + } + // if input is a Player ID + var id = parseInt(split[1]); + if (isNaN(id)) { + // If not numerical + Logger.warn(logInvalid); + return; + } + var ip = null; + for (var i in gameServer.clients) { + var client = gameServer.clients[i]; + if (client == null || !client.isConnected) + continue; + if (client.playerTracker.pID == id) { + ip = client._socket.remoteAddress; + break; + } + } + if (ip) + gameServer.banIp(ip); + else + Logger.warn("Player ID " + id + " not found!"); + }, + banlist: function (gameServer, split) { + Logger.print("Showing " + gameServer.ipBanList.length + " banned IPs: "); + Logger.print(" IP | IP "); + Logger.print("-----------------------------------"); + for (var i = 0; i < gameServer.ipBanList.length; i += 2) { + Logger.print(" " + fillChar(gameServer.ipBanList[i], " ", 15) + " | " + + (gameServer.ipBanList.length === i + 1 ? "" : gameServer.ipBanList[i + 1]) + ); + } + }, + kickbot: function (gameServer, split) { + var toRemove = parseInt(split[1]); + if (isNaN(toRemove)) { + toRemove = -1; // Kick all bots if user doesnt specify a number + } + if (toRemove < 1) { + Logger.warn("Invalid argument!"); + return; + } + var removed = 0; + for (var i = 0; i < gameServer.clients.length; i++) { + var socket = gameServer.clients[i]; + if (socket.isConnected != null) continue; + socket.close(); + removed++; + if (removed >= toRemove) + break; + } + if (removed == 0) + Logger.warn("Cannot find any bots"); + else if (toRemove == removed) + Logger.warn("Kicked " + removed + " bots"); + else + Logger.warn("Only " + removed + " bots were kicked"); + }, + board: function (gameServer, split) { + var newLB = []; + for (var i = 1; i < split.length; i++) { + if (split[i]) { + newLB[i - 1] = split[i]; + } else { + newLB[i - 1] = " "; + } + } + + // Clears the update leaderboard function and replaces it with our own + gameServer.gameMode.packetLB = 48; + gameServer.gameMode.specByLeaderboard = false; + gameServer.gameMode.updateLB = function (gameServer) { + gameServer.leaderboard = newLB; + gameServer.leaderboardType = 48; + }; + console.log("Successfully changed leaderboard values"); + }, + boardreset: function (gameServer) { + // Gets the current gamemode + var gm = GameMode.get(gameServer.gameMode.ID); + + // Replace functions + gameServer.gameMode.packetLB = gm.packetLB; + gameServer.gameMode.updateLB = gm.updateLB; + console.log("Successfully reset leaderboard"); + }, + change: function (gameServer, split) { + if (split.length < 3) { + Logger.warn("Invalid command arguments"); + return; + } + var key = split[1]; + var value = split[2]; + + // Check if int/float + if (value.indexOf('.') != -1) { + value = parseFloat(value); + } else { + value = parseInt(value); + } + gameServer.changeConfig(key, value); + }, + clear: function () { + process.stdout.write("\u001b[2J\u001b[0;0H"); + }, + color: function (gameServer, split) { + // Validation checks + var id = parseInt(split[1]); + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + + var color = { + r: 0, + g: 0, + b: 0 + }; + color.r = Math.max(Math.min(parseInt(split[2]), 255), 0); + color.g = Math.max(Math.min(parseInt(split[3]), 255), 0); + color.b = Math.max(Math.min(parseInt(split[4]), 255), 0); + + // Sets color to the specified amount + for (var i in gameServer.clients) { + if (gameServer.clients[i].playerTracker.pID == id) { + var client = gameServer.clients[i].playerTracker; + client.setColor(color); // Set color + for (var j in client.cells) { + client.cells[j].setColor(color); + } + break; + } + } + }, + exit: function (gameServer, split) { + Logger.warn("Closing server..."); + gameServer.wsServer.close(); + process.exit(1); + }, + food: function (gameServer, split) { + var pos = { + x: parseInt(split[1]), + y: parseInt(split[2]) + }; + var mass = parseInt(split[3]); + + // Make sure the input values are numbers + if (isNaN(pos.x) || isNaN(pos.y)) { + Logger.warn("Invalid coordinates"); + return; + } + + var size = gameServer.config.foodMinMass; + if (!isNaN(mass)) { + size = Math.sqrt(mass * 100); + } + + // Spawn + var cell = new Entity.Food(gameServer, null, pos, size); + cell.setColor(gameServer.getRandomColor()); + gameServer.addNode(cell); + console.log("Spawned 1 food cell at (" + pos.x + " , " + pos.y + ")"); + }, + gamemode: function (gameServer, split) { + try { + var n = parseInt(split[1]); + var gm = GameMode.get(n); // If there is an invalid gamemode, the function will exit + gameServer.gameMode.onChange(gameServer); // Reverts the changes of the old gamemode + gameServer.gameMode = gm; // Apply new gamemode + gameServer.gameMode.onServerInit(gameServer); // Resets the server + console.log("Changed game mode to " + gameServer.gameMode.name); + } catch (err) { Logger.error(err.stack); - Logger.error("Invalid game mode selected"); - } - }, - kick: function(gameServer, split) { - var id = parseInt(split[1]); - if (isNaN(id)) { - Logger.warn("Please specify a valid player ID!"); - return; - } - gameServer.kickId(id); - }, - kickall: function (gameServer, split) { - gameServer.kickId(0); - }, - kill: function(gameServer, split) { - var id = parseInt(split[1]); - if (isNaN(id)) { - Logger.warn("Please specify a valid player ID!"); - return; - } - - var count = 0; - for (var i in gameServer.clients) { - if (gameServer.clients[i].playerTracker.pID == id) { - var client = gameServer.clients[i].playerTracker; - var len = client.cells.length; - for (var j = 0; j < len; j++) { - gameServer.removeNode(client.cells[0]); - count++; - } - - console.log("Removed " + count + " cells"); - break; - } - } - }, - killall: function(gameServer, split) { - var count = 0; - for (var i = 0; i < gameServer.clients.length; i++) { - var playerTracker = gameServer.clients[i].playerTracker; - while (playerTracker.cells.length > 0) { - gameServer.removeNode(playerTracker.cells[0]); - count++; - } - } - console.log("Removed " + count + " cells"); - }, - mass: function(gameServer, split) { - // Validation checks - var id = parseInt(split[1]); - if (isNaN(id)) { - Logger.warn("Please specify a valid player ID!"); - return; - } - - var amount = Math.max(parseInt(split[2]), 9); - if (isNaN(amount)) { - Logger.warn("Please specify a valid number"); - return; - } - var size = Math.sqrt(amount * 100); - - // Sets mass to the specified amount - for (var i in gameServer.clients) { - if (gameServer.clients[i].playerTracker.pID == id) { - var client = gameServer.clients[i].playerTracker; - for (var j in client.cells) { - client.cells[j].setSize(size); - } - - console.log("Set mass of " + client.getFriendlyName() + " to " + (size*size/100).toFixed(3)); - break; - } - } - }, - merge: function(gameServer, split) { - // Validation checks - var id = parseInt(split[1]); - var set = split[2]; - if (isNaN(id)) { - Logger.warn("Please specify a valid player ID!"); - return; - } - - // Find client with same ID as player entered - var client; - for (var i = 0; i < gameServer.clients.length; i++) { - if (id == gameServer.clients[i].playerTracker.pID) { - client = gameServer.clients[i].playerTracker; - break; - } - } - - if (!client) { - Logger.warn("Client is nonexistent!"); - return; - } - - if (client.cells.length == 1) { - Logger.warn("Client already has one cell!"); - return; - } - - // Set client's merge override - var state; - if (set == "true") { - client.mergeOverride = true; - client.mergeOverrideDuration = 100; - state = true; - } else if (set == "false") { - client.mergeOverride = false; - client.mergeOverrideDuration = 0; - state = false; - } else { - if (client.mergeOverride) { - client.mergeOverride = false; - client.mergeOverrideDuration = 0; - } else { - client.mergeOverride = true; - client.mergeOverrideDuration = 100; - } - - state = client.mergeOverride; - } - - // Log - if (state) console.log("Player " + id + " is now force merging"); - else console.log("Player " + id + " isn't force merging anymore"); - }, - name: function(gameServer, split) { - // Validation checks - var id = parseInt(split[1]); - if (isNaN(id)) { - Logger.warn("Please specify a valid player ID!"); - return; - } - - var name = split.slice(2, split.length).join(' '); - if (typeof name == 'undefined') { - Logger.warn("Please type a valid name"); - return; - } - - // Change name - for (var i = 0; i < gameServer.clients.length; i++) { - var client = gameServer.clients[i].playerTracker; - - if (client.pID == id) { - console.log("Changing " + client.getFriendlyName() + " to " + name); - client.setName(name); - return; - } - } - - // Error - Logger.warn("Player " + id + " was not found"); - }, - unban: function(gameServer, split) { - if (split.length < 2 || split[1] == null || split[1].trim().length < 1) { - Logger.warn("Please specify a valid IP!"); - return; - } - gameServer.unbanIp(split[1].trim()); - }, - playerlist: function(gameServer, split) { - Logger.print("Showing " + gameServer.clients.length + " players: "); - Logger.print(" ID | IP | P | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space - Logger.print(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); - var sockets = gameServer.clients.slice(0); - sockets.sort(function (a, b) { return a.playerTracker.pID - b.playerTracker.pID; }); - for (var i = 0; i < sockets.length; i++) { - var socket = sockets[i]; - var client = socket.playerTracker; - - // ID with 3 digits length - var id = fillChar((client.pID), ' ', 6, true); - - // Get ip (15 digits length) - var ip = "[BOT]"; - if (socket.isConnected != null) { - ip = socket.remoteAddress; - } - ip = fillChar(ip, ' ', 15); - var protocol = gameServer.clients[i].packetHandler.protocol; - if (protocol == null) - protocol = "?" - // Get name and data - var nick = '', - cells = '', - score = '', - position = '', - data = ''; - if (socket.closeReason != null) { - // Disconnected - var reason = "[DISCONNECTED] "; - if (socket.closeReason.code) - reason += "[" + socket.closeReason.code + "] "; - if (socket.closeReason.message) - reason += socket.closeReason.message; - Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + reason); - } else if (!socket.packetHandler.protocol && socket.isConnected) { - Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + "[CONNECTING]"); - }else if (client.spectate) { - nick = "in free-roam"; - if (!client.freeRoam) { - var target = client.getSpectateTarget(); - if (target != null) { - nick = target.getFriendlyName(); - } - } - data = fillChar("SPECTATING: " + nick, '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); - Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + data); - } else if (client.cells.length > 0) { - nick = fillChar(client.getFriendlyName(), ' ', gameServer.config.playerMaxNickLength); - cells = fillChar(client.cells.length, ' ', 5, true); - score = fillChar((client.getScore()/100) >> 0, ' ', 6, true); - position = fillChar(client.centerPos.x >> 0, ' ', 5, true) + ', ' + fillChar(client.centerPos.y >> 0, ' ', 5, true); - Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + nick + " | " + cells + " | " + score + " | " + position); - } else { - // No cells = dead player or in-menu - data = fillChar('DEAD OR NOT PLAYING', '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); - Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + data); - } - } - }, - pause: function(gameServer, split) { - gameServer.run = !gameServer.run; // Switches the pause state - var s = gameServer.run ? "Unpaused" : "Paused"; - console.log(s + " the game."); - }, - reload: function(gameServer) { - gameServer.loadConfig(); - gameServer.loadIpBanList(); - console.log("Reloaded the config file successfully"); - }, - status: function(gameServer, split) { - // Get amount of humans/bots - var humans = 0, - bots = 0; - for (var i = 0; i < gameServer.clients.length; i++) { - if ('_socket' in gameServer.clients[i]) { - humans++; - } else { - bots++; - } - } - - console.log("Connected players: " + gameServer.clients.length + "/" + gameServer.config.serverMaxConnections); - console.log("Players: " + humans + " - Bots: " + bots); - console.log("Server has been running for " + Math.floor(process.uptime()/60) + " minutes"); - console.log("Current memory usage: " + Math.round(process.memoryUsage().heapUsed / 1048576 * 10)/10 + "/" + Math.round(process.memoryUsage().heapTotal / 1048576 * 10)/10 + " mb"); - console.log("Current game mode: " + gameServer.gameMode.name); - console.log("Current update time: " + gameServer.updateTimeAvg.toFixed(3) + " [ms] (" + ini.getLagMessage(gameServer.updateTimeAvg) + ")"); - }, - tp: function(gameServer, split) { - var id = parseInt(split[1]); - if (isNaN(id)) { - Logger.warn("Please specify a valid player ID!"); - return; - } - - // Make sure the input values are numbers - var pos = { - x: parseInt(split[2]), - y: parseInt(split[3]) - }; - if (isNaN(pos.x) || isNaN(pos.y)) { - Logger.warn("Invalid coordinates"); - return; - } - - // Spawn - for (var i in gameServer.clients) { - if (gameServer.clients[i].playerTracker.pID == id) { - var client = gameServer.clients[i].playerTracker; - for (var j in client.cells) { - client.cells[j].setPosition(pos); - gameServer.updateNodeQuad(client.cells[j]); - } - - console.log("Teleported " + client.getFriendlyName() + " to (" + pos.x + " , " + pos.y + ")"); - break; - } - } - }, - virus: function(gameServer, split) { - var pos = { - x: parseInt(split[1]), - y: parseInt(split[2]) - }; - var mass = parseInt(split[3]); - - // Make sure the input values are numbers - if (isNaN(pos.x) || isNaN(pos.y)) { - Logger.warn("Invalid coordinates"); - return; - } - var size = gameServer.config.virusMinSize; - if (!isNaN(mass)) { - size = Math.sqrt(mass * 100); - } - - // Spawn - var v = new Entity.Virus(gameServer, null, pos, size); - gameServer.addNode(v); - console.log("Spawned 1 virus at (" + pos.x + " , " + pos.y + ")"); - }, - //Aliases - st: function (gameServer, split) { - Commands.list.status(gameServer, split); - }, - pl: function(gameServer, split){ - Commands.list.playerlist(gameServer, split); - } -}; + Logger.error("Invalid game mode selected"); + } + }, + kick: function (gameServer, split) { + var id = parseInt(split[1]); + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + gameServer.kickId(id); + }, + kickall: function (gameServer, split) { + gameServer.kickId(0); + }, + kill: function (gameServer, split) { + var id = parseInt(split[1]); + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + + var count = 0; + for (var i in gameServer.clients) { + if (gameServer.clients[i].playerTracker.pID == id) { + var client = gameServer.clients[i].playerTracker; + var len = client.cells.length; + for (var j = 0; j < len; j++) { + gameServer.removeNode(client.cells[0]); + count++; + } + + console.log("Removed " + count + " cells"); + break; + } + } + }, + killall: function (gameServer, split) { + var count = 0; + for (var i = 0; i < gameServer.clients.length; i++) { + var playerTracker = gameServer.clients[i].playerTracker; + while (playerTracker.cells.length > 0) { + gameServer.removeNode(playerTracker.cells[0]); + count++; + } + } + console.log("Removed " + count + " cells"); + }, + mass: function (gameServer, split) { + // Validation checks + var id = parseInt(split[1]); + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + + var amount = Math.max(parseInt(split[2]), 9); + if (isNaN(amount)) { + Logger.warn("Please specify a valid number"); + return; + } + var size = Math.sqrt(amount * 100); + + // Sets mass to the specified amount + for (var i in gameServer.clients) { + if (gameServer.clients[i].playerTracker.pID == id) { + var client = gameServer.clients[i].playerTracker; + for (var j in client.cells) { + client.cells[j].setSize(size); + } + + console.log("Set mass of " + client.getFriendlyName() + " to " + (size * size / 100).toFixed(3)); + break; + } + } + }, + merge: function (gameServer, split) { + // Validation checks + var id = parseInt(split[1]); + var set = split[2]; + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + + // Find client with same ID as player entered + var client; + for (var i = 0; i < gameServer.clients.length; i++) { + if (id == gameServer.clients[i].playerTracker.pID) { + client = gameServer.clients[i].playerTracker; + break; + } + } + + if (!client) { + Logger.warn("Client is nonexistent!"); + return; + } + + if (client.cells.length == 1) { + Logger.warn("Client already has one cell!"); + return; + } + + // Set client's merge override + var state; + if (set == "true") { + client.mergeOverride = true; + client.mergeOverrideDuration = 100; + state = true; + } else if (set == "false") { + client.mergeOverride = false; + client.mergeOverrideDuration = 0; + state = false; + } else { + if (client.mergeOverride) { + client.mergeOverride = false; + client.mergeOverrideDuration = 0; + } else { + client.mergeOverride = true; + client.mergeOverrideDuration = 100; + } + + state = client.mergeOverride; + } + + // Log + if (state) console.log("Player " + id + " is now force merging"); + else console.log("Player " + id + " isn't force merging anymore"); + }, + name: function (gameServer, split) { + // Validation checks + var id = parseInt(split[1]); + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + + var name = split.slice(2, split.length).join(' '); + if (typeof name == 'undefined') { + Logger.warn("Please type a valid name"); + return; + } + + // Change name + for (var i = 0; i < gameServer.clients.length; i++) { + var client = gameServer.clients[i].playerTracker; + + if (client.pID == id) { + console.log("Changing " + client.getFriendlyName() + " to " + name); + client.setName(name); + return; + } + } + + // Error + Logger.warn("Player " + id + " was not found"); + }, + unban: function (gameServer, split) { + if (split.length < 2 || split[1] == null || split[1].trim().length < 1) { + Logger.warn("Please specify a valid IP!"); + return; + } + gameServer.unbanIp(split[1].trim()); + }, + playerlist: function (gameServer, split) { + Logger.print("Showing " + gameServer.clients.length + " players: "); + Logger.print(" ID | IP | P | " + fillChar('NICK', ' ', gameServer.config.playerMaxNickLength) + " | CELLS | SCORE | POSITION "); // Fill space + Logger.print(fillChar('', '-', ' ID | IP | | | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength)); + var sockets = gameServer.clients.slice(0); + sockets.sort(function (a, b) { return a.playerTracker.pID - b.playerTracker.pID; }); + for (var i = 0; i < sockets.length; i++) { + var socket = sockets[i]; + var client = socket.playerTracker; + + // ID with 3 digits length + var id = fillChar((client.pID), ' ', 6, true); + + // Get ip (15 digits length) + var ip = "[BOT]"; + if (socket.isConnected != null) { + ip = socket.remoteAddress; + } + ip = fillChar(ip, ' ', 15); + var protocol = gameServer.clients[i].packetHandler.protocol; + if (protocol == null) + protocol = "?" + // Get name and data + var nick = '', + cells = '', + score = '', + position = '', + data = ''; + if (socket.closeReason != null) { + // Disconnected + var reason = "[DISCONNECTED] "; + if (socket.closeReason.code) + reason += "[" + socket.closeReason.code + "] "; + if (socket.closeReason.message) + reason += socket.closeReason.message; + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + reason); + } else if (!socket.packetHandler.protocol && socket.isConnected) { + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + "[CONNECTING]"); + } else if (client.spectate) { + nick = "in free-roam"; + if (!client.freeRoam) { + var target = client.getSpectateTarget(); + if (target != null) { + nick = target.getFriendlyName(); + } + } + data = fillChar("SPECTATING: " + nick, '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + data); + } else if (client.cells.length > 0) { + nick = fillChar(client.getFriendlyName(), ' ', gameServer.config.playerMaxNickLength); + cells = fillChar(client.cells.length, ' ', 5, true); + score = fillChar((client.getScore() / 100) >> 0, ' ', 6, true); + position = fillChar(client.centerPos.x >> 0, ' ', 5, true) + ', ' + fillChar(client.centerPos.y >> 0, ' ', 5, true); + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + nick + " | " + cells + " | " + score + " | " + position); + } else { + // No cells = dead player or in-menu + data = fillChar('DEAD OR NOT PLAYING', '-', ' | CELLS | SCORE | POSITION '.length + gameServer.config.playerMaxNickLength, true); + Logger.print(" " + id + " | " + ip + " | " + protocol + " | " + data); + } + } + }, + pause: function (gameServer, split) { + gameServer.run = !gameServer.run; // Switches the pause state + var s = gameServer.run ? "Unpaused" : "Paused"; + console.log(s + " the game."); + }, + reload: function (gameServer) { + gameServer.loadConfig(); + gameServer.loadIpBanList(); + console.log("Reloaded the config file successfully"); + }, + status: function (gameServer, split) { + // Get amount of humans/bots + var humans = 0, + bots = 0; + for (var i = 0; i < gameServer.clients.length; i++) { + if ('_socket' in gameServer.clients[i]) { + humans++; + } else { + bots++; + } + } + + console.log("Connected players: " + gameServer.clients.length + "/" + gameServer.config.serverMaxConnections); + console.log("Players: " + humans + " - Bots: " + bots); + console.log("Server has been running for " + Math.floor(process.uptime() / 60) + " minutes"); + console.log("Current memory usage: " + Math.round(process.memoryUsage().heapUsed / 1048576 * 10) / 10 + "/" + Math.round(process.memoryUsage().heapTotal / 1048576 * 10) / 10 + " mb"); + console.log("Current game mode: " + gameServer.gameMode.name); + console.log("Current update time: " + gameServer.updateTimeAvg.toFixed(3) + " [ms] (" + ini.getLagMessage(gameServer.updateTimeAvg) + ")"); + }, + tp: function (gameServer, split) { + var id = parseInt(split[1]); + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + + // Make sure the input values are numbers + var pos = { + x: parseInt(split[2]), + y: parseInt(split[3]) + }; + if (isNaN(pos.x) || isNaN(pos.y)) { + Logger.warn("Invalid coordinates"); + return; + } + + // Spawn + for (var i in gameServer.clients) { + if (gameServer.clients[i].playerTracker.pID == id) { + var client = gameServer.clients[i].playerTracker; + for (var j in client.cells) { + client.cells[j].setPosition(pos); + gameServer.updateNodeQuad(client.cells[j]); + } + + console.log("Teleported " + client.getFriendlyName() + " to (" + pos.x + " , " + pos.y + ")"); + break; + } + } + }, + virus: function (gameServer, split) { + var pos = { + x: parseInt(split[1]), + y: parseInt(split[2]) + }; + var mass = parseInt(split[3]); + + // Make sure the input values are numbers + if (isNaN(pos.x) || isNaN(pos.y)) { + Logger.warn("Invalid coordinates"); + return; + } + var size = gameServer.config.virusMinSize; + if (!isNaN(mass)) { + size = Math.sqrt(mass * 100); + } + + // Spawn + var v = new Entity.Virus(gameServer, null, pos, size); + gameServer.addNode(v); + console.log("Spawned 1 virus at (" + pos.x + " , " + pos.y + ")"); + }, + //Aliases + st: function (gameServer, split) { + Commands.list.status(gameServer, split); + }, + pl: function (gameServer, split) { + Commands.list.playerlist(gameServer, split); + } +}; diff --git a/src/modules/Logger.js b/src/modules/Logger.js index 97c058e8e..fce97ab72 100644 --- a/src/modules/Logger.js +++ b/src/modules/Logger.js @@ -7,35 +7,35 @@ * */ -var fs = require("fs"); -var util = require('util'); -var EOL = require('os').EOL; +var fs = require("fs"); +var util = require('util'); +var EOL = require('os').EOL; var LogLevelEnum = require('../enum/LogLevelEnum'); - -module.exports.debug = debug; -module.exports.info = info; -module.exports.warn = warn; -module.exports.error = error; -module.exports.fatal = fatal; -module.exports.print = print; -module.exports.write = write; -module.exports.writeDebug = writeDebug; -module.exports.writeError = writeError; -module.exports.start = start; -module.exports.shutdown = shutdown; + +module.exports.debug = debug; +module.exports.info = info; +module.exports.warn = warn; +module.exports.error = error; +module.exports.fatal = fatal; +module.exports.print = print; +module.exports.write = write; +module.exports.writeDebug = writeDebug; +module.exports.writeError = writeError; +module.exports.start = start; +module.exports.shutdown = shutdown; module.exports.setVerbosity = function (level) { logVerbosity = level; -}; +}; module.exports.setFileVerbosity = function (level) { logFileVerbosity = level; -}; +}; module.exports.getVerbosity = function () { return logVerbosity; -}; +}; module.exports.getFileVerbosity = function () { return logFileVerbosity; -}; +}; var logVerbosity = LogLevelEnum.DEBUG; @@ -87,74 +87,74 @@ function writeError(message) { // --- utils --- function getDateTimeString() { - var date = new Date(); - var dy = date.getFullYear(); - var dm = date.getMonth() + 1; - var dd = date.getDate(); - var th = date.getHours(); - var tm = date.getMinutes(); - var ts = date.getSeconds(); - var tz = date.getMilliseconds(); - dy = ("0000" + dy).slice(-4); - dm = ("00" + dm).slice(-2); - dd = ("00" + dd).slice(-2); - th = ("00" + th).slice(-2); - tm = ("00" + tm).slice(-2); - ts = ("00" + ts).slice(-2); - tz = ("000" + tz).slice(-3); - return dy + "-" + dm + "-" + dd + "T" + th + "-" + tm + "-" + ts + "-" + tz; + var date = new Date(); + var dy = date.getFullYear(); + var dm = date.getMonth() + 1; + var dd = date.getDate(); + var th = date.getHours(); + var tm = date.getMinutes(); + var ts = date.getSeconds(); + var tz = date.getMilliseconds(); + dy = ("0000" + dy).slice(-4); + dm = ("00" + dm).slice(-2); + dd = ("00" + dd).slice(-2); + th = ("00" + th).slice(-2); + tm = ("00" + tm).slice(-2); + ts = ("00" + ts).slice(-2); + tz = ("000" + tz).slice(-3); + return dy + "-" + dm + "-" + dd + "T" + th + "-" + tm + "-" + ts + "-" + tz; }; function getTimeString() { - var date = new Date(); - var th = date.getHours(); - var tm = date.getMinutes(); - var ts = date.getSeconds(); - th = ("00" + th).slice(-2); - tm = ("00" + tm).slice(-2); - ts = ("00" + ts).slice(-2); - return th + ":" + tm + ":" + ts; + var date = new Date(); + var th = date.getHours(); + var tm = date.getMinutes(); + var ts = date.getSeconds(); + th = ("00" + th).slice(-2); + tm = ("00" + tm).slice(-2); + ts = ("00" + ts).slice(-2); + return th + ":" + tm + ":" + ts; }; function writeCon(color, level, message) { - if (level > logVerbosity) return; + if (level > logVerbosity) return; message = util.format(message); - var prefix = ""; - if (level == LogLevelEnum.DEBUG) - prefix = "[DEBUG] "; - else if (level == LogLevelEnum.INFO) - prefix = "[INFO ] "; - else if (level == LogLevelEnum.WARN) - prefix = "[WARN ] "; - else if (level == LogLevelEnum.ERROR) - prefix = "[ERROR] "; - else if (level == LogLevelEnum.FATAL) - prefix = "[FATAL] "; - process.stdout.write(color + prefix + message + "\u001B[0m" + EOL); + var prefix = ""; + if (level == LogLevelEnum.DEBUG) + prefix = "[DEBUG] "; + else if (level == LogLevelEnum.INFO) + prefix = "[INFO ] "; + else if (level == LogLevelEnum.WARN) + prefix = "[WARN ] "; + else if (level == LogLevelEnum.ERROR) + prefix = "[ERROR] "; + else if (level == LogLevelEnum.FATAL) + prefix = "[FATAL] "; + process.stdout.write(color + prefix + message + "\u001B[0m" + EOL); }; function writeLog(level, message) { - if (level > logFileVerbosity || writeError) - return; + if (level > logFileVerbosity || writeError) + return; message = util.format(message); - var prefix = ""; - if (level == LogLevelEnum.DEBUG) - prefix = "[DEBUG]"; - else if (level == LogLevelEnum.INFO) - prefix = "[INFO ]"; - else if (level == LogLevelEnum.WARN) - prefix = "[WARN ]"; - else if (level == LogLevelEnum.ERROR) - prefix = "[ERROR]"; - else if (level == LogLevelEnum.FATAL) - prefix = "[FATAL]"; - else if (level == LogLevelEnum.NONE) - prefix = "[NONE ]"; - prefix += "[" + getTimeString() + "] "; - - writeQueue.push(prefix + message + EOL); - if (writeShutdown) { - flushSync(); + var prefix = ""; + if (level == LogLevelEnum.DEBUG) + prefix = "[DEBUG]"; + else if (level == LogLevelEnum.INFO) + prefix = "[INFO ]"; + else if (level == LogLevelEnum.WARN) + prefix = "[WARN ]"; + else if (level == LogLevelEnum.ERROR) + prefix = "[ERROR]"; + else if (level == LogLevelEnum.FATAL) + prefix = "[FATAL]"; + else if (level == LogLevelEnum.NONE) + prefix = "[NONE ]"; + prefix += "[" + getTimeString() + "] "; + + writeQueue.push(prefix + message + EOL); + if (writeShutdown) { + flushSync(); } else { if (writeCounter == 0) { flushAsync(); @@ -166,88 +166,88 @@ var writeError = false; var writeCounter = 0; var writeShutdown = false; var writeStarted = false; -var writeQueue = []; +var writeQueue = []; function flushAsync() { if (writeShutdown || consoleLog == null || writeQueue.length == 0) return; - writeCounter++; - consoleLog.write(writeQueue.shift(), function () { writeCounter--; flushAsync(); }); + writeCounter++; + consoleLog.write(writeQueue.shift(), function () { writeCounter--; flushAsync(); }); }; function flushSync() { try { var tail = ""; while (writeQueue.length > 0) { - tail += writeQueue.shift(); + tail += writeQueue.shift(); } var fileName = logFolder + "/" + logFileName + ".log"; fs.appendFileSync(fileName, tail); } catch (err) { - writeError = true; - writeCon(colorRed + colorBright, LogLevelEnum.ERROR, err.message); - writeCon(colorRed + colorBright, LogLevelEnum.ERROR, "Failed to append log file!"); + writeError = true; + writeCon(colorRed + colorBright, LogLevelEnum.ERROR, err.message); + writeCon(colorRed + colorBright, LogLevelEnum.ERROR, "Failed to append log file!"); } }; function start() { - if (writeStarted) - return; - writeStarted = true; + if (writeStarted) + return; + writeStarted = true; try { - console.log = function (message) { print(message); }; - - var timeString = getDateTimeString(); + console.log = function (message) { print(message); }; + + var timeString = getDateTimeString(); var fileName = logFolder + "/" + logFileName + ".log"; - var fileName2 = logBackupFolder + "/" + logFileName + "-" + timeString + ".log"; + var fileName2 = logBackupFolder + "/" + logFileName + "-" + timeString + ".log"; if (!fs.existsSync(logFolder)) { - // Make log folder - fs.mkdirSync(logFolder); + // Make log folder + fs.mkdirSync(logFolder); } else if (fs.existsSync(fileName)) { if (!fs.existsSync(logBackupFolder)) { - // Make log backup folder - fs.mkdirSync(logBackupFolder); - } - // Backup previous log - fs.renameSync(fileName, fileName2); - } + // Make log backup folder + fs.mkdirSync(logBackupFolder); + } + // Backup previous log + fs.renameSync(fileName, fileName2); + } fs.writeFileSync(fileName, "=== Started " + timeString + " ===" + EOL); - var file = fs.createWriteStream(fileName, { flags: 'a' }); + var file = fs.createWriteStream(fileName, { flags: 'a' }); file.on('open', function () { if (writeShutdown) { - file.close(); - return; - } - consoleLog = file; - flushAsync(); - }); + file.close(); + return; + } + consoleLog = file; + flushAsync(); + }); file.on('error', function (err) { - writeError = true; - consoleLog = null; - writeCon(colorRed + colorBright, LogLevelEnum.ERROR, err.message); - }); + writeError = true; + consoleLog = null; + writeCon(colorRed + colorBright, LogLevelEnum.ERROR, err.message); + }); } catch (err) { - writeError = true; + writeError = true; consoleLog = null; - writeCon(colorRed + colorBright, LogLevelEnum.ERROR, err.message); - } -} - + writeCon(colorRed + colorBright, LogLevelEnum.ERROR, err.message); + } +} + function shutdown() { writeShutdown = true; if (writeError) return; if (consoleLog != null) { - consoleLog.end(); - consoleLog.close(); - consoleLog.destroy(); - consoleLog = null; - } - writeQueue.push("=== Shutdown " + getDateTimeString() + " ===" + EOL); - flushSync(); -}; - + consoleLog.end(); + consoleLog.close(); + consoleLog.destroy(); + consoleLog = null; + } + writeQueue.push("=== Shutdown " + getDateTimeString() + " ===" + EOL); + flushSync(); +}; + var logFolder = "./logs"; var logBackupFolder = "./logs/LogBackup"; diff --git a/src/modules/PlayerCommand.js b/src/modules/PlayerCommand.js index 3d92976cb..c9c78efbb 100644 --- a/src/modules/PlayerCommand.js +++ b/src/modules/PlayerCommand.js @@ -1,100 +1,100 @@ var Entity = require('../entity'); -var Logger = require('./Logger'); +var Logger = require('./Logger'); var UserRoleEnum = require("../enum/UserRoleEnum"); - -function PlayerCommand(gameServer, playerTracker) { - this.gameServer = gameServer; - this.playerTracker = playerTracker; -} - -module.exports = PlayerCommand; - -PlayerCommand.prototype.writeLine = function (text) { - this.gameServer.sendChatMessage(null, this.playerTracker, text); -}; - -PlayerCommand.prototype.executeCommandLine = function (commandLine) { - if (!commandLine) return; - var command = commandLine; - var args = ""; - var index = commandLine.indexOf(' '); - if (index >= 0) { - command = commandLine.slice(0, index); - args = commandLine.slice(index+1, commandLine.length); - } - command = command.trim().toLowerCase(); - var execute = playerCommands[command]; - if (typeof execute == 'function') { - execute.bind(this)(args); - } else { - this.writeLine("Unknown command, type /help for command list"); - } -}; - -var playerCommands = { - help: function (args) { - this.writeLine("/skin %shark - change skin"); - this.writeLine("/kill - self kill"); - this.writeLine("/help - this command list"); - }, - skin: function (args) { - if (this.playerTracker.cells.length > 0) { - this.writeLine("Cannot change skin while player in game!"); - return; - } - var skinName = ""; - if (args) skinName = args.trim(); - if (skinName.length > 16) - skinName = skinName.slice(0, 16); - this.playerTracker.setSkin(skinName); - if (skinName == "") - this.writeLine("Your skin was removed"); - else - this.writeLine("Your skin set to " + skinName); - }, - kill: function (args) { - if (this.playerTracker.cells.length < 1) { - this.writeLine("You cannot kill yourself, because you're still not joined to the game!"); - return; - } - while (this.playerTracker.cells.length > 0) { - var cell = this.playerTracker.cells[0]; - this.gameServer.removeNode(cell); + +function PlayerCommand(gameServer, playerTracker) { + this.gameServer = gameServer; + this.playerTracker = playerTracker; +} + +module.exports = PlayerCommand; + +PlayerCommand.prototype.writeLine = function (text) { + this.gameServer.sendChatMessage(null, this.playerTracker, text); +}; + +PlayerCommand.prototype.executeCommandLine = function (commandLine) { + if (!commandLine) return; + var command = commandLine; + var args = ""; + var index = commandLine.indexOf(' '); + if (index >= 0) { + command = commandLine.slice(0, index); + args = commandLine.slice(index + 1, commandLine.length); + } + command = command.trim().toLowerCase(); + var execute = playerCommands[command]; + if (typeof execute == 'function') { + execute.bind(this)(args); + } else { + this.writeLine("Unknown command, type /help for command list"); + } +}; + +var playerCommands = { + help: function (args) { + this.writeLine("/skin %shark - change skin"); + this.writeLine("/kill - self kill"); + this.writeLine("/help - this command list"); + }, + skin: function (args) { + if (this.playerTracker.cells.length > 0) { + this.writeLine("Cannot change skin while player in game!"); + return; + } + var skinName = ""; + if (args) skinName = args.trim(); + if (skinName.length > 16) + skinName = skinName.slice(0, 16); + this.playerTracker.setSkin(skinName); + if (skinName == "") + this.writeLine("Your skin was removed"); + else + this.writeLine("Your skin set to " + skinName); + }, + kill: function (args) { + if (this.playerTracker.cells.length < 1) { + this.writeLine("You cannot kill yourself, because you're still not joined to the game!"); + return; + } + while (this.playerTracker.cells.length > 0) { + var cell = this.playerTracker.cells[0]; + this.gameServer.removeNode(cell); // replace with food var food = new Entity.Food(this.gameServer, null, cell.position, this.gameServer.config.playerMinSize); food.setColor(this.gameServer.getGrayColor(cell.getColor())); this.gameServer.addNode(food); - } - this.writeLine("You killed yourself"); - }, + } + this.writeLine("You killed yourself"); + }, login: function (args) { - var password = (args || "").trim(); + var password = (args || "").trim(); if (password.length < 1) { - this.writeLine("ERROR: missing password argument!"); - return; - } - var user = this.gameServer.userLogin(this.playerTracker.socket.remoteAddress, password); + this.writeLine("ERROR: missing password argument!"); + return; + } + var user = this.gameServer.userLogin(this.playerTracker.socket.remoteAddress, password); if (!user) { - this.writeLine("ERROR: login failed!"); - return; - } - Logger.write("LOGIN " + this.playerTracker.socket.remoteAddress + ":" + this.playerTracker.socket.remotePort + " as \"" + user.name + "\""); - this.playerTracker.userRole = user.role; - this.playerTracker.userAuth = user.name; - this.writeLine("Login done as \"" + user.name + "\""); - return; - }, + this.writeLine("ERROR: login failed!"); + return; + } + Logger.write("LOGIN " + this.playerTracker.socket.remoteAddress + ":" + this.playerTracker.socket.remotePort + " as \"" + user.name + "\""); + this.playerTracker.userRole = user.role; + this.playerTracker.userAuth = user.name; + this.writeLine("Login done as \"" + user.name + "\""); + return; + }, logout: function (args) { if (this.playerTracker.userRole == UserRoleEnum.GUEST) { - this.writeLine("ERROR: not logged in"); - return; - } - Logger.write("LOGOUT " + this.playerTracker.socket.remoteAddress + ":" + this.playerTracker.socket.remotePort + " as \"" + this.playerTracker.userAuth + "\""); - this.playerTracker.userRole = UserRoleEnum.GUEST; - this.playerTracker.userAuth = null; - this.writeLine("Logout done"); - } -}; - - + this.writeLine("ERROR: not logged in"); + return; + } + Logger.write("LOGOUT " + this.playerTracker.socket.remoteAddress + ":" + this.playerTracker.socket.remotePort + " as \"" + this.playerTracker.userAuth + "\""); + this.playerTracker.userRole = UserRoleEnum.GUEST; + this.playerTracker.userAuth = null; + this.writeLine("Logout done"); + } +}; + + diff --git a/src/modules/ini.js b/src/modules/ini.js index 0e2e1a86f..5f277ed48 100644 --- a/src/modules/ini.js +++ b/src/modules/ini.js @@ -1,227 +1,227 @@ -exports.parse = exports.decode = decode; -exports.stringify = exports.encode = encode; - -exports.safe = safe; -exports.unsafe = unsafe; -exports.getLagMessage = getLagMessage; - -var eol = process.platform === "win32" ? "\r\n" : "\n"; - -function encode(obj, opt) { - var children = [], - out = ""; - - if (typeof opt === "string") { - opt = { - section: opt, - whitespace: false - }; - } else { - opt = opt || {}; - opt.whitespace = opt.whitespace === true; - } - - var separator = " = "; - - Object.keys(obj).forEach(function(k, _, __) { - var val = obj[k]; - if (val && Array.isArray(val)) { - val.forEach(function(item) { - out += safe(k + "[]") + separator + safe(item) + "\n"; - }); - } else if (val && typeof val === "object") { - children.push(k); - } else { - out += safe(k) + separator + safe(val) + eol; - } - }); - - if (opt.section && out.length) { - out = "[" + safe(opt.section) + "]" + eol + out; - } - - children.forEach(function(k, _, __) { - var nk = dotSplit(k).join('\\.'); - var section = (opt.section ? opt.section + "." : "") + nk; - var child = encode(obj[k], { - section: section, - whitespace: opt.whitespace - }); - if (out.length && child.length) { - out += eol; - } - out += child; - }); - - return out; -} - -function dotSplit(str) { - return str.replace(/\1/g, '\u0002LITERAL\\1LITERAL\u0002') - .replace(/\\\./g, '\u0001') - .split(/\./).map(function(part) { - return part.replace(/\1/g, '\\.') - .replace(/\2LITERAL\\1LITERAL\2/g, '\u0001'); - }); -} - -function decode(str) { - var out = {}, - p = out, - state = "START", - // section |key = value - re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i, - lines = str.split(/[\r\n]+/g), - section = null; - - lines.forEach(function(line, _, __) { - var testLine = line.trim(); - - // skip empty lines or commented lines - if (!line || line.match(/^\s*[;#]/)) { - // skip commented lines - return; - } - // E.g. serverTimeout = 30 - // Returns ["serverTimeout = 30", undefined, "serverTimeout ", "= 30", "30"] - var match = line.match(re); - - if (!match) { - return; - } - - if (match[1] !== undefined) { - section = unsafe(match[1]); - p = out[section] = out[section] || {}; - return; - } - - var key = unsafe(match[2]), - value = match[3] ? unsafe((match[4] || "")) : true; - - // Convert keys with '[]' suffix to an array - if (key.length > 2 && key.slice(-2) === "[]") { - key = key.substring(0, key.length - 2); - if (!p[key]) { - p[key] = []; - } else if (!Array.isArray(p[key])) { - p[key] = [p[key]]; - } - } - - //// Mass to Size function catcher - if (startsWith(value, "massToSize(") && endsWith(value, ")")) { - // 11: length of "massToSize(" - var strValue = value.slice(11, value.length - 1).trim(); - value = Math.sqrt(parseFloat(strValue) * 100) + 0.5; - } - function startsWith(value, pattern) { - return value.length >= pattern.length && - value.indexOf(pattern) === 0; - }; - function endsWith(value, pattern) { - return value.length >= pattern.length && - value.lastIndexOf(pattern) === value.length - pattern.length; - }; - - // safeguard against resetting a previously defined - // array by accidentally forgetting the brackets - if (isNaN(value)) { +exports.parse = exports.decode = decode; +exports.stringify = exports.encode = encode; + +exports.safe = safe; +exports.unsafe = unsafe; +exports.getLagMessage = getLagMessage; + +var eol = process.platform === "win32" ? "\r\n" : "\n"; + +function encode(obj, opt) { + var children = [], + out = ""; + + if (typeof opt === "string") { + opt = { + section: opt, + whitespace: false + }; + } else { + opt = opt || {}; + opt.whitespace = opt.whitespace === true; + } + + var separator = " = "; + + Object.keys(obj).forEach(function (k, _, __) { + var val = obj[k]; + if (val && Array.isArray(val)) { + val.forEach(function (item) { + out += safe(k + "[]") + separator + safe(item) + "\n"; + }); + } else if (val && typeof val === "object") { + children.push(k); + } else { + out += safe(k) + separator + safe(val) + eol; + } + }); + + if (opt.section && out.length) { + out = "[" + safe(opt.section) + "]" + eol + out; + } + + children.forEach(function (k, _, __) { + var nk = dotSplit(k).join('\\.'); + var section = (opt.section ? opt.section + "." : "") + nk; + var child = encode(obj[k], { + section: section, + whitespace: opt.whitespace + }); + if (out.length && child.length) { + out += eol; + } + out += child; + }); + + return out; +} + +function dotSplit(str) { + return str.replace(/\1/g, '\u0002LITERAL\\1LITERAL\u0002') + .replace(/\\\./g, '\u0001') + .split(/\./).map(function (part) { + return part.replace(/\1/g, '\\.') + .replace(/\2LITERAL\\1LITERAL\2/g, '\u0001'); + }); +} + +function decode(str) { + var out = {}, + p = out, + state = "START", + // section |key = value + re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i, + lines = str.split(/[\r\n]+/g), + section = null; + + lines.forEach(function (line, _, __) { + var testLine = line.trim(); + + // skip empty lines or commented lines + if (!line || line.match(/^\s*[;#]/)) { + // skip commented lines + return; + } + // E.g. serverTimeout = 30 + // Returns ["serverTimeout = 30", undefined, "serverTimeout ", "= 30", "30"] + var match = line.match(re); + + if (!match) { + return; + } + + if (match[1] !== undefined) { + section = unsafe(match[1]); + p = out[section] = out[section] || {}; + return; + } + + var key = unsafe(match[2]), + value = match[3] ? unsafe((match[4] || "")) : true; + + // Convert keys with '[]' suffix to an array + if (key.length > 2 && key.slice(-2) === "[]") { + key = key.substring(0, key.length - 2); + if (!p[key]) { + p[key] = []; + } else if (!Array.isArray(p[key])) { + p[key] = [p[key]]; + } + } + + //// Mass to Size function catcher + if (startsWith(value, "massToSize(") && endsWith(value, ")")) { + // 11: length of "massToSize(" + var strValue = value.slice(11, value.length - 1).trim(); + value = Math.sqrt(parseFloat(strValue) * 100) + 0.5; + } + function startsWith(value, pattern) { + return value.length >= pattern.length && + value.indexOf(pattern) === 0; + }; + function endsWith(value, pattern) { + return value.length >= pattern.length && + value.lastIndexOf(pattern) === value.length - pattern.length; + }; + + // safeguard against resetting a previously defined + // array by accidentally forgetting the brackets + if (isNaN(value)) { p[key] = value; - } else if (isInt(value)) { - p[key] = parseInt(value); - } else { - p[key] = parseFloat(value); - } - }); - - // {a:{y:1},"a.b":{x:2}} --> {a:{y:1,b:{x:2}}} - // use a filter to return the keys that have to be deleted. - Object.keys(out).filter(function(k, _, __) { - if (!out[k] || typeof out[k] !== "object" || Array.isArray(out[k])) return false; - // see if the parent section is also an object. - // if so, add it to that, and mark this one for deletion - var parts = dotSplit(k), - p = out, - l = parts.pop(), - nl = l.replace(/\\\./g, '.'); - parts.forEach(function(part, _, __) { - if (!p[part] || typeof p[part] !== "object") { - p[part] = {}; - } - p = p[part]; - }); - if (p === out && nl === l) { - return false; - } - p[nl] = out[k]; - return true; - }).forEach(function(del, _, __) { - delete out[del]; - }); - - return out; -} - -function isQuoted(val) { - return (val.charAt(0) === "\"" && val.slice(-1) === "\"") || (val.charAt(0) === "'" && val.slice(-1) === "'"); -} - -function safe(val) { - return (typeof val !== "string" || val.match(/[=\r\n]/) || val.match(/^\[/) || (val.length > 1 && isQuoted(val)) || val !== val.trim()) ? JSON.stringify(val) : val.replace(/;/g, '\\;').replace(/#/g, "\\#"); -} - -function unsafe(val, doUnesc) { - val = (val || "").trim(); - if (isQuoted(val)) { - // remove the single quotes before calling JSON.parse - if (val.charAt(0) === "'") { - val = val.substr(1, val.length - 2); - } - try { - val = JSON.parse(val); - } catch (err) { + } else if (isInt(value)) { + p[key] = parseInt(value); + } else { + p[key] = parseFloat(value); + } + }); + + // {a:{y:1},"a.b":{x:2}} --> {a:{y:1,b:{x:2}}} + // use a filter to return the keys that have to be deleted. + Object.keys(out).filter(function (k, _, __) { + if (!out[k] || typeof out[k] !== "object" || Array.isArray(out[k])) return false; + // see if the parent section is also an object. + // if so, add it to that, and mark this one for deletion + var parts = dotSplit(k), + p = out, + l = parts.pop(), + nl = l.replace(/\\\./g, '.'); + parts.forEach(function (part, _, __) { + if (!p[part] || typeof p[part] !== "object") { + p[part] = {}; + } + p = p[part]; + }); + if (p === out && nl === l) { + return false; + } + p[nl] = out[k]; + return true; + }).forEach(function (del, _, __) { + delete out[del]; + }); + + return out; +} + +function isQuoted(val) { + return (val.charAt(0) === "\"" && val.slice(-1) === "\"") || (val.charAt(0) === "'" && val.slice(-1) === "'"); +} + +function safe(val) { + return (typeof val !== "string" || val.match(/[=\r\n]/) || val.match(/^\[/) || (val.length > 1 && isQuoted(val)) || val !== val.trim()) ? JSON.stringify(val) : val.replace(/;/g, '\\;').replace(/#/g, "\\#"); +} + +function unsafe(val, doUnesc) { + val = (val || "").trim(); + if (isQuoted(val)) { + // remove the single quotes before calling JSON.parse + if (val.charAt(0) === "'") { + val = val.substr(1, val.length - 2); + } + try { + val = JSON.parse(val); + } catch (err) { Logger.error(err.stack); - } - } else { - // walk the val to find the first not-escaped ; character - var esc = false; - var unesc = ""; - for (var i = 0, l = val.length; i < l; i++) { - var c = val.charAt(i); - if (esc) { - if ("\\;#".indexOf(c) !== -1) - unesc += c; - else - unesc += "\\" + c; - esc = false; - } else if (";#".indexOf(c) !== -1) { - break; - } else if (c === "\\") { - esc = true; - } else { - unesc += c; - } - } - if (esc) - unesc += "\\"; - return unesc; - } - return val; -} - -var isInt = function(n) { - return parseInt(n) == n; -}; - -function getLagMessage(updateTimeAvg){ - if (updateTimeAvg < 20) - return "perfectly smooth"; - if (updateTimeAvg < 35) - return "good"; - if (updateTimeAvg < 40) - return "tiny lag"; - if (updateTimeAvg < 50) - return "lag"; - return "extremely high lag"; -} + } + } else { + // walk the val to find the first not-escaped ; character + var esc = false; + var unesc = ""; + for (var i = 0, l = val.length; i < l; i++) { + var c = val.charAt(i); + if (esc) { + if ("\\;#".indexOf(c) !== -1) + unesc += c; + else + unesc += "\\" + c; + esc = false; + } else if (";#".indexOf(c) !== -1) { + break; + } else if (c === "\\") { + esc = true; + } else { + unesc += c; + } + } + if (esc) + unesc += "\\"; + return unesc; + } + return val; +} + +var isInt = function (n) { + return parseInt(n) == n; +}; + +function getLagMessage(updateTimeAvg) { + if (updateTimeAvg < 20) + return "perfectly smooth"; + if (updateTimeAvg < 35) + return "good"; + if (updateTimeAvg < 40) + return "tiny lag"; + if (updateTimeAvg < 50) + return "lag"; + return "extremely high lag"; +} diff --git a/src/packet/AddNode.js b/src/packet/AddNode.js index a8539d7c1..7fe644109 100644 --- a/src/packet/AddNode.js +++ b/src/packet/AddNode.js @@ -1,11 +1,11 @@ -function AddNode(playerTracker, item) { +function AddNode(playerTracker, item) { this.playerTracker = playerTracker; this.item = item; } module.exports = AddNode; -AddNode.prototype.build = function(protocol) { +AddNode.prototype.build = function (protocol) { var buffer = new Buffer(5); buffer.writeUInt8(0x20, 0, true); // Packet ID buffer.writeUInt32LE((this.item.nodeId ^ this.playerTracker.scrambleId) >>> 0, 1, true); diff --git a/src/packet/BinaryReader.js b/src/packet/BinaryReader.js index 43567514e..bb2cb58e4 100644 --- a/src/packet/BinaryReader.js +++ b/src/packet/BinaryReader.js @@ -107,12 +107,12 @@ BinaryReader.prototype.readStringZeroUtf8 = function () { BinaryReader.prototype.readStringZeroUnicode = function () { var length = 0; var terminatorLength = ((this._buffer.length - this._offset) & 1) != 0 ? 1 : 0; - for (var i = this._offset; i+1 < this._buffer.length; i+=2) { + for (var i = this._offset; i + 1 < this._buffer.length; i += 2) { if (this._buffer.readUInt16LE(i) == 0) { terminatorLength = 2; break; } - length+=2; + length += 2; } var value = this.readStringUnicode(length); this._offset += terminatorLength; diff --git a/src/packet/ChatMessage.js b/src/packet/ChatMessage.js index b5e15a7f1..b3a5fdaea 100644 --- a/src/packet/ChatMessage.js +++ b/src/packet/ChatMessage.js @@ -27,7 +27,7 @@ ChatMessage.prototype.build = function (protocol) { color = this.sender.cells[0].getColor(); } } - + var writer = new BinaryWriter(); writer.writeUInt8(0x63); // message id (decimal 99) @@ -39,7 +39,7 @@ ChatMessage.prototype.build = function (protocol) { flags = 0x40; // admin message else if (this.sender.userRole == UserRoleEnum.MODER) flags = 0x20; // moder message - + writer.writeUInt8(flags); writer.writeUInt8(color.r >> 0); writer.writeUInt8(color.g >> 0); diff --git a/src/packet/ClearAll.js b/src/packet/ClearAll.js index 59c4f742f..cdc24a1ff 100644 --- a/src/packet/ClearAll.js +++ b/src/packet/ClearAll.js @@ -1,4 +1,4 @@ -function ClearAll() { } +function ClearAll() { } module.exports = ClearAll; diff --git a/src/packet/DrawLine.js b/src/packet/DrawLine.js index e78091c33..9114d9fcc 100644 --- a/src/packet/DrawLine.js +++ b/src/packet/DrawLine.js @@ -1,11 +1,11 @@ -function DrawLine(x, y) { +function DrawLine(x, y) { this.x = x; this.y = y; } module.exports = DrawLine; -DrawLine.prototype.build = function(protocol) { +DrawLine.prototype.build = function (protocol) { var buffer = new Buffer(5); buffer.writeUInt8(0x15, 0); buffer.writeInt16LE(this.x, 1, true); diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index b1b599304..b80454417 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -1,4 +1,4 @@ -// Import +// Import var BinaryWriter = require("./BinaryWriter"); diff --git a/src/packet/UpdateLeaderboard.js b/src/packet/UpdateLeaderboard.js index 530ffcc7d..509992069 100644 --- a/src/packet/UpdateLeaderboard.js +++ b/src/packet/UpdateLeaderboard.js @@ -1,4 +1,4 @@ -// Import +// Import var BinaryWriter = require("./BinaryWriter"); @@ -62,13 +62,13 @@ UpdateLeaderboard.prototype.buildFfa5 = function () { for (var i = 0; i < this.leaderboard.length; i++) { var item = this.leaderboard[i]; if (item == null) return null; // bad leaderboardm just don't send it - + var name = item.getNameUnicode(); var id = 0; if (item == player && item.cells.length > 0) { id = item.cells[0].nodeId ^ this.playerTracker.scrambleId; } - + writer.writeUInt32(id >>> 0); // Player cell Id if (name != null) writer.writeBytes(name); diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index ec155382c..401fe3327 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -1,9 +1,9 @@ -// Import +// Import var BinaryWriter = require("./BinaryWriter"); function UpdateNodes(playerTracker, addNodes, updNodes, eatNodes, delNodes) { - this.playerTracker = playerTracker; + this.playerTracker = playerTracker; this.addNodes = addNodes; this.updNodes = updNodes; this.eatNodes = eatNodes; @@ -18,11 +18,11 @@ UpdateNodes.prototype.build = function (protocol) { var writer = new BinaryWriter(); writer.writeUInt8(0x10); // Packet ID this.writeEatItems(writer); - + if (protocol < 5) this.writeUpdateItems4(writer); else if (protocol == 5) this.writeUpdateItems5(writer); else this.writeUpdateItems6(writer); - + this.writeRemoveItems(writer, protocol); return writer.toBuffer(); }; @@ -32,7 +32,7 @@ UpdateNodes.prototype.writeUpdateItems4 = function (writer) { var scrambleX = this.playerTracker.scrambleX; var scrambleY = this.playerTracker.scrambleY; var scrambleId = this.playerTracker.scrambleId; - + for (var i = 0; i < this.updNodes.length; i++) { var node = this.updNodes[i]; if (node.nodeId == 0) diff --git a/src/packet/UpdatePosition.js b/src/packet/UpdatePosition.js index 7a0b4c9b7..c92a1110a 100644 --- a/src/packet/UpdatePosition.js +++ b/src/packet/UpdatePosition.js @@ -1,4 +1,4 @@ -function UpdatePosition(playerTracker, x, y, scale) { +function UpdatePosition(playerTracker, x, y, scale) { this.playerTracker = playerTracker, this.x = x; this.y = y; @@ -7,7 +7,7 @@ function UpdatePosition(playerTracker, x, y, scale) { module.exports = UpdatePosition; -UpdatePosition.prototype.build = function(protocol) { +UpdatePosition.prototype.build = function (protocol) { var buffer = new Buffer(13); var offset = 0; buffer.writeUInt8(0x11, offset, true); diff --git a/src/packet/index.js b/src/packet/index.js index a78ad28f5..5f4003785 100644 --- a/src/packet/index.js +++ b/src/packet/index.js @@ -1,4 +1,4 @@ -module.exports = { +module.exports = { BinaryWriter: require('./BinaryWriter'), BinaryReader: require('./BinaryReader'), ChatMessage: require('./ChatMessage'), diff --git a/src/userRoles.json b/src/userRoles.json index cf59cba76..ccbd07562 100644 --- a/src/userRoles.json +++ b/src/userRoles.json @@ -1,20 +1,20 @@ [ - { - "ip":"127.0.0.1", - "password":"", - "role":"ADMIN", - "name":"Local Administrator" - }, - { - "ip":"127.0.0.1", - "password":"", - "role":"MODER", - "name":"Local Moderator" - }, - { - "ip":"127.0.0.1", - "password":"", - "role":"USER", - "name":"Local User" - } -] \ No newline at end of file + { + "ip":"127.0.0.1", + "password":"", + "role":"ADMIN", + "name":"Local Administrator" + }, + { + "ip":"127.0.0.1", + "password":"", + "role":"MODER", + "name":"Local Moderator" + }, + { + "ip":"127.0.0.1", + "password":"", + "role":"USER", + "name":"Local User" + } +] From 5373873b6c38a9a711f14414d8695040af9d73ba Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 15 Jul 2016 08:46:53 +0200 Subject: [PATCH 384/417] add mute/unmute commands --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 15 +++++++++++++ src/PlayerTracker.js | 1 + src/modules/CommandList.js | 46 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2a34a8d0c..1ca8187eb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.43** +Current version: **1.2.44** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index fa3c5eaa2..9f11e58f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.43", + "version": "1.2.44", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index c1fd08217..14ecd38a2 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -554,6 +554,10 @@ GameServer.prototype.onChatMessage = function (from, to, message) { // chat is disabled return; } + if (from && from.isMuted) { + // player is muted + return; + } if (message.length > 64) { message = message.slice(0, 64); } @@ -1367,6 +1371,17 @@ GameServer.prototype.updateMassDecay = function () { } }; +GameServer.prototype.getPlayerById = function (id) { + if (id == null) return null; + for (var i = 0; i < this.clients.length; i++) { + var playerTracker = this.clients[i].playerTracker; + if (playerTracker.pID == id) { + return playerTracker; + } + } + return null; +}; + var fileNameConfig = './gameserver.ini'; var fileNameBadWords = './badwords.txt'; var fileNameIpBan = './ipbanlist.txt'; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index ef5a03e1e..feaee3e33 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -61,6 +61,7 @@ function PlayerTracker(gameServer, socket) { this.connectedTime = new Date; this.isMinion = false; this.spawnCounter = 0; + this.isMuted = false; // Gamemode function if (gameServer) { diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 000bc5996..547374170 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -42,6 +42,8 @@ Commands.list = { console.log("gamemode [id] : change server gamemode"); console.log("kick [PlayerID] : kick player or bot by client ID"); console.log("kickall : kick all players and bots"); + console.log("mute [PlayerID] : mute player (block chat messages from him)"); + console.log("unmute [PlayerID] : unmute player (allow chat messages from him)"); console.log("kill [PlayerID] : kill cell(s) by client ID"); console.log("killall : kill everyone"); console.log("mass [PlayerID] [mass] : set cell(s) mass by client ID"); @@ -309,6 +311,50 @@ Commands.list = { } gameServer.kickId(id); }, + mute: function (gameServer, args) { + if (!args || args.length < 2) { + Logger.warn("Please specify a valid player ID!"); + return; + } + var id = parseInt(args[1]); + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + var player = gameServer.getPlayerById(id); + if (player == null) { + Logger.warn("Player with id=" + id + " not found!"); + return; + } + if (player.isMuted) { + Logger.warn("Player with id=" + id + " already muted!"); + return; + } + Logger.print("Player \"" + player.getFriendlyName() + "\" were muted"); + player.isMuted = true; + }, + unmute: function (gameServer, args) { + if (!args || args.length < 2) { + Logger.warn("Please specify a valid player ID!"); + return; + } + var id = parseInt(args[1]); + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + var player = gameServer.getPlayerById(id); + if (player == null) { + Logger.warn("Player with id=" + id + " not found!"); + return; + } + if (!player.isMuted) { + Logger.warn("Player with id=" + id + " already not muted!"); + return; + } + Logger.print("Player \"" + player.getFriendlyName() + "\" were unmuted"); + player.isMuted = false; + }, kickall: function (gameServer, split) { gameServer.kickId(0); }, From 2d064f7589e265aac3746d14a62b8e24443721c3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 15 Jul 2016 09:20:12 +0200 Subject: [PATCH 385/417] add ejectDistance config value --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 3 +- src/gameserver.ini | 236 +++++++++++++++++++++++---------------------- 4 files changed, 123 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 1ca8187eb..3e99a45f7 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.44** +Current version: **1.2.45** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 9f11e58f7..c2977d784 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.44", + "version": "1.2.45", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 14ecd38a2..00701a6e8 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -103,6 +103,7 @@ function GameServer() { ejectSize: 38, // Size of ejected cells (vanilla 38) ejectSizeLoss: 43, // Eject size which will be substracted from player cell (vanilla 43?) + ejectDistance: 780, // vanilla 780 ejectCooldown: 3, // min ticks between ejects ejectSpawnPlayer: 1, // if 1 then player may be spawned from ejected mass @@ -1319,7 +1320,7 @@ GameServer.prototype.ejectMass = function (client) { var ejected = new Entity.EjectedMass(this, null, pos, size2); ejected.ejector = cell; ejected.setColor(cell.getColor()); - ejected.setBoost(780, angle); + ejected.setBoost(this.config.ejectDistance, angle); this.addNode(ejected); } diff --git a/src/gameserver.ini b/src/gameserver.ini index a2d04247f..bd1becbff 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -1,134 +1,136 @@ -# MultiOgar configurations file -# Lines starting with number sign (#) are comments - -# [NOTE] -# MultiOgar uses cell size instead of cell mass to improve performance! -# In order to get the cell size from mass value, you need to calculate using this formula: -# size = SQRT( mass * 100 ) -# -# For example, to set start mass = 43: -# size = SQRT( 43 * 100 ) = SQRT( 4300 ) = 65.57 -# Set playerStartSize = 66 -# -# Also, you can use the following syntax to specify mass: -# playerStartSize = massToSize(43) -# It will be automatically converted to 66 - -# [Log] -# logVerbosity: Console log level (0=NONE; 1=FATAL; 2=ERROR; 3=WARN; 4=INFO; 5=DEBUG) -# logFileVerbosity: File log level -logVerbosity = 4 -logFileVerbosity = 5 - -# [Server] -# serverTimeout: Seconds to keep connection alive for non-responding client -# serverIpLimit: Controls the maximum connections from single IP (use 0 to disable) +# MultiOgar configurations file +# Lines starting with number sign (#) are comments + +# [NOTE] +# MultiOgar uses cell size instead of cell mass to improve performance! +# In order to get the cell size from mass value, you need to calculate using this formula: +# size = SQRT( mass * 100 ) +# +# For example, to set start mass = 43: +# size = SQRT( 43 * 100 ) = SQRT( 4300 ) = 65.57 +# Set playerStartSize = 66 +# +# Also, you can use the following syntax to specify mass: +# playerStartSize = massToSize(43) +# It will be automatically converted to 66 + +# [Log] +# logVerbosity: Console log level (0=NONE; 1=FATAL; 2=ERROR; 3=WARN; 4=INFO; 5=DEBUG) +# logFileVerbosity: File log level +logVerbosity = 4 +logFileVerbosity = 5 + +# [Server] +# serverTimeout: Seconds to keep connection alive for non-responding client +# serverIpLimit: Controls the maximum connections from single IP (use 0 to disable) # serverMinionIgnoreTime: minion detection disable time on server startup [seconds] # serverMinionThreshold: max connections within serverMinionInterval time period, which will not be marked as minion # serverMinionInterval: minion detection interval [milliseconds] # serverPort: Server port which will be used to listen for incoming connections # serverBind: Server network interface which will be used to listen for incoming connections (0.0.0.0 for all IPv4 interfaces) # serverTracker: Set to 1 if you want to show your server on the tracker http://ogar.mivabe.nl/master (check that your server port is opened for external connections before setting it to 1) -# serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games -# serverBots: Number of player bots to spawn (Experimental) -# serverViewBase: Base view distance of players. Warning: high values may cause lag! Min value is 1920x1080 -# serverSpectatorScale: Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) -# serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. -# serverStatsUpdate: Update interval of server stats in seconds -# serverScrambleLevel: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap), 3 - high level scrambling (no border) -# serverMaxLB: Controls the maximum players displayed on the leaderboard. -# serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. -# serverName: Server name, for example "My great server" -# serverWelcome1: First server welcome message -# serverWelcome2: Second server welcome message (optional, for info, etc) +# serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games +# serverBots: Number of player bots to spawn (Experimental) +# serverViewBase: Base view distance of players. Warning: high values may cause lag! Min value is 1920x1080 +# serverSpectatorScale: Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) +# serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. +# serverStatsUpdate: Update interval of server stats in seconds +# serverScrambleLevel: Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap), 3 - high level scrambling (no border) +# serverMaxLB: Controls the maximum players displayed on the leaderboard. +# serverChat: Allows the usage of server chat. 0 = no chat, 1 = use chat. +# serverName: Server name, for example "My great server" +# serverWelcome1: First server welcome message +# serverWelcome2: Second server welcome message (optional, for info, etc) serverTimeout = 300 -serverMaxConnections = 128 +serverMaxConnections = 128 serverIpLimit = 4 serverMinionIgnoreTime = 30 serverMinionThreshold = 10 serverMinionInterval = 1000 -serverPort = 443 -serverBind = "0.0.0.0" +serverPort = 443 +serverBind = "0.0.0.0" serverTracker = 0 -serverGamemode = 0 -serverBots = 0 -serverViewBaseX = 1920 -serverViewBaseY = 1080 +serverGamemode = 0 +serverBots = 0 +serverViewBaseX = 1920 +serverViewBaseY = 1080 serverSpectatorScale = 0.4 -serverStatsPort = 88 -serverStatsUpdate = 60 -serverScrambleLevel = 1 -serverMaxLB = 10 -serverChat = 1 -serverName = "MultiOgar #1" -serverWelcome1 = "Welcome to MultiOgar server!" -serverWelcome2 = "" - -# [Border] -# Border size (vanilla 14142.135623730952) +serverStatsPort = 88 +serverStatsUpdate = 60 +serverScrambleLevel = 1 +serverMaxLB = 10 +serverChat = 1 +serverName = "MultiOgar #1" +serverWelcome1 = "Welcome to MultiOgar server!" +serverWelcome2 = "" + +# [Border] +# Border size (vanilla 14142.135623730952) borderWidth = 14142 borderHeight = 14142 - -# [Spawn] -# Each interval is 1 tick (40 ms) -# foodMinSize: vanilla 10 (mass = 10*10/100 = 1) -# foodMaxSize: vanilla 20 (mass = 20*20/100 = 4) -foodMinSize = 10 -foodMaxSize = 20 -foodMinAmount = 1000 -foodMaxAmount = 2000 -foodSpawnAmount = 30 -foodMassGrow = 1 -spawnInterval = 20 - -# virusMinSize: vanilla 100 (mass = 100*100/100 = 100) -# virusMaxSize: vanilla 140 (mass = 140*140/100 = 196) -virusMinSize = 100 + +# [Spawn] +# Each interval is 1 tick (40 ms) +# foodMinSize: vanilla 10 (mass = 10*10/100 = 1) +# foodMaxSize: vanilla 20 (mass = 20*20/100 = 4) +foodMinSize = 10 +foodMaxSize = 20 +foodMinAmount = 1000 +foodMaxAmount = 2000 +foodSpawnAmount = 30 +foodMassGrow = 1 +spawnInterval = 20 + +# virusMinSize: vanilla 100 (mass = 100*100/100 = 100) +# virusMaxSize: vanilla 140 (mass = 140*140/100 = 196) +virusMinSize = 100 virusMaxSize = 140 -virusMinAmount = 50 -virusMaxAmount = 100 - -# [Ejected Mass] -# ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) -# ejectSizeLoss: Eject size which will be substracted from player cell (vanilla 43?) -# ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) -# ejectSpawnPlayer: if 1 then player may be spawned from ejected mass -ejectSize = 38 -ejectSizeLoss = 43 -ejectCooldown = 3 -ejectSpawnPlayer = 1 - -# [Player] -# Reminder: MultiOgar uses cell size instead of mass! -# playerStartMass replaced with playerStartSize -# -# playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) -# playerMaxSize: vanilla 1500 (mass = 1500*1500/100 = 22500) -# playerMinSplitSize: vanilla 60 (mass = 60*60/100 = 36) +virusMinAmount = 50 +virusMaxAmount = 100 + +# [Ejected Mass] +# ejectSize: vanilla 37 (mass = 37*37/100 = 13.69) +# ejectSizeLoss: Eject size which will be substracted from player cell (vanilla 43?) +# ejectDistance: vanilla 780 +# ejectCooldown: Tick count until a player can eject mass again (1 tick = 40 ms) +# ejectSpawnPlayer: if 1 then player may be spawned from ejected mass +ejectSize = 38 +ejectSizeLoss = 43 +ejectDistance = 780 +ejectCooldown = 3 +ejectSpawnPlayer = 1 + +# [Player] +# Reminder: MultiOgar uses cell size instead of mass! +# playerStartMass replaced with playerStartSize +# +# playerMinSize: vanilla 32 (mass = 32*32/100 = 10.24) +# playerMaxSize: vanilla 1500 (mass = 1500*1500/100 = 22500) +# playerMinSplitSize: vanilla 60 (mass = 60*60/100 = 36) # playerStartSize: Start size of the player cell (mass = 64*64/100 = 41) -# playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) -# playerRecombineTime: Base time in seconds before a cell is allowed to recombine -# playerDecayRate: Amount of size lost per second -# playerDisconnectTime: Time in seconds before a disconnected player's cell is removed (Set to -1 to never remove) -playerMinSize = 32 -playerMaxSize = 1500 -playerMinSplitSize = 60 -playerStartSize = 64 -playerMaxCells = 16 -playerSpeed = 1 -playerDecayRate = .002 -playerRecombineTime = 30 -playerMaxNickLength = 15 -playerDisconnectTime = 60 - -# [Gamemode] -# Custom gamemode settings -# tourneyTimeLimit: Time limit of the game, in minutes. -# tourneyAutoFill: If set to a value higher than 0, the tournament match will automatically fill up with bots after value seconds -# tourneyAutoFillPlayers: The timer for filling the server with bots will not count down unless there is this amount of real players -tourneyMaxPlayers = 12 -tourneyPrepTime = 10 -tourneyEndTime = 30 -tourneyTimeLimit = 20 -tourneyAutoFill = 0 -tourneyAutoFillPlayers = 1 +# playerSpeed: Player speed multiplier (1=normal speed, 2=twice faster) +# playerRecombineTime: Base time in seconds before a cell is allowed to recombine +# playerDecayRate: Amount of size lost per second +# playerDisconnectTime: Time in seconds before a disconnected player's cell is removed (Set to -1 to never remove) +playerMinSize = 32 +playerMaxSize = 1500 +playerMinSplitSize = 60 +playerStartSize = 64 +playerMaxCells = 16 +playerSpeed = 1 +playerDecayRate = .002 +playerRecombineTime = 30 +playerMaxNickLength = 15 +playerDisconnectTime = 60 + +# [Gamemode] +# Custom gamemode settings +# tourneyTimeLimit: Time limit of the game, in minutes. +# tourneyAutoFill: If set to a value higher than 0, the tournament match will automatically fill up with bots after value seconds +# tourneyAutoFillPlayers: The timer for filling the server with bots will not count down unless there is this amount of real players +tourneyMaxPlayers = 12 +tourneyPrepTime = 10 +tourneyEndTime = 30 +tourneyTimeLimit = 20 +tourneyAutoFill = 0 +tourneyAutoFillPlayers = 1 From 413d70009ee61f7d7229f58a8056f0a7e3aa6ecb Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 15 Jul 2016 10:03:34 +0200 Subject: [PATCH 386/417] reduce socket write errors on client disconnection --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 5 +++-- src/PlayerTracker.js | 6 +++++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3e99a45f7..43e091a0c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.45** +Current version: **1.2.46** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index c2977d784..4afb717a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.45", + "version": "1.2.46", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 00701a6e8..cc600ef44 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1721,9 +1721,10 @@ GameServer.prototype.getStats = function () { // Custom prototype functions WebSocket.prototype.sendPacket = function (packet) { if (packet == null) return; - - //if (this.readyState == WebSocket.OPEN && (this._socket.bufferSize == 0) && packet.build) { if (this.readyState == WebSocket.OPEN) { + if (!this._socket.writable) { + return; + } var buffer = packet.build(this.playerTracker.socket.packetHandler.protocol); if (buffer != null) { this.send(buffer, { binary: true }); diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index feaee3e33..d8a872c55 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -314,7 +314,11 @@ PlayerTracker.prototype.updateTick = function () { }; PlayerTracker.prototype.sendUpdate = function () { - if (this.isRemoved || !this.socket.isConnected || !this.socket.packetHandler.protocol) { + if (this.isRemoved|| + !this.socket.packetHandler.protocol || + !this.socket.isConnected || + !this.socket._socket.writable || + this.socket.readyState != this.socket.OPEN) { // do not send update for disconnected clients // also do not send if initialization is not complete yet return; From 4e89a345ed6334a3bca18b732653bf54bc757568 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 15 Jul 2016 10:10:41 +0200 Subject: [PATCH 387/417] default scramble level 2 --- src/GameServer.js | 2 +- src/gameserver.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index cc600ef44..789e27bea 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -78,7 +78,7 @@ function GameServer() { serverSpectatorScale: 0.4, // Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Update interval of server stats in seconds - serverScrambleLevel: 1, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap); 3 - high scrambling (no border) + serverScrambleLevel: 2, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap); 3 - high scrambling (no border) serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. serverName: 'MultiOgar #1', // Server name diff --git a/src/gameserver.ini b/src/gameserver.ini index bd1becbff..8dfb15cae 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -57,7 +57,7 @@ serverViewBaseY = 1080 serverSpectatorScale = 0.4 serverStatsPort = 88 serverStatsUpdate = 60 -serverScrambleLevel = 1 +serverScrambleLevel = 2 serverMaxLB = 10 serverChat = 1 serverName = "MultiOgar #1" From 395c14f05c98f934cc60064e0e57d8c9911b3182 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 15 Jul 2016 11:47:10 +0200 Subject: [PATCH 388/417] block non-ascii chat messages --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 9 +++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 43e091a0c..72b98c236 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.46** +Current version: **1.2.47** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 4afb717a9..53f4a4f64 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.46", + "version": "1.2.47", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 789e27bea..09c468f3e 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -562,6 +562,15 @@ GameServer.prototype.onChatMessage = function (from, to, message) { if (message.length > 64) { message = message.slice(0, 64); } + for (var i = 0; i < message.length; i++) { + var c = message.charCodeAt(i); + if (c < 0x20 || c > 0x7F) { + if (from) { + this.sendChatMessage(null, from, "You can use ASCII text only!"); + } + return; + } + } if (this.checkBadWord(message)) { if (from) { this.sendChatMessage(null, from, "Stop insulting others! Keep calm and be friendly please"); From 7663328d0a8115720d718d4a9958a3fe857291a5 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 15 Jul 2016 12:31:25 +0200 Subject: [PATCH 389/417] update readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 72b98c236..e2b092d66 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ vps.simonorj.com:24270 | Montreal | Instant Merge | https://redd.it/4mufge ## What's new: +* 1.2.47: Improved stability and performance; added mute/unmute command * Added support for secure websocket connections (TLS) * Fixed mass decay * Added ejectSizeLoss From 27fe170f2e4c4ee8f195b43ccd54bbace08a8100 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 16 Jul 2016 07:02:55 +0200 Subject: [PATCH 390/417] add serverChatAscii to block non english letters; add server console command "skin" --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 16 ++++++++++------ src/gameserver.ini | 1 + src/modules/CommandList.js | 26 ++++++++++++++++++++++++++ 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e2b092d66..3d56491b8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.47** +Current version: **1.2.48** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 53f4a4f64..75591c5d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.47", + "version": "1.2.48", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 09c468f3e..3eb93b079 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -81,6 +81,8 @@ function GameServer() { serverScrambleLevel: 2, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap); 3 - high scrambling (no border) serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. + serverChatAscii: 1, // Set to 1 to disable non-ANSI letters in the chat (english only mode) + serverName: 'MultiOgar #1', // Server name serverWelcome1: 'Welcome to MultiOgar server!', // First server welcome message serverWelcome2: '', // Second server welcome message (for info, etc) @@ -562,13 +564,15 @@ GameServer.prototype.onChatMessage = function (from, to, message) { if (message.length > 64) { message = message.slice(0, 64); } - for (var i = 0; i < message.length; i++) { - var c = message.charCodeAt(i); - if (c < 0x20 || c > 0x7F) { - if (from) { - this.sendChatMessage(null, from, "You can use ASCII text only!"); + if (this.config.serverChatAscii) { + for (var i = 0; i < message.length; i++) { + var c = message.charCodeAt(i); + if (c < 0x20 || c > 0x7F) { + if (from) { + this.sendChatMessage(null, from, "You can use ASCII text only!"); + } + return; } - return; } } if (this.checkBadWord(message)) { diff --git a/src/gameserver.ini b/src/gameserver.ini index 8dfb15cae..5cbb47357 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -60,6 +60,7 @@ serverStatsUpdate = 60 serverScrambleLevel = 2 serverMaxLB = 10 serverChat = 1 +serverChatAscii = 1 serverName = "MultiOgar #1" serverWelcome1 = "Welcome to MultiOgar server!" serverWelcome2 = "" diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index 547374170..af7b8ed9d 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -48,6 +48,7 @@ Commands.list = { console.log("killall : kill everyone"); console.log("mass [PlayerID] [mass] : set cell(s) mass by client ID"); console.log("merge [PlayerID] : merge all client's cells once"); + console.log("skin [PlayerID] [SkinName] : change player skin"); console.log("name [PlayerID] [name] : change cell(s) name by client ID"); console.log("playerlist : get list of players and bots"); console.log("pause : pause game , freeze all cells"); @@ -419,6 +420,31 @@ Commands.list = { } } }, + skin: function (gameServer, args) { + if (!args || args.length < 3) { + Logger.warn("Please specify a valid player ID and skin name!"); + return; + } + var id = parseInt(args[1]); + if (isNaN(id)) { + Logger.warn("Please specify a valid player ID!"); + return; + } + var skin = args[2].trim(); + if (!skin) { + Logger.warn("Please specify skin name!"); + } + var player = gameServer.getPlayerById(id); + if (player == null) { + Logger.warn("Player with id=" + id + " not found!"); + return; + } + if (player.cells.length > 0) { + Logger.warn("Player is alive, skin will not be applied to existing cells"); + } + Logger.print("Player \"" + player.getFriendlyName() + "\"'s skin is changed to " + skin); + player.setSkin(skin); + }, merge: function (gameServer, split) { // Validation checks var id = parseInt(split[1]); From 7ddca513553510d5df51a59c42548e0e60bd2032 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 17 Jul 2016 23:16:56 +0200 Subject: [PATCH 391/417] fix server crash on disconnect race condition --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3d56491b8..bf88e6145 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.48** +Current version: **1.2.49** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 75591c5d9..c34ab8ffa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.48", + "version": "1.2.49", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 3eb93b079..9091b6ccc 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1745,7 +1745,6 @@ WebSocket.prototype.sendPacket = function (packet) { } else { this.readyState = WebSocket.CLOSED; this.emit('close'); - this.removeAllListeners(); } }; From a047e82289121597e34e16a528d9d9526ae37411 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 18 Jul 2016 11:03:36 +0200 Subject: [PATCH 392/417] add validation for skin name and player command --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 20 ++++++++++++++++++++ src/PacketHandler.js | 28 +++++++++++++++++++--------- src/modules/PlayerCommand.js | 29 +++++++++++++++++++++++++---- 5 files changed, 66 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index bf88e6145..d5bffe54c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.49** +Current version: **1.2.50** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index c34ab8ffa..10853a580 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.49", + "version": "1.2.50", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 9091b6ccc..e173dad28 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1396,6 +1396,25 @@ GameServer.prototype.getPlayerById = function (id) { return null; }; +GameServer.prototype.checkSkinName = function (skinName) { + if (!skinName) { + return true; + } + if (skinName.length == 1 || skinName.length > 16) { + return false; + } + if (skinName[0] != '%' /* && skinName[0] != ':' */) { + return false; + } + for (var i = 1; i < skinName.length; i++) { + var c = skinName.charCodeAt(i); + if (c < 0x21 || c > 0x7F || c == '/' || c == '\\' || c == ':' || c == '%' || c == '?' || c == '&' || c == '<' || c == '>') { + return false; + } + } + return true; +}; + var fileNameConfig = './gameserver.ini'; var fileNameBadWords = './badwords.txt'; var fileNameIpBan = './ipbanlist.txt'; @@ -1449,6 +1468,7 @@ GameServer.prototype.loadBadWords = function () { }; GameServer.prototype.checkBadWord = function (value) { + if (!value) return false; value = value.toLowerCase().trim(); if (!value) return false; for (var i = 0; i < this.badWords.length; i++) { diff --git a/src/PacketHandler.js b/src/PacketHandler.js index d10221257..3bb86e187 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -166,17 +166,27 @@ PacketHandler.prototype.handleMessage = function (message) { PacketHandler.prototype.setNickname = function (text) { var name = ""; var skin = null; - if (text != null && text.length != 0) { + if (text != null && text.length > 0) { + var skinName = null; + var userName = text; var n = -1; - if (text.charAt(0) == '<' && (n = text.indexOf('>', 1)) >= 0) { - skin = "%" + text.slice(1, n); - name = text.slice(n + 1); - //} else if (text[0] == "|" && (n = text.indexOf("|", 1)) >= 0) { - // skin = ":http://i.imgur.com/" + text.slice(1, n) + ".png"; - // name = text.slice(n + 1); - } else { - name = text; + if (text[0] == '<' && (n = text.indexOf('>', 1)) >= 1) { + if (n > 1) + skinName = "%" + text.slice(1, n); + else + skinName = ""; + userName = text.slice(n + 1); + } + //else if (text[0] == "|" && (n = text.indexOf('|', 1)) >= 0) { + // skinName = ":http://i.imgur.com/" + text.slice(1, n) + ".png"; + // userName = text.slice(n + 1); + //} + if (skinName && !this.gameServer.checkSkinName(skinName)) { + skinName = null; + userName = text; } + skin = skinName; + name = userName; } if (name.length > this.gameServer.config.playerMaxNickLength) { name = name.substring(0, this.gameServer.config.playerMaxNickLength); diff --git a/src/modules/PlayerCommand.js b/src/modules/PlayerCommand.js index c9c78efbb..524b8e665 100644 --- a/src/modules/PlayerCommand.js +++ b/src/modules/PlayerCommand.js @@ -3,6 +3,10 @@ var Logger = require('./Logger'); var UserRoleEnum = require("../enum/UserRoleEnum"); +var ErrorTextInvalidCommand = "ERROR: Unknown command, type /help for command list"; +var ErrorTextBadCommand = "ERROR: Bad command!"; + + function PlayerCommand(gameServer, playerTracker) { this.gameServer = gameServer; this.playerTracker = playerTracker; @@ -24,11 +28,26 @@ PlayerCommand.prototype.executeCommandLine = function (commandLine) { args = commandLine.slice(index + 1, commandLine.length); } command = command.trim().toLowerCase(); + if (command.length > 16) { + this.writeLine(ErrorTextInvalidCommand); + return; + } + for (var i = 0; i < command.length; i++) { + var c = command.charCodeAt(i); + if (c < 0x21 || c > 0x7F) { + this.writeLine(ErrorTextInvalidCommand); + return; + } + } + if (!playerCommands.hasOwnProperty(command)) { + this.writeLine(ErrorTextInvalidCommand); + return; + } var execute = playerCommands[command]; if (typeof execute == 'function') { execute.bind(this)(args); } else { - this.writeLine("Unknown command, type /help for command list"); + this.writeLine(ErrorTextBadCommand); } }; @@ -40,13 +59,15 @@ var playerCommands = { }, skin: function (args) { if (this.playerTracker.cells.length > 0) { - this.writeLine("Cannot change skin while player in game!"); + this.writeLine("ERROR: Cannot change skin while player in game!"); return; } var skinName = ""; if (args) skinName = args.trim(); - if (skinName.length > 16) - skinName = skinName.slice(0, 16); + if (!this.gameServer.checkSkinName(skinName)) { + this.writeLine("ERROR: Invalid skin name!"); + return; + } this.playerTracker.setSkin(skinName); if (skinName == "") this.writeLine("Your skin was removed"); From a0f9c4eb0b2ee89dfeaf98c7eb2d8425810a9735 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 19 Jul 2016 00:08:31 +0200 Subject: [PATCH 393/417] extend skin name limit to 24 letters (tender_heart_bear doesn't fit in 15) --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d5bffe54c..ce2123443 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.50** +Current version: **1.2.51** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 10853a580..df0887f8f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.50", + "version": "1.2.51", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index e173dad28..678bf7176 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1400,7 +1400,7 @@ GameServer.prototype.checkSkinName = function (skinName) { if (!skinName) { return true; } - if (skinName.length == 1 || skinName.length > 16) { + if (skinName.length == 1 || skinName.length > 25) { return false; } if (skinName[0] != '%' /* && skinName[0] != ':' */) { From 2cbf52720bea46b947f81e7b9e3471733caff586 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 24 Jul 2016 01:27:19 +0200 Subject: [PATCH 394/417] add heapdump command --- README.md | 2 +- package.json | 2 +- src/modules/CommandList.js | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ce2123443..3acc22c9e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.51** +Current version: **1.2.52** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index df0887f8f..301856492 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.51", + "version": "1.2.52", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/modules/CommandList.js b/src/modules/CommandList.js index af7b8ed9d..265887edb 100644 --- a/src/modules/CommandList.js +++ b/src/modules/CommandList.js @@ -3,6 +3,7 @@ var GameMode = require('../gamemodes'); var Entity = require('../entity'); var ini = require('./ini.js'); var Logger = require('./Logger'); +var heapdump = null; function Commands() { this.list = {}; // Empty @@ -683,5 +684,30 @@ Commands.list = { }, pl: function (gameServer, split) { Commands.list.playerlist(gameServer, split); + }, + heapdump: function (gameServer, args) { + if (heapdump == null) { + function tryLoadModule(name) { + try { + return require(name); + } catch (err) { + if (err.code === 'MODULE_NOT_FOUND') + return null; + Logger.error(err); + } + return null; + } + heapdump = tryLoadModule('heapdump'); + } + if (heapdump == null) { + Logger.warn("heapdump module not installed!"); + return; + } + heapdump.writeSnapshot(function (err, filename) { + if (err) + Logger.error(err); + else + Logger.print('heapdump written to ' + filename); + }); } }; From ee9b6e41653adbc72115bfa43dc969b4c117eb73 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 25 Jul 2016 03:28:53 +0200 Subject: [PATCH 395/417] add support for ogar-tracker.tk --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 111 +++++++++++++++++++++++++++++----------------- 3 files changed, 72 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 3acc22c9e..e58948429 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.52** +Current version: **1.2.53** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 301856492..bf1769d67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.52", + "version": "1.2.53", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 678bf7176..4aea1ccbe 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -659,7 +659,7 @@ GameServer.prototype.mainLoop = function () { } // ping server tracker - if (this.config.serverTracker && (this.getTick() % (30000 / 40)) == 0) { + if (this.config.serverTracker && (this.getTick() % (10000 / 40)) == 0) { // once per 30 seconds this.pingServerTracker(); } @@ -1776,17 +1776,53 @@ GameServer.prototype.pingServerTracker = function () { var totalPlayers = 0; var alivePlayers = 0; var spectatePlayers = 0; + var robotPlayers = 0; for (var i = 0; i < this.clients.length; i++) { var socket = this.clients[i]; - if (socket == null || !socket.isConnected) + if (socket == null || socket.isConnected === false) continue; - totalPlayers++; - if (socket.playerTracker.cells.length > 0) - alivePlayers++; - else - spectatePlayers++; + if (socket.isConnected == null) { + robotPlayers++; + } + else { + totalPlayers++; + if (socket.playerTracker.cells.length > 0) + alivePlayers++; + else + spectatePlayers++; + } } - /* Sending Ping */ + + // Send Ping... + + // ogar-tracker.tk + var obj = { + port: this.config.serverPort, // [mandatory] web socket port which listens for game client connections + name: this.config.serverName, // [mandatory] server name + mode: this.gameMode.name, // [mandatory] game mode + total: totalPlayers, // [mandatory] total online players (server bots is not included!) + alive: alivePlayers, // [mandatory] alive players (server bots is not included!) + spect: spectatePlayers, // [mandatory] spectate players (server bots is not included!) + robot: robotPlayers, // [mandatory] server bots + limit: this.config.serverMaxConnections, // [mandatory] maximum allowed connection count + protocol: 'M', // [mandatory] required protocol id or 'M' for multiprotocol (if all protocols is supported) + uptime: process.uptime() >>> 0, // [mandatory] server uptime [seconds] + w: this.border.width >>> 0, // [mandatory] map border width [integer] + h: this.border.height >>> 0, // [mandatory] map border height [integer] + version: 'MultiOgar ' + pjson.version, // [optional] server version + stpavg: this.updateTimeAvg >>> 0, // [optional] average server loop time + chat: this.config.serverChat ? 1 : 0, // [optional] 0 - chat disabled, 1 - chat enabled + os: os.platform() // [optional] operating system + }; + trackerRequest({ + host: 'ogar-tracker.tk', + port: 80, + path: '/api/ping', + method: 'PUT' + }, 'application/json', JSON.stringify(obj)); + + + // mivabe.nl // Why don't just to use JSON? var data = 'current_players=' + totalPlayers + '&alive=' + alivePlayers + @@ -1800,45 +1836,38 @@ GameServer.prototype.pingServerTracker = function () { '&uptime=' + process.uptime() + // Number of seconds server has been running '&version=MultiOgar ' + pjson.version + '&start_time=' + this.startTime; - var options1 = { + trackerRequest({ host: 'ogar.mivabe.nl', - port: '80', + port: 80, path: '/master', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': Buffer.byteLength(data) - } - }; - var req = http.request(options1, function (res) { - if (res.statusCode != 200) { - Logger.writeError("Tracker Error(" + options1.host + "): " + res.statusCode); - } - }); - req.on('error', function (e) { - Logger.writeError("Tracker Error(" + options1.host + "): " + e.message); - }); - req.write(data); - req.end() - var options2 = { + method: 'POST' + }, 'application/x-www-form-urlencoded', data); + + // c0nsume.me + trackerRequest({ host: 'c0nsume.me', - port: '80', + port: 80, path: '/tracker.php', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': Buffer.byteLength(data) - } - }; - var req = http.request(options2, function (res) { + method: 'POST' + }, 'application/x-www-form-urlencoded', data); +}; + +function trackerRequest(options, type, body) { + if (options.headers == null) + options.headers = {}; + options.headers['user-agent'] = 'MultiOgar' + pjson.version; + options.headers['content-type'] = type; + options.headers['content-length'] = body == null ? 0 : Buffer.byteLength(body, 'utf8'); + var req = http.request(options, function (res) { if (res.statusCode != 200) { - Logger.writeError("Tracker Error(" + options2.host + "): " + res.statusCode); + Logger.writeError("[Tracker][" + options.host + "]: statusCode = " + res.statusCode); + return; } + res.setEncoding('utf8'); }); - req.on('error', function (e) { - Logger.writeError("Tracker Error(" + options2.host + "): " + e.message); + req.on('error', function (err) { + Logger.writeError("[Tracker][" + options.host + "]: " + err); }); - req.write(data); + req.write(body); req.end() -}; - +}; \ No newline at end of file From 7c8ec3c247b4d4a84fa608177bc85de6897e03dd Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 25 Jul 2016 04:18:05 +0200 Subject: [PATCH 396/417] update readme --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e58948429..08f301f80 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,11 @@ This lists Ogar clients and server trackers that I found on internet. ###Ogar server trackers +Welcome to http://ogar-tracker.tk :) + URL | Description --- | --- +http://ogar-tracker.tk | Ogar tracker http://ogar.mivabe.nl/master | MivaBe, tracks a lot of servers http://c0nsume.me/tracker.php | c0nsume.me server tracker From 69f4428f7d796d40bb785e2c0517216d9cccca49 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 25 Jul 2016 20:17:18 +0200 Subject: [PATCH 397/417] protocol optimizations / reduce lags / better protocol validation --- README.md | 2 +- package.json | 2 +- src/PacketHandler.js | 125 ++++++++++++++++++++++++++++++------------- 3 files changed, 89 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 08f301f80..cb57655de 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.53** +Current version: **1.2.54** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index bf1769d67..34e3cb55e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.53", + "version": "1.2.54", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 3bb86e187..1e737b6d8 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -6,8 +6,11 @@ function PacketHandler(gameServer, socket) { this.gameServer = gameServer; this.socket = socket; this.protocol = 0; - this.isHandshakePassed = false; + this.handshakeProtocol = null; + this.handshakeKey = null; this.lastChatTick = 0; + this.lastJoinTick = 0; + this.lastMouseTick = 0; this.pressQ = false; this.pressW = false; @@ -27,37 +30,8 @@ PacketHandler.prototype.handleMessage = function (message) { this.socket.close(1009, "Spam"); return; } - - // no handshake? - if (!this.isHandshakePassed) { - if (message[0] != 254 || message.length != 5) { - // wait handshake - return; - } - - var reader = new BinaryReader(message); - reader.skipBytes(1); - - // Handshake request - this.protocol = reader.readUInt32(); - if (this.protocol < 1 || this.protocol > 8) { - this.socket.close(1002, "Not supported protocol"); - return; - } - // Send handshake response - this.socket.sendPacket(new Packet.ClearAll()); - this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, this.gameServer.border, this.gameServer.config.serverGamemode, "MultiOgar " + pjson.version)); - // Send welcome message - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "MultiOgar " + pjson.version); - if (this.gameServer.config.serverWelcome1) - this.gameServer.sendChatMessage(null, this.socket.playerTracker, this.gameServer.config.serverWelcome1); - if (this.gameServer.config.serverWelcome2) - this.gameServer.sendChatMessage(null, this.socket.playerTracker, this.gameServer.config.serverWelcome2); - if (this.gameServer.config.serverChat == 0) - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "This server's chat is disabled."); - if (this.protocol < 4) - this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING: Protocol " + this.protocol + " assumed as 4!"); - this.isHandshakePassed = true; + if (this.handshakeProtocol == null || this.handshakeKey == null) { + this.handleHandshake(message); return; } this.socket.lastAliveTime = +new Date; @@ -66,14 +40,17 @@ PacketHandler.prototype.handleMessage = function (message) { return; } - var reader = new BinaryReader(message); - var packetId = reader.readUInt8(); - - switch (packetId) { + switch (message[0]) { case 0: + if (this.lastJoinTick == this.gameServer.getTick()) { + break; + } + this.lastJoinTick = this.gameServer.getTick(); if (this.socket.playerTracker.cells.length > 0) { break; } + var reader = new BinaryReader(message); + reader.skipBytes(1); var text = null; if (this.protocol <= 5) text = reader.readStringZeroUnicode(); @@ -89,19 +66,31 @@ PacketHandler.prototype.handleMessage = function (message) { this.socket.playerTracker.spectate = true; } break; + case 16: // Mouse + if (this.lastMouseTick == this.gameServer.getTick()) { + break; + } + this.lastMouseTick = this.gameServer.getTick(); + var client = this.socket.playerTracker; if (message.length == 13) { // protocol late 5, 6, 7 + var reader = new BinaryReader(message); + reader.skipBytes(1); client.mouse.x = reader.readInt32() - client.scrambleX; client.mouse.y = reader.readInt32() - client.scrambleY; } else if (message.length == 9) { // early protocol 5 + var reader = new BinaryReader(message); + reader.skipBytes(1); client.mouse.x = reader.readInt16() - client.scrambleX; client.mouse.y = reader.readInt16() - client.scrambleY; } else if (message.length == 21) { // protocol 4 + var reader = new BinaryReader(message); + reader.skipBytes(1); client.mouse.x = reader.readDouble() - client.scrambleX; client.mouse.y = reader.readDouble() - client.scrambleY; if (isNaN(client.mouse.x)) @@ -110,21 +99,26 @@ PacketHandler.prototype.handleMessage = function (message) { client.mouse.y = client.centerPos.y; } break; + case 17: // Space Press - Split cell this.pressSpace = true; break; + case 18: // Q Key Pressed this.pressQ = true; break; + case 19: // Q Key Released break; + case 21: // W Press - Eject mass this.pressW = true; break; + case 99: // Chat if (message.length < 3) // first validation @@ -138,11 +132,14 @@ PacketHandler.prototype.handleMessage = function (message) { if (deltaTick < 40 * 2) break; - var flags = reader.readUInt8(); // flags + var flags = message[1]; // flags var rvLength = (flags & 2 ? 4:0) + (flags & 4 ? 8:0) + (flags & 8 ? 16:0); if (message.length < 3 + rvLength) // second validation break; - reader.skipBytes(rvLength); // reserved + + var reader = new BinaryReader(message); + reader.skipBytes(2 + rvLength); // reserved + var text = null; if (this.protocol < 6) text = reader.readStringZeroUnicode(); @@ -150,6 +147,7 @@ PacketHandler.prototype.handleMessage = function (message) { text = reader.readStringZeroUtf8(); this.gameServer.onChatMessage(this.socket.playerTracker, null, text); break; + case 254: // Server stat var time = +new Date; @@ -158,11 +156,62 @@ PacketHandler.prototype.handleMessage = function (message) { if (dt < 1000) break; this.socket.sendPacket(new Packet.ServerStat(this.socket.playerTracker)); break; + + case 255: + if (message.length != 5) { + this.socket.close(1002, "Not supported protocol"); + break; + } + if (this.protocol > 6) { + var key = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); + if (key != 0) { + this.socket.close(1002, "Not supported protocol"); + } + } + break; + default: break; } }; +PacketHandler.prototype.handleHandshake = function (message) { + if (message.length != 5) { + this.socket.close(1002, "Invalid protocol"); + return; + } + if (message[0] == 254) { + this.handshakeProtocol = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); + if (this.handshakeProtocol < 1 || this.handshakeProtocol > 8) { + this.socket.close(1002, "Not supported protocol"); + } + } + if (message[0] == 255) { + this.handshakeKey = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); + if (this.handshakeProtocol > 6 && this.handshakeKey != 0) { + this.socket.close(1002, "Not supported protocol"); + } + } + if (this.handshakeProtocol == null || this.handshakeKey == null) { + return; + } + this.protocol = this.handshakeProtocol; + + // Send handshake response + this.socket.sendPacket(new Packet.ClearAll()); + this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, this.gameServer.border, this.gameServer.config.serverGamemode, "MultiOgar " + pjson.version)); + // Send welcome message + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "MultiOgar " + pjson.version); + if (this.gameServer.config.serverWelcome1) + this.gameServer.sendChatMessage(null, this.socket.playerTracker, this.gameServer.config.serverWelcome1); + if (this.gameServer.config.serverWelcome2) + this.gameServer.sendChatMessage(null, this.socket.playerTracker, this.gameServer.config.serverWelcome2); + if (this.gameServer.config.serverChat == 0) + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "This server's chat is disabled."); + if (this.protocol < 4) + this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING: Protocol " + this.protocol + " assumed as 4!"); +}; + PacketHandler.prototype.setNickname = function (text) { var name = ""; var skin = null; From 68887d62c5a1b4f85b7bc154822c4ea723a63a48 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 25 Jul 2016 20:23:40 +0200 Subject: [PATCH 398/417] protocol optimizations / reduce lags / better protocol validation (remove debug code) --- README.md | 2 +- package.json | 2 +- src/PacketHandler.js | 13 ------------- 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index cb57655de..ca955039c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.54** +Current version: **1.2.55** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 34e3cb55e..20474faa4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.54", + "version": "1.2.55", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 1e737b6d8..2feec86d1 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -157,19 +157,6 @@ PacketHandler.prototype.handleMessage = function (message) { this.socket.sendPacket(new Packet.ServerStat(this.socket.playerTracker)); break; - case 255: - if (message.length != 5) { - this.socket.close(1002, "Not supported protocol"); - break; - } - if (this.protocol > 6) { - var key = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); - if (key != 0) { - this.socket.close(1002, "Not supported protocol"); - } - } - break; - default: break; } From 956d4b34c12af42adfd54671bd3c1ed066113738 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Mon, 25 Jul 2016 20:37:26 +0200 Subject: [PATCH 399/417] handle error in console command handler --- README.md | 2 +- package.json | 2 +- src/index.js | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ca955039c..3603015ee 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.55** +Current version: **1.2.56** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 20474faa4..50dad035d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.55", + "version": "1.2.56", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/index.js b/src/index.js index 0eb9f5315..d5febef5d 100644 --- a/src/index.js +++ b/src/index.js @@ -56,6 +56,8 @@ function prompt() { in_.question(">", function (str) { try { parseCommands(str); + } catch (err) { + Logger.error(err.stack); } finally { setTimeout(prompt, 0); } From 6b57c08949bd226101b3e72fd048fef2c30d0efb Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 26 Jul 2016 01:01:53 +0200 Subject: [PATCH 400/417] small protocol fix --- README.md | 2 +- package.json | 2 +- src/PacketHandler.js | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3603015ee..6d6514f63 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.56** +Current version: **1.2.57** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 50dad035d..a7da5976f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.56", + "version": "1.2.57", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 2feec86d1..cd04ab0bd 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -164,16 +164,15 @@ PacketHandler.prototype.handleMessage = function (message) { PacketHandler.prototype.handleHandshake = function (message) { if (message.length != 5) { - this.socket.close(1002, "Invalid protocol"); return; } - if (message[0] == 254) { + if (message[0] == 254 && this.handshakeProtocol == null) { this.handshakeProtocol = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); if (this.handshakeProtocol < 1 || this.handshakeProtocol > 8) { this.socket.close(1002, "Not supported protocol"); } } - if (message[0] == 255) { + else if (message[0] == 255 && this.handshakeKey == null) { this.handshakeKey = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); if (this.handshakeProtocol > 6 && this.handshakeKey != 0) { this.socket.close(1002, "Not supported protocol"); From b167ee4d0075a3abe365637886b360122236e2ea Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 26 Jul 2016 22:51:04 +0200 Subject: [PATCH 401/417] fix memory leak --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d6514f63..4456712df 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.57** +Current version: **1.2.58** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index a7da5976f..52be7c13b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.57", + "version": "1.2.58", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 4aea1ccbe..8d76b837e 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1868,6 +1868,10 @@ function trackerRequest(options, type, body) { req.on('error', function (err) { Logger.writeError("[Tracker][" + options.host + "]: " + err); }); + req.shouldKeepAlive = false; + req.on('close', function () { + req.destroy(); + }); req.write(body); req.end() }; \ No newline at end of file From b26d420cb3fb3dcb886cb4e50d319e3cfbcde682 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 27 Jul 2016 00:21:27 +0200 Subject: [PATCH 402/417] fix memory leak --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4456712df..1628b96b3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.58** +Current version: **1.2.59** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 52be7c13b..fdf081da8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.58", + "version": "1.2.59", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 8d76b837e..efc0749dc 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -291,6 +291,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { }; GameServer.prototype.onClientSocketClose = function (ws, code) { + ws._socket.destroy(); if (this.socketCount < 1) { Logger.error("GameServer.onClientSocketClose: socketCount=" + this.socketCount); } else { From 98a8274a4b7353cedd595d1a34bfa6be4ced8272 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 27 Jul 2016 16:09:31 +0200 Subject: [PATCH 403/417] update readme --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 1628b96b3..bf6c56feb 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,16 @@ Original Ogar found [here](https://github.com/OgarProject/Ogar) The goal is to make good and smooth physics and cleanup the code. +## Ogar Server Tracker + +You can found active Ogar servers on http://ogar-tracker.tk +It updates server information in realtime with no need to refresh the page. + +If you want to include your server in the list. Just install the latest version of MultiOgar server and enable server tracking with `serverTracker = 1` in gameserver.ini + +If you have other server and want to include it in the list, just insert the code to ping ogar-tracker.tk into your server. +You can found example in MultiOgar source code: https://github.com/Barbosik/MultiOgar/blob/master/src/GameServer.js#L1799-L1823 + ## Screenshot From 9bfec0acc3dd5f56c4559e2ef249f8583d753345 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 28 Jul 2016 04:08:13 +0200 Subject: [PATCH 404/417] performance optimization --- README.md | 2 +- package.json | 2 +- src/packet/BinaryWriter.js | 116 ++++++++++++++++++------------------- src/packet/UpdateNodes.js | 5 +- 4 files changed, 64 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index bf6c56feb..c3f870730 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.59** +Current version: **1.2.60** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index fdf081da8..ea807a678 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.59", + "version": "1.2.60", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index 91363072c..fc9899fca 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -7,101 +7,83 @@ * License: Apache License, Version 2.0 */ -function BinaryWriter() { - this._writers = []; +function BinaryWriter(size) { + if (!size || size <= 0) { + size = Buffer.poolSize / 2; + } + this._buffer = new Buffer(size); this._length = 0; } module.exports = BinaryWriter; BinaryWriter.prototype.writeUInt8 = function (value) { - var offset = this._length; - this._writers.push(function (buffer) { - buffer.writeUInt8(value, offset, true); - }); - this._length += 1; + checkAlloc(this, 1); + this._buffer[this._length++] = value; }; BinaryWriter.prototype.writeInt8 = function (value) { - var offset = this._length; - this._writers.push(function (buffer) { - buffer.writeInt8(value, offset, true); - }); - this._length += 1; + checkAlloc(this, 1); + this._buffer[this._length++] = value; }; BinaryWriter.prototype.writeUInt16 = function (value) { - var offset = this._length; - this._writers.push(function (buffer) { - buffer.writeUInt16LE(value, offset, true); - }); - this._length += 2; + checkAlloc(this, 2); + this._buffer[this._length++] = value; + this._buffer[this._length++] = value >> 8; }; BinaryWriter.prototype.writeInt16 = function (value) { - var offset = this._length; - this._writers.push(function (buffer) { - buffer.writeInt16LE(value, offset, true); - }); - this._length += 2; + checkAlloc(this, 2); + this._buffer[this._length++] = value; + this._buffer[this._length++] = value >> 8; }; BinaryWriter.prototype.writeUInt32 = function (value) { - var offset = this._length; - this._writers.push(function (buffer) { - buffer.writeUInt32LE(value, offset, true); - }); - this._length += 4; + checkAlloc(this, 4); + this._buffer[this._length++] = value; + this._buffer[this._length++] = value >> 8; + this._buffer[this._length++] = value >> 16; + this._buffer[this._length++] = value >> 24; }; BinaryWriter.prototype.writeInt32 = function (value) { - var offset = this._length; - this._writers.push(function (buffer) { - buffer.writeInt32LE(value, offset, true); - }); - this._length += 4; + checkAlloc(this, 4); + this._buffer[this._length++] = value; + this._buffer[this._length++] = value >> 8; + this._buffer[this._length++] = value >> 16; + this._buffer[this._length++] = value >> 24; }; BinaryWriter.prototype.writeFloat = function (value) { - var offset = this._length; - this._writers.push(function (buffer) { - buffer.writeFloatLE(value, offset, true); - }); + checkAlloc(this, 4); + this._buffer.writeFloatLE(value, this._length, true); this._length += 4; }; BinaryWriter.prototype.writeDouble = function (value) { - var offset = this._length; - this._writers.push(function (buffer) { - buffer.writeDoubleLE(value, offset, true); - }); + checkAlloc(this, 8); + this._buffer.writeDoubleLE(value, this._length, true); this._length += 8; }; BinaryWriter.prototype.writeBytes = function (data) { - var length = data.length; - var offset = this._length; - this._writers.push(function (buffer) { - data.copy(buffer, offset, 0, length); - }); - this._length += length; + checkAlloc(this, data.length); + data.copy(this._buffer, this._length, 0, data.length); + this._length += data.length; }; BinaryWriter.prototype.writeStringUtf8 = function (value) { var length = Buffer.byteLength(value, 'utf8') - var offset = this._length; - this._writers.push(function (buffer) { - buffer.write(value, offset, 'utf8'); - }); + checkAlloc(this, length); + this._buffer.write(value, this._length, 'utf8'); this._length += length; }; BinaryWriter.prototype.writeStringUnicode = function (value) { var length = Buffer.byteLength(value, 'ucs2') - var offset = this._length; - this._writers.push(function (buffer) { - buffer.write(value, offset, 'ucs2'); - }); + checkAlloc(this, length); + this._buffer.write(value, this._length, 'ucs2'); this._length += length; }; @@ -115,10 +97,28 @@ BinaryWriter.prototype.writeStringZeroUnicode = function (value) { this.writeUInt16(0); }; +BinaryWriter.prototype.getLength = function () { + return this._length; +}; + +BinaryWriter.prototype.reset = function () { + this._length = 0; +}; + BinaryWriter.prototype.toBuffer = function () { - var buffer = new Buffer(this._length); - for (var i = 0; i < this._writers.length; i++) { - this._writers[i](buffer); + return this._buffer.slice(0, this._length); +}; + +function checkAlloc(writer, size) { + var needed = writer._length + size; + if (writer._buffer.length >= needed) + return; + var chunk = Math.max(Buffer.poolSize / 2, 1024); + var chunkCount = (needed / chunk) >>> 0; + if ((needed % chunk) > 0) { + chunkCount += 1; } - return buffer; + var buffer = new Buffer(chunkCount * chunk); + writer._buffer.copy(buffer, 0, 0, writer._length); + writer._buffer = buffer; }; diff --git a/src/packet/UpdateNodes.js b/src/packet/UpdateNodes.js index 401fe3327..1931ccd6d 100644 --- a/src/packet/UpdateNodes.js +++ b/src/packet/UpdateNodes.js @@ -1,6 +1,8 @@ // Import var BinaryWriter = require("./BinaryWriter"); +var Logger = require('../modules/Logger'); +var sharedWriter = new BinaryWriter(128*1024); // for about 25000 cells per client function UpdateNodes(playerTracker, addNodes, updNodes, eatNodes, delNodes) { this.playerTracker = playerTracker; @@ -15,7 +17,8 @@ module.exports = UpdateNodes; UpdateNodes.prototype.build = function (protocol) { if (!protocol) return null; - var writer = new BinaryWriter(); + var writer = sharedWriter; + writer.reset(); writer.writeUInt8(0x10); // Packet ID this.writeEatItems(writer); From 8f72563cda8f5930b702d11ff71b630e4dac76e8 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 30 Jul 2016 21:51:48 +0200 Subject: [PATCH 405/417] add player command "shutdown" to shutdown the server with chat command (helps to restart when memory consumption is too high and ssh doesn't works due to low memory) --- README.md | 2 +- package.json | 2 +- src/modules/PlayerCommand.js | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c3f870730..0ac9cb804 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.60** +Current version: **1.2.61** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index ea807a678..9c07a8a79 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.60", + "version": "1.2.61", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/modules/PlayerCommand.js b/src/modules/PlayerCommand.js index 524b8e665..273ce86c8 100644 --- a/src/modules/PlayerCommand.js +++ b/src/modules/PlayerCommand.js @@ -115,6 +115,14 @@ var playerCommands = { this.playerTracker.userRole = UserRoleEnum.GUEST; this.playerTracker.userAuth = null; this.writeLine("Logout done"); + }, + shutdown: function (args) { + if (this.playerTracker.userRole != UserRoleEnum.ADMIN) { + this.writeLine("ERROR: access denied!"); + return; + } + Logger.warn("SHUTDOWN REQUEST FROM " + this.playerTracker.socket.remoteAddress + " as " + this.playerTracker.userAuth); + process.exit(0); } }; From 9a4eee580811ca47a8faedc3f5dd5785f94c1d0a Mon Sep 17 00:00:00 2001 From: Barbosik Date: Tue, 2 Aug 2016 03:17:56 +0200 Subject: [PATCH 406/417] update server ports for test servers --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ac9cb804..80c34c134 100644 --- a/README.md +++ b/README.md @@ -116,8 +116,8 @@ http://c0nsume.me/private4.php?ip=127.0.0.1:443 | 5 | vanilla style IP | Location | Game Mode | Web Site --- | --- | --- | --- -bubble-wars.tk:443 | Netherlands | FFA | http://agar.io/?ip=bubble-wars.tk:443 (Test server) -bubble-wars.tk:444 | Netherlands | FFA IM | http://agar.io/?ip=bubble-wars.tk:444 (Test server) +bubble-wars.tk:4444 | France | FFA | http://agar.io/?ip=bubble-wars.tk:4444 (Test server) +bubble-wars.tk:4445 | France | FFA IM | http://agar.io/?ip=bubble-wars.tk:4445 (Test server) vps.simonorj.com:24270 | Montreal | Instant Merge | https://redd.it/4mufge 164.132.48.230:600 | France | FFA | http://c0nsume.me/private4.php?ip=164.132.48.230:600 149.202.87.51:443 | Paris | FFA | http://agarlist.com/ From 71823f7a6737a8b9c0e842e09e64bd311e66b3d0 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 3 Aug 2016 01:22:45 +0200 Subject: [PATCH 407/417] fix bug for clients with protocol < 6 --- README.md | 2 +- package.json | 2 +- src/packet/SetBorder.js | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 80c34c134..14e661fee 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.61** +Current version: **1.2.62** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 9c07a8a79..e5a7df7f7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.61", + "version": "1.2.62", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/packet/SetBorder.js b/src/packet/SetBorder.js index b80454417..f9b0c834d 100644 --- a/src/packet/SetBorder.js +++ b/src/packet/SetBorder.js @@ -32,6 +32,9 @@ SetBorder.prototype.build = function (protocol) { writer.writeUInt32(this.gameType >> 0); var name = this.serverName; if (name == null) name = ""; - writer.writeStringZeroUtf8(name); + if (protocol < 6) + writer.writeStringZeroUnicode(name); + else + writer.writeStringZeroUtf8(name); return writer.toBuffer(); }; From f4671291b02d3b50bace5d33af6d34f84369dc22 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 5 Aug 2016 05:30:54 +0200 Subject: [PATCH 408/417] protocol optimizations --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 21 ++- src/PacketHandler.js | 340 +++++++++++++++++++++++-------------------- src/PlayerTracker.js | 22 +-- 5 files changed, 204 insertions(+), 183 deletions(-) diff --git a/README.md b/README.md index 14e661fee..59fc9447b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.62** +Current version: **1.2.63** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index e5a7df7f7..a61bc7e80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.62", + "version": "1.2.63", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index efc0749dc..f1d32455c 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -46,7 +46,7 @@ function GameServer() { this.commands; // Command handler // Main loop tick - this.startTime = +new Date; + this.startTime = Date.now(); this.timeStamp = 0; this.updateTime = 0; this.updateTimeAvg = 0; @@ -140,7 +140,7 @@ function GameServer() { this.loadBadWords(); this.setBorder(this.config.borderWidth, this.config.borderHeight); - this.quadTree = new QuadNode(this.border, 16, 100); + this.quadTree = new QuadNode(this.border, 16, 32); // Gamemodes this.gameMode = Gamemode.get(this.config.serverGamemode); @@ -248,7 +248,7 @@ GameServer.prototype.onClientSocketOpen = function (ws) { ws.isConnected = true; ws.remoteAddress = ws._socket.remoteAddress; ws.remotePort = ws._socket.remotePort; - ws.lastAliveTime = +new Date; + ws.lastAliveTime = Date.now(); Logger.write("CONNECTED " + ws.remoteAddress + ":" + ws.remotePort + ", origin: \"" + ws.upgradeReq.headers.origin + "\""); ws.playerTracker = new PlayerTracker(this, ws); @@ -300,7 +300,7 @@ GameServer.prototype.onClientSocketClose = function (ws, code) { ws.isConnected = false; ws.sendPacket = function (data) { }; ws.closeReason = { code: ws._closeCode, message: ws._closeMessage }; - ws.closeTime = +new Date; + ws.closeTime = Date.now(); Logger.write("DISCONNECTED " + ws.remoteAddress + ":" + ws.remotePort + ", code: " + ws._closeCode + ", reason: \"" + ws._closeMessage + "\", name: \"" + ws.playerTracker.getName() + "\""); // disconnected effect @@ -317,6 +317,13 @@ GameServer.prototype.onClientSocketError = function (ws, error) { }; GameServer.prototype.onClientSocketMessage = function (ws, message) { + if (message.length == 0) { + return; + } + if (message.length > 256) { + ws.close(1009, "Spam"); + return; + } ws.packetHandler.handleMessage(message); }; @@ -492,7 +499,7 @@ GameServer.prototype.removeNode = function (node) { GameServer.prototype.updateClients = function () { // check minions - var dateTime = new Date; + var dateTime = Date.now(); for (var i = 0; i < this.minionTest.length; ) { var playerTracker = this.minionTest[i]; if (dateTime - playerTracker.connectedTime > this.config.serverMinionInterval) { @@ -604,7 +611,7 @@ GameServer.prototype.timerLoop = function () { timeStep += 5; timeStep = Math.max(timeStep, 40); - var ts = new Date().getTime(); + var ts = Date.now(); var dt = ts - this.timeStamp; if (dt < timeStep - 5) { setTimeout(this.timerLoopBind, ((timeStep - 5) - dt) >> 0); @@ -1746,7 +1753,7 @@ GameServer.prototype.getStats = function () { 'alive': alivePlayers, 'spectators': spectatePlayers, 'update_time': this.updateTimeAvg.toFixed(3), - 'uptime': Math.round((new Date().getTime() - this.startTime) / 1000 / 60), + 'uptime': Math.round((Date.now() - this.startTime) / 1000 / 60), 'start_time': this.startTime }; this.stats = JSON.stringify(s); diff --git a/src/PacketHandler.js b/src/PacketHandler.js index cd04ab0bd..0ba7b7279 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -8,181 +8,68 @@ function PacketHandler(gameServer, socket) { this.protocol = 0; this.handshakeProtocol = null; this.handshakeKey = null; - this.lastChatTick = 0; this.lastJoinTick = 0; - this.lastMouseTick = 0; + this.lastChatTick = 0; + this.lastStatTick = 0; + this.lastWTick = 0; + this.lastQTick = 0; + this.lastSpaceTick = 0; this.pressQ = false; this.pressW = false; this.pressSpace = false; - this.lastStatTime = +new Date; + this.mouseData = null; + + this.handler = { + 254: this.handshake_onProtocol.bind(this), + }; } module.exports = PacketHandler; PacketHandler.prototype.handleMessage = function (message) { - // Validation - if (message.length == 0) { - return; - } - if (message.length > 256) { - // anti-spamming - this.socket.close(1009, "Spam"); + if (!this.handler.hasOwnProperty(message[0])) { return; } - if (this.handshakeProtocol == null || this.handshakeKey == null) { - this.handleHandshake(message); - return; - } - this.socket.lastAliveTime = +new Date; - - if (this.socket.playerTracker.isMinion && this.socket.playerTracker.spawnCounter >= 2) { - return; - } - - switch (message[0]) { - case 0: - if (this.lastJoinTick == this.gameServer.getTick()) { - break; - } - this.lastJoinTick = this.gameServer.getTick(); - if (this.socket.playerTracker.cells.length > 0) { - break; - } - var reader = new BinaryReader(message); - reader.skipBytes(1); - var text = null; - if (this.protocol <= 5) - text = reader.readStringZeroUnicode(); - else - text = reader.readStringZeroUtf8(); - this.setNickname(text); - break; - - case 1: - // Spectate mode - if (this.socket.playerTracker.cells.length <= 0) { - // Make sure client has no cells - this.socket.playerTracker.spectate = true; - } - break; - - case 16: - // Mouse - if (this.lastMouseTick == this.gameServer.getTick()) { - break; - } - this.lastMouseTick = this.gameServer.getTick(); - - var client = this.socket.playerTracker; - if (message.length == 13) { - // protocol late 5, 6, 7 - var reader = new BinaryReader(message); - reader.skipBytes(1); - client.mouse.x = reader.readInt32() - client.scrambleX; - client.mouse.y = reader.readInt32() - client.scrambleY; - } else if (message.length == 9) { - // early protocol 5 - var reader = new BinaryReader(message); - reader.skipBytes(1); - client.mouse.x = reader.readInt16() - client.scrambleX; - client.mouse.y = reader.readInt16() - client.scrambleY; - } else if (message.length == 21) { - // protocol 4 - var reader = new BinaryReader(message); - reader.skipBytes(1); - client.mouse.x = reader.readDouble() - client.scrambleX; - client.mouse.y = reader.readDouble() - client.scrambleY; - if (isNaN(client.mouse.x)) - client.mouse.x = client.centerPos.x; - if (isNaN(client.mouse.y)) - client.mouse.y = client.centerPos.y; - } - break; - - case 17: - // Space Press - Split cell - this.pressSpace = true; - break; - - case 18: - // Q Key Pressed - this.pressQ = true; - break; - - case 19: - // Q Key Released - break; - - case 21: - // W Press - Eject mass - this.pressW = true; - break; - - case 99: - // Chat - if (message.length < 3) // first validation - break; - // chat anti-spam - // Just ignore if the time between two messages is smaller than 2 seconds - // The user should stop spamming for at least 2 seconds in order to send next chat message - var tick = this.gameServer.getTick(); - var deltaTick = tick - this.lastChatTick; - this.lastChatTick = tick; - if (deltaTick < 40 * 2) - break; - - var flags = message[1]; // flags - var rvLength = (flags & 2 ? 4:0) + (flags & 4 ? 8:0) + (flags & 8 ? 16:0); - if (message.length < 3 + rvLength) // second validation - break; - - var reader = new BinaryReader(message); - reader.skipBytes(2 + rvLength); // reserved - - var text = null; - if (this.protocol < 6) - text = reader.readStringZeroUnicode(); - else - text = reader.readStringZeroUtf8(); - this.gameServer.onChatMessage(this.socket.playerTracker, null, text); - break; - - case 254: - // Server stat - var time = +new Date; - var dt = time - this.lastStatTime; - this.lastStatTime = time; - if (dt < 1000) break; - this.socket.sendPacket(new Packet.ServerStat(this.socket.playerTracker)); - break; - - default: - break; - } + this.handler[message[0]](message); + this.socket.lastAliveTime = Date.now(); }; -PacketHandler.prototype.handleHandshake = function (message) { - if (message.length != 5) { +PacketHandler.prototype.handshake_onProtocol = function (message) { + if (message.length !== 5) return; + this.handshakeProtocol = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); + if (this.handshakeProtocol < 1 || this.handshakeProtocol > 8) { + this.socket.close(1002, "Not supported protocol"); return; } - if (message[0] == 254 && this.handshakeProtocol == null) { - this.handshakeProtocol = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); - if (this.handshakeProtocol < 1 || this.handshakeProtocol > 8) { - this.socket.close(1002, "Not supported protocol"); - } - } - else if (message[0] == 255 && this.handshakeKey == null) { - this.handshakeKey = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); - if (this.handshakeProtocol > 6 && this.handshakeKey != 0) { - this.socket.close(1002, "Not supported protocol"); - } - } - if (this.handshakeProtocol == null || this.handshakeKey == null) { + this.handler = { + 255: this.handshake_onKey.bind(this), + }; +}; + +PacketHandler.prototype.handshake_onKey = function (message) { + if (message.length !== 5) return; + this.handshakeKey = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); + if (this.handshakeProtocol > 6 && this.handshakeKey !== 0) { + this.socket.close(1002, "Not supported protocol"); return; } - this.protocol = this.handshakeProtocol; + this.handshake_onCompleted(this.handshakeProtocol, this.handshakeKey); +}; +PacketHandler.prototype.handshake_onCompleted = function (protocol, key) { + this.handler = { + 0: this.message_onJoin.bind(this), + 1: this.message_onSpectate.bind(this), + 16: this.message_onMouse.bind(this), + 17: this.message_onKeySpace.bind(this), + 18: this.message_onKeyQ.bind(this), + //19: AFK + 21: this.message_onKeyW.bind(this), + 99: this.message_onChat.bind(this), + 254: this.message_onStat.bind(this), + }; + this.protocol = protocol; // Send handshake response this.socket.sendPacket(new Packet.ClearAll()); this.socket.sendPacket(new Packet.SetBorder(this.socket.playerTracker, this.gameServer.border, this.gameServer.config.serverGamemode, "MultiOgar " + pjson.version)); @@ -198,6 +85,147 @@ PacketHandler.prototype.handleHandshake = function (message) { this.gameServer.sendChatMessage(null, this.socket.playerTracker, "WARNING: Protocol " + this.protocol + " assumed as 4!"); }; + +PacketHandler.prototype.message_onJoin = function (message) { + var tick = this.gameServer.getTick(); + var dt = tick - this.lastJoinTick; + this.lastJoinTick = tick; + if (dt < 25 || this.socket.playerTracker.cells.length !== 0) { + return; + } + var reader = new BinaryReader(message); + reader.skipBytes(1); + var text = null; + if (this.protocol < 6) + text = reader.readStringZeroUnicode(); + else + text = reader.readStringZeroUtf8(); + this.setNickname(text); +}; + +PacketHandler.prototype.message_onSpectate = function (message) { + if (message.length !== 1 || this.socket.playerTracker.cells.length !== 0) { + return; + } + this.socket.playerTracker.spectate = true; +}; + +PacketHandler.prototype.message_onMouse = function (message) { + if (message.length !== 13 && message.length !== 9 && message.length !== 21) { + return; + } + this.mouseData = message; +}; + +PacketHandler.prototype.message_onKeySpace = function (message) { + if (message.length !== 1) return; + var tick = this.gameServer.getTick(); + var dt = tick - this.lastSpaceTick; + if (dt < this.gameServer.config.ejectCooldown) { + return; + } + this.lastSpaceTick = tick; + this.pressSpace = true; +}; + +PacketHandler.prototype.message_onKeyQ = function (message) { + if (message.length !== 1) return; + var tick = this.gameServer.getTick(); + var dt = tick - this.lastQTick; + if (dt < this.gameServer.config.ejectCooldown) { + return; + } + this.lastQTick = tick; + this.pressQ = true; +}; + +PacketHandler.prototype.message_onKeyW = function (message) { + if (message.length !== 1) return; + var tick = this.gameServer.getTick(); + var dt = tick - this.lastWTick; + if (dt < this.gameServer.config.ejectCooldown) { + return; + } + this.lastWTick = tick; + this.pressW = true; +}; + +PacketHandler.prototype.message_onChat = function (message) { + if (message.length < 3) return; + var tick = this.gameServer.getTick(); + var dt = tick - this.lastChatTick; + this.lastChatTick = tick; + if (dt < 25 * 2) { + return; + } + + var flags = message[1]; // flags + var rvLength = (flags & 2 ? 4:0) + (flags & 4 ? 8:0) + (flags & 8 ? 16:0); + if (message.length < 3 + rvLength) // second validation + return; + + var reader = new BinaryReader(message); + reader.skipBytes(2 + rvLength); // reserved + var text = null; + if (this.protocol < 6) + text = reader.readStringZeroUnicode(); + else + text = reader.readStringZeroUtf8(); + this.gameServer.onChatMessage(this.socket.playerTracker, null, text); +}; + +PacketHandler.prototype.message_onStat = function (message) { + if (message.length !== 1) return; + var tick = this.gameServer.getTick(); + var dt = tick - this.lastStatTick; + this.lastStatTick = tick; + if (dt < 25) { + return; + } + this.socket.sendPacket(new Packet.ServerStat(this.socket.playerTracker)); +}; + +PacketHandler.prototype.processMouse = function () { + if (this.mouseData == null) return; + var client = this.socket.playerTracker; + var reader = new BinaryReader(this.mouseData); + reader.skipBytes(1); + if (this.mouseData.length === 13) { + // protocol late 5, 6, 7 + client.mouse.x = reader.readInt32() - client.scrambleX; + client.mouse.y = reader.readInt32() - client.scrambleY; + } else if (this.mouseData.length === 9) { + // early protocol 5 + client.mouse.x = reader.readInt16() - client.scrambleX; + client.mouse.y = reader.readInt16() - client.scrambleY; + } else if (this.mouseData.length === 21) { + // protocol 4 + var x = reader.readDouble() - client.scrambleX; + var y = reader.readDouble() - client.scrambleY; + if (!isNaN(x) && !isNaN(y)) { + client.mouse.x = x; + client.mouse.y = y; + } + } + this.mouseData = null; +}; + +PacketHandler.prototype.process = function () { + if (this.pressSpace) { // Split cell + this.socket.playerTracker.pressSpace(); + this.pressSpace = false; + } + if (this.pressW) { // Eject mass + this.socket.playerTracker.pressW(); + this.pressW = false; + } + if (this.pressQ) { // Q Press + this.socket.playerTracker.pressQ(); + this.pressQ = false; + } + this.processMouse(); +}; + PacketHandler.prototype.setNickname = function (text) { var name = ""; var skin = null; diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index d8a872c55..6be19ca76 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -58,7 +58,7 @@ function PlayerTracker(gameServer, socket) { this.scrambleY = 0; this.scrambleId = 0; - this.connectedTime = new Date; + this.connectedTime = Date.now(); this.isMinion = false; this.spawnCounter = 0; this.isMuted = false; @@ -249,7 +249,7 @@ PlayerTracker.prototype.checkConnection = function () { // Handle disconnection if (!this.socket.isConnected) { // wait for playerDisconnectTime - var time = +new Date; + var time = Date.now(); var dt = (time - this.socket.closeTime) / 1000; if (this.cells.length == 0 || dt >= this.gameServer.config.playerDisconnectTime) { // Remove all client cells @@ -271,7 +271,7 @@ PlayerTracker.prototype.checkConnection = function () { } // Check timeout if (!this.isCloseRequested && this.gameServer.config.serverTimeout) { - var time = +new Date; + var time = Date.now(); var dt = (time - this.socket.lastAliveTime) / 1000; if (dt >= this.gameServer.config.serverTimeout) { this.socket.close(1000, "Connection timeout"); @@ -281,21 +281,7 @@ PlayerTracker.prototype.checkConnection = function () { }; PlayerTracker.prototype.updateTick = function () { - if (this.socket.packetHandler.pressSpace) { // Split cell - this.pressSpace(); - this.socket.packetHandler.pressSpace = false; - } - - if (this.socket.packetHandler.pressW) { // Eject mass - this.pressW(); - this.socket.packetHandler.pressW = false; - } - - if (this.socket.packetHandler.pressQ) { // Q Press - this.pressQ(); - this.socket.packetHandler.pressQ = false; - } - + this.socket.packetHandler.process(); if (this.spectate) { if (this.freeRoam || this.getSpectateTarget() == null) { // free roam From 949c9ece5efa31ea7df9ff11ebae37bfe50482cb Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 6 Aug 2016 02:03:40 +0200 Subject: [PATCH 409/417] fix send large packet issue --- README.md | 2 +- package.json | 2 +- src/packet/BinaryWriter.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 59fc9447b..d738ed19c 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.63** +Current version: **1.2.64** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index a61bc7e80..1c72b27b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.63", + "version": "1.2.64", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index fc9899fca..882ebfb66 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -106,7 +106,7 @@ BinaryWriter.prototype.reset = function () { }; BinaryWriter.prototype.toBuffer = function () { - return this._buffer.slice(0, this._length); + return Buffer.concat([this._buffer.slice(0, this._length)]); }; function checkAlloc(writer, size) { From 9d1a61f8e6af3aa657fa9a121fffa3f5f8512f39 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sat, 6 Aug 2016 02:10:52 +0200 Subject: [PATCH 410/417] update headers --- src/packet/BinaryReader.js | 15 +++++++++++++-- src/packet/BinaryWriter.js | 17 +++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/packet/BinaryReader.js b/src/packet/BinaryReader.js index bb2cb58e4..5ef2e3496 100644 --- a/src/packet/BinaryReader.js +++ b/src/packet/BinaryReader.js @@ -3,8 +3,19 @@ * Simple BinaryReader is a minimal tool to read binary stream. * Useful for binary deserialization. * - * Copyright (c) 2016 Barbosik https://github.com/Barbosik - * License: Apache License, Version 2.0 + * Copyright (c) 2016 Barbosik + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ function BinaryReader(buffer) { diff --git a/src/packet/BinaryWriter.js b/src/packet/BinaryWriter.js index 882ebfb66..83926b11d 100644 --- a/src/packet/BinaryWriter.js +++ b/src/packet/BinaryWriter.js @@ -3,8 +3,21 @@ * Simple BinaryWriter is a minimal tool to write binary stream with unpredictable size. * Useful for binary serialization. * - * Copyright (c) 2016 Barbosik https://github.com/Barbosik - * License: Apache License, Version 2.0 + * Copyright (c) 2016 Barbosik + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * */ function BinaryWriter(size) { From df8dc268c62be31e6e684f5e83454057cd809be5 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Sun, 7 Aug 2016 00:13:43 +0200 Subject: [PATCH 411/417] fix virus color --- README.md | 2 +- package.json | 2 +- src/entity/MotherCell.js | 2 +- src/entity/Virus.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d738ed19c..b31998dcb 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.64** +Current version: **1.2.65** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 1c72b27b6..ab4814f99 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.64", + "version": "1.2.65", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/entity/MotherCell.js b/src/entity/MotherCell.js index d16fd8b2f..d8791612b 100644 --- a/src/entity/MotherCell.js +++ b/src/entity/MotherCell.js @@ -8,7 +8,7 @@ function MotherCell() { this.cellType = 2; this.isSpiked = true; this.isMotherCell = true; // Not to confuse bots - this.setColor({ r: 205, g: 85, b: 100 }); + this.setColor({ r: 0xce, g: 0x63, b: 0x63 }); this.motherCellMinSize = 149; // vanilla 149 (mass = 149*149/100 = 222.01) this.motherCellSpawnAmount = 2; if (!this.getSize()) { diff --git a/src/entity/Virus.js b/src/entity/Virus.js index dc66e1c3b..dd790a855 100644 --- a/src/entity/Virus.js +++ b/src/entity/Virus.js @@ -8,7 +8,7 @@ function Virus() { this.isSpiked = true; this.fed = 0; this.isMotherCell = false; // Not to confuse bots - this.setColor({ r: 0, g: 255, b: 0 }); + this.setColor({ r: 0x33, g: 0xff, b: 0x33 }); } module.exports = Virus; From 5b9b446ebbb66e6a412708b3f3489448be16e775 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 10 Aug 2016 18:12:56 +0200 Subject: [PATCH 412/417] fix lag on multisplit --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 27 +++++++++++++-------------- src/PacketHandler.js | 2 +- src/PlayerTracker.js | 24 ++++++++++++++++-------- src/gameserver.ini | 2 ++ 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index b31998dcb..fa6f17dac 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.65** +Current version: **1.2.66** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index ab4814f99..5743df136 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.65", + "version": "1.2.66", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index f1d32455c..90c7a8086 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -47,6 +47,7 @@ function GameServer() { // Main loop tick this.startTime = Date.now(); + this.stepDateTime = 0; this.timeStamp = 0; this.updateTime = 0; this.updateTimeAvg = 0; @@ -75,6 +76,7 @@ function GameServer() { serverBots: 0, // Number of player bots to spawn serverViewBaseX: 1920, // Base client screen resolution. Used to calculate view area. Warning: high values may cause lag serverViewBaseY: 1080, // min value is 1920x1080 + serverMinScale: 0.15, // Min scale for player (low value leads to lags due to large visible area) serverSpectatorScale: 0.4, // Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) serverStatsPort: 88, // Port for stats server. Having a negative number will disable the stats server. serverStatsUpdate: 60, // Update interval of server stats in seconds @@ -140,7 +142,7 @@ function GameServer() { this.loadBadWords(); this.setBorder(this.config.borderWidth, this.config.borderHeight); - this.quadTree = new QuadNode(this.border, 16, 32); + this.quadTree = new QuadNode(this.border, 64, 32); // Gamemodes this.gameMode = Gamemode.get(this.config.serverGamemode); @@ -175,7 +177,7 @@ GameServer.prototype.start = function () { var wsOptions = { server: this.httpServer, perMessageDeflate: false, - maxPayload: 1024 + maxPayload: 4096 }; this.wsServer = new WebSocket.Server(wsOptions); this.wsServer.on('error', this.onServerSocketError.bind(this)); @@ -499,10 +501,9 @@ GameServer.prototype.removeNode = function (node) { GameServer.prototype.updateClients = function () { // check minions - var dateTime = Date.now(); for (var i = 0; i < this.minionTest.length; ) { var playerTracker = this.minionTest[i]; - if (dateTime - playerTracker.connectedTime > this.config.serverMinionInterval) { + if (this.stepDateTime - playerTracker.connectedTime > this.config.serverMinionInterval) { this.minionTest.splice(i, 1); } else { i++; @@ -607,9 +608,7 @@ GameServer.prototype.sendChatMessage = function (from, to, message) { }; GameServer.prototype.timerLoop = function () { - var timeStep = this.updateTimeAvg >> 0; - timeStep += 5; - timeStep = Math.max(timeStep, 40); + var timeStep = 40; var ts = Date.now(); var dt = ts - this.timeStamp; @@ -626,9 +625,9 @@ GameServer.prototype.timerLoop = function () { setTimeout(this.timerLoopBind, 0); return; } - if (dt > 400) { + if (dt > 120) { // too high lag => resynchronize - this.timeStamp = ts - 40; + this.timeStamp = ts-timeStep; } // update average this.updateTimeAvg += 0.5 * (this.updateTime - this.updateTimeAvg); @@ -643,6 +642,7 @@ GameServer.prototype.timerLoop = function () { }; GameServer.prototype.mainLoop = function () { + this.stepDateTime = Date.now(); var tStart = process.hrtime(); // Loop main functions @@ -955,7 +955,7 @@ GameServer.prototype.updateMoveEngine = function () { cell1.updateRemerge(this); cell1.moveUser(this.border); cell1.move(this.border); - + // check size limit if (checkSize && cell1.getSize() > this.config.playerMaxSize && cell1.getAge(tick) >= 15) { if (client.cells.length >= this.config.playerMaxCells) { @@ -1018,15 +1018,14 @@ GameServer.prototype.updateMoveEngine = function () { } // resolve rigid body collisions - for (var z = 0; z < 2; z++) { // loop for better rigid body resolution quality (slow) + ////for (var z = 0; z < 2; z++) { // loop for better rigid body resolution quality (slow) for (var k = 0; k < rigidCollisions.length; k++) { var c = rigidCollisions[k]; var manifold = this.checkCellCollision(c.cell1, c.cell2); if (manifold == null) continue; this.resolveRigidCollision(manifold, this.border); - // position changed! don't forgot to update quad-tree } - } + ////} // Update quad tree for (var k = 0; k < rigidCollisions.length; k++) { var c = rigidCollisions[k]; @@ -1753,7 +1752,7 @@ GameServer.prototype.getStats = function () { 'alive': alivePlayers, 'spectators': spectatePlayers, 'update_time': this.updateTimeAvg.toFixed(3), - 'uptime': Math.round((Date.now() - this.startTime) / 1000 / 60), + 'uptime': Math.round((this.stepDateTime - this.startTime) / 1000 / 60), 'start_time': this.startTime }; this.stats = JSON.stringify(s); diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 0ba7b7279..518af8cea 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -32,7 +32,7 @@ PacketHandler.prototype.handleMessage = function (message) { return; } this.handler[message[0]](message); - this.socket.lastAliveTime = Date.now(); + this.socket.lastAliveTime = this.gameServer.stepDateTime; }; PacketHandler.prototype.handshake_onProtocol = function (message) { diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 6be19ca76..9f300ce6a 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -23,6 +23,7 @@ function PlayerTracker(gameServer, socket) { this.mergeOverride = false; // Triggered by console command this._score = 0; // Needed for leaderboard this._scale = 1; + this._scaleF = 1; this.isMassChanged = true; this.borderCounter = 0; @@ -58,13 +59,14 @@ function PlayerTracker(gameServer, socket) { this.scrambleY = 0; this.scrambleId = 0; - this.connectedTime = Date.now(); + this.connectedTime = 0; this.isMinion = false; this.spawnCounter = 0; this.isMuted = false; // Gamemode function if (gameServer) { + this.connectedTime = gameServer.stepDateTime; this.centerPos.x = gameServer.border.centerx; this.centerPos.y = gameServer.border.centery; // Player id @@ -249,8 +251,7 @@ PlayerTracker.prototype.checkConnection = function () { // Handle disconnection if (!this.socket.isConnected) { // wait for playerDisconnectTime - var time = Date.now(); - var dt = (time - this.socket.closeTime) / 1000; + var dt = (this.gameServer.stepDateTime - this.socket.closeTime) / 1000; if (this.cells.length == 0 || dt >= this.gameServer.config.playerDisconnectTime) { // Remove all client cells var cells = this.cells; @@ -271,8 +272,7 @@ PlayerTracker.prototype.checkConnection = function () { } // Check timeout if (!this.isCloseRequested && this.gameServer.config.serverTimeout) { - var time = Date.now(); - var dt = (time - this.socket.lastAliveTime) / 1000; + var dt = (this.gameServer.stepDateTime - this.socket.lastAliveTime) / 1000; if (dt >= this.gameServer.config.serverTimeout) { this.socket.close(1000, "Connection timeout"); this.isCloseRequested = true; @@ -416,7 +416,11 @@ PlayerTracker.prototype.updateCenterInGame = function () { // Get center of cell count++; } if (count == 0) return; - this.setCenterPos(cx / count, cy / count); + cx /= count; + cy /= count; + cx = (this.centerPos.x + cx) / 2; + cy = (this.centerPos.y + cy) / 2; + this.setCenterPos(cx, cy); }; PlayerTracker.prototype.updateCenterFreeRoam = function () { @@ -442,8 +446,12 @@ PlayerTracker.prototype.updateCenterFreeRoam = function () { PlayerTracker.prototype.updateViewBox = function () { var scale = this.getScale(); - var width = (this.gameServer.config.serverViewBaseX + 100) / scale; - var height = (this.gameServer.config.serverViewBaseY + 100) / scale; + scale = Math.max(scale, this.gameServer.config.serverMinScale); + this._scaleF += 0.1 * (scale - this._scaleF); + if (isNaN(this._scaleF)) + this._scaleF = 1; + var width = (this.gameServer.config.serverViewBaseX + 100) / this._scaleF; + var height = (this.gameServer.config.serverViewBaseY + 100) / this._scaleF; var halfWidth = width / 2; var halfHeight = height / 2; this.viewBox = { diff --git a/src/gameserver.ini b/src/gameserver.ini index 5cbb47357..da019c588 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -32,6 +32,7 @@ logFileVerbosity = 5 # serverGamemode: 0 = FFA, 1 = Teams, 2 = Experimental, 10 = Tournament, 11 = Hunger Games # serverBots: Number of player bots to spawn (Experimental) # serverViewBase: Base view distance of players. Warning: high values may cause lag! Min value is 1920x1080 +# serverMinScale: Min scale for player (low value leads to lags due to large visible area for big cell) # serverSpectatorScale: Scale (field of view) used for free roam spectators (low value leads to lags, vanilla=0.4, old vanilla=0.25) # serverStatsPort: Port for the stats server. Having a negative number will disable the stats server. # serverStatsUpdate: Update interval of server stats in seconds @@ -54,6 +55,7 @@ serverGamemode = 0 serverBots = 0 serverViewBaseX = 1920 serverViewBaseY = 1080 +serverMinScale = 0.15 serverSpectatorScale = 0.4 serverStatsPort = 88 serverStatsUpdate = 60 From 24f28c7ba46ae11db6feac396551dba37976ee1d Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 10 Aug 2016 19:23:46 +0200 Subject: [PATCH 413/417] add support for protocol 9 --- README.md | 2 +- package.json | 2 +- src/PacketHandler.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fa6f17dac..4fb62de09 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.66** +Current version: **1.2.67** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 5743df136..11f83f42d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.66", + "version": "1.2.67", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/PacketHandler.js b/src/PacketHandler.js index 518af8cea..f23500b90 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -38,7 +38,7 @@ PacketHandler.prototype.handleMessage = function (message) { PacketHandler.prototype.handshake_onProtocol = function (message) { if (message.length !== 5) return; this.handshakeProtocol = message[1] | (message[2] << 8) | (message[3] << 16) | (message[4] << 24); - if (this.handshakeProtocol < 1 || this.handshakeProtocol > 8) { + if (this.handshakeProtocol < 1 || this.handshakeProtocol > 9) { this.socket.close(1002, "Not supported protocol"); return; } From 60c84311eb39afe438a8dd03063a5c7427632301 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Thu, 11 Aug 2016 09:51:14 +0200 Subject: [PATCH 414/417] little code refactoring & optimizations --- src/GameServer.js | 48 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/GameServer.js b/src/GameServer.js index 90c7a8086..c95d702ff 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -422,43 +422,39 @@ GameServer.prototype.getRandomColor = function () { }; GameServer.prototype.updateNodeQuad = function (node) { - var quadItem = node.quadItem; - if (quadItem == null) { + var item = node.quadItem; + if (item == null) { throw new TypeError("GameServer.updateNodeQuad: quadItem is null!"); } + var x = node.position.x; + var y = node.position.y; + var size = node.getSize(); // check for change - if (node.position.x == quadItem.x && - node.position.y == quadItem.y && - node.getSize() == quadItem.size) { - // no change + if (item.x === x && item.y === y && item.size === size) { return; } - // update quadTree - quadItem.x = node.position.x; - quadItem.y = node.position.y; - quadItem.size = node.getSize(); - quadItem.bound = { - minx: node.quadItem.x - node.quadItem.size, - miny: node.quadItem.y - node.quadItem.size, - maxx: node.quadItem.x + node.quadItem.size, - maxy: node.quadItem.y + node.quadItem.size - }; - this.quadTree.update(quadItem); + // update quad tree + item.x = x; + item.y = y; + item.size = size; + item.bound.minx = x - size; + item.bound.miny = y - size; + item.bound.maxx = x + size; + item.bound.maxy = y + size; + this.quadTree.update(item); }; GameServer.prototype.addNode = function (node) { + var x = node.position.x; + var y = node.position.y; + var size = node.getSize(); node.quadItem = { cell: node, - x: node.position.x, - y: node.position.y, - size: node.getSize() - }; - node.quadItem.bound = { - minx: node.quadItem.x - node.quadItem.size, - miny: node.quadItem.y - node.quadItem.size, - maxx: node.quadItem.x + node.quadItem.size, - maxy: node.quadItem.y + node.quadItem.size + x: x, + y: y, + size: size, + bound: { minx: x-size, miny: y-size, maxx: x+size, maxy: y+size } }; this.quadTree.insert(node.quadItem); From e17cca4c65bc93942e36845b81e58808de6d3fd3 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 12 Aug 2016 18:36:15 +0200 Subject: [PATCH 415/417] add uws WebSocket module support: https://github.com/Barbosik/MultiOgar/pull/197 --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 44 ++++++++++++++++++++++++-------------------- src/PacketHandler.js | 2 +- src/PlayerTracker.js | 2 +- src/gameserver.ini | 2 ++ 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 4fb62de09..33654b845 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.67** +Current version: **1.2.68** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 11f83f42d..475b27bca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.67", + "version": "1.2.68", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index c95d702ff..56b038d8d 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -1,5 +1,4 @@ // Library imports -var WebSocket = require('ws'); var http = require('http'); var fs = require("fs"); var os = require('os'); @@ -64,6 +63,7 @@ function GameServer() { logFileVerbosity: 5, // File log level serverTimeout: 300, // Seconds to keep connection alive for non-responding client + serverWsModule: 'ws', // WebSocket module: 'ws' or 'uws' (install npm package before using uws) serverMaxConnections: 64, // Maximum number of connections to the server. (0 for no limit) serverIpLimit: 4, // Maximum number of connections from the same IP (0 for no limit) serverMinionIgnoreTime: 30, // minion detection disable time on server startup [seconds] @@ -83,7 +83,7 @@ function GameServer() { serverScrambleLevel: 2, // Toggles scrambling of coordinates. 0 = No scrambling, 1 = lightweight scrambling. 2 = full scrambling (also known as scramble minimap); 3 - high scrambling (no border) serverMaxLB: 10, // Controls the maximum players displayed on the leaderboard. serverChat: 1, // Set to 1 to allow chat; 0 to disable chat. - serverChatAscii: 1, // Set to 1 to disable non-ANSI letters in the chat (english only mode) + serverChatAscii: 1, // Set to 1 to disable non-ANSI letters in the chat (english only mode) serverName: 'MultiOgar #1', // Server name serverWelcome1: 'Welcome to MultiOgar server!', // First server welcome message @@ -179,6 +179,25 @@ GameServer.prototype.start = function () { perMessageDeflate: false, maxPayload: 4096 }; + Logger.info("WebSocket: " + this.config.serverWsModule); + WebSocket = require(this.config.serverWsModule); + // Custom prototype functions^M + WebSocket.prototype.sendPacket = function (packet) { + if (packet == null) return; + if (this.readyState == WebSocket.OPEN) { + if (this._socket.writable != null && !this._socket.writable) { + return; + } + var buffer = packet.build(this.playerTracker.socket.packetHandler.protocol); + if (buffer != null) { + this.send(buffer, { binary: true }); + } + } else { + this.readyState = WebSocket.CLOSED; + this.emit('close'); + } + }; + this.wsServer = new WebSocket.Server(wsOptions); this.wsServer.on('error', this.onServerSocketError.bind(this)); this.wsServer.on('connection', this.onClientSocketOpen.bind(this)); @@ -293,7 +312,9 @@ GameServer.prototype.onClientSocketOpen = function (ws) { }; GameServer.prototype.onClientSocketClose = function (ws, code) { - ws._socket.destroy(); + if (ws._socket.destroy != null && typeof ws._socket.destroy == 'function') { + ws._socket.destroy(); + } if (this.socketCount < 1) { Logger.error("GameServer.onClientSocketClose: socketCount=" + this.socketCount); } else { @@ -1754,23 +1775,6 @@ GameServer.prototype.getStats = function () { this.stats = JSON.stringify(s); }; -// Custom prototype functions -WebSocket.prototype.sendPacket = function (packet) { - if (packet == null) return; - if (this.readyState == WebSocket.OPEN) { - if (!this._socket.writable) { - return; - } - var buffer = packet.build(this.playerTracker.socket.packetHandler.protocol); - if (buffer != null) { - this.send(buffer, { binary: true }); - } - } else { - this.readyState = WebSocket.CLOSED; - this.emit('close'); - } -}; - // Ping the server tracker. // To list us on the server tracker located at http://ogar.mivabe.nl/master // Should be called every 30 seconds diff --git a/src/PacketHandler.js b/src/PacketHandler.js index f23500b90..3a3989895 100644 --- a/src/PacketHandler.js +++ b/src/PacketHandler.js @@ -114,7 +114,7 @@ PacketHandler.prototype.message_onMouse = function (message) { if (message.length !== 13 && message.length !== 9 && message.length !== 21) { return; } - this.mouseData = message; + this.mouseData = Buffer.concat([message]); }; PacketHandler.prototype.message_onKeySpace = function (message) { diff --git a/src/PlayerTracker.js b/src/PlayerTracker.js index 9f300ce6a..bcd294261 100644 --- a/src/PlayerTracker.js +++ b/src/PlayerTracker.js @@ -303,7 +303,7 @@ PlayerTracker.prototype.sendUpdate = function () { if (this.isRemoved|| !this.socket.packetHandler.protocol || !this.socket.isConnected || - !this.socket._socket.writable || + (this.socket._socket.writable != null && !this.socket._socket.writable) || this.socket.readyState != this.socket.OPEN) { // do not send update for disconnected clients // also do not send if initialization is not complete yet diff --git a/src/gameserver.ini b/src/gameserver.ini index da019c588..83b25554b 100644 --- a/src/gameserver.ini +++ b/src/gameserver.ini @@ -22,6 +22,7 @@ logFileVerbosity = 5 # [Server] # serverTimeout: Seconds to keep connection alive for non-responding client +# serverWsModule: WebSocket module: 'ws' or 'uws' (install npm package before using uws) # serverIpLimit: Controls the maximum connections from single IP (use 0 to disable) # serverMinionIgnoreTime: minion detection disable time on server startup [seconds] # serverMinionThreshold: max connections within serverMinionInterval time period, which will not be marked as minion @@ -43,6 +44,7 @@ logFileVerbosity = 5 # serverWelcome1: First server welcome message # serverWelcome2: Second server welcome message (optional, for info, etc) serverTimeout = 300 +serverWsModule = "ws" serverMaxConnections = 128 serverIpLimit = 4 serverMinionIgnoreTime = 30 From 60722903e100cdf3f732d8349c4818b46fafaeaa Mon Sep 17 00:00:00 2001 From: Barbosik Date: Fri, 12 Aug 2016 20:32:50 +0200 Subject: [PATCH 416/417] fix line-split behavior --- README.md | 2 +- package.json | 2 +- src/GameServer.js | 8 ++++---- src/entity/PlayerCell.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 33654b845..fb2b24957 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MultiOgar Ogar game server with fast and smooth vanilla physics and multi-protocol support. -Current version: **1.2.68** +Current version: **1.2.69** ## Project Info ![Language](https://img.shields.io/badge/language-node.js-yellow.svg) diff --git a/package.json b/package.json index 475b27bca..11b96a1d0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "MultiOgar", - "version": "1.2.68", + "version": "1.2.69", "description": "Open source Ogar server", "author": "Barbosik (https://github.com/Barbosik/MultiOgar)", "homepage": "https://github.com/Barbosik/MultiOgar", diff --git a/src/GameServer.js b/src/GameServer.js index 56b038d8d..87eb67565 100644 --- a/src/GameServer.js +++ b/src/GameServer.js @@ -830,8 +830,8 @@ GameServer.prototype.resolveRigidCollision = function (manifold, border) { var invd = 1 / d; // normal - var nx = manifold.dx * invd; - var ny = manifold.dy * invd; + var nx = ~~manifold.dx * invd; + var ny = ~~manifold.dy * invd; // body penetration distance var penetration = manifold.r - d; @@ -1233,8 +1233,8 @@ GameServer.prototype.splitCells = function (client) { var splitCells = 0; // How many cells have been split for (var i = 0; i < cellToSplit.length; i++) { var cell = cellToSplit[i]; - var dx = client.mouse.x - cell.position.x; - var dy = client.mouse.y - cell.position.y; + var dx = ~~(client.mouse.x - cell.position.x); + var dy = ~~(client.mouse.y - cell.position.y); var dl = dx * dx + dy * dy; if (dl < 1) { dx = 1; diff --git a/src/entity/PlayerCell.js b/src/entity/PlayerCell.js index 9227ea2b8..0b86a6e1f 100644 --- a/src/entity/PlayerCell.js +++ b/src/entity/PlayerCell.js @@ -61,8 +61,8 @@ PlayerCell.prototype.moveUser = function (border) { if (isNaN(x) || isNaN(y)) { return; } - var dx = x - this.position.x; - var dy = y - this.position.y; + var dx = ~~(x - this.position.x); + var dy = ~~(y - this.position.y); var squared = dx * dx + dy * dy; if (squared < 1) return; From d887783e017b013912d79469ede9b094ffad6db9 Mon Sep 17 00:00:00 2001 From: Barbosik Date: Wed, 1 Nov 2017 16:28:17 +0200 Subject: [PATCH 417/417] fix readBytes method https://github.com/Barbosik/BinaryWriter-node.js/issues/2 --- src/packet/BinaryReader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/packet/BinaryReader.js b/src/packet/BinaryReader.js index 5ef2e3496..8b4a75f20 100644 --- a/src/packet/BinaryReader.js +++ b/src/packet/BinaryReader.js @@ -74,8 +74,9 @@ BinaryReader.prototype.readDouble = function () { }; BinaryReader.prototype.readBytes = function (length) { - return this._buffer.slice(this._offset, this._offset + length); + var value = this._buffer.slice(this._offset, this._offset + length); this._offset += length; + return value; }; BinaryReader.prototype.skipBytes = function (length) {