Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 

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