1 #include "player_remote.h"
3 #include "player_render.h"
4 #include "network_common.h"
9 static i32 k_show_own_name
= 0;
11 static void player_remote_clear( struct network_player
*player
){
12 addon_cache_unwatch( k_addon_type_player
, player
->playermodel_view_slot
);
13 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
15 memset( player
, 0, sizeof(*player
) );
16 strcpy( player
->username
, "unknown" );
17 player
->subsystem
= k_player_subsystem_invalid
;
21 * re-attatches addon_reg pointers on the remote client if we have the same
24 static void relink_remote_player_worlds( u32 client_id
){
25 struct network_player
*player
= &netplayers
.list
[client_id
];
28 addon_uid_to_alias( player
->items
[k_netmsg_playeritem_world0
], &q
[0] );
29 addon_uid_to_alias( player
->items
[k_netmsg_playeritem_world1
], &q
[1] );
32 * currently in 10.23, the hub world will always be the same.
33 * this might but probably wont change in the future
35 for( u32 i
=0; i
<k_world_max
; i
++ ){
36 addon_reg
*reg
= world_static
.instance_addons
[ i
];
38 player
->world_match
[i
] = 0;
41 if( addon_alias_eq( &q
[i
], &world_static
.instance_addons
[i
]->alias
) )
42 player
->world_match
[i
] = 1;
48 * re-attatches addon_reg pointers on the remote client if we have the mod
51 * Run if local worlds change
53 static void relink_all_remote_player_worlds(void){
54 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
55 struct network_player
*player
= &netplayers
.list
[i
];
57 relink_remote_player_worlds(i
);
61 static void player_remote_rx_200_300( SteamNetworkingMessage_t
*msg
){
62 netmsg_blank
*tmp
= msg
->m_pData
;
64 if( tmp
->inetmsg_id
== k_inetmsg_playerjoin
){
65 netmsg_playerjoin
*playerjoin
= msg
->m_pData
;
66 if( !packet_minsize( msg
, sizeof(*playerjoin
) )) return;
68 if( playerjoin
->index
< vg_list_size(netplayers
.list
) ){
69 struct network_player
*player
= &netplayers
.list
[ playerjoin
->index
];
70 player_remote_clear( player
);
73 /* TODO: interpret the uids */
74 player
->board_view_slot
= 0;
75 player
->playermodel_view_slot
= 0;
77 struct interp_buffer
*buf
= &netplayers
.interp_data
[playerjoin
->index
];
79 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
80 buf
->frames
[i
].active
= 0;
83 vg_info( "#%u joined\n", playerjoin
->index
);
86 vg_error( "inetmsg_playerjoin: player index out of range\n" );
89 else if( tmp
->inetmsg_id
== k_inetmsg_playerleave
){
90 netmsg_playerleave
*playerleave
= msg
->m_pData
;
91 if( !packet_minsize( msg
, sizeof(*playerleave
) )) return;
93 if( playerleave
->index
< vg_list_size(netplayers
.list
) ){
94 struct network_player
*player
= &netplayers
.list
[ playerleave
->index
];
95 player_remote_clear( player
);
97 vg_info( "player leave (%d)\n", playerleave
->index
);
100 vg_error( "inetmsg_playerleave: player index out of range\n" );
103 else if( tmp
->inetmsg_id
== k_inetmsg_playerusername
){
104 netmsg_playerusername
*update
= msg
->m_pData
;
105 if( !packet_minsize( msg
, sizeof(*update
) )) return;
107 if( update
->index
< vg_list_size(netplayers
.list
) ){
108 struct network_player
*player
= &netplayers
.list
[ update
->index
];
110 network_msgstring( update
->name
, msg
->m_cbSize
, sizeof(*update
),
111 player
->username
, sizeof(player
->username
) );
113 vg_info( "#%u changed username to: %s\n",
114 update
->index
, player
->username
);
117 vg_error( "inetmsg_playerleave: player index out of range\n" );
120 else if( tmp
->inetmsg_id
== k_inetmsg_playerframe
){
121 u32 datasize
= msg
->m_cbSize
- sizeof(netmsg_playerframe
);
123 if( datasize
> sizeof(union interp_animdata
) ){
124 vg_error( "Player frame data exceeds animdata size\n" );
128 netmsg_playerframe
*frame
= msg
->m_pData
;
129 if( frame
->client
>= vg_list_size(netplayers
.list
) ){
130 vg_error( "inetmsg_playerframe: player index out of range\n" );
134 if( frame
->subsystem
>= k_player_subsystem_max
){
135 vg_error( "inetmsg_playerframe: subsystem out of range\n" );
139 struct interp_buffer
*ib
= &netplayers
.interp_data
[ frame
->client
];
140 struct interp_frame
*dest
= NULL
;
142 f64 min_time
= INFINITY
;
143 for( u32 i
=0; i
<vg_list_size(ib
->frames
); i
++ ){
144 struct interp_frame
*ifr
= &ib
->frames
[i
];
151 if( ifr
->timestamp
< min_time
){
152 min_time
= ifr
->timestamp
;
158 dest
->subsystem
= frame
->subsystem
;
159 dest
->instance_id
= frame
->instance_id
;
162 .mode
= k_bitpack_decompress
,
163 .buffer
= frame
->animdata
,
164 .buffer_len
= datasize
,
170 * -------------------------------------------------------------*/
172 for( u32 i
=0; i
<frame
->sound_effects
; i
++ ){
174 net_sfx_exchange( &ctx
, &sfx
);
176 f64 t
= (frame
->timestamp
- NETWORK_FRAMERATE
) +
177 (sfx
.subframe
*NETWORK_FRAMERATE
);
179 f32 remaining
= t
- ib
->t
;
181 if( remaining
<= 0.0f
)
182 net_sfx_play( &sfx
);
184 struct net_sfx
*dst
= NULL
;
186 for( u32 j
=0; j
<NETWORK_SFX_QUEUE_LENGTH
; j
++ ){
187 struct net_sfx
*sj
= &netplayers
.sfx_queue
[j
];
188 if( sj
->system
== k_player_subsystem_invalid
){
193 if( sj
->priority
< sfx
.priority
)
198 dst
->subframe
= remaining
;
203 * -------------------------------------------------------------*/
205 dest
->timestamp
= frame
->timestamp
;
206 dest
->boundary_hash
= frame
->boundary_hash
;
208 struct network_player
*player
= &netplayers
.list
[ frame
->client
];
209 struct player_subsystem_interface
*sys
=
210 player_subsystems
[ frame
->subsystem
];
212 if( sys
->network_animator_exchange
){
213 memset( &dest
->data
, 0, sys
->animator_size
);
214 sys
->network_animator_exchange( &ctx
, &dest
->data
);
217 bitpack_bytes( &ctx
, sys
->animator_size
, sys
->animator_data
);
220 player
->subsystem
= frame
->subsystem
;
221 player
->down_bytes
+= msg
->m_cbSize
;
223 else if( tmp
->inetmsg_id
== k_inetmsg_playeritem
){
224 netmsg_playeritem
*item
= msg
->m_pData
;
225 if( !packet_minsize( msg
, sizeof(*item
)+1 )) return;
227 if( item
->client
>= vg_list_size(netplayers
.list
) ){
228 vg_error( "inetmsg_playerframe: player index out of range\n" );
232 if( item
->type_index
>= k_netmsg_playeritem_max
){
233 vg_warn( "Client #%d invalid equip type %u\n",
234 (i32
)item
->client
, (u32
)item
->type_index
);
238 vg_info( "Client #%d equiped: [%s] %s\n",
240 (const char *[]){[k_netmsg_playeritem_board
]="board",
241 [k_netmsg_playeritem_player
]="player",
242 [k_netmsg_playeritem_world0
]="world0",
243 [k_netmsg_playeritem_world1
]="world1"
244 }[item
->type_index
], item
->uid
);
246 struct network_player
*player
= &netplayers
.list
[ item
->client
];
247 char *uid
= player
->items
[ item
->type_index
];
249 network_msgstring( item
->uid
, msg
->m_cbSize
, sizeof(*item
),
250 uid
, ADDON_UID_MAX
);
252 if( item
->type_index
== k_netmsg_playeritem_board
){
253 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
254 player
->board_view_slot
=
255 addon_cache_create_viewer_from_uid( k_addon_type_board
, uid
);
257 else if( item
->type_index
== k_netmsg_playeritem_player
){
258 addon_cache_unwatch( k_addon_type_player
,
259 player
->playermodel_view_slot
);
260 player
->playermodel_view_slot
=
261 addon_cache_create_viewer_from_uid( k_addon_type_player
, uid
);
263 else if( (item
->type_index
== k_netmsg_playeritem_world0
) ||
264 (item
->type_index
== k_netmsg_playeritem_world1
) ){
265 relink_remote_player_worlds( item
->client
);
271 * Write localplayer pose to network
273 static void remote_player_send_playerframe(void){
274 u8 sysid
= localplayer
.subsystem
;
275 if( sysid
>= k_player_subsystem_max
) return;
277 struct player_subsystem_interface
*sys
= player_subsystems
[sysid
];
279 if( sys
->animator_size
){
280 u32 max_buf_size
= sys
->animator_size
+ sizeof(localplayer
.sfx_buffer
),
281 base_size
= sizeof(struct netmsg_playerframe
),
282 max_packet
= base_size
+ max_buf_size
;
284 netmsg_playerframe
*frame
= alloca( max_packet
);
285 frame
->inetmsg_id
= k_inetmsg_playerframe
;
286 frame
->client
= 0xff;
287 frame
->subsystem
= localplayer
.subsystem
;
288 frame
->instance_id
= world_static
.active_instance
;
291 .mode
= k_bitpack_compress
,
292 .buffer
= frame
->animdata
,
293 .buffer_len
= max_buf_size
,
298 * ---------------------------------------------*/
300 frame
->sound_effects
= localplayer
.sfx_buffer_count
;
301 for( u32 i
=0; i
<localplayer
.sfx_buffer_count
; i
++ )
302 net_sfx_exchange( &ctx
, &localplayer
.sfx_buffer
[i
] );
305 * -----------------------------------------------*/
307 frame
->timestamp
= vg
.time_real
;
308 frame
->boundary_hash
= localplayer
.boundary_hash
;
309 if( sys
->network_animator_exchange
)
310 sys
->network_animator_exchange( &ctx
, sys
->animator_data
);
312 bitpack_bytes( &ctx
, sys
->animator_size
, sys
->animator_data
);
314 u32 wire_size
= base_size
+ ctx
.bytes
;
315 netplayers
.up_bytes
+= wire_size
;
317 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
318 hSteamNetworkingSockets
, network_client
.remote
,
320 k_nSteamNetworkingSend_Unreliable
, NULL
);
325 * Updates network traffic stats
327 static void remote_player_debug_update(void){
328 if( (vg
.time_real
- netplayers
.last_data_measurement
) > 1.0 ){
329 netplayers
.last_data_measurement
= vg
.time_real
;
332 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
333 struct network_player
*player
= &netplayers
.list
[i
];
334 if( player
->active
){
335 total_down
+= player
->down_bytes
;
336 player
->down_kbs
= ((f32
)player
->down_bytes
)/1024.0f
;
337 player
->down_bytes
= 0;
341 netplayers
.down_kbs
= ((f32
)total_down
)/1024.0f
;
342 netplayers
.up_kbs
= ((f32
)netplayers
.up_bytes
)/1024.0f
;
343 netplayers
.up_bytes
= 0;
347 static void remote_player_nametag( m4x4f pv
, v3f co
, const char *name
){
349 vg_ui
.font
= &vg_ui_font_big
;
355 m4x4_mulv( pv
, wpos
, wpos
);
357 if( wpos
[3] > 0.0f
){
358 v2_muls( wpos
, (1.0f
/wpos
[3]) * 0.5f
, wpos
);
359 v2_add( wpos
, (v2f
){ 0.5f
, 0.5f
}, wpos
);
362 wr
[0] = vg_clampf(wpos
[0] * vg
.window_x
, -32000.0f
,32000.0f
)-150;
363 wr
[1] = vg_clampf((1.0f
-wpos
[1]) * vg
.window_y
,
367 ui_fill( wr
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
368 ui_text( wr
, name
, 1, k_ui_align_middle_center
, 0 );
370 vg_ui
.font
= &vg_ui_font_small
;
374 * Debugging information
376 static void remote_player_network_imgui( m4x4f pv
){
377 if( !network_client
.network_info
)
380 ui_rect panel
= { (vg
.window_x
/ 2) - 200, 0, 400, 600 };
381 ui_fill( panel
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
384 const char *netstatus
= "PROGRAMMING ERROR";
386 struct { enum ESteamNetworkingConnectionState state
; const char *str
; }
388 { k_ESteamNetworkingConnectionState_None
, "None" },
389 { k_ESteamNetworkingConnectionState_Connecting
,
390 (const char *[]){"Connecting -",
394 }[(u32
)(vg
.time_real
/0.25) & 0x3 ] },
395 { k_ESteamNetworkingConnectionState_FindingRoute
, "Finding Route" },
396 { k_ESteamNetworkingConnectionState_Connected
, "Connected" },
397 { k_ESteamNetworkingConnectionState_ClosedByPeer
, "Closed by peer" },
398 { k_ESteamNetworkingConnectionState_ProblemDetectedLocally
,
399 "Problem Detected Locally" },
400 { k_ESteamNetworkingConnectionState_FinWait
, "Fin Wait" },
401 { k_ESteamNetworkingConnectionState_Linger
, "Linger" },
402 { k_ESteamNetworkingConnectionState_Dead
, "Dead" }
404 for( u32 i
=0; i
<vg_list_size(states
); i
++ ){
405 if( states
[i
].state
== network_client
.state
){
406 netstatus
= states
[i
].str
;
410 snprintf( buf
, 512, "Network: %s", netstatus
);
411 ui_info( panel
, buf
);
412 ui_info( panel
, "---------------------" );
414 if( network_client
.state
== k_ESteamNetworkingConnectionState_Connected
){
415 ui_info( panel
, "#-1: localplayer" );
417 snprintf( buf
, 512, "U%.3f/D%.3fkbs",
418 netplayers
.up_kbs
, netplayers
.down_kbs
);
419 ui_info( panel
, buf
);
421 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
422 struct network_player
*player
= &netplayers
.list
[i
];
423 if( player
->active
){
424 const char *sysname
= "invalid";
426 if( player
->subsystem
< k_player_subsystem_max
){
427 sysname
= player_subsystems
[ player
->subsystem
]->name
;
429 snprintf( buf
, 512, "#%u: %s [%s] D%.1fkbs",
430 i
, player
->username
, sysname
, player
->down_kbs
);
431 ui_info( panel
, buf
);
433 struct player_avatar
*av
= localplayer
.playeravatar
;
434 remote_player_nametag(
436 netplayers
.final_mtx
[av
->sk
.bone_count
*i
][3],
442 ui_info( panel
, "offline" );
445 struct player_avatar
*av
= localplayer
.playeravatar
;
446 remote_player_nametag(
448 localplayer
.final_mtx
[0][3],
453 * write the remote players final_mtx
455 static void pose_remote_player( u32 index
,
456 struct interp_frame
*f0
,
457 struct interp_frame
*f1
){
459 struct network_player
*player
= &netplayers
.list
[ index
];
461 struct interp_buffer
*buf
= &netplayers
.interp_data
[ index
];
462 struct player_avatar
*av
= localplayer
.playeravatar
;
463 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*index
];
464 struct player_board_pose
*board_pose
= &netplayers
.board_poses
[index
];
466 struct player_subsystem_interface
*sys0
= player_subsystems
[f0
->subsystem
],
469 player_pose pose0
, pose1
, posed
;
470 sys0
->pose( &f0
->data
, &pose0
);
475 f32 t
= (buf
->t
- f0
->timestamp
) / (f1
->timestamp
- f0
->timestamp
);
476 t
= vg_clampf( t
, 0.0f
, 1.0f
);
478 sys1
= player_subsystems
[f1
->subsystem
];
479 sys1
->pose( &f1
->data
, &pose1
);
481 u16 bounds
= f0
->boundary_hash
^f1
->boundary_hash
;
483 if( bounds
& NETMSG_BOUNDARY_BIT
)
486 if( bounds
& NETMSG_GATE_BOUNDARY_BIT
){
487 /* TODO: Extra work retransforming the root_co, instance_id.. etc */
491 instance_id
= f1
->instance_id
;
493 lerp_player_pose( &pose0
, &pose1
, t
, &posed
);
494 apply_full_skeleton_pose( &av
->sk
, &posed
, final_mtx
);
495 memcpy( board_pose
, &posed
.board
, sizeof(*board_pose
) );
498 instance_id
= f0
->instance_id
;
500 apply_full_skeleton_pose( &av
->sk
, &pose0
, final_mtx
);
501 memcpy( board_pose
, &pose0
.board
, sizeof(*board_pose
) );
504 if( player
->world_match
[ instance_id
] )
505 player
->active_world
= &world_static
.instances
[ instance_id
];
509 * animate remote player and store in final_mtx
511 static void animate_remote_player( u32 index
){
514 * Trys to keep the cursor inside the buffer
516 f64 min_time
= -999999999.9,
517 max_time
= 999999999.9,
518 abs_max_time
= -999999999.9;
520 struct interp_frame
*minframe
= NULL
,
522 *abs_max_frame
= NULL
;
524 struct interp_buffer
*buf
= &netplayers
.interp_data
[index
];
525 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
526 struct interp_frame
*ifr
= &buf
->frames
[i
];
529 if( (ifr
->timestamp
> min_time
) && (ifr
->timestamp
< buf
->t
) ){
530 min_time
= ifr
->timestamp
;
534 if( (ifr
->timestamp
< max_time
) && (ifr
->timestamp
> buf
->t
) ){
535 max_time
= ifr
->timestamp
;
539 if( ifr
->timestamp
> abs_max_time
){
540 abs_max_time
= ifr
->timestamp
;
546 struct network_player
*player
= &netplayers
.list
[ index
];
547 player
->active_world
= NULL
;
549 if( minframe
&& maxframe
){
550 pose_remote_player( index
, minframe
, maxframe
);
551 buf
->t
+= vg
.time_frame_delta
;
554 buf
->t
= abs_max_time
- 0.25;
557 pose_remote_player( index
, abs_max_frame
, NULL
);
564 * Update full final_mtx for all remote players
566 static void animate_remote_players(void){
567 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
568 struct network_player
*player
= &netplayers
.list
[i
];
569 if( !player
->active
) continue;
571 animate_remote_player( i
);
576 * Draw remote players
578 static void render_remote_players( world_instance
*world
, camera
*cam
){
579 SDL_AtomicLock( &addon_system
.sl_cache_using_resources
);
581 for( u32 i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ ){
582 struct network_player
*player
= &netplayers
.list
[i
];
583 if( !player
->active
) continue;
584 if( player
->active_world
!= world
) continue;
586 struct player_avatar
*av
= localplayer
.playeravatar
;
587 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*i
];
589 struct player_model
*model
=
590 addon_cache_item_if_loaded( k_addon_type_player
,
591 player
->playermodel_view_slot
);
593 if( !model
) model
= &localplayer
.fallback_model
;
594 render_playermodel( cam
, world
, 0, model
, &av
->sk
, final_mtx
);
596 struct player_board
*board
=
597 addon_cache_item_if_loaded( k_addon_type_board
,
598 player
->board_view_slot
);
599 render_board( cam
, world
, board
,
600 final_mtx
[localplayer
.playeravatar
->id_board
],
601 &netplayers
.board_poses
[ i
],
602 k_board_shader_player
);
605 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
608 static void render_remote_player_nametag( v3f co
, const char *name
){
610 m4x3_identity( mlocal
);
611 mlocal
[3][0] -= font3d_string_width( 2, name
) * 0.5f
;
614 m3x3_identity( mmdl
);
615 for( int i
=0; i
<3; i
++ )
616 v3_muls( skaterift
.cam
.mtx
.v
[i
], 0.2f
, mmdl
[i
] );
617 m3x3_transpose( mmdl
, mmdl
);
618 v3_add( co
, (v3f
){0.0f
,2.0f
,0.0f
}, mmdl
[3] );
620 m4x3_mul( mmdl
, mlocal
, mmdl
);
621 font3d_simple_draw( 2, name
, &skaterift
.cam
, mmdl
);
624 static void render_remote_players_tags( world_instance
*world
, camera
*cam
){
626 glEnable(GL_DEPTH_TEST
);
627 glBlendFunc( GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
628 glBlendEquation(GL_FUNC_ADD
);
630 font3d_bind( &gui
.font
, k_font_shader_default
, 1, NULL
, &skaterift
.cam
);
631 font3d_setcolour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
633 if( k_show_own_name
){
634 render_remote_player_nametag(
635 localplayer
.final_mtx
[0][3],
636 steam_username_at_startup
);
639 for( u32 i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ ){
640 struct network_player
*player
= &netplayers
.list
[i
];
641 if( !player
->active
) continue;
642 if( player
->active_world
!= world
) continue;
644 struct player_avatar
*av
= localplayer
.playeravatar
;
645 render_remote_player_nametag(
646 netplayers
.final_mtx
[av
->sk
.bone_count
*i
][3],
653 static void remote_players_init(void){
654 vg_console_reg_var( "k_show_own_name", &k_show_own_name
,
655 k_var_dtype_i32
, 0 );
656 for( u32 i
=0; i
<NETWORK_SFX_QUEUE_LENGTH
; i
++ ){
657 netplayers
.sfx_queue
[i
].system
= k_player_subsystem_invalid
;
661 static void remote_sfx_pre_update(void){
662 for( u32 i
=0; i
<NETWORK_SFX_QUEUE_LENGTH
; i
++ ){
663 struct net_sfx
*si
= &netplayers
.sfx_queue
[i
];
665 if( si
->system
!= k_player_subsystem_invalid
){
666 si
->subframe
-= vg
.time_frame_delta
;
667 if( si
->subframe
<= 0.0f
){
669 si
->system
= k_player_subsystem_invalid
;