1 #include "player_remote.h"
3 #include "player_render.h"
4 #include "player_api.h"
5 #include "network_common.h"
9 #include "ent_miniworld.h"
10 #include "ent_region.h"
11 #include "shaders/model_entity.h"
12 #include "vg/vg_steam_friends.h"
14 struct global_netplayers netplayers
;
16 static i32 k_show_own_name
= 0;
18 static void player_remote_clear( struct network_player
*player
)
20 addon_cache_unwatch( k_addon_type_player
, player
->playermodel_view_slot
);
21 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
23 memset( player
, 0, sizeof(*player
) );
24 strcpy( player
->username
, "unknown" );
25 player
->subsystem
= k_player_subsystem_invalid
;
29 * re-attatches addon_reg pointers on the remote client if we have the same
32 static void relink_remote_player_worlds( u32 client_id
){
33 struct network_player
*player
= &netplayers
.list
[client_id
];
36 addon_uid_to_alias( player
->items
[k_netmsg_playeritem_world0
], &q
[0] );
37 addon_uid_to_alias( player
->items
[k_netmsg_playeritem_world1
], &q
[1] );
40 * currently in 10.23, the hub world will always be the same.
41 * this might but probably wont change in the future
43 for( u32 i
=0; i
<k_world_max
; i
++ ){
44 addon_reg
*reg
= world_static
.instance_addons
[ i
];
46 player
->world_match
[i
] = 0;
49 if( addon_alias_eq( &q
[i
], &world_static
.instance_addons
[i
]->alias
) )
50 player
->world_match
[i
] = 1;
56 * re-attatches addon_reg pointers on the remote client if we have the mod
59 * Run if local worlds change
61 void relink_all_remote_player_worlds(void)
63 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
64 struct network_player
*player
= &netplayers
.list
[i
];
66 relink_remote_player_worlds(i
);
70 void player_remote_update_friendflags( struct network_player
*remote
)
72 ISteamFriends
*hSteamFriends
= SteamAPI_SteamFriends();
73 remote
->isfriend
= SteamAPI_ISteamFriends_HasFriend( hSteamFriends
,
74 remote
->steamid
, k_EFriendFlagImmediate
);
75 remote
->isblocked
= SteamAPI_ISteamFriends_HasFriend( hSteamFriends
,
76 remote
->steamid
, k_EFriendFlagBlocked
);
79 void player_remote_rx_200_300( SteamNetworkingMessage_t
*msg
)
81 netmsg_blank
*tmp
= msg
->m_pData
;
83 if( tmp
->inetmsg_id
== k_inetmsg_playerjoin
){
84 netmsg_playerjoin
*playerjoin
= msg
->m_pData
;
85 if( !packet_minsize( msg
, sizeof(*playerjoin
) )) return;
87 if( playerjoin
->index
< vg_list_size(netplayers
.list
) ){
88 struct network_player
*player
= &netplayers
.list
[ playerjoin
->index
];
89 player_remote_clear( player
);
91 player
->steamid
= playerjoin
->steamid
;
92 player_remote_update_friendflags( player
);
94 /* TODO: interpret the uids */
95 player
->board_view_slot
= 0;
96 player
->playermodel_view_slot
= 0;
98 struct interp_buffer
*buf
= &netplayers
.interp_data
[playerjoin
->index
];
100 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
101 buf
->frames
[i
].active
= 0;
104 vg_info( "#%u joined friend: %d, blocked: %d\n",
105 playerjoin
->index
, player
->isfriend
, player
->isblocked
);
108 vg_error( "inetmsg_playerjoin: player index out of range\n" );
111 else if( tmp
->inetmsg_id
== k_inetmsg_playerleave
){
112 netmsg_playerleave
*playerleave
= msg
->m_pData
;
113 if( !packet_minsize( msg
, sizeof(*playerleave
) )) return;
115 if( playerleave
->index
< vg_list_size(netplayers
.list
) ){
116 struct network_player
*player
= &netplayers
.list
[ playerleave
->index
];
117 player_remote_clear( player
);
119 vg_info( "player leave (%d)\n", playerleave
->index
);
122 vg_error( "inetmsg_playerleave: player index out of range\n" );
125 else if( tmp
->inetmsg_id
== k_inetmsg_playerusername
){
126 netmsg_playerusername
*update
= msg
->m_pData
;
127 if( !packet_minsize( msg
, sizeof(*update
) )) return;
129 if( update
->index
< vg_list_size(netplayers
.list
) ){
130 struct network_player
*player
= &netplayers
.list
[ update
->index
];
132 network_msgstring( update
->name
, msg
->m_cbSize
, sizeof(*update
),
133 player
->username
, sizeof(player
->username
) );
135 vg_info( "#%u changed username to: %s\n",
136 update
->index
, player
->username
);
139 vg_error( "inetmsg_playerleave: player index out of range\n" );
142 else if( tmp
->inetmsg_id
== k_inetmsg_playerframe
){
143 u32 datasize
= msg
->m_cbSize
- sizeof(netmsg_playerframe
);
145 if( datasize
> sizeof(union interp_animdata
) ){
146 vg_error( "Player frame data exceeds animdata size\n" );
150 netmsg_playerframe
*frame
= msg
->m_pData
;
152 if( frame
->client
>= vg_list_size(netplayers
.list
) ){
153 vg_error( "inetmsg_playerframe: player index out of range\n" );
157 if( frame
->subsystem
>= k_player_subsystem_max
){
158 vg_error( "inetmsg_playerframe: subsystem out of range\n" );
162 struct interp_buffer
*ib
= &netplayers
.interp_data
[ frame
->client
];
163 struct interp_frame
*dest
= NULL
;
165 f64 min_time
= INFINITY
;
166 for( u32 i
=0; i
<vg_list_size(ib
->frames
); i
++ ){
167 struct interp_frame
*ifr
= &ib
->frames
[i
];
174 if( ifr
->timestamp
< min_time
){
175 min_time
= ifr
->timestamp
;
181 dest
->subsystem
= frame
->subsystem
;
182 dest
->flags
= frame
->flags
;
185 .mode
= k_bitpack_decompress
,
186 .buffer
= frame
->animdata
,
187 .buffer_len
= datasize
,
192 * -------------------------------------------------------------*/
194 dest
->timestamp
= frame
->timestamp
;
195 dest
->boundary_hash
= frame
->boundary_hash
;
197 struct network_player
*player
= &netplayers
.list
[ frame
->client
];
198 struct player_subsystem_interface
*sys
=
199 player_subsystems
[ frame
->subsystem
];
201 memset( &dest
->data
, 0, sys
->animator_size
);
202 if( sys
->network_animator_exchange
)
203 sys
->network_animator_exchange( &ctx
, &dest
->data
);
205 bitpack_bytes( &ctx
, sys
->animator_size
, sys
->animator_data
);
208 * -------------------------------------------------------------*/
210 for( u32 i
=0; i
<frame
->sound_effects
; i
++ ){
212 net_sfx_exchange( &ctx
, &sfx
);
214 f64 t
= (frame
->timestamp
- NETWORK_FRAMERATE
) +
215 (sfx
.subframe
*NETWORK_FRAMERATE
);
217 f32 remaining
= t
- ib
->t
;
219 if( remaining
<= 0.0f
)
220 net_sfx_play( &sfx
);
222 struct net_sfx
*dst
= NULL
;
224 for( u32 j
=0; j
<NETWORK_SFX_QUEUE_LENGTH
; j
++ ){
225 struct net_sfx
*sj
= &netplayers
.sfx_queue
[j
];
226 if( sj
->system
== k_player_subsystem_invalid
){
231 if( sj
->priority
< sfx
.priority
)
236 dst
->subframe
= remaining
;
241 * -------------------------------------------------------------*/
243 memset( &dest
->data_glider
, 0, sizeof(struct remote_glider_animator
) );
244 if( dest
->flags
& (NETMSG_PLAYERFRAME_HAVE_GLIDER
|
245 NETMSG_PLAYERFRAME_GLIDER_ORPHAN
) ){
246 player_glide_remote_animator_exchange( &ctx
, &dest
->data_glider
);
249 player
->subsystem
= frame
->subsystem
;
250 player
->down_bytes
+= msg
->m_cbSize
;
252 else if( tmp
->inetmsg_id
== k_inetmsg_playeritem
){
253 netmsg_playeritem
*item
= msg
->m_pData
;
254 if( !packet_minsize( msg
, sizeof(*item
)+1 )) return;
256 if( item
->client
>= vg_list_size(netplayers
.list
) ){
257 vg_error( "inetmsg_playerframe: player index out of range\n" );
261 if( item
->type_index
>= k_netmsg_playeritem_max
){
262 vg_warn( "Client #%d invalid equip type %u\n",
263 (i32
)item
->client
, (u32
)item
->type_index
);
267 vg_info( "Client #%d equiped: [%s] %s\n",
269 (const char *[]){[k_netmsg_playeritem_board
]="board",
270 [k_netmsg_playeritem_player
]="player",
271 [k_netmsg_playeritem_world0
]="world0",
272 [k_netmsg_playeritem_world1
]="world1"
273 }[item
->type_index
], item
->uid
);
275 struct network_player
*player
= &netplayers
.list
[ item
->client
];
276 char *uid
= player
->items
[ item
->type_index
];
278 network_msgstring( item
->uid
, msg
->m_cbSize
, sizeof(*item
),
279 uid
, ADDON_UID_MAX
);
281 if( item
->type_index
== k_netmsg_playeritem_board
){
282 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
283 player
->board_view_slot
=
284 addon_cache_create_viewer_from_uid( k_addon_type_board
, uid
);
286 else if( item
->type_index
== k_netmsg_playeritem_player
){
287 addon_cache_unwatch( k_addon_type_player
,
288 player
->playermodel_view_slot
);
289 player
->playermodel_view_slot
=
290 addon_cache_create_viewer_from_uid( k_addon_type_player
, uid
);
292 else if( (item
->type_index
== k_netmsg_playeritem_world0
) ||
293 (item
->type_index
== k_netmsg_playeritem_world1
) ){
294 relink_remote_player_worlds( item
->client
);
297 else if( tmp
->inetmsg_id
== k_inetmsg_chat
){
298 netmsg_chat
*chat
= msg
->m_pData
;
300 struct network_player
*player
= &netplayers
.list
[ chat
->client
];
301 network_msgstring( chat
->msg
, msg
->m_cbSize
, sizeof(netmsg_chat
),
302 player
->chat
, NETWORK_MAX_CHAT
);
303 player
->chat_time
= vg
.time_real
;
304 vg_info( "[%d]: %s\n", chat
->client
, player
->chat
);
306 else if( tmp
->inetmsg_id
== k_inetmsg_region
){
307 netmsg_region
*region
= msg
->m_pData
;
308 struct network_player
*player
= &netplayers
.list
[ region
->client
];
310 u32 l
= network_msgstring(
311 region
->loc
, msg
->m_cbSize
, sizeof(netmsg_region
),
312 player
->region
, NETWORK_REGION_MAX
);
313 player
->region_flags
= region
->flags
;
316 player
->region_flags
|= k_ent_region_flag_hasname
;
318 player
->effect_data
.spark
.colour
= region_spark_colour(region
->flags
);
323 * Write localplayer pose to network
325 void remote_player_send_playerframe(void)
327 u8 sysid
= localplayer
.subsystem
;
328 if( sysid
>= k_player_subsystem_max
) return;
330 struct player_subsystem_interface
*sys
= player_subsystems
[sysid
];
332 if( sys
->animator_size
){
333 u32 max_buf_size
= sys
->animator_size
+ sizeof(localplayer
.sfx_buffer
),
334 base_size
= sizeof(struct netmsg_playerframe
),
335 max_packet
= base_size
+ max_buf_size
;
337 netmsg_playerframe
*frame
= alloca( max_packet
);
338 frame
->inetmsg_id
= k_inetmsg_playerframe
;
339 frame
->client
= 0xff;
340 frame
->subsystem
= localplayer
.subsystem
;
341 frame
->flags
= world_static
.active_instance
;
344 .mode
= k_bitpack_compress
,
345 .buffer
= frame
->animdata
,
346 .buffer_len
= max_buf_size
,
351 * -----------------------------------------------*/
353 frame
->timestamp
= vg
.time_real
;
354 frame
->boundary_hash
= localplayer
.boundary_hash
;
355 if( sys
->network_animator_exchange
)
356 sys
->network_animator_exchange( &ctx
, sys
->animator_data
);
358 bitpack_bytes( &ctx
, sys
->animator_size
, sys
->animator_data
);
361 * ---------------------------------------------*/
363 frame
->sound_effects
= localplayer
.sfx_buffer_count
;
364 for( u32 i
=0; i
<localplayer
.sfx_buffer_count
; i
++ )
365 net_sfx_exchange( &ctx
, &localplayer
.sfx_buffer
[i
] );
368 * -------------------------------------------------------------*/
370 if( localplayer
.have_glider
||
371 (localplayer
.subsystem
== k_player_subsystem_glide
) ) {
372 frame
->flags
|= NETMSG_PLAYERFRAME_HAVE_GLIDER
;
375 if( localplayer
.glider_orphan
)
376 frame
->flags
|= NETMSG_PLAYERFRAME_GLIDER_ORPHAN
;
378 if( frame
->flags
& (NETMSG_PLAYERFRAME_HAVE_GLIDER
|
379 NETMSG_PLAYERFRAME_GLIDER_ORPHAN
) ){
380 player_glide_remote_animator_exchange( &ctx
,
381 &player_glide
.remote_animator
);
386 u32 wire_size
= base_size
+ ctx
.bytes
;
387 netplayers
.up_bytes
+= wire_size
;
389 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
390 hSteamNetworkingSockets
, network_client
.remote
,
392 k_nSteamNetworkingSend_Unreliable
, NULL
);
397 * Updates network traffic stats
399 void remote_player_debug_update(void)
401 if( (vg
.time_real
- netplayers
.last_data_measurement
) > 1.0 ){
402 netplayers
.last_data_measurement
= vg
.time_real
;
405 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
406 struct network_player
*player
= &netplayers
.list
[i
];
407 if( player
->active
){
408 total_down
+= player
->down_bytes
;
409 player
->down_kbs
= ((f32
)player
->down_bytes
)/1024.0f
;
410 player
->down_bytes
= 0;
414 netplayers
.down_kbs
= ((f32
)total_down
)/1024.0f
;
415 netplayers
.up_kbs
= ((f32
)netplayers
.up_bytes
)/1024.0f
;
416 netplayers
.up_bytes
= 0;
421 * Debugging information
423 void remote_player_network_imgui( ui_context
*ctx
, m4x4f pv
)
425 if( network_client
.user_intent
== k_server_intent_online
)
428 (network_client
.state
== k_ESteamNetworkingConnectionState_Connected
)))
432 vg_strnull( &str
, buf
, sizeof(buf
) );
434 network_status_string( &str
, &fg
);
435 ui_text( ctx
, (ui_rect
){ vg
.window_x
- 200, 0, 200, 48 }, buf
, 1,
436 k_ui_align_middle_center
, fg
);
440 if( !network_client
.network_info
)
443 ui_rect panel
= { (vg
.window_x
/ 2) - 200, 0, 400, 600 };
444 ui_fill( ctx
, panel
, (ui_colour(ctx
, k_ui_bg
)&0x00ffffff)|0x50000000 );
446 ctx
->font
= &vgf_default_title
;
447 ui_info( ctx
, panel
, "Network" );
448 ctx
->font
= &vgf_default_large
;
449 ui_info( ctx
, panel
, "Status" );
450 ctx
->font
= &vgf_default_small
;
453 const char *netstatus
= "PROGRAMMING ERROR";
455 struct { enum ESteamNetworkingConnectionState state
; const char *str
; }
457 { k_ESteamNetworkingConnectionState_None
, "None" },
458 { k_ESteamNetworkingConnectionState_Connecting
,
459 (const char *[]){"Connecting -",
463 }[(u32
)(vg
.time_real
/0.25) & 0x3 ] },
464 { k_ESteamNetworkingConnectionState_FindingRoute
, "Finding Route" },
465 { k_ESteamNetworkingConnectionState_Connected
, "Connected" },
466 { k_ESteamNetworkingConnectionState_ClosedByPeer
, "Closed by peer" },
467 { k_ESteamNetworkingConnectionState_ProblemDetectedLocally
,
468 "Problem Detected Locally" },
469 { k_ESteamNetworkingConnectionState_FinWait
, "Fin Wait" },
470 { k_ESteamNetworkingConnectionState_Linger
, "Linger" },
471 { k_ESteamNetworkingConnectionState_Dead
, "Dead" }
473 for( u32 i
=0; i
<vg_list_size(states
); i
++ )
475 if( states
[i
].state
== network_client
.state
)
477 netstatus
= states
[i
].str
;
481 snprintf( buf
, 512, "Network: %s", netstatus
);
482 ui_info( ctx
, panel
, buf
);
483 ui_info( ctx
, panel
, "---------------------" );
485 if( network_client
.state
== k_ESteamNetworkingConnectionState_Connected
)
487 ui_info( ctx
, panel
, "#-1: localplayer" );
489 snprintf( buf
, 512, "U%.3f/D%.3fkbs",
490 netplayers
.up_kbs
, netplayers
.down_kbs
);
491 ui_info( ctx
, panel
, buf
);
493 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ )
495 struct network_player
*player
= &netplayers
.list
[i
];
498 const char *sysname
= "invalid";
500 if( player
->subsystem
< k_player_subsystem_max
)
502 sysname
= player_subsystems
[ player
->subsystem
]->name
;
504 snprintf( buf
, 512, "#%u: %s [%s] D%.1fkbs",
505 i
, player
->username
, sysname
, player
->down_kbs
);
506 ui_info( ctx
, panel
, buf
);
512 ui_info( ctx
, panel
, "offline" );
516 static void remote_player_effect( struct network_player
*player
,
517 player_pose
*final_pose
){
522 * write the remote players final_mtx
524 static void pose_remote_player( u32 index
,
525 struct interp_frame
*f0
,
526 struct interp_frame
*f1
){
528 struct network_player
*player
= &netplayers
.list
[ index
];
530 struct interp_buffer
*buf
= &netplayers
.interp_data
[ index
];
531 struct skeleton
*sk
= &localplayer
.skeleton
;
532 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ sk
->bone_count
*index
];
533 struct player_board_pose
*board_pose
= &netplayers
.board_poses
[index
];
535 struct player_subsystem_interface
*sys0
= player_subsystems
[f0
->subsystem
],
538 struct player_board
*board
=
539 addon_cache_item_if_loaded( k_addon_type_board
, player
->board_view_slot
);
541 player_pose pose0
, pose1
, posed
;
542 sys0
->pose( &f0
->data
, &pose0
);
549 t
= (buf
->t
- f0
->timestamp
) / (f1
->timestamp
- f0
->timestamp
);
550 t
= vg_clampf( t
, 0.0f
, 1.0f
);
552 sys1
= player_subsystems
[f1
->subsystem
];
553 sys1
->pose( &f1
->data
, &pose1
);
555 u16 bounds
= f0
->boundary_hash
^f1
->boundary_hash
;
557 if( bounds
& NETMSG_BOUNDARY_BIT
)
560 if( bounds
& NETMSG_GATE_BOUNDARY_BIT
){
561 /* TODO: Extra work retransforming the root_co, instance_id.. etc */
565 instance_id
= f1
->flags
& NETMSG_PLAYERFRAME_INSTANCE_ID
;
566 lerp_player_pose( &pose0
, &pose1
, t
, &posed
);
567 effect_blink_apply( &player
->effect_data
.blink
, &posed
, vg
.time_delta
);
569 apply_full_skeleton_pose( sk
, &posed
, final_mtx
);
573 sys0
->effects( &f0
->data
, final_mtx
, board
, &player
->effect_data
);
577 sys1
->effects( &f1
->data
, final_mtx
, board
, &player
->effect_data
);
580 memcpy( board_pose
, &posed
.board
, sizeof(*board_pose
) );
583 instance_id
= f0
->flags
& NETMSG_PLAYERFRAME_INSTANCE_ID
;
584 effect_blink_apply( &player
->effect_data
.blink
, &pose0
, vg
.time_delta
);
585 apply_full_skeleton_pose( sk
, &pose0
, final_mtx
);
587 sys0
->effects( &f0
->data
, final_mtx
, board
, &player
->effect_data
);
588 memcpy( board_pose
, &pose0
.board
, sizeof(*board_pose
) );
591 if( f0
->flags
& (NETMSG_PLAYERFRAME_HAVE_GLIDER
|
592 NETMSG_PLAYERFRAME_GLIDER_ORPHAN
) ){
593 player
->render_glider
= 1;
600 v3_lerp( f0
->data_glider
.root_co
, f1
->data_glider
.root_co
, t
, co
);
601 q_nlerp( f0
->data_glider
.root_q
, f1
->data_glider
.root_q
, t
, q
);
602 s
= vg_lerpf( f0
->data_glider
.s
, f1
->data_glider
.s
, t
);
605 v3_copy( f0
->data_glider
.root_co
, co
);
606 v4_copy( f0
->data_glider
.root_q
, q
);
607 s
= f0
->data_glider
.s
;
610 v3f
*mtx
= netplayers
.glider_mtx
[ index
];
612 m3x3_scalef( mtx
, s
);
613 v3_copy( co
, mtx
[3] );
616 player
->render_glider
= 0;
618 if( player
->world_match
[ instance_id
] )
619 player
->active_world
= &world_static
.instances
[ instance_id
];
623 * animate remote player and store in final_mtx
625 void animate_remote_player( u32 index
)
628 * Trys to keep the cursor inside the buffer
630 f64 min_time
= -999999999.9,
631 max_time
= 999999999.9,
632 abs_max_time
= -999999999.9;
634 struct interp_frame
*minframe
= NULL
,
636 *abs_max_frame
= NULL
;
638 struct interp_buffer
*buf
= &netplayers
.interp_data
[index
];
639 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
640 struct interp_frame
*ifr
= &buf
->frames
[i
];
643 if( (ifr
->timestamp
> min_time
) && (ifr
->timestamp
< buf
->t
) ){
644 min_time
= ifr
->timestamp
;
648 if( (ifr
->timestamp
< max_time
) && (ifr
->timestamp
> buf
->t
) ){
649 max_time
= ifr
->timestamp
;
653 if( ifr
->timestamp
> abs_max_time
){
654 abs_max_time
= ifr
->timestamp
;
660 struct network_player
*player
= &netplayers
.list
[ index
];
661 if( player
->active
!= 2 )
662 player
->active_world
= NULL
;
664 if( minframe
&& maxframe
){
665 pose_remote_player( index
, minframe
, maxframe
);
666 buf
->t
+= vg
.time_frame_delta
;
669 buf
->t
= abs_max_time
- 0.25;
672 pose_remote_player( index
, abs_max_frame
, NULL
);
679 * Update full final_mtx for all remote players
681 void animate_remote_players(void)
683 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
684 struct network_player
*player
= &netplayers
.list
[i
];
685 if( !player
->active
) continue;
687 animate_remote_player( i
);
692 * Draw remote players
694 void render_remote_players( world_instance
*world
, vg_camera
*cam
)
696 u32 draw_list
[ NETWORK_MAX_PLAYERS
],
700 for( u32 i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ ){
701 struct network_player
*player
= &netplayers
.list
[i
];
702 if( !player
->active
|| player
->isblocked
) continue;
703 if( player
->active_world
!= world
) continue;
706 if( !player
->isfriend
&&
707 (world
-world_static
.instances
== k_world_purpose_hub
)) continue;
710 draw_list
[draw_list_count
++] = i
;
712 if( player
->render_glider
)
716 struct skeleton
*sk
= &localplayer
.skeleton
;
718 SDL_AtomicLock( &addon_system
.sl_cache_using_resources
);
720 for( u32 j
=0; j
<draw_list_count
; j
++ ){
721 u32 index
= draw_list
[j
];
723 struct network_player
*player
= &netplayers
.list
[index
];
724 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ sk
->bone_count
*index
];
726 struct player_model
*model
=
727 addon_cache_item_if_loaded( k_addon_type_player
,
728 player
->playermodel_view_slot
);
730 if( !model
) model
= &localplayer
.fallback_model
;
731 render_playermodel( cam
, world
, 0, model
, sk
, final_mtx
);
733 struct player_board
*board
=
734 addon_cache_item_if_loaded( k_addon_type_board
,
735 player
->board_view_slot
);
736 render_board( cam
, world
, board
, final_mtx
[localplayer
.id_board
],
737 &netplayers
.board_poses
[ index
], k_board_shader_player
);
740 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
745 /* TODO: we really, really only need to do all this once. at some point
746 * PLEASE figure out a good place to do this once per frame!
749 shader_model_entity_use();
750 shader_model_entity_uTexMain( 0 );
751 shader_model_entity_uCamera( cam
->transform
[3] );
752 shader_model_entity_uPv( cam
->mtx
.pv
);
754 WORLD_BIND_LIGHT_BUFFERS_UB0_TEX234( world
, model_entity
);
756 for( u32 j
=0; j
<draw_list_count
; j
++ ){
757 u32 index
= draw_list
[j
];
759 struct network_player
*player
= &netplayers
.list
[index
];
760 if( !player
->render_glider
) continue;
762 if( player
->render_glider
){
763 v3f
*glider_mtx
= netplayers
.glider_mtx
[ index
];
764 render_glider_model( cam
, world
, glider_mtx
, k_board_shader_entity
);
769 static int remote_players_randomize( int argc
, const char *argv
[] ){
770 for( int i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ ){
771 struct network_player
*player
= &netplayers
.list
[i
];
773 player
->active
= (vg_randu32(&vg
.rand
) & 0x1)? 2: 0;
774 player
->isfriend
= vg_randu32(&vg
.rand
) & vg_randu32(&vg
.rand
) & 0x1;
775 player
->isblocked
= vg_randu32(&vg
.rand
) &
776 vg_randu32(&vg
.rand
) &
777 vg_randu32(&vg
.rand
) & 0x1;
778 player
->world_match
[ 0 ] = vg_randu32(&vg
.rand
) & 0x1;
779 player
->world_match
[ 1 ] = 0;
781 if( player
->world_match
[0] )
782 player
->active_world
= &world_static
.instances
[0];
784 player
->active_world
= NULL
;
786 for( int i
=0; i
<sizeof(player
->username
)-1; i
++ ){
787 player
->username
[i
] = 'a' + (vg_randu32(&vg
.rand
) % 30);
788 player
->username
[i
+1] = '\0';
790 if( (vg_randu32(&vg
.rand
) % 8) == 3 )
794 for( int i
=0; i
<3; i
++ ){
795 player
->medals
[i
] = vg_randu32(&vg
.rand
) % 3;
800 vg_rand_sphere( &vg
.rand
, pos
);
801 v3_muladds( localplayer
.rb
.co
, pos
, 100.0f
,
802 netplayers
.final_mtx
[ i
*localplayer
.skeleton
.bone_count
][3] );
808 void remote_players_init(void)
810 vg_console_reg_cmd( "add_test_players", remote_players_randomize
, NULL
);
811 vg_console_reg_var( "k_show_own_name", &k_show_own_name
,
812 k_var_dtype_i32
, 0 );
813 for( u32 i
=0; i
<NETWORK_SFX_QUEUE_LENGTH
; i
++ ){
814 netplayers
.sfx_queue
[i
].system
= k_player_subsystem_invalid
;
818 void remote_sfx_pre_update(void)
820 for( u32 i
=0; i
<NETWORK_SFX_QUEUE_LENGTH
; i
++ ){
821 struct net_sfx
*si
= &netplayers
.sfx_queue
[i
];
823 if( si
->system
!= k_player_subsystem_invalid
){
824 si
->subframe
-= vg
.time_frame_delta
;
825 if( si
->subframe
<= 0.0f
){
827 si
->system
= k_player_subsystem_invalid
;
834 * animator->root_co of remote player
836 static void remote_player_position( int id
, v3f out_co
){
837 struct skeleton
*sk
= &localplayer
.skeleton
;
838 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ sk
->bone_count
*id
];
839 v3_copy( final_mtx
[0][3], out_co
);
842 enum remote_player_gui_type
{
843 k_remote_player_gui_type_stranger
,
844 k_remote_player_gui_type_friend
,
845 k_remote_player_gui_type_you
,
849 * Given players' root_co, get the screen point where we should draw tag info.
851 static int player_tag_position( m4x4f pv
, v3f root_co
, ui_point out_point
){
853 v3_copy( root_co
, wpos
);
857 m4x4_mulv( pv
, wpos
, wpos
);
859 if( wpos
[3] > 0.0f
){
860 v2_muls( wpos
, (1.0f
/wpos
[3]) * 0.5f
, wpos
);
861 v2_add( wpos
, (v2f
){ 0.5f
, 0.5f
}, wpos
);
863 float k_max
= 32000.0f
;
865 out_point
[0] = vg_clampf(wpos
[0] * vg
.window_x
, -k_max
, k_max
);
866 out_point
[1] = vg_clampf((1.0f
-wpos
[1]) * vg
.window_y
, -k_max
, k_max
);
874 * Draw chat box relative to the root tag position on the screen
876 static void chat_box( ui_context
*ctx
,
877 ui_point tag_root
, f64 time
, const char *message
)
879 if( (vg
.time_real
- time
) > 15.0 )
883 wr
[2] = ui_text_line_width( ctx
, message
) + 8;
884 wr
[3] = ctx
->font
->ch
+ 2;
885 wr
[0] = tag_root
[0]-(wr
[2]/2);
886 wr
[1] = tag_root
[1] - wr
[3] - 8;
888 ui_fill( ctx
, wr
, ui_opacity( ui_colour(ctx
, k_ui_bg
), 0.23f
) );
889 ui_text( ctx
, wr
, message
, 1, k_ui_align_middle_center
, 0 );
893 * Draw full imgui for remote player
895 static void remote_player_nametag( ui_context
*ctx
, ui_point tag_root
,
896 struct network_player
*player
)
898 ctx
->font
= &vgf_default_large
;
901 wr
[2] = VG_MAX( ui_text_line_width( ctx
, player
->username
), 140 ) + 8;
903 wr
[0] = tag_root
[0]-(wr
[2]/2);
904 wr
[1] = tag_root
[1]-(wr
[3]/2);
906 ui_fill( ctx
, wr
, ui_opacity( ui_colour(ctx
, k_ui_bg
), 0.23f
) );
907 ui_text( ctx
, wr
, player
->username
, 1, k_ui_align_middle_center
, 0 );
908 ctx
->font
= &vgf_default_small
;
912 for( int i
=0; i
<3; i
++ )
913 if( player
->medals
[i
] )
921 f32 w
= (f32
)wr
[2] / (f32
)cols
;
924 for( int i
=0; i
<3; i
++ )
926 if( player
->medals
[i
] )
928 ui_rect col
= { wr
[0] + (f32
)cols
*w
, wr
[1] + wr
[3],
931 vg_strnull( &str
, buf
, 32 );
933 vg_strcatch( &str
, (char)k_SRglyph_vg_circle
);
935 vg_strcati32( &str
, player
->medals
[i
] );
937 ui_text( ctx
, col
, buf
, 1, k_ui_align_middle_center
,
938 ui_colour( ctx
, (enum ui_scheme_colour
[]){
939 k_ui_yellow
, k_ui_gray
, k_ui_orange
}[i
] ) );
947 static void remote_player_world_gui( ui_context
*ctx
, m4x4f pv
, v3f root_co
,
948 struct network_player
*player
){
950 if( !player_tag_position( pv
, root_co
, tag_root
) )
955 remote_player_nametag( ctx
, tag_root
, player
);
956 chat_box( ctx
, tag_root
, player
->chat_time
, player
->chat
);
960 if( netplayers
.chatting
)
961 chat_box( ctx
, tag_root
, vg
.time_real
, netplayers
.chat_buffer
);
964 chat_box( ctx
, tag_root
,
965 netplayers
.chat_time
, netplayers
.chat_message
);
970 static void remote_player_gui_info( ui_context
*ctx
, ui_rect box
,
971 const char *username
,
972 const char *activity
,
973 enum remote_player_gui_type type
,
976 f32 opacity
= in_world
? 0.6f
: 0.3f
;
978 if( type
== k_remote_player_gui_type_you
)
979 ui_fill( ctx
, box
, ui_opacity( 0xff555555, opacity
) );
981 ui_fill( ctx
, box
, ui_opacity( 0xff000000, opacity
) );
983 if( type
== k_remote_player_gui_type_friend
)
984 ui_outline( ctx
, box
, -1, ui_opacity( 0xff00c4f0, opacity
), 0 );
987 ui_split_ratio( box
, k_ui_axis_h
, 0.6666f
, 1, top
, bottom
);
991 if( type
== k_remote_player_gui_type_friend
)
992 fg
= ui_colour( ctx
, k_ui_yellow
+ (in_world
? k_ui_brighter
: 0) );
994 fg
= ui_colour( ctx
, in_world
? k_ui_fg
: k_ui_fg
+4 );
996 ctx
->font
= &vgf_default_large
;
997 ui_text( ctx
, top
, username
, 1, k_ui_align_middle_center
, fg
);
998 ctx
->font
= &vgf_default_small
;
999 ui_text( ctx
, bottom
, activity
, 1, k_ui_align_middle_center
, fg
);
1002 void remote_players_imgui_lobby( ui_context
*ctx
)
1004 if( network_client
.user_intent
== k_server_intent_online
){
1005 if( !(steam_ready
&&
1006 (network_client
.state
== k_ESteamNetworkingConnectionState_Connected
)))
1012 ui_px y
= 50, width
= 200, height
= 42, gap
= 2,
1013 x
= vg
.window_x
- width
;
1015 ctx
->font
= &vgf_default_large
;
1016 ui_text( ctx
, (ui_rect
){ x
, 0, width
, height
},
1017 "In World", 1, k_ui_align_middle_center
, 0 );
1018 ctx
->font
= &vgf_default_small
;
1021 ui_rect us
= { x
, y
, width
, height
};
1022 /* FIXME: your location */
1023 remote_player_gui_info( ctx
, us
, steam_username_at_startup
, "you",
1024 k_remote_player_gui_type_you
, 1 );
1027 for( u32 i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ )
1029 struct network_player
*player
= &netplayers
.list
[i
];
1030 if( !player
->active
|| player
->isblocked
) continue;
1032 int in_same_world
= player
->active_world
== world_current_instance();
1033 if( !player
->isfriend
&& !in_same_world
)
1036 const char *location
= in_same_world
? "": "another world";
1037 if( player
->region_flags
& k_ent_region_flag_hasname
){
1038 location
= player
->region
;
1041 ui_rect box
= { x
, y
, width
, height
};
1042 remote_player_gui_info( ctx
, box
, player
->username
, location
,
1043 player
->isfriend
, in_same_world
);
1048 void remote_players_imgui_world( ui_context
*ctx
,
1049 world_instance
*world
, m4x4f pv
,
1050 f32 max_dist
, int geo_cull
)
1052 ui_flush( ctx
, k_ui_shader_colour
, NULL
);
1054 for( u32 i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ )
1056 struct network_player
*player
= &netplayers
.list
[i
];
1057 if( player
->active
)
1060 remote_player_position( i
, co
);
1062 if( !player
->active_world
)
1064 if( !player
->isfriend
&&
1065 (world
-world_static
.instances
== k_world_purpose_hub
)) continue;
1067 /* their in our active subworld */
1068 if( player
->active_world
!= world
)
1070 m4x3_mulv( global_miniworld
.mmdl
, co
, co
);
1071 co
[1] -= 2.0f
; /* HACK lol */
1074 f32 d2
= v3_dist2( co
, localplayer
.rb
.co
);
1076 if( d2
> (max_dist
*max_dist
) )
1079 f32 dist
= sqrtf(d2
);
1080 f32 opacity
= 0.95f
* sqrtf(((max_dist
-dist
)/max_dist
));
1087 v3_sub( co
, skaterift
.cam
.pos
, dir
);
1088 v3_normalize( dir
);
1090 if( ray_world( world
, skaterift
.cam
.pos
, dir
, &hit
,
1091 k_material_flag_ghosts
) ){
1096 player
->opacity
= vg_lerpf( player
->opacity
, opacity
,
1097 vg
.time_frame_delta
* 2.0f
);
1099 remote_player_world_gui( ctx
, pv
, co
, player
);
1101 vg_ui
.colour
[3] = player
->opacity
;
1102 ui_flush( ctx
, k_ui_shader_colour
, NULL
);
1106 vg_ui
.colour
[3] = 1.0f
;
1107 remote_player_world_gui( ctx
, pv
, localplayer
.rb
.co
, NULL
);
1108 ui_flush( ctx
, k_ui_shader_colour
, NULL
);
1111 static void chat_escape( ui_context
*ctx
)
1113 netplayers
.chatting
= -1;
1116 static void chat_enter( ui_context
*ctx
, char *buf
, u32 len
){
1117 vg_strncpy( buf
, netplayers
.chat_message
, NETWORK_MAX_CHAT
,
1118 k_strncpy_always_add_null
);
1119 netplayers
.chatting
= -1;
1120 netplayers
.chat_time
= vg
.time_real
;
1121 chat_send_message( buf
);
1124 void remote_players_chat_imgui( ui_context
*ctx
)
1126 if( netplayers
.chatting
== 1 )
1128 ui_rect box
= { 0, 0, 400, 40 },
1129 window
= { 0, 0, vg
.window_x
, vg
.window_y
};
1130 ui_rect_center( window
, box
);
1132 struct ui_textbox_callbacks callbacks
=
1134 .enter
= chat_enter
,
1135 .escape
= chat_escape
1138 ui_textbox( ctx
, box
, NULL
,
1139 netplayers
.chat_buffer
, NETWORK_MAX_CHAT
, 1,
1140 UI_TEXTBOX_AUTOFOCUS
, &callbacks
);
1144 if( netplayers
.chatting
== -1 )
1146 netplayers
.chatting
= 0;
1147 srinput
.state
= k_input_state_resume
;
1151 if( (skaterift
.activity
== k_skaterift_default
) &&
1152 button_down( k_srbind_chat
) ){
1153 netplayers
.chatting
= 1;
1154 netplayers
.chat_buffer
[0] = '\0';
1155 srinput
.state
= k_input_state_pause
;