src/engine/ePlayer.cpp

Go to the documentation of this file.
00001 /*
00002 
00003 *************************************************************************
00004 
00005 ArmageTron -- Just another Tron Lightcycle Game in 3D.
00006 Copyright (C) 2000  Manuel Moos (manuel@moosnet.de)
00007 
00008 **************************************************************************
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00023 
00024 ***************************************************************************
00025 
00026 */
00027 
00028 #include "eEventNotification.h"
00029 #include "tMemManager.h"
00030 #include "ePlayer.h"
00031 //#include "tInitExit.h"
00032 #include "tConfiguration.h"
00033 #include "eNetGameObject.h"
00034 #include "rConsole.h"
00035 #include "eTimer.h"
00036 #include "tSysTime.h"
00037 #include "rFont.h"
00038 #include "uMenu.h"
00039 #include "tToDo.h"
00040 #include "rScreen.h"
00041 #include <string>
00042 #include <fstream>
00043 #include <iostream>
00044 #include <deque>
00045 #include "rRender.h"
00046 #include "rFont.h"
00047 #include "rSysdep.h"
00048 #include "nAuthentication.h"
00049 #include "tDirectories.h"
00050 #include "eTeam.h"
00051 #include "eVoter.h"
00052 #include "tReferenceHolder.h"
00053 #include "tRandom.h"
00054 #include "uInputQueue.h"
00055 #include "nServerInfo.h"
00056 #include "tRecorder.h"
00057 #include "nConfig.h"
00058 #include <time.h>
00059 #include "tRuby.h"
00060 
00061 tColoredString & operator << (tColoredString &s,const ePlayer &p){
00062     return s << tColoredString::ColorString(p.rgb[0]/15.0,
00063                                             p.rgb[1]/15.0,
00064                                             p.rgb[2]/15.0)
00065            << p.Name();
00066 }
00067 
00068 tColoredString & operator << (tColoredString &s,const ePlayerNetID &p){
00069     return s << p.GetColoredName();
00070 }
00071 
00072 std::ostream & operator << (std::ostream &s,const ePlayerNetID &p){
00073     return s << p.GetColoredName();
00074 }
00075 
00076 eAccessLevelHolder::eAccessLevelHolder()
00077 {
00078     accessLevel = tAccessLevel_Default;
00079 }
00080 
00081 void eAccessLevelHolder::SetAccessLevel( tAccessLevel level )
00082 {
00083     // sanity check. The access level must not be set higher than that of the current context.
00084     // since accessLevel is private, all access level changes need to run over this function;
00085     // it is therefore impossible to get access level escalation bugs without memory overwrites
00086     // or, of course, evil intentions.
00087     if ( level < tCurrentAccessLevel::GetAccessLevel() )
00088     {
00089         con << "INTERNAL ERROR, security violation attempted. Clamping it.\n";
00090         st_Breakpoint();
00091         accessLevel = tCurrentAccessLevel::GetAccessLevel();
00092     }
00093 
00094     accessLevel = level;
00095 }
00096 
00097 tCONFIG_ENUM( eCamMode );
00098 
00099 tList<ePlayerNetID> se_PlayerNetIDs;
00100 static ePlayer* se_Players = NULL;
00101 
00102 bool se_assignTeamAutomatically = true;
00103 static tSettingItem< bool > se_assignTeamAutomaticallyConf( "AUTO_TEAM", se_assignTeamAutomatically );
00104 
00105 static bool se_allowTeamChanges = true;
00106 static tSettingItem< bool > se_allowTeamChangesConf( "ALLOW_TEAM_CHANGE", se_allowTeamChanges );
00107 
00108 static bool se_enableChat = true;       //flag indicating whether chat should be allowed at all (logged in players can always chat)
00109 static tSettingItem<bool> se_enaChat("ENABLE_CHAT", se_enableChat);
00110 
00111 static tReferenceHolder< ePlayerNetID > se_PlayerReferences;
00112 
00113 class PasswordStorage
00114 {
00115 public:
00116     tString username; // name of the user the password belongs to
00117     tString methodCongested;   // method of scrambling
00118     nKrawall::nScrambledPassword password;
00119     bool save;
00120 
00121     PasswordStorage(): save(false){};
00122 };
00123 
00124 static bool operator == ( PasswordStorage const & a, PasswordStorage const & b )
00125 {
00126     return
00127     a.username == b.username &&
00128     a.methodCongested == b.methodCongested;
00129 }
00130     
00131 static tArray<PasswordStorage> S_passwords;
00132 
00133 // if set, user names of non-authenticated players are left as they are,
00134 // and usernames of authenticated players get a 0: prepended.
00135 // if unsed, usernames of non-authenticated players get all special characters escaped (especially all @)
00136 // and usernames of authenticated players get left as they are (with all special characters except the last
00137 // escaped.)
00138 bool se_legacyLogNames = false;
00139 static tSettingItem<bool> se_llnConf("LEGACY_LOG_NAMES", se_legacyLogNames );
00140 
00141 // transform special characters in name to escape sequences
00142 static std::string se_EscapeName( tString const & original, bool keepAt = true )
00143 {
00144     std::ostringstream filter; 
00145 
00146     int lastC = 'x';
00147     for( int i = 0; i < original.Len()-1; ++i )
00148     {
00149         unsigned int c = static_cast< unsigned char >( original[i] );
00150 
00151         // a valid character
00152         switch (c)
00153         {
00154             // characters to escape
00155         case ' ':
00156             filter << "\\_";
00157             break;
00158         case '@':
00159             if ( keepAt )
00160             {
00161                 filter << '@';
00162             }
00163             else
00164             {
00165                 filter << "\\@";
00166             }
00167             break;
00168         case '\\':
00169         case '%':
00170         case ':':
00171             filter.put('\\');
00172             filter.put(c);
00173             break;
00174         case 'x':
00175             // color codes?
00176             if ( lastC == '0' )
00177             {
00178                 filter << "\\x";
00179                 break;
00180             }    
00181         default:
00182             if ( 0x20 < c && 0x7f >= c )
00183             {
00184                 // normal ascii, print 
00185                 filter.put(c);
00186             }
00187             else
00188             {
00189                 // encode as hexcode
00190                 filter << '%' << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << c;
00191             }
00192         }
00193         
00194         lastC = c;
00195     }
00196 
00197     // done! wrap up stream as string.
00198     return filter.str();
00199 }
00200 
00201 #ifdef KRAWALL_SERVER
00202 // reverses se_EscapeName (left inverse only, UnEscape(Escape(x)) == x, but not Escape(UnEscape(y)) == y. )
00203 static std::string se_UnEscapeName( tString const & original )
00204 {
00205     std::ostringstream filter; 
00206 
00207     int lastC = 'x';
00208     for( int i = 0; i < original.Len()-1; ++i )
00209     {
00210         int c = original[i];
00211 
00212         if ( lastC == '\\' )
00213         {
00214             if ( c == '_' )
00215             {
00216                 c = ' ';
00217             }
00218             filter.put(c);
00219         }
00220         else if ( c == '%' )
00221         {
00222             // decode hex code
00223             char hex[3];
00224             hex[0] = original[i+1];
00225             hex[1] = original[i+2];
00226             hex[2] = 0;
00227             i += 2;
00228 
00229             int decoded;
00230             std::istringstream s(hex);
00231             s >> std::hex >> decoded;
00232             tASSERT( !s.fail() );
00233             filter.put(decoded);
00234         }
00235         else if ( c != '\\' )
00236         {
00237             filter.put(c);
00238         }
00239 
00240         lastC = c;
00241     }
00242 
00243     // done! wrap up stream as string.
00244     return filter.str();
00245 }
00246 #endif
00247 
00248 // generate the user name for an unauthenticated user
00249 static tString se_UnauthenticatedUserName( tString const & name )
00250 {
00251     tString ret;
00252     ePlayerNetID::FilterName( name, ret );
00253     if ( se_legacyLogNames )
00254     {
00255         return ret;
00256     }
00257     else
00258     {
00259         return tString( se_EscapeName( ret, false ).c_str() );
00260     }
00261 }
00262 
00263 void se_DeletePasswords(){
00264     S_passwords.SetLen(0);
00265 
00266     st_SaveConfig();
00267 
00268     /*
00269 
00270       REAL timeout = tSysTimeFloat() + 3;
00271 
00272       while(tSysTimeFloat() < timeout){
00273 
00274       sr_ResetRenderState(true);
00275       rViewport::s_viewportFullscreen.Select();
00276 
00277       sr_ClearGL();
00278 
00279       uMenu::GenericBackground();
00280 
00281       REAL w=16*3/640.0;
00282       REAL h=32*3/480.0;
00283 
00284 
00285       //REAL middle=-.6;
00286 
00287       Color(1,1,1);
00288       DisplayText(0,.8,w,h,tOutput("$network_opts_deletepw_complete"));
00289 
00290       sr_SwapGL();
00291       }
00292 
00293     */
00294 
00295     tConsole::Message("$network_opts_deletepw_complete", tOutput(), 5);
00296 }
00297 
00298 class tConfItemPassword:public tConfItemBase{
00299 public:
00300     tConfItemPassword():tConfItemBase("PASSWORD"){}
00301     ~tConfItemPassword(){};
00302 
00303     // write the complete passwords
00304     virtual void WriteVal(std::ostream &s){
00305         int i;
00306         bool first = 1;
00307         for (i = S_passwords.Len()-1; i>=0; i--)
00308         {
00309             PasswordStorage &storage = S_passwords[i];
00310             if (storage.save )
00311             {
00312                 if (!first)
00313                     s << "\nPASSWORD\t";
00314                 first = false;
00315 
00316                 s << "1 ";
00317                 nKrawall::WriteScrambledPassword(storage.password, s);
00318                 s << '\t' << storage.methodCongested;
00319                 s << '\t' << storage.username;
00320             }
00321         }
00322         if (first)
00323             s << "0 ";
00324     }
00325 
00326     // read one password
00327     virtual void ReadVal(std::istream &s){
00328         //    static char in[20];
00329         int test;
00330         s >> test;
00331         if (test != 0)
00332         {
00333             PasswordStorage &storage = S_passwords[S_passwords.Len()];
00334             nKrawall::ReadScrambledPassword(s, storage.password);
00335             s >> storage.methodCongested;
00336             storage.username.ReadLine(s);
00337 
00338             storage.save = true;
00339 
00340             // check for duplicates
00341             for( int i = S_passwords.Len() - 2; i >= 0; --i )
00342             {
00343                 PasswordStorage &other = S_passwords[i];
00344                 if ( other == storage )
00345                 {
00346                     storage.save = false;
00347                     break;
00348                 }
00349             }
00350         }
00351     }
00352 };
00353 
00354 static tConfItemPassword se_p;
00355 
00356 // username menu item
00357 class eMenuItemUserName: public uMenuItemString
00358 {
00359 public:
00360     eMenuItemUserName(uMenu *M,tString &c):
00361     uMenuItemString(M,"$login_username","$login_username_help",c){}
00362     virtual ~eMenuItemUserName(){}
00363 
00364     virtual bool Event(SDL_Event &e){
00365 #ifndef DEDICATED
00366         if (e.type==SDL_KEYDOWN &&
00367                 (e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
00368 
00369             // move on to password menu item
00370             MyMenu()->SetSelected(0);
00371             return true;
00372         }
00373         else
00374 #endif
00375             return uMenuItemString::Event(e);
00376     }
00377 };
00378 
00379 // password request menu item, with *** display
00380 class eMenuItemPassword: public uMenuItemString
00381 {
00382 public:
00383     static bool entered;
00384 
00385     eMenuItemPassword(uMenu *M,tString &c):
00386     uMenuItemString(M,"$login_password_title","$login_password_help",c)
00387     {
00388         entered = false;
00389     }
00390     virtual ~eMenuItemPassword(){}
00391 
00392     virtual void Render(REAL x,REAL y,REAL alpha=1,bool selected=0)
00393     {
00394         tString* pwback = content;
00395         tString star;
00396         for (int i=content->Len()-2; i>=0; i--)
00397             star << "*";
00398         content = &star;
00399         uMenuItemString::Render(x,y, alpha, selected);
00400         content = pwback;
00401     }
00402 
00403     virtual bool Event(SDL_Event &e){
00404 #ifndef DEDICATED
00405         if (e.type==SDL_KEYDOWN &&
00406                 (e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
00407 
00408             entered = true;
00409             MyMenu()->Exit();
00410             return true;
00411         }
00412         else
00413 #endif
00414             return uMenuItemString::Event(e);
00415     }
00416 };
00417 
00418 bool eMenuItemPassword::entered;
00419 
00420 static bool tr(){return true;}
00421 
00422 // password storage mode
00423 int se_PasswordStorageMode = 0; // 0: store in memory, -1: don't store, 1: store on file
00424 static tConfItem<int> pws("PASSWORD_STORAGE",
00425                           "$password_storage_help",
00426                           se_PasswordStorageMode);
00427 
00428 static void PasswordCallback( nKrawall::nPasswordRequest const & request,
00429                               nKrawall::nPasswordAnswer & answer )
00430 {
00431     int i;
00432 
00433     tString& username = answer.username;
00434     const tString& message = request.message;
00435     nKrawall::nScrambledPassword& scrambled = answer.scrambled;
00436     bool failure = request.failureOnLastTry;
00437 
00438     if ( request.method != "md5" && request.method != "bmd5" )
00439     {
00440         con << "Unknown password scrambling method requested.";
00441         answer.aborted = true;
00442         return;
00443     }
00444 
00445     // find the player with the given username:
00446     /*
00447     ePlayer* p = NULL;
00448     for (i = MAX_PLAYERS-1; i>=0 && !p; i--)
00449     {
00450         ePlayer * perhaps = ePlayer::PlayerConfig(i);
00451         tString filteredName;
00452         ePlayerNetID::FilterName( perhaps->name, filteredName );
00453         if ( filteredName == username )
00454             p = perhaps;
00455     }
00456 
00457     // or the given raw name
00458     for (i = MAX_PLAYERS-1; i>=0 && !p; i--)
00459     {
00460         ePlayer * perhaps = ePlayer::PlayerConfig(i);
00461         if ( perhaps->name == username )
00462             p = perhaps;
00463     }
00464 
00465     // or just any player, what the heck.
00466     if (!p)
00467         p = ePlayer::PlayerConfig(0);
00468     */
00469 
00470     // try to find the username in the saved passwords
00471     tString methodCongested = request.method + '|' + request.prefix + '|' + request.suffix;
00472 
00473     PasswordStorage *storage = NULL;
00474     for (i = S_passwords.Len()-1; i>=0; i--)
00475     {
00476         PasswordStorage & candidate = S_passwords(i); 
00477         if (answer.username == candidate.username && 
00478             methodCongested  == candidate.methodCongested
00479             )
00480             storage = &candidate;
00481     }
00482 
00483     if (!storage)
00484     {
00485         // find an empty slot
00486         for (i = S_passwords.Len()-1; i>=0; i--)
00487             if (S_passwords(i).username.Len() < 1)
00488                 storage = &S_passwords(i);
00489 
00490         if (!storage)
00491             storage = &S_passwords[S_passwords.Len()];
00492 
00493         failure = true;
00494     }
00495 
00496     static char const * section = "PASSWORD_MENU";
00497     tRecorder::Playback( section, failure );
00498     tRecorder::Record( section, failure );
00499 
00500     // immediately return the stored password if it was not marked as wrong:
00501     if (!failure)
00502     {
00503         if ( storage )
00504         {
00505             answer.username = storage->username;
00506             answer.scrambled = storage->password;
00507         }
00508         answer.automatic = true;
00509 
00510         return;
00511     }
00512     else
00513         storage->username.Clear();
00514 
00515     // scramble input for recording
00516     uInputScrambler scrambler;
00517 
00518     // password was not stored. Request it from user:
00519     uMenu login(message, false);
00520 
00521     // password storage;
00522     tString password;
00523 
00524     eMenuItemPassword pw(&login, password);
00525     eMenuItemUserName us(&login, username);
00526     us.SetColorMode( rTextField::COLOR_IGNORE );
00527 
00528     uMenuItemSelection<int> storepw(&login,
00529                                     "$login_storepw_text",
00530                                     "$login_storepw_help",
00531                                     se_PasswordStorageMode);
00532     storepw.NewChoice("$login_storepw_dont_text",
00533                       "$login_storepw_dont_help",
00534                       -1);
00535     storepw.NewChoice("$login_storepw_mem_text",
00536                       "$login_storepw_mem_help",
00537                       0);
00538     storepw.NewChoice("$login_storepw_disk_text",
00539                       "$login_storepw_disk_help",
00540                       1);
00541 
00542     uMenuItemExit cl(&login, "$login_cancel", "$login_cancel_help" );
00543 
00544     login.SetSelected(1);
00545 
00546     // check if the username the server sent us matches one of the players'
00547     // global IDs. If it does we can directly select the password menu
00548     // menu entry since the user probably just wants to enter the password.
00549     for(int i = 0; i < MAX_PLAYERS; ++i) {
00550         tString const &id = se_Players[i].globalID;
00551         if(id.Len() <= username.Len() || id(username.Len() - 1) != '@') {
00552             continue;
00553         }
00554         bool match = true;
00555         for(int j = username.Len() - 2; j >= 0; --j) {
00556             if(username(j) != id(j)) {
00557                 match = false;
00558                 break;
00559             }
00560         }
00561         if(match) {
00562             login.SetSelected(0);
00563         }
00564     }
00565 
00566     // force a small console while we are in here
00567     rSmallConsoleCallback cb(&tr);
00568 
00569     login.Enter();
00570 
00571     // return username/scrambled password
00572     {
00573         // scramble password
00574         request.ScramblePassword( nKrawall::nScrambleInfo( username ), password, scrambled );
00575 
00576         // if S_login still exists, we were not canceled
00577         answer.aborted = !eMenuItemPassword::entered;
00578 
00579         // clear the PW from memory
00580         for (i = password.Len()-2; i>=0; i--)
00581             password(i) = 'a';
00582 
00583         if (se_PasswordStorageMode >= 0)
00584         {
00585             storage->username = username;
00586             storage->methodCongested = methodCongested;
00587             storage->password = scrambled;
00588             storage->save = (se_PasswordStorageMode > 0);
00589         }
00590     }
00591 }
00592 
00593 #ifdef DEDICATED
00594 #ifndef KRAWALL_SERVER
00595 // the following function really is only supposed to be called from here and nowhere else
00596 // (access right escalation risk):
00597 static void se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE( ePlayerNetID * p )
00598 {
00599     // elevate access rights. We are currently in the context of player chat,
00600     // therefore in his security context, but we need to log the player on, 
00601     // and that requires superior rights.
00602     {
00603         tCurrentAccessLevel elevator( tAccessLevel_Owner, true );
00604         p->BeLoggedIn();
00605     }
00606 
00607     sn_ConsoleOut("You have been logged in!\n",p->Owner());
00608     tString serverLoginMessage;
00609     serverLoginMessage << "Remote admin login for user \"" << p->GetUserName() << "\" accepted.\n";
00610     sn_ConsoleOut(serverLoginMessage, 0);
00611 }
00612 #endif
00613 #endif
00614 
00615 #ifdef KRAWALL_SERVER
00616 // minimal access level to play
00617 static tAccessLevel se_playAccessLevel = tAccessLevel_Program;
00618 static tSettingItem< tAccessLevel > se_playAccessLevelConf( "ACCESS_LEVEL_PLAY", se_playAccessLevel );
00619 
00620 // minimal sliding access level to play (slides up as soon as enoughpeople of higher access level get authenticated )
00621 static tAccessLevel se_playAccessLevelSliding = tAccessLevel_Program;
00622 static tSettingItem< tAccessLevel > se_playAccessLevelSlidingConf( "ACCESS_LEVEL_PLAY_SLIDING", se_playAccessLevelSliding );
00623 
00624 // that many high level players are reuqired to drag the access level up
00625 static int se_playAccessLevelSliders = 4;
00626 static tSettingItem< int > se_playAccessLevelSlidersConf( "ACCESS_LEVEL_PLAY_SLIDERS", se_playAccessLevelSliders );
00627 
00628 static tAccessLevel se_accessLevelRequiredToPlay = tAccessLevel_Program;
00629 static void UpdateAccessLevelRequiredToPlay()
00630 {
00631     int newAccessLevel = se_accessLevelRequiredToPlay;
00632 
00633     // clamp to bounds
00634     if ( newAccessLevel < se_playAccessLevelSliding )
00635         newAccessLevel = se_playAccessLevelSliding;
00636 
00637     if ( newAccessLevel > se_playAccessLevel )
00638         newAccessLevel = se_playAccessLevel;
00639 
00640     bool changed = true;
00641     while ( changed )
00642     {
00643         changed = false;
00644 
00645         // count players above and on the current access level
00646         int countAbove = 0, countOn = 0;
00647         
00648         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
00649         {
00650             ePlayerNetID* player = se_PlayerNetIDs(i);
00651 
00652             // don't count AIs and spectators
00653             if ( !player->IsHuman() || !player->NextTeam() )
00654                 continue;
00655             
00656             if ( player->GetAccessLevel() < newAccessLevel )
00657             {
00658                 countAbove++;
00659             }
00660             else if ( player->GetAccessLevel() == newAccessLevel )
00661             {
00662                 countOn++;
00663             }
00664         }
00665 
00666         if ( countAbove >= se_playAccessLevelSliders && newAccessLevel > se_playAccessLevelSliding )
00667         {
00668             // if enough players are above the current level, increase it
00669             newAccessLevel --;
00670             changed = true;
00671         }
00672         else if ( countOn + countAbove < se_playAccessLevelSliders && newAccessLevel < se_playAccessLevel )
00673         {
00674             // if not enough players are on or above the current level to support it, decrease it
00675             newAccessLevel ++;
00676             changed = true;
00677         }
00678     }
00679 
00680     if ( se_accessLevelRequiredToPlay != newAccessLevel )
00681     {
00682         sn_ConsoleOut( tOutput( "$access_level_play_changed",
00683                                 tCurrentAccessLevel::GetName( se_accessLevelRequiredToPlay ),
00684                                 tCurrentAccessLevel::GetName( static_cast< tAccessLevel >( newAccessLevel ) ) ) );
00685 
00686         se_accessLevelRequiredToPlay = static_cast< tAccessLevel >( newAccessLevel );
00687     }
00688 }
00689 
00690 tAccessLevel ePlayerNetID::AccessLevelRequiredToPlay()
00691 {
00692     return se_accessLevelRequiredToPlay;
00693 }
00694 
00695 // maximal user level whose accounts can be hidden from other users
00696 static tAccessLevel se_hideAccessLevelOf = tAccessLevel_Program;
00697 static tSettingItem< tAccessLevel > se_hideAccessLevelOfConf( "ACCESS_LEVEL_HIDE_OF", se_hideAccessLevelOf );
00698 
00699 // but they are only hidden to players with a lower access level than this
00700 static tAccessLevel se_hideAccessLevelTo = tAccessLevel_Moderator;
00701 static tSettingItem< tAccessLevel > se_hideAccessLevelToConf( "ACCESS_LEVEL_HIDE_TO", se_hideAccessLevelTo );
00702 
00703 // determines whether hider can hide from seeker
00704 static bool se_Hide( ePlayerNetID const * hider, tAccessLevel currentLevel )
00705 {
00706     tASSERT( hider );
00707 
00708     return
00709     hider->GetAccessLevel() >= se_hideAccessLevelOf &&
00710     hider->StealthMode() &&
00711     currentLevel            >  se_hideAccessLevelTo;
00712 }
00713 
00714 // determines whether hider can hide from seeker
00715 static bool se_Hide( ePlayerNetID const * hider, ePlayerNetID const * seeker )
00716 {
00717     if ( !seeker )
00718     {
00719         return se_Hide( hider, tCurrentAccessLevel::GetAccessLevel() );
00720     }
00721 
00722     if ( seeker == hider )
00723     {
00724         return false;
00725     }
00726 
00727     return se_Hide( hider, seeker->GetAccessLevel() );
00728 }
00729 
00730 // console messages for players who can see users of access level hider; the two exceptions get a message anyway.
00731 void se_SecretConsoleOut( tOutput const & message, tAccessLevel hider, ePlayerNetID const * exception1, ePlayerNetID const * exception2 = 0 )
00732 {
00733     // high enough access levels are never secret
00734     if ( hider < se_hideAccessLevelOf )
00735     {
00736         sn_ConsoleOut( message );
00737     }
00738     else
00739     {
00740         // well, the admin will want to see it.
00741         con << message;
00742 
00743         bool canSee[ MAXCLIENTS+1 ];
00744         for( int i = MAXCLIENTS; i>=0; --i )
00745         {
00746             canSee[i] = false;
00747         }
00748 
00749         // look which clients have someone who can see the message
00750         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
00751         {
00752             ePlayerNetID* player = se_PlayerNetIDs(i);
00753             if ( player->GetAccessLevel() <= se_hideAccessLevelTo || player == exception1 || player == exception2 )
00754             {
00755                 canSee[ player->Owner() ] = true;
00756             }
00757         }
00758 
00759         // and send it
00760         for( int i = MAXCLIENTS; i>=0; --i )
00761         {
00762             if ( canSee[i] )
00763             {
00764                 sn_ConsoleOut( message, i );
00765             }
00766         }
00767     }
00768 }
00769 
00770 // console messages for players who can see the user object.
00771 void se_SecretConsoleOut( tOutput const & message, ePlayerNetID const * hider, ePlayerNetID const * admin = 0 )
00772 {
00773     tASSERT( hider );
00774     se_SecretConsoleOut( message, hider->GetAccessLevel(), hider, admin );
00775 }
00776 
00777 static void ResultCallback( nKrawall::nCheckResult const & result )
00778 {
00779     tString username = result.username;
00780     tString authority = result.authority;
00781     bool success = result.success;    
00782 
00783     ePlayerNetID * player = dynamic_cast< ePlayerNetID * >( static_cast< nNetObject * >( result.user ) );
00784     if ( !player || player->Owner() <= 0 )
00785     {
00786         // nobody to authenticate
00787         return;
00788     }
00789 
00790     tString authName = username + "@" + authority;
00791 
00792     // seach for double login
00793     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
00794     {
00795         ePlayerNetID* player = se_PlayerNetIDs(i);
00796         if ( player->IsAuthenticated() && player->GetRawAuthenticatedName() == authName )
00797         {
00798             sn_ConsoleOut( tOutput("$login_request_failed_dup"), player->Owner() );
00799             return;
00800         }
00801     }
00802 
00803     if (success)
00804     {
00805         player->Authenticate( authName, result.accessLevel );
00806 
00807         // log blurb to ladderlog. This is not important for debug playback,
00808         // so we just don't record it. Once more is done with the blurb, 
00809         // we need to change that.
00810         for ( std::deque< tString >::const_iterator i = result.blurb.begin(); i != result.blurb.end(); ++i )
00811         {
00812             std::istringstream s( static_cast< char const * >( *i ) );
00813             tString token, rest;
00814             s >> token;
00815             rest.ReadLine( s );
00816 
00817             std::ostringstream o;
00818             o << "AUTHORITY_BLURB_" << token << " " << player->GetFilteredAuthenticatedName() << " " << rest << std::endl;
00819 
00820             se_SaveToLadderLog( o.str().c_str() );
00821         }
00822     }
00823     else
00824     {
00825         if ( sn_GetNetState() == nSERVER )
00826         {
00827             tOutput out( tOutput("$login_failed_message", result.error ) );
00828             sn_ConsoleOut( out, player->Owner() );
00829             con << out;
00830 
00831             // redo automatic logins immedeately
00832             if ( result.automatic )
00833             {
00834                 nAuthentication::RequestLogin( authority ,username , *player, "$login_request_failed" );
00835             }
00836         }
00837     }
00838 
00839     // continue with scheduled logon messages
00840     ePlayerNetID::RequestScheduledLogins();
00841 }
00842 #else
00843 /*
00844 static bool se_Hide( ePlayerNetID const * hider, tAccessLevel currentLevel )
00845 {
00846     return false;
00847 }
00848 */
00849 
00850 static bool se_Hide( ePlayerNetID const * hider, ePlayerNetID const * seeker )
00851 {
00852     return false;
00853 }
00854 #endif
00855 
00856 // determines whether hider can hide from seeker
00857 /*
00858 static bool se_Hide( ePlayerNetID const * hider )
00859 {
00860     tASSERT( hider );
00861 
00862     return se_Hide( hider, tCurrentAccessLevel::GetAccessLevel() );
00863 }
00864 */
00865 
00866 
00867 
00868 // menu item to silence selected players
00869 class eMenuItemSilence: public uMenuItemToggle
00870 {
00871 public:
00872     eMenuItemSilence(uMenu *m, ePlayerNetID* p )
00873             : uMenuItemToggle( m, tOutput(""),tOutput("$silence_player_help" ),  p->AccessSilenced() )
00874     {
00875         this->title.Clear();
00876         this->title.SetTemplateParameter(1, p->GetColoredName() );
00877         this->title << "$silence_player_text";
00878         player_ = p;
00879     }
00880 
00881     ~eMenuItemSilence()
00882     {
00883     }
00884 private:
00885     tCONTROLLED_PTR( ePlayerNetID ) player_;            // keep player referenced
00886 };
00887 
00888 
00889 
00890 
00891 // menu where you can silence players
00892 void ePlayerNetID::SilenceMenu()
00893 {
00894     uMenu menu( "$player_police_silence_text" );
00895 
00896     int size = se_PlayerNetIDs.Len();
00897     eMenuItemSilence** items = tNEW( eMenuItemSilence* )[ size ];
00898 
00899     int i;
00900     for ( i = size-1; i>=0; --i )
00901     {
00902         ePlayerNetID* player = se_PlayerNetIDs[ i ];
00903         if ( player->IsHuman() )
00904         {
00905             items[i] = tNEW( eMenuItemSilence )( &menu, player );
00906         }
00907         else
00908         {
00909             items[i] = 0;
00910         }
00911 
00912     }
00913 
00914     menu.Enter();
00915 
00916     for ( i = size - 1; i>=0; --i )
00917     {
00918         if( items[i] ) delete items[i];
00919     }
00920     delete[] items;
00921 }
00922 
00923 void ePlayerNetID::PoliceMenu()
00924 {
00925     uMenu menu( "$player_police_text" );
00926 
00927     uMenuItemFunction kick( &menu, "$player_police_kick_text", "$player_police_kick_help", eVoter::KickMenu );
00928     uMenuItemFunction silence( &menu, "$player_police_silence_text", "$player_police_silence_help", ePlayerNetID::SilenceMenu );
00929 
00930     menu.Enter();
00931 }
00932 
00933 
00934 
00935 
00936 
00937 
00938 
00939 
00940 
00941 
00942 
00943 
00944 
00945 
00946 
00947 
00948 
00949 
00950 
00951 
00952 
00953 
00954 
00955 #ifndef DEDICATED
00956 
00957 static char const * default_instant_chat[]=
00958     {"/team \\",
00959      "/msg \\",
00960      "/me \\",
00961      "LOL!",
00962      "/team 1 Yes Oui Ja",
00963      "/team 0 No Non Nein",
00964      "/team I'm going in!",
00965      "Give the rim a break; hug a tree instead.",
00966      "Lag is a myth. It is only in your brain.",
00967      "Rubber kills gameplay!",
00968      "Every time you double bind, God kills a kitten.",
00969      "http://www.armagetronad.net",
00970      "Only idiots keep their instant chat at default values.",
00971      "/me wanted to download pr0n, but only got this stupid game.",
00972      "Speed for weaks!",
00973      "This server sucks! I'm going home.",
00974      "Grind EVERYTHING! And 180 some more!",
00975      "/me has an interesting mental disorder.",
00976      "Ah, a nice, big, roomy box all for me!",
00977      "Go that way! No, the other way!",
00978      "WD! No points!",
00979      "/me is a noob.",
00980      "/me just installed this game and still doesn't know how to talk.",
00981      "/team You all suck, I want a new team.",
00982      "Are you the real \"Player 1\"?",
00983      "0x5aff91Only idiots0xffa962 write in0xc560ff color all0x87dfff the time!",
00984      NULL};
00985 
00986 #endif
00987 
00988 
00989 ePlayer * ePlayer::PlayerConfig(int p){
00990     uPlayerPrototype *P = uPlayerPrototype::PlayerConfig(p);
00991     return dynamic_cast<ePlayer*>(P);
00992     //  return (ePlayer*)P;
00993 }
00994 
00995 void   ePlayer::StoreConfitem(tConfItemBase *c){
00996     tASSERT(CurrentConfitem < PLAYER_CONFITEMS);
00997     configuration[CurrentConfitem++] = c;
00998 }
00999 
01000 void   ePlayer::DeleteConfitems(){
01001     while (CurrentConfitem>0){
01002         CurrentConfitem--;
01003         tDESTROY(configuration[CurrentConfitem]);
01004     }
01005 }
01006 
01007 uActionPlayer *ePlayer::se_instantChatAction[MAX_INSTANT_CHAT];
01008 
01009 #ifdef WIN32
01010 #include <lmcons.h>
01011 #include <windows.h>
01012 #ifndef ULEN
01013 //we're not using more than 16 characters of it anyway
01014 #define ULEN 20
01015 #endif
01016 static tString se_UserNameHelper()
01017 {
01018     char name[ULEN+1];
01019     DWORD len = ULEN;
01020     if ( GetUserName( name, &len ) )
01021         return tString( name );
01022 
01023     return tString();
01024 }
01025 #else
01026 static char const * se_UserNameHelper()
01027 {
01028     return getenv( "USER" );
01029 }
01030 #endif
01031 
01032 static const tString& se_UserName()
01033 {
01034     srand( (unsigned)time( NULL ) );
01035 
01036     static tString ret( se_UserNameHelper() );
01037     return ret;
01038 }
01039 
01040 ePlayer::ePlayer():cockpit(0){
01041     nAuthentication::SetUserPasswordCallback(&PasswordCallback);
01042 #ifdef KRAWALL_SERVER
01043     nAuthentication::SetLoginResultCallback (&ResultCallback);
01044 #endif
01045 
01046     nameTeamAfterMe = false;
01047     favoriteNumberOfPlayersPerTeam = 3;
01048 
01049     CurrentConfitem = 0;
01050 
01051     bool getUserName = false;
01052     if ( id == 0 )
01053     {
01054         name = se_UserName();
01055         getUserName = ( name.Len() > 1 );
01056     }
01057     if ( !getUserName )
01058         name << "Player " << id+1;
01059 
01060 #ifndef DEDICATED
01061     tString confname;
01062 
01063     confname << "PLAYER_"<< id+1;
01064     StoreConfitem(tNEW(tConfItemLine) (confname,
01065                                        "$player_name_confitem_help",
01066                                        name));
01067 
01068     confname.Clear();
01069 
01070     confname << "TEAMNAME_"<< id+1;
01071     StoreConfitem(tNEW(tConfItemLine) (confname,
01072                                        "$team_name_confitem_help",
01073                                        teamname));
01074 
01075     confname.Clear();
01076 
01077     confname << "USER_"<< id+1;
01078     StoreConfitem(tNEW(tConfItemLine) (confname,
01079                                        "$player_user_confitem_help",
01080                                        globalID));
01081     
01082     confname.Clear();
01083     confname << "AUTO_LOGIN_"<< id+1;
01084     StoreConfitem(tNEW(tConfItem<bool>)(confname,
01085                                         "$auto_login_help",
01086                                         autoLogin));
01087     autoLogin = false;
01088 
01089     confname.Clear();
01090 
01091     confname << "CAMCENTER_"<< id+1;
01092     centerIncamOnTurn=true;
01093     StoreConfitem(tNEW(tConfItem<bool>)
01094                   (confname,
01095                    "$camcenter_help",
01096                    centerIncamOnTurn) );
01097 
01098     confname.Clear();
01099     startCamera=CAMERA_SMART;
01100     confname << "START_CAM_"<< id+1;
01101     StoreConfitem(tNEW(tConfItem<eCamMode>) (confname,
01102                   "$start_cam_help",
01103                   startCamera));
01104 
01105     confname.Clear();
01106     confname << "START_FOV_"<< id+1;
01107     startFOV=90;
01108     StoreConfitem(tNEW(tConfItem<int>) (confname,
01109                                         "$start_fov_help",
01110                                         startFOV));
01111     confname.Clear();
01112 
01113     confname.Clear();
01114     confname << "SMART_GLANCE_CUSTOM_"<< id+1;
01115     smartCustomGlance=true;
01116     StoreConfitem(tNEW(tConfItem<bool>) (confname,
01117                                          "$camera_smart_glance_custom_help",
01118                                          smartCustomGlance));
01119     confname.Clear();
01120 
01121     int i;
01122     for(i=CAMERA_COUNT-1;i>=0;i--){
01123         confname << "ALLOW_CAM_"<< id+1 << "_" << i;
01124         StoreConfitem(tNEW(tConfItem<bool>) (confname,
01125                                              "$allow_cam_help",
01126                                              allowCam[i]));
01127         allowCam[i]=true;
01128         confname.Clear();
01129     }
01130 
01131     for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
01132         confname << "INSTANT_CHAT_STRING_" << id+1 << '_' <<  i+1;
01133         StoreConfitem(tNEW(tConfItemLine) (confname,
01134                                            "$instant_chat_string_help",
01135                                            instantChatString[i]));
01136         confname.Clear();
01137     }
01138 
01139     for(i=0; i < MAX_INSTANT_CHAT;i++){
01140         if (!default_instant_chat[i])
01141             break;
01142 
01143         instantChatString[i]=default_instant_chat[i];
01144     }
01145 
01146     confname << "SPECTATOR_MODE_"<< id+1;
01147     StoreConfitem(tNEW(tConfItem<bool>)(confname,
01148                                         "$spectator_mode_help",
01149                                         spectate));
01150     spectate=false;
01151     confname.Clear();
01152 
01153     confname << "HIDE_IDENTITY_"<< id+1;
01154     StoreConfitem(tNEW(tConfItem<bool>)(confname,
01155                                         "$hide_identity_help",
01156                                         stealth));
01157     stealth=false;
01158     confname.Clear();
01159 
01160     confname << "NAME_TEAM_AFTER_PLAYER_"<< id+1;
01161     StoreConfitem(tNEW(tConfItem<bool>)(confname,
01162                                         "$name_team_after_player_help",
01163                                         nameTeamAfterMe));
01164     nameTeamAfterMe=false;
01165     confname.Clear();
01166 
01167     confname << "FAV_NUM_PER_TEAM_PLAYER_"<< id+1;
01168     StoreConfitem(tNEW(tConfItem<int>)(confname,
01169                                        "$fav_num_per_team_player_help",
01170                                        favoriteNumberOfPlayersPerTeam ));
01171     favoriteNumberOfPlayersPerTeam = 3;
01172     confname.Clear();
01173 
01174 
01175     confname << "AUTO_INCAM_"<< id+1;
01176     autoSwitchIncam=false;
01177     StoreConfitem(tNEW(tConfItem<bool>) (confname,
01178                                          "$auto_incam_help",
01179                                          autoSwitchIncam));
01180     confname.Clear();
01181 
01182     confname << "CAMWOBBLE_"<< id+1;
01183     wobbleIncam=false;
01184     StoreConfitem(tNEW(tConfItem<bool>) (confname,
01185                                          "$camwobble_help",
01186                                          wobbleIncam));
01187 
01188     confname.Clear();
01189     confname << "COLOR_B_"<< id+1;
01190     StoreConfitem(tNEW(tConfItem<int>) (confname,
01191                                         "$color_b_help",
01192                                         rgb[2]));
01193 
01194     confname.Clear();
01195     confname << "COLOR_G_"<< id+1;
01196     StoreConfitem(tNEW(tConfItem<int>) (confname,
01197                                         "$color_g_help",
01198                                         rgb[1]));
01199 
01200     confname.Clear();
01201     confname << "COLOR_R_"<< id+1;
01202     StoreConfitem(tNEW(tConfItem<int>) (confname,
01203                                         "$color_r_help",
01204                                         rgb[0]));
01205     confname.Clear();
01206 #endif
01207 
01208     tRandomizer & randomizer = tRandomizer::GetInstance();
01209     //static int r = rand() / ( RAND_MAX >> 2 ) + se_UserName().Len();
01210     static int r = randomizer.Get(4) + se_UserName().Len();
01211     int cid = ( r + id ) % 4;
01212 
01213     static REAL R[MAX_PLAYERS]={1,.2,.2,1};
01214     static REAL G[MAX_PLAYERS]={.2,1,.2,1};
01215     static REAL B[MAX_PLAYERS]={.2,.2,1,.2};
01216 
01217     rgb[0]=int(R[cid]*15);
01218     rgb[1]=int(G[cid]*15);
01219     rgb[2]=int(B[cid]*15);
01220 
01221     cam=NULL;
01222 }
01223 
01224 ePlayer::~ePlayer(){
01225     tCHECK_DEST;
01226     DeleteConfitems();
01227 }
01228 
01229 #ifndef DEDICATED
01230 void ePlayer::Render(){
01231     if (cam) cam->Render();
01232 }
01233 #endif
01234 
01235 static void chat( ePlayer * chatter, tString const & msgCore );
01236 void se_rubyEval(tString msgCore) {
01237 #ifdef HAVE_LIBRUBY
01238     try {
01239         tRuby::Safe safe(0.3);
01240         safe.Load(tDirectories::Data(), "scripts/subbanese.rb");
01241         VALUE val = safe.Eval(msgCore);
01242         VALUE to_s = rb_funcall(val, rb_intern("to_s"), 0);
01243         tString res("result: ");
01244         res << StringValuePtr(to_s);
01245 
01246         switch (sn_GetNetState())
01247         {
01248         case nSTANDALONE:
01249         case nCLIENT:
01250             {
01251                 ePlayerNetID * me = ePlayer::PlayerConfig( 0 )->netPlayer;
01252                 me->Chat(res);
01253             }
01254             break;
01255         case nSERVER:
01256             tColoredString send;
01257             send << tColoredString::ColorString( 1,0,0 );
01258             send << "Admin";
01259             send << tColoredString::ColorString( 1,1,.5 );
01260             send << ": " << res << "\n";
01261             sn_ConsoleOut(send);
01262             break;
01263         }
01264     }
01265     catch (std::runtime_error & e) {
01266         std::cout << e.what() << '\n';
01267     }
01268     catch(...) {
01269         std::cout << "unhandled exception\n";
01270     }
01271 #endif
01272 }
01273 
01274 static void se_RequestLogin( ePlayerNetID * p );
01275 
01276 // check whether a special username is banned
01277 #ifdef KRAWALL_SERVER
01278 static bool se_IsUserBanned( ePlayerNetID * p, tString const & name );
01279 #endif
01280 
01281 // receive a "login wanted" message from client
01282 static void se_LoginWanted( nMessage & m )
01283 {
01284 #ifdef KRAWALL_SERVER
01285 
01286     // read player
01287     ePlayerNetID * p;
01288     m >> p;
01289 
01290     if ( p && m.SenderID() == p->Owner() && !p->IsAuthenticated() )
01291     {
01292         // read wanted flag
01293         m >> p->loginWanted;
01294         tString authName;
01295 
01296         // read authentication name
01297         m >> authName;
01298         p->SetRawAuthenticatedName( authName );
01299 
01300         // check for stupid bans
01301         if ( se_IsUserBanned( p, authName ) )
01302         {
01303             return;
01304         }
01305 
01306         se_RequestLogin( p );
01307     }
01308 #else
01309     sn_ConsoleOut( tOutput( "$login_not_supported" ), m.SenderID() );
01310 #endif
01311 }
01312 
01313 static nDescriptor se_loginWanted(204,se_LoginWanted,"AuthWanted");
01314 
01315 // request authentication initiation from the client (if p->loginWanted is set)
01316 static void se_WantLogin( ePlayer * lp )
01317 {
01318     // only meaningful on client
01319     if( sn_GetNetState() != nCLIENT )
01320     {
01321         return;
01322     }
01323 
01324     // don't send if the server won't understand anyway
01325     static nVersionFeature authentication( 15 );
01326     if( !authentication.Supported(0) )
01327     {
01328         return;
01329     }
01330 
01331     tASSERT(lp);
01332     ePlayerNetID *p = lp->netPlayer;
01333     if ( !p )
01334     {
01335         return;
01336     }
01337     
01338     nMessage *m = new nMessage( se_loginWanted );
01339 
01340     // write player
01341     *m << p;
01342     
01343     // write flag and name
01344     *m << p->loginWanted;
01345 
01346     // write authentication name
01347     *m << lp->globalID;
01348 
01349     m->Send( 0 );
01350 
01351     // reset flag
01352     p->loginWanted = false;
01353 }
01354 
01355 void ePlayer::SendAuthNames()
01356 {
01357     // only meaningful on client
01358     if( sn_GetNetState() != nCLIENT )
01359     {
01360         return;
01361     }
01362     for(int i=MAX_PLAYERS-1;i>=0;i--)
01363     {
01364         se_WantLogin( ePlayer::PlayerConfig(i) );
01365     }
01366 }
01367 
01368 // on the server, this should request logins from all players who registered.
01369 static void se_RequestLogin( ePlayerNetID * p )
01370 {
01371     tString userName = p->GetUserName();
01372     tString authority;
01373     if ( p->Owner() != 0 &&  p->loginWanted )
01374     {
01375 #ifdef KRAWALL_SERVER
01376         if ( p->GetRawAuthenticatedName().Len() > 1 )
01377         {
01378             nKrawall::SplitUserName( p->GetRawAuthenticatedName(), userName, authority );
01379         }
01380 
01381         p->loginWanted = 
01382         !nAuthentication::RequestLogin( authority, 
01383                                         userName, 
01384                                         *p, 
01385                                         authority.Len() > 1 ? tOutput( "$login_request", authority ) : tOutput( "$login_request_local" ) );
01386 #endif
01387     }
01388 }
01389 
01390 void ePlayerNetID::RequestScheduledLogins()
01391 {
01392     for( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
01393     {
01394         se_RequestLogin( se_PlayerNetIDs(i) );
01395     }
01396 }
01397 
01398 void ePlayer::LogIn()
01399 {
01400     // only meaningful on client
01401     if( sn_GetNetState() != nCLIENT )
01402     {
01403         return;
01404     }
01405 
01406     // mark all players as wanting to log in
01407     for(int i=MAX_PLAYERS-1;i>=0;i--)
01408     {
01409         ePlayer * lp = ePlayer::PlayerConfig(i); 
01410         if ( lp && lp->netPlayer )
01411         {
01412             lp->netPlayer->loginWanted = true;
01413             se_WantLogin( lp );
01414         }
01415     }
01416 }
01417 
01418 static void se_DisplayChatLocally( ePlayerNetID* p, const tString& say )
01419 {
01420 #ifdef DEBUG_X
01421     if (strstr( say, "BUG" ) )
01422     {
01423         st_Breakpoint();
01424     }
01425 #endif
01426 
01427     if ( p && !p->IsSilenced() && se_enableChat )
01428     {
01429         //tColoredString say2( say );
01430         //say2.RemoveHex();
01431         tColoredString message;
01432         message << *p;
01433         message << tColoredString::ColorString(1,1,.5);
01434         message << ": " << say << '\n';
01435         con << message;
01436 
01437         if (say.StartsWith("eval ")) {
01438             se_rubyEval(say.SubStr(5));
01439         }
01440     }
01441 }
01442 
01443 static bool se_enableNameHilighting=true; // maximal size of chat history
01444 static tSettingItem< bool > se_enableNameHilightingConf("ENABLE_NAME_HILIGHTING",se_enableNameHilighting);
01445 
01446 static void se_DisplayChatLocallyClient( ePlayerNetID* p, const tString& message )
01447 {
01448     if ( p && !p->IsSilenced() && se_enableChat )
01449     {
01450         if (!p->Object() || !p->Object()->Alive()) {
01451             con << tOutput("$dead_console_decoration");
01452         }
01453 
01454         tString actualMessage(message);
01455 
01456         if(se_enableNameHilighting) {
01457             //iterate through players...
01458             rViewportConfiguration* viewportConfiguration = rViewportConfiguration::CurrentViewportConfiguration();
01459             if(viewportConfiguration == 0) return;
01460             for ( int viewport = viewportConfiguration->num_viewports-1; viewport >= 0; --viewport ) {
01461                 int playerID = sr_viewportBelongsToPlayer[ viewport ];
01462 
01463                 // get the player
01464                 ePlayer* player = ePlayer::PlayerConfig( playerID );
01465                 if(player == 0) continue;
01466                 ePlayerNetID* netPlayer = player->netPlayer;
01467                 if(netPlayer == 0) continue;
01468                 if(netPlayer == p) continue; //the same player who chatted, no point in hilighting
01469                 tString const &name = netPlayer->GetName();
01470 
01471                 if ( name.size() > 0 )
01472                 {
01473                     //find all occureces of the name...
01474                     for(tString::size_type pos = actualMessage.find(name); pos != tString::npos; pos = actualMessage.find(name, pos+16)) {
01475                         //find the last color code
01476                         tString::size_type lastcolorpos = actualMessage.rfind("0x", pos);
01477                         tString lastcolorstring;
01478                         if(lastcolorpos != tString::npos) {
01479                             lastcolorstring = actualMessage.SubStr(lastcolorpos, 8);
01480                         } else {
01481                             lastcolorstring = "0xffff7f";
01482                         }
01483 
01484                         if(lastcolorpos >= pos - 8) {
01485                             //the name we matched is within a color code... bad idea to substitute it.
01486                             pos -= 16 - name.size();
01487                             continue;
01488                         }
01489 
01490                         //actually insert the color codes around the name
01491                         actualMessage.insert(pos+name.size(), lastcolorstring);
01492                         actualMessage.insert(pos, "0xff887f");
01493                     }
01494                 }
01495             }
01496         }
01497         con << actualMessage << "\n";
01498 
01499         tString msgCore = tColoredString::RemoveColors(message);
01500         int skip = msgCore.find(": eval");
01501         if (skip > 0) {
01502             se_rubyEval(msgCore.SubStr(skip + 6));
01503         }
01504     }
01505 
01506 }
01507 
01508 static nVersionFeature se_chatRelay( 3 );
01509 
01511 static nVersionFeature se_chatHandlerClient( 6 );
01512 
01513 // chat message from client to server
01514 void handle_chat( nMessage& );
01515 static nDescriptor chat_handler(200,handle_chat,"Chat");
01516 
01517 // checks whether text_to_search contains search_for_text
01518 bool Contains( const tString & search_for_text, const tString & text_to_search ) {
01519     int m = strlen(search_for_text);
01520     int n = strlen(text_to_search);
01521     int a, b;
01522     for (b=0; b<=n-m; ++b) {
01523         for (a=0; a<m && search_for_text[a] == text_to_search[a+b]; ++a)
01524             ;
01525         if (a>=m)
01526             // a match has been found
01527             return true;
01528     }
01529     return false;
01530 }
01531 
01532 // function that returns one of the player names
01533 typedef tString const & (ePlayerNetID::*SE_NameGetter)() const;
01534 
01535 // function that filters strings
01536 typedef tString (*SE_NameFilter)( tString const & );
01537 
01538 // identity filter
01539 static tString se_NameFilterID( tString const & name )
01540 {
01541     return name;
01542 }
01543 
01544 // function that filters players
01545 typedef bool (*SE_NameHider)( ePlayerNetID const * hider, ePlayerNetID const * seeker );
01546 
01547 // non-hider
01548 static bool se_NonHide( ePlayerNetID const * hider, ePlayerNetID const * seeker  )
01549 {
01550     return false;
01551 }
01552 
01553 // the other filter is ePlayerNetID::FilterName
01554 
01555 // search for exact or partial matches in player names
01556 ePlayerNetID * CompareBufferToPlayerNames
01557   ( const tString & nameRaw, 
01558     int & num_matches, 
01559     ePlayerNetID * requester, 
01560     SE_NameGetter GetName, // = &ePlayerNetID::GetName, 
01561     SE_NameFilter Filter, // = &se_NameFilterID, 
01562     SE_NameHider Hider )// = &se_NonHide )
01563 {
01564     num_matches = 0;
01565     ePlayerNetID * match = 0;
01566 
01567     // also filter input string
01568     tString name = (*Filter)( nameRaw );
01569 
01570     // run through all players and look for a match
01571     for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a ) {
01572         ePlayerNetID* toMessage = se_PlayerNetIDs(a);
01573 
01574         if ( (*Hider)( toMessage, requester ) )
01575         {
01576             continue;
01577         }
01578 
01579         tString playerName = (*Filter)( (toMessage->*GetName)() );
01580 
01581         // exact match?
01582         if ( playerName == name )
01583         {
01584             num_matches = 1;
01585             return toMessage;
01586         }
01587 
01588         if ( Contains(name, playerName)) {
01589             match= toMessage; // Doesn't matter that this is wrote over everytime, when we only have one match it will be there.
01590             num_matches+=1;
01591         }
01592     }
01593 
01594     // return result
01595     return match;
01596 }
01597 
01598 
01599 ePlayerNetID * ePlayerNetID::FindPlayerByName( tString const & name, ePlayerNetID * requester )
01600 {
01601    int num_matches = 0;
01602 
01603     // look for matches in the exact player names first
01604    ePlayerNetID * ret = CompareBufferToPlayerNames( name, num_matches, requester, &ePlayerNetID::GetName, &se_NameFilterID, &se_NonHide );
01605     if ( ret && num_matches == 1 )
01606     {
01607         return ret;
01608     }
01609 
01610     // ok, next round: try filtering the names before comparing them, this makes the matching case-insensitive
01611     SE_NameFilter Filter = &ePlayerNetID::FilterName;
01612 
01613     // look for matches in the screen names again
01614     if ( !ret )
01615     {
01616         ret = CompareBufferToPlayerNames( name, num_matches, requester, &ePlayerNetID::GetName, Filter, &se_NonHide );
01617     }
01618     if ( ret && num_matches == 1 )
01619     {
01620         return ret;
01621     }
01622 
01623 #ifdef KRAWALL_SERVER
01624     // nothing found? try the user names.
01625     if ( !ret )
01626     {
01627         ret = CompareBufferToPlayerNames( name, num_matches, requester, &ePlayerNetID::GetUserName, &se_NameFilterID, &se_Hide  );
01628     }
01629     if ( ret && num_matches == 1 )
01630     {
01631         return ret;
01632     }
01633 
01634     // still nothing found? user names again
01635     if ( !ret )
01636     {
01637         ret = CompareBufferToPlayerNames( name, num_matches, requester, &ePlayerNetID::GetUserName, Filter, &se_Hide );
01638     }
01639     if ( ret && num_matches == 1 )
01640     {
01641         return ret;
01642     }
01643 #endif
01644 
01645     // More than than one match for the current buffer. Complain about that.
01646     else if (num_matches > 1) {
01647         tOutput toSender( "$msg_toomanymatches", name );
01648         if ( requester )
01649         { 
01650             sn_ConsoleOut(toSender,requester->Owner() );
01651         }
01652         else
01653         {
01654             con << toSender;
01655         }
01656         return NULL;
01657     }
01658     // 0 matches. The user can't spell. Complain about that, too.
01659     else {
01660         tOutput toSender( "$msg_nomatch", name );
01661         if ( requester )
01662         { 
01663             sn_ConsoleOut(toSender,requester->Owner() );
01664         }
01665         else
01666         {
01667             con << toSender;
01668         }
01669         return NULL;
01670     }
01671 
01672     return 0;
01673 }
01674 
01675 static ePlayerNetID * se_FindPlayerInChatCommand( ePlayerNetID * sender, char const * command, std::istream & s )
01676 {
01677     tString player;
01678     s >> player;
01679 
01680     if (player == "" )
01681     {
01682         sn_ConsoleOut( tOutput( "$chatcommand_requires_player", command ), sender->Owner() );
01683         return 0;
01684     }
01685     
01686     return ePlayerNetID::FindPlayerByName( player, sender );
01687 }
01688 
01689 // chat message from server to client
01690 void handle_chat_client( nMessage & );
01691 static nDescriptor chat_handler_client(203,handle_chat_client,"Chat Client");
01692 
01693 void handle_chat_client(nMessage &m)
01694 {
01695     if(sn_GetNetState()!=nSERVER)
01696     {
01697         unsigned short id;
01698         m.Read(id);
01699         tColoredString say;
01700         m >> say;
01701 
01702         tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
01703 
01704         se_DisplayChatLocallyClient( p, say );
01705     }
01706 }
01707 
01708 // appends chat message to something
01709 template< class TARGET >
01710 void se_AppendChat( TARGET & out, tString const & message )
01711 {
01712     if ( message.Len() <= se_SpamMaxLen*2 )
01713         out << message;
01714     else
01715     {
01716         tString cut( message );
01717         cut.SetLen( se_SpamMaxLen*2 );
01718         out << cut;
01719     }
01720 }
01721 
01722 // builds a colored chat string
01723 static tColoredString se_BuildChatString( ePlayerNetID const * sender, tString const & message )
01724 {
01725     tColoredString console;
01726     console << *sender;
01727     console << tColoredString::ColorString(1,1,.5) << ": ";
01728     se_AppendChat( console, message );
01729 
01730     return console;
01731 }
01732 
01733 // Build a colored /team message
01734 static tColoredString se_BuildChatString( eTeam const *team, ePlayerNetID const *sender, tString const &message )
01735 {
01736     tColoredString console;
01737     console << *sender;
01738 
01739     if( !sender->CurrentTeam() )
01740     {
01741         // foo --> Spectatos: message
01742         console << tColoredString::ColorString(1,1,.5) << " --> " << tOutput("$player_spectator_message");
01743     }
01744     else if (sender->CurrentTeam() == team)
01745     {
01746         // foo --> Teammates: some message here
01747         console << tColoredString::ColorString(1,1,.5) << " --> ";
01748         console << tColoredString::ColorString(team->R(),team->G(),team->B()) << tOutput("$player_team_message");
01749     }
01750     else {
01751         // foo (Red Team) --> Blue Team: some message here
01752         eTeam *senderTeam = sender->CurrentTeam();
01753         console << tColoredString::ColorString(1,1,.5) << " (";
01754         console << tColoredString::ColorString(senderTeam->R(),senderTeam->G(),senderTeam->B()) << senderTeam->Name();
01755         console << tColoredString::ColorString(1,1,.5) << ") --> ";
01756         console << tColoredString::ColorString(team->R(),team->G(),team->B()) << team->Name();
01757     }
01758 
01759     console << tColoredString::ColorString(1,1,.5) << ": ";
01760     se_AppendChat( console, message );
01761 
01762     return console;
01763 }
01764 
01765 // builds a colored chat /msg string
01766 static tColoredString se_BuildChatString( ePlayerNetID const * sender, ePlayerNetID const * receiver, tString const & message )
01767 {
01768     tColoredString console;
01769     console << *sender;
01770     console << tColoredString::ColorString(1,1,.5) << " --> ";
01771     console << *receiver;
01772     console << tColoredString::ColorString(1,1,.5) << ": ";
01773     se_AppendChat( console, message );
01774 
01775     return console;
01776 }
01777 
01778 // prepares a chat message with a specific total text originating from the given player
01779 static nMessage* se_ServerControlledChatMessageConsole( ePlayerNetID const * player, tString const & toConsole )
01780 {
01781     tASSERT( player );
01782 
01783     nMessage *m=tNEW(nMessage) (chat_handler_client);
01784 
01785     m->Write( player->ID() );
01786     *m << toConsole;
01787 
01788     return m;
01789 }
01790 
01791 // prepares a chat message with a chat string (only the message, not the whole console line) originating from the given player
01792 static nMessage* se_ServerControlledChatMessage( ePlayerNetID const * sender, tString const & message )
01793 {
01794     tASSERT( sender );
01795 
01796     return se_ServerControlledChatMessageConsole( sender, se_BuildChatString( sender, message ) );
01797 }
01798 
01799 // prepares a chat message with a chat message originating from the given player to the given receiver
01800 static nMessage* se_ServerControlledChatMessage( ePlayerNetID const * sender, ePlayerNetID const * receiver, tString const & message )
01801 {
01802     tASSERT( sender );
01803     tASSERT( receiver );
01804 
01805     return se_ServerControlledChatMessageConsole( sender, se_BuildChatString( sender, receiver, message ) );
01806 }
01807 
01808 // prepares a chat message with a chat message originating from the given player to the given team
01809 static nMessage* se_ServerControlledChatMessage(  eTeam const * team, ePlayerNetID const * sender, tString const & message )
01810 {
01811     tASSERT( sender );
01812 
01813     return se_ServerControlledChatMessageConsole( sender, se_BuildChatString(team, sender, message) );
01814 }
01815 
01816 // pepares a chat message the client has to put together
01817 static nMessage* se_NewChatMessage( ePlayerNetID const * player, tString const & message )
01818 {
01819     tASSERT( player );
01820 
01821     nMessage *m=tNEW(nMessage) (chat_handler);
01822     m->Write( player->ID() );
01823     se_AppendChat( *m, message );
01824 
01825     return m;
01826 }
01827 
01828 // prepares a very old style chat message: just a regular remote console output message
01829 static nMessage* se_OldChatMessage( tString const & line )
01830 {
01831     return sn_ConsoleOutMessage( line + "\n" );
01832 }
01833 
01834 // prepares a very old style chat message: just a regular remote console output message
01835 static nMessage* se_OldChatMessage( ePlayerNetID const * player, tString const & message )
01836 {
01837     tASSERT( player );
01838 
01839     return se_OldChatMessage( se_BuildChatString( player, message ) );
01840 }
01841 
01842 // send the chat of player p to all connected clients after properly formatting it
01843 // ( the clients will see <player>: <say>
01844 void se_BroadcastChat( ePlayerNetID* p, const tString& say )
01845 {
01846     // create chat messages
01847     tJUST_CONTROLLED_PTR< nMessage > mServerControlled = se_ServerControlledChatMessage( p, say );
01848     tJUST_CONTROLLED_PTR< nMessage > mNew = se_NewChatMessage( p, say );
01849     tJUST_CONTROLLED_PTR< nMessage > mOld = se_OldChatMessage( p, say );
01850 
01851     // send them to the users, depending on what they understand
01852     for ( int user = MAXCLIENTS; user > 0; --user )
01853     {
01854         if ( sn_Connections[ user ].socket )
01855         {
01856             if ( se_chatHandlerClient.Supported( user ) )
01857                 mServerControlled->Send( user );
01858             else if ( se_chatRelay.Supported( user ) )
01859                 mNew->Send( user );
01860             else
01861                 mOld->Send( user );
01862         }
01863     }
01864 }
01865 
01866 // sends the full chat line  to all connected players
01867 // ( the clients will see <line> )
01868 void se_BroadcastChatLine( ePlayerNetID* p, const tString& line, const tString& forOldClients )
01869 {
01870     // create chat messages
01871     tJUST_CONTROLLED_PTR< nMessage > mServerControlled = se_ServerControlledChatMessageConsole( p, line );
01872     tJUST_CONTROLLED_PTR< nMessage > mNew = se_NewChatMessage( p, forOldClients );
01873     tJUST_CONTROLLED_PTR< nMessage > mOld = se_OldChatMessage( line );
01874 
01875     // send them to the users, depending on what they understand
01876     for ( int user = MAXCLIENTS; user > 0; --user )
01877     {
01878         if ( sn_Connections[ user ].socket )
01879         {
01880             if ( se_chatHandlerClient.Supported( user ) )
01881                 mServerControlled->Send( user );
01882             else if ( se_chatRelay.Supported( user ) )
01883                 mNew->Send( user );
01884             else
01885                 mOld->Send( user );
01886         }
01887     }
01888 }
01889 
01890 // sends a private message from sender to receiver, and really sends it to eavesdropper (will usually be equal to receiver)
01891 void se_SendPrivateMessage( ePlayerNetID const * sender, ePlayerNetID const * receiver, ePlayerNetID const * eavesdropper, tString const & message )
01892 {
01893     tASSERT( sender );
01894     tASSERT( receiver );
01895 
01896     // determine receiver client id
01897     int cid = eavesdropper->Owner();
01898 
01899     // determine wheter the receiver knows about the server controlled chat message
01900     if ( se_chatHandlerClient.Supported( cid ) )
01901     {
01902         // send server controlled message
01903         se_ServerControlledChatMessage( sender, receiver, message )->Send( cid );
01904     }
01905     else
01906     {
01907         tColoredString say;
01908         say << tColoredString::ColorString(1,1,.5) << "( --> ";
01909         say << *receiver;
01910         say << tColoredString::ColorString(1,1,.5) << " ) ";
01911         say << message;
01912 
01913         // format message containing receiver
01914         if ( se_chatRelay.Supported( cid ) )
01915         {
01916             // send client formatted message
01917             se_NewChatMessage( sender, say )->Send( cid );
01918         }
01919         else
01920         {
01921             // send console out message
01922             se_OldChatMessage( sender, say )->Send( cid );
01923         }
01924     }
01925 }
01926 
01927 // Sends a /team message
01928 void se_SendTeamMessage( eTeam const * team, ePlayerNetID const * sender ,ePlayerNetID const * receiver, tString const & message )
01929 {
01930     tASSERT( receiver );
01931     tASSERT( sender );
01932 
01933     int clientID = receiver->Owner();
01934     if ( clientID == 0 )
01935         return;
01936 
01937     if ( se_chatHandlerClient.Supported( clientID ) ) {
01938         se_ServerControlledChatMessage( team, sender, message )->Send( clientID );
01939     }
01940     else {
01941         tColoredString say;
01942         say << tColoredString::ColorString(1,1,.5) << "( " << *sender;
01943 
01944         if( !sender->CurrentTeam() )
01945         {
01946             // foo --> Spectatos: message
01947             say << tColoredString::ColorString(1,1,.5) << " --> " << tOutput("$player_spectator_message");
01948         }
01949         else if (sender->CurrentTeam() == team) {
01950             // ( foo --> Teammates ) some message here
01951             say << tColoredString::ColorString(1,1,.5) << " --> ";
01952             say << tColoredString::ColorString(team->R(),team->G(),team->B()) << tOutput("$player_team_message");;
01953         }
01954         // ( foo (Blue Team) --> Red Team ) some message
01955         else {
01956             eTeam *senderTeam = sender->CurrentTeam();
01957             say << tColoredString::ColorString(1,1,.5) << " (";
01958             say << tColoredString::ColorString(team->R(),team->G(),team->B()) << team->Name();
01959             say << tColoredString::ColorString(1,1,.5) << " ) --> ";
01960             say << tColoredString::ColorString(senderTeam->R(),senderTeam->G(),senderTeam->B()) << senderTeam->Name();
01961         }
01962         say << tColoredString::ColorString(1,1,.5) << " ) ";
01963         say << message;
01964 
01965         // format message containing receiver
01966         if ( se_chatRelay.Supported( clientID ) )
01967             // send client formatted message
01968             se_NewChatMessage( sender, say )->Send( clientID );
01969         else
01970             // send console out message
01971             se_OldChatMessage( sender, say )->Send( clientID );
01972     }
01973 }
01974 
01975 // returns a player ( not THE player, there may be more ) belonging to a user ID
01976 /*
01977 static ePlayerNetID * se_GetPlayerFromUserID( int uid )
01978 {
01979     // run through all players and look for a match
01980     for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a )
01981     {
01982         ePlayerNetID * p = se_PlayerNetIDs(a);
01983         if ( p && p->Owner() == uid )
01984             return p;
01985     }
01986 
01987     // found nothing
01988     return 0;
01989 }
01990 */
01991 
01992 // returns a player ( not THE player, there may be more ) belonging to a user ID that is still alive
01993 // static
01994 ePlayerNetID * se_GetAlivePlayerFromUserID( int uid )
01995 {
01996     // run through all players and look for a match
01997     for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a )
01998     {
01999         ePlayerNetID * p = se_PlayerNetIDs(a);
02000         if ( p && p->Owner() == uid &&
02001                 ( ( p->Object() && p->Object()->Alive() ) ) )
02002             return p;
02003     }
02004 
02005     // found nothing
02006     return 0;
02007 }
02008 
02009 //The Base Remote Admin Password
02010 static tString sg_adminPass( "NONE" );
02011 static tConfItemLine sg_adminPassConf( "ADMIN_PASS", sg_adminPass );
02012 
02013 #ifdef DEDICATED
02014 
02015 // console with filter for remote admin output redirection
02016 class eAdminConsoleFilter:public tConsoleFilter{
02017 public:
02018     eAdminConsoleFilter( int netID )
02019             :netID_( netID )
02020     {
02021     }
02022 
02023     ~eAdminConsoleFilter()
02024     {
02025         sn_ConsoleOut( message_, netID_ );
02026     }
02027 private:
02028     // we want to come first, the admin should get unfiltered output
02029     virtual int DoGetPriority() const{ return -100; }
02030 
02031     // don't actually filter; take line and add it to the message sent to the admin
02032     virtual void DoFilterLine( tString &line )
02033     {
02034         //tColoredString message;
02035         message_ << tColoredString::ColorString(1,.3,.3) << "RA: " << tColoredString::ColorString(1,1,1) << line << "\n";
02036 
02037         // don't let message grow indefinitely
02038         if (message_.Len() > 600)
02039         {
02040             sn_ConsoleOut( message_, netID_ );
02041             message_.Clear();
02042         }
02043     }
02044 
02045     int netID_;              // the network user ID to send the result to
02046     tColoredString message_; // the console message for the remote administrator
02047 };
02048 
02049 static tString se_InterceptCommands;
02050 static tConfItemLine se_InterceptCommandsConf( "INTERCEPT_COMMANDS", se_InterceptCommands );
02051 
02052 static bool se_interceptUnknownCommands = false;
02053 static tSettingItem<bool> se_interceptUnknownCommandsConf("INTERCEPT_UNKNOWN_COMMANDS",
02054         se_interceptUnknownCommands);
02055 
02056 // minimal access level for /admin
02057 static tAccessLevel se_adminAccessLevel = tAccessLevel_Moderator;
02058 static tSettingItem< tAccessLevel > se_adminAccessLevelConf( "ACCESS_LEVEL_ADMIN", se_adminAccessLevel );
02059 
02060 void handle_command_intercept(ePlayerNetID *p, tString say) {
02061     con << "[cmd] " << *p << ": " << say << '\n';
02062 }
02063 
02064 #ifdef KRAWALL_SERVER
02065 
02066 // minimal access level for /op/deop
02067 static tAccessLevel se_opAccessLevel = tAccessLevel_TeamLeader;
02068 static tSettingItem< tAccessLevel > se_opAccessLevelConf( "ACCESS_LEVEL_OP", se_opAccessLevel );
02069 
02070 // maximal result thereof
02071 static tAccessLevel se_opAccessLevelMax = tAccessLevel_Moderator;
02072 static tSettingItem< tAccessLevel > se_opAccessLevelMaxConf( "ACCESS_LEVEL_OP_MAX", se_opAccessLevelMax );
02073 
02074 // an operation that changes the access level of another player
02075 typedef void (*OPFUNC)( ePlayerNetID * admin, ePlayerNetID * victim, int level );
02076 static void se_ChangeAccess( ePlayerNetID * admin, std::istream & s, char const * command, OPFUNC F )
02077 {
02078     if ( admin->GetAccessLevel() <= se_opAccessLevel )
02079     {
02080         ePlayerNetID * victim = se_FindPlayerInChatCommand( admin, command, s );
02081         if ( victim )
02082         {
02083             if ( victim == admin )
02084             {
02085                 sn_ConsoleOut( tOutput( "$access_level_op_self", command ), admin->Owner() );
02086             }
02087             else if ( victim->GetAccessLevel() < admin->GetAccessLevel() )
02088             {
02089                 sn_ConsoleOut( tOutput( "$access_level_op_overpowered", command ), admin->Owner() );
02090             }
02091             else
02092             {
02093                 // read optional access level
02094                 int level = 1;
02095                 s >> level;
02096 
02097                 (*F)( admin, victim, static_cast< tAccessLevel >( level ) );
02098             }
02099         }
02100     }
02101     else
02102     {
02103         sn_ConsoleOut( tOutput( "$access_level_op_denied", command ), admin->Owner() );
02104     }
02105 }
02106 
02107 // our operations of this kind: op grants access
02108 void se_OpBase( ePlayerNetID * admin, ePlayerNetID * victim, char const * command, int accessLevel )
02109 {
02110     tString authName = victim->GetUserName() + "@L_OP";
02111     if ( victim->IsAuthenticated() )
02112     {
02113         authName = victim->GetRawAuthenticatedName();
02114     }
02115 
02116     if ( accessLevel < se_opAccessLevelMax )
02117         accessLevel = se_opAccessLevelMax;
02118 
02119     // no use authenticating an AI :)
02120     if ( !victim->IsHuman() )
02121     {
02122         sn_ConsoleOut( tOutput( "$access_level_op_denied_ai", command ), admin->Owner() );
02123     }
02124 
02125     if ( accessLevel > admin->GetAccessLevel() )
02126     {
02127         victim->Authenticate( authName, static_cast< tAccessLevel >( accessLevel ), admin );
02128     }
02129     else
02130     {
02131         sn_ConsoleOut( tOutput( "$access_level_op_denied_max", command ), admin->Owner() );
02132     }
02133 }
02134 
02135 void se_Op( ePlayerNetID * admin, ePlayerNetID * victim, int level )
02136 {
02137     int accessLevel = admin->GetAccessLevel() + 1;
02138 
02139     // respect passed in level
02140     if ( accessLevel < level )
02141     {
02142         accessLevel = level;
02143     }
02144     
02145     se_OpBase( admin, victim, "/op", accessLevel );
02146 }
02147 
02148 // DeOp takes it away
02149 void se_DeOp( ePlayerNetID * admin, ePlayerNetID * victim, int )
02150 {
02151     if ( victim->IsAuthenticated() )
02152     {
02153         victim->DeAuthenticate( admin );
02154     }
02155 }
02156 
02157 void se_Demote( ePlayerNetID * admin, ePlayerNetID * victim, int level );
02158 
02159 // Promote elevates the access rights
02160 void se_Promote( ePlayerNetID * admin, ePlayerNetID * victim, int level )
02161 {
02162     if ( level < 0 )
02163     {
02164         se_Demote( admin, victim, -level );
02165         return;
02166     }
02167 
02168     int accessLevelInt = victim->GetAccessLevel() - level;
02169     tAccessLevel accessLevel = static_cast< tAccessLevel >( accessLevelInt );
02170     if ( accessLevel > tAccessLevel_Authenticated )
02171     {
02172         accessLevel = tAccessLevel_Authenticated;
02173     }
02174     if ( accessLevel < tCurrentAccessLevel::GetAccessLevel() + 1 )
02175     {
02176         accessLevel = static_cast< tAccessLevel >( tCurrentAccessLevel::GetAccessLevel() + 1 );
02177     }
02178 
02179     if ( victim->IsAuthenticated() )
02180     {
02181         victim->SetAccessLevel( accessLevel );
02182 
02183         se_SecretConsoleOut( tOutput( "$access_level_promote", 
02184                                       victim->GetLogName(),
02185                                       tCurrentAccessLevel::GetName( accessLevel ),
02186                                       admin->GetLogName() ), victim, admin );
02187     }
02188     else
02189     {
02190         se_OpBase( admin, victim, "/promote", accessLevel );
02191     }
02192 }
02193 
02194 // Deomote reduces the access rights
02195 void se_Demote( ePlayerNetID * admin, ePlayerNetID * victim, int level )
02196 {
02197     // for people who think they are smart :)
02198     if ( level < 0 )
02199     {
02200         se_Promote( admin, victim, -level );
02201         return;
02202     }
02203 
02204     int accessLevelInt = victim->GetAccessLevel() + level;
02205     tAccessLevel accessLevel = static_cast< tAccessLevel >( accessLevelInt );
02206 
02207     if ( accessLevel <= tAccessLevel_Authenticated )
02208     {
02209         se_SecretConsoleOut( tOutput( "$access_level_demote", 
02210                                       victim->GetLogName(),
02211                                       tCurrentAccessLevel::GetName( accessLevel ),
02212                                       admin->GetLogName() ), victim, admin );
02213 
02214         victim->SetAccessLevel( accessLevel );
02215     }
02216     else if ( victim->IsAuthenticated() )
02217     {
02218         victim->DeAuthenticate( admin );
02219     }
02220 }
02221 
02222 // minimal access level for /team management
02223 static tAccessLevel se_teamAccessLevel = tAccessLevel_TeamLeader;
02224 static tSettingItem< tAccessLevel > se_teamAccessLevelConf( "ACCESS_LEVEL_TEAM", se_teamAccessLevel );
02225 
02226 // returns the team managed by an admin
02227 static eTeam * se_GetManagedTeam( ePlayerNetID * admin )
02228 {
02229     // the team to invite to is, of course, the team the admin is in.
02230     eTeam * team = admin->CurrentTeam();
02231     ePlayerNetID::eTeamSet const & invitations = admin->GetInvitations();
02232     
02233     // unless, of course, he is in no team, then let him relay the one invitation
02234     // he has.
02235     if ( !team && invitations.size() == 1 )
02236     {
02237         team = *invitations.begin();
02238     }
02239 
02240     return team;
02241 }
02242 
02243 // checks
02244 static bool se_CanManageTeam( ePlayerNetID * admin, bool emergency )
02245 {
02246     // check regular access level 
02247     if ( admin->GetAccessLevel() <= se_teamAccessLevel )
02248     {
02249         return true;
02250     }
02251 
02252     // check emergency
02253     if ( !emergency )
02254     {
02255         return false;
02256     }
02257 
02258     // emergency: /invite and /unlock must be available to a player with
02259     // maximal access rights on the team, otherwise teams may become
02260     // locked and orphaned, without a chance to get new members.
02261 
02262     // no team? Nothing to manage
02263     eTeam * team = admin->CurrentTeam();
02264     if ( !team )
02265     {
02266         return false;
02267     }
02268 
02269     // check for players of a higher level
02270     for( int i = team->NumPlayers()-1; i >= 0; --i )
02271     {
02272         ePlayerNetID * otherPlayer = team->Player(i);
02273         if ( otherPlayer->IsHuman() && otherPlayer->GetAccessLevel() < admin->GetAccessLevel() )
02274         {
02275             return false;
02276         }
02277     }
02278 
02279     return true;
02280 }
02281 
02282 // invite/uninvite a player
02283 typedef void (eTeam::*INVITE)( ePlayerNetID * victim );
02284 static void se_Invite( char const * command, ePlayerNetID * admin, std::istream & s, INVITE invite )
02285 {
02286     if ( se_CanManageTeam( admin, invite == &eTeam::Invite ) )
02287     {
02288         // get the team the admin can manage
02289         eTeam * team = se_GetManagedTeam( admin );
02290 
02291         if ( team )
02292         {
02293             ePlayerNetID * victim = se_FindPlayerInChatCommand( admin, command, s );
02294             if ( victim )
02295             {
02296                 // invite/uninvite him
02297                 (team->*invite)( victim );
02298             }
02299         }
02300         else
02301         {
02302             sn_ConsoleOut( tOutput( "$invite_no_team", command ), admin->Owner() );
02303         }
02304     }
02305     else
02306     {
02307         sn_ConsoleOut( tOutput( "$access_level_op_denied", command ), admin->Owner() );
02308     }
02309 }
02310 
02311 // changes private party status of a team
02312 static void se_Lock( char const * command, ePlayerNetID * admin, std::istream & s, bool lock )
02313 {
02314     if ( se_CanManageTeam( admin, !lock ) )
02315     {
02316         // get the team the admin can manage
02317         eTeam * team = se_GetManagedTeam( admin );
02318 
02319         if ( team )
02320         {
02321             team->SetLocked( lock );
02322         }
02323         else
02324         {
02325             sn_ConsoleOut( tOutput( "$invite_no_team", command ), admin->Owner() );
02326         }
02327     }
02328     else
02329     {
02330         sn_ConsoleOut( tOutput( "$access_level_op_denied", command ), admin->Owner() );
02331     }
02332 }
02333 
02334 #else // KRAWALL
02335 // returns the team managed by an admin
02336 static eTeam * se_GetManagedTeam( ePlayerNetID * admin )
02337 {
02338     return admin->CurrentTeam();
02339 }
02340 #endif // KRAWALL
02341 
02342 // the following function really is only supposed to be called from here and nowhere else
02343 // (access right escalation risk):
02344 // log in (via admin password or hash based login)
02345 static void se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE( ePlayerNetID * p, std::istream & s )
02346 {
02347     tString params("");
02348     params.ReadLine( s );
02349 #ifndef KRAWALL_SERVER
02350     if ( params == "" )
02351         return;
02352 #endif
02353     
02354     // trim whitespace
02355 
02356     // for the trunk:
02357     // params.Trim();
02358     // here,we have to do the work manually
02359     {
02360         int lastNonSpace = params.Len() - 2;
02361         while ( lastNonSpace >= 0 && isblank(params[lastNonSpace]) )
02362         {
02363             --lastNonSpace;
02364         }
02365 
02366         if ( lastNonSpace < params.Len() - 2 )
02367         {
02368             params = params.SubStr( 0, lastNonSpace + 1 );
02369         }
02370     }
02371 
02372 #ifndef KRAWALL_SERVER
02373     // the password is not stored in the recording, hence we have to store the
02374     // result of the password test
02375     bool accept = true;
02376     static const char * section = "REMOTE_LOGIN";
02377     if ( !tRecorder::Playback( section, accept ) )
02378         accept = ( params == sg_adminPass && sg_adminPass != "NONE" );
02379     tRecorder::Record( section, accept );
02380     
02381     //change this later to read from a password file or something...
02382     //or integrate it with auth if we ever get that done...
02383     if ( accept ) {
02384         // the following function really is only supposed to be called from here and nowhere else
02385         // (access right escalation risk)
02386         se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE( p );
02387     }
02388     else
02389     {
02390         tString failedLogin;
02391         sn_ConsoleOut("Login denied!\n",p->Owner());
02392         failedLogin << "Remote admin login for user \"" << p->GetUserName();
02393         failedLogin << "\" using password \"" << params << "\" rejected.\n";
02394         sn_ConsoleOut(failedLogin, 0);
02395     }
02396 #else
02397     if ( sn_GetNetState() == nSERVER && p->Owner() != sn_myNetID )
02398     {
02399         if ( p->IsAuthenticated() )
02400         {
02401             sn_ConsoleOut( "$login_request_redundant", p->Owner() );
02402             return;
02403         }
02404         
02405         if ( p->GetRawAuthenticatedName().Len() <= 1 || params.StrPos("@") >= 0 )
02406         {
02407             if ( params.StrPos( "@" ) >= 0 )
02408             {
02409                 p->SetRawAuthenticatedName( params );
02410             }
02411             else
02412             {
02413                 p->SetRawAuthenticatedName( p->GetUserName() + "@" + params );
02414             }
02415         }
02416         
02417         // check for stupid bans
02418         if ( se_IsUserBanned( p, p->GetRawAuthenticatedName() ) )
02419         {
02420             return;
02421         }
02422         
02423         p->loginWanted = true;
02424         
02425         se_RequestLogin( p );
02426     }
02427 #endif
02428 }
02429 
02430 // log out
02431 static void se_AdminLogout( ePlayerNetID * p )
02432 {
02433 #ifdef KRAWALL_SERVER
02434     // revoke the other kind of authentication as well
02435     if ( p->IsAuthenticated() )
02436     {
02437         p->DeAuthenticate();
02438     }
02439 #else
02440     if ( p->IsLoggedIn() )
02441     {
02442         sn_ConsoleOut("You have been logged out!\n",p->Owner());
02443     }
02444     p->BeNotLoggedIn();
02445 #endif
02446 }
02447 
02448 // /admin chat command
02449 static void se_AdminAdmin( ePlayerNetID * p, std::istream & s )
02450 {
02451     if ( p->GetAccessLevel() > se_adminAccessLevel )
02452     {
02453         sn_ConsoleOut( tOutput( "$access_level_admin_denied" ), p->Owner() );
02454         return;
02455     }
02456 
02457     tString str;
02458     str.ReadLine(s);
02459     con << "Remote admin command by " << *p << "0xRESETT: " << str << "\n";
02460     std::istringstream stream(&str(0));
02461     
02462     // install filter
02463     eAdminConsoleFilter consoleFilter( p->Owner() );
02464     
02465     if ( tRecorder::IsPlayingBack() )
02466     {
02467         tConfItemBase::LoadPlayback();
02468     }
02469     else
02470     {
02471         tConfItemBase::LoadLine(stream);
02472     }
02473 }
02474 
02475 static void handle_chat_admin_commands( ePlayerNetID * p, tString const & command, tString const & say, std::istream & s )
02476 {
02477     if  (command == "/login")
02478     {
02479         // the following function really is only supposed to be called from here and nowhere else
02480         // (access right escalation risk)
02481         se_AdminLogin_ReallyOnlyCallFromChatKTHNXBYE( p, s );
02482     }
02483     else  if (command == "/logout")
02484     {
02485         se_AdminLogout( p );
02486     }
02487 #ifdef KRAWALL_SERVER
02488     else if ( command == "/op" ) 
02489     {
02490         se_ChangeAccess( p, s, "/op", &se_Op );
02491     }
02492     else if ( command == "/deop" ) 
02493     {
02494         se_ChangeAccess( p, s, "/deop", &se_DeOp );
02495     }
02496     else if ( command == "/promote" ) 
02497     {
02498         se_ChangeAccess( p, s, "/promote", &se_Promote );
02499     }
02500     else if ( command == "/demote" ) 
02501     {
02502         se_ChangeAccess( p, s, "/demote", &se_Demote );
02503     }
02504     else if ( command == "/invite" )
02505     {
02506         se_Invite( command, p, s, &eTeam::Invite );
02507     }
02508     else if ( command == "/uninvite" )
02509     {
02510         se_Invite( command, p, s, &eTeam::UnInvite );
02511     }
02512     else if ( command == "/lock" )
02513     {
02514         se_Lock( command, p, s, true );
02515     }
02516     else if ( command == "/unlock" )
02517     {
02518         se_Lock( command, p, s, false );
02519     }
02520 #endif
02521     else  if ( command == "/admin" )
02522     {
02523         se_AdminAdmin( p, s );
02524     }
02525     else
02526         if (se_interceptUnknownCommands)
02527         {
02528             handle_command_intercept(p, say);
02529         }
02530         else
02531         {
02532             sn_ConsoleOut( tOutput( "$chat_command_unknown", command ), p->Owner() );
02533         }
02534 }
02535 #else // DEDICATED
02536 // returns the team managed by an admin
02537 static eTeam * se_GetManagedTeam( ePlayerNetID * admin )
02538 {
02539     return admin->CurrentTeam();
02540 }
02541 #endif // DEDICATED
02542 
02543 // time during which no repeaded chat messages are printed
02544 static REAL se_alreadySaidTimeout=5.0;
02545 static tSettingItem<REAL> se_alreadySaidTimeoutConf("SPAM_PROTECTION_REPEAT",
02546         se_alreadySaidTimeout);
02547 
02548 #ifndef KRAWALL_SERVER
02549 // flag set to allow players to shuffle themselves up in a team
02550 static bool se_allowShuffleUp=false;
02551 static tSettingItem<bool> se_allowShuffleUpConf("TEAM_ALLOW_SHUFFLE_UP",
02552         se_allowShuffleUp);
02553 #else
02554 static tAccessLevel se_shuffleUpAccessLevel = tAccessLevel_TeamMember;
02555 static tSettingItem< tAccessLevel > se_shuffleUpAccessLevelConf( "ACCESS_LEVEL_SHUFFLE_UP", se_shuffleUpAccessLevel );
02556 #endif
02557 
02558 // help message printed out to whoever asks for it
02559 static tString se_helpMessage("");
02560 static tConfItemLine se_helpMessageConf("HELP_MESSAGE",se_helpMessage);
02561 
02562 static bool se_silenceAll = false;              // flag indicating whether everyone should be silenced
02563 
02564 // minimal access level for chat
02565 static tAccessLevel se_chatAccessLevel = tAccessLevel_Program;
02566 static tSettingItem< tAccessLevel > se_chatAccessLevelConf( "ACCESS_LEVEL_CHAT", se_chatAccessLevel );
02567 
02568 // time between public chat requests, set to 0 to disable
02569 REAL se_chatRequestTimeout = 60;
02570 static tSettingItem< REAL > se_chatRequestTimeoutConf( "ACCESS_LEVEL_CHAT_TIMEOUT", se_chatRequestTimeout );
02571 
02572 // access level a spectator has to have to be able to listen to /team messages
02573 static tAccessLevel se_teamSpyAccessLevel = tAccessLevel_Moderator;
02574 static tSettingItem< tAccessLevel > se_teamSpyAccessLevelConf( "ACCESS_LEVEL_SPY_TEAM", se_teamSpyAccessLevel );
02575 
02576 // access level a user to have to be able to listen to /msg messages
02577 static tAccessLevel se_msgSpyAccessLevel = tAccessLevel_Owner;
02578 static tSettingItem< tAccessLevel > se_msgSpyAccessLevelConf( "ACCESS_LEVEL_SPY_MSG", se_msgSpyAccessLevel );
02579 
02580 // access level a user has to have to get IP addresses in /players output
02581 static tAccessLevel se_ipAccessLevel = tAccessLevel_Moderator;
02582 static tSettingItem< tAccessLevel > se_ipAccessLevelConf( "ACCESS_LEVEL_IPS", se_ipAccessLevel );
02583 
02584 static tSettingItem<bool> se_silAll("SILENCE_ALL",
02585                                     se_silenceAll);
02586 
02587 // handles spam checking at the right time
02588 class eChatSpamTester
02589 {
02590 public:
02591     eChatSpamTester( ePlayerNetID * p, tString const & say )
02592     : tested_( false ), shouldBlock_( false ), player_( p ), say_( say )
02593     {
02594     }
02595 
02596     bool Block()
02597     {
02598         if ( !tested_ )
02599         {
02600             shouldBlock_ = Check();
02601             tested_ = true;
02602         }
02603 
02604         return shouldBlock_;
02605     }
02606 
02607     bool Check()
02608     {
02609         nTimeRolling currentTime = tSysTimeFloat();
02610 
02611         // check if the player already said the same thing not too long ago
02612         for (short c = 0; c < player_->lastSaid.Len(); c++)
02613         {
02614             if( (say_.StripWhitespace() == player_->lastSaid[c].StripWhitespace()) && ( (currentTime - player_->lastSaidTimes[c]) < se_alreadySaidTimeout) )
02615             {
02616                 sn_ConsoleOut( tOutput("$spam_protection_repeat", say_ ), player_->Owner() );
02617                 return true;
02618             }
02619         }
02620 
02621         REAL lengthMalus = say_.Len() / 20.0;
02622         if ( lengthMalus > 4.0 )
02623         {
02624             lengthMalus = 4.0;
02625         }
02626             
02627         if ( nSpamProtection::Level_Mild <= player_->chatSpam_.CheckSpam( 1+lengthMalus, player_->Owner(), tOutput("$spam_chat") ) )
02628         {
02629             return true;
02630         }
02631 
02632 #ifdef KRAWALL_SERVER
02633         if ( player_->GetAccessLevel() > se_chatAccessLevel )
02634         {
02635             // every once in a while, remind the public that someone has something to say
02636             static double nextRequest = 0;
02637             double now = tSysTimeFloat();
02638             if ( now > nextRequest && se_chatRequestTimeout > 0 )
02639             {
02640                 sn_ConsoleOut( tOutput("$access_level_chat_request", player_->GetColoredName(), player_->GetLogName() ), player_->Owner() );
02641                 nextRequest = now + se_chatRequestTimeout;
02642             }
02643             else
02644             {
02645                 sn_ConsoleOut( tOutput("$access_level_chat_denied" ), player_->Owner() );
02646             }
02647             
02648             return true;
02649         }
02650 #endif
02651             
02652         // update last said record
02653         {
02654             for( short zz = 12; zz>=1; zz-- )
02655             {
02656                 player_->lastSaid[zz] = player_->lastSaid[zz-1];
02657                 player_->lastSaidTimes[zz] = player_->lastSaidTimes[zz-1];
02658             }
02659             
02660             player_->lastSaid[0] = say_;
02661             player_->lastSaidTimes[0] = currentTime;
02662         }
02663         
02664         return false;
02665     }
02666 
02667     bool tested_;
02668     bool shouldBlock_;
02669     ePlayerNetID * player_;
02670     tString say_;
02671 };
02672 
02673 // checks whether a player is silenced, giving him appropriate warnings if he is
02674 bool IsSilencedWithWarning( ePlayerNetID const * p )
02675 {
02676     if ( !se_enableChat && ! p->IsLoggedIn() )
02677     {
02678         // everyone except the admins is silenced
02679         sn_ConsoleOut( tOutput( "$spam_protection_silenceall" ), p->Owner() );
02680         return true;
02681     }
02682     else if ( p->IsSilenced() )
02683     {
02684         if(se_silenceAll) {
02685             // player is silenced, but all players are silenced by default
02686             sn_ConsoleOut( tOutput( "$spam_protection_silenced_default" ), p->Owner() );
02687         } else {
02688             // player is specially silenced
02689             sn_ConsoleOut( tOutput( "$spam_protection_silenced" ), p->Owner() );
02690         }
02691         return true;
02692     }
02693 
02694     return false;
02695 }
02696 
02697 // /me chat commant
02698 static void se_ChatMe( ePlayerNetID * p, std::istream & s, eChatSpamTester & spam )
02699 {
02700     if ( IsSilencedWithWarning(p) || spam.Block() )
02701     {
02702         return;
02703     }
02704     
02705     tString msg;
02706     msg.ReadLine( s );
02707 
02708     tColoredString console;
02709     console << tColoredString::ColorString(1,1,1)  << "* ";
02710     console << *p;
02711     console << tColoredString::ColorString(1,1,.5) << " " << msg;
02712     console << tColoredString::ColorString(1,1,1)  << " *";
02713     
02714     tColoredString forOldClients;
02715     forOldClients << tColoredString::ColorString(1,1,1)  << "*"
02716                   << tColoredString::ColorString(1,1,.5) << msg
02717                   << tColoredString::ColorString(1,1,1)  << "*";
02718     
02719     se_BroadcastChatLine( p, console, forOldClients );
02720     console << "\n";
02721     sn_ConsoleOut(console,0);
02722     return;
02723 }
02724 
02725 // /teamleave chat command: leaves the current team
02726 static void se_ChatTeamLeave( ePlayerNetID * p )
02727 {
02728     if ( se_assignTeamAutomatically )
02729     {
02730         sn_ConsoleOut(tOutput("$player_teamleave_disallowed"), p->Owner() );
02731         return;
02732     }
02733     if(!p->TeamChangeAllowed()) {
02734         sn_ConsoleOut(tOutput("$player_disallowed_teamchange"), p->Owner() );
02735         return;
02736     }
02737     
02738     eTeam * leftTeam = p->NextTeam();
02739     if ( leftTeam )
02740     {
02741         if ( !leftTeam )
02742             leftTeam = p->CurrentTeam();
02743         
02744         if ( leftTeam->NumPlayers() > 1 )
02745         {
02746             sn_ConsoleOut( tOutput( "$player_leave_team_wish",
02747                                     tColoredString::RemoveColors(p->GetName()),
02748                                     tColoredString::RemoveColors(leftTeam->Name()) ) );
02749         }
02750         else
02751         {
02752             sn_ConsoleOut( tOutput( "$player_leave_game_wish",
02753                                     tColoredString::RemoveColors(p->GetName()) ) );
02754         }
02755     }
02756     
02757     p->SetTeamWish(0);
02758 }
02759 
02760 // /team chat commant: talk to your team
02761 static void se_ChatTeam( ePlayerNetID * p, std::istream & s, eChatSpamTester & spam )
02762 {
02763     eTeam *currentTeam = se_GetManagedTeam( p );
02764 
02765     // silencing only affects spectators here
02766     if ( ( !currentTeam && IsSilencedWithWarning(p) ) || spam.Block() )
02767     {
02768         return;
02769     }
02770     
02771     tString msg;
02772     msg.ReadLine( s );
02773 
02774     // Log message to server and sender
02775     tColoredString messageForServerAndSender = se_BuildChatString(currentTeam, p, msg);
02776     messageForServerAndSender << "\n";
02777     
02778     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
02779     {
02780         sn_ConsoleOut(messageForServerAndSender, 0);
02781         sn_ConsoleOut(messageForServerAndSender, p->Owner());
02782 
02783         // Send message to team-mates
02784         int numTeamPlayers = currentTeam->NumPlayers();
02785         for (int teamPlayerIndex = 0; teamPlayerIndex < numTeamPlayers; teamPlayerIndex++) {
02786             if (currentTeam->Player(teamPlayerIndex)->Owner() != p->Owner()) // Do not resend the message to yourself
02787                 se_SendTeamMessage(currentTeam, p, currentTeam->Player(teamPlayerIndex), msg);
02788         }
02789 
02790         // check for other players who are authorized to hear the message
02791         for( int i = se_PlayerNetIDs.Len() - 1; i >=0; --i )
02792         {
02793             ePlayerNetID * admin = se_PlayerNetIDs(i);
02794             
02795             if (
02796                 // well, you have to be a spectator. No spying on the enemy.
02797                 se_GetManagedTeam( admin ) == 0 && admin != p &&
02798                 (
02799                     // let spectating admins of sufficient rights eavesdrop
02800                     admin->GetAccessLevel() <=  se_teamSpyAccessLevel ||
02801                     // invited players are also authorized
02802                     currentTeam->IsInvited( admin )
02803                     )
02804                 )
02805             {
02806                 se_SendTeamMessage(currentTeam, p, admin, msg);
02807             }
02808         }
02809     }
02810     else
02811     {
02812         sn_ConsoleOut(messageForServerAndSender, 0);
02813         sn_ConsoleOut(messageForServerAndSender, p->Owner());
02814 
02815         // check for other spectators
02816         for( int i = se_PlayerNetIDs.Len() - 1; i >=0; --i )
02817         {
02818             ePlayerNetID * spectator = se_PlayerNetIDs(i);
02819             
02820             if ( se_GetManagedTeam( spectator ) == 0 && spectator != p )
02821             {
02822                 se_SendTeamMessage(currentTeam, p, spectator, msg);
02823             }
02824         }
02825     }
02826 }
02827 
02828 // /msg chat commant: talk to anyone team
02829 static void se_ChatMsg( ePlayerNetID * p, std::istream & s, eChatSpamTester & spam )
02830 {
02831     // odd, the refactored original did not check for silence. Probably by design.
02832     if ( /* IsSilencedWithWarning(player) || */ spam.Block() )
02833     {
02834         return;
02835     }
02836     
02837     // Check for player
02838     ePlayerNetID * receiver =  se_FindPlayerInChatCommand( p, "/msg", s );
02839     
02840     // One match, send it.
02841     if ( receiver ) {
02842         // extract rest of message: it is the true message to send
02843         std::ws(s);
02844 
02845         // read the rest of the message
02846         tString msg_core;
02847         msg_core.ReadLine(s);
02848         
02849         // build chat string
02850         tColoredString toServer = se_BuildChatString( p, receiver, msg_core );
02851         toServer << '\n';
02852         
02853         // log locally
02854         sn_ConsoleOut(toServer,0);
02855         
02856         if ( p->CurrentTeam() == receiver->CurrentTeam() || !IsSilencedWithWarning(p) )
02857         {
02858             // log to sender's console
02859             sn_ConsoleOut(toServer, p->Owner());
02860             
02861             // send to receiver
02862             if ( p->Owner() != receiver->Owner() )
02863                 se_SendPrivateMessage( p, receiver, receiver, msg_core );
02864         }
02865 
02866         // let admins of sufficient rights eavesdrop
02867         for( int i = se_PlayerNetIDs.Len() - 1; i >=0; --i )
02868         {
02869             ePlayerNetID * admin = se_PlayerNetIDs(i);
02870             
02871             if ( admin != receiver && admin != p && admin->GetAccessLevel() <=  se_msgSpyAccessLevel )
02872             {
02873                 se_SendPrivateMessage( p, receiver, admin, msg_core );
02874             }
02875         }
02876     }
02877 }
02878 
02879 static void se_SendTo( std::string const & message, ePlayerNetID * receiver )
02880 {
02881     if ( receiver )
02882     {
02883         sn_ConsoleOut(message.c_str(), receiver->Owner());
02884     }
02885     else
02886     {
02887         con << message;
02888     }
02889 }
02890 
02891 // prints an indented team member
02892 static void se_SendTeamMember( ePlayerNetID const * player, int indent, std::ostream & tos )
02893 {
02894     // send team name
02895     for( int i = indent-1; i >= 0; --i )
02896     {
02897         tos << ' ';
02898     }
02899 
02900     tos << *player << "\n";
02901 }
02902 
02903 // list teams with their formation
02904 static void se_ListTeam( ePlayerNetID * receiver, eTeam * team )
02905 {
02906     std::ostringstream tos;
02907     
02908     // send team name
02909     tColoredString teamName;
02910     teamName << tColoredStringProxy( team->R()/15.0f, team->G()/15.0f, team->B()/15.0f ) << team->Name();
02911     tos << teamName << "0xRESETT";
02912     if ( team->IsLocked() )
02913     {
02914         tos << " " << tOutput( "$invite_team_locked_list" );
02915     }
02916     tos << ":\n";
02917     
02918     // send team members
02919     int teamMembers = team->NumPlayers();
02920     
02921     int indent = 0;
02922     // print left wing, the odd team members
02923     for( int i = (teamMembers/2)*2-1; i>=0; i -= 2 )
02924     {
02925         se_SendTeamMember( team->Player(i), indent, tos );
02926         indent += 2;
02927     }
02928     // print right wing, the even team members
02929     for( int i = 0; i < teamMembers; i += 2 )
02930     {
02931         se_SendTeamMember( team->Player(i), indent, tos );
02932         indent -= 2;
02933     }
02934     
02935     tos << "\n";
02936     
02937     se_SendTo( tos.str(), receiver );
02938 }
02939 
02940 static void se_ListTeams( ePlayerNetID * receiver )
02941 {
02942     int numTeams = 0;
02943     
02944     for ( int i = eTeam::teams.Len() - 1; i >= 0; --i )
02945     {
02946         eTeam * team = eTeam::teams[i];
02947         if ( team->NumPlayers() > 1 || team->IsLocked() )
02948         {
02949             numTeams++;
02950             se_ListTeam( receiver, team );
02951         }
02952     }
02953 
02954     if ( numTeams == 0 )
02955     {
02956         se_SendTo( std::string( tString( tOutput("$no_real_teams") ) ), receiver );
02957     }
02958 }
02959 
02960 static void teams_conf(std::istream &s)
02961 {
02962     se_ListTeams( 0 );
02963 }
02964 
02965 static tConfItemFunc teams("TEAMS",&teams_conf);
02966 
02967 // /teams gives a teams list
02968 static void se_ChatTeams( ePlayerNetID * p )
02969 {
02970     se_ListTeams( p );
02971 }
02972 
02973 static void se_ListPlayers( ePlayerNetID * receiver )
02974 {
02975     for ( int i2 = se_PlayerNetIDs.Len()-1; i2>=0; --i2 )
02976     {
02977         ePlayerNetID* p2 = se_PlayerNetIDs(i2);
02978         std::ostringstream tos;
02979         tos << p2->Owner();
02980         tos << ": ";
02981         if ( p2->GetAccessLevel() < tAccessLevel_Default && !se_Hide( p2, receiver ) )
02982         {
02983             // player username comes from authentication name and may be much different from
02984             // the screen name
02985             tos << p2->GetFilteredAuthenticatedName() << " ( " << p2->GetName() << ", " 
02986                 << tCurrentAccessLevel::GetName( p2->GetAccessLevel() )
02987                 << " )";
02988         }
02989         else
02990         {
02991             tos << p2->GetName();
02992         }
02993         if ( tCurrentAccessLevel::GetAccessLevel() <= se_ipAccessLevel )
02994         {
02995             tString IP = p2->GetMachine().GetIP();
02996             if ( IP.Len() > 1 )
02997             {
02998                 tos << ", IP = " << IP;
02999             }
03000         }
03001         tos << "\n";
03002 
03003         se_SendTo( tos.str(), receiver );
03004     }
03005 }
03006 
03007 static void players_conf(std::istream &s)
03008 {
03009     se_ListPlayers( 0 );
03010 }
03011 
03012 static tConfItemFunc players("PLAYERS",&players_conf);
03013 
03014 // /players gives a player list
03015 static void se_ChatPlayers( ePlayerNetID * p )
03016 {
03017     se_ListPlayers( p );
03018 }
03019 
03020 // team shuffling: reorders team formation
03021 static void se_ChatShuffle( ePlayerNetID * p, std::istream & s )
03022 {
03023     tString msg;
03024     msg.ReadLine( s );
03025 
03026     // team position shuffling. Allow players to change their team setup.
03027     // syntax:
03028     // /teamshuffle: shuffles you all the way to the outside.
03029     // /teamshuffle <pos>: shuffles you to position pos
03030     // /teamshuffle +/-<dist>: shuffles you dist to the outside/inside
03031     // con << msgRest << "\n";
03032     int IDNow = p->TeamListID();
03033     if (!p->CurrentTeam())
03034     {
03035         sn_ConsoleOut(tOutput("$player_not_on_team"), p->Owner());
03036         return;
03037     }
03038     int len = p->CurrentTeam()->NumPlayers();
03039     int IDWish = len-1; // default to shuffle to the outside
03040     
03041                         // but read the target position as additional parameter
03042     if (msg.Len() > 1)
03043     {
03044         IDWish = IDNow;
03045         if ( msg[0] == '+' )
03046             IDWish += msg.ToInt(1);
03047         else if ( msg[0] == '-' )
03048             IDWish -= msg.ToInt(1);
03049         else
03050             IDWish = msg.ToInt()-1;
03051     }
03052 
03053     if (IDWish < 0)
03054         IDWish = 0;
03055     if (IDWish >= len)
03056         IDWish = len-1;
03057 
03058         if(IDWish < IDNow)
03059         {
03060 #ifndef KRAWALL_SERVER
03061                 if ( !se_allowShuffleUp )
03062                 {
03063                         sn_ConsoleOut(tOutput("$player_noshuffleup"), p->Owner());
03064                         return;
03065                 }
03066 #else
03067                 if ( !p->GetAccessLevel() > se_shuffleUpAccessLevel )
03068                 {
03069                         sn_ConsoleOut(tOutput("$access_level_shuffle_up_denied"), p->Owner());
03070                         return;
03071                 }
03072 #endif
03073         }
03074 
03075     if( IDNow == IDWish )
03076     {
03077         sn_ConsoleOut(tOutput("$player_noshuffle"), p->Owner());
03078         return;
03079     }
03080 
03081     p->CurrentTeam()->Shuffle( IDNow, IDWish );
03082     se_ListTeam( p, p->CurrentTeam() );
03083 }
03084 
03085 void handle_chat( nMessage &m )
03086 {
03087     nTimeRolling currentTime = tSysTimeFloat();
03088     unsigned short id;
03089     m.Read(id);
03090     tColoredString say;
03091     m >> say;
03092 
03093     tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
03094 
03095     // register player activity
03096     if ( p )
03097         p->lastActivity_ = currentTime;
03098 
03099     if(sn_GetNetState()==nSERVER){
03100         if (p)
03101         {
03102             // for the duration of this function, set the access level to the level of the user.
03103             tCurrentAccessLevel levelOverride( p->GetAccessLevel() );
03104 
03105             // check if the player is owned by the sender to avoid cheating
03106             if( p->Owner() != m.SenderID() )
03107             {
03108                 Cheater( m.SenderID() );
03109                 nReadError();
03110                 return;
03111             }
03112 
03113             eChatSpamTester spam( p, say );
03114             
03115             if (say.StartsWith("/")) {
03116                 std::string sayStr(say);
03117                 std::istringstream s(sayStr);
03118 
03119                 tString command;
03120                 s >> command;
03121 
03122                 // filter to lowercase
03123                 tToLower( command );
03124 
03125                 tConfItemBase::EatWhitespace(s);
03126 
03127                 // now, s is ready for reading the rest of the message.
03128 #ifdef DEDICATED
03129                 if (se_InterceptCommands.StrPos(command) != -1)
03130                 {
03131                     handle_command_intercept(p, say);
03132                     return;
03133                 }
03134                 else
03135 #endif
03136                     if (command == "/me") {
03137                         se_ChatMe( p, s, spam );
03138                         return;
03139                     }
03140                     else if (command == "/teamname") {
03141                         tString msg;
03142                         msg.ReadLine( s );
03143                         p->SetTeamname(msg);
03144                         return;
03145                     }
03146                     else if (command == "/teamleave") {
03147                         se_ChatTeamLeave( p );
03148                         return;
03149                     }
03150                     else if (command == "/teamleave" || command=="/spectate") {
03151                         p->SetTeamWish(NULL);
03152                         return;
03153                     }
03154                     else if (command == "/teamshuffle" || command == "/shuffle") {
03155                         se_ChatShuffle( p, s );
03156                         return;
03157                     }
03158                     else if (command == "/team")
03159                     {
03160                         se_ChatTeam( p, s, spam );
03161                         return;
03162                     }
03163                     else if (command == "/msg" ) {
03164                         se_ChatMsg( p, s, spam );
03165                         return;
03166                     }
03167                     else if (command == "/help")
03168                     {
03169                         sn_ConsoleOut(se_helpMessage + "\n", p->Owner());
03170                         se_DisplayChatLocally(p, say);
03171                         return;
03172                     }
03173                     else if (command == "/players") {
03174                         se_ChatPlayers( p );
03175                         return;
03176                     }
03177                     else if (command == "/vote" || command == "/callvote") {
03178                         eVoter::HandleChat( p, s );
03179                         return;
03180                     }
03181                     else if (command == "/teams") {
03182                         se_ChatTeams( p );
03183                         return;
03184                     }
03185 #ifdef DEDICATED
03186                     else {
03187                         handle_chat_admin_commands( p, command, say, s );
03188                         return;
03189                     }
03190 #endif
03191             }
03192 
03193             // check for spam
03194             if ( spam.Block() )
03195             {
03196                 return;
03197             }
03198 
03199             // well, that leaves only regular, boring chat.
03200             if ( say.Len() <= se_SpamMaxLen+2 && !IsSilencedWithWarning(p) )
03201             {
03202                 se_BroadcastChat( p, say );
03203                 se_DisplayChatLocally( p, say);
03204             }
03205         }
03206     }
03207     else
03208     {
03209         se_DisplayChatLocally( p, say );
03210     }
03211 }
03212 
03213 // check if a string is a legal player name
03214 // a name is only legal if it contains at least one non-witespace character.
03215 static bool IsLegalPlayerName( tString const & name )
03216 {
03217     // strip colors
03218     tString stripped( tColoredString::RemoveColors( name ) );
03219 
03220     // and count non-whitespace characters
03221     for ( int i = stripped.Len()-2; i>=0; --i )
03222     {
03223         if ( !isblank( stripped(i) ) )
03224             return true;
03225     }
03226 
03227     return false;
03228 }
03229 
03230 void ePlayerNetID::Chat(const tString &s_orig)
03231 {
03232     tColoredString s( s_orig );
03233     s.NetFilter();
03234 
03235     switch (sn_GetNetState())
03236     {
03237     case nCLIENT:
03238         {
03239             if(s_orig.StartsWith("/console") ) {
03240                 tString params("");
03241                 if (s_orig.StrPos(" ") == -1)
03242                     return;
03243                 else
03244                     params = s_orig.SubStr(s_orig.StrPos(" ") + 1);
03245 
03246                 if ( tRecorder::IsPlayingBack() )
03247                 {
03248                     tConfItemBase::LoadPlayback();
03249                 }
03250                 else
03251                 {
03252                     std::stringstream s(static_cast< char const * >( params ) );
03253                     tConfItemBase::LoadAll(s);
03254                 }
03255                 std::cout << "console selected\n";
03256             } else
03257                 se_NewChatMessage( this, s )->BroadCast();
03258             break;
03259         }
03260     case nSERVER:
03261         {
03262             se_BroadcastChat( this, s );
03263         }
03264     default:
03265         {
03266             if(s_orig.StartsWith("/console") ) {
03267                 tString params("");
03268                 if (s_orig.StrPos(" ") == -1)
03269                     return;
03270                 else
03271                     params = s_orig.SubStr(s_orig.StrPos(" ") + 1);
03272 
03273                 if ( tRecorder::IsPlayingBack() )
03274                 {
03275                     tConfItemBase::LoadPlayback();
03276                 }
03277                 else
03278                 {
03279                     std::stringstream s(static_cast< char const * >( params ) );
03280                     tConfItemBase::LoadAll(s);
03281                 }
03282                 std::cout << "console selected\n";
03283             } else
03284                 se_DisplayChatLocally( this, s );
03285 
03286             break;
03287         }
03288     }
03289 }
03290 
03291 //return the playernetid for a given name
03292 /*
03293 static ePlayerNetID* identifyPlayer(tString inname)
03294 {
03295     for(int i=0; i<se_PlayerNetIDs.Len(); i++)
03296     {
03297         ePlayerNetID *p = se_PlayerNetIDs[i];
03298 
03299         if( inname == p->GetUserName() )
03300             return p;
03301     }
03302     return NULL;
03303 }
03304 */
03305 
03306 // identify a local player
03307 static ePlayerNetID* se_GetLocalPlayer()
03308 {
03309     for(int i=0; i<se_PlayerNetIDs.Len(); i++)
03310     {
03311         ePlayerNetID *p = se_PlayerNetIDs[i];
03312 
03313         if( p->Owner() == sn_myNetID )
03314             return p;
03315     }
03316     return NULL;
03317 }
03318 
03319 static void ConsoleSay_conf(std::istream &s)
03320 {
03321     // read the message
03322     tString message;
03323     message.ReadLine( s, true );
03324 
03325     switch (sn_GetNetState())
03326     {
03327     case nCLIENT:
03328         {
03329             ePlayerNetID *me = se_GetLocalPlayer();
03330 
03331             if (me)
03332                 me->Chat( message );
03333 
03334             break;
03335         }
03336     case nSERVER:
03337         {
03338             tColoredString send;
03339             send << tColoredString::ColorString( 1,0,0 );
03340             send << "Admin";
03341             send << tColoredString::ColorString( 1,1,.5 );
03342             send << ": " << message << "\n";
03343 
03344             // display it
03345             sn_ConsoleOut( send );
03346 
03347             break;
03348         }
03349     default:
03350         {
03351             ePlayerNetID *me = se_GetLocalPlayer();
03352 
03353             if ( me )
03354                 se_DisplayChatLocally( me, message );
03355 
03356             break;
03357         }
03358     }
03359 }
03360 
03361 static tConfItemFunc ConsoleSay_c("SAY",&ConsoleSay_conf);
03362 
03363 struct eChatInsertionCommand
03364 {
03365     tString insertion_;
03366 
03367     eChatInsertionCommand( tString const & insertion )
03368             : insertion_( insertion )
03369     {}
03370 };
03371 
03372 
03373 static uMenuItemStringWithHistory::history_t &se_chatHistory() {
03374     static uMenuItemStringWithHistory::history_t instance("chat_history.txt");
03375     return instance;
03376 }
03377 static int se_chatHistoryMaxSize=10; // maximal size of chat history
03378 static tSettingItem< int > se_chatHistoryMaxSizeConf("HISTORY_SIZE_CHAT",se_chatHistoryMaxSize);
03379 
03381 class eAutoCompleterChat : public uAutoCompleter{
03382 public:
03385     eAutoCompleterChat(std::deque<tString> &words):uAutoCompleter(words) {};
03386     int DoFullCompletion(tString &string, int pos, int len, tString &match) {
03387         tString actualString;
03388         if(pos - len == 0 || pos - len == 6 && string.StartsWith("/team ")) {
03389             actualString = match + ": ";
03390         } else if(pos - len == 5 && string.StartsWith("/msg ") || string.StartsWith("/admin ")) {
03391             actualString = Simplify(match) + " ";
03392         } else {
03393             actualString = match + " ";
03394         }
03395         return DoCompletion(string, pos, len, actualString);
03396     }
03397     tString Simplify(tString const &str) {
03398         return ePlayerNetID::FilterName(str);
03399     }
03400 };
03401 
03403 class eMenuItemChat : protected uMenuItemStringWithHistory{
03404     ePlayer *me; 
03405 public:
03411     eMenuItemChat(uMenu *M,tString &c,ePlayer *Me,uAutoCompleter *completer):
03412     uMenuItemStringWithHistory(M,"$chat_title_text","",c, se_SpamMaxLen, se_chatHistory(), se_chatHistoryMaxSize, completer),me(Me) {}
03413 
03414     virtual ~eMenuItemChat(){ }
03415 
03416     virtual bool Event(SDL_Event &e){
03417 #ifndef DEDICATED
03418         if (e.type==SDL_KEYDOWN &&
03419                 (e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
03420 
03421             for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
03422                 if (se_PlayerNetIDs(i)->pID==me->ID())
03423                     se_PlayerNetIDs(i)->Chat(*content);
03424 
03425             MyMenu()->Exit();
03426             return true;
03427         }
03428         else if (e.type==SDL_KEYDOWN &&
03429                  uActionGlobal::IsBreakingGlobalBind(e.key.keysym.sym))
03430             return su_HandleEvent(e, true);
03431         else
03432         {
03433             if ( uMenuItemStringWithHistory::Event(e) )
03434             {
03435                 return true;
03436             }
03437             // exclude modifier keys from possible control triggers
03438             else if ( e.key.keysym.sym < SDLK_NUMLOCK || e.key.keysym.sym > SDLK_COMPOSE )
03439             {
03440                 // maybe it's an instant chat button?
03441                 try
03442                 {
03443                     return su_HandleEvent(e, false);
03444                 }
03445                 catch ( eChatInsertionCommand & insertion )
03446                 {
03447                     if ( content->Len() + insertion.insertion_.Len() <= maxLength_ )
03448                     {
03449                         *content = content->SubStr( 0, cursorPos ) + insertion.insertion_ + content->SubStr( cursorPos );
03450                         cursorPos += insertion.insertion_.Len()-1;
03451                     }
03452 
03453                     return true;
03454                 }
03455             }
03456         }
03457 #endif // DEDICATED
03458 
03459         return false;
03460     }
03461 };
03462 
03463 
03464 void se_ChatState(ePlayerNetID::ChatFlags flag, bool cs){
03465     for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
03466     {
03467         ePlayerNetID *p = se_PlayerNetIDs[i];
03468         if (p->Owner()==sn_myNetID && p->pID >= 0){
03469             p->SetChatting( flag, cs );
03470         }
03471     }
03472 }
03473 
03474 static ePlayer * se_chatterPlanned=NULL;
03475 static ePlayer * se_chatter =NULL;
03476 static tString se_say;
03477 static void do_chat(){
03478     if (se_chatterPlanned){
03479         su_ClearKeys();
03480 
03481         se_chatter=se_chatterPlanned;
03482         se_chatterPlanned=NULL;
03483         se_ChatState( ePlayerNetID::ChatFlags_Chat, true);
03484 
03485         sr_con.SetHeight(15,false);
03486         se_SetShowScoresAuto(false);
03487 
03488         uMenu chat_menu("",false);
03489 
03490         std::deque<tString> players;
03491         unsigned short int max=se_PlayerNetIDs.Len();
03492         for(unsigned short int i=0;i<max;i++){
03493             ePlayerNetID *p=se_PlayerNetIDs(i);
03494             players.push_back(p->GetName());
03495         }
03496         eAutoCompleterChat completer(players);
03497 
03498 
03499         eMenuItemChat s(&chat_menu,se_say,se_chatter,&completer);
03500         chat_menu.SetCenter(-.75);
03501         chat_menu.SetBot(-2);
03502         chat_menu.SetTop(-.7);
03503         chat_menu.Enter();
03504 
03505         se_ChatState( ePlayerNetID::ChatFlags_Chat, false );
03506 
03507         sr_con.SetHeight(7,false);
03508         se_SetShowScoresAuto(true);
03509     }
03510     se_chatter=NULL;
03511     se_chatterPlanned=NULL;
03512 }
03513 
03514 static void chat( ePlayer * chatter, tString const & say )
03515 {
03516     se_chatterPlanned = chatter;
03517     se_say = say;
03518     st_ToDo(&do_chat);
03519 }
03520 
03521 static void chat( ePlayer * chatter )
03522 {
03523     chat( chatter, tString() );
03524 }
03525 
03526 static bool se_allowControlDuringChat = false;
03527 static nSettingItem<bool> se_allowControlDuringChatConf("ALLOW_CONTROL_DURING_CHAT",se_allowControlDuringChat);
03528 
03529 uActionPlayer se_toggleSpectator("TOGGLE_SPECTATOR", 0);
03530 
03531 bool ePlayer::Act(uAction *act,REAL x){
03532     eGameObject *object=NULL;
03533 
03534     if ( act == &se_toggleSpectator && x > 0 )
03535     {
03536         spectate = !spectate;
03537         con << tOutput(spectate ? "$player_toggle_spectator_on" : "$player_toggle_spectator_off", name );
03538         if ( !spectate )
03539         {
03540             ePlayerNetID::Update();
03541         }
03542         return true;
03543     }
03544     else if (!se_chatter && s_chat==*reinterpret_cast<uActionPlayer *>(act)){
03545         if(x>0) {
03546             chat( this );
03547         }
03548         return true;
03549     }
03550     else{
03551         if ( x > 0 )
03552         {
03553             int i;
03554             uActionPlayer* pact = reinterpret_cast<uActionPlayer *>(act);
03555             for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
03556                 uActionPlayer* pcompare = se_instantChatAction[i];
03557                 if (pact == pcompare && x>=0){
03558                     for(int j=se_PlayerNetIDs.Len()-1;j>=0;j--)
03559                         if (se_PlayerNetIDs(j)->pID==ID())
03560                         {
03561                             tString say = instantChatString[i];
03562                             bool sendImmediately = true;
03563                             if ( say.Len() > 2 && say[say.Len()-2] == '\\' )
03564                             {
03565                                 // cut away trailing slash and note for later
03566                                 // that the message should not be sent immediately.
03567                                 say = say.SubStr( 0, say.Len()-2 );
03568                                 sendImmediately = false;
03569                             }
03570 
03571                             if ( se_chatter == this )
03572                             {
03573                                 // a chat is already active, insert the chat string
03574                                 throw eChatInsertionCommand( say );
03575                             }
03576                             else
03577                             {
03578                                 if ( sendImmediately )
03579                                 {
03580                                     // fire out chat string immediately
03581                                     se_PlayerNetIDs(j)->Chat(say);
03582                                 }
03583                                 else
03584                                 {
03585                                     // chat with instant chat string as template
03586                                     chat( this, say );
03587                                 }
03588                             }
03589                             return true;
03590                         }
03591                 }
03592             }
03593         }
03594 
03595         // no other command should get through during chat
03596         if ( se_chatter && !se_allowControlDuringChat )
03597             return false;
03598 
03599         int i;
03600         for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
03601             if (se_PlayerNetIDs[i]->pID==id && se_PlayerNetIDs[i]->object)
03602                 object=se_PlayerNetIDs[i]->object;
03603 
03604         bool objectAct = false; // set to true if an action of the object was triggered
03605         bool ret = ((cam    && cam->Act(reinterpret_cast<uActionCamera *>(act),x)) ||
03606                     ( objectAct = (object && se_GameTime()>=0 && object->Act(reinterpret_cast<uActionPlayer *>(act),x))));
03607 
03608         if (bool(netPlayer) && (objectAct || !se_chatter))
03609             netPlayer->Activity();
03610 
03611         return ret;
03612     }
03613 
03614 }
03615 
03616 rViewport * ePlayer::PlayerViewport(int p){
03617     if (!PlayerConfig(p)) return NULL;
03618 
03619     for (int i=rViewportConfiguration::CurrentViewportConfiguration()->num_viewports-1;i>=0;i--)
03620         if (sr_viewportBelongsToPlayer[i] == p)
03621             return rViewportConfiguration::CurrentViewport(i);
03622 
03623     return NULL;
03624 }
03625 
03626 bool ePlayer::PlayerIsInGame(int p){
03627     return PlayerViewport(p) && PlayerConfig(p);
03628 }
03629 
03630 static tConfItemBase *vpbtp[MAX_VIEWPORTS];
03631 
03632 void ePlayer::Init(){
03633     se_Players = tNEW( ePlayer[MAX_PLAYERS] );
03634 
03635     int i;
03636     for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
03637         tString id;
03638         id << "INSTANT_CHAT_";
03639         id << i+1;
03640         tOutput desc;
03641         desc.SetTemplateParameter(1, i+1);
03642         desc << "$input_instant_chat_text";
03643 
03644         tOutput help;
03645         help.SetTemplateParameter(1, i+1);
03646         help << "$input_instant_chat_help";
03647         ePlayer::se_instantChatAction[i]=tNEW(uActionPlayer) (id, desc, help);
03648         //,desc,       "Issues a special instant chat macro.");
03649     }
03650 
03651 
03652     for(i=MAX_VIEWPORTS-1;i>=0;i--){
03653         tString id;
03654         id << "VIEWPORT_TO_PLAYER_";
03655         id << i+1;
03656         vpbtp[i] = tNEW(tConfItem<int>(id,"$viewport_belongs_help",
03657                                        s_newViewportBelongsToPlayer[i]));
03658         s_newViewportBelongsToPlayer[i]=i;
03659     }
03660 }
03661 
03662 void ePlayer::Exit(){
03663     int i;
03664     for(i=MAX_INSTANT_CHAT-1;i>=0;i--)
03665         tDESTROY(ePlayer::se_instantChatAction[i]);
03666 
03667     for(i=MAX_VIEWPORTS-1;i>=0;i--)
03668         tDESTROY(vpbtp[i]);
03669 
03670     delete[] se_Players;
03671     se_Players = NULL;
03672 }
03673 
03674 uActionPlayer ePlayer::s_chat("CHAT");
03675 
03676 int pingCharity = 100;
03677 static const int maxPingCharity = 300;
03678 
03679 static void sg_ClampPingCharity( int & pingCharity )
03680 {
03681     if (pingCharity < 0 )
03682         pingCharity = 0;
03683     if (pingCharity > maxPingCharity )
03684         pingCharity = maxPingCharity;
03685 }
03686 
03687 static void sg_ClampPingCharity( unsigned short & pingCharity )
03688 {
03689     if (pingCharity > maxPingCharity )
03690         pingCharity = maxPingCharity;
03691 }
03692 
03693 static void sg_ClampPingCharity()
03694 {
03695     sg_ClampPingCharity( ::pingCharity );
03696 }
03697 
03698 static int IMPOSSIBLY_LOW_SCORE=(-1 << 31);
03699 
03700 static nSpamProtectionSettings se_chatSpamSettings( 1.0f, "SPAM_PROTECTION_CHAT", tOutput("$spam_protection") );
03701 
03702 ePlayerNetID::ePlayerNetID(int p):nNetObject(),listID(-1), teamListID(-1), allowTeamChange_(false), registeredMachine_(0), pID(p),chatSpam_( se_chatSpamSettings )
03703 {
03704     // default access level
03705     lastAccessLevel = tAccessLevel_Default;
03706 
03707     favoriteNumberOfPlayersPerTeam = 1;
03708 
03709     nameTeamAfterMe = false;
03710 
03711     r = g = b = 15;
03712 
03713     greeted                             = true;
03714     chatting_                   = false;
03715     spectating_         = false;
03716     stealth_            = false;
03717     chatFlags_                  = 0;
03718     disconnected                = false;
03719     suspended_          = 0;
03720 
03721     loginWanted = false;
03722     
03723 
03724     if (p>=0){
03725         ePlayer *P = ePlayer::PlayerConfig(p);
03726         if (P){
03727             SetName( P->Name() );
03728             teamname = P->Teamname();
03729             r=   P->rgb[0];
03730             g=   P->rgb[1];
03731             b=   P->rgb[2];
03732 
03733             sg_ClampPingCharity();
03734             pingCharity=::pingCharity;
03735 
03736             loginWanted = P->autoLogin;
03737         }
03738     }
03739     /*
03740     else
03741     {
03742         SetName( "AI" );
03743     teamname = "";
03744     }
03745     */
03746     lastSaid.SetLen(12);
03747     lastSaidTimes.SetLen(12);
03748 
03749     se_PlayerNetIDs.Add(this,listID);
03750     object=NULL;
03751 
03752     /*
03753       if(sn_GetNetState()!=nSERVER)
03754       ping=sn_Connections[0].ping;
03755       else
03756     */
03757     ping=0; // hehe! server has no ping.
03758 
03759     lastSync=tSysTimeFloat();
03760 
03761     RequestSync();
03762     score=0;
03763     lastScore_=IMPOSSIBLY_LOW_SCORE;
03764     // rubberstatus=0;
03765 
03766     MyInitAfterCreation();
03767 
03768     if(sn_GetNetState()==nSERVER)
03769         RequestSync();
03770 }
03771 
03772 
03773 
03774 
03775 ePlayerNetID::ePlayerNetID(nMessage &m):nNetObject(m),listID(-1), teamListID(-1)
03776         , allowTeamChange_(false), registeredMachine_(0), chatSpam_( se_chatSpamSettings )
03777 {
03778     // default access level
03779     lastAccessLevel = tAccessLevel_Default;
03780 
03781     greeted     =false;
03782     chatting_   =false;
03783     spectating_ =false;
03784     stealth_    =false;
03785     disconnected=false;
03786     suspended_  = 0;
03787     chatFlags_  =0;
03788 
03789     r = g = b = 15;
03790 
03791     nameTeamAfterMe = false;
03792     teamname = "";
03793 
03794     lastSaid.SetLen(12);
03795     lastSaidTimes.SetLen(12);
03796 
03797     pID=-1;
03798     se_PlayerNetIDs.Add(this,listID);
03799     object=NULL;
03800     ping=sn_Connections[m.SenderID()].ping;
03801     lastSync=tSysTimeFloat();
03802  
03803     loginWanted = false;
03804 
03805     score=0;
03806     lastScore_=IMPOSSIBLY_LOW_SCORE;
03807     // rubberstatus=0;
03808 }
03809 
03810 void ePlayerNetID::Activity()
03811 {
03812     // the player was active; therefore, he cannot possibly be chatting_ or disconnected.
03813 
03814     // but do nothing if we are in client mode and the player is not local to this computer
03815     if (sn_GetNetState() != nSERVER && Owner() != ::sn_myNetID)
03816         return;
03817 
03818     if (chatting_ || disconnected)
03819     {
03820 #ifdef DEBUG
03821         con << *this << " showed activity and lost chat status.\n";
03822 #endif
03823         RequestSync();
03824     }
03825 
03826     chatting_ = disconnected = false;
03827 
03828     // store time
03829     this->lastActivity_ = tSysTimeFloat();
03830 }
03831 
03832 static int se_maxPlayersPerIP = 4;
03833 static tSettingItem<int> se_maxPlayersPerIPConf( "MAX_PLAYERS_SAME_IP", se_maxPlayersPerIP );
03834 
03835 // array of players in legacy spectator mode: they have been
03836 // deleted by their clients, but no new player has popped up for them yet
03837 static tJUST_CONTROLLED_PTR< ePlayerNetID > se_legacySpectators[MAXCLIENTS+2];
03838 
03839 static void se_ClearLegacySpectator( int owner )
03840 {
03841     ePlayerNetID * player = se_legacySpectators[ owner ];
03842 
03843     // fully remove player
03844     if ( player )
03845     {
03846         player->RemoveFromGame();
03847     }
03848 
03849     se_legacySpectators[ owner ] = NULL;
03850 }
03851 
03852 // callback clearing the legacy spectator when a client enters or leaves
03853 static void se_LegacySpectatorClearer()
03854 {
03855     se_ClearLegacySpectator( nCallbackLoginLogout::User() );
03856 }
03857 static nCallbackLoginLogout se_legacySpectatorClearer( se_LegacySpectatorClearer );
03858 
03859 
03860 
03861 void ePlayerNetID::MyInitAfterCreation()
03862 {
03863     this->CreateVoter();
03864 
03865     this->silenced_ = se_silenceAll;
03866 
03867     // register with machine and kick user if too many players are present
03868     if ( Owner() != 0 && sn_GetNetState() == nSERVER )
03869     {
03870         this->RegisterWithMachine();
03871         if ( se_maxPlayersPerIP < nMachine::GetMachine(Owner()).GetPlayerCount() )
03872         {
03873             // kill them
03874             sn_DisconnectUser( Owner(), "$network_kill_too_many_players" );
03875 
03876             // technically has the same effect as the above function, but we also want
03877             // to abort registering this player object and this exception will do that as well.
03878             throw nKillHim();
03879         }
03880 
03881         // clear old legacy spectator that may be lurking around
03882         se_ClearLegacySpectator( Owner() );
03883 
03884         // get suspension count
03885         if ( GetVoter() )
03886         {
03887             suspended_ = GetVoter()->suspended_;
03888         }
03889     }
03890 
03891     this->wait_ = 0;
03892 
03893     this->lastActivity_ = tSysTimeFloat();
03894 }
03895 
03896 void ePlayerNetID::InitAfterCreation()
03897 {
03898     MyInitAfterCreation();
03899 }
03900 
03901 bool ePlayerNetID::ClearToTransmit(int user) const{
03902     return ( ( !nextTeam || nextTeam->HasBeenTransmitted( user ) ) &&
03903              ( !currentTeam || currentTeam->HasBeenTransmitted( user ) ) ) ;
03904 }
03905 
03906 ePlayerNetID::~ePlayerNetID()
03907 {
03908     //  se_PlayerNetIDs.Remove(this,listID);
03909     if ( sn_GetNetState() == nSERVER && disconnected )
03910     {
03911         tOutput mess;
03912         tColoredString name;
03913         name << *this << tColoredString::ColorString(1,.5,.5);
03914         mess.SetTemplateParameter(1, name);
03915         mess.SetTemplateParameter(2, score);
03916         mess << "$player_left_game";
03917         sn_ConsoleOut(mess);
03918     }
03919 
03920     UnregisterWithMachine();
03921 
03922     RemoveFromGame();
03923 
03924     ClearObject();
03925     //con << "Player info sent.\n";
03926 
03927     for(int i=MAX_PLAYERS-1;i>=0;i--){
03928         ePlayer *p = ePlayer::PlayerConfig(i);
03929 
03930         if (p && static_cast<ePlayerNetID *>(p->netPlayer)==this)
03931             p->netPlayer=NULL;
03932     }
03933 
03934     if ( currentTeam )
03935     {
03936         currentTeam->RemovePlayer( this );
03937     }
03938 
03939 #ifdef DEBUG
03940     con << *this << " destroyed.\n";
03941 #endif
03942 }
03943 
03944 static void player_removed_from_game_handler(nMessage &m)
03945 {
03946     // and the ID of the player that was removed
03947     unsigned short id;
03948     m.Read(id);
03949     ePlayerNetID* p = dynamic_cast< ePlayerNetID* >( nNetObject::ObjectDangerous( id ) );
03950     if ( p && sn_GetNetState() != nSERVER )
03951     {
03952         p->RemoveFromGame();
03953     }
03954 }
03955 
03956 static nDescriptor player_removed_from_game(202,&player_removed_from_game_handler,"player_removed_from_game");
03957 
03958 void ePlayerNetID::RemoveFromGame()
03959 {
03960     // unregister with the machine
03961     this->UnregisterWithMachine();
03962 
03963     // release voter
03964     if ( this->voter_ )
03965         this->voter_->RemoveFromGame();
03966     this->voter_ = 0;
03967 
03968     // log scores
03969     LogScoreDifference();
03970 
03971     if ( sn_GetNetState() != nCLIENT )
03972     {
03973         nameFromClient_ = nameFromServer_;
03974 
03975         nMessage *m=new nMessage(player_removed_from_game);
03976         m->Write(this->ID());
03977         m->BroadCast();
03978 
03979         if ( listID >= 0 ){
03980             if ( ( IsSpectating() || !se_assignTeamAutomatically ) && CurrentTeam() == NULL )
03981             {
03982                 // get colored player name
03983                 tColoredString playerName;
03984                 playerName << *this << tColoredString::ColorString(1,.5,.5);
03985 
03986                 // announce a generic leave
03987                 sn_ConsoleOut( tOutput( "$player_left_spectator", playerName ) );
03988             }
03989 
03990             if ( IsHuman() && sn_GetNetState() == nSERVER && NULL != sn_Connections[Owner()].socket )
03991             {
03992                 tString ladder;
03993                 ladder << "PLAYER_LEFT " << userName_ << " " << nMachine::GetMachine(Owner()).GetIP() << "\n";
03994                 se_SaveToLadderLog(ladder);
03995                 tString notificationMessage(userName_);
03996                 notificationMessage << " left the grid";
03997                 se_sendEventNotification(tString("Player left"), notificationMessage);
03998             }
03999         }
04000     }
04001 
04002     se_PlayerNetIDs.Remove(this, listID);
04003     SetTeamWish( NULL );
04004     SetTeam( NULL );
04005     UpdateTeam();
04006     ControlObject( NULL );
04007     //  currentTeam = NULL;
04008 }
04009 
04010 bool ePlayerNetID::ActionOnQuit()
04011 {
04012     tControlledPTR< ePlayerNetID > holder( this );
04013 
04014     // clear legacy spectator
04015     se_ClearLegacySpectator( Owner() );
04016 
04017     this->RemoveFromGame();
04018     return true;
04019 }
04020 
04021 void ePlayerNetID::ActionOnDelete()
04022 {
04023     tControlledPTR< ePlayerNetID > holder( this );
04024 
04025     if ( sn_GetNetState() == nSERVER )
04026     {
04027         // get the number of players registered from this client
04028         // int playerCount = nMachine::GetMachine(Owner()).GetPlayerCount();
04029         int playerCount = 0;
04030         for ( int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
04031         {
04032             if ( se_PlayerNetIDs[i]->Owner() == Owner() )
04033                 playerCount++;
04034         }
04035 
04036         // if this is the last player, store it
04037         if ( playerCount == 1 )
04038         {
04039             // log scores
04040             LogScoreDifference();
04041 
04042             // clear legacy spectator
04043             se_ClearLegacySpectator( Owner() );
04044 
04045             // store new legacy spectator
04046             se_legacySpectators[ Owner() ] = this;
04047             spectating_ = true;
04048 
04049             // quit team already
04050             SetTeamWish( NULL );
04051             SetTeam( NULL );
04052             UpdateTeam();
04053 
04054             // and kill controlled object
04055             ControlObject( NULL );
04056 
04057             // inform other clients that the player left
04058             TakeOwnership();
04059             RequestSync();
04060 
04061             return;
04062         }
04063     }
04064 
04065     // standard behavior: just get us out of here
04066     this->RemoveFromGame();
04067 }
04068 
04069 void ePlayerNetID::PrintName(tString &s) const
04070 {
04071     s << "ePlayerNetID nr. " << ID() << ", name " << GetName();
04072 }
04073 
04074 
04075 bool ePlayerNetID::AcceptClientSync() const{
04076     return true;
04077 }
04078 
04079 #ifdef KRAWALL_SERVER
04080 
04081 // changes various user aspects
04082 template< class P > class eUserConfig: public tConfItemBase
04083 {
04084 public:
04085     P Get(tString const & name ) const
04086     {
04087         typename Properties::const_iterator iter = properties.find( name );
04088         if ( iter != properties.end() )
04089         {
04090             return (*iter).second;
04091         }
04092         
04093         return GetDefault();
04094     }
04095 protected:
04096     typedef std::map< tString, P > Properties;
04097 
04098    eUserConfig( char const * name )
04099     : tConfItemBase( name )
04100     {
04101         requiredLevel = tAccessLevel_Owner;
04102     }
04103 
04104     virtual P ReadRawVal(tString const & name, std::istream &s) const = 0;
04105     virtual P GetDefault() const = 0;
04106     virtual void TransformName( tString & name ) const {};
04107 
04108     virtual void ReadVal(std::istream &s)
04109     {
04110         tString name;
04111         s >> name;
04112         
04113         TransformName( name );
04114         
04115         P value = ReadRawVal(name, s);
04116         
04117         properties[name] = value;
04118     }
04119     
04120     Properties properties;
04121 private:
04122 
04123     virtual void WriteVal(std::ostream &s)
04124     {
04125         tASSERT(0);
04126     }
04127     
04128     virtual bool Writable(){
04129         return false;
04130     }
04131 
04132     virtual bool Save(){
04133         return false;
04134     }
04135 };
04136 
04137 // changes the access level of a player
04138 class eUserLevel: public eUserConfig< tAccessLevel >
04139 {
04140 public:
04141     eUserLevel()
04142     : eUserConfig< tAccessLevel >( "USER_LEVEL" )
04143     {
04144         requiredLevel = tAccessLevel_Owner;
04145     }
04146 
04147     tAccessLevel GetDefault() const
04148     {
04149         return tAccessLevel_DefaultAuthenticated;
04150     }
04151 
04152     virtual tAccessLevel ReadRawVal(tString const & name, std::istream &s) const
04153     {
04154         int levelInt;
04155         s >> levelInt;
04156         tAccessLevel level = static_cast< tAccessLevel >( levelInt );
04157 
04158         if ( s.fail() )
04159         {
04160             if(printErrors)
04161             {
04162                 con << tOutput( "$user_level_usage" );
04163             }
04164             return GetDefault();
04165         }
04166 
04167         if(printChange)
04168         {
04169             con << tOutput( "$user_level_change", name, tCurrentAccessLevel::GetName( level ) );
04170         }
04171 
04172         return level;
04173     }
04174 };
04175 
04176 static eUserLevel se_userLevel;
04177 
04178 // reserves a nickname
04179 class eReserveNick: public eUserConfig< tString >
04180 {
04181 #ifdef DEBUG
04182     static void TestEscape()
04183     {
04184 #ifdef KRAWALL_SERVER
04185         tString test("ä@%bla:");
04186         tString esc(se_EscapeName( test, false ).c_str());
04187         tString rev(se_UnEscapeName( esc ).c_str());
04188         tASSERT( test == rev );
04189 #endif
04190     }
04191 #endif
04192 public:
04193     eReserveNick()
04194     : eUserConfig< tString >( "RESERVE_SCREEN_NAME" )
04195     {
04196 #ifdef DEBUG
04197         TestEscape();
04198 #endif
04199     }
04200     
04201     // filter the name so it is compatible with old school user names; those
04202     // will be used later for comparison
04203     virtual void TransformName( tString & name ) const
04204     {
04205         name = ePlayerNetID::FilterName( name );
04206     }
04207 
04208     tString GetDefault() const
04209     {
04210         return tString();
04211     }
04212 
04213     virtual tString ReadRawVal(tString const & name, std::istream &s) const
04214     {
04215         tString user;
04216         s >> user;
04217 
04218         if ( user == "" )
04219         {
04220             con << tOutput( "$reserve_screen_name_usage" );
04221             return GetDefault();
04222         }
04223 
04224         user = se_UnEscapeName( user ).c_str();
04225 
04226         con << tOutput( "$reserve_screen_name_change", name, user );
04227 
04228         return user;
04229     }
04230 };
04231 
04232 static eReserveNick se_reserveNick;
04233 
04234 // authentication alias to map a local account to a global one
04235 class eAlias: public eUserConfig< tString >
04236 {
04237 public:
04238     eAlias()
04239     : eUserConfig< tString >( "USER_ALIAS" )
04240     {
04241     }
04242 
04243     tString GetDefault() const
04244     {
04245         return tString();
04246     }
04247 
04248     virtual tString ReadRawVal(tString const & name, std::istream &s) const
04249     {
04250         tString alias;
04251         s >> alias;
04252 
04253         if ( alias == "" )
04254         {
04255             con << tOutput( "$alias_usage" );
04256             return GetDefault();
04257         }
04258 
04259         alias = se_UnEscapeName( alias ).c_str();
04260 
04261         con << tOutput( "$alias_change", name, alias );
04262 
04263         return alias;
04264     }
04265 };
04266 
04267 static eAlias se_alias;
04268 
04269 // bans for players too stupid to disable autologin
04270 class eBanUser: public eUserConfig< bool >
04271 {
04272 public:
04273     eBanUser()
04274     : eUserConfig< bool >( "BAN_USER" )
04275     {
04276     }
04277 
04278     // unbans the user again
04279     void UnBan( tString const & name )
04280     {
04281         properties[name] = false;
04282         con << tOutput( "$unban_user_message", name );
04283     }
04284 
04285     void List()
04286     {
04287         bool one = false;
04288         for ( Properties::iterator i = properties.begin(); i != properties.end(); ++i )
04289         {
04290             if ( (*i).second )
04291             {
04292                 if ( one )
04293                 {
04294                     con << ", ";
04295                 }
04296                 con << (*i).first;
04297                 one = true;
04298             }
04299         }
04300         if ( one )
04301         {
04302             con << "\n";
04303         }
04304     }
04305 
04306     bool GetDefault() const
04307     {
04308         return false;
04309     }
04310 
04311     virtual bool ReadRawVal(tString const & name, std::istream &s) const
04312     {
04313         con << tOutput( "$ban_user_message", name );
04314         return true;
04315     }
04316 };
04317 
04318 static eBanUser se_banUser;
04319 
04320 // check whether a special username is banned
04321 static bool se_IsUserBanned( ePlayerNetID * p, tString const & name )
04322 {
04323     if( se_banUser.Get( name ) )
04324     {
04325         sn_KickUser( p->Owner(), tOutput( "$network_kill_banned", 60, "" ) );
04326         return true;
04327     }
04328 
04329     return false;
04330 }
04331 
04332 class eUnBanUser: public eUserConfig< bool >
04333 {
04334 public:
04335     eUnBanUser( )
04336     : eUserConfig< bool >( "UNBAN_USER" )
04337     {
04338     }
04339 
04340     bool GetDefault() const
04341     {
04342         return false;
04343     }
04344 
04345     virtual bool ReadRawVal(tString const & name, std::istream &s) const
04346     {
04347         se_banUser.UnBan(name);
04348         return false;
04349     }
04350 };
04351 
04352 static eUnBanUser se_unBanUser;
04353 
04354 static void se_ListBannedUsers( std::istream & )
04355 {
04356     se_banUser.List();
04357 }
04358 
04359 static tConfItemFunc sn_listBanConf("BAN_USER_LIST",&se_ListBannedUsers);
04360 
04361 static void se_CheckAccessLevel( tAccessLevel & level, tString const & authName )
04362 {
04363     tAccessLevel newLevel = se_userLevel.Get( authName );
04364     if ( newLevel < level || newLevel > tAccessLevel_DefaultAuthenticated )
04365     {
04366         level = newLevel;
04367     }
04368 }
04369 
04370 void ePlayerNetID::Authenticate( tString const & authName, tAccessLevel accessLevel_, ePlayerNetID const * admin )
04371 {
04372     tString newAuthenticatedName( se_EscapeName( authName ).c_str() );
04373 
04374     // no use authenticating an AI :)
04375     if ( !IsHuman() )
04376     {
04377         return;
04378     }
04379 
04380     if ( !IsAuthenticated() )
04381     {
04382         // elevate access level for registered users
04383         se_CheckAccessLevel( accessLevel_, newAuthenticatedName );
04384 
04385         rawAuthenticatedName_ = authName;
04386         tString alias = se_alias.Get( newAuthenticatedName );
04387         if ( alias != "" )
04388         {
04389             rawAuthenticatedName_ = alias;
04390             newAuthenticatedName = GetFilteredAuthenticatedName();
04391 
04392             // elevate access level again according to the new alias
04393             se_CheckAccessLevel( accessLevel_, newAuthenticatedName );
04394         }
04395 
04396         // check for stupid bans
04397         if ( se_IsUserBanned( this, newAuthenticatedName ) )
04398         {
04399             return;
04400         }
04401 
04402         // minimal access level
04403         if ( accessLevel_ > tAccessLevel_Authenticated )
04404         {
04405             accessLevel_ = static_cast< tAccessLevel >( tAccessLevel_Program - 1 );
04406         }
04407 
04408         // take over the access level
04409         SetAccessLevel( accessLevel_ );
04410 
04411         tString order( "" );
04412         if ( admin )
04413         {
04414             order = tOutput( "$login_message_byorder", 
04415                              admin->GetLogName() );
04416         }
04417 
04418         if ( IsHuman() )
04419         {
04420             if ( GetAccessLevel() != tAccessLevel_Default )
04421             {
04422                 se_SecretConsoleOut( tOutput( "$login_message_special", 
04423                                               GetName(), 
04424                                               newAuthenticatedName,
04425                                               tCurrentAccessLevel::GetName( GetAccessLevel() ),
04426                                               order ), this, admin );
04427             }
04428             else
04429             {
04430                 se_SecretConsoleOut( tOutput( "$login_message", GetName(), newAuthenticatedName, order ), this, admin );
04431             }
04432 
04433         }
04434     }
04435 
04436     GetScoreFromDisconnectedCopy();
04437 }
04438 
04439 void ePlayerNetID::DeAuthenticate( ePlayerNetID const * admin ){
04440     if ( IsAuthenticated() )
04441     {
04442         if ( admin )
04443         {
04444             se_SecretConsoleOut( tOutput( "$logout_message_deop", GetName(), GetFilteredAuthenticatedName(), admin->GetLogName() ), this, admin );
04445         }
04446         else
04447         {
04448             se_SecretConsoleOut( tOutput( "$logout_message", GetName(), GetFilteredAuthenticatedName() ), this );
04449         }
04450     }
04451 
04452     // force falling back to regular user name on next update
04453     SetAccessLevel( tAccessLevel_Default );
04454 
04455     rawAuthenticatedName_ = "";
04456 }
04457 
04458 bool ePlayerNetID::IsAuthenticated() const
04459 {
04460     return int(GetAccessLevel()) <= int(tAccessLevel_Authenticated);
04461 }
04462 #endif
04463 
04464 // create our voter or find it
04465 void ePlayerNetID::CreateVoter()
04466 {
04467     // only count nonlocal players with voting support as voters
04468     if ( sn_GetNetState() != nCLIENT && this->Owner() != 0 && sn_Connections[ this->Owner() ].version.Max() >= 3 )
04469     {
04470         this->voter_ = eVoter::GetVoter( this->Owner() );
04471         if ( this->voter_ )
04472             this->voter_->PlayerChanged();
04473     }
04474 }
04475 
04476 void ePlayerNetID::WriteSync(nMessage &m){
04477     lastSync=tSysTimeFloat();
04478     nNetObject::WriteSync(m);
04479     m.Write(r);
04480     m.Write(g);
04481     m.Write(b);
04482 
04483     // write ping charity; spectators get a fake (high) value
04484     if ( currentTeam || nextTeam )
04485         m.Write(pingCharity);
04486     else
04487         m.Write(1000);
04488 
04489     if ( sn_GetNetState() == nCLIENT )
04490     {
04491         m << nameFromClient_;
04492     }
04493     else
04494     {
04495         m << nameFromServer_;
04496     }
04497 
04498     //if(sn_GetNetState()==nSERVER)
04499     m << ping;
04500 
04501     // pack chat, spectator and stealth status together
04502     unsigned short flags = ( chatting_ ? 1 : 0 ) | ( spectating_ ? 2 : 0 ) | ( stealth_ ? 4 : 0 );
04503     m << flags;
04504 
04505     m << score;
04506     m << static_cast<unsigned short>(disconnected);
04507 
04508     m << nextTeam;
04509     m << currentTeam;
04510 
04511     m << favoriteNumberOfPlayersPerTeam;
04512     m << nameTeamAfterMe;
04513     //TODO: Only update between rounds
04514     m << teamname;
04515 }
04516 
04517 // makes sure the passed string is not longer than the given maximum
04518 static void se_CutString( tColoredString & string, int maxLen )
04519 {
04520     if (string.Len() > maxLen )
04521     {
04522         string = string.SubStr(0, maxLen);
04523         //string[string.Len()-1]='\0';
04524         string.RemoveTrailingColor();
04525     }
04526 }
04527 
04528 static void se_CutString( tString & string, int maxLen )
04529 {
04530     se_CutString( reinterpret_cast< tColoredString & >( string ), maxLen );
04531 }
04532 
04533 static bool se_bugColorOverflow=true;
04534 tSettingItem< bool > se_bugColorOverflowColor( "BUG_COLOR_OVERFLOW", se_bugColorOverflow );
04535 void Clamp( unsigned short & colorComponent )
04536 {
04537     if ( colorComponent > 15 )
04538         colorComponent = 15;
04539 }
04540 
04541 // function prototype for character testing functions
04542 typedef bool TestCharacter( char c );
04543 
04544 // strips characters matching a test beginnings and ends of names
04545 static void se_StripMatchingEnds( tString & stripper, TestCharacter & beginTester, TestCharacter & endTester )
04546 {
04547     int len = stripper.Size() - 1;
04548     int first = 0, last = len;
04549 
04550     // eat whitespace from beginnig and end
04551     while ( first < len && beginTester( stripper[first] ) ) ++first;
04552     while ( last > 0 && ( !stripper[last] || endTester(stripper[last] ) ) ) --last;
04553 
04554     // strip everything?
04555     if ( first > last )
04556     {
04557         stripper = "";
04558         return;
04559     }
04560 
04561     // strip
04562     if ( first > 0 || last < stripper.Len() - 1 )
04563         stripper = stripper.SubStr( first, last + 1 - first );
04564 }
04565 
04566 // removed convenience function, VisualC 6 cannot cope with it...
04567 // strips characters matching a test beginnings and ends of names
04568 //static void se_StripMatchingEnds( tString & stripper, TestCharacter & tester )
04569 //{
04570 //    se_StripMatchingEnds( stripper, tester, tester );
04571 //}
04572 
04573 // function wrapper for what may be a macro
04574 static bool se_IsBlank( char c )
04575 {
04576     return isblank( c );
04577 }
04578 
04579 // enf of player names should neither be space or :
04580 static bool se_IsInvalidNameEnd( char c )
04581 {
04582     return isblank( c ) || c == ':' || c == '.';
04583 }
04584 
04585 // filter name ends
04586 static void se_StripNameEnds( tString & name )
04587 {
04588     se_StripMatchingEnds( name, se_IsBlank, se_IsInvalidNameEnd );
04589 }
04590 
04591 // test if a user name is used by anyone else than the passed player
04592 static bool se_IsNameTaken( tString const & name, ePlayerNetID const * exception )
04593 {
04594     if ( name.Len() <= 1 )
04595         return false;
04596 
04597     // check for other players with the same name
04598     for (int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
04599     {
04600         ePlayerNetID * player = se_PlayerNetIDs(i);
04601         if ( player != exception )
04602         {
04603             if ( name == player->GetUserName() || name == ePlayerNetID::FilterName( player->GetName() ) )
04604                 return true;
04605         }
04606     }
04607 
04608 #ifdef KRAWALL_SERVER
04609     // check for reserved nicknames
04610     {
04611         tString reservedFor = se_reserveNick.Get( name );
04612         if ( reservedFor != "" && 
04613              ( !exception || exception->GetAccessLevel() >= tAccessLevel_Default ||
04614                exception->GetRawAuthenticatedName() != reservedFor ) )
04615         {
04616             return true;
04617         }
04618     }
04619 #endif
04620 
04621     return false;
04622 }
04623 
04624 static bool se_allowImposters = false;
04625 static tSettingItem< bool > se_allowImposters1( "ALLOW_IMPOSTERS", se_allowImposters );
04626 static tSettingItem< bool > se_allowImposters2( "ALLOW_IMPOSTORS", se_allowImposters );
04627 
04628 static bool se_filterColorNames=false;
04629 tSettingItem< bool > se_coloredNamesConf( "FILTER_COLOR_NAMES", se_filterColorNames );
04630 static bool se_stripNames=true;
04631 tSettingItem< bool > se_stripConf( "FILTER_NAME_ENDS", se_stripNames );
04632 static bool se_stripMiddle=true;
04633 tSettingItem< bool > se_stripMiddleConf( "FILTER_NAME_MIDDLE", se_stripMiddle );
04634 
04635 // do the optional filtering steps
04636 static void se_OptionalNameFilters( tString & remoteName )
04637 {
04638     // filter colors
04639     if ( se_filterColorNames )
04640     {
04641         remoteName = tColoredString::RemoveColors( remoteName );
04642     }
04643 
04644     // don't do the fancy stuff on the client, it only makes names on score tables and
04645     // console messages go out of sync.
04646     if ( sn_GetNetState() == nCLIENT )
04647         return;
04648 
04649     // strip whitespace
04650     if ( se_stripNames )
04651         se_StripNameEnds( remoteName );
04652 
04653     if ( se_stripMiddle )
04654     {
04655         // first, scan for double whitespace
04656         bool whitespace=false;
04657         int i;
04658         for ( i = 0; i < remoteName.Len()-1; ++i )
04659         {
04660             bool newWhitespace=isblank(remoteName[i]);
04661             if ( newWhitespace && whitespace )
04662             {
04663                 // yes, double whitespace there. Filter it out.
04664                 whitespace=newWhitespace=false;
04665                 tString copy;
04666                 for ( i = 0; i < remoteName.Len()-1; ++i )
04667                 {
04668                     char c = remoteName[i];
04669                     newWhitespace=isblank(c);
04670                     if ( !whitespace || !newWhitespace )
04671                     {
04672                         copy+=c;
04673                     }
04674                     whitespace=newWhitespace;
04675                 }
04676                 remoteName=copy;
04677 
04678                 // abort loop.
04679                 break;
04680             }
04681 
04682             whitespace=newWhitespace;
04683         }
04684     }
04685 
04686     // zero strings or color code only strings are illegal
04687     if ( !IsLegalPlayerName( remoteName ) )
04688     {
04689         tString oldName = remoteName;
04690 
04691         // revert to old name if possible
04692         if ( IsLegalPlayerName( oldName ) )
04693         {
04694             remoteName = oldName;
04695         }
04696         else
04697         {
04698             // or replace it by a default value
04699             // (no, not bad localization, this is only a punishment for people who think they're smart.)
04700             remoteName = "Player 1";
04701         }
04702     }
04703 }
04704 
04705 void ePlayerNetID::ReadSync(nMessage &m){
04706     // check whether this is the first sync
04707     bool firstSync = ( this->ID() == 0 );
04708 
04709     nNetObject::ReadSync(m);
04710 
04711     m.Read(r);
04712     m.Read(g);
04713     m.Read(b);
04714 
04715     if ( !se_bugColorOverflow )
04716     {
04717         // clamp color values
04718         Clamp(r);
04719         Clamp(g);
04720         Clamp(b);
04721     }
04722 
04723     m.Read(pingCharity);
04724     sg_ClampPingCharity(pingCharity);
04725 
04726     // name as sent from the other end
04727     tString & remoteName = ( sn_GetNetState() == nCLIENT ) ? nameFromServer_ : nameFromClient_;
04728 
04729     // name handling
04730     {
04731         // read and shorten name, but don't update it yet
04732         m >> remoteName;
04733 
04734         // filter
04735         se_OptionalNameFilters( remoteName );
04736 
04737         se_CutString( remoteName, 16 );
04738     }
04739 
04740     // directly apply name changes sent from the server, they are safe.
04741     if ( sn_GetNetState() != nSERVER )
04742     {
04743         UpdateName();
04744     }
04745 
04746     REAL p;
04747     m >> p;
04748     if (sn_GetNetState()!=nSERVER)
04749         ping=p;
04750 
04751     //  if (!m.End())
04752     {
04753         // read chat and spectator status
04754         unsigned short flags;
04755         m >> flags;
04756 
04757         if (Owner() != ::sn_myNetID)
04758         {
04759             bool newChat = ( ( flags & 1 ) != 0 );
04760             bool newSpectate = ( ( flags & 2 ) != 0 );
04761             bool newStealth = ( ( flags & 4 ) != 0 );
04762 
04763             if ( chatting_ != newChat || spectating_ != newSpectate || newStealth != stealth_ )
04764                 lastActivity_ = tSysTimeFloat();
04765             chatting_   = newChat;
04766             spectating_ = newSpectate;
04767             stealth_    = newStealth;
04768         }
04769     }
04770 
04771     //  if (!m.End())
04772     {
04773         if(sn_GetNetState()!=nSERVER)
04774             m >> score;
04775         else{
04776             int s;
04777             m >> s;
04778         }
04779     }
04780 
04781     if (!m.End()){
04782         unsigned short newdisc;
04783         m >> newdisc;
04784 
04785         if (Owner() != ::sn_myNetID && sn_GetNetState()!=nSERVER)
04786             disconnected = newdisc;
04787     }
04788 
04789     if (!m.End())
04790     {
04791         if ( nSERVER != sn_GetNetState() )
04792         {
04793             eTeam *newCurrentTeam, *newNextTeam;
04794 
04795             m >> newNextTeam;
04796             m >> newCurrentTeam;
04797 
04798             // update team
04799             if ( newCurrentTeam != currentTeam )
04800             {
04801                 if ( newCurrentTeam )
04802                     // automatically removed player from currentTeam
04803                     newCurrentTeam->AddPlayerDirty( this );
04804                 else
04805                     currentTeam->RemovePlayer( this );
04806             }
04807             // update nextTeam
04808             nextTeam = newNextTeam;
04809         }
04810         else
04811         {
04812             eTeam* t;
04813             m >> t;
04814             m >> t;
04815         }
04816 
04817         m >> favoriteNumberOfPlayersPerTeam;
04818         m >> nameTeamAfterMe;
04819     }
04820     if (!m.End())
04821     {
04822         m >> teamname;
04823     }
04824     // con << "Player info updated.\n";
04825 
04826     // make sure we did not accidentally overwrite values
04827     // ePlayer::Update();
04828 
04829     // update the team
04830     if ( nSERVER == sn_GetNetState() )
04831     {
04832         if ( nextTeam )
04833             nextTeam->UpdateProperties();
04834 
04835         if ( currentTeam )
04836             currentTeam->UpdateProperties();
04837     }
04838 
04839     // first sync message
04840     if ( firstSync && sn_GetNetState() == nSERVER )
04841     {
04842         UpdateName();
04843 
04844 #ifndef KRAWALL_SERVER
04845         GetScoreFromDisconnectedCopy();
04846 #endif
04847         SetDefaultTeam();
04848 
04849         RequestSync();
04850     }
04851 
04852 }
04853 
04854 
04855 nNOInitialisator<ePlayerNetID> ePlayerNetID_init(201,"ePlayerNetID");
04856 
04857 nDescriptor &ePlayerNetID::CreatorDescriptor() const{
04858     return ePlayerNetID_init;
04859 }
04860 
04861 
04862 
04863 void ePlayerNetID::ControlObject(eNetGameObject *c){
04864     if (bool(object) && c!=object)
04865         ClearObject();
04866 
04867     if (!c)
04868     {
04869         return;
04870     }
04871 
04872 
04873     object=c;
04874     c->team = currentTeam;
04875 
04876     if (bool(object))
04877         object->SetPlayer(this);
04878 #ifdef DEBUG
04879     //con << "Player " << name << " controlles new object.\n";
04880 #endif
04881 
04882     NewObject();
04883 }
04884 
04885 void ePlayerNetID::ClearObject(){
04886     if (object)
04887     {
04888         tJUST_CONTROLLED_PTR< eNetGameObject > x=object;
04889         object=NULL;
04890         x->RemoveFromGame();
04891         x->SetPlayer( NULL );
04892     }
04893 #ifdef DEBUG
04894     //con << "Player " << name << " controlles nothing.\n";
04895 #endif
04896 }
04897 
04898 void ePlayerNetID::Greet(){
04899     if (!greeted){
04900         tOutput o;
04901         o.SetTemplateParameter(1, GetName() );
04902         o.SetTemplateParameter(2, sn_programVersion);
04903         o << "$player_welcome";
04904         tString s;
04905         s << o;
04906         s << "\n";
04907         GreetHighscores(s);
04908         s << "\n";
04909         //std::cout << s;
04910         sn_ConsoleOut(s,Owner());
04911         greeted=true;
04912     }
04913 }
04914 
04915 eNetGameObject *ePlayerNetID::Object() const{
04916     return object;
04917 }
04918 
04919 static bool se_consoleLadderLog = false;
04920 static tSettingItem< bool > se_consoleLadderLogConf( "CONSOLE_LADDER_LOG", se_consoleLadderLog );
04921 
04922 void se_SaveToLadderLog( tOutput const & out )
04923 {
04924     if (se_consoleLadderLog)
04925     {
04926         std::cout << "[L] " << out;
04927         std::cout.flush();
04928     }
04929     if (sn_GetNetState()!=nCLIENT && !tRecorder::IsPlayingBack())
04930     {
04931         std::ofstream o;
04932         if ( tDirectories::Var().Open(o, "ladderlog.txt", std::ios::app) )
04933             o << out;
04934     }
04935 }
04936 
04937 void se_SaveToScoreFile(const tOutput &o){
04938     tString s(o);
04939 
04940 #ifdef DEBUG
04941     if (sn_GetNetState()!=nCLIENT){
04942 #else
04943     if (sn_GetNetState()==nSERVER && !tRecorder::IsPlayingBack()){
04944 #endif
04945 
04946         std::ofstream o;
04947         if ( tDirectories::Var().Open(o, "scorelog.txt", std::ios::app) )
04948             o << tColoredString::RemoveColors(s);
04949     }
04950 #ifdef DEBUG
04951 }
04952 #else
04953 }
04954 #endif
04955 
04956 // void ePlayerNetID::SetRubber(REAL rubber2) {rubberstatus = rubber2;}
04957 
04958 void ePlayerNetID::AddScore(int points,
04959                             const tOutput& reasonwin,
04960                             const tOutput& reasonlose)
04961 {
04962     if (points==0)
04963         return;
04964 
04965     score += points;
04966     if (currentTeam)
04967         currentTeam->AddScore( points );
04968 
04969     tColoredString name;
04970     name << *this << tColoredString::ColorString(1,1,1);
04971 
04972     tOutput message;
04973     message.SetTemplateParameter(1, name);
04974     message.SetTemplateParameter(2, points > 0 ? points : -points);
04975 
04976 
04977     if (points>0)
04978     {
04979         if (reasonwin.IsEmpty())
04980             message << "$player_win_default";
04981         else
04982             message.Append(reasonwin);
04983     }
04984     else
04985     {
04986         if (reasonlose.IsEmpty())
04987             message << "$player_lose_default";
04988         else
04989             message.Append(reasonlose);
04990     }
04991 
04992     sn_ConsoleOut(message);
04993     RequestSync(true);
04994 
04995     se_SaveToScoreFile(message);
04996 }
04997 
04998 
04999 
05000 
05001 int ePlayerNetID::TotalScore() const
05002 {
05003     if ( currentTeam )
05004     {
05005         return score;// + currentTeam->Score() * 5;
05006     }
05007     else
05008     {
05009         return score;
05010     }
05011 }
05012 
05013 
05014 void ePlayerNetID::SwapPlayersNo(int a,int b){
05015     if (0>a || se_PlayerNetIDs.Len()<=a)
05016         return;
05017     if (0>b || se_PlayerNetIDs.Len()<=b)
05018         return;
05019     if (a==b)
05020         return;
05021 
05022     ePlayerNetID *A=se_PlayerNetIDs(a);
05023     ePlayerNetID *B=se_PlayerNetIDs(b);
05024 
05025     se_PlayerNetIDs(b)=A;
05026     se_PlayerNetIDs(a)=B;
05027     A->listID=b;
05028     B->listID=a;
05029 }
05030 
05031 
05032 void ePlayerNetID::SortByScore(){
05033     // bubble sort (AAARRGGH! but good for lists that change not much)
05034 
05035     bool inorder=false;
05036     while (!inorder){
05037         inorder=true;
05038         int i;
05039         for(i=se_PlayerNetIDs.Len()-2;i>=0;i--)
05040             if (se_PlayerNetIDs(i)->TotalScore() < se_PlayerNetIDs(i+1)->TotalScore() ){
05041                 SwapPlayersNo(i,i+1);
05042                 inorder=false;
05043             }
05044     }
05045 }
05046 
05047 void ePlayerNetID::ResetScore(){
05048     int i;
05049     for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
05050         se_PlayerNetIDs(i)->score=0;
05051         if (sn_GetNetState()==nSERVER)
05052             se_PlayerNetIDs(i)->RequestSync();
05053     }
05054 
05055     for(i=eTeam::teams.Len()-1;i>=0;i--){
05056         eTeam::teams(i)->ResetScore();
05057         if (sn_GetNetState()==nSERVER)
05058             eTeam::teams(i)->RequestSync();
05059     }
05060 
05061     ResetScoreDifferences();
05062 }
05063 
05064 void ePlayerNetID::DisplayScores(){
05065     sr_ResetRenderState(true);
05066 
05067     REAL W=sr_screenWidth;
05068     REAL H=sr_screenHeight;
05069 
05070     REAL MW=400;
05071     REAL MH=(MW*3)/4;
05072 
05073     if(W>MW)
05074         W=MW;
05075 
05076     if(H>MH)
05077         H=MH;
05078 
05079 #ifndef DEDICATED
05080     if (sr_glOut){
05081         ::Color(1,1,1);
05082         float y=.6;
05083 
05084         // print team ranking if there actually is a team with more than one player
05085         int maxPlayers = 20;
05086         bool showTeam = false;
05087         for ( int i = eTeam::teams.Len() - 1; i >= 0; --i )
05088         {
05089             if ( eTeam::teams[i]->NumPlayers() > 1 ||
05090                     ( eTeam::teams[i]->NumPlayers() == 1 && eTeam::teams[i]->Player(0)->Score() != eTeam::teams[i]->Score() ) )
05091             {
05092                 y = eTeam::RankingGraph(y);
05093                 y-=.06;
05094                 maxPlayers -= ( eTeam::teams.Len() > 6 ? 6 : eTeam::teams.Len() ) + 2;
05095                 showTeam = true;
05096                 break;
05097             }
05098         }
05099 
05100         // print player ranking
05101         RankingGraph( y , maxPlayers );
05102     }
05103 #endif
05104 }
05105 
05106 
05107 tString ePlayerNetID::Ranking( int MAX, bool cut ){
05108     SortByScore();
05109 
05110     tString ret;
05111 
05112     if (se_PlayerNetIDs.Len()>0){
05113         ret << tOutput("$player_scoretable_name");
05114         ret.SetPos(17, cut );
05115         ret << tOutput("$player_scoretable_alive");
05116         ret.SetPos(24, cut );
05117         ret << tOutput("$player_scoretable_score");
05118         ret.SetPos(31, cut );
05119         ret << tOutput("$player_scoretable_ping");
05120         ret.SetPos(37, cut );
05121         ret << tOutput("$player_scoretable_team");
05122         ret.SetPos(53, cut );
05123         ret << "\n";
05124 
05125         int max = se_PlayerNetIDs.Len();
05126 
05127         // wasting the last line with ... is as stupid if it stands for only
05128         // one player
05129         if ( MAX == max + 1 )
05130             MAX = max;
05131 
05132         if ( max > MAX && MAX > 0 )
05133         {
05134             max = MAX ;
05135         }
05136 
05137         for(int i=0;i<max;i++){
05138             tColoredString line;
05139             ePlayerNetID *p=se_PlayerNetIDs(i);
05140             // tColoredString name( p->GetColoredName() );
05141             // name.SetPos(16, cut );
05142 
05143             //  This line is example of how we manually get the player color... could come in useful.
05144             // line << tColoredString::ColorString( p->r/15.0, p->g/15.0, p->b/15.0 ) << name << tColoredString::ColorString(1,1,1);
05145 
05146             // however, using the streaming operator is a lot cleaner. The example is left, as it really can be usefull in some situations.
05147             line << *p;
05148             line.SetPos(17, false );
05149             if ( p->Object() && p->Object()->Alive() )
05150             {
05151                 line << tOutput("$player_scoretable_alive_yes");
05152             }
05153             else
05154             {
05155                 line << tOutput("$player_scoretable_alive_no");
05156             }
05157             line.SetPos(24, cut );
05158             line << p->score;
05159 
05160             if (p->IsActive())
05161             {
05162                 line.SetPos(31, cut );
05163                 //line << "ping goes here";
05164                 line << int(p->ping*1000);
05165                 line.SetPos(37, cut );
05166                 if ( p->currentTeam )
05167                 {
05168                     //tString teamtemp = p->currentTeam->Name();
05169                     //teamtemp.RemoveHex();
05170                     line << tColoredString::RemoveColors(p->currentTeam->Name());
05171                     line.SetPos(53, cut );
05172                 }
05173             }
05174             else
05175                 line << tOutput("$player_scoretable_inactive");
05176             ret << line << "\n";
05177         }
05178         if ( max < se_PlayerNetIDs.Len() )
05179         {
05180             ret << "...\n";
05181         }
05182 
05183     }
05184     else
05185         ret << tOutput("$player_scoretable_nobody");
05186     return ret;
05187 }
05188 float ePlayerNetID::RankingGraph( float y, int MAX ){
05189     SortByScore();
05190 
05191     if (se_PlayerNetIDs.Len()>0){
05192         tColoredString name;
05193         name << tColoredString::ColorString(1.,.5,.5)
05194         << tOutput("$player_scoretable_name");
05195         DisplayText(-.7, y, .06, name.c_str(), sr_fontScoretable, -1);
05196         tColoredString alive;
05197         alive << tOutput("$player_scoretable_alive");
05198         DisplayText(-.3, y, .06, alive.c_str(), sr_fontScoretable, -1);
05199         tColoredString score;
05200         score << tOutput("$player_scoretable_score");
05201         DisplayText(.05, y, .06, score.c_str(), sr_fontScoretable, 1);
05202         tColoredString ping;
05203         ping << tOutput("$player_scoretable_ping");
05204         DisplayText(.25, y, .06, ping.c_str(), sr_fontScoretable, 1);
05205         tColoredString team;
05206         team << tOutput("$player_scoretable_team");
05207         DisplayText(.3, y, .06, team.c_str(), sr_fontScoretable, -1);
05208         y-=.06;
05209 
05210         int max = se_PlayerNetIDs.Len();
05211 
05212         // wasting the last line with ... is as stupid if it stands for only
05213         // one player
05214         if ( MAX == max + 1 )
05215             MAX = max;
05216 
05217         if ( max > MAX && MAX > 0 )
05218         {
05219             max = MAX ;
05220         }
05221 
05222         for(int i=0;i<max;i++){
05223             ePlayerNetID *p=se_PlayerNetIDs(i);
05224             if(p->chatting_)
05225                 DisplayText(-.705, y, .06, "*", sr_fontScoretable, 1);
05226             tColoredString name;
05227             name << *p;
05228             DisplayText(-.7, y, .06, name.c_str(), sr_fontScoretable, -1);
05229             tColoredString alive;
05230             if ( p->Object() && p->Object()->Alive() )
05231             {
05232                 alive << tColoredString::ColorString(0,1,0)
05233                 << tOutput("$player_scoretable_alive_yes");
05234             }
05235             else
05236             {
05237                 alive << tColoredString::ColorString(1,0,0)
05238                 << tOutput("$player_scoretable_alive_no");
05239             }
05240             DisplayText(-.3, y, .06, alive.c_str(), sr_fontScoretable, -1);
05241             tColoredString score;
05242             score << p->score;
05243             DisplayText(.05, y, .06, score.c_str(), sr_fontScoretable, 1);
05244             if (p->IsActive())
05245             {
05246                 tColoredString ping;
05247                 ping << int(p->ping*1000);
05248                 DisplayText(.25, y, .06, ping.c_str(), sr_fontScoretable, 1);
05249                 if ( p->currentTeam )
05250                 {
05251                     tColoredString team;
05252                     eTeam *t = p->currentTeam;
05253                     team << tColoredStringProxy(t->R()/15.f, t->G()/15.f, t->B()/15.f) << t->Name();
05254                     DisplayText(.3, y, .06, team.c_str(), sr_fontScoretable, -1);
05255                 }
05256             }
05257             else {
05258                 tColoredString noone;
05259                 noone << tOutput("$player_scoretable_inactive");
05260                 DisplayText(.1, y, .06, noone.c_str(), sr_fontScoretable, -1);
05261             }
05262             y-=.06;
05263         }
05264         if ( max < se_PlayerNetIDs.Len() )
05265         {
05266             DisplayText(-.7, y, .06, "...", sr_fontScoretable, -1);
05267             y-=.06;
05268         }
05269 
05270     }
05271     else {
05272         tColoredString noone;
05273         noone << tOutput("$player_scoretable_nobody");
05274         DisplayText(-.7, y, .06, noone.c_str(), sr_fontScoretable, -1);
05275     }
05276     return y;
05277 }
05278 
05279 void ePlayerNetID::RankingLadderLog() {
05280     SortByScore();
05281 
05282     int num_humans = 0;
05283     int max = se_PlayerNetIDs.Len();
05284     for(int i = 0; i < max; ++i) {
05285         ePlayerNetID *p = se_PlayerNetIDs(i);
05286         if(p->Owner() == 0) continue; // ignore AIs
05287 
05288         tString line("ONLINE_PLAYER ");
05289 
05290         line << p->GetLogName();
05291 
05292         if(p->IsActive()) {
05293             line << " " << p->ping;
05294             if(p->currentTeam) {
05295                 line << " " << FilterName(p->currentTeam->Name());
05296                 ++num_humans;
05297             }
05298         }
05299 
05300         line << '\n';
05301         se_SaveToLadderLog(line);
05302     }
05303     tString line("NUM_HUMANS ");
05304     line << num_humans << '\n';
05305     se_SaveToLadderLog(line);
05306 }
05307 
05308 void ePlayerNetID::ClearAll(){
05309     for(int i=MAX_PLAYERS-1;i>=0;i--){
05310         ePlayer *local_p=ePlayer::PlayerConfig(i);
05311         if (local_p)
05312             local_p->netPlayer = NULL;
05313     }
05314 }
05315 
05316 static bool se_VisibleSpectatorsSupported()
05317 {
05318     static nVersionFeature se_visibleSpectator(13);
05319     return sn_GetNetState() != nCLIENT || se_visibleSpectator.Supported(0);
05320 }
05321 
05322 // @param spectate if true
05323 void ePlayerNetID::SpectateAll( bool spectate ){
05324     for(int i=MAX_PLAYERS-1;i>=0;i--){
05325         ePlayer *local_p=ePlayer::PlayerConfig(i);
05326         if (local_p)
05327         {
05328             if ( se_VisibleSpectatorsSupported() && bool(local_p->netPlayer) )
05329             {
05330                 local_p->netPlayer->spectating_ = spectate || local_p->spectate;
05331 
05332                 local_p->netPlayer->RequestSync();
05333             }
05334             else
05335                 local_p->netPlayer = NULL;
05336         }
05337     }
05338 }
05339 
05340 void ePlayerNetID::CompleteRebuild(){
05341     ClearAll();
05342     Update();
05343 }
05344 
05345 static int se_ColorDistance( int a[3], int b[3] )
05346 {
05347     int distance = 0;
05348     for( int i = 2; i >= 0; --i )
05349     {
05350         int diff = a[i] - b[i];
05351         distance += diff * diff;
05352         //diff = diff < 0 ? -diff : diff;
05353         //if ( diff > distance )
05354         //{
05355         //    distance = diff;
05356         //}
05357     }
05358 
05359     return distance;
05360 }
05361 
05362 bool se_randomizeColor = true;
05363 static tSettingItem< bool > se_randomizeColorConf( "PLAYER_RANDOM_COLOR", se_randomizeColor );
05364 
05365 static void se_RandomizeColor( ePlayer * l, ePlayerNetID * p )
05366 {
05367     int currentRGB[3];
05368     int newRGB[3];
05369     int nullRGB[3]={0,0,0};
05370 
05371     static tReproducibleRandomizer randomizer;
05372 
05373     for( int i = 2; i >= 0; --i )
05374     {
05375         currentRGB[i] = l->rgb[i];
05376         newRGB[i] = randomizer.Get(15);
05377     }
05378 
05379     int currentMinDiff = se_ColorDistance( currentRGB, nullRGB )/2;
05380     int newMinDiff = se_ColorDistance( newRGB, nullRGB )/2;
05381 
05382     // check the minimal distance of the new random color with all players
05383     for ( int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
05384     {
05385         ePlayerNetID * other = se_PlayerNetIDs(i);
05386         if ( other != p )
05387         {
05388             int color[3] = { other->r, other->g, other->b };
05389             int currentDiff = se_ColorDistance( currentRGB, color );
05390             int newDiff     = se_ColorDistance( newRGB, color );
05391             if ( currentDiff < currentMinDiff )
05392             {
05393                 currentMinDiff = currentDiff;
05394             }
05395             if ( newDiff < newMinDiff )
05396             {
05397                 newMinDiff = newDiff;
05398             }
05399         }
05400     }
05401 
05402     // update current color
05403     if ( currentMinDiff < newMinDiff )
05404     {
05405         for( int i = 2; i >= 0; --i )
05406         {
05407             l->rgb[i] = newRGB[i];
05408         }
05409     }
05410 }
05411 
05412 static nSettingItem<int> se_pingCharityServerConf("PING_CHARITY_SERVER",sn_pingCharityServer );
05413 static nVersionFeature   se_pingCharityServerControlled( 14 );
05414 
05415 static int se_pingCharityMax = 500, se_pingCharityMin = 0;
05416 static tSettingItem<int> se_pingCharityMaxConf( "PING_CHARITY_MAX", se_pingCharityMax );
05417 static tSettingItem<int> se_pingCharityMinConf( "PING_CHARITY_MIN", se_pingCharityMin );
05418 
05419 // Update the netPlayer_id list
05420 void ePlayerNetID::Update(){
05421 #ifdef KRAWALL_SERVER
05422         // update access level
05423         UpdateAccessLevelRequiredToPlay();
05424 
05425         // remove players with insufficient access rights
05426         tAccessLevel required = AccessLevelRequiredToPlay();
05427         for( int i=se_PlayerNetIDs.Len()-1; i >= 0; --i )
05428         {
05429             ePlayerNetID* player = se_PlayerNetIDs(i);
05430             if ( player->GetAccessLevel() > required && player->IsHuman() )
05431             {
05432                 player->SetTeamWish(0);
05433             }
05434         }
05435 #endif
05436 
05437 #ifdef DEDICATED
05438     if (sr_glOut)
05439 #endif
05440     {
05441         for(int i=0; i<MAX_PLAYERS; ++i ){
05442             bool in_game=ePlayer::PlayerIsInGame(i);
05443             ePlayer *local_p=ePlayer::PlayerConfig(i);
05444             tASSERT(local_p);
05445             tCONTROLLED_PTR(ePlayerNetID) &p=local_p->netPlayer;
05446 
05447             if (!p && in_game && ( !local_p->spectate || se_VisibleSpectatorsSupported() ) ) // insert new player
05448             {
05449                 p=tNEW(ePlayerNetID) (i);
05450                 p->SetDefaultTeam();
05451                 p->RequestSync();
05452             }
05453 
05454             if (bool(p) && (!in_game || ( local_p->spectate && !se_VisibleSpectatorsSupported() ) ) && // remove player
05455                     p->Owner() == ::sn_myNetID )
05456             {
05457                 p->RemoveFromGame();
05458 
05459                 if (p->object)
05460                     p->object->player = NULL;
05461 
05462                 p->object = NULL;
05463                 p = NULL;
05464             }
05465 
05466             if (bool(p) && in_game){ // update
05467                 p->favoriteNumberOfPlayersPerTeam=ePlayer::PlayerConfig(i)->favoriteNumberOfPlayersPerTeam;
05468                 p->nameTeamAfterMe=ePlayer::PlayerConfig(i)->nameTeamAfterMe;
05469 
05470                 if ( se_randomizeColor )
05471                 {
05472                     se_RandomizeColor(local_p,p);
05473                 }
05474 
05475                 p->r=ePlayer::PlayerConfig(i)->rgb[0];
05476                 p->g=ePlayer::PlayerConfig(i)->rgb[1];
05477                 p->b=ePlayer::PlayerConfig(i)->rgb[2];
05478 
05479                 sg_ClampPingCharity();
05480                 p->pingCharity=::pingCharity;
05481                 p->SetTeamname(local_p->Teamname());
05482 
05483                 // update spectator status
05484                 if ( p->spectating_ != local_p->spectate )
05485                     p->RequestSync();
05486                 p->spectating_ = local_p->spectate;
05487 
05488                 // update stealth status
05489                 if ( p->stealth_ != local_p->stealth )
05490                     p->RequestSync();
05491                 p->stealth_ = local_p->stealth;
05492 
05493                 // update name
05494                 tString newName( ePlayer::PlayerConfig(i)->Name() );
05495 
05496                 if ( ::sn_GetNetState() != nCLIENT || newName != p->nameFromClient_ )
05497                 {
05498                     p->RequestSync();
05499                 }
05500 
05501                 p->SetName( ePlayer::PlayerConfig(i)->Name() );
05502             }
05503         }
05504 
05505     }
05506 
05507     int i;
05508 
05509 
05510     // update the ping charity, but
05511     // don't do so on the client if the server controls the ping charity completely
05512     if ( sn_GetNetState() == nSERVER || !se_pingCharityServerControlled.Supported(0) )
05513     {
05514         int old_c=sn_pingCharityServer;
05515         sg_ClampPingCharity();
05516         sn_pingCharityServer=::pingCharity;
05517 
05518 #ifndef DEDICATED
05519         if (sn_GetNetState()==nCLIENT)
05520 #endif
05521             sn_pingCharityServer+=100000;
05522 
05523         // set configurable maximum
05524         if ( se_pingCharityServerControlled.Supported() )
05525         {
05526             sn_pingCharityServer = se_pingCharityMax;
05527         }
05528 
05529         for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
05530             ePlayerNetID *pni=se_PlayerNetIDs(i);
05531             pni->UpdateName();
05532             int new_ps=pni->pingCharity;
05533             new_ps+=int(pni->ping*500);
05534 
05535             // only take ping charity into account for non-spectators
05536             if ( sn_GetNetState() != nSERVER || pni->currentTeam || pni->nextTeam )
05537                 if (new_ps < sn_pingCharityServer)
05538                     sn_pingCharityServer=new_ps;
05539         }
05540         if (sn_pingCharityServer<0)
05541             sn_pingCharityServer=0;
05542 
05543         // set configurable minimum
05544         if ( se_pingCharityServerControlled.Supported() )
05545         {
05546             if ( sn_pingCharityServer < se_pingCharityMin )
05547                 sn_pingCharityServer = se_pingCharityMin;
05548         }
05549 
05550         if (old_c!=sn_pingCharityServer)
05551         {
05552             tOutput o;
05553             o.SetTemplateParameter(1, old_c);
05554             o.SetTemplateParameter(2, sn_pingCharityServer);
05555             o << "$player_pingcharity_changed";
05556             con << o;
05557 
05558             // trigger transmission to the clients
05559             if (sn_GetNetState()==nSERVER)
05560                 se_pingCharityServerConf.nConfItemBase::WasChanged(true);
05561         }
05562     }
05563 
05564     // update team assignment
05565     bool assigned = true;
05566     while ( assigned && sn_GetNetState() != nCLIENT )
05567     {
05568         assigned = false;
05569 
05570         int players = se_PlayerNetIDs.Len();
05571 
05572         // start with the players that came in earlier
05573         for( i=0; i<players; ++i )
05574         {
05575             ePlayerNetID* player = se_PlayerNetIDs(i);
05576 
05577             // only assign new team if it is possible
05578             if ( player->NextTeam() != player->CurrentTeam() && player->TeamChangeAllowed() && 
05579                     ( !player->NextTeam() || player->NextTeam()->PlayerMayJoin( player ) )
05580                )
05581             {
05582                 player->UpdateTeam();
05583                 if ( player->NextTeam() == player->CurrentTeam() )
05584                     assigned = true;
05585             }
05586         }
05587 
05588         // assign players that are not currently on a team, but want to, to any team. Start with the late comers here.
05589         if ( !assigned )
05590         {
05591             for( i=players-1; i>=0; --i )
05592             {
05593                 ePlayerNetID* player = se_PlayerNetIDs(i);
05594 
05595                 // 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
05596                 if ( player->NextTeam() && !player->CurrentTeam() && player->TeamChangeAllowed() )
05597                 {
05598                     eTeam * wish = player->NextTeam();
05599                     bool assignBack = se_assignTeamAutomatically;
05600                     se_assignTeamAutomatically = true;
05601                     player->FindDefaultTeam();
05602                     se_assignTeamAutomatically = assignBack;
05603                     player->SetTeamForce( wish );
05604 
05605                     if ( player->CurrentTeam() )
05606                     {
05607                         assigned = true;
05608                         break;
05609                     }
05610                 }
05611             }
05612         }
05613     }
05614 
05615     if ( sn_GetNetState() != nCLIENT )
05616     {
05617         for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
05618         {
05619             ePlayerNetID* player = se_PlayerNetIDs(i);
05620 
05621             // announce unfullfilled wishes
05622             if ( player->NextTeam() != player->CurrentTeam() && player->NextTeam() )
05623             {
05624                 //if the player can't change teams delete the team wish
05625                 if(!player->TeamChangeAllowed()) {
05626                     player->SetTeam( player->CurrentTeam() );
05627                     continue;
05628                 }
05629 
05630                 tOutput message( "$player_joins_team_wish",
05631                                  player->GetName(),
05632                                  player->NextTeam()->Name() );
05633 
05634                 sn_ConsoleOut( message );
05635 
05636                 // if team change is futile because team play is disabled,
05637                 // delete the team change wish
05638                 if ( eTeam::maxPlayers <= 1 )
05639                     player->SetTeam( player->CurrentTeam() );
05640             }
05641         }
05642     }
05643 
05644     // update the teams as well
05645     for (i=eTeam::teams.Len()-1; i>=0; --i)
05646     {
05647         eTeam::teams(i)->UpdateProperties();
05648     }
05649 
05650     // get rid of deleted netobjects
05651     nNetObject::ClearAllDeleted();
05652 }
05653 
05654 // wait at most this long for any player to leave chat state...
05655 static REAL se_playerWaitMax      = 10.0f;
05656 static tSettingItem< REAL > se_playerWaitMaxConf( "PLAYER_CHAT_WAIT_MAX", se_playerWaitMax );
05657 
05658 // and no more than this much measured relative to his non-chatting time.
05659 static REAL se_playerWaitFraction = .05f;
05660 static tSettingItem< REAL > se_playerWaitFractionConf( "PLAYER_CHAT_WAIT_FRACTION", se_playerWaitFraction );
05661 
05662 // flag to only account one player for the chat break
05663 static bool se_playerWaitSingle = false;
05664 static tSettingItem< bool > se_playerWaitSingleConf( "PLAYER_CHAT_WAIT_SINGLE", se_playerWaitSingle );
05665 
05666 // flag to only let the team leader pause the timer
05667 static bool se_playerWaitTeamleader = true;
05668 static tSettingItem< bool > se_playerWaitTeamleaderConf( "PLAYER_CHAT_WAIT_TEAMLEADER", se_playerWaitTeamleader );
05669 
05670 // wait for players to leave chat state
05671 bool ePlayerNetID::WaitToLeaveChat()
05672 {
05673     static bool lastReturn = false;
05674     static double lastTime = 0;
05675     static ePlayerNetID * lastPlayer = 0; // the last player that caused a pause. Use only for comparison, the pointer may be bad. Don't dereference!
05676     double time = tSysTimeFloat();
05677     REAL dt = time - lastTime;
05678     bool ret = false;
05679 
05680     if ( !lastReturn )
05681     {
05682         // account for non-break pause: give players additional pause time
05683         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
05684         {
05685             ePlayerNetID* player = se_PlayerNetIDs(i);
05686             if ( dt > 1.0 || !player->chatting_ )
05687             {
05688                 player->wait_ += se_playerWaitFraction * dt;
05689                 if ( player->wait_ > se_playerWaitMax )
05690                 {
05691                     player->wait_ = se_playerWaitMax;
05692                 }
05693             }
05694         }
05695 
05696         if ( dt > 1.0 )
05697             lastPlayer = 0;
05698 
05699         dt = 0;
05700     }
05701 
05702     // the chatting player with the most wait seconds left
05703     ePlayerNetID * maxPlayer = 0;
05704     REAL maxWait = .2;
05705 
05706     // iterate over chatting players
05707     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
05708     {
05709         ePlayerNetID* player = se_PlayerNetIDs(i);
05710         if ( player->CurrentTeam() && player->chatting_ && ( !se_playerWaitTeamleader || player->CurrentTeam()->OldestHumanPlayer() == player ) )
05711         {
05712             // account for waiting time if everyone is to get his time reduced
05713             if ( !se_playerWaitSingle )
05714             {
05715                 player->wait_ -= dt;
05716 
05717                 // hold back
05718                 if ( player->wait_ > 0 )
05719                 {
05720                     ret = true;
05721                 }
05722                 else
05723                 {
05724                     player->wait_ = 0;
05725                 }
05726             }
05727 
05728             // determine player we'll wait for longest
05729             if (  ( maxPlayer != lastPlayer || NULL == maxPlayer ) && ( player->wait_ > maxWait || player == lastPlayer ) && player->wait_ > 0 )
05730             {
05731                 maxWait = player->wait_;
05732                 maxPlayer = player;
05733             }
05734         }
05735     }
05736 
05737     // account for waiting time if only one player should get his waiting time reduced
05738     if ( se_playerWaitSingle && maxPlayer )
05739     {
05740         maxPlayer->wait_ -= dt;
05741 
05742         // hold back
05743         if ( maxPlayer->wait_ < 0 )
05744         {
05745             maxPlayer->wait_ = 0;
05746         }
05747     }
05748 
05749     static double lastPrint = -2;
05750 
05751     // print information: who are we waiting for?
05752     if ( maxPlayer && maxPlayer != lastPlayer && tSysTimeFloat() - lastPrint > 1 )
05753     {
05754         sn_ConsoleOut( tOutput( "$gamestate_chat_wait", maxPlayer->GetName(), int(10*maxPlayer->wait_)*.1f ) );
05755         lastPlayer = maxPlayer;
05756     }
05757 
05758     if ( lastPlayer == maxPlayer )
05759     {
05760         lastPrint = tSysTimeFloat();
05761     }
05762 
05763     // store values for future reference
05764     lastReturn = ret;
05765     lastTime = time;
05766 
05767     return ret;
05768 }
05769 
05770 // time in chat mode before a player is no longer spawned
05771 static REAL se_chatterRemoveTime = 180.0;
05772 static tSettingItem< REAL > se_chatterRemoveTimeConf( "CHATTER_REMOVE_TIME", se_chatterRemoveTime );
05773 
05774 // time without keypresses before a player is no longer spawned
05775 static REAL se_idleRemoveTime = 0;
05776 static tSettingItem< REAL > se_idleRemoveTimeConf( "IDLE_REMOVE_TIME", se_idleRemoveTime );
05777 
05778 // time without keypresses before a player is kicked
05779 static REAL se_idleKickTime = 0;
05780 static tSettingItem< REAL > se_idleKickTimeConf( "IDLE_KICK_TIME", se_idleKickTime );
05781 
05782 //removes chatbots and idling players from the game
05783 void ePlayerNetID::RemoveChatbots()
05784 {
05785     // nothing to be done on the clients
05786     if ( nCLIENT == sn_GetNetState() )
05787         return;
05788 
05789 #ifdef KRAWALL_SERVER
05790     // update access level
05791     UpdateAccessLevelRequiredToPlay();
05792 #endif
05793 
05794     // determine the length of the last round
05795     static double lastTime = 0;
05796     double currentTime = tSysTimeFloat();
05797     REAL roundTime = currentTime - lastTime;
05798     lastTime = currentTime;
05799 
05800     // go through all players that don't have a team assigned currently, and assign one
05801     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
05802     {
05803         ePlayerNetID *p = se_PlayerNetIDs(i);
05804         if ( p && p->IsHuman() )
05805         {
05806             // time allowed to be idle
05807             REAL idleTime = p->IsChatting() ? se_chatterRemoveTime : se_idleRemoveTime;
05808 
05809             // determine whether the player should have a team
05810             bool shouldHaveTeam = idleTime <= 0 || p->LastActivity() - roundTime < idleTime;
05811             shouldHaveTeam &= !p->IsSpectating();
05812 
05813             tColoredString name;
05814             name << *p << tColoredString::ColorString(1,.5,.5);
05815 
05816             // see to it that the player has or has not a team.
05817             if ( shouldHaveTeam )
05818             {
05819                 if ( !p->CurrentTeam() )
05820                 {
05821                     p->SetDefaultTeam();
05822                 }
05823             }
05824             else
05825             {
05826                 if ( p->CurrentTeam() )
05827                 {
05828                     p->SetTeam( NULL );
05829                     p->UpdateTeam();
05830                 }
05831             }
05832 
05833             // kick idle players (Removes player from list, this must be the last operation of the loop)
05834             if ( se_idleKickTime > 0 && se_idleKickTime < p->LastActivity() - roundTime )
05835             {
05836                 sn_KickUser( p->Owner(), tOutput( "$network_kill_idle" ) );
05837 
05838                 // if many players get kicked with one client, the array may have changed
05839                 if ( i >= se_PlayerNetIDs.Len() )
05840                     i = se_PlayerNetIDs.Len()-1;
05841             }
05842         }
05843     }
05844 }
05845 
05846 void ePlayerNetID::ThrowOutDisconnected()
05847 {
05848     int i;
05849     // find all disconnected players
05850 
05851     for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
05852         ePlayerNetID *pni=se_PlayerNetIDs(i);
05853         if (pni->disconnected)
05854         {
05855             // remove it from the list of players (so it won't be deleted twice...)
05856             se_PlayerNetIDs.Remove(pni, pni->listID);
05857         }
05858     }
05859 
05860     se_PlayerReferences.ReleaseAll();
05861 }
05862 
05863 void ePlayerNetID::GetScoreFromDisconnectedCopy()
05864 {
05865     int i;
05866     // find a copy
05867     for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
05868         ePlayerNetID *pni=se_PlayerNetIDs(i);
05869         if (pni->disconnected && pni->GetUserName() == GetUserName() && pni->Owner() == 0)
05870         {
05871 #ifdef DEBUG
05872             con << GetName() << " reconnected.\n";
05873 #endif
05874 
05875             pni->RequestSync();
05876             RequestSync();
05877 
05878             score = pni->score;
05879 
05880             ControlObject(pni->Object());
05881             //   object->ePlayer = this;
05882             pni->object = NULL;
05883 
05884             if (bool(object))
05885                 chatting_ = true;
05886 
05887             pni->disconnected = false;
05888             se_PlayerNetIDs.Remove(pni, pni->listID);
05889             se_PlayerReferences.Remove( pni );        // really delete it without a message
05890         }
05891     }
05892 }
05893 
05894 
05895 static bool show_scores=false;
05896 static bool ass=true;
05897 
05898 void se_AutoShowScores(){
05899     if (ass)
05900         show_scores=true;
05901 }
05902 
05903 
05904 void se_UserShowScores(bool show){
05905     show_scores=show;
05906 }
05907 
05908 void se_SetShowScoresAuto(bool a){
05909     ass=a;
05910 }
05911 
05912 
05913 static void scores(){
05914     if (show_scores){
05915         if ( se_mainGameTimer )
05916             ePlayerNetID::DisplayScores();
05917         else
05918             show_scores = false;
05919     }
05920 }
05921 
05922 
05923 static rPerFrameTask pf(&scores);
05924 
05925 static bool force_small_cons(){
05926     return show_scores;
05927 }
05928 
05929 static rSmallConsoleCallback sc(&force_small_cons);
05930 
05931 static void cd(){
05932     show_scores = false;
05933 }
05934 
05935 
05936 
05937 static uActionGlobal score("SCORE");
05938 
05939 
05940 static bool sf(REAL x){
05941     if (x>0) show_scores = !show_scores;
05942     return true;
05943 }
05944 
05945 static uActionGlobalFunc saf(&score,&sf);
05946 
05947 
05948 static rCenterDisplayCallback c_d(&cd);
05949 
05950 tOutput& operator << (tOutput& o, const ePlayerNetID& p)
05951 {
05952     tColoredString x;
05953     x << p;
05954     o << x;
05955     return o;
05956 }
05957 
05958 
05959 
05960 tCallbackString *eCallbackGreeting::anchor = NULL;
05961 ePlayerNetID* eCallbackGreeting::greeted = NULL;
05962 
05963 tString eCallbackGreeting::Greet(ePlayerNetID* player)
05964 {
05965     greeted = player;
05966     return Exec(anchor);
05967 }
05968 
05969 eCallbackGreeting::eCallbackGreeting(STRINGRETFUNC* f)
05970         :tCallbackString(anchor, f)
05971 {
05972 }
05973 
05974 void ePlayerNetID::GreetHighscores(tString &s){
05975     s << eCallbackGreeting::Greet(this);
05976     //  tOutput o;
05977     //  gHighscoresBase::Greet(this,o);
05978     //  s << o;
05979 }
05980 
05981 
05982 // *******************
05983 // *      chatting_   *
05984 // *******************
05985 void ePlayerNetID::SetChatting ( ChatFlags flag, bool chatting )
05986 {
05987     if ( sn_GetNetState() == nSTANDALONE && flag == ChatFlags_Menu )
05988     {
05989         chatting = false;
05990     }
05991 
05992     if ( chatting )
05993     {
05994         chatFlags_ |= flag;
05995         if ( !chatting_ )
05996             this->RequestSync();
05997 
05998         chatting_ = true;
05999     }
06000     else
06001     {
06002         chatFlags_ &= ~flag;
06003         if ( 0 == chatFlags_ )
06004         {
06005             if ( chatting_ )
06006                 this->RequestSync();
06007 
06008             chatting_ = false;
06009         }
06010     }
06011 }
06012 
06013 // *******************
06014 // * team management *
06015 // *******************
06016 
06017 bool ePlayerNetID::TeamChangeAllowed( bool informPlayer ) const {
06018     if (!( allowTeamChange_ || se_allowTeamChanges ))
06019     {
06020         if ( informPlayer )
06021         {
06022             sn_ConsoleOut(tOutput("$player_teamchanges_disallowed"), Owner());
06023         }
06024         return false;
06025     }
06026         
06027     int suspended = GetSuspended();
06028     if ( suspended > 0 )
06029     {
06030         if ( informPlayer )
06031         {
06032             sn_ConsoleOut(tOutput("$player_teamchanges_suspended", suspended ), Owner());
06033         }
06034         return false;
06035     }
06036 
06037 #ifdef KRAWALL_SERVER
06038        // only allow players with enough access level to enter the game, everyone is free to leave, though
06039     if (!( GetAccessLevel() <= AccessLevelRequiredToPlay() || CurrentTeam() ))
06040     {
06041         if ( informPlayer )
06042         {
06043             sn_ConsoleOut(tOutput("$player_teamchanges_accesslevel",
06044                                   tCurrentAccessLevel::GetName( GetAccessLevel() ),
06045                                   tCurrentAccessLevel::GetName( AccessLevelRequiredToPlay() ) ),
06046                           Owner());
06047         }
06048         return false;
06049     }
06050 #endif
06051 
06052     return true;
06053 }
06054 
06055 // put a new player into a default team
06056 void ePlayerNetID::SetDefaultTeam( )
06057 {
06058     // only the server should do this, the client does not have the full information on how to do do it right.
06059     if ( sn_GetNetState() == nCLIENT || !se_assignTeamAutomatically || spectating_ || !TeamChangeAllowed() )
06060         return;
06061 
06062     //    if ( !IsHuman() )
06063     //    {
06064     //        SetTeam( NULL );
06065     //        return;
06066     //    }
06067 
06068     eTeam* defaultTeam = FindDefaultTeam();
06069     if (defaultTeam)
06070         SetTeamWish(defaultTeam);
06071     else if ( eTeam::NewTeamAllowed() )
06072         CreateNewTeamWish();
06073     // yes, if all teams are full and no team creation is allowed, the player stays without team and will not be spawned.
06074     //TODO: Might add AI to null team here.
06075 }
06076 
06077 // put a new player into a default team
06078 eTeam* ePlayerNetID::FindDefaultTeam( )
06079 {
06080     // find the team with the least number of players on it
06081     eTeam *min = NULL;
06082     for ( int i=eTeam::teams.Len()-1; i>=0; --i )
06083     {
06084         eTeam *t = eTeam::teams( i );
06085         if ( t->IsHuman() && ( !min || min->NumHumanPlayers() > t->NumHumanPlayers() ) )
06086             min = t;
06087     }
06088 
06089     if ( min &&
06090             // create new teams until minTeams is matched
06091             eTeam::teams.Len() >= eTeam::minTeams &&
06092             min->PlayerMayJoin( this ) &&
06093             // no new team allowed or not more than favoriteNumberOfPlayers on team
06094             ( !eTeam::NewTeamAllowed() || ( min->NumHumanPlayers() > 0 && min->NumHumanPlayers() < favoriteNumberOfPlayersPerTeam ) )
06095        )
06096     {
06097         // return the team
06098         return min;
06099     }
06100     // return NULL to indicate no team was found => "create a new team" (?)
06101     return NULL;
06102 }
06103 
06104 // register me in the given team (callable on the server)
06105 void ePlayerNetID::SetTeam( eTeam* newTeam )
06106 {
06107     // check if the team change is legal
06108     tASSERT ( !newTeam || nCLIENT !=  sn_GetNetState() );
06109 
06110     SetTeamForce( newTeam );
06111 
06112     if (newTeam && ( !newTeam->PlayerMayJoin( this ) || IsSpectating() ) )
06113     {
06114         tOutput message;
06115         message.SetTemplateParameter( 1, GetName() );
06116         if ( newTeam )
06117             message.SetTemplateParameter(2, newTeam->Name() );
06118         else
06119             message.SetTemplateParameter(2, "NULL");
06120         message << "$player_nojoin_team";
06121 
06122         sn_ConsoleOut( message, Owner() );
06123         // return;
06124     }
06125 }
06126 
06127 // set teamname to be used for my own team
06128 void ePlayerNetID::SetTeamname(const char* newTeamname)
06129 {
06130     teamname = newTeamname;
06131     if (bool(currentTeam) && currentTeam->OldestHumanPlayer() &&
06132             currentTeam->OldestHumanPlayer()->ID()==ID())
06133     {
06134         currentTeam->UpdateAppearance();
06135     }
06136 }
06137 
06138 // register me in the given team (callable on the server)
06139 void ePlayerNetID::SetTeamForce( eTeam* newTeam )
06140 {
06141     // check if the team change is legal
06142     //tASSERT ( !newTeam || nCLIENT !=  sn_GetNetState() );
06143 #ifdef DEBUG
06144     std::cout << "DEBUG: Player:" << this->GetName() << " SetTeamForce:" << ((newTeam)?(const char*)newTeam->Name():"NULL") << "\n";
06145 #endif
06146     nextTeam = newTeam;
06147 }
06148 
06149 // register me in the given team (callable on the server)
06150 void ePlayerNetID::UpdateTeam()
06151 {
06152     // check if work is needed
06153     if ( nextTeam == currentTeam )
06154     {
06155         return;
06156     }
06157 
06158     // check if the team change is legal
06159     if ( nCLIENT ==  sn_GetNetState() )
06160     {
06161         return;
06162     }
06163 
06164     if ( bool( nextTeam ) && !nextTeam->PlayerMayJoin( this ) )
06165     {
06166         tOutput message;
06167         message.SetTemplateParameter(1, GetName() );
06168         if ( nextTeam )
06169             message.SetTemplateParameter(2, nextTeam->Name() );
06170         else
06171             message.SetTemplateParameter(2, "NULL");
06172         message << "$player_nojoin_team";
06173 
06174         sn_ConsoleOut( message, Owner() );
06175         return;
06176     }
06177 
06178     UpdateTeamForce();
06179 }
06180 
06181 void ePlayerNetID::UpdateTeamForce()
06182 {
06183     // check if work is needed
06184     if ( nextTeam == currentTeam )
06185     {
06186         return;
06187     }
06188 
06189     eTeam *oldTeam = currentTeam;
06190 
06191     if ( nextTeam )
06192         nextTeam->AddPlayer ( this );
06193     else if ( oldTeam )
06194         oldTeam->RemovePlayer( this );
06195 
06196     if( nCLIENT !=  sn_GetNetState() && GetRefcount() > 0 )
06197     {
06198         RequestSync();
06199     }
06200 }
06201 
06202 // teams this player is invited to
06203 ePlayerNetID::eTeamSet const & ePlayerNetID::GetInvitations() const
06204 {
06205     return invitations_;
06206 }
06207 
06208 // create a new team and join it (on the server)
06209 void ePlayerNetID::CreateNewTeam()
06210 {
06211     // check if the team change is legal
06212     tASSERT ( nCLIENT !=  sn_GetNetState() );
06213 
06214     if(!TeamChangeAllowed( true )) {
06215         return;
06216     }
06217 
06218     if ( !eTeam::NewTeamAllowed() ||
06219             ( bool( currentTeam ) && ( currentTeam->NumHumanPlayers() == 1 ) ) ||
06220             IsSpectating() )
06221     {
06222         tOutput message;
06223         message.SetTemplateParameter(1, GetName() );
06224         message << "$player_nocreate_team";
06225 
06226         sn_ConsoleOut( message, Owner() );
06227 
06228         // yarrt: Should CreateNewTeam only try to create a team ?
06229         // yarrt: yes, let's try
06230         // if it can't create a team => just idle?
06231         //Old code
06232         //if ( !currentTeam )
06233         //    SetDefaultTeam();
06234 
06235         return;
06236     }
06237 
06238     // create the new team and join it
06239     tJUST_CONTROLLED_PTR< eTeam > newTeam = tNEW( eTeam );
06240     nextTeam = newTeam;
06241     nextTeam->AddScore( score );
06242 
06243     // directly if possible
06244     if ( !currentTeam )
06245     {
06246         UpdateTeam();
06247     }
06248 }
06249 
06250 const unsigned short TEAMCHANGE = 0;
06251 const unsigned short NEW_TEAM   = 1;
06252 
06253 
06254 // express the wish to be part of the given team (always callable)
06255 void ePlayerNetID::SetTeamWish(eTeam* newTeam)
06256 {
06257     if (NextTeam()==newTeam)
06258         return;
06259 
06260     if ( nSERVER ==  sn_GetNetState() && newTeam==NULL && CurrentTeam()!=NULL && NextTeam()!=NULL)
06261     {
06262         if ( se_assignTeamAutomatically )
06263         {
06264             sn_ConsoleOut("$no_spectators_allowed", Owner() );
06265             return;
06266         }
06267 
06268         eTeam * leftTeam = NextTeam();
06269         if ( leftTeam )
06270         {
06271             if ( !leftTeam )
06272                 leftTeam = CurrentTeam();
06273 
06274             if ( leftTeam->NumPlayers() > 1 )
06275             {
06276                 sn_ConsoleOut( tOutput( "$player_leave_team_wish",
06277                                         tColoredString::RemoveColors(GetName()),
06278                                         tColoredString::RemoveColors(leftTeam->Name()) ) );
06279             }
06280             else
06281             {
06282                 sn_ConsoleOut( tOutput( "$player_leave_game_wish",
06283                                         tColoredString::RemoveColors(GetName()) ) );
06284             }
06285         }
06286     }
06287     if ( nCLIENT ==  sn_GetNetState() && Owner() == sn_myNetID )
06288     {
06289         nMessage* m = NewControlMessage();
06290 
06291         (*m) << TEAMCHANGE;
06292         (*m) << newTeam;
06293 
06294         m->BroadCast();
06295         // update local client data, should be ok (dangerous?, why?)
06296         SetTeamForce( newTeam );
06297     }
06298     else
06299     {
06300         SetTeam( newTeam );
06301 
06302         // directly join if possible to keep counts up to date
06303         if (!currentTeam)
06304             UpdateTeam();
06305     }
06306 }
06307 
06308 // express the wish to create a new team and join it
06309 void ePlayerNetID::CreateNewTeamWish()
06310 {
06311     if ( nCLIENT ==  sn_GetNetState() )
06312     {
06313         nMessage* m = NewControlMessage();
06314 
06315         (*m) << NEW_TEAM;
06316 
06317         m->BroadCast();
06318     }
06319     else
06320         // create a new team if possible otherwise spectate
06321         CreateNewTeam();
06322 }
06323 
06324 // receive the team control wish
06325 void ePlayerNetID::ReceiveControlNet(nMessage &m)
06326 {
06327     short messageType;
06328     m >> messageType;
06329 
06330     switch (messageType)
06331     {
06332     case NEW_TEAM:
06333         {
06334             // create a new team if possible otherwise spectate or...
06335             CreateNewTeam();
06336             // if auto_team is enabled join the default team
06337             if (se_assignTeamAutomatically && NextTeam()==NULL)
06338                 SetDefaultTeam();
06339 
06340             break;
06341         }
06342     case TEAMCHANGE:
06343         {
06344             eTeam *newTeam;
06345 
06346             m >> newTeam;
06347 
06348             if(!TeamChangeAllowed( true )) {
06349                 break;
06350             }
06351 
06352             // annihilate team if it no longer is in the game
06353             if ( bool(newTeam) && newTeam->TeamID() < 0 )
06354                 newTeam = 0;
06355 
06356             // NULL team probably means that the change target does not
06357             // exist any more. Create a new team instead.
06358             if ( !newTeam )
06359             {
06360                 if ( currentTeam )
06361                     sn_ConsoleOut( tOutput( "$player_joins_team_noex" ), Owner() );
06362                 break;
06363             }
06364 
06365             // check if the resulting message is obnoxious
06366             bool redundant = ( nextTeam == newTeam );
06367             bool obnoxious = ( nextTeam != currentTeam || redundant );
06368 
06369             SetTeam( newTeam );
06370 
06371             // announce the change
06372             if ( bool(nextTeam) && !redundant )
06373             {
06374                 tOutput message;
06375                 message.SetTemplateParameter(1, tColoredString::RemoveColors(GetName()));
06376                 message.SetTemplateParameter(2, tColoredString::RemoveColors(nextTeam->Name()) );
06377                 message << "$player_joins_team";
06378 
06379                 sn_ConsoleOut( message );
06380 
06381                 // count it as spam if it is obnoxious
06382                 if ( obnoxious )
06383                     chatSpam_.CheckSpam( 4.0, Owner(), tOutput("$spam_teamchage") );
06384             }
06385 
06386             break;
06387         }
06388     default:
06389         {
06390             tASSERT(0);
06391         }
06392     }
06393 }
06394 
06395 void ePlayerNetID::Color( REAL&a_r, REAL&a_g, REAL&a_b ) const
06396 {
06397     if ( ( static_cast<bool>(currentTeam) ) && ( currentTeam->IsHuman() ) )
06398     {
06399         REAL w = 5;
06400         REAL r_w = 2;
06401         REAL g_w = 1;
06402         REAL b_w = 2;
06403 
06404         int r = this->r;
06405         int g = this->g;
06406         int b = this->b;
06407 
06408         // don't tolerate color overflow in a real team
06409         if ( currentTeam->NumPlayers() > 1 )
06410         {
06411             if ( r > 15 )
06412                 r = 15;
06413             if ( g > 15 )
06414                 g = 15;
06415             if ( b > 15 )
06416                 b = 15;
06417         }
06418 
06419         a_r=(r_w*r + w*currentTeam->R())/( 15.0 * ( w + r_w ) );
06420         a_g=(g_w*g + w*currentTeam->G())/( 15.0 * ( w + g_w ) );
06421         a_b=(b_w*b + w*currentTeam->B())/( 15.0 * ( w + b_w ) );
06422     }
06423     else
06424     {
06425         a_r = r/15.0;
06426         a_g = g/15.0;
06427         a_b = b/15.0;
06428     }
06429 }
06430 
06431 void ePlayerNetID::TrailColor( REAL&a_r, REAL&a_g, REAL&a_b ) const
06432 {
06433     Color( a_r, a_g, a_b );
06434 
06435     /*
06436     if ( ( static_cast<bool>(currentTeam) ) && ( currentTeam->IsHuman() ) )
06437     {
06438         int w = 6;
06439         a_r=(2*r + w*currentTeam->R())/( 15.0 * ( w + 2 ) );
06440         a_g=(2*g + w*currentTeam->G())/( 15.0 * ( w + 2 ) );
06441         a_b=(2*b + w*currentTeam->B())/( 15.0 * ( w + 2 ) );
06442     }
06443     else
06444     {
06445         a_r = r/15.0;
06446         a_g = g/15.0;
06447         a_b = b/15.0;
06448     }
06449     */
06450 }
06451 
06452 /*
06453 void ePlayerNetID::AddRef()
06454 {
06455         nNetObject::AddRef();
06456 }
06457 
06458 void ePlayerNetID::Release()
06459 {
06460         nNetObject::Release();
06461 }
06462 */
06463 
06464 // reads a network ID from the stream, either the number or my user name
06465 static unsigned short se_ReadUser( std::istream &s, ePlayerNetID * requester = 0 )
06466 {
06467     // read name of player to be kicked
06468     tString name;
06469     s >> name;
06470 
06471     // try to convert it into a number and reference it as that
06472     int num;
06473     if ( name.Convert(num) && num >= 1 && num <= MAXCLIENTS && sn_Connections[num].socket )
06474     {
06475         return num;
06476     }
06477     else
06478     {
06479         // standard name lookup
06480         ePlayerNetID * p = ePlayerNetID::FindPlayerByName( name, requester );
06481         if ( p )
06482         {
06483             return p->Owner();
06484         }
06485     }
06486 
06487     return 0;
06488 }
06489 
06490 
06491 // call on commands that only work on the server; quit if it returns true
06492 static bool se_NeedsServer(char const * command, std::istream & s, bool strict = true )
06493 {
06494     if ( sn_GetNetState() != nSERVER && ( strict || sn_GetNetState() != nSTANDALONE ) )
06495     {
06496         tString rest;
06497         rest.ReadLine( s );
06498         con << tOutput("$only_works_on_server", command, rest );
06499         return true;
06500     }
06501 
06502     return false;
06503 }
06504 
06505 static void se_PlayerMessageConf(std::istream &s)
06506 {
06507     if ( se_NeedsServer( "PLAYER_MESSAGE", s ) )
06508     {
06509         return;
06510     }
06511 
06512     int receiver = se_ReadUser( s );
06513 
06514     tColoredString msg;
06515     msg.ReadLine(s);
06516 
06517     if ( receiver <= 0 || s.good() )
06518     {
06519         con << "Usage: PLAYER_MESSAGE <user ID or name> <Message>\n";
06520         return;
06521     }
06522 
06523     msg << '\n';
06524 
06525     sn_ConsoleOut(msg, 0);
06526     sn_ConsoleOut(msg, receiver);
06527 }
06528 
06529 static tConfItemFunc se_PlayerMessage_c("PLAYER_MESSAGE", &se_PlayerMessageConf);
06530 static tAccessLevelSetter se_messConfLevel( se_PlayerMessage_c, tAccessLevel_Moderator );
06531 
06532 static tString se_defaultKickReason("");
06533 static tConfItemLine se_defaultKickReasonConf( "DEFAULT_KICK_REASON", se_defaultKickReason );
06534 
06535 static void se_KickConf(std::istream &s)
06536 {
06537     if ( se_NeedsServer( "KICK", s ) )
06538     {
06539         return;
06540     }
06541 
06542     // get user ID
06543     int num = se_ReadUser( s );
06544 
06545     tString reason = se_defaultKickReason;
06546     if ( !s.eof() )
06547         reason.ReadLine(s);
06548 
06549     // and kick.
06550     if ( num > 0 && !s.good() )
06551     {
06552         sn_KickUser( num ,  reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick" );
06553     }
06554     else
06555     {
06556         con << "Usage: KICK <user ID or name> <Reason>\n";
06557         return;
06558     }
06559 }
06560 
06561 static tConfItemFunc se_kickConf("KICK",&se_KickConf);
06562 static tAccessLevelSetter se_kickConfLevel( se_kickConf, tAccessLevel_Moderator );
06563 
06564 static tString se_defaultKickToServer("");
06565 static int se_defaultKickToPort = 4534;
06566 static tString se_defaultKickToReason("");
06567 
06568 static tSettingItem< tString > se_defaultKickToServerConf( "DEFAULT_KICK_TO_SERVER", se_defaultKickToServer );
06569 static tSettingItem< int > se_defaultKickToPortConf( "DEFAULT_KICK_TO_PORT", se_defaultKickToPort );
06570 static tConfItemLine se_defaultKickToReasonConf( "DEFAULT_KICK_TO_REASON", se_defaultKickToReason );
06571 
06572 static void se_MoveToConf(std::istream &s, REAL severity )
06573 {
06574     if ( se_NeedsServer( "KICK/MOVE_TO", s ) )
06575     {
06576         return;
06577     }
06578 
06579     // get user ID
06580     int num = se_ReadUser( s );
06581 
06582     // read redirection target
06583     tString server = se_defaultKickToServer;
06584     if ( !s.eof() )
06585     {
06586         s >> server;
06587     }
06588 
06589     int port = se_defaultKickToPort;
06590     if ( !s.eof() )
06591         s >> port;
06592 
06593     nServerInfoRedirect redirect( server, port );
06594 
06595     tString reason = se_defaultKickToReason;
06596     if ( !s.eof() )
06597         reason.ReadLine(s);
06598 
06599     // and kick.
06600     if ( num > 0 && !s.good() )
06601     {
06602         sn_KickUser( num ,  reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick", severity, &redirect );
06603     }
06604     else
06605     {
06606         con << "Usage: KICK_TO <user ID or name> <server IP to kick to>:<server port to kick to> <Reason>\n";
06607         return;
06608     }
06609 }
06610 
06611 static void se_KickToConf(std::istream &s )
06612 {
06613     se_MoveToConf( s, 1 );
06614 }
06615 
06616 static tConfItemFunc se_kickToConf("KICK_TO",&se_KickToConf);
06617 static tAccessLevelSetter se_kickToConfLevel( se_kickToConf, tAccessLevel_Moderator );
06618 
06619 static void se_MoveToConf(std::istream &s )
06620 {
06621     se_MoveToConf( s, 0 );
06622 }
06623 
06624 static tConfItemFunc se_moveToConf("MOVE_TO",&se_MoveToConf);
06625 static tAccessLevelSetter se_moveConfLevel( se_moveToConf, tAccessLevel_Moderator );
06626 
06627 static void se_BanConf(std::istream &s)
06628 {
06629     if ( se_NeedsServer( "BAN", s ) )
06630     {
06631         return;
06632     }
06633 
06634     // get user ID
06635     int num = se_ReadUser( s );
06636 
06637     if ( num == 0 && !s.good() )
06638     {
06639         con << "Usage: BAN <user ID or name> <time in minutes(defaults to 60)> <Reason>\n";
06640         return;
06641     }
06642 
06643     // read time to ban
06644     REAL banTime = 60;
06645     s >> banTime;
06646     std::ws(s);
06647 
06648     tString reason;
06649     reason.ReadLine(s);
06650 
06651     // and ban.
06652     if ( num > 0 )
06653     {
06654         nMachine::GetMachine( num ).Ban( banTime * 60, reason );
06655         sn_DisconnectUser( num , reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick" );
06656     }
06657 }
06658 
06659 static tConfItemFunc se_banConf("BAN",&se_BanConf);
06660 static tAccessLevelSetter se_banConfLevel( se_banConf, tAccessLevel_Moderator );
06661 
06662 static ePlayerNetID * ReadPlayer( std::istream & s )
06663 {
06664     // read name of player to be returned
06665     tString name;
06666     s >> name;
06667 
06668     int num = name.ToInt();
06669     if ( num > 0 )
06670     {
06671         // look for a player from that client
06672         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
06673         {
06674             ePlayerNetID* p = se_PlayerNetIDs(i);
06675 
06676             // check whether it's p who should be returned
06677             if ( p->Owner() == num )
06678             {
06679                 return p;
06680             }
06681         }
06682     }
06683 
06684     return ePlayerNetID::FindPlayerByName( name );
06685 }
06686 
06687 static void Kill_conf(std::istream &s)
06688 {
06689     if ( se_NeedsServer( "KILL", s, false ) )
06690     {
06691         return;
06692     }
06693 
06694     ePlayerNetID * p = ReadPlayer( s );
06695     if ( p && p->Object() )
06696     {
06697         p->Object()->Kill();
06698         sn_ConsoleOut( tOutput( "$player_admin_kill", p->GetColoredName() ) );
06699     }
06700 }
06701 
06702 static tConfItemFunc kill_conf("KILL",&Kill_conf);
06703 static tAccessLevelSetter se_killConfLevel( kill_conf, tAccessLevel_Moderator );
06704 
06705 static void Suspend_conf_base(std::istream &s, int rounds )
06706 {
06707     if ( se_NeedsServer( "SUSPEND", s, false ) )
06708     {
06709         return;
06710     }
06711 
06712     ePlayerNetID * p = ReadPlayer( s );
06713 
06714     if ( rounds > 0 )
06715     {
06716         s >> rounds;
06717     }
06718         
06719     if ( p )
06720     {
06721         p->Suspend( rounds );
06722     }
06723 }
06724 
06725 static void Suspend_conf(std::istream &s )
06726 {
06727     Suspend_conf_base( s, 5 );
06728 }
06729 
06730 
06731 static void UnSuspend_conf(std::istream &s )
06732 {
06733     Suspend_conf_base( s, 0 );
06734 }
06735 
06736 static tConfItemFunc suspend_conf("SUSPEND",&Suspend_conf);
06737 static tAccessLevelSetter se_suspendConfLevel( suspend_conf, tAccessLevel_Moderator );
06738 
06739 static tConfItemFunc unsuspend_conf("UNSUSPEND",&UnSuspend_conf);
06740 static tAccessLevelSetter se_unsuspendConfLevel( unsuspend_conf, tAccessLevel_Moderator );
06741 
06742 static void Silence_conf(std::istream &s)
06743 {
06744     if ( se_NeedsServer( "SILENCE", s ) )
06745     {
06746         return;
06747     }
06748 
06749     ePlayerNetID * p = ReadPlayer( s );
06750     if ( p )
06751     {
06752         sn_ConsoleOut( tOutput( "$player_silenced", p->GetName() ) );
06753         p->SetSilenced( true );
06754     }
06755 }
06756 
06757 static tConfItemFunc silence_conf("SILENCE",&Silence_conf);
06758 static tAccessLevelSetter se_silenceConfLevel( silence_conf, tAccessLevel_Moderator );
06759 
06760 static void Voice_conf(std::istream &s)
06761 {
06762     if ( se_NeedsServer( "VOICE", s ) )
06763     {
06764         return;
06765     }
06766 
06767     ePlayerNetID * p = ReadPlayer( s );
06768     if ( p )
06769     {
06770         sn_ConsoleOut( tOutput( "$player_voiced", p->GetName() ) );
06771         p->SetSilenced( false );
06772     }
06773 }
06774 
06775 static tConfItemFunc voice_conf("VOICE",&Voice_conf);
06776 static tAccessLevelSetter se_voiceConfLevel( voice_conf, tAccessLevel_Moderator );
06777 
06778 static tString sg_url;
06779 static tSettingItem< tString > sg_urlConf( "URL", sg_url );
06780 
06781 static tString sg_options("Nothing special.");
06782 #ifdef DEDICATED
06783 static tConfItemLine sg_optionsConf( "SERVER_OPTIONS", sg_options );
06784 #endif
06785 
06786 class gServerInfoAdmin: public nServerInfoAdmin
06787 {
06788 public:
06789     gServerInfoAdmin(){};
06790 
06791 private:
06792     virtual tString GetUsers()          const
06793     {
06794         tString ret;
06795 
06796         for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
06797     {
06798         ePlayerNetID* p = se_PlayerNetIDs(i);
06799             if ( p->IsHuman() )
06800             {
06801                 ret << p->GetName() << "\n";
06802             }
06803         }
06804 
06805         return ret;
06806     }
06807 
06808     virtual tString     GetOptions()    const
06809     {
06810         se_CutString( sg_options, 240 );
06811         return sg_options;
06812     }
06813 
06814     virtual tString GetUrl()            const
06815     {
06816         se_CutString( sg_url, 75 );
06817         return sg_url;
06818     }
06819 };
06820 
06821 static gServerInfoAdmin sg_serverAdmin;
06822 
06823 class eNameMessenger
06824 {
06825 public:
06826     eNameMessenger( ePlayerNetID & player )
06827     : player_( player )
06828     , oldScreenName_( player.GetName() )
06829     , oldLogName_( player.GetLogName() )
06830     {
06831         // store old name for password re-request and name change message
06832         oldPrintName_ << player_ << tColoredString::ColorString(.5,1,.5);
06833     }
06834 
06835     ~eNameMessenger()
06836     {
06837         if ( sn_GetNetState() == nCLIENT )
06838         {
06839             return;
06840         }
06841 
06842         tString logName = player_.GetLogName();
06843         tString const & screenName = player_.GetName();
06844 
06845         // messages for the users
06846         tColoredString printName;
06847         printName << player_ << tColoredString::ColorString(.5,1,.5);
06848 
06849         tOutput mess;
06850         
06851         mess.SetTemplateParameter(1, printName);
06852         mess.SetTemplateParameter(2, oldPrintName_);
06853         
06854         // is the player new?
06855         if ( oldLogName_.Len() <= 1 && logName.Len() > 0 )
06856         {
06857             if ( player_.IsHuman() )
06858             {
06859                 tString ladder;
06860                 ladder << "PLAYER_ENTERED " << logName << " " << nMachine::GetMachine(player_.Owner()).GetIP() << " " << screenName << "\n";
06861                 se_SaveToLadderLog(ladder);
06862                 
06863                 player_.Greet();
06864 
06865                 // print spectating join message (regular join messages are handled by eTeam)
06866                 if ( player_.IsSpectating() || !se_assignTeamAutomatically )
06867                 {
06868                     mess << "$player_entered_spectator";
06869                     sn_ConsoleOut(mess);
06870                 }
06871             }
06872         }
06873         else if ( logName != oldLogName_ || screenName != oldScreenName_ )
06874         {
06875             tString ladder;
06876             ladder << "PLAYER_RENAMED " << oldLogName_  << " "  << logName << " " << nMachine::GetMachine(player_.Owner()).GetIP() << " " << screenName << "\n";
06877             se_SaveToLadderLog(ladder);
06878 
06879             if ( oldScreenName_ != screenName )
06880             {
06881                 if ( bool(player_.GetVoter() ) )
06882                 {
06883                     player_.GetVoter()->PlayerChanged();
06884                 }
06885 
06886                 mess << "$player_renamed";
06887 
06888                 sn_ConsoleOut(mess);
06889             }
06890         }
06891     }
06892 
06893     ePlayerNetID & player_;
06894     tString oldScreenName_, oldLogName_;
06895     tColoredString oldPrintName_;
06896 };
06897 
06898 // ******************************************************************************************
06899 // *
06900 // *    UpdateName
06901 // *
06902 // ******************************************************************************************
06905 // ******************************************************************************************
06906 
06907 void ePlayerNetID::UpdateName( void )
06908 {
06909     // don't do a thing if we're not fully synced
06910     if ( this->ID() == 0 && nameFromClient_.Len() <= 1 && sn_GetNetState() == nSERVER )
06911         return;
06912     
06913     // monitor name changes
06914     eNameMessenger messenger( *this );
06915 
06916     // apply client change, stripping excess spaces
06917     if ( sn_GetNetState() != nCLIENT )
06918     {
06919         // apply name filters only on remote players
06920         if ( Owner() != 0 )
06921             se_OptionalNameFilters( nameFromClient_ );
06922 
06923         // disallow name changes if there was a kick vote recently
06924         if ( !bool(this->voter_) || voter_->AllowNameChange() || nameFromServer_.Len() <= 1 )
06925         {
06926             nameFromServer_ = nameFromClient_;
06927         }
06928         else if ( nameFromServer_ != nameFromClient_ )
06929         {
06930             // inform victim to avoid complaints
06931             tOutput message( "$player_rename_rejected", nameFromServer_, nameFromClient_ );
06932             sn_ConsoleOut( message, Owner() );
06933             con << message;
06934 
06935             // revert name
06936             nameFromClient_ = nameFromServer_;
06937         }
06938     }
06939 
06940     // remove colors from name
06941     tString newName = tColoredString::RemoveColors( nameFromServer_ );
06942     tString newUserName = se_UnauthenticatedUserName( newName );
06943 
06944     // test if it is already taken, find an alternative name if so.
06945     if ( sn_GetNetState() != nCLIENT && !se_allowImposters && se_IsNameTaken( newUserName, this ) )
06946     {
06947         // Remove possilble trailing digit.
06948         if ( newName.Len() > 2 && isdigit(newName(newName.Len()-2)) )
06949         {
06950             newName = newName.SubStr( 0, newName.Len()-2 );
06951         }
06952 
06953         // append numbers until the name is free
06954         for ( int i=2; i<1000; ++i )
06955         {
06956             tString testName(newName);
06957             testName << i;
06958 
06959             // false the beginning if the name is too long
06960             if ( testName.Len() > 17 )
06961                 testName = testName.SubStr( testName.Len() - 17 );
06962 
06963             newUserName = se_UnauthenticatedUserName( testName );
06964 
06965             if ( !se_IsNameTaken( newUserName, this ) )
06966             {
06967                 newName = testName;
06968                 break;
06969             }
06970         }
06971 
06972         // rudely overwrite name from client
06973         nameFromServer_ = newName;
06974     }
06975 
06976     // set the colored name to the name from the client, set trailing color to white
06977     coloredName_.Clear();
06978     REAL r,g,b;
06979     Color(r,g,b);
06980     coloredName_ << tColoredString::ColorString(r,g,b) << nameFromServer_;
06981 
06982     if ( name_ != newName || lastAccessLevel != GetAccessLevel() )
06983     {
06984         // copy it to the name, removing colors of course
06985         name_ = newName;
06986 
06987         // remove spaces and possibly other nasties for the user name
06988         userName_ = se_UnauthenticatedUserName( name_ );
06989 
06990         if (sn_GetNetState()!=nCLIENT)
06991         {
06992             // sync the new name
06993             RequestSync();
06994         }
06995     }
06996 
06997     // store access level for next update
06998     lastAccessLevel = GetAccessLevel();
06999 
07000 #ifdef KRAWALL_SERVER
07001     // take the user name to be the authenticated name
07002     if ( IsAuthenticated() )
07003     {
07004         userName_ = GetFilteredAuthenticatedName();
07005         if ( se_legacyLogNames )
07006         {
07007             userName_ = tString( "0:" ) + userName_;
07008         }
07009     }
07010     else
07011     {
07012         rawAuthenticatedName_ = "";
07013     }
07014 #endif
07015 }
07016 
07017 // filters illegal player characters
07018 class ePlayerCharacterFilter
07019 {
07020 public:
07021     ePlayerCharacterFilter()
07022     {
07023         int i;
07024         filter[0]=0;
07025 
07026         // map all unknown characters to underscores
07027         for (i=255; i>0; --i)
07028         {
07029             filter[i] = '_';
07030         }
07031 
07032         // leave ASCII characters as they are
07033         for (i=126; i>32; --i)
07034         {
07035             filter[i] = i;
07036         }
07037         // but convert uppercase characters to lowercase
07038         for (i='Z'; i>='A'; --i)
07039         {
07040             filter[i] = i + ('a' - 'A');
07041         }
07042 
07044         SetMap(0xc0,0xc5,'a');
07045         SetMap(0xd1,0xd6,'o');
07046         SetMap(0xd9,0xdD,'u');
07047         SetMap(0xdf,'s');
07048         SetMap(0xe0,0xe5,'a');
07049         SetMap(0xe8,0xeb,'e');
07050         SetMap(0xec,0xef,'i');
07051         SetMap(0xf0,0xf6,'o');
07052         SetMap(0xf9,0xfc,'u');
07053 
07054         // ok, some of those are a bit questionable, but still better than _...
07055         SetMap(161,'!');
07056         SetMap(162,'c');
07057         SetMap(163,'l');
07058         SetMap(165,'y');
07059         SetMap(166,'|');
07060         SetMap(167,'s');
07061         SetMap(168,'"');
07062         SetMap(169,'c');
07063         SetMap(170,'a');
07064         SetMap(171,'"');
07065         SetMap(172,'!');
07066         SetMap(174,'r');
07067         SetMap(176,'o');
07068         SetMap(177,'+');
07069         SetMap(178,'2');
07070         SetMap(179,'3');
07071         SetMap(182,'p');
07072         SetMap(183,'.');
07073         SetMap(185,'1');
07074         SetMap(187,'"');
07075         SetMap(198,'a');
07076         SetMap(199,'c');
07077         SetMap(208,'d');
07078         SetMap(209,'n');
07079         SetMap(215,'x');
07080         SetMap(216,'o');
07081         SetMap(221,'y');
07082         SetMap(222,'p');
07083         SetMap(231,'c');
07084         SetMap(241,'n');
07085         SetMap(247,'/');
07086         SetMap(248,'o');
07087         SetMap(253,'y');
07088         SetMap(254,'p');
07089         SetMap(255,'y');
07090 
07091         //map 0 to o because they look similar
07092         SetMap('0','o');
07093 
07094         // TODO: make this data driven.
07095     }
07096 
07097     char Filter( unsigned char in )
07098     {
07099         return filter[ static_cast< unsigned int >( in )];
07100     }
07101 private:
07102     void SetMap( int in1, int in2, unsigned char out)
07103     {
07104         tASSERT( in2 <= 0xff );
07105         tASSERT( 0 <= in1 );
07106         tASSERT( in1 < in2 );
07107         for( int i = in2; i >= in1; --i )
07108             filter[ i ] = out;
07109     }
07110 
07111     void SetMap( unsigned char in, unsigned char out)
07112     {
07113         filter[ static_cast< unsigned int >( in ) ] = out;
07114     }
07115 
07116     char filter[256];
07117 };
07118 
07119 static bool se_IsUnderscore( char c )
07120 {
07121     return c == '_';
07122 }
07123 
07124 // ******************************************************************************************
07125 // *
07126 // *    FilterName
07127 // *
07128 // ******************************************************************************************
07133 // ******************************************************************************************
07134 
07135 void ePlayerNetID::FilterName( tString const & in, tString & out )
07136 {
07137     int i;
07138     static ePlayerCharacterFilter filter;
07139     out = tColoredString::RemoveColors( in );
07140     
07141     // filter out illegal characters
07142     for ( i = out.Size()-1; i>=0; --i )
07143     {
07144         char & c = out[i];
07145 
07146         c = filter.Filter( c );
07147     }
07148 
07149     // strip leading and trailing unknown characters
07150     se_StripMatchingEnds( out, se_IsUnderscore, se_IsUnderscore );
07151 }
07152 
07153 // ******************************************************************************************
07154 // *
07155 // *    FilterName
07156 // *
07157 // ******************************************************************************************
07162 // ******************************************************************************************
07163 
07164 tString ePlayerNetID::FilterName( tString const & in )
07165 {
07166     tString out;
07167     FilterName( in, out );
07168     return out;
07169 }
07170 
07171 // ******************************************************************************************
07172 // *
07173 // * SetName
07174 // *
07175 // ******************************************************************************************
07180 // ******************************************************************************************
07181 
07182 ePlayerNetID & ePlayerNetID::SetName( tString const & name )
07183 {
07184     this->nameFromClient_ = name;
07185     this->nameFromClient_.NetFilter();
07186 
07187     // replace empty name
07188     if ( !IsLegalPlayerName( nameFromClient_ ) )
07189         nameFromClient_ = "Player 1";
07190 
07191     if ( sn_GetNetState() != nCLIENT )
07192         nameFromServer_ = nameFromClient_;
07193 
07194     UpdateName();
07195 
07196     return *this;
07197 }
07198 
07199 // ******************************************************************************************
07200 // *
07201 // * SetName
07202 // *
07203 // ******************************************************************************************
07208 // ******************************************************************************************
07209 
07210 ePlayerNetID & ePlayerNetID::SetName( char const * name )
07211 {
07212     SetName( tString( name ) );
07213     return *this;
07214 }
07215 
07216 // ******************************************************************************************
07217 // *
07218 // * GetFilteredAuthenticatedName
07219 // *
07220 // ******************************************************************************************
07224 // ******************************************************************************************
07225 
07226 tString ePlayerNetID::GetFilteredAuthenticatedName( void ) const
07227 {
07228 #ifdef KRAWALL_SERVER
07229     return tString( se_EscapeName( GetRawAuthenticatedName() ).c_str() );
07230 #else
07231     return tString("");
07232 #endif
07233 }
07234 
07235 // allow enemies from the same IP?
07236 static bool se_allowEnemiesSameIP = false;
07237 static tSettingItem< bool > se_allowEnemiesSameIPConf( "ALLOW_ENEMIES_SAME_IP", se_allowEnemiesSameIP );
07238 // allow enemies from the same client?
07239 static bool se_allowEnemiesSameClient = false;
07240 static tSettingItem< bool > se_allowEnemiesSameClientConf( "ALLOW_ENEMIES_SAME_CLIENT", se_allowEnemiesSameClient );
07241 
07242 // *******************************************************************************
07243 // *
07244 // *    Enemies
07245 // *
07246 // *******************************************************************************
07252 // *******************************************************************************
07253 
07254 bool ePlayerNetID::Enemies( ePlayerNetID const * a, ePlayerNetID const * b )
07255 {
07256     // the client does not need a true answer
07257     if ( sn_GetNetState() == nCLIENT )
07258         return true;
07259 
07260     // no scoring if one of them does not exist
07261     if ( !a || !b )
07262         return false;
07263 
07264     // no scoring for two players from the same IP
07265     if ( !se_allowEnemiesSameIP && a->Owner() != 0 && a->GetMachine() == b->GetMachine() )
07266         return false;
07267 
07268     // no scoring for two players from the same client
07269     if ( !se_allowEnemiesSameClient && a->Owner() != 0 && a->Owner() == b->Owner() )
07270         return false;
07271 
07272     // no objections
07273     return true;
07274 }
07275 
07276 // *******************************************************************************
07277 // *
07278 // *    RegisterWithMachine
07279 // *
07280 // *******************************************************************************
07283 // *******************************************************************************
07284 
07285 void ePlayerNetID::RegisterWithMachine( void )
07286 {
07287     if ( !registeredMachine_ )
07288     {
07289         // store machine (it won't get deleted while this object exists; the player count prevents that)
07290         registeredMachine_ = &this->nNetObject::DoGetMachine();
07291         registeredMachine_->AddPlayer();
07292     }
07293 }
07294 
07295 // *******************************************************************************
07296 // *
07297 // *    UnregisterWithMachine
07298 // *
07299 // *******************************************************************************
07302 // *******************************************************************************
07303 
07304 void ePlayerNetID::UnregisterWithMachine( void )
07305 {
07306     if ( registeredMachine_ )
07307     {
07308         // store suspension count
07309         if ( GetVoter() )
07310         {
07311             GetVoter()->suspended_ = suspended_;
07312         }
07313 
07314         registeredMachine_->RemovePlayer();
07315         registeredMachine_ = 0;
07316     }
07317 }
07318 
07319 // *******************************************************************************
07320 // *
07321 // *    DoGetMachine
07322 // *
07323 // *******************************************************************************
07327 // *******************************************************************************
07328 
07329 nMachine & ePlayerNetID::DoGetMachine( void ) const
07330 {
07331     // return machine I'm registered at, otherwise whatever the base class thinks
07332     if ( registeredMachine_ )
07333         return *registeredMachine_;
07334     else
07335         return nNetObject::DoGetMachine();
07336 }
07337 
07338 // *******************************************************************************
07339 // *
07340 // *    LastActivity
07341 // *
07342 // *******************************************************************************
07346 // *******************************************************************************
07347 
07348 REAL ePlayerNetID::LastActivity( void ) const
07349 {
07350     return tSysTimeFloat() - lastActivity_;
07351 }
07352 
07353 // *******************************************************************************
07354 // *
07355 // *    ResetScoreDifferences
07356 // *
07357 // *******************************************************************************
07360 // *******************************************************************************
07361 
07362 void ePlayerNetID::ResetScoreDifferences( void )
07363 {
07364     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
07365     {
07366         ePlayerNetID* p = se_PlayerNetIDs(i);
07367         if ( bool(p->Object()) && p->IsHuman() )
07368             p->lastScore_ = p->score;
07369     }
07370 }
07371 
07372 // *******************************************************************************
07373 // *
07374 // *    Suspend
07375 // *
07376 // *******************************************************************************
07380 // *******************************************************************************
07381 
07382 void ePlayerNetID::Suspend( int rounds )
07383 {
07384     if ( rounds < 0 )
07385     {
07386         rounds = 0;
07387     }
07388 
07389     int & suspended = AccessSuspended();
07390 
07391     if ( suspended == rounds )
07392     {
07393         return;
07394     }
07395 
07396     suspended = rounds;
07397 
07398     if ( suspended == 0 )
07399     {
07400         sn_ConsoleOut( tOutput( "$player_no_longer_suspended", GetColoredName() ) );
07401         FindDefaultTeam();
07402     }
07403     else
07404     {
07405         sn_ConsoleOut( tOutput( "$player_suspended", GetColoredName(), suspended ) );
07406         SetTeam( NULL );
07407     }
07408 }
07409 
07410 // *******************************************************************************
07411 // *
07412 // *    LogScoreDifferences
07413 // *
07414 // *******************************************************************************
07417 // *******************************************************************************
07418 
07419 void ePlayerNetID::LogScoreDifferences( void )
07420 {
07421     for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
07422     {
07423         ePlayerNetID* p = se_PlayerNetIDs(i);
07424         p->LogScoreDifference();
07425 
07426         int suspended = p->GetSuspended();
07427         
07428         // update suspension count
07429         if ( suspended > 0 )
07430         {
07431             if ( p->CurrentTeam() && !p->NextTeam() )
07432             {
07433                 p->UpdateTeam();
07434             }
07435             else
07436             {
07437                 p->Suspend( suspended - 1 );
07438             }
07439         }
07440     }
07441 }
07442 
07443 // *******************************************************************************
07444 // *
07445 // *    LogScoreDifference
07446 // *
07447 // *******************************************************************************
07450 // *******************************************************************************
07451 
07452 void ePlayerNetID::LogScoreDifference( void )
07453 {
07454     if ( lastScore_ > IMPOSSIBLY_LOW_SCORE && IsHuman() )
07455     {
07456         tString ret;
07457         int scoreDifference = score - lastScore_;
07458         lastScore_ = IMPOSSIBLY_LOW_SCORE;
07459         ret << "ROUND_SCORE " << scoreDifference << " " << GetUserName();
07460         if ( currentTeam )
07461             ret << " " << FilterName( currentTeam->Name() ) << " " << currentTeam->Score();
07462         ret << "\n";
07463         se_SaveToLadderLog( ret );
07464     }
07465 }
07466 
07467 static void se_allowTeamChangesPlayer(bool allow, std::istream &s) {
07468     if ( se_NeedsServer( "(DIS)ALLOW_TEAM_CHANGE_PLAYER", s, false ) )
07469     {
07470         return;
07471     }
07472 
07473     ePlayerNetID * p = ReadPlayer( s );
07474     if ( p )
07475     {
07476         sn_ConsoleOut( tOutput( (allow ? "$player_allowed_teamchange" : "$player_disallowed_teamchange"), p->GetName() ) );
07477         p->SetTeamChangeAllowed( allow );
07478     }
07479 }
07480 static void se_allowTeamChangesPlayer(std::istream &s) {
07481     se_allowTeamChangesPlayer(true, s);
07482 }
07483 static void se_disallowTeamChangesPlayer(std::istream &s) {
07484     se_allowTeamChangesPlayer(false, s);
07485 }
07486 static tConfItemFunc se_allowTeamChangesPlayerConf("ALLOW_TEAM_CHANGE_PLAYER", &se_allowTeamChangesPlayer);
07487 static tConfItemFunc se_disallowTeamChangesPlayerConf("DISALLOW_TEAM_CHANGE_PLAYER", &se_disallowTeamChangesPlayer);
07488 static tAccessLevelSetter se_atcConfLevel( se_allowTeamChangesPlayerConf, tAccessLevel_TeamLeader );
07489 static tAccessLevelSetter se_dtcConfLevel( se_disallowTeamChangesPlayerConf, tAccessLevel_TeamLeader );
07490 
07492 int & ePlayerNetID::AccessSuspended()
07493 {
07494     return suspended_;
07495 }
07496 
07498 int ePlayerNetID::GetSuspended() const
07499 {
07500     return suspended_;
07501 }

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