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.
 
 
 

247 lines
5.1 KiB

  1. //System Imports
  2. const childProcess = require('child_process');
  3. //Imports
  4. const objects = require('../objects/objects');
  5. const connections = require('../security/connections');
  6. const { mapList } = require('./mapManager');
  7. //Internals
  8. const threads = [];
  9. const listenersOnZoneIdle = [];
  10. //Helpers
  11. const getThreadFromName = name => {
  12. return threads.find(t => t.name === name);
  13. };
  14. const getThreadFromId = threadId => {
  15. return threads.find(t => t.id === threadId);
  16. };
  17. const messageHandlers = {
  18. onReady: function (thread) {
  19. thread.worker.send({
  20. method: 'init',
  21. args: {
  22. zoneName: thread.name,
  23. zoneId: thread.id,
  24. path: thread.path
  25. }
  26. });
  27. },
  28. onInitialized: function (thread) {
  29. thread.isReady = true;
  30. thread.cbOnInitialized(thread);
  31. delete thread.cbOnInitialized;
  32. delete thread.promise;
  33. },
  34. event: function (thread, message) {
  35. objects.sendEvent(message, thread);
  36. },
  37. events: function (thread, message) {
  38. objects.sendEvents(message, thread);
  39. },
  40. object: function (thread, message) {
  41. objects.updateObject(message);
  42. },
  43. track: function (thread, message) {
  44. let player = objects.objects.find(o => o.id === message.serverId);
  45. if (!player)
  46. return;
  47. player.auth.gaTracker.track(message.obj);
  48. },
  49. callDifferentThread: function (thread, message) {
  50. let obj = connections.players.find(p => (p.name === message.playerName));
  51. if (!obj)
  52. return;
  53. let newThread = getThreadFromName(obj.zoneName);
  54. if (!newThread)
  55. return;
  56. newThread.worker.send({
  57. module: message.data.module,
  58. method: message.data.method,
  59. args: message.data.args
  60. });
  61. },
  62. rezone: async function (thread, message) {
  63. const { args: { obj, newZone, keepPos = true } } = message;
  64. if (thread.instanced) {
  65. thread.worker.kill();
  66. threads.spliceWhere(t => t === thread);
  67. }
  68. //When messages are sent from map threads, they have an id (id of the object in the map thread)
  69. // as well as a serverId (id of the object in the main thread)
  70. const serverId = obj.serverId;
  71. obj.id = serverId;
  72. obj.destroyed = false;
  73. const serverObj = objects.objects.find(o => o.id === obj.id);
  74. const mapExists = mapList.some(m => m.name === newZone);
  75. if (mapExists) {
  76. serverObj.zoneName = newZone;
  77. obj.zoneName = newZone;
  78. } else {
  79. obj.zoneName = clientConfig.config.defaultZone;
  80. serverObj.zoneName = clientConfig.config.defaultZone;
  81. }
  82. delete serverObj.zoneId;
  83. delete obj.zoneId;
  84. const isRezone = true;
  85. await atlas.addObject(obj, keepPos, isRezone);
  86. },
  87. onZoneIdle: function (thread) {
  88. listenersOnZoneIdle.forEach(l => l(thread));
  89. }
  90. };
  91. const onMessage = (thread, message) => {
  92. if (message.module) {
  93. try {
  94. global[message.module][message.method](message);
  95. } catch (e) {
  96. /* eslint-disable-next-line no-console */
  97. console.log('No global method found', message.module, message.method);
  98. process.exit();
  99. }
  100. } else if (message.event === 'onCrashed') {
  101. thread.worker.kill();
  102. process.exit();
  103. } else
  104. messageHandlers[message.method](thread, message);
  105. };
  106. const spawnThread = async ({ name, path, instanced }) => {
  107. let cbOnInitialized;
  108. const promise = new Promise(resolveOnReady => {
  109. cbOnInitialized = resolveOnReady;
  110. });
  111. const worker = childProcess.fork('./world/worker', [name]);
  112. const id = instanced ? _.getGuid() : name;
  113. const thread = {
  114. id,
  115. name,
  116. instanced,
  117. path,
  118. worker,
  119. isReady: false,
  120. promise,
  121. cbOnInitialized
  122. };
  123. worker.on('message', onMessage.bind(null, thread));
  124. threads.push(thread);
  125. return promise;
  126. };
  127. const getThread = async ({ zoneName, zoneId }) => {
  128. const result = {
  129. resetObjPosition: false,
  130. thread: null
  131. };
  132. let map = mapList.find(m => m.name === zoneName);
  133. if (!map)
  134. map = mapList.find(m => m.name === clientConfig.config.defaultZone);
  135. let thread = threads.find(t => t.id === zoneId && t.name === zoneName);
  136. if (!thread) {
  137. if (map.instanced) {
  138. result.resetObjPosition = true;
  139. thread = await spawnThread(map);
  140. } else
  141. thread = getThreadFromName(map.name);
  142. }
  143. if (!thread.isReady)
  144. await thread.promise;
  145. result.thread = thread;
  146. return result;
  147. };
  148. const killThread = thread => {
  149. thread.worker.kill();
  150. threads.spliceWhere(t => t === thread);
  151. };
  152. const spawnMapThreads = async () => {
  153. const promises = mapList
  154. .filter(m => !m.disabled && !m.instanced)
  155. .map(m => spawnThread(m));
  156. await Promise.all(promises);
  157. };
  158. const sendMessageToThread = ({ threadId, msg }) => {
  159. const thread = threads.find(t => t.id === threadId);
  160. if (thread)
  161. thread.worker.send(msg);
  162. };
  163. const messageAllThreads = message => {
  164. threads.forEach(t => t.worker.send(message));
  165. };
  166. const returnWhenThreadsIdle = async () => {
  167. return new Promise(res => {
  168. let doneCount = 0;
  169. const onZoneIdle = thread => {
  170. doneCount++;
  171. if (doneCount.length < threads.length)
  172. return;
  173. listenersOnZoneIdle.spliceWhere(l => l === onZoneIdle);
  174. res();
  175. };
  176. listenersOnZoneIdle.push(onZoneIdle);
  177. threads.forEach(t => {
  178. t.worker.send({
  179. method: 'notifyOnceIdle'
  180. });
  181. });
  182. });
  183. };
  184. //Exports
  185. module.exports = {
  186. getThread,
  187. killThread,
  188. getThreadFromId,
  189. spawnMapThreads,
  190. messageAllThreads,
  191. sendMessageToThread,
  192. returnWhenThreadsIdle
  193. };