Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Zandronum source code
- * Copyright (C) 2012 Santeri Piippo
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of the Skulltag Development Team nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- * 4. Redistributions in any form must be accompanied by information on how to
- * obtain complete source code for the software and any accompanying
- * software that uses the software. The source code must either be included
- * in the distribution or be available for no more than the cost of
- * distribution plus a nominal fee, and must be freely redistributable
- * under reasonable conditions. For an executable file, complete source
- * code means the source code for all modules it contains. It does not
- * include source code for modules or files that typically accompany the
- * major components of the operating system on which the executable file
- * runs.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Filename: votedef.cpp
- *
- * Description: VOTEDEF parser, vote type manager
- *
- * -----------------------------------------------------------------------------
- */
- #include "doomdef.h"
- #include "info.h"
- #include "tarray.h"
- #include "votedef.h"
- #include "network.h"
- #include "sv_main.h"
- #include "g_level.h"
- #include "v_text.h"
- #include "c_dispatch.h"
- #include "maprotation.h"
- #include "c_cvars.h"
- // ============================================================================
- // Definitions for the static members
- TArray<VoteDef*> VoteDef::k_Defs;
- NETADDRESS_s VoteDef::k_IPStorage[MAXPLAYERS];
- const char* VoteDef::k_ParameterTokens[] =
- {
- "INT",
- "FLOAT",
- "PLAYER",
- "MAP",
- NULL
- };
- const char* VoteDef::k_PropertyTokens[] =
- {
- "ARGUMENT",
- "ACSSCRIPT",
- "DESCRIPTION",
- "FLAGS",
- "FORBIDCVAR",
- "VALUERANGE",
- NULL
- };
- const char* VoteDef::k_FlagTokens[] =
- {
- "NOPASSEDLIMIT",
- "NOTSELF",
- "NOTADMIN",
- "PLAYERINDEX",
- "PLAYERINGAME",
- NULL
- };
- const char* VoteDef::k_NativeNames[] =
- {
- "KICK",
- "KICKFROMGAME",
- "CHANGEMAP",
- "MAP",
- "FRAGLIMIT",
- "TIMELIMIT",
- "POINTLIMIT",
- "DUELLIMIT",
- "WINLIMIT",
- NULL
- };
- // ============================================================================
- // VoteDef constructor
- VoteDef::VoteDef( const FString& name, VoteDef::NativeType native ) :
- m_name( name ),
- m_script( 0 ),
- m_flags( 0 ),
- m_argType( NumArgTypes ),
- m_min( INT_MAX ),
- m_max( INT_MAX ),
- m_native( native ) {}
- // ============================================================================
- // VoteDef destructor
- VoteDef::~VoteDef( ) {}
- // ============================================================================
- // Inherit properties from this vote type to the other type
- void VoteDef::copyTo( VoteDef* other ) const
- {
- other->m_description = m_description;
- other->m_argType = m_argType;
- other->m_flags = m_flags;
- other->m_min = m_min;
- other->m_max = m_max;
- other->m_script = m_script;
- // Only copy the forbid cvar name if it was not automatically generated.
- if ( !m_autogenForbidCVar )
- other->m_forbidCVar = m_forbidCVar;
- }
- // ============================================================================
- // Initializes VOTEDEF. This is called at startup.
- void VoteDef::init( )
- {
- if ( Wads.CheckNumForName( "VOTEDEF" ) == -1 )
- return;
- // Startup message. :)
- Printf( "VoteDef: Loading vote type definitions\n" );
- atexit( VoteDef::deinit );
- int lump, lastlump;
- lastlump = 0;
- while (( lump = Wads.FindLump( "VOTEDEF", &lastlump )) != -1 )
- parseLump( lump );
- }
- // ============================================================================
- // De-initializes the votedef stuff.
- void VoteDef::deinit( )
- {
- for ( unsigned i = 0; i < k_Defs.Size( ); ++i )
- delete k_Defs[i];
- k_Defs.Clear( );
- }
- // ============================================================================
- // Parse a single VOTEDEF lump.
- void VoteDef::parseLump( int lump )
- {
- FScanner sc( lump );
- while ( sc.GetToken( ))
- {
- bool isnative = false;
- // 'votetype' is the only command we have right now.
- if ( !sc.Compare( "VOTETYPE" ))
- sc.ScriptError( "Expected `votetype`, got %s", sc.String );
- // See if this vote type is native.
- NativeType native = NumNatives;
- if ( sc.CheckToken( TK_Native ))
- isnative = true;
- sc.MustGetToken( TK_Identifier );
- FString votename = sc.String;
- FString forbidCVarName;
- if ( isnative == true )
- native = (NativeType) sc.MustMatchString( k_NativeNames );
- // Check that it's not already defined
- if ( VoteDef::findByName( votename ))
- sc.ScriptError( "Vote type `%s` is already defined", votename.GetChars( ));
- VoteDef* type = new VoteDef( votename, native );
- if ( sc.CheckToken( ':' )) {
- // We're inheriting this vote type from another. Copy the properties
- // of the other vote here.
- sc.MustGetToken( TK_Identifier );
- VoteDef* src = VoteDef::findByName( sc.String );
- if ( !src )
- sc.ScriptError( "Cannot inherit from undefined vote type `%s`", sc.String );
- src->copyTo( type );
- }
- sc.MustGetToken( '{' );
- for ( ;; )
- {
- // The file shouldn't end in the middle of a block
- if ( !sc.GetToken( ))
- sc.ScriptError( "Unexpected end of file, did you miss a '}'?" );
- if ( sc.Compare( "}" ))
- break; // terminating brace
- int prop = sc.MustMatchString( k_PropertyTokens );
- // Flags uses block format, everything else needs '='
- if ( prop != VTPROP_FLAGS )
- sc.MustGetToken( '=' );
- switch ( prop )
- {
- case VTPROP_ARGUMENT:
- if ( sc.CheckToken( TK_Float ))
- type->m_argType = Float;
- else if ( sc.CheckToken( TK_Int ))
- type->m_argType = Int;
- else {
- sc.MustGetToken( TK_Identifier );
- type->m_argType = (ArgumentType) sc.MustMatchString( k_ParameterTokens );
- }
- break;
- case VTPROP_FLAGS:
- sc.MustGetToken( '{' );
- while ( !sc.CheckToken( '}' ))
- {
- // Get the sign
- bool set = false;
- if ( sc.CheckToken( '+' ))
- set = true;
- else if ( !sc.CheckToken( '-' ))
- sc.ScriptError( "Badly formed flag (expected + or -)" );
- // Name of the flag
- sc.MustGetToken( TK_Identifier );
- int flag = sc.MustMatchString( k_FlagTokens );
- // Set or unset the flag now.
- if ( set )
- type->m_flags |= 1 << flag;
- else
- type->m_flags &= ~( 1 << flag );
- }
- break;
- case VTPROP_FORBIDCVAR:
- sc.MustGetToken( TK_StringConst );
- forbidCVarName = sc.String;
- break;
- case VTPROP_ACSSCRIPT:
- sc.MustGetToken( TK_IntConst );
- type->m_script = sc.Number;
- break;
- case VTPROP_DESCRIPTION:
- sc.MustGetToken( TK_StringConst );
- type->m_description = sc.String;
- break;
- case VTPROP_VALUERANGE:
- if ( !sc.CheckToken ( TK_FloatConst ))
- sc.MustGetToken( TK_IntConst );
- type->m_min = sc.Float;
- sc.MustGetToken( ',' );
- if ( !sc.CheckToken( TK_FloatConst ))
- sc.MustGetToken( TK_IntConst );
- type->m_max = sc.Float;
- break;
- }
- if ( prop != VTPROP_FLAGS )
- sc.MustGetToken( ';' );
- }
- if ( type->m_native == NumNatives && type->m_script == 0 )
- sc.ScriptError( "Non-native vote type '%s' needs a script number.",
- type->m_name.GetChars( ));
- // Int and float types must have a value range.
- if (( type->argType() == Int || type->argType() == Float ) &&
- type->min() == INT_MAX && type->max() == INT_MAX )
- {
- sc.ScriptError( "Vote type \"%s\" needs a value range.", type->name( ).GetChars( ));
- }
- // Generate the forbid CVar name if it's not given. Note that the default
- // is different from the usual: sv_vote_no<name>. This way we can group
- // new autogenerated forbid cvars within sv_vote_* and not let them flow
- // into the sv_ namespace.
- if ( forbidCVarName.IsEmpty( ))
- {
- forbidCVarName.Format( "sv_vote_no%s", type->m_name.GetChars( ));
- type->m_autogenForbidCVar = true;
- }
- if ( type->m_native == NumNatives && type->m_argType == MapType )
- sc.ScriptError( "Only native types may use a map argument. " );
- // If the CVar does not exist, create it now.
- if (( type->m_forbidCVar = FindCVar( forbidCVarName, NULL )) == NULL )
- {
- FBoolCVar* var = new FBoolCVar( forbidCVarName, false, CVAR_AUTO | CVAR_ARCHIVE );
- var->SetArchiveBit( );
- type->m_forbidCVar = var;
- }
- k_Defs.Push( type );
- }
- }
- #define APPLY_ERROR( ... ) \
- { \
- result.error.Format( __VA_ARGS__ ); \
- return false; \
- }
- // ============================================================================
- // Apply given input data, performs validity checks and compiles the vote
- // command string, or argument for the vote's ACS script.
- bool VoteDef::applyCommand( VoteDef::ApplyInput input, VoteDef::ApplyResult& result )
- {
- FString needle;
- VoteDef* type = k_Defs[input.type];
- result.error = "";
- result.arg = 0;
- long argvalue;
- FString mapname;
- // Check that we got the proper parameter
- if ( type->m_argType != NumArgTypes )
- {
- if ( input.arg.IsEmpty( ))
- APPLY_ERROR( "argument not given" );
- // The variable contens in various types
- FString fsval = input.arg;
- const char* sval = fsval.GetChars( );
- long lval = atol( sval );
- double fval = atof( sval );
- unsigned uval = lval;
- unsigned idx;
- switch ( type->m_argType )
- {
- case Int:
- if ( !fsval.IsInt( ) || lval < type->m_min || lval > type->m_max )
- APPLY_ERROR( "Value must be a number between %d and %d, was %s",
- (int) type->min( ), (int) type->max( ), sval );
- argvalue = lval;
- break;
- case Float:
- if ( !fsval.IsFloat( ))
- APPLY_ERROR( "Value must be a decimal number between %f and %f, was %s",
- type->min( ), type->max( ), sval );
- argvalue = fval * FRACUNIT; // Convert to fixed
- break;
- case Player:
- // Find the client by name, unless PLAYERINDEX is given
- if ( type->m_flags & PlayerIndex ) {
- if ( !fsval.IsInt( ) || lval < 0 || lval >= MAXPLAYERS )
- APPLY_ERROR( "Expected player index, got %s", sval );
- idx = lval;
- } else
- idx = SERVER_GetPlayerIndexFromName( sval, true, false );
- // Must be valid
- if ( !SERVER_IsValidClient( idx ))
- APPLY_ERROR( "Player %s does not exist", sval );
- // Must be in-game if PLAYERINGAME is given
- if ( type->m_flags & PlayerInGame && players[idx].bSpectating )
- APPLY_ERROR( "%s is a spectator", sval );
- // [BB] Don't allow anyone to kick somebody who is on the admin list.
- // [Dusk] Brought this check here and flagged
- if (( type->m_flags & NotAdmin ) && playerIsAdmin( idx ))
- APPLY_ERROR( "%s is a server administrator!", sval );
- // Not self if NOTSELF is set
- if (( type->m_flags & NotSelf ) && uval == input.caller )
- APPLY_ERROR( "Cannot invoke this on yourself" );
- argvalue = idx;
- break;
- case MapType:
- if ( !P_CheckIfMapExists( input.arg ))
- APPLY_ERROR( "No such map '%s'", input.arg.GetChars( ));
- if ( NETWORK_GetState( ) == NETSTATE_SERVER && sv_maprotation &&
- !MAPROTATION_IsMapInRotation( input.arg ))
- {
- APPLY_ERROR( "Map `%s` is not in rotation\n", input.arg.GetChars( ));
- }
- mapname = input.arg;
- break;
- case NumArgTypes:
- break;
- }
- }
- // Handle native types
- if ( type->m_native != NumNatives )
- {
- // [Dusk] Write the kick reason into the ban reason, [BB] but only if it's not empty.
- FString votekickblurb;
- votekickblurb.Format( "\"Vote kick: %d to %d: %s\"",
- input.yes, input.no,
- input.reason.IsNotEmpty( ) ? input.reason.GetChars( ) : "No reason given" );
- switch ( type->m_native )
- {
- case Kick:
- // [BB, RC] If the vote is a kick vote, we have to rewrite g_VoteCommand to both use
- // the stored IP, and temporarily ban it.
- result.command.Format( "addban %s 10min %s",
- NETWORK_AddressToStringIgnorePort( SERVER_GetClient( argvalue )->Address ),
- votekickblurb.GetChars( ));
- break;
- case KickFromGame:
- result.command.Format( "kickfromgame_idx %ld %s", argvalue, votekickblurb.GetChars( ));
- break;
- case ChangeMap:
- case Map:
- result.command.Format( "%s %s", type->m_name.GetChars( ), mapname.GetChars( ));
- break;
- case FragLimit:
- case DuelLimit:
- case WinLimit:
- case PointLimit:
- result.command.Format( "%s %ld", type->m_name.GetChars( ), argvalue );
- break;
- case TimeLimit:
- result.command.Format( "timelimit %f", atof( input.arg.GetChars( )));
- break;
- case NumNatives:
- break;
- }
- }
- else
- result.arg = argvalue;
- // Application successful.
- return true;
- }
- // ============================================================================
- // Make a record of this vote for later comparing
- FString VoteDef::makeSummary( const VoteDef::ApplyInput& input )
- {
- VoteDef* type = k_Defs[input.type];
- FString record = type->m_name + " ";
- int vartype = type->m_argType;
- FString fsval = input.arg;
- const char* sval = fsval.GetChars( );
- long lval = atol( sval );
- unsigned idx;
- switch ( vartype )
- {
- case Int:
- case Float:
- case MapType:
- record += sval;
- break;
- case Player:
- idx = ( type->m_flags & PlayerIndex ) ?
- lval : SERVER_GetPlayerIndexFromName( sval, true, false );
- // Kick votes need the IP address of the victim.
- if ( type->m_native == Kick )
- record += NETWORK_AddressToStringIgnorePort( k_IPStorage[lval] );
- else
- record += sval;
- break;
- default:
- break;
- }
- return record;
- }
- // ============================================================================
- // Store everybody's IPs so that they don't get lost when the command is
- // actually applied. The client could disconnect in the midst of a kick vote...
- void VoteDef::storeIPAddresses( )
- {
- for ( unsigned i = 0; i < MAXPLAYERS; i++ )
- {
- if ( !SERVER_IsValidClient( i ))
- continue;
- k_IPStorage[i] = SERVER_GetClient( i )->Address;
- }
- }
- // ============================================================================
- // Get usage info of calling this vote.
- FString VoteDef::getUsage( ) const
- {
- FString cmd = "callvote ";
- FString arg;
- // Name of command
- cmd += name( );
- // Parameters
- if ( argType( ) != NumArgTypes )
- {
- arg = k_ParameterTokens[argType( )];
- arg.ToLower( );
- cmd.AppendFormat( " <%s>", arg.GetChars( ));
- }
- // Reasons
- cmd += " [reason]";
- return cmd;
- }
- // ============================================================================
- VoteDef* VoteDef::findByIndex( unsigned index )
- {
- if ( index >= k_Defs.Size( ))
- return NULL;
- return k_Defs[index];
- }
- // ============================================================================
- VoteDef* VoteDef::findByName( FString name )
- {
- for ( unsigned i = 0; i < k_Defs.Size( ); i++ )
- if ( !k_Defs[i]->m_name.CompareNoCase( name ))
- return k_Defs[i];
- return NULL;
- }
- // ============================================================================
- int VoteDef::getIndex( ) const
- {
- for ( unsigned i = 0; i < k_Defs.Size( ); i++ )
- if ( k_Defs[i] == this )
- return i;
- return -1;
- }
- // ============================================================================
- const ULONG& VoteDef::scriptNum( ) const
- {
- return m_script;
- }
- // ============================================================================
- const VoteDef::NativeType& VoteDef::nativeType( ) const
- {
- return m_native;
- }
- // ============================================================================
- const FString& VoteDef::name( ) const
- {
- return m_name;
- }
- // ============================================================================
- const VoteDef::ArgumentType& VoteDef::argType( ) const
- {
- return m_argType;
- }
- // ============================================================================
- const DWORD& VoteDef::flags( ) const
- {
- return m_flags;
- }
- // ============================================================================
- FBaseCVar* VoteDef::forbidCVar( ) const
- {
- return m_forbidCVar;
- }
- // ============================================================================
- unsigned VoteDef::numTypes( )
- {
- return k_Defs.Size( );
- }
- // ============================================================================
- const float& VoteDef::min( ) const
- {
- return m_min;
- }
- // ============================================================================
- const float& VoteDef::max( ) const
- {
- return m_max;
- }
- // ============================================================================
- const FString& VoteDef::description() const
- {
- return m_description;
- }
- // ============================================================================
- // Is the given player in the adminlist?
- bool VoteDef::playerIsAdmin( ULONG idx )
- {
- NETADDRESS_s address = SERVER_GetClient( idx )->Address;
- return SERVER_GetAdminList( )->isIPInList( address );
- }
- // ============================================================================
- // List all vote types and information on how to use them.
- CCMD( listvotetypes )
- {
- for ( unsigned i = 0; i < VoteDef::numTypes( ); i++ )
- {
- VoteDef* pVoteType = VoteDef::findByIndex( i );
- FString message;
- message.Format( "\\c[Orange]%s\\c-\n", pVoteType->getUsage( ).GetChars( ));
- // Add the description, if we have it.
- FString descr = pVoteType->description( );
- if ( descr.IsNotEmpty( )) {
- message.AppendFormat( "%s\n", descr.GetChars( ));
- // Add a second newline to separate the vote types, but not if this
- // is the last one left.
- if ( i < VoteDef::numTypes() - 1 )
- message += "\n";
- }
- Printf( "%s", message.GetChars( ));
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement