Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

360 rader
7.1 KiB

  1. let map = require('./map');
  2. let syncer = require('./syncer');
  3. let objects = require('../objects/objects');
  4. let spawners = require('./spawners');
  5. let physics = require('./physics');
  6. let resourceSpawner = require('./resourceSpawner');
  7. let spellCallbacks = require('../config/spells/spellCallbacks');
  8. let questBuilder = require('../config/quests/questBuilder');
  9. let events = require('../events/events');
  10. let scheduler = require('../misc/scheduler');
  11. let herbs = require('../config/herbs');
  12. let eventEmitter = require('../misc/events');
  13. const mods = require('../misc/mods');
  14. const transactions = require('../security/transactions');
  15. module.exports = {
  16. instances: [],
  17. zoneId: -1,
  18. speed: consts.tickTime,
  19. //During regens, adds are placed in a queue
  20. addQueue: [],
  21. lastTime: 0,
  22. init: function (args) {
  23. this.zoneId = args.zoneId;
  24. spellCallbacks.init();
  25. herbs.init();
  26. map.init(args);
  27. const fakeInstance = {
  28. objects,
  29. syncer,
  30. physics,
  31. zoneId: this.zoneId,
  32. spawners,
  33. questBuilder,
  34. events,
  35. zone: map.zone,
  36. map,
  37. scheduler,
  38. eventEmitter,
  39. resourceSpawner
  40. };
  41. this.instances.push(fakeInstance);
  42. spawners.init(fakeInstance);
  43. scheduler.init();
  44. map.create();
  45. if (map.mapFile.properties.isRandom) {
  46. if (!map.oldCollisionMap)
  47. map.oldCollisionMap = map.collisionMap;
  48. map.randomMap.init(fakeInstance);
  49. this.startRegen();
  50. } else
  51. _.log('(M ' + map.name + '): Ready');
  52. map.clientMap.zoneId = this.zoneId;
  53. [resourceSpawner, syncer, objects, questBuilder, events].forEach(i => i.init(fakeInstance));
  54. this.tick();
  55. },
  56. startRegen: function (respawnMap, respawnPos) {
  57. this.addQueue = [];
  58. this.regenBusy = true;
  59. this.respawnMap = respawnMap;
  60. this.respawnPos = respawnPos;
  61. },
  62. queueMessage: function (msg) {
  63. this.unqueueMessage(msg);
  64. this.addQueue.push(msg);
  65. },
  66. unqueueMessage: function (msg) {
  67. this.addQueue.spliceWhere(q => q.obj.id === msg.obj.id);
  68. },
  69. tickRegen: function () {
  70. const { respawnPos, respawnMap } = this;
  71. //Ensure that all players are gone
  72. const players = objects.objects.filter(o => o.player);
  73. players.forEach(p => {
  74. if (p.destroyed)
  75. return;
  76. p.fireEvent('beforeRezone');
  77. p.destroyed = true;
  78. const simpleObj = p.getSimple(true, false, true);
  79. if (respawnPos) {
  80. const { x, y } = respawnPos;
  81. simpleObj.x = x;
  82. simpleObj.y = y;
  83. }
  84. process.send({
  85. method: 'rezone',
  86. id: p.serverId,
  87. args: {
  88. obj: simpleObj,
  89. newZone: respawnMap,
  90. keepPos: true
  91. }
  92. });
  93. });
  94. //Only objects and syncer should update if there are players
  95. if (players.length) {
  96. objects.update();
  97. syncer.update();
  98. return;
  99. }
  100. //Clear stuff
  101. spawners.reset();
  102. objects.objects.length = 0;
  103. objects.objects = [];
  104. events.stopAll();
  105. //Try a generation
  106. const isValid = map.randomMap.generate();
  107. if (!isValid)
  108. return;
  109. map.seed = _.getGuid();
  110. //If it succeeds, set regenBusy to false and reset vars
  111. this.regenBusy = false;
  112. this.respawnPos = null;
  113. this.respawnMap = null;
  114. this.addQueue.forEach(q => this.addObject(q));
  115. this.addQueue = [];
  116. _.log('(M ' + map.name + '): Ready');
  117. },
  118. tick: function () {
  119. if (this.regenBusy) {
  120. this.tickRegen();
  121. setTimeout(this.tick.bind(this), this.speed);
  122. return;
  123. }
  124. events.update();
  125. objects.update();
  126. resourceSpawner.update();
  127. spawners.update();
  128. syncer.update();
  129. scheduler.update();
  130. mods.tick();
  131. setTimeout(this.tick.bind(this), this.speed);
  132. },
  133. addObject: function (msg) {
  134. if (this.regenBusy) {
  135. this.queueMessage(msg);
  136. return;
  137. }
  138. let obj = msg.obj;
  139. obj.serverId = obj.id;
  140. delete obj.id;
  141. let spawnPos = map.getSpawnPos(obj);
  142. let spawnEvent = {
  143. spawnPos: extend({}, spawnPos),
  144. changed: false
  145. };
  146. eventEmitter.emit('onBeforePlayerSpawn', { name: obj.name, instance: { physics } }, spawnEvent);
  147. //If a player is added, destroy any player objects with the same name
  148. const existing = objects.filter(o => o.player && o.name === msg.obj.name);
  149. existing.forEach(o => {
  150. o.destroyed = true;
  151. });
  152. if (spawnEvent.changed)
  153. msg.keepPos = false;
  154. if (msg.keepPos && (!physics.isValid(obj.x, obj.y) || !map.canPathFromPos(obj)))
  155. msg.keepPos = false;
  156. if (!msg.keepPos || !obj.has('x') || (map.mapFile.properties.isRandom && obj.instanceId !== map.seed)) {
  157. obj.x = spawnPos.x;
  158. obj.y = spawnPos.y;
  159. }
  160. obj.instanceId = map.seed || null;
  161. obj.spawn = map.spawn;
  162. syncer.queue('onGetMap', map.clientMap, [obj.serverId]);
  163. if (!msg.transfer)
  164. objects.addObject(obj, this.onAddObject.bind(this));
  165. else {
  166. let o = objects.transferObject(obj);
  167. questBuilder.obtain(o);
  168. eventEmitter.emit('onAfterPlayerEnterZone', o);
  169. }
  170. },
  171. onAddObject: function (obj) {
  172. if (obj.player) {
  173. obj.stats.onLogin();
  174. eventEmitter.emit('onAfterPlayerEnterZone', obj);
  175. }
  176. questBuilder.obtain(obj);
  177. obj.fireEvent('afterMove');
  178. if (obj.dead) {
  179. obj.instance.syncer.queue('onDeath', {
  180. x: obj.x,
  181. y: obj.y
  182. }, [obj.serverId]);
  183. }
  184. },
  185. updateObject: function (msg) {
  186. let obj = objects.find(o => o.serverId === msg.id);
  187. if (!obj)
  188. return;
  189. let msgObj = msg.obj;
  190. let components = msgObj.components || [];
  191. delete msgObj.components;
  192. for (let p in msgObj)
  193. obj[p] = msgObj[p];
  194. let cLen = components.length;
  195. for (let i = 0; i < cLen; i++) {
  196. let c = components[i];
  197. let component = obj[c.type];
  198. for (let p in c)
  199. component[p] = c[p];
  200. }
  201. },
  202. queueAction: function (msg) {
  203. let obj = objects.find(o => o.serverId === msg.id);
  204. if (!obj)
  205. return;
  206. else if (msg.action.action === 'move') {
  207. let moveEntries = obj.actionQueue.filter(q => (q.action === 'move')).length;
  208. if (moveEntries >= 50)
  209. return;
  210. }
  211. obj.queue(msg.action);
  212. },
  213. performAction: function (msg) {
  214. let obj = null;
  215. let targetId = msg.action.targetId;
  216. if (!targetId)
  217. obj = objects.find(o => o.serverId === msg.id);
  218. else {
  219. obj = objects.find(o => o.id === targetId);
  220. if (obj) {
  221. let action = msg.action;
  222. if (!action.data)
  223. action.data = {};
  224. action.data.sourceId = msg.id;
  225. }
  226. }
  227. if (!obj)
  228. return;
  229. obj.performAction(msg.action);
  230. },
  231. removeObject: async function (msg) {
  232. if (this.regenBusy) {
  233. this.unqueueMessage(msg);
  234. return;
  235. }
  236. let obj = msg.obj;
  237. obj = objects.find(o => o.serverId === obj.id);
  238. if (!obj) {
  239. //We should probably never reach this
  240. return;
  241. }
  242. if (obj.auth)
  243. await obj.auth.doSave();
  244. if (obj.player) {
  245. obj.fireEvent('beforeRezone');
  246. eventEmitter.emit('onAfterPlayerLeaveZone', obj);
  247. }
  248. obj.destroyed = true;
  249. if (msg.callbackId) {
  250. process.send({
  251. module: 'atlas',
  252. method: 'resolveCallback',
  253. msg: {
  254. id: msg.callbackId
  255. }
  256. });
  257. }
  258. },
  259. notifyOnceIdle: async function () {
  260. await transactions.returnWhenDone();
  261. process.send({
  262. method: 'onZoneIdle'
  263. });
  264. },
  265. forceSavePlayer: async function ({ playerName, callbackId }) {
  266. const player = objects.objects.find(o => o.player && o.name === playerName);
  267. if (!player?.auth)
  268. return;
  269. await player.auth.doSave();
  270. process.send({
  271. module: 'atlas',
  272. method: 'resolveCallback',
  273. msg: {
  274. id: callbackId
  275. }
  276. });
  277. }
  278. };