spoint 0.1.0 → 0.1.10
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/README.md +134 -209
- package/SKILL.md +95 -0
- package/apps/environment/index.js +200 -1
- package/apps/environment/models/decorative/.gitkeep +0 -0
- package/apps/environment/models/hazards/.gitkeep +0 -0
- package/apps/environment/models/interactive/.gitkeep +0 -0
- package/apps/environment/models/structures/.gitkeep +0 -0
- package/apps/environment/smartObjects.js +114 -0
- package/apps/interactable/index.js +155 -0
- package/apps/physics-crate/index.js +15 -9
- package/apps/power-crate/index.js +18 -12
- package/apps/tps-game/$GDUPI.vrm +0 -0
- package/apps/tps-game/Cleetus.vrm +0 -0
- package/apps/tps-game/index.js +185 -27
- package/apps/world/index.js +68 -22
- package/bin/create-app.js +337 -0
- package/client/ARControls.js +301 -0
- package/client/LoadingManager.js +117 -0
- package/client/MobileControls.js +1122 -0
- package/client/anim-lib.glb +0 -0
- package/client/animation.js +306 -0
- package/client/app.js +1341 -65
- package/client/camera.js +191 -33
- package/client/createLoadingScreen.js +69 -0
- package/client/editor/bridge.js +113 -0
- package/client/editor/css/main.css +794 -0
- package/client/editor/images/rotate.svg +4 -0
- package/client/editor/images/scale.svg +4 -0
- package/client/editor/images/translate.svg +4 -0
- package/client/editor/index.html +103 -0
- package/client/editor/js/Command.js +41 -0
- package/client/editor/js/Config.js +81 -0
- package/client/editor/js/Editor.js +785 -0
- package/client/editor/js/EditorControls.js +438 -0
- package/client/editor/js/History.js +321 -0
- package/client/editor/js/Loader.js +987 -0
- package/client/editor/js/LoaderUtils.js +90 -0
- package/client/editor/js/Menubar.Add.js +510 -0
- package/client/editor/js/Menubar.Edit.js +145 -0
- package/client/editor/js/Menubar.File.js +466 -0
- package/client/editor/js/Menubar.Help.js +73 -0
- package/client/editor/js/Menubar.Status.js +51 -0
- package/client/editor/js/Menubar.View.js +183 -0
- package/client/editor/js/Menubar.js +27 -0
- package/client/editor/js/Player.js +53 -0
- package/client/editor/js/Resizer.js +58 -0
- package/client/editor/js/Script.js +503 -0
- package/client/editor/js/Selector.js +102 -0
- package/client/editor/js/Sidebar.Geometry.BoxGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.BufferGeometry.js +115 -0
- package/client/editor/js/Sidebar.Geometry.CapsuleGeometry.js +97 -0
- package/client/editor/js/Sidebar.Geometry.CircleGeometry.js +97 -0
- package/client/editor/js/Sidebar.Geometry.CylinderGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.DodecahedronGeometry.js +73 -0
- package/client/editor/js/Sidebar.Geometry.ExtrudeGeometry.js +196 -0
- package/client/editor/js/Sidebar.Geometry.IcosahedronGeometry.js +73 -0
- package/client/editor/js/Sidebar.Geometry.LatheGeometry.js +98 -0
- package/client/editor/js/Sidebar.Geometry.Modifiers.js +73 -0
- package/client/editor/js/Sidebar.Geometry.OctahedronGeometry.js +74 -0
- package/client/editor/js/Sidebar.Geometry.PlaneGeometry.js +97 -0
- package/client/editor/js/Sidebar.Geometry.RingGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.ShapeGeometry.js +76 -0
- package/client/editor/js/Sidebar.Geometry.SphereGeometry.js +133 -0
- package/client/editor/js/Sidebar.Geometry.TetrahedronGeometry.js +74 -0
- package/client/editor/js/Sidebar.Geometry.TorusGeometry.js +109 -0
- package/client/editor/js/Sidebar.Geometry.TorusKnotGeometry.js +121 -0
- package/client/editor/js/Sidebar.Geometry.TubeGeometry.js +135 -0
- package/client/editor/js/Sidebar.Geometry.js +332 -0
- package/client/editor/js/Sidebar.Material.BooleanProperty.js +60 -0
- package/client/editor/js/Sidebar.Material.ColorProperty.js +87 -0
- package/client/editor/js/Sidebar.Material.ConstantProperty.js +62 -0
- package/client/editor/js/Sidebar.Material.MapProperty.js +249 -0
- package/client/editor/js/Sidebar.Material.NumberProperty.js +60 -0
- package/client/editor/js/Sidebar.Material.Program.js +73 -0
- package/client/editor/js/Sidebar.Material.RangeValueProperty.js +63 -0
- package/client/editor/js/Sidebar.Material.js +751 -0
- package/client/editor/js/Sidebar.Object.Animation.js +102 -0
- package/client/editor/js/Sidebar.Object.js +898 -0
- package/client/editor/js/Sidebar.Project.App.js +165 -0
- package/client/editor/js/Sidebar.Project.Image.js +225 -0
- package/client/editor/js/Sidebar.Project.Materials.js +82 -0
- package/client/editor/js/Sidebar.Project.Renderer.js +144 -0
- package/client/editor/js/Sidebar.Project.Video.js +242 -0
- package/client/editor/js/Sidebar.Project.js +31 -0
- package/client/editor/js/Sidebar.Properties.js +73 -0
- package/client/editor/js/Sidebar.Scene.js +585 -0
- package/client/editor/js/Sidebar.Script.js +129 -0
- package/client/editor/js/Sidebar.Settings.History.js +146 -0
- package/client/editor/js/Sidebar.Settings.Shortcuts.js +175 -0
- package/client/editor/js/Sidebar.Settings.js +60 -0
- package/client/editor/js/Sidebar.js +41 -0
- package/client/editor/js/Storage.js +98 -0
- package/client/editor/js/Strings.js +2028 -0
- package/client/editor/js/Toolbar.js +84 -0
- package/client/editor/js/Viewport.Controls.js +92 -0
- package/client/editor/js/Viewport.Info.js +136 -0
- package/client/editor/js/Viewport.Pathtracer.js +91 -0
- package/client/editor/js/Viewport.ViewHelper.js +39 -0
- package/client/editor/js/Viewport.XR.js +222 -0
- package/client/editor/js/Viewport.js +900 -0
- package/client/editor/js/commands/AddObjectCommand.js +68 -0
- package/client/editor/js/commands/AddScriptCommand.js +75 -0
- package/client/editor/js/commands/Commands.js +23 -0
- package/client/editor/js/commands/MoveObjectCommand.js +111 -0
- package/client/editor/js/commands/MultiCmdsCommand.js +85 -0
- package/client/editor/js/commands/RemoveObjectCommand.js +88 -0
- package/client/editor/js/commands/RemoveScriptCommand.js +81 -0
- package/client/editor/js/commands/SetColorCommand.js +73 -0
- package/client/editor/js/commands/SetGeometryCommand.js +87 -0
- package/client/editor/js/commands/SetGeometryValueCommand.js +70 -0
- package/client/editor/js/commands/SetMaterialColorCommand.js +86 -0
- package/client/editor/js/commands/SetMaterialCommand.js +79 -0
- package/client/editor/js/commands/SetMaterialMapCommand.js +143 -0
- package/client/editor/js/commands/SetMaterialRangeCommand.js +91 -0
- package/client/editor/js/commands/SetMaterialValueCommand.js +90 -0
- package/client/editor/js/commands/SetMaterialVectorCommand.js +79 -0
- package/client/editor/js/commands/SetPositionCommand.js +84 -0
- package/client/editor/js/commands/SetRotationCommand.js +84 -0
- package/client/editor/js/commands/SetScaleCommand.js +84 -0
- package/client/editor/js/commands/SetSceneCommand.js +103 -0
- package/client/editor/js/commands/SetScriptValueCommand.js +80 -0
- package/client/editor/js/commands/SetShadowValueCommand.js +73 -0
- package/client/editor/js/commands/SetUuidCommand.js +70 -0
- package/client/editor/js/commands/SetValueCommand.js +75 -0
- package/client/editor/js/libs/acorn/acorn.js +3236 -0
- package/client/editor/js/libs/acorn/acorn_loose.js +1299 -0
- package/client/editor/js/libs/acorn/walk.js +344 -0
- package/client/editor/js/libs/app/index.html +57 -0
- package/client/editor/js/libs/app.js +251 -0
- package/client/editor/js/libs/codemirror/addon/dialog.css +32 -0
- package/client/editor/js/libs/codemirror/addon/dialog.js +163 -0
- package/client/editor/js/libs/codemirror/addon/show-hint.css +36 -0
- package/client/editor/js/libs/codemirror/addon/show-hint.js +529 -0
- package/client/editor/js/libs/codemirror/addon/tern.css +87 -0
- package/client/editor/js/libs/codemirror/addon/tern.js +750 -0
- package/client/editor/js/libs/codemirror/codemirror.css +344 -0
- package/client/editor/js/libs/codemirror/codemirror.js +9849 -0
- package/client/editor/js/libs/codemirror/mode/glsl.js +233 -0
- package/client/editor/js/libs/codemirror/mode/javascript.js +959 -0
- package/client/editor/js/libs/codemirror/theme/monokai.css +41 -0
- package/client/editor/js/libs/esprima.js +6401 -0
- package/client/editor/js/libs/jsonlint.js +453 -0
- package/client/editor/js/libs/signals.min.js +14 -0
- package/client/editor/js/libs/tern-threejs/threejs.js +5031 -0
- package/client/editor/js/libs/ternjs/comment.js +87 -0
- package/client/editor/js/libs/ternjs/def.js +588 -0
- package/client/editor/js/libs/ternjs/doc_comment.js +401 -0
- package/client/editor/js/libs/ternjs/infer.js +1635 -0
- package/client/editor/js/libs/ternjs/polyfill.js +80 -0
- package/client/editor/js/libs/ternjs/signal.js +26 -0
- package/client/editor/js/libs/ternjs/tern.js +993 -0
- package/client/editor/js/libs/ui.js +1346 -0
- package/client/editor/js/libs/ui.three.js +855 -0
- package/client/facial-animation.js +455 -0
- package/client/index.html +7 -4
- package/client/loading.css +147 -0
- package/client/loading.html +25 -0
- package/client/style.css +251 -0
- package/package.json +7 -3
- package/server.js +9 -1
- package/src/apps/AppContext.js +1 -1
- package/src/apps/AppLoader.js +50 -37
- package/src/apps/AppRuntime.js +32 -8
- package/src/client/InputHandler.js +233 -0
- package/src/client/JitterBuffer.js +207 -0
- package/src/client/KalmanFilter.js +125 -0
- package/src/client/MessageHandler.js +101 -0
- package/src/client/PhysicsNetworkClient.js +141 -68
- package/src/client/ReconnectManager.js +62 -0
- package/src/client/SmoothInterpolation.js +127 -0
- package/src/client/SnapshotProcessor.js +144 -0
- package/src/connection/ConnectionManager.js +21 -3
- package/src/connection/SessionStore.js +13 -3
- package/src/index.client.js +4 -6
- package/src/netcode/EventLog.js +29 -15
- package/src/netcode/LagCompensator.js +25 -26
- package/src/netcode/NetworkState.js +4 -1
- package/src/netcode/PhysicsIntegration.js +20 -6
- package/src/netcode/PlayerManager.js +10 -2
- package/src/netcode/SnapshotEncoder.js +66 -19
- package/src/netcode/TickSystem.js +13 -4
- package/src/physics/World.js +66 -13
- package/src/protocol/msgpack.js +90 -63
- package/src/sdk/ReloadHandlers.js +12 -2
- package/src/sdk/ReloadManager.js +5 -0
- package/src/sdk/ServerHandlers.js +50 -11
- package/src/sdk/StaticHandler.js +22 -6
- package/src/sdk/TickHandler.js +101 -34
- package/src/sdk/scaffold.js +28 -0
- package/src/sdk/server.js +59 -33
- package/src/shared/movement.js +2 -1
- package/src/spatial/Octree.js +5 -0
- package/apps/interactive-door/index.js +0 -33
- package/apps/patrol-npc/index.js +0 -37
- package/src/connection/QualityMonitor.js +0 -46
- package/src/debug/StateInspector.js +0 -42
- package/src/index.js +0 -1
- package/src/index.server.js +0 -27
- package/src/protocol/Codec.js +0 -60
- package/src/protocol/SequenceTracker.js +0 -71
- package/src/sdk/ClientMessageHandler.js +0 -80
- package/src/sdk/client.js +0 -122
- package/world/kaira.glb +0 -0
- package/world/schwust.glb +0 -0
|
@@ -13,7 +13,8 @@ export class SessionStore {
|
|
|
13
13
|
token,
|
|
14
14
|
playerId,
|
|
15
15
|
state: state ? { ...state } : {},
|
|
16
|
-
createdAt: Date.now()
|
|
16
|
+
createdAt: Date.now(),
|
|
17
|
+
lastTouched: Date.now()
|
|
17
18
|
}
|
|
18
19
|
this.sessions.set(token, session)
|
|
19
20
|
this._setupExpire(token)
|
|
@@ -24,17 +25,20 @@ export class SessionStore {
|
|
|
24
25
|
const session = this.sessions.get(token)
|
|
25
26
|
if (!session) return false
|
|
26
27
|
if (data.state) Object.assign(session.state, data.state)
|
|
27
|
-
session.
|
|
28
|
+
session.lastTouched = Date.now()
|
|
29
|
+
this._refreshExpire(token)
|
|
28
30
|
return true
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
get(token) {
|
|
32
34
|
const session = this.sessions.get(token)
|
|
33
35
|
if (!session) return null
|
|
34
|
-
if (Date.now() - session.
|
|
36
|
+
if (Date.now() - session.lastTouched > this.ttl) {
|
|
35
37
|
this.destroy(token)
|
|
36
38
|
return null
|
|
37
39
|
}
|
|
40
|
+
session.lastTouched = Date.now()
|
|
41
|
+
this._refreshExpire(token)
|
|
38
42
|
return session
|
|
39
43
|
}
|
|
40
44
|
|
|
@@ -46,6 +50,12 @@ export class SessionStore {
|
|
|
46
50
|
this.timers.set(token, timer)
|
|
47
51
|
}
|
|
48
52
|
|
|
53
|
+
_refreshExpire(token) {
|
|
54
|
+
const old = this.timers.get(token)
|
|
55
|
+
if (old) clearTimeout(old)
|
|
56
|
+
this._setupExpire(token)
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
destroy(token) {
|
|
50
60
|
const timer = this.timers.get(token)
|
|
51
61
|
if (timer) clearTimeout(timer)
|
package/src/index.client.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export { InputHandler } from './client/InputHandler.js'
|
|
2
2
|
export { PhysicsNetworkClient } from './client/PhysicsNetworkClient.js'
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
|
|
6
|
-
export {
|
|
7
|
-
export { Codec } from './protocol/Codec.js'
|
|
8
|
-
export { SequenceTracker } from './protocol/SequenceTracker.js'
|
|
3
|
+
export { MSG } from './protocol/MessageTypes.js'
|
|
4
|
+
export { KalmanFilter3D, SmoothStateTracker } from './client/KalmanFilter.js'
|
|
5
|
+
export { JitterBuffer } from './client/JitterBuffer.js'
|
|
6
|
+
export { SmoothInterpolation } from './client/SmoothInterpolation.js'
|
package/src/netcode/EventLog.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export class EventLog {
|
|
2
2
|
constructor(config = {}) {
|
|
3
|
-
this.
|
|
4
|
-
this.
|
|
3
|
+
this._maxSize = config.maxSize || 1000
|
|
4
|
+
this._buf = new Array(this._maxSize)
|
|
5
|
+
this._head = 0
|
|
6
|
+
this._count = 0
|
|
5
7
|
this._nextId = 1
|
|
6
8
|
this._recording = true
|
|
7
9
|
}
|
|
@@ -16,13 +18,19 @@ export class EventLog {
|
|
|
16
18
|
data,
|
|
17
19
|
meta: { actor: meta.actor || null, reason: meta.reason || null, context: meta.context || null, sourceApp: meta.sourceApp || null, sourceEntity: meta.sourceEntity || null, causalEventId: meta.causalEventId || null, ...meta }
|
|
18
20
|
}
|
|
19
|
-
this.
|
|
20
|
-
|
|
21
|
+
this._buf[this._head] = event
|
|
22
|
+
this._head = (this._head + 1) % this._maxSize
|
|
23
|
+
if (this._count < this._maxSize) this._count++
|
|
21
24
|
return event
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
_toArray() {
|
|
28
|
+
if (this._count < this._maxSize) return this._buf.slice(0, this._count)
|
|
29
|
+
return [...this._buf.slice(this._head), ...this._buf.slice(0, this._head)]
|
|
30
|
+
}
|
|
31
|
+
|
|
24
32
|
query(filter = {}) {
|
|
25
|
-
return this.
|
|
33
|
+
return this._toArray().filter(e => {
|
|
26
34
|
if (filter.type && e.type !== filter.type) return false
|
|
27
35
|
if (filter.tick !== undefined && e.tick !== filter.tick) return false
|
|
28
36
|
if (filter.tickRange && (e.tick < filter.tickRange[0] || e.tick > filter.tickRange[1])) return false
|
|
@@ -34,30 +42,36 @@ export class EventLog {
|
|
|
34
42
|
}
|
|
35
43
|
|
|
36
44
|
getRange(startTick, endTick) {
|
|
37
|
-
return this.
|
|
45
|
+
return this._toArray().filter(e => e.tick >= startTick && e.tick <= endTick)
|
|
38
46
|
}
|
|
39
47
|
|
|
40
|
-
get size() { return this.
|
|
41
|
-
get lastTick() {
|
|
48
|
+
get size() { return this._count }
|
|
49
|
+
get lastTick() {
|
|
50
|
+
if (this._count === 0) return 0
|
|
51
|
+
const idx = (this._head - 1 + this._maxSize) % this._maxSize
|
|
52
|
+
return this._buf[idx].tick
|
|
53
|
+
}
|
|
42
54
|
|
|
43
55
|
pause() { this._recording = false }
|
|
44
56
|
resume() { this._recording = true }
|
|
45
|
-
clear() { this.
|
|
57
|
+
clear() { this._buf = new Array(this._maxSize); this._head = 0; this._count = 0; this._nextId = 1 }
|
|
46
58
|
|
|
47
|
-
serialize() { return JSON.stringify(this.
|
|
59
|
+
serialize() { return JSON.stringify(this._toArray()) }
|
|
48
60
|
|
|
49
61
|
static deserialize(json) {
|
|
50
|
-
const
|
|
51
|
-
log
|
|
52
|
-
|
|
62
|
+
const arr = JSON.parse(json)
|
|
63
|
+
const log = new EventLog({ maxSize: Math.max(arr.length, 1000) })
|
|
64
|
+
for (const e of arr) log._buf[log._head++] = e
|
|
65
|
+
log._count = arr.length
|
|
66
|
+
log._head = log._head % log._maxSize
|
|
67
|
+
log._nextId = arr.length > 0 ? arr[arr.length - 1].id + 1 : 1
|
|
53
68
|
return log
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
replay(runtime, options = {}) {
|
|
57
|
-
const speed = options.speed || 1
|
|
58
72
|
const startTick = options.startTick || 0
|
|
59
73
|
const endTick = options.endTick || Infinity
|
|
60
|
-
const events = this.
|
|
74
|
+
const events = this._toArray().filter(e => e.tick >= startTick && e.tick <= endTick)
|
|
61
75
|
const result = { eventsReplayed: 0, errors: [] }
|
|
62
76
|
for (const event of events) {
|
|
63
77
|
try {
|
|
@@ -6,37 +6,37 @@ export class LagCompensator {
|
|
|
6
6
|
|
|
7
7
|
recordPlayerPosition(playerId, position, rotation, velocity, tick) {
|
|
8
8
|
if (!this.playerHistory.has(playerId)) {
|
|
9
|
-
this.playerHistory.set(playerId,
|
|
9
|
+
this.playerHistory.set(playerId, { buf: new Array(128), head: 0, len: 0 })
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
const ring = this.playerHistory.get(playerId)
|
|
13
|
+
const idx = (ring.head + ring.len) % 128
|
|
14
|
+
if (!ring.buf[idx]) ring.buf[idx] = { tick: 0, timestamp: 0, position: [0,0,0], rotation: [0,0,0,1], velocity: [0,0,0] }
|
|
15
|
+
const entry = ring.buf[idx]
|
|
16
|
+
entry.tick = tick; entry.timestamp = Date.now()
|
|
17
|
+
entry.position[0] = position[0]; entry.position[1] = position[1]; entry.position[2] = position[2]
|
|
18
|
+
entry.rotation[0] = rotation[0]; entry.rotation[1] = rotation[1]; entry.rotation[2] = rotation[2]; entry.rotation[3] = rotation[3]
|
|
19
|
+
entry.velocity[0] = velocity[0]; entry.velocity[1] = velocity[1]; entry.velocity[2] = velocity[2]
|
|
20
|
+
if (ring.len < 128) ring.len++
|
|
21
|
+
else ring.head = (ring.head + 1) % 128
|
|
20
22
|
|
|
21
23
|
const cutoff = Date.now() - this.historyWindow
|
|
22
|
-
while (
|
|
23
|
-
|
|
24
|
+
while (ring.len > 0 && ring.buf[ring.head].timestamp < cutoff) {
|
|
25
|
+
ring.head = (ring.head + 1) % 128; ring.len--
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
getPlayerStateAtTime(playerId, millisAgo) {
|
|
28
|
-
const
|
|
29
|
-
if (!
|
|
30
|
+
const ring = this.playerHistory.get(playerId)
|
|
31
|
+
if (!ring || ring.len === 0) return null
|
|
30
32
|
|
|
31
33
|
const targetTime = Date.now() - millisAgo
|
|
32
34
|
let best = null
|
|
33
35
|
|
|
34
|
-
for (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
break
|
|
39
|
-
}
|
|
36
|
+
for (let i = 0; i < ring.len; i++) {
|
|
37
|
+
const entry = ring.buf[(ring.head + i) % 128]
|
|
38
|
+
if (entry.timestamp <= targetTime) best = entry
|
|
39
|
+
else break
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return best
|
|
@@ -56,10 +56,10 @@ export class LagCompensator {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
detectTeleport(playerId, newPosition, threshold = 50) {
|
|
59
|
-
const
|
|
60
|
-
if (!
|
|
59
|
+
const ring = this.playerHistory.get(playerId)
|
|
60
|
+
if (!ring || ring.len < 2) return false
|
|
61
61
|
|
|
62
|
-
const lastPos =
|
|
62
|
+
const lastPos = ring.buf[(ring.head + ring.len - 1) % 128].position
|
|
63
63
|
const dist = Math.sqrt((newPosition[0] - lastPos[0])**2 + (newPosition[1] - lastPos[1])**2 + (newPosition[2] - lastPos[2])**2)
|
|
64
64
|
|
|
65
65
|
return dist > threshold
|
|
@@ -70,9 +70,8 @@ export class LagCompensator {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
getStats() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
73
|
+
let total = 0
|
|
74
|
+
for (const ring of this.playerHistory.values()) total += ring.len
|
|
75
|
+
return { trackedPlayers: this.playerHistory.size, totalSamples: total }
|
|
77
76
|
}
|
|
78
77
|
}
|
|
@@ -50,7 +50,10 @@ export class NetworkState {
|
|
|
50
50
|
velocity: p.velocity,
|
|
51
51
|
onGround: p.onGround,
|
|
52
52
|
health: p.health,
|
|
53
|
-
inputSequence: p.inputSequence
|
|
53
|
+
inputSequence: p.inputSequence,
|
|
54
|
+
crouch: p.crouch || 0,
|
|
55
|
+
lookPitch: p.lookPitch || 0,
|
|
56
|
+
lookYaw: p.lookYaw || 0
|
|
54
57
|
}))
|
|
55
58
|
}
|
|
56
59
|
}
|
|
@@ -5,21 +5,26 @@ export class PhysicsIntegration {
|
|
|
5
5
|
gravity: config.gravity || [0, -9.81, 0],
|
|
6
6
|
capsuleRadius: config.capsuleRadius || 0.4,
|
|
7
7
|
capsuleHalfHeight: config.capsuleHalfHeight || 0.9,
|
|
8
|
-
|
|
8
|
+
crouchHalfHeight: config.crouchHalfHeight || 0.45,
|
|
9
|
+
playerMass: config.playerMass || 120,
|
|
9
10
|
...config
|
|
10
11
|
}
|
|
11
12
|
this.playerBodies = new Map()
|
|
13
|
+
this._crouchStates = new Map()
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
setPhysicsWorld(world) {
|
|
15
17
|
this.physicsWorld = world
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
addPlayerCollider(playerId, radius = 0.4) {
|
|
19
|
-
if (
|
|
20
|
-
this.
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
addPlayerCollider(playerId, radius = 0.4) {
|
|
21
|
+
if (this.playerBodies.has(playerId)) {
|
|
22
|
+
this.removePlayerCollider(playerId)
|
|
23
|
+
}
|
|
24
|
+
if (!this.physicsWorld) {
|
|
25
|
+
this.playerBodies.set(playerId, { id: playerId, charId: null, onGround: true })
|
|
26
|
+
return
|
|
27
|
+
}
|
|
23
28
|
const charId = this.physicsWorld.addPlayerCharacter(
|
|
24
29
|
radius,
|
|
25
30
|
this.config.capsuleHalfHeight,
|
|
@@ -129,4 +134,13 @@ export class PhysicsIntegration {
|
|
|
129
134
|
if (distance > 2.0) return { valid: false, reason: 'move_too_far', distance }
|
|
130
135
|
return { valid: true }
|
|
131
136
|
}
|
|
137
|
+
|
|
138
|
+
setCrouch(playerId, isCrouching) {
|
|
139
|
+
const data = this.playerBodies.get(playerId)
|
|
140
|
+
if (!data?.charId || !this.physicsWorld) return
|
|
141
|
+
const currentState = this._crouchStates.get(playerId)
|
|
142
|
+
if (currentState === isCrouching) return
|
|
143
|
+
this.physicsWorld.setCharacterCrouch(data.charId, isCrouching)
|
|
144
|
+
this._crouchStates.set(playerId, isCrouching)
|
|
145
|
+
}
|
|
132
146
|
}
|
|
@@ -3,6 +3,9 @@ export class PlayerManager {
|
|
|
3
3
|
this.players = new Map()
|
|
4
4
|
this.nextPlayerId = 1
|
|
5
5
|
this.inputBuffers = new Map()
|
|
6
|
+
this._connectedCache = null
|
|
7
|
+
this._connectedGen = 0
|
|
8
|
+
this._cachedGen = -1
|
|
6
9
|
}
|
|
7
10
|
|
|
8
11
|
addPlayer(socket, initialState = {}) {
|
|
@@ -17,7 +20,7 @@ export class PlayerManager {
|
|
|
17
20
|
velocity: initialState.velocity || [0, 0, 0],
|
|
18
21
|
angularVelocity: initialState.angularVelocity || [0, 0, 0],
|
|
19
22
|
onGround: true,
|
|
20
|
-
health: 100
|
|
23
|
+
health: initialState.health ?? 100
|
|
21
24
|
},
|
|
22
25
|
inputSequence: 0,
|
|
23
26
|
lastInputTime: 0,
|
|
@@ -26,12 +29,14 @@ export class PlayerManager {
|
|
|
26
29
|
}
|
|
27
30
|
this.players.set(playerId, player)
|
|
28
31
|
this.inputBuffers.set(playerId, [])
|
|
32
|
+
this._connectedGen++
|
|
29
33
|
return playerId
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
removePlayer(playerId) {
|
|
33
37
|
this.players.delete(playerId)
|
|
34
38
|
this.inputBuffers.delete(playerId)
|
|
39
|
+
this._connectedGen++
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
getPlayer(playerId) {
|
|
@@ -43,7 +48,10 @@ export class PlayerManager {
|
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
getConnectedPlayers() {
|
|
46
|
-
|
|
51
|
+
if (this._cachedGen === this._connectedGen) return this._connectedCache
|
|
52
|
+
this._connectedCache = this.getAllPlayers().filter(p => p.connected)
|
|
53
|
+
this._cachedGen = this._connectedGen
|
|
54
|
+
return this._connectedCache
|
|
47
55
|
}
|
|
48
56
|
|
|
49
57
|
getPlayerCount() {
|
|
@@ -2,28 +2,72 @@ function quantize(v, precision) {
|
|
|
2
2
|
return Math.round(v * precision) / precision
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
function encodePlayer(p) {
|
|
6
|
+
return [
|
|
7
|
+
p.id,
|
|
8
|
+
quantize(p.position[0], 100), quantize(p.position[1], 100), quantize(p.position[2], 100),
|
|
9
|
+
quantize(p.rotation[0], 10000), quantize(p.rotation[1], 10000), quantize(p.rotation[2], 10000), quantize(p.rotation[3], 10000),
|
|
10
|
+
quantize(p.velocity[0], 100), quantize(p.velocity[1], 100), quantize(p.velocity[2], 100),
|
|
11
|
+
p.onGround ? 1 : 0,
|
|
12
|
+
Math.round(p.health || 0),
|
|
13
|
+
p.inputSequence || 0,
|
|
14
|
+
p.crouch || 0,
|
|
15
|
+
Math.round(((p.lookPitch || 0) + Math.PI) / (2 * Math.PI) * 255),
|
|
16
|
+
Math.round(((p.lookYaw || 0) % (2 * Math.PI) + 2 * Math.PI) % (2 * Math.PI) / (2 * Math.PI) * 255)
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function encodeEntity(e) {
|
|
21
|
+
return [
|
|
22
|
+
e.id,
|
|
23
|
+
e.model || '',
|
|
24
|
+
quantize(e.position[0], 100), quantize(e.position[1], 100), quantize(e.position[2], 100),
|
|
25
|
+
quantize(e.rotation[0], 10000), quantize(e.rotation[1], 10000), quantize(e.rotation[2], 10000), quantize(e.rotation[3], 10000),
|
|
26
|
+
e.bodyType || 'static',
|
|
27
|
+
e.custom || null
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function entityKey(encoded) {
|
|
32
|
+
let k = ''
|
|
33
|
+
for (let i = 1; i < encoded.length; i++) {
|
|
34
|
+
const v = encoded[i]
|
|
35
|
+
k += v === null ? 'N' : typeof v === 'object' ? JSON.stringify(v) : v
|
|
36
|
+
k += '|'
|
|
37
|
+
}
|
|
38
|
+
return k
|
|
39
|
+
}
|
|
40
|
+
|
|
5
41
|
export class SnapshotEncoder {
|
|
6
42
|
static encode(snapshot) {
|
|
7
|
-
const players = (snapshot.players || []).map(
|
|
8
|
-
|
|
9
|
-
quantize(p.position[0], 100), quantize(p.position[1], 100), quantize(p.position[2], 100),
|
|
10
|
-
quantize(p.rotation[0], 10000), quantize(p.rotation[1], 10000), quantize(p.rotation[2], 10000), quantize(p.rotation[3], 10000),
|
|
11
|
-
quantize(p.velocity[0], 100), quantize(p.velocity[1], 100), quantize(p.velocity[2], 100),
|
|
12
|
-
p.onGround ? 1 : 0,
|
|
13
|
-
Math.round(p.health || 0),
|
|
14
|
-
p.inputSequence || 0
|
|
15
|
-
])
|
|
16
|
-
const entities = (snapshot.entities || []).map(e => [
|
|
17
|
-
e.id,
|
|
18
|
-
e.model || '',
|
|
19
|
-
quantize(e.position[0], 100), quantize(e.position[1], 100), quantize(e.position[2], 100),
|
|
20
|
-
quantize(e.rotation[0], 10000), quantize(e.rotation[1], 10000), quantize(e.rotation[2], 10000), quantize(e.rotation[3], 10000),
|
|
21
|
-
e.bodyType || 'static',
|
|
22
|
-
e.custom || null
|
|
23
|
-
])
|
|
43
|
+
const players = (snapshot.players || []).map(encodePlayer)
|
|
44
|
+
const entities = (snapshot.entities || []).map(encodeEntity)
|
|
24
45
|
return { tick: snapshot.tick || 0, timestamp: snapshot.timestamp || 0, players, entities }
|
|
25
46
|
}
|
|
26
47
|
|
|
48
|
+
static encodeDelta(snapshot, prevEntityMap) {
|
|
49
|
+
const players = (snapshot.players || []).map(encodePlayer)
|
|
50
|
+
const currentIds = new Set()
|
|
51
|
+
const entities = []
|
|
52
|
+
const nextMap = new Map()
|
|
53
|
+
for (const e of snapshot.entities || []) {
|
|
54
|
+
const encoded = encodeEntity(e)
|
|
55
|
+
const key = entityKey(encoded)
|
|
56
|
+
currentIds.add(e.id)
|
|
57
|
+
nextMap.set(e.id, key)
|
|
58
|
+
const prev = prevEntityMap.get(e.id)
|
|
59
|
+
if (prev !== key) entities.push(encoded)
|
|
60
|
+
}
|
|
61
|
+
const removed = []
|
|
62
|
+
for (const id of prevEntityMap.keys()) {
|
|
63
|
+
if (!currentIds.has(id)) removed.push(id)
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
encoded: { tick: snapshot.tick || 0, timestamp: snapshot.timestamp || 0, players, entities, removed: removed.length ? removed : undefined, delta: 1 },
|
|
67
|
+
entityMap: nextMap
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
27
71
|
static decode(data) {
|
|
28
72
|
if (data.players && Array.isArray(data.players)) {
|
|
29
73
|
const players = data.players.map(p => {
|
|
@@ -31,7 +75,10 @@ export class SnapshotEncoder {
|
|
|
31
75
|
id: p[0], position: [p[1], p[2], p[3]],
|
|
32
76
|
rotation: [p[4], p[5], p[6], p[7]],
|
|
33
77
|
velocity: [p[8], p[9], p[10]],
|
|
34
|
-
onGround: p[11] === 1, health: p[12], inputSequence: p[13]
|
|
78
|
+
onGround: p[11] === 1, health: p[12], inputSequence: p[13],
|
|
79
|
+
crouch: p[14] || 0,
|
|
80
|
+
lookPitch: (p[15] || 0) / 255 * 2 * Math.PI - Math.PI,
|
|
81
|
+
lookYaw: (p[16] || 0) / 255 * 2 * Math.PI
|
|
35
82
|
}
|
|
36
83
|
return p
|
|
37
84
|
})
|
|
@@ -42,7 +89,7 @@ export class SnapshotEncoder {
|
|
|
42
89
|
}
|
|
43
90
|
return e
|
|
44
91
|
})
|
|
45
|
-
return { tick: data.tick, timestamp: data.timestamp, players, entities }
|
|
92
|
+
return { tick: data.tick, timestamp: data.timestamp, players, entities, delta: data.delta, removed: data.removed }
|
|
46
93
|
}
|
|
47
94
|
return data
|
|
48
95
|
}
|
|
@@ -25,11 +25,13 @@ export class TickSystem {
|
|
|
25
25
|
loop() {
|
|
26
26
|
if (!this.running) return
|
|
27
27
|
const now = Date.now()
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
let elapsed = now - this.lastTickTime
|
|
29
|
+
let steps = 0
|
|
30
|
+
const maxSteps = 4
|
|
31
|
+
while (elapsed >= this.tickDuration && !this._reloadLocked && steps < maxSteps) {
|
|
30
32
|
this._tickInProgress = true
|
|
31
33
|
this.currentTick++
|
|
32
|
-
this.lastTickTime
|
|
34
|
+
this.lastTickTime += this.tickDuration
|
|
33
35
|
for (const callback of this.callbacks) {
|
|
34
36
|
callback(this.currentTick, this.tickDuration / 1000)
|
|
35
37
|
}
|
|
@@ -38,8 +40,15 @@ export class TickSystem {
|
|
|
38
40
|
this._reloadResolve()
|
|
39
41
|
this._reloadResolve = null
|
|
40
42
|
}
|
|
43
|
+
elapsed = now - this.lastTickTime
|
|
44
|
+
steps++
|
|
45
|
+
}
|
|
46
|
+
if (now - this.lastTickTime > this.tickDuration * maxSteps) {
|
|
47
|
+
this.lastTickTime = now
|
|
41
48
|
}
|
|
42
|
-
|
|
49
|
+
const gap = this.tickDuration - (Date.now() - this.lastTickTime)
|
|
50
|
+
if (gap > 2) setTimeout(() => this.loop(), 1)
|
|
51
|
+
else setImmediate(() => this.loop())
|
|
43
52
|
}
|
|
44
53
|
|
|
45
54
|
pauseForReload() {
|
package/src/physics/World.js
CHANGED
|
@@ -6,9 +6,11 @@ async function getJolt() { if (!joltInstance) joltInstance = await initJolt(); r
|
|
|
6
6
|
export class PhysicsWorld {
|
|
7
7
|
constructor(config = {}) {
|
|
8
8
|
this.gravity = config.gravity || [0, -9.81, 0]
|
|
9
|
+
this.crouchHalfHeight = config.crouchHalfHeight || 0.45
|
|
9
10
|
this.Jolt = null; this.jolt = null; this.physicsSystem = null; this.bodyInterface = null
|
|
10
11
|
this.bodies = new Map(); this.bodyMeta = new Map()
|
|
11
12
|
this._objFilter = null; this._ovbp = null
|
|
13
|
+
this._charShapes = new Map()
|
|
12
14
|
}
|
|
13
15
|
async init() {
|
|
14
16
|
const J = await getJolt()
|
|
@@ -99,12 +101,29 @@ export class PhysicsWorld {
|
|
|
99
101
|
this._charUpdateSettings.mStickToFloorStepDown = new J.Vec3(0, -0.5, 0)
|
|
100
102
|
this._charUpdateSettings.mWalkStairsStepUp = new J.Vec3(0, 0.4, 0)
|
|
101
103
|
this._charGravity = new J.Vec3(this.gravity[0], this.gravity[1], this.gravity[2])
|
|
104
|
+
this._tmpVec3 = new J.Vec3(0, 0, 0)
|
|
105
|
+
this._tmpRVec3 = new J.RVec3(0, 0, 0)
|
|
102
106
|
}
|
|
103
107
|
const id = this._nextCharId = (this._nextCharId || 0) + 1
|
|
104
108
|
if (!this.characters) this.characters = new Map()
|
|
105
109
|
this.characters.set(id, ch)
|
|
110
|
+
this._charShapes.set(id, { radius, standHeight: halfHeight, crouchHeight: this.crouchHalfHeight })
|
|
106
111
|
return id
|
|
107
112
|
}
|
|
113
|
+
setCharacterCrouch(charId, isCrouching) {
|
|
114
|
+
const data = this._charShapes.get(charId)
|
|
115
|
+
if (!data) return
|
|
116
|
+
const heightDiff = (data.standHeight - data.crouchHeight) * 0.5
|
|
117
|
+
const ch = this.characters?.get(charId)
|
|
118
|
+
if (!ch) return
|
|
119
|
+
const pos = this.getCharacterPosition(charId)
|
|
120
|
+
if (isCrouching) {
|
|
121
|
+
pos[1] -= heightDiff
|
|
122
|
+
} else {
|
|
123
|
+
pos[1] += heightDiff
|
|
124
|
+
}
|
|
125
|
+
this.setCharacterPosition(charId, pos)
|
|
126
|
+
}
|
|
108
127
|
updateCharacter(charId, dt) {
|
|
109
128
|
const ch = this.characters?.get(charId)
|
|
110
129
|
if (!ch) return
|
|
@@ -113,19 +132,27 @@ export class PhysicsWorld {
|
|
|
113
132
|
}
|
|
114
133
|
getCharacterPosition(charId) {
|
|
115
134
|
const ch = this.characters?.get(charId); if (!ch) return [0, 0, 0]
|
|
116
|
-
const p = ch.GetPosition()
|
|
135
|
+
const p = ch.GetPosition()
|
|
136
|
+
const r = [p.GetX(), p.GetY(), p.GetZ()]
|
|
137
|
+
this.Jolt.destroy(p)
|
|
138
|
+
return r
|
|
117
139
|
}
|
|
118
140
|
getCharacterVelocity(charId) {
|
|
119
141
|
const ch = this.characters?.get(charId); if (!ch) return [0, 0, 0]
|
|
120
|
-
const v = ch.GetLinearVelocity()
|
|
142
|
+
const v = ch.GetLinearVelocity()
|
|
143
|
+
const r = [v.GetX(), v.GetY(), v.GetZ()]
|
|
144
|
+
this.Jolt.destroy(v)
|
|
145
|
+
return r
|
|
121
146
|
}
|
|
122
147
|
setCharacterVelocity(charId, velocity) {
|
|
123
148
|
const ch = this.characters?.get(charId); if (!ch) return
|
|
124
|
-
|
|
149
|
+
const v = this._tmpVec3; v.Set(velocity[0], velocity[1], velocity[2])
|
|
150
|
+
ch.SetLinearVelocity(v)
|
|
125
151
|
}
|
|
126
152
|
setCharacterPosition(charId, position) {
|
|
127
153
|
const ch = this.characters?.get(charId); if (!ch) return
|
|
128
|
-
|
|
154
|
+
const p = this._tmpRVec3; p.Set(position[0], position[1], position[2])
|
|
155
|
+
ch.SetPosition(p)
|
|
129
156
|
}
|
|
130
157
|
getCharacterGroundState(charId) {
|
|
131
158
|
const ch = this.characters?.get(charId); if (!ch) return false
|
|
@@ -133,36 +160,53 @@ export class PhysicsWorld {
|
|
|
133
160
|
}
|
|
134
161
|
removeCharacter(charId) {
|
|
135
162
|
if (!this.characters) return
|
|
136
|
-
this.characters.
|
|
163
|
+
const ch = this.characters.get(charId)
|
|
164
|
+
if (ch) {
|
|
165
|
+
this.Jolt.destroy(ch)
|
|
166
|
+
this.characters.delete(charId)
|
|
167
|
+
}
|
|
137
168
|
}
|
|
138
169
|
_getBody(bodyId) { return this.bodies.get(bodyId) }
|
|
139
170
|
getBodyPosition(bodyId) {
|
|
140
171
|
const b = this._getBody(bodyId); if (!b) return [0, 0, 0]
|
|
141
|
-
const p = this.bodyInterface.GetPosition(b.GetID())
|
|
172
|
+
const p = this.bodyInterface.GetPosition(b.GetID())
|
|
173
|
+
const r = [p.GetX(), p.GetY(), p.GetZ()]
|
|
174
|
+
this.Jolt.destroy(p)
|
|
175
|
+
return r
|
|
142
176
|
}
|
|
143
177
|
getBodyRotation(bodyId) {
|
|
144
178
|
const b = this._getBody(bodyId); if (!b) return [0, 0, 0, 1]
|
|
145
|
-
const
|
|
179
|
+
const q = this.bodyInterface.GetRotation(b.GetID())
|
|
180
|
+
const r = [q.GetX(), q.GetY(), q.GetZ(), q.GetW()]
|
|
181
|
+
this.Jolt.destroy(q)
|
|
182
|
+
return r
|
|
146
183
|
}
|
|
147
184
|
getBodyVelocity(bodyId) {
|
|
148
185
|
const b = this._getBody(bodyId); if (!b) return [0, 0, 0]
|
|
149
|
-
const v = this.bodyInterface.GetLinearVelocity(b.GetID())
|
|
186
|
+
const v = this.bodyInterface.GetLinearVelocity(b.GetID())
|
|
187
|
+
const r = [v.GetX(), v.GetY(), v.GetZ()]
|
|
188
|
+
this.Jolt.destroy(v)
|
|
189
|
+
return r
|
|
150
190
|
}
|
|
151
191
|
setBodyPosition(bodyId, position) {
|
|
152
192
|
const b = this._getBody(bodyId); if (!b) return
|
|
153
|
-
this.
|
|
193
|
+
const p = this._tmpRVec3 || new this.Jolt.RVec3(0, 0, 0); p.Set(position[0], position[1], position[2])
|
|
194
|
+
this.bodyInterface.SetPosition(b.GetID(), p, this.Jolt.EActivation_Activate)
|
|
154
195
|
}
|
|
155
196
|
setBodyVelocity(bodyId, velocity) {
|
|
156
197
|
const b = this._getBody(bodyId); if (!b) return
|
|
157
|
-
this.
|
|
198
|
+
const v = this._tmpVec3 || new this.Jolt.Vec3(0, 0, 0); v.Set(velocity[0], velocity[1], velocity[2])
|
|
199
|
+
this.bodyInterface.SetLinearVelocity(b.GetID(), v)
|
|
158
200
|
}
|
|
159
201
|
addForce(bodyId, force) {
|
|
160
202
|
const b = this._getBody(bodyId); if (!b) return
|
|
161
|
-
this.
|
|
203
|
+
const v = this._tmpVec3 || new this.Jolt.Vec3(0, 0, 0); v.Set(force[0], force[1], force[2])
|
|
204
|
+
this.bodyInterface.AddForce(b.GetID(), v)
|
|
162
205
|
}
|
|
163
206
|
addImpulse(bodyId, impulse) {
|
|
164
207
|
const b = this._getBody(bodyId); if (!b) return
|
|
165
|
-
this.
|
|
208
|
+
const v = this._tmpVec3 || new this.Jolt.Vec3(0, 0, 0); v.Set(impulse[0], impulse[1], impulse[2])
|
|
209
|
+
this.bodyInterface.AddImpulse(b.GetID(), v)
|
|
166
210
|
}
|
|
167
211
|
step(deltaTime) { if (!this.jolt) return; this.jolt.Step(deltaTime, deltaTime > 1 / 55 ? 2 : 1) }
|
|
168
212
|
removeBody(bodyId) {
|
|
@@ -191,5 +235,14 @@ export class PhysicsWorld {
|
|
|
191
235
|
J.destroy(ray); J.destroy(rs); J.destroy(col); J.destroy(bp); J.destroy(ol); J.destroy(bf); J.destroy(sf)
|
|
192
236
|
return result
|
|
193
237
|
}
|
|
194
|
-
destroy() {
|
|
238
|
+
destroy() {
|
|
239
|
+
if (this.characters) {
|
|
240
|
+
for (const [id, ch] of this.characters) {
|
|
241
|
+
this.Jolt.destroy(ch)
|
|
242
|
+
}
|
|
243
|
+
this.characters.clear()
|
|
244
|
+
}
|
|
245
|
+
for (const [id] of this.bodies) this.removeBody(id)
|
|
246
|
+
if (this.jolt) { this.Jolt.destroy(this.jolt); this.jolt = null }
|
|
247
|
+
}
|
|
195
248
|
}
|