@@ -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,54 @@ | |||
const sendObjToZone = async ({ obj, invokingObj, zoneName, toPos, toRelativePos }) => { | |||
const { serverId, instance: { physics, syncer: globalSyncer } } = obj; | |||
/* | |||
atlas.onMessage.event / events -> only send if source zone = obj zoneName | |||
SERVER SIDE | |||
1. Change zone and position | |||
2. Save | |||
3. Syncer: Destroy, Flush and Notify other objects of destroyed obj | |||
4. Log IN CASE (if new events are queued) | |||
5. Stage rezone | |||
6. Tell client rezone is happening | |||
CLIENT SIDE | |||
events.emit('rezoneStart'); | |||
events.emit('destroyAllObjects'); | |||
events.emit('resetRenderer'); | |||
events.emit('resetPhysics'); | |||
events.emit('clearUis'); | |||
client.request({ | |||
threadModule: 'rezoneManager', | |||
method: 'clientAck', | |||
data: {} | |||
}); | |||
globalSyncer.flushForTarget(serverId); | |||
SERVER SIDE | |||
7. Server receives ack | |||
8. Map thread tells main thread about rezone | |||
9. Main thread does rezone | |||
10. New map thread registers handshake for map send | |||
11. New map thread sends new map | |||
CLIENT SIDE | |||
events.emit('onGetMap', msg); | |||
client.request({ | |||
threadModule: 'instancer', | |||
method: 'clientAck', | |||
data: {} | |||
}); | |||
SERVER SIDE | |||
12. Add object to zone | |||
*/ | |||
const sendObjToZone = async ({ obj, invokingObj, zoneName, toPos, toRelativePos }) => { | |||
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 +63,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 +71,39 @@ 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; | |||
} | |||
await obj.auth.doSave(); | |||
const simpleObj = obj.getSimple(true, false, true); | |||
//Destroy, flush events and notify other objects | |||
globalSyncer.processDestroyedObject(obj); | |||
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; | |||
//Test code, remove later | |||
const queued = Object.values(globalSyncer.buffer).filter(b => b.to.includes(serverId)); | |||
if (queued.length) { | |||
/* eslint-disable-next-line */ | |||
console.log('Found', queued.length, 'events for rezoning object'); | |||
} | |||
const simpleObj = obj.getSimple(true, false, true); | |||
rezoneManager.stageRezone(simpleObj, zoneName); | |||
process.send({ | |||
method: 'rezone', | |||
id: obj.serverId, | |||
args: { | |||
obj: simpleObj, | |||
newZone: zoneName | |||
method: 'events', | |||
data: { | |||
rezoneStart: [{ | |||
obj: { msg: {} }, | |||
to: [serverId] | |||
}] | |||
} | |||
}); | |||
}; | |||
@@ -203,7 +203,7 @@ module.exports = { | |||
return newO; | |||
}, | |||
sendEvent: function (msg) { | |||
sendEvent: function (msg, sourceThread) { | |||
let player = this.objects.find(p => p.id === msg.id); | |||
if (!player) | |||
return; | |||
@@ -213,7 +213,7 @@ module.exports = { | |||
data: msg.data.data | |||
}); | |||
}, | |||
sendEvents: function (msg) { | |||
sendEvents: function (msg, sourceThread) { | |||
let players = {}; | |||
let objects = this.objects; | |||
@@ -239,13 +239,22 @@ module.exports = { | |||
if (!findPlayer) | |||
continue; | |||
else { | |||
//If the message came from a map the player is no longer in, ignore | |||
if (findPlayer.zoneName !== sourceThread.name) | |||
continue; | |||
player = (players[toId] = { | |||
socket: findPlayer.socket, | |||
events: {} | |||
events: {}, | |||
obj: findPlayer | |||
}); | |||
} | |||
} | |||
//If the message came from a map the player is no longer in, ignore | |||
if (player.obj.zoneName !== sourceThread.name) | |||
continue; | |||
let eventList = player.events[e] || (player.events[e] = []); | |||
eventList.push(obj); | |||
} | |||
@@ -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 | |||
}; |
@@ -180,12 +180,38 @@ module.exports = { | |||
} | |||
}, | |||
processDestroyedObject: function (obj) { | |||
const { objects, queue } = this; | |||
const { id, serverId } = obj; | |||
obj.destroyed = true; | |||
this.flushForTarget(serverId); | |||
const msg = { | |||
id: id, | |||
destroyed: true | |||
}; | |||
objects.removeObject(obj); | |||
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; | |||
this.dirty = false; | |||
console.log(Object.keys(this.buffer)); | |||
process.send({ | |||
method: 'events', | |||
data: this.buffer | |||
@@ -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({ | |||