1 #include "player_remote.h"
3 #include "player_render.h"
4 #include "network_common.h"
7 static void player_remote_unwatch( struct network_player
*player
){
8 addon_cache_unwatch( k_addon_type_player
, player
->playermodel_view_slot
);
9 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
12 static void player_remote_clear( struct network_player
*player
){
13 player_remote_unwatch( player
);
14 memset( player
, 0, sizeof(*player
) );
15 strcpy( player
->username
, "unknown" );
16 player
->subsystem
= k_player_subsystem_invalid
;
19 static void player_remote_rx_200_300( SteamNetworkingMessage_t
*msg
){
20 netmsg_blank
*tmp
= msg
->m_pData
;
22 if( tmp
->inetmsg_id
== k_inetmsg_playerjoin
){
23 netmsg_playerjoin
*playerjoin
= msg
->m_pData
;
24 if( !packet_minsize( msg
, sizeof(*playerjoin
) )) return;
26 if( playerjoin
->index
< vg_list_size(netplayers
.list
) ){
27 struct network_player
*player
= &netplayers
.list
[ playerjoin
->index
];
28 player_remote_clear( player
);
31 /* TODO: interpret the uids */
32 player
->board_view_slot
= 0;
33 player
->playermodel_view_slot
= 0;
35 addon_cache_create_viewer_from_uid( k_addon_type_board
,
36 playerjoin
->board_uid
);
37 player
->playermodel_view_slot
=
38 addon_cache_create_viewer_from_uid( k_addon_type_player
,
39 playerjoin
->playermodel_uid
);
42 struct interp_buffer
*buf
= &netplayers
.interp_data
[playerjoin
->index
];
44 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
45 buf
->frames
[i
].active
= 0;
48 vg_info( "#%u joined\n", playerjoin
->index
);
51 vg_error( "inetmsg_playerjoin: player index out of range\n" );
54 else if( tmp
->inetmsg_id
== k_inetmsg_playerleave
){
55 netmsg_playerleave
*playerleave
= msg
->m_pData
;
56 if( !packet_minsize( msg
, sizeof(*playerleave
) )) return;
58 if( playerleave
->index
< vg_list_size(netplayers
.list
) ){
59 struct network_player
*player
= &netplayers
.list
[ playerleave
->index
];
60 player_remote_unwatch( player
);
62 vg_info( "player leave (%d)\n", playerleave
->index
);
65 vg_error( "inetmsg_playerleave: player index out of range\n" );
68 else if( tmp
->inetmsg_id
== k_inetmsg_playerusername
){
69 netmsg_playerusername
*update
= msg
->m_pData
;
70 if( !packet_minsize( msg
, sizeof(*update
) )) return;
72 if( update
->index
< vg_list_size(netplayers
.list
) ){
73 struct network_player
*player
= &netplayers
.list
[ update
->index
];
75 network_msgstring( update
->name
, msg
->m_cbSize
, sizeof(*update
),
76 player
->username
, sizeof(player
->username
) );
78 vg_info( "#%u changed username to: %s\n",
79 update
->index
, player
->username
);
82 vg_error( "inetmsg_playerleave: player index out of range\n" );
85 else if( tmp
->inetmsg_id
== k_inetmsg_playerframe
){
86 u32 datasize
= msg
->m_cbSize
- sizeof(netmsg_playerframe
);
88 if( datasize
> sizeof(union interp_animdata
) ){
89 vg_error( "Player frame data exceeds animdata size\n" );
93 netmsg_playerframe
*frame
= msg
->m_pData
;
94 if( frame
->client
>= vg_list_size(netplayers
.list
) ){
95 vg_error( "inetmsg_playerframe: player index out of range\n" );
99 if( frame
->subsystem
>= k_player_subsystem_max
){
100 vg_error( "inetmsg_playerframe: subsystem out of range\n" );
104 struct interp_buffer
*ib
= &netplayers
.interp_data
[ frame
->client
];
105 struct interp_frame
*dest
= NULL
;
107 f64 min_time
= INFINITY
;
108 for( u32 i
=0; i
<vg_list_size(ib
->frames
); i
++ ){
109 struct interp_frame
*ifr
= &ib
->frames
[i
];
116 if( ifr
->timestamp
< min_time
){
117 min_time
= ifr
->timestamp
;
123 dest
->timestamp
= frame
->timestamp
;
124 dest
->subsystem
= frame
->subsystem
;
126 struct network_player
*player
= &netplayers
.list
[ frame
->client
];
127 memcpy( &dest
->data
, frame
->animdata
, datasize
);
128 player
->subsystem
= frame
->subsystem
;
129 player
->down_bytes
+= msg
->m_cbSize
;
131 else if( tmp
->inetmsg_id
== k_inetmsg_playeritem
){
132 netmsg_playeritem
*item
= msg
->m_pData
;
133 if( !packet_minsize( msg
, sizeof(*item
)+1 )) return;
135 if( item
->client
>= vg_list_size(netplayers
.list
) ){
136 vg_error( "inetmsg_playerframe: player index out of range\n" );
140 vg_info( "Client #%u equiped: [%u] %s\n",
141 item
->client
, item
->type
, item
->uid
);
143 struct network_player
*player
= &netplayers
.list
[ item
->client
];
145 char uid
[ ADDON_UID_MAX
];
146 network_msgstring( item
->uid
, msg
->m_cbSize
, sizeof(*item
),
147 uid
, ADDON_UID_MAX
);
149 if( item
->type
== k_addon_type_board
){
150 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
151 player
->board_view_slot
=
152 addon_cache_create_viewer_from_uid( k_addon_type_board
, uid
);
154 else if( item
->type
== k_addon_type_player
){
155 addon_cache_unwatch( k_addon_type_player
,
156 player
->playermodel_view_slot
);
157 player
->playermodel_view_slot
=
158 addon_cache_create_viewer_from_uid( k_addon_type_player
, uid
);
164 * Write localplayer pose to network
166 static void remote_player_send_playerframe(void){
167 u8 sysid
= localplayer
.subsystem
;
168 if( sysid
>= k_player_subsystem_max
) return;
170 struct player_subsystem_interface
*sys
= player_subsystems
[sysid
];
172 if( sys
->animator_size
){
173 u32 size
= sizeof(netmsg_playerframe
)+sys
->animator_size
;
174 netmsg_playerframe
*frame
= alloca(size
);
175 frame
->inetmsg_id
= k_inetmsg_playerframe
;
177 frame
->subsystem
= localplayer
.subsystem
;
178 frame
->timestamp
= vg
.time_real
;
179 memcpy( frame
->animdata
, sys
->animator_data
, sys
->animator_size
);
181 netplayers
.up_bytes
+= size
;
183 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
184 hSteamNetworkingSockets
, network_client
.remote
,
186 k_nSteamNetworkingSend_Unreliable
, NULL
);
191 * Updates network traffic stats
193 static void remote_player_debug_update(void){
194 if( (vg
.time_real
- netplayers
.last_data_measurement
) > 1.0 ){
195 netplayers
.last_data_measurement
= vg
.time_real
;
198 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
199 struct network_player
*player
= &netplayers
.list
[i
];
200 if( player
->active
){
201 total_down
+= player
->down_bytes
;
202 player
->down_kbs
= ((f32
)player
->down_bytes
)/1024.0f
;
203 player
->down_bytes
= 0;
207 netplayers
.down_kbs
= ((f32
)total_down
)/1024.0f
;
208 netplayers
.up_kbs
= ((f32
)netplayers
.up_bytes
)/1024.0f
;
209 netplayers
.up_bytes
= 0;
214 * Debugging information
216 static void remote_player_network_imgui( m4x4f pv
){
217 if( !network_client
.network_info
)
220 ui_rect panel
= { (vg
.window_x
/ 2) - 200, 0, 400, 600 };
221 ui_fill( panel
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
224 const char *netstatus
= "PROGRAMMING ERROR";
226 struct { enum ESteamNetworkingConnectionState state
; const char *str
; }
228 { k_ESteamNetworkingConnectionState_None
, "None" },
229 { k_ESteamNetworkingConnectionState_Connecting
,
230 (const char *[]){"Connecting -",
234 }[(u32
)(vg
.time_real
/0.25) & 0x3 ] },
235 { k_ESteamNetworkingConnectionState_FindingRoute
, "Finding Route" },
236 { k_ESteamNetworkingConnectionState_Connected
, "Connected" },
237 { k_ESteamNetworkingConnectionState_ClosedByPeer
, "Closed by peer" },
238 { k_ESteamNetworkingConnectionState_ProblemDetectedLocally
,
239 "Problem Detected Locally" },
240 { k_ESteamNetworkingConnectionState_FinWait
, "Fin Wait" },
241 { k_ESteamNetworkingConnectionState_Linger
, "Linger" },
242 { k_ESteamNetworkingConnectionState_Dead
, "Dead" }
244 for( u32 i
=0; i
<vg_list_size(states
); i
++ ){
245 if( states
[i
].state
== network_client
.state
){
246 netstatus
= states
[i
].str
;
250 snprintf( buf
, 512, "Network: %s", netstatus
);
251 ui_info( panel
, buf
);
252 ui_info( panel
, "---------------------" );
254 if( network_client
.state
== k_ESteamNetworkingConnectionState_Connected
){
255 ui_info( panel
, "#-1: localplayer" );
257 snprintf( buf
, 512, "U%.1f/D%.1fkbs",
258 netplayers
.up_kbs
, netplayers
.down_kbs
);
259 ui_info( panel
, buf
);
261 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
262 struct network_player
*player
= &netplayers
.list
[i
];
263 if( player
->active
){
264 const char *sysname
= "invalid";
266 if( player
->subsystem
< k_player_subsystem_max
){
267 sysname
= player_subsystems
[ player
->subsystem
]->name
;
269 snprintf( buf
, 512, "#%u: %s [%s] D%.1fkbs",
270 i
, player
->username
, sysname
, player
->down_kbs
);
271 ui_info( panel
, buf
);
273 v4f wpos
= { 0.0f
, 2.0f
, 0.0f
, 1.0f
};
274 struct player_avatar
*av
= localplayer
.playeravatar
;
275 m4x3_mulv( netplayers
.final_mtx
[av
->sk
.bone_count
*i
], wpos
, wpos
);
276 m4x4_mulv( pv
, wpos
, wpos
);
278 if( wpos
[3] > 0.0f
){
279 v2_muls( wpos
, (1.0f
/wpos
[3]) * 0.5f
, wpos
);
280 v2_add( wpos
, (v2f
){ 0.5f
, 0.5f
}, wpos
);
283 wr
[0] = vg_clampf(wpos
[0] * vg
.window_x
, -32000.0f
,32000.0f
)-150;
284 wr
[1] = vg_clampf((1.0f
-wpos
[1]) * vg
.window_y
,
288 ui_fill( wr
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
289 ui_text( wr
, buf
, 1, k_ui_align_middle_center
, 0 );
295 ui_info( panel
, "offline" );
300 * write the remote players final_mtx
302 static void pose_remote_player( u32 index
,
303 struct interp_frame
*f0
,
304 struct interp_frame
*f1
){
306 struct interp_buffer
*buf
= &netplayers
.interp_data
[ index
];
307 struct player_avatar
*av
= localplayer
.playeravatar
;
308 struct skeleton
*sk
= &localplayer
.playeravatar
->sk
;
309 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*index
];
311 struct player_subsystem_interface
*sys0
= player_subsystems
[f0
->subsystem
],
314 player_pose pose0
, pose1
, posed
;
316 sys0
->pose( &f0
->data
, &pose0
);
319 f32 t
= (buf
->t
- f0
->timestamp
) / (f1
->timestamp
- f0
->timestamp
);
320 t
= vg_clampf( t
, 0.0f
, 1.0f
);
322 sys1
= player_subsystems
[f1
->subsystem
];
323 sys1
->pose( &f1
->data
, &pose1
);
325 lerp_player_pose( &pose0
, &pose1
, t
, &posed
);
326 apply_full_skeleton_pose( &av
->sk
, &posed
, final_mtx
);
329 apply_full_skeleton_pose( &av
->sk
, &pose0
, final_mtx
);
334 * animate remote player and store in final_mtx
336 static void animate_remote_player( u32 index
){
339 * Trys to keep the cursor inside the buffer
341 f64 min_time
= -999999999.9,
342 max_time
= 999999999.9,
343 abs_max_time
= -999999999.9;
345 struct interp_frame
*minframe
= NULL
,
347 *abs_max_frame
= NULL
;
349 struct interp_buffer
*buf
= &netplayers
.interp_data
[index
];
350 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
351 struct interp_frame
*ifr
= &buf
->frames
[i
];
354 if( (ifr
->timestamp
> min_time
) && (ifr
->timestamp
< buf
->t
) ){
355 min_time
= ifr
->timestamp
;
359 if( (ifr
->timestamp
< max_time
) && (ifr
->timestamp
> buf
->t
) ){
360 max_time
= ifr
->timestamp
;
364 if( ifr
->timestamp
> abs_max_time
){
365 abs_max_time
= ifr
->timestamp
;
371 if( minframe
&& maxframe
){
372 pose_remote_player( index
, minframe
, maxframe
);
373 buf
->t
+= vg
.time_frame_delta
;
376 buf
->t
= abs_max_time
- 0.25;
379 pose_remote_player( index
, abs_max_frame
, NULL
);
386 * Update full final_mtx for all remote players
388 static void animate_remote_players(void){
389 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
390 struct network_player
*player
= &netplayers
.list
[i
];
391 if( !player
->active
) continue;
393 animate_remote_player( i
);
398 * Draw remote players
400 static void render_remote_players( world_instance
*world
, camera
*cam
){
402 SDL_AtomicLock( &addon_system
.sl_cache_using_resources
);
404 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
405 struct network_player
*player
= &netplayers
.list
[i
];
406 if( !player
->active
) continue;
408 struct player_avatar
*av
= localplayer
.playeravatar
;
409 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*i
];
411 struct player_model
*model
=
412 addon_cache_item_if_loaded( k_addon_type_player
,
413 player
->playermodel_view_slot
);
415 if( !model
) model
= &localplayer
.fallback_model
;
416 render_playermodel( cam
, world
, 0, model
, &av
->sk
, final_mtx
);
418 struct player_board
*board
=
419 addon_cache_item_if_loaded( k_addon_type_board
,
420 player
->board_view_slot
);
422 /* TODO: Board pose */
424 render_board( cam
, world
, board
,
425 final_mtx
[localplayer
.playeravatar
->id_board
],
426 &localplayer
.pose
.board
,
427 k_board_shader_player
);
431 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);