Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 
 

375 wiersze
7.5 KiB

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