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 "eTeam.h"
29 #include "tMemManager.h"
30 #include "ePlayer.h"
31 //#include "tInitExit.h"
32 #include "tConfiguration.h"
33 #include "eNetGameObject.h"
34 #include "rConsole.h"
35 #include "eTimer.h"
36 #include "tSysTime.h"
37 #include "rFont.h"
38 #include "uMenu.h"
39 #include "tToDo.h"
40 #include "rScreen.h"
41 #include <string>
42 #include <fstream>
43 #include <iostream>
44 #include <deque>
45 #include <algorithm>
46 #include "rRender.h"
47 #include "rSysdep.h"
48 #include "nAuthentication.h"
49 #include "tDirectories.h"
50 #include "eVoter.h"
51 #include "tReferenceHolder.h"
52 #include "tRandom.h"
53 #include "uInputQueue.h"
54 #include "nServerInfo.h"
55 #include "tRecorder.h"
56 #include "nConfig.h"
57 #include "nNetwork.h"
58 #include <time.h>
59 #include <climits>
60 
61 int se_lastSaidMaxEntries = 8;
62 
63 // call on commands that only work on the server; quit if it returns true
se_NeedsServer(char const * command,std::istream & s,bool strict)64 bool se_NeedsServer(char const * command, std::istream & s, bool strict )
65 {
66     if ( sn_GetNetState() != nSERVER && ( strict || sn_GetNetState() != nSTANDALONE ) )
67     {
68         tString rest;
69         rest.ReadLine( s );
70         con << tOutput("$only_works_on_server", command, rest );
71         return true;
72     }
73 
74     return false;
75 }
76 
operator <<(tColoredString & s,const ePlayer & p)77 tColoredString & operator << (tColoredString &s,const ePlayer &p){
78     return s << tColoredString::ColorString(p.rgb[0]/15.0,
79                                             p.rgb[1]/15.0,
80                                             p.rgb[2]/15.0)
81            << p.Name();
82 }
83 
operator <<(tColoredString & s,const ePlayerNetID & p)84 tColoredString & operator << (tColoredString &s,const ePlayerNetID &p){
85     return s << p.GetColoredName();
86 }
87 
operator <<(std::ostream & s,const ePlayerNetID & p)88 std::ostream & operator << (std::ostream &s,const ePlayerNetID &p){
89     return s << p.GetColoredName();
90 }
91 
eAccessLevelHolder()92 eAccessLevelHolder::eAccessLevelHolder()
93 {
94     accessLevel = tAccessLevel_Default;
95 }
96 
SetAccessLevel(tAccessLevel level)97 void eAccessLevelHolder::SetAccessLevel( tAccessLevel level )
98 {
99     // sanity check. The access level must not be set higher than that of the current context.
100     // since accessLevel is private, all access level changes need to run over this function;
101     // it is therefore impossible to get access level escalation bugs without memory overwrites
102     // or, of course, evil intentions.
103     if ( level < tCurrentAccessLevel::GetAccessLevel() )
104     {
105         con << "INTERNAL ERROR, security violation attempted. Clamping it.\n";
106         st_Breakpoint();
107         accessLevel = tCurrentAccessLevel::GetAccessLevel();
108     }
109 
110     accessLevel = level;
111 }
112 
113 tCONFIG_ENUM( eCamMode );
114 
115 tList<ePlayerNetID> se_PlayerNetIDs;
116 static ePlayer* se_Players = NULL;
117 
118 // tracking play time (in minutes). These times are tracked on the client and yes, you can "cheat" and increase them to get access to servers you are not ready for.
119 
120 // total play time, network or not
121 static REAL se_playTimeTotal = 60*8;
122 static tConfItem< REAL > se_playTimeTotalConf( "PLAY_TIME_TOTAL", se_playTimeTotal );
123 
124 // total online time
125 static REAL se_playTimeOnline = 60*8;
126 static tConfItem< REAL > se_playTimeOnlineConf( "PLAY_TIME_ONLINE", se_playTimeOnline );
127 
128 // total online team time
129 static REAL se_playTimeTeam = 60*8;
130 static tConfItem< REAL > se_playTimeTeamConf( "PLAY_TIME_TEAM", se_playTimeTeam );
131 
132 // total play time, network or not
133 static REAL se_minPlayTimeTotal = 0;
134 static nSettingItem< REAL > se_minPlayTimeTotalConf( "MIN_PLAY_TIME_TOTAL", se_minPlayTimeTotal );
135 
136 // total online time
137 static REAL se_minPlayTimeOnline = 0;
138 static nSettingItem< REAL > se_minPlayTimeOnlineConf( "MIN_PLAY_TIME_ONLINE", se_minPlayTimeOnline );
139 
140 // total online team time
141 static REAL se_minPlayTimeTeam = 0;
142 static nSettingItem< REAL > se_minPlayTimeTeamConf( "MIN_PLAY_TIME_TEAM", se_minPlayTimeTeam );
143 
144 static bool se_assignTeamAutomatically = true;
145 static tSettingItem< bool > se_assignTeamAutomaticallyConf( "AUTO_TEAM", se_assignTeamAutomatically );
146 
147 static bool se_specSpam = true;
148 static tSettingItem< bool > se_specSpamConf( "AUTO_TEAM_SPEC_SPAM", se_specSpam );
149 
150 static bool se_allowTeamChanges = true;
151 static tSettingItem< bool > se_allowTeamChangesConf( "ALLOW_TEAM_CHANGE", se_allowTeamChanges );
152 
153 static bool se_enableChat = true;    //flag indicating whether chat should be allowed at all (logged in players can always chat)
154 static tSettingItem< bool > se_enaChat("ENABLE_CHAT", se_enableChat);
155 
156 static tString se_hiddenPlayerPrefix ("0xaaaaaa");
157 static tConfItemLine se_hiddenPlayerPrefixConf( "PLAYER_LIST_HIDDEN_PLAYER_PREFIX", se_hiddenPlayerPrefix );
158 
159 static tConfItemFunc Rename_conf("RENAME", &ForceName);
160 static tAccessLevelSetter Rename_confLevel( Rename_conf, tAccessLevel_Moderator );
161 
162 
163 static tReferenceHolder< ePlayerNetID > se_PlayerReferences;
164 
165 class PasswordStorage
166 {
167 public:
168     tString username; // name of the user the password belongs to
169     tString methodCongested;   // method of scrambling
170     nKrawall::nScrambledPassword password;
171     bool save;
172 
PasswordStorage()173     PasswordStorage(): save(false){}
174 };
175 
operator ==(PasswordStorage const & a,PasswordStorage const & b)176 static bool operator == ( PasswordStorage const & a, PasswordStorage const & b )
177 {
178     return
179     a.username == b.username &&
180     a.methodCongested == b.methodCongested;
181 }
182 
183 static tArray<PasswordStorage> S_passwords;
184 
185 // if set, user names of non-authenticated players are left as they are,
186 // and usernames of authenticated players get a 0: prepended.
187 // if unsed, usernames of non-authenticated players get all special characters escaped (especially all @)
188 // and usernames of authenticated players get left as they are (with all special characters except the last
189 // escaped.)
190 #if defined(KRAWALL_SERVER)
191 bool se_legacyLogNames = false;
192 #else
193 bool se_legacyLogNames = true;
194 #endif
195 static tSettingItem<bool> se_llnConf("LEGACY_LOG_NAMES", se_legacyLogNames );
196 
197 // transform special characters in name to escape sequences
se_EscapeName(tString const & original,bool keepAt=true)198 static std::string se_EscapeName( tString const & original, bool keepAt = true )
199 {
200     std::ostringstream filter;
201 
202     int lastC = 'x';
203     for( int i = 0; i < original.Len()-1; ++i )
204     {
205         unsigned int c = static_cast< unsigned char >( original[i] );
206 
207         // a valid character
208         switch (c)
209         {
210             // characters to escape
211         case ' ':
212             filter << "\\_";
213             break;
214         case '@':
215             if ( keepAt )
216             {
217                 filter << '@';
218             }
219             else
220             {
221                 filter << "\\@";
222             }
223             break;
224         case '\\':
225         case '%':
226         case ':':
227             filter.put('\\');
228             filter.put(c);
229             break;
230         case 'x':
231             // color codes?
232             if ( lastC == '0' )
233             {
234                 filter << "\\x";
235                 break;
236             }
237         default:
238             if ( 0x20 < c && 0x7f >= c )
239             {
240                 // normal ascii, print
241                 filter.put(c);
242             }
243             else
244             {
245                 // encode as hexcode
246                 filter << '%' << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << c;
247             }
248         }
249 
250         lastC = c;
251     }
252 
253     // done! wrap up stream as string.
254     return filter.str();
255 }
256 
257 #ifdef KRAWALL_SERVER
258 // reverses se_EscapeName (left inverse only, UnEscape(Escape(x)) == x, but not Escape(UnEscape(y)) == y. )
se_UnEscapeName(tString const & original)259 static std::string se_UnEscapeName( tString const & original )
260 {
261     std::ostringstream filter;
262 
263     int lastC = 'x';
264     for( int i = 0; i < original.Len()-1; ++i )
265     {
266         int c = original[i];
267 
268         if ( lastC == '\\' )
269         {
270             if ( c == '_' )
271             {
272                 c = ' ';
273             }
274             filter.put(c);
275 
276             // don't chain escapes
277             c = 'x';
278         }
279         else if ( c == '%' )
280         {
281             // decode hex code
282             char hex[3];
283             hex[0] = original[i+1];
284             hex[1] = original[i+2];
285             hex[2] = 0;
286             i += 2;
287 
288             int decoded;
289             std::istringstream s(hex);
290             s >> std::hex >> decoded;
291             tASSERT( !s.fail() );
292             filter.put(decoded);
293         }
294         else if ( c != '\\' )
295         {
296             filter.put(c);
297         }
298 
299         lastC = c;
300     }
301 
302     // done! wrap up stream as string.
303     return filter.str();
304 }
305 #endif
306 
307 // generate the user name for an unauthenticated user
se_UnauthenticatedUserName(tString const & name)308 static tString se_UnauthenticatedUserName( tString const & name )
309 {
310     tString ret;
311     ePlayerNetID::FilterName( name, ret );
312     if ( se_legacyLogNames )
313     {
314         return ret;
315     }
316     else
317     {
318         return tString( se_EscapeName( ret, false ).c_str() );
319     }
320 }
321 
se_DeletePasswords()322 void se_DeletePasswords(){
323     S_passwords.SetLen(0);
324 
325     st_SaveConfig();
326 
327     /*
328 
329       REAL timeout = tSysTimeFloat() + 3;
330 
331       while(tSysTimeFloat() < timeout){
332 
333       sr_ResetRenderState(true);
334       rViewport::s_viewportFullscreen.Select();
335 
336       sr_ClearGL();
337 
338       uMenu::GenericBackground();
339 
340       REAL w=16*3/640.0;
341       REAL h=32*3/480.0;
342 
343 
344       //REAL middle=-.6;
345 
346       Color(1,1,1);
347       DisplayText(0,.8,w,h,tOutput("$network_opts_deletepw_complete"));
348 
349       sr_SwapGL();
350       }
351 
352     */
353 
354     tConsole::Message("$network_opts_deletepw_complete", tOutput(), 5);
355 }
356 
357 class tConfItemPassword:public tConfItemBase{
358 public:
tConfItemPassword()359     tConfItemPassword():tConfItemBase("PASSWORD"){}
~tConfItemPassword()360     ~tConfItemPassword(){}
361 
362     // write the complete passwords
WriteVal(std::ostream & s)363     virtual void WriteVal(std::ostream &s){
364         int i;
365         bool first = 1;
366         for (i = S_passwords.Len()-1; i>=0; i--)
367         {
368             PasswordStorage &storage = S_passwords[i];
369             if (storage.save )
370             {
371                 if (!first)
372                     s << "\nPASSWORD\t";
373                 first = false;
374 
375                 s << "1 ";
376                 nKrawall::WriteScrambledPassword(storage.password, s);
377                 s << '\t' << storage.methodCongested;
378                 s << '\t' << storage.username;
379             }
380         }
381         if (first)
382             s << "0 ";
383     }
384 
385     // read one password
ReadVal(std::istream & s)386     virtual void ReadVal(std::istream &s){
387         //    static char in[20];
388         int test;
389         s >> test;
390         if (test != 0)
391         {
392             PasswordStorage &storage = S_passwords[S_passwords.Len()];
393             nKrawall::ReadScrambledPassword(s, storage.password);
394             s >> storage.methodCongested;
395             storage.username.ReadLine(s);
396 
397             storage.save = true;
398 
399             // check for duplicates
400             for( int i = S_passwords.Len() - 2; i >= 0; --i )
401             {
402                 PasswordStorage &other = S_passwords[i];
403                 if ( other == storage )
404                 {
405                     storage.save = false;
406                     break;
407                 }
408             }
409         }
410     }
411 };
412 
413 static tConfItemPassword se_p;
414 
415 // username menu item
416 class eMenuItemUserName: public uMenuItemString
417 {
418 public:
eMenuItemUserName(uMenu * M,tString & c)419     eMenuItemUserName(uMenu *M,tString &c):
420     uMenuItemString(M,"$login_username","$login_username_help",c){}
~eMenuItemUserName()421     virtual ~eMenuItemUserName(){}
422 
Event(SDL_Event & e)423     virtual bool Event(SDL_Event &e){
424 #ifndef DEDICATED
425         if (e.type==SDL_KEYDOWN &&
426                 (e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
427 
428             // move on to password menu item
429             MyMenu()->SetSelected(0);
430             return true;
431         }
432         else
433 #endif
434             return uMenuItemString::Event(e);
435     }
436 };
437 
438 // password request menu item, with *** display
439 class eMenuItemPassword: public uMenuItemString
440 {
441 public:
442     static bool entered;
443 
eMenuItemPassword(uMenu * M,tString & c)444     eMenuItemPassword(uMenu *M,tString &c):
445     uMenuItemString(M,"$login_password_title","$login_password_help",c)
446     {
447         entered = false;
448     }
~eMenuItemPassword()449     virtual ~eMenuItemPassword(){}
450 
Render(REAL x,REAL y,REAL alpha=1,bool selected=0)451     virtual void Render(REAL x,REAL y,REAL alpha=1,bool selected=0)
452     {
453         tString* pwback = content;
454         tString star;
455         for (int i=content->Len()-2; i>=0; i--)
456             star << "*";
457         content = &star;
458         uMenuItemString::Render(x,y, alpha, selected);
459         content = pwback;
460     }
461 
Event(SDL_Event & e)462     virtual bool Event(SDL_Event &e){
463 #ifndef DEDICATED
464         if (e.type==SDL_KEYDOWN &&
465                 (e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
466 
467             entered = true;
468             MyMenu()->Exit();
469             return true;
470         }
471         else
472 #endif
473             return uMenuItemString::Event(e);
474     }
475 };
476 
477 bool eMenuItemPassword::entered;
478 
tr()479 static bool tr(){return true;}
480 
481 // password storage mode
482 int se_PasswordStorageMode = 0; // 0: store in memory, -1: don't store, 1: store on file
483 static tConfItem<int> pws("PASSWORD_STORAGE",
484                           "$password_storage_help",
485                           se_PasswordStorageMode);
486 
PasswordCallback(nKrawall::nPasswordRequest const & request,nKrawall::nPasswordAnswer & answer)487 static void PasswordCallback( nKrawall::nPasswordRequest const & request,
488                               nKrawall::nPasswordAnswer & answer )
489 {
490     int i;
491 
492     tString& username = answer.username;
493     const tString& message = request.message;
494     nKrawall::nScrambledPassword& scrambled = answer.scrambled;
495     bool failure = request.failureOnLastTry;
496 
497     if ( request.method != "md5" && request.method != "bmd5" )
498     {
499         con << "INTERNAL ERROR: Unknown password scrambling method requested.";
500         answer.aborted = true;
501         return;
502     }
503 
504     // find the player with the given username:
505     /*
506     ePlayer* p = NULL;
507     for (i = MAX_PLAYERS-1; i>=0 && !p; i--)
508     {
509         ePlayer * perhaps = ePlayer::PlayerConfig(i);
510         tString filteredName;
511         ePlayerNetID::FilterName( perhaps->name, filteredName );
512         if ( filteredName == username )
513             p = perhaps;
514     }
515 
516     // or the given raw name
517     for (i = MAX_PLAYERS-1; i>=0 && !p; i--)
518     {
519         ePlayer * perhaps = ePlayer::PlayerConfig(i);
520         if ( perhaps->name == username )
521             p = perhaps;
522     }
523 
524     // or just any player, what the heck.
525     if (!p)
526         p = ePlayer::PlayerConfig(0);
527     */
528 
529     // try to find the username in the saved passwords
530     tString methodCongested = request.method + '|' + request.prefix + '|' + request.suffix;
531 
532     PasswordStorage *storage = NULL;
533     for (i = S_passwords.Len()-1; i>=0; i--)
534     {
535         PasswordStorage & candidate = S_passwords(i);
536         if (answer.username == candidate.username &&
537             methodCongested  == candidate.methodCongested
538             )
539             storage = &candidate;
540     }
541 
542     if (!storage)
543     {
544         // find an empty slot
545         for (i = S_passwords.Len()-1; i>=0; i--)
546             if (S_passwords(i).username.Len() < 1)
547                 storage = &S_passwords(i);
548 
549         if (!storage)
550             storage = &S_passwords[S_passwords.Len()];
551 
552         failure = true;
553     }
554 
555     static char const * section = "PASSWORD_MENU";
556     tRecorder::Playback( section, failure );
557     tRecorder::Record( section, failure );
558 
559     // immediately return the stored password if it was not marked as wrong:
560     if (!failure)
561     {
562         if ( storage )
563         {
564             answer.username = storage->username;
565             answer.scrambled = storage->password;
566         }
567         answer.automatic = true;
568 
569         return;
570     }
571     else
572         storage->username.Clear();
573 
574     // scramble input for recording
575     uInputScrambler scrambler;
576 
577     // password was not stored. Request it from user:
578     uMenu login(message, false);
579 
580     // password storage;
581     tString password;
582 
583     eMenuItemPassword pw(&login, password);
584     eMenuItemUserName us(&login, username);
585     us.SetColorMode( rTextField::COLOR_IGNORE );
586 
587     uMenuItemSelection<int> storepw(&login,
588                                     "$login_storepw_text",
589                                     "$login_storepw_help",
590                                     se_PasswordStorageMode);
591     storepw.NewChoice("$login_storepw_dont_text",
592                       "$login_storepw_dont_help",
593                       -1);
594     storepw.NewChoice("$login_storepw_mem_text",
595                       "$login_storepw_mem_help",
596                       0);
597     storepw.NewChoice("$login_storepw_disk_text",
598                       "$login_storepw_disk_help",
599                       1);
600 
601     uMenuItemExit cl(&login, "$login_cancel", "$login_cancel_help" );
602 
603     login.SetSelected(1);
604 
605     // check if the username the server sent us matches one of the players'
606     // global IDs. If it does we can directly select the password menu
607     // menu entry since the user probably just wants to enter the password.
608     for(int i = 0; i < MAX_PLAYERS; ++i) {
609         tString const &id = se_Players[i].globalID;
610         if(id.Len() <= username.Len() || id(username.Len() - 1) != '@') {
611             continue;
612         }
613         bool match = true;
614         for(int j = username.Len() - 2; j >= 0; --j) {
615             if(username(j) != id(j)) {
616                 match = false;
617                 break;
618             }
619         }
620         if(match) {
621             login.SetSelected(0);
622         }
623     }
624 
625     // force a small console while we are in here
626     rSmallConsoleCallback cb(&tr);
627 
628     se_ChatState( ePlayerNetID::ChatFlags_Menu, true );
629 
630     login.Enter();
631 
632     // return username/scrambled password
633     {
634         // scramble password
635         request.ScramblePassword( nKrawall::nScrambleInfo( username ), password, scrambled );
636 
637         // if S_login still exists, we were not canceled
638         answer.aborted = !eMenuItemPassword::entered;
639 
640         // clear the PW from memory
641         for (i = password.Len()-2; i>=0; i--)
642             password(i) = 'a';
643 
644         if (se_PasswordStorageMode >= 0)
645         {
646             storage->username = username;
647             storage->methodCongested = methodCongested;
648             storage->password = scrambled;
649             storage->save = (se_PasswordStorageMode > 0);
650         }
651     }
652 
653     se_ChatState( ePlayerNetID::ChatFlags_Menu, false );
654 }
655 
656 #ifdef DEDICATED
657 #ifndef KRAWALL_SERVER
658 // the following function really is only supposed to be called from here and nowhere else
659 // (access right escalation risk):
se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE(ePlayerNetID * p)660 static void se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE( ePlayerNetID * p )
661 {
662     // elevate access rights. We are currently in the context of player chat,
663     // therefore in his security context, but we need to log the player on,
664     // and that requires superior rights.
665     {
666         tCurrentAccessLevel elevator( tAccessLevel_Owner, true );
667         p->BeLoggedIn();
668     }
669 
670     sn_ConsoleOut("You have been logged in!\n",p->Owner());
671     tString serverLoginMessage;
672     serverLoginMessage << "Remote admin login for user \"" << p->GetUserName() << "\" accepted.\n";
673     sn_ConsoleOut(serverLoginMessage, 0);
674 }
675 #endif
676 #endif
677 
678 #ifdef KRAWALL_SERVER
679 // minimal access level to play
680 static tAccessLevel se_playAccessLevel = tAccessLevel_Program;
681 static tSettingItem< tAccessLevel > se_playAccessLevelConf( "ACCESS_LEVEL_PLAY", se_playAccessLevel );
682 static tAccessLevelSetter se_playAccessLevelConfLevel( se_playAccessLevelConf, tAccessLevel_Owner );
683 
684 // minimal sliding access level to play (slides up as soon as enoughpeople of higher access level get authenticated )
685 static tAccessLevel se_playAccessLevelSliding = tAccessLevel_Program;
686 static tSettingItem< tAccessLevel > se_playAccessLevelSlidingConf( "ACCESS_LEVEL_PLAY_SLIDING", se_playAccessLevelSliding );
687 static tAccessLevelSetter se_playAccessLevelSlidingConfLevel( se_playAccessLevelSlidingConf, tAccessLevel_Owner );
688 
689 // that many high level players are reuqired to drag the access level up
690 static int se_playAccessLevelSliders = 4;
691 static tSettingItem< int > se_playAccessLevelSlidersConf( "ACCESS_LEVEL_PLAY_SLIDERS", se_playAccessLevelSliders );
692 static tAccessLevelSetter se_playAccessLevelSlidersConfLevel( se_playAccessLevelSlidersConf, tAccessLevel_Owner );
693 
694 static tAccessLevel se_accessLevelRequiredToPlay = tAccessLevel_Program;
UpdateAccessLevelRequiredToPlay()695 static void UpdateAccessLevelRequiredToPlay()
696 {
697     int newAccessLevel = se_accessLevelRequiredToPlay;
698 
699     // clamp to bounds
700     if ( newAccessLevel < se_playAccessLevelSliding )
701         newAccessLevel = se_playAccessLevelSliding;
702 
703     if ( newAccessLevel > se_playAccessLevel )
704         newAccessLevel = se_playAccessLevel;
705 
706     bool changed = true;
707     while ( changed )
708     {
709         changed = false;
710 
711         // count players above and on the current access level
712         int countAbove = 0, countOn = 0;
713 
714         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
715         {
716             ePlayerNetID* player = se_PlayerNetIDs(i);
717 
718             // don't count AIs and spectators
719             if ( !player->IsHuman() || !player->NextTeam() )
720                 continue;
721 
722             if ( player->GetAccessLevel() < newAccessLevel )
723             {
724                 countAbove++;
725             }
726             else if ( player->GetAccessLevel() == newAccessLevel )
727             {
728                 countOn++;
729             }
730         }
731 
732         if ( countAbove >= se_playAccessLevelSliders && newAccessLevel > se_playAccessLevelSliding )
733         {
734             // if enough players are above the current level, increase it
735             newAccessLevel --;
736             changed = true;
737         }
738         else if ( countOn + countAbove < se_playAccessLevelSliders && newAccessLevel < se_playAccessLevel )
739         {
740             // if not enough players are on or above the current level to support it, decrease it
741             newAccessLevel ++;
742             changed = true;
743         }
744     }
745 
746     if ( se_accessLevelRequiredToPlay != newAccessLevel )
747     {
748         sn_ConsoleOut( tOutput( "$access_level_play_changed",
749                                 tCurrentAccessLevel::GetName( se_accessLevelRequiredToPlay ),
750                                 tCurrentAccessLevel::GetName( static_cast< tAccessLevel >( newAccessLevel ) ) ) );
751 
752         se_accessLevelRequiredToPlay = static_cast< tAccessLevel >( newAccessLevel );
753     }
754 }
755 
AccessLevelRequiredToPlay()756 tAccessLevel ePlayerNetID::AccessLevelRequiredToPlay()
757 {
758     return se_accessLevelRequiredToPlay;
759 }
760 
761 // maximal user level whose accounts can be hidden from other users
762 static tAccessLevel se_hideAccessLevelOf = tAccessLevel_Program;
763 static tSettingItem< tAccessLevel > se_hideAccessLevelOfConf( "ACCESS_LEVEL_HIDE_OF", se_hideAccessLevelOf );
764 static tAccessLevelSetter se_hideAccessLevelOfConfLevel( se_hideAccessLevelOfConf, tAccessLevel_Owner );
765 
766 // but they are only hidden to players with a lower access level than this
767 static tAccessLevel se_hideAccessLevelTo = tAccessLevel_Armatrator;
768 static tSettingItem< tAccessLevel > se_hideAccessLevelToConf( "ACCESS_LEVEL_HIDE_TO", se_hideAccessLevelTo );
769 static tAccessLevelSetter se_hideAccessLevelToConfLevel( se_hideAccessLevelToConf, tAccessLevel_Owner );
770 
771 // determines whether hider can hide from seeker
se_Hide(ePlayerNetID const * hider,tAccessLevel currentLevel)772 static bool se_Hide( ePlayerNetID const * hider, tAccessLevel currentLevel )
773 {
774     tASSERT( hider );
775 
776     return
777     hider->GetAccessLevel() <= se_hideAccessLevelOf &&
778     hider->StealthMode() &&
779     currentLevel            >  se_hideAccessLevelTo;
780 }
781 
782 // determines whether hider can hide from seeker
se_Hide(ePlayerNetID const * hider,ePlayerNetID const * seeker)783 static bool se_Hide( ePlayerNetID const * hider, ePlayerNetID const * seeker )
784 {
785     if ( !seeker )
786     {
787         return se_Hide( hider, tCurrentAccessLevel::GetAccessLevel() );
788     }
789 
790     if ( seeker == hider )
791     {
792         return false;
793     }
794 
795     return se_Hide( hider, seeker->GetAccessLevel() );
796 }
797 
se_CanHide(ePlayerNetID const * hider)798 static bool se_CanHide ( ePlayerNetID const * hider )
799 {
800     return hider->GetAccessLevel() <= se_hideAccessLevelOf;
801 }
802 
803 #endif
804 
805 typedef bool (*CANHIDEFUNC)( ePlayerNetID const * hider );
806 typedef bool (*HIDEFUNC)( ePlayerNetID const * hider, ePlayerNetID const * seeker );
807 
808 // secret console messages: If CanHideFunc returns false it is displayed to everyone. Else, for each player we apply HideFunc, to see if one can hide his message to the other. The two exceptions get a message anyway.
se_SecretConsoleOut(tOutput const & message,ePlayerNetID const * hider,HIDEFUNC HideFunc,ePlayerNetID const * exception1,ePlayerNetID const * exception2=0,CANHIDEFUNC CanHideFunc=0)809 void se_SecretConsoleOut( tOutput const & message, ePlayerNetID const * hider, HIDEFUNC HideFunc, ePlayerNetID const * exception1, ePlayerNetID const * exception2 = 0, CANHIDEFUNC CanHideFunc = 0 )
810 {
811     // high enough access levels are never secret
812     if ( CanHideFunc != 0 && !(*CanHideFunc)( hider ) )
813     {
814         sn_ConsoleOut( message );
815     }
816     else
817     {
818         bool canSee[ MAXCLIENTS+1 ];
819         for( int i = MAXCLIENTS; i>=0; --i )
820         {
821             canSee[i] = false;
822         }
823 
824         // well, the admin will want to see it.
825         canSee[0] = true;
826 
827         // look which clients have someone who can see the message
828         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
829         {
830             ePlayerNetID* player = se_PlayerNetIDs(i);
831             if ( player == exception1 || player == exception2 || !(*HideFunc)( hider, player ) )
832             {
833                 canSee[ player->Owner() ] = true;
834             }
835         }
836 
837         // and send it
838         for( int i = MAXCLIENTS; i>=0; --i )
839         {
840             if ( canSee[i] )
841             {
842                 sn_ConsoleOut( message, i );
843             }
844         }
845     }
846 }
847 
848 #ifdef KRAWALL_SERVER
849 
850 static eLadderLogWriter se_authorityBlurbWriter("AUTHORITY_BLURB", true);
851 
ResultCallback(nKrawall::nCheckResult const & result)852 static void ResultCallback( nKrawall::nCheckResult const & result )
853 {
854     tString username = result.username;
855     tString authority = result.authority;
856     bool success = result.success;
857 
858     ePlayerNetID * player = dynamic_cast< ePlayerNetID * >( static_cast< nNetObject * >( result.user ) );
859     if ( !player || player->Owner() <= 0 )
860     {
861         // nobody to authenticate
862         return;
863     }
864 
865     tString authName = username + "@" + authority;
866 
867     // seach for double login
868     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
869     {
870         ePlayerNetID* player2 = se_PlayerNetIDs(i);
871         if ( player2->IsAuthenticated() && player2->GetRawAuthenticatedName() == authName )
872         {
873             sn_ConsoleOut( tOutput("$login_request_failed_dup"), player->Owner() );
874             return;
875         }
876     }
877 
878     if (success)
879     {
880         player->Authenticate( authName, result.accessLevel );
881 
882         // log blurb to ladderlog. This is not important for debug playback,
883         // so we just don't record it. Once more is done with the blurb,
884         // we need to change that.
885         for ( std::deque< tString >::const_iterator i = result.blurb.begin(); i != result.blurb.end(); ++i )
886         {
887             std::istringstream s( static_cast< char const * >( *i ) );
888             tString token, rest;
889             s >> token;
890             rest.ReadLine( s );
891 
892             se_authorityBlurbWriter << token << player->GetFilteredAuthenticatedName() << rest;
893             se_authorityBlurbWriter.write();
894         }
895     }
896     else
897     {
898         if ( sn_GetNetState() == nSERVER )
899         {
900             tOutput out( tOutput("$login_failed_message", result.error ) );
901             sn_ConsoleOut( out, player->Owner() );
902             con << out;
903 
904             // redo automatic logins immedeately
905             if ( result.automatic )
906             {
907                 nAuthentication::RequestLogin( authority ,username , *player, "$login_request_failed" );
908             }
909         }
910     }
911 
912     // continue with scheduled logon messages
913     ePlayerNetID::RequestScheduledLogins();
914 }
915 #else
916 /*
917 static bool se_Hide( ePlayerNetID const * hider, tAccessLevel currentLevel )
918 {
919     return false;
920 }
921 */
922 
se_Hide(ePlayerNetID const * hider,ePlayerNetID const * seeker)923 static bool se_Hide( ePlayerNetID const * hider, ePlayerNetID const * seeker )
924 {
925     return false;
926 }
927 #endif
928 
929 // determines whether hider can hide from seeker
930 /*
931 static bool se_Hide( ePlayerNetID const * hider )
932 {
933     tASSERT( hider );
934 
935     return se_Hide( hider, tCurrentAccessLevel::GetAccessLevel() );
936 }
937 */
938 
939 
940 
941 // menu item to silence selected players
942 class eMenuItemSilence: public uMenuItemToggle
943 {
944 public:
eMenuItemSilence(uMenu * m,ePlayerNetID * p)945     eMenuItemSilence(uMenu *m, ePlayerNetID* p )
946             : uMenuItemToggle( m, tOutput(""),tOutput("$silence_player_help" ),  p->AccessSilenced() )
947     {
948         this->title.Clear();
949         this->title.SetTemplateParameter(1, p->GetColoredName() );
950         this->title << "$silence_player_text";
951         player_ = p;
952     }
953 
~eMenuItemSilence()954     ~eMenuItemSilence()
955     {
956     }
957 private:
958     tCONTROLLED_PTR( ePlayerNetID ) player_;        // keep player referenced
959 };
960 
961 
962 
963 
964 // menu where you can silence players
SilenceMenu()965 void ePlayerNetID::SilenceMenu()
966 {
967     uMenu menu( "$player_police_silence_text" );
968 
969     int size = se_PlayerNetIDs.Len();
970     eMenuItemSilence** items = tNEW( eMenuItemSilence* )[ size ];
971 
972     int i;
973     for ( i = size-1; i>=0; --i )
974     {
975         ePlayerNetID* player = se_PlayerNetIDs[ i ];
976         if ( player->IsHuman() )
977         {
978             items[i] = tNEW( eMenuItemSilence )( &menu, player );
979         }
980         else
981         {
982             items[i] = 0;
983         }
984 
985     }
986 
987     menu.Enter();
988 
989     for ( i = size - 1; i>=0; --i )
990     {
991         if( items[i] ) delete items[i];
992     }
993     delete[] items;
994 }
995 
PoliceMenu()996 void ePlayerNetID::PoliceMenu()
997 {
998     uMenu menu( "$player_police_text" );
999 
1000     uMenuItemFunction kick( &menu, "$player_police_kick_text", "$player_police_kick_help", eVoter::KickMenu );
1001     uMenuItemFunction silence( &menu, "$player_police_silence_text", "$player_police_silence_help", ePlayerNetID::SilenceMenu );
1002 
1003     menu.Enter();
1004 }
1005 
1006 
1007 
1008 
1009 
1010 
1011 
1012 
1013 
1014 
1015 
1016 
1017 
1018 
1019 
1020 
1021 
1022 
1023 
1024 
1025 
1026 
1027 
1028 #ifndef DEDICATED
1029 
1030 static char const * default_instant_chat[]=
1031     {"/team \\",
1032      "/msg \\",
1033      "/me \\",
1034      "LOL!",
1035      "/team 1 Yes Oui Ja",
1036      "/team 0 No Non Nein",
1037      "/team I'm going in!",
1038      "Give the rim a break; hug a tree instead.",
1039      "Lag is a myth. It is only in your brain.",
1040      "Rubber kills gameplay!",
1041      "Every time you double bind, God kills a kitten.",
1042      "http://www.armagetronad.net",
1043      "Only idiots keep their instant chat at default values.",
1044      "/me wanted to download pr0n, but only got this stupid game.",
1045      "Speed for weaks!",
1046      "This server sucks! I'm going home.",
1047      "Grind EVERYTHING! And 180 some more!",
1048      "/me has an interesting mental disorder.",
1049      "Ah, a nice, big, roomy box all for me!",
1050      "Go that way! No, the other way!",
1051      "WD! No points!",
1052      "/me is a noob.",
1053      "/me just installed this game and still doesn't know how to talk.",
1054      "/team You all suck, I want a new team.",
1055      "Are you the real \"Player 1\"?",
1056      NULL};
1057 
1058 #endif
1059 
1060 
PlayerConfig(int p)1061 ePlayer * ePlayer::PlayerConfig(int p){
1062     uPlayerPrototype *P = uPlayerPrototype::PlayerConfig(p);
1063     return dynamic_cast<ePlayer*>(P);
1064     //  return (ePlayer*)P;
1065 }
1066 
StoreConfitem(tConfItemBase * c)1067 void   ePlayer::StoreConfitem(tConfItemBase *c){
1068     tASSERT(CurrentConfitem < PLAYER_CONFITEMS);
1069     configuration[CurrentConfitem++] = c;
1070 }
1071 
DeleteConfitems()1072 void   ePlayer::DeleteConfitems(){
1073     while (CurrentConfitem>0){
1074         CurrentConfitem--;
1075         tDESTROY(configuration[CurrentConfitem]);
1076     }
1077 }
1078 
1079 uActionPlayer *ePlayer::se_instantChatAction[MAX_INSTANT_CHAT];
1080 
se_UserName()1081 static const tString& se_UserName()
1082 {
1083     srand( (unsigned)time( NULL ) );
1084 
1085     static tString ret( getenv( "USER" ) );
1086     return ret;
1087 }
1088 
ePlayer()1089 ePlayer::ePlayer(){
1090     nAuthentication::SetUserPasswordCallback(&PasswordCallback);
1091 #ifdef KRAWALL_SERVER
1092     nAuthentication::SetLoginResultCallback (&ResultCallback);
1093 #endif
1094 
1095     nameTeamAfterMe = false;
1096     favoriteNumberOfPlayersPerTeam = 3;
1097 
1098     CurrentConfitem = 0;
1099 
1100     bool getUserName = false;
1101     if ( id == 0 )
1102     {
1103         name = se_UserName();
1104         getUserName = ( name.Len() > 1 );
1105     }
1106     if ( !getUserName )
1107         name << "Player " << id+1;
1108 
1109 #ifndef DEDICATED
1110     tString confname;
1111 
1112     confname << "PLAYER_"<< id+1;
1113     StoreConfitem(tNEW(tConfItemLine) (confname,
1114                                        "$player_name_confitem_help",
1115                                        name));
1116 
1117     confname.Clear();
1118     confname << "USER_"<< id+1;
1119     StoreConfitem(tNEW(tConfItemLine) (confname,
1120                                        "$player_user_confitem_help",
1121                                        globalID));
1122 
1123     confname.Clear();
1124     confname << "AUTO_LOGIN_"<< id+1;
1125     StoreConfitem(tNEW(tConfItem<bool>)(confname,
1126                                         "$auto_login_confitem_help",
1127                                         autoLogin));
1128     autoLogin = false;
1129 
1130     confname.Clear();
1131     confname << "CAMCENTER_"<< id+1;
1132     centerIncamOnTurn=true;
1133     StoreConfitem(tNEW(tConfItem<bool>)
1134                   (confname,
1135                    "$camcenter_help",
1136                    centerIncamOnTurn) );
1137 
1138     confname.Clear();
1139     startCamera=CAMERA_SMART;
1140     confname << "START_CAM_"<< id+1;
1141     StoreConfitem(tNEW(tConfItem<eCamMode>) (confname,
1142                   "$start_cam_help",
1143                   startCamera));
1144 
1145     confname.Clear();
1146     confname << "START_FOV_"<< id+1;
1147     startFOV=90;
1148     StoreConfitem(tNEW(tConfItem<int>) (confname,
1149                                         "$start_fov_help",
1150                                         startFOV));
1151     confname.Clear();
1152 
1153     confname.Clear();
1154     confname << "SMART_GLANCE_CUSTOM_"<< id+1;
1155     smartCustomGlance=true;
1156     StoreConfitem(tNEW(tConfItem<bool>) (confname,
1157                                          "$camera_smart_glance_custom_help",
1158                                          smartCustomGlance));
1159     confname.Clear();
1160 
1161     int i;
1162     for(i=CAMERA_COUNT-1;i>=0;i--){
1163         confname << "ALLOW_CAM_"<< id+1 << "_" << i;
1164         StoreConfitem(tNEW(tConfItem<bool>) (confname,
1165                                              "$allow_cam_help",
1166                                              allowCam[i]));
1167         allowCam[i]=true;
1168         confname.Clear();
1169     }
1170 
1171     for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
1172         confname << "INSTANT_CHAT_STRING_" << id+1 << '_' <<  i+1;
1173         StoreConfitem(tNEW(tConfItemLine) (confname,
1174                                            "$instant_chat_string_help",
1175                                            instantChatString[i]));
1176         confname.Clear();
1177     }
1178 
1179     for(i=0; i < MAX_INSTANT_CHAT;i++){
1180         if (!default_instant_chat[i])
1181             break;
1182 
1183         instantChatString[i]=default_instant_chat[i];
1184     }
1185 
1186     confname << "SPECTATOR_MODE_"<< id+1;
1187     StoreConfitem(tNEW(tConfItem<bool>)(confname,
1188                                         "$spectator_mode_help",
1189                                         spectate));
1190     spectate=false;
1191     confname.Clear();
1192 
1193     confname << "HIDE_IDENTITY_"<< id+1;
1194     StoreConfitem(tNEW(tConfItem<bool>)(confname,
1195                                         "$hide_identity_confitem_help",
1196                                         stealth));
1197     stealth=false;
1198     confname.Clear();
1199 
1200     confname << "NAME_TEAM_AFTER_PLAYER_"<< id+1;
1201     StoreConfitem(tNEW(tConfItem<bool>)(confname,
1202                                         "$name_team_after_player_help",
1203                                         nameTeamAfterMe));
1204     nameTeamAfterMe=false;
1205     confname.Clear();
1206 
1207     confname << "FAV_NUM_PER_TEAM_PLAYER_"<< id+1;
1208     StoreConfitem(tNEW(tConfItem<int>)(confname,
1209                                        "$fav_num_per_team_player_help",
1210                                        favoriteNumberOfPlayersPerTeam ));
1211     favoriteNumberOfPlayersPerTeam = 3;
1212     confname.Clear();
1213 
1214 
1215     confname << "AUTO_INCAM_"<< id+1;
1216     autoSwitchIncam=false;
1217     StoreConfitem(tNEW(tConfItem<bool>) (confname,
1218                                          "$auto_incam_help",
1219                                          autoSwitchIncam));
1220     confname.Clear();
1221 
1222     confname << "CAMWOBBLE_"<< id+1;
1223     wobbleIncam=false;
1224     StoreConfitem(tNEW(tConfItem<bool>) (confname,
1225                                          "$camwobble_help",
1226                                          wobbleIncam));
1227 
1228     confname.Clear();
1229     confname << "COLOR_B_"<< id+1;
1230     StoreConfitem(tNEW(tConfItem<int>) (confname,
1231                                         "$color_b_help",
1232                                         rgb[2]));
1233 
1234     confname.Clear();
1235     confname << "COLOR_G_"<< id+1;
1236     StoreConfitem(tNEW(tConfItem<int>) (confname,
1237                                         "$color_g_help",
1238                                         rgb[1]));
1239 
1240     confname.Clear();
1241     confname << "COLOR_R_"<< id+1;
1242     StoreConfitem(tNEW(tConfItem<int>) (confname,
1243                                         "$color_r_help",
1244                                         rgb[0]));
1245     confname.Clear();
1246 #endif
1247 
1248     tRandomizer & randomizer = tRandomizer::GetInstance();
1249     //static int r = rand() / ( RAND_MAX >> 2 ) + se_UserName().Len();
1250     static int r = randomizer.Get(4) + se_UserName().Len();
1251     int cid = ( r + id ) % 4;
1252 
1253     static REAL R[MAX_PLAYERS]={1,.2,.2,1};
1254     static REAL G[MAX_PLAYERS]={.2,1,.2,1};
1255     static REAL B[MAX_PLAYERS]={.2,.2,1,.2};
1256 
1257     rgb[0]=int(R[cid]*15);
1258     rgb[1]=int(G[cid]*15);
1259     rgb[2]=int(B[cid]*15);
1260 
1261     cam=NULL;
1262 }
1263 
~ePlayer()1264 ePlayer::~ePlayer(){
1265     tCHECK_DEST;
1266     DeleteConfitems();
1267 }
1268 
1269 #ifndef DEDICATED
Render()1270 void ePlayer::Render(){
1271     if (cam) cam->Render();
1272 }
1273 #endif
1274 
1275 static void se_RequestLogin( ePlayerNetID * p );
1276 
1277 // check whether a special username is banned
1278 #ifdef KRAWALL_SERVER
1279 static bool se_IsUserBanned( ePlayerNetID * p, tString const & name );
1280 #endif
1281 
1282 // receive a "login wanted" message from client
se_LoginWanted(nMessage & m)1283 static void se_LoginWanted( nMessage & m )
1284 {
1285 #ifdef KRAWALL_SERVER
1286 
1287     // read player
1288     ePlayerNetID * p;
1289     m >> p;
1290 
1291     if ( p && m.SenderID() == p->Owner() && !p->IsAuthenticated() )
1292     {
1293         // read wanted flag
1294         m >> p->loginWanted;
1295         tString authName;
1296 
1297         // read authentication name
1298         m >> authName;
1299         p->SetRawAuthenticatedName( authName );
1300 
1301         // check for stupid bans
1302         if ( se_IsUserBanned( p, authName ) )
1303         {
1304             return;
1305         }
1306 
1307         se_RequestLogin( p );
1308     }
1309 #else
1310     sn_ConsoleOut( tOutput( "$login_not_supported" ), m.SenderID() );
1311 #endif
1312 }
1313 
1314 static nDescriptor se_loginWanted(204,se_LoginWanted,"AuthWanted");
1315 
1316 // request authentication initiation from the client (if p->loginWanted is set)
se_WantLogin(ePlayer * lp)1317 static void se_WantLogin( ePlayer * lp )
1318 {
1319     // only meaningful on client
1320     if( sn_GetNetState() != nCLIENT )
1321     {
1322         return;
1323     }
1324 
1325     // don't send if the server won't understand anyway
1326     static nVersionFeature authentication( 15 );
1327     if( !authentication.Supported(0) )
1328     {
1329         return;
1330     }
1331 
1332     tASSERT(lp);
1333     ePlayerNetID *p = lp->netPlayer;
1334     if ( !p )
1335     {
1336         return;
1337     }
1338 
1339     nMessage *m = new nMessage( se_loginWanted );
1340 
1341     // write player
1342     *m << p;
1343 
1344     // write flag and name
1345     *m << p->loginWanted;
1346 
1347     // write authentication name
1348     *m << lp->globalID;
1349 
1350     m->Send( 0 );
1351 
1352     // reset flag
1353     p->loginWanted = false;
1354 }
1355 
SendAuthNames()1356 void ePlayer::SendAuthNames()
1357 {
1358     // only meaningful on client
1359     if( sn_GetNetState() != nCLIENT )
1360     {
1361         return;
1362     }
1363     for(int i=MAX_PLAYERS-1;i>=0;i--)
1364     {
1365         se_WantLogin( ePlayer::PlayerConfig(i) );
1366     }
1367 }
1368 
1369 // on the server, this should request logins from all players who registered.
se_RequestLogin(ePlayerNetID * p)1370 static void se_RequestLogin( ePlayerNetID * p )
1371 {
1372     tString userName = p->GetUserName();
1373     tString authority;
1374     if ( p->Owner() != 0 &&  p->loginWanted )
1375     {
1376 #ifdef KRAWALL_SERVER
1377         if ( p->GetRawAuthenticatedName().Len() > 1 )
1378         {
1379             nKrawall::SplitUserName( p->GetRawAuthenticatedName(), userName, authority );
1380         }
1381 
1382         p->loginWanted =
1383         !nAuthentication::RequestLogin( authority,
1384                                         userName,
1385                                         *p,
1386                                         authority.Len() > 1 ? tOutput( "$login_request", authority ) : tOutput( "$login_request_local" ) );
1387 #endif
1388     }
1389 }
1390 
RequestScheduledLogins()1391 void ePlayerNetID::RequestScheduledLogins()
1392 {
1393     for( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
1394     {
1395         se_RequestLogin( se_PlayerNetIDs(i) );
1396     }
1397 }
1398 
LogIn()1399 void ePlayer::LogIn()
1400 {
1401     // only meaningful on client
1402     if( sn_GetNetState() != nCLIENT )
1403     {
1404         return;
1405     }
1406 
1407     // mark all players as wanting to log in
1408     for(int i=MAX_PLAYERS-1;i>=0;i--)
1409     {
1410         ePlayer * lp = ePlayer::PlayerConfig(i);
1411         if ( lp && lp->netPlayer )
1412         {
1413             lp->netPlayer->loginWanted = true;
1414             se_WantLogin( lp );
1415         }
1416     }
1417 }
1418 
se_DisplayChatLocally(ePlayerNetID * p,const tString & say)1419 static void se_DisplayChatLocally( ePlayerNetID* p, const tString& say )
1420 {
1421 #ifdef DEBUG_X
1422     if (strstr( say, "BUG" ) )
1423     {
1424         st_Breakpoint();
1425     }
1426 #endif
1427 
1428     if ( p && !p->IsSilenced() && se_enableChat )
1429     {
1430         //tColoredString say2( say );
1431         //say2.RemoveHex();
1432         tColoredString message;
1433         message << *p;
1434         message << tColoredString::ColorString(1,1,.5);
1435         message << ": " << say << '\n';
1436 
1437         con << message;
1438     }
1439 }
1440 
se_DisplayChatLocallyClient(ePlayerNetID * p,const tString & message)1441 static void se_DisplayChatLocallyClient( ePlayerNetID* p, const tString& message )
1442 {
1443     if ( p && !p->IsSilenced() && se_enableChat )
1444     {
1445         con << message << "\n";
1446     }
1447 }
1448 
1449 static nVersionFeature se_chatRelay( 3 );
1450 
1451 //!todo: lower this number or increase network version before next release
1452 static nVersionFeature se_chatHandlerClient( 6 );
1453 
1454 // chat message from client to server
1455 void handle_chat( nMessage& );
1456 static nDescriptor chat_handler(200,handle_chat,"Chat");
1457 
1458 // checks whether text_to_search contains search_for_text
Contains(const tString & search_for_text,const tString & text_to_search)1459 bool Contains( const tString & search_for_text, const tString & text_to_search ) {
1460     int m = strlen(search_for_text);
1461     int n = strlen(text_to_search);
1462     int a, b;
1463     for (b=0; b<=n-m; ++b) {
1464         for (a=0; a<m && search_for_text[a] == text_to_search[a+b]; ++a)
1465             ;
1466         if (a>=m)
1467             // a match has been found
1468             return true;
1469     }
1470     return false;
1471 }
1472 
1473 // function that returns one of the player names
1474 typedef tString const & (ePlayerNetID::*SE_NameGetter)() const;
1475 
1476 // function that filters strings
1477 typedef tString (*SE_NameFilter)( tString const & );
1478 
1479 // identity filter
se_NameFilterID(tString const & name)1480 static tString se_NameFilterID( tString const & name )
1481 {
1482     return name;
1483 }
1484 
1485 // function that filters players
1486 typedef bool (*SE_NameHider)( ePlayerNetID const * hider, ePlayerNetID const * seeker );
1487 
1488 // non-hider
se_NonHide(ePlayerNetID const * hider,ePlayerNetID const * seeker)1489 static bool se_NonHide( ePlayerNetID const * hider, ePlayerNetID const * seeker  )
1490 {
1491     return false;
1492 }
1493 
1494 // the other filter is ePlayerNetID::FilterName
1495 
1496 // search for exact or partial matches in player names
CompareBufferToPlayerNames(const tString & nameRaw,int & num_matches,ePlayerNetID * requester,SE_NameGetter GetName,SE_NameFilter Filter,SE_NameHider Hider)1497 ePlayerNetID * CompareBufferToPlayerNames
1498   ( const tString & nameRaw,
1499     int & num_matches,
1500     ePlayerNetID * requester,
1501     SE_NameGetter GetName, // = &ePlayerNetID::GetName,
1502     SE_NameFilter Filter, // = &se_NameFilterID,
1503     SE_NameHider Hider )// = &se_NonHide )
1504 {
1505     num_matches = 0;
1506     ePlayerNetID * match = 0;
1507 
1508     // also filter input string
1509     tString name = (*Filter)( nameRaw );
1510 
1511     // run through all players and look for a match
1512     for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a ) {
1513         ePlayerNetID* toMessage = se_PlayerNetIDs(a);
1514 
1515         if ( (*Hider)( toMessage, requester ) )
1516         {
1517             continue;
1518         }
1519 
1520         tString playerName = (*Filter)( (toMessage->*GetName)() );
1521 
1522         // exact match?
1523         if ( playerName == name )
1524         {
1525             num_matches = 1;
1526             return toMessage;
1527         }
1528 
1529         if ( Contains(name, playerName)) {
1530             match= toMessage; // Doesn't matter that this is wrote over everytime, when we only have one match it will be there.
1531             num_matches+=1;
1532         }
1533     }
1534 
1535     // return result
1536     return match;
1537 }
1538 
1539 
FindPlayerByName(tString const & name,ePlayerNetID * requester,bool print)1540 ePlayerNetID * ePlayerNetID::FindPlayerByName( tString const & name, ePlayerNetID * requester, bool print )
1541 {
1542    int num_matches = 0;
1543 
1544     // try filtering the names before comparing them, this makes the matching case-insensitive
1545     SE_NameFilter Filter = &ePlayerNetID::FilterName;
1546 
1547     // look for matches in the filtered screen names
1548     ePlayerNetID * ret = CompareBufferToPlayerNames( name, num_matches, requester, &ePlayerNetID::GetName, Filter, &se_NonHide );
1549     if ( ret && num_matches == 1 )
1550     {
1551         return ret;
1552     }
1553 
1554     // look for matches in the exact screen names.
1555     // No check for prior matches here, because the previous
1556     // search was less specific.
1557     ret = CompareBufferToPlayerNames( name, num_matches, requester, &ePlayerNetID::GetName, &se_NameFilterID, &se_NonHide );
1558     if ( ret && num_matches == 1 )
1559     {
1560         return ret;
1561     }
1562 
1563 #ifdef KRAWALL_SERVER
1564     // nothing found? try the user names.
1565     if ( !ret )
1566     {
1567         ret = CompareBufferToPlayerNames( name, num_matches, requester, &ePlayerNetID::GetUserName, &se_NameFilterID, &se_Hide  );
1568     }
1569     if ( ret && num_matches == 1 )
1570     {
1571         return ret;
1572     }
1573 
1574     // still nothing found? user names again
1575     if ( !ret )
1576     {
1577         ret = CompareBufferToPlayerNames( name, num_matches, requester, &ePlayerNetID::GetUserName, Filter, &se_Hide );
1578     }
1579     if ( ret && num_matches == 1 )
1580     {
1581         return ret;
1582     }
1583 #endif
1584 
1585     // More than than one match for the current buffer. Complain about that.
1586     else if (num_matches > 1) {
1587         tOutput toSender( "$msg_toomanymatches", name );
1588         if (print)
1589         {
1590             if ( requester )
1591                 sn_ConsoleOut(toSender,requester->Owner() );
1592             else
1593                 con << toSender;
1594         }
1595         return NULL;
1596     }
1597     // 0 matches. The user can't spell. Complain about that, too.
1598     else {
1599         tOutput toSender( "$msg_nomatch", name );
1600         if (print)
1601         {
1602             if ( requester )
1603                 sn_ConsoleOut(toSender,requester->Owner() );
1604             else
1605                 con << toSender;
1606         }
1607         return NULL;
1608     }
1609 
1610     return 0;
1611 }
1612 
se_FindPlayerInChatCommand(ePlayerNetID * sender,char const * command,std::istream & s)1613 static ePlayerNetID * se_FindPlayerInChatCommand( ePlayerNetID * sender, char const * command, std::istream & s )
1614 {
1615     tString player;
1616     s >> player;
1617 
1618     if (player == "" )
1619     {
1620         sn_ConsoleOut( tOutput( "$chatcommand_requires_player", command ), sender->Owner() );
1621         return 0;
1622     }
1623 
1624     return ePlayerNetID::FindPlayerByName( player, sender );
1625 }
1626 
1627 // chat message from server to client
1628 void handle_chat_client( nMessage & );
1629 static nDescriptor chat_handler_client(203,handle_chat_client,"Chat Client");
1630 
handle_chat_client(nMessage & m)1631 void handle_chat_client(nMessage &m)
1632 {
1633     if(sn_GetNetState()!=nSERVER)
1634     {
1635         unsigned short id;
1636         m.Read(id);
1637         tColoredString say;
1638         m >> say;
1639 
1640         tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
1641 
1642         se_DisplayChatLocallyClient( p, say );
1643     }
1644 }
1645 
1646 // appends chat message to something
1647 template< class TARGET >
se_AppendChat(TARGET & out,tString const & message)1648 void se_AppendChat( TARGET & out, tString const & message )
1649 {
1650     if ( message.Len() <= se_SpamMaxLen*2 || se_SpamMaxLen == 0 )
1651         out << message;
1652     else
1653     {
1654         tString cut( message );
1655         cut.SetLen( se_SpamMaxLen*2 );
1656         out << cut;
1657     }
1658 }
1659 
1660 // builds a colored chat string
se_BuildChatString(ePlayerNetID const * sender,tString const & message)1661 static tColoredString se_BuildChatString( ePlayerNetID const * sender, tString const & message )
1662 {
1663     tColoredString console;
1664     console << *sender;
1665     console << tColoredString::ColorString(1,1,.5) << ": ";
1666     se_AppendChat( console, message );
1667 
1668     return console;
1669 }
1670 
1671 // Build a colored /team message
se_BuildChatString(eTeam const * team,ePlayerNetID const * sender,tString const & message)1672 static tColoredString se_BuildChatString( eTeam const *team, ePlayerNetID const *sender, tString const &message )
1673 {
1674     tColoredString console;
1675     console << *sender;
1676 
1677     if( !team )
1678     {
1679         // foo --> Spectatos: message
1680         console << tColoredString::ColorString(1,1,.5) << " --> " << tOutput("$player_spectator_message");
1681     }
1682     else if (sender->CurrentTeam() == team)
1683     {
1684         // foo --> Teammates: some message here
1685         console << tColoredString::ColorString(1,1,.5) << " --> ";
1686         // We don't << team here, because we want "Teammates" to show instead of the team name
1687         console << tColoredString::ColorString( team->R() / 15.0, team->G() / 15.0, team->B() / 15.0 ) << tOutput("$player_team_message");
1688     }
1689     else {
1690         // foo (Red Team) --> Blue Team: some message here
1691         eTeam *senderTeam = sender->CurrentTeam();
1692         console << tColoredString::ColorString(1,1,.5) << " (";
1693         console << senderTeam;
1694         console << tColoredString::ColorString(1,1,.5) << ") --> ";
1695         console << team;
1696     }
1697 
1698     console << tColoredString::ColorString(1,1,.5) << ": ";
1699     se_AppendChat( console, message );
1700 
1701     return console;
1702 }
1703 
1704 // builds a colored chat /msg string
se_BuildChatString(ePlayerNetID const * sender,ePlayerNetID const * receiver,tString const & message)1705 static tColoredString se_BuildChatString( ePlayerNetID const * sender, ePlayerNetID const * receiver, tString const & message )
1706 {
1707     tColoredString console;
1708     console << *sender;
1709     console << tColoredString::ColorString(1,1,.5) << " --> ";
1710     console << *receiver;
1711     console << tColoredString::ColorString(1,1,.5) << ": ";
1712     se_AppendChat( console, message );
1713 
1714     return console;
1715 }
1716 
1717 // prepares a chat message with a specific total text originating from the given player
se_ServerControlledChatMessageConsole(ePlayerNetID const * player,tString const & toConsole)1718 static nMessage* se_ServerControlledChatMessageConsole( ePlayerNetID const * player, tString const & toConsole )
1719 {
1720     tASSERT( player );
1721 
1722     nMessage *m=tNEW(nMessage) (chat_handler_client);
1723 
1724     m->Write( player->ID() );
1725     *m << toConsole;
1726 
1727     return m;
1728 }
1729 
1730 // prepares a chat message with a chat string (only the message, not the whole console line) originating from the given player
se_ServerControlledChatMessage(ePlayerNetID const * sender,tString const & message)1731 static nMessage* se_ServerControlledChatMessage( ePlayerNetID const * sender, tString const & message )
1732 {
1733     tASSERT( sender );
1734 
1735     return se_ServerControlledChatMessageConsole( sender, se_BuildChatString( sender, message ) );
1736 }
1737 
1738 // prepares a chat message with a chat message originating from the given player to the given receiver
se_ServerControlledChatMessage(ePlayerNetID const * sender,ePlayerNetID const * receiver,tString const & message)1739 static nMessage* se_ServerControlledChatMessage( ePlayerNetID const * sender, ePlayerNetID const * receiver, tString const & message )
1740 {
1741     tASSERT( sender );
1742     tASSERT( receiver );
1743 
1744     return se_ServerControlledChatMessageConsole( sender, se_BuildChatString( sender, receiver, message ) );
1745 }
1746 
1747 // prepares a chat message with a chat message originating from the given player to the given team
se_ServerControlledChatMessage(eTeam const * team,ePlayerNetID const * sender,tString const & message)1748 static nMessage* se_ServerControlledChatMessage(  eTeam const * team, ePlayerNetID const * sender, tString const & message )
1749 {
1750     tASSERT( sender );
1751 
1752     return se_ServerControlledChatMessageConsole( sender, se_BuildChatString(team, sender, message) );
1753 }
1754 
1755 // pepares a chat message the client has to put together
se_NewChatMessage(ePlayerNetID const * player,tString const & message)1756 static nMessage* se_NewChatMessage( ePlayerNetID const * player, tString const & message )
1757 {
1758     tASSERT( player );
1759 
1760     nMessage *m=tNEW(nMessage) (chat_handler);
1761     m->Write( player->ID() );
1762     se_AppendChat( *m, message );
1763 
1764     return m;
1765 }
1766 
1767 // prepares a very old style chat message: just a regular remote console output message
se_OldChatMessage(tString const & line)1768 static nMessage* se_OldChatMessage( tString const & line )
1769 {
1770     return sn_ConsoleOutMessage( line + "\n" );
1771 }
1772 
1773 // prepares a very old style chat message: just a regular remote console output message
se_OldChatMessage(ePlayerNetID const * player,tString const & message)1774 static nMessage* se_OldChatMessage( ePlayerNetID const * player, tString const & message )
1775 {
1776     tASSERT( player );
1777 
1778     return se_OldChatMessage( se_BuildChatString( player, message ) );
1779 }
1780 
1781 // sends the full chat line to receiver, marked as originating from <sender> so
1782 // it can be silenced.
1783 // ( the client will see <fullLine> resp. <sender name> : <forOldClients> if it is pre-0.2.8 and at least 0.2.6 )
se_SendChatLine(ePlayerNetID * sender,const tString & fullLine,const tString & forOldClients,int receiver)1784 void se_SendChatLine( ePlayerNetID* sender, const tString& fullLine, const tString& forOldClients, int receiver )
1785 {
1786     // create chat messages
1787 
1788     // send them to the users, depending on what they understand
1789     if ( sn_Connections[ receiver ].socket )
1790     {
1791         if ( se_chatHandlerClient.Supported( receiver ) )
1792         {
1793             tJUST_CONTROLLED_PTR< nMessage > mServerControlled = se_ServerControlledChatMessageConsole( sender, fullLine );
1794             mServerControlled->Send( receiver );
1795         }
1796         else if ( se_chatRelay.Supported( receiver ) )
1797         {
1798             tJUST_CONTROLLED_PTR< nMessage > mNew = se_NewChatMessage( sender, forOldClients );
1799             mNew->Send( receiver );
1800         }
1801         else
1802         {
1803             tJUST_CONTROLLED_PTR< nMessage > mOld = se_OldChatMessage( fullLine );
1804             mOld->Send( receiver );
1805         }
1806     }
1807 }
1808 
1809 // sends the full chat line to all connected clients, marked as originating from <sender> so
1810 // it can be silenced.
1811 // ( the client will see <fullLine> resp. <sender name> : <forOldClients> if it is pre-0.2.8 and at least 0.2.6 )
se_BroadcastChatLine(ePlayerNetID * sender,const tString & line,const tString & forOldClients)1812 void se_BroadcastChatLine( ePlayerNetID* sender, const tString& line, const tString& forOldClients )
1813 {
1814     // create chat messages
1815     tJUST_CONTROLLED_PTR< nMessage > mServerControlled = se_ServerControlledChatMessageConsole( sender, line );
1816     tJUST_CONTROLLED_PTR< nMessage > mNew = se_NewChatMessage( sender, forOldClients );
1817     tJUST_CONTROLLED_PTR< nMessage > mOld = se_OldChatMessage( line );
1818 
1819     // send them to the users, depending on what they understand
1820     for ( int user = MAXCLIENTS; user > 0; --user )
1821     {
1822         if ( sn_Connections[ user ].socket )
1823         {
1824             if ( se_chatHandlerClient.Supported( user ) )
1825                 mServerControlled->Send( user );
1826             else if ( se_chatRelay.Supported( user ) )
1827                 mNew->Send( user );
1828             else
1829                 mOld->Send( user );
1830         }
1831     }
1832 }
1833 
1834 // send the chat of player p to all connected clients after properly formatting it
1835 // ( the clients will see <player>: <say>
se_BroadcastChat(ePlayerNetID * sender,const tString & say)1836 void se_BroadcastChat( ePlayerNetID* sender, const tString& say )
1837 {
1838     // create chat messages
1839     tJUST_CONTROLLED_PTR< nMessage > mServerControlled = se_ServerControlledChatMessage( sender, say );
1840     tJUST_CONTROLLED_PTR< nMessage > mNew = se_NewChatMessage( sender, say );
1841     tJUST_CONTROLLED_PTR< nMessage > mOld = se_OldChatMessage( sender, say );
1842 
1843     // send them to the users, depending on what they understand
1844     for ( int user = MAXCLIENTS; user > 0; --user )
1845     {
1846         if ( sn_Connections[ user ].socket )
1847         {
1848             if ( se_chatHandlerClient.Supported( user ) )
1849                 mServerControlled->Send( user );
1850             else if ( se_chatRelay.Supported( user ) )
1851                 mNew->Send( user );
1852             else
1853                 mOld->Send( user );
1854         }
1855     }
1856 }
1857 
1858 
1859 // sends a private message from sender to receiver, and really sends it to eavesdropper (will usually be equal to receiver)
se_SendPrivateMessage(ePlayerNetID const * sender,ePlayerNetID const * receiver,ePlayerNetID const * eavesdropper,tString const & message)1860 void se_SendPrivateMessage( ePlayerNetID const * sender, ePlayerNetID const * receiver, ePlayerNetID const * eavesdropper, tString const & message )
1861 {
1862     tASSERT( sender );
1863     tASSERT( receiver );
1864 
1865     // determine receiver client id
1866     int cid = eavesdropper->Owner();
1867 
1868     // determine wheter the receiver knows about the server controlled chat message
1869     if ( se_chatHandlerClient.Supported( cid ) )
1870     {
1871         // send server controlled message
1872         se_ServerControlledChatMessage( sender, receiver, message )->Send( cid );
1873     }
1874     else
1875     {
1876         tColoredString say;
1877         say << tColoredString::ColorString(1,1,.5) << "( --> ";
1878         say << *receiver;
1879         say << tColoredString::ColorString(1,1,.5) << " ) ";
1880         say << message;
1881 
1882         // format message containing receiver
1883         if ( se_chatRelay.Supported( cid ) )
1884         {
1885             // send client formatted message
1886             se_NewChatMessage( sender, say )->Send( cid );
1887         }
1888         else
1889         {
1890             // send console out message
1891             se_OldChatMessage( sender, say )->Send( cid );
1892         }
1893     }
1894 }
1895 
1896 // Sends a /team message
se_SendTeamMessage(eTeam const * team,ePlayerNetID const * sender,ePlayerNetID const * receiver,tString const & message)1897 void se_SendTeamMessage( eTeam const * team, ePlayerNetID const * sender ,ePlayerNetID const * receiver, tString const & message )
1898 {
1899     tASSERT( receiver );
1900     tASSERT( sender );
1901 
1902     int clientID = receiver->Owner();
1903     if ( clientID == 0 )
1904         return;
1905 
1906     if ( se_chatHandlerClient.Supported( clientID ) ) {
1907         se_ServerControlledChatMessage( team, sender, message )->Send( clientID );
1908     }
1909     else {
1910         tColoredString say;
1911         say << tColoredString::ColorString(1,1,.5) << "( " << *sender;
1912 
1913         if( !team )
1914         {
1915             // foo --> Spectatos: message
1916             say << tColoredString::ColorString(1,1,.5) << " --> " << tOutput("$player_spectator_message");
1917         }
1918         else if (sender->CurrentTeam() == team) {
1919             // ( foo --> Teammates ) some message here
1920             say << tColoredString::ColorString(1,1,.5) << " --> ";
1921             say << tColoredString::ColorString(team->R()/15.0,team->G()/15.0,team->B()/15.0) << tOutput("$player_team_message");;
1922         }
1923         // ( foo (Blue Team) --> Red Team ) some message
1924         else {
1925             eTeam *senderTeam = sender->CurrentTeam();
1926             say << tColoredString::ColorString(1,1,.5) << " (";
1927             say << team;
1928             say << tColoredString::ColorString(1,1,.5) << " ) --> ";
1929             say << senderTeam;
1930         }
1931         say << tColoredString::ColorString(1,1,.5) << " ) ";
1932         say << message;
1933 
1934         // format message containing receiver
1935         if ( se_chatRelay.Supported( clientID ) )
1936             // send client formatted message
1937             se_NewChatMessage( sender, say )->Send( clientID );
1938         else
1939             // send console out message
1940             se_OldChatMessage( sender, say )->Send( clientID );
1941     }
1942 }
1943 
1944 // returns a player ( not THE player, there may be more ) belonging to a user ID
1945 /*
1946 static ePlayerNetID * se_GetPlayerFromUserID( int uid )
1947 {
1948     // run through all players and look for a match
1949     for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a )
1950     {
1951         ePlayerNetID * p = se_PlayerNetIDs(a);
1952         if ( p && p->Owner() == uid )
1953             return p;
1954     }
1955 
1956     // found nothing
1957     return 0;
1958 }
1959 */
1960 
1961 // returns a player ( not THE player, there may be more ) belonging to a user ID that is still alive
1962 // static
se_GetAlivePlayerFromUserID(int uid)1963 ePlayerNetID * se_GetAlivePlayerFromUserID( int uid )
1964 {
1965     // run through all players and look for a match
1966     for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a )
1967     {
1968         ePlayerNetID * p = se_PlayerNetIDs(a);
1969         if ( p && p->Owner() == uid &&
1970                 ( ( p->Object() && p->Object()->Alive() ) ) )
1971             return p;
1972     }
1973 
1974     // found nothing
1975     return 0;
1976 }
1977 
1978 //The Base Remote Admin Password
1979 static tString sg_adminPass( "NONE" );
1980 static tConfItemLine sg_adminPassConf( "ADMIN_PASS", sg_adminPass );
1981 
1982 #ifdef DEDICATED
1983 
1984 // console with filter for remote admin output redirection
1985 class eAdminConsoleFilter:public tConsoleFilter{
1986 public:
eAdminConsoleFilter(int netID)1987     eAdminConsoleFilter( int netID )
1988             :netID_( netID )
1989     {
1990     }
1991 
~eAdminConsoleFilter()1992     ~eAdminConsoleFilter()
1993     {
1994         sn_ConsoleOut( message_, netID_ );
1995     }
1996 private:
1997     // we want to come first, the admin should get unfiltered output
DoGetPriority() const1998     virtual int DoGetPriority() const{ return -100; }
1999 
2000     // don't actually filter; take line and add it to the message sent to the admin
DoFilterLine(tString & line)2001     virtual void DoFilterLine( tString &line )
2002     {
2003         //tColoredString message;
2004         message_ << tColoredString::ColorString(1,.3,.3) << "RA: " << tColoredString::ColorString(1,1,1) << line << "\n";
2005 
2006         // don't let message grow indefinitely
2007         unsigned long len = message_.Len();
2008         tRecorderSync< unsigned long >::Archive( "_MESSAGE_LEN", 3, len );
2009         if (len > 600)
2010         {
2011             sn_ConsoleOut( message_, netID_ );
2012             message_.Clear();
2013         }
2014     }
2015 
2016     int netID_;              // the network user ID to send the result to
2017     tColoredString message_; // the console message for the remote administrator
2018 };
2019 
2020 static tString se_InterceptCommands;
2021 static tConfItemLine se_InterceptCommandsConf( "INTERCEPT_COMMANDS", se_InterceptCommands );
2022 
2023 static bool se_interceptUnknownCommands = false;
2024 static tSettingItem<bool> se_interceptUnknownCommandsConf("INTERCEPT_UNKNOWN_COMMANDS",
2025         se_interceptUnknownCommands);
2026 
2027 // minimal access level for /admin
2028 static tAccessLevel se_adminAccessLevel = tAccessLevel_Moderator;
2029 static tSettingItem< tAccessLevel > se_adminAccessLevelConf( "ACCESS_LEVEL_ADMIN", se_adminAccessLevel );
2030 static tAccessLevelSetter se_adminAccessLevelConfLevel( se_adminAccessLevelConf, tAccessLevel_Owner );
2031 
handle_command_intercept(ePlayerNetID * p,tString const & command,std::istream & s,tString const & say)2032 void handle_command_intercept( ePlayerNetID *p, tString const & command, std::istream & s, tString const & say ) {
2033     static eLadderLogWriter se_commandWriter( "COMMAND", true );
2034 
2035     tString commandArguments;
2036     commandArguments.ReadLine( s );
2037 
2038     se_commandWriter << command << p->GetLogName() << commandArguments;
2039     se_commandWriter.write();
2040 
2041     con << "[cmd] " << p->GetLogName() << ": " << say << '\n';
2042 }
2043 
2044 #ifdef KRAWALL_SERVER
2045 
2046 // minimal access level for /op/deop
2047 static tAccessLevel se_opAccessLevel = tAccessLevel_TeamLeader;
2048 static tSettingItem< tAccessLevel > se_opAccessLevelConf( "ACCESS_LEVEL_OP", se_opAccessLevel );
2049 static tAccessLevelSetter se_opAccessLevelConfLevel( se_opAccessLevelConf, tAccessLevel_Owner );
2050 
2051 // maximal result thereof
2052 static tAccessLevel se_opAccessLevelMax = tAccessLevel_Moderator;
2053 static tSettingItem< tAccessLevel > se_opAccessLevelMaxConf( "ACCESS_LEVEL_OP_MAX", se_opAccessLevelMax );
2054 static tAccessLevelSetter se_opAccessLevelMaxConfLevel( se_opAccessLevelMaxConf, tAccessLevel_Owner );
2055 
se_CanChangeAccess(ePlayerNetID * admin,ePlayerNetID * victim,char const * command)2056 static bool se_CanChangeAccess( ePlayerNetID * admin, ePlayerNetID * victim, char const * command )
2057 {
2058     tASSERT( admin );
2059     tASSERT( victim );
2060 
2061     if ( admin->GetAccessLevel() > se_opAccessLevel ) // Can he even use this command?
2062     {
2063         sn_ConsoleOut( tOutput( "$access_level_op_denied", command ), admin->Owner() );
2064     }
2065     else if ( victim == admin )
2066     {
2067         sn_ConsoleOut( tOutput( "$access_level_op_self", command ), admin->Owner() );
2068     }
2069     else if ( admin->GetAccessLevel() >= victim->GetAccessLevel()  )
2070     {
2071         sn_ConsoleOut( tOutput( "$access_level_op_overpowered", command ), admin->Owner() );
2072     }
2073     else
2074     {
2075         return true;
2076     }
2077 
2078     return false;
2079 
2080 }
2081 
2082 // an operation that changes the access level of another player
2083 typedef void (*OPFUNC)( ePlayerNetID * admin, ePlayerNetID * victim, tAccessLevel accessLevel );
2084 
se_ChangeAccess(ePlayerNetID * admin,std::istream & s,char const * command,OPFUNC F)2085 static void se_ChangeAccess( ePlayerNetID * admin, std::istream & s, char const * command, OPFUNC F )
2086 {
2087     bool isexplicit = false;
2088 
2089     ePlayerNetID * victim = se_FindPlayerInChatCommand( admin, command, s );
2090 
2091     if ( victim && se_CanChangeAccess( admin, victim, command ) )
2092     {
2093         // read optional access level, this part is merly a copypaste from the /shuffle code
2094         int level = se_opAccessLevelMax;
2095         if ( victim->IsAuthenticated() )
2096         {
2097             level = victim->GetAccessLevel();
2098         }
2099         char first;
2100         s >> first;
2101         if ( !s.eof() && !s.fail() )
2102         {
2103             isexplicit = true;
2104             s.unget();
2105             int newLevel = 0;
2106             s >> newLevel;
2107 
2108             if ( first == '+' || first == '-' )
2109             {
2110                 level += newLevel;
2111             }
2112             else
2113             {
2114                 level = newLevel;
2115             }
2116         }
2117 
2118         s >> level;
2119 
2120         // Make a last safety check on the given AL, then DON'T TOUCH IT ANYMORE
2121         if ( level <= admin->GetAccessLevel() )
2122             level = admin->GetAccessLevel() + 1;
2123 
2124         tAccessLevel accessLevel;
2125         accessLevel = static_cast< tAccessLevel >( level );
2126 
2127         if ( accessLevel == victim->GetAccessLevel() )
2128         {
2129             if ( isexplicit )
2130             {
2131                 sn_ConsoleOut( tOutput( "$access_level_op_same", command ), admin->Owner() );
2132             }
2133             else
2134             {
2135                 sn_ConsoleOut( tOutput( "$access_level_op_unclear", command ), admin->Owner() );
2136             }
2137         }
2138         else if ( accessLevel > admin->GetAccessLevel() )
2139         {
2140             (*F)( admin, victim, accessLevel );
2141         }
2142     }
2143 }
2144 
2145 // Promote changes the access rights of an already authed player
se_Promote(ePlayerNetID * admin,ePlayerNetID * victim,tAccessLevel accessLevel)2146 void se_Promote( ePlayerNetID * admin, ePlayerNetID * victim, tAccessLevel accessLevel )
2147 {
2148     if ( accessLevel > tAccessLevel_Authenticated )
2149     {
2150         accessLevel = tAccessLevel_Authenticated;
2151     }
2152     if ( accessLevel < tCurrentAccessLevel::GetAccessLevel() + 1 )
2153     {
2154         accessLevel = static_cast< tAccessLevel >( tCurrentAccessLevel::GetAccessLevel() + 1 );
2155     }
2156 
2157     if ( victim->IsAuthenticated() )
2158     {
2159         tAccessLevel oldAccessLevel = victim->GetAccessLevel();
2160         victim->SetAccessLevel( accessLevel );
2161 
2162         if ( accessLevel < oldAccessLevel )
2163         {
2164             se_SecretConsoleOut( tOutput( "$access_level_promote",
2165                                           victim->GetLogName(),
2166                                           tCurrentAccessLevel::GetName( accessLevel ),
2167                                           admin->GetLogName() ), victim, &se_Hide, admin, 0, &se_CanHide );
2168         }
2169         else if ( accessLevel > oldAccessLevel )
2170         {
2171             se_SecretConsoleOut( tOutput( "$access_level_demote",
2172                                  victim->GetLogName(),
2173                                  tCurrentAccessLevel::GetName( accessLevel ),
2174                                  admin->GetLogName() ), victim, &se_Hide, admin, 0, &se_CanHide );
2175 
2176         }
2177     }
2178 }
2179 
2180 // our operations of this kind: op grants access
se_OpBase(ePlayerNetID * admin,ePlayerNetID * victim,char const * command,tAccessLevel accessLevel)2181 void se_OpBase( ePlayerNetID * admin, ePlayerNetID * victim, char const * command, tAccessLevel accessLevel )
2182 {
2183     tString authName = victim->GetUserName() + "@L_OP";
2184     if ( victim->IsAuthenticated() )
2185     {
2186         authName = victim->GetRawAuthenticatedName();
2187     }
2188 
2189     if ( accessLevel < se_opAccessLevelMax )
2190         accessLevel = se_opAccessLevelMax;
2191 
2192     // no use authenticating an AI :)
2193     if ( !victim->IsHuman() )
2194     {
2195         sn_ConsoleOut( tOutput( "$access_level_op_denied_ai", command ), admin->Owner() );
2196     }
2197 
2198     victim->Authenticate( authName, accessLevel, admin );
2199 }
2200 
se_Op(ePlayerNetID * admin,ePlayerNetID * victim,tAccessLevel level)2201 void se_Op( ePlayerNetID * admin, ePlayerNetID * victim, tAccessLevel level )
2202 {
2203     int accessLevel = admin->GetAccessLevel() + 1;
2204 
2205     // respect passed in level
2206     if ( accessLevel < level )
2207     {
2208         accessLevel = level;
2209     }
2210 
2211     if ( victim->IsAuthenticated() )
2212     {
2213         se_Promote( admin, victim, static_cast< tAccessLevel >( accessLevel ) );
2214     }
2215     else
2216     {
2217         se_OpBase( admin, victim, "/op", static_cast< tAccessLevel >( accessLevel ) );
2218     }
2219 }
2220 
2221 // DeOp takes it away
se_DeOp(ePlayerNetID * admin,std::istream & s,char const * command)2222 void se_DeOp( ePlayerNetID * admin, std::istream & s, char const * command )
2223 {
2224     ePlayerNetID * victim = se_FindPlayerInChatCommand( admin, command, s );
2225 
2226     if ( victim && se_CanChangeAccess ( admin, victim, command ) )
2227     {
2228         if ( victim->IsAuthenticated() )
2229         {
2230             victim->DeAuthenticate( admin );
2231         }
2232         else
2233         {
2234             sn_ConsoleOut( tOutput( "$access_level_op_same", command ), admin->Owner() );
2235         }
2236     }
2237 }
2238 
2239 // minimal access level for /team management
2240 static tAccessLevel se_teamAccessLevel = tAccessLevel_TeamLeader;
2241 static tSettingItem< tAccessLevel > se_teamAccessLevelConf( "ACCESS_LEVEL_TEAM", se_teamAccessLevel );
2242 static tAccessLevelSetter se_teamAccessLevelConfLevel( se_teamAccessLevelConf, tAccessLevel_Owner );
2243 
2244 // returns the team managed by an admin
se_GetManagedTeam(ePlayerNetID * admin)2245 static eTeam * se_GetManagedTeam( ePlayerNetID * admin )
2246 {
2247     // the team to invite to is, of course, the team the admin is in.
2248     eTeam * team = admin->CurrentTeam();
2249     ePlayerNetID::eTeamSet const & invitations = admin->GetInvitations();
2250 
2251     // unless, of course, he is in no team, then let him relay the one invitation
2252     // he has.
2253     if ( !team && invitations.size() == 1 )
2254     {
2255         team = *invitations.begin();
2256     }
2257 
2258     return team;
2259 }
2260 
2261 // checks
se_CanManageTeam(ePlayerNetID * admin,bool emergency)2262 static bool se_CanManageTeam( ePlayerNetID * admin, bool emergency )
2263 {
2264     // check regular access level
2265     if ( admin->GetAccessLevel() <= se_teamAccessLevel )
2266     {
2267         return true;
2268     }
2269 
2270     // check emergency
2271     if ( !emergency )
2272     {
2273         return false;
2274     }
2275 
2276     // emergency: /invite and /unlock must be available to a player with
2277     // maximal access rights on the team, otherwise teams may become
2278     // locked and orphaned, without a chance to get new members.
2279 
2280     // no team? Nothing to manage
2281     eTeam * team = admin->CurrentTeam();
2282     if ( !team )
2283     {
2284         return false;
2285     }
2286 
2287     // check for players of a higher level
2288     for( int i = team->NumPlayers()-1; i >= 0; --i )
2289     {
2290         ePlayerNetID * otherPlayer = team->Player(i);
2291         if ( otherPlayer->IsHuman() && otherPlayer->GetAccessLevel() < admin->GetAccessLevel() )
2292         {
2293             return false;
2294         }
2295     }
2296 
2297     return true;
2298 }
2299 
2300 // invite/uninvite a player
2301 typedef void (eTeam::*INVITE)( ePlayerNetID * victim );
se_Invite(char const * command,ePlayerNetID * admin,std::istream & s,INVITE invite)2302 static void se_Invite( char const * command, ePlayerNetID * admin, std::istream & s, INVITE invite )
2303 {
2304     if ( se_CanManageTeam( admin, invite == &eTeam::Invite ) )
2305     {
2306         // get the team the admin can manage
2307         eTeam * team = se_GetManagedTeam( admin );
2308 
2309         if ( team )
2310         {
2311             ePlayerNetID * victim = se_FindPlayerInChatCommand( admin, command, s );
2312             if ( victim )
2313             {
2314                 // invite/uninvite him
2315                 (team->*invite)( victim );
2316             }
2317         }
2318         else
2319         {
2320             sn_ConsoleOut( tOutput( "$invite_no_team", command ), admin->Owner() );
2321         }
2322     }
2323     else
2324     {
2325         sn_ConsoleOut( tOutput( "$access_level_op_denied", command ), admin->Owner() );
2326     }
2327 }
2328 
2329 // changes private party status of a team
se_Lock(char const * command,ePlayerNetID * admin,std::istream & s,bool lock)2330 static void se_Lock( char const * command, ePlayerNetID * admin, std::istream & s, bool lock )
2331 {
2332     if ( se_CanManageTeam( admin, !lock ) )
2333     {
2334         // get the team the admin can manage
2335         eTeam * team = se_GetManagedTeam( admin );
2336 
2337         if ( team )
2338         {
2339             team->SetLocked( lock );
2340         }
2341         else
2342         {
2343             sn_ConsoleOut( tOutput( "$invite_no_team", command ), admin->Owner() );
2344         }
2345     }
2346     else
2347     {
2348         sn_ConsoleOut( tOutput( "$access_level_op_denied", command ), admin->Owner() );
2349     }
2350 }
2351 
2352 #else // KRAWALL
2353 // returns the team managed by an admin
se_GetManagedTeam(ePlayerNetID * admin)2354 static eTeam * se_GetManagedTeam( ePlayerNetID * admin )
2355 {
2356     return admin->CurrentTeam();
2357 }
2358 #endif // KRAWALL
2359 
2360 // the following function really is only supposed to be called from here and nowhere else
2361 // (access right escalation risk):
2362 // log in (via admin password or hash based login)
se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE(ePlayerNetID * p,std::istream & s)2363 static void se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE( ePlayerNetID * p, std::istream & s )
2364 {
2365     tString params("");
2366     params.ReadLine( s );
2367 #ifndef KRAWALL_SERVER
2368     if ( params == "" )
2369         return;
2370 #endif
2371 
2372     // trim whitespace
2373 
2374     // for the trunk:
2375     // params.Trim();
2376     // here,we have to do the work manually
2377     {
2378         int lastNonSpace = params.Len() - 2;
2379         while ( lastNonSpace >= 0 && isblank(params[lastNonSpace]) )
2380         {
2381             --lastNonSpace;
2382         }
2383 
2384         if ( lastNonSpace < params.Len() - 2 )
2385         {
2386             params = params.SubStr( 0, lastNonSpace + 1 );
2387         }
2388     }
2389 
2390 #ifndef KRAWALL_SERVER
2391     // the password is not stored in the recording, hence we have to store the
2392     // result of the password test
2393     bool accept = true;
2394     static const char * section = "REMOTE_LOGIN";
2395     if ( !tRecorder::Playback( section, accept ) )
2396         accept = ( params == sg_adminPass && sg_adminPass != "NONE" );
2397     tRecorder::Record( section, accept );
2398 
2399     //change this later to read from a password file or something...
2400     //or integrate it with auth if we ever get that done...
2401     if ( accept ) {
2402         // the following function really is only supposed to be called from here and nowhere else
2403         // (access right escalation risk)
2404         se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE( p );
2405     }
2406     else
2407     {
2408         tString failedLogin;
2409         sn_ConsoleOut("Login denied!\n",p->Owner());
2410         failedLogin << "Remote admin login for user \"" << p->GetUserName();
2411         failedLogin << "\" using password \"" << params << "\" rejected.\n";
2412         sn_ConsoleOut(failedLogin, 0);
2413     }
2414 #else
2415     if ( sn_GetNetState() == nSERVER && p->Owner() != sn_myNetID )
2416     {
2417         if ( p->IsAuthenticated() )
2418         {
2419             sn_ConsoleOut( "$login_request_redundant", p->Owner() );
2420             return;
2421         }
2422 
2423         if ( p->GetRawAuthenticatedName().Len() <= 1 || params.StrPos("@") >= 0 )
2424         {
2425             if ( params.StrPos( "@" ) >= 0 )
2426             {
2427                 p->SetRawAuthenticatedName( params );
2428             }
2429             else
2430             {
2431                 p->SetRawAuthenticatedName( p->GetUserName() + "@" + params );
2432             }
2433         }
2434 
2435         // check for stupid bans
2436         if ( se_IsUserBanned( p, p->GetRawAuthenticatedName() ) )
2437         {
2438             return;
2439         }
2440 
2441         p->loginWanted = true;
2442 
2443         se_RequestLogin( p );
2444     }
2445 #endif
2446 }
2447 
2448 // log out
se_AdminLogout(ePlayerNetID * p,char const * command)2449 static void se_AdminLogout( ePlayerNetID * p, char const * command )
2450 {
2451 #ifdef KRAWALL_SERVER
2452     // revoke the other kind of authentication as well
2453     if ( p->IsAuthenticated() )
2454     {
2455         p->DeAuthenticate();
2456     }
2457     else
2458     {
2459         sn_ConsoleOut( tOutput( "$access_level_op_same", command ), p->Owner() );
2460     }
2461 #else
2462     if ( p->IsLoggedIn() )
2463     {
2464         sn_ConsoleOut("You have been logged out!\n",p->Owner());
2465     }
2466     p->BeNotLoggedIn();
2467 #endif
2468 }
2469 
2470 // access level a user has to have to be able to see what's being typed at /admin
2471 static tAccessLevel se_consoleSpyAccessLevel = tAccessLevel_Moderator;
2472 static tSettingItem< tAccessLevel > se_consoleSpyAccessLevelConf( "ACCESS_LEVEL_SPY_CONSOLE", se_consoleSpyAccessLevel );
2473 static tAccessLevelSetter se_consoleSpyAccessLevelConfLevel( se_consoleSpyAccessLevelConf, tAccessLevel_Owner );
2474 
se_cannotSeeConsole(ePlayerNetID const *,ePlayerNetID const * seeker)2475 static bool se_cannotSeeConsole( ePlayerNetID const *, ePlayerNetID const * seeker )
2476 {
2477     return seeker->GetAccessLevel() > se_consoleSpyAccessLevel;
2478 }
2479 
2480 // /admin chat command
se_AdminAdmin(ePlayerNetID * p,std::istream & s)2481 static void se_AdminAdmin( ePlayerNetID * p, std::istream & s )
2482 {
2483     if ( p->GetAccessLevel() > se_adminAccessLevel )
2484     {
2485         sn_ConsoleOut( tOutput( "$access_level_admin_denied" ), p->Owner() );
2486         return;
2487     }
2488 
2489     tString str;
2490     str.ReadLine(s);
2491     tColoredString msg;
2492     msg << tColoredString::ColorString(1,0,0) << "Remote admin command" << tColoredString::ColorString(-1,-1,-1) << " by " << tColoredString::ColorString(1,1,.5) << p->GetUserName() << tColoredString::ColorString(-1,-1,-1) << ": " << tColoredString::ColorString(.5,.5,1) << str << "\n";
2493     se_SecretConsoleOut( msg, p, &se_cannotSeeConsole, p );
2494     std::istringstream stream(&str(0));
2495 
2496     // install filter
2497     eAdminConsoleFilter consoleFilter( p->Owner() );
2498     try
2499     {
2500         tConfItemBase::LoadLine(stream);
2501     }
2502     catch (tAbortLoading)
2503     {
2504         con << tOutput("$config_abort");
2505     }
2506 }
2507 
handle_chat_admin_commands(ePlayerNetID * p,tString const & command,tString const & say,std::istream & s,eChatSpamTester & spam)2508 static void handle_chat_admin_commands( ePlayerNetID * p, tString const & command, tString const & say, std::istream & s, eChatSpamTester &spam )
2509 {
2510     if  (command == "/login")
2511     {
2512         // Really, there's no reason one would log in and log out all the time
2513         spam.factor_ = 1;
2514         if ( spam.Block() )
2515         {
2516             return;
2517         }
2518 
2519         // the following function really is only supposed to be called from here and nowhere else
2520         // (access right escalation risk)
2521         se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE( p, s );
2522     }
2523     else  if (command == "/logout")
2524     {
2525         spam.factor_ = 1;
2526         if( spam.Block() )
2527         {
2528             return;
2529         }
2530 
2531         se_AdminLogout( p, command );
2532     }
2533 #ifdef KRAWALL_SERVER
2534     else if ( command == "/op" )
2535     {
2536         se_ChangeAccess( p, s, "/op", &se_Op );
2537     }
2538     else if ( command == "/deop" )
2539     {
2540         se_DeOp( p, s, "/deop" );
2541     }
2542     else if ( command == "/invite" )
2543     {
2544         se_Invite( command, p, s, &eTeam::Invite );
2545     }
2546     else if ( command == "/uninvite" )
2547     {
2548         se_Invite( command, p, s, &eTeam::UnInvite );
2549     }
2550     else if ( command == "/lock" )
2551     {
2552         se_Lock( command, p, s, true );
2553     }
2554     else if ( command == "/unlock" )
2555     {
2556         se_Lock( command, p, s, false );
2557     }
2558 #endif
2559     else  if ( command == "/admin" )
2560     {
2561         se_AdminAdmin( p, s );
2562     }
2563     else
2564         if (se_interceptUnknownCommands)
2565         {
2566             handle_command_intercept(p, command, s, say);
2567         }
2568         else
2569         {
2570             sn_ConsoleOut( tOutput( "$chat_command_unknown", command ), p->Owner() );
2571         }
2572 }
2573 #else // DEDICATED
2574 // returns the team managed by an admin
se_GetManagedTeam(ePlayerNetID * admin)2575 static eTeam * se_GetManagedTeam( ePlayerNetID * admin )
2576 {
2577     return admin->CurrentTeam();
2578 }
2579 #endif // DEDICATED
2580 
2581 REAL se_alreadySaidTimeout=5.0;
2582 static tSettingItem<REAL> se_alreadySaidTimeoutConf("SPAM_PROTECTION_REPEAT",
2583         se_alreadySaidTimeout);
2584 
2585 #ifndef KRAWALL_SERVER
2586 // flag set to allow players to shuffle themselves up in a team
2587 static bool se_allowShuffleUp=false;
2588 static tSettingItem<bool> se_allowShuffleUpConf("TEAM_ALLOW_SHUFFLE_UP",
2589         se_allowShuffleUp);
2590 #else
2591 static tAccessLevel se_shuffleUpAccessLevel = tAccessLevel_TeamMember;
2592 static tSettingItem< tAccessLevel > se_shuffleUpAccessLevelConf( "ACCESS_LEVEL_SHUFFLE_UP", se_shuffleUpAccessLevel );
2593 static tAccessLevelSetter se_shuffleUpAccessLevelConfLevel( se_shuffleUpAccessLevelConf, tAccessLevel_Owner );
2594 #endif
2595 
2596 static bool se_silenceDefault = false;        // flag indicating whether new players should be silenced
2597 
2598 // minimal access level for chat
2599 tAccessLevel se_chatAccessLevel = tAccessLevel_Program;
2600 static tSettingItem< tAccessLevel > se_chatAccessLevelConf( "ACCESS_LEVEL_CHAT", se_chatAccessLevel );
2601 static tAccessLevelSetter se_chatAccessLevelConfLevel( se_chatAccessLevelConf, tAccessLevel_Owner );
2602 
2603 // time between public chat requests, set to 0 to disable
2604 REAL se_chatRequestTimeout = 60;
2605 static tSettingItem< REAL > se_chatRequestTimeoutConf( "ACCESS_LEVEL_CHAT_TIMEOUT", se_chatRequestTimeout );
2606 
2607 // access level a spectator has to have to be able to listen to /team messages
2608 static tAccessLevel se_teamSpyAccessLevel = tAccessLevel_Moderator;
2609 static tSettingItem< tAccessLevel > se_teamSpyAccessLevelConf( "ACCESS_LEVEL_SPY_TEAM", se_teamSpyAccessLevel );
2610 static tAccessLevelSetter se_teamSpyAccessLevelConfLevel( se_teamSpyAccessLevelConf, tAccessLevel_Owner );
2611 
2612 // access level a user has to have to be able to listen to /msg messages
2613 static tAccessLevel se_msgSpyAccessLevel = tAccessLevel_Owner;
2614 static tSettingItem< tAccessLevel > se_msgSpyAccessLevelConf( "ACCESS_LEVEL_SPY_MSG", se_msgSpyAccessLevel );
2615 static tAccessLevelSetter se_msgSpyAccessLevelConfLevel( se_msgSpyAccessLevelConf, tAccessLevel_Owner );
2616 
2617 // access level a user has to have to get IP addresses in /players output
2618 static tAccessLevel se_ipAccessLevel = tAccessLevel_Armatrator;
2619 static tSettingItem< tAccessLevel > se_ipAccessLevelConf( "ACCESS_LEVEL_IPS", se_ipAccessLevel );
2620 static tAccessLevelSetter se_ipAccessLevelConfLevel( se_ipAccessLevelConf, tAccessLevel_Owner );
2621 
2622 static tAccessLevel se_nVerAccessLevel = tAccessLevel_Moderator;
2623 static tSettingItem< tAccessLevel > se_nVerAccessLevelConf( "ACCESS_LEVEL_NVER", se_nVerAccessLevel );
2624 static tAccessLevelSetter se_nVerAccessLevelConfLevel( se_nVerAccessLevelConf, tAccessLevel_Owner );
2625 
2626 static tSettingItem<bool> se_silAll("SILENCE_DEFAULT",
2627                                     se_silenceDefault);
2628 
2629 // checks whether a player is silenced, giving him appropriate warnings if he is
IsSilencedWithWarning(ePlayerNetID const * p)2630 bool IsSilencedWithWarning( ePlayerNetID const * p )
2631 {
2632     if ( !se_enableChat && ! p->IsLoggedIn() )
2633     {
2634         // everyone except the admins is silenced
2635         sn_ConsoleOut( tOutput( "$spam_protection_silenceall" ), p->Owner() );
2636         return true;
2637     }
2638     else if ( p->IsSilenced() )
2639     {
2640         if(se_silenceDefault) {
2641             // player is silenced, but all players are silenced by default
2642             sn_ConsoleOut( tOutput( "$spam_protection_silenced_default" ), p->Owner() );
2643         } else {
2644             // player is specially silenced
2645             sn_ConsoleOut( tOutput( "$spam_protection_silenced" ), p->Owner() );
2646         }
2647         return true;
2648     }
2649 
2650     return false;
2651 }
2652 
2653 // /me chat commant
se_ChatMe(ePlayerNetID * p,std::istream & s,eChatSpamTester & spam)2654 static void se_ChatMe( ePlayerNetID * p, std::istream & s, eChatSpamTester & spam )
2655 {
2656     if ( IsSilencedWithWarning(p) || spam.Block() )
2657     {
2658         return;
2659     }
2660 
2661     tString msg;
2662     msg.ReadLine( s );
2663 
2664     tColoredString console;
2665     console << tColoredString::ColorString(1,1,1)  << "* ";
2666     console << *p;
2667     console << tColoredString::ColorString(1,1,.5) << " " << msg;
2668     console << tColoredString::ColorString(1,1,1)  << " *";
2669 
2670     tColoredString forOldClients;
2671     forOldClients << tColoredString::ColorString(1,1,1)  << "*"
2672                   << tColoredString::ColorString(1,1,.5) << msg
2673                   << tColoredString::ColorString(1,1,1)  << "*";
2674 
2675     se_BroadcastChatLine( p, console, forOldClients );
2676     console << "\n";
2677     sn_ConsoleOut(console,0);
2678 
2679     tString str;
2680     str << p->GetUserName() << " /me " << msg;
2681     se_SaveToChatLog(str);
2682     return;
2683 }
2684 
2685 // /teamleave chat command: leaves the current team
se_ChatTeamLeave(ePlayerNetID * p)2686 static void se_ChatTeamLeave( ePlayerNetID * p )
2687 {
2688     if ( se_assignTeamAutomatically )
2689     {
2690         sn_ConsoleOut(tOutput("$player_teamleave_disallowed"), p->Owner() );
2691         return;
2692     }
2693     if(!p->TeamChangeAllowed()) {
2694         sn_ConsoleOut(tOutput("$player_disallowed_teamchange"), p->Owner() );
2695         return;
2696     }
2697 
2698     eTeam * leftTeam = p->NextTeam();
2699     if ( leftTeam )
2700     {
2701         if ( !leftTeam )
2702             leftTeam = p->CurrentTeam();
2703 
2704         if ( leftTeam->NumPlayers() > 1 )
2705         {
2706             sn_ConsoleOut( tOutput( "$player_leave_team_wish",
2707                                     tColoredString::RemoveColors(p->GetName()),
2708                                     tColoredString::RemoveColors(leftTeam->Name()) ) );
2709         }
2710         else
2711         {
2712             sn_ConsoleOut( tOutput( "$player_leave_game_wish",
2713                                     tColoredString::RemoveColors(p->GetName()) ) );
2714         }
2715     }
2716 
2717     p->SetTeamWish(0);
2718 }
2719 
2720 static bool se_filterColorTeam=false;
2721 tSettingItem< bool > se_coloredTeamConf( "FILTER_COLOR_TEAM", se_filterColorTeam );
2722 static bool se_filterDarkColorTeam=false;
2723 tSettingItem< bool > se_coloredDarkTeamConf( "FILTER_DARK_COLOR_TEAM", se_filterDarkColorTeam );
2724 
2725 // /team chat commant: talk to your team
se_ChatTeam(ePlayerNetID * p,std::istream & s,eChatSpamTester & spam)2726 static void se_ChatTeam( ePlayerNetID * p, std::istream & s, eChatSpamTester & spam )
2727 {
2728     eTeam *currentTeam = se_GetManagedTeam( p );
2729 
2730     // team messages are less spammy than public chat, take care of that.
2731     // we don't care too much about AI players (but don't remove them from the denominator because we're too lazy to count the total number of human players).
2732     spam.factor_ = ( currentTeam ? currentTeam->NumHumanPlayers() : 1 )/REAL( se_PlayerNetIDs.Len() );
2733 
2734     // silencing only affects spectators here
2735     if ( ( !currentTeam && IsSilencedWithWarning(p) ) || spam.Block() )
2736     {
2737         return;
2738     }
2739 
2740     tString msg;
2741     msg.ReadLine( s );
2742 
2743     // Apply filters if we don't already
2744     if ( se_filterColorTeam )
2745         msg = tColoredString::RemoveColors ( msg, false );
2746     else if ( se_filterDarkColorTeam )
2747         msg = tColoredString::RemoveColors ( msg, true );
2748 
2749     // Log message to server (and in previous revisions, the sender)
2750     tColoredString messageForServerAndSender = se_BuildChatString(currentTeam, p, msg);
2751     messageForServerAndSender << "\n";
2752 
2753     if (currentTeam != NULL) // If a player has just joined the game, he is not yet on a team. Sending a /team message will crash the server
2754     {
2755         sn_ConsoleOut(messageForServerAndSender, 0);
2756 
2757         // Send message to team-mates
2758         int numTeamPlayers = currentTeam->NumPlayers();
2759         for (int teamPlayerIndex = 0; teamPlayerIndex < numTeamPlayers; teamPlayerIndex++)
2760         {
2761             se_SendTeamMessage(currentTeam, p, currentTeam->Player(teamPlayerIndex), msg);
2762         }
2763 
2764         // check for other players who are authorized to hear the message
2765         for( int i = se_PlayerNetIDs.Len() - 1; i >=0; --i )
2766         {
2767             ePlayerNetID * admin = se_PlayerNetIDs(i);
2768 
2769             if (
2770                 // two cases:
2771                    (
2772                     // You are a speccing admin, and you aren't invited to anything:
2773                     se_GetManagedTeam( admin ) == 0 &&
2774                     admin->GetAccessLevel() <=  se_teamSpyAccessLevel
2775                     ) ||
2776                    (
2777                     // You are invited and spectating
2778                     admin->CurrentTeam() == NULL &&
2779                     // invited players are also authorized
2780                     currentTeam->IsInvited( admin )
2781                     )
2782                 )
2783             {
2784                 se_SendTeamMessage(currentTeam, p, admin, msg);
2785             }
2786         }
2787     }
2788     else
2789     {
2790         sn_ConsoleOut(messageForServerAndSender, 0);
2791 
2792         // check for other spectators
2793         for( int i = se_PlayerNetIDs.Len() - 1; i >=0; --i )
2794         {
2795             ePlayerNetID * spectator = se_PlayerNetIDs(i);
2796 
2797             if ( se_GetManagedTeam( spectator ) == 0 )
2798             {
2799                 se_SendTeamMessage(currentTeam, p, spectator, msg);
2800             }
2801         }
2802     }
2803 }
2804 
2805 // /msg chat commant: talk to anyone team
se_ChatMsg(ePlayerNetID * p,std::istream & s,eChatSpamTester & spam)2806 static void se_ChatMsg( ePlayerNetID * p, std::istream & s, eChatSpamTester & spam )
2807 {
2808     // odd, the refactored original did not check for silence. Probably by design.
2809     if ( /* IsSilencedWithWarning(player) || */ spam.Block() )
2810     {
2811         return;
2812     }
2813 
2814     // Check for player
2815     ePlayerNetID * receiver =  se_FindPlayerInChatCommand( p, "/msg", s );
2816 
2817     // One match, send it.
2818     if ( receiver ) {
2819         // extract rest of message: it is the true message to send
2820         std::ws(s);
2821 
2822         // read the rest of the message
2823         tString msg_core;
2824         msg_core.ReadLine(s);
2825 
2826         // build chat string
2827         tColoredString toServer = se_BuildChatString( p, receiver, msg_core );
2828         toServer << '\n';
2829 
2830         if ( p->CurrentTeam() == receiver->CurrentTeam() || !IsSilencedWithWarning(p) )
2831         {
2832             // log locally
2833             sn_ConsoleOut(toServer,0);
2834 
2835             // log to sender's console
2836             sn_ConsoleOut(toServer, p->Owner());
2837 
2838             // send to receiver
2839             if ( p->Owner() != receiver->Owner() )
2840                 se_SendPrivateMessage( p, receiver, receiver, msg_core );
2841 
2842             // let admins of sufficient rights eavesdrop
2843             for( int i = se_PlayerNetIDs.Len() - 1; i >=0; --i )
2844             {
2845                 ePlayerNetID * admin = se_PlayerNetIDs(i);
2846 
2847                 if ( admin != receiver && admin != p && admin->GetAccessLevel() <=  se_msgSpyAccessLevel )
2848                 {
2849                     se_SendPrivateMessage( p, receiver, admin, msg_core );
2850                 }
2851             }
2852         }
2853     }
2854 }
2855 
se_SendTo(std::string const & message,ePlayerNetID * receiver)2856 static void se_SendTo( std::string const & message, ePlayerNetID * receiver )
2857 {
2858     if ( receiver )
2859     {
2860         sn_ConsoleOut(message.c_str(), receiver->Owner());
2861     }
2862     else
2863     {
2864         con << message;
2865     }
2866 }
2867 
2868 // prints an indented team member with position marker
se_SendTeamMember(ePlayerNetID const * player,int indent,std::ostream & tos,int index,int width)2869 static void se_SendTeamMember( ePlayerNetID const * player, int indent, std::ostream & tos, int index, int width )
2870 {
2871     tos << '#' << std::setw( width ) << index+1 << ' ';
2872 
2873     // send team name
2874     for( int i = indent-1; i >= 0; --i )
2875     {
2876         tos << ' ';
2877     }
2878 
2879     tos << *player << "\n";
2880 }
2881 
2882 // list teams with their formation
se_ListTeam(ePlayerNetID * receiver,eTeam * team)2883 static void se_ListTeam( ePlayerNetID * receiver, eTeam * team )
2884 {
2885     std::ostringstream tos;
2886 
2887     // send team name
2888     tos << team->GetColoredName();
2889     if ( team->IsLocked() )
2890     {
2891         tos << " " << tOutput( "$invite_team_locked_list" );
2892     }
2893     tos << ":\n";
2894 
2895     // send team members
2896     int teamMembers = team->NumPlayers();
2897     int width = teamMembers >= 10 ? 2 : 1;
2898 
2899     int indent = 0;
2900     // print left wing, the odd team members
2901     for( int i = (teamMembers/2)*2-1; i>=0; i -= 2 )
2902     {
2903         se_SendTeamMember( team->Player(i), indent, tos, i, width );
2904         indent += 2;
2905     }
2906     // print right wing, the even team members
2907     for( int i = 0; i < teamMembers; i += 2 )
2908     {
2909         se_SendTeamMember( team->Player(i), indent, tos, i, width );
2910         indent -= 2;
2911     }
2912 
2913     tos << "\n";
2914 
2915     se_SendTo( tos.str(), receiver );
2916 }
2917 
se_ListTeams(ePlayerNetID * receiver)2918 static void se_ListTeams( ePlayerNetID * receiver )
2919 {
2920     int numTeams = 0;
2921 
2922     for ( int i = eTeam::teams.Len() - 1; i >= 0; --i )
2923     {
2924         eTeam * team = eTeam::teams[i];
2925         if ( team->NumPlayers() > 1 || team->IsLocked() )
2926         {
2927             numTeams++;
2928             se_ListTeam( receiver, team );
2929         }
2930     }
2931 
2932     if ( numTeams == 0 )
2933     {
2934         se_SendTo( std::string( tString( tOutput("$no_real_teams") ) ), receiver );
2935     }
2936 }
2937 
teams_conf(std::istream & s)2938 static void teams_conf(std::istream &s)
2939 {
2940     se_ListTeams( 0 );
2941 }
2942 
2943 static tConfItemFunc teams("TEAMS",&teams_conf);
2944 
2945 // /teams gives a teams list
se_ChatTeams(ePlayerNetID * p)2946 static void se_ChatTeams( ePlayerNetID * p )
2947 {
2948     se_ListTeams( p );
2949 }
2950 
se_ListPlayers(ePlayerNetID * receiver,std::istream & s,tString command)2951 static void se_ListPlayers( ePlayerNetID * receiver, std::istream &s, tString command )
2952 {
2953     tString search;
2954     bool doSearch = false;
2955 
2956     search.ReadLine( s );
2957     tToLower( search );
2958 
2959     if ( search.Len() > 1 )
2960     doSearch = true;
2961 
2962     bool hidden = false;
2963 
2964     int count = 0;
2965 
2966     for ( int i2 = se_PlayerNetIDs.Len()-1; i2>=0; --i2 )
2967     {
2968         ePlayerNetID* p2 = se_PlayerNetIDs(i2);
2969         tColoredString tos;
2970         if ( doSearch )
2971             tos << "  ";
2972         tos << p2->Owner();
2973         tos << ": ";
2974         if ( p2->GetAccessLevel() < tAccessLevel_Default && !se_Hide( p2, receiver ) )
2975         {
2976 #ifdef KRAWALL_SERVER
2977             hidden = p2->GetAccessLevel() <= se_hideAccessLevelOf && p2->StealthMode();
2978 #else
2979             hidden = false;
2980 #endif
2981             tos << p2->GetColoredName()
2982                 << tColoredString::ColorString( -1, -1, -1)
2983                 << " ( ";
2984             if ( hidden )
2985                 tos << se_hiddenPlayerPrefix;
2986 #ifdef KRAWALL_SERVER
2987             tos << p2->GetFilteredAuthenticatedName();
2988             if ( hidden )
2989                 tos << tColoredString::ColorString( -1 ,-1 ,-1 )
2990                     << ", "
2991                     << se_hiddenPlayerPrefix;
2992             else
2993                 tos << ", ";
2994 #endif
2995             tos << tCurrentAccessLevel::GetName( p2->GetAccessLevel() );
2996             if ( hidden )
2997                 tos << tColoredString::ColorString( -1 ,-1 ,-1 );
2998             tos << " )";
2999         }
3000         else
3001         {
3002             tos << p2->GetColoredName() << tColoredString::ColorString(1,1,1) << " ( )";
3003         }
3004         if ( ( p2->Owner() != 0 && tCurrentAccessLevel::GetAccessLevel() <= se_ipAccessLevel ) || ( p2->Owner() != 0 && p2->Owner() == receiver->Owner() ) )
3005         {
3006             tString IP = p2->GetMachine().GetIP();
3007             if ( IP.Len() > 1 )
3008             {
3009                 tos << ", IP = " << IP;
3010             }
3011         }
3012         if ( ( p2->Owner() != 0 && tCurrentAccessLevel::GetAccessLevel() <= se_nVerAccessLevel ) || ( p2->Owner() != 0 && p2->Owner() == receiver->Owner() ) )
3013         {
3014             tos << ", " << sn_GetClientVersionString( sn_Connections[ p2->Owner() ].version.Max() ) << " (ID: " << sn_Connections[ p2->Owner() ].version.Max() << ")";
3015         }
3016 
3017         tos << "\n";
3018 
3019         if ( !doSearch )
3020         {
3021             sn_ConsoleOut( tos, receiver->Owner() );
3022             count++;
3023         }
3024         else
3025         {
3026             tString tosLowercase( tColoredString::RemoveColors(tos) );
3027             tToLower( tosLowercase );
3028             // looks quite like a hack, but i guess it's faster( esp. for me :) ) than checking on each parameter individually
3029             if ( tosLowercase.StrPos( search ) != -1 )
3030             {
3031                 count++;
3032                 if ( count == 1 )
3033                 {
3034                     sn_ConsoleOut( tOutput( "$player_list_search", command, search ) , receiver->Owner() );
3035                 }
3036                 sn_ConsoleOut( tos, receiver->Owner() );
3037             }
3038         }
3039     }
3040 
3041     if ( doSearch && !count )
3042     {
3043         sn_ConsoleOut( tOutput( "$player_list_search_no_results", command, search ) , receiver->Owner() );
3044     }
3045     else if ( doSearch )
3046     {
3047         sn_ConsoleOut( tOutput( "$player_list_search_end", command, count ) , receiver->Owner() );
3048     }
3049     else
3050     {
3051         sn_ConsoleOut( tOutput( "$player_list_end", command, count ) , receiver->Owner() );
3052     }
3053 }
3054 
players_conf(std::istream & s)3055 static void players_conf(std::istream &s)
3056 {
3057     se_ListPlayers( 0, s, tString("PLAYERS") );
3058 }
3059 
3060 static tConfItemFunc players("PLAYERS",&players_conf);
3061 static tAccessLevelSetter players_AccessLevel( players, tAccessLevel_Owner );
3062 
3063 
3064 // /players gives a player list
se_ChatPlayers(ePlayerNetID * p,std::istream & s,tString command)3065 static void se_ChatPlayers( ePlayerNetID * p, std::istream &s, tString command )
3066 {
3067     se_ListPlayers( p, s, command );
3068 }
3069 
3070 
3071 // team shuffling: reorders team formation
se_ChatShuffle(ePlayerNetID * p,std::istream & s)3072 static void se_ChatShuffle( ePlayerNetID * p, std::istream & s )
3073 {
3074     // team position shuffling. Allow players to change their team setup.
3075     // syntax:
3076     // /teamshuffle: shuffles you all the way to the outside.
3077     // /teamshuffle <pos>: shuffles you to position pos
3078     // /teamshuffle +/-<dist>: shuffles you dist to the outside/inside
3079     // con << msgRest << "\n";
3080     int IDNow = p->TeamListID();
3081     if (!p->CurrentTeam())
3082     {
3083         sn_ConsoleOut(tOutput("$player_not_on_team"), p->Owner());
3084         return;
3085     }
3086     int len = p->CurrentTeam()->NumPlayers();
3087 
3088     // but read the target position as additional parameter
3089     int IDWish = len-1; // default to shuffle to the outside
3090 
3091     // peek at the first nonwhite character
3092     std::ws( s );
3093     char first = s.get();
3094     if ( !s.eof() && !s.fail() )
3095     {
3096         s.unget();
3097 
3098         int shuffle = 0;
3099         s >> shuffle;
3100 
3101         if ( s.fail() )
3102         {
3103             sn_ConsoleOut( tOutput("$player_shuffle_error"), p->Owner() );
3104             return;
3105         }
3106 
3107         if ( first == '+' || first == '-' )
3108         {
3109             IDWish = IDNow;
3110             IDWish += shuffle;
3111         }
3112         else
3113         {
3114             IDWish = shuffle-1;
3115         }
3116     }
3117 
3118     if (IDWish < 0)
3119         IDWish = 0;
3120     if (IDWish >= len)
3121         IDWish = len-1;
3122 
3123     if(IDWish < IDNow)
3124     {
3125 #ifndef KRAWALL_SERVER
3126         if ( !se_allowShuffleUp )
3127         {
3128             sn_ConsoleOut(tOutput("$player_noshuffleup"), p->Owner());
3129             return;
3130         }
3131 #else
3132         if ( p->GetAccessLevel() > se_shuffleUpAccessLevel )
3133         {
3134             sn_ConsoleOut(tOutput("$access_level_shuffle_up_denied", tCurrentAccessLevel::GetName(se_shuffleUpAccessLevel), tCurrentAccessLevel::GetName(p->GetAccessLevel())), p->Owner());
3135             return;
3136         }
3137 #endif
3138     }
3139 
3140     if( IDNow == IDWish )
3141     {
3142         sn_ConsoleOut(tOutput("$player_noshuffle"), p->Owner());
3143         return;
3144     }
3145 
3146     p->CurrentTeam()->Shuffle( IDNow, IDWish );
3147     se_ListTeam( p, p->CurrentTeam() );
3148 }
3149 
3150 class eHelpTopic {
3151     tString m_shortdesc, m_text;
3152 
3153     // singleton accessor
3154     static std::map<tString, eHelpTopic> & GetHelpTopics();
3155 public:
eHelpTopic()3156     eHelpTopic() {}
eHelpTopic(tString const & shortdesc,tString const & text)3157     eHelpTopic(tString const &shortdesc, tString const &text) : m_shortdesc(shortdesc), m_text(text) {
3158     }
3159 
write(tColoredString & s) const3160     void write(tColoredString &s) const {
3161         s << tColoredString::ColorString(.5,.5,1.) << tOutput(&m_shortdesc[0]) << ":\n" << tOutput(&m_text[0]) << '\n';
3162     }
3163 
addHelpTopic(std::istream & s)3164     static void addHelpTopic(std::istream &s) {
3165         tString name, shortdesc, text;
3166         s >> name >> shortdesc;
3167         if(s.fail()) {
3168             if(tConfItemBase::printErrors) {
3169                 con << tOutput("$add_help_topic_usage");
3170             }
3171             return;
3172         }
3173         s >> text;
3174         GetHelpTopics()[name] = eHelpTopic(shortdesc, text);
3175         if(tConfItemBase::printChange) {
3176             con << tOutput("$add_help_topic_success", name);
3177         }
3178     }
3179 
removeHelpTopic(std::istream & s)3180     static void removeHelpTopic(std::istream &s) {
3181         tString name;
3182         s >> name;
3183         if(GetHelpTopics().erase(name)) {
3184             if(tConfItemBase::printChange) {
3185                 con << tOutput("$remove_help_topic_success", name);
3186             }
3187         } else {
3188             if(tConfItemBase::printErrors) {
3189                 con << tOutput("$remove_help_topic_notfound", name);
3190             }
3191         }
3192     }
3193 
3194 private:
3195 
listTopics(tColoredString & s,tString const & base,std::map<tString,eHelpTopic>::const_iterator begin,std::map<tString,eHelpTopic>::const_iterator const & end)3196     static void listTopics(tColoredString &s, tString const &base, std::map<tString, eHelpTopic>::const_iterator begin, std::map<tString, eHelpTopic>::const_iterator const &end) {
3197         bool printed_start = false;
3198         for(;begin != end; ++begin) {
3199             if(!begin->first.StartsWith(base)) {
3200                 continue;
3201             }
3202             // would be easier with std::string...
3203             if(begin->first.SubStr(base.Len() - 1).StrPos("_") >= 0) {
3204                 // don't print subcommands
3205                 continue;
3206             }
3207             if(!printed_start) {
3208                 printed_start = true;
3209                 s << tOutput("$help_topics_list_start");
3210             }
3211             s << tColoredString::ColorString(.5,.5,1.) << begin->first << tColoredString::ColorString(1.,1.,.5) << ": " << tOutput(&begin->second.m_shortdesc[0]) << "\n";
3212         }
3213     }
3214 
3215 public:
3216 
listTopics(tColoredString & s,tString const & base=tString ())3217     static void listTopics(tColoredString &s, tString const &base=tString()) {
3218         listTopics(s, base, GetHelpTopics().begin(), GetHelpTopics().end());
3219     }
3220 
printTopic(tColoredString & s,tString const & name)3221     static void printTopic(tColoredString &s, tString const &name) {
3222         std::map<tString, eHelpTopic>::const_iterator iter = GetHelpTopics().find(name);
3223         if(iter != GetHelpTopics().end()) {
3224             iter->second.write(s);
3225             listTopics(s, name + "_");
3226         } else {
3227             s << tOutput("$help_topic_not_found", name);
3228         }
3229     }
3230 };
3231 
se_makeDefaultHelpTopic(std::map<tString,eHelpTopic> & helpTopics,char const * topic)3232 static void se_makeDefaultHelpTopic(  std::map<tString, eHelpTopic> & helpTopics, char const *topic) {
3233     tString topicStr(topic);
3234     tString helpTopic(tString("$help_") + topicStr);
3235     helpTopics[topicStr] = eHelpTopic(helpTopic + "_shortdesc", helpTopic + "_text");
3236 }
3237 
3238 // singleton accessor implementation
FillHelpTopics()3239 static std::map<tString, eHelpTopic> & FillHelpTopics()
3240 {
3241     static std::map<tString, eHelpTopic> helpTopics;
3242 
3243     se_makeDefaultHelpTopic(helpTopics, "commands");
3244     se_makeDefaultHelpTopic(helpTopics, "commands_chat");
3245     se_makeDefaultHelpTopic(helpTopics, "commands_team");
3246 #ifdef KRAWALL_SERVER
3247     se_makeDefaultHelpTopic(helpTopics, "commands_auth");
3248     se_makeDefaultHelpTopic(helpTopics, "commands_auth_levels");
3249     se_makeDefaultHelpTopic(helpTopics, "commands_tourney");
3250 #else
3251     se_makeDefaultHelpTopic(helpTopics, "commands_ra");
3252 #endif
3253     se_makeDefaultHelpTopic(helpTopics, "commands_misc");
3254     se_makeDefaultHelpTopic(helpTopics, "commands_pp");
3255 
3256     return helpTopics;
3257 }
3258 
3259 // singleton accessor implementation
GetHelpTopics()3260 std::map<tString, eHelpTopic> & eHelpTopic::GetHelpTopics()
3261 {
3262     static std::map<tString, eHelpTopic> & helpTopics = FillHelpTopics();
3263     return helpTopics;
3264 }
3265 
3266 static tConfItemFunc add_help_topic_conf("ADD_HELP_TOPIC",&eHelpTopic::addHelpTopic);
3267 static tConfItemFunc remove_help_topic_conf("REMOVE_HELP_TOPIC",&eHelpTopic::removeHelpTopic);
3268 static tString se_helpIntroductoryBlurb;
3269 static tConfItemLine se_helpIntroductoryBlurbConf("HELP_INTRODUCTORY_BLURB",se_helpIntroductoryBlurb);
3270 
se_Help(ePlayerNetID * sender,ePlayerNetID * receiver,std::istream & s)3271 static void se_Help( ePlayerNetID * sender, ePlayerNetID * receiver, std::istream & s ) {
3272     std::ws(s);
3273     tColoredString reply;
3274     if(s.eof() || s.fail()) {
3275         if(se_helpIntroductoryBlurb.Len() > 1) {
3276             reply << se_helpIntroductoryBlurb << "\n\n";
3277         }
3278         eHelpTopic::listTopics(reply);
3279     } else {
3280         tString name;
3281         s >> name;
3282         eHelpTopic::printTopic(reply, name);
3283     }
3284     if ( sender == receiver )
3285     {
3286         // just send a console message, the player asked for help himself
3287         sn_ConsoleOut(reply, receiver->Owner());
3288     }
3289     else
3290     {
3291         // send help disguised as a chat message (with disabled spam limit)
3292         int spamMaxLenBack = se_SpamMaxLen;
3293         se_SpamMaxLen = 0;
3294         se_SendChatLine(sender, reply, reply, receiver->Owner());
3295         se_SpamMaxLen = spamMaxLenBack;
3296     }
3297 }
3298 
3299 static tAccessLevel se_rtfmAccessLevel = tAccessLevel_Moderator;
3300 static tSettingItem< tAccessLevel > se_rtfmAccessLevelConf( "ACCESS_LEVEL_RTFM", se_rtfmAccessLevel );
3301 static tAccessLevelSetter se_rtfmAccessLevelConfLevel( se_rtfmAccessLevelConf, tAccessLevel_Owner );
3302 
3303 #ifdef DEDICATED
se_Rtfm(tString const & command,ePlayerNetID * p,std::istream & s,eChatSpamTester & spam)3304 static void se_Rtfm( tString const &command, ePlayerNetID *p, std::istream &s, eChatSpamTester &spam ) {
3305 #ifdef KRAWALL_SERVER
3306     if ( p->GetAccessLevel() > se_rtfmAccessLevel ) {
3307         sn_ConsoleOut(tOutput("$access_level_rtfm_denied", tCurrentAccessLevel::GetName(se_rtfmAccessLevel), tCurrentAccessLevel::GetName(p->GetAccessLevel())), p->Owner());
3308         return;
3309     }
3310 #else
3311     if (!p->IsLoggedIn()) {
3312         sn_ConsoleOut(tOutput("$rtfm_denied"));
3313         return;
3314     }
3315 #endif
3316     if(IsSilencedWithWarning(p)) {
3317         return;
3318     }
3319     if(spam.Block()) {
3320         return;
3321     }
3322     ePlayerNetID *newbie = se_FindPlayerInChatCommand(p, command, s);
3323     if(newbie) {
3324         // somewhat hacky, but what the hack...
3325         tString str;
3326         str.ReadLine(s);
3327         std::istringstream s1(&str(0)), s2(&str(0));
3328         tColoredString name;
3329         name << *p << tColoredString::ColorString(1,1,1);
3330         tColoredString newbie_name;
3331         newbie_name << *newbie << tColoredString::ColorString(1,1,1);
3332 
3333         tString announcement( tOutput("$rtfm_announcement", str, name, newbie_name) );
3334         se_BroadcastChatLine( p, announcement, announcement );
3335         con << announcement;
3336 
3337         se_Help(p, newbie, s1);
3338 
3339         // avoid sending you the same doc twice if you tell yourself to rtfm :)
3340         if ( newbie != p )
3341         {
3342             se_Help(p, p, s2);
3343         }
3344     }
3345 }
3346 #endif
3347 
3348 #if defined(DEDICATED) && defined(KRAWALL_SERVER)
3349 void se_ListAdmins( ePlayerNetID *, std::istream &s, tString command );
3350 #endif
3351 
handle_chat(nMessage & m)3352 void handle_chat( nMessage &m )
3353 {
3354     nTimeRolling currentTime = tSysTimeFloat();
3355     unsigned short id;
3356     m.Read(id);
3357     tColoredString say;
3358     m >> say;
3359 
3360     tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
3361 
3362     // register player activity
3363     if ( p )
3364         p->lastActivity_ = currentTime;
3365 
3366     if(sn_GetNetState()==nSERVER){
3367         if (p)
3368         {
3369             // for the duration of this function, set the access level to the level of the user.
3370             tCurrentAccessLevel levelOverride( p->GetAccessLevel(), true );
3371 
3372             // check if the player is owned by the sender to avoid cheating
3373             if( p->Owner() != m.SenderID() )
3374             {
3375                 Cheater( m.SenderID() );
3376                 nReadError();
3377                 return;
3378             }
3379 
3380             eChatSpamTester spam( p, say );
3381 
3382             if (say.StartsWith("/")) {
3383                 std::string sayStr(say);
3384                 std::istringstream s(sayStr);
3385 
3386                 tString command;
3387                 s >> command;
3388 
3389                 // filter to lowercase
3390                 tToLower( command );
3391 
3392                 tConfItemBase::EatWhitespace(s);
3393 
3394                 // now, s is ready for reading the rest of the message.
3395 #ifdef DEDICATED
3396                 if (se_InterceptCommands.StrPos(command) != -1)
3397                 {
3398                     handle_command_intercept(p, command, s, say);
3399                     return;
3400                 }
3401                 else
3402 #endif
3403                     if (command == "/me") {
3404                         spam.lastSaidType_ = eChatMessageType_Me;
3405                         spam.say_ = spam.say_.SubStr(4); // cut /me prefix
3406                         se_ChatMe( p, s, spam );
3407                         return;
3408                     }
3409                     else if (command == "/teamleave") {
3410                         spam.lastSaidType_ = eChatMessageType_Command;
3411                         se_ChatTeamLeave( p );
3412                         return;
3413                     }
3414                     else if (command == "/teamshuffle" || command == "/shuffle") {
3415                         spam.lastSaidType_ = eChatMessageType_Command;
3416                         se_ChatShuffle( p, s );
3417                         return;
3418                     }
3419                     else if (command == "/team")
3420                     {
3421                         spam.lastSaidType_ = eChatMessageType_Team;
3422                         se_ChatTeam( p, s, spam );
3423                         return;
3424                     }
3425                     else if (command == "/msg" ) {
3426                         spam.lastSaidType_ = eChatMessageType_Private;
3427                         se_ChatMsg( p, s, spam );
3428                         return;
3429                     }
3430                     else if (command == "/players" || command == "/listplayers") {
3431                         spam.lastSaidType_= eChatMessageType_Command;
3432                         se_ChatPlayers( p, s, command );
3433                         return;
3434                     }
3435 #if defined(DEDICATED) && defined(KRAWALL_SERVER)
3436                     else if (command == "/admins" || command == "/listadmins") {
3437                         spam.lastSaidType_ = eChatMessageType_Command;
3438                         se_ListAdmins( p, s, command );
3439                         return;
3440                     }
3441 #endif
3442                     else if (command == "/vote" || command == "/callvote") {
3443                         spam.lastSaidType_ = eChatMessageType_Command;
3444                         eVoter::HandleChat( p, s );
3445                         return;
3446                     }
3447                     else if (command == "/teams") {
3448                         spam.lastSaidType_ = eChatMessageType_Command;
3449                         se_ChatTeams( p );
3450                         return;
3451                     }
3452                     else if (command == "/myteam") {
3453                         spam.lastSaidType_ = eChatMessageType_Command;
3454                         eTeam *currentTeam = se_GetManagedTeam( p );
3455                         if( currentTeam )
3456                         {
3457                             se_ListTeam( p, currentTeam );
3458                         }
3459                         return;
3460                     }
3461                     else if (command == "/help") {
3462                         spam.lastSaidType_ = eChatMessageType_Command;
3463                         se_Help( p, p, s );
3464                         return;
3465                     }
3466 #ifdef DEDICATED
3467                     else  if ( command == "/rtfm" || command == "/teach" )
3468                     {
3469                         spam.lastSaidType_ = eChatMessageType_Command;
3470                         se_Rtfm( command, p, s, spam );
3471                         return;
3472                     }
3473                     else {
3474                         spam.lastSaidType_ = eChatMessageType_Command;
3475                         handle_chat_admin_commands( p, command, say, s, spam );
3476                         return;
3477                     }
3478 #endif
3479             }
3480 
3481             // check for spam
3482             if ( spam.Block() )
3483             {
3484                 return;
3485             }
3486 
3487             // well, that leaves only regular, boring chat.
3488             if ( say.Len() <= se_SpamMaxLen+2 && !IsSilencedWithWarning(p) )
3489             {
3490                 se_BroadcastChat( p, say );
3491                 se_DisplayChatLocally( p, say);
3492 
3493                 tString s;
3494                 s << p->GetUserName() << ' ' << say;
3495                 se_SaveToChatLog(s);
3496             }
3497         }
3498     }
3499     else
3500     {
3501         se_DisplayChatLocally( p, say );
3502     }
3503 }
3504 
3505 // check if a string is a legal player name
3506 // a name is only legal if it contains at least one non-witespace character.
IsLegalPlayerName(tString const & name)3507 static bool IsLegalPlayerName( tString const & name )
3508 {
3509     // strip colors
3510     tString stripped( tColoredString::RemoveColors( name ) );
3511 
3512     // and count non-whitespace characters
3513     for ( int i = stripped.Len()-2; i>=0; --i )
3514     {
3515         if ( !isblank( stripped(i) ) )
3516             return true;
3517     }
3518 
3519     return false;
3520 }
3521 
Chat(const tString & s_orig)3522 void ePlayerNetID::Chat(const tString &s_orig)
3523 {
3524     tColoredString s( s_orig );
3525     s.NetFilter();
3526 
3527 #ifndef DEDICATED
3528     // check for direct console commands
3529     if( s_orig.StartsWith("/console") )
3530     {
3531         // direct commands are executed at owner level
3532         tCurrentAccessLevel level( tAccessLevel_Owner, true );
3533 
3534         tString params("");
3535         if (s_orig.StrPos(" ") == -1)
3536             return;
3537         else
3538             params = s_orig.SubStr(s_orig.StrPos(" ") + 1);
3539 
3540         if ( tRecorder::IsPlayingBack() )
3541         {
3542             tConfItemBase::LoadPlayback();
3543         }
3544         else
3545         {
3546             std::stringstream s(static_cast< char const * >( params ) );
3547             tConfItemBase::LoadAll(s);
3548         }
3549     }
3550     else
3551 #endif
3552     {
3553         switch (sn_GetNetState())
3554         {
3555         case nCLIENT:
3556         {
3557             se_NewChatMessage( this, s )->BroadCast();
3558             break;
3559         }
3560         case nSERVER:
3561         {
3562             se_BroadcastChat( this, s );
3563 
3564             // falling through on purpose
3565             // break;
3566         }
3567         default:
3568         {
3569             se_DisplayChatLocally( this, s );
3570             break;
3571         }
3572         }
3573     }
3574 }
3575 
3576 //return the playernetid for a given name
3577 /*
3578 static ePlayerNetID* identifyPlayer(tString inname)
3579 {
3580     for(int i=0; i<se_PlayerNetIDs.Len(); i++)
3581     {
3582         ePlayerNetID *p = se_PlayerNetIDs[i];
3583 
3584         if( inname == p->GetUserName() )
3585             return p;
3586     }
3587     return NULL;
3588 }
3589 */
3590 
3591 // identify a local player
se_GetLocalPlayer()3592 static ePlayerNetID* se_GetLocalPlayer()
3593 {
3594     for(int i=0; i<se_PlayerNetIDs.Len(); i++)
3595     {
3596         ePlayerNetID *p = se_PlayerNetIDs[i];
3597 
3598         if( p->Owner() == sn_myNetID )
3599             return p;
3600     }
3601     return NULL;
3602 }
3603 
ConsoleSay_conf(std::istream & s)3604 static void ConsoleSay_conf(std::istream &s)
3605 {
3606     // read the message
3607     tString message;
3608     message.ReadLine( s, true );
3609 
3610     switch (sn_GetNetState())
3611     {
3612     case nCLIENT:
3613         {
3614             ePlayerNetID *me = se_GetLocalPlayer();
3615 
3616             if (me)
3617                 me->Chat( message );
3618 
3619             break;
3620         }
3621     case nSERVER:
3622         {
3623             tColoredString send;
3624             send << tColoredString::ColorString( 1,0,0 );
3625             send << "Admin";
3626             send << tColoredString::ColorString( 1,1,.5 );
3627             send << ": " << message << "\n";
3628 
3629             // display it
3630             sn_ConsoleOut( send );
3631 
3632             break;
3633         }
3634     default:
3635         {
3636             ePlayerNetID *me = se_GetLocalPlayer();
3637 
3638             if ( me )
3639                 se_DisplayChatLocally( me, message );
3640 
3641             break;
3642         }
3643     }
3644 }
3645 
3646 static tConfItemFunc ConsoleSay_c("SAY",&ConsoleSay_conf);
3647 
3648 struct eChatInsertionCommand
3649 {
3650     tString insertion_;
3651 
eChatInsertionCommandeChatInsertionCommand3652     eChatInsertionCommand( tString const & insertion )
3653             : insertion_( insertion )
3654     {}
3655 };
3656 
3657 
3658 static std::deque<tString> se_chatHistory; // global since the class doesn't live beyond the execution of the command
3659 static int se_chatHistoryMaxSize=10; // maximal size of chat history
3660 static tSettingItem< int > se_chatHistoryMaxSizeConf("HISTORY_SIZE_CHAT",se_chatHistoryMaxSize);
3661 
3662 class eMenuItemChat : uMenuItemStringWithHistory{
3663     ePlayer *me;
3664 public:
eMenuItemChat(uMenu * M,tString & c,ePlayer * Me)3665     eMenuItemChat(uMenu *M,tString &c,ePlayer *Me):
3666     uMenuItemStringWithHistory(M,"$chat_title_text","",c, se_SpamMaxLen, se_chatHistory, se_chatHistoryMaxSize),me(Me) {}
3667 
3668 
~eMenuItemChat()3669     virtual ~eMenuItemChat(){ }
3670 
3671     //virtual void Render(REAL x,REAL y,REAL alpha=1,bool selected=0);
3672 
Event(SDL_Event & e)3673     virtual bool Event(SDL_Event &e){
3674 #ifndef DEDICATED
3675         if (e.type==SDL_KEYDOWN &&
3676                 (e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
3677 
3678             for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
3679                 if (se_PlayerNetIDs(i)->pID==me->ID())
3680                     se_PlayerNetIDs(i)->Chat(*content);
3681 
3682             MyMenu()->Exit();
3683             return true;
3684         }
3685         else if (e.type==SDL_KEYDOWN &&
3686                  uActionGlobal::IsBreakingGlobalBind(e.key.keysym.sym))
3687             return su_HandleEvent(e, true);
3688         else
3689         {
3690             if ( uMenuItemStringWithHistory::Event(e) )
3691             {
3692                 return true;
3693             }
3694             // exclude modifier keys from possible control triggers
3695             else if ( e.key.keysym.sym < SDLK_NUMLOCK || e.key.keysym.sym > SDLK_COMPOSE )
3696             {
3697                 // maybe it's an instant chat button?
3698                 try
3699                 {
3700                     return su_HandleEvent(e, false);
3701                 }
3702                 catch ( eChatInsertionCommand & insertion )
3703                 {
3704                     if ( content->Len() + insertion.insertion_.Len() <= maxLength_ )
3705                     {
3706                         *content = content->SubStr( 0, cursorPos ) + insertion.insertion_ + content->SubStr( cursorPos );
3707                         cursorPos += insertion.insertion_.Len()-1;
3708                     }
3709 
3710                     return true;
3711                 }
3712             }
3713         }
3714 #endif // DEDICATED
3715 
3716         return false;
3717     }
3718 };
3719 
3720 
se_ChatState(ePlayerNetID::ChatFlags flag,bool cs)3721 void se_ChatState(ePlayerNetID::ChatFlags flag, bool cs){
3722     for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
3723     {
3724         ePlayerNetID *p = se_PlayerNetIDs[i];
3725         if (p->Owner()==sn_myNetID && p->pID >= 0){
3726             p->SetChatting( flag, cs );
3727         }
3728     }
3729 }
3730 
3731 static ePlayer * se_chatterPlanned=NULL;
3732 static ePlayer * se_chatter =NULL;
3733 static tString se_say;
do_chat()3734 static void do_chat(){
3735     if (se_chatterPlanned){
3736         su_ClearKeys();
3737 
3738         se_chatter=se_chatterPlanned;
3739         se_chatterPlanned=NULL;
3740         se_ChatState( ePlayerNetID::ChatFlags_Chat, true);
3741 
3742         sr_con.SetHeight(15,false);
3743         se_SetShowScoresAuto(false);
3744 
3745         uMenu chat_menu("",false);
3746         eMenuItemChat s(&chat_menu,se_say,se_chatter);
3747         chat_menu.SetCenter(-.75);
3748         chat_menu.SetBot(-2);
3749         chat_menu.SetTop(-.7);
3750         chat_menu.Enter();
3751 
3752         se_ChatState( ePlayerNetID::ChatFlags_Chat, false );
3753 
3754         sr_con.SetHeight(7,false);
3755         se_SetShowScoresAuto(true);
3756     }
3757     se_chatter=NULL;
3758     se_chatterPlanned=NULL;
3759 }
3760 
chat(ePlayer * chatter,tString const & say)3761 static void chat( ePlayer * chatter, tString const & say )
3762 {
3763     se_chatterPlanned = chatter;
3764     se_say = say;
3765     st_ToDo(&do_chat);
3766 }
3767 
chat(ePlayer * chatter)3768 static void chat( ePlayer * chatter )
3769 {
3770     chat( chatter, tString() );
3771 }
3772 
3773 static bool se_allowControlDuringChat = false;
3774 static nSettingItem<bool> se_allowControlDuringChatConf("ALLOW_CONTROL_DURING_CHAT",se_allowControlDuringChat);
3775 
3776 uActionPlayer se_toggleSpectator("TOGGLE_SPECTATOR", 0);
3777 
Act(uAction * act,REAL x)3778 bool ePlayer::Act(uAction *act,REAL x){
3779     eGameObject *object=NULL;
3780 
3781     if ( act == &se_toggleSpectator && x > 0 )
3782     {
3783         spectate = !spectate;
3784         con << tOutput(spectate ? "$player_toggle_spectator_on" : "$player_toggle_spectator_off", name );
3785         if ( !spectate )
3786         {
3787             ePlayerNetID::Update();
3788         }
3789         return true;
3790     }
3791     else if (!se_chatter && s_chat==*reinterpret_cast<uActionPlayer *>(act)){
3792         if(x>0) {
3793             chat( this );
3794         }
3795         return true;
3796     }
3797     else{
3798         if ( x > 0 )
3799         {
3800             int i;
3801             uActionPlayer* pact = reinterpret_cast<uActionPlayer *>(act);
3802             for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
3803                 uActionPlayer* pcompare = se_instantChatAction[i];
3804                 if (pact == pcompare && x>=0){
3805                     for(int j=se_PlayerNetIDs.Len()-1;j>=0;j--)
3806                         if (se_PlayerNetIDs(j)->pID==ID())
3807                         {
3808                             tString say = instantChatString[i];
3809                             bool sendImmediately = true;
3810                             if ( say.Len() > 2 && say[say.Len()-2] == '\\' )
3811                             {
3812                                 // cut away trailing slash and note for later
3813                                 // that the message should not be sent immediately.
3814                                 say = say.SubStr( 0, say.Len()-2 );
3815                                 sendImmediately = false;
3816                             }
3817 
3818                             if ( se_chatter == this )
3819                             {
3820                                 // a chat is already active, insert the chat string
3821                                 throw eChatInsertionCommand( say );
3822                             }
3823                             else
3824                             {
3825                                 if ( sendImmediately )
3826                                 {
3827                                     // fire out chat string immediately
3828                                     se_PlayerNetIDs(j)->Chat(say);
3829                                 }
3830                                 else
3831                                 {
3832                                     // chat with instant chat string as template
3833                                     chat( this, say );
3834                                 }
3835                             }
3836                             return true;
3837                         }
3838                 }
3839             }
3840         }
3841 
3842         // no other command should get through during chat
3843         if ( se_chatter && !se_allowControlDuringChat )
3844             return false;
3845 
3846         int i;
3847         for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
3848             if (se_PlayerNetIDs[i]->pID==id && se_PlayerNetIDs[i]->object)
3849                 object=se_PlayerNetIDs[i]->object;
3850 
3851         bool objectAct = false; // set to true if an action of the object was triggered
3852         bool ret = ((cam    && cam->Act(reinterpret_cast<uActionCamera *>(act),x)) ||
3853                     ( objectAct = (object && se_GameTime()>=0 && object->Act(reinterpret_cast<uActionPlayer *>(act),x))));
3854 
3855         if (bool(netPlayer) && (objectAct || !se_chatter))
3856             netPlayer->Activity();
3857 
3858         return ret;
3859     }
3860 
3861 }
3862 
PlayerViewport(int p)3863 rViewport * ePlayer::PlayerViewport(int p){
3864     if (!PlayerConfig(p)) return NULL;
3865 
3866     for (int i=rViewportConfiguration::CurrentViewportConfiguration()->num_viewports-1;i>=0;i--)
3867         if (sr_viewportBelongsToPlayer[i] == p)
3868             return rViewportConfiguration::CurrentViewport(i);
3869 
3870     return NULL;
3871 }
3872 
PlayerIsInGame(int p)3873 bool ePlayer::PlayerIsInGame(int p){
3874     return PlayerViewport(p) && PlayerConfig(p);
3875 }
3876 
3877 static tConfItemBase *vpbtp[MAX_VIEWPORTS];
3878 
Init()3879 void ePlayer::Init(){
3880     se_Players = tNEW( ePlayer[MAX_PLAYERS] );
3881 
3882     int i;
3883     for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
3884         tString id;
3885         id << "INSTANT_CHAT_";
3886         id << i+1;
3887         tOutput desc;
3888         desc.SetTemplateParameter(1, i+1);
3889         desc << "$input_instant_chat_text";
3890 
3891         tOutput help;
3892         help.SetTemplateParameter(1, i+1);
3893         help << "$input_instant_chat_help";
3894         ePlayer::se_instantChatAction[i]=tNEW(uActionPlayer) (id, desc, help);
3895         //,desc,       "Issues a special instant chat macro.");
3896     }
3897 
3898 
3899     for(i=MAX_VIEWPORTS-1;i>=0;i--){
3900         tString id;
3901         id << "VIEWPORT_TO_PLAYER_";
3902         id << i+1;
3903         vpbtp[i] = tNEW(tConfItem<int>(id,"$viewport_belongs_help",
3904                                        s_newViewportBelongsToPlayer[i]));
3905         s_newViewportBelongsToPlayer[i]=i;
3906     }
3907 }
3908 
Exit()3909 void ePlayer::Exit(){
3910     int i;
3911     for(i=MAX_INSTANT_CHAT-1;i>=0;i--)
3912         tDESTROY(ePlayer::se_instantChatAction[i]);
3913 
3914     for(i=MAX_VIEWPORTS-1;i>=0;i--)
3915         tDESTROY(vpbtp[i]);
3916 
3917     delete[] se_Players;
3918     se_Players = NULL;
3919 }
3920 
3921 uActionPlayer ePlayer::s_chat("CHAT");
3922 
3923 int pingCharity = 100;
3924 static const int maxPingCharity = 300;
3925 
sg_ClampPingCharity(int & pingCharity)3926 static void sg_ClampPingCharity( int & pingCharity )
3927 {
3928     if (pingCharity < 0 )
3929         pingCharity = 0;
3930     if (pingCharity > maxPingCharity )
3931         pingCharity = maxPingCharity;
3932 }
3933 
sg_ClampPingCharity(unsigned short & pingCharity)3934 static void sg_ClampPingCharity( unsigned short & pingCharity )
3935 {
3936     if (pingCharity > maxPingCharity )
3937         pingCharity = maxPingCharity;
3938 }
3939 
sg_ClampPingCharity()3940 static void sg_ClampPingCharity()
3941 {
3942     sg_ClampPingCharity( ::pingCharity );
3943 }
3944 
3945 static int IMPOSSIBLY_LOW_SCORE=INT_MIN;
3946 
3947 static nSpamProtectionSettings se_chatSpamSettings( 1.0f, "SPAM_PROTECTION_CHAT", tOutput("$spam_protection") );
3948 
ePlayerNetID(int p)3949 ePlayerNetID::ePlayerNetID(int p):nNetObject(),listID(-1), teamListID(-1), timeCreated_( tSysTimeFloat() ), allowTeamChange_(false), registeredMachine_(0), pID(p),chatSpam_( se_chatSpamSettings ), lastSaid_()
3950 {
3951     // default access level
3952     lastAccessLevel = tAccessLevel_Default;
3953 
3954     favoriteNumberOfPlayersPerTeam = 1;
3955 
3956     nameTeamAfterMe = false;
3957 
3958     r = g = b = 15;
3959 
3960     greeted             = true;
3961     chatting_           = false;
3962     spectating_         = false;
3963     stealth_            = false;
3964     chatFlags_          = 0;
3965     disconnected        = false;
3966     suspended_          = 0;
3967 
3968     loginWanted = false;
3969 
3970 
3971     if (p>=0){
3972         ePlayer *P = ePlayer::PlayerConfig(p);
3973         if (P){
3974             SetName( P->Name() );
3975             r=   P->rgb[0];
3976             g=   P->rgb[1];
3977             b=   P->rgb[2];
3978 
3979             sg_ClampPingCharity();
3980             pingCharity=::pingCharity;
3981 
3982             loginWanted = P->autoLogin;
3983         }
3984     }
3985 
3986     se_PlayerNetIDs.Add(this,listID);
3987     object=NULL;
3988 
3989     /*
3990       if(sn_GetNetState()!=nSERVER)
3991       ping=sn_Connections[0].ping;
3992       else
3993     */
3994     ping=0; // hehe! server has no ping.
3995 
3996     lastSync=tSysTimeFloat();
3997 
3998     RequestSync();
3999     score=0;
4000     lastScore_=IMPOSSIBLY_LOW_SCORE;
4001     // rubberstatus=0;
4002 
4003     MyInitAfterCreation();
4004 
4005     if(sn_GetNetState()==nSERVER)
4006         RequestSync();
4007 }
4008 
4009 
4010 
4011 
ePlayerNetID(nMessage & m)4012 ePlayerNetID::ePlayerNetID(nMessage &m):nNetObject(m),listID(-1), teamListID(-1), timeCreated_( tSysTimeFloat() )
4013         , allowTeamChange_(false), registeredMachine_(0), chatSpam_( se_chatSpamSettings ), lastSaid_()
4014 {
4015     // default access level
4016     lastAccessLevel = tAccessLevel_Default;
4017 
4018     greeted     =false;
4019     chatting_   =false;
4020     spectating_ =false;
4021     stealth_    =false;
4022     disconnected=false;
4023     suspended_  = 0;
4024     chatFlags_  =0;
4025 
4026     r = g = b = 15;
4027 
4028     nameTeamAfterMe = false;
4029 
4030 
4031     pID=-1;
4032     se_PlayerNetIDs.Add(this,listID);
4033     object=NULL;
4034     ping=sn_Connections[m.SenderID()].ping;
4035     lastSync=tSysTimeFloat();
4036 
4037     loginWanted = false;
4038 
4039     score=0;
4040     lastScore_=IMPOSSIBLY_LOW_SCORE;
4041     // rubberstatus=0;
4042 }
4043 
Activity()4044 void ePlayerNetID::Activity()
4045 {
4046     // the player was active; therefore, he cannot possibly be chatting_ or disconnected.
4047 
4048     // but do nothing if we are in client mode and the player is not local to this computer
4049     if (sn_GetNetState() != nSERVER && Owner() != ::sn_myNetID)
4050         return;
4051 
4052     if (chatting_ || disconnected)
4053     {
4054 #ifdef DEBUG
4055         con << *this << " showed activity and lost chat status.\n";
4056 #endif
4057         RequestSync();
4058     }
4059 
4060     chatting_ = disconnected = false;
4061 
4062     // store time
4063     this->lastActivity_ = tSysTimeFloat();
4064 }
4065 
4066 static int se_maxPlayersPerIP = 4;
4067 static tSettingItem<int> se_maxPlayersPerIPConf( "MAX_PLAYERS_SAME_IP", se_maxPlayersPerIP );
4068 
4069 // array of players in legacy spectator mode: they have been
4070 // deleted by their clients, but no new player has popped up for them yet
4071 static tJUST_CONTROLLED_PTR< ePlayerNetID > se_legacySpectators[MAXCLIENTS+2];
4072 
se_ClearLegacySpectator(int owner)4073 static void se_ClearLegacySpectator( int owner )
4074 {
4075     ePlayerNetID * player = se_legacySpectators[ owner ];
4076 
4077     // fully remove player
4078     if ( player )
4079     {
4080         player->RemoveFromGame();
4081     }
4082 
4083     se_legacySpectators[ owner ] = NULL;
4084 }
4085 
4086 // callback clearing the legacy spectator when a client enters or leaves
se_LegacySpectatorClearer()4087 static void se_LegacySpectatorClearer()
4088 {
4089     se_ClearLegacySpectator( nCallbackLoginLogout::User() );
4090 }
4091 static nCallbackLoginLogout se_legacySpectatorClearer( se_LegacySpectatorClearer );
4092 
4093 
4094 
MyInitAfterCreation()4095 void ePlayerNetID::MyInitAfterCreation()
4096 {
4097     this->CreateVoter();
4098 
4099     this->silenced_ = se_silenceDefault;
4100     this->renameAllowed_ = true;
4101 
4102     // register with machine and kick user if too many players are present
4103     if ( Owner() != 0 && sn_GetNetState() == nSERVER )
4104     {
4105         this->RegisterWithMachine();
4106         if ( se_maxPlayersPerIP < nMachine::GetMachine(Owner()).GetPlayerCount() )
4107         {
4108             // kill them
4109             sn_DisconnectUser( Owner(), "$network_kill_too_many_players" );
4110 
4111             // technically has the same effect as the above function, but we also want
4112             // to abort registering this player object and this exception will do that as well.
4113             throw nKillHim();
4114         }
4115 
4116         // clear old legacy spectator that may be lurking around
4117         se_ClearLegacySpectator( Owner() );
4118 
4119         // get suspension count
4120         if ( GetVoter() )
4121         {
4122             suspended_ = GetVoter()->suspended_;
4123             silenced_ = GetVoter()->silenced_;
4124         }
4125     }
4126 
4127     this->wait_ = 0;
4128 
4129     this->lastActivity_ = tSysTimeFloat();
4130 }
4131 
InitAfterCreation()4132 void ePlayerNetID::InitAfterCreation()
4133 {
4134     MyInitAfterCreation();
4135 }
4136 
ClearToTransmit(int user) const4137 bool ePlayerNetID::ClearToTransmit(int user) const{
4138     return ( ( !nextTeam || nextTeam->HasBeenTransmitted( user ) ) &&
4139              ( !currentTeam || currentTeam->HasBeenTransmitted( user ) ) ) ;
4140 }
4141 
~ePlayerNetID()4142 ePlayerNetID::~ePlayerNetID()
4143 {
4144     // se_PlayerNetIDs.Remove(this,listID);
4145     if ( sn_GetNetState() == nSERVER && disconnected )
4146     {
4147         tOutput mess;
4148         tColoredString name;
4149         name << *this << tColoredString::ColorString(1,.5,.5);
4150         mess.SetTemplateParameter(1, name);
4151         mess.SetTemplateParameter(2, score);
4152         mess << "$player_left_game";
4153         sn_ConsoleOut(mess);
4154     }
4155 
4156     UnregisterWithMachine();
4157 
4158     RemoveFromGame();
4159 
4160     ClearObject();
4161     //con << "Player info sent.\n";
4162 
4163     for(int i=MAX_PLAYERS-1;i>=0;i--){
4164         ePlayer *p = ePlayer::PlayerConfig(i);
4165 
4166         if (p && static_cast<ePlayerNetID *>(p->netPlayer)==this)
4167             p->netPlayer=NULL;
4168     }
4169 
4170     if ( currentTeam )
4171     {
4172         currentTeam->RemovePlayer( this );
4173     }
4174 }
4175 
player_removed_from_game_handler(nMessage & m)4176 static void player_removed_from_game_handler(nMessage &m)
4177 {
4178     // and the ID of the player that was removed
4179     unsigned short id;
4180     m.Read(id);
4181     ePlayerNetID* p = dynamic_cast< ePlayerNetID* >( nNetObject::ObjectDangerous( id ) );
4182     if ( p && sn_GetNetState() != nSERVER )
4183     {
4184         p->RemoveFromGame();
4185     }
4186 }
4187 
4188 static nDescriptor player_removed_from_game(202,&player_removed_from_game_handler,"player_removed_from_game");
4189 
4190 static eLadderLogWriter se_playerLeftWriter("PLAYER_LEFT", true);
4191 
RemoveFromGame()4192 void ePlayerNetID::RemoveFromGame()
4193 {
4194     // unregister with the machine
4195     this->UnregisterWithMachine();
4196 
4197     // release voter
4198     if ( this->voter_ )
4199         this->voter_->RemoveFromGame();
4200     this->voter_ = 0;
4201 
4202     // log scores
4203     LogScoreDifference();
4204 
4205     if ( sn_GetNetState() != nCLIENT )
4206     {
4207         nameFromClient_ = nameFromServer_;
4208 
4209         nMessage *m=new nMessage(player_removed_from_game);
4210         m->Write(this->ID());
4211         m->BroadCast();
4212 
4213         if ( listID >= 0 ){
4214             if ( ( IsSpectating() || IsSuspended() || !se_assignTeamAutomatically ) && ( se_assignTeamAutomatically || se_specSpam ) && CurrentTeam() == NULL )
4215             {
4216                 // get colored player name
4217                 tColoredString playerName;
4218                 playerName << *this << tColoredString::ColorString(1,.5,.5);
4219 
4220                 // announce a generic leave
4221                 sn_ConsoleOut( tOutput( "$player_left_spectator", playerName ) );
4222             }
4223 
4224             if ( IsHuman() && sn_GetNetState() == nSERVER && NULL != sn_Connections[Owner()].socket )
4225             {
4226                 tString ladder;
4227                 se_playerLeftWriter << userName_ << nMachine::GetMachine(Owner()).GetIP();
4228                 se_playerLeftWriter.write();
4229             }
4230         }
4231     }
4232 
4233     se_PlayerNetIDs.Remove(this, listID);
4234     SetTeamWish( NULL );
4235     SetTeam( NULL );
4236     UpdateTeam();
4237     ControlObject( NULL );
4238     // currentTeam = NULL;
4239 }
4240 
ActionOnQuit()4241 bool ePlayerNetID::ActionOnQuit()
4242 {
4243     tControlledPTR< ePlayerNetID > holder( this );
4244 
4245     // clear legacy spectator
4246     se_ClearLegacySpectator( Owner() );
4247 
4248     this->RemoveFromGame();
4249     return true;
4250 }
4251 
ActionOnDelete()4252 void ePlayerNetID::ActionOnDelete()
4253 {
4254     tControlledPTR< ePlayerNetID > holder( this );
4255 
4256     if ( sn_GetNetState() == nSERVER )
4257     {
4258         // get the number of players registered from this client
4259         // int playerCount = nMachine::GetMachine(Owner()).GetPlayerCount();
4260         int playerCount = 0;
4261         for ( int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
4262         {
4263             if ( se_PlayerNetIDs[i]->Owner() == Owner() )
4264                 playerCount++;
4265         }
4266 
4267         // if this is the last player, store it
4268         if ( playerCount == 1 )
4269         {
4270             // log scores
4271             LogScoreDifference();
4272 
4273             // clear legacy spectator
4274             se_ClearLegacySpectator( Owner() );
4275 
4276             // store new legacy spectator
4277             se_legacySpectators[ Owner() ] = this;
4278             spectating_ = true;
4279 
4280             // quit team already
4281             SetTeamWish( NULL );
4282             SetTeam( NULL );
4283             UpdateTeam();
4284 
4285             // and kill controlled object
4286             ControlObject( NULL );
4287 
4288             // inform other clients that the player left
4289             TakeOwnership();
4290             RequestSync();
4291 
4292             return;
4293         }
4294     }
4295 
4296     // standard behavior: just get us out of here
4297     this->RemoveFromGame();
4298 }
4299 
PrintName(tString & s) const4300 void ePlayerNetID::PrintName(tString &s) const
4301 {
4302     s << "ePlayerNetID nr. " << ID() << ", name " << GetName();
4303 }
4304 
4305 
AcceptClientSync() const4306 bool ePlayerNetID::AcceptClientSync() const{
4307     return AcceptClientSyncStatic();
4308 }
AcceptClientSyncStatic()4309 bool ePlayerNetID::AcceptClientSyncStatic(){
4310     return true;
4311 }
4312 
4313 #ifdef KRAWALL_SERVER
4314 
se_GetAuthorityFromGid(const tString gid)4315 tString se_GetAuthorityFromGid ( const tString gid )
4316 {
4317     int pos;
4318     if ( ( pos = gid.StrPos( "@" ) ) != -1 )
4319     {
4320         return gid.SubStr( pos +1 );
4321     }
4322     return gid;
4323 }
4324 
4325 // changes various user aspects
4326 template< class P > class eUserConfig: public tConfItemBase
4327 {
4328 public:
4329     typedef typename std::map< tString, P > Properties;
Get(tString const & name) const4330     P Get(tString const & name ) const
4331     {
4332         typename Properties::const_iterator iter = properties.find( name );
4333         if ( iter != properties.end() )
4334         {
4335             return (*iter).second;
4336         }
4337 
4338         return GetDefault();
4339     }
GetMap() const4340     Properties GetMap () const
4341     {
4342         return properties;
4343     }
4344 protected:
eUserConfig(char const * name)4345    eUserConfig( char const * name )
4346     : tConfItemBase( name )
4347     {
4348         requiredLevel = tAccessLevel_Owner;
4349     }
4350 
4351     virtual P ReadRawVal(tString const & name, std::istream &s) const = 0;
4352     virtual P GetDefault() const = 0;
TransformName(tString & name) const4353     virtual void TransformName( tString & name ) const {}
4354 
ReadVal(std::istream & s)4355     virtual void ReadVal(std::istream &s)
4356     {
4357         tString name;
4358         s >> name;
4359 
4360         if ( name == "" )
4361         {
4362             tString usageKey("$");
4363             usageKey += GetTitle();
4364             usageKey += "_usage";
4365             tToLower( usageKey );
4366             con << tOutput( (const char *)usageKey );
4367             return;
4368         }
4369 
4370         TransformName( name );
4371 
4372         P value = ReadRawVal(name, s);
4373 
4374         properties[name] = value;
4375     }
4376 
4377     Properties properties;
4378 private:
4379 
WriteVal(std::ostream & s)4380     virtual void WriteVal(std::ostream &s)
4381     {
4382         tASSERT(0);
4383     }
4384 
Writable()4385     virtual bool Writable(){
4386         return false;
4387     }
4388 
Save()4389     virtual bool Save(){
4390         return false;
4391     }
4392 };
4393 
4394 // changes the access level of a player
4395 class eUserLevel: public eUserConfig< tAccessLevel >
4396 {
4397 public:
eUserLevel()4398     eUserLevel()
4399     : eUserConfig< tAccessLevel >( "USER_LEVEL" )
4400     {
4401         requiredLevel = tAccessLevel_Owner;
4402     }
4403 
GetDefault() const4404     tAccessLevel GetDefault() const
4405     {
4406         return tAccessLevel_DefaultAuthenticated;
4407     }
4408 
ReadRawVal(tString const & name,std::istream & s) const4409     virtual tAccessLevel ReadRawVal(tString const & name, std::istream &s) const
4410     {
4411         int levelInt;
4412         s >> levelInt;
4413         tAccessLevel level = static_cast< tAccessLevel >( levelInt );
4414 
4415         if ( s.fail() )
4416         {
4417             if(printErrors)
4418             {
4419                 con << tOutput( "$user_level_usage" );
4420             }
4421             return GetDefault();
4422         }
4423 
4424         if(printChange)
4425         {
4426             con << tOutput( "$user_level_change", name, tCurrentAccessLevel::GetName( level ) );
4427         }
4428 
4429         return level;
4430     }
4431 
TransformName(tString & name) const4432     virtual void TransformName( tString & name ) const
4433     {
4434         name = se_EscapeName( name ).c_str();
4435     }
4436 };
4437 
4438 static eUserLevel se_userLevel;
4439 
4440 class eAuthorityLevel: public eUserConfig< tAccessLevel >
4441 {
4442 public:
eAuthorityLevel()4443     eAuthorityLevel()
4444     : eUserConfig< tAccessLevel >( "AUTHORITY_LEVEL" )
4445     {
4446         requiredLevel = tAccessLevel_Owner;
4447     }
4448 
GetDefault() const4449     tAccessLevel GetDefault() const
4450     {
4451         return tAccessLevel_DefaultAuthenticated;
4452     }
4453 
ReadRawVal(tString const & name,std::istream & s) const4454     virtual tAccessLevel ReadRawVal(tString const & name, std::istream &s) const
4455     {
4456         int levelInt;
4457         s >> levelInt;
4458         tAccessLevel level = static_cast< tAccessLevel >( levelInt );
4459 
4460         if ( s.fail() || name.StrPos( "@" ) != -1 )
4461         {
4462             if(printErrors)
4463             {
4464                 con << tOutput( "$authority_level_usage" );
4465             }
4466             return GetDefault();
4467         }
4468 
4469         if(printChange)
4470         {
4471             con << tOutput( "$authority_level_change", name, tCurrentAccessLevel::GetName( level ) );
4472         }
4473 
4474         return level;
4475     }
4476 };
4477 
4478 static eAuthorityLevel se_authorityLevel;
4479 
4480 #ifdef KRAWALL_SERVER
4481 
4482 static tAccessLevel se_adminListMinAccessLevel = tAccessLevel_Moderator;
4483 static tSettingItem< tAccessLevel > se_adminListMinAccessLevelConf( "ADMIN_LIST_MIN_ACCESS_LEVEL", se_adminListMinAccessLevel );
4484 static tAccessLevelSetter se_adminListMinAccessLevelConfLevel( se_adminListMinAccessLevelConf, tAccessLevel_Owner );
4485 
4486 static tAccessLevel se_accessLevelListAdmins = tAccessLevel_Moderator; /* This command is sensible, especially if admin rights are set to an
4487                                                                         * account that can still be registered at.
4488                                                                         */
4489 static tSettingItem< tAccessLevel > se_accessLevelListAdminsConf( "ACCESS_LEVEL_LIST_ADMINS", se_accessLevelListAdmins );
4490 static tAccessLevelSetter se_accessLevelListAdminsConfLevel( se_accessLevelListAdminsConf, tAccessLevel_Owner );
4491 
4492 static tAccessLevel se_accessLevelListAdminsSeeEveryone = tAccessLevel_Moderator;
4493 static tSettingItem< tAccessLevel > se_accessLevelListAdminsSeeEveryoneConf( "ACCESS_LEVEL_LIST_ADMINS_SEE_EVERYONE", se_accessLevelListAdminsSeeEveryone );
4494 static tAccessLevelSetter se_accessLevelListAdminsSeeEveryoneConfLevel( se_accessLevelListAdminsSeeEveryoneConf, tAccessLevel_Owner );
4495 
4496 static int se_adminListColors_BestRed = 15;
4497 static tSettingItem< int > se_adminListColors_BetterRed_Conf( "ADMIN_LIST_COLORS_BEST_RED", se_adminListColors_BestRed );
4498 
4499 static int se_adminListColors_WorstRed = 15;
4500 static tSettingItem< int > se_adminListColors_WorstRed_Conf( "ADMIN_LIST_COLORS_WORST_RED", se_adminListColors_WorstRed );
4501 
4502 static int se_adminListColors_BestGreen = 0;
4503 static tSettingItem< int > se_adminListColors_BetterGreen_Conf( "ADMIN_LIST_COLORS_BEST_GREEN", se_adminListColors_BestGreen );
4504 
4505 static int se_adminListColors_WorstGreen = 15;
4506 static tSettingItem< int > se_adminListColors_WorstGreen_Conf( "ADMIN_LIST_COLORS_WORST_GREEN", se_adminListColors_WorstGreen );
4507 
4508 static int se_adminListColors_BestBlue = 0;
4509 static tSettingItem< int > se_adminListColors_BetterBlue_Conf( "ADMIN_LIST_COLORS_BEST_BLUE", se_adminListColors_BestBlue );
4510 
4511 static int se_adminListColors_WorstBlue = 7;
4512 static tSettingItem< int > se_adminListColors_WorstBlue_Conf( "ADMIN_LIST_COLORS_WORST_BLUE", se_adminListColors_WorstBlue );
4513 
se_ListAdmins(ePlayerNetID * receiver,std::istream & s,tString command)4514 void se_ListAdmins ( ePlayerNetID * receiver, std::istream &s, tString command )
4515 {
4516     // What's going to be sent ? But wait..are we sending anything at all?
4517     if ( receiver != 0 && receiver->GetAccessLevel() > se_accessLevelListAdmins )
4518     {
4519         sn_ConsoleOut( tOutput("$chat_command_accesslevel", command,
4520                                tCurrentAccessLevel::GetName( receiver->GetAccessLevel() ),
4521                                tCurrentAccessLevel::GetName( se_accessLevelListAdmins ) ),
4522                        receiver->Owner() );
4523         return;
4524     }
4525 
4526     bool canSeeEverything = false;
4527     if ( receiver == 0 || receiver->GetAccessLevel() <= se_accessLevelListAdminsSeeEveryone )
4528     {
4529         canSeeEverything = true;
4530     }
4531 
4532     int firstNumber, secondNumber;
4533     bool gotFirstNumber = true, gotSecondNumber = true;
4534     s >> firstNumber;
4535     if ( s.fail() )
4536     {
4537         gotFirstNumber = false;
4538         gotSecondNumber = false;
4539     }
4540     else
4541     {
4542         s >> secondNumber;
4543         if ( s.fail() )
4544         {
4545             gotSecondNumber = false;
4546         }
4547     }
4548 
4549     int userCount = 0, authorityCount = 0;
4550 
4551     std::set< tAccessLevel > accessLevelsToList;
4552 
4553     if ( gotFirstNumber && !gotSecondNumber )
4554     {
4555         if ( canSeeEverything || firstNumber <= se_adminListMinAccessLevel )
4556         {
4557             accessLevelsToList.insert( static_cast<tAccessLevel>( firstNumber ) );
4558         }
4559         else
4560         {
4561             sn_ConsoleOut( tOutput( "$admin_list_cant_see", command ), receiver->Owner() );
4562             return;
4563         }
4564     }
4565     else if ( ( gotFirstNumber && gotSecondNumber ) || ( !gotFirstNumber && !gotSecondNumber ) )
4566     {
4567         int lowest, highest;
4568         if ( !gotFirstNumber && !gotSecondNumber )
4569         {
4570             lowest = 0;
4571             highest = se_adminListMinAccessLevel;
4572         }
4573         else
4574         {
4575             if ( secondNumber > firstNumber )
4576             {
4577                 lowest = firstNumber;
4578                 highest = secondNumber;
4579             }
4580             else
4581             {
4582                 lowest = secondNumber;
4583                 highest = firstNumber;
4584             }
4585             if ( !canSeeEverything && highest > se_adminListMinAccessLevel )
4586             {
4587                 sn_ConsoleOut( tOutput("$admin_list_cant_see"), receiver->Owner() );
4588                 return;
4589             }
4590         }
4591 
4592         for ( int al = lowest; al <= highest; ++al )
4593         {
4594             accessLevelsToList.insert( static_cast<tAccessLevel>(al) );
4595         }
4596     }
4597 
4598     // Now browse trough all users in se_userLevel and sort them by access level in adminLevelsMap, then output it
4599     typedef std::map< tString, tString > aSetOfAdmins;
4600     typedef std::map< tAccessLevel, aSetOfAdmins > AdminLevelsMap;
4601 
4602     AdminLevelsMap adminLevelsMap;
4603     tString user;
4604     tAccessLevel accessLevel;
4605     AdminLevelsMap::iterator usersAccessLevelInSet;
4606 
4607     eUserLevel::Properties gidMap = se_userLevel.GetMap();
4608 
4609     for ( eUserLevel::Properties::iterator iter = gidMap.begin(); iter != gidMap.end(); ++iter )
4610     {
4611         tColoredString userinfo, lowerUser;
4612 
4613         user = (*iter).first;
4614         accessLevel = (*iter).second;
4615         if ( accessLevelsToList.find( accessLevel ) != accessLevelsToList.end() )
4616         {
4617             // Prepare a string with the info about that user
4618 //            userinfo << "  " << user;
4619 
4620             usersAccessLevelInSet = adminLevelsMap.find( accessLevel );
4621 
4622             if ( usersAccessLevelInSet == adminLevelsMap.end() )
4623             {
4624 
4625                 adminLevelsMap[ accessLevel ] = aSetOfAdmins();
4626 
4627                 usersAccessLevelInSet = adminLevelsMap.find( accessLevel );
4628             }
4629 
4630             aSetOfAdmins & theRightSet = (*usersAccessLevelInSet).second;
4631 
4632             lowerUser = user;
4633             tToLower( lowerUser );
4634             theRightSet[ lowerUser ] = user;
4635 
4636             userCount++;
4637         }
4638     }
4639 
4640     // Do it again for authorities
4641     eAuthorityLevel::Properties authoritiesMap = se_authorityLevel.GetMap();
4642 
4643     for ( eUserLevel::Properties::iterator iter = authoritiesMap.begin(); iter != authoritiesMap.end(); ++iter )
4644     {
4645         tColoredString userinfo, lowerUser;
4646 
4647         user = (*iter).first;
4648         accessLevel = (*iter).second;
4649         if ( accessLevelsToList.find( accessLevel ) != accessLevelsToList.end() )
4650         {
4651             // Prepare a string with the info about that user
4652             usersAccessLevelInSet = adminLevelsMap.find( accessLevel );
4653 
4654             if ( usersAccessLevelInSet == adminLevelsMap.end() )
4655             {
4656 
4657                 adminLevelsMap[ accessLevel ] = aSetOfAdmins();
4658 
4659                 usersAccessLevelInSet = adminLevelsMap.find( accessLevel );
4660             }
4661 
4662             aSetOfAdmins & theRightSet = (*usersAccessLevelInSet).second;
4663 
4664             lowerUser = user;
4665             tToLower( lowerUser );
4666             user = tOutput ( "$admin_list_authoritylevel", user );
4667             lowerUser = tString( "AAA" ) << lowerUser;
4668             theRightSet[ lowerUser ] = user;
4669 
4670             authorityCount++;
4671         }
4672     }
4673 
4674     // Now we have'em sorted by access level, it's show-time!
4675     // for each access level out there
4676     AdminLevelsMap::iterator it;
4677 
4678     int numberOfAccessLevelsShown = adminLevelsMap.size() -1;
4679     int advancement = 0;
4680     REAL red, green, blue;
4681     tColoredString accessLevelColor;
4682     tColoredString output;
4683 
4684     for ( it = adminLevelsMap.begin(); it != adminLevelsMap.end(); ++it )
4685     {
4686         output = "";
4687 
4688         // First, print the access level's name
4689         tAccessLevel accessLevel = (*it).first;
4690 
4691         // Find the appropriate color for it
4692         if (numberOfAccessLevelsShown <= 0)
4693             numberOfAccessLevelsShown = 1;
4694         red = (
4695                     se_adminListColors_BestRed * ( numberOfAccessLevelsShown - advancement )
4696                   + se_adminListColors_WorstRed  * ( advancement )
4697               ) / 16.0 / numberOfAccessLevelsShown;
4698         green = (
4699                     se_adminListColors_BestGreen * ( numberOfAccessLevelsShown - advancement )
4700                   + se_adminListColors_WorstGreen  * ( advancement )
4701               ) / 16.0 / numberOfAccessLevelsShown;
4702         blue = (
4703                     se_adminListColors_BestBlue * ( numberOfAccessLevelsShown - advancement )
4704                   + se_adminListColors_WorstBlue  * ( advancement )
4705               ) / 16.0 / numberOfAccessLevelsShown;
4706 
4707         accessLevelColor << tColoredString::ColorString( red, green, blue );
4708         output << accessLevelColor
4709                << tCurrentAccessLevel::GetName( accessLevel ) << ": ";
4710 //        accessLevelColor = "";
4711 
4712         // Then print the admin's names
4713         for ( aSetOfAdmins::iterator userIt = (*it).second.begin(); userIt != (*it).second.end(); ++userIt )
4714         {
4715             if ( userIt == (*it).second.begin() )
4716             {
4717                 output << (*userIt).second;
4718             }
4719             else
4720             {
4721                 output << ", " << (*userIt).second;
4722             }
4723         }
4724 
4725         output << "\n";
4726         sn_ConsoleOut( output, receiver->Owner() );
4727 
4728         ++advancement;
4729     }
4730 
4731     tOutput sumup;
4732     sumup = "";
4733 
4734     sn_ConsoleOut( tOutput( "$admin_list_end", command, userCount, authorityCount, static_cast<int>( adminLevelsMap.size() ) ), receiver->Owner() );
4735 }
4736 
se_ListAdmins_conf(std::istream & s)4737 static void se_ListAdmins_conf( std::istream &s )
4738 {
4739     se_ListAdmins( 0, s, tString( "PLAYERS" ) );
4740 }
4741 
4742 static tConfItemFunc se_ListAdminsConf("ADMINS",&se_ListAdmins_conf);
4743 static tAccessLevelSetter se_ListAdminsConfLevel( se_ListAdminsConf, tAccessLevel_Owner );
4744 
4745 #endif
4746 
4747 // reserves a nickname
4748 class eReserveNick: public eUserConfig< tString >
4749 {
4750 #ifdef DEBUG
4751 #ifdef KRAWALL_SERVER
TestEscape(char const * t)4752     static void TestEscape( char const * t )
4753     {
4754         tString test(t);
4755         tString esc(se_EscapeName( test, false ).c_str());
4756         tString rev(se_UnEscapeName( esc ).c_str());
4757         tASSERT( test == rev );
4758     }
4759 #endif
4760 
TestEscape()4761     static void TestEscape()
4762     {
4763 #ifdef KRAWALL_SERVER
4764         TestEscape("ä@%bla:");
4765         TestEscape("a b@%bl%:");
4766         TestEscape("a\\_b@%bl%:");
4767 #endif
4768     }
4769 #endif
4770 public:
eReserveNick()4771     eReserveNick()
4772     : eUserConfig< tString >( "RESERVE_SCREEN_NAME" )
4773     {
4774 #ifdef DEBUG
4775         TestEscape();
4776 #endif
4777     }
4778 
4779     // filter the name so it is compatible with old school user names; those
4780     // will be used later for comparison
TransformName(tString & name) const4781     virtual void TransformName( tString & name ) const
4782     {
4783         name = ePlayerNetID::FilterName( name );
4784     }
4785 
GetDefault() const4786     tString GetDefault() const
4787     {
4788         return tString();
4789     }
4790 
ReadRawVal(tString const & name,std::istream & s) const4791     virtual tString ReadRawVal(tString const & name, std::istream &s) const
4792     {
4793         tString user;
4794         s >> user;
4795 
4796         if ( user == "" )
4797         {
4798             con << tOutput( "$reserve_screen_name_usage" );
4799             return GetDefault();
4800         }
4801 
4802         user = se_UnEscapeName( user ).c_str();
4803 
4804         con << tOutput( "$reserve_screen_name_change", name, user );
4805 
4806         return user;
4807     }
4808 };
4809 
4810 static eReserveNick se_reserveNick;
4811 
4812 // authentication alias to map a local account to a global one
4813 class eAlias: public eUserConfig< tString >
4814 {
4815 public:
eAlias()4816     eAlias()
4817     : eUserConfig< tString >( "USER_ALIAS" )
4818     {
4819     }
4820 
GetDefault() const4821     tString GetDefault() const
4822     {
4823         return tString();
4824     }
4825 
ReadRawVal(tString const & name,std::istream & s) const4826     virtual tString ReadRawVal(tString const & name, std::istream &s) const
4827     {
4828         tString alias;
4829         s >> alias;
4830 
4831         if ( alias == "" )
4832         {
4833             con << tOutput( "$user_alias_usage" );
4834             return GetDefault();
4835         }
4836 
4837         alias = se_UnEscapeName( alias ).c_str();
4838 
4839         con << tOutput( "$alias_change", name, alias );
4840 
4841         return alias;
4842     }
4843 };
4844 
4845 static eAlias se_alias;
4846 
4847 // bans for players too stupid to disable autologin
4848 class eBanUser: public eUserConfig< bool >
4849 {
4850 public:
eBanUser()4851     eBanUser()
4852     : eUserConfig< bool >( "BAN_USER" )
4853     {
4854     }
4855 
4856     // unbans the user again
UnBan(tString const & name)4857     void UnBan( tString const & name )
4858     {
4859         properties[name] = false;
4860         con << tOutput( "$unban_user_message", name );
4861     }
4862 
List()4863     void List()
4864     {
4865         bool one = false;
4866         for ( Properties::iterator i = properties.begin(); i != properties.end(); ++i )
4867         {
4868             if ( (*i).second )
4869             {
4870                 if ( one )
4871                 {
4872                     con << ", ";
4873                 }
4874                 con << (*i).first;
4875                 one = true;
4876             }
4877         }
4878         if ( one )
4879         {
4880             con << "\n";
4881         }
4882     }
4883 
GetDefault() const4884     bool GetDefault() const
4885     {
4886         return false;
4887     }
4888 
ReadRawVal(tString const & name,std::istream & s) const4889     virtual bool ReadRawVal(tString const & name, std::istream &s) const
4890     {
4891         con << tOutput( "$ban_user_message", name );
4892         return true;
4893     }
4894 };
4895 
4896 static eBanUser se_banUser;
4897 
4898 // check whether a special username is banned
se_IsUserBanned(ePlayerNetID * p,tString const & name)4899 static bool se_IsUserBanned( ePlayerNetID * p, tString const & name )
4900 {
4901     if( se_banUser.Get( name ) )
4902     {
4903         sn_KickUser( p->Owner(), tOutput( "$network_kill_banned", 60, "" ) );
4904         return true;
4905     }
4906 
4907     return false;
4908 }
4909 
4910 class eUnBanUser: public eUserConfig< bool >
4911 {
4912 public:
eUnBanUser()4913     eUnBanUser( )
4914     : eUserConfig< bool >( "UNBAN_USER" )
4915     {
4916     }
4917 
GetDefault() const4918     bool GetDefault() const
4919     {
4920         return false;
4921     }
4922 
ReadRawVal(tString const & name,std::istream & s) const4923     virtual bool ReadRawVal(tString const & name, std::istream &s) const
4924     {
4925         se_banUser.UnBan(name);
4926         return false;
4927     }
4928 };
4929 
4930 static eUnBanUser se_unBanUser;
4931 
se_ListBannedUsers(std::istream &)4932 static void se_ListBannedUsers( std::istream & )
4933 {
4934     se_banUser.List();
4935 }
4936 
4937 static tConfItemFunc sn_listBanConf("BAN_USER_LIST",&se_ListBannedUsers);
4938 
se_CheckAccessLevel(tAccessLevel & level,tString const & authName)4939 static void se_CheckAccessLevel( tAccessLevel & level, tString const & authName )
4940 {
4941     tAccessLevel newLevel;
4942     tString authority;
4943     tString user;
4944 
4945     nKrawall::SplitUserName( authName, user, authority );
4946 
4947     newLevel = se_authorityLevel.Get( authority );
4948     if ( newLevel < level || newLevel > tAccessLevel_DefaultAuthenticated )
4949     {
4950         level = newLevel;
4951     }
4952 
4953     newLevel = se_userLevel.Get( authName );
4954     if ( newLevel < level || newLevel > tAccessLevel_DefaultAuthenticated )
4955     {
4956         level = newLevel;
4957     }
4958 }
4959 
Authenticate(tString const & authName,tAccessLevel accessLevel_,ePlayerNetID const * admin)4960 void ePlayerNetID::Authenticate( tString const & authName, tAccessLevel accessLevel_, ePlayerNetID const * admin )
4961 {
4962     tString newAuthenticatedName( se_EscapeName( authName ).c_str() );
4963 
4964     // no use authenticating an AI :)
4965     if ( !IsHuman() )
4966     {
4967         return;
4968     }
4969 
4970     if ( !IsAuthenticated() )
4971     {
4972         // elevate access level for registered users
4973         se_CheckAccessLevel( accessLevel_, newAuthenticatedName );
4974 
4975         rawAuthenticatedName_ = authName;
4976         tString alias = se_alias.Get( newAuthenticatedName );
4977         if ( alias != "" )
4978         {
4979             rawAuthenticatedName_ = alias;
4980             newAuthenticatedName = GetFilteredAuthenticatedName();
4981 
4982             // elevate access level again according to the new alias
4983             se_CheckAccessLevel( accessLevel_, newAuthenticatedName );
4984         }
4985 
4986         // check for stupid bans
4987         if ( se_IsUserBanned( this, newAuthenticatedName ) )
4988         {
4989             return;
4990         }
4991 
4992         // minimal access level
4993         if ( accessLevel_ > tAccessLevel_Authenticated )
4994         {
4995             accessLevel_ = static_cast< tAccessLevel >( tAccessLevel_Program - 1 );
4996         }
4997 
4998         {
4999             // authorize access level change (careful, don't blindly copy/paste to other places)
5000             tCurrentAccessLevel level( tAccessLevel_Owner, true );
5001 
5002             // take over the access level
5003             SetAccessLevel( accessLevel_ );
5004         }
5005 
5006         tString order( "" );
5007         if ( admin )
5008         {
5009             order = tOutput( "$login_message_byorder",
5010                              admin->GetLogName() );
5011         }
5012 
5013         if ( IsHuman() )
5014         {
5015             if ( GetAccessLevel() != tAccessLevel_Default )
5016             {
5017                 se_SecretConsoleOut( tOutput( "$login_message_special",
5018                                               GetName(),
5019                                               newAuthenticatedName,
5020                                               tCurrentAccessLevel::GetName( GetAccessLevel() ),
5021                                               order ), this, &se_Hide, admin, 0, &se_CanHide );
5022             }
5023             else
5024             {
5025                 se_SecretConsoleOut( tOutput( "$login_message", GetName(), newAuthenticatedName, order ), this, &se_Hide, admin, 0, &se_CanHide );
5026             }
5027 
5028         }
5029     }
5030 
5031     GetScoreFromDisconnectedCopy();
5032 
5033     // force update (removed again to fix name change possibility during a round)
5034     // UpdateName();
5035 }
5036 
DeAuthenticate(ePlayerNetID const * admin)5037 void ePlayerNetID::DeAuthenticate( ePlayerNetID const * admin ){
5038     if ( IsAuthenticated() )
5039     {
5040         if ( admin )
5041         {
5042             se_SecretConsoleOut( tOutput( "$logout_message_deop", GetName(), GetFilteredAuthenticatedName(), admin->GetLogName() ), this, &se_Hide, admin, 0, &se_CanHide );
5043         }
5044         else
5045         {
5046             se_SecretConsoleOut( tOutput( "$logout_message", GetName(), GetFilteredAuthenticatedName() ), this, &se_Hide, 0, 0, &se_CanHide );
5047         }
5048     }
5049 
5050     // force falling back to regular user name on next update
5051     SetAccessLevel( tAccessLevel_Default );
5052 
5053     rawAuthenticatedName_ = "";
5054 
5055     // force update (removed again to fix name change possibility during a round)
5056     // UpdateName();
5057 }
5058 
IsAuthenticated() const5059 bool ePlayerNetID::IsAuthenticated() const
5060 {
5061     return int(GetAccessLevel()) <= int(tAccessLevel_Authenticated);
5062 }
5063 #endif
5064 
5065 // create our voter or find it
CreateVoter()5066 void ePlayerNetID::CreateVoter()
5067 {
5068     // only count nonlocal players with voting support as voters
5069     if ( sn_GetNetState() != nCLIENT && this->Owner() != 0 && sn_Connections[ this->Owner() ].version.Max() >= 3 )
5070     {
5071         this->voter_ = eVoter::GetVoter( this->Owner() );
5072         if ( this->voter_ )
5073             this->voter_->PlayerChanged();
5074     }
5075 }
5076 
WriteSync(nMessage & m)5077 void ePlayerNetID::WriteSync(nMessage &m){
5078     lastSync=tSysTimeFloat();
5079     nNetObject::WriteSync(m);
5080     m.Write(r);
5081     m.Write(g);
5082     m.Write(b);
5083 
5084     // write ping charity; spectators get a fake (high) value
5085     if ( currentTeam || nextTeam )
5086         m.Write(pingCharity);
5087     else
5088         m.Write(1000);
5089 
5090     if ( sn_GetNetState() == nCLIENT )
5091     {
5092         m << nameFromClient_;
5093     }
5094     else
5095     {
5096         m << nameFromServer_;
5097     }
5098 
5099     //if(sn_GetNetState()==nSERVER)
5100     m << ping;
5101 
5102     // pack chat, spectator and stealth status together
5103     unsigned short flags = ( chatting_ ? 1 : 0 ) | ( spectating_ ? 2 : 0 ) | ( stealth_ ? 4 : 0 );
5104     m << flags;
5105 
5106     m << score;
5107     m << static_cast<unsigned short>(disconnected);
5108 
5109     m << nextTeam;
5110     m << currentTeam;
5111 
5112     m << favoriteNumberOfPlayersPerTeam;
5113     m << nameTeamAfterMe;
5114 }
5115 
5116 // makes sure the passed string is not longer than the given maximum
se_CutString(tColoredString & string,int maxLen)5117 static void se_CutString( tColoredString & string, int maxLen )
5118 {
5119     if (string.Len() > maxLen )
5120     {
5121         string.SetLen(maxLen);
5122         string[string.Len()-1]='\0';
5123         string.RemoveTrailingColor();
5124     }
5125 }
5126 
se_CutString(tString & string,int maxLen)5127 static void se_CutString( tString & string, int maxLen )
5128 {
5129     se_CutString( reinterpret_cast< tColoredString & >( string ), maxLen );
5130 }
5131 
5132 static bool se_bugColorOverflow=true;
5133 tSettingItem< bool > se_bugColorOverflowColor( "BUG_COLOR_OVERFLOW", se_bugColorOverflow );
Clamp(unsigned short & colorComponent)5134 void Clamp( unsigned short & colorComponent )
5135 {
5136     if ( colorComponent > 15 )
5137         colorComponent = 15;
5138 }
5139 
5140 // function prototype for character testing functions
5141 typedef bool TestCharacter( char c );
5142 
5143 // strips characters matching a test beginnings and ends of names
se_StripMatchingEnds(tString & stripper,TestCharacter & beginTester,TestCharacter & endTester)5144 static void se_StripMatchingEnds( tString & stripper, TestCharacter & beginTester, TestCharacter & endTester )
5145 {
5146     int len = stripper.Len() - 1;
5147     int first = 0, last = len;
5148 
5149     // eat whitespace from beginnig and end
5150     while ( first < len && beginTester( stripper[first] ) ) ++first;
5151     while ( last > 0 && ( !stripper[last] || endTester(stripper[last] ) ) ) --last;
5152 
5153     // strip everything?
5154     if ( first > last )
5155     {
5156         stripper = "";
5157         return;
5158     }
5159 
5160     // strip
5161     if ( first > 0 || last < stripper.Len() - 1 )
5162         stripper = stripper.SubStr( first, last + 1 - first );
5163 }
5164 
5165 // removed convenience function, VisualC 6 cannot cope with it...
5166 // strips characters matching a test beginnings and ends of names
5167 //static void se_StripMatchingEnds( tString & stripper, TestCharacter & tester )
5168 //{
5169 //    se_StripMatchingEnds( stripper, tester, tester );
5170 //}
5171 
5172 // function wrapper for what may be a macro
se_IsBlank(char c)5173 static bool se_IsBlank( char c )
5174 {
5175     return isblank( c );
5176 }
5177 
5178 // enf of player names should neither be space or :
se_IsInvalidNameEnd(char c)5179 static bool se_IsInvalidNameEnd( char c )
5180 {
5181     return isblank( c ) || c == ':' || c == '.';
5182 }
5183 
5184 // filter name ends
se_StripNameEnds(tString & name)5185 static void se_StripNameEnds( tString & name )
5186 {
5187     se_StripMatchingEnds( name, se_IsBlank, se_IsInvalidNameEnd );
5188 }
5189 
5190 static bool se_allowImposters = false;
5191 static tSettingItem< bool > se_allowImposters1( "ALLOW_IMPOSTERS", se_allowImposters );
5192 static tSettingItem< bool > se_allowImposters2( "ALLOW_IMPOSTORS", se_allowImposters );
5193 
5194 // test if a user name is used by anyone else than the passed player
se_IsNameTaken(tString const & name,ePlayerNetID const * exception)5195 static bool se_IsNameTaken( tString const & name, ePlayerNetID const * exception )
5196 {
5197     if ( name.Len() <= 1 )
5198         return false;
5199 
5200     if ( !se_allowImposters )
5201     {
5202         // check for other players with the same name
5203         for (int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
5204         {
5205             ePlayerNetID * player = se_PlayerNetIDs(i);
5206             if ( player != exception )
5207             {
5208                 if ( name == player->GetUserName() || name == ePlayerNetID::FilterName( player->GetName() ) )
5209                     return true;
5210             }
5211         }
5212     }
5213 
5214 #ifdef KRAWALL_SERVER
5215     // check for reserved nicknames
5216     {
5217         tString reservedFor = se_reserveNick.Get( name );
5218         if ( reservedFor != "" &&
5219              ( !exception || exception->GetAccessLevel() >= tAccessLevel_Default ||
5220                exception->GetRawAuthenticatedName() != reservedFor ) )
5221         {
5222             return true;
5223         }
5224     }
5225 #endif
5226 
5227     return false;
5228 }
5229 
5230 static bool se_filterColorNames=false;
5231 tSettingItem< bool > se_coloredNamesConf( "FILTER_COLOR_NAMES", se_filterColorNames );
5232 static bool se_filterDarkColorNames=false;
5233 tSettingItem< bool > se_coloredDarkNamesConf( "FILTER_DARK_COLOR_NAMES", se_filterDarkColorNames );
5234 
5235 static bool se_stripNames=true;
5236 tSettingItem< bool > se_stripConf( "FILTER_NAME_ENDS", se_stripNames );
5237 static bool se_stripMiddle=true;
5238 tSettingItem< bool > se_stripMiddleConf( "FILTER_NAME_MIDDLE", se_stripMiddle );
5239 
5240 // do the optional filtering steps
se_OptionalNameFilters(tString & remoteName)5241 static void se_OptionalNameFilters( tString & remoteName )
5242 {
5243     // filter colors
5244     if ( se_filterColorNames )
5245     {
5246         remoteName = tColoredString::RemoveColors( remoteName, false );
5247     }
5248     else if ( se_filterDarkColorNames )
5249     {
5250         remoteName = tColoredString::RemoveColors( remoteName, true );
5251     }
5252 
5253     // don't do the fancy stuff on the client, it only makes names on score tables and
5254     // console messages go out of sync.
5255     if ( sn_GetNetState() == nCLIENT )
5256         return;
5257 
5258     // strip whitespace
5259     if ( se_stripNames )
5260         se_StripNameEnds( remoteName );
5261 
5262     if ( se_stripMiddle )
5263     {
5264         // first, scan for double whitespace
5265         bool whitespace=false;
5266         int i;
5267         for ( i = 0; i < remoteName.Len()-1; ++i )
5268         {
5269             bool newWhitespace=isblank(remoteName[i]);
5270             if ( newWhitespace && whitespace )
5271             {
5272                 // yes, double whitespace there. Filter it out.
5273                 whitespace=newWhitespace=false;
5274                 tString copy;
5275                 for ( i = 0; i < remoteName.Len()-1; ++i )
5276                 {
5277                     char c = remoteName[i];
5278                     newWhitespace=isblank(c);
5279                     if ( !whitespace || !newWhitespace )
5280                     {
5281                         copy+=c;
5282                     }
5283                     whitespace=newWhitespace;
5284                 }
5285                 remoteName=copy;
5286 
5287                 // abort loop.
5288                 break;
5289             }
5290 
5291             whitespace=newWhitespace;
5292         }
5293     }
5294 
5295     // zero strings or color code only strings are illegal
5296     if ( !IsLegalPlayerName( remoteName ) )
5297     {
5298         tString oldName = remoteName;
5299 
5300         // revert to old name if possible
5301         if ( IsLegalPlayerName( oldName ) )
5302         {
5303             remoteName = oldName;
5304         }
5305         else
5306         {
5307             // or replace it by a default value
5308             remoteName = "Player 1";
5309         }
5310     }
5311 }
5312 
ReadSync(nMessage & m)5313 void ePlayerNetID::ReadSync(nMessage &m){
5314     // check whether this is the first sync
5315     bool firstSync = ( this->ID() == 0 );
5316 
5317     nNetObject::ReadSync(m);
5318 
5319     m.Read(r);
5320     m.Read(g);
5321     m.Read(b);
5322 
5323     if ( !se_bugColorOverflow )
5324     {
5325         // clamp color values
5326         Clamp(r);
5327         Clamp(g);
5328         Clamp(b);
5329     }
5330 
5331     m.Read(pingCharity);
5332     sg_ClampPingCharity(pingCharity);
5333 
5334     // name as sent from the other end
5335     tString & remoteName = ( sn_GetNetState() == nCLIENT ) ? nameFromServer_ : nameFromClient_;
5336 
5337     // name handling
5338     {
5339         // read and shorten name, but don't update it yet
5340         m >> remoteName;
5341 
5342         // filter
5343         se_OptionalNameFilters( remoteName );
5344 
5345         se_CutString( remoteName, 16 );
5346     }
5347 
5348     // directly apply name changes sent from the server, they are safe.
5349     if ( sn_GetNetState() != nSERVER )
5350     {
5351         UpdateName();
5352     }
5353 
5354     REAL p;
5355     m >> p;
5356     if (sn_GetNetState()!=nSERVER)
5357         ping=p;
5358 
5359     //  if (!m.End())
5360     {
5361         // read chat and spectator status
5362         unsigned short flags;
5363         m >> flags;
5364 
5365         if (Owner() != ::sn_myNetID)
5366         {
5367             bool newChat = ( ( flags & 1 ) != 0 );
5368             bool newSpectate = ( ( flags & 2 ) != 0 );
5369             bool newStealth = ( ( flags & 4 ) != 0 );
5370 
5371             if ( chatting_ != newChat || spectating_ != newSpectate || newStealth != stealth_ )
5372                 lastActivity_ = tSysTimeFloat();
5373             chatting_   = newChat;
5374             spectating_ = newSpectate;
5375             stealth_    = newStealth;
5376         }
5377     }
5378 
5379     //  if (!m.End())
5380     {
5381         if(sn_GetNetState()!=nSERVER)
5382             m >> score;
5383         else{
5384             int s;
5385             m >> s;
5386         }
5387     }
5388 
5389     if (!m.End()){
5390         unsigned short newdisc;
5391         m >> newdisc;
5392 
5393         if (Owner() != ::sn_myNetID && sn_GetNetState()!=nSERVER)
5394             disconnected = newdisc;
5395     }
5396 
5397     if (!m.End())
5398     {
5399         if ( nSERVER != sn_GetNetState() )
5400         {
5401             eTeam *newCurrentTeam, *newNextTeam;
5402 
5403             m >> newNextTeam;
5404             m >> newCurrentTeam;
5405 
5406             // update team
5407             tJUST_CONTROLLED_PTR< eTeam > oldTeam( currentTeam );
5408             if ( newCurrentTeam != currentTeam )
5409             {
5410                 if ( newCurrentTeam )
5411                 {
5412                     newCurrentTeam->AddPlayerDirty( this );
5413                     newCurrentTeam->UpdateProperties();
5414                 }
5415                 else
5416                 {
5417                     currentTeam->RemovePlayer( this );
5418                 }
5419             }
5420 
5421             nextTeam = newNextTeam;
5422 
5423             // update properties of the old current team in all cases
5424             if ( oldTeam )
5425             {
5426                 oldTeam->UpdateProperties();
5427             }
5428         }
5429         else
5430         {
5431             eTeam* t;
5432             m >> t;
5433             m >> t;
5434         }
5435 
5436         m >> favoriteNumberOfPlayersPerTeam;
5437         m >> nameTeamAfterMe;
5438     }
5439     // con << "Player info updated.\n";
5440 
5441     // make sure we did not accidentally overwrite values
5442     // ePlayer::Update();
5443 
5444     // update the team
5445     if ( nSERVER == sn_GetNetState() )
5446     {
5447         if ( nextTeam )
5448             nextTeam->UpdateProperties();
5449 
5450         if ( currentTeam )
5451             currentTeam->UpdateProperties();
5452     }
5453 
5454     // first sync message
5455     if ( firstSync && sn_GetNetState() == nSERVER )
5456     {
5457         UpdateName();
5458 
5459 #ifndef KRAWALL_SERVER
5460         GetScoreFromDisconnectedCopy();
5461 #endif
5462         FindDefaultTeam();
5463 
5464         RequestSync();
5465     }
5466 
5467 }
5468 
5469 
5470 nNOInitialisator<ePlayerNetID> ePlayerNetID_init(201,"ePlayerNetID");
5471 
CreatorDescriptor() const5472 nDescriptor &ePlayerNetID::CreatorDescriptor() const{
5473     return ePlayerNetID_init;
5474 }
5475 
5476 
5477 
ControlObject(eNetGameObject * c)5478 void ePlayerNetID::ControlObject(eNetGameObject *c){
5479     if (bool(object) && c!=object)
5480         ClearObject();
5481 
5482     if (!c)
5483     {
5484         return;
5485     }
5486 
5487 
5488     object=c;
5489     c->team = currentTeam;
5490 
5491     if (bool(object))
5492         object->SetPlayer(this);
5493 #ifdef DEBUG
5494     //con << "Player " << name << " controlles new object.\n";
5495 #endif
5496 
5497     NewObject();
5498 }
5499 
ClearObject()5500 void ePlayerNetID::ClearObject(){
5501     if (object)
5502     {
5503         tJUST_CONTROLLED_PTR< eNetGameObject > x=object;
5504         object=NULL;
5505         x->RemoveFromGame();
5506         x->SetPlayer( NULL );
5507     }
5508 #ifdef DEBUG
5509     //con << "Player " << name << " controlles nothing.\n";
5510 #endif
5511 }
5512 
Greet()5513 void ePlayerNetID::Greet(){
5514     if (!greeted){
5515         tOutput o;
5516         o.SetTemplateParameter(1, GetName() );
5517         o.SetTemplateParameter(2, sn_programVersion);
5518         o << "$player_welcome";
5519         tString s;
5520         s << o;
5521         s << "\n";
5522         GreetHighscores(s);
5523         s << "\n";
5524         //std::cout << s;
5525         sn_ConsoleOut(s,Owner());
5526         greeted=true;
5527     }
5528 }
5529 
Object() const5530 eNetGameObject *ePlayerNetID::Object() const{
5531     return object;
5532 }
5533 
5534 static bool se_consoleLadderLog = false;
5535 static tSettingItem< bool > se_consoleLadderLogConf( "CONSOLE_LADDER_LOG", se_consoleLadderLog );
5536 
5537 static bool se_ladderlogDecorateTS = false;
5538 static tSettingItem< bool > se_ladderlogDecorateTSConf( "LADDERLOG_DECORATE_TIMESTAMP", se_ladderlogDecorateTS );
5539 extern bool sn_decorateTS; // from nNetwork.cpp
5540 
se_SaveToLadderLog(tOutput const & out)5541 void se_SaveToLadderLog( tOutput const & out )
5542 {
5543     if (se_consoleLadderLog)
5544     {
5545         std::cout << "[L";
5546         if(sn_decorateTS) {
5547             std::cout << st_GetCurrentTime(" TS=%Y/%m/%d-%H:%M:%S");
5548         }
5549         std::cout << "] " << out << std::endl;
5550     }
5551     if (sn_GetNetState()!=nCLIENT && !tRecorder::IsPlayingBack())
5552     {
5553         std::ofstream o;
5554         if ( tDirectories::Var().Open(o, "ladderlog.txt", std::ios::app) )
5555         {
5556             if(se_ladderlogDecorateTS) {
5557                 o << st_GetCurrentTime("%Y/%m/%d-%H:%M:%S ");
5558             }
5559             o << out << std::endl;
5560         }
5561     }
5562 }
5563 
5564 static bool se_chatLog = false;
5565 static tSettingItem<bool> se_chatLogConf("CHAT_LOG", se_chatLog);
5566 
5567 static eLadderLogWriter se_chatWriter("CHAT", false);
5568 
se_SaveToChatLog(tOutput const & out)5569 void se_SaveToChatLog(tOutput const &out) {
5570     if(sn_GetNetState() != nCLIENT && !tRecorder::IsPlayingBack()) {
5571         if(se_chatWriter.isEnabled()) {
5572             se_chatWriter << out;
5573             se_chatWriter.write();
5574         }
5575         if(se_chatLog) {
5576             std::ofstream o;
5577             if ( tDirectories::Var().Open(o, "chatlog.txt", std::ios::app) ) {
5578                 o << st_GetCurrentTime("[%Y/%m/%d-%H:%M:%S] ") << out << std::endl;
5579             }
5580         }
5581     }
5582 }
5583 
writers()5584 std::list<eLadderLogWriter *> &eLadderLogWriter::writers() {
5585     static std::list<eLadderLogWriter *> list;
5586     return list;
5587 }
5588 
eLadderLogWriter(char const * ID,bool enabledByDefault)5589 eLadderLogWriter::eLadderLogWriter(char const *ID, bool enabledByDefault) :
5590     id(ID),
5591     enabled(enabledByDefault),
5592     conf(new tSettingItem<bool>(&(tString("LADDERLOG_WRITE_") + id)(0),
5593     enabled)),
5594     cache(id) {
5595     writers().push_back(this);
5596 }
5597 
~eLadderLogWriter()5598 eLadderLogWriter::~eLadderLogWriter() {
5599     if(conf) {
5600         delete conf;
5601     }
5602     // generic algorithms aren't exactly easier to understand than regular
5603     // code, but anyways, let's try one...
5604     std::list<eLadderLogWriter *> list = writers();
5605     list.erase(std::find_if(list.begin(), list.end(), std::bind2nd(std::equal_to<eLadderLogWriter *>(), this)));
5606 }
5607 
write()5608 void eLadderLogWriter::write() {
5609     if(enabled) {
5610         se_SaveToLadderLog(cache);
5611     }
5612     cache = id;
5613 }
5614 
setAll(bool enabled)5615 void eLadderLogWriter::setAll(bool enabled) {
5616     std::list<eLadderLogWriter *> list = writers();
5617     std::list<eLadderLogWriter *>::iterator end = list.end();
5618     for(std::list<eLadderLogWriter *>::iterator iter = list.begin(); iter != end; ++iter) {
5619         (*iter)->enabled = enabled;
5620     }
5621 }
5622 
LadderLogWriteAll(std::istream & s)5623 static void LadderLogWriteAll(std::istream &s) {
5624     bool enabled;
5625     s >> enabled;
5626     if(s.fail()) {
5627         if(tConfItemBase::printErrors) {
5628             con << tOutput("$ladderlog_write_all_usage") << '\n';
5629         }
5630         return;
5631     }
5632     if(tConfItemBase::printChange) {
5633         if(enabled) {
5634             con << tOutput("$ladderlog_write_all_enabled") << '\n';
5635         } else {
5636             con << tOutput("$ladderlog_write_all_disabled") << '\n';
5637         }
5638     }
5639     eLadderLogWriter::setAll(enabled);
5640 }
5641 
5642 static tConfItemFunc LadderLogWriteAllConf("LADDERLOG_WRITE_ALL", &LadderLogWriteAll);
5643 
se_SaveToScoreFile(const tOutput & o)5644 void se_SaveToScoreFile(const tOutput &o){
5645     tString s(o);
5646 
5647 #ifdef DEBUG
5648     if (sn_GetNetState()!=nCLIENT){
5649 #else
5650     if (sn_GetNetState()==nSERVER && !tRecorder::IsPlayingBack()){
5651 #endif
5652 
5653         std::ofstream o;
5654         if ( tDirectories::Var().Open(o, "scorelog.txt", std::ios::app) )
5655             o << tColoredString::RemoveColors(s);
5656     }
5657 #ifdef DEBUG
5658 }
5659 #else
5660 }
5661 #endif
5662 
5663 // void ePlayerNetID::SetRubber(REAL rubber2) {rubberstatus = rubber2;}
5664 
AddScore(int points,const tOutput & reasonwin,const tOutput & reasonlose)5665 void ePlayerNetID::AddScore(int points,
5666                             const tOutput& reasonwin,
5667                             const tOutput& reasonlose)
5668 {
5669     if (points==0)
5670         return;
5671 
5672     score += points;
5673     if (currentTeam)
5674         currentTeam->AddScore( points );
5675 
5676     tColoredString name;
5677     name << *this << tColoredString::ColorString(-1,-1,-1);
5678 
5679     tOutput message;
5680     message.SetTemplateParameter(1, name);
5681     message.SetTemplateParameter(2, points > 0 ? points : -points);
5682 
5683 
5684     if (points>0)
5685     {
5686         if (reasonwin.IsEmpty())
5687             message << "$player_win_default";
5688         else
5689             message.Append(reasonwin);
5690     }
5691     else
5692     {
5693         if (reasonlose.IsEmpty())
5694             message << "$player_lose_default";
5695         else
5696             message.Append(reasonlose);
5697     }
5698 
5699     sn_ConsoleOut(message);
5700     RequestSync(true);
5701 
5702     se_SaveToScoreFile(message);
5703 }
5704 
5705 
5706 
5707 
TotalScore() const5708 int ePlayerNetID::TotalScore() const
5709 {
5710     if ( currentTeam )
5711     {
5712         return score;// + currentTeam->Score() * 5;
5713     }
5714     else
5715     {
5716         return score;
5717     }
5718 }
5719 
5720 
SwapPlayersNo(int a,int b)5721 void ePlayerNetID::SwapPlayersNo(int a,int b){
5722     if (0>a || se_PlayerNetIDs.Len()<=a)
5723         return;
5724     if (0>b || se_PlayerNetIDs.Len()<=b)
5725         return;
5726     if (a==b)
5727         return;
5728 
5729     ePlayerNetID *A=se_PlayerNetIDs(a);
5730     ePlayerNetID *B=se_PlayerNetIDs(b);
5731 
5732     se_PlayerNetIDs(b)=A;
5733     se_PlayerNetIDs(a)=B;
5734     A->listID=b;
5735     B->listID=a;
5736 }
5737 
5738 
SortByScore()5739 void ePlayerNetID::SortByScore(){
5740     // bubble sort (AAARRGGH! but good for lists that change not much)
5741 
5742     bool inorder=false;
5743     while (!inorder){
5744         inorder=true;
5745         int i;
5746         for(i=se_PlayerNetIDs.Len()-2;i>=0;i--)
5747             if (se_PlayerNetIDs(i)->TotalScore() < se_PlayerNetIDs(i+1)->TotalScore() ){
5748                 SwapPlayersNo(i,i+1);
5749                 inorder=false;
5750             }
5751     }
5752 }
5753 
ResetScore()5754 void ePlayerNetID::ResetScore(){
5755     int i;
5756     for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
5757         se_PlayerNetIDs(i)->score=0;
5758         if (sn_GetNetState()==nSERVER)
5759             se_PlayerNetIDs(i)->RequestSync();
5760     }
5761 
5762     for(i=eTeam::teams.Len()-1;i>=0;i--){
5763         eTeam::teams(i)->ResetScore();
5764         if (sn_GetNetState()==nSERVER)
5765             eTeam::teams(i)->RequestSync();
5766     }
5767 
5768     ResetScoreDifferences();
5769 }
5770 
DisplayScores()5771 void ePlayerNetID::DisplayScores(){
5772     sr_ResetRenderState(true);
5773 
5774     REAL W=sr_screenWidth;
5775     REAL H=sr_screenHeight;
5776 
5777     REAL MW=400;
5778     REAL MH=(MW*3)/4;
5779 
5780     if(W>MW)
5781         W=MW;
5782 
5783     if(H>MH)
5784         H=MH;
5785 
5786 #ifndef DEDICATED
5787     if (sr_glOut){
5788         ::Color(1,1,1);
5789         rTextField c(-.7,.6,10/W,18/H);
5790 
5791         // print team ranking if there actually is a team with more than one player
5792         int maxPlayers = 20;
5793         for ( int i = eTeam::teams.Len() - 1; i >= 0; --i )
5794         {
5795             if ( eTeam::teams[i]->NumPlayers() > 1 ||
5796                     ( eTeam::teams[i]->NumPlayers() == 1 && eTeam::teams[i]->Player(0)->Score() != eTeam::teams[i]->Score() ) )
5797             {
5798                 c << eTeam::Ranking();
5799                 c << "\n";
5800                 maxPlayers -= ( eTeam::teams.Len() > 6 ? 6 : eTeam::teams.Len() ) + 2;
5801                 break;
5802             }
5803         }
5804 
5805         // print player ranking
5806         c << Ranking( maxPlayers );
5807     }
5808 #endif
5809 }
5810 
5811 
Ranking(int MAX,bool cut)5812 tString ePlayerNetID::Ranking( int MAX, bool cut ){
5813     SortByScore();
5814 
5815     tColoredString ret;
5816 
5817     if (se_PlayerNetIDs.Len()>0){
5818         ret << tColoredString::ColorString(1,.5,.5);
5819         ret << tOutput("$player_scoretable_name");
5820         ret << tColoredString::ColorString(-1,-1,-1);
5821         ret.SetPos(17, cut );
5822         ret << tOutput("$player_scoretable_alive");
5823         ret.SetPos(24, cut );
5824         ret << tOutput("$player_scoretable_score");
5825         ret.SetPos(31, cut );
5826         ret << tOutput("$player_scoretable_ping");
5827         ret.SetPos(37, cut );
5828         ret << tOutput("$player_scoretable_team");
5829         ret.SetPos(53, cut );
5830         ret << "\n";
5831 
5832         int max = se_PlayerNetIDs.Len();
5833 
5834         // wasting the last line with ... is as stupid if it stands for only
5835         // one player
5836         if ( MAX == max + 1 )
5837             MAX = max;
5838 
5839         if ( max > MAX && MAX > 0 )
5840         {
5841             max = MAX ;
5842         }
5843 
5844         for(int i=0;i<max;i++){
5845             tColoredString line;
5846             ePlayerNetID *p=se_PlayerNetIDs(i);
5847             // tColoredString name( p->GetColoredName() );
5848             // name.SetPos(16, cut );
5849 
5850             // This line is example of how we manually get the player color... could come in useful.
5851             // line << tColoredString::ColorString( p->r/15.0, p->g/15.0, p->b/15.0 ) << name << tColoredString::ColorString(1,1,1);
5852 
5853             // however, using the streaming operator is a lot cleaner. The example is left, as it really can be usefull in some situations.
5854             line << *p;
5855             line.SetPos(17, false );
5856             if ( p->Object() && p->Object()->Alive() )
5857             {
5858                 line << tColoredString::ColorString(0,1,0) << tOutput("$player_scoretable_alive_yes") << tColoredString::ColorString(-1,-1,-1);
5859             }
5860             else
5861             {
5862                 line << tColoredString::ColorString(1,0,0) << tOutput("$player_scoretable_alive_no") << tColoredString::ColorString(-1,-1,-1);
5863             }
5864             line.SetPos(24, cut );
5865             line << p->score;
5866 
5867             if (p->IsActive())
5868             {
5869                 line.SetPos(31, cut );
5870                 //line << "ping goes here";
5871                 line << int(p->ping*1000);
5872                 line.SetPos(37, cut );
5873                 if ( p->currentTeam )
5874                 {
5875                     //tString teamtemp = p->currentTeam->Name();
5876                     //teamtemp.RemoveHex();
5877                     line << tColoredString::RemoveColors(p->currentTeam->Name());
5878                     line.SetPos(53, cut );
5879                 }
5880             }
5881             else
5882                 line << tOutput("$player_scoretable_inactive");
5883             ret << line << "\n";
5884         }
5885         if ( max < se_PlayerNetIDs.Len() )
5886         {
5887             ret << "...\n";
5888         }
5889 
5890     }
5891     else
5892         ret << tOutput("$player_scoretable_nobody");
5893     return ret;
5894 }
5895 
5896 static eLadderLogWriter se_onlinePlayerWriter("ONLINE_PLAYER", false);
5897 static eLadderLogWriter se_numHumansWriter("NUM_HUMANS", false);
5898 
RankingLadderLog()5899 void ePlayerNetID::RankingLadderLog() {
5900     SortByScore();
5901 
5902     int num_humans = 0;
5903     int max = se_PlayerNetIDs.Len();
5904     for(int i = 0; i < max; ++i) {
5905         ePlayerNetID *p = se_PlayerNetIDs(i);
5906         if(p->Owner() == 0) continue; // ignore AIs
5907 
5908         se_onlinePlayerWriter << p->GetLogName();
5909 
5910         if(p->IsActive()) {
5911             se_onlinePlayerWriter << p->ping;
5912             if(p->currentTeam) {
5913                 se_onlinePlayerWriter << FilterName(p->currentTeam->Name());
5914                 ++num_humans;
5915             }
5916         }
5917         se_onlinePlayerWriter.write();
5918     }
5919     se_numHumansWriter << num_humans;
5920     se_numHumansWriter.write();
5921 }
5922 
ClearAll()5923 void ePlayerNetID::ClearAll(){
5924     for(int i=MAX_PLAYERS-1;i>=0;i--){
5925         ePlayer *local_p=ePlayer::PlayerConfig(i);
5926         if (local_p)
5927             local_p->netPlayer = NULL;
5928     }
5929 }
5930 
se_VisibleSpectatorsSupported()5931 static bool se_VisibleSpectatorsSupported()
5932 {
5933     static nVersionFeature se_visibleSpectator(13);
5934     return sn_GetNetState() != nCLIENT || se_visibleSpectator.Supported(0);
5935 }
5936 
5937 // @param spectate if true
SpectateAll(bool spectate)5938 void ePlayerNetID::SpectateAll( bool spectate ){
5939     for(int i=MAX_PLAYERS-1;i>=0;i--){
5940         ePlayer *local_p=ePlayer::PlayerConfig(i);
5941         if (local_p)
5942         {
5943             if ( se_VisibleSpectatorsSupported() && bool(local_p->netPlayer) )
5944             {
5945                 local_p->netPlayer->spectating_ = spectate || local_p->spectate;
5946 
5947                 local_p->netPlayer->RequestSync();
5948             }
5949             else
5950                 local_p->netPlayer = NULL;
5951         }
5952     }
5953 }
5954 
CompleteRebuild()5955 void ePlayerNetID::CompleteRebuild(){
5956     ClearAll();
5957     Update();
5958 }
5959 
se_ColorDistance(int a[3],int b[3])5960 static int se_ColorDistance( int a[3], int b[3] )
5961 {
5962     int distance = 0;
5963     for( int i = 2; i >= 0; --i )
5964     {
5965         int diff = a[i] - b[i];
5966         distance += diff * diff;
5967         //diff = diff < 0 ? -diff : diff;
5968         //if ( diff > distance )
5969         //{
5970         //    distance = diff;
5971         //}
5972     }
5973 
5974     return distance;
5975 }
5976 
5977 bool se_randomizeColor = true;
5978 static tSettingItem< bool > se_randomizeColorConf( "PLAYER_RANDOM_COLOR", se_randomizeColor );
5979 
se_RandomizeColor(ePlayer * l,ePlayerNetID * p)5980 static void se_RandomizeColor( ePlayer * l, ePlayerNetID * p )
5981 {
5982     int currentRGB[3];
5983     int newRGB[3];
5984     int nullRGB[3]={0,0,0};
5985 
5986     static tReproducibleRandomizer randomizer;
5987 
5988     for( int i = 2; i >= 0; --i )
5989     {
5990         currentRGB[i] = l->rgb[i];
5991         newRGB[i] = randomizer.Get(15);
5992     }
5993 
5994     int currentMinDiff = se_ColorDistance( currentRGB, nullRGB )/2;
5995     int newMinDiff = se_ColorDistance( newRGB, nullRGB )/2;
5996 
5997     // check the minimal distance of the new random color with all players
5998     for ( int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
5999     {
6000         ePlayerNetID * other = se_PlayerNetIDs(i);
6001         if ( other != p )
6002         {
6003             int color[3] = { other->r, other->g, other->b };
6004             int currentDiff = se_ColorDistance( currentRGB, color );
6005             int newDiff     = se_ColorDistance( newRGB, color );
6006             if ( currentDiff < currentMinDiff )
6007             {
6008                 currentMinDiff = currentDiff;
6009             }
6010             if ( newDiff < newMinDiff )
6011             {
6012                 newMinDiff = newDiff;
6013             }
6014         }
6015     }
6016 
6017     // update current color
6018     if ( currentMinDiff < newMinDiff )
6019     {
6020         for( int i = 2; i >= 0; --i )
6021         {
6022             l->rgb[i] = newRGB[i];
6023         }
6024     }
6025 }
6026 
6027 static nSettingItem<int> se_pingCharityServerConf("PING_CHARITY_SERVER",sn_pingCharityServer );
6028 static nVersionFeature   se_pingCharityServerControlled( 14 );
6029 
6030 static int se_pingCharityMax = 500, se_pingCharityMin = 0;
6031 static tSettingItem<int> se_pingCharityMaxConf( "PING_CHARITY_MAX", se_pingCharityMax );
6032 static tSettingItem<int> se_pingCharityMinConf( "PING_CHARITY_MIN", se_pingCharityMin );
6033 
se_ForceSpectate(REAL & time,REAL minTime,char const * message,char const * & cantPlayMessage,int & experienceNeeded)6034 static bool se_ForceSpectate( REAL & time, REAL minTime, char const * message, char const * & cantPlayMessage, int & experienceNeeded )
6035 {
6036     if ( time < 0 )
6037     {
6038         time = 0;
6039     }
6040 
6041     if ( time >= minTime )
6042     {
6043         return false;
6044     }
6045     else
6046     {
6047         // store message. The player should be presented with a single
6048         // message telling him the easyest way to gain the required experience;
6049         // if 10 minutes of total play time are needed and 5 minutes of online play time,
6050         // we tell the player 10 more minutes of online play are required.
6051         int needed = int( minTime - time + 2 );
6052         if ( needed > experienceNeeded )
6053         {
6054             experienceNeeded = needed;
6055         }
6056         cantPlayMessage = message;
6057 
6058         return true;
6059     }
6060 }
6061 
6062 // Update the netPlayer_id list
Update()6063 void ePlayerNetID::Update(){
6064 #ifdef KRAWALL_SERVER
6065     // update access level
6066     UpdateAccessLevelRequiredToPlay();
6067 
6068     // remove players with insufficient access rights
6069     tAccessLevel required = AccessLevelRequiredToPlay();
6070     for( int i=se_PlayerNetIDs.Len()-1; i >= 0; --i )
6071     {
6072         ePlayerNetID* player = se_PlayerNetIDs(i);
6073         if ( player->GetAccessLevel() > required && player->IsHuman() )
6074         {
6075             player->SetTeamWish(0);
6076         }
6077     }
6078 #endif
6079 
6080 #ifdef DEDICATED
6081     if (sr_glOut)
6082 #endif
6083     {
6084         // the last update time
6085         static nTimeRolling lastTime = tSysTimeFloat();
6086         bool playing = false; // set to true if someone is playing
6087         bool teamPlay = false; // set to true if someone is playing in a team
6088 
6089         char const * cantPlayMessage = NULL;
6090         int experienceNeeded = 0;
6091 
6092         for(int i=0; i<MAX_PLAYERS; ++i ){
6093             bool in_game=ePlayer::PlayerIsInGame(i);
6094             ePlayer *local_p=ePlayer::PlayerConfig(i);
6095             tASSERT(local_p);
6096             tCONTROLLED_PTR(ePlayerNetID) &p=local_p->netPlayer;
6097 
6098             if (!p && in_game && ( !local_p->spectate || se_VisibleSpectatorsSupported() ) ) // insert new player
6099             {
6100                 // reset last time so idle time in the menus does not count as play time
6101                 lastTime = tSysTimeFloat();
6102 
6103                 p=tNEW(ePlayerNetID) (i);
6104                 p->FindDefaultTeam();
6105                 p->RequestSync();
6106             }
6107 
6108             if (bool(p) && (!in_game || ( local_p->spectate && !se_VisibleSpectatorsSupported() ) ) && // remove player
6109                     p->Owner() == ::sn_myNetID )
6110             {
6111                 p->RemoveFromGame();
6112 
6113                 if (p->object)
6114                     p->object->player = NULL;
6115 
6116                 p->object = NULL;
6117                 p = NULL;
6118             }
6119 
6120             if (bool(p) && in_game){ // update
6121                 p->favoriteNumberOfPlayersPerTeam=ePlayer::PlayerConfig(i)->favoriteNumberOfPlayersPerTeam;
6122                 p->nameTeamAfterMe=ePlayer::PlayerConfig(i)->nameTeamAfterMe;
6123 
6124                 if ( se_randomizeColor )
6125                 {
6126                     se_RandomizeColor(local_p,p);
6127                 }
6128 
6129                 p->r=ePlayer::PlayerConfig(i)->rgb[0];
6130                 p->g=ePlayer::PlayerConfig(i)->rgb[1];
6131                 p->b=ePlayer::PlayerConfig(i)->rgb[2];
6132 
6133                 sg_ClampPingCharity();
6134                 p->pingCharity=::pingCharity;
6135 
6136                 // update spectator status
6137                 bool spectate = local_p->spectate;
6138 
6139                 // force to spectator mode if the player lacks experience
6140                 if ( !spectate )
6141                 {
6142                     se_ForceSpectate( se_playTimeTotal, se_minPlayTimeTotal, "$play_time_total_lacking", cantPlayMessage, experienceNeeded );
6143                     se_ForceSpectate( se_playTimeOnline, se_minPlayTimeOnline, "$play_time_online_lacking", cantPlayMessage, experienceNeeded  );
6144                     se_ForceSpectate( se_playTimeTeam, se_minPlayTimeTeam, "$play_time_team_lacking", cantPlayMessage, experienceNeeded );
6145                     if ( cantPlayMessage )
6146                     {
6147                         spectate = true;
6148 
6149                         // print message to console (but don't spam)
6150                         static nTimeRolling lastTime = -100;
6151                         nTimeRolling now = tSysTimeFloat();
6152                         if ( now - lastTime > 30 )
6153                         {
6154                             lastTime = now;
6155                             con << tOutput( cantPlayMessage, experienceNeeded );
6156                         }
6157                     }
6158                 }
6159 
6160                 if ( p->spectating_ != spectate )
6161                     p->RequestSync();
6162                 p->spectating_ = spectate;
6163 
6164                 // set playing flags
6165                 if ( !spectate )
6166                 {
6167                     playing = true;
6168                 }
6169 
6170 
6171                 if ( p->CurrentTeam() && p->CurrentTeam()->NumHumanPlayers() > 1 )
6172                 {
6173                     teamPlay = true;
6174                 }
6175 
6176                 // update stealth status
6177                 if ( p->stealth_ != local_p->stealth )
6178                     p->RequestSync();
6179                 p->stealth_ = local_p->stealth;
6180 
6181                 // update name
6182                 tString newName( ePlayer::PlayerConfig(i)->Name() );
6183 
6184                 if ( ::sn_GetNetState() != nCLIENT || newName != p->nameFromClient_ )
6185                 {
6186                     p->RequestSync();
6187                 }
6188 
6189                 p->SetName( local_p->Name() );
6190             }
6191         }
6192 
6193         // account for play time
6194         nTimeRolling now = tSysTimeFloat();
6195         REAL time = (now - lastTime)/60.0f;
6196         lastTime = now;
6197 
6198         if ( playing )
6199         {
6200             // all activity gets logged here
6201             se_playTimeTotal += time;
6202 
6203             if ( sn_GetNetState() == nCLIENT )
6204             {
6205                 // all online activity counts here
6206                 se_playTimeOnline += time;
6207 
6208                 if ( teamPlay )
6209                 {
6210                     // all online team play counts here
6211                     se_playTimeTeam += time;
6212                 }
6213             }
6214         }
6215     }
6216 
6217     int i;
6218 
6219 
6220     // update the ping charity, but
6221     // don't do so on the client if the server controls the ping charity completely
6222     if ( sn_GetNetState() == nSERVER || !se_pingCharityServerControlled.Supported(0) )
6223     {
6224         int old_c=sn_pingCharityServer;
6225         sg_ClampPingCharity();
6226         sn_pingCharityServer=::pingCharity;
6227 
6228 #ifndef DEDICATED
6229         if (sn_GetNetState()==nCLIENT)
6230 #endif
6231             sn_pingCharityServer+=100000;
6232 
6233         // set configurable maximum
6234         if ( se_pingCharityServerControlled.Supported() )
6235         {
6236             sn_pingCharityServer = se_pingCharityMax;
6237         }
6238 
6239         for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
6240             ePlayerNetID *pni=se_PlayerNetIDs(i);
6241             pni->UpdateName();
6242             int new_ps=pni->pingCharity;
6243             new_ps+=int(pni->ping*500);
6244 
6245             // only take ping charity into account for non-spectators
6246             if ( sn_GetNetState() != nSERVER || pni->currentTeam || pni->nextTeam )
6247                 if (new_ps < sn_pingCharityServer)
6248                     sn_pingCharityServer=new_ps;
6249         }
6250         if (sn_pingCharityServer<0)
6251             sn_pingCharityServer=0;
6252 
6253         // set configurable minimum
6254         if ( se_pingCharityServerControlled.Supported() )
6255         {
6256             if ( sn_pingCharityServer < se_pingCharityMin )
6257                 sn_pingCharityServer = se_pingCharityMin;
6258         }
6259 
6260         if (old_c!=sn_pingCharityServer)
6261         {
6262             tOutput o;
6263             o.SetTemplateParameter(1, old_c);
6264             o.SetTemplateParameter(2, sn_pingCharityServer);
6265             o << "$player_pingcharity_changed";
6266             con << o;
6267 
6268             // trigger transmission to the clients
6269             if (sn_GetNetState()==nSERVER)
6270                 se_pingCharityServerConf.nConfItemBase::WasChanged(true);
6271         }
6272     }
6273 
6274     // update team assignment
6275     bool assigned = true;
6276     while ( assigned && sn_GetNetState() != nCLIENT )
6277     {
6278         assigned = false;
6279 
6280         int players = se_PlayerNetIDs.Len();
6281 
6282         // start with the players that came in earlier
6283         for( i=0; i<players; ++i )
6284         {
6285             ePlayerNetID* player = se_PlayerNetIDs(i);
6286 
6287             // only assign new team if it is possible
6288             if ( player->NextTeam() != player->CurrentTeam() && player->TeamChangeAllowed() &&
6289                     ( !player->NextTeam() || player->NextTeam()->PlayerMayJoin( player ) )
6290                )
6291             {
6292                 player->UpdateTeam();
6293                 if ( player->NextTeam() == player->CurrentTeam() )
6294                     assigned = true;
6295             }
6296         }
6297 
6298         // assign players that are not currently on a team, but want to, to any team. Start with the late comers here.
6299         if ( !assigned )
6300         {
6301             for( i=players-1; i>=0; --i )
6302             {
6303                 ePlayerNetID* player = se_PlayerNetIDs(i);
6304 
6305                 // if the player is not currently on a team, but wants to join a specific team, let it join any, but keep the wish stored
6306                 if ( player->NextTeam() && !player->CurrentTeam() && player->TeamChangeAllowed() )
6307                 {
6308                     eTeam * wish = player->NextTeam();
6309                     bool assignBack = se_assignTeamAutomatically;
6310                     se_assignTeamAutomatically = true;
6311                     player->FindDefaultTeam();
6312                     se_assignTeamAutomatically = assignBack;
6313                     player->SetTeamForce( wish );
6314 
6315                     if ( player->CurrentTeam() )
6316                     {
6317                         assigned = true;
6318                         break;
6319                     }
6320                 }
6321             }
6322         }
6323     }
6324 
6325     if ( sn_GetNetState() != nCLIENT )
6326     {
6327         for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
6328         {
6329             ePlayerNetID* player = se_PlayerNetIDs(i);
6330 
6331             // announce unfullfilled wishes
6332             if ( player->NextTeam() != player->CurrentTeam() && player->NextTeam() )
6333             {
6334                 //if the player can't change teams delete the team wish
6335                 if(!player->TeamChangeAllowed()) {
6336                     player->SetTeam( player->CurrentTeam() );
6337                     continue;
6338                 }
6339 
6340                 tOutput message( "$player_joins_team_wish",
6341                                  player->GetName(),
6342                                  player->NextTeam()->Name() );
6343 
6344                 sn_ConsoleOut( message );
6345 
6346                 // if team change is futile because team play is disabled,
6347                 // delete the team change wish
6348                 if ( eTeam::maxPlayers <= 1 )
6349                     player->SetTeam( player->CurrentTeam() );
6350             }
6351         }
6352     }
6353 
6354     // update the teams as well
6355     for (i=eTeam::teams.Len()-1; i>=0; --i)
6356     {
6357         eTeam::teams(i)->UpdateProperties();
6358     }
6359 
6360     // get rid of deleted netobjects
6361     nNetObject::ClearAllDeleted();
6362 }
6363 
6364 // wait at most this long for any player to leave chat state...
6365 static REAL se_playerWaitMax      = 10.0f;
6366 static tSettingItem< REAL > se_playerWaitMaxConf( "PLAYER_CHAT_WAIT_MAX", se_playerWaitMax );
6367 
6368 // and no more than this much measured relative to his non-chatting time.
6369 static REAL se_playerWaitFraction = .05f;
6370 static tSettingItem< REAL > se_playerWaitFractionConf( "PLAYER_CHAT_WAIT_FRACTION", se_playerWaitFraction );
6371 
6372 // flag to only account one player for the chat break
6373 static bool se_playerWaitSingle = false;
6374 static tSettingItem< bool > se_playerWaitSingleConf( "PLAYER_CHAT_WAIT_SINGLE", se_playerWaitSingle );
6375 
6376 // flag to only let the team leader pause the timer
6377 static bool se_playerWaitTeamleader = true;
6378 static tSettingItem< bool > se_playerWaitTeamleaderConf( "PLAYER_CHAT_WAIT_TEAMLEADER", se_playerWaitTeamleader );
6379 
6380 // wait for players to leave chat state
WaitToLeaveChat()6381 bool ePlayerNetID::WaitToLeaveChat()
6382 {
6383     static bool lastReturn = false;
6384     static double lastTime = 0;
6385     static ePlayerNetID * lastPlayer = 0; // the last player that caused a pause. Use only for comparison, the pointer may be bad. Don't dereference!
6386     double time = tSysTimeFloat();
6387     REAL dt = time - lastTime;
6388     bool ret = false;
6389 
6390     if ( !lastReturn )
6391     {
6392         // account for non-break pause: give players additional pause time
6393         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
6394         {
6395             ePlayerNetID* player = se_PlayerNetIDs(i);
6396             if ( dt > 1.0 || !player->chatting_ )
6397             {
6398                 player->wait_ += se_playerWaitFraction * dt;
6399                 if ( player->wait_ > se_playerWaitMax )
6400                 {
6401                     player->wait_ = se_playerWaitMax;
6402                 }
6403             }
6404         }
6405 
6406         if ( dt > 1.0 )
6407             lastPlayer = 0;
6408 
6409         dt = 0;
6410     }
6411 
6412     // the chatting player with the most wait seconds left
6413     ePlayerNetID * maxPlayer = 0;
6414     REAL maxWait = .2;
6415 
6416     // iterate over chatting players
6417     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
6418     {
6419         ePlayerNetID* player = se_PlayerNetIDs(i);
6420         if ( player->CurrentTeam() && !player->IsSilenced() && player->chatting_ && ( !se_playerWaitTeamleader || player->CurrentTeam()->OldestHumanPlayer() == player ) )
6421         {
6422             // account for waiting time if everyone is to get his time reduced
6423             if ( !se_playerWaitSingle )
6424             {
6425                 player->wait_ -= dt;
6426 
6427                 // hold back
6428                 if ( player->wait_ > 0 )
6429                 {
6430                     ret = true;
6431                 }
6432                 else
6433                 {
6434                     player->wait_ = 0;
6435                 }
6436             }
6437 
6438             // determine player we'll wait for longest
6439             if (  ( maxPlayer != lastPlayer || NULL == maxPlayer ) && ( player->wait_ > maxWait || player == lastPlayer ) && player->wait_ > 0 )
6440             {
6441                 maxWait = player->wait_;
6442                 maxPlayer = player;
6443             }
6444         }
6445     }
6446 
6447     // account for waiting time if only one player should get his waiting time reduced
6448     if ( se_playerWaitSingle && maxPlayer )
6449     {
6450         maxPlayer->wait_ -= dt;
6451 
6452         // hold back
6453         if ( maxPlayer->wait_ < 0 )
6454         {
6455             maxPlayer->wait_ = 0;
6456         }
6457     }
6458 
6459     static double lastPrint = -2;
6460 
6461     // print information: who are we waiting for?
6462     if ( maxPlayer && maxPlayer != lastPlayer && tSysTimeFloat() - lastPrint > 1 )
6463     {
6464         sn_ConsoleOut( tOutput( "$gamestate_chat_wait", maxPlayer->GetName(), int(10*maxPlayer->wait_)*.1f ) );
6465         lastPlayer = maxPlayer;
6466     }
6467 
6468     if ( lastPlayer == maxPlayer )
6469     {
6470         lastPrint = tSysTimeFloat();
6471     }
6472 
6473     // store values for future reference
6474     lastReturn = ret;
6475     lastTime = time;
6476 
6477     return ret;
6478 }
6479 
6480 // time in chat mode before a player is no longer spawned
6481 static REAL se_chatterRemoveTime = 180.0;
6482 static tSettingItem< REAL > se_chatterRemoveTimeConf( "CHATTER_REMOVE_TIME", se_chatterRemoveTime );
6483 
6484 // time without keypresses before a player is no longer spawned
6485 static REAL se_idleRemoveTime = 0;
6486 static tSettingItem< REAL > se_idleRemoveTimeConf( "IDLE_REMOVE_TIME", se_idleRemoveTime );
6487 
6488 // time without keypresses before a player is kicked
6489 static REAL se_idleKickTime = 0;
6490 static tSettingItem< REAL > se_idleKickTimeConf( "IDLE_KICK_TIME", se_idleKickTime );
6491 
6492 //removes chatbots and idling players from the game
RemoveChatbots()6493 void ePlayerNetID::RemoveChatbots()
6494 {
6495     // nothing to be done on the clients
6496     if ( nCLIENT == sn_GetNetState() )
6497         return;
6498 
6499 #ifdef KRAWALL_SERVER
6500     // update access level
6501     UpdateAccessLevelRequiredToPlay();
6502 #endif
6503 
6504     // determine the length of the last round
6505     static double lastTime = 0;
6506     double currentTime = tSysTimeFloat();
6507     REAL roundTime = currentTime - lastTime;
6508     lastTime = currentTime;
6509 
6510     // go through all players that don't have a team assigned currently, and assign one
6511     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
6512     {
6513         ePlayerNetID *p = se_PlayerNetIDs(i);
6514         if ( p && p->IsHuman() )
6515         {
6516             // time allowed to be idle
6517             REAL idleTime = p->IsChatting() ? se_chatterRemoveTime : se_idleRemoveTime;
6518 
6519             // determine whether the player should have a team
6520             bool shouldHaveTeam = idleTime <= 0 || p->LastActivity() - roundTime < idleTime;
6521             shouldHaveTeam &= !p->IsSpectating();
6522 
6523             tColoredString name;
6524             name << *p << tColoredString::ColorString(1,.5,.5);
6525 
6526             // see to it that the player has or has not a team.
6527             if ( shouldHaveTeam )
6528             {
6529                 if ( !p->CurrentTeam() )
6530                 {
6531                     p->FindDefaultTeam();
6532                 }
6533             }
6534             else
6535             {
6536                 if ( p->CurrentTeam() )
6537                 {
6538                     p->SetTeam( NULL );
6539                     p->UpdateTeam();
6540                 }
6541             }
6542 
6543             // kick idle players (Removes player from list, this must be the last operation of the loop)
6544             if ( se_idleKickTime > 0 && se_idleKickTime < p->LastActivity() - roundTime )
6545             {
6546                 sn_KickUser( p->Owner(), tOutput( "$network_kill_idle" ) );
6547 
6548                 // if many players get kicked with one client, the array may have changed
6549                 if ( i >= se_PlayerNetIDs.Len() )
6550                     i = se_PlayerNetIDs.Len()-1;
6551             }
6552         }
6553     }
6554 }
6555 
ThrowOutDisconnected()6556 void ePlayerNetID::ThrowOutDisconnected()
6557 {
6558     int i;
6559     // find all disconnected players
6560 
6561     for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
6562         ePlayerNetID *pni=se_PlayerNetIDs(i);
6563         if (pni->disconnected)
6564         {
6565             // remove it from the list of players (so it won't be deleted twice...)
6566             se_PlayerNetIDs.Remove(pni, pni->listID);
6567         }
6568     }
6569 
6570     se_PlayerReferences.ReleaseAll();
6571 }
6572 
GetScoreFromDisconnectedCopy()6573 void ePlayerNetID::GetScoreFromDisconnectedCopy()
6574 {
6575     int i;
6576     // find a copy
6577     for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
6578         ePlayerNetID *pni=se_PlayerNetIDs(i);
6579         if (pni->disconnected && pni->GetUserName() == GetUserName() && pni->Owner() == 0)
6580         {
6581 #ifdef DEBUG
6582             con << GetName() << " reconnected.\n";
6583 #endif
6584 
6585             pni->RequestSync();
6586             RequestSync();
6587 
6588             score = pni->score;
6589 
6590             ControlObject(pni->Object());
6591             // object->ePlayer = this;
6592             pni->object = NULL;
6593 
6594             if (bool(object))
6595                 chatting_ = true;
6596 
6597             pni->disconnected = false;
6598             se_PlayerNetIDs.Remove(pni, pni->listID);
6599             se_PlayerReferences.Remove( pni );        // really delete it without a message
6600         }
6601     }
6602 }
6603 
6604 
6605 static bool show_scores=false;
6606 static bool ass=true;
6607 
se_AutoShowScores()6608 void se_AutoShowScores(){
6609     if (ass)
6610         show_scores=true;
6611 }
6612 
6613 
se_UserShowScores(bool show)6614 void se_UserShowScores(bool show){
6615     show_scores=show;
6616 }
6617 
se_SetShowScoresAuto(bool a)6618 void se_SetShowScoresAuto(bool a){
6619     ass=a;
6620 }
6621 
6622 
scores()6623 static void scores(){
6624     if (show_scores){
6625         if ( se_mainGameTimer )
6626             ePlayerNetID::DisplayScores();
6627         else
6628             show_scores = false;
6629     }
6630 }
6631 
6632 
6633 static rPerFrameTask pf(&scores);
6634 
force_small_cons()6635 static bool force_small_cons(){
6636     return show_scores;
6637 }
6638 
6639 static rSmallConsoleCallback sc(&force_small_cons);
6640 
cd()6641 static void cd(){
6642     show_scores = false;
6643 }
6644 
6645 
6646 
6647 static uActionGlobal score("SCORE");
6648 
6649 
sf(REAL x)6650 static bool sf(REAL x){
6651     if (x>0) show_scores = !show_scores;
6652     return true;
6653 }
6654 
6655 static uActionGlobalFunc saf(&score,&sf);
6656 
6657 
6658 static rCenterDisplayCallback c_d(&cd);
6659 
operator <<(tOutput & o,const ePlayerNetID & p)6660 tOutput& operator << (tOutput& o, const ePlayerNetID& p)
6661 {
6662     tColoredString x;
6663     x << p;
6664     o << x;
6665     return o;
6666 }
6667 
6668 
6669 
6670 tCallbackString *eCallbackGreeting::anchor = NULL;
6671 ePlayerNetID* eCallbackGreeting::greeted = NULL;
6672 
Greet(ePlayerNetID * player)6673 tString eCallbackGreeting::Greet(ePlayerNetID* player)
6674 {
6675     greeted = player;
6676     return Exec(anchor);
6677 }
6678 
eCallbackGreeting(STRINGRETFUNC * f)6679 eCallbackGreeting::eCallbackGreeting(STRINGRETFUNC* f)
6680         :tCallbackString(anchor, f)
6681 {
6682 }
6683 
GreetHighscores(tString & s)6684 void ePlayerNetID::GreetHighscores(tString &s){
6685     s << eCallbackGreeting::Greet(this);
6686     //  tOutput o;
6687     //  gHighscoresBase::Greet(this,o);
6688     //  s << o;
6689 }
6690 
6691 
6692 // *******************
6693 // *      chatting_   *
6694 // *******************
SetChatting(ChatFlags flag,bool chatting)6695 void ePlayerNetID::SetChatting ( ChatFlags flag, bool chatting )
6696 {
6697     if ( sn_GetNetState() == nSTANDALONE && flag == ChatFlags_Menu )
6698     {
6699         chatting = false;
6700     }
6701 
6702     if ( chatting )
6703     {
6704         chatFlags_ |= flag;
6705         if ( !chatting_ )
6706             this->RequestSync();
6707 
6708         chatting_ = true;
6709     }
6710     else
6711     {
6712         chatFlags_ &= ~flag;
6713         if ( 0 == chatFlags_ )
6714         {
6715             if ( chatting_ )
6716                 this->RequestSync();
6717 
6718             chatting_ = false;
6719         }
6720     }
6721 }
6722 
6723 // *******************
6724 // * team management *
6725 // *******************
6726 
TeamChangeAllowed(bool informPlayer) const6727 bool ePlayerNetID::TeamChangeAllowed( bool informPlayer ) const {
6728     if (!( allowTeamChange_ || se_allowTeamChanges ))
6729     {
6730         if ( informPlayer )
6731         {
6732             sn_ConsoleOut(tOutput("$player_teamchanges_disallowed"), Owner());
6733         }
6734         return false;
6735     }
6736 
6737     int suspended = GetSuspended();
6738     if ( suspended > 0 )
6739     {
6740         if ( informPlayer )
6741         {
6742             sn_ConsoleOut(tOutput("$player_teamchanges_suspended", suspended ), Owner());
6743         }
6744         return false;
6745     }
6746 
6747 #ifdef KRAWALL_SERVER
6748        // only allow players with enough access level to enter the game, everyone is free to leave, though
6749     if (!( GetAccessLevel() <= AccessLevelRequiredToPlay() || CurrentTeam() ))
6750     {
6751         if ( informPlayer )
6752         {
6753             sn_ConsoleOut(tOutput("$player_teamchanges_accesslevel",
6754                                   tCurrentAccessLevel::GetName( GetAccessLevel() ),
6755                                   tCurrentAccessLevel::GetName( AccessLevelRequiredToPlay() ) ),
6756                           Owner());
6757         }
6758         return false;
6759     }
6760 #endif
6761 
6762     return true;
6763 }
6764 
6765 // put a new player into a default team
FindDefaultTeam()6766 void ePlayerNetID::FindDefaultTeam( )
6767 {
6768     // only the server should do this, the client does not have the full information on how to do do it right.
6769     if ( sn_GetNetState() == nCLIENT || !se_assignTeamAutomatically || spectating_ || !TeamChangeAllowed() )
6770         return;
6771 
6772     static bool recursion = false;
6773     if ( recursion )
6774     {
6775         return;
6776     }
6777 
6778     if ( !IsHuman() )
6779     {
6780         SetTeam( NULL );
6781         return;
6782     }
6783 
6784     recursion = true;
6785 
6786     // find the team with the least number of players on it
6787     eTeam *min = NULL;
6788     for ( int i=eTeam::teams.Len()-1; i>=0; --i )
6789     {
6790         eTeam *t = eTeam::teams( i );
6791         if ( t->IsHuman() && !t->IsLocked() && ( !min || min->NumHumanPlayers() > t->NumHumanPlayers() ) )
6792             min = t;
6793     }
6794 
6795     if ( min &&
6796             eTeam::teams.Len() >= eTeam::minTeams &&
6797             min->PlayerMayJoin( this ) &&
6798             ( !eTeam::NewTeamAllowed() || ( min->NumHumanPlayers() > 0 && min->NumHumanPlayers() < favoriteNumberOfPlayersPerTeam ) )
6799        )
6800         SetTeamWish( min );             // join the team
6801     else if ( eTeam::NewTeamAllowed() )
6802         CreateNewTeamWish();            // create a new team
6803 
6804     // yes, if all teams are full and no team creation is allowed, the player stays without team and will not be spawned.
6805 
6806     recursion = false;
6807 }
6808 
6809 // register me in the given team (callable on the server)
SetTeam(eTeam * newTeam)6810 void ePlayerNetID::SetTeam( eTeam* newTeam )
6811 {
6812     // check if the team change is legal
6813     tASSERT ( !newTeam || nCLIENT !=  sn_GetNetState() );
6814 
6815     SetTeamForce( newTeam );
6816 
6817     if (newTeam && ( !newTeam->PlayerMayJoin( this ) || IsSpectating() ) )
6818     {
6819         tOutput message;
6820         message.SetTemplateParameter( 1, GetName() );
6821         if ( newTeam )
6822             message.SetTemplateParameter(2, newTeam->Name() );
6823         else
6824             message.SetTemplateParameter(2, "NULL");
6825         message << "$player_nojoin_team";
6826 
6827         sn_ConsoleOut( message, Owner() );
6828         // return;
6829     }
6830 }
6831 
6832 // register me in the given team (callable on the server)
SetTeamForce(eTeam * newTeam)6833 void ePlayerNetID::SetTeamForce( eTeam* newTeam )
6834 {
6835     // check if the team change is legal
6836     tASSERT ( !newTeam || nCLIENT !=  sn_GetNetState() );
6837 
6838     nextTeam = newTeam;
6839 }
6840 
6841 // register me in the given team (callable on the server)
UpdateTeam()6842 void ePlayerNetID::UpdateTeam()
6843 {
6844     // check if work is needed
6845     if ( nextTeam == currentTeam )
6846     {
6847         return;
6848     }
6849 
6850     // check if the team change is legal
6851     if ( nCLIENT ==  sn_GetNetState() )
6852     {
6853         return;
6854     }
6855 
6856     if ( bool( nextTeam ) && !nextTeam->PlayerMayJoin( this ) )
6857     {
6858         tOutput message;
6859         message.SetTemplateParameter(1, GetName() );
6860         if ( nextTeam )
6861             message.SetTemplateParameter(2, nextTeam->Name() );
6862         else
6863             message.SetTemplateParameter(2, "NULL");
6864         message << "$player_nojoin_team";
6865 
6866         sn_ConsoleOut( message, Owner() );
6867         return;
6868     }
6869 
6870     UpdateTeamForce();
6871 }
6872 
UpdateTeamForce()6873 void ePlayerNetID::UpdateTeamForce()
6874 {
6875     // check if work is needed
6876     if ( nextTeam == currentTeam )
6877     {
6878         return;
6879     }
6880 
6881     eTeam *oldTeam = currentTeam;
6882 
6883     if ( nextTeam )
6884         nextTeam->AddPlayer ( this );
6885     else if ( oldTeam )
6886         oldTeam->RemovePlayer( this );
6887 
6888     if( nCLIENT !=  sn_GetNetState() && GetRefcount() > 0 )
6889     {
6890         RequestSync();
6891     }
6892 }
6893 
6894 // teams this player is invited to
GetInvitations() const6895 ePlayerNetID::eTeamSet const & ePlayerNetID::GetInvitations() const
6896 {
6897     return invitations_;
6898 }
6899 
6900 // sends a team change notification message to everyone's console
se_TeamChangeMessage(ePlayerNetID const & player)6901 static void se_TeamChangeMessage( ePlayerNetID const & player )
6902 {
6903     if ( player.NextTeam() )
6904     {
6905         // send notification
6906         tOutput message;
6907         tString playerName = tColoredString::RemoveColors(player.GetName());
6908         tString teamName = tColoredString::RemoveColors(player.NextTeam()->Name());
6909         message.SetTemplateParameter(1, playerName );
6910         if ( playerName != teamName )
6911         {
6912             message.SetTemplateParameter(2, teamName );
6913             message << "$player_joins_team";
6914         }
6915         else
6916         {
6917             message << "$player_joins_team_solo";
6918         }
6919 
6920         sn_ConsoleOut( message );
6921     }
6922 }
6923 
6924 // create a new team and join it (on the server)
CreateNewTeam()6925 void ePlayerNetID::CreateNewTeam()
6926 {
6927     // check if the team change is legal
6928     tASSERT ( nCLIENT !=  sn_GetNetState() );
6929 
6930     if(!TeamChangeAllowed( true )) {
6931         return;
6932     }
6933 
6934     if ( !eTeam::NewTeamAllowed() ||
6935             ( bool( currentTeam ) && ( currentTeam->NumHumanPlayers() == 1 ) ) ||
6936             IsSpectating() )
6937     {
6938         tOutput message;
6939         message.SetTemplateParameter(1, GetName() );
6940         message << "$player_nocreate_team";
6941 
6942         sn_ConsoleOut( message, Owner() );
6943 
6944         if ( !currentTeam )
6945         {
6946             bool assignBack = se_assignTeamAutomatically;
6947             se_assignTeamAutomatically = true;
6948             FindDefaultTeam();
6949             se_assignTeamAutomatically = assignBack;
6950         }
6951 
6952         return;
6953     }
6954 
6955     // create the new team and join it
6956     tJUST_CONTROLLED_PTR< eTeam > newTeam = tNEW( eTeam );
6957     nextTeam = newTeam;
6958     nextTeam->AddScore( score );
6959 
6960     // directly if possible
6961     if ( !currentTeam )
6962     {
6963         UpdateTeam();
6964     }
6965     else
6966     {
6967         nextTeam->UpdateAppearance();
6968         se_TeamChangeMessage( *this );
6969     }
6970 }
6971 
6972 const unsigned short TEAMCHANGE = 0;
6973 const unsigned short NEW_TEAM   = 1;
6974 
6975 
6976 // express the wish to be part of the given team (always callable)
SetTeamWish(eTeam * newTeam)6977 void ePlayerNetID::SetTeamWish(eTeam* newTeam)
6978 {
6979     if ( nCLIENT ==  sn_GetNetState() && Owner() == sn_myNetID )
6980     {
6981         nMessage* m = NewControlMessage();
6982 
6983         (*m) << TEAMCHANGE;
6984         (*m) << newTeam;
6985 
6986         m->BroadCast();
6987     }
6988     else
6989     {
6990         SetTeam( newTeam );
6991 
6992         // directly join if possible to keep counts up to date
6993         if (!currentTeam)
6994             UpdateTeam();
6995     }
6996 }
6997 
6998 // express the wish to create a new team and join it
CreateNewTeamWish()6999 void ePlayerNetID::CreateNewTeamWish()
7000 {
7001     if ( nCLIENT ==  sn_GetNetState() )
7002     {
7003         nMessage* m = NewControlMessage();
7004 
7005         (*m) << NEW_TEAM;
7006 
7007         m->BroadCast();
7008     }
7009     else
7010         CreateNewTeam();
7011 
7012 }
7013 
7014 // receive the team control wish
ReceiveControlNet(nMessage & m)7015 void ePlayerNetID::ReceiveControlNet(nMessage &m)
7016 {
7017     short messageType;
7018     m >> messageType;
7019 
7020     switch (messageType)
7021     {
7022     case NEW_TEAM:
7023         {
7024             CreateNewTeam();
7025 
7026             break;
7027         }
7028     case TEAMCHANGE:
7029         {
7030             eTeam *newTeam;
7031 
7032             m >> newTeam;
7033 
7034             if(!TeamChangeAllowed( true )) {
7035                 break;
7036             }
7037 
7038             // annihilate team if it no longer is in the game
7039             if ( bool(newTeam) && newTeam->TeamID() < 0 )
7040                 newTeam = 0;
7041 
7042             // NULL team probably means that the change target does not
7043             // exist any more. Create a new team instead.
7044             if ( !newTeam )
7045             {
7046                 if ( currentTeam )
7047                     sn_ConsoleOut( tOutput( "$player_joins_team_noex", tColoredString::RemoveColors(GetName()) ), Owner() );
7048                 break;
7049             }
7050 
7051             // check if the resulting message is obnoxious
7052             bool redundant = ( nextTeam == newTeam );
7053             bool obnoxious = ( nextTeam != currentTeam || redundant );
7054 
7055             SetTeam( newTeam );
7056 
7057             // announce the change
7058             if ( bool(nextTeam) && !redundant )
7059             {
7060                 se_TeamChangeMessage( *this );
7061 
7062                 // count it as spam if it is obnoxious
7063                 if ( obnoxious )
7064                     chatSpam_.CheckSpam( 4.0, Owner(), tOutput("$spam_teamchage") );
7065             }
7066 
7067             break;
7068         }
7069     default:
7070         {
7071             tASSERT(0);
7072         }
7073     }
7074 }
7075 
Color(REAL & a_r,REAL & a_g,REAL & a_b) const7076 void ePlayerNetID::Color( REAL&a_r, REAL&a_g, REAL&a_b ) const
7077 {
7078     if ( ( static_cast<bool>(currentTeam) ) && ( currentTeam->IsHuman() ) )
7079     {
7080         REAL w = 5;
7081         REAL r_w = 2;
7082         REAL g_w = 1;
7083         REAL b_w = 2;
7084 
7085         int r = this->r;
7086         int g = this->g;
7087         int b = this->b;
7088 
7089         // don't tolerate color overflow in a real team
7090         if ( currentTeam->NumPlayers() > 1 )
7091         {
7092             if ( r > 15 )
7093                 r = 15;
7094             if ( g > 15 )
7095                 g = 15;
7096             if ( b > 15 )
7097                 b = 15;
7098         }
7099 
7100         a_r=(r_w*r + w*currentTeam->R())/( 15.0 * ( w + r_w ) );
7101         a_g=(g_w*g + w*currentTeam->G())/( 15.0 * ( w + g_w ) );
7102         a_b=(b_w*b + w*currentTeam->B())/( 15.0 * ( w + b_w ) );
7103     }
7104     else
7105     {
7106         a_r = r/15.0;
7107         a_g = g/15.0;
7108         a_b = b/15.0;
7109     }
7110 }
7111 
TrailColor(REAL & a_r,REAL & a_g,REAL & a_b) const7112 void ePlayerNetID::TrailColor( REAL&a_r, REAL&a_g, REAL&a_b ) const
7113 {
7114     Color( a_r, a_g, a_b );
7115 
7116     /*
7117     if ( ( static_cast<bool>(currentTeam) ) && ( currentTeam->IsHuman() ) )
7118     {
7119 int w = 6;
7120         a_r=(2*r + w*currentTeam->R())/( 15.0 * ( w + 2 ) );
7121         a_g=(2*g + w*currentTeam->G())/( 15.0 * ( w + 2 ) );
7122         _b=(2*b + w*currentTeam->B())/( 15.0 * ( w + 2 ) );
7123     }
7124     else
7125     {
7126         a_r = r/15.0;
7127         a_g = g/15.0;
7128         a_b = b/15.0;
7129     }
7130     */
7131 }
7132 
7133 /*
7134 void ePlayerNetID::AddRef()
7135 {
7136     nNetObject::AddRef();
7137 }
7138 
7139 void ePlayerNetID::Release()
7140 {
7141     nNetObject::Release();
7142 }
7143 */
7144 
7145 // reads a network ID from the stream, either the number or my user name
se_ReadUser(std::istream & s,ePlayerNetID * requester=0)7146 static unsigned short se_ReadUser( std::istream &s, ePlayerNetID * requester = 0 )
7147 {
7148     // read name of player to be kicked
7149     tString name;
7150     s >> name;
7151 
7152     // try to convert it into a number and reference it as that
7153     int num = name.toInt();
7154     if ( num >= 1 && num <= MAXCLIENTS && sn_Connections[num].socket )
7155     {
7156         return num;
7157     }
7158     else
7159     {
7160         // standard name lookup
7161         ePlayerNetID * p = ePlayerNetID::FindPlayerByName( name, requester );
7162         if ( p )
7163         {
7164             return p->Owner();
7165         }
7166     }
7167 
7168     return 0;
7169 }
7170 
se_PlayerMessageConf(std::istream & s)7171 static void se_PlayerMessageConf(std::istream &s)
7172 {
7173     if ( se_NeedsServer( "PLAYER_MESSAGE", s ) )
7174     {
7175         return;
7176     }
7177 
7178     int receiver = se_ReadUser( s );
7179 
7180     tColoredString msg;
7181     s >> msg;
7182 
7183     if ( receiver <= 0 || s.good() )
7184     {
7185         con << tOutput("$player_message_usage");
7186         return;
7187     }
7188 
7189     msg << '\n';
7190 
7191     sn_ConsoleOut(msg, 0);
7192     sn_ConsoleOut(msg, receiver);
7193 }
7194 
7195 static tConfItemFunc se_PlayerMessage_c("PLAYER_MESSAGE", &se_PlayerMessageConf);
7196 static tAccessLevelSetter se_messConfLevel( se_PlayerMessage_c, tAccessLevel_Moderator );
7197 
7198 static tString se_defaultKickReason("");
7199 static tConfItemLine se_defaultKickReasonConf( "DEFAULT_KICK_REASON", se_defaultKickReason );
7200 
se_KickConf(std::istream & s)7201 static void se_KickConf(std::istream &s)
7202 {
7203     if ( se_NeedsServer( "KICK", s ) )
7204     {
7205         return;
7206     }
7207 
7208     // get user ID
7209     int num = se_ReadUser( s );
7210 
7211     tString reason = se_defaultKickReason;
7212     if ( !s.eof() )
7213         reason.ReadLine(s);
7214 
7215     // and kick.
7216     if ( num > 0 && !s.good() )
7217     {
7218         sn_KickUser( num ,  reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick" );
7219     }
7220     else
7221     {
7222         con << tOutput("$kick_usage");
7223         return;
7224     }
7225 }
7226 
7227 static tConfItemFunc se_kickConf("KICK",&se_KickConf);
7228 static tAccessLevelSetter se_kickConfLevel( se_kickConf, tAccessLevel_Moderator );
7229 
7230 static tString se_defaultKickToServer("");
7231 static int se_defaultKickToPort = 4534;
7232 static tString se_defaultKickToReason("");
7233 
7234 static tSettingItem< tString > se_defaultKickToServerConf( "DEFAULT_KICK_TO_SERVER", se_defaultKickToServer );
7235 static tSettingItem< int > se_defaultKickToPortConf( "DEFAULT_KICK_TO_PORT", se_defaultKickToPort );
7236 static tConfItemLine se_defaultKickToReasonConf( "DEFAULT_KICK_TO_REASON", se_defaultKickToReason );
7237 
se_MoveToConf(std::istream & s,REAL severity,const char * command)7238 static void se_MoveToConf(std::istream &s, REAL severity, const char * command )
7239 {
7240     if ( se_NeedsServer( "KICK/MOVE_TO", s ) )
7241     {
7242         return;
7243     }
7244 
7245     // get user ID
7246     int num = se_ReadUser( s );
7247 
7248     // read redirection target
7249     tString server = se_defaultKickToServer;
7250     if ( !s.eof() )
7251     {
7252         s >> server;
7253     }
7254 
7255     int pos, port = se_defaultKickToPort;
7256     if ( ( pos = server.StrPos( ":" ) ) != -1 )
7257     {
7258         port = atoi( server.SubStr( pos + 1 ) );
7259         server = server.SubStr( 0, pos );
7260     }
7261 
7262     nServerInfoRedirect redirect( server, port );
7263 
7264     tString reason = se_defaultKickToReason;
7265     if ( !s.eof() )
7266         reason.ReadLine(s);
7267 
7268     // and kick.
7269     if ( num > 0 && !s.good() )
7270     {
7271         sn_KickUser( num ,  reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick", severity, &redirect );
7272     }
7273     else
7274     {
7275         con << tOutput( "$kickmove_usage", command );
7276         return;
7277     }
7278 }
7279 
se_KickToConf(std::istream & s)7280 static void se_KickToConf(std::istream &s )
7281 {
7282     se_MoveToConf( s, 1, "KICK_TO" );
7283 }
7284 
7285 static tConfItemFunc se_kickToConf("KICK_TO",&se_KickToConf);
7286 static tAccessLevelSetter se_kickToConfLevel( se_kickToConf, tAccessLevel_Moderator );
7287 
se_MoveToConf(std::istream & s)7288 static void se_MoveToConf(std::istream &s )
7289 {
7290     se_MoveToConf( s, 0, "MOVE_TO" );
7291 }
7292 
7293 static tConfItemFunc se_moveToConf("MOVE_TO",&se_MoveToConf);
7294 static tAccessLevelSetter se_moveConfLevel( se_moveToConf, tAccessLevel_Moderator );
7295 
se_BanConf(std::istream & s)7296 static void se_BanConf(std::istream &s)
7297 {
7298     if ( se_NeedsServer( "BAN", s ) )
7299     {
7300         return;
7301     }
7302 
7303     // get user ID
7304     int num = se_ReadUser( s );
7305 
7306     if ( num == 0 && !s.good() )
7307     {
7308         con << tOutput( "$ban_usage" );
7309         return;
7310     }
7311 
7312     // read time to ban
7313     REAL banTime = 60;
7314     s >> banTime;
7315     std::ws(s);
7316 
7317     tString reason;
7318     reason.ReadLine(s);
7319 
7320     // and ban.
7321     if ( num > 0 )
7322     {
7323         nMachine::GetMachine( num ).Ban( banTime * 60, reason );
7324         sn_DisconnectUser( num , reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick" );
7325     }
7326 }
7327 
7328 static tConfItemFunc se_banConf("BAN",&se_BanConf);
7329 static tAccessLevelSetter se_banConfLevel( se_banConf, tAccessLevel_Moderator );
7330 
ReadPlayer(std::istream & s)7331 static ePlayerNetID * ReadPlayer( std::istream & s )
7332 {
7333     // read name of player to be returned
7334     tString name;
7335     s >> name;
7336 
7337     int num = name.toInt();
7338     if ( num > 0 )
7339     {
7340         // look for a player from that client
7341         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
7342         {
7343             ePlayerNetID* p = se_PlayerNetIDs(i);
7344 
7345             // check whether it's p who should be returned
7346             if ( p->Owner() == num )
7347             {
7348                 return p;
7349             }
7350         }
7351     }
7352 
7353     return ePlayerNetID::FindPlayerByName( name );
7354 }
7355 
Kill_conf(std::istream & s)7356 static void Kill_conf(std::istream &s)
7357 {
7358     if ( se_NeedsServer( "KILL", s, false ) )
7359     {
7360         return;
7361     }
7362 
7363     ePlayerNetID * p = ReadPlayer( s );
7364 
7365     if ( p && p->Object() && p->Object()->Alive() )
7366     {
7367         p->Object()->Kill();
7368         sn_ConsoleOut( tOutput( "$player_admin_kill", p->GetColoredName() ) );
7369     }
7370 }
7371 
7372 static tConfItemFunc kill_conf("KILL",&Kill_conf);
7373 static tAccessLevelSetter se_killConfLevel( kill_conf, tAccessLevel_Moderator );
7374 
Slap_conf(std::istream & s)7375 static void Slap_conf(std::istream &s)
7376 {
7377     if ( se_NeedsServer( "SLAP", s, false ) )
7378     {
7379         return;
7380     }
7381 
7382     ePlayerNetID * victim = ReadPlayer( s );
7383 
7384     if ( victim )
7385     {
7386         int points = 1;
7387 
7388         s >> points;
7389 
7390         if ( points == 0)
7391         {
7392             // oh well :)
7393             sn_ConsoleOut( tOutput("$player_admin_slap_free", victim->GetColoredName() ) );
7394         }
7395 
7396         tOutput win;
7397         tOutput lose;
7398         win << "$player_admin_slap_win";
7399         lose << "$player_admin_slap_lose";
7400         victim->AddScore( -points, win, lose );
7401     }
7402 }
7403 
7404 static tConfItemFunc slap_conf("SLAP", &Slap_conf);
7405 
7406 static int se_suspendDefault = 5;
7407 static tSettingItem< int > se_suspendDefaultConf( "SUSPEND_DEFAULT_ROUNDS", se_suspendDefault );
7408 
Suspend_conf_base(std::istream & s,int rounds)7409 static void Suspend_conf_base(std::istream &s, int rounds )
7410 {
7411     if ( se_NeedsServer( "SUSPEND", s, false ) )
7412     {
7413         return;
7414     }
7415 
7416     ePlayerNetID * p = ReadPlayer( s );
7417 
7418     if ( rounds > 0 )
7419     {
7420         s >> rounds;
7421     }
7422 
7423     if ( p )
7424     {
7425         p->Suspend( rounds );
7426     }
7427 }
7428 
Suspend_conf(std::istream & s)7429 static void Suspend_conf(std::istream &s )
7430 {
7431     Suspend_conf_base( s, se_suspendDefault );
7432 }
7433 
7434 
UnSuspend_conf(std::istream & s)7435 static void UnSuspend_conf(std::istream &s )
7436 {
7437     Suspend_conf_base( s, 0 );
7438 }
7439 
7440 static tConfItemFunc suspend_conf("SUSPEND",&Suspend_conf);
7441 static tAccessLevelSetter se_suspendConfLevel( suspend_conf, tAccessLevel_Moderator );
7442 
7443 static tConfItemFunc unsuspend_conf("UNSUSPEND",&UnSuspend_conf);
7444 static tAccessLevelSetter se_unsuspendConfLevel( unsuspend_conf, tAccessLevel_Moderator );
7445 
Silence_conf(std::istream & s)7446 static void Silence_conf(std::istream &s)
7447 {
7448     if ( se_NeedsServer( "SILENCE", s ) )
7449     {
7450         return;
7451     }
7452 
7453     ePlayerNetID * p = ReadPlayer( s );
7454     if ( p && !p->IsSilenced() )
7455     {
7456         sn_ConsoleOut( tOutput( "$player_silenced", p->GetColoredName() ) );
7457         p->SetSilenced( true );
7458     }
7459 }
7460 
7461 static tConfItemFunc silence_conf("SILENCE",&Silence_conf);
7462 static tAccessLevelSetter se_silenceConfLevel( silence_conf, tAccessLevel_Moderator );
7463 
Voice_conf(std::istream & s)7464 static void Voice_conf(std::istream &s)
7465 {
7466     if ( se_NeedsServer( "VOICE", s ) )
7467     {
7468         return;
7469     }
7470 
7471     ePlayerNetID * p = ReadPlayer( s );
7472     if ( p && p->IsSilenced() )
7473     {
7474         sn_ConsoleOut( tOutput( "$player_voiced", p->GetColoredName() ) );
7475         p->SetSilenced( false );
7476     }
7477 }
7478 
7479 static tConfItemFunc voice_conf("VOICE",&Voice_conf);
7480 static tAccessLevelSetter se_voiceConfLevel( voice_conf, tAccessLevel_Moderator );
7481 static tConfItemFunc unsilence_conf("UNSILENCE",&Voice_conf);
7482 static tAccessLevelSetter se_unsilenceConfLevel( unsilence_conf, tAccessLevel_Moderator );
7483 
7484 static tString sg_url;
7485 static tSettingItem< tString > sg_urlConf( "URL", sg_url );
7486 
7487 static tString sg_options("Nothing special.");
7488 #ifdef DEDICATED
7489 static tConfItemLine sg_optionsConf( "SERVER_OPTIONS", sg_options );
7490 #endif
7491 
7492 class gServerInfoAdmin: public nServerInfoAdmin
7493 {
7494 public:
gServerInfoAdmin()7495     gServerInfoAdmin(){}
7496 
7497 private:
GetUsers() const7498     virtual tString GetUsers() const
7499     {
7500         tString ret;
7501 
7502         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
7503         {
7504             ePlayerNetID* p = se_PlayerNetIDs(i);
7505             if ( p->IsHuman() )
7506             {
7507                 ret << p->GetName() << "\n";
7508             }
7509         }
7510 
7511         return ret;
7512     }
7513 
GetGlobalIDs() const7514     virtual tString GetGlobalIDs() const
7515     {
7516         tString ret;
7517 #ifdef KRAWALL_SERVER
7518         int count = 0;
7519 
7520         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
7521         {
7522             ePlayerNetID* p = se_PlayerNetIDs(i);
7523             if ( p->IsHuman() )
7524             {
7525                 if( p->IsAuthenticated() && !se_Hide(p, tAccessLevel_Default) )
7526                 {
7527                     for(; count > 0; --count)
7528                     {
7529                         ret << "\n";
7530                     }
7531                     ret << p->GetFilteredAuthenticatedName();
7532                 }
7533                 ++count;
7534             }
7535         }
7536 #endif
7537         return ret;
7538     }
7539 
GetOptions() const7540     virtual tString GetOptions() const
7541     {
7542         se_CutString( sg_options, 240 );
7543         return sg_options;
7544     }
7545 
GetUrl() const7546     virtual tString GetUrl() const
7547     {
7548         se_CutString( sg_url, 75 );
7549         return sg_url;
7550     }
7551 };
7552 
7553 static gServerInfoAdmin sg_serverAdmin;
7554 
7555 static eLadderLogWriter se_playerEnteredWriter("PLAYER_ENTERED", true);
7556 static eLadderLogWriter se_playerRenamedWriter("PLAYER_RENAMED", true);
7557 
7558 class eNameMessenger
7559 {
7560 public:
eNameMessenger(ePlayerNetID & player)7561     eNameMessenger( ePlayerNetID & player )
7562     : player_( player )
7563     , oldScreenName_( player.GetName() )
7564     , oldLogName_( player.GetLogName() )
7565     , adminRename_( !player.IsAllowedToRename() )
7566     {
7567         // store old name for password re-request and name change message
7568         oldPrintName_ << player_ << tColoredString::ColorString(.5,1,.5);
7569     }
7570 
~eNameMessenger()7571     ~eNameMessenger()
7572     {
7573         if ( sn_GetNetState() == nCLIENT )
7574         {
7575             return;
7576         }
7577 
7578         tString logName = player_.GetLogName();
7579         tString const & screenName = player_.GetName();
7580 
7581         // messages for the users
7582         tColoredString printName;
7583         printName << player_ << tColoredString::ColorString(.5,1,.5);
7584 
7585         tOutput mess;
7586 
7587         mess.SetTemplateParameter(1, printName);
7588         mess.SetTemplateParameter(2, oldPrintName_);
7589 
7590         // is the player new?
7591         if ( oldLogName_.Len() <= 1 && logName.Len() > 0 )
7592         {
7593             if ( player_.IsHuman() )
7594             {
7595                 se_playerEnteredWriter << logName << nMachine::GetMachine(player_.Owner()).GetIP() << screenName;
7596                 se_playerEnteredWriter.write();
7597 
7598                 player_.Greet();
7599 
7600                 // print spectating join message (regular join messages are handled by eTeam)
7601                 if ( ( player_.IsSpectating() || player_.IsSuspended() || !se_assignTeamAutomatically ) && ( se_assignTeamAutomatically || se_specSpam ) )
7602                 {
7603                     mess << "$player_entered_spectator";
7604                     sn_ConsoleOut(mess);
7605                 }
7606             }
7607         }
7608         else if ( logName != oldLogName_ || screenName != oldScreenName_ )
7609         {
7610             se_playerRenamedWriter << oldLogName_ << logName << nMachine::GetMachine(player_.Owner()).GetIP() << screenName;
7611             se_playerRenamedWriter.write();
7612 
7613             if ( oldScreenName_ != screenName )
7614             {
7615                 if ( bool(player_.GetVoter() ) )
7616                 {
7617                     player_.GetVoter()->PlayerChanged();
7618                 }
7619 
7620                 if ( adminRename_ )
7621                 {
7622                     // if our player isn't able to rename, he didn't do it alone, did he ? :)
7623                     mess << "$player_got_renamed";
7624                 }
7625                 else
7626                 {
7627                     mess << "$player_renamed";
7628                 }
7629                 sn_ConsoleOut(mess);
7630             }
7631         }
7632     }
7633 
7634     ePlayerNetID & player_;
7635     tString oldScreenName_, oldLogName_;
7636     tColoredString oldPrintName_;
7637     bool adminRename_;
7638 };
7639 
7640 // ******************************************************************************************
7641 // *
7642 // *    UpdateName
7643 // *
7644 // ******************************************************************************************
7645 //!
7646 //!
7647 // ******************************************************************************************
7648 
UpdateName(void)7649 void ePlayerNetID::UpdateName( void )
7650 {
7651     // don't do a thing if we're not fully synced
7652     if ( this->ID() == 0 && nameFromClient_.Len() <= 1 && sn_GetNetState() == nSERVER )
7653         return;
7654 
7655     // monitor name changes
7656     eNameMessenger messenger( *this );
7657 
7658     // apply client change, stripping excess spaces
7659     if ( sn_GetNetState() == nSTANDALONE
7660     || ( Owner() == 0 && sn_GetNetState() != nCLIENT )
7661     || (
7662             IsHuman()
7663          && ( sn_GetNetState() == nCLIENT || !messenger.adminRename_ )
7664          && ( sn_GetNetState() != nCLIENT || Owner() == sn_myNetID )
7665        )
7666        )
7667     {
7668         // apply name filters only on remote players
7669         if ( Owner() != 0 )
7670             se_OptionalNameFilters( nameFromClient_ );
7671 
7672         // nothing wrong ? proceed to renaming
7673         nameFromAdmin_ = nameFromServer_ = nameFromClient_;
7674     }
7675     else if ( sn_GetNetState() == nCLIENT )
7676     {
7677         // If we are in client mode, just follow our leader the server :)
7678         nameFromAdmin_ = nameFromClient_ = nameFromServer_;
7679     }
7680     else
7681     {
7682         // revert name
7683         nameFromClient_ = nameFromServer_ = nameFromAdmin_;
7684     }
7685     // remove colors from name
7686     tString newName = tColoredString::RemoveColors( nameFromServer_ );
7687     tString newUserName = se_UnauthenticatedUserName( newName );
7688 
7689     // test if it is already taken, find an alternative name if so.
7690     if ( sn_GetNetState() != nCLIENT && se_IsNameTaken( newUserName, this ) )
7691     {
7692         // Remove possilble trailing digit.
7693         if ( newName.Len() > 2 && isdigit(newName(newName.Len()-2)) )
7694         {
7695             newName = newName.SubStr( 0, newName.Len()-2 );
7696         }
7697 
7698         // append numbers until the name is free
7699         for ( int i=2; i<1000; ++i )
7700         {
7701             tString testName(newName);
7702             testName << i;
7703 
7704             // cut the beginning if the name is too long
7705             if ( testName.Len() > 17 )
7706                 testName = testName.SubStr( testName.Len() - 17 );
7707 
7708             newUserName = se_UnauthenticatedUserName( testName );
7709 
7710             if ( !se_IsNameTaken( newUserName, this ) )
7711             {
7712                 newName = testName;
7713                 break;
7714             }
7715         }
7716 
7717         // rudely overwrite name from client
7718         nameFromAdmin_ = nameFromServer_ = newName;
7719     }
7720 
7721     // set the colored name to the name from the client, set trailing color to white
7722     coloredName_.Clear();
7723     REAL r,g,b;
7724     Color(r,g,b);
7725     coloredName_ << tColoredString::ColorString(r,g,b) << nameFromServer_;
7726 
7727     if ( name_ != newName || lastAccessLevel != GetAccessLevel() )
7728     {
7729         // copy it to the name, removing colors of course
7730         name_ = newName;
7731 
7732         // remove spaces and possibly other nasties for the user name
7733         userName_ = se_UnauthenticatedUserName( name_ );
7734 
7735         if (sn_GetNetState()!=nCLIENT)
7736         {
7737             // sync the new name
7738             RequestSync();
7739         }
7740     }
7741 
7742     // store access level for next update
7743     lastAccessLevel = GetAccessLevel();
7744 
7745 #ifdef KRAWALL_SERVER
7746     // take the user name to be the authenticated name
7747     if ( IsAuthenticated() )
7748     {
7749         userName_ = GetFilteredAuthenticatedName();
7750         if ( se_legacyLogNames )
7751         {
7752             userName_ = tString( "0:" ) + userName_;
7753         }
7754     }
7755     else
7756     {
7757         rawAuthenticatedName_ = "";
7758     }
7759 #endif
7760 }
7761 
7762 // ******************************************************************************************
7763 // *
7764 // *    AllowRename
7765 // *
7766 // ******************************************************************************************
7767 //!
7768 //!        @param    allow  let the player rename or not ?
7769 //!
7770 // ******************************************************************************************
7771 
AllowRename(bool allow)7772 void ePlayerNetID::AllowRename( bool allow )
7773 {
7774     // simple eh ? :-)
7775     renameAllowed_ = allow;
7776 }
7777 // Now we're here, set commands for it
AllowRename(std::istream & s,bool allow,bool announce)7778 static void AllowRename( std::istream & s , bool allow , bool announce )
7779 {
7780     if ( se_NeedsServer( "(DIS)ALLOW_RENAME_PLAYER", s ) )
7781     {
7782         return;
7783     }
7784     ePlayerNetID * p;
7785     p = ReadPlayer( s );
7786     if ( p )
7787     {
7788 //      announce it?
7789         if ( announce && allow )
7790         {
7791             sn_ConsoleOut( tOutput("$player_allowed_rename", p->GetColoredName() ) );
7792         }
7793         else if ( announce && !allow )
7794         {
7795             sn_ConsoleOut( tOutput("$player_disallowed_rename", p->GetColoredName() ) );
7796         }
7797 
7798         p->AllowRename ( allow );
7799     }
7800 }
AllowRename(std::istream & s)7801 static void AllowRename( std::istream & s )
7802 {
7803     if ( se_NeedsServer( "ALLOW_RENAME_PLAYER", s ) )
7804     {
7805         return;
7806     }
7807     AllowRename( s , true, true );
7808 }
7809 static tConfItemFunc AllowRename_conf("ALLOW_RENAME_PLAYER", &AllowRename);
7810 static tAccessLevelSetter AllowRename_confLevel( AllowRename_conf, tAccessLevel_Moderator );
7811 
DisAllowRename(std::istream & s)7812 static void DisAllowRename( std::istream & s )
7813 {
7814     if ( se_NeedsServer( "DISALLOW_RENAME_PLAYER", s ) )
7815     {
7816         return;
7817     }
7818     AllowRename( s , false, true );
7819 }
7820 static tConfItemFunc DisAllowRename_conf("DISALLOW_RENAME_PLAYER", &DisAllowRename);
7821 static tAccessLevelSetter DisAllowRename_confLevel( DisAllowRename_conf, tAccessLevel_Moderator );
7822 
7823 // ******************************************************************************************
7824 // *
7825 // *    IsAllowedToRename
7826 // *
7827 // ******************************************************************************************
7828 //!
7829 //!
7830 // ******************************************************************************************
7831 
HasRenameCapability(ePlayerNetID const * victim,ePlayerNetID const * admin)7832 bool ePlayerNetID::HasRenameCapability ( ePlayerNetID const * victim, ePlayerNetID const * admin )
7833 {
7834     return Rename_conf.GetRequiredLevel() <= admin->GetAccessLevel();
7835 }
7836 
7837 // ******************************************************************************************
7838 // *
7839 // *    IsAllowedToRename
7840 // *
7841 // ******************************************************************************************
7842 //!
7843 //!
7844 // ******************************************************************************************
7845 
7846 
IsAllowedToRename(void)7847 bool ePlayerNetID::IsAllowedToRename ( void )
7848 {
7849     if ( !IsHuman() || ( nameFromServer_ == nameFromClient_ && nameFromServer_ == nameFromAdmin_ ) || nameFromServer_.Len() < 1 || sn_GetNetState() == nCLIENT )
7850     {
7851         // Don't complain about people who either are bots, doesn't change name, or are entering the grid
7852         return true;
7853     }
7854     if ( nameFromServer_ != nameFromAdmin_ )
7855     {
7856         // We are going to rename this player, so, no need to tell him he's trying to rename from his old name to his old name :-P
7857         // Just return false so the server thinks the player can't be renamed in any other way than admin-rename.
7858         return false;
7859     }
7860     // didn't we disallow this player to rename via a command ?
7861     if ( !this->renameAllowed_ )
7862     {
7863         tOutput message( "$player_rename_rejected_admin", nameFromServer_, nameFromClient_ );
7864         se_SecretConsoleOut( message, this, &ePlayerNetID::HasRenameCapability, this );
7865         con << message;
7866         return false;
7867     }
7868     // disallow name changes if there was a kick vote recently
7869     if ( !( !bool(this->voter_) || voter_->AllowNameChange() || nameFromServer_.Len() <= 1 ) && nameFromServer_ != nameFromClient_ )
7870     {
7871         // inform victim to avoid complaints
7872         tOutput message( "$player_rename_rejected_kickvote", nameFromServer_, nameFromClient_ );
7873         sn_ConsoleOut( message, Owner() );
7874         con << message;
7875         return false;
7876     }
7877 
7878     // Nothing to complain about ? fine, he can rename
7879     return true;
7880 }
7881 
7882 
7883 // filters illegal player characters
7884 class ePlayerCharacterFilter
7885 {
7886 public:
ePlayerCharacterFilter()7887     ePlayerCharacterFilter()
7888     {
7889         int i;
7890         filter[0]=0;
7891 
7892         // map all unknown characters to underscores
7893         for (i=255; i>0; --i)
7894         {
7895             filter[i] = '_';
7896         }
7897 
7898         // leave ASCII characters as they are
7899         for (i=126; i>32; --i)
7900         {
7901             filter[i] = i;
7902         }
7903         // but convert uppercase characters to lowercase
7904         for (i='Z'; i>='A'; --i)
7905         {
7906             filter[i] = i + ('a' - 'A');
7907         }
7908 
7909         //! map umlauts and stuff to their base characters
7910         SetMap(0xc0,0xc5,'a');
7911         SetMap(0xd1,0xd6,'o');
7912         SetMap(0xd9,0xdD,'u');
7913         SetMap(0xdf,'s');
7914         SetMap(0xe0,0xe5,'a');
7915         SetMap(0xe8,0xeb,'e');
7916         SetMap(0xec,0xef,'i');
7917         SetMap(0xf0,0xf6,'o');
7918         SetMap(0xf9,0xfc,'u');
7919 
7920         // ok, some of those are a bit questionable, but still better than _...
7921         SetMap(161,'!');
7922         SetMap(162,'c');
7923         SetMap(163,'l');
7924         SetMap(165,'y');
7925         SetMap(166,'|');
7926         SetMap(167,'s');
7927         SetMap(168,'"');
7928         SetMap(169,'c');
7929         SetMap(170,'a');
7930         SetMap(171,'"');
7931         SetMap(172,'!');
7932         SetMap(174,'r');
7933         SetMap(176,'o');
7934         SetMap(177,'+');
7935         SetMap(178,'2');
7936         SetMap(179,'3');
7937         SetMap(182,'p');
7938         SetMap(183,'.');
7939         SetMap(185,'1');
7940         SetMap(187,'"');
7941         SetMap(198,'a');
7942         SetMap(199,'c');
7943         SetMap(208,'d');
7944         SetMap(209,'n');
7945         SetMap(215,'x');
7946         SetMap(216,'o');
7947         SetMap(221,'y');
7948         SetMap(222,'p');
7949         SetMap(231,'c');
7950         SetMap(241,'n');
7951         SetMap(247,'/');
7952         SetMap(248,'o');
7953         SetMap(253,'y');
7954         SetMap(254,'p');
7955         SetMap(255,'y');
7956 
7957         //map 0 to o because they look similar
7958         SetMap('0','o');
7959 
7960         // TODO: make this data driven.
7961     }
7962 
Filter(unsigned char in)7963     char Filter( unsigned char in )
7964     {
7965         return filter[ static_cast< unsigned int >( in )];
7966     }
7967 private:
SetMap(int in1,int in2,unsigned char out)7968     void SetMap( int in1, int in2, unsigned char out)
7969     {
7970         tASSERT( in2 <= 0xff );
7971         tASSERT( 0 <= in1 );
7972         tASSERT( in1 < in2 );
7973         for( int i = in2; i >= in1; --i )
7974             filter[ i ] = out;
7975     }
7976 
SetMap(unsigned char in,unsigned char out)7977     void SetMap( unsigned char in, unsigned char out)
7978     {
7979         filter[ static_cast< unsigned int >( in ) ] = out;
7980     }
7981 
7982     char filter[256];
7983 };
7984 
se_IsUnderscore(char c)7985 static bool se_IsUnderscore( char c )
7986 {
7987     return c == '_';
7988 }
7989 
7990 // ******************************************************************************************
7991 // *
7992 // *    FilterName
7993 // *
7994 // ******************************************************************************************
7995 //!
7996 //!        @param    in   input name
7997 //!        @param    out  output name cleared to be usable as a username
7998 //!
7999 // ******************************************************************************************
8000 
FilterName(tString const & in,tString & out)8001 void ePlayerNetID::FilterName( tString const & in, tString & out )
8002 {
8003     int i;
8004     static ePlayerCharacterFilter filter;
8005     out = tColoredString::RemoveColors( in );
8006 
8007     // filter out illegal characters
8008     for ( i = out.Len()-1; i>=0; --i )
8009     {
8010         char & c = out[i];
8011 
8012         c = filter.Filter( c );
8013     }
8014 
8015     // strip leading and trailing unknown characters
8016     se_StripMatchingEnds( out, se_IsUnderscore, se_IsUnderscore );
8017 }
8018 
8019 // ******************************************************************************************
8020 // *
8021 // *    FilterName
8022 // *
8023 // ******************************************************************************************
8024 //!
8025 //!        @param    in   input name
8026 //!        @return        output name cleared to be usable as a username
8027 //!
8028 // ******************************************************************************************
8029 
FilterName(tString const & in)8030 tString ePlayerNetID::FilterName( tString const & in )
8031 {
8032     tString out;
8033     FilterName( in, out );
8034     return out;
8035 }
8036 
8037 // ******************************************************************************************
8038 // *
8039 // * SetName
8040 // *
8041 // ******************************************************************************************
8042 //!
8043 //!      @param  name    this player's name without colors. to set
8044 //!       @return     A reference to this to allow chaining
8045 //!
8046 // ******************************************************************************************
8047 
SetName(tString const & name)8048 ePlayerNetID & ePlayerNetID::SetName( tString const & name )
8049 {
8050     this->nameFromClient_ = name;
8051     this->nameFromClient_.NetFilter();
8052 
8053     // replace empty name
8054     if ( !IsLegalPlayerName( nameFromClient_ ) )
8055         nameFromClient_ = "Player 1";
8056 
8057     if ( sn_GetNetState() != nCLIENT )
8058         nameFromServer_ = nameFromClient_;
8059 
8060     UpdateName();
8061 
8062     return *this;
8063 }
8064 
8065 // ******************************************************************************************
8066 // *
8067 // * SetName
8068 // *
8069 // ******************************************************************************************
8070 //!
8071 //!      @param  name    this player's name without colors. to set
8072 //!       @return     A reference to this to allow chaining
8073 //!
8074 // ******************************************************************************************
8075 
SetName(char const * name)8076 ePlayerNetID & ePlayerNetID::SetName( char const * name )
8077 {
8078     SetName( tString( name ) );
8079     return *this;
8080 }
8081 
8082 // ******************************************************************************************
8083 // *
8084 // * ForceName
8085 // *
8086 // ******************************************************************************************
8087 //!
8088 //!      @param  name    this player's name without colors. to set
8089 //!
8090 // ******************************************************************************************
ForceName(tString const & name)8091 ePlayerNetID & ePlayerNetID::ForceName( tString const & name )
8092 {
8093     // This just sets nameFromAdmin_ waiting for the server to look at it next round (this->UpdateName())
8094     // Also forbid the player from renameing then, because it would be useless :-P
8095     if ( sn_GetNetState() != nCLIENT )
8096     {
8097         tColoredString oldName;
8098         tColoredString newName;
8099 
8100         oldName << this->GetColoredName() << tColoredString::ColorString( -1, -1, -1 );
8101 
8102         this->nameFromAdmin_ = name;
8103         this->nameFromAdmin_.NetFilter();
8104         se_CutString( this->nameFromAdmin_, 16 );
8105 
8106         // crappiest line ever :-/
8107         newName << tColoredString::ColorString( r/15.0, g/15.0, b/15.0 ) << this->nameFromAdmin_ << tColoredString::ColorString( -1, -1, -1 );
8108 
8109         con << tOutput("$player_will_be_renamed", newName, oldName);
8110 
8111         AllowRename ( false );
8112     }
8113 
8114     return *this;
8115 }
8116 
8117 // Set an admin command for it
ForceName(std::istream & s)8118 void ForceName ( std::istream & s )
8119 {
8120     ePlayerNetID * p;
8121     p = ReadPlayer( s );
8122     if ( p )
8123     {
8124         tString newname;
8125         newname.ReadLine( s );
8126         p->ForceName ( newname );
8127     }
8128 }
8129 // the tConfItemFunc is defined in ePlayer.h
8130 // ******************************************************************************************
8131 // *
8132 // * GetFilteredAuthenticatedName
8133 // *
8134 // ******************************************************************************************
8135 //!
8136 //!       @return     The filtered authentication name, or "" if no authentication is supported or the player is not authenticated
8137 //!
8138 // ******************************************************************************************
8139 
GetFilteredAuthenticatedName(void) const8140 tString ePlayerNetID::GetFilteredAuthenticatedName( void ) const
8141 {
8142 #ifdef KRAWALL_SERVER
8143     return tString( se_EscapeName( GetRawAuthenticatedName() ).c_str() );
8144 #else
8145     return tString("");
8146 #endif
8147 }
8148 
8149 // allow enemies from the same IP?
8150 static bool se_allowEnemiesSameIP = false;
8151 static tSettingItem< bool > se_allowEnemiesSameIPConf( "ALLOW_ENEMIES_SAME_IP", se_allowEnemiesSameIP );
8152 // allow enemies from the same client?
8153 static bool se_allowEnemiesSameClient = false;
8154 static tSettingItem< bool > se_allowEnemiesSameClientConf( "ALLOW_ENEMIES_SAME_CLIENT", se_allowEnemiesSameClient );
8155 
8156 // *******************************************************************************
8157 // *
8158 // *    Enemies
8159 // *
8160 // *******************************************************************************
8161 //!
8162 //!        @param    a    first player to compare
8163 //!        @param    b    second player to compare
8164 //!        @return        true if a should be able to score against b or vice versa
8165 //!
8166 // *******************************************************************************
8167 
Enemies(ePlayerNetID const * a,ePlayerNetID const * b)8168 bool ePlayerNetID::Enemies( ePlayerNetID const * a, ePlayerNetID const * b )
8169 {
8170     // the client does not need a true answer
8171     if ( sn_GetNetState() == nCLIENT )
8172         return true;
8173 
8174     // no scoring if one of them does not exist
8175     if ( !a || !b )
8176         return false;
8177 
8178     // no scoring for two players from the same IP
8179     if ( !se_allowEnemiesSameIP && a->Owner() != 0 && a->GetMachine() == b->GetMachine() )
8180         return false;
8181 
8182     // no scoring for two players from the same client
8183     if ( !se_allowEnemiesSameClient && a->Owner() != 0 && a->Owner() == b->Owner() )
8184         return false;
8185 
8186     // no objections
8187     return true;
8188 }
8189 
8190 // *******************************************************************************
8191 // *
8192 // *    RegisterWithMachine
8193 // *
8194 // *******************************************************************************
8195 //!
8196 //!
8197 // *******************************************************************************
8198 
RegisterWithMachine(void)8199 void ePlayerNetID::RegisterWithMachine( void )
8200 {
8201     if ( !registeredMachine_ )
8202     {
8203         // store machine (it won't get deleted while this object exists; the player count prevents that)
8204         registeredMachine_ = &this->nNetObject::DoGetMachine();
8205         registeredMachine_->AddPlayer();
8206     }
8207 }
8208 
8209 // *******************************************************************************
8210 // *
8211 // *    UnregisterWithMachine
8212 // *
8213 // *******************************************************************************
8214 //!
8215 //!
8216 // *******************************************************************************
8217 
UnregisterWithMachine(void)8218 void ePlayerNetID::UnregisterWithMachine( void )
8219 {
8220     if ( registeredMachine_ )
8221     {
8222         // store suspension count
8223         if ( GetVoter() )
8224         {
8225             GetVoter()->suspended_ = suspended_;
8226             GetVoter()->silenced_ = silenced_;
8227         }
8228 
8229         registeredMachine_->RemovePlayer();
8230         registeredMachine_ = 0;
8231     }
8232 }
8233 
8234 // *******************************************************************************
8235 // *
8236 // *    DoGetMachine
8237 // *
8238 // *******************************************************************************
8239 //!
8240 //!        @return        the machine this object belongs to
8241 //!
8242 // *******************************************************************************
8243 
DoGetMachine(void) const8244 nMachine & ePlayerNetID::DoGetMachine( void ) const
8245 {
8246     // return machine I'm registered at, otherwise whatever the base class thinks
8247     if ( registeredMachine_ )
8248         return *registeredMachine_;
8249     else
8250         return nNetObject::DoGetMachine();
8251 }
8252 
8253 // *******************************************************************************
8254 // *
8255 // *    LastActivity
8256 // *
8257 // *******************************************************************************
8258 //!
8259 //!        @return
8260 //!
8261 // *******************************************************************************
8262 
LastActivity(void) const8263 REAL ePlayerNetID::LastActivity( void ) const
8264 {
8265     return tSysTimeFloat() - lastActivity_;
8266 }
8267 
8268 // *******************************************************************************
8269 // *
8270 // *    ResetScoreDifferences
8271 // *
8272 // *******************************************************************************
8273 //!
8274 //!
8275 // *******************************************************************************
8276 
ResetScoreDifferences(void)8277 void ePlayerNetID::ResetScoreDifferences( void )
8278 {
8279     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
8280     {
8281         ePlayerNetID* p = se_PlayerNetIDs(i);
8282         if ( bool(p->Object()) && p->IsHuman() )
8283             p->lastScore_ = p->score;
8284     }
8285 
8286     eTeam::ResetScoreDifferences();
8287 }
8288 
8289 // *******************************************************************************
8290 // *
8291 // *    Suspend
8292 // *
8293 // *******************************************************************************
8294 //!
8295 //!  @param rounds number of rounds to suspend
8296 //!
8297 // *******************************************************************************
8298 
Suspend(int rounds)8299 void ePlayerNetID::Suspend( int rounds )
8300 {
8301     if ( rounds < 0 )
8302     {
8303         rounds = 0;
8304     }
8305 
8306     int & suspended = AccessSuspended();
8307 
8308     if ( suspended == rounds )
8309     {
8310         return;
8311     }
8312 
8313     suspended = rounds;
8314 
8315     if ( suspended == 0 )
8316     {
8317         sn_ConsoleOut( tOutput( "$player_no_longer_suspended", GetColoredName() ) );
8318         FindDefaultTeam();
8319     }
8320     else
8321     {
8322         sn_ConsoleOut( tOutput( "$player_suspended", GetColoredName(), suspended ) );
8323         SetTeam( NULL );
8324         if ( Object() && Object()->Alive() )
8325             Object()->Kill();
8326     }
8327 }
8328 
8329 // *******************************************************************************
8330 // *
8331 // *    LogScoreDifferences
8332 // *
8333 // *******************************************************************************
8334 //!
8335 //!
8336 // *******************************************************************************
8337 
LogScoreDifferences(void)8338 void ePlayerNetID::LogScoreDifferences( void )
8339 {
8340     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
8341     {
8342         ePlayerNetID* p = se_PlayerNetIDs(i);
8343         p->LogScoreDifference();
8344     }
8345 
8346     eTeam::LogScoreDifferences();
8347 }
8348 
UpdateSuspensions()8349 void ePlayerNetID::UpdateSuspensions() {
8350     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
8351     {
8352         ePlayerNetID* p = se_PlayerNetIDs(i);
8353 
8354         int suspended = p->GetSuspended();
8355 
8356         // update suspension count
8357         if ( suspended > 0 )
8358         {
8359             if ( p->CurrentTeam() && !p->NextTeam() )
8360             {
8361                 p->UpdateTeam();
8362             }
8363             else
8364             {
8365                 p->Suspend( suspended - 1 );
8366             }
8367         }
8368     }
8369 }
8370 
UpdateShuffleSpamTesters()8371 void ePlayerNetID::UpdateShuffleSpamTesters()
8372 {
8373     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
8374     {
8375         ePlayerNetID *p = se_PlayerNetIDs( i );
8376         p->shuffleSpam.Reset();
8377     }
8378 }
8379 
8380 // *******************************************************************************
8381 // *
8382 // *    LogScoreDifference
8383 // *
8384 // *******************************************************************************
8385 //!
8386 //!
8387 // *******************************************************************************
8388 
8389 static eLadderLogWriter se_roundScoreWriter("ROUND_SCORE", true);
8390 
LogScoreDifference(void)8391 void ePlayerNetID::LogScoreDifference( void )
8392 {
8393     if ( lastScore_ > IMPOSSIBLY_LOW_SCORE && IsHuman() )
8394     {
8395         int scoreDifference = score - lastScore_;
8396         lastScore_ = IMPOSSIBLY_LOW_SCORE;
8397         se_roundScoreWriter << scoreDifference << GetUserName();
8398         if ( currentTeam )
8399             se_roundScoreWriter << FilterName( currentTeam->Name() );
8400         se_roundScoreWriter.write();
8401     }
8402 }
8403 
se_allowTeamChangesPlayer(bool allow,std::istream & s)8404 static void se_allowTeamChangesPlayer(bool allow, std::istream &s) {
8405     if ( se_NeedsServer( "(DIS)ALLOW_TEAM_CHANGE_PLAYER", s, false ) )
8406     {
8407         return;
8408     }
8409 
8410     ePlayerNetID * p = ReadPlayer( s );
8411     if ( p )
8412     {
8413         sn_ConsoleOut( tOutput( (allow ? "$player_allowed_teamchange" : "$player_disallowed_teamchange"), p->GetName() ) );
8414         p->SetTeamChangeAllowed( allow );
8415     }
8416 }
se_allowTeamChangesPlayer(std::istream & s)8417 static void se_allowTeamChangesPlayer(std::istream &s) {
8418     se_allowTeamChangesPlayer(true, s);
8419 }
se_disallowTeamChangesPlayer(std::istream & s)8420 static void se_disallowTeamChangesPlayer(std::istream &s) {
8421     se_allowTeamChangesPlayer(false, s);
8422 }
8423 static tConfItemFunc se_allowTeamChangesPlayerConf("ALLOW_TEAM_CHANGE_PLAYER", &se_allowTeamChangesPlayer);
8424 static tConfItemFunc se_disallowTeamChangesPlayerConf("DISALLOW_TEAM_CHANGE_PLAYER", &se_disallowTeamChangesPlayer);
8425 static tAccessLevelSetter se_atcConfLevel( se_allowTeamChangesPlayerConf, tAccessLevel_TeamLeader );
8426 static tAccessLevelSetter se_dtcConfLevel( se_disallowTeamChangesPlayerConf, tAccessLevel_TeamLeader );
8427 
8428 //! accesses the suspension count
AccessSuspended()8429 int & ePlayerNetID::AccessSuspended()
8430 {
8431     return suspended_;
8432 }
8433 
8434 //! returns the suspension count
GetSuspended() const8435 int ePlayerNetID::GetSuspended() const
8436 {
8437     return suspended_;
8438 }
8439