#include "vg/vg_console.h"
#include "gameserver_replay.h"
#include "gameserver_requests.h"
+#include "gameserver_monitor.h"
struct _gameserver _gameserver =
{
if( !client->steamid )
return;
+ _gameserver.bytes_send += cbData;
SteamAPI_ISteamNetworkingSockets_SendMessageToConnection( hSteamNetworkingSockets, client->connection,
pData, cbData, nSendFlags, NULL );
}
vg_success( "User is authenticated! steamid %lu (%u) [%s]\n", steamid.m_unAll64Bits, msg->m_conn,
client->admin? "Admin": "User" );
gameserver_player_join( client_id );
+ _gs_monitor_playerjoin();
}
/*
static void process_network_message( SteamNetworkingMessage_t *msg )
{
+ _gameserver.bytes_recv += msg->m_cbSize;
+
if( msg->m_cbSize < sizeof(netmsg_blank) )
{
vg_warn( "Discarding message (too small: %d)\n", msg->m_cbSize );
}
else
{
- vg_info( "Usage: edit_profile\n"
+ vg_info( "Usage: edit_user <steamid>\n"
" Options:\n"
" +FLAG (LEGEND,EARLY,ADMIN)\n"
" -FLAG\n"
if( vg_long_opt( "replay-info", "Print replay info periodically" ) )
_gs_replay.print_info = 1;
+ if( (arg = vg_long_opt_arg( "journal", "Journal important events into file" )) )
+ if( !_gs_monitor_start_journal( arg ) )
+ return 0;
+
+ if( (arg = vg_long_opt_arg( "status-html", "Record server status to HTML file" )) )
+ _gs_monitor_start_html( arg );
+
+ if( (arg = vg_long_opt_arg( "status-interval", "Inverval which HTML page gets written" )) )
+ _gs_monitor_set_interval( atof( arg ) );
+
if( !_vg_opt_check() )
return 0;
}
poll_connections();
_gs_replay_server_tick();
_gs_requests_tick();
+ _gs_monitor_tick();
usleep(10000);
_gameserver.ticks ++;
}
}
-EE:vg_info( "Program ends\n" );
+EE:vg_info( "Server end\n" );
SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets, _gameserver.client_group );
SteamAPI_ISteamNetworkingSockets_CloseListenSocket( hSteamNetworkingSockets, listener );
SteamGameServer_Shutdown();
E1:db_free();
-E0:return 0;
+E0:_gs_monitor_cleanup();
+ return 0;
}
#include "gameserver_replay.c"
#include "gameserver_requests.c"
#include "gameserver_database.c"
+#include "gameserver_monitor.c"
#include "vg/vg_async2.c"
#include "vg/vg_mem_pool.c"
#include "vg/vg_db.c"
--- /dev/null
+struct
+{
+ const char *html_path;
+ u64 interval, timer;
+ bool joiner_notify;
+
+ FILE *journal_fp;
+}
+_gs_monitor;
+
+struct task_monitor_write
+{
+ char buf[ 20000 ];
+};
+
+void _monitor_write_task( vg_async_task *task )
+{
+ THREAD_1;
+ struct task_monitor_write *info = (void *)task->data;
+
+ FILE *fp = fopen( _gs_monitor.html_path, "w" );
+ if( fp )
+ {
+ fputs( info->buf, fp );
+ fclose( fp );
+ vg_low( "Written monitor HTML (%s)\n", _gs_monitor.html_path );
+ }
+ else
+ vg_warn( "Failed to open '%s' (server monitor html path)\n" );
+}
+
+void _gs_monitor_tick(void)
+{
+ if( _gs_monitor.html_path )
+ {
+ _gs_monitor.timer ++;
+ if( _gs_monitor.timer >= _gs_monitor.interval )
+ {
+ if( _gs_monitor.interval < 10 )
+ _gs_monitor_set_interval( 30.0f );
+
+ vg_async_task *task = vg_allocate_async_task( &_gs_db.tasks, sizeof(struct task_monitor_write), 0 );
+ if( task )
+ {
+ _gs_monitor.timer = 0;
+ struct task_monitor_write *info = (void *)task->data;
+
+ vg_str str;
+ vg_strnull( &str, info->buf, sizeof(info->buf) );
+ vg_strcat( &str, "<html>\n"
+ " <head>\n"
+ " <meta http-equiv=\"refresh\" content=\"30\">\n"
+ " </head>\n"
+ " <body>\n"
+ " <p>Written: <span id=\"elapsed\">JavaScript Disabled.</span></p>\n"
+ " <p>Notifications: <span id=\"notif\">None.</span></p>\n"
+ " <table>\n"
+ " <tr>\n"
+ " <th>Item</th>\n"
+ " <th>Value</th>\n"
+ " </tr>\n" );
+
+ vg_strcat( &str, " <tr>\n"
+ " <td>Tick</td>\n"
+ " <td>" );
+ vg_strcatu64( &str, _gameserver.ticks );
+ vg_strcat( &str, "</td>\n"
+ " </tr>\n" );
+
+ vg_strcat( &str, " <tr>\n"
+ " <td>Global UID</td>\n"
+ " <td>" );
+ vg_strcatu64( &str, _gameserver.global_uid );
+ vg_strcat( &str, "</td>\n"
+ " </tr>\n" );
+
+ u32 online = 0, authenticated = 0, admins = 0;
+ for( u32 i=0; i< NETWORK_MAX_PLAYERS; i ++ )
+ {
+ if( _gameserver.clients[i].active )
+ online ++;
+
+ if( _gameserver.clients[i].steamid )
+ authenticated ++;
+
+ if( _gameserver.clients[i].admin )
+ admins ++;
+ }
+
+ static u32 peak = 0;
+ if( authenticated > peak )
+ peak = authenticated;
+
+ vg_strcat( &str, " <tr>\n"
+ " <td>Active Players</td>\n"
+ " <td>" );
+ vg_strcati32( &str, online );
+ vg_strcat( &str, "</td>\n"
+ " </tr>\n" );
+
+ vg_strcat( &str, " <tr>\n"
+ " <td>Peak Players</td>\n"
+ " <td>" );
+ vg_strcati32( &str, peak );
+ vg_strcat( &str, "</td>\n"
+ " </tr>\n" );
+
+ vg_strcat( &str, " <tr>\n"
+ " <td>Authenticated Players</td>"
+ " <td>" );
+ vg_strcati32( &str, authenticated );
+ vg_strcat( &str, "</td>\n"
+ " </tr>\n" );
+
+ vg_strcat( &str, " <tr>\n"
+ " <td>Admins</td>"
+ " <td>" );
+ vg_strcati32( &str, admins );
+ vg_strcat( &str, "</td>\n"
+ " </tr>\n" );
+
+ vg_strcat( &str, " <tr>\n"
+ " <td>Bytes send (channel 0)</td>"
+ " <td>" );
+ vg_strcatu64( &str, _gameserver.bytes_send );
+ vg_strcat( &str, "</td>\n"
+ " </tr>\n" );
+
+ vg_strcat( &str, " <tr>\n"
+ " <td>Bytes send (channel 1)</td>"
+ " <td>" );
+ vg_strcatu64( &str, _gameserver.bytes_send1 );
+ vg_strcat( &str, "</td>\n"
+ " </tr>\n" );
+
+ vg_strcat( &str, " <tr>\n"
+ " <td>Bytes recv (channel 0)</td>"
+ " <td>" );
+ vg_strcatu64( &str, _gameserver.bytes_recv );
+ vg_strcat( &str, "</td>\n"
+ " </tr>\n" );
+
+ vg_strcat( &str, " </table>\n"
+ " </body>\n"
+ " <script>\n"
+ " function rept(){\n"
+ " const now = new Date().getTime() / 1000;\n"
+ " var reference = " );
+ vg_strcatu64( &str, time(NULL) );
+ vg_strcat( &str, ";\n"
+ " var secs = Math.floor(now - reference);\n"
+ " document.getElementById('elapsed').textContent = secs.toString() + \" Seconds Ago\";\n"
+ " var audio = 0;"
+ " if( secs > 80.0 )\n"
+ " {\n"
+ " if( audio == 0 )\n"
+ " audio = new Audio('alarm.ogg');\n"
+ " audio.play();\n"
+ " document.getElementById('notif').textContent += \"NO UPDATE? \";\n"
+ " }\n"
+ " }\n"
+ " rept(); setInterval(rept,2000);\n" );
+
+ if( _gs_monitor.joiner_notify )
+ {
+ _gs_monitor.joiner_notify = 0;
+ vg_strcat( &str, " var audio = new Audio('joiner.ogg');\n"
+ " audio.play();\n"
+ " document.getElementById('notif').textContent += \"PLAYER JOIN! \";\n" );
+ }
+
+ vg_strcat( &str, " </script>\n"
+ "</html>\n" );
+
+ vg_async_task_dispatch( task, _monitor_write_task );
+ }
+ }
+ }
+}
+
+bool _gs_monitor_start_journal( const char *path )
+{
+ _gs_monitor.journal_fp = fopen( path, "a+" );
+
+ if( !_gs_monitor.journal_fp )
+ {
+ vg_error( "Failed to open journal for writing (%s)\n", path );
+ return 0;
+ }
+
+ return 1;
+}
+
+void _gs_monitor_start_html( const char *path )
+{
+ _gs_monitor.html_path = path;
+}
+
+void _gs_monitor_set_interval( f64 seconds )
+{
+ _gs_monitor.interval = seconds_to_server_ticks( seconds );
+ _gs_monitor.timer = 0;
+}
+
+void _gs_monitor_log_event( const char *event )
+{
+ /* TODO */
+}
+
+void _gs_monitor_playerjoin(void)
+{
+ _gs_monitor.joiner_notify = 1;
+}
+
+void _gs_monitor_cleanup(void)
+{
+ if( _gs_monitor.journal_fp )
+ {
+ fclose( _gs_monitor.journal_fp );
+ _gs_monitor.journal_fp = NULL;
+ }
+}