1 /*
2 
3 *************************************************************************
4 
5 ArmageTron -- Just another Tron Lightcycle Game in 3D.
6 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
7 
8 **************************************************************************
9 
10 This program is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; either version 2
13 of the License, or (at your option) any later version.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 ***************************************************************************
25 
26 */
27 
28 #include "rSDL.h"
29 
30 #include "eVoter.h"
31 
32 #include "tMemManager.h"
33 #include "tSysTime.h"
34 #include "tDirectories.h"
35 
36 #include "uMenu.h"
37 
38 #include "nConfig.h"
39 #include "nServerInfo.h"
40 
41 #include "rConsole.h"
42 
43 #include "ePlayer.h"
44 #include "eGrid.h"
45 
46 #ifndef DEDICATED
47 // use server controlled votes (just for the client, to avoid UPGRADE messages)
48 static bool se_useServerControlledKick = false;
49 static nSettingItem< bool > se_usc( "VOTE_USE_SERVER_CONTROLLED_KICK", se_useServerControlledKick );
50 #endif
51 
52 // basic vote timeout value
53 static unsigned short se_votingItemID = 0;
54 static float se_votingTimeout = 300.0f;
55 static nSettingItem< float > se_vt( "VOTING_TIMEOUT", se_votingTimeout );
56 
57 // additional timeout for every voter present
58 static float se_votingTimeoutPerVoter = 0.0f;
59 static nSettingItem< float > se_vtp( "VOTING_TIMEOUT_PER_VOTER", se_votingTimeoutPerVoter );
60 
61 static float se_votingStartDecay = 60.0f;
62 static nSettingItem< float > se_vsd( "VOTING_START_DECAY", se_votingStartDecay );
63 
64 static float se_votingDecay = 60.0f;
65 static nSettingItem< float > se_vd( "VOTING_DECAY", se_votingDecay );
66 
67 // spam level of issuing a vote
68 static float se_votingSpamIssue = 1.0f;
69 static nSettingItem< float > se_vsi( "VOTING_SPAM_ISSUE", se_votingSpamIssue );
70 
71 // spam level of getting your vote rejected
72 static float se_votingSpamReject = 5.0f;
73 static nSettingItem< float > se_vsr( "VOTING_SPAM_REJECT", se_votingSpamReject );
74 
75 static bool se_allowVoting = false;
76 static tSettingItem< bool > se_av( "ALLOW_VOTING", se_allowVoting );
77 
78 static bool se_allowVotingSpectator = false;
79 static tSettingItem< bool > se_avo( "ALLOW_VOTING_SPECTATOR", se_allowVotingSpectator );
80 
81 // number of rounds to suspend
82 static int se_suspendRounds = 5;
83 static tSettingItem< int > se_sr( "VOTING_SUSPEND_ROUNDS", se_suspendRounds );
84 
85 static int se_minVoters = 3;
86 static tSettingItem< int > se_mv( "MIN_VOTERS", se_minVoters );
87 
88 // the number set here always acts as votes against a change.
89 static int se_votingBias = 0;
90 static tSettingItem< int > se_vb( "VOTING_BIAS", se_votingBias );
91 
92 // the number set here always acts as additional votes against a kick vote.
93 static int se_votingBiasKick = 0;
94 static tSettingItem< int > se_vbKick( "VOTING_BIAS_KICK", se_votingBiasKick );
95 
96 // the number set here always acts as additional votes against a suspend vote.
97 static int se_votingBiasSuspend = 0;
98 static tSettingItem< int > se_vbSuspend( "VOTING_BIAS_SUSPEND", se_votingBiasSuspend );
99 
100 // the number set here always acts as additional votes against a suspend vote.
101 static int se_votingBiasInclude = 0;
102 static tSettingItem< int > se_vbInclude( "VOTING_BIAS_INCLUDE", se_votingBiasInclude );
103 
104 // the number set here always acts as additional votes against a command vote.
105 static int se_votingBiasCommand = 0;
106 static tSettingItem< int > se_vbCommand( "VOTING_BIAS_COMMAND", se_votingBiasCommand );
107 
108 // voting privacy level. -2 means total disclosure, +2 total secrecy.
109 static int se_votingPrivacy = 1;
110 static tSettingItem< int > se_vp( "VOTING_PRIVACY", se_votingPrivacy );
111 
112 // maximum number of concurrent votes
113 static int se_maxVotes = 5;
114 static tSettingItem< int > se_maxVotesSI( "MAX_VOTES", se_maxVotes );
115 
116 // maximum number of concurrent votes per voter
117 static int se_maxVotesPerVoter = 2;
118 static tSettingItem< int > se_maxVotesPerVoterSI( "MAX_VOTES_PER_VOTER", se_maxVotesPerVoter );
119 
120 // time between kick votes against the same target in seconds
121 static int se_minTimeBetweenKicks = 300;
122 static tSettingItem< int > se_minTimeBetweenKicksSI( "VOTING_KICK_TIME", se_minTimeBetweenKicks );
123 
124 // time between harmful votes against the same target in seconds
125 static int se_minTimeBetweenHarms = 180;
126 static tSettingItem< int > se_minTimeBetweenHarmsSI( "VOTING_HARM_TIME", se_minTimeBetweenHarms );
127 
128 // time between name changes and you being allowed to issue votes again
129 static int se_votingMaturity = 300;
130 static tSettingItem< int > se_votingMaturitySI( "VOTING_MATURITY", se_votingMaturity );
131 
132 #ifdef KRAWALL_SERVER
133 // minimal access level for kick votes
134 static tAccessLevel se_accessLevelVoteKick = tAccessLevel_Program;
135 static tSettingItem< tAccessLevel > se_accessLevelVoteKickSI( "ACCESS_LEVEL_VOTE_KICK", se_accessLevelVoteKick );
136 static tAccessLevelSetter se_accessLevelVoteKickSILevel( se_accessLevelVoteKickSI, tAccessLevel_Owner );
137 
138 // minimal access level for suspend votes
139 static tAccessLevel se_accessLevelVoteSuspend = tAccessLevel_Program;
140 static tSettingItem< tAccessLevel > se_accessLevelVoteSuspendSI( "ACCESS_LEVEL_VOTE_SUSPEND", se_accessLevelVoteSuspend );
141 static tAccessLevelSetter se_accessLevelVoteSuspendSILevel( se_accessLevelVoteSuspendSI, tAccessLevel_Owner );
142 
143 // minimal access level for include votes
144 static tAccessLevel se_accessLevelVoteInclude = tAccessLevel_Moderator;
145 static tSettingItem< tAccessLevel > se_accessLevelVoteIncludeSI( "ACCESS_LEVEL_VOTE_INCLUDE", se_accessLevelVoteInclude );
146 static tAccessLevelSetter se_accessLevelVoteIncludeSILevel( se_accessLevelVoteIncludeSI, tAccessLevel_Owner );
147 
148 // minimal access level for include votes
149 static tAccessLevel se_accessLevelVoteIncludeExecute = tAccessLevel_Moderator;
150 static tSettingItem< tAccessLevel > se_accessLevelVoteIncludeExecuteSI( "ACCESS_LEVEL_VOTE_INCLUDE_EXECUTE", se_accessLevelVoteIncludeExecute );
151 static tAccessLevelSetter se_accessLevelVoteIncludeExecuteSILevel( se_accessLevelVoteIncludeExecuteSI, tAccessLevel_Owner );
152 
153 // minimal access level for direct command votes
154 static tAccessLevel se_accessLevelVoteCommand = tAccessLevel_Moderator;
155 static tSettingItem< tAccessLevel > se_accessLevelVoteCommandSI( "ACCESS_LEVEL_VOTE_COMMAND", se_accessLevelVoteCommand );
156 static tAccessLevelSetter se_accessLevelVoteCommandSILevel( se_accessLevelVoteCommandSI, tAccessLevel_Owner );
157 
158 // access level direct command votes will be executed with (minimal level is,
159 // however, the access level of the vote submitter)
160 static tAccessLevel se_accessLevelVoteCommandExecute = tAccessLevel_Moderator;
161 static tSettingItem< tAccessLevel > se_accessLevelVoteCommandExecuteSI( "ACCESS_LEVEL_VOTE_COMMAND_EXECUTE", se_accessLevelVoteCommandExecute );
162 static tAccessLevelSetter se_accessLevelVoteCommandExecuteSILevel( se_accessLevelVoteCommandExecuteSI, tAccessLevel_Owner );
163 
164 #endif
165 
166 static REAL se_defaultVotesSuspendLength = 3;
167 static tSettingItem< REAL > se_defaultVotesSuspendLenght_Conf( "VOTES_SUSPEND_DEFAULT", se_defaultVotesSuspendLength );
168 static REAL se_votesSuspendTimeout = 0;
169 
se_GetVoter(const nMessage & m)170 static eVoter* se_GetVoter( const nMessage& m )
171 {
172     return eVoter::GetVoter( m.SenderID(), true );
173 }
174 
eVoterPlayerInfo()175 eVoterPlayerInfo::eVoterPlayerInfo(): suspended_(0), silenced_(0){}
176 
se_GetAccessLevel(int userID)177 static tAccessLevel se_GetAccessLevel( int userID )
178 {
179     tAccessLevel ret = tAccessLevel_Default;
180 
181     // scan players of given user ID
182     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
183     {
184         ePlayerNetID* p = se_PlayerNetIDs(i);
185 
186         if ( p->Owner() == userID )
187         {
188             if( p->GetAccessLevel() < ret )
189             {
190                 ret = p->GetAccessLevel();
191             }
192         }
193     }
194 
195     return ret;
196 }
197 
198 static nVersionFeature serverControlledVotesBroken( 10 );
199 static nVersionFeature serverControlledVotes( 15 );
200 
201 
202 // something to vote on
203 class eVoteItem: public tListMember
204 {
205     friend class eMenuItemVote;
206 public:
207     // constructors/destructor
eVoteItem(void)208     eVoteItem( void ): creationTime_( tSysTimeFloat() ), user_( 0 ), id_( ++se_votingItemID ), menuItem_( 0 ), total_( 0 )
209     {
210         items_.Add( this );
211     }
212 
213     virtual ~eVoteItem( void );
214 
FillFromMessage(nMessage & m)215     bool FillFromMessage( nMessage& m )
216     {
217         // cloak the ID of the sener for privacy
218         nCurrentSenderID cloak;
219         if ( se_votingPrivacy > 1 )
220             cloak.SetID(0);
221 
222         if ( !DoFillFromMessage( m ) )
223             return false;
224 
225         if ( sn_GetNetState() == nSERVER )
226         {
227             if ( !CheckValid( m.SenderID() ) )
228                 return false;
229         }
230 
231         ReBroadcast( m.SenderID() );
232 
233         return true;
234     }
235 
236 
ReBroadcast(int exceptTo)237     void ReBroadcast( int exceptTo )
238     {
239         // rebroadcast message to all non-voters that may be able to vote
240         if ( sn_GetNetState() == nSERVER )
241         {
242             // prepare message
243             tOutput voteMessage;
244             voteMessage.SetTemplateParameter( 1, suggestor_->Name( user_ ) );
245             voteMessage.SetTemplateParameter( 2, GetDescription() );
246             voteMessage << "$vote_submitted";
247 
248             // print it
249             if ( se_votingPrivacy <= -1 )
250             {
251                 sn_ConsoleOut( voteMessage );	// broadcast it
252             }
253             else
254             {
255                 if ( exceptTo > 0 )
256                 {
257                     sn_ConsoleOut( voteMessage, exceptTo );	// inform submitter
258                 }
259 
260                 if ( se_votingPrivacy <= 1 )
261                 {
262                     con << voteMessage;				// print it for the server admin
263                 }
264             }
265 
266             // create messages for old and new clients
267             tJUST_CONTROLLED_PTR< nMessage > retNew = this->CreateMessage();
268             tJUST_CONTROLLED_PTR< nMessage > retLegacy = this->CreateMessageLegacy();
269 
270             // set so every voter ony gets each vote once
271             std::set< eVoter * > sentTo;
272             total_ = 1;
273 
274             for ( int i = MAXCLIENTS; i > 0; --i )
275             {
276                 eVoter * voter = eVoter::GetVoter( i );
277                 if ( sn_Connections[ i ].socket && i != exceptTo && 0 != voter &&
278                      sentTo.find(voter) == sentTo.end() )
279                 {
280 
281                     if ( serverControlledVotes.Supported( i ) )
282                     {
283                         sentTo.insert(voter);
284                         retNew->Send( i );
285                         total_++;
286                     }
287                     else if ( retLegacy )
288                     {
289                         sentTo.insert(voter);
290                         retLegacy->Send( i );
291                         total_++;
292                     }
293                 }
294             }
295             //			item->SendMessage();
296 
297             if ( suggestor_ )
298                 suggestor_->Spam( exceptTo, se_votingSpamIssue, tOutput("$spam_vote_kick_issue") );
299         }
300 
301         con << tOutput( "$vote_new", GetDescription() );
302 
303         this->Evaluate();
304     }
305 
CreateMessage(void) const306     nMessage* CreateMessage( void ) const
307     {
308         nMessage* m = tNEW( nMessage )( this->DoGetDescriptor() );
309         this->DoFillToMessage( *m );
310         return m;
311     }
312 
CreateMessageLegacy(void) const313     nMessage* CreateMessageLegacy( void ) const
314     {
315         nDescriptor * descriptor = this->DoGetDescriptorLegacy();
316         if ( descriptor )
317         {
318             nMessage* m = tNEW( nMessage )( *descriptor );
319             this->DoFillToMessageLegacy( *m );
320             return m;
321         }
322         else
323         {
324             return 0;
325         }
326     }
327 
SendMessage(void) const328     void SendMessage( void ) const
329     {
330         this->CreateMessage()->BroadCast();
331     }
332 
333     // message sending
334     void Vote( bool accept );														// called on the clients to accept or decline the vote
335 
AcceptNewVote(eVoter * voter,int senderID)336     static bool AcceptNewVote( eVoter * voter, int senderID )										// check if a new voting item should be accepted
337     {
338         // cloak the ID of the sener for privacy
339         nCurrentSenderID cloak;
340         if ( se_votingPrivacy > 0 )
341             cloak.SetID(0);
342 
343         int i;
344 
345         // let old messages time out
346         for ( i = items_.Len()-1; i>=0; --i )
347         {
348             items_[i]->Evaluate();
349         }
350 
351         // always accept in client mode
352         if ( sn_GetNetState() == nCLIENT )
353             return true;
354 
355         // check if voting is allowed
356         if ( !voter )
357         {
358             return false;
359         }
360 
361         // reject voting
362         if ( !se_allowVoting )
363         {
364             tOutput message("$vote_disabled");
365             sn_ConsoleOut( message, senderID );
366             return false;
367         }
368 
369         // spawn spectator voters
370         for ( i = MAXCLIENTS; i > 0; --i )
371         {
372             if ( sn_Connections[ i ].socket )
373                 eVoter::GetVoter( i );
374         }
375 
376         // enough voters online?
377         if ( eVoter::voters_.Len() < se_minVoters )
378         {
379             tOutput message("$vote_toofew");
380             sn_ConsoleOut( message, senderID );
381             return false;
382         }
383 
384         // check for spam
385         if ( voter->IsSpamming( senderID ) )
386         {
387             return false;
388         }
389 
390         // count number of votes by the voter
391         int voteCount = 0;
392         for ( i = items_.Len()-1; i>=0; --i )
393         {
394             eVoteItem * other = items_[i];
395             if ( other->suggestor_ == voter )
396                 voteCount ++;
397         }
398         if ( voteCount >= se_maxVotesPerVoter )
399         {
400             tOutput message("$vote_overflow");
401             sn_ConsoleOut( message, senderID );
402             return false;
403         }
404 
405         if ( items_.Len() < se_maxVotes )
406         {
407             return true;
408         }
409         else
410         {
411             tOutput message("$vote_overflow");
412             sn_ConsoleOut( message, senderID );
413             return false;
414         }
415     }
416 
AcceptNewVote(nMessage const & m)417     static bool AcceptNewVote( nMessage const & m )										// check if a new voting item should be accepted
418     {
419         return AcceptNewVote( se_GetVoter( m ), m.SenderID() );
420     }
421 
RemoveVoter(eVoter * voter)422     void RemoveVoter( eVoter* voter )
423     {
424         // remove voter from the lists
425         for ( int res = 1; res >= 0; --res )
426             this->voters_[ res ].Remove( voter );
427     }
428 
RemoveVoterCompletely(eVoter * voter)429     void RemoveVoterCompletely( eVoter* voter )
430     {
431         RemoveVoter( voter );
432         if ( suggestor_ == voter )
433         {
434             suggestor_ = 0;
435             user_ = 0;
436         }
437     }
438 
439     // message receival
GetControlMessage(nMessage & m)440     static void GetControlMessage( nMessage& m )								   	// handles a voting message
441     {
442         if ( sn_GetNetState() == nSERVER )
443         {
444             unsigned short id;
445             m.Read( id );
446 
447             bool result;
448             m >> result;
449             result = result ? 1 : 0;
450 
451             for ( int i = items_.Len()-1; i>=0; --i )
452             {
453                 eVoteItem* vote = items_[i];
454                 if ( vote->id_ == id )
455                 {
456                     // found the vote; find the voter
457                     tCONTROLLED_PTR( eVoter ) voter = se_GetVoter( m );
458                     if ( voter )
459                     {
460                         // prepare message
461                         tOutput voteMessage;
462                         voteMessage.SetTemplateParameter( 1, voter->Name( m.SenderID() ) );
463                         voteMessage.SetTemplateParameter( 2, vote->GetDescription() );
464                         if ( result )
465                             voteMessage << "$vote_vote_for";
466                         else
467                             voteMessage << "$vote_vote_against";
468 
469                         // print it
470                         if ( se_votingPrivacy <= -2 )
471                             sn_ConsoleOut( voteMessage );	// broadcast it
472                         else if ( se_votingPrivacy <= 0 )
473                             con << voteMessage;				// print it for the server admin
474                         else
475                         {
476                             sn_ConsoleOut( voteMessage, m.SenderID() );
477                         }
478 
479                         // remove him from the lists
480                         vote->RemoveVoter( voter );
481 
482                         // insert hum
483                         vote->voters_[ result ].Insert( voter );
484                     }
485 
486                     // are enough votes cast?
487                     vote->Evaluate();
488                     return;
489                 }
490             }
491         }
492     }
493 
494     // information
GetStats(int & pro,int & con,int & total) const495     void GetStats( int& pro, int& con, int& total ) const						// returns voting statistics about this item
496     {
497         pro = voters_[1].Len();
498         con = voters_[0].Len();
499         total = total_;
500     }
501 
502     // message
BroadcastMessage(const tOutput & message) const503     void BroadcastMessage( const tOutput& message ) const
504     {
505         if ( sn_GetNetState() == nSERVER )
506         {
507             tOutput m;
508             m.SetTemplateParameter( 1, this->GetDescription() );
509             m.Append( message );
510 
511             sn_ConsoleOut( m );
512         }
513     }
514 
515     // access level required for this kind of vote
DoGetAccessLevel() const516     virtual tAccessLevel DoGetAccessLevel() const
517     {
518         return tAccessLevel_Default;
519     }
520 
521     // return vote-specific extra bias
DoGetExtraBias() const522     virtual int DoGetExtraBias() const
523     {
524         return 0;
525     }
526 
527     // evaluation
Evaluate()528     virtual void Evaluate()																	// check if this voting item is to be kept around
529     {
530         int pro, con, total;
531 
532         GetStats( pro, con, total );
533 
534         if ( sn_GetNetState() == nSERVER )
535         {
536             // see if there are enough voters
537             if ( total <= se_minVoters )
538             {
539                 this->BroadcastMessage( tOutput("$vote_toofew") );
540                 delete this;
541                 return;
542             }
543         }
544 
545         int bias = se_votingBias + DoGetExtraBias();
546 
547         // apply bias
548         con 	+= bias;
549         total 	+= bias;
550 
551         // reduce number of total voters
552         if ( se_votingDecay > 0 )
553         {
554             int reduce = int( ( tSysTimeFloat() - this->creationTime_ - se_votingStartDecay ) / se_votingDecay );
555             if ( reduce > 0 )
556             {
557                 total -= reduce;
558             }
559         }
560 
561         if ( sn_GetNetState() == nSERVER )
562         {
563             // see if the vote has been rejected
564             if ( con >= pro && con * 2 >= total )
565             {
566                 if ( this->suggestor_ )
567                     this->suggestor_->Spam( user_, se_votingSpamReject, tOutput("$spam_vote_rejected") );
568 
569                 this->BroadcastMessage( tOutput( "$vote_rejected" ) );
570                 delete this;
571                 return;
572             }
573 
574             // see if the vote has been accepted
575             if ( pro >= con && pro * 2 > total )
576             {
577                 this->BroadcastMessage( tOutput( "$vote_accepted" ) );
578 
579                 this->DoExecute();
580 
581                 delete this;
582                 return;
583             }
584         }
585 
586         // see if the voting has timed out
587         int relevantNumVoters = sn_GetNetState() == nCLIENT ? se_PlayerNetIDs.Len() + MAXCLIENTS : eVoter::voters_.Len(); // the number of voters (overestimate the value on the client)
588         if ( this->creationTime_ < tSysTimeFloat() - se_votingTimeout - se_votingTimeoutPerVoter * relevantNumVoters )
589         {
590             this->BroadcastMessage( tOutput( "$vote_timeout" ) );
591 
592             delete this;
593             return;
594         }
595     }
596 
597     // accessors
GetItems()598     static const tList< eVoteItem >& GetItems() { return items_; }					// returns the list of all items
GetSuggestor() const599     inline eVoter* GetSuggestor() const { return suggestor_; }						// returns the voter that suggested the item
GetDescription() const600     inline tString GetDescription() const{ return this->DoGetDescription(); }		// returns the description of the voting item
GetDetails() const601     inline tString GetDetails() const{ return this->DoGetDetails(); }               // returns the detailed description of the voting item
602 
GetID()603     unsigned short GetID(){ return id_; }
604     void UpdateMenuItem();                // update the menu item about a status change
605 
606     // checks whether the vote is a valid vote to make
CheckValid(int senderID)607     bool CheckValid( int senderID )
608     {
609         if ( sn_GetNetState() != nSERVER )
610         {
611             return true;
612         }
613 
614         if ( se_votesSuspendTimeout > tSysTimeFloat() )
615         {
616             sn_ConsoleOut(tOutput("$vote_rejected_voting_suspended"),
617                           senderID );
618 
619             return false;
620         }
621 
622         // fill suggestor
623         if ( !suggestor_ )
624         {
625             suggestor_ = eVoter::GetVoter( senderID );
626             if ( !suggestor_ )
627                 return false;
628 
629             // add suggestor to supporters
630             this->voters_[1].Insert( suggestor_ );
631 
632             user_ = senderID;
633         }
634 
635         // check access level
636         tAccessLevel accessLevel = se_GetAccessLevel( senderID );
637         if ( accessLevel < tCurrentAccessLevel::GetAccessLevel() )
638         {
639             accessLevel = tCurrentAccessLevel::GetAccessLevel();
640         }
641 
642         tAccessLevel required = DoGetAccessLevel();
643         if ( accessLevel > required )
644         {
645             sn_ConsoleOut(tOutput("$player_vote_accesslevel",
646                                   tCurrentAccessLevel::GetName( accessLevel ),
647                                   tCurrentAccessLevel::GetName( required ) ),
648                           senderID );
649 
650             return false;
651         }
652 
653         return DoCheckValid( senderID );
654     }
655 
Update()656     virtual void Update() //!< update description and details
657     {}
658 protected:
DoFillFromMessage(nMessage & m)659     virtual bool DoFillFromMessage( nMessage& m )
660     {
661         // get user
662         user_ = m.SenderID();
663 
664         // get originator of vote
665         if(sn_GetNetState()!=nSERVER)
666         {
667             m.Read( id_ );
668         }
669 
670         return true;
671     }
672 
DoCheckValid(int senderID)673     virtual bool DoCheckValid( int senderID ){ return true; }
674 
DoFillToMessage(nMessage & m) const675     virtual void DoFillToMessage( nMessage& m ) const
676     {
677         if(sn_GetNetState()==nSERVER)
678         {
679             // write our message ID
680             m.Write( id_ );
681         }
682     }
683 
684 protected:
DoGetDetails() const685     virtual tString DoGetDetails() const 		    // returns the detailed description of the voting item
686     {
687         tString ret;
688         if ( se_votingPrivacy <= -1 && bool( suggestor_ ) )
689             ret << tOutput( "$vote_submitter_text", suggestor_->Name( user_ ) ) << " ";
690 
691         return ret;
692     }
693     static tList< eVoteItem > items_;				// list of vote items
694 private:
DoGetDescriptorLegacy() const695     virtual nDescriptor * DoGetDescriptorLegacy() const	// returns the creation descriptor
696     {
697         return 0;
698     }
699 
DoFillToMessageLegacy(nMessage & m) const700     virtual void DoFillToMessageLegacy( nMessage& m ) const
701     {
702         return DoFillToMessage( m );
703     }
704 
705     virtual nDescriptor& DoGetDescriptor() const = 0;	// returns the creation descriptor
706     virtual tString DoGetDescription() const = 0;		// returns the description of the voting item
707     virtual void DoExecute() = 0;						// called when the voting was successful
708 
709     nTimeAbsolute creationTime_;					// time the vote was cast
710     tCONTROLLED_PTR( eVoter ) suggestor_;			// the voter suggesting the vote
711     unsigned int user_;								// user suggesting the vote
712     tArray< tCONTROLLED_PTR( eVoter ) > voters_[2];	// array of voters approving or disapproving of the vote
713     unsigned short id_;								// running id of voting item
714     eMenuItemVote *menuItem_;						// menu item
715     int total_;                                     // total number of voters aware of this item
716 
717     eVoteItem& operator=( const eVoteItem& );
718     eVoteItem( const eVoteItem& );
719 };
720 
721 tList< eVoteItem > eVoteItem::items_;				// list of vote items
722 
se_CancelAllVotes(bool announce)723 void se_CancelAllVotes( bool announce )
724 {
725     if ( sn_GetNetState() == nCLIENT )
726     {
727         return;
728     }
729 
730     if (announce)
731     {
732         sn_ConsoleOut( tOutput( "$vote_cancel_all" ) );
733     }
734 
735     tList< eVoteItem > const & items = eVoteItem::GetItems();
736 
737     while ( items.Len() )
738     {
739         delete items(0);
740     }
741 }
742 
se_CancelAllVotes(std::istream &)743 void se_CancelAllVotes( std::istream & )
744 {
745 	se_CancelAllVotes ( true );
746 }
747 
748 static tConfItemFunc se_cancelAllVotes_conf( "VOTES_CANCEL", &se_CancelAllVotes );
749 
750 
751 
752 
753 
se_votesSuspend(REAL minutes,bool announce,std::istream & s)754 void se_votesSuspend( REAL minutes, bool announce, std::istream & s )
755 {
756     if ( sn_GetNetState() == nCLIENT )
757     {
758         return;
759     }
760 
761     if ( minutes > 0)
762     {
763         s >> minutes;
764     }
765 
766     se_CancelAllVotes( false );
767 
768     se_votesSuspendTimeout = tSysTimeFloat() + ( minutes * 60 );
769 
770     if ( announce && minutes > 0 )
771     {
772         sn_ConsoleOut( tOutput( "$voting_suspended", minutes ) );
773     }
774     else if ( announce && minutes <= 0 )
775     {
776         sn_ConsoleOut( tOutput( "$voting_unsuspended" ) );
777     }
778 
779 }
780 
se_SuspendVotes(std::istream & s)781 void se_SuspendVotes( std::istream & s )
782 {
783     se_votesSuspend(  se_defaultVotesSuspendLength, true, s );
784 }
se_UnSuspendVotes(std::istream & s)785 void se_UnSuspendVotes( std::istream & s )
786 {
787     se_votesSuspend( 0, true, s );
788 }
789 
790 static tConfItemFunc se_suspendVotes_conf( "VOTES_SUSPEND", &se_SuspendVotes );
791 static tConfItemFunc se_unSuspendVotes_conf( "VOTES_UNSUSPEND", &se_UnSuspendVotes );
792 
793 
794 static nDescriptor vote_handler(230,eVoteItem::GetControlMessage,"vote cast");
795 
796 // called on the clients to accept or decline the vote
Vote(bool accept)797 void eVoteItem::Vote( bool accept )
798 {
799     tJUST_CONTROLLED_PTR< nMessage > m = tNEW( nMessage )( vote_handler );
800     *m << id_;
801     *m << accept;
802     m->BroadCast();
803 
804     delete this;
805 }
806 
807 //nDescriptor& eVoteItem::DoGetDescriptor() const;	// returns the creation descriptor
808 //tString eVoteItem::DoGetDescription() const;		// returns the description of the voting item
809 //void DoExecute();						// called when the voting was successful
810 
811 // ****************************************************************************************
812 // ****************************************************************************************
813 
814 // voting decision
815 enum Vote
816 {
817     Vote_Approve,
818     Vote_Reject,
819     Vote_DontMind
820 };
821 
822 #ifdef _MSC_VER
823 #pragma warning ( disable: 4355 )
824 #endif
825 
826 // menu item to silence selected players
827 class eMenuItemVote: public uMenuItemSelection< Vote >
828 {
829     friend class eVoteItem;
830     friend class eVoteItemServerControlled;
831 
832 public:
eMenuItemVote(uMenu * m,eVoteItem * v)833     eMenuItemVote(uMenu *m, eVoteItem* v )
834             : uMenuItemSelection< Vote >( m, tOutput(""), tOutput("$vote_help"), vote_ )
835             , item_( v )
836             , vote_ ( Vote_DontMind )
837             , reject_	( *this, "$vote_reject"		, "$vote_reject_help"		, Vote_Reject 	)
838             , dontMind_	( *this, "$vote_dont_mind"	, "$vote_dont_mind_help"	, Vote_DontMind )
839             , approve_	( *this, "$vote_approve"	, "$vote_approve_help"		, Vote_Approve 	)
840     {
841         tASSERT( v );
842 
843         if ( v )
844         {
845             v->menuItem_ = this;
846             v->UpdateMenuItem();
847         }
848     }
849 
~eMenuItemVote()850     ~eMenuItemVote()
851     {
852         if ( item_ )
853         {
854             item_->menuItem_ = 0;
855 
856             switch ( vote_ )
857             {
858             case Vote_Approve:
859                 item_->Vote( true );
860                 break;
861             case Vote_Reject:
862                 item_->Vote( false );
863                 break;
864             default:
865                 break;
866             }
867         }
868     }
869 
870 private:
871     eVoteItem* item_;										// vote item
872     Vote vote_;												// result
873     uSelectEntry< Vote >  reject_, dontMind_, approve_;		// selection entries
874 };
875 
876 // **************************************************************************************
877 // **************************************************************************************
878 
879 static void se_HandleServerVoteChanged( nMessage& m );
880 static nDescriptor server_vote_expired_handler(233,se_HandleServerVoteChanged,"Server controlled vote expired");
881 
882 // something to vote on: completely controlled by the server
883 class eVoteItemServerControlled: public virtual eVoteItem
884 {
885 public:
886     // constructors/destructor
eVoteItemServerControlled()887     eVoteItemServerControlled()
888             : description_( "No Info" )
889             , details_( "No Info" )
890             , expired_( false )
891     {
892     }
893 
eVoteItemServerControlled(tString const & description,tString const & details)894     eVoteItemServerControlled( tString const & description, tString const & details )
895             : description_( description )
896             , details_( details )
897             , expired_( false )
898     {}
899 
~eVoteItemServerControlled()900     ~eVoteItemServerControlled()
901     {
902         if ( sn_GetNetState() == nSERVER )
903         {
904             expired_ = true;
905             SendChanged();
906         }
907     }
908 
s_HandleChanged(nMessage & m)909     static void s_HandleChanged( nMessage & m )
910     {
911         unsigned short id;
912         m.Read( id );
913         for ( int i = items_.Len()-1; i>=0; --i )
914         {
915             eVoteItem* vote = items_[i];
916             if ( vote->GetID() == id )
917             {
918                 eVoteItemServerControlled * vote2 = dynamic_cast< eVoteItemServerControlled * >( vote );
919                 if ( vote2 )
920                     vote2->HandleChanged( m );
921             }
922         }
923     }
924 
HandleChanged(nMessage & m)925     void HandleChanged( nMessage & m )
926     {
927         unsigned short expired;
928         m.Read( expired );
929         expired_ = expired;
930         m >> description_;
931         m >> details_;
932 
933         Update();
934         UpdateMenuItem();
935     }
936 
SendChanged()937     void SendChanged()
938     {
939         tJUST_CONTROLLED_PTR< nMessage > m = tNEW( nMessage )( server_vote_expired_handler );
940         *m << GetID();
941         *m << (unsigned short)expired_;
942         *m << description_;
943         *m << details_;
944         m->BroadCast();
945     }
946 protected:
947 
DoFillFromMessage(nMessage & m)948     virtual bool DoFillFromMessage( nMessage& m )
949     {
950         m >> description_;
951         m >> details_;
952         return eVoteItem::DoFillFromMessage( m );
953     }
954 
DoFillToMessage(nMessage & m) const955     virtual void DoFillToMessage( nMessage& m ) const
956     {
957         m << description_;
958         m << details_;
959         eVoteItem::DoFillToMessage( m );
960     }
961 
DoExecute()962     virtual void DoExecute(){}						// called when the voting was successful
963 protected:
964     virtual nDescriptor& DoGetDescriptor() const;	// returns the creation descriptor
965 
Evaluate()966     virtual void Evaluate()
967     {
968         // update clients (i.e. if a player to be kicked changed his name)
969         if ( sn_GetNetState() == nSERVER )
970         {
971             Update();
972             SendChanged();
973         }
974 
975         if ( expired_ )
976             delete this;
977         else
978             eVoteItem::Evaluate();
979     }
980 
DoGetDescription() const981     virtual tString DoGetDescription() const		// returns the description of the voting item
982     {
983         return expired_ ? tString("Expired vote") : description_;
984     }
985 
DoGetDetails() const986     virtual tString DoGetDetails() const		    // returns the detailed description of the voting item
987     {
988         return expired_ ? tString("Expired vote") : details_;
989     }
990 protected:
991     mutable tString description_;              //!< the description of the vote
992     mutable tString details_;                  //!< details on the vote
993 private:
994     bool expired_;                             //!< flag set when the vote expired on the server
995 };
996 
se_HandleServerVoteChanged(nMessage & m)997 static void se_HandleServerVoteChanged( nMessage& m )
998 {
999     eVoteItemServerControlled::s_HandleChanged( m );
1000 }
1001 
se_HandleNewServerVote(nMessage & m)1002 static void se_HandleNewServerVote( nMessage& m )
1003 {
1004     if ( sn_GetNetState() != nCLIENT ||  eVoteItem::AcceptNewVote( m ) )
1005     {
1006         // accept message
1007         eVoteItem* item = tNEW( eVoteItemServerControlled )();
1008         if ( !item->FillFromMessage( m ) )
1009             delete item;
1010     }
1011 }
1012 
1013 static nDescriptor new_server_vote_handler(232,se_HandleNewServerVote,"Server controlled vote");
1014 
1015 // returns the creation descriptor
DoGetDescriptor() const1016 nDescriptor& eVoteItemServerControlled::DoGetDescriptor() const
1017 {
1018     return new_server_vote_handler;
1019 }
1020 
1021 // *******************************************************************************************
1022 // *******************************************************************************************
1023 
1024 class nMachineObserver: public nMachineDecorator
1025 {
1026 public:
nMachineObserver(nMachine & machine)1027     nMachineObserver( nMachine & machine )
1028             : nMachineDecorator( machine ), machine_( &machine ){}
1029 
GetMachine()1030     nMachine * GetMachine()
1031     {
1032         return machine_;
1033     }
1034 protected:
OnDestroy()1035     virtual void OnDestroy()
1036     {
1037         machine_ = 0;
1038     }
1039 private:
1040     nMachine * machine_;
1041 };
1042 
1043 static tString se_voteKickToServer("");
1044 static int se_voteKickToPort = 4534;
1045 static tSettingItem< tString > se_voteKickToServerConf( "VOTE_KICK_TO_SERVER", se_voteKickToServer );
1046 static tSettingItem< int > se_voteKickToPortConf( "VOTE_KICK_TO_PORT", se_voteKickToPort );
1047 
1048 // minimal previous harmful votes (kick, silence, suspend) before
1049 // a successful kick vote really results in a kick. Before that, the result is a
1050 // suspension.
1051 static int se_kickMinHarm = 0;
1052 static tSettingItem< int > se_kickMinHarmSI( "VOTING_KICK_MINHARM", se_kickMinHarm );
1053 
1054 // reason given on vote kicks
1055 static tString se_voteKickReason("");
1056 static tConfItemLine se_voteKickReasonConf( "VOTE_KICK_REASON", se_voteKickReason );
1057 
se_VoteKickUser(int user)1058 void se_VoteKickUser( int user )
1059 {
1060     if ( user == 0 )
1061     {
1062         return;
1063     }
1064 
1065     tString reason;
1066     if ( se_voteKickReason.Len() >= 2 )
1067     {
1068         reason = se_voteKickReason;
1069     }
1070     else
1071     {
1072         reason = tOutput("$voted_kill_kick");
1073     }
1074 
1075     if ( se_voteKickToServer.Len() < 2 )
1076     {
1077         sn_KickUser( user, reason );
1078     }
1079     else
1080     {
1081         // kick player to default destination
1082         nServerInfoRedirect redirect( se_voteKickToServer, se_voteKickToPort );
1083         sn_KickUser( user, reason, 1, &redirect );
1084     }
1085 }
1086 
se_VoteKickPlayer(ePlayerNetID * p)1087 void se_VoteKickPlayer( ePlayerNetID * p )
1088 {
1089     if ( !p )
1090     {
1091         return;
1092     }
1093 
1094     se_VoteKickUser( p->Owner() );
1095 }
1096 
1097 // something to vote on: harming a player
1098 class eVoteItemHarm: public virtual eVoteItem
1099 {
1100 public:
1101     // constructors/destructor
eVoteItemHarm(ePlayerNetID * player=0)1102     eVoteItemHarm( ePlayerNetID* player = 0 )
1103             : player_( player )
1104             , machine_(NULL)
1105             , name_( "(Player who already left)" )
1106     {}
1107 
~eVoteItemHarm()1108     ~eVoteItemHarm()
1109     {
1110         delete machine_;
1111         machine_ = NULL;
1112     }
1113 
1114     // returns the player that is to be harmed
GetPlayer() const1115     ePlayerNetID * GetPlayer() const
1116     {
1117         ePlayerNetID const * player = player_;
1118         return const_cast< ePlayerNetID * >( player );
1119     }
1120 protected:
1121     // this is a good spot to put in legacy hooks
DoGetDescriptorLegacy() const1122     virtual nDescriptor * DoGetDescriptorLegacy() const
1123     {
1124         return &eVoteItemHarm::DoGetDescriptor();
1125     }
1126 
DoFillToMessageLegacy(nMessage & m) const1127     virtual void DoFillToMessageLegacy( nMessage& m ) const
1128     {
1129         return eVoteItemHarm::DoFillToMessage( m );
1130     }
1131 
DoFillFromMessage(nMessage & m)1132     virtual bool DoFillFromMessage( nMessage& m )
1133     {
1134         // read player ID
1135         unsigned short id;
1136         m.Read(id);
1137         tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
1138         player_ = p;
1139 
1140         return eVoteItem::DoFillFromMessage( m );
1141     }
1142 
DoCheckValid(int senderID)1143     virtual bool DoCheckValid( int senderID )
1144     {
1145         // always accept votes from server
1146         if ( sn_GetNetState() == nCLIENT && senderID == 0 )
1147         {
1148             return true;
1149         }
1150 
1151         eVoter * sender = eVoter::GetVoter( senderID  );
1152 
1153         double time = tSysTimeFloat();
1154 
1155         // check whether the issuer is allowed to start a vote
1156         if ( sender && player_ && player_->GetTimeCreated() + se_votingMaturity > time )
1157         {
1158             REAL timeLeft = player_->GetTimeCreated() + se_votingMaturity - time;
1159             tOutput message( "$vote_maturity", timeLeft );
1160             sn_ConsoleOut( message, senderID );
1161             return false;
1162         }
1163 
1164         // prevent the sender from changing his name for confusion
1165         if ( sender )
1166             sender->lastNameChangePreventor_ = time;
1167 
1168         // check if player is protected from kicking
1169         if ( player_ && sn_GetNetState() != nCLIENT )
1170         {
1171             // check whether the player is on the server
1172             if ( player_->Owner() == 0 )
1173             {
1174                 sn_ConsoleOut( tOutput( "$vote_kick_local", player_->GetName() ), senderID );
1175                 return false;
1176             }
1177 
1178             name_ = player_->GetName();
1179             eVoter * voter = eVoter::GetVoter( player_->Owner() );
1180             if ( voter )
1181             {
1182                 machine_ = tNEW( nMachineObserver )( voter->machine_ );
1183 
1184                 if ( time < voter->lastHarmVote_ + se_minTimeBetweenHarms )
1185                 {
1186                     tOutput message("$vote_redundant");
1187                     sn_ConsoleOut( message, senderID );
1188                     return false;
1189                 }
1190                 else
1191                 {
1192                     voter->lastHarmVote_ = time;
1193                     voter->lastNameChangePreventor_ = time;
1194                 }
1195 
1196                 // count harmful votes
1197                 voter->harmCount_++;
1198             }
1199         }
1200 
1201         return eVoteItem::DoCheckValid( senderID );
1202     }
1203 
DoFillToMessage(nMessage & m) const1204     virtual void DoFillToMessage( nMessage& m  ) const
1205     {
1206         if ( player_ )
1207             m.Write( player_->ID() );
1208         else
1209             m.Write( 0 );
1210 
1211         eVoteItem::DoFillToMessage( m );
1212     }
1213 
1214 protected:
1215     virtual nDescriptor& DoGetDescriptor() const;	// returns the creation descriptor
1216 
1217     // get the language string prefix
1218     virtual char const * DoGetPrefix() const = 0;
1219 
DoGetDescription() const1220     virtual tString DoGetDescription() const		// returns the description of the voting item
1221     {
1222         // get name from player
1223         if ( player_ )
1224             name_ = player_->GetName();
1225 
1226         return tString( tOutput( tString("$") + DoGetPrefix() + "_player_text", name_ ) );
1227     }
1228 
DoGetDetails() const1229     virtual tString DoGetDetails() const		    // returns the detailed description of the voting item
1230     {
1231         // get name from player
1232         if ( player_ )
1233             name_ = player_->GetName();
1234 
1235         return eVoteItem::DoGetDetails() + tString( tOutput( tString("$") + DoGetPrefix() + "_player_details_text", name_ ) );
1236     }
1237 
GetMachine() const1238     nMachine * GetMachine() const
1239     {
1240         if ( !machine_ )
1241         {
1242             return 0;
1243         }
1244         else
1245         {
1246             return machine_->GetMachine();
1247         }
1248     }
1249 private:
1250     nObserverPtr< ePlayerNetID > player_;		// keep player referenced
1251     nMachineObserver * machine_;                // pointer to the machine of the player to be kicked
1252     mutable tString name_;                      // the name of the player to be kicked
1253 };
1254 
1255 // something to vote on: kicking a player
1256 class eVoteItemKick: public virtual eVoteItemHarm
1257 {
1258 public:
1259     // constructors/destructor
eVoteItemKick(ePlayerNetID * player)1260     eVoteItemKick( ePlayerNetID* player )
1261         : eVoteItemHarm( player )
1262     {}
1263 
~eVoteItemKick()1264     ~eVoteItemKick()
1265     {}
1266 
1267 protected:
1268     // get the language string prefix
DoGetPrefix() const1269     virtual char const * DoGetPrefix() const{ return "kick"; }
1270 
1271 #ifdef KRAWALL_SERVER
1272     // access level required for this kind of vote
DoGetAccessLevel() const1273     virtual tAccessLevel DoGetAccessLevel() const
1274     {
1275         return se_accessLevelVoteKick;
1276     }
1277 #endif
1278 
1279     // return vote-specific extra bias
DoGetExtraBias() const1280     virtual int DoGetExtraBias() const
1281     {
1282         return se_votingBiasKick;
1283     }
1284 
DoCheckValid(int senderID)1285     virtual bool DoCheckValid( int senderID )
1286     {
1287         ePlayerNetID * player = GetPlayer();
1288 
1289         // check if player is protected from kicking
1290         if ( player && sn_GetNetState() != nCLIENT )
1291         {
1292             eVoter * voter = eVoter::GetVoter( player->Owner() );
1293             if ( voter )
1294             {
1295                 double time = tSysTimeFloat();
1296                 if ( time < voter->lastKickVote_ + se_minTimeBetweenKicks )
1297                 {
1298                     tOutput message("$vote_redundant");
1299                     sn_ConsoleOut( message, senderID );
1300                     return false;
1301                 }
1302                 else
1303                 {
1304                     voter->lastKickVote_ = time;
1305                     voter->lastNameChangePreventor_ = time;
1306                 }
1307             }
1308         }
1309 
1310         return eVoteItemHarm::DoCheckValid( senderID );
1311     }
1312 
DoExecute()1313     virtual void DoExecute()						// called when the voting was successful
1314     {
1315         ePlayerNetID * player = GetPlayer();
1316         nMachine * machine = GetMachine();
1317         if ( player )
1318         {
1319             // kick the player, he is online
1320             se_VoteKickPlayer( player );
1321         }
1322         else if ( machine )
1323         {
1324             // the player left. Inform the machine that he would have gotten kicked.
1325             // kick all players that connected from that machine
1326             bool kick = false;
1327             for ( int user = MAXCLIENTS; user > 0; --user )
1328             {
1329                 if ( &nMachine::GetMachine( user ) == machine )
1330                 {
1331                     se_VoteKickUser( user );
1332                     kick = true;
1333                 }
1334             }
1335 
1336             // if no user could be kicked, notify at least the machine that
1337             // somebody would have been kicked.
1338             if ( !kick )
1339             {
1340                 machine->OnKick();
1341             }
1342         }
1343     }
1344 
1345 private:
1346 };
1347 
1348 // harming vote items, server controlled
1349 class eVoteItemHarmServerControlled: public virtual eVoteItemServerControlled, public virtual eVoteItemHarm
1350 {
1351 public:
1352     // constructors/destructor
eVoteItemHarmServerControlled(ePlayerNetID * player=0)1353     eVoteItemHarmServerControlled( ePlayerNetID* player = 0 )
1354             : eVoteItemHarm( player )
1355     {}
1356 
~eVoteItemHarmServerControlled()1357     ~eVoteItemHarmServerControlled()
1358     {}
1359 protected:
DoFillFromMessage(nMessage & m)1360     virtual bool DoFillFromMessage( nMessage& m )
1361     {
1362         // delegate
1363         bool ret = eVoteItemHarm::DoFillFromMessage( m );
1364 
1365         // fill in description
1366         Update();
1367 
1368         return ret;
1369     }
1370 
DoFillToMessage(nMessage & m) const1371     virtual void DoFillToMessage( nMessage& m  ) const
1372     {
1373         // should never be called on the client
1374         tASSERT( sn_GetNetState() != nCLIENT );
1375 
1376         eVoteItemServerControlled::DoFillToMessage( m );
1377     }
1378 private:
Update()1379     virtual void Update() //!< update description and details
1380     {
1381         description_ = eVoteItemHarm::DoGetDescription();
1382         details_ = eVoteItemHarm::DoGetDetails();
1383     }
1384 
DoGetDescriptor() const1385     virtual nDescriptor& DoGetDescriptor() const	// returns the creation descriptor
1386     {
1387         return eVoteItemServerControlled::DoGetDescriptor();
1388     }
1389 
DoGetDescription() const1390     virtual tString DoGetDescription() const		// returns the description of the voting item
1391     {
1392         return eVoteItemServerControlled::DoGetDescription();
1393     }
1394 
DoGetDetails() const1395     virtual tString DoGetDetails() const		// returns the detailed description of the voting item
1396     {
1397         return eVoteItemServerControlled::DoGetDetails();
1398     }
1399 };
1400 
1401 // remove vote items, server controlled
1402 class eVoteItemSuspend: public virtual eVoteItemHarmServerControlled
1403 {
1404 public:
1405     // constructors/destructor
eVoteItemSuspend(ePlayerNetID * player=0)1406     eVoteItemSuspend( ePlayerNetID* player = 0 )
1407         : eVoteItemHarm( player )
1408         {}
1409 
~eVoteItemSuspend()1410     ~eVoteItemSuspend()
1411     {}
1412 protected:
1413     // get the language string prefix
DoGetPrefix() const1414     virtual char const * DoGetPrefix() const{ return "suspend"; }
1415 
1416 #ifdef KRAWALL_SERVER
1417     // access level required for this kind of vote
DoGetAccessLevel() const1418     virtual tAccessLevel DoGetAccessLevel() const
1419     {
1420         return se_accessLevelVoteSuspend;
1421     }
1422 #endif
1423 
1424     // return vote-specific extra bias
DoGetExtraBias() const1425     virtual int DoGetExtraBias() const
1426     {
1427         return se_votingBiasSuspend;
1428     }
1429 
DoExecute()1430     virtual void DoExecute()						// called when the voting was successful
1431     {
1432         ePlayerNetID * player = GetPlayer();
1433         if ( player )
1434         {
1435             player->Suspend( se_suspendRounds );
1436         }
1437     }
1438 };
1439 
1440 // kick vote items, server controlled
1441 class eVoteItemKickServerControlled: public virtual eVoteItemHarmServerControlled, public virtual eVoteItemKick
1442 {
1443 public:
1444     // constructors/destructor
eVoteItemKickServerControlled(bool fromMenu,ePlayerNetID * player)1445     eVoteItemKickServerControlled( bool fromMenu, ePlayerNetID* player )
1446     : eVoteItemHarm( player ), eVoteItemKick( player ), fromMenu_( fromMenu )
1447     {}
1448 
~eVoteItemKickServerControlled()1449     ~eVoteItemKickServerControlled()
1450     {}
1451 protected:
DoCheckValid(int senderID)1452     virtual bool DoCheckValid( int senderID )
1453     {
1454         // check whether enough harmful votes were collected already
1455         ePlayerNetID * p = GetPlayer();
1456         if ( p && !p->GetVoter() )
1457         {
1458             p->CreateVoter();
1459         }
1460 
1461         if ( fromMenu_ && p && p->GetVoter() && p->GetVoter()->HarmCount() < se_kickMinHarm )
1462         {
1463             // try to transfor the vote to a suspension
1464             eVoteItem * item = tNEW ( eVoteItemSuspend )( p );
1465 
1466             // let item check its validity
1467             if ( !item->CheckValid( senderID ) )
1468             {
1469                 delete item;
1470             }
1471             else
1472             {
1473                 // no objection? Broadcast it to everyone.
1474                 item->Update();
1475                 item->ReBroadcast( senderID );
1476             }
1477 
1478             // and cancel this item here.
1479             return false;
1480         }
1481 
1482         // no transformation needed or transformation failed. Proceed as usual.
1483         return eVoteItemHarm::DoCheckValid( senderID );
1484     }
1485 
DoExecute()1486     virtual void DoExecute()						// called when the voting was successful
1487     {
1488         eVoteItemKick::DoExecute();
1489     }
1490 private:
1491     bool fromMenu_; // flag set if the vote came from the menu
1492 };
1493 
se_HandleKickVote(nMessage & m)1494 static void se_HandleKickVote( nMessage& m )
1495 {
1496     // set high default access level for menu issued kick votes, the true access level
1497     // is taken from the highest level player from the sending client later
1498     tCurrentAccessLevel level( tAccessLevel_Owner, true );
1499 
1500     // accept message
1501     if ( eVoteItem::AcceptNewVote( m ) )
1502     {
1503         eVoteItemHarm* item = tNEW( eVoteItemKickServerControlled )( true, 0 );
1504         if ( !item->FillFromMessage( m ) )
1505         {
1506             delete item;
1507             return;
1508         }
1509     }
1510 }
1511 
1512 static nDescriptor kill_vote_handler(231,se_HandleKickVote,"Kick vote");
1513 
1514 // returns the creation descriptor
DoGetDescriptor() const1515 nDescriptor& eVoteItemHarm::DoGetDescriptor() const
1516 {
1517     return kill_vote_handler;
1518 }
1519 
se_SendKick(ePlayerNetID * p)1520 static void se_SendKick( ePlayerNetID* p )
1521 {
1522     eVoteItemKick kick( p );
1523     kick.SendMessage();
1524 }
1525 
1526 #ifdef KRAWALL_SERVER
1527 
1528 // console with filter for redirection to anyone with a certain access level
1529 class eAccessConsoleFilter: public tConsoleFilter
1530 {
1531 public:
eAccessConsoleFilter(tAccessLevel level)1532     eAccessConsoleFilter( tAccessLevel level )
1533             :level_( level )
1534     {
1535     }
1536 
Send()1537     void Send()
1538     {
1539         bool canSee[ MAXCLIENTS+1 ];
1540         for( int i = MAXCLIENTS; i>=0; --i )
1541         {
1542             canSee[i] = false;
1543         }
1544 
1545         // look which clients have someone who can see the message
1546         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
1547         {
1548             ePlayerNetID* player = se_PlayerNetIDs(i);
1549             if ( player->GetAccessLevel() <= level_ )
1550             {
1551                 canSee[ player->Owner() ] = true;
1552             }
1553         }
1554 
1555         // and send it
1556         for( int i = MAXCLIENTS; i>=0; --i )
1557         {
1558             if ( canSee[i] )
1559             {
1560                 sn_ConsoleOut( message_, i );
1561             }
1562         }
1563 
1564         message_.Clear();
1565     }
1566 
~eAccessConsoleFilter()1567     ~eAccessConsoleFilter()
1568     {
1569         Send();
1570     }
1571 private:
1572     // we want to come first, the admins should get unfiltered output
DoGetPriority() const1573     virtual int DoGetPriority() const{ return -100; }
1574 
1575     // don't actually filter; take line and add it to the message sent to the admin
DoFilterLine(tString & line)1576     virtual void DoFilterLine( tString &line )
1577     {
1578         //tColoredString message;
1579         message_ << tColoredString::ColorString(1,.3,.3) << "RA: " << tColoredString::ColorString(1,1,1) << line << "\n";
1580 
1581         // don't let message grow indefinitely
1582         if (message_.Len() > 600)
1583         {            Send();
1584         }
1585     }
1586 
1587     tAccessLevel level_;     // the access level required
1588     tColoredString message_; // the console message for the remote administrator
1589 };
1590 
1591 // include vote items
1592 class eVoteItemInclude: public eVoteItemServerControlled
1593 {
1594 public:
1595     // constructors/destructor
eVoteItemInclude(tString const & file,tAccessLevel submitterLevel)1596     eVoteItemInclude( tString const & file, tAccessLevel submitterLevel )
1597     : eVoteItemServerControlled()
1598     , file_( file )
1599     {
1600         description_ = tOutput( "$vote_include_text", file );
1601         file_ = tString( "vote/" ) + file_;
1602         details_ = tOutput( "$vote_include_details_text", file_ );
1603 
1604         if ( submitterLevel > se_accessLevelVoteIncludeExecute )
1605         {
1606             submitterLevel = se_accessLevelVoteIncludeExecute;
1607         }
1608         level_ = submitterLevel;
1609     }
1610 
~eVoteItemInclude()1611     ~eVoteItemInclude()
1612     {}
1613 protected:
1614     // access level required for this kind of vote
DoGetAccessLevel() const1615     virtual tAccessLevel DoGetAccessLevel() const
1616     {
1617         return se_accessLevelVoteInclude;
1618     }
1619 
1620     // return vote-specific extra bias
DoGetExtraBias() const1621     virtual int DoGetExtraBias() const
1622     {
1623         return se_votingBiasInclude;
1624     }
1625 
Open(std::ifstream & s,int userToNotify)1626     bool Open( std::ifstream & s, int userToNotify )
1627     {
1628         bool ret = tConfItemBase::OpenFile( s, file_, tConfItemBase::All );
1629 
1630         if ( ret )
1631         {
1632             return true;
1633         }
1634         else
1635         {
1636             con << tOutput( "$vote_include_error", file_ );
1637             sn_ConsoleOut( tOutput( "$vote_include_error", file_ ), userToNotify );
1638             return false;
1639         }
1640     }
1641 
DoCheckValid(int senderID)1642     virtual bool DoCheckValid( int senderID )
1643     {
1644         std::ifstream s;
1645         return ( Open( s, senderID ) && eVoteItemServerControlled::DoCheckValid( senderID ) );
1646     }
1647 
DoExecute()1648     virtual void DoExecute()						// called when the voting was successful
1649     {
1650         // set the access level for the following operation
1651         tCurrentAccessLevel accessLevel( level_, true );
1652 
1653         // load contents of voted file for real
1654         std::ifstream s;
1655         if ( Open( s, 0 ) )
1656         {
1657             sn_ConsoleOut( tOutput( "$vote_include_message", file_, tCurrentAccessLevel::GetName( level_ ) ) );
1658             eAccessConsoleFilter filter( level_ );
1659             tConfItemBase::ReadFile( s );
1660         }
1661     }
1662 
1663     tString file_;       //!< the file to include (inside the vote/ subdirectory)
1664     tAccessLevel level_; //!< the level to execute the file with
1665 };
1666 
1667 // command vote items
1668 class eVoteItemCommand: public eVoteItemServerControlled
1669 {
1670 public:
1671     // constructors/destructor
eVoteItemCommand(tString const & command,tAccessLevel submitterLevel)1672     eVoteItemCommand( tString const & command, tAccessLevel submitterLevel )
1673     : eVoteItemServerControlled()
1674     , command_( command )
1675     {
1676         description_ = tOutput( "$vote_command_text", command );
1677         details_ = tOutput( "$vote_command_details_text", command );
1678 
1679         if ( submitterLevel > se_accessLevelVoteCommandExecute )
1680         {
1681             submitterLevel = se_accessLevelVoteCommandExecute;
1682         }
1683         level_ = submitterLevel;
1684     }
1685 
~eVoteItemCommand()1686     ~eVoteItemCommand()
1687     {}
1688 protected:
1689     // access level required for this kind of vote
DoGetAccessLevel() const1690     virtual tAccessLevel DoGetAccessLevel() const
1691     {
1692         return se_accessLevelVoteCommand;
1693     }
1694 
1695     // return vote-specific extra bias
DoGetExtraBias() const1696     virtual int DoGetExtraBias() const
1697     {
1698         return se_votingBiasCommand;
1699     }
1700 
DoExecute()1701     virtual void DoExecute()						// called when the voting was successful
1702     {
1703         // set the access level for the following operation
1704         tCurrentAccessLevel accessLevel( level_, true );
1705 
1706         // load contents of everytime.cfg for real
1707         std::istringstream s( static_cast< char const * >( command_ ) );
1708         sn_ConsoleOut( tOutput( "$vote_command_message" ) );
1709         eAccessConsoleFilter filter( tAccessLevel_Default );
1710         tConfItemBase::LoadLine(s);
1711     }
1712 
1713     tString command_;    //!< the command to execute
1714     tAccessLevel level_; //!< the level to execute the file with
1715 };
1716 
1717 #endif
1718 
1719 // **************************************************************************************
1720 // **************************************************************************************
1721 
1722 
1723 // menu item to silence selected players
1724 class eMenuItemKick: public uMenuItemAction
1725 {
1726 public:
eMenuItemKick(uMenu * m,ePlayerNetID * p)1727     eMenuItemKick(uMenu *m, ePlayerNetID* p )
1728             : uMenuItemAction( m, tOutput(""),tOutput("$kick_player_help" ) )
1729     {
1730         this->name_.Clear();
1731         this->name_.SetTemplateParameter(1, p->GetName() );
1732         this->name_ << "$kick_player_text";
1733         player_ = p;
1734     }
1735 
~eMenuItemKick()1736     ~eMenuItemKick()
1737     {
1738     }
1739 
Enter()1740     virtual void Enter()
1741     {
1742         if(sn_GetNetState()==nSERVER)
1743         {
1744             // kill user directly
1745             se_VoteKickPlayer( player_ );
1746         }
1747         {
1748             // issue kick vote
1749             se_SendKick( player_ );
1750         }
1751 
1752         // leave menu to release smart pointers
1753         this->menu->Exit();
1754     }
1755 private:
1756     tCONTROLLED_PTR( ePlayerNetID ) player_;		// keep player referenced
1757 };
1758 
1759 
1760 // ****************************************************************************************
1761 // ****************************************************************************************
1762 
1763 static nSpamProtectionSettings se_voteSpamProtection( 50.0f, "SPAM_PROTECTION_VOTE", tOutput("$vote_spam_protection") );
1764 
eVoter(nMachine & machine)1765 eVoter::eVoter( nMachine & machine )
1766         : nMachineDecorator( machine ), machine_( machine ), votingSpam_( se_voteSpamProtection )
1767 {
1768     selfReference_ = this;
1769     voters_.Add( this );
1770     harmCount_ = 0;
1771     lastKickVote_ = -1E+40;
1772     lastHarmVote_ = -1E+40;
1773     lastNameChangePreventor_ = -1E+40;
1774     lastChange_ = tSysTimeFloat();
1775 }
1776 
~eVoter()1777 eVoter::~eVoter()
1778 {
1779     voters_.Remove( this );
1780 }
1781 
Spam(int user,REAL spamLevel,tOutput const & message)1782 void eVoter::Spam( int user, REAL spamLevel, tOutput const & message )
1783 {
1784     if ( sn_GetNetState() == nSERVER )
1785         votingSpam_.CheckSpam( spamLevel, user, message );
1786 }
1787 
IsSpamming(int user)1788 bool eVoter::IsSpamming( int user )
1789 {
1790     if ( sn_GetNetState() == nSERVER )
1791     {
1792         return nSpamProtection::Level_Ok != votingSpam_.CheckSpam( 0.0f, user, tOutput("$spam_vote_kick_issue") );
1793     }
1794 
1795     return false;
1796 }
1797 
1798 // *******************************************************************************
1799 // *
1800 // *	OnDestroy
1801 // *
1802 // *******************************************************************************
1803 //!
1804 //!
1805 // *******************************************************************************
1806 
OnDestroy(void)1807 void eVoter::OnDestroy( void )
1808 {
1809     tJUST_CONTROLLED_PTR< eVoter > keepAlive( this );
1810     selfReference_ = 0;
1811 }
1812 
1813 
1814 // *******************************************************************************
1815 // *
1816 // *	OnDestroy
1817 // *
1818 // *******************************************************************************
1819 //!
1820 //! @return the last change to players on this voter in seconds
1821 //!
1822 // *******************************************************************************
1823 
Age() const1824 REAL eVoter::Age() const
1825 {
1826     return tSysTimeFloat() - lastChange_;
1827 }
1828 
1829 
1830 
1831 // *******************************************************************************
1832 // *
1833 // *	AllowNameChange
1834 // *
1835 // *******************************************************************************
1836 //! @return true if the players belonging to this voter should be allowed to rename
1837 //!
1838 // *******************************************************************************
1839 
AllowNameChange(void) const1840 bool eVoter::AllowNameChange( void ) const
1841 {
1842     return tSysTimeFloat() > this->lastNameChangePreventor_ + se_minTimeBetweenKicks;
1843 }
1844 
RemoveFromGame()1845 void eVoter::RemoveFromGame()
1846 {
1847     tCONTROLLED_PTR( eVoter ) keeper( this );
1848 
1849     voters_.Remove( this );
1850 
1851     // remove from items
1852     for ( int i = eVoteItem::GetItems().Len()-1; i>=0; --i )
1853     {
1854         eVoteItem::GetItems()( i )->RemoveVoterCompletely( this );
1855     }
1856 }
1857 
KickMenu()1858 void eVoter::KickMenu()							// activate player kick menu
1859 {
1860     uMenu menu( "$player_police_kick_text" );
1861 
1862     int size = se_PlayerNetIDs.Len();
1863     eMenuItemKick** items = tNEW( eMenuItemKick* )[ size ];
1864 
1865     int i;
1866     for ( i = size-1; i>=0; --i )
1867     {
1868         ePlayerNetID* player = se_PlayerNetIDs[ i ];
1869         if ( player->IsHuman() )
1870         {
1871             items[i] = tNEW( eMenuItemKick )( &menu, player );
1872         }
1873         else
1874         {
1875             items[i] = 0;
1876         }
1877     }
1878 
1879     menu.Enter();
1880 
1881     for ( i = size - 1; i>=0; --i )
1882     {
1883         if( items[i] )
1884             delete items[i];
1885     }
1886     delete[] items;
1887 }
1888 
1889 #ifndef DEDICATED
se_KeepConsoleSmall()1890 static bool se_KeepConsoleSmall()
1891 {
1892     return true;
1893 }
1894 #endif
1895 
1896 static uMenu* votingMenu = 0;
1897 
VotingMenu()1898 void eVoter::VotingMenu()						// activate voting menu ( you can vote about suggestions there )
1899 {
1900     static bool recursion = false;
1901     if ( ! recursion )
1902     {
1903 
1904         // expire old items
1905         if ( !VotingPossible() )
1906             return;
1907 
1908 #ifndef DEDICATED
1909         rSmallConsoleCallback SmallConsole( se_KeepConsoleSmall );
1910 
1911         // count items
1912         int size = eVoteItem::GetItems().Len();
1913         if ( size == 0 )
1914             return;
1915 
1916         // fill menu
1917         uMenu menu( "$voting_menu_text" );
1918 
1919         eMenuItemVote** items = tNEW( eMenuItemVote* )[ size ];
1920 
1921         int i;
1922         for ( i = size-1; i>=0; --i )
1923         {
1924             items[i] = tNEW( eMenuItemVote )( &menu, eVoteItem::GetItems()( i ) );
1925         }
1926 
1927         // enter menu
1928         recursion = true;
1929         votingMenu = &menu;
1930         menu.Enter();
1931         votingMenu = 0;
1932         recursion = false;
1933 
1934         for ( i = size - 1; i>=0; --i )
1935         {
1936             delete items[i];
1937         }
1938         delete[] items;
1939 
1940         // expire old items
1941         VotingPossible();
1942 #endif
1943     }
1944 }
1945 
VotingPossible()1946 bool eVoter::VotingPossible()
1947 {
1948     // expire old items
1949     for ( int i = eVoteItem::GetItems().Len()-1; i>=0; --i )
1950     {
1951         eVoteItem::GetItems()( i )->Evaluate();
1952     }
1953 
1954     if ( sn_GetNetState() != nCLIENT )
1955     {
1956         return false;
1957     }
1958 
1959     return eVoteItem::GetItems().Len() > 0;
1960 }
1961 
GetVoter(int ID,bool complain)1962 eVoter* eVoter::GetVoter( int ID, bool complain )			// find or create the voter for the specified ID
1963 {
1964     // the server has no voter
1965 #ifdef DEDICATED
1966     if ( ID == 0 )
1967         return NULL;
1968 #endif
1969 
1970     // see if there is a real player on the specified ID
1971     if ( !se_allowVotingSpectator )
1972     {
1973         bool player = false;
1974         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
1975         {
1976             ePlayerNetID* p = se_PlayerNetIDs(i);
1977             if ( p->Owner() == ID && p->CurrentTeam() )
1978                 player = true;
1979         }
1980         if (!player)
1981         {
1982             if ( complain )
1983             {
1984                 tOutput message("$vote_disabled_spectator");
1985                 sn_ConsoleOut( message, ID );
1986             }
1987             return NULL;
1988         }
1989     }
1990 
1991     // get machine from network subsystem
1992     nMachine & machine = nMachine::GetMachine( ID );
1993 
1994     return GetVoter( machine );
1995 }
1996 
GetVoter(nMachine & machine)1997 eVoter* eVoter::GetVoter( nMachine & machine )			// find or create the voter for the specified machine
1998 {
1999     // iterate through the machine's decorators, find a voter
2000     nMachineDecorator * run = machine.GetDecorators();
2001     while ( run )
2002     {
2003         eVoter * voter = dynamic_cast< eVoter * >( run );
2004         if ( voter )
2005         {
2006             // reinsert voter into lists
2007             if ( voter->ListID() < 0 )
2008             {
2009                 voters_.Add( voter );
2010                 voter->lastKickVote_ = -1E30;
2011                 voter->lastNameChangePreventor_ = -1E30;
2012             }
2013 
2014             // return result
2015             return voter;
2016         }
2017 
2018         run = run->Next();
2019     }
2020 
2021     // create new voter
2022     return tNEW(eVoter)( machine );
2023 }
2024 
2025 tList< eVoter > eVoter::voters_;					// list of all voters
2026 
se_Cleanup()2027 static void se_Cleanup()
2028 {
2029     if ( nCallbackLoginLogout::User() == 0 )
2030     {
2031         if ( votingMenu )
2032         {
2033             votingMenu->Exit();
2034         }
2035 
2036         if ( !nCallbackLoginLogout::Login() && eGrid::CurrentGrid() )
2037         {
2038             //			uMenu::exitToMain = true;
2039         }
2040 
2041         // client login/logout: delete voting items
2042         const tList< eVoteItem >& list = eVoteItem::GetItems();
2043         while ( list.Len() > 0 )
2044         {
2045             delete list(0);
2046         }
2047     }
2048     else if ( nCallbackLoginLogout::Login() )
2049     {
2050         // new user: send pending voting items
2051         const tList< eVoteItem >& list = eVoteItem::GetItems();
2052         for ( int i = list.Len()-1; i >= 0; -- i)
2053         {
2054             eVoteItem* vote = list( i );
2055             nMessage* m = vote->CreateMessage();
2056             m->Send( nCallbackLoginLogout::User() );
2057         }
2058     }
2059 }
2060 
2061 
2062 static nCallbackLoginLogout se_cleanup( se_Cleanup );
2063 
~eVoteItem(void)2064 eVoteItem::~eVoteItem( void )
2065 {
2066     items_.Remove( this );
2067 
2068     if ( menuItem_ )
2069     {
2070         menuItem_->item_ = 0;
2071         menuItem_->title.Clear();
2072         menuItem_->helpText.Clear();
2073         menuItem_ = 0;
2074     }
2075 }
2076 
UpdateMenuItem(void)2077 void eVoteItem::UpdateMenuItem( void )
2078 {
2079     if ( menuItem_ )
2080     {
2081         menuItem_->title.Clear();
2082         menuItem_->title = GetDescription();
2083 
2084         menuItem_->helpText.Clear();
2085         menuItem_->helpText << tString( tOutput( "$vote_details_help", GetDetails() ) );
2086     }
2087 }
2088 
2089 // *******************************************************************************************
2090 // *
2091 // *	Name
2092 // *
2093 // *******************************************************************************************
2094 //!
2095 //!		@param senderID 	the ID of the network user ( default: take any )
2096 //!		@return				the name of the voter ( all players of that IP )
2097 //!
2098 // *******************************************************************************************
2099 
Name(int senderID) const2100 tString eVoter::Name( int senderID ) const
2101 {
2102     tString name;
2103 
2104     // collect the names of all players associated with this voter
2105     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
2106     {
2107         ePlayerNetID* p = se_PlayerNetIDs(i);
2108         if ( eVoter::GetVoter( p->Owner() ) == this && ( senderID < 0 || p->Owner() == senderID ) )
2109         {
2110             if ( name.Len() > 1 )
2111                 name << ", ";
2112             name << p->GetName();
2113         }
2114     }
2115 
2116     if ( name.Len() < 2 )
2117         name = machine_.GetIP();
2118 
2119     return name;
2120 }
2121 
2122 // *******************************************************************************
2123 // *
2124 // *	PlayerChanged
2125 // *
2126 // *******************************************************************************
2127 //!
2128 //!
2129 // *******************************************************************************
2130 
PlayerChanged(void)2131 void eVoter::PlayerChanged( void )
2132 {
2133     this->lastChange_ = tSysTimeFloat();
2134 }
2135 
2136 // *******************************************************************************
2137 // *
2138 // *	HandleChat
2139 // *
2140 // *******************************************************************************
2141 //! @param p the player chatting
2142 //! @param message the rest of the message after "/vote"
2143 // *******************************************************************************
2144 
HandleChat(ePlayerNetID * p,std::istream & message)2145 void eVoter::HandleChat( ePlayerNetID * p, std::istream & message ) //!< handles player "/vote" command.
2146 {
2147     // cloak the ID of the sener for privacy
2148     nCurrentSenderID cloak;
2149     if ( se_votingPrivacy > 1 )
2150         cloak.SetID(0);
2151 
2152     if ( !p )
2153     {
2154         return;
2155     }
2156 
2157     // read command part (kick, remove, include)
2158     tString command;
2159     message >> command;
2160     tToLower( command );
2161 
2162     eVoter * voter = eVoter::GetVoter( p->Owner(), true ); // can't use ePlayerNedID::GetVoter here,
2163                                                            // as it can't show a warning,
2164                                                            // for example if the voter has only spectators
2165     if ( !eVoteItem::AcceptNewVote( voter, p->Owner() ) )
2166     {
2167         return;
2168     }
2169 
2170     eVoteItem * item = 0;
2171 
2172     if ( command == "kick" )
2173     {
2174         tString name;
2175         name.ReadLine( message );
2176         ePlayerNetID * toKick = ePlayerNetID::FindPlayerByName( name, p );
2177         if ( toKick )
2178         {
2179             // accept message
2180             item = tNEW( eVoteItemKickServerControlled )( false, toKick );
2181         }
2182     }
2183     else if ( command == "suspend" )
2184     {
2185         tString name;
2186         name.ReadLine( message );
2187         ePlayerNetID * toSuspend = ePlayerNetID::FindPlayerByName( name, p );
2188         if ( toSuspend )
2189         {
2190             // accept message
2191             item = tNEW( eVoteItemSuspend )( toSuspend );
2192         }
2193     }
2194 #ifdef KRAWALL_SERVER
2195     else if ( command == "include" )
2196     {
2197         tString file;
2198         file.ReadLine( message );
2199         {
2200             // accept message
2201             item = tNEW( eVoteItemInclude )( file, p->GetAccessLevel() );
2202         }
2203     }
2204     else if ( command == "command" )
2205     {
2206         tString console;
2207         console.ReadLine( message );
2208         {
2209             // accept message
2210             item = tNEW( eVoteItemCommand )( console, p->GetAccessLevel() );
2211         }
2212     }
2213 #endif
2214     else
2215     {
2216 #ifdef KRAWALL_SERVER
2217         sn_ConsoleOut( tOutput("$vote_unknown_command", command, "suspend, kick, include, command" ), p->Owner() );
2218 #else
2219         sn_ConsoleOut( tOutput("$vote_unknown_command", command, "suspend, kick" ), p->Owner() );
2220 #endif
2221     }
2222 
2223     // nothing created
2224     if ( !item )
2225     {
2226         return;
2227     }
2228 
2229     // let item check its validity
2230     if ( !item->CheckValid( p->Owner() ) )
2231     {
2232         delete item;
2233         return;
2234     }
2235 
2236     // no objection? Broadcast it to everyone.
2237     item->Update();
2238     item->ReBroadcast( p->Owner() );
2239 }
2240 
2241