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