humus-old/server/game.js
2019-05-21 03:10:14 -05:00

129 lines
3.4 KiB
JavaScript

// Node.
import msgpack from 'msgpack-lite';
import {performance} from 'perf_hooks';
// 3rd party.
// 2nd party.
import {InputPacket} from '@avocado/input';
import {World} from '@avocado/physics/matter/world';
import {Synchronizer} from '@avocado/state';
import {Ticker} from '@avocado/timing';
import {Room} from '@avocado/topdown';
// 1st party.
import {SelfEntityPacket} from '../common/packets/self-entity.packet';
import {WorldTime} from '../common/world-time';
import {createEntityForConnection} from './create-entity-for-connection';
// Create game.
export default class Game {
constructor() {
const config = this.readConfig();
// Room.
this.room = undefined;
Room.load('/kitty-fire.room.json').then((room) => {
room.world = new World();
room.world.stepTime = config.simulationInterval;
this.synchronizer.addChild(room);
this.room = room;
});
// World time. Start at 10 am for testing.
this.worldTime = new WorldTime();
this.worldTime.hour = 10;
// Entity tracking.
this.informables = [];
// State synchronization.
this.synchronizer = new Synchronizer([
this.worldTime,
]);
this.informTicker = new Ticker(config.informInterval);
this.informTicker.on('tick', this.inform, this);
// Simulation.
this.mainLoopHandle = setInterval(
this.createMainLoop(),
1000 * config.simulationInterval
);
}
destroy(fn) {
clearInterval(this.mainLoopHandle);
if (this.room) {
this.room.destroy();
}
fn();
}
acceptConnection(socket) {
// Create and track a new entity for the connection.
const entity = createEntityForConnection(socket);
// Track informables.
this.informables.push(entity);
entity.on('destroyed', () => {
const index = this.informables.indexOf(entity);
if (-1 !== index) {
this.informables.splice(index, 1);
}
});
// Add entity to room.
if (this.room) {
this.room.addEntityToLayer(entity, 0);
}
// Initial information.
const packets = this.synchronizer.packetsForUpdate(true);
packets.unshift(new SelfEntityPacket(entity.numericUid));
entity.inform(packets);
// Listen for events.
socket.on('packet', this.createPacketListener(socket));
socket.on('disconnect', this.createDisconnectionListener(socket));
}
createDisconnectionListener(socket) {
const {entity} = socket;
return () => {
if (entity.is('existent')) {
entity.destroy();
}
};
}
createMainLoop() {
let lastTime = performance.now();
return () => {
const now = performance.now();
const elapsed = (now - lastTime) / 1000;
lastTime = now;
// Tick synchronized.
if (this.room) {
this.room.tick(elapsed);
}
this.worldTime.tick(elapsed);
// Tick informer.
this.informTicker.tick(elapsed);
}
}
createPacketListener(socket) {
const {entity} = socket;
return (packet) => {
if (packet instanceof InputPacket) {
entity.inputState = packet.toState();
}
};
}
inform() {
const packets = this.synchronizer.packetsForUpdate();
// Inform entities of the new state.
for (let i = 0; i < this.informables.length; ++i) {
const entity = this.informables[i];
entity.inform(packets);
}
}
readConfig() {
return {
informInterval: 1 / 60,
simulationInterval: 1 / 60,
};
}
}