feat #1866: Rebuilt rezone logic to use handshakes Closes #1866 See merge request Isleward/isleward!575merge-requests/575/merge
@@ -1,7 +1,9 @@ | |||
define([ | |||
'js/misc/distanceToPolygon' | |||
'js/misc/distanceToPolygon', | |||
'js/system/events' | |||
], function ( | |||
distanceToPolygon | |||
distanceToPolygon, | |||
events | |||
) { | |||
return { | |||
grid: null, | |||
@@ -10,6 +12,8 @@ define([ | |||
height: 0, | |||
init: function (collisionMap) { | |||
events.on('resetPhysics', this.reset.bind(this)); | |||
this.width = collisionMap.length; | |||
this.height = collisionMap[0].length; | |||
@@ -22,6 +26,13 @@ define([ | |||
} | |||
}, | |||
reset: function () { | |||
this.width = 0; | |||
this.height = 0; | |||
this.grid = []; | |||
}, | |||
isTileBlocking: function (x, y, mob, obj) { | |||
if ((x < 0) || (y < 0) || (x >= this.width) | (y >= this.height)) | |||
return true; | |||
@@ -174,7 +174,7 @@ define([ | |||
offEvents: function () { | |||
if (this.pather) | |||
this.pather.onDeath(); | |||
this.pather.resetPath(); | |||
for (let e in this.eventCallbacks) | |||
this.eventCallbacks[e].forEach(c => events.off(e, c)); | |||
@@ -15,11 +15,15 @@ define([ | |||
objects: [], | |||
init: function () { | |||
events.on('onGetObject', this.onGetObject.bind(this)); | |||
events.on('onRezone', this.onRezone.bind(this)); | |||
events.on('onChangeHoverTile', this.getLocation.bind(this)); | |||
events.on('onTilesVisible', this.onTilesVisible.bind(this)); | |||
events.on('onToggleNameplates', this.onToggleNameplates.bind(this)); | |||
[ | |||
'onGetObject', | |||
'onTilesVisible', | |||
'onToggleNameplates', | |||
'destroyAllObjects' | |||
] | |||
.forEach(e => events.on(e, this[e].bind(this))); | |||
}, | |||
getLocation: function (x, y) { | |||
@@ -87,20 +91,14 @@ define([ | |||
return list[fromIndex]; | |||
}, | |||
onRezone: function (oldZone) { | |||
let objects = this.objects; | |||
let oLen = objects.length; | |||
for (let i = 0; i < oLen; i++) { | |||
let o = objects[i]; | |||
destroyAllObjects: function () { | |||
this.objects.forEach(o => { | |||
o.destroy(); | |||
}); | |||
if (oldZone === null) | |||
o.destroy(); | |||
else if (o.zoneId === oldZone) | |||
o.destroy(); | |||
} | |||
this.objects.length = 0; | |||
if (window.player) | |||
window.player.offEvents(); | |||
window?.player?.offEvents(); | |||
}, | |||
onGetObject: function (obj) { | |||
@@ -0,0 +1,45 @@ | |||
define([ | |||
'js/rendering/spritePool' | |||
], function ( | |||
spritePool | |||
) { | |||
return function () { | |||
let map = this.map; | |||
let w = this.w = map.length; | |||
let h = this.h = map[0].length; | |||
this.stage.removeChild(this.layers.hiders); | |||
this.layers.hiders = new PIXI.Container(); | |||
this.layers.hiders.layer = 'hiders'; | |||
this.stage.addChild(this.layers.hiders); | |||
let container = this.layers.tileSprites; | |||
this.stage.removeChild(container); | |||
this.layers.tileSprites = container = new PIXI.Container(); | |||
container.layer = 'tiles'; | |||
this.stage.addChild(container); | |||
this.stage.children.sort((a, b) => { | |||
if (a.layer === 'hiders') | |||
return 1; | |||
else if (b.layer === 'hiders') | |||
return -1; | |||
else if (a.layer === 'tiles') | |||
return -1; | |||
else if (b.layer === 'tiles') | |||
return 1; | |||
return 0; | |||
}); | |||
spritePool.clean(); | |||
this.sprites = _.get2dArray(w, h, 'array'); | |||
this.map = []; | |||
this.w = 0; | |||
this.h = 0; | |||
delete this.moveTo; | |||
}; | |||
}); |
@@ -8,7 +8,8 @@ define([ | |||
'js/rendering/shaders/outline', | |||
'js/rendering/spritePool', | |||
'js/system/globals', | |||
'js/rendering/renderLoginBackground' | |||
'js/rendering/renderLoginBackground', | |||
'js/rendering/helpers/resetRenderer' | |||
], function ( | |||
resources, | |||
events, | |||
@@ -19,7 +20,8 @@ define([ | |||
shaderOutline, | |||
spritePool, | |||
globals, | |||
renderLoginBackground | |||
renderLoginBackground, | |||
resetRenderer | |||
) { | |||
const mRandom = Math.random.bind(Math); | |||
@@ -84,6 +86,7 @@ define([ | |||
events.on('onGetMap', this.onGetMap.bind(this)); | |||
events.on('onToggleFullscreen', this.toggleScreen.bind(this)); | |||
events.on('onMoveSpeedChange', this.adaptCameraMoveSpeed.bind(this)); | |||
events.on('resetRenderer', resetRenderer.bind(this)); | |||
this.width = $('body').width(); | |||
this.height = $('body').height(); | |||
@@ -18,7 +18,38 @@ define([ | |||
this.socket.on('event', this.onEvent.bind(this)); | |||
this.socket.on('events', this.onEvents.bind(this)); | |||
this.socket.on('dc', this.onDisconnect.bind(this)); | |||
Object.entries(this.processAction).forEach(([k, v]) => { | |||
this.processAction[k] = v.bind(this); | |||
}); | |||
}, | |||
onRezoneStart: function () { | |||
//Fired for mods to listen to | |||
events.emit('rezoneStart'); | |||
events.emit('destroyAllObjects'); | |||
events.emit('resetRenderer'); | |||
events.emit('resetPhysics'); | |||
events.emit('clearUis'); | |||
client.request({ | |||
threadModule: 'rezoneManager', | |||
method: 'clientAck', | |||
data: {} | |||
}); | |||
}, | |||
onGetMap: function ([msg]) { | |||
events.emit('onGetMap', msg); | |||
client.request({ | |||
threadModule: 'instancer', | |||
method: 'clientAck', | |||
data: {} | |||
}); | |||
}, | |||
onConnected: function (onReady) { | |||
if (this.doneConnect) | |||
this.onDisconnect(); | |||
@@ -28,45 +59,72 @@ define([ | |||
if (onReady) | |||
onReady(); | |||
}, | |||
onDisconnect: function () { | |||
window.location = window.location; | |||
}, | |||
onHandshake: function () { | |||
events.emit('onHandshake'); | |||
this.socket.emit('handshake'); | |||
}, | |||
request: function (msg) { | |||
this.socket.emit('request', msg, msg.callback); | |||
}, | |||
onEvent: function (response) { | |||
events.emit(response.event, response.data); | |||
}, | |||
onEvents: function (response) { | |||
//If we get objects, self needs to be first | |||
// otherwise we might create the object (setting his position or attack animation) | |||
// before instantiating it | |||
let oList = response.onGetObject; | |||
if (oList) { | |||
let prepend = oList.filter(o => o.self); | |||
oList.spliceWhere(o => prepend.some(p => p === o)); | |||
oList.unshift.apply(oList, prepend); | |||
} | |||
for (let e in response) { | |||
let r = response[e]; | |||
processAction: { | |||
default: function (eventName, msgs) { | |||
msgs.forEach(m => events.emit(eventName, m)); | |||
}, | |||
rezoneStart: function (eventName, msgs) { | |||
events.emit('rezoneStart'); | |||
events.emit('destroyAllObjects'); | |||
events.emit('resetRenderer'); | |||
events.emit('resetPhysics'); | |||
events.emit('clearUis'); | |||
//Certain messages expect to be performed last (because the object they act on hasn't been created when they get queued) | |||
r.sort(function (a, b) { | |||
if (a.performLast) | |||
return 1; | |||
else if (b.performLast) | |||
return -1; | |||
return 0; | |||
client.request({ | |||
threadModule: 'rezoneManager', | |||
method: 'clientAck', | |||
data: {} | |||
}); | |||
}, | |||
r.forEach(function (o) { | |||
events.emit(e, o); | |||
getMap: function (eventName, msgs) { | |||
events.emit('onGetMap', msgs[0]); | |||
client.request({ | |||
threadModule: 'instancer', | |||
method: 'clientAck', | |||
data: {} | |||
}); | |||
}, | |||
onGetObject: function (eventName, msgs) { | |||
const prepend = msgs.filter(o => o.self); | |||
msgs.spliceWhere(o => prepend.some(p => p === o)); | |||
msgs.unshift.apply(msgs, prepend); | |||
this.processAction.default(eventName, msgs); | |||
} | |||
}, | |||
onEvent: function ({ event: eventName, data: eventData }) { | |||
const handler = this.processAction[eventName] || this.processAction.default; | |||
handler(eventName, [eventData]); | |||
}, | |||
onEvents: function (response) { | |||
for (let eventName in response) { | |||
const eventMsgs = response[eventName]; | |||
const handler = this.processAction[eventName] || this.processAction.default; | |||
handler(eventName, eventMsgs); | |||
} | |||
} | |||
}; | |||
@@ -20,14 +20,14 @@ define([ | |||
this.onEvent('onPermadeath', this.onPermadeath.bind(this)); | |||
this.find('.btn-logout').on('click', this.onLogout.bind(this)); | |||
this.find('.btn-respawn').on('click', this.onRespawn.bind(this)); | |||
this.find('.btn-respawn').on('click', this.performRespawn.bind(this)); | |||
}, | |||
onLogout: function () { | |||
$('.uiMainMenu').data('ui').charSelect(); | |||
}, | |||
onRespawn: function () { | |||
performRespawn: function () { | |||
events.emit('onHideOverlay', this.el); | |||
this.hide(true); | |||
@@ -26,7 +26,7 @@ define([ | |||
this.find('.btnCollapse').on('click', this.toggleButtons.bind(this)); | |||
} | |||
this.onEvent('onRezone', this.onRezone.bind(this)); | |||
this.onEvent('clearUis', this.clear.bind(this)); | |||
this.onEvent('onObtainEvent', this.onObtainEvent.bind(this)); | |||
this.onEvent('onRemoveEvent', this.onRemoveEvent.bind(this)); | |||
@@ -37,7 +37,7 @@ define([ | |||
this.onToggleEventsVisibility(config.showEvents); | |||
}, | |||
onRezone: function () { | |||
clear: function () { | |||
this.list = []; | |||
this.el.find('.list').empty(); | |||
}, | |||
@@ -58,8 +58,10 @@ define([ | |||
}, | |||
onCharSelect: function () { | |||
renderer.clean(); | |||
objects.onRezone(); | |||
events.emit('destroyAllObjects'); | |||
events.emit('resetRenderer'); | |||
events.emit('resetPhysics'); | |||
renderer.buildTitleScreen(); | |||
sound.unload(); | |||
@@ -25,7 +25,7 @@ define([ | |||
this.find('.btnCollapse').on('click', this.toggleButtons.bind(this)); | |||
} | |||
this.onEvent('onRezone', this.onRezone.bind(this)); | |||
this.onEvent('clearUis', this.clear.bind(this)); | |||
this.onEvent('onObtainQuest', this.onObtainQuest.bind(this)); | |||
this.onEvent('onUpdateQuest', this.onUpdateQuest.bind(this)); | |||
@@ -35,7 +35,7 @@ define([ | |||
this.onToggleQuestsVisibility(config.showQuests); | |||
}, | |||
onRezone: function () { | |||
clear: function () { | |||
this.quests = []; | |||
this.el.find('.list').empty(); | |||
}, | |||
@@ -18,11 +18,7 @@ define([ | |||
postRender: function () { | |||
this.onEvent('onGetTalk', this.onGetTalk.bind(this)); | |||
this.onEvent('onRezone', this.onRezone.bind(this)); | |||
}, | |||
onRezone: function () { | |||
this.hide(); | |||
this.onEvent('clearUis', this.hide.bind(this)); | |||
}, | |||
onGetTalk: function (dialogue) { | |||
@@ -58,7 +58,9 @@ | |||
"leaderboard": false, | |||
"clientConfig": false, | |||
"random": false, | |||
"consts": false | |||
"consts": false, | |||
"rezoneManager": false, | |||
"eventManager": false | |||
}, | |||
"rules": { | |||
@@ -13,7 +13,6 @@ define([ | |||
init: function () { | |||
this.obj.on('onKeyDown', this.onKeyDown.bind(this)); | |||
this.hookEvent('onRezone', this.onRezone.bind(this)); | |||
}, | |||
extend: function (msg) { | |||
@@ -53,13 +52,6 @@ define([ | |||
} | |||
}, | |||
onRezone: function () { | |||
this.extend({ | |||
progress: 100, | |||
action: 'Fishing' | |||
}); | |||
}, | |||
onKeyDown: function (key) { | |||
if (key !== 'g') | |||
return; | |||
@@ -27,9 +27,9 @@ define([ | |||
lastY: 0, | |||
init: function () { | |||
events.on('onRespawn', this.onDeath.bind(this)); | |||
events.on('onDeath', this.onDeath.bind(this)); | |||
events.on('onClearQueue', this.onDeath.bind(this)); | |||
events.on('teleportToPosition', this.resetPath.bind(this)); | |||
events.on('onDeath', this.resetPath.bind(this)); | |||
events.on('onClearQueue', this.resetPath.bind(this)); | |||
this.pathPos.x = round(this.obj.x); | |||
this.pathPos.y = round(this.obj.y); | |||
@@ -46,7 +46,7 @@ define([ | |||
this.path = []; | |||
}, | |||
onDeath: function () { | |||
resetPath: function () { | |||
this.clearPath(); | |||
this.pathPos.x = round(this.obj.x); | |||
@@ -29,7 +29,7 @@ define([ | |||
obj.addComponent('serverActions'); | |||
obj.addComponent('pather'); | |||
this.hookEvent('onRespawn', this.onRespawn.bind(this)); | |||
this.hookEvent('teleportToPosition', this.teleportToPosition.bind(this)); | |||
events.emit('onGetPortrait', obj.portrait); | |||
}, | |||
@@ -74,7 +74,7 @@ define([ | |||
}, instant); | |||
}, | |||
onRespawn: function ({ x, y }) { | |||
teleportToPosition: function ({ x, y }) { | |||
this.positionCamera(x, y, true); | |||
sound.update(x, y); | |||
}, | |||
@@ -235,7 +235,7 @@ module.exports = { | |||
obj.instance.physics.addObject(obj, obj.x, obj.y); | |||
obj.instance.syncer.queue('onRespawn', { | |||
obj.instance.syncer.queue('teleportToPosition', { | |||
x: obj.x, | |||
y: obj.y | |||
}, [obj.serverId]); | |||
@@ -1,9 +1,9 @@ | |||
const sendObjToZone = async ({ obj, invokingObj, zoneName, toPos, toRelativePos }) => { | |||
const { serverId, instance: { physics, syncer: globalSyncer } } = obj; | |||
globalSyncer.flushForTarget(serverId); | |||
const { serverId, instance: { syncer: globalSyncer, physics } } = obj; | |||
if (obj.zoneName === zoneName) { | |||
globalSyncer.flushForTarget(serverId); | |||
physics.removeObject(obj, obj.x, obj.y); | |||
if (toRelativePos) { | |||
@@ -18,7 +18,7 @@ const sendObjToZone = async ({ obj, invokingObj, zoneName, toPos, toRelativePos | |||
physics.addObject(obj, obj.x, obj.y); | |||
globalSyncer.queue('onRespawn', { | |||
globalSyncer.queue('teleportToPosition', { | |||
x: obj.x, | |||
y: obj.y | |||
}, [obj.serverId]); | |||
@@ -26,28 +26,41 @@ const sendObjToZone = async ({ obj, invokingObj, zoneName, toPos, toRelativePos | |||
return; | |||
} | |||
obj.fireEvent('beforeRezone'); | |||
obj.destroyed = true; | |||
//We set this before saving so that objects aren't saved ON portals | |||
obj.zoneName = zoneName; | |||
if (toPos) { | |||
obj.x = toPos.x; | |||
obj.y = toPos.y; | |||
} else if (toRelativePos) { | |||
obj.x = invokingObj.obj.x + toRelativePos.x; | |||
obj.y = invokingObj.obj.y + toRelativePos.y; | |||
} | |||
//Destroy, flush events and notify other objects | |||
globalSyncer.processDestroyedObject(obj); | |||
await obj.auth.doSave(); | |||
//Test code, remove later | |||
Object.entries(globalSyncer.buffer).forEach(([k, v]) => { | |||
v.forEach(e => { | |||
if (e.to.includes(serverId)) { | |||
/* eslint-disable-next-line */ | |||
console.log('Found event', k, 'for rezoning object'); | |||
} | |||
}); | |||
}); | |||
const simpleObj = obj.getSimple(true, false, true); | |||
if (toPos) { | |||
simpleObj.x = toPos.x; | |||
simpleObj.y = toPos.y; | |||
} else if (toRelativePos) { | |||
simpleObj.x = invokingObj.obj.x + toRelativePos.x; | |||
simpleObj.y = invokingObj.obj.y + toRelativePos.y; | |||
} | |||
rezoneManager.stageRezone(simpleObj, zoneName); | |||
process.send({ | |||
method: 'rezone', | |||
id: obj.serverId, | |||
args: { | |||
obj: simpleObj, | |||
newZone: zoneName | |||
method: 'events', | |||
data: { | |||
rezoneStart: [{ | |||
obj: { msg: {} }, | |||
to: [serverId] | |||
}] | |||
} | |||
}); | |||
}; | |||
@@ -3,6 +3,9 @@ const sendObjToZone = require('../portal/sendObjToZone'); | |||
module.exports = (cpnSocial, targetZone) => { | |||
const { obj } = cpnSocial; | |||
if (obj.zoneName === targetZone) | |||
return; | |||
sendObjToZone({ | |||
obj, | |||
zoneName: targetZone | |||
@@ -1,6 +1,6 @@ | |||
module.exports = { | |||
init: function (event) { | |||
const { config, rewards, eventManager } = event; | |||
const { config, rewards, eventManager: eManager } = event; | |||
const { name: eventName, rewardSenderName } = config; | |||
@@ -26,7 +26,7 @@ module.exports = { | |||
}); | |||
if ((config.events) && (config.events.afterGiveRewards)) | |||
config.events.afterGiveRewards(eventManager, config); | |||
config.events.afterGiveRewards(eManager, config); | |||
this.end = true; | |||
} | |||
@@ -203,64 +203,80 @@ module.exports = { | |||
return newO; | |||
}, | |||
sendEvent: function (msg) { | |||
let player = this.objects.find(p => p.id === msg.id); | |||
if (!player) | |||
sendEvent: function (msg, { name: sourceZone }) { | |||
const { id, data } = msg; | |||
const player = this.objects.find(p => p.id === id); | |||
if (!player || player.zoneName !== sourceZone) | |||
return; | |||
player.socket.emit('event', { | |||
event: msg.data.event, | |||
data: msg.data.data | |||
event: data.event, | |||
data: data.data | |||
}); | |||
}, | |||
sendEvents: function (msg) { | |||
let players = {}; | |||
let objects = this.objects; | |||
let data = msg.data; | |||
sendEvents: function ({ data }, { name: sourceZone }) { | |||
const { objects } = this; | |||
//Store will contain all events to be sent to players | |||
const store = {}; | |||
for (let e in data) { | |||
let event = data[e]; | |||
let eLen = event.length; | |||
const event = data[e]; | |||
const eLen = event.length; | |||
for (let j = 0; j < eLen; j++) { | |||
let eventEntry = event[j]; | |||
let obj = eventEntry.obj; | |||
if (e !== 'serverModule') { | |||
let to = eventEntry.to; | |||
let toLen = to.length; | |||
for (let i = 0; i < toLen; i++) { | |||
let toId = to[i]; | |||
let player = players[toId]; | |||
if (!player) { | |||
let findPlayer = objects.find(o => o.id === toId); | |||
if (!findPlayer) | |||
continue; | |||
else { | |||
player = (players[toId] = { | |||
socket: findPlayer.socket, | |||
events: {} | |||
}); | |||
} | |||
} | |||
let eventList = player.events[e] || (player.events[e] = []); | |||
eventList.push(obj); | |||
const eventEntry = event[j]; | |||
const { obj: eventObj, to } = eventEntry; | |||
if (e === 'serverModule') { | |||
const { method, msg } = eventObj; | |||
if (Array.isArray(msg)) | |||
global[eventObj.module][method](...msg); | |||
else | |||
global[eventObj.module][method](msg); | |||
continue; | |||
} | |||
const toLen = to.length; | |||
for (let i = 0; i < toLen; i++) { | |||
const toId = to[i]; | |||
let storeEntry = store[toId]; | |||
if (!storeEntry) { | |||
const playerObj = objects.find(o => o.id === toId); | |||
if (!playerObj || playerObj.zoneName !== sourceZone) | |||
continue; | |||
store[toId] = { | |||
obj: playerObj, | |||
events: { [e]: [eventObj] } | |||
}; | |||
continue; | |||
} | |||
} else if (obj.msg instanceof Array) | |||
global[obj.module][obj.method](...obj.msg); | |||
else | |||
global[obj.module][obj.method](obj.msg); | |||
if (!storeEntry.events[e]) | |||
storeEntry.events[e] = []; | |||
storeEntry.events[e].push(eventObj); | |||
} | |||
} | |||
} | |||
for (let p in players) { | |||
let player = players[p]; | |||
player.socket.emit('events', player.events); | |||
for (let p in store) { | |||
const { obj: { socket }, events } = store[p]; | |||
socket.emit('events', events); | |||
} | |||
}, | |||
updateObject: async function (msg) { | |||
let player = this.objects.find(p => p.id === msg.serverId); | |||
if (!player) | |||
@@ -324,6 +340,14 @@ module.exports = { | |||
if ((o.update) && (!o.destroyed)) | |||
o.update(); | |||
//When objects are sent to other zones, we destroy them immediately (thhrough sendObjToZone) | |||
// In these cases, we DO need to remove it | |||
if (o.forceDestroy) { | |||
i--; | |||
len--; | |||
continue; | |||
} | |||
if (o.ttl) { | |||
o.ttl--; | |||
if (!o.ttl) | |||
@@ -24,7 +24,9 @@ const routerConfig = { | |||
globalAllowed: { | |||
clientConfig: ['getClientConfig'], | |||
leaderboard: ['requestList'], | |||
cons: ['unzone'] | |||
cons: ['unzone'], | |||
rezoneManager: ['clientAck'], | |||
instancer: ['clientAck'] | |||
}, | |||
allowTargetId: { | |||
door: ['lock', 'unlock'], | |||
@@ -174,11 +174,11 @@ module.exports = { | |||
}, | |||
event: function (thread, message) { | |||
objects.sendEvent(message); | |||
objects.sendEvent(message, thread); | |||
}, | |||
events: function (thread, message) { | |||
objects.sendEvents(message); | |||
objects.sendEvents(message, thread); | |||
}, | |||
object: function (thread, message) { | |||
@@ -13,6 +13,9 @@ let eventEmitter = require('../misc/events'); | |||
const mods = require('../misc/mods'); | |||
const transactions = require('../security/transactions'); | |||
//Own helpers | |||
const { stageZoneIn, unstageZoneIn, clientAck } = require('./instancer/handshakes'); | |||
module.exports = { | |||
instances: [], | |||
zoneId: -1, | |||
@@ -65,6 +68,9 @@ module.exports = { | |||
[resourceSpawner, syncer, objects, questBuilder, events].forEach(i => i.init(fakeInstance)); | |||
this.tick(); | |||
this.clientAck = clientAck; | |||
eventEmitter.on('removeObject', unstageZoneIn); | |||
}, | |||
startRegen: function (respawnMap, respawnPos) { | |||
@@ -211,15 +217,17 @@ module.exports = { | |||
obj.spawn = map.spawn; | |||
syncer.queue('onGetMap', map.clientMap, [obj.serverId]); | |||
stageZoneIn(msg); | |||
if (!msg.transfer) | |||
objects.addObject(obj, this.onAddObject.bind(this)); | |||
else { | |||
let o = objects.transferObject(obj); | |||
questBuilder.obtain(o); | |||
eventEmitter.emit('onAfterPlayerEnterZone', o); | |||
} | |||
process.send({ | |||
method: 'events', | |||
data: { | |||
getMap: [{ | |||
obj: map.clientMap, | |||
to: [obj.serverId] | |||
}] | |||
} | |||
}); | |||
}, | |||
onAddObject: function (obj) { | |||
@@ -302,6 +310,10 @@ module.exports = { | |||
return; | |||
} | |||
//We fire this event because even though an object might be destroyed already, | |||
// mods and modules might have staged events/actions we need to clear | |||
eventEmitter.emit('removeObject', { obj: msg.obj }); | |||
let obj = msg.obj; | |||
obj = objects.find(o => o.serverId === obj.id); | |||
if (!obj) { | |||
@@ -0,0 +1,49 @@ | |||
//Local State | |||
const stagedZoneIns = []; | |||
//Methods | |||
//Fired when an object is removed through a socket dc | |||
// We do this because a client might DC during rezone handshake | |||
const unstageZoneIn = msg => { | |||
stagedZoneIns.spliceWhere(s => s.obj.serverId === msg.obj.id); | |||
}; | |||
const stageZoneIn = msg => { | |||
const { serverId } = msg.obj; | |||
stagedZoneIns.spliceWhere(o => o.obj.serverId === serverId); | |||
stagedZoneIns.push(msg); | |||
}; | |||
const doZoneIn = function (staged) { | |||
const { onAddObject, instances: [ { objects, questBuilder, eventEmitter } ] } = instancer; | |||
const { transfer, obj } = staged; | |||
if (!transfer) | |||
objects.addObject(obj, onAddObject.bind(instancer)); | |||
else { | |||
let o = objects.transferObject(obj); | |||
questBuilder.obtain(o); | |||
eventEmitter.emit('onAfterPlayerEnterZone', o); | |||
} | |||
}; | |||
const clientAck = msg => { | |||
const staged = stagedZoneIns.find(s => s.obj.serverId === msg.sourceId); | |||
if (!staged) | |||
return; | |||
stagedZoneIns.spliceWhere(s => s === staged); | |||
doZoneIn(staged); | |||
}; | |||
//Exports | |||
module.exports = { | |||
unstageZoneIn, | |||
stageZoneIn, | |||
clientAck | |||
}; |
@@ -0,0 +1,55 @@ | |||
//Imports | |||
const eventEmitter = require('../misc/events'); | |||
//Local State | |||
const stagedRezones = []; | |||
//Methods | |||
//Fired when an object is removed through a socket dc | |||
// We do this because a client might DC during rezone handshake | |||
const unstageRezone = msg => { | |||
stagedRezones.spliceWhere(s => s.simplifiedObj.serverId === msg.obj.id); | |||
}; | |||
const stageRezone = (simplifiedObj, targetZone) => { | |||
const { serverId } = simplifiedObj; | |||
stagedRezones.spliceWhere(o => o.simplifiedObj.serverId === serverId); | |||
stagedRezones.push({ simplifiedObj, targetZone }); | |||
}; | |||
const doRezone = stagedRezone => { | |||
const { simplifiedObj, targetZone } = stagedRezone; | |||
process.send({ | |||
method: 'rezone', | |||
id: simplifiedObj.serverId, | |||
args: { | |||
obj: simplifiedObj, | |||
newZone: targetZone | |||
} | |||
}); | |||
}; | |||
const clientAck = msg => { | |||
const staged = stagedRezones.find(s => s.simplifiedObj.serverId === msg.sourceId); | |||
if (!staged) | |||
return; | |||
stagedRezones.spliceWhere(s => s === staged); | |||
doRezone(staged); | |||
}; | |||
const init = () => { | |||
eventEmitter.on('removeObject', unstageRezone); | |||
}; | |||
//Exports | |||
module.exports = { | |||
init, | |||
stageRezone, | |||
clientAck | |||
}; |
@@ -60,7 +60,6 @@ module.exports = { | |||
this.syncer.queue('onGetObject', { | |||
id: obj.id, | |||
performLast: true, | |||
components: [spawnAnimation] | |||
}, -1); | |||
} | |||
@@ -180,6 +180,35 @@ module.exports = { | |||
} | |||
}, | |||
processDestroyedObject: function (obj) { | |||
const { objects, queue } = this; | |||
const { id, serverId } = obj; | |||
obj.destroyed = true; | |||
//We mark forceDestroy to tell objects that we're destroying an object outside of the | |||
// syncer's update method | |||
obj.forceDestroy = true; | |||
const msg = { | |||
id: id, | |||
destroyed: true | |||
}; | |||
objects.removeObject(obj); | |||
this.flushForTarget(serverId); | |||
const fnQueueMsg = queue.bind(this, 'onGetObject'); | |||
//Find any players that have seen this obj | |||
objects | |||
.filter(o => !o.destroyed && o?.player?.hasSeen(id)) | |||
.forEach(o => { | |||
fnQueueMsg(msg); | |||
}); | |||
}, | |||
send: function () { | |||
if (!this.dirty) | |||
return; | |||
@@ -5,6 +5,7 @@ global.consts = require('../config/consts'); | |||
global.instancer = require('./instancer'); | |||
global.eventManager = require('../events/events'); | |||
global.clientConfig = require('../config/clientConfig'); | |||
global.rezoneManager = require('./rezoneManager'); | |||
const components = require('../components/components'); | |||
const mods = require('../misc/mods'); | |||
@@ -33,6 +34,8 @@ let onCpnsReady = async function () { | |||
recipes.init(); | |||
itemEffects.init(); | |||
profanities.init(); | |||
rezoneManager.init(); | |||
await clientConfig.init(); | |||
process.send({ | |||