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 RunAnimations from '@/ecs-systems/run-animations.js'; import ControlDirection from '@/ecs-systems/control-direction.js'; import SpriteDirection from '@/ecs-systems/sprite-direction.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}, Direction: {direction: 2}, Momentum: {}, Position: {x: 368, y: 368}, VisibleAabb: {}, World: {world: 1}, Sprite: { animation: 'moving:down', frame: 0, frames: 8, source: '/assets/dude.json', speed: 0.115, }, }, }; export default class Engine { static Ecs = Ecs; incoming = []; constructor(Server) { const ecs = new this.constructor.Ecs(); const layerSize = {x: Math.ceil(RESOLUTION.x / 4), y: Math.ceil(RESOLUTION.y / 4)}; ecs.create({ AreaSize: {x: RESOLUTION.x * 4, y: RESOLUTION.y * 4}, TileLayers: { layers: [ { data: ( Array(layerSize.x * layerSize.y) .fill(0) .map(() => 1 + Math.floor(Math.random() * 4)) ), size: layerSize, } ], }, }); ecs.addSystem(ControlMovement); ecs.addSystem(ApplyMomentum); ecs.addSystem(FollowCamera); ecs.addSystem(CalculateAabbs); ecs.addSystem(UpdateSpatialHash); ecs.addSystem(ControlDirection); ecs.addSystem(SpriteDirection); ecs.addSystem(RunAnimations); 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, packet) { this.incoming.push([this.connectedPlayers.get(connection).entity, packet]); } 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(); } for (const [{Controlled}, {payload, type}] of this.incoming) { switch (type) { case 'Action': { if (payload.type in MOVE_MAP) { Controlled[MOVE_MAP[payload.type]] = payload.value; } break; } default: } } this.incoming = []; for (const i in this.ecses) { 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); // Master entity. nearby.add(ecs.get(1)); 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; } }