humus-old/client/index.js

117 lines
2.9 KiB
JavaScript
Raw Normal View History

2019-03-20 15:28:18 -05:00
import {create as createClient} from '@avocado/client';
2019-03-21 18:33:20 -05:00
import {EntityList} from '@avocado/entity';
2019-03-20 15:28:18 -05:00
import {ActionRegistry} from '@avocado/input';
2019-03-25 15:05:16 -05:00
import {Container, Renderer} from '@avocado/graphics';
2019-03-24 19:22:47 -05:00
import {World} from '@avocado/physics/matter/world';
2019-03-20 15:28:18 -05:00
import {StateSynchronizer} from '@avocado/state';
2019-03-25 18:57:28 -05:00
const renderer = new Renderer([640, 360]);
2019-03-20 15:28:18 -05:00
const stage = new Container();
const entityList = new EntityList();
2019-03-24 19:07:33 -05:00
entityList.world = new World();
2019-03-20 15:28:18 -05:00
const stateSynchronizer = new StateSynchronizer({
entityList,
});
let selfEntity;
2019-03-21 18:33:20 -05:00
function hasSelfEntity() {
return selfEntity && 'string' !== typeof selfEntity;
}
2019-03-20 15:28:18 -05:00
entityList.on('entityAdded', (entity) => {
2019-03-21 18:33:20 -05:00
// Add to graphics.
2019-03-20 15:28:18 -05:00
if ('container' in entity) {
stage.addChild(entity.container);
}
2019-03-21 18:33:20 -05:00
// Set self entity.
if ('string' === typeof selfEntity) {
2019-03-21 18:33:20 -05:00
const mappedUuid = entityList.mappedUuid(selfEntity);
if (entity.instanceUuid === mappedUuid) {
selfEntity = entity;
}
}
else {
// Only self entity should be controllable.
entity.removeTrait('controllable');
}
2019-03-20 15:28:18 -05:00
});
entityList.on('entityRemoved', (entity) => {
2019-03-21 18:33:20 -05:00
// Remove from graphics.
2019-03-20 15:28:18 -05:00
if ('container' in entity) {
stage.removeChild(entity.container);
}
});
2019-03-21 18:33:20 -05:00
// Accept input.
2019-03-20 15:28:18 -05:00
const actionRegistry = new ActionRegistry();
actionRegistry.mapKeysToActions({
'w': 'MoveUp',
'a': 'MoveLeft',
's': 'MoveDown',
'd': 'MoveRight',
});
2019-03-21 18:33:20 -05:00
actionRegistry.listen();
2019-03-20 15:28:18 -05:00
let actionState = actionRegistry.state();
2019-03-21 18:33:20 -05:00
// Create the socket connection.
2019-03-20 15:28:18 -05:00
const socket = createClient(window.location.href);
2019-03-21 18:33:20 -05:00
// Create the graphics canvas.
2019-03-20 15:28:18 -05:00
const appNode = document.querySelector('.app');
appNode.appendChild(renderer.element);
2019-03-21 18:33:20 -05:00
// Input messages.
const messageHandle = setInterval(() => {
2019-03-20 15:28:18 -05:00
if (actionState !== actionRegistry.state()) {
actionState = actionRegistry.state();
socket.send({
type: 'input',
payload: actionState.toJS()
});
}
2019-03-21 18:33:20 -05:00
}, 1000 / 60);
// Prediction.
2019-03-23 23:38:14 -05:00
let lastTime = performance.now();
const predictionHandle = setInterval(() => {
const now = performance.now();
const elapsed = (now - lastTime) / 1000;
lastTime = now;
if (hasSelfEntity()) {
selfEntity.inputState = actionState.toJS();
}
entityList.tick(elapsed);
}, 1000 / 80);
2019-03-21 18:33:20 -05:00
// State updates.
2019-03-20 15:28:18 -05:00
let dirty = false;
function onMessage({type, payload}) {
switch (type) {
case 'state-update':
2019-03-20 21:09:32 -05:00
if (payload.selfEntity) {
selfEntity = payload.selfEntity;
2019-03-20 21:09:32 -05:00
}
stateSynchronizer.acceptStateChange(payload);
2019-03-20 15:28:18 -05:00
dirty = true;
break;
default:
}
}
socket.on('message', onMessage);
// Render.
function render() {
stage.tick();
if (!dirty) {
return;
}
renderer.render(stage);
dirty = false;
}
const renderHandle = setInterval(render, 1000 / 60);
2019-03-21 18:33:20 -05:00
// Hot reloading.
if (module.hot) {
module.hot.accept((error) => {
console.error(error);
});
module.hot.dispose(() => {
entityList.destroy();
appNode.removeChild(renderer.element);
stage.destroy();
renderer.destroy();
});
}