Fix net code
This commit is contained in:
parent
1f10b9c750
commit
a1fb6755c7
23 changed files with 408 additions and 236 deletions
|
|
@ -0,0 +1,22 @@
|
|||
import { Circle } from 'shared';
|
||||
import { LinearInterpolator } from './linear-interpolator';
|
||||
import { Vec2Interpolator } from './vec2-interpolator';
|
||||
|
||||
export class CircleInterpolator {
|
||||
private center: Vec2Interpolator;
|
||||
private radius: LinearInterpolator;
|
||||
|
||||
constructor(currentValue: Circle) {
|
||||
this.center = new Vec2Interpolator(currentValue.center);
|
||||
this.radius = new LinearInterpolator(currentValue.radius);
|
||||
}
|
||||
|
||||
public addFrame(value: Circle, rateOfChange: Circle) {
|
||||
this.center.addFrame(value.center, rateOfChange.center);
|
||||
this.radius.addFrame(value.radius, rateOfChange.radius);
|
||||
}
|
||||
|
||||
public getValue(deltaTime: number): Circle {
|
||||
return new Circle(this.center.getValue(deltaTime), this.radius.getValue(deltaTime));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import { last, mix } from 'shared';
|
||||
import { serverTimeline } from '../server-timeline';
|
||||
|
||||
interface Frame {
|
||||
time: number;
|
||||
value: number;
|
||||
velocity: number;
|
||||
}
|
||||
|
||||
// How far past the newest snapshot the value may coast on its last known
|
||||
// velocity (network hiccup) before freezing in place.
|
||||
const maxCoastSeconds = 0.06;
|
||||
|
||||
// When fresh snapshots arrive after a coast, they usually disagree with where
|
||||
// the coast ended up; the difference decays away with this time constant
|
||||
// instead of being shown as a jump.
|
||||
const blendSeconds = 0.12;
|
||||
|
||||
// At 25 snapshots per second this spans over a second of state, far more than
|
||||
// rendering ever needs; it only bounds memory while the tab is hidden and
|
||||
// snapshots keep arriving without being consumed.
|
||||
const maxFrames = 32;
|
||||
|
||||
/**
|
||||
* Replays a server-streamed scalar by interpolating between timestamped
|
||||
* snapshots at the shared timeline's render time, which trails the newest
|
||||
* snapshot. The streamed rate of change is only used to coast briefly when
|
||||
* the buffer runs dry.
|
||||
*/
|
||||
export class LinearInterpolator {
|
||||
private frames: Array<Frame> = [];
|
||||
private blendOffset = 0;
|
||||
private lastValue: number;
|
||||
private isCoasting = false;
|
||||
|
||||
constructor(private readonly initialValue: number) {
|
||||
this.lastValue = initialValue;
|
||||
}
|
||||
|
||||
public addFrame(value: number, rateOfChange: number) {
|
||||
const time = serverTimeline.snapshotTime;
|
||||
const newest = last(this.frames);
|
||||
const wasCoasting = this.isCoasting;
|
||||
|
||||
if (newest && time <= newest.time) {
|
||||
this.frames[this.frames.length - 1] = {
|
||||
time: newest.time,
|
||||
value,
|
||||
velocity: rateOfChange,
|
||||
};
|
||||
} else {
|
||||
this.frames.push({ time, value, velocity: rateOfChange });
|
||||
if (this.frames.length > maxFrames) {
|
||||
this.frames.shift();
|
||||
}
|
||||
}
|
||||
|
||||
if (wasCoasting) {
|
||||
// Continue from where the coast left off and let the offset decay,
|
||||
// instead of jumping onto the freshly revealed trajectory.
|
||||
this.blendOffset = this.lastValue - this.sample(serverTimeline.renderTime);
|
||||
}
|
||||
}
|
||||
|
||||
public getValue(deltaTimeInSeconds: number): number {
|
||||
this.blendOffset *= Math.exp(-deltaTimeInSeconds / blendSeconds);
|
||||
return (this.lastValue = this.sample(serverTimeline.renderTime) + this.blendOffset);
|
||||
}
|
||||
|
||||
private sample(time: number): number {
|
||||
if (this.frames.length === 0) {
|
||||
return this.initialValue;
|
||||
}
|
||||
|
||||
while (this.frames.length >= 2 && this.frames[1].time <= time) {
|
||||
this.frames.shift();
|
||||
}
|
||||
|
||||
const [current, next] = this.frames;
|
||||
this.isCoasting = false;
|
||||
|
||||
if (time <= current.time) {
|
||||
return current.value;
|
||||
}
|
||||
|
||||
if (next) {
|
||||
return mix(
|
||||
current.value,
|
||||
next.value,
|
||||
(time - current.time) / (next.time - current.time),
|
||||
);
|
||||
}
|
||||
|
||||
this.isCoasting = true;
|
||||
return (
|
||||
current.value + current.velocity * Math.min(time - current.time, maxCoastSeconds)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import { vec2 } from 'gl-matrix';
|
||||
import { LinearInterpolator } from './linear-interpolator';
|
||||
|
||||
export class Vec2Interpolator {
|
||||
private x: LinearInterpolator;
|
||||
private y: LinearInterpolator;
|
||||
|
||||
constructor(currentValue: vec2) {
|
||||
this.x = new LinearInterpolator(currentValue.x);
|
||||
this.y = new LinearInterpolator(currentValue.y);
|
||||
}
|
||||
|
||||
public addFrame(value: vec2, rateOfChange: vec2) {
|
||||
this.x.addFrame(value.x, rateOfChange.x);
|
||||
this.y.addFrame(value.y, rateOfChange.y);
|
||||
}
|
||||
|
||||
public getValue(deltaTime: number): vec2 {
|
||||
return vec2.fromValues(this.x.getValue(deltaTime), this.y.getValue(deltaTime));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue