├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .travis.yml ├── LICENSE ├── README.md ├── cpu.js ├── heap.js ├── index.js ├── install.json ├── package.json ├── source ├── ai │ ├── Bot.js │ ├── Minion.js │ ├── botAI.js │ └── fakePlayer.js ├── child │ ├── Player.js │ ├── index.js │ ├── manager.js │ └── node.js ├── commands │ ├── index.js │ └── lib │ │ ├── addBot.js │ │ ├── ban.js │ │ ├── debug.js │ │ ├── kick.js │ │ ├── kickbots.js │ │ ├── libraries │ │ ├── list.js │ │ ├── pause.js │ │ ├── plugin.js │ │ ├── redraw.js │ │ ├── reload.js │ │ ├── reset.js │ │ ├── restart.js │ │ ├── save.js │ │ ├── server.js │ │ ├── startv.js │ │ ├── stop.js │ │ └── update.js ├── core │ ├── GUIHandler.js │ ├── GraphicalInterface.js │ ├── Player.js │ ├── PlayerTemplate.js │ ├── Statistics.js │ ├── binaryNodes.js │ ├── childHolder.js │ ├── childManager.js │ ├── childService.js │ ├── collisionHandler.js │ ├── configService.js │ ├── controller.js │ ├── dataService.js │ ├── errorManager.js │ ├── foodService.js │ ├── gameData.js │ ├── gameMode.js │ ├── globalData.js │ ├── main.js │ ├── pluginParser.js │ ├── pluginService.js │ ├── serverService.js │ ├── shellService.js │ ├── skinHandler.js │ ├── socket.js │ ├── socketService.js │ ├── stringBuilder.js │ ├── uid.js │ ├── updater.js │ ├── utilities.js │ └── worldModel.js ├── entities │ ├── MotherCell.js │ ├── bullet.js │ ├── cell.js │ ├── ejectedMass.js │ ├── food.js │ ├── index.js │ ├── playerCell.js │ ├── template.js │ ├── virus.js │ └── wormHole.js ├── modes │ ├── Experimental.js │ ├── FFA.js │ ├── HideNSeek.js │ ├── Minions.js │ ├── Teams.js │ ├── advTeams.js │ ├── index.js │ └── template.js ├── modules │ ├── FastBuffers.js │ ├── HashBounds.js │ ├── LZString.js │ ├── fastSquares.js │ └── ini.js ├── plugins │ └── README.md ├── settings │ ├── botnames.txt │ ├── clientConfig.ini │ ├── config.ini │ ├── customSkins.ini │ ├── physicsConfig.ini │ ├── pluginConfig.ini │ ├── pluginConfig │ │ └── README.md │ ├── serverConfig.ini │ └── servers │ │ └── README.md └── sounds │ ├── README.md │ ├── alert.mp3 │ ├── start.mp3 │ └── tone.mp3 └── start.bat /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Important rules for pull requests: 2 | 3 | 4 | 1. Everything must be tested. 5 | 2. Everything must be optimised to the highest degree. 6 | 3. Profile the server before and after your changes. 7 | 4. Avoid bad practices. 8 | 5. Use the log function instead of console.log 9 | 6. Avoid circlular data structures 10 | 11 | 12 | IF THE PR's FUNCTIONALITY CAN BE MADE INTO A PLUGIN, DO THAT INSTEAD 13 | 14 | 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "5.9.0" 4 | 5 | script: 6 | node source/core/main.js 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | OpenAgar - Open source web game 2 | Copyright (C) 2016 Andrew S 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU Affero General Public License as published 6 | by the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | 14 | You should have received a copy of the GNU Affero General Public License 15 | along with this program. If not, see . 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CLOSED!!! 2 | 3 | This project has been closed. Please do not go to opnagar.us, it is a scam. 4 | 5 | 6 | 7 |

8 | 9 |

10 | 11 | [![Build Status](https://travis-ci.org/AJS-development/OpenAgar.svg?branch=master)](https://travis-ci.org/AJS-development/OpenAgar) [![Forums](https://img.shields.io/badge/Forums-home.opnagar.us-blue.svg)](http://home.opnagar.us) [![chat](https://img.shields.io/badge/Chat-Discord-green.svg)](https://discord.gg/8JPsyDn) ![Status](https://img.shields.io/badge/Status-Basically%20Finished-brightgreen.svg) ![Project](https://img.shields.io/badge/Project-Semi--Private-yellow.svg) [![Plugins](https://img.shields.io/badge/Plugins-OAPlugins-green.svg)](https://github.com/AJS-development/OAPlugins) ![Stats](https://img.shields.io/badge/Code-152747%20Lines-green.svg) 12 | 13 | Hello, we are AJS Development! We are the same team that brought you OgarUl! In AJS, we have many members. The main members are Andrews54757 and LegitSoulja. We decided to create this project after OgarUl's Ogar infrastructure couldnt hold with all those cool features! So, we created our own, completely from scratch. Now, OpenAgar is the most efficient Ogar-like game server available! Twice as efficient as MultiOgar, a gazillion times efficient than OgarUL, and 5 times efficient than Ogar! ([Proof](https://github.com/AJS-development/OpenAgar/issues/1)) We also decided to make OpenAgar as interesting as possible! New physics, new features, new gameplay and more! 14 | 15 | ## What game? 16 | It is a web game that we are making, similar to [agar.io](https://agar.io), but very different 17 | 18 | ## How to Use 19 | 20 | **Please note that a key is required, that it's distribution is limited! This is so that computer newbies wont waste our time** 21 | 22 | #### Dependencies 23 | * Nodejs (**v5.10.0**+) 24 | * Npm (included with Nodejs) 25 | 26 | #### Guide 27 | 28 | 1. Download this repo, or do `git clone https://github.com/AJS-development/OpenAgar.git` to download from command line. 29 | 2. Change directory to the project folder `cd OpenAgar` 30 | 3. Make sure all the dependancies are installed 31 | 4. Install npm dependancies `npm install` 32 | 5. Run index.js using node `node index.js` 33 | 6. Record the ID given and register by going here: http://login.opnagar.us. (OPTIONAL but recommended) 34 | 8. Run index.js again 35 | 9. Connect using `opnagar.us` 36 | 37 | #### Registration 38 | While you do not need to registrate to run openagar (not anymore), you need to registrate to use services provided by AJS. Such include: 39 | 40 | 1. Statistics - Publishing your server online to opnagar.us 41 | 2. Plugins - Use of our libraries (One plugins is allowed for unregistered copies) 42 | 3. Socket Protection 43 | 4. Skin publishing - globally to opnagar.us 44 | 5. Auto issue reports 45 | 6. Our client. 46 | 47 | 48 | If you dont registrate, you are unable to: 49 | 50 | 1. Publish server on stats 51 | 2. Use more than 1 plugin 52 | 3. No socket protection 53 | 4. Inability to use our client (excluding localhost and lan) 54 | 5. Inability to publish global skins 55 | 6. Auto issue reports 56 | 57 | ##### Login.opnagar.us 58 | This is the website to generate keys from. You will have to sign up/sign in using your github account. We are using github because: 59 | 60 | 1. We would rather have developers, not noobs 61 | 2. We dont want to deal with managing accounts (ie, making sure they are real) 62 | 63 | Then, once you are logged in, you can register servers. You may only have two registrated servers in your account. You may delete or deactivate unused registrations 64 | 65 | #### Please do not misuse the registration system 66 | Please do not be an idiot and try to make money off of ths system. We will monitor accounts and suspend them that are known for misuse. 67 | 68 | ## NEVER BUY OPENAGAR! 69 | If you bought a copy of openagar, wow, you have been ripped. People who have a registrated version might try to sell it for money. While sharing of servers is allowed, however note that if multiple people are using it at the same time - your server will become unjoinable - I suggest not doing this unless you are his/her friend. Another thing, is that openagar is free. Yes, its FREE. Also, the registration is FREE and UNLIMITED. 70 | 71 | ## Commands 72 | There are commands you can type in command line. 73 | 74 | |Command | Desc | 75 | |-----------|--------| 76 | |help |Displays a list of commands| 77 | |startv |Start visual gui, not implemented| 78 | |plugin |Plugin command, the core feature of OpenAgar| 79 | |list |List the players/bots/minions in the game| 80 | |server |Create servers, remove servers, select servers, and list them | 81 | |pause |Pause/unpause the game| 82 | |restart |Restart. Use just like you would use in command prompt. Do restart all [time] to restart all servers| 83 | |kick |Kick a player| 84 | |kickbots |Kick bots| 85 | |addbots |Add bots| 86 | |ban |Ban players| 87 | |debug |Toggle the debug console| 88 | |update |Updates the software| 89 | |stop |Stops the server| 90 | 91 | 92 | ## Chat Commands 93 | There are also chat commands 94 | 95 | |Command | Desc | 96 | |-----------|--------| 97 | |help |Displays a list of commands| 98 | 99 | ## Gamemodes 100 | There are also some gamemodes 101 | 102 | | ID | Mode | 103 | |----|------------| 104 | |0 |FFA | 105 | |1 |Teams | 106 | |2 |Experimental| 107 | |3 |Minions | 108 | |4 |Hide n seek | 109 | |5 |Adv Teams | 110 | |6 |Get rich or die trying (not finished)| 111 | |7 |Leap (not finished)| 112 | |8 |Timed FFA (not finished)| 113 | |9 |Hunger Games (not finished)| 114 | 115 | ## Minigames 116 | Minigames are planned for OpenAgar, we dont know exactly how they will work, but we have an idea. Some ideas: 117 | 118 | * Pool: Like 8-ball pool with some tweaks. (2 players) 119 | * Maze: A maze, with teams and the first team to solve first wins (2-20 players) 120 | * Get the F off my turf: A game where you defend your turf from others. 121 | 122 | ## Statistics 123 | Statistics allow you to host a public server. To post your server to statistics, edit serverConfig.ini's statistics config values. Then, your server will be listed and it will be joinable at `http://opnagar.us/server/yoururl`. 124 | 125 | ## Plugins 126 | Since most of OpenAgar's features doesnt come in the box, plugins are very important. So we made it very easy to install plugins. 127 | 128 | #### The Plugin Command 129 | The plugin command has four important actions. 130 | 131 | ##### 1. install 132 | > plugin install [name] 133 | 134 | Install a plugin from plugin library. The default library is [this](https://github.com/AJS-development/OAPlugins). Note that plugins are case sensitive. 135 | 136 | ##### 2. search 137 | > plugin search [search term] 138 | 139 | Search plugin librarys for plugins matching your search term 140 | 141 | ##### 3. library 142 | > plugin library [url] 143 | 144 | This adds a library to install/search plugins from. The url is the plugin JSON file with all the plugin data ([example](https://github.com/AJS-development/OAPlugins/blob/master/plugins.json)). All the libraries are stored in `source/commands/lib/libraries`. It is a line-break separated list of urls. 145 | 146 | ##### 4. add 147 | > plugin add [name] [url] 148 | 149 | This adds a plugin from it's url. 150 | 151 | ## Multi-Server system 152 | OpenAgar is made so you can host multiple servers on one port. There are two ways to create additional servers in the multi-server system 153 | 154 | #### The settings/server folder 155 | Using this folder, you can automatically have OpenAgar create additional servers for you. Also, this allows you to easily change the configurations of the individual servers. 156 | 157 | ##### Usage 158 | 1. Create a file in the source/settings/server folder, the file should be `[Server name].ini` 159 | 2. Then, you can override configs in that file. For example, I can put the line `gameMode = 1` to have the server be in teams 160 | 3. Start the server. Your server will automatically be created. 161 | 162 | #### The server command 163 | The server command has 4 different usages 164 | 165 | ##### 1. create 166 | > server create [name] [configs (optional)] 167 | 168 | This adds an additional server. The configs argument overrides default configs and looks like this: `configname1:value1,configname2:value2`. You do not have to specify every config values. 169 | 170 | ##### 2. remove 171 | > server remove [id] 172 | 173 | This removes a server by id 174 | 175 | ##### 3. select 176 | > server select [id] 177 | 178 | This selects a server for you to control via console. 179 | 180 | ##### 4. list 181 | > server list 182 | 183 | This lists all servers: their ids, names, players/bots, uptime, and status 184 | 185 | ## SSL 186 | OpenAgar is compatable with SSL. To use, set ssl to true in serverConfig.ini. 187 | 188 | #### rsa.json 189 | SSL uses this file to configure SSL. 190 | 191 | ``` 192 | { 193 | "key": "", // RSA Private key (PEM format) 194 | "certificate": "", // RSA Signed Certificate 195 | "ca": "", // certificate authority 196 | "expire": int // Expiration (in milliseconds) (Optional) 197 | } 198 | ``` 199 | 200 | #### Self-Signed-cert 201 | OpenAgar can generate its own self-signed RSA certificate. To use, just set ssl to true, without adding the rsa.json file. However, to be able to connect however (in most browsers), you must go to `https:yourip:yourport` in the browser and verify the certificate. (Since no CA is used) 202 | 203 | ## Developers 204 | 205 | * Andrews54757 206 | * LegitSoulja 207 | 208 | # Support 209 | One thing we decided, was that we are not going to help other people run this project. If you cant... Too bad, use ogar. We are really trying to have this project be semi-private. 210 | 211 | ## More like ClosedAgar 212 | People have complained to me about this project being semi private. But in order to have everybody enjoy it, we must, since being too open means disaster. This was the problem with OgarUL, it got too big it collapsed on itself. Resources cost money you know. 213 | 214 | ## FAQ 215 | #### How can I help? 216 | Make a pr. But please make good ones 217 | 218 | #### How come features are so limited? 219 | Actually, there are a lot of features, but none come in the box. You must use the plugin command to add them 220 | 221 | #### Can I make a Pull Request? 222 | Yes, but note that we are very very strict. So many pull requests wont be accepted 223 | 224 | #### Can I make a fork? 225 | Yes, you may. 226 | 227 | #### I want to post a server to stats, however I am worried over ddos attacks and other things 228 | Statistics will not show your ip, minimising ddos attacks. Also, OpenAgar is made to handle them. It will detect those attacks and mitigate them. DDOSers have no life. 229 | 230 | #### Why are some files encrypted? 231 | Some files are encrypted because they are needed to secure things such as sockets, or because they use our assets (our servers, other resources...). 232 | -------------------------------------------------------------------------------- /cpu.js: -------------------------------------------------------------------------------- 1 | // SOURCE: https://gist.github.com/danielkhan/9cfa77b97bc7ba0a3220#file-cpuprofiler-js-L5 2 | 3 | /** 4 | * Simple userland CPU profiler using v8-profiler 5 | * Usage: require('[path_to]/CpuProfiler').init('datadir') 6 | * 7 | * @module CpuProfiler 8 | * @type {exports} 9 | */ 10 | 11 | var fs = require('fs'); 12 | var profiler = require('v8-profiler'); 13 | var _datadir = null; 14 | 15 | /** 16 | * Init and schedule profiler runs 17 | * 18 | * @param datadir Folder to save the data to 19 | */ 20 | module.exports.init = function (datadir) { 21 | _datadir = datadir; 22 | setInterval(startProfiling, 10 * 1000); 23 | }; 24 | 25 | /** 26 | * Starts profiling and schedules its end 27 | */ 28 | function startProfiling() { 29 | var stamp = Date.now(); 30 | var id = 'profile-' + stamp; 31 | 32 | // Use stdout directly to bypass eventloop 33 | fs.writeSync(1, 'Start profiler with Id [' + id + ']\n'); 34 | 35 | // Start profiling 36 | profiler.startProfiling(id); 37 | 38 | 39 | // Schedule stop of profiling in x seconds 40 | setTimeout(function () { 41 | stopProfiling(id) 42 | }, 5000); 43 | } 44 | 45 | /** 46 | * Stops the profiler and writes the data to a file 47 | * @param id the id of the profiler process to stop 48 | */ 49 | function stopProfiling(id) { 50 | var profile = profiler.stopProfiling(id); 51 | fs.writeFile(_datadir + '/' + id + '.cpuprofile', JSON.stringify(profile), function () { 52 | console.log('Profiler data written'); 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /heap.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var fs = require('fs'); 4 | var profiler = require('heapdump'); 5 | var _datadir = null; 6 | 7 | var startProfiling = function() { 8 | var stamp = Date.now(); 9 | var id = 'profile-' + stamp; 10 | 11 | // Use stdout directly to bypass eventloop 12 | fs.writeSync(1, 'Start profiler with Id [' + id + ']\n'); 13 | 14 | // Start profiling 15 | profiler.writeSnapshot(_datadir + '/' + id + '.heapsnapshot',function() { 16 | console.log("Done profiling") 17 | setTimeout(function () { 18 | startProfiling() 19 | }, 5000); 20 | }); 21 | 22 | 23 | 24 | 25 | } 26 | 27 | module.exports.init = function (datadir) { 28 | _datadir = datadir; 29 | startProfiling() 30 | }; 31 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | 3 | var toCheck = JSON.parse(fs.readFileSync(__dirname + "/install.json", "utf8")) 4 | var exec = require('child_process').exec; 5 | var modules = [] 6 | console.log("Checking Modules") 7 | try { 8 | var f = JSON.parse(fs.readFileSync("modules.json", "utf8")); 9 | var u = 0, 10 | a = 0 11 | for (var i = 0; i < toCheck.length; i++) { 12 | var m = toCheck[i], 13 | b = f[i]; 14 | 15 | if (!b) {; 16 | a++; 17 | modules.push(b) 18 | } else if (m.version != b.version) {; 19 | u++; 20 | modules.push(b) 21 | } 22 | } 23 | console.log(a + " Modules need to be installed; " + u + " modules need to be updated") 24 | } catch (e) { 25 | modules = toCheck; 26 | console.log(toCheck.length + " Modules need to be installed") 27 | } 28 | if (!modules.length) return callback(); 29 | console.log("Installing Module(s)") 30 | var todo = 1; 31 | var done = 0; 32 | 33 | function loading(ne) { 34 | done++; 35 | var percent = Math.round(done / todo * 10) 36 | var bar = "" 37 | for (var i = 0; i < percent; i++) { 38 | bar = bar + "==="; 39 | } 40 | if (percent == 10) bar = bar + "="; 41 | else bar = bar + ">"; 42 | var extras = 31 - bar.length; 43 | var extra = ""; 44 | for (var i = 0; i < extras; i++) extra = extra + " "; 45 | process.stdout.write("\u001B[?25l\r\x1b[K[npm] [" + bar + extra + "] " + percent * 10 + "% " + ne); 46 | } 47 | var index = 0; 48 | todo += modules.length * 2; 49 | install() 50 | 51 | function install() { 52 | var b = modules[index] 53 | if (!b) { 54 | loading("Done!"); 55 | process.stdout.write("\n\u001B[?25h") 56 | return callback() 57 | } 58 | setTimeout(function () { 59 | loading("Installing " + b.name); 60 | }, 500) 61 | exec("npm install " + b.name + "@" + b.version, function (error, stdout, stderr) { 62 | if (error) { 63 | console.log("Error with installing module: " + b.name + "\n" + error + "\n"); 64 | } 65 | loading("Installed " + b.name); 66 | index++; 67 | install(); 68 | }); 69 | } 70 | 71 | function callback() { 72 | fs.writeFileSync("modules.json", JSON.stringify(toCheck), "utf8") 73 | var main = require('./source/core/controller.js') 74 | //require('./cpu.js').init('./data') 75 | //require('./heap.js').init('./data') 76 | 77 | 78 | Map.prototype.every = function (c) { 79 | var a = this.entries() 80 | var b; 81 | while (b = a.next().value) { 82 | if (!c(b[1], b[0])) return false; 83 | } 84 | 85 | return true; 86 | } 87 | Map.prototype.toArray = function () { 88 | var array = []; 89 | this.forEach(function (a) { 90 | array.push(a) 91 | }) 92 | return array 93 | } 94 | 95 | Map.prototype.map = function (c) { 96 | var f = new Map(); 97 | var a = this.entries() 98 | var b; 99 | while (b = a.next().value) { 100 | f.set(b[0], c(b[1], b[0])) 101 | } 102 | return f; 103 | 104 | } 105 | Map.prototype.filter = function (c) { 106 | var f = new Map(); 107 | var a = this.entries() 108 | var b; 109 | while (b = a.next().value) { 110 | if (c(b[1], b[0])) f.set(b[0], b[1]) 111 | } 112 | return f; 113 | 114 | } 115 | Map.prototype.peek = function () { 116 | var a = this.entries(); 117 | var b = a.next().value; 118 | return (b) ? b[1] : false; 119 | } 120 | 121 | 122 | 123 | main() 124 | 125 | } 126 | -------------------------------------------------------------------------------- /install.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "cli-gui", 4 | "version": "latest" 5 | }, 6 | { 7 | "name": "toobusy-js", 8 | "version": "latest" 9 | }, 10 | { 11 | "name": "rson", 12 | "version": "latest" 13 | }, 14 | { 15 | "name": "quickmap", 16 | "version": "latest" 17 | }, 18 | { 19 | "name": "hashbounds", 20 | "version": "4.2.2" 21 | }, 22 | { 23 | "name": "styleme", 24 | "version": "latest" 25 | }, 26 | { 27 | "name": "node-cryptojs-aes", 28 | "version": "latest" 29 | }, 30 | { 31 | "name": "minirequest", 32 | "version": "latest" 33 | }, 34 | { 35 | "name": "asyncconsole", 36 | "version": "latest" 37 | }, 38 | { 39 | "name": "nodesounds", 40 | "version": "latest" 41 | }, 42 | { 43 | "name": "cligui2", 44 | "version": "latest" 45 | }, 46 | { 47 | "name": "express", 48 | "version": "4" 49 | }, 50 | { 51 | "name": "pem", 52 | "version": "latest" 53 | }, 54 | { 55 | "name": "simplesockets", 56 | "version": "1.0.3" 57 | } 58 | ] 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "OpenAgar", 3 | "version": "4.5.0", 4 | "description": "An open source web game", 5 | "main": "index.js", 6 | "dependencies": { 7 | "SmartConfig": "latest", 8 | "asyncconsole": "^1.3.9", 9 | "cli-gui": "^2.5.9", 10 | "cligui2": "^2.0.0", 11 | "express": "^4.15.3", 12 | "hashbounds": "^4.2.2", 13 | "minirequest": "^1.4.0", 14 | "nodesounds": "^1.7.0", 15 | "pem": "^1.9.7", 16 | "quickmap": "^1.2.9", 17 | "rson": "^1.5.8", 18 | "simplesockets": "1.0.3", 19 | "styleme": "^2.5.4", 20 | "toobusy-js": "^0.5.1" 21 | }, 22 | "devDependencies": {}, 23 | "scripts": { 24 | "test": "node build.js", 25 | "start": "node index.js" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "https://github.com/AJS-development/OpenAgar" 30 | }, 31 | "author": "AJS-development", 32 | "license": "AGPL-3.0", 33 | "bugs": { 34 | "url": "https://github.com/AJS-development/OpenAgar/issues" 35 | }, 36 | "homepage": "https://github.com/AJS-development/OpenAgar" 37 | } 38 | -------------------------------------------------------------------------------- /source/ai/Bot.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var BotAi = require('./botAI.js') 20 | module.exports = class Bot { 21 | constructor(id, server, botid) { 22 | this.id = id 23 | this.botid = botid 24 | this.score = 0; 25 | this.mouse = { 26 | x: 0, 27 | y: 0 28 | } 29 | 30 | this.center = { 31 | x: 0, 32 | y: 0 33 | } 34 | this.team = 0; 35 | 36 | this.server = server; 37 | this.cells = []; 38 | this.send = false; 39 | this.ai = new BotAi(this) 40 | } 41 | onRemove(main) { 42 | 43 | this.cells.forEach((cell) => { 44 | main.removeNode(cell) 45 | }) 46 | 47 | } 48 | addCell(cell) { 49 | 50 | if (this.cells.indexOf(cell) != -1) return; 51 | this.cells.push(cell) 52 | 53 | } 54 | 55 | onDeath() { 56 | 57 | 58 | } 59 | spawn() { 60 | 61 | this.server.spawn(this) 62 | 63 | } 64 | getScore(re) { 65 | if (this.cells.length == 0) return 0; 66 | 67 | var l = 0; 68 | this.cells.forEach((n) => { 69 | l += n.mass; 70 | }) 71 | this.mass = l; 72 | this.score = Math.max(this.score, l) 73 | return this.score 74 | } 75 | 76 | setRandom() { 77 | if (!this.a) return; 78 | var a = this.a 79 | this.mouse.x = Math.floor(a.width * Math.random()) + a.x; 80 | this.mouse.y = Math.floor(a.height * Math.random()) + a.y 81 | 82 | } 83 | ejectMass() { 84 | this.server.ejectMass(this) 85 | } 86 | splitCells() { 87 | this.server.splitPlayer(this) 88 | } 89 | getSmallest() { 90 | if (this.cells.length == 0) return; 91 | var min = this.cells[0] 92 | 93 | this.cells.forEach((cell) => { 94 | if (cell.mass < min.mass) min = cell; 95 | }) 96 | return min; 97 | } 98 | update() { // 0.05 sec 99 | 100 | if (this.cells.length == 0) this.spawn() 101 | var a = this.calcView() 102 | if (!a) return 103 | 104 | // if (this.center.x == this.mouse.x || this.center.y == this.mouse.y) 105 | this.view = a; 106 | this.nodes = this.server.nodes.toArray(this.view) 107 | this.ai.update() 108 | 109 | // this.checkDeath() 110 | /* 111 | if (this.timers.changeDir >= 20) { 112 | this.timers.changeDir = 0; 113 | var a = this.calcView() 114 | this.mouse.x = Math.floor(a.width * Math.random()) + a.x; 115 | this.moude.y = Math.floor(a.height * Math.random()) + a.y 116 | this.checkDeath() 117 | } else { 118 | this.timers.changeDir ++; 119 | } 120 | */ 121 | 122 | 123 | } 124 | 125 | calcView() { 126 | if (this.cells.length == 0) return 127 | var totalSize = 1.0; 128 | var x = 0, 129 | y = 0; 130 | // console.log(this.cells) 131 | this.cells.forEach((cell) => { 132 | 133 | if (!cell) return 134 | x += cell.position.x 135 | y += cell.position.y 136 | totalSize += cell.getSize(); 137 | }) 138 | this.center.x = x / this.cells.length 139 | this.center.y = y / this.cells.length 140 | var factor = Math.pow(Math.min(64.0 / totalSize, 1), 0.4); 141 | this.sightRangeX = this.server.getConfig().serverViewBaseX / factor; 142 | this.sightRangeY = this.server.getConfig().serverViewBaseY / factor; 143 | this.a = { 144 | x: this.center.x - this.sightRangeX, 145 | y: this.center.y - this.sightRangeY, 146 | height: 2 * this.sightRangeY, 147 | width: 2 * this.sightRangeX 148 | } 149 | return this.a 150 | } 151 | shouldSend() { 152 | var a = this.send 153 | if (this.send) this.send = false; 154 | return a 155 | } 156 | changeColor(color) { 157 | 158 | 159 | } 160 | changeName(name) { 161 | 162 | 163 | } 164 | removeCell(cell) { 165 | var ind = this.cells.indexOf(cell) 166 | if (ind != -1) this.cells.splice(ind, 1) 167 | // console.log(this.cells.length) 168 | if (this.cells.length == 0) return this.onDeath() 169 | } 170 | 171 | } -------------------------------------------------------------------------------- /source/ai/Minion.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var Template = require('../core/PlayerTemplate.js'); 20 | 21 | module.exports = class Minion extends Template { 22 | constructor(server, id, name, botid, parent) { 23 | super(id, server) 24 | this.parent = parent 25 | this.botid = botid 26 | this.isBot = true; 27 | this.isMinion = true; 28 | this.mouse = this.parent.mouse 29 | 30 | this.timer = { 31 | changeDir: 0, 32 | } 33 | this.gameData = { 34 | name: name, 35 | color: server.getRandomColor(), 36 | chatname: "", 37 | reservedChatNames: [], 38 | chkDeath: false 39 | } 40 | 41 | this.spawn() 42 | } 43 | 44 | 45 | onRemove(main) { 46 | this.parent.removeMinion(this) 47 | this.removed = true; 48 | } 49 | kick() { 50 | this.server.removeMinion(this) 51 | } 52 | 53 | 54 | 55 | spawn() { 56 | if (this.cells.size > 0 || this.removed) return; 57 | 58 | 59 | this.server.spawn(this) 60 | 61 | 62 | if (this.parent.pausem) this.frozen = true; 63 | } 64 | 65 | 66 | setRandom() { 67 | if (!this.a) return; 68 | var a = this.a 69 | this.mouse.x = Math.floor(a.width * Math.random()) + a.x; 70 | this.mouse.y = Math.floor(a.height * Math.random()) + a.y 71 | } 72 | 73 | 74 | 75 | } -------------------------------------------------------------------------------- /source/ai/fakePlayer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var Template = require('../core/PlayerTemplate.js'); 20 | 21 | module.exports = class FakePlayer extends Template { 22 | constructor(server, id, name, botid) { 23 | super(id, server) 24 | 25 | this.botid = botid 26 | 27 | this.isBot = true; 28 | this.gameData = { 29 | name: name, 30 | color: server.getRandomColor(), 31 | chatname: "", 32 | reservedChatNames: [], 33 | chkDeath: false 34 | } 35 | 36 | server.spawn(this) 37 | } 38 | onRemove(main) { 39 | 40 | } 41 | 42 | kick() { 43 | this.server.removeBot(this) 44 | } 45 | 46 | onDeath() { 47 | this.mass = 0; 48 | this.score = 0; 49 | } 50 | onSpawn() { 51 | this.alive = this.server.timer.time; 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /source/child/Player.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | module.exports = class Bot { 20 | constructor(id, server) { 21 | this.id = id 22 | this.team = 0; 23 | this.score = 0; 24 | this.mouse = { 25 | x: 0, 26 | y: 0 27 | } 28 | this.center = { 29 | x: 0, 30 | y: 0 31 | } 32 | 33 | this.server = server; 34 | this.cells = []; 35 | 36 | } 37 | onRemove(main) { 38 | this.cells.forEach((cell) => { 39 | main.removeNode(cell) 40 | }) 41 | } 42 | addCell(cell) { 43 | 44 | if (this.cells.indexOf(cell) != -1) return; 45 | this.cells.push(cell) 46 | 47 | } 48 | 49 | onDeath() { 50 | 51 | 52 | } 53 | spawn() { 54 | 55 | this.server.spawn(this) 56 | 57 | } 58 | getScore(re) { 59 | if (this.cells.length == 0) return 0; 60 | 61 | var l = 0; 62 | this.cells.forEach((n) => { 63 | l += n.mass; 64 | }) 65 | this.mass = l; 66 | this.score = Math.max(this.score, l) 67 | return this.score 68 | 69 | } 70 | 71 | getRandom() { 72 | if (!this.a) return; 73 | var a = this.a 74 | return { 75 | x: Math.floor(a.width * Math.random()) + a.x, 76 | y: Math.floor(a.height * Math.random()) + a.y 77 | } 78 | 79 | } 80 | 81 | calcView() { 82 | if (this.cells.length == 0) return 83 | var totalSize = 1.0; 84 | var x = 0, 85 | y = 0; 86 | // console.log(this.cells) 87 | this.cells.forEach((cell) => { 88 | 89 | if (!cell) return 90 | x += cell.position.x 91 | y += cell.position.y 92 | totalSize += cell.getSize(); 93 | }) 94 | this.center.x = x / this.cells.length 95 | this.center.y = y / this.cells.length 96 | var factor = Math.pow(Math.min(64.0 / totalSize, 1), 0.4); 97 | this.sightRangeX = this.server.getConfig().serverViewBaseX / factor; 98 | this.sightRangeY = this.server.getConfig().serverViewBaseY / factor; 99 | this.a = { 100 | x: this.center.x - this.sightRangeX, 101 | y: this.center.y - this.sightRangeY, 102 | height: 2 * this.sightRangeY, 103 | width: 2 * this.sightRangeX 104 | } 105 | return this.a 106 | } 107 | 108 | changeColor(color) { 109 | 110 | 111 | } 112 | changeName(name) { 113 | 114 | 115 | } 116 | removeCell(cell) { 117 | var ind = this.cells.indexOf(cell) 118 | if (ind != -1) this.cells.splice(ind, 1) 119 | // console.log(this.cells.length) 120 | if (this.cells.length == 0) return this.onDeath() 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /source/child/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | OpenAgar - Open source web game 3 | Copyright (C) 2016 Andrew S 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU Affero General Public License as published 6 | by the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | This program is distributed in the hope that it will be useful, 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | GNU Affero General Public License for more details. 12 | You should have received a copy of the GNU Affero General Public License 13 | along with this program. If not, see . 14 | */ 15 | var Manager = require('./manager.js') 16 | var manager = new Manager() 17 | var managers = new Map() 18 | //require('../../cpu.js').init('./data') 19 | process.on('message', function (msg) { 20 | 21 | 22 | if (msg.sid || msg.sid == 0) { 23 | var manager = managers.get(msg.sid) 24 | if (!manager) return; 25 | } 26 | 27 | switch (msg.type) { 28 | case 0: // init 29 | 30 | manager.init(msg) 31 | break; 32 | case 1: // addnodes 33 | manager.addNodes(msg.nodes) 34 | break; 35 | case 2: // deletenodes 36 | manager.removeNodes(msg.nodes) 37 | break; 38 | case 3: // movecode 39 | manager.moveCode(msg.nodes) 40 | break; 41 | case 4: // assign 42 | manager.assign(msg.nodes) 43 | break; 44 | case 5: 45 | manager.addBot(msg.id, msg.bot) 46 | break; 47 | case 6: // stop 48 | managers.forEach(function (m) { 49 | m.onRemove() 50 | }) 51 | process.exit(0) 52 | break; 53 | case 7: // event 54 | manager.event(msg) 55 | break; 56 | 57 | case 8: // assign/deassign 58 | if (msg.a || msg.a === 0) { 59 | var mn = new Manager(msg.a) 60 | 61 | managers.set(msg.a, mn) 62 | } else if (msg.da || msg.a === 0) { 63 | var mn = manager.get(msg.da) 64 | if (mn) mn.onRemove() 65 | manager.delete(msg.da) 66 | } 67 | 68 | 69 | break; 70 | case 9: // pause; 71 | 72 | manager.pause(msg) 73 | break; 74 | 75 | } 76 | 77 | 78 | }) -------------------------------------------------------------------------------- /source/child/manager.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | You should have received a copy of the GNU Affero General Public License 14 | along with this program. If not, see . 15 | */ 16 | var HashBounds = require('../modules/HashBounds.js') 17 | var Node = require('./node.js') 18 | var Bot = require('../ai/Bot.js') 19 | var Player = require('./Player.js') 20 | var TooBusy = require('toobusy-js') 21 | 22 | 23 | 24 | 25 | module.exports = class Manager { 26 | constructor(id) { 27 | this.id = id; 28 | this.addedHash = []; 29 | 30 | this.lagDel = false; 31 | this.toSend = []; 32 | this.map = new Map() 33 | this.bots = new Map() 34 | this.config = {}; 35 | this.s = false; 36 | 37 | this.haveTeams = false; 38 | this.paused = false; 39 | this.events = {} 40 | this.bounds; 41 | this.timers = { 42 | a: 0, 43 | b: 0, 44 | c: 0 45 | } 46 | this.lag = false 47 | this.players = new Map(); 48 | setInterval(function () { 49 | var lag = TooBusy.lag(); 50 | this.lag = lag > 150 51 | if (this.lag) this.emit("lag", lag) 52 | }.bind(this), 4000) 53 | } 54 | addNodes(nodes) { 55 | 56 | nodes.forEach((node) => { 57 | 58 | if (this.addedHash[node.id]) { 59 | 60 | var n = this.map.get(node.id) 61 | n.set(node) 62 | this.nodes.update(n) 63 | return; 64 | 65 | }; 66 | 67 | this.addedHash[node.id] = true; 68 | var owner = false 69 | if (node.owner && node.type == 0) { 70 | owner = this.bots.get(node.owner) 71 | if (!owner) { 72 | owner = this.players.get(node.owner) 73 | if (!owner) { 74 | owner = new Player(node.owner, this) 75 | this.players.set(node.owner, owner) 76 | } 77 | } 78 | 79 | } 80 | 81 | 82 | var n = new Node(node, owner) 83 | n.bounds = { 84 | x: n.position.x - n.size, 85 | y: n.position.y - n.size, 86 | width: n.size * 2, 87 | height: n.size * 2 88 | } 89 | this.nodes.insert(n) 90 | this.map.set(node.id, n) 91 | 92 | }) 93 | } 94 | pause(msg) { 95 | this.paused = msg.p 96 | } 97 | updateLB() { 98 | var players = []; 99 | 100 | function insert(pl) { 101 | if (!pl.cells.length) return; 102 | pl.getScore() 103 | if (!pl.mass) return; 104 | players.push({ 105 | i: pl.id, 106 | m: pl.mass 107 | }) 108 | } 109 | 110 | this.bots.forEach((bot) => { 111 | insert(bot) 112 | }) 113 | this.players.forEach((player) => { 114 | insert(player) 115 | 116 | }) 117 | 118 | var amount = this.getConfig().leaderBoardLen; 119 | 120 | var lb = players.sort(function (a, b) { 121 | return b.m - a.m; 122 | }).slice(0, amount); 123 | 124 | return lb; 125 | } 126 | 127 | spawn(bot) { 128 | this.toSend.push({ 129 | id: bot.id, 130 | action: 1 131 | }) 132 | 133 | } 134 | ejectMass(bot) { 135 | this.toSend.push({ 136 | id: bot.id, 137 | action: 2 138 | }) 139 | } 140 | splitPlayer(bot) { 141 | this.toSend.push({ 142 | id: bot.id, 143 | action: 3 144 | }) 145 | } 146 | removeNode(node) { 147 | node.destroyed = true; 148 | node.dead = true; 149 | this.nodes.delete(node) 150 | this.map.delete(node.id) 151 | this.addedHash[node.id] = false; 152 | node.onDelete(this) 153 | } 154 | 155 | removeNodes(nodes) { 156 | nodes.forEach((node) => { 157 | var n = this.map.get(node.id) 158 | if (n) this.removeNode(n) 159 | }) 160 | 161 | } 162 | asign() { 163 | 164 | } 165 | getConfig() { 166 | return this.config 167 | } 168 | moveCode(nodes) { 169 | 170 | nodes.forEach((node) => { 171 | var n = this.map.get(node.id) 172 | if (n) { 173 | 174 | n.position.x = node.x 175 | n.position.y = node.y 176 | n.bounds = { 177 | x: n.position.x - n.size, 178 | y: n.position.y - n.size, 179 | width: n.size * 2, 180 | height: n.size * 2 181 | } 182 | this.nodes.update(n) 183 | } 184 | }) 185 | } 186 | getRandomPos() { 187 | var x = Math.floor(this.bounds.width * Math.random()); 188 | var y = Math.floor(Math.random() * this.bounds.height); 189 | return { 190 | x: x, 191 | y: y 192 | }; 193 | } 194 | init(msg) { 195 | 196 | this.config = msg.config; 197 | this.haveTeams = msg.teams; 198 | this.bounds = msg.bounds; 199 | this.nodes = new HashBounds(5, 3, Math.max(this.bounds.width, this.bounds.height) + 700, 700); // 32 min, 256 max 200 | try { 201 | 202 | clearInterval(this.interval) 203 | } catch (e) { 204 | 205 | } 206 | this.interval = setInterval(function () { 207 | if (this.paused) return; 208 | this.loop() 209 | }.bind(this), 50) 210 | this.slowInt = setInterval(function () { 211 | this.slowLoop() 212 | }.bind(this), 5000) 213 | this.on('delPlayer', function (ps) { 214 | 215 | this.removeClient(ps) 216 | 217 | 218 | }.bind(this)) 219 | } 220 | onRemove() { 221 | try { 222 | clearInterval(this.interval) 223 | } catch (e) { 224 | 225 | } 226 | try { 227 | clearInterval(this.slowInt) 228 | } catch (e) { 229 | 230 | } 231 | this.bots.forEach((b) => { 232 | b.onRemove(this) 233 | }) 234 | this.players.forEach((b) => { 235 | b.onRemove(this) 236 | }) 237 | this.addedHash = false; 238 | this.nodes = false 239 | this.toSend = false; 240 | this.map = false 241 | this.bots = false 242 | this.config = false; 243 | this.s = false; 244 | this.haveTeams = false; 245 | this.events = false 246 | this.timers = false 247 | this.players = false 248 | 249 | } 250 | 251 | removeClient(id) { 252 | var a = this.bots.get(id) 253 | if (a) { 254 | this.bots.delete(id) 255 | a.onRemove(this) 256 | return; 257 | } 258 | var a = this.players.get(id) 259 | if (a) { 260 | this.players.delete(id) 261 | a.onRemove(this) 262 | return; 263 | } 264 | } 265 | addBot(id, bot) { 266 | this.bots.set(id, new Bot(id, this, bot)) 267 | } 268 | emit(event, data) { 269 | var a = { 270 | e: event, 271 | d: data 272 | } 273 | this.toSend.push(a) 274 | } 275 | event(msg) { 276 | var e = msg.e 277 | var d = msg.d 278 | if (this.events[e]) this.events[e](d) 279 | } 280 | on(e, f) { 281 | 282 | this.events[e] = f 283 | } 284 | clearEvents() { 285 | this.events = {}; 286 | } 287 | slowLoop() { // 5 s 288 | if (this.timers.c >= 1) { 289 | var mass = this.getTotalMass() 290 | 291 | this.emit('totmass', mass) 292 | 293 | this.timers.c = 12 294 | } else this.timers.c++; 295 | } 296 | loop() { // 0.005 s 297 | if (this.lag) { 298 | this.lagDel = !this.lagDel 299 | if (this.lagDel) return; 300 | 301 | } 302 | setTimeout(function () { 303 | this.updatePlayers() 304 | }.bind(this), 1) 305 | 306 | 307 | if (this.timers.a >= 100) { 308 | var lb = this.updateLB() 309 | this.checkMass() 310 | if (lb.length != 0) this.emit('lb', lb) 311 | 312 | this.timers.a = 0; 313 | } else this.timers.a++; 314 | 315 | 316 | if (this.timers.b >= 10) { 317 | if (this.bots.size > 0) { 318 | 319 | this.bots.forEach((bot) => { 320 | setTimeout(function () { 321 | bot.update() 322 | }, 1) 323 | if (bot.shouldSend()) this.toSend.push({ 324 | i: bot.id, 325 | m: bot.mouse 326 | }) 327 | }) 328 | 329 | } 330 | if (this.toSend[0]) this.send(this.toSend) 331 | 332 | this.toSend = []; 333 | this.timers.b = 0; 334 | } else this.timers.b++; 335 | 336 | 337 | 338 | 339 | 340 | } 341 | updatePlayers() { 342 | var final = []; 343 | if (this.players.size == 0 && this.bots.size == 0) return; 344 | this.players.forEach((player) => { 345 | if (player.cells.length == 0) return; 346 | player.cells.forEach((cell) => { 347 | var list = []; 348 | this.nodes.forEach(cell.getCheck(), (node) => { 349 | if (node.id != cell.id) { 350 | 351 | if (this.lag || cell.checkSend(node)) list.push(node.id) 352 | 353 | } 354 | }) 355 | final.push({ 356 | i: cell.id, 357 | l: list 358 | }) 359 | }) 360 | }) 361 | this.bots.forEach((player) => { 362 | if (player.cells.length == 0) return; 363 | player.cells.forEach((cell) => { 364 | var list = []; 365 | this.nodes.forEach(cell.getCheck(), (node) => { 366 | if (node.id != cell.id) { 367 | 368 | if (this.lag || cell.checkSend(node)) list.push(node.id) 369 | } 370 | }) 371 | 372 | final.push({ 373 | i: cell.id, 374 | l: list 375 | }) 376 | }) 377 | }) 378 | if (final.length == 0) return; 379 | var a = { 380 | d: final, 381 | p: true 382 | } 383 | 384 | this.send(a) 385 | 386 | } 387 | send(data) { 388 | 389 | try { 390 | process.send({ 391 | id: this.id, 392 | data: data 393 | }) 394 | 395 | } catch (e) { 396 | process.exit(0) 397 | } 398 | } 399 | getTotalMass() { 400 | var amount = 0; 401 | 402 | this.map.forEach((node, i) => { 403 | if (node.dead) { 404 | this.nodes.delete(i) 405 | this.map.delete(i) 406 | this.addedHash[node.id] = false; 407 | return; 408 | } 409 | amount += node.mass 410 | }) 411 | return amount; 412 | } 413 | checkMass() { 414 | var list = []; 415 | var max = this.config.playerMaxMass 416 | this.players.forEach((player) => { 417 | player.cells.forEach((cell) => { 418 | if (cell.mass > max) list.push(cell.id) 419 | }) 420 | 421 | }) 422 | this.bots.forEach((player) => { 423 | player.cells.forEach((cell) => { 424 | if (cell.mass > max) list.push(cell.id) 425 | }) 426 | 427 | }) 428 | if (list.length == 0) return; 429 | this.emit('mass', list) 430 | } 431 | 432 | other() { 433 | 434 | } 435 | 436 | } 437 | -------------------------------------------------------------------------------- /source/child/node.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = class Node { 4 | constructor(node, owner) { 5 | this.type = node.type; 6 | this.position = node.position; 7 | this.bounds = null; 8 | this.id = node.id; 9 | this.owner = owner 10 | this.size = node.size; 11 | this.speed = node.speed 12 | 13 | this.mass = node.mass; 14 | 15 | this.destroyed = false; 16 | this.init() 17 | 18 | } 19 | 20 | init() { 21 | 22 | if (this.owner) { 23 | this.owner.addCell(this) 24 | } 25 | } 26 | getCheck() { // get checking bounds 27 | var dif = Math.min(100 / this.size, 2) * 25; // smaller nodes have bigger collision range since they go faster. 28 | 29 | return { 30 | x: this.bounds.x - dif, 31 | y: this.bounds.y - dif, 32 | width: this.bounds.width + dif * 2, 33 | height: this.bounds.height + dif * 2 34 | 35 | } 36 | 37 | } 38 | 39 | 40 | 41 | checkSend(node) { 42 | var size = node.size + this.size + (Math.min(100 / this.size, 1.1) * 100) // smaller nodes have bigger collision range since they go faster. Fine tunes previous check. 43 | 44 | /* 45 | ____________________________________ 46 | / I tried to make a flowchart, but I \ 47 | \ failed. / 48 | ------------------------------------ 49 | \ 50 | \ 51 | 52 | [-] 53 | (+)=C 54 | | | 55 | OOO 56 | 57 | 58 | */ 59 | 60 | var x = this.position.x - node.position.x 61 | var y = this.position.y - node.position.y 62 | return (size * size >= x * x + y * y) 63 | } 64 | set(node) { 65 | this.type = node.type || this.type; 66 | this.position = node.position || this.position; 67 | this.bounds = node.bounds || this.bounds; 68 | 69 | this.size = node.size || this.size; 70 | 71 | 72 | this.mass = node.mass || this.mass; 73 | this.speed = node.speed || this.speed 74 | } 75 | getSize() { 76 | return this.mass 77 | } 78 | onDelete(main) { 79 | if (this.owner) this.owner.removeCell(this) 80 | } 81 | 82 | 83 | 84 | } -------------------------------------------------------------------------------- /source/commands/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | list: { 4 | addbot: require('./lib/addBot.js'), 5 | kick: require('./lib/kick.js'), 6 | pause: require('./lib/pause.js'), 7 | kickbots: require('./lib/kickbots.js'), 8 | list: require('./lib/list.js'), 9 | help: function (str, main, log) { 10 | log("|-----------------Available Commands------------------|") 11 | log("| Help | Shows help for commands |") 12 | log("| Stop | Stops the server |") 13 | log("| List | List players, bots, etc |") 14 | log("| Plugin | Plugin command |") 15 | log("| Kick | Kick a player |") 16 | log("| Ban | Ban a player |") 17 | log("| Server | Multi-Server command |") 18 | log("| Pause | Pause the game |") 19 | log("| Addbot | Add bots |") 20 | log("| Debug | Toggle debug console |") 21 | log("| Update | Update software |") 22 | log("| Restart | Schedule restarts |") 23 | log("| Kickbots | Kick bots |"); 24 | 25 | if (_lvl() != 0) { 26 | log("| Mod | OpenAgar Sys Moderation |"); 27 | } 28 | main.pluginService.addToHelp.forEach((cmd) => { 29 | log(cmd) 30 | }) 31 | log("|-----------------------------------------------------|") 32 | 33 | } 34 | }, 35 | serverService: { 36 | server: require('./lib/server.js'), 37 | startv: require('./lib/startv.js'), 38 | stop: require('./lib/stop.js'), 39 | ban: require('./lib/ban.js'), 40 | redraw: require('./lib/redraw.js'), 41 | plugin: require('./lib/plugin.js'), 42 | update: require('./lib/update.js'), 43 | restart: require('./lib/restart.js'), 44 | debug: require('./lib/debug.js'), 45 | mod: function (a, b, c) { 46 | _mod(a, b, c) 47 | } 48 | }, 49 | chat: { 50 | help: function (str, main, player, log) { 51 | log("================= Commands =================") 52 | log("Help | Shows help for commands") 53 | main.pluginService.chatA.forEach((cmd) => { 54 | log(cmd) 55 | }) 56 | log("============================================") 57 | 58 | } 59 | 60 | } 61 | 62 | 63 | }; 64 | -------------------------------------------------------------------------------- /source/commands/lib/addBot.js: -------------------------------------------------------------------------------- 1 | module.exports = function (str, main, log) { 2 | str = str.split(" ") 3 | var amount = parseInt(str[1]) 4 | 5 | if (isNaN(amount)) amount = 1 6 | main.addBots(amount) 7 | 8 | log("gre{[OpenAgar]} ".styleMe() + amount + " bots added") 9 | } -------------------------------------------------------------------------------- /source/commands/lib/ban.js: -------------------------------------------------------------------------------- 1 | module.exports = function (str, ss, log) { 2 | var split = str.split(" ") 3 | if (split[1] == "record") { 4 | 5 | require('fs').writeFileSync(__dirname + '/../../../ban.txt', ss.globalData.ban.join("\n")) 6 | log("gre{[OpenAgar]} Succesfully recorded ban".styleMe()) 7 | 8 | return; 9 | 10 | } 11 | var id = parseInt(split[1]) 12 | if (isNaN(id)) { 13 | 14 | return log("cya{[OpenAgar]} Please specify a player id!".styleMe()) 15 | } 16 | var player = ss.getPlayer(id) 17 | if (!player) return log("cya{[OpenAgar]} That player wasnt found!".styleMe()) 18 | if (player.isBot) return log("cya{[OpenAgar]} That player is a bot!".styleMe()) 19 | var ip = player.socket.remoteAddress 20 | ss.globalData.ban.push(ip) 21 | var count = 0; 22 | ss.clients.forEach((client) => { 23 | if (client._remoteAddress == ip) { 24 | count++; 25 | client._player.kick("You have been banned!") 26 | } 27 | 28 | }) 29 | 30 | log("gre{[OpenAgar]} Banned ".styleMe() + count + " players with an ip of " + player.socket.remoteAddress) 31 | 32 | } 33 | -------------------------------------------------------------------------------- /source/commands/lib/debug.js: -------------------------------------------------------------------------------- 1 | module.exports = function(str,ss,log) { 2 | if (ss._DEBUG) { 3 | ss._DEBUG = false; 4 | 5 | ss.controller.shellService.select(ss.selected.id) 6 | console.log("Gettting off the debug console...") 7 | } else { 8 | ss._DEBUG = true; 9 | 10 | ss.controller.shellService.select(0) 11 | console.log("Debug console loading...") 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /source/commands/lib/kick.js: -------------------------------------------------------------------------------- 1 | module.exports = function (str, main, log) { 2 | var id = parseInt(str.split(" ")[1]) 3 | if (isNaN(id)) { 4 | 5 | return log("cya{[OpenAgar]} Please specify a player id!".styleMe()) 6 | } 7 | var player = main.getPlayer(id) 8 | if (!player) return log("cya{[OpenAgar]} That player wasnt found!".styleMe()) 9 | 10 | player.kick() 11 | log("gre{[OpenAgar]} Succesfully kicked ".styleMe() + player.gameData.name) 12 | 13 | } -------------------------------------------------------------------------------- /source/commands/lib/kickbots.js: -------------------------------------------------------------------------------- 1 | module.exports = function(str,main,log) { 2 | str = str.split(" "); 3 | var count = parseInt(str[1]) 4 | if (isNaN(count)) count = -1; 5 | var kicked = 0; 6 | main.bots.every(function(bot) { 7 | main.removeBot(bot) 8 | kicked ++; 9 | if (kicked >= count && count > 0) return false; 10 | return true; 11 | }) 12 | log("gre{[OpenAgar]} Kicked ".styleMe() + kicked + " bots") 13 | } 14 | -------------------------------------------------------------------------------- /source/commands/lib/libraries: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/AJS-development/OAPlugins/master/plugins.json -------------------------------------------------------------------------------- /source/commands/lib/list.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | module.exports = function (str, main, log) { 3 | var fill = function (a, num, char) { 4 | char = char || " " 5 | num -= a.length 6 | for (var i = 0; i < num; i++) { 7 | a += char 8 | } 9 | return a 10 | } 11 | str = str.split(" ") 12 | if (str[1] == "players") { 13 | 14 | if (main.clients.size == 0) return log("cya{[OpenAgar]} There are no players in the game!".styleMe()) 15 | // 50 16 | log("|------------------------------------Players-----------------------------------|") 17 | log("| Id | Ip | Name | ChatName | LBrank | posX | posY |") 18 | main.clients.forEach((client) => { 19 | 20 | 21 | var name = client.gameData.name 22 | var chatname = client.gameData.chatName 23 | name = name || "An Unamed Cell"; 24 | var rank = client.rank || "NA"; 25 | var center = { 26 | x: "NA", 27 | y: "NA" 28 | } 29 | 30 | if (client.center.x) { 31 | center = client.center 32 | center.y = Math.round(center.y) 33 | center.x = Math.round(center.x) 34 | } 35 | log("|" + fill(client.id.toString(), 5) + "|" + fill(client.socket.remoteAddress, 12) + "|" + fill(name, 16) + "|" + fill(chatname, 18) + "|" + fill(rank.toString(), 8) + "|" + fill(center.x.toString(), 7) + "|" + fill(center.y.toString(), 6) + "|") 36 | }) 37 | 38 | } else if (str[1] == "bots") { 39 | if (main.bots.size == 0) return log("cya{[OpenAgar]} There are no bots in the game!".styleMe()) 40 | log("|-------------------------------------Bots-------------------------------------|") 41 | log("| Id | BotId | Name | LBrank | posX | posY | Alive | Mass |") 42 | main.bots.forEach((client) => { 43 | 44 | 45 | var name = client.gameData.name 46 | var chatname = client.gameData.chatName 47 | name = name || "An Unamed Cell"; 48 | var rank = client.rank || "NA"; 49 | var center = { 50 | x: "NA", 51 | y: "NA" 52 | } 53 | var alive = main.timer.time - client.alive 54 | alive = Math.round(alive / 6000) 55 | alive = alive / 10 56 | 57 | if (client.cells.peek()) { 58 | center = client.cells.peek().position 59 | center.y = Math.round(center.y) 60 | center.x = Math.round(center.x) 61 | } 62 | 63 | log("|" + fill(client.id.toString(), 5) + "|" + fill(client.botid.toString(), 7) + "|" + fill(name, 16) + "|" + fill(rank.toString(), 8) + "|" + fill(center.x.toString(), 9) + "|" + fill(center.y.toString(), 9) + "|" + fill(alive.toString(), 9) + "|" + fill(Math.round(client.mass).toString(), 8) + "|") 64 | }) 65 | } else if (str[1] == "minions") { 66 | if (main.minions.size == 0) return log("cya{[OpenAgar]} There are no minions in the game!".styleMe()) 67 | log("|-----------------------------------Minions------------------------------------|") 68 | log("| Id | BotId | Name | LBrank | posX | posY | OwnerId | Mass |") 69 | main.minions.forEach((client) => { 70 | 71 | 72 | var name = client.gameData.name 73 | var chatname = client.gameData.chatName 74 | name = name || "An Unamed Cell"; 75 | var rank = client.rank || "NA"; 76 | var center = { 77 | x: "NA", 78 | y: "NA" 79 | } 80 | 81 | 82 | if (client.cells.peek()) { 83 | center = client.cells.peek().position 84 | center.y = Math.round(center.y) 85 | center.x = Math.round(center.x) 86 | } 87 | 88 | log("|" + fill(client.id.toString(), 5) + "|" + fill(client.botid.toString(), 7) + "|" + fill(name, 16) + "|" + fill(rank.toString(), 8) + "|" + fill(center.x.toString(), 9) + "|" + fill(center.y.toString(), 9) + "|" + fill(client.parent.id.toString(), 9) + "|" + fill(Math.round(client.mass).toString(), 8) + "|") 89 | }) 90 | } else if (str[1] == "help") { 91 | log("|-------Available Commands for List-------|") 92 | log("|players | Lists the players in the server|") 93 | log("|bots | Lists the bots in the server |") 94 | log("|minions | Lists the minions in the server|") 95 | log("|help | Displays a list of actions |") 96 | log("|-----------------------------------------|") 97 | } else { 98 | log("cya{[OpenAgar]} Action not found, please do list help to see a list of actions".styleMe()) 99 | 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /source/commands/lib/pause.js: -------------------------------------------------------------------------------- 1 | module.exports = function(str,main,log) { 2 | main.pause() 3 | if (main.paused) { 4 | log("gre{[OpenAgar]} Paused the game".styleMe()) 5 | } else { 6 | log("gre{[OpenAgar]} Unpaused the game".styleMe()) 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /source/commands/lib/plugin.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var request = require('minirequest') 3 | var fs = require('fs') 4 | function search(a,list) { 5 | 6 | var title = []; 7 | var desc = []; 8 | var others = [] 9 | list.forEach((item)=>{ 10 | if (item.name) { 11 | var b = item.name.toLowerCase(); 12 | if (b.indexOf(a.toLowerCase()) != -1) { 13 | title.push(item) 14 | return; 15 | } 16 | } 17 | if (item.description) { 18 | if (item.description.toLowerCase().indexOf(a.toLowerCase()) != -1) { 19 | desc.push(item) 20 | return; 21 | } 22 | } 23 | var b = a.split(" ") 24 | var f = 0; 25 | b.every((c)=>{ 26 | if (f > 5) { 27 | others.push(item) 28 | return false; 29 | 30 | } 31 | if (item.name && item.name.toLowerCase().indexOf(c.toLowerCase()) != -1) { 32 | f += 2; 33 | return true; 34 | } 35 | if (item.description && item.description.toLowerCase().indexOf(c.toLowerCase()) != -1) { 36 | f ++; 37 | return true; 38 | } 39 | }) 40 | 41 | 42 | }) 43 | var final = []; 44 | 45 | final = final.concat(title); 46 | final = final.concat(desc); 47 | final = final.concat(others); 48 | return final 49 | 50 | } 51 | 52 | module.exports = function(str,ss,log) { 53 | var main = ss.selected 54 | str = str.split(" ") 55 | var action = str[1] 56 | 57 | switch (action) { 58 | case "add": 59 | if (!str[2]) return log("Please specify the plugin name!") 60 | if (!str[3]) return log("Please specify the plugin url!") 61 | log("Connecting to " + str[3]) 62 | request(str[3],function(e,r,b) { 63 | if (!e && r.responseCode == 200 && b) { 64 | fs.writeFileSync(__dirname + '/../../plugins/' + str[2] + '.ajs',b) 65 | log("Plugin succesfully downloaded. Reloading plugins") 66 | setTimeout(function() { 67 | main.pluginService.reload() 68 | },1000) 69 | } else { 70 | return log("That URL was not found!") 71 | } 72 | 73 | }) 74 | break; 75 | case "install": 76 | if (!str[2]) return log("Please specify a plugin name!") 77 | var libraries = fs.readFileSync(__dirname + '/libraries',"utf8") 78 | libraries = libraries.split('\n') 79 | var done = false; 80 | var count = 0; 81 | var am = 0; 82 | libraries.forEach((library)=>{ 83 | library = library.replace(/\s/g,"") 84 | if (!library) return; 85 | count ++; 86 | am ++; 87 | request(library + "?" + Math.floor(Math.random() * 1000),(e,r,b)=>{ 88 | 89 | if (!e && r.statusCode == 200 && b) { 90 | var plugins = JSON.parse(b) 91 | plugins.every((plugin)=>{ 92 | if (done) return false; 93 | if (!plugin.name || !plugin.src || str[2] != plugin.name) return true; 94 | done = true; 95 | log("Found plugin. Downloading...") 96 | request(plugin.src + "?" + Math.floor(Math.random() * 1000),(e,r,b)=>{ 97 | if (!e && r.statusCode == 200 && b) { 98 | fs.writeFileSync(__dirname + '/../../plugins/' + plugin.name + '.ajs',b) 99 | log("Plugin succesfully downloaded. Reloading plugins") 100 | setTimeout(function() { 101 | main.pluginService.reload() 102 | },1000) 103 | } else { 104 | log("URL " + plugin.src + " is not accesable!") 105 | } 106 | }) 107 | return false; 108 | }) 109 | } else { 110 | log("Failed to connect to library " + library) 111 | } 112 | am --; 113 | if (am <= 0) { 114 | if (!done) log("Plugin " + str[2] + " was not found. Search for plugins using plugin search [keyword]") 115 | } 116 | }) 117 | }) 118 | log("Requesting plugins from " + count + " libraries") 119 | 120 | break; 121 | case "reload": 122 | log("Reloading plugins...") 123 | main.pluginService.reload() 124 | break; 125 | case "library": 126 | if (!str[2]) return log("Please specify the library file url") 127 | var libraries = fs.readFileSync(__dirname + '/libraries',"utf8") 128 | libraries = libraries.split('\n') 129 | var out = []; 130 | libraries.forEach((library)=>{ 131 | library = library.replace(/\s/g,"") 132 | if (library) out.push(library) 133 | }) 134 | out.push(str[2]) 135 | fs.writeFileSync(__dirname + '/libraries',out.join("\n")) 136 | log("Added library " + str[2]) 137 | break; 138 | case "search": 139 | var a = str.slice(2).join(" ") 140 | if (!a) return log("Please enter something to search") 141 | var libraries = fs.readFileSync(__dirname + '/libraries',"utf8").split("\n") 142 | var list = []; 143 | var count = 0; 144 | var out = []; 145 | libraries.forEach((library)=>{ 146 | library = library.replace(/\s/g,"") 147 | if (!library) return; 148 | count ++; 149 | request(library + "?" + Math.floor(Math.random() * 1000),(e,r,b)=>{ 150 | 151 | if (!e && r.statusCode == 200 && b) { 152 | list = list.concat(JSON.parse(b)) 153 | } else { 154 | 155 | log("Couldnt connect to " + library) 156 | 157 | } 158 | count --; 159 | if (count <= 0) { 160 | var items = search(a,list) 161 | 162 | if (items.length == 0) return log("No items were found with the search " + a) 163 | log("Search results for " + a + " :"); 164 | items.forEach((item)=>{ 165 | log(" " + item.name + "\n Description: "+ item.description); 166 | 167 | }) 168 | } 169 | }) 170 | }) 171 | break; 172 | default: 173 | log("Command not found. Available commands: install, add, library, search") 174 | break; 175 | 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /source/commands/lib/redraw.js: -------------------------------------------------------------------------------- 1 | module.exports = function(str,ss,log) { 2 | log("gre{[OpenAgar]} Redrawing...".styleMe()) 3 | ss.controller.shellService.writeLog(function() { 4 | log("gre{[OpenAgar]} Done".styleMe()) 5 | }) 6 | 7 | } -------------------------------------------------------------------------------- /source/commands/lib/reload.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/commands/lib/reset.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/commands/lib/restart.js: -------------------------------------------------------------------------------- 1 | module.exports = function(str,ss,log) { 2 | 3 | str = str.split(" ") 4 | 5 | if (str[1] == "all") { 6 | var time = parseInt(str[2]) 7 | if (str[2] == "now" || time == 0) { 8 | 9 | 10 | ss.restartAll() 11 | ss.controller.shellService.drawSplash() 12 | log("gre{[OpenAgar]} Restarted all servers".styleMe()) 13 | return; 14 | } 15 | if (isNaN(time)) return log("Please specify time, in minutes, or type 'restart now' to restart now") 16 | setTimeout(function() { 17 | console.log('\033[2J'); 18 | 19 | ss.restartAll() 20 | ss.controller.shellService.drawSplash() 21 | log("gre{[OpenAgar]} Restarted all servers".styleMe()) 22 | },60000 * time) 23 | 24 | } else { 25 | var time = parseInt(str[1]) 26 | if (str[1] == "now" || time == 0) { 27 | process.stdout.write("\u001b[2J\u001b[0;0H"); 28 | ss.restartSelected() 29 | log("gre{[OpenAgar]} Restarted selected server".styleMe()) 30 | return; 31 | } 32 | if (isNaN(time)) return log("Please specify time, in minutes, or type 'restart now' to restart now") 33 | setTimeout(function() { 34 | process.stdout.write("\u001b[2J\u001b[0;0H"); 35 | ss.restartSelected() 36 | log("gre{[OpenAgar]} Restarted selected server".styleMe()) 37 | },60000 * time) 38 | 39 | 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /source/commands/lib/save.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/commands/lib/server.js: -------------------------------------------------------------------------------- 1 | module.exports = function (str, ss, log) { 2 | var split = str.split(" ") 3 | 4 | if (split[1] == "list") { 5 | log("gre{[OpenAgar]} Listing servers...".styleMe()) 6 | var servers = ss.servers 7 | log("|------------------------------------Servers-----------------------------------|") 8 | log("| Id | Name | ScreenName | Players | Bots | Uptime | Status |") 9 | var fill = function (a, num, char) { 10 | char = char || " " 11 | num -= a.length 12 | for (var i = 0; i < num; i++) { 13 | a += char 14 | } 15 | return a 16 | } 17 | 18 | var time = Date.now() 19 | servers.forEach(function (server) { 20 | var upt = time - server.timer.init 21 | upt = (upt / 6000) >> 0 22 | upt = upt / 10 23 | var status = ""; 24 | 25 | if (server.selected && server.isMain) { 26 | status = "[Selected][M]"; 27 | } else if (server.selected) { 28 | status = "[Selected]" 29 | } else if (server.isMain) { 30 | status = "[Main]" 31 | } 32 | 33 | log("|" + fill(server.id.toString(), 5) + "|" + fill(server.name, 15) + "|" + fill(server.scname, 16) + "|" + fill(server.clients.size.toString(), 9) + "|" + fill(server.bots.size.toString(), 6) + "|" + fill(upt.toString(), 8) + "|" + fill(status, 13) + "|") 34 | }); 35 | 36 | } else if (split[1] == "create") { 37 | var name = split[2] 38 | 39 | if (!name) { 40 | 41 | return log("cya{[OpenAgar]} Please provide a name!".styleMe()) 42 | } 43 | var configOv = Util.argsParser(str, 3); 44 | 45 | var config = {}; 46 | for (var i in ss.defconfig) { 47 | config[i] = ss.defconfig[i]; 48 | } 49 | for (var i in configOv) { 50 | config[i] = configOv[i]; 51 | } 52 | if (ss.createServer(name, name, config, false)) log("gre{[OpenAgar]} Server succesfully created".styleMe()); 53 | else return log("yel{[OpenAgar]} Could not create server".styleMe()) 54 | 55 | ss.reloadInfoP() 56 | 57 | } else if (split[1] == "remove") { 58 | var id = parseInt(split[2]) 59 | if (isNaN(id)) return log("cya{[OpenAgar]} Please specify a server id!".styleMe()) 60 | 61 | if (ss.removeServer(id)) log("gre{[OpenAgar]} Removed server".styleMe()); 62 | else return log("yel{[OpenAgar]} Failed to remove server. Check to make sure it is not main or is not selected.".styleMe()) 63 | ss.reloadInfoP() 64 | } else if (split[1] == "select") { 65 | if (!split[2]) { 66 | log("cya{[OpenAgar]} Please specify a server ids".styleMe()); 67 | return; 68 | } 69 | if (ss.select(parseInt(split[2]), function () { 70 | log("gre{[OpenAgar]} Successfully switched servers".styleMe()) 71 | 72 | })) console.log("gre{[OpenAgar]} Switching servers...".styleMe()); 73 | else 74 | log("yel{[OpenAgar]} That server doesnt exist or is already selected!".styleMe()); 75 | } else { 76 | log("cya{[OpenAgar]} Please specify a command! (list, select,remove,create)".styleMe()); 77 | } 78 | 79 | 80 | } -------------------------------------------------------------------------------- /source/commands/lib/startv.js: -------------------------------------------------------------------------------- 1 | module.exports = function(serverService,txt) { 2 | 3 | 4 | } 5 | -------------------------------------------------------------------------------- /source/commands/lib/stop.js: -------------------------------------------------------------------------------- 1 | module.exports = function(str,serverService) { 2 | console.log("gre{[OpenAgar]} Closing Server".styleMe()) 3 | exit(0); 4 | 5 | } 6 | -------------------------------------------------------------------------------- /source/commands/lib/update.js: -------------------------------------------------------------------------------- 1 | module.exports = function(str,ss,log) { 2 | if (ss._dwe) ss.updater.update(); else { 3 | ss._dwe = true; 4 | setTimeout(function() { 5 | ss._dwe = false; 6 | },5000) 7 | log("Are you sure you want to update? Enter command again to proceed.") 8 | 9 | 10 | } 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /source/core/GUIHandler.js: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/core/GraphicalInterface.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | -------------------------------------------------------------------------------- /source/core/PlayerTemplate.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | 21 | module.exports = class PlayerTemplate { 22 | constructor(id, server) { 23 | this.id = id; 24 | 25 | this.server = server; 26 | 27 | this.mouse = { 28 | x: 0, 29 | y: 0 30 | } 31 | this.center = { 32 | x: 0, 33 | y: 0 34 | } 35 | this.owning = new Map(); 36 | this.mass = 0; 37 | this.frozen = false; 38 | 39 | this.score = 0; 40 | this.golden = false; 41 | this.gameData = { 42 | name: "", 43 | color: Util.getRandomColor(), 44 | chatColor: Util.getRandomColor(), 45 | reservedChatNames: [], 46 | chatName: "", 47 | chkDeath: false, 48 | chatBan: false, 49 | reservedNamesMap: [] 50 | } 51 | 52 | this.bulletsleft = 0 53 | this.killer = false; 54 | 55 | 56 | this.minions = new Map(); 57 | this.cells = new Map(); 58 | 59 | 60 | this.alive = Date.now() 61 | 62 | } 63 | msg() { 64 | 65 | } 66 | setOwn(node) { 67 | this.owning.set(node.id, node) 68 | } 69 | removeOwn(node) { 70 | this.owning.delete(node.id) 71 | } 72 | addMinion(minion) { 73 | this.minions.set(minion.id, minion) 74 | 75 | } 76 | addCell(cell) { 77 | this.cells.set(cell.id, cell) 78 | } 79 | setMass(m) { 80 | this.cells.forEach((cell) => { 81 | cell.updateMass(m) 82 | }) 83 | } 84 | removeMinion(minion) { 85 | this.minions.delete(minion.id) 86 | } 87 | reset() { 88 | this.minions.clear(); 89 | this.cells.clear(); 90 | this.visible = []; 91 | this.gameData = { 92 | name: "", 93 | color: this.server.getRandomColor(), 94 | chatColor: this.server.getRandomColor(), 95 | reservedChatNames: [], 96 | chatName: "", 97 | chkDeath: false, 98 | chatBan: false, 99 | reservedNamesMap: [] 100 | } 101 | this.owning.clear() 102 | 103 | this.sendData = false; 104 | } 105 | 106 | setColor(color) { 107 | this.gameData.color = color 108 | this.cells.forEach((cell) => { 109 | cell.color = color 110 | }) 111 | 112 | } 113 | setName(name) { 114 | this.gameData.name = name 115 | this.cells.forEach((cell) => { 116 | cell.name = name 117 | cell.updCode() 118 | }) 119 | 120 | 121 | } 122 | onDeath() { 123 | this.mass = 0; 124 | this.score = 0; 125 | this.alive = this.server.timer.time; 126 | 127 | this.spawn() 128 | } 129 | removeCell(cell) { 130 | this.cells.delete(cell.id) 131 | 132 | if (this.cells.size == 0) this.onDeath(cell.killer) 133 | } 134 | 135 | 136 | getBiggest() { 137 | var cell = false; 138 | this.cells.forEach((c) => { 139 | if (!cell || c.mass > cell.mass) cell = c 140 | }) 141 | return cell; 142 | } 143 | getScore(re) { 144 | 145 | if (re) { 146 | var l = 0; 147 | this.cells.forEach((n) => { 148 | l += n.mass; 149 | }) 150 | this.mass = l; 151 | this.score = Math.max(this.score, l) 152 | return l 153 | } 154 | this.score = Math.max(this.score, this.mass) 155 | return this.score 156 | } 157 | deleteNodes(main) { 158 | 159 | this.socket.sendDelete(main.deleteR) 160 | } 161 | 162 | } -------------------------------------------------------------------------------- /source/core/childHolder.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | module.exports = class ChildHolder { 21 | constructor(id, child) { 22 | this.id = id; 23 | this.child = child; 24 | this.listeners = []; 25 | this.assigned = 0; 26 | this.init() 27 | } 28 | _send(data) { 29 | this.child.send(data) 30 | } 31 | init() { 32 | this.child.on('message', function (msg) { 33 | if (this.listeners[msg.id]) this.listeners[msg.id](msg.data) 34 | }.bind(this)) 35 | } 36 | send(id, data) { 37 | data.sid = id; 38 | try { 39 | 40 | this._send(data) 41 | 42 | } catch (e) { 43 | 44 | } 45 | 46 | } 47 | removeListener(id) { 48 | delete this.listeners[id] 49 | } 50 | assign(sid) { 51 | 52 | this.assigned++; 53 | this._send({ 54 | type: 8, 55 | a: sid 56 | }) 57 | } 58 | deAssign(sid) { 59 | this.assigned--; 60 | } 61 | stop() { 62 | this._send({ 63 | type: 6 64 | }) 65 | delete this.child 66 | 67 | } 68 | on(id, func) { 69 | this.listeners[id] = func 70 | } 71 | } -------------------------------------------------------------------------------- /source/core/childManager.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | var Child = require('child_process') 21 | 22 | var ChildHolder = require('./childHolder.js') 23 | module.exports = class childManager { 24 | constructor(ss) { 25 | this.ss = ss; 26 | this.cpus = require('os').cpus() 27 | this.childs = new Map(); 28 | this.cid = 0 29 | this.init() 30 | 31 | } 32 | debug(a) { 33 | this.ss.controller.shellService.log(0, a) 34 | } 35 | init() { 36 | this.debug("gre{[Debug]} Number of computer cores detected: ".styleMe() + this.cpus.length) 37 | if (this.cpus.length <= 1) { 38 | console.log("red{[OpenAgar]} Your computer must have more than one core in order to run this program.".styleMe()) 39 | exit(0) 40 | } 41 | process.on('exit', function () { 42 | var count = 0; 43 | this.childs.forEach((child) => { 44 | child.stop() 45 | count++; 46 | }) 47 | Sounds.play('alert') 48 | this.debug("gre{[Debug]} Killed ".styleMe() + count + " processes") 49 | console.log("\ngre{[OpenAgar]} Killed all processes".styleMe()) 50 | }.bind(this)); 51 | } 52 | 53 | assignChild(sid) { 54 | if (this.childs.size < this.cpus.length - 1) { 55 | var child = this.createNewChild() 56 | child.assign(sid) 57 | this.debug("gre{[Debug]} Created new child (ID: ".styleMe() + child.id + ") and assigned server " + sid + " to it.") 58 | return child 59 | } 60 | var lowest = false; 61 | this.childs.forEach((child) => { 62 | if (!lowest || child.assigned < lowest.assigned) lowest = child 63 | 64 | }) 65 | if (!lowest) throw "ERR: Child was not found" 66 | 67 | lowest.assign(sid) 68 | this.debug("gre{[Debug]} Assigned server ".styleMe() + sid + " to child (ID: " + lowest.id + " ASSIGNED: " + lowest.assigned + ")") 69 | return lowest 70 | 71 | } 72 | deAssignChild(id, sid) { 73 | var child = this.childs.get(id) 74 | if (!child) throw "ERR: Cannot deassign child that doesnt exsist!" 75 | 76 | child.deAssign(sid) 77 | this.debug("gre{[Debug]} Deassigned server ".styleMe() + sid + " to child (ID: " + child.id + " ASSIGNED: " + child.assigned + ")") 78 | if (child.assigned <= 0) { 79 | child.stop() 80 | this.childs.delete(id) 81 | this.debug("gre{[Debug]} Killed child (ID: ".styleMe() + id + ")") 82 | } 83 | 84 | } 85 | 86 | getNextCID() { 87 | return this.cid++; 88 | } 89 | createNewChild() { 90 | var child = Child.fork(__dirname + '/../child/index.js') 91 | var id = this.getNextCID() 92 | var data = new ChildHolder(id, child) 93 | this.childs.set(id, data) 94 | return data; 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /source/core/childService.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | You should have received a copy of the GNU Affero General Public License 14 | along with this program. If not, see . 15 | */ 16 | 17 | module.exports = class childService { 18 | constructor(main, child) { 19 | 20 | 21 | this.child = child 22 | 23 | this.main = main; 24 | this.totalMass = 0; 25 | this.toSend = []; 26 | this.buf = 0; 27 | this.updHash = {}; 28 | this.movHash = {}; 29 | this.movCode = []; 30 | this.hash = []; 31 | this.events = {} 32 | this.lb = []; 33 | 34 | 35 | } 36 | 37 | init() { 38 | this.child.on(this.main.id, function (data) { 39 | 40 | this.onData(data) 41 | }.bind(this)) 42 | this.send(0, { 43 | hello: "hello", 44 | config: this.main.getConfig(), 45 | teams: this.main.haveTeams, 46 | bounds: this.main.bounds 47 | }) 48 | this.on('lb', function (lb) { 49 | this.lb = lb 50 | }.bind(this)) 51 | this.on('mass', function (m) { 52 | var nodes = this.main.getWorld().getNodes('player'), 53 | max = this.main.getConfig().playerMaxMass, 54 | maxCells = this.main.getConfig().playerMaxCells, 55 | splitSpeed = this.main.getConfig().splitSpeed, 56 | splitDecay = this.main.getConfig().splitDecay; 57 | m.forEach((k) => { 58 | var node = nodes.get(k) 59 | 60 | if (node) { 61 | if (node.owner.cells.size >= maxCells) node.mass = Math.min(node.mass, max); 62 | else this.main.splitPlayerCell(node, Math.random() * 6.28, splitSpeed, splitDecay) 63 | node.updCode() 64 | } 65 | }) 66 | }.bind(this)) 67 | this.on('totmass', function (m) { 68 | this.totalMass = m; 69 | }.bind(this)) 70 | this.on('lag', function (lag) { 71 | this.main.debug("yel{[Debug]} Lag detected for child ".styleMe() + this.child.id + " Latency: " + lag) 72 | }.bind(this)) 73 | } 74 | pause(state) { 75 | this.send(9, { 76 | p: state 77 | }) 78 | 79 | } 80 | on(e, f) { 81 | this.events[e] = f 82 | } 83 | emit(e, d) { 84 | this.send(7, { 85 | e: e, 86 | d: d 87 | }) 88 | } 89 | clearEvents() { 90 | this.events = {} 91 | } 92 | action(bot, action, data) { 93 | switch (action) { 94 | case 1: // spawn 95 | bot.onSpawn() 96 | this.main.spawn(bot) 97 | 98 | break; 99 | case 2: // eject 100 | this.main.ejectMass(bot) 101 | break; 102 | case 3: // split 103 | this.main.splitPlayer(bot) 104 | break; 105 | } 106 | } 107 | event(event, data) { 108 | if (this.events[event]) this.events[event](data) 109 | } 110 | 111 | onData(data) { 112 | if (this.main.destroyed) return 113 | if (data.p) { 114 | 115 | var world = this.main.getWorld().getNodes('map') 116 | data.d.forEach((c) => { 117 | var cell = world.get(c.i) 118 | if (!cell) return; 119 | cell.nearby = [] 120 | c.l.forEach((n) => { 121 | 122 | var node = world.get(n) 123 | 124 | if (node) cell.nearby.push(node) 125 | }) 126 | }) 127 | return; 128 | } 129 | 130 | 131 | // console.log(data) 132 | data.forEach((bot) => { 133 | if (bot.e) { 134 | this.event(bot.e, bot.d) 135 | } else 136 | if (bot.action) { 137 | var b = this.main.bots.get(bot.id) 138 | if (!b) return 139 | this.action(b, bot.action, bot) 140 | } else { 141 | var b = this.main.bots.get(bot.i) 142 | if (!b) return 143 | b.mouse.x = bot.m.x 144 | b.mouse.y = bot.m.y 145 | } 146 | }) 147 | } 148 | stop() { 149 | this.child.removeListener(this.main.id) 150 | delete this.child 151 | 152 | } 153 | send(type, data) { 154 | data.type = type; 155 | try { 156 | this.child.send(this.main.id, data) 157 | } catch (e) { 158 | 159 | } 160 | } 161 | addBot(bot) { 162 | 163 | this.send(5, { 164 | id: bot.id, 165 | bot: bot.botid 166 | }) 167 | } 168 | removeClient(client) { 169 | this.emit('delPlayer', client.id) 170 | } 171 | addNode(node) { 172 | 173 | if (this.hash[node.id]) return; 174 | this.toSend.push({ 175 | id: node.id, 176 | size: node.size, 177 | type: node.type, 178 | position: node.position, 179 | owner: (node.owner) ? node.owner.id : false, 180 | mass: node.mass, 181 | speed: node.speed 182 | 183 | 184 | }) 185 | this.hash[node.id] = true; 186 | 187 | } 188 | deleteNodes(nodes) { 189 | if (nodes[0]) 190 | this.send(2, { 191 | nodes: nodes 192 | }) 193 | 194 | } 195 | sendMove(node) { 196 | this.movCode.push({ 197 | id: node.id, 198 | x: node.position.x, 199 | y: node.position.y 200 | }) 201 | 202 | } 203 | update() { 204 | if (this.main.lag > 0) { 205 | if (this.buf > 5) { 206 | this.buf = 0 207 | 208 | } else { 209 | this.buf++; 210 | } 211 | } 212 | 213 | var nodes = this.main.getWorld().getNodes('player') 214 | 215 | nodes.forEach((node) => { 216 | 217 | if (node.updateCode != this.updHash[node.id]) { 218 | this.hash[node.id] = false; 219 | this.updHash[node.id] = node.updateCode 220 | this.addNode(node) 221 | } else if (node.moveCode != this.movHash[node.id]) { 222 | 223 | this.movHash[node.id] = node.moveCode 224 | this.movCode.push({ 225 | id: node.id, 226 | x: node.position.x, 227 | y: node.position.y 228 | }) 229 | } 230 | 231 | }) 232 | if (this.movCode[0]) this.send(3, { 233 | nodes: this.movCode 234 | }) 235 | this.movCode = []; 236 | 237 | } 238 | 239 | sendNodes() { 240 | if (this.toSend.length == 0) return 241 | this.send(1, { 242 | nodes: this.toSend 243 | }) 244 | 245 | this.toSend = []; 246 | } 247 | 248 | } -------------------------------------------------------------------------------- /source/core/collisionHandler.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | module.exports = class CollisionHandler { 20 | constructor(main) { 21 | this.main = main; 22 | this.timer = 0; 23 | } 24 | collidePlayer(cell) { 25 | 26 | cell.nearby.forEach((node) => { 27 | 28 | if (!node || node.dead) return; 29 | 30 | // relative collision 31 | if (node.type == 0 && node.owner == cell.owner) { 32 | if (node.canMerge && cell.canMerge || node.getAge(this.main) < 500) return; 33 | this.relativeCollision2(cell, node); 34 | //this.relativeCollision2(cell,node); 35 | 36 | 37 | } else if (node.type == 5 && node.owner == cell.owner && node.getAge(this.main) < 500) { 38 | this.relativeCollision2(cell, node); 39 | } else if (cell.doesCollide(node, this.main)) { 40 | this.relativeCollision2(cell, node); 41 | } 42 | }); 43 | } 44 | rigidCollision(cell, node) { // where one is stationary 45 | var dist = cell.getDistance(node.position.x, node.position.y) 46 | if (dist <= 1) return; 47 | 48 | var midpointx = (cell.position.x + node.position.x) / 2, 49 | midpointy = (cell.position.y + node.position.y) / 2, 50 | x1 = midpointx + cell.size * (cell.position.x - node.position.x) / dist, 51 | y1 = midpointy + cell.size * (cell.position.y - node.position.y) / dist, 52 | x2 = midpointx + node.size * (node.position.x - cell.position.x) / dist, 53 | y2 = midpointy + node.size * (node.position.y - cell.position.y) / dist; 54 | 55 | cell.setPos(x1, y1) 56 | node.setPos(x2, y2) 57 | } 58 | relativeCollision2(cell, node) { // From multiogar SRC: https://github.com/Barbosik/MultiOgar/blob/master/src/GameServer.js 59 | 60 | // distance from cell1 to cell2 61 | var d = cell.getDistance(node.position.x, node.position.y) 62 | if (d <= 0) return; 63 | var invd = 1 / d; 64 | var dx = node.position.x - cell.position.x 65 | var dy = node.position.y - cell.position.y 66 | // normal 67 | var nx = dx * invd; 68 | var ny = dy * invd; 69 | var r = node.size + cell.size 70 | // body penetration distance 71 | var penetration = r - d; 72 | if (penetration <= 0) return; 73 | 74 | // penetration vector = penetration * normal 75 | var px = penetration * nx; 76 | var py = penetration * ny; 77 | 78 | // body impulse 79 | 80 | 81 | // cell1 = this 82 | // cell2 = node 83 | var cs1 = cell.size * cell.size 84 | var cs2 = node.size * node.size 85 | var totalMass = cs1 + cs2; 86 | if (totalMass <= 0) return; 87 | var invTotalMass = 1 / totalMass; 88 | var impulse1 = cs2 * invTotalMass; 89 | var impulse2 = cs1 * invTotalMass; 90 | 91 | // apply extrusion force 92 | 93 | 94 | 95 | cell.addPos(-px * impulse1, -py * impulse1) 96 | 97 | node.addPos(px * impulse2, py * impulse2) 98 | 99 | if (!node.moving) { 100 | node.checkGameBorders(this.main) 101 | node.movCode() 102 | 103 | } 104 | } 105 | relativeCollision(cell, node) { // considering mass 106 | var coll = cell.size + node.size; 107 | var dist = cell.getDistance(node.position.x, node.position.y) 108 | var angle = Math.atan(cell.getSlope(node.position.x, node.position.y)) 109 | if (dist - coll > 0 || node == cell) return; 110 | var frac = node.mass / cell.mass 111 | var a = Math.sqrt(frac) / 2; 112 | var push = a * (coll - dist); 113 | push = Math.min(push, coll - dist); 114 | 115 | cell.addPos(Math.sin(angle) * push, Math.cos(angle) * push) 116 | } 117 | 118 | } -------------------------------------------------------------------------------- /source/core/configService.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | You should have received a copy of the GNU Affero General Public License 14 | along with this program. If not, see . 15 | */ 16 | const fs = require("fs"); 17 | const ini = require('../modules/ini.js'); 18 | 19 | 20 | module.exports = { 21 | 22 | loadSConfig: function (d) { 23 | try { 24 | var config = ini.parse(fs.readFileSync(__dirname + '/../settings/serverConfig.ini', "utf8")) 25 | console.log("gre{[OpenAgar]} Loaded server configs".styleMe()) 26 | return config; 27 | 28 | } catch (e) { 29 | if (d) { 30 | console.log("red{[OpenAgar]} Error with getting config:".styleMe()) 31 | throw e 32 | } 33 | return false 34 | } 35 | }, 36 | loadSkins: function (d) { 37 | try { 38 | var skins = ini.parse(fs.readFileSync(__dirname + '/../settings/customSkins.ini', "utf8")) 39 | console.log("gre{[OpenAgar]} Loaded custom skins".styleMe()) 40 | return skins; 41 | 42 | } catch (e) { 43 | if (d) { 44 | console.log("red{[OpenAgar]} Error with getting custom skins:".styleMe()) 45 | throw e 46 | } 47 | return false 48 | } 49 | }, 50 | loadBan: function () { 51 | var ban = []; 52 | try { 53 | var file = fs.readFileSync(__dirname + '/../../ban.txt', "utf8") 54 | console.log("gre{[OpenAgar]} Loaded ban file and cleaned it".styleMe()) 55 | if (!file) return ban 56 | file = file.split("\n") 57 | 58 | file.forEach((b) => { 59 | if (b) { 60 | b = b.replace(/\s/g, "") 61 | 62 | ban.push(b) 63 | } 64 | }) 65 | 66 | fs.writeFileSync(__dirname + '/../../ban.txt', ban.join("\n")) 67 | } catch (e) { 68 | 69 | console.log("yel{[OpenAgar]} Ban file not found. Generating...".styleMe()) 70 | fs.writeFileSync(__dirname + '/../../ban.txt', "") 71 | } 72 | return ban 73 | }, 74 | loadConfig: function (dir, d) { 75 | try { 76 | var physics = ini.parse(fs.readFileSync(dir + '/physicsConfig.ini', "utf8")) 77 | var config = ini.parse(fs.readFileSync(dir + '/config.ini', "utf8")) 78 | var client = ini.parse(fs.readFileSync(dir + '/clientConfig.ini', "utf8")) 79 | for (var i in physics) { 80 | if (!config[i]) config[i] = physics[i] 81 | } 82 | for (var i in client) { 83 | if (!config[i]) config[i] = client[i] 84 | } 85 | console.log("gre{[OpenAgar]} Loaded game server configs".styleMe()) 86 | return config; 87 | 88 | } catch (e) { 89 | if (d) { 90 | console.log("red{[OpenAgar]} Error with getting config:".styleMe()) 91 | throw e 92 | } 93 | return false 94 | } 95 | 96 | 97 | }, 98 | loadServers: function (defaultconf) { 99 | var servers = {}; 100 | var files = fs.readdirSync(__dirname + '/../settings/servers/'); 101 | files.forEach((file) => { 102 | var split = file.split("."); 103 | if (split[1] != "ini") return; 104 | 105 | var f = __dirname + '/../settings/servers/' + file; 106 | var configs = ini.parse(fs.readFileSync(f, "utf8")); 107 | for (var i in defaultconf) { 108 | if (configs[i] === undefined) configs[i] = defaultconf[i]; 109 | } 110 | servers[split[0]] = configs; 111 | 112 | }) 113 | return servers; 114 | }, 115 | loadBotNames: function () { 116 | try { 117 | var file = fs.readFileSync(__dirname + "/../settings/botnames.txt", "utf8"); 118 | 119 | file = file.split("\n"); 120 | 121 | 122 | var out = []; 123 | for (var i = 0; i < file.length; i++) { 124 | var b = file[i].trim(); 125 | if (b && b.length > 0) out.push(b) 126 | } 127 | 128 | fs.writeFileSync(__dirname + "/../settings/botnames.txt", out.join("\n")); 129 | 130 | console.log("gre{[OpenAgar]} Retrieved ".styleMe() + out.length + " botnames"); 131 | return out; 132 | 133 | } catch (e) { 134 | 135 | fs.writeFileSync(__dirname + "/../settings/botnames.txt", "") 136 | console.log("yel{[OpenAgar]} Created botnames.txt".styleMe()) 137 | return []; 138 | } 139 | } 140 | 141 | 142 | 143 | }; 144 | -------------------------------------------------------------------------------- /source/core/controller.js: -------------------------------------------------------------------------------- 1 | // DO NOT USE STRICT AS IT WONT ALLOW GLOBAL VARS 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var ServerService = require('./serverService.js'); 20 | var GlobalData = require('./globalData.js'); 21 | var request = require('minirequest') 22 | var ShellService = require('./shellService.js') 23 | var Config = require('./configService.js') 24 | 25 | Util = require('./utilities.js') 26 | Style = require('styleme') 27 | Style.extend() 28 | var a = require('../modules/fastSquares.js') 29 | Sqrt = new a() 30 | 31 | var a = require('nodesounds') 32 | Sounds = new a(__dirname + '/../sounds') 33 | Sounds.add('start', 'start.mp3') 34 | Sounds.add('alert', 'alert.mp3') 35 | Sounds.add('tone', 'tone.mp3') 36 | 37 | _version = "5.0.5" 38 | _key = "" 39 | exit = function (a) { 40 | process.exit(a) 41 | } 42 | 43 | var UID = require('./uid.js'); 44 | 45 | module.exports = function () { 46 | _getUID(function (code) { 47 | 48 | if (!code) { 49 | setTimeout(function () { 50 | console.log("yel{[OpenAgar]} This is an unregistered copy. Please register at login.opnagar.us. Your id is ".styleMe() + _uid()); 51 | }, 1000); 52 | 53 | } 54 | 55 | if (_lvl() == 1) { 56 | setTimeout(function () { 57 | console.log("gre{[OpenAgar]} rai{Welcome} back, system moderator.".styleMe()); 58 | }, 1000); 59 | 60 | } else if (_lvl() == 2) { 61 | setTimeout(function () { 62 | console.log("gre{[OpenAgar]} rai{Welcome} back, system administrator.".styleMe()); 63 | }, 1000); 64 | } 65 | 66 | require("./errorManager.js"); 67 | 68 | var main = new Controller(); 69 | main.start(); 70 | }); 71 | 72 | 73 | 74 | function Controller() { 75 | 76 | console.log("gre{[OpenAgar]} Starting OpenAgar V".styleMe() + _version) 77 | this.config = Config.loadSConfig(true) 78 | var ban = Config.loadBan() 79 | var skins = Config.loadSkins(true) 80 | var botnames = Config.loadBotNames(); 81 | 82 | this.globalData = new GlobalData(this.config, ban, skins, botnames); 83 | this.shellService = new ShellService(this) 84 | this.serverService = new ServerService(this, this.globalData); 85 | 86 | } 87 | 88 | Controller.prototype.start = function () { 89 | 90 | 91 | 92 | this.shellService.init() 93 | this.serverService.start(); 94 | 95 | } 96 | Controller.prototype.execCommand = function (str) { 97 | this.serverService.execCommand(str) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /source/core/dataService.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | const WorldModel = require('./worldModel.js'); 20 | const GameData = require('./gameData.js'); 21 | module.exports = class DataService { 22 | constructor(main,globalData,config) { 23 | this.globalData = globalData; 24 | this.config = config; 25 | 26 | this.world = new WorldModel(main); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /source/core/foodService.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | module.exports = class FoodService { 20 | constructor(main) { 21 | this.main = main; 22 | this.loops = []; 23 | } 24 | checkFood() { 25 | // console.log(this.main.dataService.world.nodes.getWithMerged(this.main.bounds).length) 26 | if (this.main.food < this.main.getConfig().minFood) { 27 | this.addFood(this.main.getConfig().minFood - this.main.food); 28 | } else { 29 | // console.log(this.main.dataService.world.getNodes()) 30 | } 31 | 32 | } 33 | getRandomPos() { 34 | var x = Math.floor(this.main.bounds.width * Math.random()); 35 | var y = Math.floor(Math.random() * this.main.bounds.height); 36 | return { 37 | x: x, 38 | y: y 39 | }; 40 | } 41 | addFood(m) { 42 | for (var i = 0; i < m; i++) { 43 | var pos = this.getRandomPos(); 44 | // console.log(pos) 45 | this.main.addNode(pos, 2, 4); 46 | } 47 | } 48 | checkVirus() { 49 | if (this.main.viruses < this.main.getConfig().minVirus) { 50 | this.addVirus(this.main.getConfig().minVirus - this.main.viruses); 51 | } else { 52 | // console.log(this.main.dataService.world.getNodes()) 53 | } 54 | } 55 | addWormHole(m) { 56 | for (var i = 0; i < m; i++) { 57 | var pos = this.getRandomPos(); 58 | // console.log(pos) 59 | var mass = Math.floor(Math.random() * 500) + 100 60 | if (this.main.checkCollide(pos, Math.ceil(Math.sqrt(this.mass) * 10) + 5)) { 61 | --i; 62 | continue; 63 | }; 64 | this.main.addNode(pos, mass, 6); 65 | } 66 | } 67 | checkWormHole() { 68 | if (this.main.wormHoles < this.main.getConfig().minWormHole) { 69 | this.addWormHole(this.main.getConfig().minWormHole - this.main.wormHoles); 70 | } else { 71 | // console.log(this.main.dataService.world.getNodes()) 72 | } 73 | } 74 | addVirus(m) { 75 | 76 | for (var i = 0; i < m; i++) { 77 | var pos = this.getRandomPos(); 78 | if (this.main.checkCollide(pos, 120)) { 79 | --i; 80 | continue; 81 | }; 82 | // console.log(pos) 83 | this.main.addNode(pos, this.main.getConfig().virusMass, 2); 84 | } 85 | } 86 | loop() { 87 | this.loops.forEach((l) => { 88 | this.checkNode(l.min, l.id, l.name, l.start) 89 | }); 90 | } 91 | addLoop(min, id, name, start) { 92 | this.loops.push({ 93 | min: min, 94 | id: id, 95 | name: name, 96 | start: start 97 | }) 98 | } 99 | 100 | checkNode(min, id, name, start) { 101 | var amount = this.main.getWorld().getNodes(name).size 102 | if (amount < min) { 103 | this.addNode(min - amount, id, start) 104 | } 105 | } 106 | addNode(m, id, start) { 107 | for (var i = 0; i < m; i++) { 108 | var pos = this.getRandomPos(); 109 | // console.log(pos) 110 | this.main.addNode(pos, start, id); 111 | } 112 | } 113 | }; -------------------------------------------------------------------------------- /source/core/gameData.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | module.exports = class gameData { 20 | constructor() { 21 | this.data = {}; 22 | } 23 | getData() { 24 | return this.data; 25 | } 26 | addData(name,value) { 27 | this.data[name] = value; 28 | } 29 | removeData(name) { 30 | this.data[name] = null; 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /source/core/gameMode.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | You should have received a copy of the GNU Affero General Public License 14 | along with this program. If not, see . 15 | */ 16 | var ingame = require('../modes/index.js') 17 | module.exports = class gameModeHandler { 18 | constructor(main) { 19 | this.main = main; 20 | this.mode = false; 21 | this.init() 22 | } 23 | init() { 24 | this.setMode(this.main.getConfig().gameMode) 25 | 26 | 27 | this.event('onServerInit') 28 | this.main.log("gre{[OpenAgar]} Current gamemode is ".styleMe() + this.main.getConfig().gameMode + " (" + this.mode.name + ")") 29 | } 30 | setMode(mode) { 31 | this.mode = ingame.get(mode) 32 | if (!this.mode) this.mode = this.main.pluginService.gamemodes[mode] 33 | if (!this.mode) throw "ERROR: Invalid gamemode! Gamemode doesnt exsist" 34 | } 35 | event(event, data) { 36 | if (!data) data = {} 37 | if (!this.mode) return true; 38 | if (!this.mode[event]) return true; 39 | data.main = this.main; 40 | data.log = this.main.log 41 | if (this.mode[event](data) === false) return false 42 | return true; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /source/core/globalData.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | module.exports = class GlobalData { 20 | constructor(config, ban, skins, botnames) { 21 | this.data = {}; 22 | var fin = {}; 23 | var id = -1 24 | var f = []; 25 | for (var i in skins) { 26 | fin[i] = { 27 | skin: skins[i], 28 | id: id 29 | } 30 | f[id--] = skins[i] 31 | } 32 | 33 | this.skins = fin; 34 | this.skins2 = f 35 | this.config = config; 36 | this.id = 1; 37 | this.ban = ban 38 | this.botnames = botnames; 39 | this.nameList = botnames.slice(0) 40 | this.globalBan = {}; 41 | } 42 | getNextId() { 43 | 44 | if (this.id >= 4294967295) this.id = 0; 45 | 46 | return this.id++; 47 | } 48 | getRandomName() { 49 | 50 | if (this.nameList.length === 0) this.nameList = this.botnames.slice(0) 51 | var index = Math.floor(Math.random() * this.nameList.length); 52 | 53 | var name = this.nameList[index]; 54 | 55 | this.nameList.splice(index, 1); 56 | 57 | return name; 58 | } 59 | 60 | getData() { 61 | return this.data; 62 | } 63 | addData(name, value) { 64 | this.data[name] = value; 65 | } 66 | removeData(name) { 67 | this.data[name] = null; 68 | } 69 | updateGlobalBan(data) { 70 | this.globalBan = {}; 71 | for (var i = 0; i < data.length; i++) { 72 | this.globalBan[data[i].ip] = data[i]; 73 | } 74 | 75 | } 76 | }; 77 | -------------------------------------------------------------------------------- /source/core/pluginService.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | You should have received a copy of the GNU Affero General Public License 14 | along with this program. If not, see . 15 | */ 16 | var pluginParser = require('./pluginParser.js') 17 | var fs = require('fs') 18 | var ini = require('../modules/ini.js') 19 | module.exports = class PluginService { 20 | constructor(main) { 21 | this.vars = ["configs","commands","addToHelp","gamemodes","chatCommands","chatHelp"] 22 | this.main = main 23 | this.plugins = {}; 24 | this.pdata = {} 25 | this.pluginNames = []; 26 | this.commands = {}; 27 | this.addToHelp = []; 28 | this.data = { 29 | main: main, 30 | log: main.log 31 | 32 | } 33 | this.chatC = {}; 34 | this.chatA = []; 35 | this.gamemodes = [] 36 | this.configs = ini.parse(fs.readFileSync(__dirname + '/../settings/pluginConfig.ini',"utf8")) 37 | if (this.configs.allowed) this.configs.allowed = this.configs.allowed.split(",") 38 | this.parser = new pluginParser(function(a) {main.log("gre{[PluginService]} ".styleMe() + a)}.bind(this),__dirname + "/../plugins",this.vars,this.data,_version,this.configs.allowed,this.configs.dev == 1) 39 | 40 | 41 | } 42 | stop() { 43 | for (var i in this.plugins) { 44 | var plugin = this.plugins[i] 45 | if (plugin.stop) plugin.stop() 46 | } 47 | this.plugins = {}; 48 | this.pdata = {}; 49 | } 50 | init() { 51 | this.parser.init() 52 | this.plugins = this.parser.getPlugins() 53 | this.pdata = this.parser.getData() 54 | var command = this.getData('chatCommands') 55 | var help = this.getData('chatHelp') 56 | this.chatC = {} 57 | this.chatA = [] 58 | command.forEach((comm)=>{ 59 | if (!comm) return; 60 | for (var i in comm) { 61 | this.chatC[i] = comm[i] 62 | 63 | } 64 | }) 65 | help.forEach((help)=>{ 66 | this.chatA = this.chatA.concat(help) 67 | }) 68 | var command = this.getData('commands') 69 | var help = this.getData('addToHelp') 70 | var gamemodes = this.getData('gamemodes') 71 | this.commands = {}; 72 | 73 | this.addToHelp = []; 74 | help.forEach((help)=>{ 75 | this.addToHelp = this.addToHelp.concat(help) 76 | }) 77 | command.forEach((comm)=>{ 78 | if (!comm) return; 79 | for (var i in comm) { 80 | this.commands[i] = comm[i] 81 | 82 | } 83 | }) 84 | gamemodes.forEach((gamemode)=>{ 85 | if (!gamemode) return; 86 | gamemode.forEach((g,i)=>{ 87 | if (!g || !g.id || !g.name) return; 88 | this.gamemodes[parseInt(g.id)] = g 89 | }) 90 | }) 91 | for (var i in this.plugins) { 92 | var plugin = this.plugins[i]; 93 | this.pluginNames.push(plugin.name); 94 | } 95 | 96 | } 97 | reload() { 98 | this.init() 99 | } 100 | getData(data) { 101 | if (data) { 102 | return this.pdata[data] 103 | } else { 104 | return this.pdata 105 | } 106 | } 107 | send(event,data) { 108 | if (!data.log) data.log = this.main.log; 109 | if (!data.main) data.main = this.main; 110 | if (!data.pluginService) data.pluginService = this; 111 | for (var i in this.plugins) { 112 | var plugin = this.plugins[i] 113 | if (plugin && plugin[event]) { 114 | if (plugin[event](data) === false) return false; 115 | } 116 | continue; 117 | } 118 | return true; 119 | } 120 | getPlugins() { 121 | return this.plugins 122 | } 123 | 124 | 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /source/core/shellService.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const AsyncConsole = require('asyncconsole') 21 | const EOL = require('os').EOL 22 | module.exports = class ShellService { 23 | constructor(controlservice) { 24 | this.controller = controlservice; 25 | this.text = "" 26 | this.commands = []; 27 | this.redrawing = false 28 | 29 | this.input = new AsyncConsole(">", function (command) { 30 | this.parseCommands(command) 31 | }.bind(this), function (key) { 32 | if (this.redrawing || !this.controller.serverService.selected.interface) return false; 33 | return true; 34 | 35 | }.bind(this),function() { 36 | exit(); 37 | }) 38 | this.ind = 0; 39 | this.console = [] 40 | 41 | this.selected = 1; 42 | 43 | 44 | } 45 | select(id, c) { 46 | if (this.selected == id) return; 47 | this.selected = id 48 | this.writeLog(c) 49 | } 50 | removeServ(id) { 51 | delete this.console[id] 52 | } 53 | clearAnim(height, width, callback) { 54 | process.stdout.write(" ") 55 | var a = false 56 | var text = "" 57 | for (var i = 0; i < width; i++) { 58 | text += " " 59 | } 60 | var interval = setInterval(function () { 61 | if (a) process.stdout.write(EOL) 62 | a = true; 63 | 64 | process.stdout.write(text) 65 | height--; 66 | if (height <= 0) { 67 | clearInterval(interval) 68 | callback() 69 | } 70 | }.bind(this), 30) 71 | } 72 | interval(num, func, call, time) { 73 | var int = setInterval(function () { 74 | func() 75 | num--; 76 | if (num <= 1) { 77 | clearInterval(int) 78 | call() 79 | } 80 | }.bind(this), time) 81 | } 82 | writeLog(cbk) { // CALLBACK HELL! (Yet too cool) 83 | var eol = require('os').EOL; 84 | 85 | var logs = this.console[this.selected] 86 | if (!logs) return 87 | var height = process.stdout.rows 88 | var width = process.stdout.columns 89 | 90 | process.stdout.write('\u001B[H\u001B[2r') 91 | this.redrawing = true; 92 | 93 | this.clearAnim(height, width - 1, function () { 94 | process.stdout.write("\x1b[K") 95 | 96 | this.interval(height, function () { 97 | process.stdout.write("\x1b[1A") 98 | }, function () { 99 | this.interval(width, function () { 100 | process.stdout.write("\x1b[1D") 101 | }, function () { 102 | process.stdout.write('\u001B[0r') 103 | var sel = Math.max(logs.length - height, 0); 104 | var self = this; 105 | 106 | function set() { 107 | var ind = 0; 108 | var int = setInterval(function () { 109 | if (logs[sel]) { 110 | var d = logs[sel].charAt(ind) 111 | if (!d) { 112 | process.stdout.write(eol) 113 | 114 | sel++; 115 | clearInterval(int) 116 | set() 117 | } 118 | process.stdout.write(d) 119 | ind++; 120 | } else { 121 | clearInterval(int) 122 | if (cbk) cbk() 123 | self.redrawing = false 124 | } 125 | }, 3) 126 | } 127 | 128 | set() 129 | }.bind(this), 6) 130 | }.bind(this), 15) 131 | 132 | 133 | 134 | 135 | 136 | /* 137 | 138 | for (var i = 0; i < logs.length; i ++) { 139 | process.stdout.write(logs[i] +"\n") 140 | } 141 | */ 142 | 143 | }.bind(this)) 144 | 145 | } 146 | log(id, a, log) { 147 | if (id == this.selected && !log && !this.redrawing) console.log(a) 148 | if (!this.console[id]) this.console[id] = []; 149 | this.console[id].push(a.toString()) 150 | } 151 | drawSplash() { 152 | Sounds.play('start') 153 | this.log(1, "\u001b[2J\u001b[0;0H"); 154 | this.log(1, "cya{ ___ }yel{ _ }".styleMe()) 155 | this.log(1, "cya{ / _ \\ _ __ ___ _ __ }yel{ / \\ __ _ __ _ _ __ }".styleMe()) 156 | this.log(1, "cya{ | | | | '_ \\ / _ \\ '_ \\}yel{ / _ \\ / _` |/ _` | '__|}".styleMe()) 157 | this.log(1, "cya{ | |_| | |_) | __/ | | |}yel{/ ___ \\ (_| | (_| | | }".styleMe()) 158 | this.log(1, "cya{ \\___/| .__/ \\___|_| |_}yel{/_/ \\_\\__, |\\__,_|_| }".styleMe()) 159 | this.log(1, "cya{ |_| }yel{ |___/ }".styleMe()) 160 | this.log(1, " gre{OpenAgar} - cya{An open source web game}".styleMe()) 161 | this.log(1, " yel{Also by the cya{AJS} Development team}".styleMe()) 162 | 163 | } 164 | init() { 165 | this.drawSplash() 166 | 167 | } 168 | 169 | 170 | parseCommands(str) { 171 | 172 | // this.controller.logger.onCommand(str); 173 | if (str === '') return; 174 | 175 | this.controller.execCommand(str) 176 | } 177 | }; -------------------------------------------------------------------------------- /source/core/skinHandler.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | module.exports = class SkinHandler { 20 | constructor(player) { 21 | this.player = player 22 | this.skin = 0; 23 | this.sentHash = {}; 24 | 25 | } 26 | 27 | setSkin(name) { 28 | 29 | if (name.charAt(0) == "<") { 30 | var a = name.indexOf(">") 31 | if (a == -1) return name; 32 | var skin = name.substring(1, a) 33 | if (!skin) return name.substr(a + 1); 34 | 35 | var b = this.player.globalData.skins[skin.toLowerCase()]; 36 | if (!b) return name.substr(a + 1); 37 | this.skin = b.id; 38 | return name.substr(a + 1) 39 | } 40 | return name; 41 | } 42 | getSend(skin) { 43 | 44 | if (skin > 0 || this.sentHash[skin]) { 45 | return skin.toString(); 46 | } else { 47 | if (this.player.globalData.skins2[skin]) { 48 | this.sentHash[skin] = true; 49 | 50 | return skin + "|" + this.player.globalData.skins2[skin]; 51 | 52 | } else { 53 | return ""; 54 | } 55 | } 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /source/core/socket.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | const RSON = require('rson') 20 | const LZString = require('../modules/LZString.js') 21 | module.exports = class Socket { 22 | constructor(socket, player) { 23 | this.player = player; 24 | this.socket = socket 25 | 26 | 27 | this.remoteAddress = socket.IP; 28 | this.IPv6 = socket.IPv6; 29 | } 30 | sendNodes(dt) { 31 | 32 | this.socket.socket.send(dt, { 33 | binary: true 34 | }); 35 | 36 | } 37 | sendDelete(tex) { 38 | 39 | this.socket.hasBinary(false).emit('delnodes', tex) 40 | } 41 | disconnect() { 42 | this.socket._diconnect = true; 43 | this.socket.disconnect() 44 | 45 | } 46 | emit(a, b) { 47 | 48 | this.socket.emit(a, b) 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /source/core/stringBuilder.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // No license here. Link: https://github.com/AJS-development/SimpleStringBuilder/blob/master/index.js 3 | module.exports = class SB { 4 | constructor(string) { 5 | this.t = []; 6 | this.t.push(string) 7 | } 8 | append(/**/) { 9 | if (!arguments) return false; 10 | arguments.forEach((str)=>{ 11 | this.t.push(str) 12 | }) 13 | return true; 14 | } 15 | toString() { 16 | return this.t.join("") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /source/core/updater.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | var request = require('minirequest') 3 | var fs = require('fs') 4 | var exec = require('child_process').exec 5 | module.exports = class Updater { 6 | constructor(ss) { 7 | this.ss = ss; 8 | this.dir = 'https://raw.githubusercontent.com/AJS-development/OpenAgar/master' 9 | this.tobe = 0; 10 | this.dow = 0; 11 | } 12 | updateDone() { 13 | this.loading("done. Installing modules..") 14 | this.install(function (e) { 15 | if (e) throw e; 16 | else { 17 | this.loading("done. Restarting... "); 18 | exit(0); 19 | } 20 | }.bind(this)) 21 | 22 | } 23 | install(call) { 24 | var child = exec("npm install", function (error, stdout, stderr) { 25 | call(error) 26 | }); 27 | } 28 | update() { 29 | this.dow = 0; 30 | this.tobe = 2; 31 | console.log("gre{[Update]} Updating...".styleMe()) 32 | request('https://raw.githubusercontent.com/AJS-development/OpAgMS/master/files.json', function (e, r, b) { 33 | try { 34 | if (!e && r.statusCode == 200) { 35 | var data = JSON.parse(b) 36 | this.count = 0; 37 | data.forEach((dt) => { 38 | if (!dt) return; 39 | this.count++; 40 | this.tobe++; 41 | 42 | this.downloadFile(dt, function (e) { 43 | if (e) throw e 44 | 45 | this.count--; 46 | if (this.count <= 0) { 47 | this.updateDone() 48 | 49 | } 50 | }.bind(this)) 51 | }) 52 | 53 | } 54 | } catch (e) { 55 | throw e 56 | } 57 | }.bind(this)) 58 | } 59 | writeFileSafe(dir, file, data, call) { 60 | 61 | file = file.split("/") 62 | if (!file[0]) { 63 | file = file.slice(1) 64 | } 65 | try { 66 | fs.lstatSync(dir + "/" + file.join("/")) 67 | } catch (e) { 68 | var test = dir 69 | for (var i = 0; i < file.length - 1; i++) { 70 | var a = file[i] 71 | test += "/" + a 72 | try { 73 | 74 | fs.lstatSync(test) 75 | } catch (e) { 76 | 77 | fs.mkdir(test) 78 | } 79 | } 80 | } 81 | 82 | 83 | fs.writeFile(dir + "/" + file.join("/"), data, function () { 84 | call() 85 | }) 86 | 87 | } 88 | loading(action) { 89 | this.dow++; 90 | var percent = Math.round(this.dow / this.tobe * 10) 91 | var bar = "" 92 | for (var i = 0; i < percent; i++) { 93 | bar = bar + "==="; 94 | } 95 | if (percent == 10) bar = bar + "="; 96 | else bar = bar + ">"; 97 | var extras = 31 - bar.length; 98 | var extra = ""; 99 | for (var i = 0; i < extras; i++) extra = extra + " "; 100 | process.stdout.write("gre{[Update]} [".styleMe() + bar + extra + "] " + percent * 10 + "% " + action + "\r"); 101 | 102 | 103 | 104 | } 105 | downloadFile(data, call) { 106 | var src = data.src 107 | var url = this.dir + data.url 108 | 109 | 110 | request(url, function (e, r, b) { 111 | if (!e && r.statusCode == 200 && b) { 112 | this.loading("Downloading"); 113 | this.writeFileSafe(__dirname + "/../../", src, b, call) 114 | 115 | 116 | } else { 117 | 118 | call("Could not locate " + this.dir + data.url) 119 | } 120 | 121 | }.bind(this)) 122 | 123 | 124 | 125 | } 126 | 127 | 128 | } -------------------------------------------------------------------------------- /source/core/utilities.js: -------------------------------------------------------------------------------- 1 | // No license here! 2 | 3 | module.exports = { 4 | argsParser: function(str,splitpoint) { // commandname first:hello,secound:just,third:testing --> Object{first:"hello",secound:"just",third:"testing" } 5 | var cmd = str.split(" "); 6 | if (!splitpoint) splitpoint = 1; 7 | var args = cmd.slice(splitpoint,cmd.length).join(" ").split(","); 8 | var results = []; 9 | for (var i in args) { 10 | var arg = args[i]; 11 | var a = arg.split(":"); 12 | results[a[0]] = a[1]; 13 | } 14 | return results; 15 | }, 16 | dirEscape: function(before,dir,ext) { 17 | 18 | dir = dir.split('/') 19 | var file = dir[dir.length-1] 20 | var ex = file.split(".")[1] 21 | if (ext && ex != ext ) return false; 22 | var f = dir.slice(0,dir.length-1) 23 | f = f.join("/") 24 | if (f) f = f.replace(/\./g,"") 25 | return before + f + "/" + file 26 | }, 27 | getRandomColor: function() { 28 | var colorRGB = [0xFF, 0x07, (Math.random() * 256) >> 0]; 29 | colorRGB.sort(function () { 30 | return 0.5 - Math.random(); 31 | }); 32 | 33 | return { 34 | r: colorRGB[0], 35 | b: colorRGB[1], 36 | g: colorRGB[2] 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/core/worldModel.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | const HashBounds = require('../modules/HashBounds.js'); 21 | module.exports = class WorldModel { 22 | constructor(main) { 23 | this.nodes = new HashBounds(8, 2, Math.max(main.bounds.width, main.bounds.height) + 700, 700); 24 | this.main = main 25 | this.mapnodes = new Map(); 26 | this.playerNodes = new Map(); 27 | this.virusNodes = new Map(); 28 | this.ejectedNodes = new Map(); 29 | this.movingNodes = new Map(); 30 | this.mergeNodes = new Map(); 31 | this.lastID = 2; 32 | this.rainbowNodes = new Map(); 33 | this.bulletNodes = new Map(); 34 | this.wormHoleNodes = new Map(); 35 | this.entities = {}; 36 | this.entMap = []; 37 | } 38 | 39 | getNodes(s) { 40 | switch (s) { 41 | case "all": 42 | return this.nodes.allnodes; 43 | break; 44 | case "map": 45 | return this.mapnodes; 46 | break; 47 | case "hash": 48 | return this.nodes; 49 | break; 50 | case "moving": 51 | return this.movingNodes; 52 | break; 53 | case "player": 54 | return this.playerNodes; 55 | break; 56 | case "virus": 57 | return this.virusNodes; 58 | case "ejected": 59 | return this.ejectedNodes; 60 | case "merge": 61 | return this.mergeNodes; 62 | break; 63 | case "bullet": 64 | return this.bulletNodes; 65 | break; 66 | case "wormhole": 67 | return this.wormHoleNodes; 68 | break; 69 | default: 70 | if (!s) return this.nodes.allnodes; 71 | if (!this.entities[s]) return false; 72 | return this.entities[s]; 73 | break; 74 | 75 | } 76 | } 77 | 78 | getNextID() { 79 | if (this.lastID >= 4294967295) { 80 | this.lastID = 0; 81 | } 82 | 83 | return this.lastID++; 84 | } 85 | update(node) { 86 | this.nodes.update(node) 87 | 88 | } 89 | addEntity(id, name) { 90 | this.entMap[id] = name; 91 | this.entities[name] = new Map(); 92 | } 93 | addNode(node, type, flags) { 94 | 95 | var id = this.getNextID(); 96 | node.bounds = { 97 | x: node.position.x - node.size, 98 | y: node.position.y - node.size, 99 | width: node.size * 2, 100 | height: node.size * 2 101 | } 102 | node.onAdd(id); 103 | 104 | this.nodes.insert(node); 105 | 106 | this.mapnodes.set(id, node); 107 | this.main.gameMode.event('onAllAdd', { 108 | cell: node 109 | }) 110 | // add specified nodes 111 | switch (type) { 112 | case 0: // player 113 | this.playerNodes.set(id, node); 114 | this.main.gameMode.event('onCellAdd', { 115 | cell: node 116 | }) 117 | break; 118 | case 1: // cell 119 | break; 120 | case 2: // virus 121 | this.virusNodes.set(id, node); 122 | break; 123 | case 3: // ejected 124 | this.ejectedNodes.set(id, node); 125 | break; 126 | case 4: // food 127 | break; 128 | case 5: // bullets 129 | this.bulletNodes.set(id, node) 130 | break; 131 | case 6: // wormhole 132 | this.wormHoleNodes.set(id, node) 133 | break; 134 | default: 135 | if (!this.entMap[type]) return false; 136 | this.entities[this.entMap[type]].set(id, node) 137 | break; 138 | } 139 | 140 | // set node as moving 141 | this.setFlags(node, flags) 142 | } 143 | setFlags(node, flags) { 144 | if (!flags) return; 145 | flags = flags.split(","); 146 | flags.forEach((flag) => { 147 | if (!flag) return; 148 | switch (flag) { 149 | case "m": // moving 150 | 151 | this.movingNodes.set(node.id, node); 152 | node.moving = true; 153 | break; 154 | case "r": // rainbow 155 | this.rainbowNodes.set(node.id, node) 156 | break; 157 | case "merge": // merge 158 | this.mergeNodes.set(node.id, node) 159 | break; 160 | default: 161 | return; 162 | break; 163 | } 164 | 165 | }); 166 | } 167 | 168 | removeFlags(node, flags) { 169 | 170 | if (!flags) return; 171 | flags = flags.split(","); 172 | flags.forEach((flag) => { 173 | if (!flag) return; 174 | switch (flag) { 175 | case "m": // moving 176 | this.movingNodes.delete(node.id); 177 | if (node.type != 0) node.moving = false; 178 | this.main.childService.sendMove(node) 179 | break; 180 | case "r": // rainbow 181 | this.rainbowNodes.delete(node.id) 182 | break; 183 | case "merge": 184 | this.mergeNodes.delete(node.id) 185 | break; 186 | default: 187 | return; 188 | break; 189 | } 190 | 191 | }); 192 | } 193 | deleteFromEnt(node) { 194 | for (var i in this.entities) { 195 | this.entities[i].delete(node.id) 196 | } 197 | } 198 | clear() { 199 | this.nodes.clear(); 200 | this.mapnodes.clear(); 201 | this.playerNodes.clear(); 202 | this.virusNodes.clear(); 203 | this.ejectedNodes.clear(); 204 | this.movingNodes.clear(); 205 | this.mergeNodes.clear(); 206 | this.lastID = 2; 207 | this.rainbowNodes.clear(); 208 | this.bulletNodes.clear(); 209 | this.wormHoleNodes.clear(); 210 | for (var i in this.entities) { 211 | this.entities[i].clear(); 212 | } 213 | } 214 | removeNode(node) { 215 | 216 | if (node.hash) this.nodes.delete(node); 217 | this.main.gameMode.event('onAllRemove', { 218 | cell: node 219 | }); 220 | this.rainbowNodes.delete(node.id) 221 | this.mapnodes.delete(node.id); 222 | this.movingNodes.delete(node.id); 223 | this.deleteFromEnt(node) 224 | node.moving = false 225 | this.ejectedNodes.delete(node.id); 226 | if (this.playerNodes.delete(node.id)) { 227 | this.main.gameMode.event('onCellRemove', { 228 | cell: node 229 | }); 230 | return this.mergeNodes.delete(node.id) 231 | } 232 | 233 | if (this.bulletNodes.delete(node.id)) return; 234 | if (this.wormHoleNodes.delete(node.id)) return; 235 | if (this.virusNodes.delete(node.id)) return; 236 | } 237 | }; 238 | -------------------------------------------------------------------------------- /source/entities/MotherCell.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var template = require('./template.js'); 20 | module.exports = class MotherCell extends template { 21 | constructor(position, mass, type, owner, name) { 22 | super(position, mass, type, owner); 23 | 24 | this.color = { 25 | r: 190 + Math.floor(30 * Math.random()), 26 | g: 70 + Math.floor(30 * Math.random()), 27 | b: 85 + Math.floor(30 * Math.random()) 28 | }; 29 | 30 | this.type = 7; 31 | 32 | this.spiked = true; 33 | 34 | 35 | } 36 | feed(node, main) { 37 | 38 | node.eat(this, main) 39 | } 40 | 41 | getEatRange() { 42 | 43 | return this.size * -0.8; 44 | 45 | } 46 | collide(node, main) { 47 | if (node.mass < this.mass) { 48 | node.eat(this, main) 49 | } else if (node.mass > this.mass * 1.1) { 50 | var split = main.getConfig().playerMaxCells - node.owner.cells.size; 51 | if (split == 0) { 52 | node.addMass(this.mass) 53 | } 54 | var defaultmass = ~~(node.mass / (split + 2)) 55 | var big = defaultmass * 2 + defaultmass / 2 56 | var medium = defaultmass / 2 + defaultmass 57 | var angle = (Math.random() * 6.28318530718) // Math.floor(Math.random() * max) + min 58 | node.updateMass(big) 59 | this.eat(node, main) 60 | 61 | // need to divide angle in order to spread them evenly 62 | // 360 degrees -> 2PI radians -> 6.28318530718 63 | var increment = 6.28318530718 / (split + 1) // want to add some randomness 64 | var g = ~~(split.length / 2) 65 | for (var i = 0; i < split; i++) { 66 | var mass = (i == 0) ? medium : defaultmass; 67 | var a = main.splitCell(node, angle, main.getConfig().splitSpeed, main.getConfig().splitDecay, mass) 68 | a.setMerge(main, main.getConfig().playerMerge, main.getConfig().playerMergeMult) 69 | main.getWorld().setFlags(a, "merge") 70 | main.getWorld().setFlags(node, "merge") 71 | angle += increment + (Math.random() * 0.03) 72 | if (angle > 6.28318530718) angle += -6.28318530718 // radians dims 0 < x < 2PI 73 | } 74 | } 75 | } 76 | update(main) { 77 | if (Math.random() > 0.971) { 78 | var maxFood = Math.random() * 2; // Max food spawned per tick 79 | var i = 0; // Food spawn counter 80 | while (i < maxFood) { 81 | 82 | this.spawnFood(main); 83 | 84 | 85 | // Increment 86 | i++; 87 | } 88 | } 89 | 90 | if (this.mass > 222) { 91 | 92 | 93 | var remaining = this.mass - 222; 94 | var maxAmount = Math.min(Math.floor(remaining / 2), 2); 95 | for (var i = 0; i < maxAmount; i++) { 96 | this.spawnFood(main); 97 | this.addMass(-2); 98 | } 99 | } 100 | } 101 | spawnFood(main) { 102 | 103 | if (main.food >= main.getConfig().maxFood) return 104 | var angle = Math.random() * 6.28; 105 | var pos = { 106 | x: this.position.x + (this.size * Math.cos(angle)), 107 | y: this.position.y + (this.size * Math.sin(angle)) 108 | }; 109 | var food = main.addNode(pos, 2, 4, false, false, "m"); 110 | food.setEngine1(angle, 20, (Math.random() * 4) + 4) 111 | 112 | } 113 | move(main, sp) { 114 | 115 | // dont move 116 | 117 | } 118 | }; 119 | -------------------------------------------------------------------------------- /source/entities/bullet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var template = require('./template.js'); 20 | module.exports = class Bullet extends template { 21 | constructor(position, mass, type, owner, name) { 22 | super(position, mass, type, owner); 23 | if (this.owner.golden) { 24 | this.golden = true; 25 | this.color = { 26 | r: 200, 27 | g: 200, 28 | b: 20 29 | } 30 | } else { 31 | this.golden = false; 32 | this.color = { 33 | r: 200, 34 | g: 200, 35 | b: 200 36 | } 37 | } 38 | this.fed = 0; 39 | this.type = 5; 40 | 41 | this.agit = true; 42 | this.nearby = []; 43 | this.own = owner 44 | 45 | } 46 | moveDone(main, method) { 47 | 48 | if (!this.moveEngine.useEngine && !this.moveEngine2.useEngine) main.removeNode(this) 49 | 50 | } 51 | feed(node, main) { 52 | 53 | var m1 = 4; 54 | var m2 = 100; 55 | var v1 = node.moveEngine.velocity; 56 | var v2 = this.moveEngine.velocity; 57 | var angle1 = node.moveEngine.angle; 58 | var angle2 = this.moveEngine.angle; 59 | 60 | var px = m1 * v1 * Math.cos(angle1) + m2 * v2 * Math.cos(angle1); 61 | var py = m1 * v1 * Math.sin(angle2) + m2 * v2 * Math.sin(angle2); 62 | 63 | var angle = Math.atan2(py, px); 64 | 65 | 66 | 67 | this.moveEngine.angle = angle; 68 | this.moveEngine.cos = Math.cos(angle) 69 | this.moveEngine.sin = Math.sin(angle) 70 | this.moveEngine.deltaT = 0; 71 | 72 | main.removeNode(node) 73 | } 74 | doesCollide(node, main) { 75 | return true; 76 | 77 | } 78 | 79 | onDeletion(main) { 80 | this.own.removeOwn(this) 81 | } 82 | onCreation(main) { 83 | this.own.setOwn(this) 84 | } 85 | getEatRange() { 86 | 87 | return this.size * -0.5; 88 | 89 | } 90 | explodeCell(node, main) { 91 | 92 | 93 | while (node.mass > 10) { 94 | node.addMass(-main.getConfig().ejectedMass); 95 | var pos = { 96 | x: node.position.x + Math.floor(Math.random() * 1000) - 500, 97 | y: node.position.y + Math.floor(Math.random() * 1000) - 500 98 | 99 | } 100 | var ejected = main.addNode(pos, main.getConfig().ejectedMass, 3, node.owner, [], "m") 101 | ejected.setEngine1(6.28 * Math.random(), main.getConfig().ejectedSpeed, main.getConfig().ejectedDecay) 102 | ejected.setCurve(10) 103 | 104 | 105 | 106 | } 107 | 108 | 109 | } 110 | collide(node, main) { 111 | if (this.owner == node.owner || node.owner.mass <= 500) { 112 | 113 | this.eat(node, main) 114 | node.addMass(2) 115 | node.owner.bulletsleft++; 116 | return; 117 | } 118 | if (this.golden) { 119 | this.explodeCell(node, main) 120 | return 121 | } 122 | var split = main.getConfig().playerMaxCells - node.owner.cells.size; 123 | 124 | var defaultmass = ~~(node.mass / (split + 15)) 125 | var big = defaultmass * 3 126 | var medium = defaultmass / 2 + defaultmass * 2 127 | var angle = (Math.random() * 6.28318530718) // Math.floor(Math.random() * max) + min 128 | node.updateMass(big) 129 | this.eat(node, main) 130 | 131 | // need to divide angle in order to spread them evenly 132 | // 360 degrees -> 2PI radians -> 6.28318530718 133 | var increment = 6.28318530718 / (split + 1) // want to add some randomness 134 | var g = ~~(split.length / 2) 135 | for (var i = 0; i < split + 10; i++) { 136 | var mass = (i == 0) ? medium : defaultmass; 137 | var a = main.splitCell(node, angle, main.getConfig().splitSpeed, main.getConfig().splitDecay, mass) 138 | main.getWorld().setFlags(a, "merge") 139 | main.getWorld().setFlags(node, "merge") 140 | angle += increment + (Math.random() * 0.03) 141 | if (angle > 6.28318530718) angle += -6.28318530718 // radians dims 0 < x < 2PI 142 | } 143 | 144 | } 145 | 146 | }; 147 | -------------------------------------------------------------------------------- /source/entities/cell.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var template = require('./template.js'); 20 | module.exports = class Cell extends template { 21 | constructor(position, mass, type, owner, other) { 22 | super(position, mass, type, owner, other); 23 | } 24 | }; -------------------------------------------------------------------------------- /source/entities/ejectedMass.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var template = require('./template.js'); 20 | module.exports = class EjectedMass extends template { 21 | constructor(position, mass, type, owner, other) { 22 | super(position, mass, type, owner, other); 23 | this.color = owner.gameData.color; 24 | this.type = 3; 25 | this.viruses = []; 26 | this.up = false 27 | this.own = owner 28 | 29 | } 30 | onDeletion(main) { 31 | this.own.removeOwn(this) 32 | } 33 | onCreation(main) { 34 | this.own.setOwn(this) 35 | } 36 | move(main, speed) { // Speed code: 0 = 0.05, 1 = 0.1, 2 = 0.2 37 | if (this.moveEngine2.useEngine) this.calcMove2(main, speed) 38 | if (this.moveEngine.useEngine) this.calcMove(main, speed) 39 | 40 | 41 | this.checkGameBorders(main) 42 | 43 | main.updateHash(this) 44 | this.checkVirus(main) 45 | this.movCode() 46 | 47 | } 48 | checkVirus(main) { 49 | this.up = !this.up 50 | if (this.up) 51 | 52 | 53 | main.getWorld().getNodes('hash').every(this.bounds, (node) => { 54 | 55 | if (node.type == 2) { 56 | 57 | if (!node.collisionCheckCircle(this, true)) return true; 58 | node.feed(this, main) 59 | 60 | return false; 61 | } else 62 | if (node.type == 5) { 63 | if (!node.collisionCheckCircle(this)) return true; 64 | node.feed(this, main) 65 | return false; 66 | } else 67 | if (node.type == 0) { 68 | if (!node.collisionCheckCircle(this)) return true; 69 | this.eat(node, main) 70 | return false; 71 | } else if (node.type == 6) { 72 | 73 | if (!node.collisionCheckCircle(this)) return true; 74 | node.feed(this, main); 75 | return false; 76 | } else if (main.feedListeners[node.type]) { 77 | if (!node.collisionCheckCircle(this)) return true; 78 | node.feed(this, main) 79 | return false; 80 | } 81 | 82 | return true; 83 | 84 | }) 85 | } 86 | }; -------------------------------------------------------------------------------- /source/entities/food.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var template = require('./template.js'); 20 | module.exports = class Food extends template { 21 | constructor(position, mass, type, owner, other) { 22 | super(position, mass, type, owner, other); 23 | this.type = 4; 24 | } 25 | onDeletion(main) { 26 | main.food--; 27 | } 28 | onCreation(main) { 29 | main.food++; 30 | } 31 | getEatRange() { 32 | 33 | return -this.size; 34 | 35 | } 36 | }; -------------------------------------------------------------------------------- /source/entities/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | cell: require('./cell.js'), 3 | food: require('./food.js'), 4 | playerCell: require('./playerCell.js'), 5 | ejectedMass: require('./ejectedMass.js'), 6 | virus: require('./virus.js'), 7 | bullet: require('./bullet.js'), 8 | wormHole: require('./wormHole.js') 9 | }; -------------------------------------------------------------------------------- /source/entities/playerCell.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var template = require('./template.js'); 20 | module.exports = class PlayerCell extends template { 21 | constructor(position, mass, type, owner) { 22 | super(position, mass, type, owner); 23 | this.name = owner.gameData.name; 24 | this.color = owner.gameData.color; 25 | if (owner.skinHandler) this.skin = owner.skinHandler.skin; 26 | this.mergeage = 0; 27 | this.canMerge = false; 28 | this.mergeMult = 1; 29 | this.mergeStatus = 1 30 | this.type = 0; 31 | 32 | this.moving = true; 33 | this.nearby = []; 34 | this.owner.mass += mass 35 | } 36 | 37 | onCreation(main) { 38 | this.owner.addCell(this); 39 | } 40 | 41 | onDeletion(main) { 42 | this.owner.mass -= this.mass 43 | this.owner.removeCell(this); 44 | } 45 | 46 | mergeDone(main) { 47 | 48 | } 49 | setMerge(main, num, mult) { 50 | this.mergeage = num 51 | this.canMerge = false; 52 | this.mergeMult = mult || 0 53 | main.getWorld().setFlags(this, 'merge') 54 | } 55 | calcMerge(main) { // once every 0.5 sec 56 | if (this.mergeStatus == 3) return; // 0 = instant merge, 3 = never merge 57 | if (this.mergeStatus == 0) { 58 | this.canMerge = true; 59 | 60 | main.getWorld().removeFlags(this, 'merge') 61 | 62 | this.mergeDone(main) 63 | return; 64 | } 65 | if (this.mergeage <= this.mass * -this.mergeMult) { 66 | this.canMerge = true; 67 | main.getWorld().removeFlags(this, 'merge') 68 | 69 | this.mergeDone(main) 70 | return; 71 | } 72 | this.mergeage--; 73 | 74 | //console.log(this.mergeage, this.mass * this.mergeMult, this.canMerge) 75 | 76 | } 77 | updateMass(mass) { 78 | 79 | if (this.mass === mass) return; 80 | 81 | var add = Math.max(mass, 10) 82 | var dif = add - this.mass 83 | this.owner.mass += dif 84 | this.mass = add 85 | 86 | this.getSize() 87 | this.speed = Math.pow(this.mass, -0.222) * 1.25; 88 | this.updCode() 89 | } 90 | moveToMouse(main, sp) { 91 | 92 | var speed = 0; 93 | if (sp == 0) { 94 | speed = this.speed 95 | } else if (sp == 1) { 96 | speed = this.speed * 2 97 | } else if (sp == 2) { 98 | speed = this.speed * 4 99 | } 100 | var mouse = this.owner.mouse, 101 | 102 | distx = mouse.x - this.position.x, 103 | disty = mouse.y - this.position.y, 104 | // y^2 = x^2 + b^2 -> y = sq(x^2 + b^2) 105 | num = Math.min(distx * distx + disty * disty, 625), 106 | 107 | dist = ~~(Sqrt.sqrt(num)); 108 | 109 | var angle = Math.atan2(disty, distx) 110 | if (!dist) return; // dont want 0 111 | // want cell to slow down as it gets closer to mouse 112 | var k = Math.min(Math.abs(dist), 25) / 25, // max is 1 113 | p = speed * k * main.getConfig().playerSpeed, 114 | x = Math.cos(angle) * p, 115 | y = Math.sin(angle) * p; 116 | // console.log((speed * k * main.getConfig().playerSpeed)/50) 117 | this.position.x += Math.round(x); 118 | this.position.y += Math.round(y); 119 | 120 | } 121 | move(main, sp) { 122 | this.moveToMouse(main, sp) 123 | if (this.moveEngine2.useEngine) this.calcMove2(main, sp); 124 | if (this.moveEngine.useEngine) this.calcMove(main, sp); 125 | 126 | 127 | 128 | } 129 | }; 130 | -------------------------------------------------------------------------------- /source/entities/virus.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var template = require('./template.js'); 20 | module.exports = class Virus extends template { 21 | constructor(position, mass, type, owner, name) { 22 | super(position, mass, type, owner); 23 | 24 | this.color = { 25 | r: 0, 26 | g: 255, 27 | b: 0 28 | } 29 | this.fed = 0; 30 | this.type = 2; 31 | 32 | this.spiked = true; 33 | this.nearby = []; 34 | 35 | } 36 | feed(node, main) { 37 | 38 | /* 39 | Viruses have a completely different mech for OpenAgar. Instead of using the angle of the ejected mass, we get the point of collision and get the angle from that. The result? If you eject mass and it hits the edge of the virus, then it will travel the opposite direction. Like so 40 | \ // the path of split one is relative to the position collision 41 | \ 42 | () 43 | | // ejected mass hits edge of virus 44 | 45 | */ 46 | 47 | node.eat(this, main) 48 | if (this.mass > main.getConfig().maxVirusMass) this.updateMass(main.getConfig().maxVirusMass) 49 | 50 | 51 | if (Math.random() < 0.8) this.fed++; 52 | if (this.fed > main.getConfig().virusFeedMin && main.viruses < main.getConfig().maxVirus) { 53 | var x1 = this.position.x, 54 | y1 = this.position.y, 55 | x2 = node.position.x, 56 | y2 = node.position.y 57 | 58 | var difx = x1 - x2 59 | var dify = y1 - y2 60 | var angle = Math.atan2(dify, difx) 61 | this.split(angle, main) 62 | this.fed = 0; 63 | this.updateMass(main.getConfig().virusMass) 64 | 65 | } 66 | 67 | } 68 | split(angle, main) { 69 | main.splitCell(this, angle, main.getConfig().virusSpeed, main.getConfig().virusDecay, main.getConfig().virusMass) 70 | } 71 | 72 | 73 | onDeletion(main) { 74 | main.viruses--; 75 | } 76 | onCreation(main) { 77 | main.viruses++; 78 | } 79 | getEatRange() { 80 | 81 | return this.size * -0.5; 82 | 83 | } 84 | collide(node, main) { 85 | this.eat(node, main); 86 | var split = main.getConfig().playerMaxCells - node.owner.cells.size; 87 | 88 | 89 | 90 | var defaultmass = ~~(node.mass / (split + 3)) 91 | var big = defaultmass * 2 + (defaultmass / 2) // 2.5 92 | var medium = defaultmass / 2 + defaultmass // 1.5 93 | var angle = (Math.random() * 6.28318530718) // Math.floor(Math.random() * max) + min 94 | node.updateMass(big) 95 | 96 | 97 | // need to divide angle in order to spread them evenly 98 | // 360 degrees -> 2PI radians -> 6.28318530718 99 | var increment = 6.28318530718 / (split + 1) // want to add some randomness 100 | var g = ~~(split.length / 2) 101 | for (var i = 0; i < split; i++) { 102 | var mass = (i == 0) ? medium : defaultmass; 103 | var a = main.splitCell(node, angle, main.getConfig().splitSpeed, main.getConfig().splitDecay, mass) 104 | a.setMerge(main, main.getConfig().playerMerge, main.getConfig().playerMergeMult) 105 | main.getWorld().setFlags(a, "merge") 106 | main.getWorld().setFlags(node, "merge") 107 | angle += increment + (Math.random() * 0.03) 108 | if (angle > 6.28318530718) angle += -6.28318530718 // radians dims 0 < x < 2PI 109 | } 110 | 111 | } 112 | move(main, sp) { 113 | 114 | if (this.moveEngine2.useEngine) this.calcMove2(main, sp) 115 | if (this.moveEngine.useEngine) this.calcMove(main, sp); 116 | this.checkGameBorders(main) 117 | main.updateHash(this) 118 | this.movCode() 119 | 120 | 121 | } 122 | }; 123 | -------------------------------------------------------------------------------- /source/entities/wormHole.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | This program is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU Affero General Public License as published 7 | by the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | You should have received a copy of the GNU Affero General Public License 14 | along with this program. If not, see . 15 | */ 16 | var template = require('./template.js'); 17 | module.exports = class WormHole extends template { 18 | constructor(position, mass, type, owner, name) { 19 | super(position, mass, type, owner); 20 | 21 | this.color = { 22 | r: 0, 23 | g: 0, 24 | b: 0 25 | } 26 | this.okay = []; 27 | 28 | this.agit = true; 29 | this.type = 6; 30 | 31 | 32 | this.nearby = []; 33 | 34 | 35 | } 36 | feed(node, main) { 37 | this.teleport(node, main) 38 | } 39 | onDeletion(main) { 40 | main.wormHoles--; 41 | } 42 | onCreation(main) { 43 | main.wormHoles++; 44 | } 45 | 46 | tpa(node, main) { 47 | 48 | node.position.x = this.position.x 49 | node.position.y = this.position.y 50 | this.okay[node.id] = true; 51 | setTimeout(function () { 52 | this.okay[node.id] = false 53 | }.bind(this), 5000) 54 | 55 | } 56 | teleport(node, main) { 57 | var a = main.getWorld().getNodes('wormhole').toArray() 58 | 59 | a = a[Math.floor(Math.random() * a.length)] 60 | 61 | if (a) { 62 | a.tpa(node, main) 63 | } 64 | } 65 | explode(node, main) { 66 | var split = main.getConfig().playerMaxCells - node.owner.cells.size; 67 | 68 | var defaultmass = ~~(node.mass / (split + 7)) 69 | var big = defaultmass * 3 70 | var medium = defaultmass / 2 + defaultmass * 2 71 | var angle = (Math.random() * 6.28318530718) // Math.floor(Math.random() * max) + min 72 | node.updateMass(big) 73 | this.eat(node, main) 74 | 75 | // need to divide angle in order to spread them evenly 76 | // 360 degrees -> 2PI radians -> 6.28318530718 77 | var increment = 6.28318530718 / (split + 1) // want to add some randomness 78 | var g = ~~(split.length / 2) 79 | for (var i = 0; i < split + 3; i++) { 80 | var mass = (i == 0) ? medium : defaultmass; 81 | var a = main.splitCell(node, angle, main.getConfig().splitSpeed, main.getConfig().splitDecay, mass) 82 | a.setMerge(main, main.getConfig().playerMerge, main.getConfig().playerMergeMult) 83 | main.getWorld().setFlags(a, "merge") 84 | main.getWorld().setFlags(node, "merge") 85 | angle += increment + (Math.random() * 0.03) 86 | if (angle > 6.28318530718) angle += -6.28318530718 // radians dims 0 < x < 2PI 87 | } 88 | } 89 | collide(node, main) { 90 | 91 | if (this.okay[node.id]) return; 92 | if (node.mass > this.mass) { 93 | var random = Math.random() * 100 94 | if (random <= 50) { 95 | 96 | } else if (random <= 80) { 97 | 98 | this.explode(node, main) 99 | return; 100 | } else { 101 | this.okay[node.id] = true; 102 | setTimeout(function () { 103 | this.okay[node.id] = false 104 | }.bind(this), 30000) 105 | return; 106 | } 107 | } 108 | 109 | if (node.owner.cells.size == 1) { 110 | this.teleport(node, main) 111 | } else { 112 | node.owner.cells.forEach((cell) => { 113 | if (cell == node) return; 114 | main.removeNode(cell) 115 | 116 | }) 117 | 118 | this.teleport(node, main) 119 | 120 | } 121 | 122 | } 123 | 124 | }; -------------------------------------------------------------------------------- /source/modes/Experimental.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | var template = require('./template.js'); 21 | module.exports = class Experimental extends template { 22 | constructor() { 23 | super() 24 | this.id = 2; 25 | this.name = "Experimental"; 26 | 27 | } 28 | onServerInit(data) { 29 | var main = data.main; 30 | main.addEntityType(7, "mother", require('../entities/MotherCell.js')); // add the mothercell entity. Type = 7, AccessName = "mother". 31 | main.addCollisionListener(7); // Add a player collision listener 32 | main.addFeedListener(7); // Add a ejected mass feed listener 33 | 34 | main.override("virus", "feed", function (node, main) { 35 | main.setFlags(this, "m") 36 | this.setEngine1(node.moveEngine.angle, 50, 2) 37 | 38 | main.removeNode(node); 39 | }) 40 | 41 | main.addGenerationLoop(20, 7, "mother", 222) // set generation of mothercells. 20 mothercells min. 42 | } // override 43 | 44 | onTick(data) { 45 | data.main.getWorld().getNodes('mother').forEach((m) => { 46 | m.update(data.main); 47 | }) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /source/modes/FFA.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | var template = require('./template.js'); 3 | module.exports = class FFA extends template { 4 | constructor() { 5 | super() 6 | this.id = 0; 7 | this.name = "FFA"; 8 | 9 | } 10 | 11 | 12 | } -------------------------------------------------------------------------------- /source/modes/Minions.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* 3 | OpenAgar - Open source web game 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | var template = require('./template.js'); 20 | module.exports = class Minions extends template { 21 | constructor() { 22 | super() 23 | this.id = 3; 24 | this.name = "Minions"; 25 | 26 | } 27 | onPlayerInit(data) { 28 | 29 | var num = 5, 30 | player = data.player, 31 | main = data.main; 32 | main.addMinions(player, num); 33 | } 34 | 35 | 36 | } -------------------------------------------------------------------------------- /source/modes/Teams.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | var template = require('./template.js'); 3 | module.exports = class Teams extends template { 4 | constructor() { 5 | super() 6 | this.id = 1; 7 | this.name = "Teams"; 8 | this.lowest = 0; 9 | this.highest = false; 10 | this.colors = [{ 11 | 'r': 223, 12 | 'g': 0, 13 | 'b': 0 14 | }, { 15 | 'r': 0, 16 | 'g': 223, 17 | 'b': 0 18 | }, { 19 | 'r': 0, 20 | 'g': 0, 21 | 'b': 223 22 | }, ]; 23 | } 24 | 25 | onServerInit(data) { 26 | var main = data.main 27 | main.haveTeams = true; 28 | 29 | } 30 | onAllInit(data) { 31 | data.player.team = this.lowest; 32 | 33 | data.player.setColor(this.colors[data.player.team]); 34 | 35 | } 36 | onCellAdd(data) { 37 | data.cell.doesCollide = function (cell) { 38 | 39 | if (cell.type == 0 && this.owner.team == cell.owner.team) return true; 40 | return false; 41 | } 42 | data.cell.canEat = function (cell, main) { 43 | if (cell.type != 0 || this.owner.team != cell.owner.team) return true; 44 | return false; 45 | } 46 | } 47 | updateLB(data) { 48 | var main = data.main; 49 | var data = []; 50 | data[0] = 0; 51 | data[1] = 0; 52 | data[2] = 0; 53 | var total = 0; 54 | main.loopPlayers((player) => { 55 | 56 | data[player.team] += player.mass; 57 | total += player.mass; 58 | }) 59 | var g = 0, 60 | h = 0, 61 | l = data[0], 62 | j = 0; 63 | 64 | data.forEach((n, i) => { 65 | 66 | if (n > h) { 67 | h = n; 68 | g = i; 69 | } 70 | if (n < l) { 71 | l = n; 72 | j = i; 73 | } 74 | }) 75 | 76 | this.lowest = j; 77 | this.highest = g; 78 | 79 | data[0] = data[0] / total; 80 | data[1] = data[1] / total; 81 | data[2] = data[2] / total; 82 | var lb = { 83 | lb: data, 84 | conf: { 85 | lbtype: 1 86 | } 87 | 88 | } 89 | main.clients.forEach((client) => { 90 | 91 | client.socket.emit('lb', lb); 92 | }) 93 | return false; 94 | } 95 | } -------------------------------------------------------------------------------- /source/modes/advTeams.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | var template = require('./template.js'); 3 | module.exports = class Teams extends template { 4 | constructor() { 5 | super() 6 | this.id = 1; 7 | this.name = "Advanced Teams"; 8 | this.lowest = 0; 9 | this.highest = false; 10 | this.colors = [{ 11 | 'r': 223, 12 | 'g': 0, 13 | 'b': 0 14 | }, { 15 | 'r': 0, 16 | 'g': 223, 17 | 'b': 0 18 | }, { 19 | 'r': 0, 20 | 'g': 0, 21 | 'b': 223 22 | }, ]; 23 | } 24 | 25 | onServerInit(data) { 26 | var main = data.main 27 | main.haveTeams = true; 28 | 29 | } 30 | onAllInit(data) { 31 | data.player.team = this.lowest; 32 | 33 | data.player.setColor(this.colors[data.player.team]); 34 | 35 | } 36 | 37 | onCellAdd(data) { 38 | 39 | data.cell.doesCollide = function (cell) { 40 | 41 | if (cell.type == 0 && this.owner.team == cell.owner.team && cell.owner.cells.size <= 1 && this.owner.cells.size <= 1) return true; 42 | 43 | return false; 44 | } 45 | data.cell.canEat = function (cell, main) { 46 | 47 | if (cell.type != 0 || this.owner.team != cell.owner.team || cell.owner.cells.size > 1) return true; 48 | return false; 49 | } 50 | } 51 | updateLB(data) { 52 | var main = data.main; 53 | var data = []; 54 | data[0] = 0; 55 | data[1] = 0; 56 | data[2] = 0; 57 | var total = 0; 58 | main.loopPlayers((player) => { 59 | 60 | data[player.team] += player.mass; 61 | total += player.mass; 62 | }) 63 | var g = 0, 64 | h = 0, 65 | l = data[0], 66 | j = 0; 67 | 68 | data.forEach((n, i) => { 69 | 70 | if (n > h) { 71 | h = n; 72 | g = i; 73 | } 74 | if (n < l) { 75 | l = n; 76 | j = i; 77 | } 78 | }) 79 | 80 | this.lowest = j; 81 | this.highest = g; 82 | 83 | data[0] = data[0] / total; 84 | data[1] = data[1] / total; 85 | data[2] = data[2] / total; 86 | var lb = { 87 | lb: data, 88 | conf: { 89 | lbtype: 1 90 | } 91 | 92 | } 93 | main.clients.forEach((client) => { 94 | 95 | client.socket.emit('lb', lb); 96 | }) 97 | return false; 98 | } 99 | } -------------------------------------------------------------------------------- /source/modes/index.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | module.exports = { 3 | modes: [require('./FFA.js'), require('./Teams.js'), require('./Experimental.js'), require('./Minions.js'), require('./HideNSeek.js'), require('./advTeams.js')], 4 | get: function (id) { 5 | if (!this.modes[id]) return false; 6 | var a = new this.modes[id]() 7 | return a; 8 | }, 9 | 10 | } -------------------------------------------------------------------------------- /source/modes/template.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | 3 | module.exports = class Template { // made gamemodes very alike ogar because then people will already know what to do 4 | constructor() { 5 | this.id = -1; 6 | this.name = "Template"; 7 | this.decayMod = 1; 8 | } 9 | 10 | onServerInit(data) { // Called when the server starts 11 | 12 | 13 | } 14 | onTick(data) { // Called at every 0.1s 15 | 16 | } 17 | onChange(data) { // Called when someone changes the gamemode via console commands 18 | 19 | } 20 | 21 | onPlayerInit(data) { // Called when a player joins a server 22 | 23 | } 24 | 25 | onPlayerSpawn(data) { // Called on spawn 26 | 27 | 28 | } 29 | onAllInit(data) { // Called when a player/bot/minion joins 30 | 31 | } 32 | 33 | pressQ(data) { // Called when the Q key is pressed 34 | 35 | // returning nothing will make it use the default action, returning false will make it be disabled entirely 36 | } 37 | 38 | pressW(data) { // Called when the W key is pressed 39 | 40 | 41 | } 42 | pressSpace(data) { // Called when the Space bar is pressed 43 | 44 | 45 | } 46 | onCellAdd(data) { 47 | // Called when a player cell is added 48 | } 49 | onCellRemove(data) { 50 | // Called when a player cell is removed 51 | } 52 | onAllAdd(data) { // Called when a cell is added 53 | 54 | } 55 | onAllRemove(data) { 56 | // Called when a cell is removed 57 | } 58 | updateLB(data) { 59 | // Called when the leaderboard update function is called 60 | 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /source/modules/FastBuffers.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* 3 | Copyright 2016 Andrew S 4 | 5 | You may use this without this copyright header, this header is only so people cant sue me if they claim it as theirs. 6 | */ 7 | module.exports = { 8 | writer: class Writer { 9 | constructor(size) { 10 | this.index = 0; 11 | this.buffer = Buffer.alloc(size); 12 | } 13 | writeString8(string) { 14 | 15 | for (var i = 0; i < string.length; i++) { 16 | var c = string.charCodeAt(i); 17 | this.writeUInt8(c); 18 | } 19 | this.writeUInt8(0) 20 | } 21 | writeString16(string) { 22 | 23 | for (var i = 0; i < string.length; i++) { 24 | var c = string.charCodeAt(i); 25 | 26 | this.writeUInt16BE(c); 27 | } 28 | 29 | this.writeUInt16BE(0); 30 | 31 | } 32 | writeString32(string) { 33 | 34 | for (var i = 0; i < string.length; i++) { 35 | var c = string.charCodeAt(i) 36 | if (c) this.writeUInt32BE(c) 37 | } 38 | this.writeUInt32BE(0); 39 | } 40 | writeDynamic(a) { 41 | var i; 42 | if (a > 270549119) { 43 | throw "ERR: OUT OF BOUNDS" 44 | } else if (a > 2113663) { 45 | a = a - 2113664; 46 | i = 3; 47 | } else if (a > 16511) { 48 | a = a - 16512; 49 | i = 2; 50 | } else if (a > 127) { 51 | a = a - 128; 52 | i = 1; 53 | } else { 54 | i = 0; 55 | } 56 | 57 | for (var j = 0; j < i; j++) { 58 | 59 | this.writeUInt8((a & 127) | 128); 60 | a = a >> 7; 61 | } 62 | this.writeUInt8(a); 63 | } 64 | 65 | writeInt8(n) { 66 | this.buffer.writeInt8(n, this.index++) 67 | } 68 | writeInt16BE(n) { 69 | this.buffer.writeInt16BE(n, this.index) 70 | this.index += 2; 71 | } 72 | writeInt16LE(n) { 73 | this.buffer.writeInt16LE(n, this.index) 74 | this.index += 2; 75 | } 76 | writeInt32BE(n) { 77 | this.buffer.writeInt32BE(n, this.index) 78 | this.index += 4; 79 | } 80 | writeInt32LE(n) { 81 | this.buffer.writeInt32LE(n, this.index) 82 | this.index += 4; 83 | } 84 | writeUInt8(n) { 85 | this.buffer.writeUInt8(n, this.index++) 86 | } 87 | writeUInt16BE(n) { 88 | this.buffer.writeUInt16BE(n, this.index) 89 | this.index += 2; 90 | } 91 | writeUInt16LE(n) { 92 | this.buffer.writeUInt16LE(n, this.index) 93 | this.index += 2; 94 | } 95 | writeUInt32BE(n) { 96 | this.buffer.writeUInt32BE(n, this.index) 97 | this.index += 4; 98 | } 99 | writeUInt32LE(n) { 100 | this.buffer.writeUInt32LE(n, this.index) 101 | this.index += 4; 102 | } 103 | toBuffer() { 104 | return this.buffer; 105 | } 106 | }, 107 | getDynamicSize: function (a) { 108 | if (a > 270549119) { 109 | throw "ERR: OUT OF BOUNDS" 110 | } else if (a > 2113663) { 111 | return 4; 112 | } else if (a > 16511) { 113 | return 3; 114 | } else if (a > 127) { 115 | return 2; 116 | } else { 117 | return 1; 118 | } 119 | }, 120 | reader: class Reader { 121 | constructor(buf) { 122 | this.index = 0; 123 | this.buffer = buf; 124 | } 125 | readString8() { 126 | var data = ""; 127 | while (this.index <= this.buffer.length) { 128 | var d = this.readUInt8(); 129 | if (!d) break; 130 | data += String.fromCharCode(d); 131 | } 132 | return data; 133 | } 134 | readString16() { 135 | var data = ""; 136 | while (this.index <= this.buffer.length) { 137 | var d = this.readUInt16BE(); 138 | if (!d) break; 139 | data += String.fromCharCode(d); 140 | } 141 | return data; 142 | } 143 | readString32() { 144 | var data = ""; 145 | while (this.index <= this.buffer.length) { 146 | var d = this.readUInt32BE(); 147 | if (!d) break; 148 | data += String.fromCharCode(d); 149 | } 150 | return data; 151 | } 152 | readDynamic() { 153 | var num = 0; 154 | 155 | for (var i = 0; i < 4; i++) { 156 | 157 | var n = this.readUInt8(); 158 | 159 | num += (n & 127) << (i * 7); 160 | 161 | 162 | if (n < 127) { 163 | break; 164 | } 165 | } 166 | 167 | if (i === 2) num += 128; 168 | else if (i === 3) num += 16512; 169 | else if (i === 4) num += 2113664; 170 | 171 | return num; 172 | } 173 | readInt8() { 174 | return this.buffer.readInt8(this.index++); 175 | } 176 | readUInt8() { 177 | return this.buffer.readUInt8(this.index++); 178 | } 179 | readInt16BE() { 180 | var data = this.buffer.readInt16BE(this.index); 181 | this.index += 2; 182 | return data; 183 | } 184 | readInt16LE() { 185 | var data = this.buffer.readInt16LE(this.index); 186 | this.index += 2; 187 | return data; 188 | } 189 | readUInt16BE() { 190 | var data = this.buffer.readUInt16BE(this.index); 191 | this.index += 2; 192 | return data; 193 | } 194 | readUInt16LE() { 195 | var data = this.buffer.readUInt16LE(this.index); 196 | this.index += 2; 197 | return data; 198 | } 199 | readInt32BE() { 200 | var data = this.buffer.readInt32BE(this.index); 201 | this.index += 4; 202 | return data; 203 | } 204 | readInt32LE() { 205 | var data = this.buffer.readInt32LE(this.index); 206 | this.index += 4; 207 | return data; 208 | } 209 | readUInt32BE() { 210 | var data = this.buffer.readUInt32BE(this.index); 211 | this.index += 4; 212 | return data; 213 | } 214 | readUInt32LE() { 215 | var data = this.buffer.readUInt32LE(this.index); 216 | this.index += 4; 217 | return data; 218 | } 219 | 220 | } 221 | 222 | } 223 | -------------------------------------------------------------------------------- /source/modules/HashBounds.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | /* 3 | HashBounds - A hierarchical spacial hashing system 4 | Copyright (C) 2016 Andrew S 5 | 6 | This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Affero General Public License as published 8 | by the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Affero General Public License for more details. 15 | 16 | You should have received a copy of the GNU Affero General Public License 17 | along with this program. If not, see . 18 | */ 19 | 20 | 21 | class Holder { 22 | constructor(parent, x, y, power, lvl) { 23 | this.PARENT = parent; 24 | this.PARENT.CHILDREN.push(this) 25 | // this.MAP = new QuickMapV2(); 26 | this.MAP = []; 27 | this.POWER = power; 28 | this.LVL = lvl 29 | this.LEN = 0; // problem with lots of objs 30 | this.X = x; 31 | this.Y = y; 32 | this.BOUNDS = { 33 | x: x << power, 34 | y: y << power, 35 | width: 1 << power, 36 | height: 1 << power 37 | } 38 | this.CHILDREN = [] 39 | 40 | } 41 | checkIntersect(r1, r2) { 42 | var mx1 = r1.x + r1.width, 43 | mx2 = r2.x + r2.width, 44 | my1 = r1.y + r1.height, 45 | my2 = r2.y + r2.height; 46 | /* 47 | !(r2.left > r1.right || 48 | r2.right < r1.left || 49 | r2.top > r1.bottom || 50 | r2.bottom < r1.top); 51 | 52 | */ 53 | 54 | 55 | 56 | return !(r2.x >= mx1 || mx2 <= r1.x || r2.y >= my1 || my2 <= r1.y) 57 | 58 | } 59 | 60 | set(node) { 61 | 62 | this.MAP.push(node); 63 | this.add() 64 | } 65 | delete(node) { 66 | var ind = this.MAP.indexOf(node) 67 | 68 | this.MAP[ind] = this.MAP[this.MAP.length - 1]; 69 | this.MAP.pop(); 70 | this.sub() 71 | } 72 | add() { 73 | ++this.LEN; 74 | this.PARENT.add(); 75 | 76 | 77 | } 78 | 79 | getQuad(bounds, bounds2) { 80 | if (!this.CHILDREN[0]) return -2; 81 | 82 | var minX = bounds.x, 83 | minY = bounds.y, 84 | maxX = bounds.x + bounds.width, 85 | maxY = bounds.y + bounds.height, 86 | minX2 = bounds2.x, 87 | minY2 = bounds2.y, 88 | maxX2 = bounds2.x + bounds2.width, 89 | maxY2 = bounds2.y + bounds2.height, 90 | halfY = bounds2.y + (bounds2.height >> 1), 91 | halfX = bounds2.x + (bounds2.width >> 1); 92 | 93 | 94 | var top = maxY <= halfY; 95 | var bottom = minY > halfY; 96 | var left = maxX <= halfX; 97 | var right = minX > halfX; 98 | 99 | 100 | if (top) { 101 | if (left) return [0]; 102 | else if (right) return [2]; 103 | return [0, 2]; 104 | } else if (bottom) { 105 | if (left) return [1]; 106 | else if (right) return [3]; 107 | return [1, 3]; 108 | } 109 | 110 | if (left) { 111 | return [0, 1]; 112 | } else if (right) { 113 | return [2, 3]; 114 | } 115 | 116 | if (bounds.width < bounds2.width || bounds.height < bounds2.height) return [0, 1, 2, 3]; 117 | return -1; // too big 118 | } 119 | 120 | 121 | 122 | forEachAll(call) { 123 | if (!this.LEN) return; 124 | this.MAP.forEach(call) 125 | 126 | for (var i = 0; i < this.CHILDREN.length; ++i) { 127 | this.CHILDREN[i].forEachAll(call) 128 | } 129 | 130 | 131 | } 132 | forEach(bounds, call) { 133 | if (!this.LEN) return; 134 | 135 | 136 | var quads = this.getQuad(bounds, this.BOUNDS) 137 | 138 | if (quads === -1) return this.forEachAll(call); 139 | 140 | this.MAP.forEach(call) 141 | 142 | if (quads === -2) return 143 | 144 | for (var i = 0, l = quads.length; i < l; i++) { 145 | var child = this.CHILDREN[quads[i]]; 146 | if (child) child.forEach(bounds, call) 147 | } 148 | 149 | 150 | return; 151 | } 152 | every(bounds, call) { 153 | if (!this.LEN) return true; 154 | 155 | var quads = this.getQuad(bounds, this.BOUNDS) 156 | 157 | if (quads === -1) return this.everyAll(call); 158 | 159 | if (!this.MAP.every(call)) return false; 160 | 161 | if (quads === -2) return true; 162 | 163 | return quads.every((q) => { 164 | var child = this.CHILDREN[q]; 165 | if (!child) return true; 166 | return child.every(bounds, call) 167 | }) 168 | } 169 | everyAll(call) { 170 | if (!this.LEN) return true; 171 | if (!this.MAP.every(call)) return false; 172 | for (var i = 0; i < this.CHILDREN.length; ++i) { 173 | if (!this.CHILDREN[i].everyAll(call)) return false; 174 | } 175 | return true; 176 | } 177 | 178 | sub() { 179 | --this.LEN; 180 | this.PARENT.sub(); 181 | } 182 | 183 | 184 | } 185 | class Grid { 186 | constructor(g, p, size, prev) { 187 | this.POWER = g; 188 | this.LEVEL = p; 189 | this.PREV = prev; 190 | this.SIZE = size; 191 | 192 | this.DATA = {}; 193 | this.init() 194 | } 195 | 196 | init() { 197 | if (this.SIZE >= 65535) { 198 | throw "Maximum amount of buckets are 65535^2" 199 | } // Max limit is 65535 (16 bits) 200 | // console.log(this.SIZE) 201 | for (var j = 0; j < this.SIZE; ++j) { 202 | var x = j * this.SIZE; 203 | if (this.PREV) var bx = (j >> 1) * this.PREV.SIZE; 204 | for (var i = 0; i < this.SIZE; ++i) { 205 | 206 | var by = i >> 1; 207 | var key = x + i; 208 | 209 | 210 | if (this.PREV) var l = this.PREV.DATA[bx + by]; 211 | else 212 | var l = { 213 | CHILDREN: [], 214 | add: function () {}, 215 | sub: function () {} 216 | } 217 | this.DATA[key] = new Holder(l, j, i, this.POWER, this.LEVEL); 218 | 219 | } 220 | } 221 | } 222 | 223 | getKey(x, y) { 224 | return { 225 | x: Math.max(x >> this.POWER, 0), 226 | y: Math.max(y >> this.POWER, 0) 227 | } 228 | } 229 | _getKey(x, y) { 230 | return x | y 231 | 232 | } 233 | _get(bounds, call) { 234 | var x1 = bounds.x, 235 | y1 = bounds.y, 236 | x2 = bounds.x + bounds.width, 237 | y2 = bounds.y + bounds.height; 238 | 239 | var k1 = this.getKey(x1, y1) 240 | var k2 = this.getKey(x2, y2) 241 | 242 | for (var j = k1.x; j <= k2.x; ++j) { 243 | 244 | var x = j * this.SIZE; 245 | 246 | for (var i = k1.y; i <= k2.y; ++i) { 247 | 248 | 249 | var key = x + i; 250 | if (this.DATA[key]) { 251 | if (!call(this.DATA[key])) return false 252 | } 253 | 254 | } 255 | } 256 | return true; 257 | } 258 | 259 | insert2(node) { 260 | 261 | // var a = this.getKey(node.bounds.width, node.bounds.height); 262 | // if (a.x + a.y >= 2 && this.LEVEL != 0) return false; 263 | 264 | var x1 = node.bounds.x, 265 | y1 = node.bounds.y, 266 | x2 = node.bounds.x + node.bounds.width, 267 | y2 = node.bounds.y + node.bounds.height; 268 | 269 | var k1 = this.getKey(x1, y1) 270 | var k2 = this.getKey(x2, y2) 271 | node.hash.k1 = k1 272 | node.hash.k2 = k2 273 | node.hash.level = this.LEVEL; 274 | 275 | for (var j = k1.x; j <= k2.x; ++j) { 276 | var x = j * this.SIZE; 277 | for (var i = k1.y; i <= k2.y; ++i) { 278 | 279 | var ke = x + i; 280 | 281 | // console.log(ke) 282 | if (this.DATA[ke]) this.DATA[ke].set(node) 283 | 284 | 285 | 286 | } 287 | 288 | } 289 | 290 | 291 | return true; 292 | } 293 | delete(node) { 294 | var k1 = node.hash.k1 295 | var k2 = node.hash.k2 296 | var lenX = k2.x, 297 | lenY = k2.y; 298 | for (var j = k1.x; j <= lenX; ++j) { 299 | var x = j * this.SIZE; 300 | for (var i = k1.y; i <= lenY; ++i) { 301 | 302 | 303 | var ke = x + i; 304 | 305 | if (this.DATA[ke]) this.DATA[ke].delete(node) 306 | } 307 | 308 | } 309 | } 310 | toArray(bounds) { 311 | var hsh = {}; 312 | var array = []; 313 | this._get(bounds, function (cell) { 314 | 315 | cell.forEach(bounds, function (obj) { 316 | if (hsh[obj._HashID]) return; 317 | hsh[obj._HashID] = true; 318 | array.push(obj); 319 | 320 | }) 321 | return true; 322 | }) 323 | return array; 324 | } 325 | every(bounds, call) { 326 | var hsh = {}; 327 | 328 | return this._get(bounds, function (cell) { 329 | 330 | return cell.every(bounds, function (obj, i) { 331 | if (hsh[obj._HashID]) return true; 332 | hsh[obj._HashID] = true; 333 | return call(obj); 334 | 335 | }) 336 | }) 337 | } 338 | forEach(bounds, call) { 339 | 340 | var hsh = {}; 341 | 342 | this._get(bounds, function (cell) { 343 | 344 | cell.forEach(bounds, function (obj, i) { 345 | if (hsh[obj._HashID]) return; 346 | hsh[obj._HashID] = true; 347 | call(obj); 348 | 349 | }) 350 | return true; 351 | }) 352 | } 353 | } 354 | 355 | module.exports = class HashBounds { 356 | constructor(power, lvl, max) { 357 | this.INITIAL = power; 358 | this.LVL = lvl; 359 | this.MAX = max; 360 | 361 | this.MIN = power; 362 | this.LEVELS = [] 363 | this.lastid = 0; 364 | this.BASE = false; 365 | this.createLevels() 366 | this.log2 = []; 367 | this.setupLog2() 368 | } 369 | setupLog2() { 370 | var pow = 1 << this.LVL; 371 | for (var i = 0; i < pow; ++i) { 372 | this.log2[i - 1] = Math.floor(Math.log2(i)) 373 | } 374 | } 375 | createLevels() { 376 | this.LEVELS = []; 377 | 378 | var last = false; 379 | for (var i = this.LVL - 1; i >= 0; --i) { 380 | var a = this.INITIAL + i; 381 | var b = 1 << a; 382 | var grid = new Grid(a, i, Math.ceil(this.MAX / b), last) 383 | if (!this.BASE) this.BASE = grid; 384 | this.LEVELS[i] = grid; 385 | last = grid; 386 | } 387 | 388 | } 389 | clear() { 390 | this.createLevels(); 391 | } 392 | update(node) { 393 | this.delete(node) 394 | this.insert(node) 395 | } 396 | insert(node) { 397 | if (node._IsInHash) throw "ERR: A node cannot be already in a hash!" 398 | var bounds = node.bounds; 399 | node._IsInHash = true; 400 | 401 | if (node._HashSizeX === bounds.width && node._HashSizeY === bounds.height) { 402 | this.LEVELS[node._HashIndex].insert2(node); 403 | return; 404 | } 405 | 406 | if (!node._HashID) { 407 | node._HashID = ++this.lastid; 408 | node.hash = {}; 409 | } 410 | 411 | var index = this.log2[(Math.max(bounds.width, bounds.height) >> this.MIN)] 412 | if (index === undefined) index = this.LVL - 1; 413 | 414 | node._HashIndex = index; 415 | node._HashSizeX = bounds.width; 416 | node._HashSizeY = bounds.height; 417 | 418 | this.LEVELS[index].insert2(node); 419 | 420 | //for (var i = 0; i < len; ++i) { 421 | // if (this.LEVELS[len - i - 1].insert(node)) break; 422 | //} 423 | } 424 | 425 | 426 | delete(node) { 427 | if (!node._IsInHash) throw "ERR: Node is not in a hash!" 428 | this.LEVELS[node.hash.level].delete(node) 429 | node._IsInHash = false; 430 | } 431 | toArray(bounds) { 432 | return this.BASE.toArray(bounds); 433 | } 434 | every(bounds, call) { 435 | return this.BASE.every(bounds, call); 436 | } 437 | forEach(bounds, call) { 438 | 439 | 440 | this.BASE.forEach(bounds, call) 441 | 442 | 443 | 444 | } 445 | 446 | } -------------------------------------------------------------------------------- /source/modules/fastSquares.js: -------------------------------------------------------------------------------- 1 | // No license here! This is free to the public! 2 | 3 | // Use: A faster alternative to Math.sqrt() 4 | 5 | /* 6 | How to use: 7 | 8 | var a = require('path to fastsquares') 9 | Sqrt = new a() 10 | 11 | Then you can use this anywhere in your code by doing 12 | 13 | Sqrt.sqrt(num) 14 | 15 | */ 16 | 17 | "use strict" 18 | 19 | module.exports = class FastSquares { 20 | constructor() { 21 | this.lib = []; 22 | this.init() 23 | } 24 | init() { 25 | 26 | 27 | for (var i = 0; i < 1000000; i ++) { 28 | this.lib.push(Math.sqrt(i)) 29 | } 30 | 31 | 32 | } 33 | sqrt(a) { 34 | var b = this.lib[~~a] 35 | if (b === undefined) { 36 | return Math.sqrt(~~a) 37 | } 38 | return b 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /source/modules/ini.js: -------------------------------------------------------------------------------- 1 | 2 | exports.parse = exports.decode = decode; 3 | exports.stringify = exports.encode = encode; 4 | 5 | exports.safe = safe; 6 | exports.unsafe = unsafe; 7 | 8 | var eol = process.platform === "win32" ? "\r\n" : "\n"; 9 | 10 | function encode (obj, opt) { 11 | var children = [], 12 | out = ""; 13 | 14 | if (typeof opt === "string") { 15 | opt = { 16 | section: opt, 17 | whitespace: false 18 | }; 19 | } else { 20 | opt = opt || {}; 21 | opt.whitespace = opt.whitespace === true; 22 | } 23 | 24 | var separator = " = "; 25 | 26 | Object.keys(obj).forEach(function (k, _, __) { 27 | var val = obj[k]; 28 | if (val && Array.isArray(val)) { 29 | val.forEach(function(item) { 30 | out += safe(k + "[]") + separator + safe(item) + "\n"; 31 | }); 32 | } 33 | else if (val && typeof val === "object") { 34 | children.push(k); 35 | } else { 36 | out += safe(k) + separator + safe(val) + eol; 37 | } 38 | }); 39 | 40 | if (opt.section && out.length) { 41 | out = "[" + safe(opt.section) + "]" + eol + out; 42 | } 43 | 44 | children.forEach(function (k, _, __) { 45 | var nk = dotSplit(k).join('\\.'); 46 | var section = (opt.section ? opt.section + "." : "") + nk; 47 | var child = encode(obj[k], { 48 | section: section, 49 | whitespace: opt.whitespace 50 | }); 51 | if (out.length && child.length) { 52 | out += eol; 53 | } 54 | out += child; 55 | }); 56 | 57 | return out; 58 | } 59 | 60 | function dotSplit (str) { 61 | return str.replace(/\1/g, '\u0002LITERAL\\1LITERAL\u0002') 62 | .replace(/\\\./g, '\u0001') 63 | .split(/\./).map(function (part) { 64 | return part.replace(/\1/g, '\\.') 65 | .replace(/\2LITERAL\\1LITERAL\2/g, '\u0001'); 66 | }); 67 | } 68 | 69 | function decode (str) { 70 | var out = {}, 71 | p = out, 72 | state = "START", 73 | // section |key = value 74 | re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i, 75 | lines = str.split(/[\r\n]+/g), 76 | section = null; 77 | 78 | lines.forEach(function (line, _, __) { 79 | if (!line || line.match(/^\s*(\/\/|[#;])/)) { 80 | return; 81 | } 82 | 83 | var match = line.match(re); 84 | 85 | if (!match) { 86 | return; 87 | } 88 | 89 | if (match[1] !== undefined) { 90 | section = unsafe(match[1]); 91 | p = out[section] = out[section] || {}; 92 | return; 93 | } 94 | 95 | var key = unsafe(match[2]), 96 | value = match[3] ? unsafe((match[4] || "")) : true; 97 | 98 | // Convert keys with '[]' suffix to an array 99 | if (key.length > 2 && key.slice(-2) === "[]") { 100 | key = key.substring(0, key.length - 2); 101 | if (!p[key]) { 102 | p[key] = []; 103 | } else if (!Array.isArray(p[key])) { 104 | p[key] = [p[key]]; 105 | } 106 | } 107 | 108 | // safeguard against resetting a previously defined 109 | // array by accidentally forgetting the brackets 110 | if (isNaN(value)) { 111 | if (value == 'true') { // Booleans 112 | p[key] = true; 113 | } else if (value == 'false') { 114 | p[key] = false; 115 | } else { 116 | p[key] = value; 117 | } 118 | } else { 119 | if (isInt(value)) { 120 | p[key] = parseInt(value); 121 | } else { 122 | p[key] = parseFloat(value); 123 | } 124 | } 125 | }); 126 | 127 | // {a:{y:1},"a.b":{x:2}} --> {a:{y:1,b:{x:2}}} 128 | // use a filter to return the keys that have to be deleted. 129 | Object.keys(out).filter(function (k, _, __) { 130 | if (!out[k] || typeof out[k] !== "object" || Array.isArray(out[k])) return false 131 | // see if the parent section is also an object. 132 | // if so, add it to that, and mark this one for deletion 133 | var parts = dotSplit(k), 134 | p = out, 135 | l = parts.pop(), 136 | nl = l.replace(/\\\./g, '.'); 137 | parts.forEach(function (part, _, __) { 138 | if (!p[part] || typeof p[part] !== "object") { 139 | p[part] = {}; 140 | } 141 | p = p[part]; 142 | }); 143 | if (p === out && nl === l) { 144 | return false; 145 | } 146 | p[nl] = out[k]; 147 | return true; 148 | }).forEach(function (del, _, __) { 149 | delete out[del]; 150 | }); 151 | 152 | return out; 153 | } 154 | 155 | function isQuoted (val) { 156 | return (val.charAt(0) === "\"" && val.slice(-1) === "\"") 157 | || (val.charAt(0) === "'" && val.slice(-1) === "'"); 158 | } 159 | 160 | function safe (val) { 161 | return (typeof val !== "string" 162 | || val.match(/[=\r\n]/) 163 | || val.match(/^\[/) 164 | || (val.length > 1 165 | && isQuoted(val)) 166 | || val !== val.trim()) 167 | ? JSON.stringify(val) 168 | : val.replace(/;/g, '\\;').replace(/#/g, "\\#"); 169 | } 170 | 171 | function unsafe (val, doUnesc) { 172 | val = (val || "").trim(); 173 | if (isQuoted(val)) { 174 | // remove the single quotes before calling JSON.parse 175 | if (val.charAt(0) === "'") { 176 | val = val.substr(1, val.length - 2); 177 | } 178 | try { 179 | val = JSON.parse(val); 180 | } catch (_) {} 181 | } else { 182 | // walk the val to find the first not-escaped ; character 183 | var esc = false; 184 | var unesc = ""; 185 | for (var i = 0, l = val.length; i < l; i++) { 186 | var c = val.charAt(i); 187 | if (esc) { 188 | if ("\\;#".indexOf(c) !== -1) 189 | unesc += c; 190 | else 191 | unesc += "\\" + c; 192 | esc = false; 193 | } else if (";#".indexOf(c) !== -1) { 194 | break; 195 | } else if (c === "\\") { 196 | esc = true; 197 | } else { 198 | unesc += c; 199 | } 200 | } 201 | if (esc) 202 | unesc += "\\"; 203 | return unesc; 204 | } 205 | return val; 206 | } 207 | 208 | var isInt = function(n) { 209 | return parseInt(n) === n; 210 | }; -------------------------------------------------------------------------------- /source/plugins/README.md: -------------------------------------------------------------------------------- 1 | # Plugins 2 | 3 | -------------------------------------------------------------------------------- /source/settings/botnames.txt: -------------------------------------------------------------------------------- 1 | 8 2 | 8ch 3 | argentina 4 | australia 5 | austria 6 | ayy lmao 7 | bait 8 | bangladesh 9 | belgium 10 | bosnia 11 | botswana 12 | brazil 13 | bulgaria 14 | byzantium 15 | cambodia 16 | canada 17 | chile 18 | china 19 | cia 20 | confederate 21 | Croatia 22 | cuba 23 | denmark 24 | doge 25 | ea 26 | earth 27 | estonia 28 | european union 29 | facebook 30 | facepunch 31 | feminism 32 | finland 33 | france 34 | french kingdom 35 | german empire 36 | germany 37 | greece 38 | hong kong 39 | hungary 40 | imperial japan 41 | india 42 | indiana 43 | indonesia 44 | iran 45 | iraq 46 | ireland 47 | irs 48 | italy 49 | jamaica 50 | japan 51 | kc 52 | latvia 53 | lithuania 54 | luxembourg 55 | maldivas 56 | mars 57 | matriarchy 58 | mexico 59 | moon 60 | nasa 61 | netherlands 62 | nigeria 63 | north korea 64 | norway 65 | origin 66 | pakistan 67 | patriarchy 68 | peru 69 | piccolo 70 | pokerface 71 | poland 72 | portugal 73 | prodota 74 | prussia 75 | qing dynasty 76 | quebec 77 | receita federal 78 | reddit 79 | romania 80 | russia 81 | sanik 82 | scotland 83 | sealand 84 | sir 85 | somalia 86 | south korea 87 | spain 88 | stalin 89 | steam 90 | stussy 91 | sweden 92 | switzerland 93 | taiwan 94 | texas 95 | thailand 96 | tsarist russia 97 | tumblr 98 | turkey 99 | ukraine 100 | united kingdom 101 | usa 102 | ussr 103 | vinesauce 104 | wojak 105 | yaranaika -------------------------------------------------------------------------------- /source/settings/clientConfig.ini: -------------------------------------------------------------------------------- 1 | // Client Configs. Alters the client (play.ogarul.io) 2 | 3 | // [Macros] 4 | // 0 = off, 1 = on. (SMacro = SpaceMacro) 5 | clientSMacro = 0 6 | clientWMacro = 0 7 | clientQMacro = 0 8 | clientEMacro = 0 9 | clientRMacro = 0 10 | 11 | // [Configs] 12 | // 0 = disabled, 1 = disabled on default but changeable, 2 = enabled on default but changeable, 3 = always on 13 | clientDarkBG = 1 14 | clientChat = 2 15 | clientSkins = 2 16 | clientGrid = 1 17 | clientAcid = 1 18 | clientColors = 2 19 | clientNames = 2 20 | clientShowMass = 1 21 | clientSmooth = 1 22 | 23 | // [Others] 24 | // clientMaxName: Number of maximum charactors in the name box 25 | // Title: What to display at the header 26 | // Instructions: Text to show in the instructions area 27 | // defaultUsername: The default username 28 | // nickPlaceHolder: The placeholder for the nickname box 29 | // leavemessage: What to display when a player closes the tab 30 | clientMaxName = 15 31 | clientTitle = "" 32 | clientInstructions = "" 33 | clientNickPlaceholder = "" 34 | clientDefaultUsername = "" 35 | clientLeaveMessage = "" -------------------------------------------------------------------------------- /source/settings/config.ini: -------------------------------------------------------------------------------- 1 | // OpenAgar server configs. 2 | 3 | gameMode = 0 4 | serverViewBaseX = 1380 5 | serverViewBaseY = 820 6 | boundX = 0 7 | boundY = 0 8 | boundWidth = 14000 9 | boundHeight = 14000 10 | serverBots = 0 11 | leaderBoardLen = 10 12 | disconnectTime = 30 13 | botMaxSplit = 4 14 | -------------------------------------------------------------------------------- /source/settings/customSkins.ini: -------------------------------------------------------------------------------- 1 | andrews54757 = "https://avatars1.githubusercontent.com/u/13282284?v=3&s=460" 2 | legitsoulja = "https://avatars2.githubusercontent.com/u/4976824?v=3&s=400" 3 | -------------------------------------------------------------------------------- /source/settings/physicsConfig.ini: -------------------------------------------------------------------------------- 1 | // OpenAgar server configs. 2 | 3 | startMass = 20 4 | playerMaxMass = 25000 5 | minFood = 900 6 | maxFood = 1400 7 | playerSpeed = 40 8 | playerMinMass = 10 9 | splitSpeed = 100 10 | splitDecay = 10 11 | playerMaxCells = 16 12 | ejectedMass = 10 13 | ejectedSpeed = 100 14 | ejectedDecay = 5 15 | ejectMassCooldown = 100 16 | ejectMassMin = 25 17 | bulletSpeed = 20 18 | bulletDecay = 30 19 | bulletReload = 25 20 | bulletReloadMin = 1000 21 | playerMergeMult = 0.06 22 | playerMerge = 10 23 | maxVirusMass = 1000 24 | virusMass = 100 25 | virusFeedMin = 5 26 | maxVirus = 130 27 | minVirus = 90 28 | virusSpeed = 50 29 | virusDecay = 12 30 | splitMin = 32 31 | minWormHole = 0 32 | decayRate = .03 33 | decayRateMax = 10 34 | decayMax = 25000 35 | 36 | -------------------------------------------------------------------------------- /source/settings/pluginConfig.ini: -------------------------------------------------------------------------------- 1 | // allowed: A comma separated list of plugins to allow if they dont pass the security test 2 | // dev: If 1, uncompiled (folder) plugins are allowed and errors will be thrown 3 | 4 | allowed="" 5 | dev = 0 6 | -------------------------------------------------------------------------------- /source/settings/pluginConfig/README.md: -------------------------------------------------------------------------------- 1 | This is where all the plugin configs go 2 | -------------------------------------------------------------------------------- /source/settings/serverConfig.ini: -------------------------------------------------------------------------------- 1 | // OpenAgar server config file. 2 | 3 | // serverPort: Port in which to run the server 4 | // socketProtection: Sets if sockets are secure or not. Protects server against bots. 5 | // SSL: Secure sockets - Please see the readme 6 | 7 | serverPort = 8080 8 | socketProtection = true 9 | ssl = false 10 | 11 | // Statistics configurations 12 | // serverName: Name of server to display 13 | // url: The url to access the server. Your server then can be accessed by going to http://opnagar.us/server/yoururl 14 | // logo: Url of logo to display on list 15 | 16 | serverName = "" 17 | url = "" 18 | logo = "http://opnagar.us/woa.png" 19 | 20 | 21 | -------------------------------------------------------------------------------- /source/settings/servers/README.md: -------------------------------------------------------------------------------- 1 | ## Multi-Servers configuration 2 | Using this folder, you can automatically have OpenAgar create additional servers for you. That way, you dont have to use the servers command every time you restart. Also, this allows you to easily change the configurations of the individual servers. 3 | 4 | ## Usage: 5 | 6 | 1. Create a file in this directory, the file should be `[Server name].ini` 7 | 2. Then, you can override configs in that file. For example, I can put the line `gameMode = 2` to have the server be in teams 8 | 3. Start the server. Your server will automatically be created. 9 | -------------------------------------------------------------------------------- /source/sounds/README.md: -------------------------------------------------------------------------------- 1 | ## The place where all the sounds go 2 | 3 | ### Credits: 4 | Since I dont have time to create sound effects. I got these sound effects from websites. Currently used website: http://soundbible.com. When I have time, I will make them myself 5 | 6 | 7 | ### NOTE: 8 | Although plugins can play sounds from this directory, plugins are unable to add sounds as a security feature, since they run in a separate process. 9 | -------------------------------------------------------------------------------- /source/sounds/alert.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AJS-development/OpenAgar/90fefe007da2c9ebd8221b9f75cac4afdf4ad9cd/source/sounds/alert.mp3 -------------------------------------------------------------------------------- /source/sounds/start.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AJS-development/OpenAgar/90fefe007da2c9ebd8221b9f75cac4afdf4ad9cd/source/sounds/start.mp3 -------------------------------------------------------------------------------- /source/sounds/tone.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AJS-development/OpenAgar/90fefe007da2c9ebd8221b9f75cac4afdf4ad9cd/source/sounds/tone.mp3 -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | node index.js 3 | pause 4 | --------------------------------------------------------------------------------