You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

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