src/engine/eVoter.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007 
00008 **************************************************************************
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023   
00024 ***************************************************************************
00025 
00026 */
00027 
00028 #include "rSDL.h"
00029 
00030 #include "eVoter.h"
00031 
00032 #include "tMemManager.h"
00033 #include "tSysTime.h"
00034 #include "tDirectories.h"
00035 
00036 #include "uMenu.h"
00037 
00038 #include "nConfig.h"
00039 #include "nServerInfo.h"
00040 
00041 #include "rConsole.h"
00042 
00043 #include "ePlayer.h"
00044 #include "eGrid.h"
00045 
00046 #ifndef DEDICATED
00047 // use server controlled votes (just for the client, to avoid UPGRADE messages)
00048 static bool se_useServerControlledKick = false;
00049 static nSettingItem< bool > se_usc( "VOTE_USE_SERVER_CONTROLLED_KICK", se_useServerControlledKick );
00050 #endif
00051 
00052 // basic vote timeout value
00053 static unsigned short se_votingItemID = 0;
00054 static float se_votingTimeout = 300.0f;
00055 static nSettingItem< float > se_vt( "VOTING_TIMEOUT", se_votingTimeout );
00056 
00057 // additional timeout for every voter present
00058 static float se_votingTimeoutPerVoter = 0.0f;
00059 static nSettingItem< float > se_vtp( "VOTING_TIMEOUT_PER_VOTER", se_votingTimeoutPerVoter );
00060 
00061 static float se_votingStartDecay = 60.0f;
00062 static nSettingItem< float > se_vsd( "VOTING_START_DECAY", se_votingStartDecay );
00063 
00064 static float se_votingDecay = 60.0f;
00065 static nSettingItem< float > se_vd( "VOTING_DECAY", se_votingDecay );
00066 
00067 // spam level of issuing a vote
00068 static float se_votingSpamIssue = 1.0f;
00069 static nSettingItem< float > se_vsi( "VOTING_SPAM_ISSUE", se_votingSpamIssue );
00070 
00071 // spam level of getting your vote rejected
00072 static float se_votingSpamReject = 5.0f;
00073 static nSettingItem< float > se_vsr( "VOTING_SPAM_REJECT", se_votingSpamReject );
00074 
00075 static bool se_allowVoting = false;
00076 static tSettingItem< bool > se_av( "ALLOW_VOTING", se_allowVoting );
00077 
00078 static bool se_allowVotingSpectator = false;
00079 static tSettingItem< bool > se_avo( "ALLOW_VOTING_SPECTATOR", se_allowVotingSpectator );
00080 
00081 // number of rounds to suspend
00082 static int se_suspendRounds = 5;
00083 static tSettingItem< int > se_sr( "VOTING_SUSPEND_ROUNDS", se_suspendRounds );
00084 
00085 static int se_minVoters = 3;
00086 static tSettingItem< int > se_mv( "MIN_VOTERS", se_minVoters );
00087 
00088 // the number set here always acts as votes against a change.
00089 static int se_votingBias = 0;
00090 static tSettingItem< int > se_vb( "VOTING_BIAS", se_votingBias );
00091 
00092 // the number set here always acts as additional votes against a kick vote.
00093 static int se_votingBiasKick = 0;
00094 static tSettingItem< int > se_vbKick( "VOTING_BIAS_KICK", se_votingBiasKick );
00095 
00096 // the number set here always acts as additional votes against a suspend vote.
00097 static int se_votingBiasSuspend = 0;
00098 static tSettingItem< int > se_vbSuspend( "VOTING_BIAS_SUSPEND", se_votingBiasSuspend );
00099 
00100 // the number set here always acts as additional votes against a suspend vote.
00101 static int se_votingBiasInclude = 0;
00102 static tSettingItem< int > se_vbInclude( "VOTING_BIAS_INCLUDE", se_votingBiasInclude );
00103 
00104 // the number set here always acts as additional votes against a command vote.
00105 static int se_votingBiasCommand = 0;
00106 static tSettingItem< int > se_vbCommand( "VOTING_BIAS_COMMAND", se_votingBiasCommand );
00107 
00108 // voting privacy level. -2 means total disclosure, +2 total secrecy.
00109 static int se_votingPrivacy = 1;
00110 static tSettingItem< int > se_vp( "VOTING_PRIVACY", se_votingPrivacy );
00111 
00112 // maximum number of concurrent votes
00113 static int se_maxVotes = 5;
00114 static tSettingItem< int > se_maxVotesSI( "MAX_VOTES", se_maxVotes );
00115 
00116 // maximum number of concurrent votes per voter
00117 static int se_maxVotesPerVoter = 2;
00118 static tSettingItem< int > se_maxVotesPerVoterSI( "MAX_VOTES_PER_VOTER", se_maxVotesPerVoter );
00119 
00120 // time between kick votes against the same target in seconds
00121 static int se_minTimeBetweenKicks = 300;
00122 static tSettingItem< int > se_minTimeBetweenKicksSI( "VOTING_KICK_TIME", se_minTimeBetweenKicks );
00123 
00124 // time between harmful votes against the same target in seconds
00125 static int se_minTimeBetweenHarms = 180;
00126 static tSettingItem< int > se_minTimeBetweenHarmsSI( "VOTING_HARM_TIME", se_minTimeBetweenHarms );
00127 
00128 // time between name changes and you being allowed to issue votes again
00129 static int se_votingMaturity = 300;
00130 static tSettingItem< int > se_votingMaturitySI( "VOTING_MATURITY", se_votingMaturity );
00131 
00132 #ifdef KRAWALL_SERVER
00133 // minimal access level for kick votes
00134 static tAccessLevel se_accessLevelVoteKick = tAccessLevel_Program;
00135 static tSettingItem< tAccessLevel > se_accessLevelVoteKickSI( "ACCESS_LEVEL_VOTE_KICK", se_accessLevelVoteKick );
00136 
00137 // minimal access level for suspend votes
00138 static tAccessLevel se_accessLevelVoteSuspend = tAccessLevel_Program;
00139 static tSettingItem< tAccessLevel > se_accessLevelVoteSuspendSI( "ACCESS_LEVEL_VOTE_SUSPEND", se_accessLevelVoteSuspend );
00140 
00141 // minimal access level for include votes
00142 static tAccessLevel se_accessLevelVoteInclude = tAccessLevel_Moderator;
00143 static tSettingItem< tAccessLevel > se_accessLevelVoteIncludeSI( "ACCESS_LEVEL_VOTE_INCLUDE", se_accessLevelVoteInclude );
00144 
00145 // minimal access level for include votes
00146 static tAccessLevel se_accessLevelVoteIncludeExecute = tAccessLevel_Moderator;
00147 static tSettingItem< tAccessLevel > se_accessLevelVoteIncludeExecuteSI( "ACCESS_LEVEL_VOTE_INCLUDE_EXECUTE", se_accessLevelVoteIncludeExecute );
00148 
00149 // minimal access level for direct command votes
00150 static tAccessLevel se_accessLevelVoteCommand = tAccessLevel_Moderator;
00151 static tSettingItem< tAccessLevel > se_accessLevelVoteCommandSI( "ACCESS_LEVEL_VOTE_COMMAND", se_accessLevelVoteCommand );
00152 
00153 // access level direct command votes will be executed with (minimal level is,
00154 // however, the access level of the vote submitter)
00155 static tAccessLevel se_accessLevelVoteCommandExecute = tAccessLevel_Moderator;
00156 static tSettingItem< tAccessLevel > se_accessLevelVoteCommandExecuteSI( "ACCESS_LEVEL_VOTE_COMMAND_EXECUTE", se_accessLevelVoteCommandExecute );
00157 #endif
00158 
00159 static eVoter* se_GetVoter( const nMessage& m )
00160 {
00161     return eVoter::GetVoter( m.SenderID(), true );
00162 }
00163 
00164 eVoterPlayerInfo::eVoterPlayerInfo(): suspended_(0){}
00165 
00166 static tAccessLevel se_GetAccessLevel( int userID )
00167 {
00168     tAccessLevel ret = tAccessLevel_Default;
00169 
00170     // scan players of given user ID
00171     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
00172     {
00173         ePlayerNetID* p = se_PlayerNetIDs(i);
00174         
00175         if ( p->Owner() == userID )
00176         {
00177             if( p->GetAccessLevel() < ret )
00178             {
00179                 ret = p->GetAccessLevel();
00180             }
00181         }
00182     }
00183     
00184     return ret;
00185 }
00186 
00187 // something to vote on
00188 class eVoteItem: public tListMember
00189 {
00190     friend class eMenuItemVote;
00191 public:
00192     // constructors/destructor
00193     eVoteItem( void ): creationTime_( tSysTimeFloat() ), user_( 0 ), id_( ++se_votingItemID ), menuItem_( 0 )
00194     {
00195         items_.Add( this );
00196     };
00197 
00198     virtual ~eVoteItem( void );
00199 
00200     bool FillFromMessage( nMessage& m )
00201     {
00202         // cloak the ID of the sener for privacy
00203         nCurrentSenderID cloak;
00204         if ( se_votingPrivacy > 1 )
00205             cloak.SetID(0);
00206 
00207         if ( !DoFillFromMessage( m ) )
00208             return false;
00209 
00210         if ( sn_GetNetState() == nSERVER )
00211         {
00212             if ( !CheckValid( m.SenderID() ) )
00213                 return false;
00214 
00215             ReBroadcast( m.SenderID() );
00216         }
00217         return true;
00218     }
00219 
00220     
00221     void ReBroadcast( int exceptTo = -1 )
00222     {
00223         // rebroadcast message to all non-voters that may be able to vote
00224         if ( sn_GetNetState() == nSERVER )
00225         {
00226             // prepare message
00227             tOutput voteMessage;
00228             voteMessage.SetTemplateParameter( 1, suggestor_->Name( user_ ) );
00229             voteMessage.SetTemplateParameter( 2, GetDescription() );
00230             voteMessage << "$vote_submitted";
00231 
00232             // print it
00233             if ( se_votingPrivacy <= -1 )
00234                 sn_ConsoleOut( voteMessage );   // broadcast it
00235             else if ( se_votingPrivacy <= 1 )
00236                 con << voteMessage;                             // print it for the server admin
00237 
00238             static nVersionFeature serverControlledVotes( 10 );
00239 
00240             // create messages for old and new clients
00241             tJUST_CONTROLLED_PTR< nMessage > retNew = this->CreateMessage();
00242             tJUST_CONTROLLED_PTR< nMessage > retLegacy = this->CreateMessageLegacy();
00243             for ( int i = MAXCLIENTS; i > 0; --i )
00244             {
00245                 if ( sn_Connections[ i ].socket && i != exceptTo && 0 != eVoter::GetVoter( i ) )
00246                 {
00247                     if ( serverControlledVotes.Supported( i ) )
00248                     {
00249                         retNew->Send( i );
00250                     }
00251                     else if ( retLegacy )
00252                     {
00253                         retLegacy->Send( i );
00254                     }
00255                 }
00256             }
00257             //                  item->SendMessage();
00258         }
00259 
00260         con << tOutput( "$vote_new", GetDescription() );
00261 
00262         this->Evaluate();
00263     };
00264 
00265     nMessage* CreateMessage( void ) const
00266     {
00267         nMessage* m = tNEW( nMessage )( this->DoGetDescriptor() );
00268         this->DoFillToMessage( *m );
00269         return m;
00270     }
00271 
00272     nMessage* CreateMessageLegacy( void ) const
00273     {
00274         nDescriptor * descriptor = this->DoGetDescriptorLegacy();
00275         if ( descriptor )
00276         {
00277             nMessage* m = tNEW( nMessage )( *descriptor );
00278             this->DoFillToMessageLegacy( *m );
00279             return m;
00280         }
00281         else
00282         {
00283             return 0;
00284         }
00285     }
00286 
00287     void SendMessage( void ) const
00288     {
00289         this->CreateMessage()->BroadCast();
00290     }
00291 
00292     // message sending
00293     void Vote( bool accept );                                                                                                           // called on the clients to accept or decline the vote
00294 
00295     static bool AcceptNewVote( eVoter * voter, int senderID )                                                                           // check if a new voting item should be accepted
00296     {
00297         // cloak the ID of the sener for privacy
00298         nCurrentSenderID cloak;
00299         if ( se_votingPrivacy > 0 )
00300             cloak.SetID(0);
00301 
00302         int i;
00303 
00304         // let old messages time out
00305         for ( i = items_.Len()-1; i>=0; --i )
00306         {
00307             items_[i]->Evaluate();
00308         }
00309 
00310         // always accept in client mode
00311         if ( sn_GetNetState() == nCLIENT )
00312             return true;
00313 
00314         // check if voting is allowed
00315         if ( !voter )
00316         {
00317             return false;
00318         }
00319 
00320         // reject voting
00321         if ( !se_allowVoting )
00322         {
00323             tOutput message("$vote_disabled");
00324             sn_ConsoleOut( message, senderID );
00325             return false;
00326         }
00327 
00328         // spawn spectator voters
00329         for ( i = MAXCLIENTS; i > 0; --i )
00330         {
00331             if ( sn_Connections[ i ].socket )
00332                 eVoter::GetVoter( i );
00333         }
00334 
00335         // enough voters online?
00336         if ( eVoter::voters_.Len() < se_minVoters )
00337         {
00338             tOutput message("$vote_toofew");
00339             sn_ConsoleOut( message, senderID );
00340             return false;
00341         }
00342 
00343         // check for spam
00344         if ( voter->IsSpamming( senderID ) )
00345         {
00346             return false;
00347         }
00348 
00349         // count number of votes by the voter
00350         int voteCount = 0;
00351         for ( i = items_.Len()-1; i>=0; --i )
00352         {
00353             eVoteItem * other = items_[i];
00354             if ( other->suggestor_ == voter )
00355                 voteCount ++;
00356         }
00357         if ( voteCount >= se_maxVotesPerVoter )
00358         {
00359             tOutput message("$vote_overflow");
00360             sn_ConsoleOut( message, senderID );
00361             return false;
00362         }
00363 
00364         if ( items_.Len() < se_maxVotes )
00365         {
00366             voter->Spam( senderID, se_votingSpamIssue, tOutput("$spam_vote_kick_issue") );
00367             return true;
00368         }
00369         else
00370         {
00371             tOutput message("$vote_overflow");
00372             sn_ConsoleOut( message, senderID );
00373             return false;
00374         }
00375     }
00376 
00377     static bool AcceptNewVote( nMessage const & m )                                                                             // check if a new voting item should be accepted
00378     {
00379         return AcceptNewVote( se_GetVoter( m ), m.SenderID() );
00380     }
00381 
00382     void RemoveVoter( eVoter* voter )
00383     {
00384         // remove voter from the lists
00385         for ( int res = 1; res >= 0; --res )
00386             this->voters_[ res ].Remove( voter );
00387     }
00388 
00389     void RemoveVoterCompletely( eVoter* voter )
00390     {
00391         RemoveVoter( voter );
00392         if ( suggestor_ == voter )
00393         {
00394             suggestor_ = 0;
00395             user_ = 0;
00396         }
00397     }
00398 
00399     // message receival
00400     static void GetControlMessage( nMessage& m )                                                                        // handles a voting message
00401     {
00402         if ( sn_GetNetState() == nSERVER )
00403         {
00404             unsigned short id;
00405             m.Read( id );
00406 
00407             bool result;
00408             m >> result;
00409             result = result ? 1 : 0;
00410 
00411             for ( int i = items_.Len()-1; i>=0; --i )
00412             {
00413                 eVoteItem* vote = items_[i];
00414                 if ( vote->id_ == id )
00415                 {
00416                     // found the vote; find the voter
00417                     tCONTROLLED_PTR( eVoter ) voter = se_GetVoter( m );
00418                     if ( voter )
00419                     {
00420                         // prepare message
00421                         tOutput voteMessage;
00422                         voteMessage.SetTemplateParameter( 1, voter->Name( m.SenderID() ) );
00423                         voteMessage.SetTemplateParameter( 2, vote->GetDescription() );
00424                         if ( result )
00425                             voteMessage << "$vote_vote_for";
00426                         else
00427                             voteMessage << "$vote_vote_against";
00428 
00429                         // print it
00430                         if ( se_votingPrivacy <= -2 )
00431                             sn_ConsoleOut( voteMessage );       // broadcast it
00432                         else if ( se_votingPrivacy <= 0 )
00433                             con << voteMessage;                         // print it for the server admin
00434 
00435                         // remove him from the lists
00436                         vote->RemoveVoter( voter );
00437 
00438                         // insert hum
00439                         vote->voters_[ result ].Insert( voter );
00440                     }
00441 
00442                     // are enough votes cast?
00443                     vote->Evaluate();
00444                     return;
00445                 }
00446             }
00447         }
00448     }
00449 
00450     // information
00451     void GetStats( int& pro, int& con, int& total ) const                                               // returns voting statistics about this item
00452     {
00453         pro = voters_[1].Len();
00454         con = voters_[0].Len();
00455         total = eVoter::voters_.Len();
00456     }
00457 
00458     // message
00459     void BroadcastMessage( const tOutput& message ) const
00460     {
00461         if ( sn_GetNetState() == nSERVER )
00462         {
00463             tOutput m;
00464             m.SetTemplateParameter( 1, this->GetDescription() );
00465             m.Append( message );
00466 
00467             sn_ConsoleOut( m );
00468         }
00469     }
00470 
00471     // access level required for this kind of vote
00472     virtual tAccessLevel DoGetAccessLevel() const
00473     {
00474         return tAccessLevel_Default;
00475     }
00476 
00477     // return vote-specific extra bias
00478     virtual int DoGetExtraBias() const
00479     {
00480         return 0;
00481     }
00482 
00483     // evaluation
00484     virtual void Evaluate()                                                                                                                                     // check if this voting item is to be kept around
00485     {
00486         int pro, con, total;
00487 
00488         GetStats( pro, con, total );
00489 
00490         int bias = se_votingBias + DoGetExtraBias();
00491 
00492         // apply bias
00493         con     += bias;
00494         total   += bias;
00495 
00496         // reduce number of total voters
00497         if ( se_votingDecay > 0 )
00498         {
00499             int reduce = int( ( tSysTimeFloat() - this->creationTime_ - se_votingStartDecay ) / se_votingDecay );
00500             if ( reduce > 0 )
00501             {
00502                 total -= reduce;
00503             }
00504         }
00505 
00506         if ( sn_GetNetState() == nSERVER )
00507         {
00508             // see if the vote has been rejected
00509             if ( con >= pro && con * 2 >= total )
00510             {
00511                 if ( this->suggestor_ )
00512                     this->suggestor_->Spam( user_, se_votingSpamReject, tOutput("$spam_vote_rejected") );
00513 
00514                 tOutput voteMessage;
00515                 voteMessage.SetTemplateParameter( 1, GetDescription() );
00516                 voteMessage.SetTemplateParameter( 2, pro-1 );
00517                 voteMessage.SetTemplateParameter( 3, con-se_votingBias );
00518                 voteMessage.SetTemplateParameter( 4, total-se_votingBias);
00519                 voteMessage << "$vote_rejected";
00520                 this->BroadcastMessage( voteMessage );
00521                 delete this;
00522                 return;
00523             }
00524 
00525             // see if the vote has been accepted
00526             if ( pro >= con && pro * 2 > total )
00527             {
00528                 this->DoExecute();
00529                 tOutput voteMessage;
00530                 voteMessage.SetTemplateParameter( 1, GetDescription() );
00531                 voteMessage.SetTemplateParameter( 2, pro-1 );
00532                 voteMessage.SetTemplateParameter( 3, con-se_votingBias );
00533                 voteMessage.SetTemplateParameter( 4, total-se_votingBias);
00534                 voteMessage << "$vote_accepted";
00535                 this->BroadcastMessage( voteMessage );
00536                 delete this;
00537                 return;
00538             }
00539         }
00540 
00541         // see if the voting has timed out
00542         int relevantNumVoters = sn_GetNetState() == nCLIENT ? se_PlayerNetIDs.Len() + MAXCLIENTS : eVoter::voters_.Len(); // the number of voters (overestimate the value on the client)
00543         if ( this->creationTime_ < tSysTimeFloat() - se_votingTimeout - se_votingTimeoutPerVoter * relevantNumVoters )
00544         {
00545             this->BroadcastMessage( tOutput( "$vote_timeout" ) );
00546 
00547             delete this;
00548             return;
00549         }
00550     }
00551 
00552     // accessors
00553     static const tList< eVoteItem >& GetItems() { return items_; }                                      // returns the list of all items
00554     inline eVoter* GetSuggestor() const { return suggestor_; }                                          // returns the voter that suggested the item
00555     inline tString GetDescription() const{ return this->DoGetDescription(); }           // returns the description of the voting item
00556     inline tString GetDetails() const{ return this->DoGetDetails(); }               // returns the detailed description of the voting item
00557 
00558     unsigned short GetID(){ return id_; }
00559     void UpdateMenuItem();                // update the menu item about a status change
00560 
00561     // checks whether the vote is a valid vote to make
00562     bool CheckValid( int senderID )
00563     {
00564         if ( sn_GetNetState() != nSERVER )
00565         {
00566             return true;
00567         }
00568 
00569         // fill suggestor
00570         if ( !suggestor_ )
00571         {
00572             suggestor_ = eVoter::GetVoter( senderID );
00573             if ( !suggestor_ )
00574                 return false;
00575 
00576             // add suggestor to supporters
00577             this->voters_[1].Insert( suggestor_ );
00578 
00579             user_ = senderID;
00580         }
00581 
00582         // check access level
00583         tAccessLevel accessLevel = se_GetAccessLevel( senderID );
00584         if ( accessLevel < tCurrentAccessLevel::GetAccessLevel() )
00585         {
00586             accessLevel = tCurrentAccessLevel::GetAccessLevel();
00587         }
00588 
00589         tAccessLevel required = DoGetAccessLevel();
00590         if ( accessLevel > required )
00591         {
00592             sn_ConsoleOut(tOutput("$player_vote_accesslevel",
00593                                   tCurrentAccessLevel::GetName( accessLevel ),
00594                                   tCurrentAccessLevel::GetName( required ) ),
00595                           senderID );
00596             
00597             return false;
00598         }
00599 
00600         return DoCheckValid( senderID );
00601     }
00602 
00603     virtual void Update() 
00604     {}
00605 protected:
00606     virtual bool DoFillFromMessage( nMessage& m )
00607     {
00608         // get user
00609         user_ = m.SenderID();
00610 
00611         // get originator of vote
00612         if(sn_GetNetState()!=nSERVER)
00613         {
00614             m.Read( id_ );
00615         }
00616 
00617         return true;
00618     };
00619 
00620     virtual bool DoCheckValid( int senderID ){ return true; }
00621 
00622     virtual void DoFillToMessage( nMessage& m ) const
00623     {
00624         if(sn_GetNetState()==nSERVER)
00625         {
00626             // write our message ID
00627             m.Write( id_ );
00628         }
00629     };
00630 
00631 protected:
00632     virtual tString DoGetDetails() const                    // returns the detailed description of the voting item
00633     {
00634         tString ret;
00635         if ( se_votingPrivacy <= -1 && bool( suggestor_ ) )
00636             ret << tOutput( "$vote_submitter_text", suggestor_->Name( user_ ) ) << " ";
00637 
00638         return ret;
00639     }
00640     static tList< eVoteItem > items_;                           // list of vote items
00641 private:
00642     virtual nDescriptor * DoGetDescriptorLegacy() const // returns the creation descriptor
00643     {
00644         return 0;
00645     }
00646 
00647     virtual void DoFillToMessageLegacy( nMessage& m ) const
00648     {
00649         return DoFillToMessage( m );
00650     };
00651 
00652     virtual nDescriptor& DoGetDescriptor() const = 0;   // returns the creation descriptor
00653     virtual tString DoGetDescription() const = 0;               // returns the description of the voting item
00654     virtual void DoExecute() = 0;                                               // called when the voting was successful
00655 
00656     nTimeAbsolute creationTime_;                                        // time the vote was cast
00657     tCONTROLLED_PTR( eVoter ) suggestor_;                       // the voter suggesting the vote
00658     unsigned int user_;                                                         // user suggesting the vote
00659     tArray< tCONTROLLED_PTR( eVoter ) > voters_[2];     // array of voters approving or disapproving of the vote
00660     unsigned short id_;                                                         // running id of voting item
00661     eMenuItemVote *menuItem_;                                           // menu item
00662 
00663     eVoteItem& operator=( const eVoteItem& );
00664     eVoteItem( const eVoteItem& );
00665 };
00666 
00667 tList< eVoteItem > eVoteItem::items_;                           // list of vote items
00668 
00669 void se_CancelAllVotes( std::istream & )
00670 {
00671     if ( sn_GetNetState() == nCLIENT )
00672     {
00673         return;
00674     }
00675 
00676     sn_ConsoleOut( tOutput( "$vote_cancel_all" ) );
00677 
00678     tList< eVoteItem > const & items = eVoteItem::GetItems();
00679     
00680     while ( items.Len() )
00681     {
00682         delete items(0);
00683     }
00684 }
00685 
00686 static tConfItemFunc se_cancelAllVotes_conf( "VOTES_CANCEL", &se_CancelAllVotes );
00687 
00688 static nDescriptor vote_handler(230,eVoteItem::GetControlMessage,"vote cast");
00689 
00690 // called on the clients to accept or decline the vote
00691 void eVoteItem::Vote( bool accept )
00692 {
00693     tJUST_CONTROLLED_PTR< nMessage > m = tNEW( nMessage )( vote_handler );
00694     *m << id_;
00695     *m << accept;
00696     m->BroadCast();
00697 
00698     delete this;
00699 }
00700 
00701 //nDescriptor& eVoteItem::DoGetDescriptor() const;      // returns the creation descriptor
00702 //tString eVoteItem::DoGetDescription() const;          // returns the description of the voting item
00703 //void DoExecute();                                             // called when the voting was successful
00704 
00705 // ****************************************************************************************
00706 // ****************************************************************************************
00707 
00708 // voting decision
00709 enum Vote
00710 {
00711     Vote_Approve,
00712     Vote_Reject,
00713     Vote_DontMind
00714 };
00715 
00716 #ifdef _MSC_VER
00717 #pragma warning ( disable: 4355 )
00718 #endif
00719 
00720 // menu item to silence selected players
00721 class eMenuItemVote: public uMenuItemSelection< Vote >
00722 {
00723     friend class eVoteItem;
00724     friend class eVoteItemServerControlled;
00725 
00726 public:
00727     eMenuItemVote(uMenu *m, eVoteItem* v )
00728             : uMenuItemSelection< Vote >( m, tOutput(""), tOutput("$vote_help"), vote_ )
00729             , item_( v )
00730             , vote_ ( Vote_DontMind )
00731             , reject_   ( *this, "$vote_reject"         , "$vote_reject_help"           , Vote_Reject   )
00732             , dontMind_ ( *this, "$vote_dont_mind"      , "$vote_dont_mind_help"        , Vote_DontMind )
00733             , approve_  ( *this, "$vote_approve"        , "$vote_approve_help"          , Vote_Approve  )
00734     {
00735         tASSERT( v );
00736 
00737         if ( v )
00738         {
00739             v->menuItem_ = this;
00740             v->UpdateMenuItem();
00741         }
00742     }
00743 
00744     ~eMenuItemVote()
00745     {
00746         if ( item_ )
00747         {
00748             item_->menuItem_ = 0;
00749 
00750             switch ( vote_ )
00751             {
00752             case Vote_Approve:
00753                 item_->Vote( true );
00754                 break;
00755             case Vote_Reject:
00756                 item_->Vote( false );
00757                 break;
00758             default:
00759                 break;
00760             }
00761         }
00762     }
00763 
00764 private:
00765     eVoteItem* item_;                                                                           // vote item
00766     Vote vote_;                                                                                         // result
00767     uSelectEntry< Vote >  reject_, dontMind_, approve_;         // selection entries
00768 };
00769 
00770 // **************************************************************************************
00771 // **************************************************************************************
00772 
00773 static void se_HandleServerVoteChanged( nMessage& m );
00774 static nDescriptor server_vote_expired_handler(233,se_HandleServerVoteChanged,"Server controlled vote expired");
00775 
00776 // something to vote on: completely controlled by the server
00777 class eVoteItemServerControlled: public virtual eVoteItem
00778 {
00779 public:
00780     // constructors/destructor
00781     eVoteItemServerControlled()
00782             : description_( "No Info" )
00783             , details_( "No Info" )
00784             , expired_( false )
00785     {
00786     }
00787 
00788     eVoteItemServerControlled( tString const & description, tString const & details )
00789             : description_( description )
00790             , details_( details )
00791             , expired_( false )
00792     {}
00793 
00794     ~eVoteItemServerControlled()
00795     {
00796         if ( sn_GetNetState() == nSERVER )
00797         {
00798             expired_ = true;
00799             SendChanged();
00800         }
00801     }
00802 
00803     static void s_HandleChanged( nMessage & m )
00804     {
00805         unsigned short id;
00806         m.Read( id );
00807         for ( int i = items_.Len()-1; i>=0; --i )
00808         {
00809             eVoteItem* vote = items_[i];
00810             if ( vote->GetID() == id )
00811             {
00812                 eVoteItemServerControlled * vote2 = dynamic_cast< eVoteItemServerControlled * >( vote );
00813                 if ( vote2 )
00814                     vote2->HandleChanged( m );
00815             }
00816         }
00817     }
00818 
00819     void HandleChanged( nMessage & m )
00820     {
00821         unsigned short expired;
00822         m.Read( expired );
00823         expired_ = expired;
00824         m >> description_;
00825         m >> details_;
00826 
00827         Update();
00828         UpdateMenuItem();
00829     }
00830 
00831     void SendChanged()
00832     {
00833         tJUST_CONTROLLED_PTR< nMessage > m = tNEW( nMessage )( server_vote_expired_handler );
00834         *m << GetID();
00835         *m << (unsigned short)expired_;
00836         *m << description_;
00837         *m << details_;
00838         m->BroadCast();
00839     }
00840 protected:
00841 
00842     virtual bool DoFillFromMessage( nMessage& m )
00843     {
00844         m >> description_;
00845         m >> details_;
00846         return eVoteItem::DoFillFromMessage( m );
00847     };
00848 
00849     virtual void DoFillToMessage( nMessage& m ) const
00850     {
00851         m << description_;
00852         m << details_;
00853         eVoteItem::DoFillToMessage( m );
00854     };
00855 
00856     virtual void DoExecute(){};                                         // called when the voting was successful
00857 protected:
00858     virtual nDescriptor& DoGetDescriptor() const;       // returns the creation descriptor
00859 
00860     virtual void Evaluate()
00861     {
00862         // update clients (i.e. if a player to be kicked changed his name)
00863         if ( sn_GetNetState() == nSERVER )
00864         {
00865             Update();
00866             SendChanged();
00867         }
00868 
00869         if ( expired_ )
00870             delete this;
00871         else
00872             eVoteItem::Evaluate();
00873     }
00874 
00875     virtual tString DoGetDescription() const            // returns the description of the voting item
00876     {
00877         return expired_ ? tString("Expired vote") : description_;
00878     }
00879 
00880     virtual tString DoGetDetails() const                    // returns the detailed description of the voting item
00881     {
00882         return expired_ ? tString("Expired vote") : details_;
00883     }
00884 protected:
00885     mutable tString description_;              
00886     mutable tString details_;                  
00887 private:
00888     bool expired_;                             
00889 };
00890 
00891 static void se_HandleServerVoteChanged( nMessage& m )
00892 {
00893     eVoteItemServerControlled::s_HandleChanged( m );
00894 }
00895 
00896 static void se_HandleNewServerVote( nMessage& m )
00897 {
00898     if ( sn_GetNetState() != nCLIENT ||  eVoteItem::AcceptNewVote( m ) )
00899     {
00900         // accept message
00901         eVoteItem* item = tNEW( eVoteItemServerControlled )();
00902         if ( !item->FillFromMessage( m ) )
00903             delete item;
00904     }
00905 }
00906 
00907 static nDescriptor new_server_vote_handler(232,se_HandleNewServerVote,"Server controlled vote");
00908 
00909 // returns the creation descriptor
00910 nDescriptor& eVoteItemServerControlled::DoGetDescriptor() const
00911 {
00912     return new_server_vote_handler;
00913 }
00914 
00915 // *******************************************************************************************
00916 // *******************************************************************************************
00917 
00918 class nMachineObserver: public nMachineDecorator
00919 {
00920 public:
00921     nMachineObserver( nMachine & machine )
00922             : nMachineDecorator( machine ), machine_( &machine ){}
00923 
00924     nMachine * GetMachine()
00925     {
00926         return machine_;
00927     }
00928 protected:
00929     virtual void OnDestroy()
00930     {
00931         machine_ = 0;
00932     }
00933 private:
00934     nMachine * machine_;
00935 };
00936 
00937 static tString se_voteKickToServer("");
00938 static int se_voteKickToPort = 4534;
00939 static tSettingItem< tString > se_voteKickToServerConf( "VOTE_KICK_TO_SERVER", se_voteKickToServer );
00940 static tSettingItem< int > se_voteKickToPortConf( "VOTE_KICK_TO_PORT", se_voteKickToPort );
00941 
00942 // minimal previous harmful votes (kick, silence, suspend) before
00943 // a successful kick vote really results in a kick. Before that, the result is a
00944 // suspension.
00945 static int se_kickMinHarm = 0;
00946 static tSettingItem< int > se_kickMinHarmSI( "VOTING_KICK_MINHARM", se_kickMinHarm );
00947 
00948 // reason given on vote kicks
00949 static tString se_voteKickReason("");
00950 static tConfItemLine se_voteKickReasonConf( "VOTE_KICK_REASON", se_voteKickReason );
00951 
00952 void se_VoteKickUser( int user )
00953 {
00954     if ( user == 0 )
00955     {
00956         return;
00957     }
00958 
00959     tString reason;
00960     if ( se_voteKickReason.Len() >= 2 )
00961     {
00962         reason = se_voteKickReason;
00963     }
00964     else
00965     {
00966         reason = tOutput("$voted_kill_kick");
00967     }
00968 
00969     if ( se_voteKickToServer.Len() < 2 )
00970     {
00971         sn_KickUser( user, reason );
00972     }
00973     else
00974     {
00975         // kick player to default destination
00976         nServerInfoRedirect redirect( se_voteKickToServer, se_voteKickToPort );
00977         sn_KickUser( user, reason, 1, &redirect );
00978     }
00979 }
00980 
00981 void se_VoteKickPlayer( ePlayerNetID * p )
00982 {
00983     if ( !p )
00984     {
00985         return;
00986     }
00987 
00988     se_VoteKickUser( p->Owner() );
00989 }
00990 
00991 // something to vote on: harming a player
00992 class eVoteItemHarm: public virtual eVoteItem
00993 {
00994 public:
00995     // constructors/destructor
00996     eVoteItemHarm( ePlayerNetID* player = 0 )
00997             : player_( player )
00998             , machine_(NULL)
00999             , name_( "(Player who already left)" )
01000     {}
01001 
01002     ~eVoteItemHarm()
01003     {
01004         delete machine_;
01005         machine_ = NULL;
01006     }
01007 
01008     // returns the player that is to be harmed
01009     ePlayerNetID * GetPlayer() const
01010     {
01011         ePlayerNetID const * player = player_;
01012         return const_cast< ePlayerNetID * >( player );
01013     }
01014 protected:
01015     // this is a good spot to put in legacy hooks
01016     virtual nDescriptor * DoGetDescriptorLegacy() const
01017     {
01018         return &eVoteItemHarm::DoGetDescriptor();
01019     }
01020 
01021     virtual void DoFillToMessageLegacy( nMessage& m ) const
01022     {
01023         return eVoteItemHarm::DoFillToMessage( m );
01024     };
01025 
01026     virtual bool DoFillFromMessage( nMessage& m )
01027     {
01028         // read player ID
01029         unsigned short id;
01030         m.Read(id);
01031         tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
01032         player_ = p;
01033 
01034         return eVoteItem::DoFillFromMessage( m );
01035     }
01036 
01037     virtual bool DoCheckValid( int senderID )
01038     {
01039         // always accept votes from server
01040         if ( sn_GetNetState() == nCLIENT && senderID == 0 )
01041         {
01042             return true;
01043         }
01044 
01045         eVoter * sender = eVoter::GetVoter( senderID  );
01046 
01047         double time = tSysTimeFloat();
01048 
01049         // check whether the issuer is allowed to start a vote
01050         if ( sender && sender->lastChange_ + se_votingMaturity > tSysTimeFloat() && sender->lastChange_ * 2 > tSysTimeFloat() )
01051         {
01052             REAL time = sender->lastChange_ + se_votingMaturity - tSysTimeFloat();
01053             tOutput message( "$vote_maturity", time );
01054             sn_ConsoleOut( message, senderID );
01055             return false;
01056         }
01057 
01058         // prevent the sender from changing his name for confusion
01059         if ( sender )
01060             sender->lastNameChangePreventor_ = time;
01061 
01062         // check if player is protected from kicking
01063         if ( player_ && sn_GetNetState() != nCLIENT )
01064         {
01065             // check whether the player is on the server
01066             if ( player_->Owner() == 0 )
01067             {
01068                 sn_ConsoleOut( tOutput( "$vote_kick_local", player_->GetName() ), senderID );
01069                 return false;
01070             }
01071 
01072             name_ = player_->GetName();
01073             eVoter * voter = eVoter::GetVoter( player_->Owner() );
01074             if ( voter )
01075             {
01076                 machine_ = tNEW( nMachineObserver )( voter->machine_ );
01077 
01078                 if ( time < voter->lastHarmVote_ + se_minTimeBetweenHarms )
01079                 {
01080                     tOutput message("$vote_redundant");
01081                     sn_ConsoleOut( message, senderID );
01082                     return false;
01083                 }
01084                 else
01085                 {
01086                     voter->lastHarmVote_ = time;
01087                     voter->lastNameChangePreventor_ = time;
01088                 }
01089 
01090                 // count harmful votes
01091                 voter->harmCount_++;
01092             }
01093         }
01094 
01095         return eVoteItem::DoCheckValid( senderID );
01096     };
01097 
01098     virtual void DoFillToMessage( nMessage& m  ) const
01099     {
01100         if ( player_ )
01101             m.Write( player_->ID() );
01102         else
01103             m.Write( 0 );
01104 
01105         eVoteItem::DoFillToMessage( m );
01106     };
01107 
01108 protected:
01109     virtual nDescriptor& DoGetDescriptor() const;       // returns the creation descriptor
01110 
01111     // get the language string prefix
01112     virtual char const * DoGetPrefix() const = 0;
01113 
01114     virtual tString DoGetDescription() const            // returns the description of the voting item
01115     {
01116         // get name from player
01117         if ( player_ )
01118             name_ = player_->GetName();
01119 
01120         return tString( tOutput( tString("$") + DoGetPrefix() + "_player_text", name_ ) );
01121     }
01122 
01123     virtual tString DoGetDetails() const                    // returns the detailed description of the voting item
01124     {
01125         // get name from player
01126         if ( player_ )
01127             name_ = player_->GetName();
01128 
01129         return eVoteItem::DoGetDetails() + tString( tOutput( tString("$") + DoGetPrefix() + "_player_details_text", name_ ) );
01130     }
01131 
01132     nMachine * GetMachine() const
01133     {
01134         if ( !machine_ )
01135         {
01136             return 0;
01137         }
01138         else
01139         {
01140             return machine_->GetMachine();
01141         }
01142     }
01143 private:
01144     nObserverPtr< ePlayerNetID > player_;               // keep player referenced
01145     nMachineObserver * machine_;                // pointer to the machine of the player to be kicked
01146     mutable tString name_;                      // the name of the player to be kicked
01147 };
01148 
01149 // something to vote on: kicking a player
01150 class eVoteItemKick: public virtual eVoteItemHarm
01151 {
01152 public:
01153     // constructors/destructor
01154     eVoteItemKick( ePlayerNetID* player )
01155         : eVoteItemHarm( player )
01156     {}
01157 
01158     ~eVoteItemKick()
01159     {}
01160 
01161 protected:
01162     // get the language string prefix
01163     virtual char const * DoGetPrefix() const{ return "kick"; }
01164 
01165 #ifdef KRAWALL_SERVER
01166     // access level required for this kind of vote
01167     virtual tAccessLevel DoGetAccessLevel() const
01168     {
01169         return se_accessLevelVoteKick;
01170     }
01171 #endif
01172 
01173     // return vote-specific extra bias
01174     virtual int DoGetExtraBias() const
01175     {
01176         return se_votingBiasKick;
01177     }
01178 
01179     virtual bool DoCheckValid( int senderID )
01180     {
01181         ePlayerNetID * player = GetPlayer();
01182 
01183         // check if player is protected from kicking
01184         if ( player && sn_GetNetState() != nCLIENT )
01185         {
01186             eVoter * voter = eVoter::GetVoter( player->Owner() );
01187             if ( voter )
01188             {
01189                 double time = tSysTimeFloat();
01190                 if ( time < voter->lastKickVote_ + se_minTimeBetweenKicks )
01191                 {
01192                     tOutput message("$vote_redundant");
01193                     sn_ConsoleOut( message, senderID );
01194                     return false;
01195                 }
01196                 else
01197                 {
01198                     voter->lastKickVote_ = time;
01199                     voter->lastNameChangePreventor_ = time;
01200                 }
01201             }
01202         }
01203 
01204         return eVoteItemHarm::DoCheckValid( senderID );
01205     };
01206 
01207     virtual void DoExecute()                                            // called when the voting was successful
01208     {
01209         ePlayerNetID * player = GetPlayer();
01210         nMachine * machine = GetMachine();
01211         if ( player )
01212         {
01213             // kick the player, he is online
01214             se_VoteKickPlayer( player );
01215         }
01216         else if ( machine )
01217         {
01218             // the player left. Inform the machine that he would have gotten kicked.
01219             // kick all players that connected from that machine
01220             bool kick = false;
01221             for ( int user = MAXCLIENTS; user > 0; --user )
01222             {
01223                 if ( &nMachine::GetMachine( user ) == machine )
01224                 {
01225                     se_VoteKickUser( user );
01226                     kick = true;
01227                 }
01228             }
01229             
01230             // if no user could be kicked, notify at least the machine that
01231             // somebody would have been kicked.
01232             if ( !kick )
01233             {
01234                 machine->OnKick();
01235             }
01236         }
01237     }
01238 
01239 private:
01240 };
01241 
01242 // harming vote items, server controlled
01243 class eVoteItemHarmServerControlled: public virtual eVoteItemServerControlled, public virtual eVoteItemHarm
01244 {
01245 public:
01246     // constructors/destructor
01247     eVoteItemHarmServerControlled( ePlayerNetID* player = 0 )
01248             : eVoteItemHarm( player )
01249     {}
01250 
01251     ~eVoteItemHarmServerControlled()
01252     {}
01253 protected:
01254     virtual bool DoFillFromMessage( nMessage& m )
01255     {
01256         // should never be called on the client
01257         tASSERT( sn_GetNetState() != nCLIENT );
01258 
01259         // deletage
01260         bool ret = eVoteItemHarm::DoFillFromMessage( m );
01261 
01262         // fill in description
01263         Update();
01264 
01265         return ret;
01266     };
01267 
01268     virtual void DoFillToMessage( nMessage& m  ) const
01269     {
01270         // should never be called on the client
01271         tASSERT( sn_GetNetState() != nCLIENT );
01272 
01273         eVoteItemServerControlled::DoFillToMessage( m );
01274     };
01275 private:
01276     virtual void Update() 
01277     {
01278         description_ = eVoteItemHarm::DoGetDescription();
01279         details_ = eVoteItemHarm::DoGetDetails();
01280     }
01281 
01282     virtual nDescriptor& DoGetDescriptor() const        // returns the creation descriptor
01283     {
01284         return eVoteItemServerControlled::DoGetDescriptor();
01285     }
01286 
01287     virtual tString DoGetDescription() const            // returns the description of the voting item
01288     {
01289         return eVoteItemServerControlled::DoGetDescription();
01290     }
01291 
01292     virtual tString DoGetDetails() const                // returns the detailed description of the voting item
01293     {
01294         return eVoteItemServerControlled::DoGetDetails();
01295     }
01296 };
01297 
01298 // remove vote items, server controlled
01299 class eVoteItemSuspend: public virtual eVoteItemHarmServerControlled
01300 {
01301 public:
01302     // constructors/destructor
01303     eVoteItemSuspend( ePlayerNetID* player = 0 )
01304         : eVoteItemHarm( player )
01305         {}
01306 
01307     ~eVoteItemSuspend()
01308     {}
01309 protected:
01310     // get the language string prefix
01311     virtual char const * DoGetPrefix() const{ return "suspend"; }
01312 
01313 #ifdef KRAWALL_SERVER
01314     // access level required for this kind of vote
01315     virtual tAccessLevel DoGetAccessLevel() const
01316     {
01317         return se_accessLevelVoteSuspend;
01318     }
01319 #endif
01320 
01321     // return vote-specific extra bias
01322     virtual int DoGetExtraBias() const
01323     {
01324         return se_votingBiasSuspend;
01325     }
01326 
01327     virtual void DoExecute()                                            // called when the voting was successful
01328     {
01329         ePlayerNetID * player = GetPlayer();
01330         if ( player )
01331         {
01332             player->Suspend( se_suspendRounds );
01333         }
01334     }
01335 };
01336 
01337 // kick vote items, server controlled
01338 class eVoteItemKickServerControlled: public virtual eVoteItemHarmServerControlled, public virtual eVoteItemKick
01339 {
01340 public:
01341     // constructors/destructor
01342     eVoteItemKickServerControlled( bool fromMenu, ePlayerNetID* player )
01343     : eVoteItemHarm( player ), eVoteItemKick( player ), fromMenu_( fromMenu )
01344     {}
01345 
01346     ~eVoteItemKickServerControlled()
01347     {}
01348 protected:
01349     virtual bool DoCheckValid( int senderID )
01350     {
01351         // check whether enough harmful votes were collected already
01352         ePlayerNetID * p = GetPlayer();
01353         if ( fromMenu_ && p && p->GetVoter()->HarmCount() - 1 < se_kickMinHarm )
01354         {
01355             // try to transfor the vote to a suspension
01356             eVoteItem * item = tNEW ( eVoteItemSuspend )( p );
01357             
01358             // let item check its validity
01359             if ( !item->CheckValid( senderID ) )
01360             {
01361                 delete item;
01362             }
01363             else
01364             {
01365                 // no objection? Broadcast it to everyone.
01366                 item->Update();
01367                 item->ReBroadcast( senderID );
01368             }
01369 
01370             // and cancel this item here.
01371             return false;
01372         }
01373 
01374         // no transformation needed or transformation failed. Proceed as usual.
01375         return eVoteItemHarm::DoCheckValid( senderID );
01376     };
01377 
01378     virtual void DoExecute()                                            // called when the voting was successful
01379     {
01380         eVoteItemKick::DoExecute();
01381     }
01382 private:
01383     bool fromMenu_; // flag set if the vote came from the menu
01384 };
01385 
01386 static void se_HandleKickVote( nMessage& m )
01387 {
01388     // accept message
01389     if ( eVoteItem::AcceptNewVote( m ) )
01390     {
01391         eVoteItemHarm* item = tNEW( eVoteItemKickServerControlled )( true, 0 );
01392         if ( !item->FillFromMessage( m ) )
01393         {
01394             delete item;
01395             return;
01396         }
01397     }
01398 }
01399 
01400 static nDescriptor kill_vote_handler(231,se_HandleKickVote,"Kick vote");
01401 
01402 // returns the creation descriptor
01403 nDescriptor& eVoteItemHarm::DoGetDescriptor() const
01404 {
01405     return kill_vote_handler;
01406 }
01407 
01408 static void se_SendKick( ePlayerNetID* p )
01409 {
01410     eVoteItemKick kick( p );
01411     kick.SendMessage();
01412 }
01413 
01414 #ifdef KRAWALL_SERVER
01415 
01416 // console with filter for redirection to anyone with a certain access level
01417 class eAccessConsoleFilter: public tConsoleFilter
01418 {
01419 public:
01420     eAccessConsoleFilter( tAccessLevel level )
01421             :level_( level )
01422     {
01423     }
01424 
01425     void Send()
01426     {
01427         bool canSee[ MAXCLIENTS+1 ];
01428         for( int i = MAXCLIENTS; i>=0; --i )
01429         {
01430             canSee[i] = false;
01431         }
01432 
01433         // look which clients have someone who can see the message
01434         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
01435         {
01436             ePlayerNetID* player = se_PlayerNetIDs(i);
01437             if ( player->GetAccessLevel() <= level_ )
01438             {
01439                 canSee[ player->Owner() ] = true;
01440             }
01441         }
01442 
01443         // and send it
01444         for( int i = MAXCLIENTS; i>=0; --i )
01445         {
01446             if ( canSee[i] )
01447             {
01448                 sn_ConsoleOut( message_, i );
01449             }
01450         }
01451 
01452         message_.Clear();
01453     }
01454 
01455     ~eAccessConsoleFilter()
01456     {
01457         Send();
01458     }
01459 private:
01460     // we want to come first, the admins should get unfiltered output
01461     virtual int DoGetPriority() const{ return -100; }
01462 
01463     // don't actually filter; take line and add it to the message sent to the admin
01464     virtual void DoFilterLine( tString &line )
01465     {
01466         //tColoredString message;
01467         message_ << tColoredString::ColorString(1,.3,.3) << "RA: " << tColoredString::ColorString(1,1,1) << line << "\n";
01468 
01469         // don't let message grow indefinitely
01470         if (message_.Len() > 600)
01471         {            Send();
01472         }
01473     }
01474 
01475     tAccessLevel level_;     // the access level required 
01476     tColoredString message_; // the console message for the remote administrator
01477 };
01478 
01479 // include vote items
01480 class eVoteItemInclude: public eVoteItemServerControlled
01481 {
01482 public:
01483     // constructors/destructor
01484     eVoteItemInclude( tString const & file, tAccessLevel submitterLevel )
01485     : eVoteItemServerControlled()
01486     , file_( file )
01487     {
01488         description_ = tOutput( "$vote_include_text", file );
01489         file_ = tString( "vote/" ) + file_;
01490         details_ = tOutput( "$vote_include_details_text", file_ );
01491 
01492         if ( submitterLevel > se_accessLevelVoteIncludeExecute )
01493         {
01494             submitterLevel = se_accessLevelVoteIncludeExecute;
01495         }
01496         level_ = submitterLevel;
01497     }
01498 
01499     ~eVoteItemInclude()
01500     {}
01501 protected:
01502     // access level required for this kind of vote
01503     virtual tAccessLevel DoGetAccessLevel() const
01504     {
01505         return se_accessLevelVoteInclude;
01506     }
01507 
01508     // return vote-specific extra bias
01509     virtual int DoGetExtraBias() const
01510     {
01511         return se_votingBiasInclude;
01512     }
01513 
01514     bool Open( std::ifstream & s, int userToNotify )
01515     {
01516         if ( tDirectories::Config().Open(s, file_ ) || tDirectories::Var().Open(s, file_ ) )
01517         {
01518             return true;
01519         }
01520         else
01521         {
01522             con << tOutput( "$vote_include_error", file_ );
01523             sn_ConsoleOut( tOutput( "$vote_include_error", file_ ), userToNotify );
01524             return false;
01525         }
01526     }
01527 
01528     virtual bool DoCheckValid( int senderID )
01529     { 
01530         std::ifstream s;
01531         return ( Open( s, senderID ) && eVoteItemServerControlled::DoCheckValid( senderID ) );
01532     }
01533 
01534     virtual void DoExecute()                                            // called when the voting was successful
01535     {
01536         // set the access level for the following operation
01537         tCurrentAccessLevel accessLevel( level_, true );
01538 
01539         // load contents of everytime.cfg for real
01540         std::ifstream s;
01541         if ( Open( s, 0 ) )
01542         {
01543             sn_ConsoleOut( tOutput( "$vote_include_message", file_ ) );
01544             eAccessConsoleFilter filter( level_ );
01545             tConfItemBase::LoadAll(s);
01546             tConfItemBase::LoadPlayback();
01547         }
01548     }
01549 
01550     tString file_;       
01551     tAccessLevel level_; 
01552 };
01553 
01554 // command vote items
01555 class eVoteItemCommand: public eVoteItemServerControlled
01556 {
01557 public:
01558     // constructors/destructor
01559     eVoteItemCommand( tString const & command, tAccessLevel submitterLevel )
01560     : eVoteItemServerControlled()
01561     , command_( command )
01562     {
01563         description_ = tOutput( "$vote_command_text", command );
01564         details_ = tOutput( "$vote_command_details_text", command );
01565 
01566         if ( submitterLevel > se_accessLevelVoteCommandExecute )
01567         {
01568             submitterLevel = se_accessLevelVoteCommandExecute;
01569         }
01570         level_ = submitterLevel;
01571     }
01572 
01573     ~eVoteItemCommand()
01574     {}
01575 protected:
01576     // access level required for this kind of vote
01577     virtual tAccessLevel DoGetAccessLevel() const
01578     {
01579         return se_accessLevelVoteCommand;
01580     }
01581 
01582     // return vote-specific extra bias
01583     virtual int DoGetExtraBias() const
01584     {
01585         return se_votingBiasCommand;
01586     }
01587 
01588     virtual void DoExecute()                                            // called when the voting was successful
01589     {
01590         // set the access level for the following operation
01591         tCurrentAccessLevel accessLevel( level_, true );
01592 
01593         // load contents of everytime.cfg for real
01594         std::istringstream s( static_cast< char const * >( command_ ) );
01595         sn_ConsoleOut( tOutput( "$vote_command_message" ) );
01596         eAccessConsoleFilter filter( tAccessLevel_Default );
01597         tConfItemBase::LoadLine(s);
01598         tConfItemBase::LoadPlayback();
01599     }
01600 
01601     tString command_;    
01602     tAccessLevel level_; 
01603 };
01604 
01605 #endif
01606 
01607 // **************************************************************************************
01608 // **************************************************************************************
01609 
01610 
01611 // menu item to silence selected players
01612 class eMenuItemKick: public uMenuItemAction
01613 {
01614 public:
01615     eMenuItemKick(uMenu *m, ePlayerNetID* p )
01616             : uMenuItemAction( m, tOutput(""),tOutput("$kick_player_help" ) )
01617     {
01618         this->name_.Clear();
01619         this->name_.SetTemplateParameter(1, p->GetName() );
01620         this->name_ << "$kick_player_text";
01621         player_ = p;
01622     }
01623 
01624     ~eMenuItemKick()
01625     {
01626     }
01627 
01628     virtual void Enter()
01629     {
01630         if(sn_GetNetState()==nSERVER)
01631         {
01632             // kill user directly
01633             se_VoteKickPlayer( player_ );
01634         }
01635         {
01636             // issue kick vote
01637             se_SendKick( player_ );
01638         }
01639 
01640         // leave menu to release smart pointers
01641         this->menu->Exit();
01642     }
01643 private:
01644     tCONTROLLED_PTR( ePlayerNetID ) player_;            // keep player referenced
01645 };
01646 
01647 
01648 // ****************************************************************************************
01649 // ****************************************************************************************
01650 
01651 static nSpamProtectionSettings se_voteSpamProtection( 50.0f, "SPAM_PROTECTION_VOTE", tOutput("$vote_spam_protection") );
01652 
01653 eVoter::eVoter( nMachine & machine )
01654         : nMachineDecorator( machine ), machine_( machine ), votingSpam_( se_voteSpamProtection )
01655 {
01656     selfReference_ = this;
01657     voters_.Add( this );
01658     harmCount_ = 0;
01659     lastKickVote_ = -1E+40;
01660     lastHarmVote_ = -1E+40;
01661     lastNameChangePreventor_ = -1E+40;
01662     lastChange_ = tSysTimeFloat();
01663 }
01664 
01665 eVoter::~eVoter()
01666 {
01667     voters_.Remove( this );
01668 }
01669 
01670 void eVoter::Spam( int user, REAL spamLevel, tOutput const & message )
01671 {
01672     if ( sn_GetNetState() == nSERVER )
01673         votingSpam_.CheckSpam( spamLevel, user, message );
01674 }
01675 
01676 bool eVoter::IsSpamming( int user )
01677 {
01678     if ( sn_GetNetState() == nSERVER )
01679     {
01680         return nSpamProtection::Level_Ok != votingSpam_.CheckSpam( 0.0f, user, tOutput("$spam_vote_kick_issue") );
01681     }
01682 
01683     return false;
01684 }
01685 
01686 // *******************************************************************************
01687 // *
01688 // *    OnDestroy
01689 // *
01690 // *******************************************************************************
01693 // *******************************************************************************
01694 
01695 void eVoter::OnDestroy( void )
01696 {
01697     tJUST_CONTROLLED_PTR< eVoter > keepAlive( this );
01698     selfReference_ = 0;
01699 }
01700 
01701 
01702 // *******************************************************************************
01703 // *
01704 // *    OnDestroy
01705 // *
01706 // *******************************************************************************
01710 // *******************************************************************************
01711 
01712 REAL eVoter::Age() const
01713 {
01714     return tSysTimeFloat() - lastChange_;
01715 }
01716 
01717 
01718 
01719 // *******************************************************************************
01720 // *
01721 // *    AllowNameChange
01722 // *
01723 // *******************************************************************************
01726 // *******************************************************************************
01727 
01728 bool eVoter::AllowNameChange( void ) const
01729 {
01730     return tSysTimeFloat() > this->lastNameChangePreventor_ + se_minTimeBetweenKicks;
01731 }
01732 
01733 void eVoter::RemoveFromGame()
01734 {
01735     tCONTROLLED_PTR( eVoter ) keeper( this );
01736 
01737     voters_.Remove( this );
01738 
01739     // remove from items
01740     for ( int i = eVoteItem::GetItems().Len()-1; i>=0; --i )
01741     {
01742         eVoteItem::GetItems()( i )->RemoveVoterCompletely( this );
01743     }
01744 }
01745 
01746 void eVoter::KickMenu()                                                 // activate player kick menu
01747 {
01748     uMenu menu( "$player_police_kick_text" );
01749 
01750     int size = se_PlayerNetIDs.Len();
01751     eMenuItemKick** items = tNEW( eMenuItemKick* )[ size ];
01752 
01753     int i;
01754     for ( i = size-1; i>=0; --i )
01755     {
01756         ePlayerNetID* player = se_PlayerNetIDs[ i ];
01757         if ( player->IsHuman() )
01758         {
01759             items[i] = tNEW( eMenuItemKick )( &menu, player );
01760         }
01761         else
01762         {
01763             items[i] = 0;
01764         }
01765     }
01766 
01767     menu.Enter();
01768 
01769     for ( i = size - 1; i>=0; --i )
01770     {
01771         if( items[i] )
01772             delete items[i];
01773     }
01774     delete[] items;
01775 }
01776 
01777 #ifndef DEDICATED
01778 static bool se_KeepConsoleSmall()
01779 {
01780     return true;
01781 }
01782 #endif
01783 
01784 static uMenu* votingMenu = 0;
01785 
01786 void eVoter::VotingMenu()                                               // activate voting menu ( you can vote about suggestions there )
01787 {
01788     static bool recursion = false;
01789     if ( ! recursion )
01790     {
01791 
01792         // expire old items
01793         if ( !VotingPossible() )
01794             return;
01795 
01796 #ifndef DEDICATED
01797         rSmallConsoleCallback SmallConsole( se_KeepConsoleSmall );
01798 
01799         // count items
01800         int size = eVoteItem::GetItems().Len();
01801         if ( size == 0 )
01802             return;
01803 
01804         // fill menu
01805         uMenu menu( "$voting_menu_text" );
01806 
01807         eMenuItemVote** items = tNEW( eMenuItemVote* )[ size ];
01808 
01809         int i;
01810         for ( i = size-1; i>=0; --i )
01811         {
01812             items[i] = tNEW( eMenuItemVote )( &menu, eVoteItem::GetItems()( i ) );
01813         }
01814 
01815         // enter menu
01816         recursion = true;
01817         votingMenu = &menu;
01818         menu.Enter();
01819         votingMenu = 0;
01820         recursion = false;
01821 
01822         for ( i = size - 1; i>=0; --i )
01823         {
01824             delete items[i];
01825         }
01826         delete[] items;
01827 
01828         // expire old items
01829         VotingPossible();
01830 #endif
01831     }
01832 }
01833 
01834 bool eVoter::VotingPossible()
01835 {
01836     // expire old items
01837     for ( int i = eVoteItem::GetItems().Len()-1; i>=0; --i )
01838     {
01839         eVoteItem::GetItems()( i )->Evaluate();
01840     }
01841 
01842     if ( sn_GetNetState() != nCLIENT )
01843     {
01844         return false;
01845     }
01846 
01847     return eVoteItem::GetItems().Len() > 0;
01848 }
01849 
01850 eVoter* eVoter::GetVoter( int ID, bool complain )                       // find or create the voter for the specified ID
01851 {
01852     // the server has no voter
01853 #ifdef DEDICATED
01854     if ( ID == 0 )
01855         return NULL;
01856 #endif
01857 
01858     // see if there is a real player on the specified ID
01859     if ( !se_allowVotingSpectator )
01860     {
01861         bool player = false;
01862         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
01863         {
01864             ePlayerNetID* p = se_PlayerNetIDs(i);
01865             if ( p->Owner() == ID && !p->IsSpectating() )
01866                 player = true;
01867         }
01868         if (!player)
01869         {
01870             if ( complain )
01871             {
01872                 tOutput message("$vote_disabled_spectator");
01873                 sn_ConsoleOut( message, ID );
01874             }
01875             return NULL;
01876         }
01877     }
01878 
01879     // get machine from network subsystem
01880     nMachine & machine = nMachine::GetMachine( ID );
01881 
01882     return GetVoter( machine );
01883 }
01884 
01885 eVoter* eVoter::GetVoter( nMachine & machine )                  // find or create the voter for the specified machine
01886 {
01887     // iterate through the machine's decorators, find a voter
01888     nMachineDecorator * run = machine.GetDecorators();
01889     while ( run )
01890     {
01891         eVoter * voter = dynamic_cast< eVoter * >( run );
01892         if ( voter )
01893         {
01894             // reinsert voter into lists
01895             if ( voter->ListID() < 0 )
01896             {
01897                 voters_.Add( voter );
01898                 voter->lastKickVote_ = -1E30;
01899                 voter->lastNameChangePreventor_ = -1E30;
01900             }
01901 
01902             // return result
01903             return voter;
01904         }
01905 
01906         run = run->Next();
01907     }
01908 
01909     // create new voter
01910     return tNEW(eVoter)( machine );
01911 }
01912 
01913 tList< eVoter > eVoter::voters_;                                        // list of all voters
01914 
01915 static void se_Cleanup()
01916 {
01917     if ( nCallbackLoginLogout::User() == 0 )
01918     {
01919         if ( votingMenu )
01920         {
01921             votingMenu->Exit();
01922         }
01923 
01924         if ( !nCallbackLoginLogout::Login() && eGrid::CurrentGrid() )
01925         {
01926             //                  uMenu::exitToMain = true;
01927         }
01928 
01929         // client login/logout: delete voting items
01930         const tList< eVoteItem >& list = eVoteItem::GetItems();
01931         while ( list.Len() > 0 )
01932         {
01933             delete list(0);
01934         }
01935     }
01936     else if ( nCallbackLoginLogout::Login() )
01937     {
01938         // new user: send pending voting items
01939         const tList< eVoteItem >& list = eVoteItem::GetItems();
01940         for ( int i = list.Len()-1; i >= 0; -- i)
01941         {
01942             eVoteItem* vote = list( i );
01943             nMessage* m = vote->CreateMessage();
01944             m->Send( nCallbackLoginLogout::User() );
01945         }
01946     }
01947 }
01948 
01949 
01950 static nCallbackLoginLogout se_cleanup( se_Cleanup );
01951 
01952 eVoteItem::~eVoteItem( void )
01953 {
01954     items_.Remove( this );
01955 
01956     if ( menuItem_ )
01957     {
01958         menuItem_->item_ = 0;
01959         menuItem_->title.Clear();
01960         menuItem_->helpText.Clear();
01961         menuItem_ = 0;
01962     }
01963 }
01964 
01965 void eVoteItem::UpdateMenuItem( void )
01966 {
01967     if ( menuItem_ )
01968     {
01969         menuItem_->title.Clear();
01970         menuItem_->title = GetDescription();
01971 
01972         menuItem_->helpText.Clear();
01973         menuItem_->helpText << tString( tOutput( "$vote_details_help", GetDetails() ) );
01974     }
01975 }
01976 
01977 // *******************************************************************************************
01978 // *
01979 // *    Name
01980 // *
01981 // *******************************************************************************************
01986 // *******************************************************************************************
01987 
01988 tString eVoter::Name( int senderID ) const
01989 {
01990     tString name;
01991 
01992     // collect the names of all players associated with this voter
01993     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
01994     {
01995         ePlayerNetID* p = se_PlayerNetIDs(i);
01996         if ( eVoter::GetVoter( p->Owner() ) == this && ( senderID < 0 || p->Owner() == senderID ) )
01997         {
01998             if ( name.Len() > 1 )
01999                 name << ", ";
02000             name << p->GetName();
02001         }
02002     }
02003 
02004     if ( name.Len() < 2 )
02005         name = machine_.GetIP();
02006 
02007     return name;
02008 }
02009 
02010 // *******************************************************************************
02011 // *
02012 // *    PlayerChanged
02013 // *
02014 // *******************************************************************************
02017 // *******************************************************************************
02018 
02019 void eVoter::PlayerChanged( void )
02020 {
02021     this->lastChange_ = tSysTimeFloat();
02022 }
02023 
02024 // *******************************************************************************
02025 // *
02026 // *    HandleChat
02027 // *
02028 // *******************************************************************************
02031 // *******************************************************************************
02032 
02033 void eVoter::HandleChat( ePlayerNetID * p, std::istream & message ) 
02034 {
02035     // cloak the ID of the sener for privacy
02036     nCurrentSenderID cloak;
02037     if ( se_votingPrivacy > 1 )
02038         cloak.SetID(0);
02039 
02040     if ( !p )
02041     {
02042         return;
02043     }
02044 
02045     // read command part (kick, remove, include)
02046     tString command;
02047     message >> command;
02048     tToLower( command );
02049 
02050     eVoter * voter = p->GetVoter();
02051     if ( !eVoteItem::AcceptNewVote( voter, p->Owner() ) )
02052     {
02053         return;
02054     }
02055 
02056     eVoteItem * item = 0;
02057 
02058     if ( command == "kick" )
02059     {
02060         tString name;
02061         name.ReadLine( message );
02062         ePlayerNetID * toKick = ePlayerNetID::FindPlayerByName( name, p );
02063         if ( toKick )
02064         {
02065             // accept message
02066             item = tNEW( eVoteItemKickServerControlled )( false, toKick );
02067         }
02068     }
02069     else if ( command == "suspend" )
02070     {
02071         tString name;
02072         name.ReadLine( message );
02073         ePlayerNetID * toSuspend = ePlayerNetID::FindPlayerByName( name, p );
02074         if ( toSuspend )
02075         {
02076             // accept message
02077             item = tNEW( eVoteItemSuspend )( toSuspend );
02078         }
02079     }
02080 #ifdef KRAWALL_SERVER
02081     else if ( command == "include" )
02082     {
02083         tString file;
02084         file.ReadLine( message );
02085         {
02086             // accept message
02087             item = tNEW( eVoteItemInclude )( file, p->GetAccessLevel() );
02088         }
02089     }
02090     else if ( command == "command" )
02091     {
02092         tString console;
02093         console.ReadLine( message );
02094         {
02095             // accept message
02096             item = tNEW( eVoteItemCommand )( console, p->GetAccessLevel() );
02097         }
02098     }
02099 #endif
02100     else
02101     {
02102 #ifdef KRAWALL_SERVER
02103         sn_ConsoleOut( tOutput("$vote_unknown_command", command, "suspend, kick, include, command" ), p->Owner() );
02104 #else
02105         sn_ConsoleOut( tOutput("$vote_unknown_command", command, "suspend, kick" ), p->Owner() );
02106 #endif
02107     }
02108 
02109     // nothing created
02110     if ( !item )
02111     {
02112         return;
02113     }
02114 
02115     // let item check its validity
02116     if ( !item->CheckValid( p->Owner() ) )
02117     {
02118         delete item;
02119         return;
02120     }
02121 
02122     // no objection? Broadcast it to everyone.
02123     item->Update();
02124     item->ReBroadcast( p->Owner() );
02125 }
02126 

Generated on Sat Mar 15 22:55:48 2008 for Armagetron Advanced by  doxygen 1.5.4