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_update_friendflags( struct network_player
*remote
){
62 ISteamFriends
*hSteamFriends
= SteamAPI_SteamFriends();
63 remote
->isfriend
= SteamAPI_ISteamFriends_HasFriend( hSteamFriends
,
64 remote
->steamid
, k_EFriendFlagImmediate
);
65 remote
->isblocked
= SteamAPI_ISteamFriends_HasFriend( hSteamFriends
,
66 remote
->steamid
, k_EFriendFlagBlocked
);
69 static void player_remote_rx_200_300( SteamNetworkingMessage_t
*msg
){
70 netmsg_blank
*tmp
= msg
->m_pData
;
72 if( tmp
->inetmsg_id
== k_inetmsg_playerjoin
){
73 netmsg_playerjoin
*playerjoin
= msg
->m_pData
;
74 if( !packet_minsize( msg
, sizeof(*playerjoin
) )) return;
76 if( playerjoin
->index
< vg_list_size(netplayers
.list
) ){
77 struct network_player
*player
= &netplayers
.list
[ playerjoin
->index
];
78 player_remote_clear( player
);
80 player
->steamid
= playerjoin
->steamid
;
81 player_remote_update_friendflags( player
);
83 /* TODO: interpret the uids */
84 player
->board_view_slot
= 0;
85 player
->playermodel_view_slot
= 0;
87 struct interp_buffer
*buf
= &netplayers
.interp_data
[playerjoin
->index
];
89 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
90 buf
->frames
[i
].active
= 0;
93 vg_info( "#%u joined friend: %d, blocked: %d\n",
94 playerjoin
->index
, player
->isfriend
, player
->isblocked
);
97 vg_error( "inetmsg_playerjoin: player index out of range\n" );
100 else if( tmp
->inetmsg_id
== k_inetmsg_playerleave
){
101 netmsg_playerleave
*playerleave
= msg
->m_pData
;
102 if( !packet_minsize( msg
, sizeof(*playerleave
) )) return;
104 if( playerleave
->index
< vg_list_size(netplayers
.list
) ){
105 struct network_player
*player
= &netplayers
.list
[ playerleave
->index
];
106 player_remote_clear( player
);
108 vg_info( "player leave (%d)\n", playerleave
->index
);
111 vg_error( "inetmsg_playerleave: player index out of range\n" );
114 else if( tmp
->inetmsg_id
== k_inetmsg_playerusername
){
115 netmsg_playerusername
*update
= msg
->m_pData
;
116 if( !packet_minsize( msg
, sizeof(*update
) )) return;
118 if( update
->index
< vg_list_size(netplayers
.list
) ){
119 struct network_player
*player
= &netplayers
.list
[ update
->index
];
121 network_msgstring( update
->name
, msg
->m_cbSize
, sizeof(*update
),
122 player
->username
, sizeof(player
->username
) );
124 vg_info( "#%u changed username to: %s\n",
125 update
->index
, player
->username
);
128 vg_error( "inetmsg_playerleave: player index out of range\n" );
131 else if( tmp
->inetmsg_id
== k_inetmsg_playerframe
){
132 u32 datasize
= msg
->m_cbSize
- sizeof(netmsg_playerframe
);
134 if( datasize
> sizeof(union interp_animdata
) ){
135 vg_error( "Player frame data exceeds animdata size\n" );
139 netmsg_playerframe
*frame
= msg
->m_pData
;
140 if( frame
->client
>= vg_list_size(netplayers
.list
) ){
141 vg_error( "inetmsg_playerframe: player index out of range\n" );
145 if( frame
->subsystem
>= k_player_subsystem_max
){
146 vg_error( "inetmsg_playerframe: subsystem out of range\n" );
150 struct interp_buffer
*ib
= &netplayers
.interp_data
[ frame
->client
];
151 struct interp_frame
*dest
= NULL
;
153 f64 min_time
= INFINITY
;
154 for( u32 i
=0; i
<vg_list_size(ib
->frames
); i
++ ){
155 struct interp_frame
*ifr
= &ib
->frames
[i
];
162 if( ifr
->timestamp
< min_time
){
163 min_time
= ifr
->timestamp
;
169 dest
->subsystem
= frame
->subsystem
;
170 dest
->instance_id
= frame
->instance_id
;
173 .mode
= k_bitpack_decompress
,
174 .buffer
= frame
->animdata
,
175 .buffer_len
= datasize
,
181 * -------------------------------------------------------------*/
183 for( u32 i
=0; i
<frame
->sound_effects
; i
++ ){
185 net_sfx_exchange( &ctx
, &sfx
);
187 f64 t
= (frame
->timestamp
- NETWORK_FRAMERATE
) +
188 (sfx
.subframe
*NETWORK_FRAMERATE
);
190 f32 remaining
= t
- ib
->t
;
192 if( remaining
<= 0.0f
)
193 net_sfx_play( &sfx
);
195 struct net_sfx
*dst
= NULL
;
197 for( u32 j
=0; j
<NETWORK_SFX_QUEUE_LENGTH
; j
++ ){
198 struct net_sfx
*sj
= &netplayers
.sfx_queue
[j
];
199 if( sj
->system
== k_player_subsystem_invalid
){
204 if( sj
->priority
< sfx
.priority
)
209 dst
->subframe
= remaining
;
214 * -------------------------------------------------------------*/
216 dest
->timestamp
= frame
->timestamp
;
217 dest
->boundary_hash
= frame
->boundary_hash
;
219 struct network_player
*player
= &netplayers
.list
[ frame
->client
];
220 struct player_subsystem_interface
*sys
=
221 player_subsystems
[ frame
->subsystem
];
223 if( sys
->network_animator_exchange
){
224 memset( &dest
->data
, 0, sys
->animator_size
);
225 sys
->network_animator_exchange( &ctx
, &dest
->data
);
228 bitpack_bytes( &ctx
, sys
->animator_size
, sys
->animator_data
);
231 player
->subsystem
= frame
->subsystem
;
232 player
->down_bytes
+= msg
->m_cbSize
;
234 else if( tmp
->inetmsg_id
== k_inetmsg_playeritem
){
235 netmsg_playeritem
*item
= msg
->m_pData
;
236 if( !packet_minsize( msg
, sizeof(*item
)+1 )) return;
238 if( item
->client
>= vg_list_size(netplayers
.list
) ){
239 vg_error( "inetmsg_playerframe: player index out of range\n" );
243 if( item
->type_index
>= k_netmsg_playeritem_max
){
244 vg_warn( "Client #%d invalid equip type %u\n",
245 (i32
)item
->client
, (u32
)item
->type_index
);
249 vg_info( "Client #%d equiped: [%s] %s\n",
251 (const char *[]){[k_netmsg_playeritem_board
]="board",
252 [k_netmsg_playeritem_player
]="player",
253 [k_netmsg_playeritem_world0
]="world0",
254 [k_netmsg_playeritem_world1
]="world1"
255 }[item
->type_index
], item
->uid
);
257 struct network_player
*player
= &netplayers
.list
[ item
->client
];
258 char *uid
= player
->items
[ item
->type_index
];
260 network_msgstring( item
->uid
, msg
->m_cbSize
, sizeof(*item
),
261 uid
, ADDON_UID_MAX
);
263 if( item
->type_index
== k_netmsg_playeritem_board
){
264 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
265 player
->board_view_slot
=
266 addon_cache_create_viewer_from_uid( k_addon_type_board
, uid
);
268 else if( item
->type_index
== k_netmsg_playeritem_player
){
269 addon_cache_unwatch( k_addon_type_player
,
270 player
->playermodel_view_slot
);
271 player
->playermodel_view_slot
=
272 addon_cache_create_viewer_from_uid( k_addon_type_player
, uid
);
274 else if( (item
->type_index
== k_netmsg_playeritem_world0
) ||
275 (item
->type_index
== k_netmsg_playeritem_world1
) ){
276 relink_remote_player_worlds( item
->client
);
282 * Write localplayer pose to network
284 static void remote_player_send_playerframe(void){
285 u8 sysid
= localplayer
.subsystem
;
286 if( sysid
>= k_player_subsystem_max
) return;
288 struct player_subsystem_interface
*sys
= player_subsystems
[sysid
];
290 if( sys
->animator_size
){
291 u32 max_buf_size
= sys
->animator_size
+ sizeof(localplayer
.sfx_buffer
),
292 base_size
= sizeof(struct netmsg_playerframe
),
293 max_packet
= base_size
+ max_buf_size
;
295 netmsg_playerframe
*frame
= alloca( max_packet
);
296 frame
->inetmsg_id
= k_inetmsg_playerframe
;
297 frame
->client
= 0xff;
298 frame
->subsystem
= localplayer
.subsystem
;
299 frame
->instance_id
= world_static
.active_instance
;
302 .mode
= k_bitpack_compress
,
303 .buffer
= frame
->animdata
,
304 .buffer_len
= max_buf_size
,
309 * ---------------------------------------------*/
311 frame
->sound_effects
= localplayer
.sfx_buffer_count
;
312 for( u32 i
=0; i
<localplayer
.sfx_buffer_count
; i
++ )
313 net_sfx_exchange( &ctx
, &localplayer
.sfx_buffer
[i
] );
316 * -----------------------------------------------*/
318 frame
->timestamp
= vg
.time_real
;
319 frame
->boundary_hash
= localplayer
.boundary_hash
;
320 if( sys
->network_animator_exchange
)
321 sys
->network_animator_exchange( &ctx
, sys
->animator_data
);
323 bitpack_bytes( &ctx
, sys
->animator_size
, sys
->animator_data
);
325 u32 wire_size
= base_size
+ ctx
.bytes
;
326 netplayers
.up_bytes
+= wire_size
;
328 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
329 hSteamNetworkingSockets
, network_client
.remote
,
331 k_nSteamNetworkingSend_Unreliable
, NULL
);
336 * Updates network traffic stats
338 static void remote_player_debug_update(void){
339 if( (vg
.time_real
- netplayers
.last_data_measurement
) > 1.0 ){
340 netplayers
.last_data_measurement
= vg
.time_real
;
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 total_down
+= player
->down_bytes
;
347 player
->down_kbs
= ((f32
)player
->down_bytes
)/1024.0f
;
348 player
->down_bytes
= 0;
352 netplayers
.down_kbs
= ((f32
)total_down
)/1024.0f
;
353 netplayers
.up_kbs
= ((f32
)netplayers
.up_bytes
)/1024.0f
;
354 netplayers
.up_bytes
= 0;
358 static void remote_player_nametag( m4x4f pv
, v3f co
, const char *name
){
360 vg_ui
.font
= &vg_ui_font_big
;
366 m4x4_mulv( pv
, wpos
, wpos
);
368 if( wpos
[3] > 0.0f
){
369 v2_muls( wpos
, (1.0f
/wpos
[3]) * 0.5f
, wpos
);
370 v2_add( wpos
, (v2f
){ 0.5f
, 0.5f
}, wpos
);
373 wr
[0] = vg_clampf(wpos
[0] * vg
.window_x
, -32000.0f
,32000.0f
)-150;
374 wr
[1] = vg_clampf((1.0f
-wpos
[1]) * vg
.window_y
,
378 ui_fill( wr
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
379 ui_text( wr
, name
, 1, k_ui_align_middle_center
, 0 );
381 vg_ui
.font
= &vg_ui_font_small
;
385 * Debugging information
387 static void remote_player_network_imgui( m4x4f pv
){
388 if( !network_client
.network_info
)
391 ui_rect panel
= { (vg
.window_x
/ 2) - 200, 0, 400, 600 };
392 ui_fill( panel
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
395 const char *netstatus
= "PROGRAMMING ERROR";
397 struct { enum ESteamNetworkingConnectionState state
; const char *str
; }
399 { k_ESteamNetworkingConnectionState_None
, "None" },
400 { k_ESteamNetworkingConnectionState_Connecting
,
401 (const char *[]){"Connecting -",
405 }[(u32
)(vg
.time_real
/0.25) & 0x3 ] },
406 { k_ESteamNetworkingConnectionState_FindingRoute
, "Finding Route" },
407 { k_ESteamNetworkingConnectionState_Connected
, "Connected" },
408 { k_ESteamNetworkingConnectionState_ClosedByPeer
, "Closed by peer" },
409 { k_ESteamNetworkingConnectionState_ProblemDetectedLocally
,
410 "Problem Detected Locally" },
411 { k_ESteamNetworkingConnectionState_FinWait
, "Fin Wait" },
412 { k_ESteamNetworkingConnectionState_Linger
, "Linger" },
413 { k_ESteamNetworkingConnectionState_Dead
, "Dead" }
415 for( u32 i
=0; i
<vg_list_size(states
); i
++ ){
416 if( states
[i
].state
== network_client
.state
){
417 netstatus
= states
[i
].str
;
421 snprintf( buf
, 512, "Network: %s", netstatus
);
422 ui_info( panel
, buf
);
423 ui_info( panel
, "---------------------" );
425 if( network_client
.state
== k_ESteamNetworkingConnectionState_Connected
){
426 ui_info( panel
, "#-1: localplayer" );
428 snprintf( buf
, 512, "U%.3f/D%.3fkbs",
429 netplayers
.up_kbs
, netplayers
.down_kbs
);
430 ui_info( panel
, buf
);
432 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
433 struct network_player
*player
= &netplayers
.list
[i
];
434 if( player
->active
){
435 const char *sysname
= "invalid";
437 if( player
->subsystem
< k_player_subsystem_max
){
438 sysname
= player_subsystems
[ player
->subsystem
]->name
;
440 snprintf( buf
, 512, "#%u: %s [%s] D%.1fkbs",
441 i
, player
->username
, sysname
, player
->down_kbs
);
442 ui_info( panel
, buf
);
444 struct player_avatar
*av
= localplayer
.playeravatar
;
445 remote_player_nametag(
447 netplayers
.final_mtx
[av
->sk
.bone_count
*i
][3],
453 ui_info( panel
, "offline" );
456 struct player_avatar
*av
= localplayer
.playeravatar
;
457 remote_player_nametag(
459 localplayer
.final_mtx
[0][3],
464 * write the remote players final_mtx
466 static void pose_remote_player( u32 index
,
467 struct interp_frame
*f0
,
468 struct interp_frame
*f1
){
470 struct network_player
*player
= &netplayers
.list
[ index
];
472 struct interp_buffer
*buf
= &netplayers
.interp_data
[ index
];
473 struct player_avatar
*av
= localplayer
.playeravatar
;
474 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*index
];
475 struct player_board_pose
*board_pose
= &netplayers
.board_poses
[index
];
477 struct player_subsystem_interface
*sys0
= player_subsystems
[f0
->subsystem
],
480 player_pose pose0
, pose1
, posed
;
481 sys0
->pose( &f0
->data
, &pose0
);
486 f32 t
= (buf
->t
- f0
->timestamp
) / (f1
->timestamp
- f0
->timestamp
);
487 t
= vg_clampf( t
, 0.0f
, 1.0f
);
489 sys1
= player_subsystems
[f1
->subsystem
];
490 sys1
->pose( &f1
->data
, &pose1
);
492 u16 bounds
= f0
->boundary_hash
^f1
->boundary_hash
;
494 if( bounds
& NETMSG_BOUNDARY_BIT
)
497 if( bounds
& NETMSG_GATE_BOUNDARY_BIT
){
498 /* TODO: Extra work retransforming the root_co, instance_id.. etc */
502 instance_id
= f1
->instance_id
;
504 lerp_player_pose( &pose0
, &pose1
, t
, &posed
);
505 apply_full_skeleton_pose( &av
->sk
, &posed
, final_mtx
);
506 memcpy( board_pose
, &posed
.board
, sizeof(*board_pose
) );
509 instance_id
= f0
->instance_id
;
511 apply_full_skeleton_pose( &av
->sk
, &pose0
, final_mtx
);
512 memcpy( board_pose
, &pose0
.board
, sizeof(*board_pose
) );
515 if( player
->world_match
[ instance_id
] )
516 player
->active_world
= &world_static
.instances
[ instance_id
];
520 * animate remote player and store in final_mtx
522 static void animate_remote_player( u32 index
){
525 * Trys to keep the cursor inside the buffer
527 f64 min_time
= -999999999.9,
528 max_time
= 999999999.9,
529 abs_max_time
= -999999999.9;
531 struct interp_frame
*minframe
= NULL
,
533 *abs_max_frame
= NULL
;
535 struct interp_buffer
*buf
= &netplayers
.interp_data
[index
];
536 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
537 struct interp_frame
*ifr
= &buf
->frames
[i
];
540 if( (ifr
->timestamp
> min_time
) && (ifr
->timestamp
< buf
->t
) ){
541 min_time
= ifr
->timestamp
;
545 if( (ifr
->timestamp
< max_time
) && (ifr
->timestamp
> buf
->t
) ){
546 max_time
= ifr
->timestamp
;
550 if( ifr
->timestamp
> abs_max_time
){
551 abs_max_time
= ifr
->timestamp
;
557 struct network_player
*player
= &netplayers
.list
[ index
];
558 player
->active_world
= NULL
;
560 if( minframe
&& maxframe
){
561 pose_remote_player( index
, minframe
, maxframe
);
562 buf
->t
+= vg
.time_frame_delta
;
565 buf
->t
= abs_max_time
- 0.25;
568 pose_remote_player( index
, abs_max_frame
, NULL
);
575 * Update full final_mtx for all remote players
577 static void animate_remote_players(void){
578 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
579 struct network_player
*player
= &netplayers
.list
[i
];
580 if( !player
->active
) continue;
582 animate_remote_player( i
);
587 * Draw remote players
589 static void render_remote_players( world_instance
*world
, camera
*cam
){
590 SDL_AtomicLock( &addon_system
.sl_cache_using_resources
);
592 for( u32 i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ ){
593 struct network_player
*player
= &netplayers
.list
[i
];
594 if( !player
->active
|| player
->isblocked
) continue;
595 if( player
->active_world
!= world
) continue;
597 struct player_avatar
*av
= localplayer
.playeravatar
;
598 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*i
];
600 struct player_model
*model
=
601 addon_cache_item_if_loaded( k_addon_type_player
,
602 player
->playermodel_view_slot
);
604 if( !model
) model
= &localplayer
.fallback_model
;
605 render_playermodel( cam
, world
, 0, model
, &av
->sk
, final_mtx
);
607 struct player_board
*board
=
608 addon_cache_item_if_loaded( k_addon_type_board
,
609 player
->board_view_slot
);
610 render_board( cam
, world
, board
,
611 final_mtx
[localplayer
.playeravatar
->id_board
],
612 &netplayers
.board_poses
[ i
],
613 k_board_shader_player
);
616 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
619 static void render_remote_player_nametag( v3f co
, const char *name
){
621 m4x3_identity( mlocal
);
622 mlocal
[3][0] -= font3d_string_width( 2, name
) * 0.5f
;
625 m3x3_identity( mmdl
);
626 for( int i
=0; i
<3; i
++ )
627 v3_muls( skaterift
.cam
.mtx
.v
[i
], 0.2f
, mmdl
[i
] );
628 m3x3_transpose( mmdl
, mmdl
);
629 v3_add( co
, (v3f
){0.0f
,2.0f
,0.0f
}, mmdl
[3] );
631 m4x3_mul( mmdl
, mlocal
, mmdl
);
632 font3d_simple_draw( 2, name
, &skaterift
.cam
, mmdl
);
635 static void render_remote_players_tags( world_instance
*world
, camera
*cam
){
637 glEnable(GL_DEPTH_TEST
);
638 glBlendFunc( GL_SRC_ALPHA
, GL_ONE_MINUS_SRC_ALPHA
);
639 glBlendEquation(GL_FUNC_ADD
);
641 font3d_bind( &gui
.font
, k_font_shader_default
, 1, NULL
, &skaterift
.cam
);
642 font3d_setcolour( (v4f
){1.0f
,1.0f
,1.0f
,1.0f
} );
644 if( k_show_own_name
){
645 render_remote_player_nametag(
646 localplayer
.final_mtx
[0][3],
647 steam_username_at_startup
);
650 for( u32 i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ ){
651 struct network_player
*player
= &netplayers
.list
[i
];
652 if( !player
->active
|| player
->isblocked
) continue;
653 if( player
->active_world
!= world
) continue;
655 struct player_avatar
*av
= localplayer
.playeravatar
;
656 render_remote_player_nametag(
657 netplayers
.final_mtx
[av
->sk
.bone_count
*i
][3],
664 static void remote_players_init(void){
665 vg_console_reg_var( "k_show_own_name", &k_show_own_name
,
666 k_var_dtype_i32
, 0 );
667 for( u32 i
=0; i
<NETWORK_SFX_QUEUE_LENGTH
; i
++ ){
668 netplayers
.sfx_queue
[i
].system
= k_player_subsystem_invalid
;
672 static void remote_sfx_pre_update(void){
673 for( u32 i
=0; i
<NETWORK_SFX_QUEUE_LENGTH
; i
++ ){
674 struct net_sfx
*si
= &netplayers
.sfx_queue
[i
];
676 if( si
->system
!= k_player_subsystem_invalid
){
677 si
->subframe
-= vg
.time_frame_delta
;
678 if( si
->subframe
<= 0.0f
){
680 si
->system
= k_player_subsystem_invalid
;