2 * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
5 #define _DEFAULT_SOURCE
10 volatile sig_atomic_t sig_stop
;
12 #include "gameserver.h"
13 #include "highscores.c"
14 #include "servermonitor_server.c"
15 #include "vg/vg_opt.h"
16 #include "network_common.h"
17 #include "gameserver_db.h"
19 #include "vg/vg_msg.h"
21 static void inthandler( int signum
) {
25 static const u64 k_connection_unauthorized
= 0xffffffffffffffff;
27 static u64_steamid
get_connection_authsteamid( SteamNetworkingMessage_t
*msg
){
28 i64 userdata
= SteamAPI_ISteamNetworkingSockets_GetConnectionUserData(
29 hSteamNetworkingSockets
, msg
->m_conn
);
31 return *((u64_steamid
*)&userdata
);
34 static void set_connection_authsteamid(HSteamNetConnection con
, u64_steamid id
){
35 i64 userdata
= *((i64
*)&id
);
37 SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
38 hSteamNetworkingSockets
, con
, userdata
);
41 static void gameserver_send_to_all( int ignore
,
42 const void *pData
, u32 cbData
,
44 for( int i
=0; i
<vg_list_size(gameserver
.clients
); i
++ ){
45 struct gameserver_client
*client
= &gameserver
.clients
[i
];
47 if( (i
==ignore
) || !client
->active
)
50 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
51 hSteamNetworkingSockets
, client
->connection
,
52 pData
, cbData
, nSendFlags
, NULL
);
56 static void gameserver_player_join( int index
){
57 struct gameserver_client
*joiner
= &gameserver
.clients
[index
];
59 netmsg_playerjoin join
= { .inetmsg_id
= k_inetmsg_playerjoin
,
61 gameserver_send_to_all( index
, &join
, sizeof(join
),
62 k_nSteamNetworkingSend_Reliable
);
64 /* update the joining user about current connections */
66 netmsg_playerusername
*username
=
67 alloca( sizeof(netmsg_playerusername
) + NETWORK_USERNAME_MAX
);
68 username
->inetmsg_id
= k_inetmsg_playerusername
;
70 netmsg_playeritem
*item
=
71 alloca( sizeof(netmsg_playeritem
) + ADDON_UID_MAX
);
72 item
->inetmsg_id
= k_inetmsg_playeritem
;
74 for( int i
=0; i
<vg_list_size(gameserver
.clients
); i
++ ){
75 struct gameserver_client
*client
= &gameserver
.clients
[i
];
77 if( (i
==index
) || !client
->active
)
81 netmsg_playerjoin init
= { .inetmsg_id
= k_inetmsg_playerjoin
,
83 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
84 hSteamNetworkingSockets
, joiner
->connection
,
85 &init
, sizeof(init
), k_nSteamNetworkingSend_Reliable
, NULL
);
89 u32 chs
= vg_strncpy( client
->username
, username
->name
,
91 k_strncpy_always_add_null
);
92 u32 size
= sizeof(netmsg_playerusername
) + chs
+ 1;
93 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
94 hSteamNetworkingSockets
, joiner
->connection
,
95 username
, size
, k_nSteamNetworkingSend_Reliable
, NULL
);
98 for( int j
=0; j
<k_netmsg_playeritem_max
; j
++ ){
99 chs
= vg_strncpy( client
->items
[j
], item
->uid
, ADDON_UID_MAX
,
100 k_strncpy_always_add_null
);
101 item
->type_index
= j
;
103 size
= sizeof(netmsg_playeritem
) + chs
+ 1;
104 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
105 hSteamNetworkingSockets
, joiner
->connection
,
106 item
, size
, k_nSteamNetworkingSend_Reliable
, NULL
);
111 static void gameserver_player_leave( int index
){
112 netmsg_playerjoin leave
;
113 leave
.inetmsg_id
= k_inetmsg_playerleave
;
116 vg_info( "Player leave (%d)\n", index
);
117 gameserver_send_to_all( index
, &leave
, sizeof(leave
),
118 k_nSteamNetworkingSend_Reliable
);
121 static void new_client_connecting( HSteamNetConnection client
){
125 for( int i
=0; i
<vg_list_size(gameserver
.clients
); i
++ ){
126 if( !gameserver
.clients
[i
].active
){
133 vg_error( "Server full\n" );
134 SteamAPI_ISteamNetworkingSockets_CloseConnection(
135 hSteamNetworkingSockets
, client
,
141 EResult accept_status
= SteamAPI_ISteamNetworkingSockets_AcceptConnection(
142 hSteamNetworkingSockets
, client
);
143 if( accept_status
== k_EResultOK
){
144 vg_success( "Accepted client (id: %u, index: %d)\n", client
, index
);
145 memset( &gameserver
.clients
[index
], 0, sizeof(struct gameserver_client
) );
147 gameserver
.clients
[index
].active
= 1;
148 gameserver
.clients
[index
].connection
= client
;
150 SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
151 hSteamNetworkingSockets
,
152 client
, gameserver
.client_group
);
154 /* Just to be sure */
155 set_connection_authsteamid( client
, -1 );
156 gameserver_player_join( index
);
159 vg_warn( "Error accepting client (id: %u)\n", client
);
160 SteamAPI_ISteamNetworkingSockets_CloseConnection(
161 hSteamNetworkingSockets
, client
,
162 k_ESteamNetConnectionEnd_Misc_InternalError
,
164 gameserver
.clients
[index
].active
= 0;
165 gameserver
.clients
[index
].connection
= 0;
169 static void on_auth_status( CallbackMsg_t
*msg
){
170 SteamNetAuthenticationStatus_t
*info
= (void *)msg
->m_pubParam
;
171 vg_info( " Authentication availibility: %s\n",
172 string_ESteamNetworkingAvailability(info
->m_eAvail
) );
173 vg_info( " %s\n", info
->m_debugMsg
);
176 static int gameserver_client_index( HSteamNetConnection hconn
){
177 for( int i
=0; i
<vg_list_size(gameserver
.clients
); i
++ ){
178 struct gameserver_client
*client
= &gameserver
.clients
[i
];
180 if( client
->active
){
181 if( client
->connection
== hconn
){
189 static void on_connect_status( CallbackMsg_t
*msg
){
190 SteamNetConnectionStatusChangedCallback_t
*info
= (void *)msg
->m_pubParam
;
191 vg_info( " Connection status changed for %lu\n", info
->m_hConn
);
193 vg_info( " %s -> %s\n",
194 string_ESteamNetworkingConnectionState(info
->m_eOldState
),
195 string_ESteamNetworkingConnectionState(info
->m_info
.m_eState
) );
197 if( info
->m_info
.m_eState
==k_ESteamNetworkingConnectionState_Connecting
){
198 new_client_connecting( info
->m_hConn
);
201 if( (info
->m_info
.m_eState
==
202 k_ESteamNetworkingConnectionState_ClosedByPeer
) ||
203 (info
->m_info
.m_eState
==
204 k_ESteamNetworkingConnectionState_ProblemDetectedLocally
) ){
206 int client_id
= gameserver_client_index( info
->m_hConn
);
207 if( client_id
!= -1 ){
208 struct gameserver_client
*client
= &gameserver
.clients
[client_id
];
209 client
->connection
= 0;
211 gameserver_player_leave(client_id
);
214 vg_info( "End reason: %d\n", info
->m_info
.m_eEndReason
);
215 SteamAPI_ISteamNetworkingSockets_CloseConnection(
216 hSteamNetworkingSockets
, info
->m_hConn
, 0, NULL
, 0 );
220 static void gameserver_rx_auth( SteamNetworkingMessage_t
*msg
){
221 if( gameserver
.auth_mode
!= eServerModeAuthentication
){
222 vg_error( "Running server without authentication. "
223 "Connection %u tried to authenticate.\n", msg
->m_conn
);
227 if( get_connection_authsteamid( msg
) != k_connection_unauthorized
){
228 vg_warn( "Already authorized this user but app ticket was sent"
229 " again (%u)\n", msg
->m_conn
);
233 vg_low( "Attempting to verify user\n" );
235 if( msg
->m_cbSize
< sizeof(netmsg_auth
) ){
236 vg_error( "Malformed auth ticket, too small (%u)\n", msg
->m_conn
);
240 netmsg_auth
*auth
= msg
->m_pData
;
242 if( msg
->m_cbSize
< sizeof(netmsg_auth
)+auth
->ticket_length
||
243 auth
->ticket_length
> 1024 ){
244 vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n",
245 auth
->ticket_length
);
250 u32 ticket_len
= 1024;
252 int success
= SteamEncryptedAppTicket_BDecryptTicket(
253 auth
->ticket
, auth
->ticket_length
, decrypted
,
254 &ticket_len
, gameserver
.app_symmetric_key
,
255 k_nSteamEncryptedAppTicketSymmetricKeyLen
);
258 vg_error( "Failed to decrypt users ticket (client %u)\n", msg
->m_conn
);
259 vg_error( " ticket length: %u\n", auth
->ticket_length
);
261 SteamAPI_ISteamNetworkingSockets_CloseConnection(
262 hSteamNetworkingSockets
,
263 msg
->m_conn
, 0, NULL
, 1 );
267 if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted
, ticket_len
)){
268 RTime32 ctime
= time(NULL
),
269 tickettime
= SteamEncryptedAppTicket_GetTicketIssueTime(
270 decrypted
, ticket_len
),
271 expiretime
= tickettime
+ 24*3*60*60;
273 if( ctime
> expiretime
){
274 vg_error( "Ticket expired (client %u)\n", msg
->m_conn
);
276 /* TODO: Send expired information */
277 SteamAPI_ISteamNetworkingSockets_CloseConnection(
278 hSteamNetworkingSockets
,
279 msg
->m_conn
, 0, NULL
, 1 );
285 SteamEncryptedAppTicket_GetTicketSteamID( decrypted
, ticket_len
, &steamid
);
286 vg_success( "User is authenticated! steamid %lu (%u)\n",
287 steamid
.m_unAll64Bits
, msg
->m_conn
);
289 set_connection_authsteamid( msg
->m_conn
, steamid
.m_unAll64Bits
);
292 static int inet_require_auth( SteamNetworkingMessage_t
*msg
){
293 if( gameserver
.auth_mode
== eServerModeNoAuthentication
)
296 if( get_connection_authsteamid( msg
) == k_connection_unauthorized
){
297 vg_warn( "Unauthorized request! Disconnecting client: %u\n",
300 SteamAPI_ISteamNetworkingSockets_CloseConnection(
301 hSteamNetworkingSockets
,
302 msg
->m_conn
, 0, NULL
, 1 );
310 * Player updates sent to us
311 * -----------------------------------------------------------------------------
314 static int packet_minsize( SteamNetworkingMessage_t
*msg
, u32 size
){
315 if( msg
->m_cbSize
< size
) {
316 vg_error( "Invalid packet size (must be at least %u)\n", size
);
324 static void gameserver_rx_200_300( SteamNetworkingMessage_t
*msg
){
325 netmsg_blank
*tmp
= msg
->m_pData
;
327 int client_id
= gameserver_client_index( msg
->m_conn
);
328 if( client_id
== -1 ) return;
330 if( tmp
->inetmsg_id
== k_inetmsg_playerusername
){
331 if( !packet_minsize( msg
, sizeof(netmsg_playerusername
)+1 ))
334 struct gameserver_client
*client
= &gameserver
.clients
[ client_id
];
335 netmsg_playerusername
*src
= msg
->m_pData
;
337 u32 name_len
= network_msgstring( src
->name
, msg
->m_cbSize
,
338 sizeof(netmsg_playerusername
),
340 NETWORK_USERNAME_MAX
);
342 /* update other users about this change */
343 netmsg_playerusername
*prop
= alloca(sizeof(netmsg_playerusername
)+
344 NETWORK_USERNAME_MAX
);
346 prop
->inetmsg_id
= k_inetmsg_playerusername
;
347 prop
->index
= client_id
;
348 u32 chs
= vg_strncpy( client
->username
, prop
->name
, NETWORK_USERNAME_MAX
,
349 k_strncpy_always_add_null
);
351 vg_info( "client #%d changed name to: %s\n", client_id
, prop
->name
);
353 u32 propsize
= sizeof(netmsg_playerusername
) + chs
+ 1;
354 gameserver_send_to_all( client_id
, prop
, propsize
,
355 k_nSteamNetworkingSend_Reliable
);
357 else if( tmp
->inetmsg_id
== k_inetmsg_playerframe
){
359 netmsg_playerframe
*frame
= alloca(msg
->m_cbSize
);
360 memcpy( frame
, msg
->m_pData
, msg
->m_cbSize
);
361 frame
->client
= client_id
;
362 gameserver_send_to_all( client_id
, frame
, msg
->m_cbSize
,
363 k_nSteamNetworkingSend_Unreliable
);
365 else if( tmp
->inetmsg_id
== k_inetmsg_playeritem
){
366 netmsg_playeritem
*item
= msg
->m_pData
;
369 struct gameserver_client
*client
= &gameserver
.clients
[ client_id
];
371 if( item
->type_index
>= k_netmsg_playeritem_max
){
372 vg_warn( "Client #%d invalid equip type %u\n",
373 client_id
, (u32
)item
->type_index
);
377 char *dest
= client
->items
[ item
->type_index
];
379 network_msgstring( item
->uid
, msg
->m_cbSize
, sizeof(netmsg_playeritem
),
380 dest
, ADDON_UID_MAX
);
382 vg_info( "Client #%d equiped: [%s] %s\n",
384 (const char *[]){[k_netmsg_playeritem_board
]="board",
385 [k_netmsg_playeritem_player
]="player",
386 [k_netmsg_playeritem_world0
]="world0",
387 [k_netmsg_playeritem_world1
]="world1"
388 }[item
->type_index
], item
->uid
);
391 netmsg_playeritem
*prop
= alloca(msg
->m_cbSize
);
392 memcpy( prop
, msg
->m_pData
, msg
->m_cbSize
);
393 prop
->client
= client_id
;
394 gameserver_send_to_all( client_id
, prop
, msg
->m_cbSize
,
395 k_nSteamNetworkingSend_Reliable
);
398 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
403 static void gameserver_request_respond( enum request_status status
,
404 netmsg_request
*res
, vg_msg
*body
,
405 SteamNetworkingMessage_t
*msg
){
406 int client_id
= gameserver_client_index( msg
->m_conn
);
408 if( status
== k_request_status_ok
){
410 vg_success( "[%d#%d] Response: %d\n", client_id
, (i32
)res
->id
, status
);
411 vg_msg_print( body
, len
);
414 vg_warn( "[%d#%d] Response: %d\n", client_id
, (i32
)res
->id
, status
);
417 res
->status
= status
;
419 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
420 hSteamNetworkingSockets
, msg
->m_conn
,
421 res
, sizeof(netmsg_request
) + len
,
422 k_nSteamNetworkingSend_Reliable
, NULL
);
423 SteamAPI_SteamNetworkingMessage_t_Release( msg
);
426 struct user_request_thread_data
{
427 SteamNetworkingMessage_t
*msg
;
430 static void gameserver_process_user_request( db_request
*db_req
){
431 struct user_request_thread_data
*inf
= (void *)db_req
->data
;
432 SteamNetworkingMessage_t
*msg
= inf
->msg
;
434 int client_id
= gameserver_client_index( msg
->m_conn
);
435 if( client_id
== -1 ){
436 SteamAPI_SteamNetworkingMessage_t_Release( msg
);
440 netmsg_request
*req
= (netmsg_request
*)msg
->m_pData
;
442 vg_msg_init( &data
, req
->q
, msg
->m_cbSize
- sizeof(netmsg_request
) );
444 /* create response packet */
445 netmsg_request
*res
= alloca( sizeof(netmsg_request
) + 512 );
446 res
->inetmsg_id
= k_inetmsg_response
;
449 vg_msg_init( &body
, res
->q
, 512 );
451 const char *endpoint
= vg_msg_getkvstr( &data
, "endpoint" );
454 gameserver_request_respond( k_request_status_invalid_endpoint
,
459 if( !strcmp( endpoint
, "scoreboard" ) ){
460 const char *mod
= vg_msg_getkvstr( &data
, "mod" );
461 const char *route
= vg_msg_getkvstr( &data
, "route" );
462 u32 week
= vg_msg_getkvu32( &data
, "week", 0 );
464 char table_name
[ DB_TABLE_UID_MAX
];
465 if( !db_get_highscore_table_name( mod
, route
, week
, table_name
) ){
466 gameserver_request_respond( k_request_status_out_of_memory
,
473 vg_strnull( &q
, buf
, 512 );
474 vg_strcat( &q
, "SELECT * FROM \"" );
475 vg_strcat( &q
, table_name
);
476 vg_strcat( &q
, "\" ORDER BY time DESC LIMIT 10;" );
477 if( !vg_strgood(&q
) ) {
478 gameserver_request_respond( k_request_status_out_of_memory
,
483 sqlite3_stmt
*stmt
= db_stmt( q
.buffer
);
486 gameserver_request_respond( k_request_status_database_error
,
491 vg_msg_frame( &body
, "rows" );
492 for( u32 i
=0; i
<10; i
++ ){
493 int fc
= sqlite3_step( stmt
);
495 if( fc
== SQLITE_ROW
){
496 i32 time
= sqlite3_column_int( stmt
, 1 );
497 i64 steamid_i64
= sqlite3_column_int64( stmt
, 0 );
498 u64 steamid
= *((u64
*)&steamid_i64
);
500 vg_msg_frame( &body
, "" );
501 vg_msg_wkvu32( &body
, "time", time
);
502 vg_msg_wkvu64( &body
, "steamid", steamid
);
505 if( db_getuserinfo( steamid
, username
, sizeof(username
), NULL
) ){
506 vg_msg_wkvstr( &body
, "username", username
);
509 vg_msg_end_frame( &body
);
511 else if( fc
== SQLITE_DONE
){
520 sqlite3_finalize( stmt
);
521 vg_msg_end_frame( &body
);
523 if( body
.error
!= k_vg_msg_error_OK
){
524 gameserver_request_respond( k_request_status_out_of_memory
,
529 gameserver_request_respond( k_request_status_ok
, res
, &body
, msg
);
532 gameserver_request_respond( k_request_status_invalid_endpoint
,
537 static void gameserver_rx_300_400( SteamNetworkingMessage_t
*msg
){
538 netmsg_blank
*tmp
= msg
->m_pData
;
540 int client_id
= gameserver_client_index( msg
->m_conn
);
541 if( client_id
== -1 ){
542 SteamAPI_SteamNetworkingMessage_t_Release( msg
);
546 if( tmp
->inetmsg_id
== k_inetmsg_request
){
547 if( !packet_minsize( msg
, sizeof(netmsg_request
)+1 ))
550 db_request
*call
= db_alloc_request(
551 sizeof(struct user_request_thread_data
) );
552 struct user_request_thread_data
*inf
= (void *)call
->data
;
554 call
->handler
= gameserver_process_user_request
;
555 db_send_request( call
);
558 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
560 SteamAPI_SteamNetworkingMessage_t_Release( msg
);
564 static void poll_connections(void){
565 SteamNetworkingMessage_t
*messages
[32];
569 len
= SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
570 hSteamNetworkingSockets
,
571 gameserver
.client_group
, messages
, vg_list_size(messages
) );
576 for( int i
=0; i
<len
; i
++ ){
577 SteamNetworkingMessage_t
*msg
= messages
[i
];
579 if( msg
->m_cbSize
< sizeof(netmsg_blank
) ){
580 vg_warn( "Discarding message (too small: %d)\n",
585 netmsg_blank
*tmp
= msg
->m_pData
;
587 if( (tmp
->inetmsg_id
>= 200) && (tmp
->inetmsg_id
< 300) ){
588 gameserver_rx_200_300( msg
);
589 SteamAPI_SteamNetworkingMessage_t_Release( msg
);
591 else if( (tmp
->inetmsg_id
>= 300) && (tmp
->inetmsg_id
< 400) ){
592 gameserver_rx_300_400( msg
);
595 if( tmp
->inetmsg_id
== k_inetmsg_auth
)
596 gameserver_rx_auth( msg
);
598 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
601 SteamAPI_SteamNetworkingMessage_t_Release( msg
);
607 static u64
seconds_to_server_ticks( double s
){
611 static void test_runner( db_request
*req
){
612 vg_warn( "RUNNER\n" );
613 char table
[DB_TABLE_UID_MAX
];
614 if( db_get_highscore_table_name( "sr002-local-mp_mtzero",
615 "Coastal Run", 0, table
) ){
616 if( db_writeusertime( table
, 76561198072130043, 232, 1 ) ){
617 vg_success( "Written time\n" );
618 i32 v
= db_readusertime( table
, 76561198072130043 );
619 vg_success( "Returned time: %u\n", v
);
624 int main( int argc
, char *argv
[] ){
625 signal( SIGINT
, inthandler
);
626 signal( SIGQUIT
, inthandler
);
627 signal( SIGPIPE
, SIG_IGN
);
630 while( vg_argp( argc
, argv
) ){
631 if( vg_long_opt( "noauth" ) )
632 gameserver
.auth_mode
= eServerModeNoAuthentication
;
634 /* TODO: Options to override, ammend, remove etc */
637 vg_set_mem_quota( 80*1024*1024 );
641 db_request
*req
= db_alloc_request(0);
643 req
->handler
= test_runner
;
644 db_send_request(req
);
647 monitor_start_server(); /* UNIX socket monitor */
650 * --------------------------------------------------------------- */
651 steamworks_ensure_txt( "2103940" );
652 if( gameserver
.auth_mode
== eServerModeAuthentication
){
653 if( !vg_load_steam_symetric_key( "application_key",
654 gameserver
.app_symmetric_key
)){
659 vg_warn( "Running without user authentication.\n" );
662 if( !SteamGameServer_Init( 0, 27400, 27401,
663 gameserver
.auth_mode
, "1.0.0.0" ) ){
664 vg_error( "SteamGameServer_Init failed\n" );
668 void *hSteamGameServer
= SteamAPI_SteamGameServer();
669 SteamAPI_ISteamGameServer_LogOnAnonymous( hSteamGameServer
);
671 SteamAPI_ManualDispatch_Init();
672 HSteamPipe hsteampipe
= SteamGameServer_GetHSteamPipe();
673 hSteamNetworkingSockets
=
674 SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
676 steam_register_callback( k_iSteamNetAuthenticationStatus
, on_auth_status
);
677 steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack
,
680 vg_success( "Steamworks API running\n" );
681 steamworks_event_loop( hsteampipe
);
686 HSteamListenSocket listener
;
687 SteamNetworkingIPAddr localAddr
;
688 SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr
);
689 localAddr
.m_port
= 27402;
691 listener
= SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
692 hSteamNetworkingSockets
, &localAddr
, 0, NULL
);
693 gameserver
.client_group
= SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
694 hSteamNetworkingSockets
);
696 u64 server_ticks
= 8000,
697 last_record_save
= 8000,
698 last_scoreboard_gen
= 0,
699 last_monitor_heartbeat
= 0;
702 monitor_event_loop();
703 steamworks_event_loop( hsteampipe
);
710 (last_monitor_heartbeat
+ seconds_to_server_ticks(10.0))){
711 last_monitor_heartbeat
= server_ticks
;
719 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets
,
720 gameserver
.client_group
);
721 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
722 hSteamNetworkingSockets
, listener
);
724 vg_info( "Shutting down\n..." );
725 SteamGameServer_Shutdown();