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.
 
 
 

251 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 => {
  156. return new Promise(async res => {
  157. await spawnThread(m);
  158. });
  159. });
  160. await Promise.all(promises);
  161. };
  162. const sendMessageToThread = ({ threadId, msg }) => {
  163. const thread = threads.find(t => t.id === threadId);
  164. if (thread)
  165. thread.worker.send(msg);
  166. };
  167. const messageAllThreads = message => {
  168. threads.forEach(t => t.worker.send(message));
  169. };
  170. const returnWhenThreadsIdle = async () => {
  171. return new Promise(res => {
  172. let doneCount = 0;
  173. const onZoneIdle = thread => {
  174. doneCount++;
  175. if (doneCount.length < threads.length)
  176. return;
  177. listenersOnZoneIdle.spliceWhere(l => l === onZoneIdle);
  178. res();
  179. };
  180. listenersOnZoneIdle.push(onZoneIdle);
  181. threads.forEach(t => {
  182. t.worker.send({
  183. method: 'notifyOnceIdle'
  184. });
  185. });
  186. });
  187. };
  188. //Exports
  189. module.exports = {
  190. getThread,
  191. killThread,
  192. getThreadFromId,
  193. spawnMapThreads,
  194. messageAllThreads,
  195. sendMessageToThread,
  196. returnWhenThreadsIdle
  197. };