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.
 
 
 

336 lines
7.1 KiB

  1. let childProcess = require('child_process');
  2. let objects = require('../objects/objects');
  3. let mapList = require('../config/maps/mapList');
  4. let connections = require('../security/connections');
  5. let events = require('../misc/events');
  6. const listenersOnZoneIdle = [];
  7. module.exports = {
  8. nextId: 0,
  9. lastCallbackId: 0,
  10. threads: [],
  11. callbacks: [],
  12. init: function () {
  13. this.getMapFiles();
  14. },
  15. addObject: async function (obj, keepPos, transfer) {
  16. const serverObj = objects.objects.find(o => o.id === obj.id);
  17. if (!serverObj)
  18. return;
  19. events.emit('onBeforePlayerEnterWorld', obj);
  20. let thread;
  21. let map = mapList.mapList.find(m => m.name === obj.zoneName);
  22. if (!map)
  23. map = mapList.mapList.find(m => m.name === clientConfig.config.defaultZone);
  24. thread = this.threads.find(t => t.id === obj.zoneId && t.name === obj.zoneName);
  25. if (!thread) {
  26. if (map.instanced) {
  27. delete obj.x;
  28. delete obj.y;
  29. thread = this.spawnMap(map);
  30. await new Promise(res => setTimeout(res, 2000));
  31. } else
  32. thread = this.getThreadFromName(map.name);
  33. }
  34. obj.zoneName = thread.name;
  35. obj.zoneId = thread.id;
  36. serverObj.zoneId = thread.id;
  37. serverObj.zoneName = thread.name;
  38. const simpleObj = obj.getSimple ? obj.getSimple(true, true) : obj;
  39. this.send(obj.zoneId, {
  40. method: 'addObject',
  41. args: {
  42. keepPos: keepPos,
  43. obj: simpleObj,
  44. transfer: transfer
  45. }
  46. });
  47. },
  48. removeObjectFromInstancedZone: async function (thread, obj, callback) {
  49. await new Promise(res => {
  50. const cb = this.registerCallback(res);
  51. thread.worker.send({
  52. method: 'forceSavePlayer',
  53. args: {
  54. playerName: obj.name,
  55. callbackId: cb
  56. }
  57. });
  58. });
  59. thread.worker.kill();
  60. this.threads.spliceWhere(t => t === thread);
  61. if (callback)
  62. callback();
  63. },
  64. removeObject: function (obj, skipLocal, callback) {
  65. if (!skipLocal)
  66. objects.removeObject(obj);
  67. let thread = this.findObjectThread(obj);
  68. if (!thread)
  69. return;
  70. if (thread.instanced) {
  71. this.removeObjectFromInstancedZone(thread, obj, callback);
  72. return;
  73. }
  74. let callbackId = null;
  75. if (callback)
  76. callbackId = this.registerCallback(callback);
  77. this.send(obj.zoneId, {
  78. method: 'removeObject',
  79. args: {
  80. obj: obj.getSimple(true),
  81. callbackId: callbackId
  82. }
  83. });
  84. },
  85. updateObject: function (obj, msgObj) {
  86. this.send(obj.zoneId, {
  87. method: 'updateObject',
  88. args: {
  89. id: obj.id,
  90. obj: msgObj
  91. }
  92. });
  93. },
  94. queueAction: function (obj, action) {
  95. this.send(obj.zoneId, {
  96. method: 'queueAction',
  97. args: {
  98. id: obj.id,
  99. action: action
  100. }
  101. });
  102. },
  103. performAction: function (obj, action) {
  104. this.send(obj.zoneId, {
  105. method: 'performAction',
  106. args: {
  107. id: obj.id,
  108. action: action
  109. }
  110. });
  111. },
  112. registerCallback: function (callback) {
  113. this.callbacks.push({
  114. id: ++this.lastCallbackId,
  115. callback: callback
  116. });
  117. return this.lastCallbackId;
  118. },
  119. resolveCallback: function (msg) {
  120. let callback = this.callbacks.spliceFirstWhere(c => c.id === msg.msg.id);
  121. if (!callback)
  122. return;
  123. callback.callback(msg.msg.result);
  124. },
  125. send: function (threadId, msg) {
  126. const thread = this.threads.find(t => t.id === threadId);
  127. if (thread)
  128. thread.worker.send(msg);
  129. },
  130. findObjectThread: function ({ zoneId }) {
  131. return this.threads.find(t => t.id === zoneId);
  132. },
  133. getThreadFromName: function (name) {
  134. return this.threads.find(t => t.name === name);
  135. },
  136. getMapFiles: function () {
  137. mapList.mapList
  138. .filter(m => !m.disabled && !m.instanced)
  139. .forEach(m => this.spawnMap(m));
  140. },
  141. spawnMap: function ({ name, path, instanced }) {
  142. const worker = childProcess.fork('./world/worker', [name]);
  143. const id = instanced ? _.getGuid() : name;
  144. const thread = {
  145. id,
  146. name,
  147. instanced,
  148. path,
  149. worker
  150. };
  151. const onMessage = this.onMessage.bind(this, thread);
  152. worker.on('message', function (m) {
  153. onMessage(m);
  154. });
  155. this.threads.push(thread);
  156. return thread;
  157. },
  158. onMessage: function (thread, message) {
  159. if (message.module)
  160. global[message.module][message.method](message);
  161. else if (message.event === 'onCrashed') {
  162. thread.worker.kill();
  163. process.exit();
  164. } else
  165. this.thread[message.method].call(this, thread, message);
  166. },
  167. messageAllThreads: function (message) {
  168. this.threads.forEach(t => t.worker.send(message));
  169. },
  170. fireEventOnAllThreads: function ({ msg: { event, data } }) {
  171. this.threads.forEach(t => t.worker.send({ event, data }));
  172. },
  173. thread: {
  174. onReady: function (thread) {
  175. thread.worker.send({
  176. method: 'init',
  177. args: {
  178. zoneName: thread.name,
  179. zoneId: thread.id,
  180. path: thread.path
  181. }
  182. });
  183. },
  184. event: function (thread, message) {
  185. objects.sendEvent(message, thread);
  186. },
  187. events: function (thread, message) {
  188. objects.sendEvents(message, thread);
  189. },
  190. object: function (thread, message) {
  191. objects.updateObject(message);
  192. },
  193. track: function (thread, message) {
  194. let player = objects.objects.find(o => o.id === message.serverId);
  195. if (!player)
  196. return;
  197. player.auth.gaTracker.track(message.obj);
  198. },
  199. callDifferentThread: function (thread, message) {
  200. let obj = connections.players.find(p => (p.name === message.playerName));
  201. if (!obj)
  202. return;
  203. let newThread = this.getThreadFromName(obj.zoneName);
  204. if (!newThread)
  205. return;
  206. newThread.worker.send({
  207. module: message.data.module,
  208. method: message.data.method,
  209. args: message.data.args
  210. });
  211. },
  212. rezone: async function (thread, message) {
  213. const { args: { obj, newZone, keepPos = true } } = message;
  214. //When messages are sent from map threads, they have an id (id of the object in the map thread)
  215. // as well as a serverId (id of the object in the main thread)
  216. const serverId = obj.serverId;
  217. obj.id = serverId;
  218. obj.destroyed = false;
  219. const serverObj = objects.objects.find(o => o.id === obj.id);
  220. const mapExists = mapList.mapList.some(m => m.name === newZone);
  221. if (mapExists) {
  222. serverObj.zoneName = newZone;
  223. obj.zoneName = newZone;
  224. } else {
  225. obj.zoneName = clientConfig.config.defaultZone;
  226. serverObj.zoneName = clientConfig.config.defaultZone;
  227. }
  228. delete serverObj.zoneId;
  229. delete obj.zoneId;
  230. serverObj.player.broadcastSelf();
  231. const isRezone = true;
  232. await this.addObject(obj, keepPos, isRezone);
  233. },
  234. onZoneIdle: function (thread) {
  235. listenersOnZoneIdle.forEach(l => l(thread));
  236. }
  237. },
  238. returnWhenZonesIdle: async function () {
  239. return new Promise(res => {
  240. const waiting = [...this.threads];
  241. const onZoneIdle = thread => {
  242. waiting.spliceWhere(w => w === thread);
  243. if (waiting.length)
  244. return;
  245. listenersOnZoneIdle.spliceWhere(l => l === onZoneIdle);
  246. res();
  247. };
  248. listenersOnZoneIdle.push(onZoneIdle);
  249. this.threads.forEach(t => {
  250. t.worker.send({
  251. method: 'notifyOnceIdle'
  252. });
  253. });
  254. });
  255. },
  256. forceSavePlayer: async function (playerName, zoneId) {
  257. const thread = this.threads.find(t => t.id === zoneId);
  258. if (!thread)
  259. return;
  260. return new Promise(res => {
  261. const callbackId = this.registerCallback(res);
  262. thread.worker.send({
  263. method: 'forceSavePlayer',
  264. args: {
  265. playerName,
  266. callbackId
  267. }
  268. });
  269. });
  270. }
  271. };