silphius/app/engine/engine.js
2024-06-11 02:10:08 -05:00

166 lines
4.1 KiB
JavaScript

import {
MOVE_MAP,
RESOLUTION,
TPS,
} from '@/constants.js';
import ControlMovement from '@/ecs-systems/control-movement.js';
import ApplyMomentum from '@/ecs-systems/apply-momentum.js';
import CalculateAabbs from '@/ecs-systems/calculate-aabbs.js';
import FollowCamera from '@/ecs-systems/follow-camera.js';
import UpdateSpatialHash from '@/ecs-systems/update-spatial-hash.js';
import Ecs from '@/engine/ecs.js';
import {decode, encode} from '@/packets/index.js';
const players = {
0: {
Camera: {},
Controlled: {up: 0, right: 0, down: 0, left: 0},
Momentum: {},
Position: {x: 50, y: 50},
VisibleAabb: {},
World: {world: 1},
Sprite: {image: '/assets/bunny.png'},
},
};
export default class Engine {
static Ecs = Ecs;
constructor(Server) {
const ecs = new this.constructor.Ecs();
ecs.create({
AreaSize: {x: RESOLUTION[0] * 4, y: RESOLUTION[1] * 4},
});
ecs.addSystem(ControlMovement);
ecs.addSystem(ApplyMomentum);
ecs.addSystem(FollowCamera);
ecs.addSystem(CalculateAabbs);
ecs.addSystem(UpdateSpatialHash);
this.ecses = {
1: ecs,
};
this.connections = [];
this.connectedPlayers = new Map();
this.frame = 0;
this.last = Date.now();
class SilphiusServer extends Server {
accept(connection, packed) {
super.accept(connection, decode(packed));
}
transmit(connection, packet) {
super.transmit(connection, encode(packet));
}
}
this.server = new SilphiusServer();
this.server.addPacketListener((connection, packet) => {
this.accept(connection, packet);
});
}
accept(connection, {payload, type}) {
switch (type) {
case 'Action': {
const {entity} = this.connectedPlayers.get(connection);
if (payload.type in MOVE_MAP) {
entity.Controlled[MOVE_MAP[payload.type]] = payload.value;
}
break;
}
default:
}
}
async connectPlayer(connection) {
this.connections.push(connection);
const entityJson = await this.loadPlayer(connection);
const ecs = this.ecses[entityJson.World.world];
const entity = ecs.create(entityJson);
this.connectedPlayers.set(
connection,
{
entity: ecs.get(entity),
memory: new Set(),
},
);
}
disconnectPlayer(connection) {
const {entity} = this.connectedPlayers.get(connection);
const ecs = this.ecses[entity.World.world];
players[0] = JSON.parse(JSON.stringify(entity.toJSON()));
ecs.destroy(entity.id);
this.connectedPlayers.delete(connection);
this.connections.splice(this.connections.indexOf(connection), 1);
}
async load() {
}
async loadPlayer() {
return players[0];
}
start() {
return setInterval(() => {
const elapsed = (Date.now() - this.last) / 1000;
this.last = Date.now();
this.tick(elapsed);
this.update(elapsed);
this.frame += 1;
}, 1000 / TPS);
}
tick(elapsed) {
for (const i in this.ecses) {
this.ecses[i].setClean();
this.ecses[i].tick(elapsed);
}
}
update(elapsed) {
for (const connection of this.connections) {
this.server.send(
connection,
{
type: 'Tick',
payload: {
ecs: this.updateFor(connection),
elapsed,
frame: this.frame,
},
},
);
}
}
updateFor(connection) {
const update = {};
const {entity, memory} = this.connectedPlayers.get(connection);
const mainEntityId = entity.id;
const ecs = this.ecses[entity.World.world];
const nearby = ecs.system(UpdateSpatialHash).nearby(entity);
const lastMemory = new Set(memory.values());
for (const entity of nearby) {
const {id} = entity;
lastMemory.delete(id);
if (!memory.has(id)) {
update[id] = entity.toJSON();
if (mainEntityId === id) {
update[id].MainEntity = {};
}
}
else if (ecs.diff[id]) {
update[id] = ecs.diff[id];
}
memory.add(id);
}
for (const id of lastMemory) {
memory.delete(id);
update[id] = false;
}
return update;
}
}