humus-old/server/traits/informed.trait.js

221 lines
6.9 KiB
JavaScript
Raw Normal View History

import {performance} from 'perf_hooks';
2019-03-20 18:35:59 -05:00
import * as I from 'immutable';
2019-04-07 14:59:55 -05:00
import immutablediff from 'immutablediff';
2019-03-20 18:35:59 -05:00
2019-03-20 18:58:19 -05:00
import {compose} from '@avocado/core';
2019-05-13 21:07:57 -05:00
import {EntityCreatePacket, EntityPacket, EntityRemovePacket, Trait} from '@avocado/entity';
import {Rectangle, Vector} from '@avocado/math';
2019-05-13 21:07:57 -05:00
import {Synchronizer} from '@avocado/state';
2019-04-11 15:27:39 -05:00
2019-03-20 18:58:19 -05:00
const decorate = compose(
);
2019-04-05 11:59:14 -05:00
export class Informed extends decorate(Trait) {
2019-03-20 18:35:59 -05:00
2019-05-05 04:26:23 -05:00
static type() {
return 'informed';
}
2019-05-06 04:04:03 -05:00
constructor(entity, params, state) {
super(entity, params, state);
2019-05-13 21:07:57 -05:00
this.seenEntities = [];
this._socket = undefined;
2019-03-20 18:35:59 -05:00
}
2019-03-20 23:22:54 -05:00
destroy() {
2019-03-21 01:32:59 -05:00
if (this._socket) {
delete this._socket.entity;
delete this._socket;
}
2019-03-20 23:22:54 -05:00
}
2019-04-28 22:35:20 -05:00
get areaToInform() {
// Reduce entity list to visible.
const room = this.entity.room;
const camera = this.entity.camera;
// 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 socket() {
return this._socket;
}
set socket(socket) {
socket.entity = this.entity;
this._socket = socket;
}
2019-03-20 18:35:59 -05:00
methods() {
return {
2019-05-13 21:07:57 -05:00
inform: (packets) => {
if (0 === packets.length) {
2019-04-07 14:59:55 -05:00
return;
2019-04-05 22:50:24 -05:00
}
2019-05-13 21:07:57 -05:00
// Filter invisible entities.
packets = 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.is('visible');
});
// Build entity map.
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 outOfRangeEntities = [];
let it = packetEntitiesMap.keys();
for (let value = it.next(); !value.done; value = it.next()) {
const entity = value.value;
if (-1 === visibleEntities.indexOf(entity)) {
outOfRangeEntities.push(entity);
}
}
// TODO? Upgrade seen entity packets that are out of range to entity
// remembers.
// Filter all the rest of out of range entity updates and remove them
// from 'packet entities' map.
packets = 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
&& -1 !== this.seenEntities.indexOf(entity)
) {
return true;
}
return -1 === outOfRangeEntities.indexOf(packet.entity);
});
for (let i = 0; i < outOfRangeEntities.length; i++) {
packetEntitiesMap.delete(outOfRangeEntities[i]);
}
// Filter known creates. TODO: downgrade to trait updates.
packets = packets.filter((packet) => {
if (!packet.entity) {
return true;
}
if (!(packet instanceof EntityCreatePacket)) {
return true;
}
return -1 === this.seenEntities.indexOf(packet.entity);
});
// Inject creates for any visible-but-not-yet-seen entities.
const currentEntities = Array.from(packetEntitiesMap.keys());
for (let i = 0; i < visibleEntities.length; i++) {
const entity = visibleEntities[i];
if (
// Haven't seen?
-1 === this.seenEntities.indexOf(entity)
// Isn't already addressed by some present update?
&& -1 === currentEntities.indexOf(entity)
) {
packets.push(new EntityCreatePacket(entity.toJSON(), entity));
this.seenEntities.push(entity);
}
}
// Upgrade unknown entity updates to entity creates.
// TODO would be nice to JIT inject a create right before the update.
const unknownUpdatedEntities = [];
// First filter out the bad updates.
packets = packets.filter((packet) => {
// Only care about entity packets.
if (!(packet instanceof EntityPacket)) {
return true;
}
// But not creates.
if (packet instanceof EntityCreatePacket) {
return true;
}
// Nor removes.
if (packet instanceof EntityRemovePacket) {
return true;
}
// Known? Nevermind.
if (-1 !== this.seenEntities.indexOf(packet.entity)) {
return true;
}
// Side-effect.
if (-1 === unknownUpdatedEntities.indexOf(packet.entity)) {
unknownUpdatedEntities.push(packet.entity);
}
return false;
});
// Then, inject creates.
for (let i = 0; i < unknownUpdatedEntities.length; i++) {
const entity = unknownUpdatedEntities[i];
packets.push(new EntityCreatePacket(entity.toJSON(), entity));
this.seenEntities.push(entity);
}
// Inject removes for any previously seen entity that isn't visible
// anymore.
for (let i = 0; i < this.seenEntities.length; i++) {
const entity = this.seenEntities[i];
if (-1 === visibleEntities.indexOf(entity)) {
packets.push(new EntityRemovePacket({}, entity));
2019-05-09 17:44:38 -05:00
}
2019-04-11 12:55:21 -05:00
}
2019-05-13 21:07:57 -05:00
// Document any new entities.
it = packetEntitiesMap.keys();
for (let value = it.next(); !value.done; value = it.next()) {
const entity = value.value;
if (-1 === this.seenEntities.indexOf(entity)) {
this.seenEntities.push(entity);
}
}
// Unsee any removed entities.
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);
}
}
// Ship it!
for (let i = 0; i < packets.length; i++) {
const packet = packets[i];
this._socket.send(packet);
2019-05-09 17:44:38 -05:00
}
2019-03-20 18:35:59 -05:00
},
2019-04-28 22:35:20 -05:00
seesEntity: (entity) => {
2019-05-08 23:53:26 -05:00
return Rectangle.isTouching(
2019-05-09 19:14:28 -05:00
this.entity.areaToInform,
2019-05-08 23:53:26 -05:00
entity.visibleAabb
);
2019-04-28 22:35:20 -05:00
}
2019-03-20 18:35:59 -05:00
};
}
}