spoint 0.1.60 → 0.1.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client/app.js
CHANGED
|
@@ -1033,20 +1033,10 @@ const client = new PhysicsNetworkClient({
|
|
|
1033
1033
|
predictionEnabled: true,
|
|
1034
1034
|
smoothInterpolation: true,
|
|
1035
1035
|
onStateUpdate: (state) => {
|
|
1036
|
-
const
|
|
1037
|
-
for (const p of smoothState.players) {
|
|
1036
|
+
for (const p of state.players) {
|
|
1038
1037
|
if (!playerMeshes.has(p.id)) createPlayerVRM(p.id)
|
|
1039
|
-
const mesh = playerMeshes.get(p.id)
|
|
1040
|
-
const feetOff = mesh?.userData?.feetOffset ?? 1.3
|
|
1041
|
-
const tx = p.position[0], ty = p.position[1] - feetOff, tz = p.position[2]
|
|
1042
|
-
const existingTarget = playerTargets.get(p.id)
|
|
1043
|
-
if (existingTarget) { existingTarget.x = tx; existingTarget.y = ty; existingTarget.z = tz }
|
|
1044
|
-
else playerTargets.set(p.id, { x: tx, y: ty, z: tz })
|
|
1045
|
-
playerStates.set(p.id, p)
|
|
1046
|
-
const dx = tx - mesh.position.x, dy = ty - mesh.position.y, dz = tz - mesh.position.z
|
|
1047
|
-
if (!mesh.userData.initialized || dx * dx + dy * dy + dz * dz > 100) { mesh.position.set(tx, ty, tz); mesh.userData.initialized = true }
|
|
1048
1038
|
}
|
|
1049
|
-
for (const e of
|
|
1039
|
+
for (const e of state.entities) {
|
|
1050
1040
|
const mesh = entityMeshes.get(e.id)
|
|
1051
1041
|
if (mesh && e.position) {
|
|
1052
1042
|
const et = entityTargets.get(e.id)
|
|
@@ -1057,11 +1047,10 @@ const client = new PhysicsNetworkClient({
|
|
|
1057
1047
|
}
|
|
1058
1048
|
if (!entityMeshes.has(e.id)) loadEntityModel(e.id, e)
|
|
1059
1049
|
}
|
|
1060
|
-
rebuildEntityHierarchy(smoothState.entities)
|
|
1061
1050
|
latestState = state
|
|
1062
1051
|
if (!firstSnapshotReceived) {
|
|
1063
1052
|
firstSnapshotReceived = true
|
|
1064
|
-
for (const e of
|
|
1053
|
+
for (const e of state.entities) {
|
|
1065
1054
|
if (e.model && !entityMeshes.has(e.id)) firstSnapshotEntityPending.add(e.id)
|
|
1066
1055
|
}
|
|
1067
1056
|
checkAllLoaded()
|
|
@@ -1467,7 +1456,27 @@ function animate(timestamp) {
|
|
|
1467
1456
|
const frameDt = smoothDt
|
|
1468
1457
|
fpsFrames++
|
|
1469
1458
|
if (now - fpsLast >= 1000) { fpsDisplay = fpsFrames; fpsFrames = 0; fpsLast = now }
|
|
1470
|
-
const
|
|
1459
|
+
const rttMs = client.getRTT?.() || 0
|
|
1460
|
+
const lerpConstant = rttMs > 100 ? 24.0 : 16.0
|
|
1461
|
+
const lerpFactor = 1.0 - Math.exp(-lerpConstant * frameDt)
|
|
1462
|
+
const smoothState = client.getSmoothState()
|
|
1463
|
+
for (const p of smoothState.players) {
|
|
1464
|
+
if (!playerMeshes.has(p.id)) continue
|
|
1465
|
+
const mesh = playerMeshes.get(p.id)
|
|
1466
|
+
const feetOff = mesh?.userData?.feetOffset ?? 1.3
|
|
1467
|
+
const tx = p.position[0], ty = p.position[1] - feetOff, tz = p.position[2]
|
|
1468
|
+
const existingTarget = playerTargets.get(p.id)
|
|
1469
|
+
if (existingTarget) { existingTarget.x = tx; existingTarget.y = ty; existingTarget.z = tz }
|
|
1470
|
+
else playerTargets.set(p.id, { x: tx, y: ty, z: tz })
|
|
1471
|
+
playerStates.set(p.id, p)
|
|
1472
|
+
if (!mesh.userData.initialized) { mesh.position.set(tx, ty, tz); mesh.userData.initialized = true }
|
|
1473
|
+
}
|
|
1474
|
+
for (const e of smoothState.entities) {
|
|
1475
|
+
const mesh = entityMeshes.get(e.id)
|
|
1476
|
+
if (mesh && e.position) mesh.position.set(e.position[0], e.position[1], e.position[2])
|
|
1477
|
+
if (mesh && e.rotation) mesh.quaternion.set(e.rotation[0], e.rotation[1], e.rotation[2], e.rotation[3])
|
|
1478
|
+
}
|
|
1479
|
+
if (smoothState.entities.length > 0) rebuildEntityHierarchy(smoothState.entities)
|
|
1471
1480
|
playerTargets.forEach((target, id) => {
|
|
1472
1481
|
const mesh = playerMeshes.get(id)
|
|
1473
1482
|
if (!mesh) return
|
package/package.json
CHANGED
|
@@ -34,7 +34,7 @@ export class JitterBuffer {
|
|
|
34
34
|
|
|
35
35
|
while (this.buffer.length > this.maxSize) this.buffer.shift()
|
|
36
36
|
|
|
37
|
-
const maxAge = Math.max(
|
|
37
|
+
const maxAge = Math.max(400, this.rtt + this.jitter * 3 + 150)
|
|
38
38
|
const cutoff = now - maxAge
|
|
39
39
|
while (this.buffer.length > 0 && this.buffer[0].clientTime < cutoff) this.buffer.shift()
|
|
40
40
|
}
|
|
@@ -111,8 +111,9 @@ export class JitterBuffer {
|
|
|
111
111
|
updateRTT(pingTime, pongTime) {
|
|
112
112
|
const instant = pongTime - pingTime
|
|
113
113
|
this.rttVariance = this.rttVariance * 0.75 + Math.abs(instant - this.rtt) * 0.25
|
|
114
|
-
|
|
115
|
-
this.
|
|
114
|
+
const alpha = instant > this.rtt ? 0.5 : 0.1
|
|
115
|
+
this.rtt = this.rtt * (1 - alpha) + instant * alpha
|
|
116
|
+
this.targetDelay = Math.min(250, this.baseDelay + this.rtt * 0.5 + this.jitter * 2)
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
getBufferHealth() { return this.buffer.length }
|
|
@@ -56,15 +56,14 @@ export class MessageHandler {
|
|
|
56
56
|
const oldPlayerId = this._playerId
|
|
57
57
|
this._playerId = payload.playerId
|
|
58
58
|
this._currentTick = payload.tick
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
this.
|
|
63
|
-
}
|
|
64
|
-
if (!this._predEngine) {
|
|
65
|
-
this._predEngine = new PredictionEngine(this._config.tickRate || 128)
|
|
66
|
-
this._predEngine.init(this._playerId)
|
|
59
|
+
snapProc?.clear()
|
|
60
|
+
if (this._smoothInterp) {
|
|
61
|
+
this._smoothInterp.reset()
|
|
62
|
+
this._smoothInterp.setLocalPlayer(this._playerId)
|
|
67
63
|
}
|
|
64
|
+
if (oldPlayerId) this._callbacks.onPlayerLeft?.(oldPlayerId)
|
|
65
|
+
this._predEngine = new PredictionEngine(this._config.tickRate || 128)
|
|
66
|
+
this._predEngine.init(this._playerId)
|
|
68
67
|
if (this._config.smoothInterpolation !== false && !this._smoothInterp) {
|
|
69
68
|
this._smoothInterp = new SmoothInterpolation({ predictionEnabled: this._config.predictionEnabled !== false })
|
|
70
69
|
this._smoothInterp.setLocalPlayer(this._playerId)
|
|
@@ -2,35 +2,22 @@ export class ReconciliationEngine {
|
|
|
2
2
|
constructor(config = {}) {
|
|
3
3
|
this.correctionThreshold = config.correctionThreshold || 0.01
|
|
4
4
|
this.correctionSpeed = config.correctionSpeed || 0.5
|
|
5
|
-
this.lastReconcileTime = 0
|
|
6
|
-
this.reconcileInterval = config.reconcileInterval || 100
|
|
7
5
|
}
|
|
8
6
|
|
|
9
7
|
reconcile(serverState, localState, tick) {
|
|
10
|
-
const now = Date.now()
|
|
11
|
-
if (now - this.lastReconcileTime < this.reconcileInterval) {
|
|
12
|
-
return { needsCorrection: false, correction: null }
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
this.lastReconcileTime = now
|
|
16
|
-
|
|
17
8
|
const divergence = this.calculateDivergence(serverState, localState)
|
|
18
|
-
|
|
19
9
|
if (divergence < this.correctionThreshold) {
|
|
20
10
|
return { needsCorrection: false, divergence }
|
|
21
11
|
}
|
|
22
|
-
|
|
23
12
|
const correction = this.generateCorrection(serverState, localState)
|
|
24
13
|
return { needsCorrection: true, correction, divergence }
|
|
25
14
|
}
|
|
26
15
|
|
|
27
16
|
calculateDivergence(serverState, localState) {
|
|
28
17
|
if (!serverState || !localState) return 0
|
|
29
|
-
|
|
30
18
|
const dx = serverState.position[0] - localState.position[0]
|
|
31
19
|
const dy = serverState.position[1] - localState.position[1]
|
|
32
20
|
const dz = serverState.position[2] - localState.position[2]
|
|
33
|
-
|
|
34
21
|
return Math.sqrt(dx * dx + dy * dy + dz * dz)
|
|
35
22
|
}
|
|
36
23
|
|
|
@@ -12,7 +12,6 @@ export class SmoothInterpolation {
|
|
|
12
12
|
this.entityKalmanConfig = config.entityKalman || {
|
|
13
13
|
positionQ: 2.0, velocityQ: 4.0, positionR: 0.01, velocityR: 0.5
|
|
14
14
|
}
|
|
15
|
-
|
|
16
15
|
this.localPlayerId = null
|
|
17
16
|
this.predictionEnabled = config.predictionEnabled !== false
|
|
18
17
|
this._lastDisplayTime = 0
|