diff -r 3e26f5eca137 src/CMakeLists.txt --- a/src/CMakeLists.txt Sun Oct 05 21:47:04 2014 +0200 +++ b/src/CMakeLists.txt Wed Oct 08 11:31:35 2014 +0200 @@ -1026,7 +1026,7 @@ if( CMAKE_COMPILER_IS_GNUCXX ) # GCC misoptimizes this file - set_source_files_properties( oplsynth/fmopl.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-dominator-opts -fno-tree-fre" ) +# set_source_files_properties( oplsynth/fmopl.cpp PROPERTIES COMPILE_FLAGS "-fno-tree-dominator-opts -fno-tree-fre" ) # Compile this one file with SSE2 support. set_source_files_properties( nodebuild_classify_sse2.cpp PROPERTIES COMPILE_FLAGS "-msse2 -mfpmath=sse" ) diff -r 3e26f5eca137 src/astar.cpp --- a/src/astar.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/astar.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -204,6 +204,7 @@ { ULONG ulIdx; + Printf("Clearing bot nodes...\n"); delete[] g_aMasterNodeList; g_aMasterNodeList = NULL; diff -r 3e26f5eca137 src/bots.cpp --- a/src/bots.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/bots.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -89,6 +89,7 @@ #include "p_trace.h" #include "sbar.h" #include "doomerrors.h" +#include "duel.h" //***************************************************************************** // VARIABLES @@ -683,6 +684,10 @@ // Refresh the HUD since the number of players in the game is potentially changing. SCOREBOARD_RefreshHUD( ); + + // [K6] If there are no more bots left, clear the bot nodes. + if (BOTS_CountBots( ) == 0 && ASTAR_IsInitialized( )) + ASTAR_ClearNodes( ); } //***************************************************************************** @@ -998,6 +1003,29 @@ } //***************************************************************************** +// +// [K6] Who created this function?! +void BOTS_RemoveTempBots( bool bAdd ) +{ + if ( sv_minplayers == 0 ) + return; + + if ( sv_minplayers > 2 && sv_removetempbots && ( bAdd ? SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) + 1 >= 2 : SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) >= 2 )) + { + C_DoCommand ( "removebots" ); + } + else if ( bAdd ? sv_minplayers < static_cast( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) + 1 : sv_minplayers < static_cast( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS ))) + { + C_DoCommand ( "removebot" ); + } + else + return; + + // [K6] If a bot was just removed from duel to be replaced by a player, set up a new duel. + if ( duel ) + DUEL_SetState( DS_WAITINGFORPLAYERS ); +} + //***************************************************************************** // void bots_ParseBotInfoLump( FScanner &sc ) @@ -1609,6 +1637,9 @@ { ULONG ulIdx; + if ( ASTAR_IsInitialized( ) == false ) + ASTAR_BuildNodes( ); + g_bBotIsInitialized[ulPlayerNum] = false; // First, initialize all variables. @@ -3852,16 +3883,9 @@ return; } - if ( ASTAR_IsInitialized( ) == false ) + if ( level.flags2 & LEVEL2_NOBOTNODES ) { - if ( sv_disallowbots ) - Printf( "The bot pathing nodes have not been set up. Please set \"sv_disallowbots\" to \"false\" if you wish to use bots.\n" ); - else if ( level.flags2 & LEVEL2_NOBOTNODES ) - Printf( "The bot pathing nodes have not been set up. This level has disabled the ability to do so.\n" ); - else - Printf( "The bot pathing nodes have not been set up. Please reload the level if you wish to use bots.\n" ); - - // Don't allow the bot to spawn. + Printf( "The bot pathing nodes have not been set up. This level has disabled the ability to do so.\n" ); return; } diff -r 3e26f5eca137 src/bots.h --- a/src/bots.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/bots.h Wed Oct 08 11:31:35 2014 +0200 @@ -644,6 +644,7 @@ void BOTS_PostWeaponFiredEvent( ULONG ulPlayer, BOTEVENT_e EventIfSelf, BOTEVENT_e EventIfEnemy, BOTEVENT_e EventIfPlayer ); void BOTS_RemoveGoal( AActor* pGoal ); ULONG BOTS_CountBots( void ); +void BOTS_RemoveTempBots( bool bAdd = false ); // Botinfo access functions. ULONG BOTINFO_GetNumBotInfos( void ); @@ -704,5 +705,7 @@ EXTERN_CVAR( Float, botdebug_maxgiveupnodes ) EXTERN_CVAR( Float, botdebug_maxroamgiveupnodes ) EXTERN_CVAR( Int, botdebug_shownodes ) +EXTERN_CVAR (Int, sv_minplayers) +EXTERN_CVAR (Bool, sv_removetempbots) #endif // __BOTS_H__ diff -r 3e26f5eca137 src/c_cmds.cpp --- a/src/c_cmds.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/c_cmds.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -164,6 +164,10 @@ if ( ACS_IsCalledFromConsoleCommand( )) return; + // [K6] Graceful shutdown, remove the map crash info file. + if ( NETWORK_GetState( ) == NETSTATE_SERVER ) + remove( GAMENAMELOWERCASE"-server.map" ); + if (!insave) exit (0); } @@ -173,6 +177,10 @@ if ( ACS_IsCalledFromConsoleCommand( )) return; + // [K6] Graceful shutdown, remove the map crash info file. + if ( NETWORK_GetState( ) == NETSTATE_SERVER ) + remove( GAMENAMELOWERCASE"-server.map" ); + if (!insave) exit (0); } diff -r 3e26f5eca137 src/c_console.cpp --- a/src/c_console.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/c_console.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -941,10 +941,8 @@ char *outlinecopy = new char[strlen(outline)+1]; strcpy (outlinecopy,outline); - if (Logfile) - { - // Strip out any color escape sequences before writing to the log file - char * copy = new char[strlen(outlinecopy)+1]; + // Strip out any color escape sequences before writing to the log file + char * copy = new char[strlen(outlinecopy)+1]; /* [BB] ST handles color codes a little differently (not all of them are converted to TEXTCOLOR_ESCAPE yet), so we strip them differently. const char * srcp = outlinecopy; char * dstp = copy; @@ -963,10 +961,12 @@ } *dstp=0; */ - strcpy (copy,outlinecopy); - V_ColorizeString( copy ); - V_RemoveColorCodes( copy ); + strcpy (copy,outlinecopy); + V_ColorizeString( copy ); + V_RemoveColorCodes( copy ); + if (Logfile) + { if( sv_logfiletimestamp ) { // [BB] Generate time string "[YY:MM:DD;HH:MM:SS] " or "[HH:MM:SS] " and write it to the logfile. @@ -983,7 +983,6 @@ } fputs (copy, Logfile); - delete [] copy; //#ifdef _DEBUG fflush (Logfile); //#endif @@ -995,7 +994,12 @@ if ( printlevel != PRINT_LOW ) { // [RC] Send this to any connected RCON clients. - SERVER_RCON_Printf( outlinecopy ); + // [K6] Include color codes only in case this is a message for luk. + if ( strstr(outlinecopy, "\bluk") ) + SERVER_RCON_Printf( outlinecopy ); + else + SERVER_RCON_Printf( copy ); + if ( g_ulRCONPlayer != MAXPLAYERS ) SERVER_PrintfPlayer( printlevel, g_ulRCONPlayer, outlinecopy ); @@ -1004,6 +1008,7 @@ const int length = static_cast(strlen (outlinecopy)); delete [] outlinecopy; + delete [] copy; return length; } @@ -1034,6 +1039,7 @@ } const int length = static_cast(strlen (outlinecopy)); delete [] outlinecopy; + delete [] copy; return length; } diff -r 3e26f5eca137 src/callvote.cpp --- a/src/callvote.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/callvote.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -666,6 +666,10 @@ if ( sv_limitnumvotes == false ) return true; + // [K6] No point in restricting how much a lone player can vote. + if ( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) == 1 && !PLAYER_IsTrueSpectator( &players[ulPlayer] )) + return true; + // Run through the vote cache (backwards, from recent to old) and search for grounds on which to reject the vote. for( std::list::reverse_iterator i = g_PreviousVotes.rbegin(); i != g_PreviousVotes.rend(); ++i ) { diff -r 3e26f5eca137 src/d_main.cpp --- a/src/d_main.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/d_main.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -2578,18 +2578,50 @@ G_NewInit( ); // Check if we have map rotation setup. If we do, use the first map there. + // [K6] If a file with last played map exists, start from that map. if (( sv_maprotation ) && ( MAPROTATION_GetNumEntries( ) > 0 )) { // [BB] G_InitNew seems to alter the contents of the first argument, which it shouldn't. // This causes the "Frags" bug. The following is just a workaround, the behavior of // G_InitNew should be fixed. char levelname[10]; - sprintf( levelname, "%s", MAPROTATION_GetMap( 0 )->mapname ); + FILE * pFile; + pFile = fopen( GAMENAMELOWERCASE"-server.map","r" ); + if ( pFile ) + { + fgets( levelname, 10, pFile ); // let's just hijack the "levelname" variable here + int i; + i = atoi(levelname); + sprintf( levelname, "%s", MAPROTATION_GetMap( i )->mapname ); + fclose( pFile ); + MAPROTATION_SetPositionToMap( levelname ); + } + else + { + // [K6] Start with a random map if we are using sv_randommaprotation. + sprintf( levelname, "%s", MAPROTATION_GetMap( sv_randommaprotation ? + M_Random.Random( ) % MAPROTATION_GetNumEntries( ) : 0 )->mapname ); + MAPROTATION_SetPositionToMap( levelname ); + } G_InitNew( levelname, false ); //G_InitNew( MAPROTATION_GetMapName( 0 ), false ); } else - G_InitNew( startmap, false ); + { + FILE * pFile; + pFile = fopen( GAMENAMELOWERCASE"-server.map","r" ); + if ( pFile ) + { + char levelname[10]; + fgets( levelname, 10, pFile ); + fclose( pFile ); + G_InitNew( levelname, false ); + } + else + { + G_InitNew( startmap, false ); + } + } } else { diff -r 3e26f5eca137 src/d_netinfo.cpp --- a/src/d_netinfo.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/d_netinfo.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -1339,6 +1339,8 @@ if (argv.argc() < 2) { int i; + int position; + std::string strpass2; for (i = 0; i < MAXPLAYERS; i++) { @@ -1359,6 +1361,22 @@ // [K6/BB] Show the player's country, if the GeoIP db is available. if ( NETWORK_IsGeoIPAvailable() ) infoString.AppendFormat ( "\\ce - FROM %s", NETWORK_GetCountryCodeFromAddress ( SERVER_GetClient( i )->Address ).GetChars() ); + + + // [Dusk] Also, if we're using a password list, display + // the password the client connected with. + // [K6] If we use a colon delimiter in passwords (for user:pass), strip the pass part to RCON clients. + if (sv_passwordlist || sv_joinpasswordlist) + { + strpass2 = SERVER_GetClient(i)->szPassword; + position = strpass2.find(":"); + if (position != (-1)) + { + strpass2.erase(position); + infoString.AppendFormat("\\cp - AUTH %s", strpass2.c_str()); + } + } + } if ( PLAYER_IsTrueSpectator( &players[i] )) diff -r 3e26f5eca137 src/d_player.h --- a/src/d_player.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/d_player.h Wed Oct 08 11:31:35 2014 +0200 @@ -602,7 +602,7 @@ void PLAYER_SetTeam( player_t *pPlayer, ULONG ulTeam, bool bNoBroadcast ); void PLAYER_SetSpectator( player_t *pPlayer, bool bBroadcast, bool bDeadSpectator ); void PLAYER_SetDefaultSpectatorValues( player_t *pPlayer ); -void PLAYER_SpectatorJoinsGame ( player_t *pPlayer ); +void PLAYER_SpectatorJoinsGame ( player_t *pPlayer, bool bRemoveTempBots = true ); void PLAYER_SetWins( player_t *pPlayer, ULONG ulWins ); void PLAYER_GetName( player_t *pPlayer, char *pszOutBuf ); // [BB] PLAYER_GetHealth and PLAYER_GetLivesLeft are helper functions for PLAYER_GetPlayerWithSingleHighestValue. diff -r 3e26f5eca137 src/deathmatch.cpp --- a/src/deathmatch.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/deathmatch.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -162,6 +162,9 @@ teamlms.ForceSet( Val, CVAR_Bool ); possession.ForceSet( Val, CVAR_Bool ); teampossession.ForceSet( Val, CVAR_Bool ); + + Val.Int = 2; + sv_maxplayers.ForceSet( Val, CVAR_Int ); } // Reset what the current game mode is. diff -r 3e26f5eca137 src/duel.cpp --- a/src/duel.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/duel.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -150,7 +150,7 @@ //***************************************************************************** // -ULONG DUEL_CountActiveDuelers( void ) +ULONG DUEL_CountActiveDuelers( bool bCountBots ) { ULONG ulIdx; ULONG ulDuelers; @@ -158,7 +158,7 @@ ulDuelers = 0; for ( ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) { - if (( playeringame[ulIdx] ) && ( players[ulIdx].bSpectating == false )) + if (( playeringame[ulIdx] ) && ( players[ulIdx].bSpectating == false ) && ( !players[ulIdx].bIsBot || ( players[ulIdx].bIsBot && bCountBots ))) ulDuelers++; } diff -r 3e26f5eca137 src/duel.h --- a/src/duel.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/duel.h Wed Oct 08 11:31:35 2014 +0200 @@ -71,7 +71,7 @@ void DUEL_Construct( void ); void DUEL_Tick( void ); -ULONG DUEL_CountActiveDuelers( void ); +ULONG DUEL_CountActiveDuelers( bool bCountBots = true ); void DUEL_StartCountdown( ULONG ulTicks ); void DUEL_DoFight( void ); void DUEL_DoWinSequence( ULONG ulPlayer ); diff -r 3e26f5eca137 src/g_level.cpp --- a/src/g_level.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/g_level.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -130,6 +130,8 @@ #define RCLS_ID MAKE_ID('r','c','L','s') #define PCLS_ID MAKE_ID('p','c','L','s') +std::vector g_CvarOverrideEntries; + static void SetEndSequence (char *nextmap, int type); void G_VerifySkill(); @@ -163,6 +165,9 @@ extern bool sendpause, sendsave, sendturn180, SendLand; extern const AInventory *SendItemUse, *SendItemDrop; +extern std::vector g_MapRotationEntries; +extern ULONG g_ulCurMapInList; + void *statcopy; // for statistics driver FLevelLocals level; // info about current level @@ -303,7 +308,7 @@ } else { - Printf ("Usage: map \n"); + Printf( "%s: %s\n", level.mapname, level.LevelName.GetChars() ); } } @@ -1082,6 +1087,62 @@ CAMPAIGNINFO_s *pInfo; UCVarValue Val; + + // [K6] Save our position in the map rotation to an external file so we can revive it in case of a crash. + // If we don't have a rotation, just use the map name. + if ( NETWORK_GetState( ) == NETSTATE_SERVER ) + { + FILE * pFile; + pFile = fopen( GAMENAMELOWERCASE"-server.map","w" ); + if (pFile) + { + if ( sv_maprotation ) + { + fprintf( pFile, "%lu", g_ulCurMapInList ); + } + else + { + fprintf( pFile, "%s", level.mapname ); + } + fclose( pFile ); + } + + + // [K6] Apply/revert map specific CVAR overrides here. + FBaseCVar* var; + UCVarValue val; + bool bLogRevert = false; + const ULONG boobSize = g_CvarOverrideEntries.size( ); + + for ( ULONG ulIdx = 0; ulIdx < boobSize; ulIdx++ ) + if (g_CvarOverrideEntries[ulIdx].originalValue.IsNotEmpty()) + { + cvar_forceset (g_CvarOverrideEntries[ulIdx].cvarToOverride, g_CvarOverrideEntries[ulIdx].originalValue); + g_CvarOverrideEntries[ulIdx].originalValue = NULL; + bLogRevert = true; + } + + if ( bLogRevert ) + Printf("Reverting CVAR overrides\n"); + + for ( ULONG ulIdx = 0; ulIdx < boobSize; ulIdx++ ) + if (stricmp(g_CvarOverrideEntries[ulIdx].mapname, level.mapname) == 0) + { + if ((var = FindCVar (g_CvarOverrideEntries[ulIdx].cvarToOverride, NULL))) + { + val = var->GetGenericRep (CVAR_String); + g_CvarOverrideEntries[ulIdx].originalValue = val.String; // save the current value so we can restore it when the map ends + cvar_forceset (g_CvarOverrideEntries[ulIdx].cvarToOverride, g_CvarOverrideEntries[ulIdx].overrideValue); + Printf("Applying CVAR override for next map: %s %s\n", g_CvarOverrideEntries[ulIdx].cvarToOverride.GetChars(), + g_CvarOverrideEntries[ulIdx].overrideValue.GetChars()); + } + } + + // [K6] Mark the map as being visited for the purpose of sv_randommaprotation. + if ( sv_randommaprotation && ( g_MapRotationEntries.size( ) > 1 ) ) + g_MapRotationEntries[g_ulCurMapInList].bUsed = true; + } + // [BB] Make sure that dead spectators are respawned before moving to the next map. if ( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_DEADSPECTATORS ) GAMEMODE_RespawnDeadSpectatorsAndPopQueue( ); @@ -1479,6 +1540,12 @@ // Make sure the game isn't frozen. level.flags2 &= ~LEVEL2_FROZEN; + if ( NETWORK_GetState( ) == NETSTATE_SERVER && g_bGameFrozen ) + { + C_DoCommand ( "freezeall" ); + g_bGameFrozen = 0; + } + // [BB] Initialize/clear the free network ID list, but only if there are no level snapshots. // The actors stored in these snapshots still have their NetIDs, so as long as snapshots exist, // we may not clear the NetID list. @@ -2676,3 +2743,22 @@ } } } + +// [K6] Map specific CVAR overrides. +CCMD(add_cvaroverride) +{ + if ( argv.argc( ) > 3 ) + { + CVAROVERRIDEENTRY_t newEntry; + + newEntry.mapname = argv[1]; + newEntry.cvarToOverride = argv[2]; + for(int i = 3; i < argv.argc(); i++) + newEntry.overrideValue.AppendFormat("%s ", argv[i]); + V_RemoveTrailingCrapFromFString ( newEntry.overrideValue ); + g_CvarOverrideEntries.push_back( newEntry ); + Printf("CVAR override for %s: %s %s\n", newEntry.mapname.GetChars(), newEntry.cvarToOverride.GetChars(), newEntry.overrideValue.GetChars()); + } + else + Printf( "Usage: add_cvaroverride \n" ); +} diff -r 3e26f5eca137 src/g_level.h --- a/src/g_level.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/g_level.h Wed Oct 08 11:31:35 2014 +0200 @@ -57,6 +57,15 @@ #endif +typedef struct +{ + FString mapname; + FString cvarToOverride; + FString overrideValue; + FString originalValue; + +} CVAROVERRIDEENTRY_t; // [K6] Yeah it's probably lame to use strings but it was easier and it works... + struct FMapInfoParser { enum EFormatType diff -r 3e26f5eca137 src/gamemode.cpp --- a/src/gamemode.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/gamemode.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -660,6 +660,13 @@ // Otherwise, just spawn at their normal player start. else G_CooperativeSpawnPlayer( ulPlayer, bClientUpdate ); + + // [K6] If the game is paused, freeze the newly respawned player. + if ( g_bGameFrozen ) + { + players[ulPlayer].cheats |= CF_TOTALLYFROZEN | CF_GODMODE; + SERVERCOMMANDS_SetPlayerCheats( ulPlayer, ulPlayer, SVCF_ONLYTHISCLIENT ); + } } //***************************************************************************** @@ -741,7 +748,8 @@ bool GAMEMODE_PreventPlayersFromJoining( ULONG ulExcludePlayer ) { // [BB] No free player slots. - if ( ( NETWORK_GetState( ) == NETSTATE_SERVER ) && ( SERVER_CalcNumNonSpectatingPlayers( ulExcludePlayer ) >= static_cast (sv_maxplayers) ) ) + // [K6] Temporary bots don't count. + if ( ( NETWORK_GetState( ) == NETSTATE_SERVER ) && ( SERVER_CalcNumNonSpectatingPlayers( ulExcludePlayer, (sv_minplayers > 0 ? false : true) ) >= static_cast (sv_maxplayers) ) ) return true; // [BB] Don't let players join during intermission. They will be put in line and the queue is popped once intermission ends. @@ -749,7 +757,8 @@ return true; // [BB] Duel in progress. - if ( duel && ( DUEL_CountActiveDuelers( ) >= 2 ) ) + // [K6] Temporary bots don't count. + if ( duel && ( DUEL_CountActiveDuelers( sv_minplayers > 0 ? false : true ) >= 2 ) ) return true; // [BB] If lives are limited, players are not allowed to join most of the time. diff -r 3e26f5eca137 src/joinqueue.cpp --- a/src/joinqueue.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/joinqueue.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -68,6 +68,8 @@ #include "sv_commands.h" #include "team.h" +#include "v_font.h" + //***************************************************************************** // VARIABLES @@ -274,7 +276,15 @@ { // [K6] Reset their AFK timer now - they may have been waiting in the queue silently and we don't want to kick them. SERVER_GetClient( g_lJoinQueue[ulIdx].ulPlayer )->lLastActionTic = gametic; - PLAYER_SpectatorJoinsGame ( &players[g_lJoinQueue[ulIdx].ulPlayer] ); + + // [K6] We should somehow let the (possibly even minimized) client loudly know that he's in the game now. + // This might not be the ideal solution but it works for me... + SERVERCOMMANDS_AnnouncerSound( "fight", (ULONG)g_lJoinQueue[ulIdx].ulPlayer, SVCF_ONLYTHISCLIENT ); + SERVERCOMMANDS_PrintHUDMessageFadeOut( "FIGHT!", 160.4f, 75.0f, 320, 200, CR_RED, 3.0f, 2.0f, "BigFont", false, MAKE_ID('C','N','T','R'), + (ULONG)g_lJoinQueue[ulIdx].ulPlayer, SVCF_ONLYTHISCLIENT ); + + // [K6] We can't remove temporary bots just now, or we get a crash, so postpone it. + PLAYER_SpectatorJoinsGame ( &players[g_lJoinQueue[ulIdx].ulPlayer], false ); // [BB/Spleen] The "lag interval" is only half of the "spectate info send" interval. Account for this here. if (( gametic - SERVER_GetClient( g_lJoinQueue[ulIdx].ulPlayer )->ulLastCommandTic ) <= 2*TICRATE ) @@ -318,6 +328,9 @@ JOINQUEUE_RemovePlayerAtPosition ( ulIdx ); + // [K6] Remove temporary bots as needed. + BOTS_RemoveTempBots( ); + if ( lNumSlots > 0 ) lNumSlots--; } diff -r 3e26f5eca137 src/maprotation.cpp --- a/src/maprotation.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/maprotation.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -61,7 +61,7 @@ std::vector g_MapRotationEntries; -static ULONG g_ulCurMapInList; +/*static*/ ULONG g_ulCurMapInList; static ULONG g_ulNextMapInList; //***************************************************************************** @@ -91,7 +91,7 @@ if ( sv_randommaprotation && ( g_MapRotationEntries.size( ) > 1 ) ) { // Mark the current map in the list as being used. - g_MapRotationEntries[g_ulCurMapInList].bUsed = true; +// g_MapRotationEntries[g_ulCurMapInList].bUsed = true; // If all the maps have been played, make them all available again. { @@ -291,7 +291,8 @@ { Printf( "Map rotation list: \n" ); for ( ULONG ulIdx = 0; ulIdx < g_MapRotationEntries.size( ); ulIdx++ ) - Printf( "%lu. %s - %s\n", ulIdx + 1, g_MapRotationEntries[ulIdx].pMap->mapname, g_MapRotationEntries[ulIdx].pMap->LookupLevelName( ).GetChars( )); + Printf( "%lu. %s - %s%s\n", ulIdx + 1, g_MapRotationEntries[ulIdx].pMap->mapname, g_MapRotationEntries[ulIdx].pMap->LookupLevelName( ).GetChars( ), + g_MapRotationEntries[ulIdx].bUsed ? "\\cd - used" : ""); // [K6] Added debug output. } } diff -r 3e26f5eca137 src/network.cpp --- a/src/network.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/network.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -912,7 +912,7 @@ //***************************************************************************** // [BB] -FString NETWORK_GetCountryCodeFromAddress( NETADDRESS_s Address ) +FString NETWORK_GetCountryCodeFromAddress( NETADDRESS_s Address, bool FullName ) { const char * addressString = NETWORK_AddressToStringIgnorePort( Address ); if ( ( strnicmp( "10.", addressString, 3 ) == 0 ) || @@ -923,7 +923,8 @@ if ( g_GeoIPDB == NULL ) return ""; - FString country = GeoIP_country_code_by_addr ( g_GeoIPDB, NETWORK_AddressToStringIgnorePort( Address ) ); + FString country = FullName? GeoIP_country_name_by_addr ( g_GeoIPDB, NETWORK_AddressToStringIgnorePort( Address ) ) : + GeoIP_country_code_by_addr ( g_GeoIPDB, NETWORK_AddressToStringIgnorePort( Address ) ); return country.IsEmpty() ? "N/A" : country; } diff -r 3e26f5eca137 src/network.h --- a/src/network.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/network.h Wed Oct 08 11:31:35 2014 +0200 @@ -347,7 +347,7 @@ NETBUFFER_s *NETWORK_GetNetworkMessageBuffer( void ); ULONG NETWORK_ntohs( ULONG ul ); bool NETWORK_IsGeoIPAvailable( void ); -FString NETWORK_GetCountryCodeFromAddress( NETADDRESS_s Address ); +FString NETWORK_GetCountryCodeFromAddress( NETADDRESS_s Address, bool FullName=false ); USHORT NETWORK_GetLocalPort( void ); std::list > *NETWORK_GetPWADList( void ); // [RC] diff -r 3e26f5eca137 src/networkshared.cpp --- a/src/networkshared.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/networkshared.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -678,8 +678,8 @@ } fclose( pFile ); - if ( _numberOfEntries > 0 ) - Printf( "%s: %d entr%s loaded.\n", FileName, static_cast(_numberOfEntries), ( _numberOfEntries == 1 ) ? "y" : "ies" ); +// if ( _numberOfEntries > 0 ) +// Printf( "%s: %d entr%s loaded.\n", FileName, static_cast(_numberOfEntries), ( _numberOfEntries == 1 ) ? "y" : "ies" ); return true; } diff -r 3e26f5eca137 src/p_acs.cpp --- a/src/p_acs.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/p_acs.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -557,6 +557,12 @@ // [BB] The armor display amount has to be updated separately. if( item->GetClass()->IsDescendantOf (RUNTIME_CLASS(AArmor))) SERVERCOMMANDS_SetPlayerArmor( actor->player - players ); + + if (strcmp(item->GetClass( )->TypeName.GetChars( ), "BombCrateFXGiverBlue") == 0 + || strcmp(item->GetClass( )->TypeName.GetChars( ), "BombCrateFXGiverRed") == 0) + { + SERVER_GetClient( ULONG(actor->player - players) )->lBombCrateTic = gametic; + } } // [BB] diff -r 3e26f5eca137 src/p_interaction.cpp --- a/src/p_interaction.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/p_interaction.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -2549,7 +2549,7 @@ //***************************************************************************** // -void PLAYER_SpectatorJoinsGame( player_t *pPlayer ) +void PLAYER_SpectatorJoinsGame( player_t *pPlayer, bool bRemoveTempBots ) { if ( pPlayer == NULL ) return; @@ -2581,6 +2581,10 @@ // [BB] If he's a bot, tell him that he successfully joined. if ( pPlayer->pSkullBot ) pPlayer->pSkullBot->PostEvent( BOTEVENT_JOINEDGAME ); + + // [K6] Remove temporary bots as needed. + if ( bRemoveTempBots ) + BOTS_RemoveTempBots( ); } //***************************************************************************** @@ -3032,6 +3036,11 @@ // [BB] Clear the players medals and the medal related counters. The former is something also clients need to do. memset( players[ulPlayer].ulMedalCount, 0, sizeof( ULONG ) * NUM_MEDALS ); PLAYER_ResetSpecialCounters ( &players[ulPlayer] ); + + // [K6] Remove temporary bots as needed. + // For some reason we get a crash if this is in BOTS_RemoveTempBots() AND that function is called from elsewhere. + if ( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) == 0 ) + C_DoCommand ( "removebots" ); } //***************************************************************************** diff -r 3e26f5eca137 src/p_setup.cpp --- a/src/p_setup.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/p_setup.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -2899,13 +2899,17 @@ blockmap = blockmaplump+4; // [BC] Also, build the node list for the bot pathing module. + // [K6] For the server, this is handled in CSkullBot(), unless we already have bots in game (from the previous map). if (( NETWORK_GetState( ) != NETSTATE_CLIENT ) && ( CLIENTDEMO_IsPlaying( ) == false ) && (( level.flags2 & LEVEL2_NOBOTNODES ) == false ) && - (( NETWORK_GetState( ) != NETSTATE_SERVER ) || ( sv_disallowbots == false ))) + (( NETWORK_GetState( ) != NETSTATE_SERVER ) || ( BOTS_CountBots( ) > 0 ))) { ASTAR_BuildNodes( ); } + + if ( level.flags2 & LEVEL2_NOBOTNODES || level.flags2 & LEVEL2_ISLOBBY ) + BOTS_RemoveAllBots( false ); } diff -r 3e26f5eca137 src/p_spec.cpp --- a/src/p_spec.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/p_spec.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -87,6 +87,9 @@ static FRandom pr_playerinspecialsector ("PlayerInSpecialSector"); +// [Dusk] +static int g_lastLevelExit = -1; + // [GrafZahl] Make this message changable by the user! ;) CVAR(String, secretmessage, "A Secret is revealed!", CVAR_ARCHIVE) @@ -164,6 +167,12 @@ { cluster_info_t *cluster; + // [Dusk] Never exit twice. + if ( g_lastLevelExit == level.time ) + return false; + + g_lastLevelExit = level.time; + // The world can always exit itself. if (self == NULL) return true; diff -r 3e26f5eca137 src/sdl/i_main.cpp --- a/src/sdl/i_main.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/sdl/i_main.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -73,6 +73,10 @@ // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- +#ifdef unix +void handle_sighup(int signum); +#endif + // PRIVATE FUNCTION PROTOTYPES --------------------------------------------- // EXTERNAL DATA DECLARATIONS ---------------------------------------------- @@ -204,6 +208,9 @@ int main (int argc, char **argv) { +#ifdef unix +signal(SIGHUP, handle_sighup); +#endif { int s[4] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS }; cc_install_handlers(argc, argv, 4, s, GAMENAMELOWERCASE"-crash.log", DoomSpecificInfo); diff -r 3e26f5eca137 src/sv_ban.cpp --- a/src/sv_ban.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/sv_ban.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -47,6 +47,9 @@ // //----------------------------------------------------------------------------- +#ifdef unix +#include +#endif #include #include @@ -429,6 +432,11 @@ // Kick any players using a banned address. serverban_KickBannedPlayers( ); + + // [Dusk] Take the opportunity to load the password list too. + PASSLIST_Construct (); + PASSLIST_EnforcePasswords (); + } //***************************************************************************** @@ -713,3 +721,14 @@ { serverban_LoadBansAndBanExemptions( ); } + +#ifdef unix +//***************************************************************************** +// +void handle_sighup(int signum) +{ + // in case we registered this handler for multiple signals + if (signum == SIGHUP) + serverban_LoadBansAndBanExemptions( ); +} +#endif diff -r 3e26f5eca137 src/sv_commands.cpp --- a/src/sv_commands.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/sv_commands.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -886,7 +886,13 @@ NETWORK_WriteByte( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, players[ulPlayer].userinfo.lRailgunTrailColor ); if ( ulUserInfoFlags & USERINFO_SKIN ) - NETWORK_WriteString( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, SERVER_GetClient( ulPlayer )->szSkin ); + { + //[K6] Don't bother with skins on ZH - fixes a skin exploit. + if (strstr(sv_hostname, "Zombie HORDE")) + NETWORK_WriteString( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, "0" ); + else + NETWORK_WriteString( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, SERVER_GetClient( ulPlayer )->szSkin ); + } if ( ulUserInfoFlags & USERINFO_HANDICAP ) NETWORK_WriteByte( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, players[ulPlayer].userinfo.lHandicap ); @@ -2459,6 +2465,13 @@ NETWORK_WriteByte( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, ulProperty ); NETWORK_WriteLong( &SERVER_GetClient( ulIdx )->PacketBuffer.ByteStream, ulPropertyValue ); } + if (strstr(sv_hostname, "All Out War") && ulProperty == APROP_Speed) + { + if (pActor->tid && pActor->tid == 410) + BlueAOWTickets = ulPropertyValue; + if (pActor->tid && pActor->tid == 411) + RedAOWTickets = ulPropertyValue; + } } //***************************************************************************** diff -r 3e26f5eca137 src/sv_commands.h --- a/src/sv_commands.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/sv_commands.h Wed Oct 08 11:31:35 2014 +0200 @@ -58,6 +58,11 @@ #include "vectors.h" #include "a_weaponpiece.h" + +extern int BlueAOWTickets; +extern int RedAOWTickets; + + //***************************************************************************** // DEFINES diff -r 3e26f5eca137 src/sv_main.cpp --- a/src/sv_main.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/sv_main.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -116,6 +116,13 @@ #include "network/cl_auth.h" #include "network/sv_auth.h" +#include "maprotation.h" +// [Dusk] we're reading files here +#include + +int BlueAOWTickets = 0; +int RedAOWTickets = 0; + //***************************************************************************** // MISC CRAP THAT SHOULDN'T BE HERE BUT HAS TO BE BECAUSE OF SLOPPY CODING @@ -126,6 +133,8 @@ void SERVERCONSOLE_ReListPlayers( void ); EXTERN_CVAR( Bool, sv_cheats ); +EXTERN_CVAR (Float, sv_gravity); +EXTERN_CVAR( Float, sv_aircontrol ); //***************************************************************************** // PROTOTYPES @@ -160,6 +169,8 @@ static bool server_MorphCheat( BYTESTREAM_s *pByteStream ); static bool server_CheckForClientMinorCommandFlood( ULONG ulClient ); static bool server_ProcessMoveCommand( CLIENT_MOVE_COMMAND_s &ClientMoveCmd, const ULONG ulClient ); +static ULONG CalcNumZombies( void ); +static void SendMaplistToPlayer( ULONG ulPlayer ); static bool server_CheckJoinPassword( const FString& clientPassword ); static bool server_Linetarget( BYTESTREAM_s* pByteStream ); static bool server_CheckLogin( const ULONG ulClient ); @@ -232,6 +243,13 @@ // [BB] List of all sector links created by calls to Sector_SetLink. static TArray g_SectorLinkList; +// [Dusk] List of passwords +std::vector g_PasswordList; + +// [K6] +bool g_bGameFrozen = 0; +static char *g_pszStartupTime; + // [RC] File to log packets to. #ifdef CREATE_PACKET_LOG static FILE *PacketLogFile = NULL; @@ -259,7 +277,6 @@ CVAR( Bool, sv_timestamp, false, CVAR_ARCHIVE ) CVAR( Int, sv_timestampformat, 0, CVAR_ARCHIVE ) CVAR( Int, sv_colorstripmethod, 0, CVAR_ARCHIVE ) -CVAR( Bool, sv_disallowbots, false, CVAR_ARCHIVE ) CVAR( Bool, sv_minimizetosystray, true, CVAR_ARCHIVE ) CVAR( Int, sv_queryignoretime, 10, CVAR_ARCHIVE ) CVAR( Bool, sv_markchatlines, false, CVAR_ARCHIVE ) @@ -268,6 +285,21 @@ CVAR( Bool, sv_pure, true, CVAR_SERVERINFO | CVAR_LATCH ) CVAR( Int, sv_maxclientsperip, 2, CVAR_ARCHIVE ) CVAR( Int, sv_afk2spec, 0, CVAR_ARCHIVE ) // [K6] +CUSTOM_CVAR( Int, sv_minplayers, 0, CVAR_ARCHIVE ) // [K6] +{ + if (self < 0) + self == 0; + + if (self > sv_maxplayers) + { + Printf( "sv_minplayers must be less than or equal to sv_maxplayers.\n" ); + self = sv_maxplayers; + } +} +CVAR( Bool, sv_removetempbots, 0, CVAR_ARCHIVE ) // [K6] + +CVAR( Bool, sv_passwordlist, false, CVAR_ARCHIVE ) // [Dusk] +CVAR( Bool, sv_joinpasswordlist, false, CVAR_ARCHIVE ) // [Dusk] CVAR( Bool, sv_forcelogintojoin, false, CVAR_ARCHIVE|CVAR_NOSETBYACS ) CUSTOM_CVAR( String, sv_adminlistfile, "adminlist.txt", CVAR_ARCHIVE|CVAR_NOSETBYACS ) @@ -297,6 +329,9 @@ // [BB] To stay compatible with old mods, the default value is at most 32. CUSTOM_CVAR( Int, sv_maxplayers, MIN ( MAXPLAYERS, 32 ), CVAR_ARCHIVE ) { + if ( duel && self != 2 ) + self = 2; + if ( self < 0 ) self = 0; @@ -358,6 +393,19 @@ } //***************************************************************************** +// [Dusk] +CUSTOM_CVAR (String, sv_passwordlistfile, "passwordlist.txt", CVAR_ARCHIVE) +{ + if (NETWORK_GetState () != NETSTATE_SERVER) + return; + + PASSLIST_Construct (); + +} + + +//***************************************************************************** + // FUNCTIONS void SERVER_Construct( void ) @@ -452,6 +500,9 @@ sprintf( g_szCurrentFont, "SmallFont" ); sprintf( g_szScriptActiveFont, "SmallFont" ); + time_t rawtime = time(NULL); + g_pszStartupTime = ctime(&rawtime); + #ifdef CREATE_PACKET_LOG // [RC] Create the packet log. @@ -942,7 +993,7 @@ //***************************************************************************** // -ULONG SERVER_CalcNumNonSpectatingPlayers( ULONG ulExcludePlayer ) +ULONG SERVER_CalcNumNonSpectatingPlayers( ULONG ulExcludePlayer, bool bCountBots ) { ULONG ulIdx; ULONG ulNumPlayers = 0; @@ -951,7 +1002,8 @@ { if (( playeringame[ulIdx] == false ) || ( PLAYER_IsTrueSpectator( &players[ulIdx] )) || - ( ulIdx == ulExcludePlayer )) + ( ulIdx == ulExcludePlayer ) || + ( players[ulIdx].bIsBot && bCountBots == false )) { continue; } @@ -1115,12 +1167,18 @@ SERVERCOMMANDS_PlayerSay( ulPlayer, pszString, ulMode, bFordidChatToPlayers ); + V_EscapeBacklashes( cleanedChatString ); + pszString = cleanedChatString.GetChars(); + // [BB] This is to make the lines readily identifiable, necessary // for MiX-MaN's IRC server control tool for example. bool sv_logfiletimestampOldValue = sv_logfiletimestamp; if( sv_markchatlines ) { - Printf( "CHAT " ); + if ( ulMode == CHATMODE_TEAM ) + Printf( "TEAMCHAT " ); + else + Printf( "CHAT " ); sv_logfiletimestamp = false; } // Print this message in the server's local window. @@ -1138,6 +1196,33 @@ Printf( ": %s\n", pszString ); else Printf( "%s: %s\n", players[ulPlayer].userinfo.netname, pszString ); + + // [K6] On duel, either of players may pause the game by saying "hold" and resume by saying "go" + if (( duel ) && ( stricmp( "hold", pszString ) == 0 ) && (playeringame[ulPlayer]) && ( !PLAYER_IsTrueSpectator( &players[ulPlayer] )) && (!g_bGameFrozen)) + { + C_DoCommand ( "freezeall" ); + SERVER_Printf( PRINT_HIGH, "Game paused. Say \"go\" to resume.\n" ); + } + if (( duel ) && ( stricmp( "go", pszString ) == 0 ) && (playeringame[ulPlayer]) && ( !PLAYER_IsTrueSpectator( &players[ulPlayer] )) && (g_bGameFrozen)) + { + char commandString[32]; + sprintf ( commandString, "wait %d;freezeall", 5 * TICRATE ); + AddCommandString ( commandString ); + SERVER_Printf( PRINT_HIGH, "Resuming in 5..4..3..\n" ); + } + + if (strstr(sv_hostname, "All Out War") && (strnicmp( "tickets", pszString, 7 ) == 0 || strnicmp( "lives", pszString, 5 ) == 0) + && gamestate == GS_LEVEL && BlueAOWTickets && RedAOWTickets) + SERVER_Printf(PRINT_HIGH, "Lives: \\cH%d \\c-- \\cG%d\\c-\n", BlueAOWTickets, RedAOWTickets); + + if (stricmp( "!music", pszString ) == 0 && gamestate == GS_LEVEL) + SERVER_Printf(PRINT_HIGH, "%s\n", SERVER_GetMapMusic()); + + if (stricmp( "!maplist", pszString ) == 0) + SendMaplistToPlayer(ulPlayer); + + + } if( sv_markchatlines && sv_logfiletimestampOldValue ) sv_logfiletimestamp = true; @@ -1309,7 +1394,7 @@ // Don't restart the map! There's people here! g_lMapRestartTimer = 0; - g_aClients[g_lCurrentClient].ulLastGameTic = gametic; + g_aClients[g_lCurrentClient].ulLastGameTime = I_MSTime(); g_aClients[g_lCurrentClient].ulLastCommandTic = gametic; // Clear out the client's netbuffer. @@ -1319,7 +1404,58 @@ SERVERCOMMANDS_BeginSnapshot( g_lCurrentClient ); // Send welcome message. - SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Version %s Server\n", DOTVERSIONSTR ); + // [K6] Include the server startup time. + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Version %s Server, started on %s\n", DOTVERSIONSTR, g_pszStartupTime ); + + // [K6] Tell the player some info about our server + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "\\cnServer running kpatch build %s\n", KPATCH_BUILD ); + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Consoleplayer is: %d\n", g_lCurrentClient ); + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "SERVER: dmflags=%lu dmflags2=%lu dmflags3=%lu compatflags=%lu compatflags2=%lu\n", (LONG)dmflags, (LONG)dmflags2, (LONG)dmflags3, (LONG)compatflags, (LONG)compatflags2 ); + if ( instagib ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Instagib mode\n" ); + if ( buckshot ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Buckshot mode\n" ); + if ( ( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_PLAYERSONTEAMS ) || cooperative ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Team damage factor: %lu%%\n", ((LONG)teamdamage.GetGenericRep( CVAR_Float ).Float * 100) ); + if ( fraglimit ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Frag limit: %d\n", fraglimit.GetGenericRep( CVAR_Int ) ); + if ( pointlimit ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Point limit: %d\n", pointlimit.GetGenericRep( CVAR_Int ) ); + if ( winlimit ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Win limit: %d\n", winlimit.GetGenericRep( CVAR_Int ) ); + if ( duellimit ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Duel limit: %d\n", duellimit.GetGenericRep( CVAR_Int ) ); + if ( timelimit ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Time limit: %d\n", timelimit.GetGenericRep( CVAR_Int ) ); + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Gravity: %lu%%\n", ((LONG)sv_gravity.GetGenericRep( CVAR_Float ).Float / 8) ); + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Air control: %s\n", sv_aircontrol.GetGenericRep( CVAR_String ) ); + if ( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_PLAYERSONTEAMS ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "%d teams\n", sv_maxteams.GetGenericRep( CVAR_Int ) ); + + FString FSVoting; + if ( sv_nocallvote.GetGenericRep( CVAR_Int ).Int != 1 ) + { + if ( !sv_nomapvote ) + FSVoting.AppendFormat ( " map" ); + if ( !sv_nochangemapvote ) + FSVoting.AppendFormat ( " changemap" ); + if ( !sv_nokickvote ) + FSVoting.AppendFormat ( " kick" ); + if ( !sv_noduellimitvote ) + FSVoting.AppendFormat ( " duellimit" ); + if ( !sv_nofraglimitvote ) + FSVoting.AppendFormat ( " fraglimit" ); + if ( !sv_nopointlimitvote ) + FSVoting.AppendFormat ( " pointlimit" ); + if ( !sv_notimelimitvote ) + FSVoting.AppendFormat ( " timelimit" ); + if ( !sv_nowinlimitvote ) + FSVoting.AppendFormat ( " winlimit" ); + } + + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Voting:%s\n", FSVoting.IsEmpty() ? " off" : FSVoting.GetChars() ); + if ( sv_coop_damagefactor != 1.0f ) + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Coop damage factor: %.2f\n", static_cast (sv_coop_damagefactor) ); // Send consoleplayer number. SERVERCOMMANDS_SetConsolePlayer( g_lCurrentClient ); @@ -1562,14 +1698,37 @@ { // [K6/BB] Show the player's country on connect, if the GeoIP db is available. FString countryInfo; - // [BB] All players see the connect message, so only show the country code if the player doesn't want it to be hidden. - if ( NETWORK_IsGeoIPAvailable() && ( SERVER_GetClient( g_lCurrentClient )->bWantHideCountry == false ) ) - countryInfo.AppendFormat ( " (from: %s)", NETWORK_GetCountryCodeFromAddress ( SERVER_GetClient( g_lCurrentClient )->Address ).GetChars() ); - - if ( players[g_lCurrentClient].bSpectating ) - SERVER_Printf( PRINT_HIGH, "%s \\c-has connected.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() ); + std::string strpass; + int position; + + strpass = g_aClients[g_lCurrentClient].szPassword; + position = strpass.find(":"); + if (sv_passwordlist && position != (-1)) + { + strpass.erase(position); + + // [BB] All players see the connect message, so only show the country code if the player doesn't want it to be hidden. + if ( NETWORK_IsGeoIPAvailable() && ( SERVER_GetClient( g_lCurrentClient )->bWantHideCountry == false ) ) + countryInfo.AppendFormat ( " (from: %s, auth: %s)", NETWORK_GetCountryCodeFromAddress ( SERVER_GetClient( g_lCurrentClient )->Address, true ).GetChars(), strpass.c_str() ); + + if ( players[g_lCurrentClient].bSpectating ) + SERVER_Printf( PRINT_HIGH, "%s \\c-has connected.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() ); + else + SERVER_Printf( PRINT_HIGH, "%s \\c-entered the game.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() ); + } + else - SERVER_Printf( PRINT_HIGH, "%s \\c-entered the game.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() ); + { + // [BB] All players see the connect message, so only show the country code if the player doesn't want it to be hidden. + if ( NETWORK_IsGeoIPAvailable() && ( SERVER_GetClient( g_lCurrentClient )->bWantHideCountry == false ) ) + countryInfo.AppendFormat ( " (from: %s)", NETWORK_GetCountryCodeFromAddress ( SERVER_GetClient( g_lCurrentClient )->Address, true ).GetChars() ); + + if ( players[g_lCurrentClient].bSpectating ) + SERVER_Printf( PRINT_HIGH, "%s \\c-has connected.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() ); + else + SERVER_Printf( PRINT_HIGH, "%s \\c-entered the game.%s\n", players[g_lCurrentClient].userinfo.netname, countryInfo.GetChars() ); + } + SERVERCOMMANDS_Sound( CHAN_VOICE, "zandronum/connect", 1.f, ATTN_NONE, (ULONG)g_lCurrentClient, SVCF_SKIPTHISCLIENT ); } BOTCMD_SetLastJoinedPlayer( players[g_lCurrentClient].userinfo.netname ); @@ -1598,6 +1757,10 @@ // [RC] Clients may wish to ignore this new player. SERVERCOMMANDS_PotentiallyIgnorePlayer( g_lCurrentClient ); + + // [K6] Remove temporary bots as needed. + if ( players[g_lCurrentClient].bSpectating == false ) + BOTS_RemoveTempBots( ); } //***************************************************************************** @@ -1751,7 +1914,7 @@ NETWORK_WriteByte( &TempBuffer.ByteStream, 254 /* = NETWORK_ERROR in 97d-beta4.3 */ ); NETWORK_WriteByte( &TempBuffer.ByteStream, 1 /* = NETWORK_ERRORCODE_WRONGVERSION in 97d-beta4.3 */ ); FString versionStringMessage; - versionStringMessage.Format ( "%s\nYou are way out of the loop :)", DOTVERSIONSTR ); + versionStringMessage.Format ( "Zandronum %s\n*** Skulltag has been updated to Zandronum! You are way out of the loop :)\n*** Visit http://www.zandronum.com/ for updates.", DOTVERSIONSTR ); NETWORK_WriteString( &TempBuffer.ByteStream, versionStringMessage.GetChars() ); NETWORK_LaunchPacket( &TempBuffer, NETWORK_GetFromAddress( ) ); @@ -1773,8 +1936,24 @@ } // Don't handle connection attempts from clients if we're in intermission. + // [K6] But inform the client so he doesn't just stare at "connecting connecting". if ( gamestate != GS_LEVEL ) + { + if ( gamestate == GS_INTERMISSION ) // is this really necessary? + { + NETBUFFER_s TempBuffer; + + NETWORK_InitBuffer( &TempBuffer, MAX_UDP_PACKET, BUFFERTYPE_WRITE ); + NETWORK_ClearBuffer( &TempBuffer ); + NETWORK_WriteByte( &TempBuffer.ByteStream, SVC_UNRELIABLEPACKET ); + NETWORK_WriteHeader( &TempBuffer.ByteStream, SVC_PRINT ); + NETWORK_WriteByte( &TempBuffer.ByteStream, PRINT_HIGH ); + NETWORK_WriteString( &TempBuffer.ByteStream, "Server is in intermission. Please wait a moment.\n" ); + NETWORK_LaunchPacket( &TempBuffer, NETWORK_GetFromAddress( ) ); + NETWORK_FreeBuffer( &TempBuffer ); + } return; + } // Setup a new player (setup CLIENT_t and player_t) SERVER_SetupNewConnection( pByteStream, true ); @@ -1787,6 +1966,7 @@ LONG lClient; FString clientVersion; FString clientPassword; + FString tempPassword; char szServerPassword[MAX_NETWORK_STRING]; LONG lClientNetworkGameVersion; char szAddress[4][4]; @@ -1902,7 +2082,7 @@ // Make sure the version matches. if ( stricmp( clientVersion.GetChars(), DOTVERSIONSTR ) != 0 ) { - SERVER_ClientError( lClient, NETWORK_ERRORCODE_WRONGVERSION ); + SERVER_ClientError( lClient, NETWORK_ERRORCODE_WRONGVERSION, clientVersion ); #ifdef CREATE_PACKET_LOG server_LogPacket(pByteStream, NETWORK_GetFromAddress( ), "Wrong version."); #endif @@ -1910,6 +2090,7 @@ } } +/* // Make sure the network game version matches. if ( NETGAMEVERSION != lClientNetworkGameVersion ) { @@ -1920,12 +2101,29 @@ return; } +*/ // Client is now in the loop, prevent from a early timeout // [BB] Only do this if this is actually a new player connecting. Otherwise it // could be abused to keep non-finished connections alive. if ( bNewPlayer ) - g_aClients[lClient].ulLastCommandTic = g_aClients[lClient].ulLastGameTic = gametic; + { + g_aClients[lClient].ulLastCommandTic = gametic; + g_aClients[lClient].ulLastGameTime = I_MSTime(); + } + + // Store the password. + tempPassword = clientPassword; + if (sv_passwordlist) { + tempPassword.ToLower(); + // [Dusk] The string must be truncated first to prevent + // buffer overflows. + // [K6] A bit more + if (tempPassword.Len () > 48) + tempPassword.Truncate (48); + + strcpy (g_aClients[lClient].szPassword, tempPassword.GetChars()); + } // Check if we require a password to join this server. Val = sv_password.GetGenericRep( CVAR_String ); @@ -1941,6 +2139,16 @@ SERVER_ClientError( lClient, NETWORK_ERRORCODE_WRONGPASSWORD ); return; } + } else if (sv_passwordlist) { + // [Dusk] We use a password list, so check if the password is listed. + if (!PASSLIST_CheckPassword (clientPassword)) { + // not listed! + SERVER_ClientError (lClient, NETWORK_ERRORCODE_WRONGPASSWORD); + return; + } + + + Printf ("%s uses password %s\n", NETWORK_AddressToString( NETWORK_GetFromAddress( )), tempPassword.GetChars()); } // Check if this IP has been banned. @@ -1975,7 +2183,11 @@ players[lClient].ulTeam = teams.Size( ); players[lClient].bOnTeam = false; - g_aClients[lClient].bRCONAccess = false; + if ( SERVER_GetAdminList()->isIPInList( SERVER_GetClient( lClient )->Address ) ) + g_aClients[lClient].bRCONAccess = true; + else + g_aClients[lClient].bRCONAccess = false; + g_aClients[lClient].ulDisplayPlayer = lClient; g_aClients[lClient].bFullUpdateIncomplete = false; g_aClients[lClient].commandInstances.clear(); @@ -1996,6 +2208,8 @@ g_aClients[lClient].ulNumConsistencyWarnings = 0; g_aClients[lClient].szSkin[0] = 0; g_aClients[lClient].IgnoredAddresses.clear(); + g_aClients[lClient].lLastActionTic = 0; + g_aClients[lClient].lBombCrateTic = -5*TICRATE; SERVER_InitClientSRPData ( lClient ); @@ -2021,6 +2235,13 @@ pPlayer = &players[g_lCurrentClient]; + // Read in which userinfo entries the client is sending us. + ulFlags = NETWORK_ReadShort( pByteStream ); + + // [K6] Not all user info properties trigger the kick. + if ( ulFlags == USERINFO_AIMDISTANCE ) + bAllowKick = false; + if ( bAllowKick ) { ulUserInfoInstance = ( ++g_aClients[g_lCurrentClient].ulLastUserInfoInstance % MAX_USERINFOINSTANCE_STORAGE ); @@ -2066,9 +2287,6 @@ } } - // Read in which userinfo entries the client is sending us. - ulFlags = NETWORK_ReadShort( pByteStream ); - // Read in the player's name. if ( ulFlags & USERINFO_NAME ) { @@ -2086,7 +2304,7 @@ if ( nameString.Compare( pPlayer->userinfo.netname ) != 0 ) { bKickPlayer = true; - kickReason = "User name contains illegal characters." ; + kickReason = "Attempted to use illegal user name." ; } // [BB] It's extremely critical that we NEVER use the uncleaned player name! @@ -2094,22 +2312,37 @@ // a tampered name. strcpy( pPlayer->userinfo.netname, nameString.GetChars() ); + char szPlayerNameNoColor[MAXPLAYERNAME+1]; + char szOldPlayerNameNoColor[MAXPLAYERNAME+1]; + + sprintf( szPlayerNameNoColor, "%s", pPlayer->userinfo.netname ); + sprintf( szOldPlayerNameNoColor, "%s", szOldPlayerName ); + + V_ColorizeString( szPlayerNameNoColor ); + V_ColorizeString( szOldPlayerNameNoColor ); + V_RemoveColorCodes( szPlayerNameNoColor ); + V_RemoveColorCodes( szOldPlayerNameNoColor ); + + // [K6] No duplicate names on the server. In case of name collision, we find a free alternate name for the player. + unsigned int counter = 0; + while ( SERVER_GetPlayerIndexFromName( szPlayerNameNoColor, true, false, g_lCurrentClient ) != MAXPLAYERS ) + { + counter++; + mysnprintf( pPlayer->userinfo.netname, MAXPLAYERNAME+1, "(%u) %s", counter, nameString.GetChars() ); + V_CleanPlayerName( pPlayer->userinfo.netname ); // truncating the name might have exposed trailing crap and god knows what else + sprintf( szPlayerNameNoColor, "%s", pPlayer->userinfo.netname ); + V_ColorizeString( szPlayerNameNoColor ); + V_RemoveColorCodes( szPlayerNameNoColor ); + } + if ( g_aClients[g_lCurrentClient].State == CLS_SPAWNED ) { - char szPlayerNameNoColor[MAXPLAYERNAME+1]; - char szOldPlayerNameNoColor[MAXPLAYERNAME+1]; - - sprintf( szPlayerNameNoColor, "%s", pPlayer->userinfo.netname ); - sprintf( szOldPlayerNameNoColor, "%s", szOldPlayerName ); - - V_ColorizeString( szPlayerNameNoColor ); - V_ColorizeString( szOldPlayerNameNoColor ); - V_RemoveColorCodes( szPlayerNameNoColor ); - V_RemoveColorCodes( szOldPlayerNameNoColor ); - if ( stricmp( szPlayerNameNoColor, szOldPlayerNameNoColor ) != 0 ) { - SERVER_Printf( PRINT_HIGH, "%s \\c-is now known as %s\n", szOldPlayerName, pPlayer->userinfo.netname ); + // [K6] Include the player's IP in server console. + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + SERVER_PrintfPlayer( PRINT_HIGH, ulIdx, "%s \\c-is now known as %s\n", szOldPlayerName, pPlayer->userinfo.netname ); + Printf( "%s \\c-(%s) is now known as %s\n", szOldPlayerName, NETWORK_AddressToString( SERVER_GetClient( g_lCurrentClient )->Address ), pPlayer->userinfo.netname ); // [RC] Update clients using the RCON utility. SERVER_RCON_UpdateInfo( SVRCU_PLAYERDATA ); @@ -2193,12 +2426,42 @@ if ( bKickPlayer ) { + + // [K6] Handle ZH ragequitting. + if (strstr(sv_hostname, "Zombie HORDE")) + { + if (players[g_lCurrentClient].mo == NULL) + Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname); + else if ((static_cast (SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) > 2) + && (players[g_lCurrentClient].mo->MaxHealth > 2000) && (CalcNumZombies() == 1) && (level.time < (3 * MINUTE * TICRATE))) + { + SERVER_Printf( PRINT_HIGH, "%s \\c-ragefloods as the first zombie! Temporarily banning %s and restarting the map.\n", players[g_lCurrentClient].userinfo.netname, players[g_lCurrentClient].userinfo.gender == GENDER_MALE ? "him" : players[g_lCurrentClient].userinfo.gender == GENDER_FEMALE ? "her" : "it" ); + SERVERBAN_BanPlayer( g_lCurrentClient, "10min", "10 minutes ban for rageflooding as the first zombie." ); + C_DoCommand ( "freezeall" ); + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + { + if ( playeringame[ulIdx] == false ) + continue; + players[ulIdx].health = 100; + } + char commandString[50]; + sprintf ( commandString, "wait %d;freezeall;changemap %s", 3 * TICRATE, level.mapname ); + AddCommandString ( commandString ); + return (false); + } + } + + SERVER_KickPlayer( g_lCurrentClient, kickReason.GetChars() ); return ( false ); } // Now send out the player's userinfo out to other players. - SERVERCOMMANDS_SetPlayerUserInfo( g_lCurrentClient, ulFlags, g_lCurrentClient, SVCF_SKIPTHISCLIENT ); + // [K6] We always send the player name to reduce desyncs with consoleplayer in case the player was renamed. + // [K6] There is still a desync though when the client silently changes his name to the one it thinks he has :( + if ( !(ulFlags & USERINFO_NAME) ) + ulFlags |= USERINFO_NAME; + SERVERCOMMANDS_SetPlayerUserInfo( g_lCurrentClient, ulFlags ); // Also, update the scoreboard. SERVERCONSOLE_UpdatePlayerInfo( g_lCurrentClient, UDF_NAME ); @@ -2233,7 +2496,7 @@ //***************************************************************************** // -void SERVER_ClientError( ULONG ulClient, ULONG ulErrorCode ) +void SERVER_ClientError( ULONG ulClient, ULONG ulErrorCode, FString clientVersion ) { NETWORK_WriteByte( &g_aClients[ulClient].PacketBuffer.ByteStream, SVCC_ERROR ); NETWORK_WriteByte( &g_aClients[ulClient].PacketBuffer.ByteStream, ulErrorCode ); @@ -2250,11 +2513,19 @@ Printf( "Incorrect version.\n" ); // Tell the client what version this server using. - NETWORK_WriteString( &g_aClients[ulClient].PacketBuffer.ByteStream, DOTVERSIONSTR ); + // [K6] Add link to zandronum.com explicitly here. It will be shown to 98d or older clients (though only if the server doesn't load ST compatibility wads) + // Normally this is handled on the client but old ST clients instruct the user to go to st.com... Better this than nothing. + char verString[256]; + if (strncmp(clientVersion, "0", 1) == 0) + // We have an old Skulltag client connecting! + sprintf(verString, "\\cnZandronum %s\\n\\c-Your version is: \\cnSkulltag %s\\n\\cg*** Skulltag has been updated to Zandronum! You are way out of the loop :)\\n\\cg*** Visit http://www.zandronum.com/ for updates.\\c-", DOTVERSIONSTR, clientVersion.GetChars()); + else + sprintf(verString, "\\cn%s\\n\\c-Your version is: \\cn%s\\c-", DOTVERSIONSTR, clientVersion.GetChars()); + NETWORK_WriteString( &g_aClients[ulClient].PacketBuffer.ByteStream, verString ); break; case NETWORK_ERRORCODE_BANNED: - Printf( "Client banned.\n" ); + Printf( "Client banned (%s)\n", SERVERBAN_GetBanList( )->getEntryComment( g_aClients[ulClient].Address ) ); // Tell the client why he was banned, and when his ban expires. NETWORK_WriteString( &g_aClients[ulClient].PacketBuffer.ByteStream, SERVERBAN_GetBanList( )->getEntryComment( g_aClients[ulClient].Address )); @@ -2774,11 +3045,29 @@ } } } + + // [K6] Add temporary bots one by one as needed. + if (( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) > 0 && sv_minplayers > 0 && + sv_minplayers > static_cast( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) && + static_cast( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) < sv_maxplayers && GAMEMODE_PreventPlayersFromJoining( ) == false && + !( level.flags2 & LEVEL2_ISLOBBY ) && !( level.flags2 & LEVEL2_NOBOTNODES )) && + ( sv_removetempbots == false || ( sv_removetempbots == true && SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS, false ) == 1 ))) + { + C_DoCommand( "addbot" ); + } } // Once every minute, update the level time. if (( gametic % ( 60 * TICRATE )) == 0 ) - SERVERCOMMANDS_SetMapTime( ); + { + SERVERCOMMANDS_SetMapTime( ); + + if (SERVER_STATISTIC_GetTotalSecondsElapsed() > 24 * DAY && SERVER_CountPlayers(false) == 0) + { + Printf("----------Scheduled emergency shutdown after 24 days----------\n"); + exit(-1); + } + } } //***************************************************************************** @@ -2814,10 +3103,19 @@ // (which we do when the player is spawned in SERVER_ConnectNewPlayer) if ( players[ulClient].userinfo.netname && ( SERVER_GetClient( ulClient )->State >= CLS_SPAWNED_BUT_NEEDS_AUTHENTICATION ) ) { + // [K6] Include the player's IP in server console. if ( ( gametic - g_aClients[ulClient].ulLastCommandTic ) == ( CLIENT_TIMEOUT * 35 ) ) - SERVER_Printf( PRINT_HIGH, "%s \\c-timed out.\n", players[ulClient].userinfo.netname ); + { + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + SERVER_PrintfPlayer( PRINT_HIGH, ulIdx, "%s \\c-timed out.\n", players[ulClient].userinfo.netname ); + Printf( "%s \\c-(%s) timed out.\n", players[ulClient].userinfo.netname, NETWORK_AddressToString( g_aClients[ulClient].Address )); + } else - SERVER_Printf( PRINT_HIGH, "client %s \\c-disconnected.\n", players[ulClient].userinfo.netname ); + { + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + SERVER_PrintfPlayer( PRINT_HIGH, ulIdx, "%s \\c-disconnected.\n", players[ulClient].userinfo.netname ); + Printf( "%s \\c-(%s) disconnected.\n", players[ulClient].userinfo.netname, NETWORK_AddressToString( g_aClients[ulClient].Address )); + } } else Printf( "%s \\c-disconnected.\n", NETWORK_AddressToString( g_aClients[ulClient].Address )); @@ -2889,9 +3187,12 @@ memset( &g_aClients[ulClient].Address, 0, sizeof( g_aClients[ulClient].Address )); g_aClients[ulClient].State = CLS_FREE; - g_aClients[ulClient].ulLastGameTic = 0; + g_aClients[ulClient].ulLastGameTime = 0; playeringame[ulClient] = false; + // [K6] Clear the player's password. + g_aClients[ulClient].szPassword[0] = '\0'; + // Run the disconnect scripts now that the player is leaving. if (( players[ulClient].bSpectating == false ) || ( players[ulClient].bDeadSpectator )) @@ -2972,7 +3273,7 @@ // Save the time we pinged them at. The client's ping can be calculated // subtracting the client's pong from this. for ( ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) - g_aClients[ulIdx].ulLastGameTic = I_MSTime( ); + g_aClients[ulIdx].ulLastGameTime = I_MSTime( ); } //***************************************************************************** @@ -3493,6 +3794,12 @@ SERVER_PrintfPlayer( PRINT_HIGH, ulIdx, szKickString ); } + + if ( SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS ) < 2 && g_bGameFrozen ) + { + C_DoCommand ( "freezeall" ); + g_bGameFrozen = 0; + } } //***************************************************************************** @@ -4196,6 +4503,53 @@ break; case CLC_QUIT: + // [K6] Handle ZH ragequitting. + if (strstr(sv_hostname, "Zombie HORDE")) + { + if (players[g_lCurrentClient].mo == NULL) + Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname); + else if ((static_cast (SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) > 2) + && (players[g_lCurrentClient].mo->MaxHealth > 2000) && (CalcNumZombies() == 1) && (level.time < (3 * MINUTE * TICRATE))) + { + SERVER_Printf( PRINT_HIGH, "%s \\c-ragequits as the first zombie! Temporarily banning %s and restarting the map.\n", players[g_lCurrentClient].userinfo.netname, players[g_lCurrentClient].userinfo.gender == GENDER_MALE ? "him" : players[g_lCurrentClient].userinfo.gender == GENDER_FEMALE ? "her" : "it" ); + SERVERBAN_BanPlayer( g_lCurrentClient, "10min", "10 minutes ban for ragequitting as the first zombie." ); + C_DoCommand ( "freezeall" ); + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + { + if ( playeringame[ulIdx] == false ) + continue; + players[ulIdx].health = 100; + } + char commandString[50]; + sprintf ( commandString, "wait %d;freezeall;changemap %s", 3 * TICRATE, level.mapname ); + AddCommandString ( commandString ); + return (true); + } + } + + // [K6] Handle WDI ragequitting. (is this still needed?) + else if (strstr(sv_hostname, "Who Dun It")) + { + if (players[g_lCurrentClient].mo == NULL) + Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname); + else if (players[g_lCurrentClient].mo->FindInventory( PClass::FindClass( "MurderMarker" ))) + { + GAMEMODE_RespawnDeadSpectatorsAndPopQueue( ); + Printf("Respawning players\n"); + } + } + + // [K6] Handle AOW spectate exploit. + else if (strstr(sv_hostname, "All Out War")) + { + if (players[g_lCurrentClient].mo == NULL) + Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname); + else if (g_aClients[g_lCurrentClient].lBombCrateTic + 5*TICRATE > gametic) // did the player just recently got bomb crate? + { + return ( false ); + } + } + // Client has left the game. SERVER_DisconnectClient( g_lCurrentClient, true, true ); break; @@ -4280,6 +4634,54 @@ return ( server_Taunt( pByteStream )); case CLC_SPECTATE: + // [K6] Handle ZH ragequitting. + if (strstr(sv_hostname, "Zombie HORDE")) + { + if (players[g_lCurrentClient].mo == NULL) + Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname); + else if ((static_cast (SERVER_CalcNumNonSpectatingPlayers( MAXPLAYERS )) > 2) + && (players[g_lCurrentClient].mo->MaxHealth > 2000) && (CalcNumZombies() == 1) && (level.time < (3 * MINUTE * TICRATE))) + { + SERVER_Printf( PRINT_HIGH, "%s \\c-ragespects as the first zombie! Temporarily banning %s and restarting the map.\n", players[g_lCurrentClient].userinfo.netname, players[g_lCurrentClient].userinfo.gender == GENDER_MALE ? "him" : players[g_lCurrentClient].userinfo.gender == GENDER_FEMALE ? "her" : "it" ); + SERVERBAN_BanPlayer( g_lCurrentClient, "10min", "10 minutes ban for ragespecting as the first zombie." ); + C_DoCommand ( "freezeall" ); + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + { + if ( playeringame[ulIdx] == false ) + continue; + players[ulIdx].health = 100; + } + char commandString[50]; + sprintf ( commandString, "wait %d;freezeall;changemap %s", 3 * TICRATE, level.mapname ); + AddCommandString ( commandString ); + return ( true ); + } + } + + // [K6] Handle WDI ragequitting. (is this still needed?) + else if (strstr(sv_hostname, "Who Dun It")) + { + if (players[g_lCurrentClient].mo == NULL) + Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname); + else if (players[g_lCurrentClient].mo->FindInventory( PClass::FindClass( "MurderMarker" ))) + { + GAMEMODE_RespawnDeadSpectatorsAndPopQueue( ); + Printf("Respawning players\n"); + } + } + + // [K6] Handle AOW spectate exploit. + else if (strstr(sv_hostname, "All Out War")) + { + if (players[g_lCurrentClient].mo == NULL) + Printf("WTF, no mo for player %s - what now?\n", players[g_lCurrentClient].userinfo.netname); + else if (g_aClients[g_lCurrentClient].lBombCrateTic + 5*TICRATE > gametic) // did the player just recently got bomb crate? + { + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "You cannot spectate at this moment (averting exploit)\n" ); + return ( false ); + } + } + // Client now wishes to spectate. return ( server_Spectate( pByteStream )); case CLC_REQUESTJOIN: @@ -4406,7 +4808,8 @@ //***************************************************************************** // // [RC] Finds the first player (or, optionally, bot) with the given name; returns MAXPLAYERS if none were found. -ULONG SERVER_GetPlayerIndexFromName( const char *pszName, bool bIgnoreColors, bool bReturnBots ) +// [K6] Added ulIgnoreIdx. +ULONG SERVER_GetPlayerIndexFromName( const char *pszName, bool bIgnoreColors, bool bReturnBots, ULONG ulIgnoreIdx ) { char szPlayerName[64]; @@ -4425,7 +4828,10 @@ if ( stricmp( bIgnoreColors ? szPlayerName : players[ulIdx].userinfo.netname, pszName ) == 0 ) { if ( !players[ulIdx].bIsBot || bReturnBots ) - return ulIdx; + { + if ( ulIdx != ulIgnoreIdx ) + return ulIdx; + } } } @@ -5038,6 +5444,7 @@ // static bool server_UpdateClientPing( BYTESTREAM_s *pByteStream ) { + ULONG ulPing; ulPing = NETWORK_ReadLong( pByteStream ); @@ -5047,7 +5454,7 @@ if ( ulPing > nowTime ) return false; - ULONG currentPing = (nowTime - ulPing); + ULONG currentPing = (nowTime - g_aClients[g_lCurrentClient].ulLastGameTime); const ULONG ticLength = 1000 / TICRATE; player_t *p = &players[g_lCurrentClient]; // [BB] Lag spike, reset the averaging. @@ -5161,6 +5568,9 @@ { ULONG ulIdx; + if ( server_CheckForClientCommandFlood ( g_lCurrentClient ) == true ) + return ( true ); + // Already a spectator! if ( PLAYER_IsTrueSpectator( &players[g_lCurrentClient] )) { @@ -5207,6 +5617,9 @@ // [BB/Spleen] Read in the client's gametic. ulGametic = NETWORK_ReadLong( pByteStream ); + if ( server_CheckForClientCommandFlood ( g_lCurrentClient ) == true ) + return ( true ); + // Player can't rejoin game if he's not spectating! if (( playeringame[g_lCurrentClient] == false ) || ( players[g_lCurrentClient].bSpectating == false )) return ( false ); @@ -5215,8 +5628,27 @@ if (( GAMEMODE_GetFlags( GAMEMODE_GetCurrentMode( )) & GMF_DEADSPECTATORS ) && ( players[g_lCurrentClient].bDeadSpectator )) return ( false ); + + if (sv_joinpasswordlist) { + // [Dusk] We're using a password list for joining. + if (!PASSLIST_CheckPassword (clientJoinPassword)) { + // not listed! + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "Bad join password, not listed.\n" ); + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "This server is using a password list (account) system. Contact administrators to request one.\n" ); + return (false); + } + + Printf ("%s \\c-uses password %s to join the game.\n", + players[g_lCurrentClient].userinfo.netname, clientJoinPassword.GetChars()); + + // Store the password. + FString tempPassword = clientJoinPassword; + tempPassword.ToLower(); + strcpy (g_aClients[g_lCurrentClient].szPassword, tempPassword.GetChars()); + } + // If we're forcing a join password, prevent him from joining if it doesn't match. - if ( server_CheckJoinPassword( clientJoinPassword ) == false ) + else if ( server_CheckJoinPassword( clientJoinPassword ) == false ) return ( false ); // [BB] Possibly the player needs to login before joining. @@ -5278,6 +5710,13 @@ // If the user password matches our PW, and we have a PW set, give him RCON access. pszUserPassword = NETWORK_ReadString( pByteStream ); + // kgsws - already admin check + if(g_aClients[g_lCurrentClient].bRCONAccess) + { + SERVER_PrintfPlayer( PRINT_HIGH, g_lCurrentClient, "You already have RCON access.\n" ); + return ( false ); + } + // [BB] If the client is flooding the server with commands, the client is // kicked and we don't need to handle the command. if ( server_CheckForClientCommandFlood ( g_lCurrentClient ) == true ) @@ -5473,6 +5912,8 @@ else { SERVER_Printf( PRINT_HIGH, "%s \\c-joined the \\c%c%s \\c-team.\n", players[g_lCurrentClient].userinfo.netname, V_GetColorChar( TEAM_GetTextColor( players[g_lCurrentClient].ulTeam )), TEAM_GetName( players[g_lCurrentClient].ulTeam )); + // [K6] Here we need to add 1 to count the newly joined player. Shrug. + BOTS_RemoveTempBots( true ); } if ( players[g_lCurrentClient].mo ) @@ -6250,6 +6691,36 @@ return false; } + +//***************************************************************************** +// [K6] +extern std::vector g_MapRotationEntries; +static void SendMaplistToPlayer( ULONG ulPlayer ) +{ + if ( g_MapRotationEntries.size( ) == 0 ) + SERVER_PrintfPlayer( PRINT_HIGH, ulPlayer, "The map rotation list is empty.\n" ); + else + { + SERVER_PrintfPlayer( PRINT_HIGH, ulPlayer, "Map rotation list: \n" ); + for ( ULONG ulIdx = 0; ulIdx < g_MapRotationEntries.size( ); ulIdx++ ) + SERVER_PrintfPlayer( PRINT_HIGH, ulPlayer, "%lu. %s - %s\n", ulIdx + 1, g_MapRotationEntries[ulIdx].pMap->mapname, g_MapRotationEntries[ulIdx].pMap->LookupLevelName( ).GetChars( )); + } +} +//***************************************************************************** +// [K6] +static ULONG CalcNumZombies( void ) +{ + ULONG ulNumZombies = 0; + + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + { + if ( playeringame[ulIdx] && (!PLAYER_IsTrueSpectator( &players[ulIdx] )) && players[ulIdx].mo->FindInventory( PClass::FindClass( "zombiemaker" ))) + ulNumZombies++; + } + + return ulNumZombies; +} + //***************************************************************************** // CONSOLE COMMANDS @@ -6617,3 +7088,218 @@ fflush (PacketLogFile); } #endif + +//***************************************************************************** +// [Dusk] Constructs the password list. +void PASSLIST_Construct () { + const char *szFileName = sv_passwordlistfile.GetGenericRep (CVAR_String).String; + FILE *fp = fopen (szFileName, "r"); + + // crap. + if (fp == NULL) { + Printf ("%s\n", GenerateCouldNotOpenFileErrorString ("PASSLIST_Construct", szFileName, errno).c_str()); + return; + } + + g_PasswordList.clear (); + + FString password; + int len; + int lineNumber = 1; + int passwordCount = 0; + bool bSlashed = false; + bool bCommenting = false; + char c; + + while (1) { + c = fgetc (fp); + if (feof (fp)) break; + + if (c == '/') { + if (bSlashed) { + // Double slash. Ignore text until newline + bCommenting = true; + bSlashed = false; + } else { + bSlashed = true; + } + continue; + } + + if (c == '\n') { + // Newline! + if (bCommenting) + bCommenting = false; + if (bSlashed) + bSlashed = false; + + len = strlen(password.GetChars()); + if (len > 0) { + password.ToUpper(); // Passwords are stored in uppercase + g_PasswordList.push_back (password); + passwordCount++; + } + + password = ""; + lineNumber++; + continue; + } + + if (c == '\r' || c == -1 || c == ' ' || bCommenting) { + continue; + } + + if (bSlashed) { + // We have one slash recorded and there is no second one. + // Add the ignored slash first now that it's known to be + // legit. + + password += "/"; + bSlashed = false; + } + + password += c; + } + fclose( fp ); +} + +bool PASSLIST_CheckPassword (FString pw) { + // We're assuming passwords are in uppercase. They are *now*, but... + pw.ToUpper(); + + std::vector::iterator i; + FString entry; + for (i = g_PasswordList.begin (); i < g_PasswordList.end (); i++) { + entry = *i; + if (!strcmp (pw.GetChars(), entry.GetChars())) return true; + } + + return false; +} + +// Goes through all the clients. Anyone who doesn't have a +// proper password that is in the list is booted out. +void PASSLIST_EnforcePasswords () { + // If we do not use the list, do not enforce it either. + if (!sv_passwordlist && !sv_joinpasswordlist) return; + + for (int i = 0; i < MAXPLAYERS; i++) { + if (!playeringame[i]) continue; + if (!PASSLIST_CheckPassword (FString (g_aClients[i].szPassword))) { + if (sv_passwordlist) { + SERVER_KickPlayer (i, "Password not in list"); + } else { + SERVER_KickPlayerFromGame (i, "Password not in list"); + strcpy (g_aClients[i].szPassword, ""); + } + } + } +} + +// List all passwords in the list +// [K6] If we use a colon delimiter in passwords (for user:pass), strip the pass part to RCON clients. +CCMD (viewpasswordlist) { + std::vector::iterator i; + FString entry; + std::string strpass; + int idx = 0; + int position = 0; + for (i = g_PasswordList.begin (); i < g_PasswordList.end (); i++) { + strpass = *i; + position = strpass.find(":"); + if (position != (-1)) + { + strpass.erase(position); + Printf ("%d. %s\n", idx, strpass.c_str()); + } + + idx++; + } + Printf ("End of password auth list.\n"); +} + +// [K6] +CCMD( kickall ) +{ + // Only the server can boot players! + if ( NETWORK_GetState( ) != NETSTATE_SERVER ) + return; + + if ( argv.argc( ) > 1 ) + SERVER_KickAllPlayers( argv[1] ); + + else + SERVER_KickAllPlayers( "" ); +} + +// kgsws +CCMD (freeze_idx) { + int i; + if ( NETWORK_GetState( ) != NETSTATE_SERVER ) + { + Printf("This is only for the server.\n"); + return; + } + if(argv.argc() <= 1) { + Printf("usage: freeze_idx \n"); + return; + } + i = atoi(argv[1]); + if(PLAYER_IsValidPlayer( i ) == false) return; + players[i].cheats ^= CF_TOTALLYFROZEN; + if(players[i].cheats & CF_TOTALLYFROZEN) + Printf("%s is frozen\n", players[i].userinfo.netname); + else + Printf("%s can move\n", players[i].userinfo.netname); + SERVERCOMMANDS_SetPlayerCheats( i, i, SVCF_ONLYTHISCLIENT ); +} + +// [K6] To be used for pausing the game +CCMD (freezeall) { + if ( NETWORK_GetState( ) != NETSTATE_SERVER ) + { + Printf("This is only for the server.\n"); + return; + } + if ( !g_bGameFrozen ) + { + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + { + if (playeringame[ulIdx] == false) + continue; + players[ulIdx].cheats |= CF_TOTALLYFROZEN | CF_GODMODE; + SERVERCOMMANDS_SetPlayerCheats( ulIdx, ulIdx, SVCF_ONLYTHISCLIENT ); + } + g_bGameFrozen = 1; + } + else + { + for ( ULONG ulIdx = 0; ulIdx < MAXPLAYERS; ulIdx++ ) + { + if (playeringame[ulIdx] == false) + continue; + players[ulIdx].cheats &= ~CF_TOTALLYFROZEN & ~CF_GODMODE; + SERVERCOMMANDS_SetPlayerCheats( ulIdx, ulIdx, SVCF_ONLYTHISCLIENT ); + } + g_bGameFrozen = 0; + } +} + +// kgsws +CCMD (say_bot) { + FString FullMessage; + int i; + if ( NETWORK_GetState( ) != NETSTATE_SERVER ) + { + Printf("This is only for the server.\n"); + return; + } + if(argv.argc() <= 1) { + Printf("usage: say_bot \n"); + return; + } + for(i = 1; i < argv.argc(); i++) + FullMessage.AppendFormat("%s ", argv[i]); + SERVER_Printf(PRINT_CHAT, "\\ci%s\n", FullMessage.GetChars()); // send message + SERVERCOMMANDS_Sound( CHAN_AUTO, "misc/chat2", 1.f, ATTN_NONE ); // play sound +} diff -r 3e26f5eca137 src/sv_main.h --- a/src/sv_main.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/sv_main.h Wed Oct 08 11:31:35 2014 +0200 @@ -218,7 +218,7 @@ ULONG ulLastCommandTic; // Used for calculating pings. - ULONG ulLastGameTic; + ULONG ulLastGameTime; // Can client remotely control server? bool bRCONAccess; @@ -297,9 +297,15 @@ // [RC] List of IP addresses that this client is ignoring. std::list IgnoredAddresses; + // [Dusk] The password the client connected with, if we're using a password list. + char szPassword[64]; + // [K6] Last tic we got some action from the client. Used to determine his presence. LONG lLastActionTic; + // [K6] When did the player receive a bomb crate on AOW? + LONG lBombCrateTic; + // [BB] Buffer storing all movement commands received from the client we haven't executed yet. TArray MoveCMDs; @@ -372,7 +378,7 @@ ULONG SERVER_CalcNumConnectedClients( void ); ULONG SERVER_CalcNumPlayers( void ); ULONG SERVER_CountPlayers( bool bCountBots ); -ULONG SERVER_CalcNumNonSpectatingPlayers( ULONG ulExcludePlayer ); +ULONG SERVER_CalcNumNonSpectatingPlayers( ULONG ulExcludePlayer, bool bCountBots = true ); void SERVER_CheckTimeouts( void ); void SERVER_GetPackets( void ); void SERVER_SendChatMessage( ULONG ulPlayer, ULONG ulMode, const char *pszString ); @@ -384,7 +390,7 @@ void SERVER_ConnectNewPlayer( BYTESTREAM_s *pByteStream ); bool SERVER_GetUserInfo( BYTESTREAM_s *pByteStream, bool bAllowKick ); void SERVER_ConnectionError( NETADDRESS_s Address, const char *pszMessage, ULONG ulErrorCode ); -void SERVER_ClientError( ULONG ulClient, ULONG ulErrorCode ); +void SERVER_ClientError( ULONG ulClient, ULONG ulErrorCode, FString clientVersion="" ); void SERVER_SendFullUpdate( ULONG ulClient ); void SERVER_WriteCommands( void ); bool SERVER_IsValidClient( ULONG ulClient ); @@ -431,7 +437,7 @@ void SERVER_ErrorCleanup( void ); void SERVER_ParsePacket( BYTESTREAM_s *pByteStream ); bool SERVER_ProcessCommand( LONG lCommand, BYTESTREAM_s *pByteStream ); -ULONG SERVER_GetPlayerIndexFromName( const char *pszName, bool bIgnoreColors, bool bReturnBots ); +ULONG SERVER_GetPlayerIndexFromName( const char *pszName, bool bIgnoreColors, bool bReturnBots, ULONG ulIgnoreIdx = MAXPLAYERS ); LONG SERVER_GetCurrentClient( void ); void SERVER_GiveInventoryToPlayer( const player_t *player, AInventory *pInventory ); void SERVER_HandleWeaponStateJump( ULONG ulPlayer, FState *pState, LONG lPosition ); @@ -468,6 +474,14 @@ void SERVER_STATISTIC_AddToInboundDataTransfer( ULONG ulNumBytes ); LONG SERVER_STATISTIC_GetCurrentInboundDataTransfer( void ); +// [Dusk] Password list +void PASSLIST_Construct (); +bool PASSLIST_CheckPassword (FString pw); +void PASSLIST_EnforcePasswords (); + +// [K6] +extern bool g_bGameFrozen; + //***************************************************************************** // EXTERNAL CONSOLE VARIABLES @@ -485,10 +499,12 @@ EXTERN_CVAR( Bool, sv_timestamp ); EXTERN_CVAR( Int, sv_timestampformat ); EXTERN_CVAR( Int, sv_colorstripmethod ); -EXTERN_CVAR( Bool, sv_disallowbots ); EXTERN_CVAR( Bool, sv_minimizetosystray ) EXTERN_CVAR( Int, sv_queryignoretime ) EXTERN_CVAR( Bool, sv_forcelogintojoin ) +EXTERN_CVAR( Bool, sv_passwordlist ); +EXTERN_CVAR( Bool, sv_joinpasswordlist ); +EXTERN_CVAR( String, sv_passwordlistfile ); // From sv_master.cpp EXTERN_CVAR( Bool, sv_updatemaster ); diff -r 3e26f5eca137 src/sv_master.cpp --- a/src/sv_master.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/sv_master.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -48,6 +48,9 @@ // //----------------------------------------------------------------------------- +#ifndef _WIN32 +#include +#endif #include "networkheaders.h" #include "c_dispatch.h" #include "cooperative.h" @@ -67,6 +70,8 @@ #include "sv_ban.h" #include "version.h" +#include "v_text.h" + //-------------------------------------------------------------------------------------------------------------------------------------------------- //-- VARIABLES ------------------------------------------------------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------------------------------------------------------------------- @@ -88,6 +93,8 @@ extern NETADDRESS_s g_LocalAddress; +char versionWithOS[256]; + //***************************************************************************** // CONSOLE VARIABLES @@ -123,6 +130,15 @@ g_lStoredQueryIPHead = 0; g_lStoredQueryIPTail = 0; +#ifndef _WIN32 + struct utsname u_name; + uname(&u_name); + if (uname(&u_name) < 0) + sprintf(versionWithOS, "%s", DOTVERSIONSTR_REV); //error, no data + else + sprintf(versionWithOS, "%s on %s %s", DOTVERSIONSTR_REV, u_name.sysname, u_name.release); // "Linux 2.6.32.5-amd64" or "FreeBSD 9.0-RELEASE" etc +#endif + // Call SERVER_MASTER_Destruct() when Skulltag closes. atterm( SERVER_MASTER_Destruct ); } @@ -289,24 +305,25 @@ } // Now, check to see if this IP has been banend from this server. + // [K6] Let's not. NETWORK_AddressToIPStringArray( Address, szAddress ); - if ( SERVERBAN_IsIPBanned( szAddress )) - { +// if ( SERVERBAN_IsIPBanned( szAddress )) +// { // Write our header. - NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, SERVER_LAUNCHER_BANNED ); +// NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, SERVER_LAUNCHER_BANNED ); // Send the time the launcher sent to us. - NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, ulTime ); +// NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, ulTime ); // Send the packet. - NETWORK_LaunchPacket( &g_MasterServerBuffer, Address ); +// NETWORK_LaunchPacket( &g_MasterServerBuffer, Address ); - if ( sv_showlauncherqueries ) - Printf( "Denied BANNED IP launcher challenge.\n" ); +// if ( sv_showlauncherqueries ) +// Printf( "Denied BANNED IP launcher challenge.\n" ); // Nothing more to do here. - return; - } +// return; +// } // This IP didn't exist in the list. and it wasn't banned. // So, add it, and keep it there for 10 seconds. @@ -325,8 +342,8 @@ // Send the time the launcher sent to us. NETWORK_WriteLong( &g_MasterServerBuffer.ByteStream, ulTime ); - // Send our version. - NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, DOTVERSIONSTR_REV ); + // Send our version. [K6] ...with OS + NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, versionWithOS ); // Send the information about the data that will be sent. ulBits = ulFlags; @@ -421,10 +438,10 @@ NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, NETWORK_GetIWAD( )); if ( ulBits & SQF_FORCEPASSWORD ) - NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, sv_forcepassword ); + NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, sv_forcepassword || sv_passwordlist ); if ( ulBits & SQF_FORCEJOINPASSWORD ) - NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, sv_forcejoinpassword ); + NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, sv_forcejoinpassword || sv_joinpasswordlist ); if ( ulBits & SQF_GAMESKILL ) NETWORK_WriteByte( &g_MasterServerBuffer.ByteStream, gameskill ); @@ -494,7 +511,12 @@ if ( playeringame[ulIdx] == false ) continue; - NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, players[ulIdx].userinfo.netname ); + // [K6/Dusk] IDE can't cope with color codes in "\c[bla]" format... so we'll have to get rid of them here. + FString cleanName (players[ulIdx].userinfo.netname); + if (cleanName.IndexOf (g_NewColors) != -1) + V_RemoveColorCodes(cleanName); + + NETWORK_WriteString( &g_MasterServerBuffer.ByteStream, cleanName.GetChars() ); if ( GAMEMODE_GetFlags(GAMEMODE_GetCurrentMode()) & GMF_PLAYERSEARNPOINTS ) NETWORK_WriteShort( &g_MasterServerBuffer.ByteStream, players[ulIdx].lPointCount ); else if ( GAMEMODE_GetFlags(GAMEMODE_GetCurrentMode()) & GMF_PLAYERSEARNWINS ) diff -r 3e26f5eca137 src/sv_rcon.cpp --- a/src/sv_rcon.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/sv_rcon.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -156,6 +156,10 @@ { int iIndex = -1; +// [K6] When a client fails rcon utility authorization, ignore him for a while. +if ( g_BadRequestFloodQueue.isFull( ) || g_BadRequestFloodQueue.addressInQueue( NETWORK_GetFromAddress( ))) + return; + switch ( lMessage ) { case CLRC_BEGINCONNECTION: diff -r 3e26f5eca137 src/timidity/timidity.h --- a/src/timidity/timidity.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/timidity/timidity.h Wed Oct 08 11:31:35 2014 +0200 @@ -117,7 +117,7 @@ #define PI 3.14159265358979323846 #endif -#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#if defined(__GNUC__) && !defined(__clang__) && (defined(__i386__) || defined(__x86_64__)) // [RH] MinGW's pow() function is terribly slow compared to VC8's // (I suppose because it's using an old version from MSVCRT.DLL). // On an Opteron running x86-64 Linux, this also ended up being about diff -r 3e26f5eca137 src/v_text.cpp --- a/src/v_text.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/v_text.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -713,7 +713,8 @@ } // Check the length again, as characters were removed. - if ( ulNonWhitespace < 3 ) + // [K6] The player's name really shouldn't be ""... + if ( ulNonWhitespace < 3 || stricmp( "", szColorlessName ) == 0 ) strcpy( pszStart, "Player" ); } diff -r 3e26f5eca137 src/v_text.h --- a/src/v_text.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/v_text.h Wed Oct 08 11:31:35 2014 +0200 @@ -74,6 +74,9 @@ #define TEXTCOLOR_CHAT "\034*" #define TEXTCOLOR_TEAMCHAT "\034!" +// [K6] New color codes start sequence. +const char g_NewColors[] = { TEXTCOLOR_ESCAPE, '[', '\0' }; + // [BC] New text functions. void V_ApplyCharArrayFunctionToFString ( FString &String, void (*CharArrayFunction) ( char *pszString ) ); void V_ColorizeString( char *pszString ); diff -r 3e26f5eca137 src/version.h --- a/src/version.h Sun Oct 05 21:47:04 2014 +0200 +++ b/src/version.h Wed Oct 08 11:31:35 2014 +0200 @@ -36,7 +36,12 @@ // The svnrevision.h is automatically updated to grab the revision of // of the current source tree so that it can be included with version numbers. -#include "svnrevision.h" +//#include "svnrevision.h" +#define SVN_REVISION_STRING "141005-1947M" +#define SVN_REVISION_NUMBER 1412538424 +#define HG_REVISION_HASH_STRING "-" +#define KPATCH_BUILD "46" + /** Lots of different version numbers **/ @@ -51,7 +56,8 @@ #define ZD_SVN_REVISION_NUMBER 1551 // [BB] The version string that includes revision / compatibility data. -#define DOTVERSIONSTR_REV DOTVERSIONSTR "-r" SVN_REVISION_STRING +// [K6] Include our kpatch version to mark the server as such. +#define DOTVERSIONSTR_REV DOTVERSIONSTR "-r" SVN_REVISION_STRING "-kpatch" KPATCH_BUILD // [BC] What version of ZDoom is this based off of? #define ZDOOMVERSIONSTR ZDVER_STRING"-"ZD_SVN_REVISION_STRING diff -r 3e26f5eca137 src/w_wad.cpp --- a/src/w_wad.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/w_wad.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -499,6 +499,7 @@ // [BC] Edited a little. void FWadCollection::AddFile (const char *filename, const char *data, int length, bool bLoadedAutomatically) { + FString infoString; WadFileRecord *wadinfo; MergedHeader header; LumpRecord* lump_p; @@ -528,7 +529,7 @@ // This is an in-memory WAD created from a WAD inside a .zip wadinfo = new WadFileRecord(data, length); } - Printf (" adding %s", filename); + infoString.AppendFormat (" adding %s", filename); startlump = NumLumps; @@ -555,7 +556,7 @@ wadinfo->Seek (header.wad.InfoTableOfs, SEEK_SET); wadinfo->Read (fileinfo, header.wad.NumLumps * sizeof(wadlump_t)); NumLumps += header.wad.NumLumps; - Printf (" (%u lumps)", header.wad.NumLumps); + infoString.AppendFormat (" (%u lumps)", header.wad.NumLumps); } else if (header.magic[0] == RFF_ID) { // This is a Blood RFF file @@ -607,7 +608,7 @@ lump_p->fullname = copystring ((char *)rff_p->IDontKnow); lump_p++; } - Printf (" (%u files)", header.rff.NumLumps); + infoString.AppendFormat (" (%u files)", header.rff.NumLumps); delete[] lumps; } else if (header.magic[0] == GRP_ID_0 && header.magic[1] == GRP_ID_1 && header.magic[2] == GRP_ID_2) @@ -639,7 +640,7 @@ lump_p->namespc = ns_global; lump_p++; } - Printf (" (%u files)", header.grp.NumLumps); + infoString.AppendFormat (" (%u files)", header.grp.NumLumps); delete[] lumps; } else if (header.magic[0] == ZIP_ID) @@ -751,7 +752,7 @@ // Resize the lump record array to its actual size NumLumps -= skipped; LumpInfo.Resize(NumLumps); - Printf (" (%d files)", LittleShort(info.NumEntries) - skipped); + infoString.AppendFormat (" (%d files)", LittleShort(info.NumEntries) - skipped); // Entries in Zips are sorted alphabetically. qsort(&LumpInfo[startlump], NumLumps - startlump, sizeof(LumpRecord), lumpcmp); @@ -767,7 +768,7 @@ { delete arc; delete wadinfo; - Printf("\n%s: ", filename); + Printf("\n%s:\n", filename); if (res == SZ_ERROR_UNSUPPORTED) { Printf("Decoder does not support this archive\n"); @@ -831,7 +832,7 @@ // Resize the lump record array to its actual size NumLumps -= skipped; LumpInfo.Resize(NumLumps); - Printf (" (%u files)", arc->DB.db.NumFiles - skipped); + infoString.AppendFormat (" (%u files)", arc->DB.db.NumFiles - skipped); // Entries in archives are sorted alphabetically qsort(&LumpInfo[startlump], NumLumps - startlump, sizeof(LumpRecord), lumpcmp); @@ -846,7 +847,7 @@ uppercopy(singleinfo.Name, name); NumLumps++; } - Printf ("\n"); + Printf ("%s\n", infoString.GetChars()); // Fill in lumpinfo if (header.magic[0] != RFF_ID && diff -r 3e26f5eca137 src/win32/i_system.cpp --- a/src/win32/i_system.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/win32/i_system.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -107,6 +107,8 @@ const IWADInfo *DoomStartupInfo; +extern char versionWithOS[256]; + int (*I_GetTime) (bool saveMS); int (*I_WaitForTic) (int); void (*I_FreezeTime) (bool frozen); @@ -294,11 +296,17 @@ // [RH] Detect the OS the game is running under void I_DetectOS (void) { - OSVERSIONINFO info; +// from ZDoom -> + OSVERSIONINFOEX info; const char *osname; - info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx (&info); + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if (!GetVersionEx((OSVERSIONINFO *)&info)) + { + // Retry with the older OSVERSIONINFO structure. + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx((OSVERSIONINFO *)&info); + } switch (info.dwPlatformId) { @@ -336,9 +344,42 @@ osname = "Server 2003"; } } - else if (info.dwMajorVersion == 6 && info.dwMinorVersion == 0) + else if (info.dwMajorVersion == 6) { - osname = "Vista"; + if (info.dwMinorVersion == 0) + { + if (info.wProductType == VER_NT_WORKSTATION) + { + osname = "Vista"; + } + else + { + osname = "Server 2008"; + } + } + if (info.dwMinorVersion == 1) + { + if (info.wProductType == VER_NT_WORKSTATION) + { + osname = "7"; + } + else + { + osname = "Server 2008 R2"; + } + } +// from ZDoom <- + else if (info.dwMinorVersion == 2) + { + if (info.wProductType == VER_NT_WORKSTATION) + { + osname = "8"; + } + else + { + osname = "Server 2012"; + } + } } break; @@ -354,13 +395,19 @@ osname, info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber & 0xffff, info.szCSDVersion); + sprintf(versionWithOS, "%s on Windows %s (%lu.%lu.%lu)", DOTVERSIONSTR_REV, osname, + info.dwMajorVersion, info.dwMinorVersion, + info.dwBuildNumber & 0xffff); } else { - Printf ("OS: Windows %s %lu.%lu (Build %lu)\n %s\n", + Printf ("OS: Windows %s (NT %lu.%lu) Build %lu\n %s\n", // from ZDoom osname, info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber, info.szCSDVersion); + sprintf(versionWithOS, "%s on Windows %s (%lu.%lu.%lu)", DOTVERSIONSTR_REV, osname, + info.dwMajorVersion, info.dwMinorVersion, + info.dwBuildNumber); } if (OSPlatform == os_unknown) diff -r 3e26f5eca137 src/x86.cpp --- a/src/x86.cpp Sun Oct 05 21:47:04 2014 +0200 +++ b/src/x86.cpp Wed Oct 08 11:31:35 2014 +0200 @@ -212,18 +212,19 @@ Printf(" Family %d, Model %d, Stepping %d\n", cpu->Family, cpu->Model, cpu->Stepping); } - Printf(" Features:"); - if (cpu->bMMX) Printf(" MMX"); - if (cpu->bMMXPlus) Printf(" MMX+"); - if (cpu->bSSE) Printf(" SSE"); - if (cpu->bSSE2) Printf(" SSE2"); - if (cpu->bSSE3) Printf(" SSE3"); - if (cpu->bSSSE3) Printf(" SSSE3"); - if (cpu->bSSE41) Printf(" SSE4.1"); - if (cpu->bSSE42) Printf(" SSE4.2"); - if (cpu->b3DNow) Printf(" 3DNow!"); - if (cpu->b3DNowPlus) Printf(" 3DNow!+"); - Printf ("\n"); + FString infoString; + infoString.AppendFormat(" Features:"); + if (cpu->bMMX) infoString.AppendFormat(" MMX"); + if (cpu->bMMXPlus) infoString.AppendFormat(" MMX+"); + if (cpu->bSSE) infoString.AppendFormat(" SSE"); + if (cpu->bSSE2) infoString.AppendFormat(" SSE2"); + if (cpu->bSSE3) infoString.AppendFormat(" SSE3"); + if (cpu->bSSSE3) infoString.AppendFormat(" SSSE3"); + if (cpu->bSSE41) infoString.AppendFormat(" SSE4.1"); + if (cpu->bSSE42) infoString.AppendFormat(" SSE4.2"); + if (cpu->b3DNow) infoString.AppendFormat(" 3DNow!"); + if (cpu->b3DNowPlus) infoString.AppendFormat(" 3DNow!+"); + Printf ("%s\n", infoString.GetChars()); } }