3
*************************************************************************
5
ArmageTron -- Just another Tron Lightcycle Game in 3D.
6
Copyright (C) 2000 Manuel Moos (manuel@moosnet.de)
8
**************************************************************************
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.
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.
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.
24
***************************************************************************
28
#include "tMemManager.h"
30
//#include "tInitExit.h"
31
#include "tConfiguration.h"
32
#include "eNetGameObject.h"
46
#include "nAuthentification.h"
47
#include "tDirectories.h"
50
#include "tReferenceHolder.h"
52
#include "nServerInfo.h"
53
#include "tRecorder.h"
57
tCONFIG_ENUM( eCamMode );
59
tList<ePlayerNetID> se_PlayerNetIDs;
60
static ePlayer* se_Players = NULL;
62
static bool se_assignTeamAutomatically = true;
63
static tSettingItem< bool > se_assignTeamAutomaticallyConf( "AUTO_TEAM", se_assignTeamAutomatically );
65
static tReferenceHolder< ePlayerNetID > se_PlayerReferences;
71
nKrawall::nScrambledPassword password;
74
PasswordStorage(): save(false){};
78
static tArray<PasswordStorage> S_passwords;
80
void se_DeletePasswords(){
81
S_passwords.SetLen(0);
87
REAL timeout = tSysTimeFloat() + 3;
89
while(tSysTimeFloat() < timeout){
91
sr_ResetRenderState(true);
92
rViewport::s_viewportFullscreen.Select();
96
uMenu::GenericBackground();
105
DisplayText(0,.8,w,h,tOutput("$network_opts_deletepw_complete"));
112
tConsole::Message("$network_opts_deletepw_complete", tOutput(), 5);
115
class tConfItemPassword:public tConfItemBase{
117
tConfItemPassword():tConfItemBase("PASSWORD"){}
118
~tConfItemPassword(){};
120
// write the complete passwords
121
virtual void WriteVal(std::ostream &s){
124
for (i = S_passwords.Len()-1; i>=0; i--)
126
PasswordStorage &storage = S_passwords[i];
134
nKrawall::WriteScrambledPassword(storage.password, s);
135
s << '\t' << storage.username;
143
virtual void ReadVal(std::istream &s){
144
// static char in[20];
149
PasswordStorage &storage = S_passwords[S_passwords.Len()];
150
nKrawall::ReadScrambledPassword(s, storage.password);
151
storage.username.ReadLine(s);
157
static tConfItemPassword p;
159
// password request menu
160
class eMenuItemPassword: public uMenuItemString
163
eMenuItemPassword(uMenu *M,tString &c):
164
uMenuItemString(M,"$login_password_title","$login_password_help",c){}
165
virtual ~eMenuItemPassword(){}
167
virtual void Render(REAL x,REAL y,REAL alpha=1,bool selected=0)
169
tString* pwback = content;
171
for (int i=content->Len()-2; i>=0; i--)
174
uMenuItemString::Render(x,y, alpha, selected);
178
virtual bool Event(SDL_Event &e){
180
if (e.type==SDL_KEYDOWN &&
181
(e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
186
// else if (e.type==SDL_KEYDOWN &&
187
// uActionGlobal::IsBreakingGlobalBind(e.key.keysym.sym))
188
// return su_HandleEvent(e);
191
return uMenuItemString::Event(e);
196
static bool tr(){return true;}
198
static uMenu *S_login=NULL;
200
static void cancelLogin()
207
// password storage mode
208
int se_PasswordStorageMode = 0; // 0: store in memory, -1: don't store, 1: store on file
209
static tConfItem<int> pws("PASSWORD_STORAGE",
210
"$password_storage_help",
211
se_PasswordStorageMode);
213
static void PasswordCallback(tString& username,
214
const tString& message,
215
nKrawall::nScrambledPassword& scrambled,
220
// find the player with the given username:
222
for (i = MAX_PLAYERS-1; i>=0 && !p; i--)
224
ePlayer * perhaps = ePlayer::PlayerConfig(i);
225
tString filteredName;
226
ePlayerNetID::FilterName( perhaps->name, filteredName );
227
if ( filteredName == username )
231
// or the given raw name
232
for (i = MAX_PLAYERS-1; i>=0 && !p; i--)
234
ePlayer * perhaps = ePlayer::PlayerConfig(i);
235
if ( perhaps->name == username )
239
// or just any player, what the heck.
241
p = ePlayer::PlayerConfig(0);
243
// try to find the username in the saved passwords:
244
PasswordStorage *storage = NULL;
245
for (i = S_passwords.Len()-1; i>=0; i--)
246
if (p->name == S_passwords(i).username)
247
storage = &S_passwords(i);
251
// find an empty slot
252
for (i = S_passwords.Len()-1; i>=0; i--)
253
if (S_passwords(i).username.Len() < 1)
254
storage = &S_passwords(i);
257
storage = &S_passwords[S_passwords.Len()];
262
// immediately return the stored password if it was not marked as wrong:
265
username = storage->username;
266
memcpy(scrambled, storage->password, sizeof(nKrawall::nScrambledPassword));
270
storage->username.Clear();
272
// password was not stored. Request it from user:
274
uMenu login(message, false);
280
eMenuItemPassword pw(&login, password);
281
uMenuItemString us(&login, "$login_username","$login_username_help", p->name);
283
uMenuItemSelection<int> storepw(&login,
284
"$login_storepw_text",
285
"$login_storepw_help",
286
se_PasswordStorageMode);
287
storepw.NewChoice("$login_storepw_dont_text",
288
"$login_storepw_dont_help",
290
storepw.NewChoice("$login_storepw_mem_text",
291
"$login_storepw_mem_help",
293
storepw.NewChoice("$login_storepw_disk_text",
294
"$login_storepw_disk_help",
297
uMenuItemFunction cl(&login, "$login_cancel", "$login_cancel_help", &cancelLogin);
299
login.SetSelected(1);
301
// force a small console while we are in here
302
rSmallConsoleCallback cb(&tr);
307
// return username/scrambled password
311
nKrawall::ScramblePassword(password, scrambled);
313
// clear the PW from memory
314
for (i = password.Len()-2; i>=0; i--)
317
if (se_PasswordStorageMode >= 0)
319
storage->username = p->name;
320
memcpy(storage->password, scrambled, sizeof(nKrawall::nScrambledPassword));
321
storage->save = (se_PasswordStorageMode > 0);
325
sn_SetNetState(nSTANDALONE);
332
static void ResultCallback(const tString& usernameUnfiltered,
333
const tString& origUsername,
334
int user, bool success)
338
// filter the user name
340
ePlayerNetID::FilterName( usernameUnfiltered, username );
342
// record and playback result (required because on playback, a new
343
// salt is generated and this way, a recoding does not contain ANY
344
// exploitable information for password theft: the scrambled password
345
// stored in the incoming network stream has an unknown salt value. )
346
static char const * section = "AUTH";
347
tRecorder::Playback( section, success );
348
tRecorder::Record( section, success );
354
// authenticate the user that logged in and change his name
355
for (i = se_PlayerNetIDs.Len()-1; i>=0 && !found; i--)
357
ePlayerNetID *p = se_PlayerNetIDs(i);
358
if (p->Owner() == user && p->GetUserName() == origUsername)
360
p->SetUserName(username);
361
// right now, user name and player name should match. Take care of that.
362
// the next line is scheduled to be removed for real authentication.
363
p->SetName(username);
369
// if the first step was not successful,
370
// authenticate the player from that client with the new user name (maybe he renamed?)
371
for (i = se_PlayerNetIDs.Len()-1; i>=0 && !found; i--)
373
ePlayerNetID *p = se_PlayerNetIDs(i);
374
if (p->Owner() == user && p->GetUserName() == username)
381
// last attempt: authenticate any player from that client.
382
//!TODO: think about whether we actually want that.
383
for (i = se_PlayerNetIDs.Len()-1; i>=0 && !found; i--)
385
ePlayerNetID *p = se_PlayerNetIDs(i);
386
if (p->Owner() == user)
388
p->SetUserName( username );
394
// request other logins
395
for (i = se_PlayerNetIDs.Len()-1; i>=0; i--)
397
ePlayerNetID *p = se_PlayerNetIDs(i);
403
if (sn_GetNetState() == nSERVER && user != sn_myNetID )
404
nAuthentification::RequestLogin(username, user, "$login_request_failed", true);
411
// menu item to silence selected players
412
class eMenuItemSilence: public uMenuItemToggle
415
eMenuItemSilence(uMenu *m, ePlayerNetID* p )
416
: uMenuItemToggle( m, tOutput(""),tOutput("$silence_player_help" ), p->AccessSilenced() )
419
this->title.SetTemplateParameter(1, p->GetColoredName() );
420
this->title << "$silence_player_text";
428
tCONTROLLED_PTR( ePlayerNetID ) player_; // keep player referenced
434
// menu where you can silence players
435
void ePlayerNetID::SilenceMenu()
437
uMenu menu( "$player_police_silence_text" );
439
int size = se_PlayerNetIDs.Len();
440
eMenuItemSilence** items = tNEW( eMenuItemSilence* )[ size ];
443
for ( i = size-1; i>=0; --i )
445
ePlayerNetID* player = se_PlayerNetIDs[ i ];
446
if ( player->IsHuman() )
448
items[i] = tNEW( eMenuItemSilence )( &menu, player );
459
for ( i = size - 1; i>=0; --i )
461
if( items[i] ) delete items[i];
466
void ePlayerNetID::PoliceMenu()
468
uMenu menu( "$player_police_text" );
470
uMenuItemFunction kick( &menu, "$player_police_kick_text", "$player_police_kick_help", eVoter::KickMenu );
471
uMenuItemFunction silence( &menu, "$player_police_silence_text", "$player_police_silence_help", ePlayerNetID::SilenceMenu );
500
static char *default_instant_chat[]=
505
"/team 1 Yes Oui Ja",
506
"/team 0 No Non Nein",
507
"/team I'm going in!",
508
"Give the rim a break; hug a tree instead.",
509
"Lag is a myth. It is only in your brain.",
510
"Rubber kills gameplay!",
511
"Every time you double bind, God kills a kitten.",
512
"http://www.armagetronad.net",
513
"Only idiots keep their instant chat at default values.",
514
"/me wanted to download pr0n, but only got this stupid game.",
516
"This server sucks! I'm going home.",
517
"Grind EVERYTHING! And 180 some more!",
518
"/me has an interesting mental disorder.",
519
"Ah, a nice, big, roomy box all for me!",
520
"Go that way! No, the other way!",
523
"/me just installed this game and still doesn't know how to talk.",
524
"/team You all suck, I want a new team.",
525
"Are you the real \"Player 1\"?",
531
ePlayer * ePlayer::PlayerConfig(int p){
532
uPlayerPrototype *P = uPlayerPrototype::PlayerConfig(p);
533
return dynamic_cast<ePlayer*>(P);
534
// return (ePlayer*)P;
537
void ePlayer::StoreConfitem(tConfItemBase *c){
538
tASSERT(CurrentConfitem < PLAYER_CONFITEMS);
539
configuration[CurrentConfitem++] = c;
542
void ePlayer::DeleteConfitems(){
543
while (CurrentConfitem>0){
545
tDESTROY(configuration[CurrentConfitem]);
549
uActionPlayer *ePlayer::se_instantChatAction[MAX_INSTANT_CHAT];
551
static const tString& se_UserName()
553
srand( (unsigned)time( NULL ) );
555
static tString ret( getenv( "USER" ) );
560
nAuthentification::SetUserPasswordCallback(&PasswordCallback);
561
nAuthentification::SetLoginResultCallback (&ResultCallback);
563
nameTeamAfterMe = false;
564
favoriteNumberOfPlayersPerTeam = 3;
568
bool getUserName = false;
571
name = se_UserName();
572
getUserName = ( name.Len() > 1 );
575
name << "Player " << id+1;
579
confname << "PLAYER_"<< id+1;
580
StoreConfitem(tNEW(tConfItemLine) (confname,
581
"$player_name_confitem_help",
585
confname << "CAMCENTER_"<< id+1;
586
centerIncamOnTurn=true;
587
StoreConfitem(tNEW(tConfItem<bool>)
590
centerIncamOnTurn) );
593
startCamera=CAMERA_SMART;
594
confname << "START_CAM_"<< id+1;
595
StoreConfitem(tNEW(tConfItem<eCamMode>) (confname,
600
confname << "START_FOV_"<< id+1;
602
StoreConfitem(tNEW(tConfItem<int>) (confname,
608
confname << "SMART_GLANCE_CUSTOM_"<< id+1;
609
smartCustomGlance=true;
610
StoreConfitem(tNEW(tConfItem<bool>) (confname,
611
"$camera_smart_glance_custom_help",
616
for(i=CAMERA_SMART_IN;i>=0;i--){
617
confname << "ALLOW_CAM_"<< id+1 << "_" << i;
618
StoreConfitem(tNEW(tConfItem<bool>) (confname,
625
for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
626
confname << "INSTANT_CHAT_STRING_" << id+1 << '_' << i+1;
627
StoreConfitem(tNEW(tConfItemLine) (confname,
628
"$instant_chat_string_help",
629
instantChatString[i]));
633
for(i=0; i < MAX_INSTANT_CHAT;i++){
634
if (!default_instant_chat[i])
637
instantChatString[i]=default_instant_chat[i];
640
confname << "SPECTATOR_MODE_"<< id+1;
641
StoreConfitem(tNEW(tConfItem<bool>)(confname,
642
"$spectator_mode_help",
647
confname << "NAME_TEAM_AFTER_PLAYER_"<< id+1;
648
StoreConfitem(tNEW(tConfItem<bool>)(confname,
649
"$name_team_after_player_help",
651
nameTeamAfterMe=false;
654
confname << "FAV_NUM_PER_TEAM_PLAYER_"<< id+1;
655
StoreConfitem(tNEW(tConfItem<int>)(confname,
656
"$fav_num_per_team_player_help",
657
favoriteNumberOfPlayersPerTeam ));
658
favoriteNumberOfPlayersPerTeam = 3;
662
confname << "AUTO_INCAM_"<< id+1;
663
autoSwitchIncam=false;
664
StoreConfitem(tNEW(tConfItem<bool>) (confname,
669
confname << "CAMWOBBLE_"<< id+1;
671
StoreConfitem(tNEW(tConfItem<bool>) (confname,
676
confname << "COLOR_B_"<< id+1;
677
StoreConfitem(tNEW(tConfItem<int>) (confname,
682
confname << "COLOR_G_"<< id+1;
683
StoreConfitem(tNEW(tConfItem<int>) (confname,
688
confname << "COLOR_R_"<< id+1;
689
StoreConfitem(tNEW(tConfItem<int>) (confname,
694
tRandomizer & randomizer = tRandomizer::GetInstance();
695
//static int r = rand() / ( RAND_MAX >> 2 ) + se_UserName().Len();
696
static int r = randomizer.Get(4) + se_UserName().Len();
697
int cid = ( r + id ) % 4;
699
static REAL R[MAX_PLAYERS]={1,.2,.2,1};
700
static REAL G[MAX_PLAYERS]={.2,1,.2,1};
701
static REAL B[MAX_PLAYERS]={.2,.2,1,.2};
703
rgb[0]=int(R[cid]*15);
704
rgb[1]=int(G[cid]*15);
705
rgb[2]=int(B[cid]*15);
716
void ePlayer::Render(){
717
if (cam) cam->Render();
721
static void se_DisplayChatLocally( ePlayerNetID* p, const tString& say )
724
if (strstr( say, "BUG" ) )
730
if ( p && !p->IsSilenced() )
732
//tColoredString say2( say );
734
tColoredString message;
736
message << tColoredString::ColorString(1,1,.5);
737
message << ": " << say << '\n';
743
static void se_DisplayChatLocallyClient( ePlayerNetID* p, const tString& message )
745
if ( p && !p->IsSilenced() )
747
con << message << "\n";
751
static nVersionFeature se_chatRelay( 3 );
753
//!todo: lower this number or increase network version before next release
754
static nVersionFeature se_chatHandlerClient( 6 );
756
// chat message from client to server
757
void handle_chat( nMessage& );
758
static nDescriptor chat_handler(200,handle_chat,"Chat");
760
// chat message from server to client
761
void handle_chat_client( nMessage & );
762
static nDescriptor chat_handler_client(203,handle_chat_client,"Chat Client");
764
void handle_chat_client(nMessage &m)
766
if(sn_GetNetState()!=nSERVER)
773
tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
775
se_DisplayChatLocallyClient( p, say );
779
// appends chat message to something
780
template< class TARGET >
781
void se_AppendChat( TARGET & out, tString const & message )
783
if ( message.Len() <= se_SpamMaxLen*2 )
787
tString cut( message );
788
cut.SetLen( se_SpamMaxLen*2 );
793
// builds a colored chat string
794
static tColoredString se_BuildChatString( ePlayerNetID const * sender, tString const & message )
796
tColoredString console;
798
console << tColoredString::ColorString(1,1,.5) << ": ";
799
se_AppendChat( console, message );
804
// Build a colored /team message
805
static tColoredString se_BuildChatString( eTeam const *team, ePlayerNetID const *sender, tString const &message )
807
tColoredString console;
810
if (sender->CurrentTeam() == team) {
811
// foo --> Teammates: some message here
812
console << tColoredString::ColorString(1,1,.5) << " --> ";
813
console << tColoredString::ColorString(team->R(),team->G(),team->B()) << "Teammates";
816
// foo (Red Team) --> Blue Team: some message here
817
eTeam *senderTeam = sender->CurrentTeam();
818
console << tColoredString::ColorString(1,1,.5) << " (";
819
console << tColoredString::ColorString(senderTeam->R(),senderTeam->G(),senderTeam->B()) << senderTeam->Name();
820
console << tColoredString::ColorString(1,1,.5) << ") --> ";
821
console << tColoredString::ColorString(team->R(),team->G(),team->B()) << team->Name();
824
console << tColoredString::ColorString(1,1,.5) << ": ";
825
se_AppendChat( console, message );
830
// builds a colored chat /msg string
831
static tColoredString se_BuildChatString( ePlayerNetID const * sender, ePlayerNetID const * receiver, tString const & message )
833
tColoredString console;
835
console << tColoredString::ColorString(1,1,.5) << " --> ";
836
console << *receiver;
837
console << tColoredString::ColorString(1,1,.5) << ": ";
838
se_AppendChat( console, message );
843
// prepares a chat message with a specific total text originating from the given player
844
static nMessage* se_ServerControlledChatMessageConsole( ePlayerNetID const * player, tString const & toConsole )
848
nMessage *m=tNEW(nMessage) (chat_handler_client);
850
m->Write( player->ID() );
856
// prepares a chat message with a chat string (only the message, not the whole console line) originating from the given player
857
static nMessage* se_ServerControlledChatMessage( ePlayerNetID const * sender, tString const & message )
861
return se_ServerControlledChatMessageConsole( sender, se_BuildChatString( sender, message ) );
864
// prepares a chat message with a chat message originating from the given player to the given receiver
865
static nMessage* se_ServerControlledChatMessage( ePlayerNetID const * sender, ePlayerNetID const * receiver, tString const & message )
870
return se_ServerControlledChatMessageConsole( sender, se_BuildChatString( sender, receiver, message ) );
873
// prepares a chat message with a chat message originating from the given player to the given team
874
static nMessage* se_ServerControlledChatMessage( eTeam const * team, ePlayerNetID const * sender, tString const & message )
879
return se_ServerControlledChatMessageConsole( sender, se_BuildChatString(team, sender, message) );
882
// pepares a chat message the client has to put together
883
static nMessage* se_NewChatMessage( ePlayerNetID const * player, tString const & message )
887
nMessage *m=tNEW(nMessage) (chat_handler);
888
m->Write( player->ID() );
889
se_AppendChat( *m, message );
894
// prepares a very old style chat message: just a regular remote console output message
895
static nMessage* se_OldChatMessage( tString const & line )
897
return sn_ConsoleOutMessage( line + "\n" );
900
// prepares a very old style chat message: just a regular remote console output message
901
static nMessage* se_OldChatMessage( ePlayerNetID const * player, tString const & message )
905
return se_OldChatMessage( se_BuildChatString( player, message ) );
908
// send the chat of player p to all connected clients after properly formatting it
909
// ( the clients will see <player>: <say>
910
void se_BroadcastChat( ePlayerNetID* p, const tString& say )
912
// create chat messages
913
tJUST_CONTROLLED_PTR< nMessage > mServerControlled = se_ServerControlledChatMessage( p, say );
914
tJUST_CONTROLLED_PTR< nMessage > mNew = se_NewChatMessage( p, say );
915
tJUST_CONTROLLED_PTR< nMessage > mOld = se_OldChatMessage( p, say );
917
// send them to the users, depending on what they understand
918
for ( int user = MAXCLIENTS; user > 0; --user )
920
if ( sn_Connections[ user ].socket )
922
if ( se_chatHandlerClient.Supported( user ) )
923
mServerControlled->Send( user );
924
else if ( se_chatRelay.Supported( user ) )
932
// sends the full chat line to all connected players
933
// ( the clients will see <line> )
934
void se_BroadcastChatLine( ePlayerNetID* p, const tString& line, const tString& forOldClients )
936
// create chat messages
937
tJUST_CONTROLLED_PTR< nMessage > mServerControlled = se_ServerControlledChatMessageConsole( p, line );
938
tJUST_CONTROLLED_PTR< nMessage > mNew = se_NewChatMessage( p, forOldClients );
939
tJUST_CONTROLLED_PTR< nMessage > mOld = se_OldChatMessage( line );
941
// send them to the users, depending on what they understand
942
for ( int user = MAXCLIENTS; user > 0; --user )
944
if ( sn_Connections[ user ].socket )
946
if ( se_chatHandlerClient.Supported( user ) )
947
mServerControlled->Send( user );
948
else if ( se_chatRelay.Supported( user ) )
956
// sends a private message from sender to receiver
957
void se_SendPrivateMessage( ePlayerNetID const * sender, ePlayerNetID const * receiver, tString const & message )
962
// determine receiver client id
963
int cid = receiver->Owner();
965
// determine wheter the receiver knows about the server controlled chat message
966
if ( se_chatHandlerClient.Supported( cid ) )
968
// send server controlled message
969
se_ServerControlledChatMessage( sender, receiver, message )->Send( cid );
974
say << tColoredString::ColorString(1,1,.5) << "( --> ";
976
say << tColoredString::ColorString(1,1,.5) << " ) ";
979
// format message containing receiver
980
if ( se_chatRelay.Supported( cid ) )
982
// send client formatted message
983
se_NewChatMessage( sender, say )->Send( cid );
987
// send console out message
988
se_OldChatMessage( sender, say )->Send( cid );
993
// Sends a /team message
994
void se_SendTeamMessage( eTeam const * team, ePlayerNetID const * sender ,ePlayerNetID const * receiver, tString const & message )
1000
int clientID = receiver->Owner();
1001
if ( clientID == 0 )
1004
if ( se_chatHandlerClient.Supported( clientID ) ) {
1005
se_ServerControlledChatMessage( team, sender, message )->Send( clientID );
1009
say << tColoredString::ColorString(1,1,.5) << "( " << *sender;
1011
// ( foo --> Teammates ) some message here
1012
if (sender->CurrentTeam() == team) {
1013
say << tColoredString::ColorString(1,1,.5) << " --> ";
1014
say << tColoredString::ColorString(team->R(),team->G(),team->B()) << "Teammates";
1016
// ( foo (Blue Team) --> Red Team ) some message
1018
eTeam *senderTeam = sender->CurrentTeam();
1019
say << tColoredString::ColorString(1,1,.5) << " (";
1020
say << tColoredString::ColorString(team->R(),team->G(),team->B()) << team->Name();
1021
say << tColoredString::ColorString(1,1,.5) << " ) --> ";
1022
say << tColoredString::ColorString(senderTeam->R(),senderTeam->G(),senderTeam->B()) << senderTeam->Name();
1024
say << tColoredString::ColorString(1,1,.5) << " ) ";
1027
// format message containing receiver
1028
if ( se_chatRelay.Supported( clientID ) )
1029
// send client formatted message
1030
se_NewChatMessage( sender, say )->Send( clientID );
1032
// send console out message
1033
se_OldChatMessage( sender, say )->Send( clientID );
1037
// checks whether text_to_search contains search_for_text
1038
bool Contains( const tString & search_for_text, const tString & text_to_search ) {
1039
int m = strlen(search_for_text);
1040
int n = strlen(text_to_search);
1042
for (b=0; b<=n-m; ++b) {
1043
for (a=0; a<m && search_for_text[a] == text_to_search[a+b]; ++a)
1046
// a match has been found
1052
ePlayerNetID * CompareBufferToPlayerNames( const tString & current_buffer, int & num_matches ) {
1054
ePlayerNetID * match = 0;
1056
// run through all players and look for a match
1057
for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a ) {
1058
ePlayerNetID* toMessage = se_PlayerNetIDs(a);
1061
if ( current_buffer == toMessage->GetUserName() )
1067
if ( Contains(current_buffer, toMessage->GetUserName())) {
1068
match= toMessage; // Doesn't matter that this is wrote over everytime, when we only have one match it will be there.
1077
// returns a player ( not THE player, there may be more ) belonging to a user ID
1079
static ePlayerNetID * se_GetPlayerFromUserID( int uid )
1081
// run through all players and look for a match
1082
for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a )
1084
ePlayerNetID * p = se_PlayerNetIDs(a);
1085
if ( p && p->Owner() == uid )
1094
// returns a player ( not THE player, there may be more ) belonging to a user ID that is still alive
1096
ePlayerNetID * se_GetAlivePlayerFromUserID( int uid )
1098
// run through all players and look for a match
1099
for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a )
1101
ePlayerNetID * p = se_PlayerNetIDs(a);
1102
if ( p && p->Owner() == uid &&
1103
( ( p->Object() && p->Object()->Alive() ) ) )
1111
//The Base Remote Admin Password
1112
static tString sg_adminPass( "NONE" );
1113
static tConfItemLine sg_adminPassConf( "ADMIN_PASS", sg_adminPass );
1117
// console with filter for remote admin output redirection
1118
class eAdminConsoleFilter:public tConsoleFilter{
1120
eAdminConsoleFilter( int netID )
1125
~eAdminConsoleFilter()
1127
sn_ConsoleOut( message_, netID_ );
1130
// we want to come first, the admin should get unfiltered output
1131
virtual int DoGetPriority() const{ return -100; }
1133
// don't actually filter; take line and add it to the message sent to the admin
1134
virtual void DoFilterLine( tString &line )
1136
//tColoredString message;
1137
message_ << tColoredString::ColorString(1,.3,.3) << "RA: " << tColoredString::ColorString(1,1,1) << line << "\n";
1139
// don't let message grow indefinitely
1140
if (message_.Len() > 600)
1142
sn_ConsoleOut( message_, netID_ );
1147
int netID_; // the network user ID to send the result to
1148
tColoredString message_; // the console message for the remote administrator
1151
void handle_chat_admin_commands(tJUST_CONTROLLED_PTR< ePlayerNetID > &p, tString say){
1152
if (say.StartsWith("/login")) {
1154
if (say.StrPos(" ") == -1)
1157
params = say.SubStr(say.StrPos(" ") + 1);
1159
//change this later to read from a password file or something...
1160
//or integrate it with auth if we ever get that done...
1161
if (params == sg_adminPass && sg_adminPass != "NONE") {
1163
p->setAccessLevel(1);
1164
sn_ConsoleOut("You have been logged in!\n",p->Owner());
1165
tString serverLoginMessage;
1166
serverLoginMessage << "Remote admin login for user \"" << p->GetUserName() << "\" accepted.\n";
1167
sn_ConsoleOut(serverLoginMessage, 0);
1171
tString failedLogin;
1172
sn_ConsoleOut("Login denied!\n",p->Owner());
1173
failedLogin << "Remote admin login for user \"" << p->GetUserName();
1174
failedLogin << "\" using password \"" << params << "\" rejected.\n";
1175
sn_ConsoleOut(failedLogin, 0);
1178
else if (say.StartsWith("/logout")) {
1179
if ( p->isLoggedIn() )
1181
sn_ConsoleOut("You have been logged out!\n",p->Owner());
1184
p->setAccessLevel(0);
1186
else if (say.StartsWith("/admin")) {
1187
if (!p->isLoggedIn())
1189
sn_ConsoleOut("You are not logged in.\n",p->Owner());
1194
if (say.StrPos(" ") == -1)
1197
params = say.SubStr(say.StrPos(" ") + 1);
1200
eAdminConsoleFilter consoleFilter( p->Owner() );
1202
if ( tRecorder::IsPlayingBack() )
1204
tConfItemBase::LoadPlayback();
1208
std::stringstream s(static_cast< char const * >( params ) );
1209
tConfItemBase::LoadAll(s);
1214
sn_ConsoleOut("Unknown chat command.\n",p->Owner());
1219
// time during which no repeaded chat messages are printed
1220
static REAL se_alreadySaidTimeout=5.0;
1221
static tSettingItem<REAL> se_alreadySaidTimeoutConf("SPAM_PROTECTION_REPEAT",
1222
se_alreadySaidTimeout);
1224
// flag set to allow players to shuffle themselves up in a team
1225
static bool se_allowShuffleUp=false;
1226
static tSettingItem<bool> se_allowShuffleUpConf("TEAM_ALLOW_SHUFFLE_UP",
1229
void handle_chat(nMessage &m){
1230
nTimeRolling currentTime = tSysTimeFloat();
1236
tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
1238
// register player activity
1240
p->lastActivity_ = currentTime;
1242
if(sn_GetNetState()==nSERVER){
1245
// check if the player is owned by the sender to avoid cheating
1246
if( p->Owner() != m.SenderID() )
1248
Cheater( m.SenderID() );
1254
REAL lengthMalus = say.Len() / 20.0;
1255
if ( lengthMalus > 4.0 )
1260
// check if the player already said the same thing not too long ago
1261
for (short c = 0; c < p->lastSaid.Len(); c++) {
1262
if( (say.StripWhitespace() == p->lastSaid[c].StripWhitespace()) && ( (currentTime - p->lastSaidTimes[c]) < se_alreadySaidTimeout) ) {
1263
sn_ConsoleOut( tOutput("$spam_protection_repeat", say ), m.SenderID() );
1268
// update last said record
1270
for( short zz = 12; zz>=1; zz-- )
1272
p->lastSaid[zz] = p->lastSaid[zz-1];
1273
p->lastSaidTimes[zz] = p->lastSaidTimes[zz-1];
1276
p->lastSaid[0] = say;
1277
p->lastSaidTimes[0] = currentTime;
1280
nSpamProtection::Level spamLevel = p->chatSpam_.CheckSpam( 1.0f + lengthMalus, m.SenderID() );
1283
if (say.StartsWith("/")) {
1284
std::string sayStr(say);
1285
std::istringstream s(sayStr);
1289
tConfItemBase::EatWhitespace(s);
1291
if (command == "/me") {
1292
tColoredString console;
1293
console << tColoredString::ColorString(1,1,1) << "* ";
1295
console << tColoredString::ColorString(1,1,.5) << " " << msg;
1296
console << tColoredString::ColorString(1,1,1) << " *";
1298
tColoredString forOldClients;
1299
forOldClients << tColoredString::ColorString(1,1,1) << "*"
1300
<< tColoredString::ColorString(1,1,.5) << msg
1301
<< tColoredString::ColorString(1,1,1) << "*";
1303
se_BroadcastChatLine( p, console, forOldClients );
1306
else if (command == "/teamleave") {
1307
if ( se_assignTeamAutomatically )
1309
sn_ConsoleOut("Sorry, does not work with automatic team assignment.\n", p->Owner() );
1313
eTeam * leftTeam = p->NextTeam();
1317
leftTeam = p->CurrentTeam();
1319
if ( leftTeam->NumPlayers() > 1 )
1321
sn_ConsoleOut( tOutput( "$player_leave_team_wish",
1322
tColoredString::RemoveColors(p->GetName()),
1323
tColoredString::RemoveColors(leftTeam->Name()) ) );
1327
sn_ConsoleOut( tOutput( "$player_leave_game_wish",
1328
tColoredString::RemoveColors(p->GetName()) ) );
1335
else if (command == "/teamshuffle" || command == "/shuffle") {
1336
// team position shuffling. Allow players to change their team setup.
1338
// /teamshuffle: shuffles you all the way to the outside.
1339
// /teamshuffle <pos>: shuffles you to position pos
1340
// /teamshuffle +/-<dist>: shuffles you dist to the outside/inside
1341
// con << msgRest << "\n";
1342
int IDNow = p->TeamListID();
1343
if (!p->CurrentTeam())
1345
sn_ConsoleOut(tOutput("$player_not_on_team"), p->Owner());
1348
int len = p->CurrentTeam()->NumPlayers();
1349
int IDWish = len-1; // default to shuffle to the outside
1351
// but read the target position as additional parameter
1355
if ( msg[0] == '+' )
1356
IDWish += msg.toInt(1);
1357
else if ( msg[0] == '-' )
1358
IDWish -= msg.toInt(1);
1360
IDWish = msg.toInt()-1;
1368
if ( !se_allowShuffleUp && IDWish < IDNow )
1370
sn_ConsoleOut(tOutput("$player_noshuffleup"), p->Owner());
1374
if( IDNow == IDWish )
1376
sn_ConsoleOut(tOutput("$player_noshuffle"), p->Owner());
1379
p->CurrentTeam()->Shuffle( IDNow, IDWish );
1382
// Send a message to your team
1383
else if (command == "/team") {
1384
eTeam *currentTeam = p->CurrentTeam();
1386
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
1388
// Log message to server and sender
1389
tColoredString messageForServerAndSender = se_BuildChatString(currentTeam, p, msg);
1390
messageForServerAndSender << "\n";
1391
sn_ConsoleOut(messageForServerAndSender, 0);
1392
sn_ConsoleOut(messageForServerAndSender, p->Owner());
1394
// Send message to team-mates
1395
int numTeamPlayers = currentTeam->NumPlayers();
1396
for (int teamPlayerIndex = 0; teamPlayerIndex < numTeamPlayers; teamPlayerIndex++) {
1397
if (currentTeam->Player(teamPlayerIndex)->Owner() != p->Owner()) // Do not resend the message to yourself
1398
se_SendTeamMessage(currentTeam, p, currentTeam->Player(teamPlayerIndex), msg);
1403
sn_ConsoleOut(tOutput("$player_not_on_team"), p->Owner());
1408
else if (command == "/msg") {
1409
int current_place=0; // current place in buffer_name.
1411
// search for end of recipient and store recipient in buffer_name
1412
tString buffer_name;
1413
while (current_place < msg.Len() && !isspace(msg[current_place])) {
1414
buffer_name[current_place]=msg[current_place];
1417
buffer_name[current_place] = '\0';
1418
buffer_name = ePlayerNetID::FilterName(buffer_name);
1422
ePlayerNetID * receiver = CompareBufferToPlayerNames(buffer_name, num_matches);
1424
// One match, send it.
1425
if (num_matches == 1) {
1426
// extract rest of message: it is the true message to send
1428
while (current_place < msg.Len()-1) {
1430
msg_core += msg[current_place];
1433
// build chat string
1434
tColoredString toServer = se_BuildChatString( p, receiver, msg_core );
1438
sn_ConsoleOut(toServer,0);
1440
// log to sender's console
1441
sn_ConsoleOut(toServer, p->Owner());
1444
if ( p->Owner() != receiver->Owner())
1445
se_SendPrivateMessage( p, receiver, msg_core );
1448
// More than than one match for the current buffer. Complain about that.
1449
else if (num_matches > 1) {
1451
toSender.SetTemplateParameter(1, buffer_name);
1452
toSender << "$msg_toomanymatches";
1453
sn_ConsoleOut(toSender,p->Owner());
1456
// 0 matches. The user can't spell. Complain about that, too.
1459
toSender.SetTemplateParameter(1, buffer_name);
1460
toSender << "$msg_nomatch";
1461
sn_ConsoleOut(toSender, p->Owner());
1466
else if (command == "/players") {
1467
for ( int i2 = se_PlayerNetIDs.Len()-1; i2>=0; --i2 )
1469
ePlayerNetID* p2 = se_PlayerNetIDs(i2);
1473
tos << p2->GetUserName();
1474
if (p2->isLoggedIn())
1475
tos << " (logged in)";
1477
tos << " (logged out)";
1479
sn_ConsoleOut(tos, p->Owner());
1484
handle_chat_admin_commands(p, say);
1490
if ( spamLevel < nSpamProtection::Level_Mild && say.Len() <= se_SpamMaxLen+2 && ( !p->IsSilenced() ) && pass != true )
1492
se_BroadcastChat( p, say );
1493
se_DisplayChatLocally( p, say);
1499
se_DisplayChatLocally( p, say );
1503
// check if a string is a legal player name
1504
// a name is only legal if it contains at least one non-witespace character.
1505
static bool IsLegalPlayerName( tString const & name )
1508
tString stripped( tColoredString::RemoveColors( name ) );
1510
// and count non-whitespace characters
1511
for ( int i = stripped.Len()-2; i>=0; --i )
1513
if ( !isblank( stripped(i) ) )
1520
void ePlayerNetID::Chat(const tString &s_orig)
1522
tColoredString s( s_orig );
1525
switch (sn_GetNetState())
1529
se_NewChatMessage( this, s )->BroadCast();
1534
se_BroadcastChat( this, s );
1538
se_DisplayChatLocally( this, s );
1545
//return the playernetid for a given name
1547
static ePlayerNetID* identifyPlayer(tString inname)
1549
for(int i=0; i<se_PlayerNetIDs.Len(); i++)
1551
ePlayerNetID *p = se_PlayerNetIDs[i];
1553
if( inname == p->GetUserName() )
1560
// identify a local player
1561
static ePlayerNetID* se_GetLocalPlayer()
1563
for(int i=0; i<se_PlayerNetIDs.Len(); i++)
1565
ePlayerNetID *p = se_PlayerNetIDs[i];
1567
if( p->Owner() == sn_myNetID )
1573
static void ConsoleSay_conf(std::istream &s)
1577
message.ReadLine( s, true );
1579
switch (sn_GetNetState())
1583
ePlayerNetID *me = se_GetLocalPlayer();
1586
me->Chat( message );
1592
tColoredString send;
1593
send << tColoredString::ColorString( 1,0,0 );
1595
send << tColoredString::ColorString( 1,1,.5 );
1596
send << ": " << message << "\n";
1599
sn_ConsoleOut( send );
1605
ePlayerNetID *me = se_GetLocalPlayer();
1608
se_DisplayChatLocally( me, message );
1615
static tConfItemFunc ConsoleSay_c("SAY",&ConsoleSay_conf);
1617
struct eChatInsertionCommand
1621
eChatInsertionCommand( tString const & insertion )
1622
: insertion_( insertion )
1627
static std::deque<tString> se_chatHistory; // global since the class doesn't live beyond the execution of the command
1628
static int se_chatHistoryMaxSize=10; // maximal size of chat history
1629
static tSettingItem< int > se_chatHistoryMaxSizeConf("HISTORY_SIZE_CHAT",se_chatHistoryMaxSize);
1631
class eMenuItemChat : uMenuItemStringWithHistory{
1634
eMenuItemChat(uMenu *M,tString &c,ePlayer *Me):
1635
uMenuItemStringWithHistory(M,"$chat_title_text","",c, se_SpamMaxLen, se_chatHistory, se_chatHistoryMaxSize),me(Me) {}
1638
virtual ~eMenuItemChat(){ }
1640
//virtual void Render(REAL x,REAL y,REAL alpha=1,bool selected=0);
1642
virtual bool Event(SDL_Event &e){
1644
if (e.type==SDL_KEYDOWN &&
1645
(e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
1647
for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
1648
if (se_PlayerNetIDs(i)->pID==me->ID())
1649
se_PlayerNetIDs(i)->Chat(*content);
1654
else if (e.type==SDL_KEYDOWN &&
1655
uActionGlobal::IsBreakingGlobalBind(e.key.keysym.sym))
1656
return su_HandleEvent(e, true);
1659
if ( uMenuItemStringWithHistory::Event(e) )
1663
// exclude modifier keys from possible control triggers
1664
else if ( e.key.keysym.sym < SDLK_NUMLOCK || e.key.keysym.sym > SDLK_COMPOSE )
1666
// maybe it's an instant chat button?
1669
return su_HandleEvent(e, false);
1671
catch ( eChatInsertionCommand & insertion )
1673
if ( content->Len() + insertion.insertion_.Len() <= maxLength_ )
1675
*content = content->SubStr( 0, cursorPos ) + insertion.insertion_ + content->SubStr( cursorPos );
1676
cursorPos += insertion.insertion_.Len()-1;
1690
void se_ChatState(ePlayerNetID::ChatFlags flag, bool cs){
1691
for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
1693
ePlayerNetID *p = se_PlayerNetIDs[i];
1694
if (p->Owner()==sn_myNetID && p->pID >= 0){
1695
p->SetChatting( flag, cs );
1700
static ePlayer * se_chatterPlanned=NULL;
1701
static ePlayer * se_chatter =NULL;
1702
static tString se_say;
1703
static void do_chat(){
1704
if (se_chatterPlanned){
1707
se_chatter=se_chatterPlanned;
1708
se_chatterPlanned=NULL;
1709
se_ChatState( ePlayerNetID::ChatFlags_Chat, true);
1711
sr_con.SetHeight(15,false);
1712
se_SetShowScoresAuto(false);
1714
uMenu chat_menu("",false);
1715
eMenuItemChat s(&chat_menu,se_say,se_chatter);
1716
chat_menu.SetCenter(-.75);
1717
chat_menu.SetBot(-2);
1718
chat_menu.SetTop(-.7);
1721
se_ChatState( ePlayerNetID::ChatFlags_Chat, false );
1723
sr_con.SetHeight(7,false);
1724
se_SetShowScoresAuto(true);
1727
se_chatterPlanned=NULL;
1730
static void chat( ePlayer * chatter, tString const & say )
1732
se_chatterPlanned = chatter;
1737
static void chat( ePlayer * chatter )
1739
chat( chatter, tString() );
1742
static bool se_allowControlDuringChat = false;
1743
static nSettingItem<bool> se_allowControlDuringChatConf("ALLOW_CONTROL_DURING_CHAT",se_allowControlDuringChat);
1745
uActionPlayer se_toggleSpectator("TOGGLE_SPECTATOR", 0);
1747
bool ePlayer::Act(uAction *act,REAL x){
1748
eGameObject *object=NULL;
1750
if ( act == &se_toggleSpectator && x > 0 )
1752
spectate = !spectate;
1753
con << tOutput(spectate ? "$player_toggle_spectator_on" : "$player_toggle_spectator_off", name );
1756
ePlayerNetID::Update();
1760
else if (!se_chatter && s_chat==*reinterpret_cast<uActionPlayer *>(act)){
1770
uActionPlayer* pact = reinterpret_cast<uActionPlayer *>(act);
1771
for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
1772
uActionPlayer* pcompare = se_instantChatAction[i];
1773
if (pact == pcompare && x>=0){
1774
for(int j=se_PlayerNetIDs.Len()-1;j>=0;j--)
1775
if (se_PlayerNetIDs(j)->pID==ID())
1777
tString say = instantChatString[i];
1778
bool sendImmediately = true;
1779
if ( say.Len() > 2 && say[say.Len()-2] == '\\' )
1781
// cut away trailing slash and note for later
1782
// that the message should not be sent immediately.
1783
say = say.SubStr( 0, say.Len()-2 );
1784
sendImmediately = false;
1787
if ( se_chatter == this )
1789
// a chat is already active, insert the chat string
1790
throw eChatInsertionCommand( say );
1794
if ( sendImmediately )
1796
// fire out chat string immediately
1797
se_PlayerNetIDs(j)->Chat(say);
1801
// chat with instant chat string as template
1811
// no other command should get through during chat
1812
if ( se_chatter && !se_allowControlDuringChat )
1816
for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
1817
if (se_PlayerNetIDs[i]->pID==id && se_PlayerNetIDs[i]->object)
1818
object=se_PlayerNetIDs[i]->object;
1820
bool objectAct = false; // set to true if an action of the object was triggered
1821
bool ret = ((cam && cam->Act(reinterpret_cast<uActionCamera *>(act),x)) ||
1822
( objectAct = (object && se_GameTime()>=0 && object->Act(reinterpret_cast<uActionPlayer *>(act),x))));
1824
if (bool(netPlayer) && (objectAct || !se_chatter))
1825
netPlayer->Activity();
1832
rViewport * ePlayer::PlayerViewport(int p){
1833
if (!PlayerConfig(p)) return NULL;
1835
for (int i=rViewportConfiguration::CurrentViewportConfiguration()->num_viewports-1;i>=0;i--)
1836
if (sr_viewportBelongsToPlayer[i] == p)
1837
return rViewportConfiguration::CurrentViewport(i);
1842
bool ePlayer::PlayerIsInGame(int p){
1843
return PlayerViewport(p) && PlayerConfig(p);
1846
static tConfItemBase *vpbtp[MAX_VIEWPORTS];
1848
void ePlayer::Init(){
1849
se_Players = tNEW( ePlayer[MAX_PLAYERS] );
1852
for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
1854
id << "INSTANT_CHAT_";
1857
desc.SetTemplateParameter(1, i+1);
1858
desc << "$input_instant_chat_text";
1861
help.SetTemplateParameter(1, i+1);
1862
help << "$input_instant_chat_help";
1863
ePlayer::se_instantChatAction[i]=tNEW(uActionPlayer) (id, desc, help);
1864
//,desc, "Issues a special instant chat macro.");
1868
for(i=MAX_VIEWPORTS-1;i>=0;i--){
1870
id << "VIEWPORT_TO_PLAYER_";
1872
vpbtp[i] = tNEW(tConfItem<int>(id,"$viewport_belongs_help",
1873
s_newViewportBelongsToPlayer[i]));
1874
s_newViewportBelongsToPlayer[i]=i;
1878
void ePlayer::Exit(){
1880
for(i=MAX_INSTANT_CHAT-1;i>=0;i--)
1881
tDESTROY(ePlayer::se_instantChatAction[i]);
1883
for(i=MAX_VIEWPORTS-1;i>=0;i--)
1886
delete[] se_Players;
1890
uActionPlayer ePlayer::s_chat("CHAT");
1892
int pingCharity = 100;
1894
static int IMPOSSIBLY_LOW_SCORE=(-1 << 31);
1896
static nSpamProtectionSettings se_chatSpamSettings( 1.0f, "SPAM_PROTECTION_CHAT", tOutput("$spam_protection") );
1898
ePlayerNetID::ePlayerNetID(int p):nNetObject(),listID(-1), teamListID(-1)
1899
,registeredMachine_(0), pID(p),chatSpam_( se_chatSpamSettings )
1901
favoriteNumberOfPlayersPerTeam = 1;
1903
nameTeamAfterMe = false;
1910
spectating_ = false;
1912
disconnected = false;
1915
ePlayer *P = ePlayer::PlayerConfig(p);
1917
SetName( P->Name() );
1921
pingCharity=::pingCharity;
1928
lastSaid.SetLen(12);
1929
lastSaidTimes.SetLen(12);
1931
se_PlayerNetIDs.Add(this,listID);
1935
if(sn_GetNetState()!=nSERVER)
1936
ping=sn_Connections[0].ping;
1939
ping=0; // hehe! server has no ping.
1941
lastSync=tSysTimeFloat();
1945
lastScore_=IMPOSSIBLY_LOW_SCORE;
1948
//Remote Addmin add-ins...
1952
MyInitAfterCreation();
1958
ePlayerNetID::ePlayerNetID(nMessage &m):nNetObject(m),listID(-1), teamListID(-1)
1959
, registeredMachine_(0), chatSpam_( se_chatSpamSettings )
1969
nameTeamAfterMe = false;
1971
lastSaid.SetLen(12);
1972
lastSaidTimes.SetLen(12);
1975
se_PlayerNetIDs.Add(this,listID);
1977
ping=sn_Connections[m.SenderID()].ping;
1978
lastSync=tSysTimeFloat();
1980
if(sn_GetNetState()==nSERVER)
1984
lastScore_=IMPOSSIBLY_LOW_SCORE;
1987
//Remote Addmin add-ins...
1991
#ifdef KRAWALL_SERVER
1998
void ePlayerNetID::Activity()
2000
// the player was active; therefore, he cannot possibly be chatting_ or disconnected.
2002
// but do nothing if we are in client mode and the player is not local to this computer
2003
if (sn_GetNetState() != nSERVER && Owner() != ::sn_myNetID)
2006
if (chatting_ || disconnected)
2009
con << *this << " showed activity and lost chat status.\n";
2014
chatting_ = disconnected = false;
2017
this->lastActivity_ = tSysTimeFloat();
2020
static bool se_SilenceAll = false; // flag indicating whether everyone should be silenced
2022
static tSettingItem<bool> se_silAll("SILENCE_ALL",
2025
static int se_maxPlayersPerIP = 4;
2026
static tSettingItem<int> se_maxPlayersPerIPConf( "MAX_PLAYERS_SAME_IP", se_maxPlayersPerIP );
2028
void ePlayerNetID::MyInitAfterCreation()
2030
this->CreateVoter();
2032
this->silenced_ = se_SilenceAll;
2036
// register with machine and kick user if too many players are present
2037
if ( Owner() != 0 && sn_GetNetState() == nSERVER )
2039
this->RegisterWithMachine();
2040
if ( se_maxPlayersPerIP < nMachine::GetMachine(Owner()).GetPlayerCount() )
2043
sn_DisconnectUser( Owner(), "$network_kill_too_many_players" );
2049
this->lastActivity_ = tSysTimeFloat();
2052
void ePlayerNetID::InitAfterCreation()
2054
MyInitAfterCreation();
2056
if (sn_GetNetState() == nSERVER)
2058
#ifndef KRAWALL_SERVER
2059
GetScoreFromDisconnectedCopy();
2065
bool ePlayerNetID::ClearToTransmit(int user) const{
2066
return ( ( !nextTeam || nextTeam->HasBeenTransmitted( user ) ) &&
2067
( !currentTeam || currentTeam->HasBeenTransmitted( user ) ) ) ;
2070
ePlayerNetID::~ePlayerNetID()
2072
// se_PlayerNetIDs.Remove(this,listID);
2073
if ( sn_GetNetState() == nSERVER && disconnected )
2076
tColoredString name;
2077
name << *this << tColoredString::ColorString(1,.5,.5);
2078
mess.SetTemplateParameter(1, name);
2079
mess.SetTemplateParameter(2, score);
2080
mess << "$player_left_game";
2081
sn_ConsoleOut(mess);
2084
UnregisterWithMachine();
2089
//con << "Player info sent.\n";
2091
for(int i=MAX_PLAYERS-1;i>=0;i--){
2092
ePlayer *p = ePlayer::PlayerConfig(i);
2094
if (p && static_cast<ePlayerNetID *>(p->netPlayer)==this)
2100
currentTeam->RemovePlayer( this );
2104
con << *this << " destroyed.\n";
2108
static void player_removed_from_game_handler(nMessage &m)
2110
// and the ID of the player that was removed
2113
ePlayerNetID* p = dynamic_cast< ePlayerNetID* >( nNetObject::ObjectDangerous( id ) );
2114
if ( p && sn_GetNetState() != nSERVER )
2116
p->RemoveFromGame();
2120
static nDescriptor player_removed_from_game(202,&player_removed_from_game_handler,"player_removed_from_game");
2122
void ePlayerNetID::RemoveFromGame()
2126
this->voter_->RemoveFromGame();
2130
LogScoreDifference();
2132
if ( sn_GetNetState() != nCLIENT )
2134
nameFromClient_ = nameFromServer_;
2136
nMessage *m=new nMessage(player_removed_from_game);
2137
m->Write(this->ID());
2141
if ( IsSpectating() )
2143
// get colored player name
2144
tColoredString playerName;
2145
playerName << *this << tColoredString::ColorString(1,.5,.5);
2147
// announce a generic leave
2148
sn_ConsoleOut( tOutput( "$player_left_spectator", playerName ) );
2151
if ( IsHuman() && sn_GetNetState() == nSERVER && NULL != sn_Connections[Owner()].socket )
2154
ladder << "PLAYER_LEFT " << userName_ << " " << nMachine::GetMachine(Owner()).GetIP() << "\n";
2155
se_SaveToLadderLog(ladder);
2160
se_PlayerNetIDs.Remove(this, listID);
2161
SetTeamWish( NULL );
2164
ControlObject( NULL );
2165
// currentTeam = NULL;
2168
bool ePlayerNetID::ActionOnQuit()
2170
UnregisterWithMachine();
2172
tControlledPTR< ePlayerNetID > holder( this );
2176
this->RemoveFromGame();
2178
se_PlayerNetIDs.Remove(this,listID);
2184
void ePlayerNetID::ActionOnDelete()
2186
UnregisterWithMachine();
2188
tControlledPTR< ePlayerNetID > holder( this );
2190
this->RemoveFromGame();
2192
se_PlayerNetIDs.Remove(this,listID);
2195
void ePlayerNetID::PrintName(tString &s) const
2197
s << "ePlayerNetID nr. " << ID() << ", name " << GetName();
2201
bool ePlayerNetID::AcceptClientSync() const{
2205
void ePlayerNetID::Auth(){
2207
GetScoreFromDisconnectedCopy();
2210
bool ePlayerNetID::IsAuth() const{
2211
#ifdef KRAWALL_SERVER
2212
if (!auth && sn_GetNetState() == nSERVER && Owner() != sn_myNetID )
2213
nAuthentification::RequestLogin(GetUserName(), Owner(), "$login_request_first");
2219
// create our voter or find it
2220
void ePlayerNetID::CreateVoter()
2222
// only count nonlocal players with voting support as voters
2223
if ( sn_GetNetState() != nCLIENT && this->Owner() != 0 && sn_Connections[ this->Owner() ].version.Max() >= 3 )
2225
this->voter_ = eVoter::GetVoter( this->Owner() );
2229
void ePlayerNetID::WriteSync(nMessage &m){
2230
lastSync=tSysTimeFloat();
2231
nNetObject::WriteSync(m);
2235
m.Write(pingCharity);
2236
if ( sn_GetNetState() == nCLIENT )
2238
m << nameFromClient_;
2242
m << nameFromServer_;
2245
//if(sn_GetNetState()==nSERVER)
2248
// pack chat and spectator status together
2249
unsigned short flags = ( chatting_ ? 1 : 0 ) | ( spectating_ ? 2 : 0 );
2253
m << static_cast<unsigned short>(disconnected);
2258
m << favoriteNumberOfPlayersPerTeam;
2259
m << nameTeamAfterMe;
2262
// makes sure the passed string is not longer than the given maximum
2263
static void se_CutString( tColoredString & string, int maxLen )
2265
if (string.Len() > maxLen )
2267
string.SetLen(maxLen);
2268
string[string.Len()-1]='\0';
2269
string.RemoveTrailingColor();
2273
static void se_CutString( tString & string, int maxLen )
2275
se_CutString( reinterpret_cast< tColoredString & >( string ), maxLen );
2278
static bool se_bugColorOverflow=true;
2279
tSettingItem< bool > se_bugColorOverflowColor( "BUG_COLOR_OVERFLOW", se_bugColorOverflow );
2280
void Clamp( unsigned short & colorComponent )
2282
if ( colorComponent > 15 )
2283
colorComponent = 15;
2286
// function prototype for character testing functions
2287
typedef bool TestCharacter( char c );
2289
// strips characters matching a test beginnings and ends of names
2290
static void se_StripMatchingEnds( tString & stripper, TestCharacter & beginTester, TestCharacter & endTester )
2292
int len = stripper.Len() - 1;
2293
int first = 0, last = len;
2295
// eat whitespace from beginnig and end
2296
while ( first < len && beginTester( stripper[first] ) ) ++first;
2297
while ( last > 0 && ( !stripper[last] || endTester(stripper[last] ) ) ) --last;
2299
// strip everything?
2307
if ( first > 0 || last < stripper.Len() - 1 )
2308
stripper = stripper.SubStr( first, last + 1 - first );
2311
// removed convenience function, VisualC 6 cannot cope with it...
2312
// strips characters matching a test beginnings and ends of names
2313
//static void se_StripMatchingEnds( tString & stripper, TestCharacter & tester )
2315
// se_StripMatchingEnds( stripper, tester, tester );
2318
// function wrapper for what may be a macro
2319
static bool se_IsSpace( char c )
2321
return isspace( c );
2324
// enf of player names should neither be space or :
2325
static bool se_IsInvalidNameEnd( char c )
2327
return isspace( c ) || c == ':' || c == '.';
2331
static void se_StripNameEnds( tString & name )
2333
se_StripMatchingEnds( name, se_IsSpace, se_IsInvalidNameEnd );
2336
// test if a user name is used by anyone else than the passed player
2337
static bool se_IsNameTaken( tString const & name, ePlayerNetID const * exception )
2339
for (int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
2341
ePlayerNetID * player = se_PlayerNetIDs(i);
2342
if ( player != exception )
2344
if ( name == player->GetUserName() )
2352
static bool se_allowImposters = false;
2353
static tSettingItem< bool > se_allowImposters1( "ALLOW_IMPOSTERS", se_allowImposters );
2354
static tSettingItem< bool > se_allowImposters2( "ALLOW_IMPOSTORS", se_allowImposters );
2356
static bool se_filterColorNames=false;
2357
tSettingItem< bool > se_coloredNamesConf( "FILTER_COLOR_NAMES", se_filterColorNames );
2358
static bool se_stripNames=true;
2359
tSettingItem< bool > se_stripConf( "FILTER_NAME_ENDS", se_stripNames );
2361
void ePlayerNetID::ReadSync(nMessage &m){
2362
nNetObject::ReadSync(m);
2368
if ( !se_bugColorOverflow )
2370
// clamp color values
2376
m.Read(pingCharity);
2378
// name as sent from the other end
2379
tString & remoteName = ( sn_GetNetState() == nCLIENT ) ? nameFromServer_ : nameFromClient_;
2383
tString oldName = remoteName;
2385
// read and shorten name, but don't update it yet
2389
if ( se_filterColorNames )
2390
remoteName = tColoredString::RemoveColors( remoteName );
2393
if ( se_stripNames )
2394
se_StripNameEnds( remoteName );
2396
se_CutString( remoteName, 16 );
2398
// zero strings or color code only strings are illegal
2399
if ( !IsLegalPlayerName( remoteName ) )
2401
// revert to old name if possible
2402
if ( IsLegalPlayerName( oldName ) )
2404
remoteName = oldName;
2408
// or replace it by a default value
2409
remoteName = "Player 1";
2414
// directly apply name changes sent from the server, they are safe.
2415
if ( sn_GetNetState() != nSERVER )
2422
if (sn_GetNetState()!=nSERVER)
2427
// read chat and spectator status
2428
unsigned short flags;
2431
if (Owner() != ::sn_myNetID)
2433
bool newChat = ( ( flags & 1 ) != 0 );
2434
bool newSpectate = ( ( flags & 2 ) != 0 );
2436
if ( chatting_ != newChat || spectating_ != newSpectate )
2437
lastActivity_ = tSysTimeFloat();
2438
chatting_ = newChat;
2439
spectating_ = newSpectate;
2445
if(sn_GetNetState()!=nSERVER)
2454
unsigned short newdisc;
2457
if (Owner() != ::sn_myNetID && sn_GetNetState()!=nSERVER)
2458
disconnected = newdisc;
2463
if ( nSERVER != sn_GetNetState() )
2465
eTeam *newCurrentTeam, *newNextTeam;
2468
m >> newCurrentTeam;
2471
if ( newCurrentTeam != currentTeam )
2473
if ( newCurrentTeam )
2474
newCurrentTeam->AddPlayerDirty( this );
2476
currentTeam->RemovePlayer( this );
2478
nextTeam = newNextTeam;
2487
m >> favoriteNumberOfPlayersPerTeam;
2488
m >> nameTeamAfterMe;
2490
// con << "Player info updated.\n";
2492
// make sure we did not accidentally overwrite values
2493
// ePlayer::Update();
2496
if ( nSERVER == sn_GetNetState() )
2499
nextTeam->UpdateProperties();
2502
currentTeam->UpdateProperties();
2507
nNOInitialisator<ePlayerNetID> ePlayerNetID_init(201,"ePlayerNetID");
2509
nDescriptor &ePlayerNetID::CreatorDescriptor() const{
2510
return ePlayerNetID_init;
2515
void ePlayerNetID::ControlObject(eNetGameObject *c){
2516
if (bool(object) && c!=object)
2526
c->team = currentTeam;
2529
object->SetPlayer(this);
2531
//con << "Player " << name << " controlles new object.\n";
2537
void ePlayerNetID::ClearObject(){
2540
tJUST_CONTROLLED_PTR< eNetGameObject > x=object;
2542
x->RemoveFromGame();
2543
x->SetPlayer( NULL );
2546
//con << "Player " << name << " controlles nothing.\n";
2550
// message of day presented to clients logging in
2551
tString se_greeting("");
2552
static tConfItemLine a_mod("MESSAGE_OF_DAY",se_greeting);
2554
void ePlayerNetID::Greet(){
2557
o.SetTemplateParameter(1, GetName() );
2558
o.SetTemplateParameter(2, sn_programVersion);
2559
o << "$player_welcome";
2565
if (se_greeting.Len()>1)
2566
s << se_greeting << "\n";
2569
sn_ConsoleOut(s,Owner());
2574
eNetGameObject *ePlayerNetID::Object() const{
2578
void se_SaveToLadderLog( tOutput const & out )
2580
if (sn_GetNetState()!=nCLIENT && !tRecorder::IsPlayingBack())
2583
if ( tDirectories::Var().Open(o, "ladderlog.txt", std::ios::app) )
2588
void se_SaveToScoreFile(const tOutput &o){
2592
if (sn_GetNetState()!=nCLIENT){
2594
if (sn_GetNetState()==nSERVER && !tRecorder::IsPlayingBack()){
2598
if ( tDirectories::Var().Open(o, "scorelog.txt", std::ios::app) )
2599
o << tColoredString::RemoveColors(s);
2607
// void ePlayerNetID::SetRubber(REAL rubber2) {rubberstatus = rubber2;}
2609
void ePlayerNetID::AddScore(int points,
2610
const tOutput& reasonwin,
2611
const tOutput& reasonlose)
2618
currentTeam->AddScore( points );
2620
tColoredString name;
2621
name << *this << tColoredString::ColorString(1,1,1);
2624
message.SetTemplateParameter(1, name);
2625
message.SetTemplateParameter(2, points > 0 ? points : -points);
2630
if (reasonwin.IsEmpty())
2631
message << "$player_win_default";
2633
message.Append(reasonwin);
2637
if (reasonlose.IsEmpty())
2638
message << "$player_lose_default";
2640
message.Append(reasonlose);
2643
sn_ConsoleOut(message);
2646
se_SaveToScoreFile(message);
2652
int ePlayerNetID::TotalScore() const
2656
return score;// + currentTeam->Score() * 5;
2665
void ePlayerNetID::SwapPlayersNo(int a,int b){
2666
if (0>a || se_PlayerNetIDs.Len()<=a)
2668
if (0>b || se_PlayerNetIDs.Len()<=b)
2673
ePlayerNetID *A=se_PlayerNetIDs(a);
2674
ePlayerNetID *B=se_PlayerNetIDs(b);
2676
se_PlayerNetIDs(b)=A;
2677
se_PlayerNetIDs(a)=B;
2683
void ePlayerNetID::SortByScore(){
2684
// bubble sort (AAARRGGH! but good for lists that change not much)
2690
for(i=se_PlayerNetIDs.Len()-2;i>=0;i--)
2691
if (se_PlayerNetIDs(i)->TotalScore() < se_PlayerNetIDs(i+1)->TotalScore() ){
2692
SwapPlayersNo(i,i+1);
2698
void ePlayerNetID::ResetScore(){
2700
for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
2701
se_PlayerNetIDs(i)->score=0;
2702
if (sn_GetNetState()==nSERVER)
2703
se_PlayerNetIDs(i)->RequestSync();
2706
for(i=eTeam::teams.Len()-1;i>=0;i--){
2707
eTeam::teams(i)->ResetScore();
2708
if (sn_GetNetState()==nSERVER)
2709
eTeam::teams(i)->RequestSync();
2712
ResetScoreDifferences();
2715
void ePlayerNetID::DisplayScores(){
2716
sr_ResetRenderState(true);
2718
REAL W=sr_screenWidth;
2719
REAL H=sr_screenHeight;
2733
rTextField c(-.7,.6,10/W,18/H);
2735
// print team ranking if there actually is a team with more than one player
2736
int maxPlayers = 20;
2737
for ( int i = eTeam::teams.Len() - 1; i >= 0; --i )
2739
if ( eTeam::teams[i]->NumPlayers() > 1 ||
2740
( eTeam::teams[i]->NumPlayers() == 1 && eTeam::teams[i]->Player(0)->Score() != eTeam::teams[i]->Score() ) )
2742
c << eTeam::Ranking();
2744
maxPlayers -= ( eTeam::teams.Len() > 6 ? 6 : eTeam::teams.Len() ) + 2;
2749
// print player ranking
2750
c << Ranking( maxPlayers );
2756
tString ePlayerNetID::Ranking( int MAX, bool cut ){
2761
if (se_PlayerNetIDs.Len()>0){
2762
ret << tColoredString::ColorString(1,.5,.5);
2763
ret << tOutput("$player_scoretable_name");
2764
ret << tColoredString::ColorString(1,1,1);
2765
ret.SetPos(17, cut );
2766
ret << tOutput("$player_scoretable_alive");
2767
ret.SetPos(24, cut );
2768
ret << tOutput("$player_scoretable_score");
2769
ret.SetPos(31, cut );
2770
ret << tOutput("$player_scoretable_ping");
2771
ret.SetPos(37, cut );
2772
ret << tOutput("$player_scoretable_team");
2773
ret.SetPos(53, cut );
2776
int max = se_PlayerNetIDs.Len();
2778
// wasting the last line with ... is as stupid if it stands for only
2780
if ( MAX == max + 1 )
2783
if ( max > MAX && MAX > 0 )
2788
for(int i=0;i<max;i++){
2789
tColoredString line;
2790
ePlayerNetID *p=se_PlayerNetIDs(i);
2791
// tColoredString name( p->GetColoredName() );
2792
// name.SetPos(16, cut );
2794
// This line is example of how we manually get the player color... could come in useful.
2795
// line << tColoredString::ColorString( p->r/15.0, p->g/15.0, p->b/15.0 ) << name << tColoredString::ColorString(1,1,1);
2797
// however, using the streaming operator is a lot cleaner. The example is left, as it really can be usefull in some situations.
2799
line.SetPos(17, false );
2800
if ( p->Object() && p->Object()->Alive() )
2802
line << tColoredString::ColorString(0,1,0) << tOutput("$player_scoretable_alive_yes") << tColoredString::ColorString(1,1,1);
2806
line << tColoredString::ColorString(1,0,0) << tOutput("$player_scoretable_alive_no") << tColoredString::ColorString(1,1,1);
2808
line.SetPos(24, cut );
2813
line.SetPos(31, cut );
2814
//line << "ping goes here";
2815
line << int(p->ping*1000);
2816
line.SetPos(37, cut );
2817
if ( p->currentTeam )
2819
//tString teamtemp = p->currentTeam->Name();
2820
//teamtemp.RemoveHex();
2821
line << tColoredString::RemoveColors(p->currentTeam->Name());
2822
line.SetPos(53, cut );
2826
line << tOutput("$player_scoretable_inactive");
2827
ret << line << "\n";
2829
if ( max < se_PlayerNetIDs.Len() )
2836
ret << tOutput("$player_scoretable_nobody");
2842
tColoredString & operator << (tColoredString &s,const ePlayer &p){
2843
return s << tColoredString::ColorString(p.rgb[0]/15.0,
2849
tColoredString & operator << (tColoredString &s,const ePlayerNetID &p){
2850
return s << p.GetColoredName();
2853
void ePlayerNetID::ClearAll(){
2854
for(int i=MAX_PLAYERS-1;i>=0;i--){
2855
ePlayer *local_p=ePlayer::PlayerConfig(i);
2857
local_p->netPlayer = NULL;
2861
void ePlayerNetID::CompleteRebuild(){
2866
static bool se_VisubleSpectatorsSupported()
2868
static nVersionFeature se_visibleSpectator(13);
2869
return sn_GetNetState() != nCLIENT || se_visibleSpectator.Supported(0);
2872
// Update the netPlayer_id list
2873
void ePlayerNetID::Update(){
2878
for(int i=0; i<MAX_PLAYERS; ++i ){
2879
bool in_game=ePlayer::PlayerIsInGame(i);
2880
ePlayer *local_p=ePlayer::PlayerConfig(i);
2882
tCONTROLLED_PTR(ePlayerNetID) &p=local_p->netPlayer;
2884
if (!p && in_game && ( !local_p->spectate || se_VisubleSpectatorsSupported() ) ) // insert new player
2886
p=tNEW(ePlayerNetID) (i);
2887
p->FindDefaultTeam();
2891
if (bool(p) && (!in_game || ( local_p->spectate && !se_VisubleSpectatorsSupported() ) ) && // remove player
2892
p->Owner() == ::sn_myNetID )
2894
p->RemoveFromGame();
2897
p->object->player = NULL;
2903
if (bool(p) && in_game){ // update
2904
p->favoriteNumberOfPlayersPerTeam=ePlayer::PlayerConfig(i)->favoriteNumberOfPlayersPerTeam;
2905
p->nameTeamAfterMe=ePlayer::PlayerConfig(i)->nameTeamAfterMe;
2906
p->r=ePlayer::PlayerConfig(i)->rgb[0];
2907
p->g=ePlayer::PlayerConfig(i)->rgb[1];
2908
p->b=ePlayer::PlayerConfig(i)->rgb[2];
2909
p->pingCharity=::pingCharity;
2911
// update spectator status
2912
if ( p->spectating_ != local_p->spectate )
2914
p->spectating_ = local_p->spectate;
2917
tString newName( ePlayer::PlayerConfig(i)->Name() );
2919
if ( ::sn_GetNetState() != nCLIENT || newName != p->nameFromClient_ )
2924
p->SetName( ePlayer::PlayerConfig(i)->Name() );
2929
// update the ping charity
2930
int old_c=sn_pingCharityServer;
2931
sn_pingCharityServer=::pingCharity;
2933
if (sn_GetNetState()==nCLIENT)
2935
sn_pingCharityServer+=100000;
2938
for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
2939
ePlayerNetID *pni=se_PlayerNetIDs(i);
2941
int new_ps=pni->pingCharity;
2942
new_ps+=int(pni->ping*500);
2944
if (new_ps< sn_pingCharityServer)
2945
sn_pingCharityServer=new_ps;
2947
if (sn_pingCharityServer<0)
2948
sn_pingCharityServer=0;
2949
if (old_c!=sn_pingCharityServer)
2952
o.SetTemplateParameter(1, old_c);
2953
o.SetTemplateParameter(2, sn_pingCharityServer);
2954
o << "$player_pingcharity_changed";
2958
// update team assignment
2959
bool assigned = true;
2963
for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
2965
ePlayerNetID* player = se_PlayerNetIDs(i);
2967
// only assign new team if it is possible
2968
if ( player->NextTeam() != player->CurrentTeam() &&
2969
( !player->NextTeam() || player->NextTeam()->PlayerMayJoin( player ) )
2972
player->UpdateTeam();
2973
if ( player->NextTeam() == player->CurrentTeam() )
2979
if ( sn_GetNetState() != nCLIENT )
2981
for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
2983
ePlayerNetID* player = se_PlayerNetIDs(i);
2985
// announce unfullfilled wishes
2986
if ( player->NextTeam() != player->CurrentTeam() && player->NextTeam() )
2988
tOutput message( "$player_joins_team_wish",
2990
player->NextTeam()->Name() );
2992
sn_ConsoleOut( message );
2994
// if team change is futile because team play is disabled,
2995
// delete the team change wish
2996
if ( eTeam::maxPlayers <= 1 )
2997
player->SetTeam( player->CurrentTeam() );
3002
// update the teams as well
3003
for (i=eTeam::teams.Len()-1; i>=0; --i)
3005
eTeam::teams(i)->UpdateProperties();
3008
// get rid of deleted netobjects
3009
nNetObject::ClearAllDeleted();
3012
// wait at most this long for any player to leave chat state...
3013
static REAL se_playerWaitMax = 10.0f;
3014
static tSettingItem< REAL > se_playerWaitMaxConf( "PLAYER_CHAT_WAIT_MAX", se_playerWaitMax );
3016
// and no more than this much measured relative to his non-chatting time.
3017
static REAL se_playerWaitFraction = .05f;
3018
static tSettingItem< REAL > se_playerWaitFractionConf( "PLAYER_CHAT_WAIT_FRACTION", se_playerWaitFraction );
3020
// flag to only account one player for the chat break
3021
static bool se_playerWaitSingle = false;
3022
static tSettingItem< bool > se_playerWaitSingleConf( "PLAYER_CHAT_WAIT_SINGLE", se_playerWaitSingle );
3024
// flag to only let the team leader pause the timer
3025
static bool se_playerWaitTeamleader = true;
3026
static tSettingItem< bool > se_playerWaitTeamleaderConf( "PLAYER_CHAT_WAIT_TEAMLEADER", se_playerWaitTeamleader );
3028
// wait for players to leave chat state
3029
bool ePlayerNetID::WaitToLeaveChat()
3031
static bool lastReturn = false;
3032
static double lastTime = 0;
3033
static ePlayerNetID * lastPlayer = 0; // the last player that caused a pause. Use only for comparison, the pointer may be bad. Don't dereference!
3034
double time = tSysTimeFloat();
3035
REAL dt = time - lastTime;
3040
// account for non-break pause: give players additional pause time
3041
for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
3043
ePlayerNetID* player = se_PlayerNetIDs(i);
3044
if ( dt > 1.0 || !player->chatting_ )
3046
player->wait_ += se_playerWaitFraction * dt;
3047
if ( player->wait_ > se_playerWaitMax )
3049
player->wait_ = se_playerWaitMax;
3060
// the chatting player with the most wait seconds left
3061
ePlayerNetID * maxPlayer = 0;
3064
// iterate over chatting players
3065
for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
3067
ePlayerNetID* player = se_PlayerNetIDs(i);
3068
if ( player->CurrentTeam() && player->chatting_ && ( !se_playerWaitTeamleader || player->CurrentTeam()->OldestHumanPlayer() == player ) )
3070
// account for waiting time if everyone is to get his time reduced
3071
if ( !se_playerWaitSingle )
3073
player->wait_ -= dt;
3076
if ( player->wait_ > 0 )
3086
// determine player we'll wait for longest
3087
if ( ( maxPlayer != lastPlayer || NULL == maxPlayer ) && ( player->wait_ > maxWait || player == lastPlayer ) && player->wait_ > 0 )
3089
maxWait = player->wait_;
3095
// account for waiting time if only one player should get his waiting time reduced
3096
if ( se_playerWaitSingle && maxPlayer )
3098
maxPlayer->wait_ -= dt;
3101
if ( maxPlayer->wait_ < 0 )
3103
maxPlayer->wait_ = 0;
3107
static double lastPrint = -2;
3109
// print information: who are we waiting for?
3110
if ( maxPlayer && maxPlayer != lastPlayer && tSysTimeFloat() - lastPrint > 1 )
3112
sn_ConsoleOut( tOutput( "$gamestate_chat_wait", maxPlayer->GetName(), int(10*maxPlayer->wait_)*.1f ) );
3113
lastPlayer = maxPlayer;
3116
if ( lastPlayer == maxPlayer )
3118
lastPrint = tSysTimeFloat();
3121
// store values for future reference
3128
// time in chat mode before a player is no longer spawned
3129
static REAL se_chatterRemoveTime = 180.0;
3130
static tSettingItem< REAL > se_chatterRemoveTimeConf( "CHATTER_REMOVE_TIME", se_chatterRemoveTime );
3132
// time without keypresses before a player is no longer spawned
3133
static REAL se_idleRemoveTime = 0;
3134
static tSettingItem< REAL > se_idleRemoveTimeConf( "IDLE_REMOVE_TIME", se_idleRemoveTime );
3136
// time without keypresses before a player is kicked
3137
static REAL se_idleKickTime = 0;
3138
static tSettingItem< REAL > se_idleKickTimeConf( "IDLE_KICK_TIME", se_idleKickTime );
3140
//removes chatbots and idling players from the game
3141
void ePlayerNetID::RemoveChatbots()
3143
// nothing to be done on the clients
3144
if ( nCLIENT == sn_GetNetState() )
3147
// determine the length of the last round
3148
static double lastTime = 0;
3149
double currentTime = tSysTimeFloat();
3150
REAL roundTime = currentTime - lastTime;
3151
lastTime = currentTime;
3153
// go through all players that don't have a team assigned currently, and assign one
3154
for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
3156
ePlayerNetID *p = se_PlayerNetIDs(i);
3157
if ( p && p->IsHuman() )
3159
// time allowed to be idle
3160
REAL idleTime = p->IsChatting() ? se_chatterRemoveTime : se_idleRemoveTime;
3162
// determine whether the player should have a team
3163
bool shouldHaveTeam = idleTime <= 0 || p->LastActivity() - roundTime < idleTime;
3164
shouldHaveTeam &= !p->IsSpectating();
3166
tColoredString name;
3167
name << *p << tColoredString::ColorString(1,.5,.5);
3169
// see to it that the player has or has not a team.
3170
if ( shouldHaveTeam )
3172
if ( !p->CurrentTeam() )
3174
p->FindDefaultTeam();
3179
if ( p->CurrentTeam() )
3186
// kick idle players (Removes player from list, this must be the last operation of the loop)
3187
if ( se_idleKickTime > 0 && se_idleKickTime < p->LastActivity() - roundTime )
3189
sn_KickUser( p->Owner(), tOutput( "$network_kill_idle" ) );
3191
// if many players get kicked with one client, the array may have changed
3192
if ( i >= se_PlayerNetIDs.Len() )
3193
i = se_PlayerNetIDs.Len()-1;
3199
void ePlayerNetID::ThrowOutDisconnected()
3202
// find all disconnected players
3204
for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
3205
ePlayerNetID *pni=se_PlayerNetIDs(i);
3206
if (pni->disconnected)
3208
// remove it from the list of players (so it won't be deleted twice...)
3209
se_PlayerNetIDs.Remove(pni, pni->listID);
3213
se_PlayerReferences.ReleaseAll();
3216
void ePlayerNetID::GetScoreFromDisconnectedCopy()
3220
for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
3221
ePlayerNetID *pni=se_PlayerNetIDs(i);
3222
if (pni->disconnected && pni->GetName() == GetName() && pni->Owner() == 0)
3225
con << GetName() << " reconnected.\n";
3233
ControlObject(pni->Object());
3234
// object->ePlayer = this;
3240
pni->disconnected = false;
3241
se_PlayerNetIDs.Remove(pni, pni->listID);
3242
se_PlayerReferences.Remove( pni ); // really delete it without a message
3248
static bool show_scores=false;
3249
static bool ass=true;
3251
void se_AutoShowScores(){
3257
void se_UserShowScores(bool show){
3261
void se_SetShowScoresAuto(bool a){
3266
static void scores(){
3268
if ( se_mainGameTimer )
3269
ePlayerNetID::DisplayScores();
3271
show_scores = false;
3276
static rPerFrameTask pf(&scores);
3278
static bool force_small_cons(){
3282
static rSmallConsoleCallback sc(&force_small_cons);
3285
show_scores = false;
3290
static uActionGlobal score("SCORE");
3293
static bool sf(REAL x){
3294
if (x>0) show_scores = !show_scores;
3298
static uActionGlobalFunc saf(&score,&sf);
3301
static rCenterDisplayCallback c_d(&cd);
3303
tOutput& operator << (tOutput& o, const ePlayerNetID& p)
3313
tCallbackString *eCallbackGreeting::anchor = NULL;
3314
ePlayerNetID* eCallbackGreeting::greeted = NULL;
3316
tString eCallbackGreeting::Greet(ePlayerNetID* player)
3319
return Exec(anchor);
3322
eCallbackGreeting::eCallbackGreeting(STRINGRETFUNC* f)
3323
:tCallbackString(anchor, f)
3327
void ePlayerNetID::GreetHighscores(tString &s){
3328
s << eCallbackGreeting::Greet(this);
3330
// gHighscoresBase::Greet(this,o);
3335
// *******************
3337
// *******************
3338
void ePlayerNetID::SetChatting ( ChatFlags flag, bool chatting )
3340
if ( sn_GetNetState() == nSTANDALONE && flag == ChatFlags_Menu )
3349
this->RequestSync();
3355
chatFlags_ &= ~flag;
3356
if ( 0 == chatFlags_ )
3359
this->RequestSync();
3366
// *******************
3367
// * team management *
3368
// *******************
3370
// put a new player into a default team
3371
void ePlayerNetID::FindDefaultTeam( )
3373
// only the server should do this, the client does not have the full information on how to do do it right.
3374
if ( sn_GetNetState() == nCLIENT || !se_assignTeamAutomatically || spectating_ )
3377
static bool recursion = false;
3391
// find the team with the least number of players on it
3393
for ( int i=eTeam::teams.Len()-1; i>=0; --i )
3395
eTeam *t = eTeam::teams( i );
3396
if ( t->IsHuman() && ( !min || min->NumHumanPlayers() > t->NumHumanPlayers() ) )
3401
eTeam::teams.Len() >= eTeam::minTeams &&
3402
min->PlayerMayJoin( this ) &&
3403
( !eTeam::NewTeamAllowed() || ( min->NumHumanPlayers() > 0 && min->NumHumanPlayers() < favoriteNumberOfPlayersPerTeam ) )
3405
SetTeamWish( min ); // join the team
3406
else if ( eTeam::NewTeamAllowed() )
3407
CreateNewTeamWish(); // create a new team
3409
// yes, if all teams are full and no team creation is allowed, the player stays without team and will not be spawned.
3414
// register me in the given team (callable on the server)
3415
void ePlayerNetID::SetTeam( eTeam* newTeam )
3417
// check if the team change is legal
3418
tASSERT ( !newTeam || nCLIENT != sn_GetNetState() );
3420
SetTeamForce( newTeam );
3422
if (newTeam && ( !newTeam->PlayerMayJoin( this ) || IsSpectating() ) )
3425
message.SetTemplateParameter( 1, GetName() );
3427
message.SetTemplateParameter(2, newTeam->Name() );
3429
message.SetTemplateParameter(2, "NULL");
3430
message << "$player_nojoin_team";
3432
sn_ConsoleOut( message, Owner() );
3437
// register me in the given team (callable on the server)
3438
void ePlayerNetID::SetTeamForce( eTeam* newTeam )
3440
// check if the team change is legal
3441
tASSERT ( !newTeam || nCLIENT != sn_GetNetState() );
3446
// register me in the given team (callable on the server)
3447
void ePlayerNetID::UpdateTeam()
3449
// check if work is needed
3450
if ( nextTeam == currentTeam )
3455
// check if the team change is legal
3456
if ( nCLIENT == sn_GetNetState() )
3461
if ( bool( nextTeam ) && !nextTeam->PlayerMayJoin( this ) )
3464
message.SetTemplateParameter(1, GetName() );
3466
message.SetTemplateParameter(2, nextTeam->Name() );
3468
message.SetTemplateParameter(2, "NULL");
3469
message << "$player_nojoin_team";
3471
sn_ConsoleOut( message, Owner() );
3478
void ePlayerNetID::UpdateTeamForce()
3480
// check if work is needed
3481
if ( nextTeam == currentTeam )
3486
eTeam *oldTeam = currentTeam;
3489
nextTeam->AddPlayer ( this );
3491
oldTeam->RemovePlayer( this );
3493
if( nCLIENT != sn_GetNetState() && GetRefcount() > 0 )
3499
// create a new team and join it (on the server)
3500
void ePlayerNetID::CreateNewTeam()
3502
// check if the team change is legal
3503
tASSERT ( nCLIENT != sn_GetNetState() );
3505
if ( !eTeam::NewTeamAllowed() ||
3506
( bool( currentTeam ) && ( currentTeam->NumHumanPlayers() == 1 ) ) ||
3510
message.SetTemplateParameter(1, GetName() );
3511
message << "$player_nocreate_team";
3513
sn_ConsoleOut( message, Owner() );
3521
// create the new team and join it
3522
tJUST_CONTROLLED_PTR< eTeam > newTeam = tNEW( eTeam );
3524
nextTeam->AddScore( score );
3526
// directly if possible
3533
const unsigned short TEAMCHANGE = 0;
3534
const unsigned short NEW_TEAM = 1;
3537
// express the wish to be part of the given team (always callable)
3538
void ePlayerNetID::SetTeamWish(eTeam* newTeam)
3540
if ( nCLIENT == sn_GetNetState() && Owner() == sn_myNetID )
3542
nMessage* m = NewControlMessage();
3553
// directly join if possible to keep counts up to date
3559
// express the wish to create a new team and join it
3560
void ePlayerNetID::CreateNewTeamWish()
3562
if ( nCLIENT == sn_GetNetState() )
3564
nMessage* m = NewControlMessage();
3575
// receive the team control wish
3576
void ePlayerNetID::ReceiveControlNet(nMessage &m)
3581
switch (messageType)
3595
// NULL team probably means that the change target does not
3596
// exist any more. Create a new team instead.
3599
sn_ConsoleOut( tOutput( "$player_joins_team_noex" ), Owner() );
3603
// check if the resulting message is obnoxious
3604
bool redundant = ( nextTeam == newTeam );
3605
bool obnoxious = ( nextTeam != currentTeam || redundant );
3609
// announce the change
3610
if ( bool(nextTeam) && !redundant )
3613
message.SetTemplateParameter(1, tColoredString::RemoveColors(GetName()));
3614
message.SetTemplateParameter(2, tColoredString::RemoveColors(nextTeam->Name()) );
3615
message << "$player_joins_team";
3617
sn_ConsoleOut( message );
3619
// count it as spam if it is obnoxious
3621
chatSpam_.CheckSpam( 4.0, Owner() );
3633
void ePlayerNetID::Color( REAL&a_r, REAL&a_g, REAL&a_b ) const
3635
if ( ( static_cast<bool>(currentTeam) ) && ( currentTeam->IsHuman() ) )
3646
// don't tolerate color overflow in a real team
3647
if ( currentTeam->NumPlayers() > 1 )
3657
a_r=(r_w*r + w*currentTeam->R())/( 15.0 * ( w + r_w ) );
3658
a_g=(g_w*g + w*currentTeam->G())/( 15.0 * ( w + g_w ) );
3659
a_b=(b_w*b + w*currentTeam->B())/( 15.0 * ( w + b_w ) );
3669
void ePlayerNetID::TrailColor( REAL&a_r, REAL&a_g, REAL&a_b ) const
3671
Color( a_r, a_g, a_b );
3674
if ( ( static_cast<bool>(currentTeam) ) && ( currentTeam->IsHuman() ) )
3677
a_r=(2*r + w*currentTeam->R())/( 15.0 * ( w + 2 ) );
3678
a_g=(2*g + w*currentTeam->G())/( 15.0 * ( w + 2 ) );
3679
a_b=(2*b + w*currentTeam->B())/( 15.0 * ( w + 2 ) );
3691
void ePlayerNetID::AddRef()
3693
nNetObject::AddRef();
3696
void ePlayerNetID::Release()
3698
nNetObject::Release();
3702
// reads a network ID from the stream, either the number or my user name
3703
static unsigned short se_ReadUser( std::istream &s )
3705
// read name of player to be kicked
3709
// try to convert it into a number and reference it as that
3710
int num = name.toInt();
3711
if ( num >= 1 && num <= MAXCLIENTS && sn_Connections[num].socket )
3717
// compare the read name with the players' user names
3718
for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
3720
ePlayerNetID* p = se_PlayerNetIDs(i);
3721
if ( p && p->GetUserName() == name )
3723
int owner = p->Owner();
3729
con << tOutput( "$network_kick_notfound", name );
3737
static void se_KickConf(std::istream &s)
3740
int num = se_ReadUser( s );
3746
if ( num > 0 && !s.good() )
3748
sn_KickUser( num , reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick" );
3752
con << "Usage: KICK <user ID or name> <Reason>\n";
3757
static tConfItemFunc se_kickConf("KICK",&se_KickConf);
3760
static void se_BanConf(std::istream &s)
3763
int num = se_ReadUser( s );
3765
if ( num == 0 && !s.good() )
3767
con << "Usage: BAN <user ID or name> <time in minutes(defaults to 60)> <Reason>\n";
3782
nMachine::GetMachine( num ).Ban( banTime * 60, reason );
3783
sn_DisconnectUser( num , reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick" );
3787
static tConfItemFunc se_banConf("BAN",&se_BanConf);
3789
static void Kill_conf(std::istream &s)
3791
// read name of player to be killed
3794
int num = name.toInt();
3796
for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
3798
ePlayerNetID* p = se_PlayerNetIDs(i);
3800
// check whether it's p who should be killed,
3801
// either by comparing the owner or the name.
3802
bool itsHim = false;
3804
itsHim = ( p->Owner() == num );
3806
itsHim = ( p->GetUserName() == name );
3808
// kill the player's game object
3812
p->Object()->Kill();
3819
o.SetTemplateParameter( 1, name );
3820
o << "$network_kick_notfound";
3824
static tConfItemFunc kill_conf("KILL",&Kill_conf);
3826
static void players_conf(std::istream &s)
3828
/* for ( int i=0; i < se_PlayerNetIDs.Len(); i++ )
3830
ePlayerNetID* p = se_PlayerNetIDs(i);
3832
o << "Player " << p->Owner() << ": " << p->name;
3833
if (p->isLoggedIn())
3834
o << " (logged in)\n";
3836
o << " (logged out)\n";
3839
for ( int i2 = se_PlayerNetIDs.Len()-1; i2>=0; --i2 )
3841
ePlayerNetID* p2 = se_PlayerNetIDs(i2);
3845
tos << p2->GetUserName();
3846
if (p2->isLoggedIn())
3847
tos << " (logged in)";
3849
tos << " (logged out)";
3855
static tConfItemFunc players("PLAYERS",&players_conf);
3857
static tString sg_url;
3858
static tSettingItem< tString > sg_urlConf( "URL", sg_url );
3860
static tString sg_options("Nothing special.");
3862
static tConfItemLine sg_optionsConf( "SERVER_OPTIONS", sg_options );
3865
class gServerInfoAdmin: public nServerInfoAdmin
3868
gServerInfoAdmin(){};
3871
virtual tString GetUsers() const
3875
for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
3877
ePlayerNetID* p = se_PlayerNetIDs(i);
3880
ret << p->GetName() << "\n";
3887
virtual tString GetOptions() const
3889
se_CutString( sg_options, 240 );
3893
virtual tString GetUrl() const
3895
se_CutString( sg_url, 75 );
3900
static gServerInfoAdmin sg_serverAdmin;
3902
// ******************************************************************************************
3906
// ******************************************************************************************
3909
// ******************************************************************************************
3911
void ePlayerNetID::UpdateName( void )
3913
// store old name for password re-request and name change message
3914
tColoredString oldprintname;
3915
tString oldUserName( GetUserName() );
3916
oldprintname << *this << tColoredString::ColorString(.5,1,.5);
3918
// apply client change, stripping excess spaces
3919
if ( sn_GetNetState() != nCLIENT )
3921
if ( se_stripNames )
3922
se_StripNameEnds( nameFromClient_ );
3924
// disallow name changes if there was a kick vote recently
3925
if ( !bool(this->voter_) || voter_->AllowNameChange() || nameFromServer_.Len() <= 1 )
3927
nameFromServer_ = nameFromClient_;
3929
else if ( nameFromServer_ != nameFromClient_ )
3931
// inform victim to avoid complaints
3932
tOutput message( "$player_rename_rejected", nameFromServer_, nameFromClient_ );
3933
sn_ConsoleOut( message, Owner() );
3937
nameFromClient_ = nameFromServer_;
3941
// remove colors from name
3942
tString newName = tColoredString::RemoveColors( nameFromServer_ );
3943
tString newUserName;
3944
FilterName( newName, newUserName );
3946
// test if it is already taken, find an alternative name if so.
3947
if ( sn_GetNetState() != nCLIENT && !se_allowImposters && se_IsNameTaken( newUserName, this ) )
3949
// Remove possilble trailing digit.
3950
if ( newName.Len() > 2 && isdigit(newName(newName.Len()-2)) )
3952
newName = newName.SubStr( 0, newName.Len()-2 );
3955
// append numbers until the name is free
3956
for ( int i=2; i<1000; ++i )
3958
tString testName(newName);
3961
// cut the beginning if the name is too long
3962
if ( testName.Len() > 17 )
3963
testName = testName.SubStr( testName.Len() - 17 );
3965
FilterName( testName, newUserName );
3967
if ( !se_IsNameTaken( newUserName, this ) )
3974
// rudely overwrite name from client
3975
nameFromServer_ = newName;
3978
// set the colored name to the name from the client, set trailing color to white
3979
coloredName_.Clear();
3982
coloredName_ << tColoredString::ColorString(r,g,b) << nameFromServer_;
3984
if ( name_ != newName )
3986
// copy it to the name, removing colors of course
3989
// remove spaces and possibly other nasties for the user name
3990
FilterName( name_, userName_ );
3992
if (sn_GetNetState()!=nCLIENT)
3994
// sync the new name
3999
tColoredString printname;
4000
printname << *this << tColoredString::ColorString(.5,1,.5);
4002
mess.SetTemplateParameter(1, printname);
4003
mess.SetTemplateParameter(2, oldprintname);
4005
if (oldUserName.Len()<=1 && GetUserName().Len()>=1){
4007
#ifdef KRAWALL_SERVER
4008
if (sn_GetNetState() == nSERVER && Owner() != sn_myNetID )
4011
nAuthentification::RequestLogin(GetUserName(), Owner(), "$login_request_first");
4015
// print spectating join message (regular join messages are handled by eTeam)
4016
if ( IsSpectating() )
4018
mess << "$player_entered_spectator";
4019
sn_ConsoleOut(mess);
4025
ladder << "PLAYER_ENTERED " << userName_ << " " << nMachine::GetMachine(Owner()).GetIP() << "\n";
4026
se_SaveToLadderLog(ladder);
4031
else if (strcmp(oldUserName,GetUserName()))
4033
#ifdef KRAWALL_SERVER
4034
if (sn_GetNetState() == nSERVER && Owner() != sn_myNetID )
4036
nAuthentification::RequestLogin(GetUserName(), Owner(), "$login_request_namechange");
4038
userName_ = oldUserName; // restore the old name until the new one is authenticated
4041
mess << "$player_renamed";
4042
sn_ConsoleOut(mess);
4046
ladder << "PLAYER_RENAMED " << oldUserName << " " << userName_ << " " << nMachine::GetMachine(Owner()).GetIP() << "\n";
4047
se_SaveToLadderLog(ladder);
4054
// filters illegal player characters
4055
class ePlayerCharacterFilter
4058
ePlayerCharacterFilter()
4063
// map all unknown characters to underscores
4064
for (i=255; i>0; --i)
4069
// leave ASCII characters as they are
4070
for (i=126; i>32; --i)
4074
// but convert uppercase characters to lowercase
4075
for (i='Z'; i>='A'; --i)
4077
filter[i] = i + ('a' - 'A');
4080
//! map umlauts and stuff to their base characters
4081
SetMap(0xc0,0xc5,'a');
4082
SetMap(0xd1,0xd6,'o');
4083
SetMap(0xd9,0xdD,'u');
4085
SetMap(0xe0,0xe5,'a');
4086
SetMap(0xe8,0xeb,'e');
4087
SetMap(0xec,0xef,'i');
4088
SetMap(0xf0,0xf6,'o');
4089
SetMap(0xf9,0xfc,'u');
4091
// ok, some of those are a bit questionable, but still better than _...
4128
//map 0 to o because they look similar
4131
// TODO: make this data driven.
4134
char Filter( unsigned char in )
4136
return filter[ static_cast< unsigned int >( in )];
4139
void SetMap( int in1, int in2, unsigned char out)
4141
tASSERT( in2 <= 0xff );
4142
tASSERT( 0 <= in1 );
4143
tASSERT( in1 < in2 );
4144
for( int i = in2; i >= in1; --i )
4148
void SetMap( unsigned char in, unsigned char out)
4150
filter[ static_cast< unsigned int >( in ) ] = out;
4156
static bool se_IsUnderscore( char c )
4161
// ******************************************************************************************
4165
// ******************************************************************************************
4167
//! @param in input name
4168
//! @param out output name cleared to be usable as a username
4170
// ******************************************************************************************
4172
void ePlayerNetID::FilterName( tString const & in, tString & out )
4175
static ePlayerCharacterFilter filter;
4176
out = tColoredString::RemoveColors( in );
4178
// filter out illegal characters
4179
for ( i = out.Len()-1; i>=0; --i )
4183
c = filter.Filter( c );
4186
// strip leading and trailing unknown characters
4187
se_StripMatchingEnds( out, se_IsUnderscore, se_IsUnderscore );
4190
// ******************************************************************************************
4194
// ******************************************************************************************
4196
//! @param in input name
4197
//! @return output name cleared to be usable as a username
4199
// ******************************************************************************************
4201
tString ePlayerNetID::FilterName( tString const & in )
4204
FilterName( in, out );
4208
// ******************************************************************************************
4212
// ******************************************************************************************
4214
//! @param name this player's name without colors. to set
4215
//! @return A reference to this to allow chaining
4217
// ******************************************************************************************
4219
ePlayerNetID & ePlayerNetID::SetName( tString const & name )
4221
this->nameFromClient_ = name;
4222
this->nameFromClient_.NetFilter();
4224
// replace empty name
4225
if ( !IsLegalPlayerName( nameFromClient_ ) )
4226
nameFromClient_ = "Player 1";
4228
if ( sn_GetNetState() != nCLIENT )
4229
nameFromServer_ = nameFromClient_;
4236
// ******************************************************************************************
4240
// ******************************************************************************************
4242
//! @param name this player's name without colors. to set
4243
//! @return A reference to this to allow chaining
4245
// ******************************************************************************************
4247
ePlayerNetID & ePlayerNetID::SetName( char const * name )
4249
SetName( tString( name ) );
4253
// allow enemies from the same IP?
4254
static bool se_allowEnemiesSameIP = false;
4255
static tSettingItem< bool > se_allowEnemiesSameIPConf( "ALLOW_ENEMIES_SAME_IP", se_allowEnemiesSameIP );
4256
// allow enemies from the same client?
4257
static bool se_allowEnemiesSameClient = false;
4258
static tSettingItem< bool > se_allowEnemiesSameClientConf( "ALLOW_ENEMIES_SAME_CLIENT", se_allowEnemiesSameClient );
4260
// *******************************************************************************
4264
// *******************************************************************************
4266
//! @param a first player to compare
4267
//! @param b second player to compare
4268
//! @return true if a should be able to score against b or vice versa
4270
// *******************************************************************************
4272
bool ePlayerNetID::Enemies( ePlayerNetID const * a, ePlayerNetID const * b )
4274
// the client does not need a true answer
4275
if ( sn_GetNetState() == nCLIENT )
4278
// no scoring if one of them does not exist
4282
// no scoring for two players from the same IP
4283
if ( !se_allowEnemiesSameIP && a->Owner() != 0 && a->GetMachine() == b->GetMachine() )
4286
// no scoring for two players from the same client
4287
if ( !se_allowEnemiesSameClient && a->Owner() != 0 && a->Owner() == b->Owner() )
4294
// *******************************************************************************
4296
// * RegisterWithMachine
4298
// *******************************************************************************
4301
// *******************************************************************************
4303
void ePlayerNetID::RegisterWithMachine( void )
4305
if ( !registeredMachine_ )
4307
// store machine (it won't get deleted while this object exists; the player count prevents that)
4308
registeredMachine_ = &this->nNetObject::DoGetMachine();
4309
registeredMachine_->AddPlayer();
4313
// *******************************************************************************
4315
// * UnregisterWithMachine
4317
// *******************************************************************************
4320
// *******************************************************************************
4322
void ePlayerNetID::UnregisterWithMachine( void )
4324
if ( registeredMachine_ )
4326
registeredMachine_->RemovePlayer();
4327
registeredMachine_ = 0;
4331
// *******************************************************************************
4335
// *******************************************************************************
4337
//! @return the machine this object belongs to
4339
// *******************************************************************************
4341
nMachine & ePlayerNetID::DoGetMachine( void ) const
4343
// return machine I'm registered at, otherwise whatever the base class thinks
4344
if ( registeredMachine_ )
4345
return *registeredMachine_;
4347
return nNetObject::DoGetMachine();
4350
// *******************************************************************************
4354
// *******************************************************************************
4358
// *******************************************************************************
4360
REAL ePlayerNetID::LastActivity( void ) const
4362
return tSysTimeFloat() - lastActivity_;
4365
// *******************************************************************************
4367
// * ResetScoreDifferences
4369
// *******************************************************************************
4372
// *******************************************************************************
4374
void ePlayerNetID::ResetScoreDifferences( void )
4376
for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
4378
ePlayerNetID* p = se_PlayerNetIDs(i);
4379
if ( bool(p->Object()) && p->IsHuman() )
4380
p->lastScore_ = p->score;
4384
// *******************************************************************************
4386
// * LogScoreDifferences
4388
// *******************************************************************************
4391
// *******************************************************************************
4393
void ePlayerNetID::LogScoreDifferences( void )
4395
for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
4397
ePlayerNetID* p = se_PlayerNetIDs(i);
4398
p->LogScoreDifference();
4402
// *******************************************************************************
4404
// * LogScoreDifference
4406
// *******************************************************************************
4409
// *******************************************************************************
4411
void ePlayerNetID::LogScoreDifference( void )
4413
if ( lastScore_ > IMPOSSIBLY_LOW_SCORE && IsHuman() )
4416
int scoreDifference = score - lastScore_;
4417
lastScore_ = IMPOSSIBLY_LOW_SCORE;
4418
ret << "ROUND_SCORE " << scoreDifference << " " << GetUserName();
4420
ret << " " << FilterName( currentTeam->Name() );
4422
se_SaveToLadderLog( ret );