From bcd5b36e77961e33bc9da177dfca890cf1c490da Mon Sep 17 00:00:00 2001 From: cha0s Date: Sun, 14 Mar 2021 03:07:13 -0500 Subject: [PATCH] refactor: Synchronization --- packages/combat/src/traits/vulnerable.js | 3 +- packages/core/src/hooks/use-room.js | 6 +- packages/core/src/traits/informed.js | 90 ++++++++++++--------- packages/farm/src/traits/plant.js | 2 +- packages/inventory/src/traits/receptacle.js | 3 +- packages/universe/src/client/index.js | 6 +- packages/universe/src/client/receiver.js | 23 ++++++ packages/universe/src/player.js | 4 + packages/universe/src/resources/universe.js | 9 ++- 9 files changed, 99 insertions(+), 47 deletions(-) create mode 100644 packages/universe/src/client/receiver.js diff --git a/packages/combat/src/traits/vulnerable.js b/packages/combat/src/traits/vulnerable.js index bfabe7e..ab28399 100644 --- a/packages/combat/src/traits/vulnerable.js +++ b/packages/combat/src/traits/vulnerable.js @@ -82,6 +82,7 @@ export default (latus) => class Vulnerable extends Trait { } destroy() { + super.destroy(); this.#locks.clear(); } @@ -389,7 +390,7 @@ export default (latus) => class Vulnerable extends Trait { }; } - packets() { + packetsFor() { return this.#harms.length > 0 ? [['Harm', this.#harms.map((harm) => ({ ...harm, diff --git a/packages/core/src/hooks/use-room.js b/packages/core/src/hooks/use-room.js index 5aaf24f..6974396 100644 --- a/packages/core/src/hooks/use-room.js +++ b/packages/core/src/hooks/use-room.js @@ -15,7 +15,7 @@ export default () => { return undefined; } const onDisconnect = () => { - synchronizer.deleteSynchronized(room.constructor.resourceId, room.s13nId()); + synchronizer.destroySynchronized(room.constructor.resourceId, room.s13nId); setRoom(undefined); }; socket.on('disconnect', onDisconnect); @@ -27,9 +27,9 @@ export default () => { if (!synchronizer) { return undefined; } - const onCreated = (type, created) => { + const onCreated = (created) => { const {Room} = latus.get('%resources'); - switch (type) { + switch (created.constructor.resourceId) { // Track room. case Room.resourceId: { setRoom(created); diff --git a/packages/core/src/traits/informed.js b/packages/core/src/traits/informed.js index d75f5ba..c52c319 100644 --- a/packages/core/src/traits/informed.js +++ b/packages/core/src/traits/informed.js @@ -1,52 +1,68 @@ +/* eslint-disable max-classes-per-file */ + import {Rectangle, Vector} from '@avocado/math'; +import {Resource} from '@avocado/resource'; +import {Synchronized} from '@avocado/s13n'; import {Trait} from '@avocado/traits'; -import {SenderSynchronizer} from '@avocado/s13n'; +import {compose} from '@latus/core'; -export default (latus) => class Informed extends Trait { +export default (latus) => { + const decorate = compose( + Synchronized(latus), + ); + class Synchronizer extends decorate(Resource) {} + return class Informed extends Trait { - #synchronizer = new SenderSynchronizer(latus); + #synchronizer = new Synchronizer(); - static dependencies() { - return [ - 'Followed', - ]; - } + static dependencies() { + return [ + 'Followed', + ]; + } - destroy() { - this.#synchronizer.destroy(); - } + destroy() { + super.destroy(); + this.#synchronizer.destroy(); + } - get areaToInform() { - // Reduce entity list to visible. - const {camera} = this.entity; - // Blow up camera rectangle to compensate for camera desync. - const size = Rectangle.size(camera.rectangle); - return Rectangle.expand( - camera.rectangle, - Vector.scale(size, 0.5), - ); - } + get areaToInform() { + // Reduce entity list to visible. + const {camera} = this.entity; + // Blow up camera rectangle to compensate for camera desync. + const size = Rectangle.size(camera.rectangle); + return Rectangle.expand( + camera.rectangle, + Vector.scale(size, 0.5), + ); + } - methods() { - return { + methods() { + return { - inform: async (socket) => { - this.#synchronizer.send(socket, this.entity); - }, + cleanInformingPackets: () => { + this.#synchronizer.cleanPackets(); + }, - addSynchronized: (synchronized) => { - this.#synchronizer.addSynchronized(synchronized); - }, + inform: async (socket) => { + if (socket) { + const packets = this.#synchronizer.packetsFor(this.entity); + if (packets.length > 0) { + await socket.send(['Bundle', packets]); + } + } + }, - queuePacket: (packet) => { - this.#synchronizer.queuePacket(packet); - }, + startInforming: (synchronized) => { + this.#synchronizer.startSynchronizing(synchronized); + }, - removeSynchronized: (synchronized) => { - this.#synchronizer.removeSynchronized(synchronized); - }, + stopInforming: (synchronized) => { + this.#synchronizer.stopSynchronizing(synchronized); + }, - }; - } + }; + } + }; }; diff --git a/packages/farm/src/traits/plant.js b/packages/farm/src/traits/plant.js index 61f5a60..5fbcfac 100644 --- a/packages/farm/src/traits/plant.js +++ b/packages/farm/src/traits/plant.js @@ -111,7 +111,7 @@ export default (latus) => class Plant extends decorate(Trait) { }; } - packets() { + packetsFor() { const {growthStage} = this.stateDifferences(); return growthStage ? [[ diff --git a/packages/inventory/src/traits/receptacle.js b/packages/inventory/src/traits/receptacle.js index d35027a..0044cec 100644 --- a/packages/inventory/src/traits/receptacle.js +++ b/packages/inventory/src/traits/receptacle.js @@ -147,6 +147,7 @@ export default (latus) => class Receptacle extends decorate(Trait) { } destroy() { + super.destroy(); for (let i = 0; i < this.entity.slotCount; ++i) { const item = this.entity.removeItemFromSlot(i); if (item) { @@ -292,7 +293,7 @@ export default (latus) => class Receptacle extends decorate(Trait) { }; } - packets(informed) { + packetsFor(informed) { return informed === this.entity ? this.packetUpdates : []; } diff --git a/packages/universe/src/client/index.js b/packages/universe/src/client/index.js index 10ac5cf..98c639d 100644 --- a/packages/universe/src/client/index.js +++ b/packages/universe/src/client/index.js @@ -1,11 +1,13 @@ -import {ReceiverSynchronizer} from '@avocado/s13n'; import {gatherWithLatus} from '@latus/core'; +import Receiver from './receiver'; + export default { hooks: { '@latus/http/client/up': (latus) => { window.latus = latus; - const synchronizer = new ReceiverSynchronizer(latus); + const Synchronizer = Receiver(latus); + const synchronizer = new Synchronizer(); latus.set('%synchronizer', synchronizer); latus.get('%socket').on('packet', synchronizer.acceptPacket, synchronizer); }, diff --git a/packages/universe/src/client/receiver.js b/packages/universe/src/client/receiver.js new file mode 100644 index 0000000..08436e1 --- /dev/null +++ b/packages/universe/src/client/receiver.js @@ -0,0 +1,23 @@ +import {Resource} from '@avocado/resource'; +import {Synchronized} from '@avocado/s13n'; +import {compose, EventEmitter} from '@latus/core'; + +export default (latus) => { + const decorate = compose( + EventEmitter, + Synchronized(latus), + ); + return class Receiver extends decorate(Resource) { + + async acceptPacket(packet) { + await super.acceptPacket(packet); + const {s13nType} = packet; + if ('create' === s13nType) { + const {Room} = latus.get('%resources'); + const {id} = packet.data.synchronized; + this.emit('created', this.synchronized(Room.resourceId, id)); + } + } + + }; +}; diff --git a/packages/universe/src/player.js b/packages/universe/src/player.js index bfd8cc0..6cbdb61 100644 --- a/packages/universe/src/player.js +++ b/packages/universe/src/player.js @@ -6,6 +6,10 @@ export default class Player { this.user = user; } + cleanInformingPackets() { + this.entity.cleanInformingPackets(); + } + inform() { this.entity.inform(this.socket); } diff --git a/packages/universe/src/resources/universe.js b/packages/universe/src/resources/universe.js index e86b425..4ac01b0 100644 --- a/packages/universe/src/resources/universe.js +++ b/packages/universe/src/resources/universe.js @@ -26,7 +26,7 @@ export default (latus) => class Universe extends JsonResource { addPlayer({entity, socket, user}) { const room = this.room(entity.currentRoom); room.addEntityToLayer(entity, 0); - entity.addSynchronized(room); + entity.startInforming(room); const player = new Player({entity, socket, user}); this.#players.push(player); return player; @@ -41,6 +41,7 @@ export default (latus) => class Universe extends JsonResource { super.load(json); const {tps = 60, uri} = json; const {Room} = latus.get('%resources'); + // console.log(Object.getPrototypeOf(Object.getPrototypeOf(Room)).toString()); if (uri) { const universePath = dirname(uri); await Promise.all( @@ -72,10 +73,14 @@ export default (latus) => class Universe extends JsonResource { for (let i = 0; i < this.#players.length; ++i) { promises.push(this.#players[i].inform()); } + // TODO: rogue client? await Promise.all(promises); for (let i = 0; i < this.#roomsFlat.length; i++) { this.#roomsFlat[i].cleanPackets(); } + for (let i = 0; i < this.#players.length; ++i) { + this.#players[i].cleanInformingPackets(); + } } static async load(json = {}) { @@ -88,7 +93,7 @@ export default (latus) => class Universe extends JsonResource { removePlayer(player) { const {entity} = player; const room = this.room(entity.currentRoom); - entity.removeSynchronized(room); + entity.stopInforming(room); room.removeEntityFromLayer(entity, 0); const index = this.#players.indexOf(player); if (-1 !== index) {