1 #include "player_remote.h"
3 #include "player_render.h"
5 static void player_remote_unwatch( struct network_player
*player
){
6 addon_cache_unwatch( k_addon_type_player
, player
->playermodel_view_slot
);
7 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
10 static void player_remote_clear( struct network_player
*player
){
11 player_remote_unwatch( player
);
12 memset( player
, 0, sizeof(*player
) );
13 strcpy( player
->username
, "unknown" );
14 player
->subsystem
= k_player_subsystem_invalid
;
17 static void player_remote_clear_interp( u32 index
){
18 struct interp_buffer
*buf
= &netplayers
.interp_data
[ index
];
20 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
21 buf
->frames
[i
].active
= 0;
25 static void player_remote_rx_200_300( SteamNetworkingMessage_t
*msg
){
26 netmsg_blank
*tmp
= msg
->m_pData
;
28 if( tmp
->inetmsg_id
== k_inetmsg_playerjoin
){
29 netmsg_playerjoin
*playerjoin
= msg
->m_pData
;
30 if( !packet_minsize( msg
, sizeof(*playerjoin
) )) return;
32 if( playerjoin
->index
< vg_list_size(netplayers
.list
) ){
33 struct network_player
*player
= &netplayers
.list
[ playerjoin
->index
];
34 player_remote_clear( player
);
37 /* TODO: interpret the uids */
38 player
->board_view_slot
= 0;
39 player
->playermodel_view_slot
= 0;
40 player_remote_clear_interp( playerjoin
->index
);
42 vg_strncpy( playerjoin
->username
, player
->username
,
43 sizeof(player
->username
), k_strncpy_always_add_null
);
45 vg_info( "#%u joined with name: %s\n",
46 playerjoin
->index
, player
->username
);
49 vg_error( "inetmsg_playerjoin: player index out of range\n" );
52 else if( tmp
->inetmsg_id
== k_inetmsg_playerleave
){
53 netmsg_playerleave
*playerleave
= msg
->m_pData
;
54 if( !packet_minsize( msg
, sizeof(*playerleave
) )) return;
56 if( playerleave
->index
< vg_list_size(netplayers
.list
) ){
57 struct network_player
*player
= &netplayers
.list
[ playerleave
->index
];
58 player_remote_unwatch( player
);
60 vg_info( "player leave (%d)\n", playerleave
->index
);
63 vg_error( "inetmsg_playerleave: player index out of range\n" );
66 else if( tmp
->inetmsg_id
== k_inetmsg_playerusername
){
67 netmsg_playerusername
*update
= msg
->m_pData
;
68 if( !packet_minsize( msg
, sizeof(*update
) )) return;
70 if( update
->index
< vg_list_size(netplayers
.list
) ){
71 struct network_player
*player
= &netplayers
.list
[ update
->index
];
72 vg_strncpy( update
->username
, player
->username
,
73 sizeof(player
->username
), k_strncpy_always_add_null
);
75 vg_info( "#%u changed username: %s\n", player
->username
);
78 vg_error( "inetmsg_playerleave: player index out of range\n" );
81 else if( tmp
->inetmsg_id
== k_inetmsg_playerframe
){
82 u32 datasize
= msg
->m_cbSize
- sizeof(netmsg_playerframe
);
84 if( datasize
> sizeof(union interp_animdata
) ){
85 vg_error( "Player frame data exceeds animdata size\n" );
89 netmsg_playerframe
*frame
= msg
->m_pData
;
90 if( frame
->client
>= vg_list_size(netplayers
.list
) ){
91 vg_error( "inetmsg_playerframe: player index out of range\n" );
95 if( frame
->subsystem
>= k_player_subsystem_max
){
96 vg_error( "inetmsg_playerframe: subsystem out of range\n" );
100 struct interp_buffer
*ib
= &netplayers
.interp_data
[ frame
->client
];
101 struct interp_frame
*dest
= NULL
;
103 f64 min_time
= INFINITY
;
104 for( u32 i
=0; i
<vg_list_size(ib
->frames
); i
++ ){
105 struct interp_frame
*ifr
= &ib
->frames
[i
];
112 if( ifr
->timestamp
< min_time
){
113 min_time
= ifr
->timestamp
;
119 dest
->timestamp
= frame
->timestamp
;
120 dest
->subsystem
= frame
->subsystem
;
122 struct network_player
*player
= &netplayers
.list
[ frame
->client
];
123 memcpy( &dest
->data
, frame
->animdata
, datasize
);
124 player
->subsystem
= frame
->subsystem
;
125 player
->down_bytes
+= msg
->m_cbSize
;
129 static void remote_player_send_playerframe(void){
130 u8 sysid
= localplayer
.subsystem
;
131 if( sysid
>= k_player_subsystem_max
) return;
133 struct player_subsystem_interface
*sys
= player_subsystems
[sysid
];
135 if( sys
->animator_size
){
136 u32 size
= sizeof(netmsg_playerframe
)+sys
->animator_size
;
137 netmsg_playerframe
*frame
= alloca(size
);
138 frame
->inetmsg_id
= k_inetmsg_playerframe
;
140 frame
->subsystem
= localplayer
.subsystem
;
141 frame
->timestamp
= vg
.time_real
;
142 memcpy( frame
->animdata
, sys
->animator_data
, sys
->animator_size
);
144 netplayers
.up_bytes
+= size
;
146 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
147 hSteamNetworkingSockets
, network_client
.remote
,
149 k_nSteamNetworkingSend_Unreliable
, NULL
);
153 static void remote_player_debug_update(void){
154 if( (vg
.time_real
- netplayers
.last_data_measurement
) > 1.0 ){
155 netplayers
.last_data_measurement
= vg
.time_real
;
158 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
159 struct network_player
*player
= &netplayers
.list
[i
];
160 if( player
->active
){
161 total_down
+= player
->down_bytes
;
162 player
->down_kbs
= ((f32
)player
->down_bytes
)/1024.0f
;
163 player
->down_bytes
= 0;
167 netplayers
.down_kbs
= ((f32
)total_down
)/1024.0f
;
168 netplayers
.up_kbs
= ((f32
)netplayers
.up_bytes
)/1024.0f
;
169 netplayers
.up_bytes
= 0;
173 static void remote_player_network_imgui( m4x4f pv
){
174 if( !network_client
.network_info
)
177 ui_rect panel
= { (vg
.window_x
/ 2) - 200, 0, 400, 600 };
178 ui_fill( panel
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
181 const char *netstatus
= "PROGRAMMING ERROR";
183 struct { enum ESteamNetworkingConnectionState state
; const char *str
; }
185 { k_ESteamNetworkingConnectionState_None
, "None" },
186 { k_ESteamNetworkingConnectionState_Connecting
,
187 (const char *[]){"Connecting -",
191 }[(u32
)(vg
.time_real
/0.25) & 0x3 ] },
192 { k_ESteamNetworkingConnectionState_FindingRoute
, "Finding Route" },
193 { k_ESteamNetworkingConnectionState_Connected
, "Connected" },
194 { k_ESteamNetworkingConnectionState_ClosedByPeer
, "Closed by peer" },
195 { k_ESteamNetworkingConnectionState_ProblemDetectedLocally
,
196 "Problem Detected Locally" },
197 { k_ESteamNetworkingConnectionState_FinWait
, "Fin Wait" },
198 { k_ESteamNetworkingConnectionState_Linger
, "Linger" },
199 { k_ESteamNetworkingConnectionState_Dead
, "Dead" }
201 for( u32 i
=0; i
<vg_list_size(states
); i
++ ){
202 if( states
[i
].state
== network_client
.state
){
203 netstatus
= states
[i
].str
;
207 snprintf( buf
, 512, "Network: %s", netstatus
);
208 ui_info( panel
, buf
);
209 ui_info( panel
, "---------------------" );
211 if( network_client
.state
== k_ESteamNetworkingConnectionState_Connected
){
212 ui_info( panel
, "#-1: localplayer" );
214 snprintf( buf
, 512, "U%.1f/D%.1fkbs",
215 netplayers
.up_kbs
, netplayers
.down_kbs
);
216 ui_info( panel
, buf
);
218 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
219 struct network_player
*player
= &netplayers
.list
[i
];
220 if( player
->active
){
221 const char *sysname
= "invalid";
223 if( player
->subsystem
< k_player_subsystem_max
){
224 sysname
= player_subsystems
[ player
->subsystem
]->name
;
226 snprintf( buf
, 512, "#%u: %s [%s] D%.1fkbs",
227 i
, player
->username
, sysname
, player
->down_kbs
);
228 ui_info( panel
, buf
);
230 v4f wpos
= { 0.0f
, 2.0f
, 0.0f
, 1.0f
};
231 struct player_avatar
*av
= localplayer
.playeravatar
;
232 m4x3_mulv( netplayers
.final_mtx
[av
->sk
.bone_count
*i
], wpos
, wpos
);
233 m4x4_mulv( pv
, wpos
, wpos
);
235 if( wpos
[3] > 0.0f
){
236 v2_muls( wpos
, (1.0f
/wpos
[3]) * 0.5f
, wpos
);
237 v2_add( wpos
, (v2f
){ 0.5f
, 0.5f
}, wpos
);
240 wr
[0] = vg_clampf(wpos
[0] * vg
.window_x
, -32000.0f
,32000.0f
)-150;
241 wr
[1] = vg_clampf((1.0f
-wpos
[1]) * vg
.window_y
,
245 ui_fill( wr
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
246 ui_text( wr
, buf
, 1, k_ui_align_middle_center
, 0 );
252 ui_info( panel
, "offline" );
256 static void pose_remote_player( u32 index
,
257 struct interp_frame
*f0
,
258 struct interp_frame
*f1
){
260 struct interp_buffer
*buf
= &netplayers
.interp_data
[ index
];
261 struct player_avatar
*av
= localplayer
.playeravatar
;
262 struct skeleton
*sk
= &localplayer
.playeravatar
->sk
;
263 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*index
];
265 struct player_subsystem_interface
*sys0
= player_subsystems
[f0
->subsystem
],
268 player_pose pose0
, pose1
, posed
;
270 sys0
->pose( &f0
->data
, &pose0
);
273 f32 t
= (buf
->t
- f0
->timestamp
) / (f1
->timestamp
- f0
->timestamp
);
274 t
= vg_clampf( t
, 0.0f
, 1.0f
);
276 sys1
= player_subsystems
[f1
->subsystem
];
277 sys1
->pose( &f1
->data
, &pose1
);
279 if( pose0
.type
!= pose1
.type
){
280 /* it would be nice to apply IK pass in-keyframes. TOO BAD! */
281 skeleton_copy_pose( sk
, pose0
.keyframes
, posed
.keyframes
);
284 skeleton_lerp_pose( sk
, pose0
.keyframes
, pose1
.keyframes
, t
,
288 apply_full_skeleton_pose( &av
->sk
, &posed
, final_mtx
);
291 apply_full_skeleton_pose( &av
->sk
, &pose0
, final_mtx
);
295 static void animate_remote_player( u32 index
){
297 f64 min_time
= 999999999.9,
298 max_time
= -999999999.9,
299 abs_max_time
= -999999999.9;
301 struct interp_frame
*minframe
= NULL
,
303 *abs_max_frame
= NULL
;
305 struct interp_buffer
*buf
= &netplayers
.interp_data
[index
];
306 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
307 struct interp_frame
*ifr
= &buf
->frames
[i
];
310 if( (ifr
->timestamp
< min_time
) && (ifr
->timestamp
> buf
->t
) ){
311 min_time
= ifr
->timestamp
;
315 if( (ifr
->timestamp
> max_time
) && (ifr
->timestamp
< buf
->t
) ){
316 max_time
= ifr
->timestamp
;
320 if( ifr
->timestamp
> abs_max_time
){
321 abs_max_time
= ifr
->timestamp
;
327 if( minframe
&& maxframe
){
328 pose_remote_player( index
, minframe
, maxframe
);
329 buf
->t
+= vg
.time_frame_delta
;
332 buf
->t
= abs_max_time
- 0.25;
335 pose_remote_player( index
, abs_max_frame
, NULL
);