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
;
20 * re-attatches addon_reg pointers on the remote client if we have the same
23 static void relink_remote_player_worlds( u32 client_id
){
24 struct network_player
*player
= &netplayers
.list
[client_id
];
26 player
->hub_match
= 0;
27 player
->client_match
= 0;
30 addon_uid_to_alias( player
->items
[k_netmsg_playeritem_world0
], &q0
);
31 addon_uid_to_alias( player
->items
[k_netmsg_playeritem_world1
], &q1
);
34 * currently in 10.23, the hub world will always be the same.
35 * this might but probably wont change in the future
37 if( world_static
.addon_hub
)
38 if( addon_alias_eq( &q0
, &world_static
.addon_hub
->alias
) )
39 player
->hub_match
= 1;
41 if( world_static
.addon_client
)
42 if( addon_alias_eq( &q1
, &world_static
.addon_client
->alias
) )
43 player
->client_match
= 1;
47 * re-attatches addon_reg pointers on the remote client if we have the mod
50 * Run if local worlds change
52 static void relink_all_remote_player_worlds(void){
53 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
54 struct network_player
*player
= &netplayers
.list
[i
];
56 relink_remote_player_worlds(i
);
60 static void player_remote_rx_200_300( SteamNetworkingMessage_t
*msg
){
61 netmsg_blank
*tmp
= msg
->m_pData
;
63 if( tmp
->inetmsg_id
== k_inetmsg_playerjoin
){
64 netmsg_playerjoin
*playerjoin
= msg
->m_pData
;
65 if( !packet_minsize( msg
, sizeof(*playerjoin
) )) return;
67 if( playerjoin
->index
< vg_list_size(netplayers
.list
) ){
68 struct network_player
*player
= &netplayers
.list
[ playerjoin
->index
];
69 player_remote_clear( player
);
72 /* TODO: interpret the uids */
73 player
->board_view_slot
= 0;
74 player
->playermodel_view_slot
= 0;
76 struct interp_buffer
*buf
= &netplayers
.interp_data
[playerjoin
->index
];
78 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
79 buf
->frames
[i
].active
= 0;
82 vg_info( "#%u joined\n", playerjoin
->index
);
85 vg_error( "inetmsg_playerjoin: player index out of range\n" );
88 else if( tmp
->inetmsg_id
== k_inetmsg_playerleave
){
89 netmsg_playerleave
*playerleave
= msg
->m_pData
;
90 if( !packet_minsize( msg
, sizeof(*playerleave
) )) return;
92 if( playerleave
->index
< vg_list_size(netplayers
.list
) ){
93 struct network_player
*player
= &netplayers
.list
[ playerleave
->index
];
94 player_remote_unwatch( player
);
96 vg_info( "player leave (%d)\n", playerleave
->index
);
99 vg_error( "inetmsg_playerleave: player index out of range\n" );
102 else if( tmp
->inetmsg_id
== k_inetmsg_playerusername
){
103 netmsg_playerusername
*update
= msg
->m_pData
;
104 if( !packet_minsize( msg
, sizeof(*update
) )) return;
106 if( update
->index
< vg_list_size(netplayers
.list
) ){
107 struct network_player
*player
= &netplayers
.list
[ update
->index
];
109 network_msgstring( update
->name
, msg
->m_cbSize
, sizeof(*update
),
110 player
->username
, sizeof(player
->username
) );
112 vg_info( "#%u changed username to: %s\n",
113 update
->index
, player
->username
);
116 vg_error( "inetmsg_playerleave: player index out of range\n" );
119 else if( tmp
->inetmsg_id
== k_inetmsg_playerframe
){
120 u32 datasize
= msg
->m_cbSize
- sizeof(netmsg_playerframe
);
122 if( datasize
> sizeof(union interp_animdata
) ){
123 vg_error( "Player frame data exceeds animdata size\n" );
127 netmsg_playerframe
*frame
= msg
->m_pData
;
128 if( frame
->client
>= vg_list_size(netplayers
.list
) ){
129 vg_error( "inetmsg_playerframe: player index out of range\n" );
133 if( frame
->subsystem
>= k_player_subsystem_max
){
134 vg_error( "inetmsg_playerframe: subsystem out of range\n" );
138 struct interp_buffer
*ib
= &netplayers
.interp_data
[ frame
->client
];
139 struct interp_frame
*dest
= NULL
;
141 f64 min_time
= INFINITY
;
142 for( u32 i
=0; i
<vg_list_size(ib
->frames
); i
++ ){
143 struct interp_frame
*ifr
= &ib
->frames
[i
];
150 if( ifr
->timestamp
< min_time
){
151 min_time
= ifr
->timestamp
;
157 dest
->timestamp
= frame
->timestamp
;
158 dest
->subsystem
= frame
->subsystem
;
159 dest
->instance_id
= frame
->instance_id
;
160 dest
->boundary_hash
= frame
->boundary_hash
;
162 struct network_player
*player
= &netplayers
.list
[ frame
->client
];
163 memcpy( &dest
->data
, frame
->animdata
, datasize
);
164 player
->subsystem
= frame
->subsystem
;
165 player
->down_bytes
+= msg
->m_cbSize
;
167 else if( tmp
->inetmsg_id
== k_inetmsg_playeritem
){
168 netmsg_playeritem
*item
= msg
->m_pData
;
169 if( !packet_minsize( msg
, sizeof(*item
)+1 )) return;
171 if( item
->client
>= vg_list_size(netplayers
.list
) ){
172 vg_error( "inetmsg_playerframe: player index out of range\n" );
176 if( item
->type_index
>= k_netmsg_playeritem_max
){
177 vg_warn( "Client #%d invalid equip type %u\n",
178 (i32
)item
->client
, (u32
)item
->type_index
);
182 struct network_player
*player
= &netplayers
.list
[ item
->client
];
183 char *uid
= player
->items
[ item
->type_index
];
185 network_msgstring( item
->uid
, msg
->m_cbSize
, sizeof(*item
),
186 uid
, ADDON_UID_MAX
);
188 if( item
->type_index
== k_netmsg_playeritem_board
){
189 addon_cache_unwatch( k_addon_type_board
, player
->board_view_slot
);
190 player
->board_view_slot
=
191 addon_cache_create_viewer_from_uid( k_addon_type_board
, uid
);
193 else if( item
->type_index
== k_netmsg_playeritem_player
){
194 addon_cache_unwatch( k_addon_type_player
,
195 player
->playermodel_view_slot
);
196 player
->playermodel_view_slot
=
197 addon_cache_create_viewer_from_uid( k_addon_type_player
, uid
);
199 else if( (item
->type_index
== k_netmsg_playeritem_world0
) ||
200 (item
->type_index
== k_netmsg_playeritem_world1
) ){
201 relink_remote_player_worlds( item
->client
);
207 * Write localplayer pose to network
209 static void remote_player_send_playerframe(void){
210 u8 sysid
= localplayer
.subsystem
;
211 if( sysid
>= k_player_subsystem_max
) return;
213 struct player_subsystem_interface
*sys
= player_subsystems
[sysid
];
215 if( sys
->animator_size
){
216 u32 size
= sizeof(netmsg_playerframe
)+sys
->animator_size
;
217 netmsg_playerframe
*frame
= alloca(size
);
218 frame
->inetmsg_id
= k_inetmsg_playerframe
;
219 frame
->client
= 0xff;
220 frame
->subsystem
= localplayer
.subsystem
;
221 frame
->timestamp
= vg
.time_real
;
222 frame
->boundary_hash
= localplayer
.boundary_hash
;
223 frame
->instance_id
= world_static
.active_instance
;
225 memcpy( frame
->animdata
, sys
->animator_data
, sys
->animator_size
);
227 netplayers
.up_bytes
+= size
;
229 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
230 hSteamNetworkingSockets
, network_client
.remote
,
232 k_nSteamNetworkingSend_Unreliable
, NULL
);
237 * Updates network traffic stats
239 static void remote_player_debug_update(void){
240 if( (vg
.time_real
- netplayers
.last_data_measurement
) > 1.0 ){
241 netplayers
.last_data_measurement
= vg
.time_real
;
244 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
245 struct network_player
*player
= &netplayers
.list
[i
];
246 if( player
->active
){
247 total_down
+= player
->down_bytes
;
248 player
->down_kbs
= ((f32
)player
->down_bytes
)/1024.0f
;
249 player
->down_bytes
= 0;
253 netplayers
.down_kbs
= ((f32
)total_down
)/1024.0f
;
254 netplayers
.up_kbs
= ((f32
)netplayers
.up_bytes
)/1024.0f
;
255 netplayers
.up_bytes
= 0;
260 * Debugging information
262 static void remote_player_network_imgui( m4x4f pv
){
263 if( !network_client
.network_info
)
266 ui_rect panel
= { (vg
.window_x
/ 2) - 200, 0, 400, 600 };
267 ui_fill( panel
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
270 const char *netstatus
= "PROGRAMMING ERROR";
272 struct { enum ESteamNetworkingConnectionState state
; const char *str
; }
274 { k_ESteamNetworkingConnectionState_None
, "None" },
275 { k_ESteamNetworkingConnectionState_Connecting
,
276 (const char *[]){"Connecting -",
280 }[(u32
)(vg
.time_real
/0.25) & 0x3 ] },
281 { k_ESteamNetworkingConnectionState_FindingRoute
, "Finding Route" },
282 { k_ESteamNetworkingConnectionState_Connected
, "Connected" },
283 { k_ESteamNetworkingConnectionState_ClosedByPeer
, "Closed by peer" },
284 { k_ESteamNetworkingConnectionState_ProblemDetectedLocally
,
285 "Problem Detected Locally" },
286 { k_ESteamNetworkingConnectionState_FinWait
, "Fin Wait" },
287 { k_ESteamNetworkingConnectionState_Linger
, "Linger" },
288 { k_ESteamNetworkingConnectionState_Dead
, "Dead" }
290 for( u32 i
=0; i
<vg_list_size(states
); i
++ ){
291 if( states
[i
].state
== network_client
.state
){
292 netstatus
= states
[i
].str
;
296 snprintf( buf
, 512, "Network: %s", netstatus
);
297 ui_info( panel
, buf
);
298 ui_info( panel
, "---------------------" );
300 if( network_client
.state
== k_ESteamNetworkingConnectionState_Connected
){
301 ui_info( panel
, "#-1: localplayer" );
303 snprintf( buf
, 512, "U%.1f/D%.1fkbs",
304 netplayers
.up_kbs
, netplayers
.down_kbs
);
305 ui_info( panel
, buf
);
307 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
308 struct network_player
*player
= &netplayers
.list
[i
];
309 if( player
->active
){
310 const char *sysname
= "invalid";
312 if( player
->subsystem
< k_player_subsystem_max
){
313 sysname
= player_subsystems
[ player
->subsystem
]->name
;
315 snprintf( buf
, 512, "#%u: %s [%s] D%.1fkbs",
316 i
, player
->username
, sysname
, player
->down_kbs
);
317 ui_info( panel
, buf
);
319 v4f wpos
= { 0.0f
, 2.0f
, 0.0f
, 1.0f
};
320 struct player_avatar
*av
= localplayer
.playeravatar
;
321 m4x3_mulv( netplayers
.final_mtx
[av
->sk
.bone_count
*i
], wpos
, wpos
);
322 m4x4_mulv( pv
, wpos
, wpos
);
324 if( wpos
[3] > 0.0f
){
325 v2_muls( wpos
, (1.0f
/wpos
[3]) * 0.5f
, wpos
);
326 v2_add( wpos
, (v2f
){ 0.5f
, 0.5f
}, wpos
);
329 wr
[0] = vg_clampf(wpos
[0] * vg
.window_x
, -32000.0f
,32000.0f
)-150;
330 wr
[1] = vg_clampf((1.0f
-wpos
[1]) * vg
.window_y
,
334 ui_fill( wr
, (ui_colour(k_ui_bg
)&0x00ffffff)|0x50000000 );
335 ui_text( wr
, buf
, 1, k_ui_align_middle_center
, 0 );
341 ui_info( panel
, "offline" );
346 * write the remote players final_mtx
348 static void pose_remote_player( u32 index
,
349 struct interp_frame
*f0
,
350 struct interp_frame
*f1
){
352 struct network_player
*player
= &netplayers
.list
[ index
];
354 struct interp_buffer
*buf
= &netplayers
.interp_data
[ index
];
355 struct player_avatar
*av
= localplayer
.playeravatar
;
356 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*index
];
357 struct player_board_pose
*board_pose
= &netplayers
.board_poses
[index
];
359 struct player_subsystem_interface
*sys0
= player_subsystems
[f0
->subsystem
],
362 player_pose pose0
, pose1
, posed
;
363 sys0
->pose( &f0
->data
, &pose0
);
368 f32 t
= (buf
->t
- f0
->timestamp
) / (f1
->timestamp
- f0
->timestamp
);
369 t
= vg_clampf( t
, 0.0f
, 1.0f
);
371 sys1
= player_subsystems
[f1
->subsystem
];
372 sys1
->pose( &f1
->data
, &pose1
);
374 u16 bounds
= f0
->boundary_hash
^f1
->boundary_hash
;
376 if( bounds
& NETMSG_BOUNDARY_BIT
)
379 if( bounds
& NETMSG_GATE_BOUNDARY_BIT
){
380 /* TODO: Extra work retransforming the root_co, instance_id.. etc */
384 instance_id
= f1
->instance_id
;
386 lerp_player_pose( &pose0
, &pose1
, t
, &posed
);
387 apply_full_skeleton_pose( &av
->sk
, &posed
, final_mtx
);
388 memcpy( board_pose
, &posed
.board
, sizeof(*board_pose
) );
391 instance_id
= f0
->instance_id
;
393 apply_full_skeleton_pose( &av
->sk
, &pose0
, final_mtx
);
394 memcpy( board_pose
, &pose0
.board
, sizeof(*board_pose
) );
398 if( player
->client_match
){
399 player
->active_world
= &world_static
.instances
[ instance_id
];
403 if( player
->hub_match
){
404 player
->active_world
= &world_static
.instances
[ instance_id
];
410 * animate remote player and store in final_mtx
412 static void animate_remote_player( u32 index
){
415 * Trys to keep the cursor inside the buffer
417 f64 min_time
= -999999999.9,
418 max_time
= 999999999.9,
419 abs_max_time
= -999999999.9;
421 struct interp_frame
*minframe
= NULL
,
423 *abs_max_frame
= NULL
;
425 struct interp_buffer
*buf
= &netplayers
.interp_data
[index
];
426 for( u32 i
=0; i
<vg_list_size(buf
->frames
); i
++ ){
427 struct interp_frame
*ifr
= &buf
->frames
[i
];
430 if( (ifr
->timestamp
> min_time
) && (ifr
->timestamp
< buf
->t
) ){
431 min_time
= ifr
->timestamp
;
435 if( (ifr
->timestamp
< max_time
) && (ifr
->timestamp
> buf
->t
) ){
436 max_time
= ifr
->timestamp
;
440 if( ifr
->timestamp
> abs_max_time
){
441 abs_max_time
= ifr
->timestamp
;
447 struct network_player
*player
= &netplayers
.list
[ index
];
448 player
->active_world
= NULL
;
450 if( minframe
&& maxframe
){
451 pose_remote_player( index
, minframe
, maxframe
);
452 buf
->t
+= vg
.time_frame_delta
;
455 buf
->t
= abs_max_time
- 0.25;
458 pose_remote_player( index
, abs_max_frame
, NULL
);
465 * Update full final_mtx for all remote players
467 static void animate_remote_players(void){
468 for( u32 i
=0; i
<vg_list_size(netplayers
.list
); i
++ ){
469 struct network_player
*player
= &netplayers
.list
[i
];
470 if( !player
->active
) continue;
472 animate_remote_player( i
);
477 * Draw remote players
479 static void render_remote_players( world_instance
*world
, camera
*cam
){
481 SDL_AtomicLock( &addon_system
.sl_cache_using_resources
);
483 for( u32 i
=0; i
<NETWORK_MAX_PLAYERS
; i
++ ){
484 struct network_player
*player
= &netplayers
.list
[i
];
485 if( !player
->active
) continue;
486 if( player
->active_world
!= world
) continue;
488 struct player_avatar
*av
= localplayer
.playeravatar
;
489 m4x3f
*final_mtx
= &netplayers
.final_mtx
[ av
->sk
.bone_count
*i
];
491 struct player_model
*model
=
492 addon_cache_item_if_loaded( k_addon_type_player
,
493 player
->playermodel_view_slot
);
495 if( !model
) model
= &localplayer
.fallback_model
;
496 render_playermodel( cam
, world
, 0, model
, &av
->sk
, final_mtx
);
498 struct player_board
*board
=
499 addon_cache_item_if_loaded( k_addon_type_board
,
500 player
->board_view_slot
);
501 render_board( cam
, world
, board
,
502 final_mtx
[localplayer
.playeravatar
->id_board
],
503 &netplayers
.board_poses
[ i
],
504 k_board_shader_player
);
507 SDL_AtomicUnlock( &addon_system
.sl_cache_using_resources
);
510 /* TODO: Which world is the player in
511 * nametags with occlusion