120 lines
3.1 KiB
JavaScript
120 lines
3.1 KiB
JavaScript
let authoritative = [];
|
|
let duration = 0;
|
|
let last = performance.now();
|
|
let latest = null;
|
|
let location = 0;
|
|
let penultimate;
|
|
let tracking = [];
|
|
|
|
const interpolate = () => {
|
|
const now = performance.now();
|
|
const elapsed = (now - last) / 1000;
|
|
last = now;
|
|
if (authoritative.length > 0) {
|
|
for (const packet of authoritative) {
|
|
postMessage(packet);
|
|
}
|
|
authoritative = [];
|
|
}
|
|
if (tracking.length > 0) {
|
|
location += elapsed;
|
|
const fraction = location / duration;
|
|
const [from, to] = [penultimate.payload.ecs, latest.payload.ecs];
|
|
const interpolated = {};
|
|
for (const {entityId, componentName, properties} of tracking) {
|
|
if (!interpolated[entityId]) {
|
|
interpolated[entityId] = {};
|
|
}
|
|
if (!interpolated[entityId][componentName]) {
|
|
interpolated[entityId][componentName] = {};
|
|
}
|
|
for (const property of properties) {
|
|
if (
|
|
!(property in from[entityId][componentName])
|
|
|| !(property in to[entityId][componentName])
|
|
) {
|
|
continue;
|
|
}
|
|
interpolated[entityId][componentName][property] = (
|
|
from[entityId][componentName][property]
|
|
+ (
|
|
fraction
|
|
* (to[entityId][componentName][property] - from[entityId][componentName][property])
|
|
)
|
|
);
|
|
}
|
|
}
|
|
postMessage({
|
|
type: 'Tick',
|
|
payload: {
|
|
...penultimate.payload,
|
|
ecs: interpolated,
|
|
elapsed,
|
|
frame: penultimate.payload.frame + fraction,
|
|
},
|
|
});
|
|
}
|
|
requestAnimationFrame(interpolate);
|
|
}
|
|
requestAnimationFrame(interpolate);
|
|
|
|
onmessage = async (event) => {
|
|
const packet = event.data;
|
|
switch (packet.type) {
|
|
case 'EcsChange': {
|
|
authoritative = [];
|
|
latest = null;
|
|
tracking = [];
|
|
postMessage(packet);
|
|
break;
|
|
}
|
|
case 'Tick': {
|
|
penultimate = latest;
|
|
latest = packet;
|
|
if (penultimate) {
|
|
duration = penultimate.payload.elapsed;
|
|
location = 0;
|
|
tracking = [];
|
|
const [from, to] = [penultimate.payload.ecs, latest.payload.ecs];
|
|
for (const entityId in from) {
|
|
for (const componentName in from[entityId]) {
|
|
if (
|
|
['Camera', 'Position'].includes(componentName)
|
|
&& to[entityId]?.[componentName]
|
|
) {
|
|
tracking.push({
|
|
entityId,
|
|
componentName,
|
|
properties: ['x', 'y'],
|
|
});
|
|
}
|
|
if (
|
|
['Sprite'].includes(componentName)
|
|
&& to[entityId]?.[componentName]
|
|
) {
|
|
tracking.push({
|
|
entityId,
|
|
componentName,
|
|
properties: ['alpha', 'scaleX', 'scaleY'],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
authoritative.push({
|
|
type: 'Tick',
|
|
payload: {
|
|
...penultimate.payload,
|
|
elapsed: last ? (performance.now() - last) / 1000 : 0,
|
|
},
|
|
});
|
|
last = performance.now();
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
postMessage(packet);
|
|
break;
|
|
}
|
|
}
|
|
};
|