2019-05-02 23:34:07 -05:00
|
|
|
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';
|
2019-04-12 00:45:25 -05:00
|
|
|
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 = [];
|
2019-03-20 23:01:48 -05:00
|
|
|
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),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-03-20 23:01:48 -05:00
|
|
|
get socket() {
|
|
|
|
return this._socket;
|
|
|
|
}
|
|
|
|
|
|
|
|
set socket(socket) {
|
2019-03-21 00:29:44 -05:00
|
|
|
socket.entity = this.entity;
|
2019-03-20 23:01:48 -05:00
|
|
|
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
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|