network items, interp boundaries
[carveJwlIkooP6JGAAIwe30JlM.git] / player_remote.c
1 #include "player_remote.h"
2 #include "skeleton.h"
3 #include "player_render.h"
4 #include "network_common.h"
5 #include "addon.h"
6
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 );
10 }
11
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;
17 }
18
19 /*
20 * re-attatches addon_reg pointers on the remote client if we have the same
21 * world loaded.
22 */
23 static void relink_remote_player_worlds( u32 client_id ){
24 struct network_player *player = &netplayers.list[client_id];
25
26 player->hub_match = 0;
27 player->client_match = 0;
28
29 addon_alias q0,q1;
30 addon_uid_to_alias( player->items[k_netmsg_playeritem_world0], &q0 );
31 addon_uid_to_alias( player->items[k_netmsg_playeritem_world1], &q1 );
32
33 /*
34 * currently in 10.23, the hub world will always be the same.
35 * this might but probably wont change in the future
36 */
37 if( world_static.addon_hub )
38 if( addon_alias_eq( &q0, &world_static.addon_hub->alias ) )
39 player->hub_match = 1;
40
41 if( world_static.addon_client )
42 if( addon_alias_eq( &q1, &world_static.addon_client->alias ) )
43 player->client_match = 1;
44 }
45
46 /*
47 * re-attatches addon_reg pointers on the remote client if we have the mod
48 * installed locally.
49 *
50 * Run if local worlds change
51 */
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];
55 if( player->active )
56 relink_remote_player_worlds(i);
57 }
58 }
59
60 static void player_remote_rx_200_300( SteamNetworkingMessage_t *msg ){
61 netmsg_blank *tmp = msg->m_pData;
62
63 if( tmp->inetmsg_id == k_inetmsg_playerjoin ){
64 netmsg_playerjoin *playerjoin = msg->m_pData;
65 if( !packet_minsize( msg, sizeof(*playerjoin) )) return;
66
67 if( playerjoin->index < vg_list_size(netplayers.list) ){
68 struct network_player *player = &netplayers.list[ playerjoin->index ];
69 player_remote_clear( player );
70 player->active = 1;
71
72 /* TODO: interpret the uids */
73 player->board_view_slot = 0;
74 player->playermodel_view_slot = 0;
75
76 struct interp_buffer *buf = &netplayers.interp_data[playerjoin->index];
77 buf->t = -99999999.9;
78 for( u32 i=0; i<vg_list_size(buf->frames); i ++ ){
79 buf->frames[i].active = 0;
80 }
81
82 vg_info( "#%u joined\n", playerjoin->index );
83 }
84 else {
85 vg_error( "inetmsg_playerjoin: player index out of range\n" );
86 }
87 }
88 else if( tmp->inetmsg_id == k_inetmsg_playerleave ){
89 netmsg_playerleave *playerleave = msg->m_pData;
90 if( !packet_minsize( msg, sizeof(*playerleave) )) return;
91
92 if( playerleave->index < vg_list_size(netplayers.list) ){
93 struct network_player *player = &netplayers.list[ playerleave->index ];
94 player_remote_unwatch( player );
95 player->active = 0;
96 vg_info( "player leave (%d)\n", playerleave->index );
97 }
98 else {
99 vg_error( "inetmsg_playerleave: player index out of range\n" );
100 }
101 }
102 else if( tmp->inetmsg_id == k_inetmsg_playerusername ){
103 netmsg_playerusername *update = msg->m_pData;
104 if( !packet_minsize( msg, sizeof(*update) )) return;
105
106 if( update->index < vg_list_size(netplayers.list) ){
107 struct network_player *player = &netplayers.list[ update->index ];
108
109 network_msgstring( update->name, msg->m_cbSize, sizeof(*update),
110 player->username, sizeof(player->username) );
111
112 vg_info( "#%u changed username to: %s\n",
113 update->index, player->username );
114 }
115 else {
116 vg_error( "inetmsg_playerleave: player index out of range\n" );
117 }
118 }
119 else if( tmp->inetmsg_id == k_inetmsg_playerframe ){
120 u32 datasize = msg->m_cbSize - sizeof(netmsg_playerframe);
121
122 if( datasize > sizeof(union interp_animdata) ){
123 vg_error( "Player frame data exceeds animdata size\n" );
124 return;
125 }
126
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" );
130 return;
131 }
132
133 if( frame->subsystem >= k_player_subsystem_max ){
134 vg_error( "inetmsg_playerframe: subsystem out of range\n" );
135 return;
136 }
137
138 struct interp_buffer *ib = &netplayers.interp_data[ frame->client ];
139 struct interp_frame *dest = NULL;
140
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];
144
145 if( !ifr->active ){
146 dest = ifr;
147 break;
148 }
149
150 if( ifr->timestamp < min_time ){
151 min_time = ifr->timestamp;
152 dest = ifr;
153 }
154 }
155
156 dest->active = 1;
157 dest->timestamp = frame->timestamp;
158 dest->subsystem = frame->subsystem;
159 dest->instance_id = frame->instance_id;
160 dest->boundary_hash = frame->boundary_hash;
161
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;
166 }
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;
170
171 if( item->client >= vg_list_size(netplayers.list) ){
172 vg_error( "inetmsg_playerframe: player index out of range\n" );
173 return;
174 }
175
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 );
179 return;
180 }
181
182 struct network_player *player = &netplayers.list[ item->client ];
183 char *uid = player->items[ item->type_index ];
184
185 network_msgstring( item->uid, msg->m_cbSize, sizeof(*item),
186 uid, ADDON_UID_MAX );
187
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 );
192 }
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 );
198 }
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 );
202 }
203 }
204 }
205
206 /*
207 * Write localplayer pose to network
208 */
209 static void remote_player_send_playerframe(void){
210 u8 sysid = localplayer.subsystem;
211 if( sysid >= k_player_subsystem_max ) return;
212
213 struct player_subsystem_interface *sys = player_subsystems[sysid];
214
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;
224
225 memcpy( frame->animdata, sys->animator_data, sys->animator_size );
226
227 netplayers.up_bytes += size;
228
229 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
230 hSteamNetworkingSockets, network_client.remote,
231 frame, size,
232 k_nSteamNetworkingSend_Unreliable, NULL );
233 }
234 }
235
236 /*
237 * Updates network traffic stats
238 */
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;
242 u32 total_down = 0;
243
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;
250 }
251 }
252
253 netplayers.down_kbs = ((f32)total_down)/1024.0f;
254 netplayers.up_kbs = ((f32)netplayers.up_bytes)/1024.0f;
255 netplayers.up_bytes = 0;
256 }
257 }
258
259 /*
260 * Debugging information
261 */
262 static void remote_player_network_imgui( m4x4f pv ){
263 if( !network_client.network_info )
264 return;
265
266 ui_rect panel = { (vg.window_x / 2) - 200, 0, 400, 600 };
267 ui_fill( panel, (ui_colour(k_ui_bg)&0x00ffffff)|0x50000000 );
268
269 char buf[512];
270 const char *netstatus = "PROGRAMMING ERROR";
271
272 struct { enum ESteamNetworkingConnectionState state; const char *str; }
273 states[] = {
274 { k_ESteamNetworkingConnectionState_None, "None" },
275 { k_ESteamNetworkingConnectionState_Connecting,
276 (const char *[]){"Connecting -",
277 "Connecting /",
278 "Connecting |",
279 "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" }
289 };
290 for( u32 i=0; i<vg_list_size(states); i ++ ){
291 if( states[i].state == network_client.state ){
292 netstatus = states[i].str;
293 break;
294 }
295 }
296 snprintf( buf, 512, "Network: %s", netstatus );
297 ui_info( panel, buf );
298 ui_info( panel, "---------------------" );
299
300 if( network_client.state == k_ESteamNetworkingConnectionState_Connected ){
301 ui_info( panel, "#-1: localplayer" );
302
303 snprintf( buf, 512, "U%.1f/D%.1fkbs",
304 netplayers.up_kbs, netplayers.down_kbs );
305 ui_info( panel, buf );
306
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";
311
312 if( player->subsystem < k_player_subsystem_max ){
313 sysname = player_subsystems[ player->subsystem ]->name;
314 }
315 snprintf( buf, 512, "#%u: %s [%s] D%.1fkbs",
316 i, player->username, sysname, player->down_kbs );
317 ui_info( panel, buf );
318
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 );
323
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 );
327
328 ui_rect wr;
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,
331 -32000.0f,32000.0f);
332 wr[2] = 300;
333 wr[3] = 17;
334 ui_fill( wr, (ui_colour(k_ui_bg)&0x00ffffff)|0x50000000 );
335 ui_text( wr, buf, 1, k_ui_align_middle_center, 0 );
336 }
337 }
338 }
339 }
340 else {
341 ui_info( panel, "offline" );
342 }
343 }
344
345 /*
346 * write the remote players final_mtx
347 */
348 static void pose_remote_player( u32 index,
349 struct interp_frame *f0,
350 struct interp_frame *f1 ){
351
352 struct network_player *player = &netplayers.list[ index ];
353
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];
358
359 struct player_subsystem_interface *sys0 = player_subsystems[f0->subsystem],
360 *sys1 = NULL;
361
362 player_pose pose0, pose1, posed;
363 sys0->pose( &f0->data, &pose0 );
364
365 u8 instance_id = 0;
366
367 if( f1 ){
368 f32 t = (buf->t - f0->timestamp) / (f1->timestamp - f0->timestamp);
369 t = vg_clampf( t, 0.0f, 1.0f );
370
371 sys1 = player_subsystems[f1->subsystem];
372 sys1->pose( &f1->data, &pose1 );
373
374 u16 bounds = f0->boundary_hash^f1->boundary_hash;
375
376 if( bounds & NETMSG_BOUNDARY_BIT )
377 t = 1.0f;
378
379 if( bounds & NETMSG_GATE_BOUNDARY_BIT ){
380 /* TODO: Extra work retransforming the root_co, instance_id.. etc */
381 t = 1.0f;
382 }
383
384 instance_id = f1->instance_id;
385
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) );
389 }
390 else {
391 instance_id = f0->instance_id;
392
393 apply_full_skeleton_pose( &av->sk, &pose0, final_mtx );
394 memcpy( board_pose, &pose0.board, sizeof(*board_pose) );
395 }
396
397 if( instance_id ){
398 if( player->client_match ){
399 player->active_world = &world_static.instances[ instance_id ];
400 }
401 }
402 else{
403 if( player->hub_match ){
404 player->active_world = &world_static.instances[ instance_id ];
405 }
406 }
407 }
408
409 /*
410 * animate remote player and store in final_mtx
411 */
412 static void animate_remote_player( u32 index ){
413
414 /*
415 * Trys to keep the cursor inside the buffer
416 */
417 f64 min_time = -999999999.9,
418 max_time = 999999999.9,
419 abs_max_time = -999999999.9;
420
421 struct interp_frame *minframe = NULL,
422 *maxframe = NULL,
423 *abs_max_frame = NULL;
424
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];
428
429 if( ifr->active ){
430 if( (ifr->timestamp > min_time) && (ifr->timestamp < buf->t) ){
431 min_time = ifr->timestamp;
432 minframe = ifr;
433 }
434
435 if( (ifr->timestamp < max_time) && (ifr->timestamp > buf->t) ){
436 max_time = ifr->timestamp;
437 maxframe = ifr;
438 }
439
440 if( ifr->timestamp > abs_max_time ){
441 abs_max_time = ifr->timestamp;
442 abs_max_frame = ifr;
443 }
444 }
445 }
446
447 struct network_player *player = &netplayers.list[ index ];
448 player->active_world = NULL;
449
450 if( minframe && maxframe ){
451 pose_remote_player( index, minframe, maxframe );
452 buf->t += vg.time_frame_delta;
453 }
454 else {
455 buf->t = abs_max_time - 0.25;
456
457 if( abs_max_frame )
458 pose_remote_player( index, abs_max_frame, NULL );
459 else
460 return;
461 }
462 }
463
464 /*
465 * Update full final_mtx for all remote players
466 */
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;
471
472 animate_remote_player( i );
473 }
474 }
475
476 /*
477 * Draw remote players
478 */
479 static void render_remote_players( world_instance *world, camera *cam ){
480
481 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
482
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;
487
488 struct player_avatar *av = localplayer.playeravatar;
489 m4x3f *final_mtx = &netplayers.final_mtx[ av->sk.bone_count*i ];
490
491 struct player_model *model =
492 addon_cache_item_if_loaded( k_addon_type_player,
493 player->playermodel_view_slot );
494
495 if( !model ) model = &localplayer.fallback_model;
496 render_playermodel( cam, world, 0, model, &av->sk, final_mtx );
497
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 );
505 }
506
507 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
508 }
509
510 /* TODO: Which world is the player in
511 * nametags with occlusion
512 */