2024-06-10 22:42:30 -05:00
|
|
|
import {
|
|
|
|
MOVE_MAP,
|
|
|
|
RESOLUTION,
|
|
|
|
TPS,
|
|
|
|
} from '@/constants.js';
|
2024-06-10 23:55:06 -05:00
|
|
|
import ControlMovement from '@/ecs-systems/control-movement.js';
|
|
|
|
import ApplyMomentum from '@/ecs-systems/apply-momentum.js';
|
|
|
|
import CalculateAabbs from '@/ecs-systems/calculate-aabbs.js';
|
2024-06-11 01:41:19 -05:00
|
|
|
import FollowCamera from '@/ecs-systems/follow-camera.js';
|
2024-06-10 23:55:06 -05:00
|
|
|
import UpdateSpatialHash from '@/ecs-systems/update-spatial-hash.js';
|
|
|
|
import Ecs from '@/engine/ecs.js';
|
|
|
|
import {decode, encode} from '@/packets/index.js';
|
2024-06-10 22:42:30 -05:00
|
|
|
|
|
|
|
const players = {
|
|
|
|
0: {
|
2024-06-11 01:41:19 -05:00
|
|
|
Camera: {},
|
2024-06-10 22:42:30 -05:00
|
|
|
Controlled: {up: 0, right: 0, down: 0, left: 0},
|
|
|
|
Momentum: {},
|
|
|
|
Position: {x: 50, y: 50},
|
|
|
|
VisibleAabb: {},
|
2024-06-11 00:08:31 -05:00
|
|
|
World: {world: 1},
|
2024-06-10 22:42:30 -05:00
|
|
|
Sprite: {image: '/assets/bunny.png'},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export default class Engine {
|
|
|
|
|
|
|
|
static Ecs = Ecs;
|
|
|
|
|
|
|
|
constructor(Server) {
|
|
|
|
const ecs = new this.constructor.Ecs();
|
|
|
|
ecs.create({
|
2024-06-11 01:41:19 -05:00
|
|
|
AreaSize: {x: RESOLUTION[0] * 4, y: RESOLUTION[1] * 4},
|
2024-06-10 22:42:30 -05:00
|
|
|
});
|
|
|
|
ecs.addSystem(ControlMovement);
|
|
|
|
ecs.addSystem(ApplyMomentum);
|
2024-06-11 01:41:19 -05:00
|
|
|
ecs.addSystem(FollowCamera);
|
2024-06-10 22:42:30 -05:00
|
|
|
ecs.addSystem(CalculateAabbs);
|
|
|
|
ecs.addSystem(UpdateSpatialHash);
|
|
|
|
this.ecses = {
|
2024-06-11 00:08:31 -05:00
|
|
|
1: ecs,
|
2024-06-10 22:42:30 -05:00
|
|
|
};
|
|
|
|
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);
|
2024-06-11 01:41:19 -05:00
|
|
|
const mainEntityId = entity.id;
|
2024-06-10 22:42:30 -05:00
|
|
|
const ecs = this.ecses[entity.World.world];
|
|
|
|
const {hash} = ecs.system(UpdateSpatialHash);
|
|
|
|
const nearby = new Set();
|
|
|
|
for (const [cx, cy] of hash.data[entity.id]) {
|
|
|
|
hash.chunks[cx][cy].forEach((id) => {
|
|
|
|
nearby.add(ecs.get(id));
|
|
|
|
});
|
|
|
|
}
|
|
|
|
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();
|
2024-06-11 01:41:19 -05:00
|
|
|
if (mainEntityId === id) {
|
|
|
|
update[id].MainEntity = {};
|
|
|
|
}
|
2024-06-10 22:42:30 -05:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|