flow: massive sync update
This commit is contained in:
parent
4534d0b5ea
commit
631e07e4c6
104
client/app.js
104
client/app.js
|
@ -7,7 +7,13 @@ import {Stage} from '@avocado/graphics';
|
||||||
import {ActionRegistry, InputPacket} from '@avocado/input';
|
import {ActionRegistry, InputPacket} from '@avocado/input';
|
||||||
import {Vector} from '@avocado/math';
|
import {Vector} from '@avocado/math';
|
||||||
import {SocketClient} from '@avocado/net/client/socket';
|
import {SocketClient} from '@avocado/net/client/socket';
|
||||||
import {BundlePacket, ClientSynchronizer, SocketIoParser} from '@avocado/net';
|
import {
|
||||||
|
BundlePacket,
|
||||||
|
ClientSynchronizer,
|
||||||
|
idFromSynchronized,
|
||||||
|
SocketIoParser,
|
||||||
|
SynchronizedCreatePacket,
|
||||||
|
} from '@avocado/net';
|
||||||
import {clearAnimation, setAnimation} from '@avocado/timing';
|
import {clearAnimation, setAnimation} from '@avocado/timing';
|
||||||
import {World} from '@avocado/physics/matter/world';
|
import {World} from '@avocado/physics/matter/world';
|
||||||
import {Room, RoomView} from '@avocado/topdown';
|
import {Room, RoomView} from '@avocado/topdown';
|
||||||
|
@ -59,7 +65,7 @@ export class App extends decorate(class {}) {
|
||||||
super();
|
super();
|
||||||
const config = this.readConfig();
|
const config = this.readConfig();
|
||||||
// Room.
|
// Room.
|
||||||
this.room = new Room();
|
this.room = null;
|
||||||
// World time.
|
// World time.
|
||||||
this.worldTime = new WorldTime();
|
this.worldTime = new WorldTime();
|
||||||
// Graphics.
|
// Graphics.
|
||||||
|
@ -70,13 +76,10 @@ export class App extends decorate(class {}) {
|
||||||
this.renderHandle = undefined;
|
this.renderHandle = undefined;
|
||||||
this.rps = new CycleTracker(1 / 60); // Refresh rate, actually.
|
this.rps = new CycleTracker(1 / 60); // Refresh rate, actually.
|
||||||
this.stage = new Stage(config.visibleSize, config.visibleScale);
|
this.stage = new Stage(config.visibleSize, config.visibleScale);
|
||||||
this.roomView = new RoomView(this.room, this.stage.renderer);
|
this.roomView = null;
|
||||||
this.stage.addChild(this.roomView);
|
|
||||||
this.on('darknessChanged', this.applyLighting, this);
|
this.on('darknessChanged', this.applyLighting, this);
|
||||||
this.on('isFocusedChanged', this.applyMuting, this)
|
this.on('isFocusedChanged', this.applyMuting, this)
|
||||||
this.on('isMenuOpenedChanged', this.applyMuting, this)
|
this.on('isMenuOpenedChanged', this.applyMuting, this)
|
||||||
// Listen for new entities.
|
|
||||||
this.room.on('entityAdded', this.onRoomEntityAdded, this);
|
|
||||||
// Input.
|
// Input.
|
||||||
this.actionRegistry = new ActionRegistry();
|
this.actionRegistry = new ActionRegistry();
|
||||||
this.actionRegistry.mapKeysToActions(config.actionKeyMap);
|
this.actionRegistry.mapKeysToActions(config.actionKeyMap);
|
||||||
|
@ -97,14 +100,49 @@ export class App extends decorate(class {}) {
|
||||||
this.tps = new CycleTracker(config.simulationFrequency);
|
this.tps = new CycleTracker(config.simulationFrequency);
|
||||||
this.simulationHandle = undefined;
|
this.simulationHandle = undefined;
|
||||||
this.world = config.doPhysicsSimulation ? new World() : undefined;
|
this.world = config.doPhysicsSimulation ? new World() : undefined;
|
||||||
this.room.world = this.world;
|
this.world.stepTime = config.simulationFrequency;
|
||||||
this.room.world.stepTime = config.simulationFrequency;
|
|
||||||
// State synchronization.
|
// State synchronization.
|
||||||
this.state = undefined;
|
this.state = undefined;
|
||||||
this.synchronizer = new ClientSynchronizer();
|
this.synchronizer = new ClientSynchronizer();
|
||||||
this.synchronizer.addSynchronized(this.worldTime);
|
this.synchronizer.addSynchronized(this.worldTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
acceptPacket(packet) {
|
||||||
|
this.synchronizer.acceptPacket(packet);
|
||||||
|
// Keep refs to new synchronizeds.
|
||||||
|
if (packet instanceof SynchronizedCreatePacket) {
|
||||||
|
const roomId = idFromSynchronized(Room);
|
||||||
|
const {type, id} = packet.data.synchronized;
|
||||||
|
switch (type) {
|
||||||
|
// Track room.
|
||||||
|
case roomId:
|
||||||
|
const room = this.synchronizer.synchronized(type, id);
|
||||||
|
this.onRoomCreated(room);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (packet instanceof SelfEntityPacket) {
|
||||||
|
// Set self entity.
|
||||||
|
const selfEntity = this.room.findEntity(packet.data);
|
||||||
|
if (selfEntity) {
|
||||||
|
this.selfEntity = selfEntity;
|
||||||
|
this.selfEntityUuid = undefined;
|
||||||
|
// Add back our self entity traits.
|
||||||
|
const selfEntityOnlyTraits = [
|
||||||
|
'controllable',
|
||||||
|
'followed',
|
||||||
|
];
|
||||||
|
for (const type of selfEntityOnlyTraits) {
|
||||||
|
selfEntity.addTrait(type);
|
||||||
|
}
|
||||||
|
const {camera} = selfEntity;
|
||||||
|
this.stage.camera = camera;
|
||||||
|
// Avoid the initial 'lerp.
|
||||||
|
camera.realPosition = camera.position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
applyLighting() {
|
applyLighting() {
|
||||||
const roomView = this.roomView;
|
const roomView = this.roomView;
|
||||||
if (!roomView) {
|
if (!roomView) {
|
||||||
|
@ -165,7 +203,7 @@ export class App extends decorate(class {}) {
|
||||||
});
|
});
|
||||||
this.socket.on('connect', () => {
|
this.socket.on('connect', () => {
|
||||||
this.removeFromDom(document.querySelector('.app'));
|
this.removeFromDom(document.querySelector('.app'));
|
||||||
this.room.layers.destroy();
|
this.room = null;
|
||||||
this.selfEntity = undefined;
|
this.selfEntity = undefined;
|
||||||
this.selfEntityUuid = undefined;
|
this.selfEntityUuid = undefined;
|
||||||
this.isConnected = true;
|
this.isConnected = true;
|
||||||
|
@ -272,12 +310,11 @@ export class App extends decorate(class {}) {
|
||||||
this.hasReceivedState = true;
|
this.hasReceivedState = true;
|
||||||
}
|
}
|
||||||
if (packet instanceof BundlePacket) {
|
if (packet instanceof BundlePacket) {
|
||||||
return this.synchronizer.acceptPackets(packet.data);
|
for (let i = 0; i < packet.data.length; i++) {
|
||||||
|
this.onPacket(packet.data[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (packet instanceof SelfEntityPacket) {
|
this.acceptPacket(packet);
|
||||||
this.selfEntityUuid = packet.data;
|
|
||||||
}
|
|
||||||
this.synchronizer.acceptPackets([packet]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onPointerDown(event) {
|
onPointerDown(event) {
|
||||||
|
@ -301,6 +338,26 @@ export class App extends decorate(class {}) {
|
||||||
this.pointingAt = [-1, -1];
|
this.pointingAt = [-1, -1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onRoomCreated(room) {
|
||||||
|
// Keep tabs on the room.
|
||||||
|
if (this.room) {
|
||||||
|
this.room.off('entityAdded', this.onRoomEntityAdded);
|
||||||
|
}
|
||||||
|
this.room = room;
|
||||||
|
// View.
|
||||||
|
this.stage.removeChild(this.roomView);
|
||||||
|
this.roomView = new RoomView(this.room, this.stage.renderer);
|
||||||
|
this.stage.addChild(this.roomView);
|
||||||
|
// Listen for new entities.
|
||||||
|
this.room.on('entityAdded', this.onRoomEntityAdded, this);
|
||||||
|
const allEntities = this.room.allEntities();
|
||||||
|
for (let i = 0; i < allEntities.length; i++) {
|
||||||
|
this.onRoomEntityAdded(allEntities[i]);
|
||||||
|
}
|
||||||
|
// Physics.
|
||||||
|
this.room.world = this.world;
|
||||||
|
}
|
||||||
|
|
||||||
onRoomEntityAdded(entity) {
|
onRoomEntityAdded(entity) {
|
||||||
// Traits that shouldn't be on client.
|
// Traits that shouldn't be on client.
|
||||||
const noClientTraits = [
|
const noClientTraits = [
|
||||||
|
@ -339,21 +396,6 @@ export class App extends decorate(class {}) {
|
||||||
entity.container.night(this.darkness);
|
entity.container.night(this.darkness);
|
||||||
}
|
}
|
||||||
entity.stage = this.stage;
|
entity.stage = this.stage;
|
||||||
// Set self entity.
|
|
||||||
if (this.selfEntityUuid) {
|
|
||||||
if (entity === this.room.findEntity(this.selfEntityUuid)) {
|
|
||||||
this.selfEntity = entity;
|
|
||||||
this.selfEntityUuid = undefined;
|
|
||||||
// Add back our self entity traits.
|
|
||||||
for (const type of selfEntityOnlyTraits) {
|
|
||||||
entity.addTrait(type);
|
|
||||||
}
|
|
||||||
const {camera} = entity;
|
|
||||||
this.stage.camera = camera;
|
|
||||||
// Avoid the initial 'lerp.
|
|
||||||
camera.realPosition = camera.position;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onWheel(event) {
|
onWheel(event) {
|
||||||
|
@ -570,7 +612,9 @@ export class App extends decorate(class {}) {
|
||||||
}
|
}
|
||||||
// Tick.
|
// Tick.
|
||||||
this.worldTime.tick(elapsed);
|
this.worldTime.tick(elapsed);
|
||||||
this.room.tick(elapsed);
|
if (this.room) {
|
||||||
|
this.room.tick(elapsed);
|
||||||
|
}
|
||||||
// Sample.
|
// Sample.
|
||||||
this.tps.sample(elapsed);
|
this.tps.sample(elapsed);
|
||||||
}, 1000 * config.simulationFrequency);
|
}, 1000 * config.simulationFrequency);
|
||||||
|
|
|
@ -60,9 +60,11 @@ export class Vulnerable extends Trait {
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrate() {
|
hydrate() {
|
||||||
this._isHydrating = true;
|
if (AVOCADO_CLIENT) {
|
||||||
this.addEmitter();
|
this._isHydrating = true;
|
||||||
this.addEmitterRenderer();
|
this.addEmitter();
|
||||||
|
this.addEmitterRenderer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
acceptDamage(damage) {
|
acceptDamage(damage) {
|
||||||
|
|
|
@ -79,13 +79,15 @@ export class Item extends decorate(Trait) {
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrate() {
|
hydrate() {
|
||||||
const promises = [];
|
if (AVOCADO_CLIENT) {
|
||||||
for (const index in this.params.slotImages) {
|
const promises = [];
|
||||||
promises.push(Image.load(this.params.slotImages[index]).then((image) => {
|
for (const index in this.params.slotImages) {
|
||||||
this._slotImages[index] = image;
|
promises.push(Image.load(this.params.slotImages[index]).then((image) => {
|
||||||
}));
|
this._slotImages[index] = image;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return Promise.all(promises);
|
||||||
}
|
}
|
||||||
return Promise.all(promises);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get itemActions() {
|
get itemActions() {
|
||||||
|
|
|
@ -45,16 +45,10 @@ export class Receptacle extends decorate(Trait) {
|
||||||
item.qty = slotSpec.qty;
|
item.qty = slotSpec.qty;
|
||||||
// Set wielder.
|
// Set wielder.
|
||||||
item.wielder = this.entity;
|
item.wielder = this.entity;
|
||||||
// On the client, hydrate the item before adding it to the inventory.
|
// Hydrate the item before adding it to the inventory.
|
||||||
if (AVOCADO_CLIENT) {
|
item.hydrate().then(() => {
|
||||||
item.hydrate().then(() => {
|
|
||||||
this.entity.addItemToSlot(item, index);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Server just adds it.
|
|
||||||
else {
|
|
||||||
this.entity.addItemToSlot(item, index);
|
this.entity.addItemToSlot(item, index);
|
||||||
}
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ export class WorldTime extends decorate(class {}) {
|
||||||
|
|
||||||
acceptPacket(packet) {
|
acceptPacket(packet) {
|
||||||
if (packet instanceof WorldTimePacket) {
|
if (packet instanceof WorldTimePacket) {
|
||||||
this.fromNetwork(packet.data.hour);
|
this.fromNetwork(packet.data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,14 +51,10 @@ export class WorldTime extends decorate(class {}) {
|
||||||
|
|
||||||
fromJSON(json) {
|
fromJSON(json) {
|
||||||
if (json.hour) {
|
if (json.hour) {
|
||||||
this._hour = this.fromNetwork(json.hour);
|
this._hour = json.hour / MAGIC_TO_FIT_HOUR_INTO_USHORT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fromNetwork(hour) {
|
|
||||||
this._hour = hour / MAGIC_TO_FIT_HOUR_INTO_USHORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hour() {
|
get hour() {
|
||||||
return this._hour;
|
return this._hour;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +67,7 @@ export class WorldTime extends decorate(class {}) {
|
||||||
if (!this._isUpdateReady) {
|
if (!this._isUpdateReady) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return new WorldTimePacket(this.toNetwork());
|
return new WorldTimePacket(this.toNetwork(informed));
|
||||||
}
|
}
|
||||||
|
|
||||||
secondsPerHour() {
|
secondsPerHour() {
|
||||||
|
@ -84,14 +80,10 @@ export class WorldTime extends decorate(class {}) {
|
||||||
this.ticker.tick(elapsed);
|
this.ticker.tick(elapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
toNetwork() {
|
toJSON() {
|
||||||
return {
|
return {
|
||||||
hour: (this._hour * MAGIC_TO_FIT_HOUR_INTO_USHORT) >> 0,
|
hour: (this._hour * MAGIC_TO_FIT_HOUR_INTO_USHORT) >> 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
|
||||||
return this.toNetwork();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,12 +67,12 @@ export function kittyFireJSON() {
|
||||||
// for (let i = 0; i < 20; ++i) {
|
// for (let i = 0; i < 20; ++i) {
|
||||||
// addEntityWithRandomPosition('/flower-barrel.entity.json');
|
// addEntityWithRandomPosition('/flower-barrel.entity.json');
|
||||||
// }
|
// }
|
||||||
for (let i = 0; i < 3; ++i) {
|
// for (let i = 0; i < 3; ++i) {
|
||||||
addEntityWithRandomPosition('/mama-kitty-spawner.entity.json');
|
// addEntityWithRandomPosition('/mama-kitty-spawner.entity.json');
|
||||||
}
|
// }
|
||||||
for (let i = 0; i < 5; ++i) {
|
// for (let i = 0; i < 5; ++i) {
|
||||||
addEntityWithRandomPosition('/fire.entity.json');
|
// addEntityWithRandomPosition('/fire.entity.json');
|
||||||
}
|
// }
|
||||||
// for (let i = 0; i < 1; ++i) {
|
// for (let i = 0; i < 1; ++i) {
|
||||||
// addEntityWithRandomPosition('/blue-fire.entity.json');
|
// addEntityWithRandomPosition('/blue-fire.entity.json');
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -57,14 +57,15 @@ export default class Game {
|
||||||
this.informables.splice(index, 1);
|
this.informables.splice(index, 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Sync world time.
|
Promise.resolve(entity.hydrate()).then(() => {
|
||||||
entity.addSynchronized(this.worldTime);
|
// Sync world time.
|
||||||
// Add entity to room.
|
entity.addSynchronized(this.worldTime);
|
||||||
if (this.room) {
|
// Add entity to room.
|
||||||
this.room.addEntityToLayer(entity, 0);
|
if (this.room) {
|
||||||
}
|
this.room.addEntityToLayer(entity, 0);
|
||||||
// Initial information.
|
entity.addSynchronized(this.room);
|
||||||
entity.inform();
|
}
|
||||||
|
});
|
||||||
// Listen for events.
|
// Listen for events.
|
||||||
socket.on('packet', this.createPacketListener(socket));
|
socket.on('packet', this.createPacketListener(socket));
|
||||||
socket.on('disconnect', this.createDisconnectionListener(socket));
|
socket.on('disconnect', this.createDisconnectionListener(socket));
|
||||||
|
@ -110,6 +111,9 @@ export default class Game {
|
||||||
this.informables[i].inform();
|
this.informables[i].inform();
|
||||||
}
|
}
|
||||||
// Clean packets.
|
// Clean packets.
|
||||||
|
if (this.room) {
|
||||||
|
this.room.cleanPackets();
|
||||||
|
}
|
||||||
this.worldTime.cleanPackets();
|
this.worldTime.cleanPackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ import {EntityCreatePacket, EntityPacket, EntityRemovePacket, Trait} from '@avoc
|
||||||
import {Rectangle, Vector} from '@avocado/math';
|
import {Rectangle, Vector} from '@avocado/math';
|
||||||
import {BundlePacket, ServerSynchronizer} from '@avocado/net';
|
import {BundlePacket, ServerSynchronizer} from '@avocado/net';
|
||||||
|
|
||||||
|
import {SelfEntityPacket} from '../../common/packets/self-entity.packet';
|
||||||
|
|
||||||
const decorate = compose(
|
const decorate = compose(
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -19,7 +21,7 @@ export class Informed extends decorate(Trait) {
|
||||||
|
|
||||||
constructor(entity, params, state) {
|
constructor(entity, params, state) {
|
||||||
super(entity, params, state);
|
super(entity, params, state);
|
||||||
this.seenEntities = [];
|
this._sentSelfEntityPacket = false;
|
||||||
this._socket = undefined;
|
this._socket = undefined;
|
||||||
this._synchronizer = new ServerSynchronizer();
|
this._synchronizer = new ServerSynchronizer();
|
||||||
}
|
}
|
||||||
|
@ -44,251 +46,6 @@ export class Informed extends decorate(Trait) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
deduplicateEntityCreatePackets(packets) {
|
|
||||||
const created = new Map();
|
|
||||||
return packets.filter((packet) => {
|
|
||||||
const entity = packet.entity;
|
|
||||||
if (!entity) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Only care about creates.
|
|
||||||
if (!(packet instanceof EntityCreatePacket)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (created.has(entity)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
created.set(entity, true);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
filter(packets) {
|
|
||||||
packets = packets.filter((packet) => {
|
|
||||||
return !(packet instanceof EntityPacket);
|
|
||||||
});
|
|
||||||
// // Filter invisible entities.
|
|
||||||
// packets = this.filterInvisibleEntityPackets(packets);
|
|
||||||
// // Reduce entities by range.
|
|
||||||
// const [
|
|
||||||
// inRangeEntities,
|
|
||||||
// outOfRangeEntities,
|
|
||||||
// visibleEntities,
|
|
||||||
// ] = this.reducePacketEntitiesByRange(packets);
|
|
||||||
|
|
||||||
// // TODO? Upgrade seen entity packets that are out of range to entity
|
|
||||||
// // remembers.
|
|
||||||
|
|
||||||
// // Filter the out of range entity updates.
|
|
||||||
// packets = this.filterOutOfRangeEntityPackets(
|
|
||||||
// packets,
|
|
||||||
// outOfRangeEntities
|
|
||||||
// );
|
|
||||||
// // Deduplicate entity creates.
|
|
||||||
// packets = this.deduplicateEntityCreatePackets(packets);
|
|
||||||
// // Filter known creates.
|
|
||||||
// packets = this.filterKnownEntityCreatePackets(packets);
|
|
||||||
// // Inject create packets.
|
|
||||||
// packets = this.injectEntityCreatePackets(packets, visibleEntities);
|
|
||||||
// // Inject removes for any previously seen entity that isn't visible
|
|
||||||
// // anymore.
|
|
||||||
// packets = this.injectEntityRemovePackets(packets, visibleEntities);
|
|
||||||
// // "See" entities.
|
|
||||||
// this.markEntitiesSeen(visibleEntities);
|
|
||||||
// // Unsee any removed entities.
|
|
||||||
// this.markEntitiesUnseen(packets);
|
|
||||||
|
|
||||||
return packets;
|
|
||||||
}
|
|
||||||
|
|
||||||
filterInvisibleEntityPackets(packets) {
|
|
||||||
return packets.filter((packet) => {
|
|
||||||
const entity = packet.entity;
|
|
||||||
if (!entity) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Removes could be on destroyed entities, so pass them.
|
|
||||||
if (packet instanceof EntityRemovePacket) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return entity.isVisible;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
filterKnownEntityCreatePackets(packets) {
|
|
||||||
return packets.filter((packet) => {
|
|
||||||
if (!packet.entity) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!(packet instanceof EntityCreatePacket)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return !this.hasSeenEntity(packet.entity);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
filterOutOfRangeEntityPackets(packets, outOfRangeEntities) {
|
|
||||||
return packets.filter((packet) => {
|
|
||||||
const entity = packet.entity;
|
|
||||||
if (!entity) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Send removes even if they're out of range, if client knows about
|
|
||||||
// them.
|
|
||||||
if (
|
|
||||||
packet instanceof EntityRemovePacket
|
|
||||||
&& this.hasSeenEntity(entity)
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return -1 === outOfRangeEntities.indexOf(packet.entity);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
hasSeenEntity(entity) {
|
|
||||||
return -1 !== this.seenEntities.indexOf(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
injectEntityCreatePackets(packets, visibleEntities) {
|
|
||||||
// Get a list of all visible but not yet seen entities.
|
|
||||||
const visibleButNotYetSeen = [];
|
|
||||||
for (let i = 0; i < visibleEntities.length; i++) {
|
|
||||||
const entity = visibleEntities[i];
|
|
||||||
if (!this.hasSeenEntity(entity)) {
|
|
||||||
visibleButNotYetSeen.push(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get a list of all existing created entities.
|
|
||||||
const allExistingCreatedEntities = packets.filter((packet) => {
|
|
||||||
return packet instanceof EntityCreatePacket;
|
|
||||||
}).map((packet) => {
|
|
||||||
return packet.entity;
|
|
||||||
});
|
|
||||||
// JIT inject creates before any unknown updates.
|
|
||||||
for (let i = 0; i < packets.length; i++) {
|
|
||||||
const packet = packets[i];
|
|
||||||
// Only care about entity packets.
|
|
||||||
if (!(packet instanceof EntityPacket)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Only unknown.
|
|
||||||
const entity = packet.entity;
|
|
||||||
if (this.hasSeenEntity(entity)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (-1 !== allExistingCreatedEntities.indexOf(entity)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Not creates nor removes.
|
|
||||||
if (
|
|
||||||
packet instanceof EntityCreatePacket
|
|
||||||
|| packet instanceof EntityRemovePacket
|
|
||||||
) {
|
|
||||||
// This does count as seen.
|
|
||||||
const index = visibleButNotYetSeen.indexOf(entity);
|
|
||||||
if (-1 !== index) {
|
|
||||||
visibleButNotYetSeen.splice(index, 1);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Inject.
|
|
||||||
packets.splice(
|
|
||||||
i,
|
|
||||||
0,
|
|
||||||
new EntityCreatePacket(entity.mergeDiff(), entity)
|
|
||||||
);
|
|
||||||
i += 1;
|
|
||||||
// We've seen it.
|
|
||||||
const index = visibleButNotYetSeen.indexOf(entity);
|
|
||||||
if (-1 !== index) {
|
|
||||||
visibleButNotYetSeen.splice(index, 1);
|
|
||||||
}
|
|
||||||
allExistingCreatedEntities.push(entity);
|
|
||||||
}
|
|
||||||
// Append creates for any visible-but-not-yet-seen entities.
|
|
||||||
for (let i = 0; i < visibleButNotYetSeen.length; i++) {
|
|
||||||
const entity = visibleButNotYetSeen[i];
|
|
||||||
// Skip any existing creates.
|
|
||||||
if (-1 === allExistingCreatedEntities.indexOf(entity)) {
|
|
||||||
packets.push(new EntityCreatePacket(entity.mergeDiff(), entity));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return packets;
|
|
||||||
}
|
|
||||||
|
|
||||||
injectEntityRemovePackets(packets, visibleEntities) {
|
|
||||||
const alreadyRemovedEntities = packets.filter((packet) => {
|
|
||||||
return packet instanceof EntityRemovePacket;
|
|
||||||
}).map((packet) => {
|
|
||||||
return packet.entity;
|
|
||||||
});
|
|
||||||
for (let i = 0; i < this.seenEntities.length; i++) {
|
|
||||||
const entity = this.seenEntities[i];
|
|
||||||
if (-1 === visibleEntities.indexOf(entity)) {
|
|
||||||
if (-1 === alreadyRemovedEntities.indexOf(entity)) {
|
|
||||||
packets.push(new EntityRemovePacket({}, entity));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return packets;
|
|
||||||
}
|
|
||||||
|
|
||||||
markEntitiesSeen(visibleEntities) {
|
|
||||||
for (let i = 0; i < visibleEntities.length; i++) {
|
|
||||||
const entity = visibleEntities[i];
|
|
||||||
if (-1 === this.seenEntities.indexOf(entity)) {
|
|
||||||
this.seenEntities.push(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
markEntitiesUnseen(packets) {
|
|
||||||
const removedEntities = packets.filter((packet) => {
|
|
||||||
return packet instanceof EntityRemovePacket;
|
|
||||||
}).map((packet) => {
|
|
||||||
return packet.entity;
|
|
||||||
});
|
|
||||||
for (let i = 0; i < removedEntities.length; i++) {
|
|
||||||
const entity = removedEntities[i];
|
|
||||||
const index = this.seenEntities.indexOf(entity)
|
|
||||||
if (-1 !== index) {
|
|
||||||
this.seenEntities.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
reducePacketEntitiesByRange(packets) {
|
|
||||||
// Unique packet entities.
|
|
||||||
const packetEntitiesMap = new Map();
|
|
||||||
for (let i = 0; i < packets.length; i++) {
|
|
||||||
const entity = packets[i].entity;
|
|
||||||
if (entity && !packetEntitiesMap.has(entity)) {
|
|
||||||
packetEntitiesMap.set(entity, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Locate visible entities.
|
|
||||||
const areaToInform = this.areaToInform;
|
|
||||||
const visibleEntities = this.entity.room.visibleEntities(areaToInform);
|
|
||||||
// Document out of range entities.
|
|
||||||
const inRangeEntities = [];
|
|
||||||
const outOfRangeEntities = [];
|
|
||||||
let it = packetEntitiesMap.keys();
|
|
||||||
for (let value = it.next(); !value.done; value = it.next()) {
|
|
||||||
const entity = value.value;
|
|
||||||
if (-1 === visibleEntities.indexOf(entity)) {
|
|
||||||
if (-1 === outOfRangeEntities.indexOf(entity)) {
|
|
||||||
outOfRangeEntities.push(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (-1 === inRangeEntities.indexOf(entity)) {
|
|
||||||
inRangeEntities.push(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [inRangeEntities, outOfRangeEntities, visibleEntities];
|
|
||||||
}
|
|
||||||
|
|
||||||
get socket() {
|
get socket() {
|
||||||
return this._socket;
|
return this._socket;
|
||||||
}
|
}
|
||||||
|
@ -303,42 +60,18 @@ export class Informed extends decorate(Trait) {
|
||||||
|
|
||||||
inform: () => {
|
inform: () => {
|
||||||
const payload = this._synchronizer.packetsFor(this.entity);
|
const payload = this._synchronizer.packetsFor(this.entity);
|
||||||
// const payload = [];
|
|
||||||
// for (let i = 0; i < this._willWatch.length; i++) {
|
|
||||||
// const packets = this._willWatch[i].packets(this.entity);
|
|
||||||
// for (let j = 0; j < packets.length; j++) {
|
|
||||||
// payload.push(packets[j]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// for (let i = 0; i < this._watching.length; i++) {
|
|
||||||
// const packets = this._watching[i].packets(this.entity);
|
|
||||||
// for (let j = 0; j < packets.length; j++) {
|
|
||||||
// payload.push(packets[j]);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// for (let i = 0; i < this._willWatch.length; i++) {
|
|
||||||
// this._watching.push(this._willWatch[i]);
|
|
||||||
// }
|
|
||||||
// this._willWatch = [];
|
|
||||||
// // Clean all packets.
|
|
||||||
// for (let i = 0; i < this._watching.length; i++) {
|
|
||||||
// this._watching[i].cleanPackets();
|
|
||||||
// }
|
|
||||||
if (0 === payload.length) {
|
if (0 === payload.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!this._sentSelfEntityPacket) {
|
||||||
|
this._sentSelfEntityPacket = true;
|
||||||
|
payload.push(new SelfEntityPacket(this.entity.numericUid));
|
||||||
|
}
|
||||||
if (this._socket) {
|
if (this._socket) {
|
||||||
this._socket.send(new BundlePacket(payload));
|
this._socket.send(new BundlePacket(payload));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
seesEntity: (entity) => {
|
|
||||||
return Rectangle.isTouching(
|
|
||||||
this.entity.areaToInform,
|
|
||||||
entity.visibleAabb
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
addSynchronized: (synchronized) => {
|
addSynchronized: (synchronized) => {
|
||||||
this._synchronizer.addSynchronized(synchronized);
|
this._synchronizer.addSynchronized(synchronized);
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue
Block a user