summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/game/g_local.h2
-rw-r--r--src/game/g_main.c124
-rw-r--r--src/game/g_public.h2
-rw-r--r--src/game/g_syscalls.asm97
-rw-r--r--src/game/g_syscalls.c6
-rw-r--r--src/master/Makefile17
-rw-r--r--src/master/common.h2
-rw-r--r--src/master/messages.c30
-rw-r--r--src/master/stats.c135
-rw-r--r--src/server/server.h1
-rw-r--r--src/server/sv_game.c4
-rw-r--r--src/server/sv_main.c31
12 files changed, 354 insertions, 97 deletions
diff --git a/src/game/g_local.h b/src/game/g_local.h
index 1648f116..82f294b0 100644
--- a/src/game/g_local.h
+++ b/src/game/g_local.h
@@ -1136,4 +1136,4 @@ int trap_DebugPolygonCreate(int color, int numPoints, vec3_t *points);
void trap_DebugPolygonDelete(int id);
void trap_SnapVector( float *v );
-
+void trap_SendGameStat( const char *data );
diff --git a/src/game/g_main.c b/src/game/g_main.c
index d3e5b13e..a72d0459 100644
--- a/src/game/g_main.c
+++ b/src/game/g_main.c
@@ -452,6 +452,8 @@ void G_InitGame( int levelTime, int randomSeed, int restart )
memset( &level, 0, sizeof( level ) );
level.time = levelTime;
level.startTime = levelTime;
+ level.alienStage2Time = level.alienStage3Time =
+ level.humanStage2Time = level.humanStage3Time = level.startTime;
level.snd_fry = G_SoundIndex( "sound/misc/fry.wav" ); // FIXME standing in lava / slime
@@ -1452,6 +1454,87 @@ void QDECL G_LogPrintf( const char *fmt, ... )
}
/*
+=================
+G_SendGameStat
+=================
+*/
+void G_SendGameStat( pTeam_t team )
+{
+ char map[ MAX_STRING_CHARS ];
+ char teamChar;
+ char data[ BIG_INFO_STRING ];
+ char entry[ MAX_STRING_CHARS ];
+ int i, dataLength, entryLength;
+ gclient_t *cl;
+
+ trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );
+
+ switch( team )
+ {
+ case PTE_ALIENS: teamChar = 'A'; break;
+ case PTE_HUMANS: teamChar = 'H'; break;
+ case PTE_NONE: teamChar = 'L'; break;
+ default: return;
+ }
+
+ Com_sprintf( data, BIG_INFO_STRING,
+ "%s T:%c A:%f H:%f M:%s D:%d AS:%d AS2T:%d AS3T:%d HS:%d HS2T:%d HS3T:%d CL:%d",
+ Q3_VERSION,
+ teamChar,
+ level.averageNumAlienClients,
+ level.averageNumHumanClients,
+ map,
+ level.time - level.startTime,
+ g_alienStage.integer,
+ level.alienStage2Time - level.startTime,
+ level.alienStage3Time - level.startTime,
+ g_humanStage.integer,
+ level.humanStage2Time - level.startTime,
+ level.humanStage3Time - level.startTime,
+ level.numConnectedClients );
+
+ dataLength = strlen( data );
+
+ for( i = 0; i < level.numConnectedClients; i++ )
+ {
+ int ping;
+
+ cl = &level.clients[ level.sortedClients[ i ] ];
+
+ if( cl->pers.connected == CON_CONNECTING )
+ ping = -1;
+ else
+ ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
+
+ switch( cl->ps.stats[ STAT_PTEAM ] )
+ {
+ case PTE_ALIENS: teamChar = 'A'; break;
+ case PTE_HUMANS: teamChar = 'H'; break;
+ case PTE_NONE: teamChar = 'S'; break;
+ default: return;
+ }
+
+ Com_sprintf( entry, MAX_STRING_CHARS,
+ " %s %c %d %d %d",
+ cl->pers.netname,
+ teamChar,
+ cl->ps.persistant[ PERS_SCORE ],
+ ping,
+ ( level.time - cl->pers.enterTime ) / 60000 );
+
+ entryLength = strlen( entry );
+
+ if( dataLength + entryLength > MAX_STRING_CHARS )
+ break;
+
+ Q_strncpyz( data + dataLength, entry, BIG_INFO_STRING );
+ dataLength += entryLength;
+ }
+
+ trap_SendGameStat( data );
+}
+
+/*
================
LogExit
@@ -1508,6 +1591,8 @@ void LogExit( const char *string )
ent->use( ent, ent, ent );
}
}
+
+ G_SendGameStat( level.lastWin );
}
@@ -1628,10 +1713,6 @@ can see the last frag.
*/
void CheckExitRules( void )
{
- char s[ MAX_STRING_CHARS ];
-
- trap_Cvar_VariableStringBuffer( "mapname", s, sizeof( s ) );
-
// if at the intermission, wait for all non-bots to
// signal ready, then go to next level
if( level.intermissiontime )
@@ -1655,23 +1736,13 @@ void CheckExitRules( void )
{
if( level.time - level.startTime >= g_timelimit.integer * 60000 )
{
- G_SendCommandFromServer( -1, "print \"Timelimit hit\n\"" );
-
- G_LogPrintf( "STATS T:L A:%f H:%f M:%s D:%d AS:%d AS2T:%d AS3T:%d HS:%d HS2T:%d HS3T:%d\n",
- level.averageNumAlienClients, level.averageNumHumanClients,
- s, level.time - level.startTime,
- g_alienStage.integer,
- level.alienStage2Time - level.startTime, level.alienStage3Time - level.startTime,
- g_humanStage.integer,
- level.humanStage2Time - level.startTime, level.humanStage3Time - level.startTime );
-
level.lastWin = PTE_NONE;
+ G_SendCommandFromServer( -1, "print \"Timelimit hit\n\"" );
LogExit( "Timelimit hit." );
return;
}
}
- //TA: end the game on these conditions
if( level.uncondHumanWin ||
( ( level.time > level.startTime + 1000 ) &&
( level.numAlienSpawns == 0 ) &&
@@ -1680,17 +1751,7 @@ void CheckExitRules( void )
//humans win
level.lastWin = PTE_HUMANS;
G_SendCommandFromServer( -1, "print \"Humans win\n\"");
-
- G_LogPrintf( "STATS T:H A:%f H:%f M:%s D:%d AS:%d AS2T:%d AS3T:%d HS:%d HS2T:%d HS3T:%d\n",
- level.averageNumAlienClients, level.averageNumHumanClients,
- s, level.time - level.startTime,
- g_alienStage.integer,
- level.alienStage2Time - level.startTime, level.alienStage3Time - level.startTime,
- g_humanStage.integer,
- level.humanStage2Time - level.startTime, level.humanStage3Time - level.startTime );
-
LogExit( "Humans win." );
- return;
}
else if( level.uncondAlienWin ||
( ( level.time > level.startTime + 1000 ) &&
@@ -1700,21 +1761,8 @@ void CheckExitRules( void )
//aliens win
level.lastWin = PTE_ALIENS;
G_SendCommandFromServer( -1, "print \"Aliens win\n\"");
-
- G_LogPrintf( "STATS T:A A:%f H:%f M:%s D:%d AS:%d AS2T:%d AS3T:%d HS:%d HS2T:%d HS3T:%d\n",
- level.averageNumAlienClients, level.averageNumHumanClients,
- s, level.time - level.startTime,
- g_alienStage.integer,
- level.alienStage2Time - level.startTime, level.alienStage3Time - level.startTime,
- g_humanStage.integer,
- level.humanStage2Time - level.startTime, level.humanStage3Time - level.startTime );
-
LogExit( "Aliens win." );
- return;
}
-
- if( level.numPlayingClients < 2 )
- return;
}
diff --git a/src/game/g_public.h b/src/game/g_public.h
index de955e30..ba7a6e69 100644
--- a/src/game/g_public.h
+++ b/src/game/g_public.h
@@ -224,6 +224,8 @@ typedef enum {
// 1.32
G_FS_SEEK,
+ G_SEND_GAMESTAT,
+
BOTLIB_SETUP = 200, // ( void );
BOTLIB_SHUTDOWN, // ( void );
BOTLIB_LIBVAR_SET,
diff --git a/src/game/g_syscalls.asm b/src/game/g_syscalls.asm
index 8035c349..fda100a0 100644
--- a/src/game/g_syscalls.asm
+++ b/src/game/g_syscalls.asm
@@ -1,44 +1,44 @@
code
-equ trap_Printf -1
-equ trap_Error -2
-equ trap_Milliseconds -3
-equ trap_Cvar_Register -4
-equ trap_Cvar_Update -5
-equ trap_Cvar_Set -6
-equ trap_Cvar_VariableIntegerValue -7
-equ trap_Cvar_VariableStringBuffer -8
-equ trap_Argc -9
-equ trap_Argv -10
-equ trap_FS_FOpenFile -11
-equ trap_FS_Read -12
-equ trap_FS_Write -13
-equ trap_FS_FCloseFile -14
-equ trap_SendConsoleCommand -15
-equ trap_LocateGameData -16
-equ trap_DropClient -17
-equ trap_SendServerCommand -18
-equ trap_SetConfigstring -19
-equ trap_GetConfigstring -20
-equ trap_GetUserinfo -21
-equ trap_SetUserinfo -22
-equ trap_GetServerinfo -23
-equ trap_SetBrushModel -24
-equ trap_Trace -25
-equ trap_PointContents -26
+equ trap_Printf -1
+equ trap_Error -2
+equ trap_Milliseconds -3
+equ trap_Cvar_Register -4
+equ trap_Cvar_Update -5
+equ trap_Cvar_Set -6
+equ trap_Cvar_VariableIntegerValue -7
+equ trap_Cvar_VariableStringBuffer -8
+equ trap_Argc -9
+equ trap_Argv -10
+equ trap_FS_FOpenFile -11
+equ trap_FS_Read -12
+equ trap_FS_Write -13
+equ trap_FS_FCloseFile -14
+equ trap_SendConsoleCommand -15
+equ trap_LocateGameData -16
+equ trap_DropClient -17
+equ trap_SendServerCommand -18
+equ trap_SetConfigstring -19
+equ trap_GetConfigstring -20
+equ trap_GetUserinfo -21
+equ trap_SetUserinfo -22
+equ trap_GetServerinfo -23
+equ trap_SetBrushModel -24
+equ trap_Trace -25
+equ trap_PointContents -26
equ trap_InPVS -27
-equ trap_InPVSIgnorePortals -28
-equ trap_AdjustAreaPortalState -29
-equ trap_AreasConnected -30
-equ trap_LinkEntity -31
-equ trap_UnlinkEntity -32
-equ trap_EntitiesInBox -33
-equ trap_EntityContact -34
-equ trap_BotAllocateClient -35
-equ trap_BotFreeClient -36
-equ trap_GetUsercmd -37
-equ trap_GetEntityToken -38
-equ trap_FS_GetFileList -39
+equ trap_InPVSIgnorePortals -28
+equ trap_AdjustAreaPortalState -29
+equ trap_AreasConnected -30
+equ trap_LinkEntity -31
+equ trap_UnlinkEntity -32
+equ trap_EntitiesInBox -33
+equ trap_EntityContact -34
+equ trap_BotAllocateClient -35
+equ trap_BotFreeClient -36
+equ trap_GetUsercmd -37
+equ trap_GetEntityToken -38
+equ trap_FS_GetFileList -39
equ trap_DebugPolygonCreate -40
equ trap_DebugPolygonDelete -41
equ trap_RealTime -42
@@ -46,16 +46,17 @@ equ trap_SnapVector -43
equ trap_TraceCapsule -44
equ trap_EntityContactCapsule -45
equ trap_FS_Seek -46
+equ trap_SendGameStat -47
-equ memset -101
-equ memcpy -102
-equ strncpy -103
-equ sin -104
-equ cos -105
-equ atan2 -106
-equ sqrt -107
+equ memset -101
+equ memcpy -102
+equ strncpy -103
+equ sin -104
+equ cos -105
+equ atan2 -106
+equ sqrt -107
equ floor -111
-equ ceil -112
-equ testPrintInt -113
-equ testPrintFloat -114
+equ ceil -112
+equ testPrintInt -113
+equ testPrintFloat -114
diff --git a/src/game/g_syscalls.c b/src/game/g_syscalls.c
index b1e55e59..4fe05c91 100644
--- a/src/game/g_syscalls.c
+++ b/src/game/g_syscalls.c
@@ -269,3 +269,9 @@ void trap_SnapVector( float *v )
syscall( G_SNAPVECTOR, v );
return;
}
+
+void trap_SendGameStat( const char *data )
+{
+ syscall( G_SEND_GAMESTAT, data );
+ return;
+}
diff --git a/src/master/Makefile b/src/master/Makefile
index df2deed4..dab5c08a 100644
--- a/src/master/Makefile
+++ b/src/master/Makefile
@@ -3,16 +3,16 @@ BD_RELEASE=release-$(PLATFORM)-$(ARCH)
ifeq ($(PLATFORM),mingw32)
BINEXT=.exe
- RELEASE_LDFLAGS=-lwsock32
- DEBUG_LDFLAGS=-lwsock32
- RM=del
- MKDIR=mkdir
+ RELEASE_LDFLAGS=-lwsock32
+ DEBUG_LDFLAGS=-lwsock32
+ RM=del
+ MKDIR=mkdir
else
BINEXT=
- RELEASE_LDFLAGS=
- DEBUG_LDFLAGS=
- RM=rm -f
- MKDIR=mkdir
+ RELEASE_LDFLAGS=-static -ltdb
+ DEBUG_LDFLAGS=-static -ltdb
+ RM=rm -f
+ MKDIR=mkdir
endif
CC=gcc
@@ -21,6 +21,7 @@ DEBUG_CFLAGS=-g
OBJECTS= \
$(BD)/master.o \
$(BD)/messages.o \
+ $(BD)/stats.o \
$(BD)/servers.o
release: makedirs
diff --git a/src/master/common.h b/src/master/common.h
index e38aa039..47c29a93 100644
--- a/src/master/common.h
+++ b/src/master/common.h
@@ -84,5 +84,7 @@ extern char peer_address [128];
// Print a message to screen, depending on its verbose level
int MsgPrint (msg_level_t msg_level, const char* format, ...);
+void RecordClientStat( const char *address, const char *version, const char *renderer );
+void RecordGameStat( const char *address, const char *dataText );
#endif // #ifndef _COMMON_H_
diff --git a/src/master/messages.c b/src/master/messages.c
index 665b9382..b30bc334 100644
--- a/src/master/messages.c
+++ b/src/master/messages.c
@@ -44,6 +44,9 @@
// "heartbeat Tremulous\n"
#define S2M_HEARTBEAT "heartbeat"
+// "gamestat <data>"
+#define S2M_GAMESTAT "gamestat"
+
// "getinfo A_Challenge"
#define M2S_GETINFO "getinfo"
@@ -408,6 +411,7 @@ static void HandleGetMotd( const char* msg, const struct sockaddr_in* addr )
const char *motd = ""; //FIXME
size_t packetind;
char *value;
+ char version[ 1024 ], renderer[ 1024 ];
MsgPrint( MSG_DEBUG, "%s ---> getmotd\n", peer_address );
@@ -424,17 +428,21 @@ static void HandleGetMotd( const char* msg, const struct sockaddr_in* addr )
value = SearchInfostring( msg, "renderer" );
if( value )
{
- //FIXME: create renderer stats
+ strncpy( renderer, value, 1024 );
MsgPrint( MSG_DEBUG, "%s is using renderer %s\n", peer_address, value );
}
value = SearchInfostring( msg, "version" );
if( value )
{
- //FIXME: create version stats
+ strncpy( version, value, 1024 );
MsgPrint( MSG_DEBUG, "%s is using version %s\n", peer_address, value );
}
+#ifndef _WIN32
+ RecordClientStat( peer_address, version, renderer );
+#endif
+
// Initialize the packet contents with the header
packetind = headersize;
memcpy( packet, packetheader, headersize );
@@ -461,6 +469,18 @@ static void HandleGetMotd( const char* msg, const struct sockaddr_in* addr )
sizeof( *addr ) );
}
+/*
+====================
+HandleGameStat
+====================
+*/
+static void HandleGameStat( const char* msg, const struct sockaddr_in* addr )
+{
+#ifndef _WIN32
+ RecordGameStat( peer_address, msg );
+#endif
+}
+
// ---------- Public functions ---------- //
/*
@@ -523,4 +543,10 @@ void HandleMessage (const char* msg, size_t length,
{
HandleGetMotd (msg + strlen (C2M_GETMOTD), address);
}
+
+ // If it's a game statistic
+ else if( !strncmp( S2M_GAMESTAT, msg, strlen ( S2M_GAMESTAT ) ) )
+ {
+ HandleGameStat( msg + strlen( S2M_GAMESTAT ), address );
+ }
}
diff --git a/src/master/stats.c b/src/master/stats.c
new file mode 100644
index 00000000..98ebb32e
--- /dev/null
+++ b/src/master/stats.c
@@ -0,0 +1,135 @@
+/*
+stats.c
+
+Statistics for tremmaster
+
+Copyright (C) 2006 Tim Angus
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _WIN32
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <tdb.h>
+
+#include "common.h"
+
+#define MAX_DATA_SIZE 1024
+#define CS_FILENAME "clientStats.tdb"
+
+/*
+====================
+RecordClientStat
+====================
+*/
+void RecordClientStat( const char *address, const char *version, const char *renderer )
+{
+ TDB_CONTEXT *tctx = NULL;
+ TDB_DATA key, data;
+ char ipText[ 22 ];
+ char dataText[ MAX_DATA_SIZE ] = { 0 };
+ char *p;
+ int i;
+
+ tctx = tdb_open( CS_FILENAME, 0, 0, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR );
+
+ if( !tctx )
+ {
+ MsgPrint( MSG_DEBUG, "Couldn't open %s\n", CS_FILENAME );
+ return;
+ }
+
+ strncpy( ipText, address, 22 );
+ if( ( p = strrchr( ipText, ':' ) ) ) // Remove port
+ *p = '\0';
+
+ key.dptr = ipText;
+ key.dsize = strlen( ipText );
+
+ strncat( dataText, "\"", MAX_DATA_SIZE );
+ strncat( dataText, version, MAX_DATA_SIZE );
+
+ // Remove last three tokens (the date)
+ for( i = 0; i < 3; i++ )
+ {
+ if( ( p = strrchr( dataText, ' ' ) ) )
+ *p = '\0';
+ }
+ strncat( dataText, "\"", MAX_DATA_SIZE );
+
+ strncat( dataText, " \"", MAX_DATA_SIZE );
+ strncat( dataText, renderer, MAX_DATA_SIZE );
+ strncat( dataText, "\"", MAX_DATA_SIZE );
+
+ data.dptr = dataText;
+ data.dsize = strlen( dataText );
+
+ if( tdb_store( tctx, key, data, 0 ) < 0 )
+ MsgPrint( MSG_DEBUG, "tdb_store failed\n" );
+
+ tdb_close( tctx );
+ MsgPrint( MSG_DEBUG, "Recorded client stat for %s\n", address );
+}
+
+#define GS_FILENAME "gameStats.tdb"
+
+/*
+====================
+RecordGameStat
+====================
+*/
+void RecordGameStat( const char *address, const char *dataText )
+{
+ TDB_CONTEXT *tctx = NULL;
+ TDB_DATA key, data;
+ char keyText[ MAX_DATA_SIZE ] = { 0 };
+ char *p;
+ time_t tm = time( NULL );
+
+ tctx = tdb_open( GS_FILENAME, 0, 0, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR );
+
+ if( !tctx )
+ {
+ MsgPrint( MSG_DEBUG, "Couldn't open %s\n", GS_FILENAME );
+ return;
+ }
+
+ strncpy( keyText, address, 22 );
+ if( ( p = strrchr( keyText, ':' ) ) ) // Remove port
+ *p = '\0';
+
+ strncat( keyText, " ", MAX_DATA_SIZE );
+ strncat( keyText, asctime( gmtime( &tm ) ), MAX_DATA_SIZE );
+
+ key.dptr = keyText;
+ key.dsize = strlen( keyText );
+
+ data.dptr = (char *)dataText;
+ data.dsize = strlen( dataText );
+
+ if( tdb_store( tctx, key, data, 0 ) < 0 )
+ MsgPrint( MSG_DEBUG, "tdb_store failed\n" );
+
+ tdb_close( tctx );
+ MsgPrint( MSG_NORMAL, "Recorded game stat from %s\n", address );
+}
+
+#endif
diff --git a/src/server/server.h b/src/server/server.h
index da8c8aae..45e199f7 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -261,6 +261,7 @@ void SV_RemoveOperatorCommands (void);
void SV_MasterHeartbeat (void);
void SV_MasterShutdown (void);
+void SV_MasterGameStat( const char *data );
diff --git a/src/server/sv_game.c b/src/server/sv_game.c
index a0ccc0f7..cefcddeb 100644
--- a/src/server/sv_game.c
+++ b/src/server/sv_game.c
@@ -446,6 +446,10 @@ long SV_GameSystemCalls( long *args ) {
Sys_SnapVector( VMA(1) );
return 0;
+ case G_SEND_GAMESTAT:
+ SV_MasterGameStat( VMA(1) );
+ return 0;
+
//====================================
case BOTLIB_SETUP:
diff --git a/src/server/sv_main.c b/src/server/sv_main.c
index 1d658a60..d017e7b1 100644
--- a/src/server/sv_main.c
+++ b/src/server/sv_main.c
@@ -293,6 +293,37 @@ void SV_MasterShutdown( void ) {
// it will be removed from the list
}
+/*
+=================
+SV_MasterGameStat
+=================
+*/
+void SV_MasterGameStat( const char *data )
+{
+ static netadr_t adr;
+
+ if( !com_dedicated || com_dedicated->integer != 2 )
+ return; // only dedicated servers send stats
+
+ Com_Printf( "Resolving %s\n", MASTER_SERVER_NAME );
+ if( !NET_StringToAdr( MASTER_SERVER_NAME, &adr ) )
+ {
+ Com_Printf( "Couldn't resolve address: %s\n", MASTER_SERVER_NAME );
+ return;
+ }
+ else
+ {
+ if( !strstr( ":", MASTER_SERVER_NAME ) )
+ adr.port = BigShort( PORT_MASTER );
+
+ Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", MASTER_SERVER_NAME,
+ adr.ip[0], adr.ip[1], adr.ip[2], adr.ip[3],
+ BigShort( adr.port ) );
+ }
+
+ Com_Printf ("Sending gamestat to %s\n", MASTER_SERVER_NAME );
+ NET_OutOfBandPrint( NS_SERVER, adr, "gamestat %s", data );
+}
/*
==============================================================================