1 #include "player_remote.h"
3 #include "player_render.h"
4 #include "network_common.h"
7 static void player_remote_clear( 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
);
11 memset( player
, 0, sizeof(*player
) );
12 strcpy( player
->username
, "unknown" );
13 player
->subsystem
= k_player_subsystem_invalid
;
17 * re-attatches addon_reg pointers on the remote client if we have the same
20 static void relink_remote_player_worlds( u32 client_id
){
21 struct network_player
*player
= &netplayers
.list
[client_id
];
23 player
->hub_match
= 0;
24 player
->client_match
= 0;
27 addon_uid_to_alias( player
->items
[k_netmsg_playeritem_world0
], &q0
);
28 addon_uid_to_alias( player
->items
[k_netmsg_playeritem_world1
], &q1
);
31 * currently in 10.23, the hub world will always be the same.
32 * this might but probably wont change in the future
34 if( world_static
.addon_hub
)
35 if( addon_alias_eq( &q0
, &world_static
.addon_hub
->alias
) )
36 player
->hub_match
= 1;
38 if( world_static
.addon_client
)
39 if( addon_alias_eq( &q1
, &world_static
.addon_client
->alias
) )
40 player
->client_match
= 1;
44 * re-attatches addon_reg pointers on the remote client if we have the mod
47 * Run if local worlds change
49 static void relink_all_remote_player_worlds(void){
50 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
51 struct network_player
*player
= &netplayers
.list
[i
];
53 relink_remote_player_worlds(i
);
57 static void player_remote_rx_200_300( SteamNetworkingMessage_t
*msg
){
58 netmsg_blank
*tmp
= msg
->m_pData
;
60 if( tmp
->inetmsg_id
== k_inetmsg_playerjoin
){
61 netmsg_playerjoin
*playerjoin
= msg
->m_pData
;
62 if( !packet_minsize( msg
, sizeof(*playerjoin
) )) return;
64 if( playerjoin
->index
< vg_list_size(netplayers
.list
) ){
65 struct network_player
*player
= &netplayers
.list
[ playerjoin
->index
];
66 player_remote_clear( player
);
69 /* TODO: interpret the uids */
70 player
->board_view_slot
= 0;
71 player
->playermodel_view_slot
= 0;
73 struct interp_buffer
*buf
= &netplayers
.interp_data
[playerjoin
->index
];
75 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
76 buf
->frames
[i
].active
= 0;
79 vg_info( "#%u joined\n", playerjoin
->index
);
82 vg_error( "inetmsg_playerjoin: player index out of range\n" );
85 else if( tmp
->inetmsg_id
== k_inetmsg_playerleave
){
86 netmsg_playerleave
*playerleave
= msg
->m_pData
;
87 if( !packet_minsize( msg
, sizeof(*playerleave
) )) return;
89 if( playerleave
->index
< vg_list_size(netplayers
.list
) ){
90 struct network_player
*player
= &netplayers
.list
[ playerleave
->index
];
91 player_remote_clear( player
);
93 vg_info( "player leave (%d)\n", playerleave
->index
);
96 vg_error( "inetmsg_playerleave: player index out of range\n" );
99 else if( tmp
->inetmsg_id
== k_inetmsg_playerusername
){
100 netmsg_playerusername
*update
= msg
->m_pData
;
101 if( !packet_minsize( msg
, sizeof(*update
) )) return;
103 if( update
->index
< vg_list_size(netplayers
.list
) ){
104 struct network_player
*player
= &netplayers
.list
[ update
->index
];
106 network_msgstring( update
->name
, msg
->m_cbSize
, sizeof(*update
),
107 player
->username
, sizeof(player
->username
) );
109 vg_info( "#%u changed username to: %s\n",
110 update
->index
, player
->username
);
113 vg_error( "inetmsg_playerleave: player index out of range\n" );
116 else if( tmp
->inetmsg_id
== k_inetmsg_playerframe
){
117 u32 datasize
= msg
->m_cbSize
- sizeof(netmsg_playerframe
);
119 if( datasize
> sizeof(union interp_animdata
) ){
120 vg_error( "Player frame data exceeds animdata size\n" );
124 netmsg_playerframe
*frame
= msg
->m_pData
;
125 if( frame
->client
>= vg_list_size(netplayers
.list
) ){
126 vg_error( "inetmsg_playerframe: player index out of range\n" );
130 if( frame
->subsystem
>= k_player_subsystem_max
){
131 vg_error( "inetmsg_playerframe: subsystem out of range\n" );
135 struct interp_buffer
*ib
= &netplayers
.interp_data
[ frame
->client
];
136 struct interp_frame
*dest
= NULL
;
138 f64 min_time
= INFINITY
;
139 for( u32 i
=0; i
<vg_list_size(ib
->frames
); i
++ ){
140 struct interp_frame
*ifr
= &ib
->frames
[i
];
147 if( ifr
->timestamp
< min_time
){
148 min_time
= ifr
->timestamp
;
154 dest
->timestamp
= frame
->timestamp
;
155 dest
->subsystem
= frame
->subsystem
;
156 dest
->instance_id
= frame
->instance_id
;
157 dest
->boundary_hash
= frame
->boundary_hash
;
159 struct network_player
*player
= &netplayers
.list
[ frame
->client
];
161 struct player_subsystem_interface
*sys
=
162 player_subsystems
[ frame
->subsystem
];
164 if( sys
->network_animator_exchange
){
166 .mode
= k_bitpack_decompress
,
167 .buffer
= frame
->animdata
,
168 .buffer_len
= datasize
,
172 memset( &dest
->data
, 0, sys
->animator_size
);
173 sys
->network_animator_exchange( &ctx
, &dest
->data
);
176 memcpy( &dest
->data
, frame
->animdata
, datasize
);
179 player
->subsystem
= frame
->subsystem
;
180 player
->down_bytes
+= msg
->m_cbSize
;
182 else if( tmp
->inetmsg_id
== k_inetmsg_playeritem
){
183 netmsg_playeritem
*item
= msg
->m_pData
;
184 if( !packet_minsize( msg
, sizeof(*item
)+1 )) return;
186 if( item
->client
>= vg_list_size(netplayers
.list
) ){
187 vg_error( "inetmsg_playerframe: player index out of range\n" );
191 if( item
->type_index
>= k_netmsg_playeritem_max
){
192 vg_warn( "Client #%d invalid equip type %u\n",
193 (i32
)item
->client
, (u32
)item
->type_index
);
197 vg_info( "Client #%d equiped: [%s] %s\n",
199 (const char *[]){[k_netmsg_playeritem_board
]="board",
200 [k_netmsg_playeritem_player
]="player",
201 [k_netmsg_playeritem_world0
]="world0",
202 [k_netmsg_playeritem_world1
]="world1"
203 }[item
->type_index
], item
->uid
);
205 struct network_player
*player
= &netplayers
.list
[ item
->client
];
206 char *uid
= player
->items
[ item
->type_index
];
208 network_msgstring( item
->uid
, msg
->m_cbSize
, sizeof(*item
),
209 uid
, ADDON_UID_MAX
);
211 if( item
->type_index
== k_netmsg_playeritem_board
){
212 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
213 player
->board_view_slot
=
214 addon_cache_create_viewer_from_uid( k_addon_type_board
, uid
);
216 else if( item
->type_index
== k_netmsg_playeritem_player
){
217 addon_cache_unwatch( k_addon_type_player
,
218 player
->playermodel_view_slot
);
219 player
->playermodel_view_slot
=
220 addon_cache_create_viewer_from_uid( k_addon_type_player
, uid
);
222 else if( (item
->type_index
== k_netmsg_playeritem_world0
) ||
223 (item
->type_index
== k_netmsg_playeritem_world1
) ){
224 relink_remote_player_worlds( item
->client
);
230 * Write localplayer pose to network
232 static void remote_player_send_playerframe(void){
233 u8 sysid
= localplayer
.subsystem
;
234 if( sysid
>= k_player_subsystem_max
) return;
236 struct player_subsystem_interface
*sys
= player_subsystems
[sysid
];
238 if( sys
->animator_size
){
239 u32 size
= sizeof(netmsg_playerframe
)+sys
->animator_size
;
240 netmsg_playerframe
*frame
= alloca(size
);
241 frame
->inetmsg_id
= k_inetmsg_playerframe
;
242 frame
->client
= 0xff;
243 frame
->subsystem
= localplayer
.subsystem
;
244 frame
->timestamp
= vg
.time_real
;
245 frame
->boundary_hash
= localplayer
.boundary_hash
;
246 frame
->instance_id
= world_static
.active_instance
;
248 if( sys
->network_animator_exchange
){
250 .mode
= k_bitpack_compress
,
251 .buffer
= frame
->animdata
,
256 sys
->network_animator_exchange( &ctx
, sys
->animator_data
);
260 memcpy( frame
->animdata
, sys
->animator_data
, sys
->animator_size
);
263 netplayers
.up_bytes
+= size
;
265 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
266 hSteamNetworkingSockets
, network_client
.remote
,
268 k_nSteamNetworkingSend_Unreliable
, NULL
);
273 * Updates network traffic stats
275 static void remote_player_debug_update(void){
276 if( (vg
.time_real
- netplayers
.last_data_measurement
) > 1.0 ){
277 netplayers
.last_data_measurement
= vg
.time_real
;
280 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
281 struct network_player
*player
= &netplayers
.list
[i
];
282 if( player
->active
){
283 total_down
+= player
->down_bytes
;
284 player
->down_kbs
= ((f32
)player
->down_bytes
)/1024.0f
;
285 player
->down_bytes
= 0;
289 netplayers
.down_kbs
= ((f32
)total_down
)/1024.0f
;
290 netplayers
.up_kbs
= ((f32
)netplayers
.up_bytes
)/1024.0f
;
291 netplayers
.up_bytes
= 0;
296 * Debugging information
298 static void remote_player_network_imgui( m4x4f pv
){
299 if( !network_client
.network_info
)
302 ui_rect panel
= { (vg
.window_x
/ 2) - 200, 0, 400, 600 };
303 ui_fill( panel
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
306 const char *netstatus
= "PROGRAMMING ERROR";
308 struct { enum ESteamNetworkingConnectionState state
; const char *str
; }
310 { k_ESteamNetworkingConnectionState_None
, "None" },
311 { k_ESteamNetworkingConnectionState_Connecting
,
312 (const char *[]){"Connecting -",
316 }[(u32
)(vg
.time_real
/0.25) & 0x3 ] },
317 { k_ESteamNetworkingConnectionState_FindingRoute
, "Finding Route" },
318 { k_ESteamNetworkingConnectionState_Connected
, "Connected" },
319 { k_ESteamNetworkingConnectionState_ClosedByPeer
, "Closed by peer" },
320 { k_ESteamNetworkingConnectionState_ProblemDetectedLocally
,
321 "Problem Detected Locally" },
322 { k_ESteamNetworkingConnectionState_FinWait
, "Fin Wait" },
323 { k_ESteamNetworkingConnectionState_Linger
, "Linger" },
324 { k_ESteamNetworkingConnectionState_Dead
, "Dead" }
326 for( u32 i
=0; i
<vg_list_size(states
); i
++ ){
327 if( states
[i
].state
== network_client
.state
){
328 netstatus
= states
[i
].str
;
332 snprintf( buf
, 512, "Network: %s", netstatus
);
333 ui_info( panel
, buf
);
334 ui_info( panel
, "---------------------" );
336 if( network_client
.state
== k_ESteamNetworkingConnectionState_Connected
){
337 ui_info( panel
, "#-1: localplayer" );
339 snprintf( buf
, 512, "U%.1f/D%.1fkbs",
340 netplayers
.up_kbs
, netplayers
.down_kbs
);
341 ui_info( panel
, buf
);
343 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
344 struct network_player
*player
= &netplayers
.list
[i
];
345 if( player
->active
){
346 const char *sysname
= "invalid";
348 if( player
->subsystem
< k_player_subsystem_max
){
349 sysname
= player_subsystems
[ player
->subsystem
]->name
;
351 snprintf( buf
, 512, "#%u: %s [%s] D%.1fkbs",
352 i
, player
->username
, sysname
, player
->down_kbs
);
353 ui_info( panel
, buf
);
355 v4f wpos
= { 0.0f
, 2.0f
, 0.0f
, 1.0f
};
356 struct player_avatar
*av
= localplayer
.playeravatar
;
357 m4x3_mulv( netplayers
.final_mtx
[av
->sk
.bone_count
*i
], wpos
, wpos
);
358 m4x4_mulv( pv
, wpos
, wpos
);
360 if( wpos
[3] > 0.0f
){
361 v2_muls( wpos
, (1.0f
/wpos
[3]) * 0.5f
, wpos
);
362 v2_add( wpos
, (v2f
){ 0.5f
, 0.5f
}, wpos
);
365 wr
[0] = vg_clampf(wpos
[0] * vg
.window_x
, -32000.0f
,32000.0f
)-150;
366 wr
[1] = vg_clampf((1.0f
-wpos
[1]) * vg
.window_y
,
370 ui_fill( wr
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
371 ui_text( wr
, buf
, 1, k_ui_align_middle_center
, 0 );
377 ui_info( panel
, "offline" );
382 * write the remote players final_mtx
384 static void pose_remote_player( u32 index
,
385 struct interp_frame
*f0
,
386 struct interp_frame
*f1
){
388 struct network_player
*player
= &netplayers
.list
[ index
];
390 struct interp_buffer
*buf
= &netplayers
.interp_data
[ index
];
391 struct player_avatar
*av
= localplayer
.playeravatar
;
392 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*index
];
393 struct player_board_pose
*board_pose
= &netplayers
.board_poses
[index
];
395 struct player_subsystem_interface
*sys0
= player_subsystems
[f0
->subsystem
],
398 player_pose pose0
, pose1
, posed
;
399 sys0
->pose( &f0
->data
, &pose0
);
404 f32 t
= (buf
->t
- f0
->timestamp
) / (f1
->timestamp
- f0
->timestamp
);
405 t
= vg_clampf( t
, 0.0f
, 1.0f
);
407 sys1
= player_subsystems
[f1
->subsystem
];
408 sys1
->pose( &f1
->data
, &pose1
);
410 u16 bounds
= f0
->boundary_hash
^f1
->boundary_hash
;
412 if( bounds
& NETMSG_BOUNDARY_BIT
)
415 if( bounds
& NETMSG_GATE_BOUNDARY_BIT
){
416 /* TODO: Extra work retransforming the root_co, instance_id.. etc */
420 instance_id
= f1
->instance_id
;
422 lerp_player_pose( &pose0
, &pose1
, t
, &posed
);
423 apply_full_skeleton_pose( &av
->sk
, &posed
, final_mtx
);
424 memcpy( board_pose
, &posed
.board
, sizeof(*board_pose
) );
427 instance_id
= f0
->instance_id
;
429 apply_full_skeleton_pose( &av
->sk
, &pose0
, final_mtx
);
430 memcpy( board_pose
, &pose0
.board
, sizeof(*board_pose
) );
434 if( player
->client_match
){
435 player
->active_world
= &world_static
.instances
[ instance_id
];
439 if( player
->hub_match
){
440 player
->active_world
= &world_static
.instances
[ instance_id
];
446 * animate remote player and store in final_mtx
448 static void animate_remote_player( u32 index
){
451 * Trys to keep the cursor inside the buffer
453 f64 min_time
= -999999999.9,
454 max_time
= 999999999.9,
455 abs_max_time
= -999999999.9;
457 struct interp_frame
*minframe
= NULL
,
459 *abs_max_frame
= NULL
;
461 struct interp_buffer
*buf
= &netplayers
.interp_data
[index
];
462 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
463 struct interp_frame
*ifr
= &buf
->frames
[i
];
466 if( (ifr
->timestamp
> min_time
) && (ifr
->timestamp
< buf
->t
) ){
467 min_time
= ifr
->timestamp
;
471 if( (ifr
->timestamp
< max_time
) && (ifr
->timestamp
> buf
->t
) ){
472 max_time
= ifr
->timestamp
;
476 if( ifr
->timestamp
> abs_max_time
){
477 abs_max_time
= ifr
->timestamp
;
483 struct network_player
*player
= &netplayers
.list
[ index
];
484 player
->active_world
= NULL
;
486 if( minframe
&& maxframe
){
487 pose_remote_player( index
, minframe
, maxframe
);
488 buf
->t
+= vg
.time_frame_delta
;
491 buf
->t
= abs_max_time
- 0.25;
494 pose_remote_player( index
, abs_max_frame
, NULL
);
501 * Update full final_mtx for all remote players
503 static void animate_remote_players(void){
504 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
505 struct network_player
*player
= &netplayers
.list
[i
];
506 if( !player
->active
) continue;
508 animate_remote_player( i
);
513 * Draw remote players
515 static void render_remote_players( world_instance
*world
, camera
*cam
){
517 SDL_AtomicLock( &addon_system
.sl_cache_using_resources
);
519 for( u32 i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ ){
520 struct network_player
*player
= &netplayers
.list
[i
];
521 if( !player
->active
) continue;
522 if( player
->active_world
!= world
) continue;
524 struct player_avatar
*av
= localplayer
.playeravatar
;
525 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*i
];
527 struct player_model
*model
=
528 addon_cache_item_if_loaded( k_addon_type_player
,
529 player
->playermodel_view_slot
);
531 if( !model
) model
= &localplayer
.fallback_model
;
532 render_playermodel( cam
, world
, 0, model
, &av
->sk
, final_mtx
);
534 struct player_board
*board
=
535 addon_cache_item_if_loaded( k_addon_type_board
,
536 player
->board_view_slot
);
537 render_board( cam
, world
, board
,
538 final_mtx
[localplayer
.playeravatar
->id_board
],
539 &netplayers
.board_poses
[ i
],
540 k_board_shader_player
);
543 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
546 /* TODO: Which world is the player in
547 * nametags with occlusion