25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 

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