refactor: client code

This commit is contained in:
cha0s 2024-11-05 11:52:34 -06:00
parent e8c9633a4e
commit 11457a3c75
6 changed files with 111 additions and 159 deletions

View File

@ -1,92 +1,27 @@
import Client from '@/silphius/net/client.js';
import {decode, encode} from '@/silphius/net/packets/index.js';
import {CLIENT_INTERPOLATION, CLIENT_PREDICTION} from '@/lib/constants.js';
export default class LocalClient extends Client {
server = null;
interpolator = null;
predictor = null;
async connect() {
await super.connect();
this.server = new Worker(
new URL('../server/worker.js', import.meta.url),
{type: 'module'},
);
if (CLIENT_INTERPOLATION) {
this.interpolator = new Worker(
new URL('./interpolator.js', import.meta.url),
{type: 'module'},
);
this.interpolator.addEventListener('message', (event) => {
const packet = event.data;
if (CLIENT_PREDICTION) {
this.predictor.postMessage([1, packet]);
}
else {
this.accept(packet);
}
});
}
if (CLIENT_PREDICTION) {
this.predictor = new Worker(
new URL('./predictor.js', import.meta.url),
{type: 'module'},
);
// loaded
await new Promise((resolve) => {
this.predictor.addEventListener('message', resolve, {once: true});
});
this.predictor.addEventListener('message', (event) => {
const [flow, packet] = event.data;
switch (flow) {
case 0: {
const packed = encode(packet);
this.throughput.$$up += packed.byteLength;
this.server.postMessage(packed);
break;
}
case 1: {
this.accept(packet);
break;
}
}
});
}
this.server.addEventListener('message', (event) => {
if (0 === event.data) {
this.server.terminate();
this.server = null;
return;
}
this.throughput.$$down += event.data.byteLength;
const packet = decode(event.data);
if (CLIENT_INTERPOLATION) {
this.interpolator.postMessage(packet);
}
else if (CLIENT_PREDICTION) {
this.predictor.postMessage([1, packet]);
}
else {
this.accept(packet);
}
this.receive(event.data);
});
}
disconnect() {
super.disconnect();
this.server.postMessage(0);
if (CLIENT_INTERPOLATION) {
this.interpolator.terminate();
}
if (CLIENT_PREDICTION) {
this.predictor.terminate();
}
}
transmit(packet) {
if (CLIENT_PREDICTION) {
this.predictor.postMessage([0, packet]);
}
else {
const packed = encode(packet);
this.throughput.$$up += packed.byteLength;
this.server.postMessage(packed);
}
transmit(packed) {
this.server.postMessage(packed);
}
}

View File

@ -1,66 +1,13 @@
import Client from '@/silphius/net/client.js';
import {decode, encode} from '@/silphius/net/packets/index.js';
import {CLIENT_INTERPOLATION, CLIENT_PREDICTION} from '@/lib/constants.js';
export default class RemoteClient extends Client {
socket = null;
interpolator = null;
predictor = null;
async connect(host) {
if (CLIENT_INTERPOLATION) {
this.interpolator = new Worker(
new URL('./interpolator.js', import.meta.url),
{type: 'module'},
);
this.interpolator.addEventListener('message', (event) => {
const packet = event.data;
if (CLIENT_PREDICTION) {
this.predictor.postMessage([1, packet]);
}
else {
this.accept(packet);
}
});
}
if (CLIENT_PREDICTION) {
this.predictor = new Worker(
new URL('./predictor.js', import.meta.url),
{type: 'module'},
);
// loaded
await new Promise((resolve) => {
this.predictor.addEventListener('message', resolve, {once: true});
});
this.predictor.addEventListener('message', (event) => {
const [flow, packet] = event.data;
switch (flow) {
case 0: {
const packed = encode(packet);
this.throughput.$$up += packed.byteLength;
this.socket.send(packed);
break;
}
case 1: {
this.accept(packet);
break;
}
}
});
}
await super.connect();
this.socket = new WebSocket(`//${host}/silphius`);
this.socket.binaryType = 'arraybuffer';
this.socket.addEventListener('message', (event) => {
this.throughput.$$down += event.data.byteLength;
const packet = decode(event.data);
if (CLIENT_INTERPOLATION) {
this.interpolator.postMessage(packet);
}
else if (CLIENT_PREDICTION) {
this.predictor.postMessage([1, packet]);
}
else {
this.accept(packet);
}
this.receive(event.data);
});
this.socket.addEventListener('close', () => {
this.accept({type: 'ConnectionStatus', payload: 'aborted'});
@ -72,22 +19,7 @@ export default class RemoteClient extends Client {
this.accept({type: 'ConnectionStatus', payload: 'connected'});
});
}
disconnect() {
if (CLIENT_INTERPOLATION) {
this.interpolator.terminate();
}
if (CLIENT_PREDICTION) {
this.predictor.terminate();
}
}
transmit(packet) {
if (CLIENT_PREDICTION) {
this.predictor.postMessage([0, packet]);
}
else {
const packed = encode(packet);
this.throughput.$$up += packed.byteLength;
this.socket.send(packed);
}
transmit(packed) {
this.socket.send(packed);
}
}

View File

@ -1,8 +1,13 @@
import {CLIENT_LATENCY, CLIENT_PREDICTION} from '@/lib/constants.js';
import {CLIENT_LATENCY, CLIENT_INTERPOLATION, CLIENT_PREDICTION} from '@/lib/constants.js';
import EventEmitter from '@/lib/event-emitter.js';
import {decode, encode} from '@/silphius/net/packets/index.js';
import {Flow} from './constants.js';
export default class Client {
emitter = new EventEmitter();
interpolator = null;
predictor = null;
rtt = 0;
throughput = {$$down: 0, down: 0, $$up: 0, up: 0};
constructor() {
@ -24,17 +29,90 @@ export default class Client {
addPacketListener(type, listener) {
this.emitter.addListener(type, listener);
}
async connect() {
if (CLIENT_INTERPOLATION) {
this.interpolator = new Worker(
new URL('./interpolator.js', import.meta.url),
{type: 'module'},
);
this.interpolator.addEventListener('message', (event) => {
const packet = event.data;
if (CLIENT_PREDICTION) {
this.predictor.postMessage([1, packet]);
}
else {
this.accept(packet);
}
});
}
if (CLIENT_PREDICTION) {
this.predictor = new Worker(
new URL('./predictor.js', import.meta.url),
{type: 'module'},
);
// loaded
await new Promise((resolve) => {
this.predictor.addEventListener('message', resolve, {once: true});
});
this.predictor.addEventListener('message', (event) => {
const [flow, packet] = event.data;
switch (flow) {
case Flow.UP: {
const packed = encode(packet);
this.throughput.$$up += packed.byteLength;
this.transmit(packed);
break;
}
case Flow.DOWN: {
this.accept(packet);
break;
}
}
});
}
}
disconnect() {
if (CLIENT_INTERPOLATION) {
this.interpolator?.terminate();
this.interpolator = null;
}
if (CLIENT_PREDICTION) {
this.predictor?.terminate();
this.predictor = null;
}
}
receive(packed) {
this.throughput.$$down += packed.byteLength;
const packet = decode(packed);
if (CLIENT_INTERPOLATION) {
this.interpolator.postMessage(packet);
}
else if (CLIENT_PREDICTION) {
this.predictor.postMessage([Flow.DOWN, packet]);
}
else {
this.accept(packet);
}
}
removePacketListener(type, listener) {
this.emitter.removeListener(type, listener);
}
send(packet) {
if (CLIENT_LATENCY > 0 && !CLIENT_PREDICTION) {
setTimeout(() => {
this.transmit(packet);
}, CLIENT_LATENCY);
const transmitOrPredict = () => {
if (CLIENT_PREDICTION) {
this.predictor.postMessage([Flow.UP, packet]);
}
else {
const packed = encode(packet);
this.throughput.$$up += packed.byteLength;
this.transmit(packed);
}
}
if (CLIENT_LATENCY > 0) {
setTimeout(transmitOrPredict, CLIENT_LATENCY);
}
else {
this.transmit(packet);
transmitOrPredict();
}
}
}

View File

@ -0,0 +1,4 @@
export const Flow = {
UP: 0,
DOWN: 1,
};

View File

@ -3,15 +3,18 @@ import Ecs from '@/silphius/ecs/ecs.js';
import Systems from '@/silphius/ecs/systems/index.js';
import {get, loadResources, readAsset} from '@/lib/resources.js';
import {Flow} from './constants.js';
class PredictionEcs extends Ecs {
readAsset(path) {
return readAsset(path) ?? new ArrayBuffer(0);
}
}
const Flow = {
UP: 0,
DOWN: 1,
const Action = {
ACTIVE: 0,
FINISHING: 1,
FINISHED: 2,
};
const actions = new Map();
@ -24,7 +27,7 @@ function applyClientActions(elapsed) {
const {Controlled} = main;
const finished = [];
for (const [id, action] of actions) {
if (0 === action.finished && !action.ack) {
if (Action.ACTIVE === action.state && !action.ack) {
if (!Controlled.locked) {
switch (action.action.type) {
case 'moveUp':
@ -37,7 +40,7 @@ function applyClientActions(elapsed) {
}
}
}
if (1 === action.finished) {
if (Action.FINISHING === action.state) {
if (!Controlled.locked) {
switch (action.action.type) {
case 'moveUp':
@ -49,12 +52,12 @@ function applyClientActions(elapsed) {
}
}
}
action.finished = 2;
action.state = Action.FINISHED;
}
if (!action.ack) {
action.stop += elapsed;
}
if (action.ack && 2 === action.finished) {
if (action.ack && Action.FINISHED === action.state) {
action.start += elapsed;
if (action.start >= action.stop) {
finished.push(id);
@ -85,7 +88,7 @@ onmessage = (event) => {
if (0 === packet.payload.value) {
const ack = pending.get(packet.payload.type);
const action = actions.get(ack);
action.finished = 1;
action.state = Action.FINISHING;
pending.delete(packet.payload.type);
}
else {
@ -93,7 +96,7 @@ onmessage = (event) => {
const tx = {
action: packet.payload,
ack: false,
finished: 0,
state: 0,
start: now,
stop: now,
};
@ -106,7 +109,7 @@ onmessage = (event) => {
break;
}
}
postMessage([0, packet]);
postMessage([Flow.UP, packet]);
break;
}
case Flow.DOWN: {
@ -154,7 +157,7 @@ onmessage = (event) => {
break;
}
}
postMessage([1, packet]);
postMessage([Flow.DOWN, packet]);
break;
}
}