controls overlay
[carveJwlIkooP6JGAAIwe30JlM.git] / network.c
1 #include "skaterift.h"
2 #include "vg/vg_steam.h"
3 #include "vg/vg_steam_networking.h"
4 #include "vg/vg_steam_auth.h"
5 #include "vg/vg_steam_friends.h"
6 #include "player.h"
7 #include "network.h"
8 #include "network_msg.h"
9 #include "network_common.h"
10 #include "player_remote.h"
11 #include "world.h"
12 #include "world_sfd.h"
13 #include "world_routes.h"
14 #include "vg/vg_imgui.h"
15 #include "gui.h"
16 #include "ent_region.h"
17
18 struct network_client network_client =
19 {
20 .auth_mode = eServerModeAuthentication,
21 .state = k_ESteamNetworkingConnectionState_None,
22 .server_adress = "46.101.34.155",
23 .last_intent_change = -99999.9
24 };
25
26 static void scores_update(void);
27
28 int packet_minsize( SteamNetworkingMessage_t *msg, u32 size ){
29 if( msg->m_cbSize < size ) {
30 vg_error( "Invalid packet size (must be at least %u)\n", size );
31 return 0;
32 }
33 else{
34 return 1;
35 }
36 }
37
38 static void on_auth_ticket_recieved( void *result, void *context ){
39 EncryptedAppTicketResponse_t *response = result;
40
41 if( response->m_eResult == k_EResultOK ){
42 vg_info( " New app ticket ready\n" );
43 }
44 else{
45 vg_warn( " Could not request new encrypted app ticket (%u)\n",
46 response->m_eResult );
47 }
48
49 if( SteamAPI_ISteamUser_GetEncryptedAppTicket( hSteamUser,
50 network_client.app_symmetric_key,
51 vg_list_size(network_client.app_symmetric_key),
52 &network_client.app_key_length )){
53 vg_success( " Loaded app ticket\n" );
54 }
55 else{
56 vg_error( " No ticket availible\n" );
57 network_client.app_key_length = 0;
58 }
59 }
60
61 static void request_auth_ticket(void){
62 /*
63 * TODO Check for one thats cached on the disk and load it.
64 * This might be OK though because steam seems to cache the result
65 */
66
67 vg_info( "Requesting new authorization ticket\n" );
68
69 vg_steam_async_call *call = vg_alloc_async_steam_api_call();
70 call->userdata = NULL;
71 call->p_handler = on_auth_ticket_recieved;
72 call->id =
73 SteamAPI_ISteamUser_RequestEncryptedAppTicket( hSteamUser, NULL, 0 );
74 }
75
76 static void network_send_username(void){
77 if( !network_connected() )
78 return;
79
80 netmsg_playerusername *update = alloca( sizeof(netmsg_playerusername)+
81 NETWORK_USERNAME_MAX );
82 update->inetmsg_id = k_inetmsg_playerusername;
83 update->index = 0xff;
84
85 ISteamFriends *hSteamFriends = SteamAPI_SteamFriends();
86 const char *username = SteamAPI_ISteamFriends_GetPersonaName(hSteamFriends);
87 u32 chs = str_utf8_collapse( username, update->name, NETWORK_USERNAME_MAX );
88
89 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
90 hSteamNetworkingSockets, network_client.remote,
91 update, sizeof(netmsg_playerusername)+chs+1,
92 k_nSteamNetworkingSend_Reliable, NULL );
93 }
94
95 void network_send_region(void)
96 {
97 if( !network_connected() )
98 return;
99
100 netmsg_region *region = alloca( sizeof(netmsg_region) + NETWORK_REGION_MAX );
101
102 region->inetmsg_id = k_inetmsg_region;
103 region->client = 0;
104 region->flags = global_ent_region.flags;
105
106 u32 l = vg_strncpy( global_ent_region.location, region->loc,
107 NETWORK_REGION_MAX, k_strncpy_always_add_null );
108
109 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
110 hSteamNetworkingSockets, network_client.remote,
111 region, sizeof(netmsg_region)+l+1,
112 k_nSteamNetworkingSend_Reliable, NULL );
113 }
114
115 static void network_send_request( netmsg_request *req, vg_msg *body,
116 void (*callback)(
117 netmsg_request *res, vg_msg *body,
118 u64 userdata),
119 u64 userdata ){
120 u32 len = 0;
121 if( body ){
122 len = body->cur.co;
123 vg_info( "Request scoreboard. Info (%u):\n", body->cur.co );
124 vg_msg_print( body, len );
125
126 if( body->error != k_vg_msg_error_OK ){
127 vg_error( "Body not OK\n" );
128 return;
129 }
130 }
131
132 if( callback ){
133 req->id = vg_pool_lru( &network_client.request_pool );
134 if( req->id ){
135 vg_pool_watch( &network_client.request_pool, req->id );
136 struct network_request *pn =
137 vg_pool_item( &network_client.request_pool, req->id );
138 pn->callback = callback;
139 pn->sendtime = vg.time_real;
140 pn->userdata = userdata;
141 }
142 else{
143 vg_error( "Unable to send request. Pool is full.\n" );
144 return;
145 }
146 }
147 else
148 req->id = 0;
149
150 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
151 hSteamNetworkingSockets, network_client.remote,
152 req, sizeof(netmsg_request)+len,
153 k_nSteamNetworkingSend_Reliable, NULL );
154 }
155
156 static void network_scoreboard_callback( netmsg_request *res, vg_msg *body,
157 u64 userdata ){
158 world_instance *world = world_current_instance();
159
160 world_routes_recv_scoreboard( world, body, userdata, res->status );
161 if( userdata == world_sfd.active_route_board )
162 world_sfd_compile_active_scores();
163 }
164
165
166
167 /* mod_uid: world mod uid,
168 * route_uid: run name (just a string)
169 * week:
170 * 0 ALL TIME
171 * 1 CURRENT WEEK
172 * 2 ALL TIME + CURRENT WEEK
173 * .
174 * 10+ specific week index
175 */
176 void network_request_scoreboard( const char *mod_uid,
177 const char *route_uid,
178 u32 week, u64 userdata ){
179 if( !network_connected() )
180 return;
181
182 netmsg_request *req = alloca( sizeof(netmsg_request) + 512 );
183 req->inetmsg_id = k_inetmsg_request;
184
185 vg_msg data;
186 vg_msg_init( &data, req->q, 512 );
187 vg_msg_wkvstr( &data, "endpoint", "scoreboard" );
188 vg_msg_wkvstr( &data, "mod", mod_uid );
189 vg_msg_wkvstr( &data, "route", route_uid );
190 vg_msg_wkvnum( &data, "week", k_vg_msg_u32, 1, &week );
191 network_send_request( req, &data, network_scoreboard_callback, userdata );
192 }
193
194 static void network_publish_callback( netmsg_request *res, vg_msg *body,
195 u64 userdata ){
196 if( res->status != k_request_status_ok ){
197 vg_error( "Publish laptime, server error #%d\n", (i32)res->status );
198 }
199 }
200
201 void network_publish_laptime( const char *mod_uid,
202 const char *route_uid, f64 lap_time ){
203 if( !network_connected() )
204 return;
205
206 i32 time_centiseconds = lap_time * 100.0;
207
208 netmsg_request *req = alloca( sizeof(netmsg_request) + 512 );
209 req->inetmsg_id = k_inetmsg_request;
210
211 vg_msg data;
212 vg_msg_init( &data, req->q, 512 );
213 vg_msg_wkvstr( &data, "endpoint", "setlap" );
214 vg_msg_wkvstr( &data, "mod", mod_uid );
215 vg_msg_wkvstr( &data, "route", route_uid );
216 vg_msg_wkvnum( &data, "time", k_vg_msg_i32, 1, &time_centiseconds );
217 network_send_request( req, &data, network_publish_callback, 0 );
218 }
219
220 static void network_request_rx_300_400( SteamNetworkingMessage_t *msg ){
221 netmsg_blank *tmp = msg->m_pData;
222
223 if( tmp->inetmsg_id == k_inetmsg_request ){
224
225 }
226 else if( tmp->inetmsg_id == k_inetmsg_response ){
227 netmsg_request *res = (netmsg_request *)msg->m_pData;
228
229 vg_msg *body = NULL;
230
231 vg_msg data;
232 if( res->status == k_request_status_ok ){
233 vg_msg_init( &data, res->q, msg->m_cbSize - sizeof(netmsg_request) );
234 vg_success( "Response to #%d:\n", (i32)res->id );
235 vg_msg_print( &data, data.max );
236 body = &data;
237 }
238 else {
239 vg_warn( "Server response to #%d: %d\n", (i32)res->id, res->status );
240 }
241
242 if( res->id ){
243 struct network_request *pn =
244 vg_pool_item( &network_client.request_pool, res->id );
245 pn->callback( res, body, pn->userdata );
246 vg_pool_unwatch( &network_client.request_pool, res->id );
247 }
248 }
249 }
250
251 void network_send_item( enum netmsg_playeritem_type type )
252 {
253 if( !network_connected() )
254 return;
255
256 netmsg_playeritem *item =
257 alloca( sizeof(netmsg_playeritem) + ADDON_UID_MAX );
258 item->inetmsg_id = k_inetmsg_playeritem;
259 item->type_index = type;
260 item->client = 0;
261
262 if( (type == k_netmsg_playeritem_world0) ||
263 (type == k_netmsg_playeritem_world1) ){
264
265 enum world_purpose purpose = type - k_netmsg_playeritem_world0;
266 addon_reg *reg = world_static.instance_addons[ purpose ];
267
268 if( reg )
269 addon_alias_uid( &reg->alias, item->uid );
270 else
271 item->uid[0] = '\0';
272 }
273 else{
274 u16 view_id = 0;
275 enum addon_type addon_type = k_addon_type_none;
276 if( type == k_netmsg_playeritem_board ){
277 view_id = localplayer.board_view_slot;
278 addon_type = k_addon_type_board;
279 }
280 else if( type == k_netmsg_playeritem_player ){
281 view_id = localplayer.playermodel_view_slot;
282 addon_type = k_addon_type_player;
283 }
284
285 struct addon_cache *cache = &addon_system.cache[addon_type];
286 vg_pool *pool = &cache->pool;
287
288 SDL_AtomicLock( &addon_system.sl_cache_using_resources );
289 addon_cache_entry *entry = vg_pool_item( pool, view_id );
290 addon_alias_uid( &entry->reg_ptr->alias, item->uid );
291 SDL_AtomicUnlock( &addon_system.sl_cache_using_resources );
292 }
293
294 vg_info( "send equip: [%u] %s\n",
295 item->type_index, item->uid );
296 u32 chs = strlen(item->uid);
297
298 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
299 hSteamNetworkingSockets, network_client.remote,
300 item, sizeof(netmsg_playeritem)+chs+1,
301 k_nSteamNetworkingSend_Reliable, NULL );
302 }
303
304 static void network_disconnect(void){
305 SteamAPI_ISteamNetworkingSockets_CloseConnection(
306 hSteamNetworkingSockets, network_client.remote, 0, NULL, 0 );
307 network_client.remote = 0;
308 network_client.state = k_ESteamNetworkingConnectionState_None;
309
310 for( int i=0; i<vg_list_size(netplayers.list); i++ ){
311 netplayers.list[i].active = 0;
312 }
313 }
314
315 void network_status_string( vg_str *str, u32 *colour )
316 {
317 if( skaterift.demo_mode ){
318 vg_strcat( str, "Offline" );
319 return;
320 }
321
322 if( steam_ready ){
323 if( network_client.user_intent == k_server_intent_offline ){
324 vg_strcat( str, "Offline" );
325 }
326 else {
327 ESteamNetworkingConnectionState state = network_client.state;
328
329 if( state == k_ESteamNetworkingConnectionState_None )
330 vg_strcat( str, "No Connection" );
331 else if( state == k_ESteamNetworkingConnectionState_Connecting ){
332 vg_strcat( str, "Connecting to:\nskaterift.com" );
333
334 if( network_client.retries ){
335 vg_strcat( str, "\n(" );
336 vg_strcati32( str, network_client.retries );
337 vg_strcat( str, " retries)" );
338 }
339 }
340 else if( state == k_ESteamNetworkingConnectionState_Connected ){
341 vg_strcat( str, "Connected to:\nskaterift.com" );
342 *colour = 0xff00a020;
343 }
344 else if( state == k_ESteamNetworkingConnectionState_ClosedByPeer )
345 vg_strcat( str, "Connection Closed" );
346 else if( state == k_ESteamNetworkingConnectionState_FindingRoute )
347 vg_strcat( str, "Finding Route" );
348 else if( state ==
349 k_ESteamNetworkingConnectionState_ProblemDetectedLocally){
350 vg_strcat( str, "Problem Detected\nLocally" );
351 *colour = 0xff0000a0;
352 }
353 else
354 vg_strcat( str, "???" );
355 }
356 }
357 else {
358 vg_strcat( str, "Steam Offline" );
359 *colour = 0xff0000a0;
360 }
361 }
362
363 void render_server_status_gui(void)
364 {
365 render_fb_bind( gpipeline.fb_workshop_preview, 0 );
366
367 /* HACK */
368 vg_ui.cur_vert = 0;
369 vg_ui.cur_indice = 0;
370 vg_ui.vert_start = 0;
371 vg_ui.indice_start = 0;
372
373 ui_rect r = { 0, 0, 128, 48 };
374
375 char buf[128];
376 vg_str str;
377 vg_strnull( &str, buf, sizeof(buf) );
378
379 u32 bg = 0xff000000;
380 network_status_string( &str, &bg );
381
382 ui_fill( r, bg );
383 ui_text( r, buf, 1, k_ui_align_center, 0 );
384 ui_flush( k_ui_shader_colour, 128, 48 );
385
386 skaterift.rt_textures[ k_skaterift_rt_server_status ] =
387 gpipeline.fb_workshop_preview->attachments[0].id;
388 }
389
390 static void on_server_connect_status( CallbackMsg_t *msg ){
391 SteamNetConnectionStatusChangedCallback_t *info = (void *)msg->m_pubParam;
392 vg_info( " Connection status changed for %lu\n", info->m_hConn );
393 vg_info( " %s -> %s\n",
394 string_ESteamNetworkingConnectionState(info->m_eOldState),
395 string_ESteamNetworkingConnectionState(info->m_info.m_eState) );
396
397 if( info->m_hConn == network_client.remote ){
398 network_client.state = info->m_info.m_eState;
399
400 if( info->m_info.m_eState ==
401 k_ESteamNetworkingConnectionState_Connected ){
402 vg_success(" Connected to remote server.. authenticating\n");
403
404 /* send version info to server */
405 netmsg_version version;
406 version.inetmsg_id = k_inetmsg_version;
407 version.version = NETWORK_SKATERIFT_VERSION;
408 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
409 hSteamNetworkingSockets, network_client.remote, &version,
410 sizeof(netmsg_version), k_nSteamNetworkingSend_Reliable, NULL );
411
412 /* TODO: We should really wait to see if the server is in auth mode
413 * first... */
414 u32 size = sizeof(netmsg_auth) + network_client.app_key_length;
415 netmsg_auth *auth = alloca(size);
416 auth->inetmsg_id = k_inetmsg_auth;
417 auth->ticket_length = network_client.app_key_length;
418 for( int i=0; i<network_client.app_key_length; i++ )
419 auth->ticket[i] = network_client.app_symmetric_key[i];
420
421 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
422 hSteamNetworkingSockets, network_client.remote, auth, size,
423 k_nSteamNetworkingSend_Reliable, NULL );
424 }
425 else if( info->m_info.m_eState ==
426 k_ESteamNetworkingConnectionState_ClosedByPeer ){
427
428 if( info->m_info.m_eEndReason ==
429 k_ESteamNetConnectionEnd_Misc_InternalError ){
430 network_client.retries = 40;
431 }
432 network_disconnect();
433 }
434 else if( info->m_info.m_eState ==
435 k_ESteamNetworkingConnectionState_ProblemDetectedLocally ){
436 network_disconnect();
437 }
438 }
439 else{
440 //vg_warn( " Recieved signal from unknown connection\n" );
441 }
442
443 render_server_status_gui();
444 }
445
446 static void on_persona_state_change( CallbackMsg_t *msg ){
447 if( !network_connected() )
448 return;
449
450 PersonaStateChange_t *info = (void *)msg->m_pubParam;
451 ISteamUser *hSteamUser = SteamAPI_SteamUser();
452
453 vg_info( "User: %llu, change: %u\n", info->m_ulSteamID,
454 info->m_nChangeFlags );
455
456 if( info->m_ulSteamID == SteamAPI_ISteamUser_GetSteamID(hSteamUser) ){
457 if( info->m_nChangeFlags & k_EPersonaChangeName ){
458 network_send_username();
459 }
460 }
461
462 if( info->m_nChangeFlags & k_EPersonaChangeRelationshipChanged ){
463 for( u32 i=0; i<NETWORK_MAX_PLAYERS; i ++ ){
464 struct network_player *rp = &netplayers.list[i];
465 if( rp->steamid == info->m_ulSteamID ){
466 player_remote_update_friendflags( rp );
467 }
468 }
469 }
470 }
471
472 static void network_connect(void){
473 char ip_buf[128];
474 vg_str str;
475 vg_strnull( &str, ip_buf, sizeof(ip_buf) );
476 vg_strcat( &str, network_client.server_adress );
477 vg_strcat( &str, ":" );
478 vg_strcati32( &str, NETWORK_PORT );
479
480 if( !vg_strgood(&str) ) return;
481
482 /* Connect to server if not connected */
483 SteamNetworkingIPAddr remoteAddr;
484 SteamAPI_SteamNetworkingIPAddr_ParseString( &remoteAddr, str.buffer );
485
486 char buf[256];
487 SteamAPI_SteamNetworkingIPAddr_ToString( &remoteAddr, buf, 256, 1 );
488 vg_info( "connect to: %s\n", buf );
489
490 network_client.remote = SteamAPI_ISteamNetworkingSockets_ConnectByIPAddress(
491 hSteamNetworkingSockets, &remoteAddr, 0, NULL );
492 }
493
494 static void network_sign_on_complete(void){
495 vg_success( "Sign on completed\n" );
496
497 /* send our init info */
498 network_send_username();
499 for( u32 i=0; i<k_netmsg_playeritem_max; i ++ ){
500 network_send_item(i);
501 }
502 network_send_region();
503 }
504
505 static void poll_remote_connection(void){
506 SteamNetworkingMessage_t *messages[32];
507 int len;
508
509 for( int i=0; i<10; i++ ){
510 len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnConnection(
511 hSteamNetworkingSockets, network_client.remote,
512 messages, vg_list_size(messages));
513
514 if( len <= 0 )
515 return;
516
517 for( int i=0; i<len; i++ ){
518 SteamNetworkingMessage_t *msg = messages[i];
519
520 if( msg->m_cbSize < sizeof(netmsg_blank) ){
521 vg_warn( "Discarding message (too small: %d)\n", msg->m_cbSize );
522 continue;
523 }
524
525 netmsg_blank *tmp = msg->m_pData;
526
527 if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) ){
528 player_remote_rx_200_300( msg );
529 }
530 else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) ){
531 network_request_rx_300_400( msg );
532 }
533 else {
534 if( tmp->inetmsg_id == k_inetmsg_version ){
535 netmsg_version *version = msg->m_pData;
536 if( version->version != NETWORK_SKATERIFT_VERSION ){
537 network_disconnect();
538 /* we dont want to connect to this server ever */
539 network_client.retries = 999;
540 network_client.last_attempt = 999999999.9;
541 vg_error( "version mismatch with server\n" );
542 }
543 else {
544 network_client.remote_version = version->version;
545 network_sign_on_complete();
546 }
547 }
548 }
549
550 SteamAPI_SteamNetworkingMessage_t_Release( msg );
551 }
552 }
553 }
554
555 void network_update(void)
556 {
557 if( !steam_ready )
558 return;
559
560 ESteamNetworkingConnectionState state = network_client.state;
561
562 if( network_client.user_intent == k_server_intent_offline ){
563 if( state != k_ESteamNetworkingConnectionState_None )
564 network_disconnect();
565
566 return;
567 }
568
569 if( state == k_ESteamNetworkingConnectionState_Connected ){
570 poll_remote_connection();
571 f64 frame_delta = vg.time_real - network_client.last_frame;
572
573 if( frame_delta > NETWORK_FRAMERATE ){
574 network_client.last_frame = vg.time_real;
575 remote_player_send_playerframe();
576 localplayer.sfx_buffer_count = 0;
577 }
578
579 remote_player_debug_update();
580 }
581 else {
582 if( (state == k_ESteamNetworkingConnectionState_Connecting) ||
583 (state == k_ESteamNetworkingConnectionState_FindingRoute) ){
584 return;
585 }
586 else {
587 f64 waited = vg.time_real - network_client.last_attempt,
588 min_wait = 1.0;
589
590 if( network_client.retries > 5 )
591 min_wait = 60.0;
592
593 if( waited < min_wait )
594 return;
595
596 network_connect();
597 network_client.retries ++;
598 network_client.last_attempt = vg.time_real;
599 }
600 }
601 }
602
603 void chat_send_message( const char *message )
604 {
605 if( !network_connected() ){
606 return;
607 }
608
609 netmsg_chat *chat = alloca( sizeof(netmsg_chat) + NETWORK_MAX_CHAT );
610 chat->inetmsg_id = k_inetmsg_chat;
611 chat->client = 0;
612
613 u32 l = vg_strncpy( message, chat->msg, NETWORK_MAX_CHAT,
614 k_strncpy_always_add_null );
615
616 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
617 hSteamNetworkingSockets, network_client.remote,
618 chat, sizeof(netmsg_chat)+l+1,
619 k_nSteamNetworkingSend_Reliable, NULL );
620 }
621
622 static int cmd_network_send_message( int argc, const char *argv[] ){
623 char buf[ NETWORK_MAX_CHAT ];
624 vg_str str;
625 vg_strnull( &str, buf, NETWORK_MAX_CHAT );
626
627 for( int i=0; i<argc; i ++ ){
628 vg_strcat( &str, argv[i] );
629
630 if( i < argc-1 )
631 vg_strcatch( &str, ' ' );
632 }
633
634 chat_send_message( buf );
635 return 0;
636 }
637
638 void network_init(void)
639 {
640 vg_console_reg_var( "network_info", &network_client.network_info,
641 k_var_dtype_i32, VG_VAR_PERSISTENT );
642 vg_console_reg_var( "auto_connect", &network_client.auto_connect,
643 k_var_dtype_i32, VG_VAR_PERSISTENT );
644 if( steam_ready ){
645 u32 alloc_size = sizeof(struct network_request)*NETWORK_MAX_REQUESTS;
646 network_client.request_buffer =
647 vg_linear_alloc( vg_mem.rtmemory, alloc_size );
648 memset( network_client.request_buffer, 0, alloc_size );
649
650 vg_pool *pool = &network_client.request_pool;
651 pool->buffer = network_client.request_buffer;
652 pool->count = NETWORK_MAX_REQUESTS;
653 pool->stride = sizeof( struct network_request );
654 pool->offset = offsetof( struct network_request, poolnode );
655 vg_pool_init( pool );
656
657 steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
658 on_server_connect_status );
659 steam_register_callback( k_iPersonaStateChange,
660 on_persona_state_change );
661 request_auth_ticket();
662
663 vg_console_reg_cmd( "say", cmd_network_send_message, NULL );
664 }
665 }
666
667 void network_end(void)
668 {
669 /* TODO: Send buffered highscores that were not already */
670 if( (network_client.state == k_ESteamNetworkingConnectionState_Connected) ||
671 (network_client.state == k_ESteamNetworkingConnectionState_Connecting) )
672 {
673 SteamAPI_ISteamNetworkingSockets_CloseConnection(
674 hSteamNetworkingSockets, network_client.remote, 0, NULL, 1 );
675 }
676 }