avocado-old/packages/entity/list/index.js

244 lines
6.6 KiB
JavaScript
Raw Normal View History

2019-05-02 20:04:25 -05:00
import {compose, EventEmitter} from '@avocado/core';
import {QuadTree, Rectangle, Vector} from '@avocado/math';
2019-09-30 20:08:05 -05:00
import {
SynchronizedCreatePacket,
SynchronizedDestroyPacket,
} from '@avocado/net';
2019-09-30 01:36:02 -05:00
import {EntityListUpdateEntityPacket} from '../packets/entity-list-update-entity.packet';
2019-09-22 18:45:33 -05:00
import {Entity} from '../entity.synchronized';
2019-03-17 23:45:48 -05:00
const decorate = compose(
EventEmitter,
);
2019-04-16 17:52:56 -05:00
export class EntityList extends decorate(class {}) {
2019-03-17 23:45:48 -05:00
constructor() {
super();
2019-04-19 14:46:00 -05:00
this._afterDestructionTickers = [];
2019-04-07 12:00:11 -05:00
this._entities = {};
2019-05-05 17:11:46 -05:00
this._entityTickers = []
2019-05-03 13:26:51 -05:00
this._flatEntities = [];
2019-09-30 20:08:05 -05:00
this._informedEntities = new Map();
this._quadTree = new QuadTree();
2019-03-17 23:45:48 -05:00
}
*[Symbol.iterator]() {
2019-04-07 12:00:11 -05:00
for (const uuid in this._entities) {
const entity = this._entities[uuid];
2019-03-17 23:45:48 -05:00
yield entity;
}
}
2019-05-13 21:07:51 -05:00
acceptPacket(packet) {
2019-09-30 01:36:02 -05:00
if (packet instanceof EntityListUpdateEntityPacket) {
for (let i = 0; i < packet.data.length; i++) {
const {uuid, packets} = packet.data[i];
// WHY ?
if (!this._entities[uuid]) {
continue;
}
for (let j = 0; j < packets.length; j++) {
this._entities[uuid].acceptPacket(packets[j]);
}
}
}
2019-09-30 20:08:05 -05:00
if (packet instanceof SynchronizedCreatePacket) {
const entity = new Entity(packet.data.spec);
this.addEntity(entity);
}
if (packet instanceof SynchronizedDestroyPacket) {
const uuid = packet.data.synchronized.id;
if (this._entities[uuid]) {
this._entities[uuid].destroy();
}
}
2019-05-13 21:07:51 -05:00
}
2019-03-17 23:45:48 -05:00
addEntity(entity) {
const uuid = entity.instanceUuid;
2019-06-05 20:16:51 -05:00
// Already exists?
if (this._entities[uuid]) {
return;
}
2019-04-07 12:00:11 -05:00
this._entities[uuid] = entity;
2019-05-03 13:26:51 -05:00
this._flatEntities.push(entity);
2019-05-05 17:11:46 -05:00
this._entityTickers.push(entity.tick);
2019-05-13 21:07:51 -05:00
if (AVOCADO_SERVER) {
2019-09-30 20:08:05 -05:00
this._informedEntities.set(entity, []);
2019-05-13 21:07:51 -05:00
}
2019-05-03 13:26:51 -05:00
entity.setIntoList(this);
entity.once('destroy', () => {
2019-03-21 00:09:17 -05:00
this.removeEntity(entity);
2019-04-19 14:46:00 -05:00
// In the process of destroying, allow entities to specify tickers that
// must live on past destruction.
const tickers = entity.invokeHookFlat('afterDestructionTickers');
2019-05-03 13:26:51 -05:00
for (let i = 0; i < tickers.length; i++) {
const ticker = tickers[i];
this._afterDestructionTickers.push(ticker);
}
2019-03-20 18:35:19 -05:00
});
this.emit('entityAdded', entity);
2019-03-17 23:45:48 -05:00
}
2019-09-30 01:36:02 -05:00
cleanPackets() {
for (let i = 0; i < this._flatEntities.length; i++) {
this._flatEntities[i].cleanPackets();
}
}
2019-03-21 01:32:49 -05:00
destroy() {
2019-05-13 21:07:51 -05:00
for (let i = 0; i < this._flatEntities.length; i++) {
this._flatEntities[i].destroy();
2019-03-21 01:32:49 -05:00
}
}
2019-03-27 17:36:57 -05:00
findEntity(uuid) {
2019-05-03 13:26:51 -05:00
if (uuid in this._entities) {
2019-04-07 12:00:11 -05:00
return this._entities[uuid];
2019-03-27 17:36:57 -05:00
}
2019-03-20 21:07:57 -05:00
}
2019-05-16 23:01:57 -05:00
fromJSON(json) {
for (let i = 0; i < json.length; i++) {
const entityJSON = json[i];
2019-09-29 13:19:57 -05:00
if (entityJSON.uri) {
2019-05-16 23:01:57 -05:00
Entity.read(entityJSON.uri).then((readJSON) => {
2019-05-17 04:35:10 -05:00
this.addEntity(new Entity(readJSON, entityJSON));
2019-05-16 23:01:57 -05:00
});
}
else {
this.addEntity(new Entity(json[i]));
}
}
}
2019-09-29 13:19:57 -05:00
packets(informed) {
2019-05-13 21:07:51 -05:00
const packets = [];
2019-09-30 20:08:05 -05:00
// Visible entities.
const {areaToInform} = informed;
const previousVisibleEntities = this._informedEntities.get(informed);
const visibleEntities = this.visibleEntities(areaToInform);
2019-09-30 01:36:02 -05:00
const updates = [];
2019-09-30 20:08:05 -05:00
for (let i = 0; i < visibleEntities.length; i++) {
const entity = visibleEntities[i];
// Newly visible entity.
const index = previousVisibleEntities.indexOf(entity);
if (-1 === index) {
packets.push(entity.createPacket(informed));
}
// Still visible entity.
else {
const entityPackets = entity.packets(informed);
if (entityPackets.length > 0) {
updates.push({
uuid: entity.instanceUuid,
packets: entityPackets,
});
}
previousVisibleEntities.splice(index, 1);
}
}
for (let i = 0; i < previousVisibleEntities.length; i++) {
const entity = previousVisibleEntities[i];
// Newly removed entity.
if (-1 === visibleEntities.indexOf(entity)) {
packets.push(entity.destroyPacket(informed));
2019-09-30 01:36:02 -05:00
}
}
2019-09-30 20:08:05 -05:00
this._informedEntities.set(informed, visibleEntities);
2019-09-30 01:36:02 -05:00
if (updates.length > 0) {
packets.push(new EntityListUpdateEntityPacket(updates));
}
2019-05-13 21:07:51 -05:00
return packets;
2019-04-05 15:16:55 -05:00
}
get quadTree() {
return this._quadTree;
2019-03-20 18:35:19 -05:00
}
2019-03-17 23:45:48 -05:00
removeEntity(entity) {
const uuid = entity.instanceUuid;
2019-05-05 17:28:52 -05:00
if (!(uuid in this._entities)) {
2019-05-05 17:11:46 -05:00
return;
}
2019-05-13 21:07:51 -05:00
if (AVOCADO_SERVER) {
2019-09-30 20:08:05 -05:00
this._informedEntities.delete(entity);
2019-05-13 21:07:51 -05:00
}
2019-05-26 12:03:24 -05:00
entity.removeFromList();
2019-04-07 12:00:11 -05:00
delete this._entities[uuid];
2019-05-05 17:28:52 -05:00
this._flatEntities.splice(this._flatEntities.indexOf(entity), 1);
2019-05-05 17:11:46 -05:00
this._entityTickers.splice(this._entityTickers.indexOf(entity.tick), 1);
this.emit('entityRemoved', entity);
2019-03-17 23:45:48 -05:00
}
tick(elapsed) {
2019-05-02 21:37:37 -05:00
// Run after destruction tickers.
2019-05-03 13:26:51 -05:00
if (this._afterDestructionTickers.length > 0) {
this.tickAfterDestructionTickers(elapsed);
}
2019-04-19 14:46:00 -05:00
// Run normal tickers.
this.tickEntities(elapsed)
2019-03-17 23:45:48 -05:00
}
tickAfterDestructionTickers(elapsed) {
const finishedTickers = [];
for (let i = 0; i < this._afterDestructionTickers.length; ++i) {
const ticker = this._afterDestructionTickers[i];
if (ticker(elapsed)) {
finishedTickers.push(ticker);
}
}
for (let i = 0; i < finishedTickers.length; ++i) {
const ticker = finishedTickers[i];
const index = this._afterDestructionTickers.indexOf(ticker);
this._afterDestructionTickers.splice(index, 1);
}
}
tickEntities(elapsed) {
2019-05-05 17:11:46 -05:00
for (let i = 0; i < this._entityTickers.length; i++) {
this._entityTickers[i](elapsed);
}
}
2019-09-29 13:19:57 -05:00
toJSON() {
const json = [];
for (let i = 0; i < this._flatEntities.length; i++) {
json.push(this._flatEntities[i].mergeDiff());
}
return json;
}
visibleEntities(query) {
2019-04-16 23:59:08 -05:00
const entities = [];
2019-05-09 01:32:49 -05:00
const entitiesChecked = [];
const quadTree = this._quadTree;
2019-04-16 23:59:08 -05:00
const nodes = quadTree.search(query);
2019-05-09 01:32:49 -05:00
// Check all nodes.
2019-04-16 23:59:08 -05:00
for (let i = 0; i < nodes.length; ++i) {
const node = nodes[i];
const entity = node.data[2];
2019-05-09 01:32:49 -05:00
const aabb = node.data[3];
if (-1 === entitiesChecked.indexOf(entity)) {
entitiesChecked.push(entity);
// Make sure the AABB is actually in the query due to expansion.
2019-06-05 20:18:30 -05:00
if (entity.isVisible && Rectangle.intersects(query, aabb)) {
2019-05-09 01:32:49 -05:00
entities.push(entity);
}
2019-05-02 20:04:25 -05:00
}
}
return entities;
}
2019-06-08 00:23:51 -05:00
visibleEntitiesWithUri(query, uri) {
return this.visibleEntities(query).filter((entity) => {
return entity.uri === uri;
});
}
2019-03-17 23:45:48 -05:00
}
2019-04-13 20:53:02 -05:00
export {EntityListView} from './view';