Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 

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