Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 
 

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