1
1
import { useMemo , useRef , useState } from "react" ;
2
- import { useFrame } from "@react-three/fiber" ;
3
2
import {
4
3
CylinderBufferGeometry ,
5
4
InstancedMesh ,
6
5
MeshNormalMaterial ,
7
6
Object3D ,
8
7
} from "three" ;
9
8
import { useNetwork } from "../logic/network" ;
10
- import { useLimitedFrame , useLimiter } from "../../../logic/limiter" ;
9
+ import { useLimitedFrame } from "../../../logic/limiter" ;
10
+ import { SnapshotInterpolation } from "@geckos.io/snapshot-interpolation" ;
11
+ import {
12
+ Snapshot ,
13
+ Entity as EntityState ,
14
+ Quat ,
15
+ } from "@geckos.io/snapshot-interpolation/lib/types" ;
11
16
12
17
export default function NetworkedEntities ( ) {
13
18
const { connections, connected, useChannel } = useNetwork ( ) ;
14
19
15
20
const mesh = useRef < InstancedMesh > ( null ) ;
16
- const geo = useMemo ( ( ) => new CylinderBufferGeometry ( 0.3 , 0.3 , 1 , 30 ) , [ ] ) ;
21
+ const geo = useMemo ( ( ) => new CylinderBufferGeometry ( 0.3 , 0.3 , 1 , 32 ) , [ ] ) ;
17
22
const mat = useMemo ( ( ) => new MeshNormalMaterial ( ) , [ ] ) ;
18
23
const obj = useMemo ( ( ) => {
19
24
const o = new Object3D ( ) ;
@@ -30,45 +35,72 @@ export default function NetworkedEntities() {
30
35
if ( ! sameIds ) setEntityIds ( ids ) ;
31
36
} ) ;
32
37
38
+ const NETWORK_FPS = 12 ;
33
39
type Entity = { pos : number [ ] ; rot : number [ ] } ;
40
+ const SI = useMemo ( ( ) => new SnapshotInterpolation ( NETWORK_FPS ) , [ ] ) ;
34
41
const entityChannel = useChannel < Entity , { [ key in string ] : Entity } > (
35
42
"player" ,
36
43
"stream" ,
37
44
( m , s ) => {
38
45
if ( ! m . conn ) return ;
39
46
s [ m . conn . peer ] = m . data ;
47
+
48
+ const state : EntityState [ ] = Object . keys ( s ) . map ( ( key ) => ( {
49
+ id : key ,
50
+ x : s [ key ] . pos [ 0 ] ,
51
+ y : s [ key ] . pos [ 1 ] ,
52
+ z : s [ key ] . pos [ 2 ] ,
53
+ q : {
54
+ x : s [ key ] . rot [ 0 ] ,
55
+ y : s [ key ] . rot [ 1 ] ,
56
+ z : s [ key ] . rot [ 2 ] ,
57
+ w : s [ key ] . rot [ 3 ] ,
58
+ } ,
59
+ } ) ) ;
60
+
61
+ const snapshot : Snapshot = {
62
+ id : Math . random ( ) . toString ( ) ,
63
+ time : new Date ( ) . getTime ( ) ,
64
+ state,
65
+ } ;
66
+
67
+ SI . vault . add ( snapshot ) ;
40
68
}
41
69
) ;
42
70
43
71
// send own player data
44
- useLimitedFrame ( 15 , ( { camera } ) => {
72
+ useLimitedFrame ( NETWORK_FPS , ( { camera } ) => {
45
73
if ( ! connected ) return ;
46
74
entityChannel . send ( {
47
- pos : camera . position . toArray ( ) . map ( ( p ) => parseFloat ( p . toPrecision ( 3 ) ) ) ,
48
- rot : camera . rotation
49
- . toArray ( )
50
- . slice ( 0 , 3 )
51
- . map ( ( r ) => parseFloat ( r . toPrecision ( 3 ) ) ) ,
75
+ pos : camera . position . toArray ( ) ,
76
+ rot : camera . quaternion . toArray ( ) ,
52
77
} ) ;
53
78
} ) ;
54
79
55
80
// receive player data
56
- useLimitedFrame ( 50 , ( ) => {
81
+ useLimitedFrame ( 55 , ( ) => {
57
82
if ( ! mesh . current ) return ;
58
- for ( const id of Object . keys ( entityChannel . state ) ) {
59
- const index = entityIds . indexOf ( id ) ;
60
- if ( index < 0 ) return ;
61
- const { pos, rot } = entityChannel . state [ id ] ;
62
- obj . position . fromArray ( pos ) ;
63
- obj . rotation . fromArray ( rot ) ;
83
+
84
+ const snapshot = SI . calcInterpolation ( "x y z q(quat)" ) ;
85
+ if ( ! snapshot ) return ;
86
+
87
+ let i = 0 ;
88
+ for ( const entityState of snapshot . state ) {
89
+ const { x, y, z, q } = entityState ;
90
+ obj . position . x = x as number ;
91
+ obj . position . y = y as number ;
92
+ obj . position . z = z as number ;
93
+ obj . position . y -= 0.2 ; // they were floating before, idk where the constant comes from really
94
+ const quat = q as Quat ;
95
+ obj . quaternion . x = quat . x ;
96
+ obj . quaternion . y = quat . y ;
97
+ obj . quaternion . z = quat . z ;
98
+ obj . quaternion . w = quat . w ;
64
99
obj . updateMatrix ( ) ;
65
- mesh . current ?. setMatrixAt ( index , obj . matrix ) ;
100
+ mesh . current . setMatrixAt ( i , obj . matrix ) ;
101
+ i ++ ;
66
102
}
67
- } ) ;
68
103
69
- const renderLimiter = useLimiter ( 40 ) ;
70
- useFrame ( ( { clock } ) => {
71
- if ( ! mesh . current || ! renderLimiter . isReady ( clock ) ) return ;
72
104
mesh . current . instanceMatrix . needsUpdate = true ;
73
105
} ) ;
74
106
0 commit comments