~swag/armagetronad/0.2.9-sty+ct+ap-fork

« back to all changes in this revision

Viewing changes to src/engine/ePlayer.cpp

  • Committer: luke-jr
  • Date: 2006-05-29 01:55:42 UTC
  • Revision ID: svn-v3-list-QlpoOTFBWSZTWZvbKhsAAAdRgAAQABK6798QIABURMgAAaeoNT1TxT1DQbKaeobXKiyAmlWT7Y5MkdJOtXDtB7w7DOGFBHiOBxaUIu7HQyyQSvxdyRThQkJvbKhs:7d95bf1e-0414-0410-9756-b78462a59f44:armagetronad%2Fbranches%2F0.2.8%2Farmagetronad:4612
Unify tags/branches of modules released together

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "tMemManager.h"
 
29
#include "ePlayer.h"
 
30
//#include "tInitExit.h"
 
31
#include "tConfiguration.h"
 
32
#include "eNetGameObject.h"
 
33
#include "rConsole.h"
 
34
#include "eTimer.h"
 
35
#include "tSysTime.h"
 
36
#include "rFont.h"
 
37
#include "uMenu.h"
 
38
#include "tToDo.h"
 
39
#include "rScreen.h"
 
40
#include <string>
 
41
#include <fstream>
 
42
#include <iostream>
 
43
#include <deque>
 
44
#include "rRender.h"
 
45
#include "rSysdep.h"
 
46
#include "nAuthentification.h"
 
47
#include "tDirectories.h"
 
48
#include "eTeam.h"
 
49
#include "eVoter.h"
 
50
#include "tReferenceHolder.h"
 
51
#include "tRandom.h"
 
52
#include "nServerInfo.h"
 
53
#include "tRecorder.h"
 
54
#include "nConfig.h"
 
55
#include <time.h>
 
56
 
 
57
tCONFIG_ENUM( eCamMode );
 
58
 
 
59
tList<ePlayerNetID> se_PlayerNetIDs;
 
60
static ePlayer* se_Players = NULL;
 
61
 
 
62
static bool se_assignTeamAutomatically = true;
 
63
static tSettingItem< bool > se_assignTeamAutomaticallyConf( "AUTO_TEAM", se_assignTeamAutomatically );
 
64
 
 
65
static tReferenceHolder< ePlayerNetID > se_PlayerReferences;
 
66
 
 
67
class PasswordStorage
 
68
{
 
69
public:
 
70
    tString username;
 
71
    nKrawall::nScrambledPassword password;
 
72
    bool save;
 
73
 
 
74
    PasswordStorage(): save(false){};
 
75
};
 
76
 
 
77
 
 
78
static tArray<PasswordStorage> S_passwords;
 
79
 
 
80
void se_DeletePasswords(){
 
81
    S_passwords.SetLen(0);
 
82
 
 
83
    st_SaveConfig();
 
84
 
 
85
    /*
 
86
 
 
87
      REAL timeout = tSysTimeFloat() + 3;
 
88
     
 
89
      while(tSysTimeFloat() < timeout){
 
90
       
 
91
      sr_ResetRenderState(true);
 
92
      rViewport::s_viewportFullscreen.Select();
 
93
       
 
94
      sr_ClearGL();
 
95
       
 
96
      uMenu::GenericBackground();
 
97
 
 
98
      REAL w=16*3/640.0;
 
99
      REAL h=32*3/480.0;
 
100
       
 
101
       
 
102
      //REAL middle=-.6;
 
103
       
 
104
      Color(1,1,1);
 
105
      DisplayText(0,.8,w,h,tOutput("$network_opts_deletepw_complete"));
 
106
       
 
107
      sr_SwapGL();
 
108
      }
 
109
 
 
110
    */
 
111
 
 
112
    tConsole::Message("$network_opts_deletepw_complete", tOutput(), 5);
 
113
}
 
114
 
 
115
class tConfItemPassword:public tConfItemBase{
 
116
public:
 
117
    tConfItemPassword():tConfItemBase("PASSWORD"){}
 
118
    ~tConfItemPassword(){};
 
119
 
 
120
    // write the complete passwords
 
121
    virtual void WriteVal(std::ostream &s){
 
122
        int i;
 
123
        bool first = 1;
 
124
        for (i = S_passwords.Len()-1; i>=0; i--)
 
125
        {
 
126
            PasswordStorage &storage = S_passwords[i];
 
127
            if (storage.save)
 
128
            {
 
129
                if (!first)
 
130
                    s << "\nPASSWORD\t";
 
131
                first = false;
 
132
 
 
133
                s << "1 ";
 
134
                nKrawall::WriteScrambledPassword(storage.password, s);
 
135
                s << '\t' << storage.username;
 
136
            }
 
137
        }
 
138
        if (first)
 
139
            s << "0 ";
 
140
    }
 
141
 
 
142
    // read one password
 
143
    virtual void ReadVal(std::istream &s){
 
144
        //    static char in[20];
 
145
        int test;
 
146
        s >> test;
 
147
        if (test != 0)
 
148
        {
 
149
            PasswordStorage &storage = S_passwords[S_passwords.Len()];
 
150
            nKrawall::ReadScrambledPassword(s, storage.password);
 
151
            storage.username.ReadLine(s);
 
152
            storage.save = true;
 
153
        }
 
154
    }
 
155
};
 
156
 
 
157
static tConfItemPassword p;
 
158
 
 
159
// password request menu
 
160
class eMenuItemPassword: public uMenuItemString
 
161
{
 
162
public:
 
163
    eMenuItemPassword(uMenu *M,tString &c):
 
164
    uMenuItemString(M,"$login_password_title","$login_password_help",c){}
 
165
    virtual ~eMenuItemPassword(){}
 
166
 
 
167
    virtual void Render(REAL x,REAL y,REAL alpha=1,bool selected=0)
 
168
    {
 
169
        tString* pwback = content;
 
170
        tString star;
 
171
        for (int i=content->Len()-2; i>=0; i--)
 
172
            star << "*";
 
173
        content = &star;
 
174
        uMenuItemString::Render(x,y, alpha, selected);
 
175
        content = pwback;
 
176
    }
 
177
 
 
178
    virtual bool Event(SDL_Event &e){
 
179
#ifndef DEDICATED
 
180
        if (e.type==SDL_KEYDOWN &&
 
181
                (e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
 
182
 
 
183
            MyMenu()->Exit();
 
184
            return true;
 
185
        }
 
186
        //    else if (e.type==SDL_KEYDOWN &&
 
187
        //           uActionGlobal::IsBreakingGlobalBind(e.key.keysym.sym))
 
188
        //      return su_HandleEvent(e);
 
189
        else
 
190
#endif
 
191
            return uMenuItemString::Event(e);
 
192
    }
 
193
};
 
194
 
 
195
 
 
196
static bool tr(){return true;}
 
197
 
 
198
static uMenu *S_login=NULL;
 
199
 
 
200
static void cancelLogin()
 
201
{
 
202
    if (S_login)
 
203
        S_login->Exit();
 
204
    S_login = NULL;
 
205
}
 
206
 
 
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);
 
212
 
 
213
static void PasswordCallback(tString& username,
 
214
                             const tString& message,
 
215
                             nKrawall::nScrambledPassword& scrambled,
 
216
                             bool failure)
 
217
{
 
218
    int i;
 
219
 
 
220
    // find the player with the given username:
 
221
    ePlayer* p = NULL;
 
222
    for (i = MAX_PLAYERS-1; i>=0 && !p; i--)
 
223
    {
 
224
        ePlayer * perhaps = ePlayer::PlayerConfig(i);
 
225
        tString filteredName;
 
226
        ePlayerNetID::FilterName( perhaps->name, filteredName );
 
227
        if ( filteredName == username )
 
228
            p = perhaps;
 
229
    }
 
230
 
 
231
    // or the given raw name
 
232
    for (i = MAX_PLAYERS-1; i>=0 && !p; i--)
 
233
    {
 
234
        ePlayer * perhaps = ePlayer::PlayerConfig(i);
 
235
        if ( perhaps->name == username )
 
236
            p = perhaps;
 
237
    }
 
238
 
 
239
    // or just any player, what the heck.
 
240
    if (!p)
 
241
        p = ePlayer::PlayerConfig(0);
 
242
 
 
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);
 
248
 
 
249
    if (!storage)
 
250
    {
 
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);
 
255
 
 
256
        if (!storage)
 
257
            storage = &S_passwords[S_passwords.Len()];
 
258
 
 
259
        failure = true;
 
260
    }
 
261
 
 
262
    // immediately return the stored password if it was not marked as wrong:
 
263
    if (!failure)
 
264
    {
 
265
        username = storage->username;
 
266
        memcpy(scrambled, storage->password, sizeof(nKrawall::nScrambledPassword));
 
267
        return;
 
268
    }
 
269
    else
 
270
        storage->username.Clear();
 
271
 
 
272
    // password was not stored. Request it from user:
 
273
 
 
274
    uMenu login(message, false);
 
275
 
 
276
    // password storage;
 
277
    tString password;
 
278
 
 
279
 
 
280
    eMenuItemPassword pw(&login, password);
 
281
    uMenuItemString us(&login, "$login_username","$login_username_help", p->name);
 
282
 
 
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",
 
289
                      -1);
 
290
    storepw.NewChoice("$login_storepw_mem_text",
 
291
                      "$login_storepw_mem_help",
 
292
                      0);
 
293
    storepw.NewChoice("$login_storepw_disk_text",
 
294
                      "$login_storepw_disk_help",
 
295
                      1);
 
296
 
 
297
    uMenuItemFunction cl(&login, "$login_cancel", "$login_cancel_help", &cancelLogin);
 
298
 
 
299
    login.SetSelected(1);
 
300
 
 
301
    // force a small console while we are in here
 
302
    rSmallConsoleCallback cb(&tr);
 
303
 
 
304
    S_login = &login;
 
305
    login.Enter();
 
306
 
 
307
    // return username/scrambled password
 
308
    if (S_login)
 
309
    {
 
310
        username = p->name;
 
311
        nKrawall::ScramblePassword(password, scrambled);
 
312
 
 
313
        // clear the PW from memory
 
314
        for (i = password.Len()-2; i>=0; i--)
 
315
            password(i) = 'a';
 
316
 
 
317
        if (se_PasswordStorageMode >= 0)
 
318
        {
 
319
            storage->username = p->name;
 
320
            memcpy(storage->password, scrambled, sizeof(nKrawall::nScrambledPassword));
 
321
            storage->save = (se_PasswordStorageMode > 0);
 
322
        }
 
323
    }
 
324
    else // or log out
 
325
        sn_SetNetState(nSTANDALONE);
 
326
 
 
327
    S_login = NULL;
 
328
}
 
329
 
 
330
 
 
331
 
 
332
static void ResultCallback(const tString& usernameUnfiltered,
 
333
                           const tString& origUsername,
 
334
                           int user, bool success)
 
335
{
 
336
    int i;
 
337
 
 
338
    // filter the user name
 
339
    tString username;
 
340
    ePlayerNetID::FilterName( usernameUnfiltered, username );
 
341
 
 
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 );
 
349
 
 
350
    if (success)
 
351
    {
 
352
        bool found = false;
 
353
 
 
354
        // authenticate the user that logged in and change his name
 
355
        for (i = se_PlayerNetIDs.Len()-1; i>=0 && !found; i--)
 
356
        {
 
357
            ePlayerNetID *p = se_PlayerNetIDs(i);
 
358
            if (p->Owner() == user && p->GetUserName() == origUsername)
 
359
            {
 
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);
 
364
                p->Auth();
 
365
                found = true;
 
366
            }
 
367
        }
 
368
 
 
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--)
 
372
        {
 
373
            ePlayerNetID *p = se_PlayerNetIDs(i);
 
374
            if (p->Owner() == user && p->GetUserName() == username)
 
375
            {
 
376
                p->Auth();
 
377
                found = true;
 
378
            }
 
379
        }
 
380
 
 
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--)
 
384
        {
 
385
            ePlayerNetID *p = se_PlayerNetIDs(i);
 
386
            if (p->Owner() == user)
 
387
            {
 
388
                p->SetUserName( username );
 
389
                p->Auth();
 
390
                found = true;
 
391
            }
 
392
        }
 
393
 
 
394
        // request other logins
 
395
        for (i = se_PlayerNetIDs.Len()-1; i>=0; i--)
 
396
        {
 
397
            ePlayerNetID *p = se_PlayerNetIDs(i);
 
398
            p->IsAuth();
 
399
        }
 
400
    }
 
401
    else
 
402
    {
 
403
        if (sn_GetNetState() == nSERVER  && user != sn_myNetID )
 
404
            nAuthentification::RequestLogin(username, user, "$login_request_failed", true);
 
405
    }
 
406
}
 
407
 
 
408
 
 
409
 
 
410
 
 
411
// menu item to silence selected players
 
412
class eMenuItemSilence: public uMenuItemToggle
 
413
{
 
414
public:
 
415
    eMenuItemSilence(uMenu *m, ePlayerNetID* p )
 
416
            : uMenuItemToggle( m, tOutput(""),tOutput("$silence_player_help" ),  p->AccessSilenced() )
 
417
    {
 
418
        this->title.Clear();
 
419
        this->title.SetTemplateParameter(1, p->GetColoredName() );
 
420
        this->title << "$silence_player_text";
 
421
        player_ = p;
 
422
    }
 
423
 
 
424
    ~eMenuItemSilence()
 
425
    {
 
426
    }
 
427
private:
 
428
    tCONTROLLED_PTR( ePlayerNetID ) player_;            // keep player referenced
 
429
};
 
430
 
 
431
 
 
432
 
 
433
 
 
434
// menu where you can silence players
 
435
void ePlayerNetID::SilenceMenu()
 
436
{
 
437
    uMenu menu( "$player_police_silence_text" );
 
438
 
 
439
    int size = se_PlayerNetIDs.Len();
 
440
    eMenuItemSilence** items = tNEW( eMenuItemSilence* )[ size ];
 
441
 
 
442
    int i;
 
443
    for ( i = size-1; i>=0; --i )
 
444
    {
 
445
        ePlayerNetID* player = se_PlayerNetIDs[ i ];
 
446
        if ( player->IsHuman() )
 
447
        {
 
448
            items[i] = tNEW( eMenuItemSilence )( &menu, player );
 
449
        }
 
450
        else
 
451
        {
 
452
            items[i] = 0;
 
453
        }
 
454
 
 
455
    }
 
456
 
 
457
    menu.Enter();
 
458
 
 
459
    for ( i = size - 1; i>=0; --i )
 
460
    {
 
461
        if( items[i] ) delete items[i];
 
462
    }
 
463
    delete[] items;
 
464
}
 
465
 
 
466
void ePlayerNetID::PoliceMenu()
 
467
{
 
468
    uMenu menu( "$player_police_text" );
 
469
 
 
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 );
 
472
 
 
473
    menu.Enter();
 
474
}
 
475
 
 
476
 
 
477
 
 
478
 
 
479
 
 
480
 
 
481
 
 
482
 
 
483
 
 
484
 
 
485
 
 
486
 
 
487
 
 
488
 
 
489
 
 
490
 
 
491
 
 
492
 
 
493
 
 
494
 
 
495
 
 
496
 
 
497
 
 
498
 
 
499
 
 
500
static char *default_instant_chat[]=
 
501
    {"/team \\",
 
502
     "/msg \\",
 
503
     "/me \\",
 
504
     "LOL!",
 
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.",
 
515
     "Speed for weaks!",
 
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!",
 
521
     "WD! No points!",
 
522
     "/me is a noob.",
 
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\"?",
 
526
     NULL};
 
527
 
 
528
 
 
529
 
 
530
 
 
531
ePlayer * ePlayer::PlayerConfig(int p){
 
532
    uPlayerPrototype *P = uPlayerPrototype::PlayerConfig(p);
 
533
    return dynamic_cast<ePlayer*>(P);
 
534
    //  return (ePlayer*)P;
 
535
}
 
536
 
 
537
void   ePlayer::StoreConfitem(tConfItemBase *c){
 
538
    tASSERT(CurrentConfitem < PLAYER_CONFITEMS);
 
539
    configuration[CurrentConfitem++] = c;
 
540
}
 
541
 
 
542
void   ePlayer::DeleteConfitems(){
 
543
    while (CurrentConfitem>0){
 
544
        CurrentConfitem--;
 
545
        tDESTROY(configuration[CurrentConfitem]);
 
546
    }
 
547
}
 
548
 
 
549
uActionPlayer *ePlayer::se_instantChatAction[MAX_INSTANT_CHAT];
 
550
 
 
551
static const tString& se_UserName()
 
552
{
 
553
    srand( (unsigned)time( NULL ) );
 
554
 
 
555
    static tString ret( getenv( "USER" ) );
 
556
    return ret;
 
557
}
 
558
 
 
559
ePlayer::ePlayer(){
 
560
    nAuthentification::SetUserPasswordCallback(&PasswordCallback);
 
561
    nAuthentification::SetLoginResultCallback (&ResultCallback);
 
562
 
 
563
    nameTeamAfterMe = false;
 
564
    favoriteNumberOfPlayersPerTeam = 3;
 
565
 
 
566
    CurrentConfitem = 0;
 
567
 
 
568
    bool getUserName = false;
 
569
    if ( id == 0 )
 
570
    {
 
571
        name = se_UserName();
 
572
        getUserName = ( name.Len() > 1 );
 
573
    }
 
574
    if ( !getUserName )
 
575
        name << "Player " << id+1;
 
576
 
 
577
    tString confname;
 
578
 
 
579
    confname << "PLAYER_"<< id+1;
 
580
    StoreConfitem(tNEW(tConfItemLine) (confname,
 
581
                                       "$player_name_confitem_help",
 
582
                                       name));
 
583
 
 
584
    confname.Clear();
 
585
    confname << "CAMCENTER_"<< id+1;
 
586
    centerIncamOnTurn=true;
 
587
    StoreConfitem(tNEW(tConfItem<bool>)
 
588
                  (confname,
 
589
                   "$camcenter_help",
 
590
                   centerIncamOnTurn) );
 
591
 
 
592
    confname.Clear();
 
593
    startCamera=CAMERA_SMART;
 
594
    confname << "START_CAM_"<< id+1;
 
595
    StoreConfitem(tNEW(tConfItem<eCamMode>) (confname,
 
596
                  "$start_cam_help",
 
597
                  startCamera));
 
598
 
 
599
    confname.Clear();
 
600
    confname << "START_FOV_"<< id+1;
 
601
    startFOV=90;
 
602
    StoreConfitem(tNEW(tConfItem<int>) (confname,
 
603
                                        "$start_fov_help",
 
604
                                        startFOV));
 
605
    confname.Clear();
 
606
 
 
607
    confname.Clear();
 
608
    confname << "SMART_GLANCE_CUSTOM_"<< id+1;
 
609
    smartCustomGlance=true;
 
610
    StoreConfitem(tNEW(tConfItem<bool>) (confname,
 
611
                                         "$camera_smart_glance_custom_help",
 
612
                                         smartCustomGlance));
 
613
    confname.Clear();
 
614
 
 
615
    int i;
 
616
    for(i=CAMERA_SMART_IN;i>=0;i--){
 
617
        confname << "ALLOW_CAM_"<< id+1 << "_" << i;
 
618
        StoreConfitem(tNEW(tConfItem<bool>) (confname,
 
619
                                             "$allow_cam_help",
 
620
                                             allowCam[i]));
 
621
        allowCam[i]=true;
 
622
        confname.Clear();
 
623
    }
 
624
 
 
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]));
 
630
        confname.Clear();
 
631
    }
 
632
 
 
633
    for(i=0; i < MAX_INSTANT_CHAT;i++){
 
634
        if (!default_instant_chat[i])
 
635
            break;
 
636
 
 
637
        instantChatString[i]=default_instant_chat[i];
 
638
    }
 
639
 
 
640
    confname << "SPECTATOR_MODE_"<< id+1;
 
641
    StoreConfitem(tNEW(tConfItem<bool>)(confname,
 
642
                                        "$spectator_mode_help",
 
643
                                        spectate));
 
644
    spectate=false;
 
645
    confname.Clear();
 
646
 
 
647
    confname << "NAME_TEAM_AFTER_PLAYER_"<< id+1;
 
648
    StoreConfitem(tNEW(tConfItem<bool>)(confname,
 
649
                                        "$name_team_after_player_help",
 
650
                                        nameTeamAfterMe));
 
651
    nameTeamAfterMe=false;
 
652
    confname.Clear();
 
653
 
 
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;
 
659
    confname.Clear();
 
660
 
 
661
 
 
662
    confname << "AUTO_INCAM_"<< id+1;
 
663
    autoSwitchIncam=false;
 
664
    StoreConfitem(tNEW(tConfItem<bool>) (confname,
 
665
                                         "$auto_incam_help",
 
666
                                         autoSwitchIncam));
 
667
    confname.Clear();
 
668
 
 
669
    confname << "CAMWOBBLE_"<< id+1;
 
670
    wobbleIncam=false;
 
671
    StoreConfitem(tNEW(tConfItem<bool>) (confname,
 
672
                                         "$camwobble_help",
 
673
                                         wobbleIncam));
 
674
 
 
675
    confname.Clear();
 
676
    confname << "COLOR_B_"<< id+1;
 
677
    StoreConfitem(tNEW(tConfItem<int>) (confname,
 
678
                                        "$color_b_help",
 
679
                                        rgb[2]));
 
680
 
 
681
    confname.Clear();
 
682
    confname << "COLOR_G_"<< id+1;
 
683
    StoreConfitem(tNEW(tConfItem<int>) (confname,
 
684
                                        "$color_g_help",
 
685
                                        rgb[1]));
 
686
 
 
687
    confname.Clear();
 
688
    confname << "COLOR_R_"<< id+1;
 
689
    StoreConfitem(tNEW(tConfItem<int>) (confname,
 
690
                                        "$color_r_help",
 
691
                                        rgb[0]));
 
692
    confname.Clear();
 
693
 
 
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;
 
698
 
 
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};
 
702
 
 
703
    rgb[0]=int(R[cid]*15);
 
704
    rgb[1]=int(G[cid]*15);
 
705
    rgb[2]=int(B[cid]*15);
 
706
 
 
707
    cam=NULL;
 
708
}
 
709
 
 
710
ePlayer::~ePlayer(){
 
711
    tCHECK_DEST;
 
712
    DeleteConfitems();
 
713
}
 
714
 
 
715
#ifndef DEDICATED
 
716
void ePlayer::Render(){
 
717
    if (cam) cam->Render();
 
718
}
 
719
#endif
 
720
 
 
721
static void se_DisplayChatLocally( ePlayerNetID* p, const tString& say )
 
722
{
 
723
#ifdef DEBUG_X
 
724
    if (strstr( say, "BUG" ) )
 
725
    {
 
726
        st_Breakpoint();
 
727
    }
 
728
#endif
 
729
 
 
730
    if ( p && !p->IsSilenced() )
 
731
    {
 
732
        //tColoredString say2( say );
 
733
        //say2.RemoveHex();
 
734
        tColoredString message;
 
735
        message << *p;
 
736
        message << tColoredString::ColorString(1,1,.5);
 
737
        message << ": " << say << '\n';
 
738
 
 
739
        con << message;
 
740
    }
 
741
}
 
742
 
 
743
static void se_DisplayChatLocallyClient( ePlayerNetID* p, const tString& message )
 
744
{
 
745
    if ( p && !p->IsSilenced() )
 
746
    {
 
747
        con << message << "\n";
 
748
    }
 
749
}
 
750
 
 
751
static nVersionFeature se_chatRelay( 3 );
 
752
 
 
753
//!todo: lower this number or increase network version before next release
 
754
static nVersionFeature se_chatHandlerClient( 6 );
 
755
 
 
756
// chat message from client to server
 
757
void handle_chat( nMessage& );
 
758
static nDescriptor chat_handler(200,handle_chat,"Chat");
 
759
 
 
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");
 
763
 
 
764
void handle_chat_client(nMessage &m)
 
765
{
 
766
    if(sn_GetNetState()!=nSERVER)
 
767
    {
 
768
        unsigned short id;
 
769
        m.Read(id);
 
770
        tColoredString say;
 
771
        m >> say;
 
772
 
 
773
        tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
 
774
 
 
775
        se_DisplayChatLocallyClient( p, say );
 
776
    }
 
777
}
 
778
 
 
779
// appends chat message to something
 
780
template< class TARGET >
 
781
void se_AppendChat( TARGET & out, tString const & message )
 
782
{
 
783
    if ( message.Len() <= se_SpamMaxLen*2 )
 
784
        out << message;
 
785
    else
 
786
    {
 
787
        tString cut( message );
 
788
        cut.SetLen( se_SpamMaxLen*2 );
 
789
        out << cut;
 
790
    }
 
791
}
 
792
 
 
793
// builds a colored chat string
 
794
static tColoredString se_BuildChatString( ePlayerNetID const * sender, tString const & message )
 
795
{
 
796
    tColoredString console;
 
797
    console << *sender;
 
798
    console << tColoredString::ColorString(1,1,.5) << ": ";
 
799
    se_AppendChat( console, message );
 
800
 
 
801
    return console;
 
802
}
 
803
 
 
804
// Build a colored /team message
 
805
static tColoredString se_BuildChatString( eTeam const *team, ePlayerNetID const *sender, tString const &message )
 
806
{
 
807
    tColoredString console;
 
808
    console << *sender;
 
809
 
 
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";
 
814
    }
 
815
    else {
 
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();
 
822
    }
 
823
 
 
824
    console << tColoredString::ColorString(1,1,.5) << ": ";
 
825
    se_AppendChat( console, message );
 
826
 
 
827
    return console;
 
828
}
 
829
 
 
830
// builds a colored chat /msg string
 
831
static tColoredString se_BuildChatString( ePlayerNetID const * sender, ePlayerNetID const * receiver, tString const & message )
 
832
{
 
833
    tColoredString console;
 
834
    console << *sender;
 
835
    console << tColoredString::ColorString(1,1,.5) << " --> ";
 
836
    console << *receiver;
 
837
    console << tColoredString::ColorString(1,1,.5) << ": ";
 
838
    se_AppendChat( console, message );
 
839
 
 
840
    return console;
 
841
}
 
842
 
 
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 )
 
845
{
 
846
    tASSERT( player );
 
847
 
 
848
    nMessage *m=tNEW(nMessage) (chat_handler_client);
 
849
 
 
850
    m->Write( player->ID() );
 
851
    *m << toConsole;
 
852
 
 
853
    return m;
 
854
}
 
855
 
 
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 )
 
858
{
 
859
    tASSERT( sender );
 
860
 
 
861
    return se_ServerControlledChatMessageConsole( sender, se_BuildChatString( sender, message ) );
 
862
}
 
863
 
 
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 )
 
866
{
 
867
    tASSERT( sender );
 
868
    tASSERT( receiver );
 
869
 
 
870
    return se_ServerControlledChatMessageConsole( sender, se_BuildChatString( sender, receiver, message ) );
 
871
}
 
872
 
 
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 )
 
875
{
 
876
    tASSERT( team );
 
877
    tASSERT( sender );
 
878
 
 
879
    return se_ServerControlledChatMessageConsole( sender, se_BuildChatString(team, sender, message) );
 
880
}
 
881
 
 
882
// pepares a chat message the client has to put together
 
883
static nMessage* se_NewChatMessage( ePlayerNetID const * player, tString const & message )
 
884
{
 
885
    tASSERT( player );
 
886
 
 
887
    nMessage *m=tNEW(nMessage) (chat_handler);
 
888
    m->Write( player->ID() );
 
889
    se_AppendChat( *m, message );
 
890
 
 
891
    return m;
 
892
}
 
893
 
 
894
// prepares a very old style chat message: just a regular remote console output message
 
895
static nMessage* se_OldChatMessage( tString const & line )
 
896
{
 
897
    return sn_ConsoleOutMessage( line + "\n" );
 
898
}
 
899
 
 
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 )
 
902
{
 
903
    tASSERT( player );
 
904
 
 
905
    return se_OldChatMessage( se_BuildChatString( player, message ) );
 
906
}
 
907
 
 
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 )
 
911
{
 
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 );
 
916
 
 
917
    // send them to the users, depending on what they understand
 
918
    for ( int user = MAXCLIENTS; user > 0; --user )
 
919
    {
 
920
        if ( sn_Connections[ user ].socket )
 
921
        {
 
922
            if ( se_chatHandlerClient.Supported( user ) )
 
923
                mServerControlled->Send( user );
 
924
            else if ( se_chatRelay.Supported( user ) )
 
925
                mNew->Send( user );
 
926
            else
 
927
                mOld->Send( user );
 
928
        }
 
929
    }
 
930
}
 
931
 
 
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 )
 
935
{
 
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 );
 
940
 
 
941
    // send them to the users, depending on what they understand
 
942
    for ( int user = MAXCLIENTS; user > 0; --user )
 
943
    {
 
944
        if ( sn_Connections[ user ].socket )
 
945
        {
 
946
            if ( se_chatHandlerClient.Supported( user ) )
 
947
                mServerControlled->Send( user );
 
948
            else if ( se_chatRelay.Supported( user ) )
 
949
                mNew->Send( user );
 
950
            else
 
951
                mOld->Send( user );
 
952
        }
 
953
    }
 
954
}
 
955
 
 
956
// sends a private message from sender to receiver
 
957
void se_SendPrivateMessage( ePlayerNetID const * sender, ePlayerNetID const * receiver, tString const & message )
 
958
{
 
959
    tASSERT( sender );
 
960
    tASSERT( receiver );
 
961
 
 
962
    // determine receiver client id
 
963
    int cid = receiver->Owner();
 
964
 
 
965
    // determine wheter the receiver knows about the server controlled chat message
 
966
    if ( se_chatHandlerClient.Supported( cid ) )
 
967
    {
 
968
        // send server controlled message
 
969
        se_ServerControlledChatMessage( sender, receiver, message )->Send( cid );
 
970
    }
 
971
    else
 
972
    {
 
973
        tColoredString say;
 
974
        say << tColoredString::ColorString(1,1,.5) << "( --> ";
 
975
        say << *receiver;
 
976
        say << tColoredString::ColorString(1,1,.5) << " ) ";
 
977
        say << message;
 
978
 
 
979
        // format message containing receiver
 
980
        if ( se_chatRelay.Supported( cid ) )
 
981
        {
 
982
            // send client formatted message
 
983
            se_NewChatMessage( sender, say )->Send( cid );
 
984
        }
 
985
        else
 
986
        {
 
987
            // send console out message
 
988
            se_OldChatMessage( sender, say )->Send( cid );
 
989
        }
 
990
    }
 
991
}
 
992
 
 
993
// Sends a /team message
 
994
void se_SendTeamMessage( eTeam const * team, ePlayerNetID const * sender ,ePlayerNetID const * receiver, tString const & message )
 
995
{
 
996
    tASSERT( team );
 
997
    tASSERT( receiver );
 
998
    tASSERT( sender );
 
999
 
 
1000
    int clientID = receiver->Owner();
 
1001
    if ( clientID == 0 )
 
1002
        return;
 
1003
 
 
1004
    if ( se_chatHandlerClient.Supported( clientID ) ) {
 
1005
        se_ServerControlledChatMessage( team, sender, message )->Send( clientID );
 
1006
    }
 
1007
    else {
 
1008
        tColoredString say;
 
1009
        say << tColoredString::ColorString(1,1,.5) << "( " << *sender;
 
1010
 
 
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";
 
1015
        }
 
1016
        // ( foo (Blue Team) --> Red Team ) some message
 
1017
        else {
 
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();
 
1023
        }
 
1024
        say << tColoredString::ColorString(1,1,.5) << " ) ";
 
1025
        say << message;
 
1026
 
 
1027
        // format message containing receiver
 
1028
        if ( se_chatRelay.Supported( clientID ) )
 
1029
            // send client formatted message
 
1030
            se_NewChatMessage( sender, say )->Send( clientID );
 
1031
        else
 
1032
            // send console out message
 
1033
            se_OldChatMessage( sender, say )->Send( clientID );
 
1034
    }
 
1035
}
 
1036
 
 
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);
 
1041
    int a, b;
 
1042
    for (b=0; b<=n-m; ++b) {
 
1043
        for (a=0; a<m && search_for_text[a] == text_to_search[a+b]; ++a)
 
1044
            ;
 
1045
        if (a>=m)
 
1046
            // a match has been found
 
1047
            return true;
 
1048
    }
 
1049
    return false;
 
1050
}
 
1051
 
 
1052
ePlayerNetID * CompareBufferToPlayerNames( const tString & current_buffer, int & num_matches ) {
 
1053
    num_matches = 0;
 
1054
    ePlayerNetID * match = 0;
 
1055
 
 
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);
 
1059
 
 
1060
        // exact match?
 
1061
        if ( current_buffer == toMessage->GetUserName() )
 
1062
        {
 
1063
            num_matches = 1;
 
1064
            return toMessage;
 
1065
        }
 
1066
 
 
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.
 
1069
            num_matches+=1;
 
1070
        }
 
1071
    }
 
1072
 
 
1073
    // return result
 
1074
    return match;
 
1075
}
 
1076
 
 
1077
// returns a player ( not THE player, there may be more ) belonging to a user ID
 
1078
/*
 
1079
static ePlayerNetID * se_GetPlayerFromUserID( int uid )
 
1080
{
 
1081
    // run through all players and look for a match
 
1082
    for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a )
 
1083
    {
 
1084
        ePlayerNetID * p = se_PlayerNetIDs(a);
 
1085
        if ( p && p->Owner() == uid )
 
1086
            return p;
 
1087
    }
 
1088
 
 
1089
    // found nothing
 
1090
    return 0;
 
1091
}
 
1092
*/
 
1093
 
 
1094
// returns a player ( not THE player, there may be more ) belonging to a user ID that is still alive
 
1095
// static
 
1096
ePlayerNetID * se_GetAlivePlayerFromUserID( int uid )
 
1097
{
 
1098
    // run through all players and look for a match
 
1099
    for ( int a = se_PlayerNetIDs.Len()-1; a>=0; --a )
 
1100
    {
 
1101
        ePlayerNetID * p = se_PlayerNetIDs(a);
 
1102
        if ( p && p->Owner() == uid &&
 
1103
                ( ( p->Object() && p->Object()->Alive() ) ) )
 
1104
            return p;
 
1105
    }
 
1106
 
 
1107
    // found nothing
 
1108
    return 0;
 
1109
}
 
1110
 
 
1111
//The Base Remote Admin Password
 
1112
static tString sg_adminPass( "NONE" );
 
1113
static tConfItemLine sg_adminPassConf( "ADMIN_PASS", sg_adminPass );
 
1114
 
 
1115
#ifdef DEDICATED
 
1116
 
 
1117
// console with filter for remote admin output redirection
 
1118
class eAdminConsoleFilter:public tConsoleFilter{
 
1119
public:
 
1120
    eAdminConsoleFilter( int netID )
 
1121
            :netID_( netID )
 
1122
    {
 
1123
    }
 
1124
 
 
1125
    ~eAdminConsoleFilter()
 
1126
    {
 
1127
        sn_ConsoleOut( message_, netID_ );
 
1128
    }
 
1129
private:
 
1130
    // we want to come first, the admin should get unfiltered output
 
1131
    virtual int DoGetPriority() const{ return -100; }
 
1132
 
 
1133
    // don't actually filter; take line and add it to the message sent to the admin
 
1134
    virtual void DoFilterLine( tString &line )
 
1135
    {
 
1136
        //tColoredString message;
 
1137
        message_ << tColoredString::ColorString(1,.3,.3) << "RA: " << tColoredString::ColorString(1,1,1) << line << "\n";
 
1138
 
 
1139
        // don't let message grow indefinitely
 
1140
        if (message_.Len() > 600)
 
1141
        {
 
1142
            sn_ConsoleOut( message_, netID_ );
 
1143
            message_.Clear();
 
1144
        }
 
1145
    }
 
1146
 
 
1147
    int netID_;              // the network user ID to send the result to
 
1148
    tColoredString message_; // the console message for the remote administrator
 
1149
};
 
1150
 
 
1151
void handle_chat_admin_commands(tJUST_CONTROLLED_PTR< ePlayerNetID > &p, tString say){
 
1152
    if  (say.StartsWith("/login")) {
 
1153
        tString params("");
 
1154
        if (say.StrPos(" ") == -1)
 
1155
            return;
 
1156
        else
 
1157
            params = say.SubStr(say.StrPos(" ") + 1);
 
1158
 
 
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") {
 
1162
            p->beLoggedIn();
 
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);
 
1168
        }
 
1169
        else
 
1170
        {
 
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);
 
1176
        }
 
1177
    }
 
1178
    else  if (say.StartsWith("/logout")) {
 
1179
        if ( p->isLoggedIn() )
 
1180
        {
 
1181
            sn_ConsoleOut("You have been logged out!\n",p->Owner());
 
1182
        }
 
1183
        p->beNotLoggedIn();
 
1184
        p->setAccessLevel(0);
 
1185
    }
 
1186
    else  if (say.StartsWith("/admin")) {
 
1187
        if (!p->isLoggedIn())
 
1188
        {
 
1189
            sn_ConsoleOut("You are not logged in.\n",p->Owner());
 
1190
            return;
 
1191
        }
 
1192
 
 
1193
        tString params("");
 
1194
        if (say.StrPos(" ") == -1)
 
1195
            return;
 
1196
        else
 
1197
            params = say.SubStr(say.StrPos(" ") + 1);
 
1198
 
 
1199
        // install filter
 
1200
        eAdminConsoleFilter consoleFilter( p->Owner() );
 
1201
 
 
1202
        if ( tRecorder::IsPlayingBack() )
 
1203
        {
 
1204
            tConfItemBase::LoadPlayback();
 
1205
        }
 
1206
        else
 
1207
        {
 
1208
            std::stringstream s(static_cast< char const * >( params ) );
 
1209
            tConfItemBase::LoadAll(s);
 
1210
        }
 
1211
    }
 
1212
    else
 
1213
    {
 
1214
        sn_ConsoleOut("Unknown chat command.\n",p->Owner());
 
1215
    }
 
1216
}
 
1217
#endif
 
1218
 
 
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);
 
1223
 
 
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",
 
1227
        se_allowShuffleUp);
 
1228
 
 
1229
void handle_chat(nMessage &m){
 
1230
    nTimeRolling currentTime = tSysTimeFloat();
 
1231
    unsigned short id;
 
1232
    m.Read(id);
 
1233
    tColoredString say;
 
1234
    m >> say;
 
1235
 
 
1236
    tJUST_CONTROLLED_PTR< ePlayerNetID > p=dynamic_cast<ePlayerNetID *>(nNetObject::ObjectDangerous(id));
 
1237
 
 
1238
    // register player activity
 
1239
    if ( p )
 
1240
        p->lastActivity_ = currentTime;
 
1241
 
 
1242
    if(sn_GetNetState()==nSERVER){
 
1243
        if (p)
 
1244
        {
 
1245
            // check if the player is owned by the sender to avoid cheating
 
1246
            if( p->Owner() != m.SenderID() )
 
1247
            {
 
1248
                Cheater( m.SenderID() );
 
1249
                nReadError();
 
1250
                return;
 
1251
            }
 
1252
 
 
1253
            // spam protection:
 
1254
            REAL lengthMalus = say.Len() / 20.0;
 
1255
            if ( lengthMalus > 4.0 )
 
1256
            {
 
1257
                lengthMalus = 4.0;
 
1258
            }
 
1259
 
 
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() );
 
1264
                    return;
 
1265
                }
 
1266
            }
 
1267
 
 
1268
            // update last said record
 
1269
            {
 
1270
                for( short zz = 12; zz>=1; zz-- )
 
1271
                {
 
1272
                    p->lastSaid[zz] = p->lastSaid[zz-1];
 
1273
                    p->lastSaidTimes[zz] = p->lastSaidTimes[zz-1];
 
1274
                }
 
1275
 
 
1276
                p->lastSaid[0] = say;
 
1277
                p->lastSaidTimes[0] = currentTime;
 
1278
            }
 
1279
 
 
1280
            nSpamProtection::Level spamLevel = p->chatSpam_.CheckSpam( 1.0f + lengthMalus, m.SenderID() );
 
1281
            bool pass = false;
 
1282
 
 
1283
            if (say.StartsWith("/")) {
 
1284
                std::string sayStr(say);
 
1285
                std::istringstream s(sayStr);
 
1286
                tString command;
 
1287
                s >> command;
 
1288
                tString msg;
 
1289
                tConfItemBase::EatWhitespace(s);
 
1290
                msg.ReadLine(s);
 
1291
                if (command == "/me") {
 
1292
                    tColoredString console;
 
1293
                    console << tColoredString::ColorString(1,1,1)  << "* ";
 
1294
                    console << *p;
 
1295
                    console << tColoredString::ColorString(1,1,.5) << " " << msg;
 
1296
                    console << tColoredString::ColorString(1,1,1)  << " *";
 
1297
 
 
1298
                    tColoredString forOldClients;
 
1299
                    forOldClients << tColoredString::ColorString(1,1,1)  << "*"
 
1300
                    << tColoredString::ColorString(1,1,.5) << msg
 
1301
                    << tColoredString::ColorString(1,1,1)  << "*";
 
1302
 
 
1303
                    se_BroadcastChatLine( p, console, forOldClients );
 
1304
                    return;
 
1305
                }
 
1306
                else if (command == "/teamleave") {
 
1307
                    if ( se_assignTeamAutomatically )
 
1308
                    {
 
1309
                        sn_ConsoleOut("Sorry, does not work with automatic team assignment.\n", p->Owner() );
 
1310
                        return;
 
1311
                    }
 
1312
 
 
1313
                    eTeam * leftTeam = p->NextTeam();
 
1314
                    if ( leftTeam )
 
1315
                    {
 
1316
                        if ( !leftTeam )
 
1317
                            leftTeam = p->CurrentTeam();
 
1318
 
 
1319
                        if ( leftTeam->NumPlayers() > 1 )
 
1320
                        {
 
1321
                            sn_ConsoleOut( tOutput( "$player_leave_team_wish",
 
1322
                                                    tColoredString::RemoveColors(p->GetName()),
 
1323
                                                    tColoredString::RemoveColors(leftTeam->Name()) ) );
 
1324
                        }
 
1325
                        else
 
1326
                        {
 
1327
                            sn_ConsoleOut( tOutput( "$player_leave_game_wish",
 
1328
                                                    tColoredString::RemoveColors(p->GetName()) ) );
 
1329
                        }
 
1330
                    }
 
1331
 
 
1332
                    p->SetTeamWish(0);
 
1333
                    return;
 
1334
                }
 
1335
                else if (command == "/teamshuffle" || command == "/shuffle") {
 
1336
                    // team position shuffling. Allow players to change their team setup.
 
1337
                    // syntax:
 
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())
 
1344
                    {
 
1345
                        sn_ConsoleOut(tOutput("$player_not_on_team"), p->Owner());
 
1346
                        return;
 
1347
                    }
 
1348
                    int len = p->CurrentTeam()->NumPlayers();
 
1349
                    int IDWish = len-1; // default to shuffle to the outside
 
1350
 
 
1351
                    // but read the target position as additional parameter
 
1352
                    if (msg.Len() > 1)
 
1353
                    {
 
1354
                        IDWish = IDNow;
 
1355
                        if ( msg[0] == '+' )
 
1356
                            IDWish += msg.toInt(1);
 
1357
                        else if ( msg[0] == '-' )
 
1358
                            IDWish -= msg.toInt(1);
 
1359
                        else
 
1360
                            IDWish = msg.toInt()-1;
 
1361
                    }
 
1362
 
 
1363
                    if (IDWish < 0)
 
1364
                        IDWish = 0;
 
1365
                    if (IDWish >= len)
 
1366
                        IDWish = len-1;
 
1367
 
 
1368
                    if ( !se_allowShuffleUp && IDWish < IDNow )
 
1369
                    {
 
1370
                        sn_ConsoleOut(tOutput("$player_noshuffleup"), p->Owner());
 
1371
                        return;
 
1372
                    }
 
1373
 
 
1374
                    if( IDNow == IDWish )
 
1375
                    {
 
1376
                        sn_ConsoleOut(tOutput("$player_noshuffle"), p->Owner());
 
1377
                    }
 
1378
 
 
1379
                    p->CurrentTeam()->Shuffle( IDNow, IDWish );
 
1380
                    return;
 
1381
                }
 
1382
                // Send a message to your team
 
1383
                else if (command == "/team") {
 
1384
                    eTeam *currentTeam = p->CurrentTeam();
 
1385
 
 
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
 
1387
                    {
 
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());
 
1393
 
 
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);
 
1399
                        }
 
1400
                    }
 
1401
                    else
 
1402
                    {
 
1403
                        sn_ConsoleOut(tOutput("$player_not_on_team"), p->Owner());
 
1404
                    }
 
1405
 
 
1406
                    return;
 
1407
                }
 
1408
                else if (command == "/msg") {
 
1409
                    int current_place=0; // current place in buffer_name.
 
1410
 
 
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];
 
1415
                        current_place++;
 
1416
                    }
 
1417
                    buffer_name[current_place] = '\0';
 
1418
                    buffer_name = ePlayerNetID::FilterName(buffer_name);
 
1419
 
 
1420
                    // Check for match
 
1421
                    int num_matches=-1;
 
1422
                    ePlayerNetID * receiver = CompareBufferToPlayerNames(buffer_name, num_matches);
 
1423
 
 
1424
                    // One match, send it.
 
1425
                    if (num_matches == 1) {
 
1426
                        // extract rest of message: it is the true message to send
 
1427
                        tString msg_core;
 
1428
                        while (current_place < msg.Len()-1) {
 
1429
                            current_place++;
 
1430
                            msg_core += msg[current_place];
 
1431
                        }
 
1432
 
 
1433
                        // build chat string
 
1434
                        tColoredString toServer = se_BuildChatString( p, receiver, msg_core );
 
1435
                        toServer << '\n';
 
1436
 
 
1437
                        // log locally
 
1438
                        sn_ConsoleOut(toServer,0);
 
1439
 
 
1440
                        // log to sender's console
 
1441
                        sn_ConsoleOut(toServer, p->Owner());
 
1442
 
 
1443
                        // send to receiver
 
1444
                        if ( p->Owner() != receiver->Owner())
 
1445
                            se_SendPrivateMessage( p, receiver, msg_core );
 
1446
                        return;
 
1447
                    }
 
1448
                    // More than than one match for the current buffer. Complain about that.
 
1449
                    else if (num_matches > 1) {
 
1450
                        tOutput toSender;
 
1451
                        toSender.SetTemplateParameter(1, buffer_name);
 
1452
                        toSender << "$msg_toomanymatches";
 
1453
                        sn_ConsoleOut(toSender,p->Owner());
 
1454
                        return;
 
1455
                    }
 
1456
                    // 0 matches. The user can't spell. Complain about that, too.
 
1457
                    else {
 
1458
                        tOutput toSender;
 
1459
                        toSender.SetTemplateParameter(1, buffer_name);
 
1460
                        toSender << "$msg_nomatch";
 
1461
                        sn_ConsoleOut(toSender, p->Owner());
 
1462
                        return;
 
1463
                    }
 
1464
                }
 
1465
#ifdef DEDICATED
 
1466
                else if (command == "/players") {
 
1467
                    for ( int i2 = se_PlayerNetIDs.Len()-1; i2>=0; --i2 )
 
1468
                    {
 
1469
                        ePlayerNetID* p2 = se_PlayerNetIDs(i2);
 
1470
                        tString tos;
 
1471
                        tos << p2->Owner();
 
1472
                        tos << ": ";
 
1473
                        tos << p2->GetUserName();
 
1474
                        if (p2->isLoggedIn())
 
1475
                            tos << " (logged in)";
 
1476
                        else
 
1477
                            tos << " (logged out)";
 
1478
                        tos << "\n";
 
1479
                        sn_ConsoleOut(tos, p->Owner());
 
1480
                    }
 
1481
                    return;
 
1482
                }
 
1483
                else {
 
1484
                    handle_chat_admin_commands(p, say);
 
1485
                    return;
 
1486
                }
 
1487
#endif
 
1488
            }
 
1489
 
 
1490
            if ( spamLevel < nSpamProtection::Level_Mild && say.Len() <= se_SpamMaxLen+2 && ( !p->IsSilenced() ) && pass != true )
 
1491
            {
 
1492
                se_BroadcastChat( p, say );
 
1493
                se_DisplayChatLocally( p, say);
 
1494
            }
 
1495
        }
 
1496
    }
 
1497
    else
 
1498
    {
 
1499
        se_DisplayChatLocally( p, say );
 
1500
    }
 
1501
}
 
1502
 
 
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 )
 
1506
{
 
1507
    // strip colors
 
1508
    tString stripped( tColoredString::RemoveColors( name ) );
 
1509
 
 
1510
    // and count non-whitespace characters
 
1511
    for ( int i = stripped.Len()-2; i>=0; --i )
 
1512
    {
 
1513
        if ( !isblank( stripped(i) ) )
 
1514
            return true;
 
1515
    }
 
1516
 
 
1517
    return false;
 
1518
}
 
1519
 
 
1520
void ePlayerNetID::Chat(const tString &s_orig)
 
1521
{
 
1522
    tColoredString s( s_orig );
 
1523
    s.NetFilter();
 
1524
 
 
1525
    switch (sn_GetNetState())
 
1526
    {
 
1527
    case nCLIENT:
 
1528
        {
 
1529
            se_NewChatMessage( this, s )->BroadCast();
 
1530
            break;
 
1531
        }
 
1532
    case nSERVER:
 
1533
        {
 
1534
            se_BroadcastChat( this, s );
 
1535
        }
 
1536
    default:
 
1537
        {
 
1538
            se_DisplayChatLocally( this, s );
 
1539
 
 
1540
            break;
 
1541
        }
 
1542
    }
 
1543
}
 
1544
 
 
1545
//return the playernetid for a given name
 
1546
/*
 
1547
static ePlayerNetID* identifyPlayer(tString inname)
 
1548
{
 
1549
    for(int i=0; i<se_PlayerNetIDs.Len(); i++)
 
1550
    {
 
1551
        ePlayerNetID *p = se_PlayerNetIDs[i];
 
1552
 
 
1553
        if( inname == p->GetUserName() )
 
1554
            return p;
 
1555
    }
 
1556
    return NULL;
 
1557
}
 
1558
*/
 
1559
 
 
1560
// identify a local player
 
1561
static ePlayerNetID* se_GetLocalPlayer()
 
1562
{
 
1563
    for(int i=0; i<se_PlayerNetIDs.Len(); i++)
 
1564
    {
 
1565
        ePlayerNetID *p = se_PlayerNetIDs[i];
 
1566
 
 
1567
        if( p->Owner() == sn_myNetID )
 
1568
            return p;
 
1569
    }
 
1570
    return NULL;
 
1571
}
 
1572
 
 
1573
static void ConsoleSay_conf(std::istream &s)
 
1574
{
 
1575
    // read the message
 
1576
    tString message;
 
1577
    message.ReadLine( s, true );
 
1578
 
 
1579
    switch (sn_GetNetState())
 
1580
    {
 
1581
    case nCLIENT:
 
1582
        {
 
1583
            ePlayerNetID *me = se_GetLocalPlayer();
 
1584
 
 
1585
            if (me)
 
1586
                me->Chat( message );
 
1587
 
 
1588
            break;
 
1589
        }
 
1590
    case nSERVER:
 
1591
        {
 
1592
            tColoredString send;
 
1593
            send << tColoredString::ColorString( 1,0,0 );
 
1594
            send << "Admin";
 
1595
            send << tColoredString::ColorString( 1,1,.5 );
 
1596
            send << ": " << message << "\n";
 
1597
 
 
1598
            // display it
 
1599
            sn_ConsoleOut( send );
 
1600
 
 
1601
            break;
 
1602
        }
 
1603
    default:
 
1604
        {
 
1605
            ePlayerNetID *me = se_GetLocalPlayer();
 
1606
 
 
1607
            if ( me )
 
1608
                se_DisplayChatLocally( me, message );
 
1609
 
 
1610
            break;
 
1611
        }
 
1612
    }
 
1613
}
 
1614
 
 
1615
static tConfItemFunc ConsoleSay_c("SAY",&ConsoleSay_conf);
 
1616
 
 
1617
struct eChatInsertionCommand
 
1618
{
 
1619
    tString insertion_;
 
1620
 
 
1621
    eChatInsertionCommand( tString const & insertion )
 
1622
            : insertion_( insertion )
 
1623
    {}
 
1624
};
 
1625
 
 
1626
 
 
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);
 
1630
 
 
1631
class eMenuItemChat : uMenuItemStringWithHistory{
 
1632
    ePlayer *me;
 
1633
public:
 
1634
    eMenuItemChat(uMenu *M,tString &c,ePlayer *Me):
 
1635
    uMenuItemStringWithHistory(M,"$chat_title_text","",c, se_SpamMaxLen, se_chatHistory, se_chatHistoryMaxSize),me(Me) {}
 
1636
 
 
1637
 
 
1638
    virtual ~eMenuItemChat(){ }
 
1639
 
 
1640
    //virtual void Render(REAL x,REAL y,REAL alpha=1,bool selected=0);
 
1641
 
 
1642
    virtual bool Event(SDL_Event &e){
 
1643
#ifndef DEDICATED
 
1644
        if (e.type==SDL_KEYDOWN &&
 
1645
                (e.key.keysym.sym==SDLK_KP_ENTER || e.key.keysym.sym==SDLK_RETURN)){
 
1646
 
 
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);
 
1650
 
 
1651
            MyMenu()->Exit();
 
1652
            return true;
 
1653
        }
 
1654
        else if (e.type==SDL_KEYDOWN &&
 
1655
                 uActionGlobal::IsBreakingGlobalBind(e.key.keysym.sym))
 
1656
            return su_HandleEvent(e, true);
 
1657
        else
 
1658
        {
 
1659
            if ( uMenuItemStringWithHistory::Event(e) )
 
1660
            {
 
1661
                return true;
 
1662
            }
 
1663
            // exclude modifier keys from possible control triggers
 
1664
            else if ( e.key.keysym.sym < SDLK_NUMLOCK || e.key.keysym.sym > SDLK_COMPOSE )
 
1665
            {
 
1666
                // maybe it's an instant chat button?
 
1667
                try
 
1668
                {
 
1669
                    return su_HandleEvent(e, false);
 
1670
                }
 
1671
                catch ( eChatInsertionCommand & insertion )
 
1672
                {
 
1673
                    if ( content->Len() + insertion.insertion_.Len() <= maxLength_ )
 
1674
                    {
 
1675
                        *content = content->SubStr( 0, cursorPos ) + insertion.insertion_ + content->SubStr( cursorPos );
 
1676
                        cursorPos += insertion.insertion_.Len()-1;
 
1677
                    }
 
1678
 
 
1679
                    return true;
 
1680
                }
 
1681
            }
 
1682
        }
 
1683
#endif // DEDICATED
 
1684
 
 
1685
        return false;
 
1686
    }
 
1687
};
 
1688
 
 
1689
 
 
1690
void se_ChatState(ePlayerNetID::ChatFlags flag, bool cs){
 
1691
    for(int i=se_PlayerNetIDs.Len()-1;i>=0;i--)
 
1692
    {
 
1693
        ePlayerNetID *p = se_PlayerNetIDs[i];
 
1694
        if (p->Owner()==sn_myNetID && p->pID >= 0){
 
1695
            p->SetChatting( flag, cs );
 
1696
        }
 
1697
    }
 
1698
}
 
1699
 
 
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){
 
1705
        su_ClearKeys();
 
1706
 
 
1707
        se_chatter=se_chatterPlanned;
 
1708
        se_chatterPlanned=NULL;
 
1709
        se_ChatState( ePlayerNetID::ChatFlags_Chat, true);
 
1710
 
 
1711
        sr_con.SetHeight(15,false);
 
1712
        se_SetShowScoresAuto(false);
 
1713
 
 
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);
 
1719
        chat_menu.Enter();
 
1720
 
 
1721
        se_ChatState( ePlayerNetID::ChatFlags_Chat, false );
 
1722
 
 
1723
        sr_con.SetHeight(7,false);
 
1724
        se_SetShowScoresAuto(true);
 
1725
    }
 
1726
    se_chatter=NULL;
 
1727
    se_chatterPlanned=NULL;
 
1728
}
 
1729
 
 
1730
static void chat( ePlayer * chatter, tString const & say )
 
1731
{
 
1732
    se_chatterPlanned = chatter;
 
1733
    se_say = say;
 
1734
    st_ToDo(&do_chat);
 
1735
}
 
1736
 
 
1737
static void chat( ePlayer * chatter )
 
1738
{
 
1739
    chat( chatter, tString() );
 
1740
}
 
1741
 
 
1742
static bool se_allowControlDuringChat = false;
 
1743
static nSettingItem<bool> se_allowControlDuringChatConf("ALLOW_CONTROL_DURING_CHAT",se_allowControlDuringChat);
 
1744
 
 
1745
uActionPlayer se_toggleSpectator("TOGGLE_SPECTATOR", 0);
 
1746
 
 
1747
bool ePlayer::Act(uAction *act,REAL x){
 
1748
    eGameObject *object=NULL;
 
1749
 
 
1750
    if ( act == &se_toggleSpectator && x > 0 )
 
1751
    {
 
1752
        spectate = !spectate;
 
1753
        con << tOutput(spectate ? "$player_toggle_spectator_on" : "$player_toggle_spectator_off", name );
 
1754
        if ( !spectate )
 
1755
        {
 
1756
            ePlayerNetID::Update();
 
1757
        }
 
1758
        return true;
 
1759
    }
 
1760
    else if (!se_chatter && s_chat==*reinterpret_cast<uActionPlayer *>(act)){
 
1761
        if(x>0) {
 
1762
            chat( this );
 
1763
        }
 
1764
        return true;
 
1765
    }
 
1766
    else{
 
1767
        if ( x > 0 )
 
1768
        {
 
1769
            int i;
 
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())
 
1776
                        {
 
1777
                            tString say = instantChatString[i];
 
1778
                            bool sendImmediately = true;
 
1779
                            if ( say.Len() > 2 && say[say.Len()-2] == '\\' )
 
1780
                            {
 
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;
 
1785
                            }
 
1786
 
 
1787
                            if ( se_chatter == this )
 
1788
                            {
 
1789
                                // a chat is already active, insert the chat string
 
1790
                                throw eChatInsertionCommand( say );
 
1791
                            }
 
1792
                            else
 
1793
                            {
 
1794
                                if ( sendImmediately )
 
1795
                                {
 
1796
                                    // fire out chat string immediately
 
1797
                                    se_PlayerNetIDs(j)->Chat(say);
 
1798
                                }
 
1799
                                else
 
1800
                                {
 
1801
                                    // chat with instant chat string as template
 
1802
                                    chat( this, say );
 
1803
                                }
 
1804
                            }
 
1805
                            return true;
 
1806
                        }
 
1807
                }
 
1808
            }
 
1809
        }
 
1810
 
 
1811
        // no other command should get through during chat
 
1812
        if ( se_chatter && !se_allowControlDuringChat )
 
1813
            return false;
 
1814
 
 
1815
        int i;
 
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;
 
1819
 
 
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))));
 
1823
 
 
1824
        if (bool(netPlayer) && (objectAct || !se_chatter))
 
1825
            netPlayer->Activity();
 
1826
 
 
1827
        return ret;
 
1828
    }
 
1829
 
 
1830
}
 
1831
 
 
1832
rViewport * ePlayer::PlayerViewport(int p){
 
1833
    if (!PlayerConfig(p)) return NULL;
 
1834
 
 
1835
    for (int i=rViewportConfiguration::CurrentViewportConfiguration()->num_viewports-1;i>=0;i--)
 
1836
        if (sr_viewportBelongsToPlayer[i] == p)
 
1837
            return rViewportConfiguration::CurrentViewport(i);
 
1838
 
 
1839
    return NULL;
 
1840
}
 
1841
 
 
1842
bool ePlayer::PlayerIsInGame(int p){
 
1843
    return PlayerViewport(p) && PlayerConfig(p);
 
1844
}
 
1845
 
 
1846
static tConfItemBase *vpbtp[MAX_VIEWPORTS];
 
1847
 
 
1848
void ePlayer::Init(){
 
1849
    se_Players = tNEW( ePlayer[MAX_PLAYERS] );
 
1850
 
 
1851
    int i;
 
1852
    for(i=MAX_INSTANT_CHAT-1;i>=0;i--){
 
1853
        tString id;
 
1854
        id << "INSTANT_CHAT_";
 
1855
        id << i+1;
 
1856
        tOutput desc;
 
1857
        desc.SetTemplateParameter(1, i+1);
 
1858
        desc << "$input_instant_chat_text";
 
1859
 
 
1860
        tOutput help;
 
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.");
 
1865
    }
 
1866
 
 
1867
 
 
1868
    for(i=MAX_VIEWPORTS-1;i>=0;i--){
 
1869
        tString id;
 
1870
        id << "VIEWPORT_TO_PLAYER_";
 
1871
        id << i+1;
 
1872
        vpbtp[i] = tNEW(tConfItem<int>(id,"$viewport_belongs_help",
 
1873
                                       s_newViewportBelongsToPlayer[i]));
 
1874
        s_newViewportBelongsToPlayer[i]=i;
 
1875
    }
 
1876
}
 
1877
 
 
1878
void ePlayer::Exit(){
 
1879
    int i;
 
1880
    for(i=MAX_INSTANT_CHAT-1;i>=0;i--)
 
1881
        tDESTROY(ePlayer::se_instantChatAction[i]);
 
1882
 
 
1883
    for(i=MAX_VIEWPORTS-1;i>=0;i--)
 
1884
        tDESTROY(vpbtp[i]);
 
1885
 
 
1886
    delete[] se_Players;
 
1887
    se_Players = NULL;
 
1888
}
 
1889
 
 
1890
uActionPlayer ePlayer::s_chat("CHAT");
 
1891
 
 
1892
int pingCharity = 100;
 
1893
 
 
1894
static int IMPOSSIBLY_LOW_SCORE=(-1 << 31);
 
1895
 
 
1896
static nSpamProtectionSettings se_chatSpamSettings( 1.0f, "SPAM_PROTECTION_CHAT", tOutput("$spam_protection") );
 
1897
 
 
1898
ePlayerNetID::ePlayerNetID(int p):nNetObject(),listID(-1), teamListID(-1)
 
1899
        ,registeredMachine_(0), pID(p),chatSpam_( se_chatSpamSettings )
 
1900
{
 
1901
    favoriteNumberOfPlayersPerTeam = 1;
 
1902
 
 
1903
    nameTeamAfterMe = false;
 
1904
 
 
1905
    r = g = b = 15;
 
1906
 
 
1907
    greeted                             = true;
 
1908
    auth                                = true;
 
1909
    chatting_                   = false;
 
1910
    spectating_         = false;
 
1911
    chatFlags_                  = 0;
 
1912
    disconnected                = false;
 
1913
 
 
1914
    if (p>=0){
 
1915
        ePlayer *P = ePlayer::PlayerConfig(p);
 
1916
        if (P){
 
1917
            SetName( P->Name() );
 
1918
            r=   P->rgb[0];
 
1919
            g=   P->rgb[1];
 
1920
            b=   P->rgb[2];
 
1921
            pingCharity=::pingCharity;
 
1922
        }
 
1923
    }
 
1924
    /*
 
1925
    else
 
1926
        SetName( "AI" );
 
1927
    */
 
1928
    lastSaid.SetLen(12);
 
1929
    lastSaidTimes.SetLen(12);
 
1930
 
 
1931
    se_PlayerNetIDs.Add(this,listID);
 
1932
    object=NULL;
 
1933
 
 
1934
    /*
 
1935
      if(sn_GetNetState()!=nSERVER)
 
1936
      ping=sn_Connections[0].ping;
 
1937
      else
 
1938
    */
 
1939
    ping=0; // hehe! server has no ping.
 
1940
 
 
1941
    lastSync=tSysTimeFloat();
 
1942
 
 
1943
    RequestSync();
 
1944
    score=0;
 
1945
    lastScore_=IMPOSSIBLY_LOW_SCORE;
 
1946
    // rubberstatus=0;
 
1947
 
 
1948
    //Remote Addmin add-ins...
 
1949
    loggedIn = false;
 
1950
    accessLevel = 0;
 
1951
 
 
1952
    MyInitAfterCreation();
 
1953
}
 
1954
 
 
1955
 
 
1956
 
 
1957
 
 
1958
ePlayerNetID::ePlayerNetID(nMessage &m):nNetObject(m),listID(-1), teamListID(-1)
 
1959
        , registeredMachine_(0), chatSpam_( se_chatSpamSettings )
 
1960
{
 
1961
    greeted     =false;
 
1962
    chatting_   =false;
 
1963
    spectating_ =false;
 
1964
    disconnected=false;
 
1965
    chatFlags_  =0;
 
1966
 
 
1967
    r = g = b = 15;
 
1968
 
 
1969
    nameTeamAfterMe = false;
 
1970
 
 
1971
    lastSaid.SetLen(12);
 
1972
    lastSaidTimes.SetLen(12);
 
1973
 
 
1974
    pID=-1;
 
1975
    se_PlayerNetIDs.Add(this,listID);
 
1976
    object=NULL;
 
1977
    ping=sn_Connections[m.SenderID()].ping;
 
1978
    lastSync=tSysTimeFloat();
 
1979
 
 
1980
    if(sn_GetNetState()==nSERVER)
 
1981
        RequestSync();
 
1982
 
 
1983
    score=0;
 
1984
    lastScore_=IMPOSSIBLY_LOW_SCORE;
 
1985
    // rubberstatus=0;
 
1986
 
 
1987
    //Remote Addmin add-ins...
 
1988
    loggedIn = false;
 
1989
    accessLevel = 0;
 
1990
 
 
1991
#ifdef KRAWALL_SERVER
 
1992
    auth=false;
 
1993
#else
 
1994
    auth=true;
 
1995
#endif
 
1996
}
 
1997
 
 
1998
void ePlayerNetID::Activity()
 
1999
{
 
2000
    // the player was active; therefore, he cannot possibly be chatting_ or disconnected.
 
2001
 
 
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)
 
2004
        return;
 
2005
 
 
2006
    if (chatting_ || disconnected)
 
2007
    {
 
2008
#ifdef DEBUG
 
2009
        con << *this << " showed activity and lost chat status.\n";
 
2010
#endif
 
2011
        RequestSync();
 
2012
    }
 
2013
 
 
2014
    chatting_ = disconnected = false;
 
2015
 
 
2016
    // store time
 
2017
    this->lastActivity_ = tSysTimeFloat();
 
2018
}
 
2019
 
 
2020
static bool se_SilenceAll = false;              // flag indicating whether everyone should be silenced
 
2021
 
 
2022
static tSettingItem<bool> se_silAll("SILENCE_ALL",
 
2023
                                    se_SilenceAll);
 
2024
 
 
2025
static int se_maxPlayersPerIP = 4;
 
2026
static tSettingItem<int> se_maxPlayersPerIPConf( "MAX_PLAYERS_SAME_IP", se_maxPlayersPerIP );
 
2027
 
 
2028
void ePlayerNetID::MyInitAfterCreation()
 
2029
{
 
2030
    this->CreateVoter();
 
2031
 
 
2032
    this->silenced_ = se_SilenceAll;
 
2033
 
 
2034
    this->UpdateName();
 
2035
 
 
2036
    // register with machine and kick user if too many players are present
 
2037
    if ( Owner() != 0 && sn_GetNetState() == nSERVER )
 
2038
    {
 
2039
        this->RegisterWithMachine();
 
2040
        if ( se_maxPlayersPerIP < nMachine::GetMachine(Owner()).GetPlayerCount() )
 
2041
        {
 
2042
            // kill them
 
2043
            sn_DisconnectUser( Owner(), "$network_kill_too_many_players" );
 
2044
        }
 
2045
    }
 
2046
 
 
2047
    this->wait_ = 0;
 
2048
 
 
2049
    this->lastActivity_ = tSysTimeFloat();
 
2050
}
 
2051
 
 
2052
void ePlayerNetID::InitAfterCreation()
 
2053
{
 
2054
    MyInitAfterCreation();
 
2055
 
 
2056
    if (sn_GetNetState() == nSERVER)
 
2057
    {
 
2058
#ifndef KRAWALL_SERVER
 
2059
        GetScoreFromDisconnectedCopy();
 
2060
#endif
 
2061
        FindDefaultTeam();
 
2062
    }
 
2063
}
 
2064
 
 
2065
bool ePlayerNetID::ClearToTransmit(int user) const{
 
2066
    return ( ( !nextTeam || nextTeam->HasBeenTransmitted( user ) ) &&
 
2067
             ( !currentTeam || currentTeam->HasBeenTransmitted( user ) ) ) ;
 
2068
}
 
2069
 
 
2070
ePlayerNetID::~ePlayerNetID()
 
2071
{
 
2072
    //  se_PlayerNetIDs.Remove(this,listID);
 
2073
    if ( sn_GetNetState() == nSERVER && disconnected )
 
2074
    {
 
2075
        tOutput mess;
 
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);
 
2082
    }
 
2083
 
 
2084
    UnregisterWithMachine();
 
2085
 
 
2086
    RemoveFromGame();
 
2087
 
 
2088
    ClearObject();
 
2089
    //con << "Player info sent.\n";
 
2090
 
 
2091
    for(int i=MAX_PLAYERS-1;i>=0;i--){
 
2092
        ePlayer *p = ePlayer::PlayerConfig(i);
 
2093
 
 
2094
        if (p && static_cast<ePlayerNetID *>(p->netPlayer)==this)
 
2095
            p->netPlayer=NULL;
 
2096
    }
 
2097
 
 
2098
    if ( currentTeam )
 
2099
    {
 
2100
        currentTeam->RemovePlayer( this );
 
2101
    }
 
2102
 
 
2103
#ifdef DEBUG
 
2104
    con << *this << " destroyed.\n";
 
2105
#endif
 
2106
}
 
2107
 
 
2108
static void player_removed_from_game_handler(nMessage &m)
 
2109
{
 
2110
    // and the ID of the player that was removed
 
2111
    unsigned short id;
 
2112
    m.Read(id);
 
2113
    ePlayerNetID* p = dynamic_cast< ePlayerNetID* >( nNetObject::ObjectDangerous( id ) );
 
2114
    if ( p && sn_GetNetState() != nSERVER )
 
2115
    {
 
2116
        p->RemoveFromGame();
 
2117
    }
 
2118
}
 
2119
 
 
2120
static nDescriptor player_removed_from_game(202,&player_removed_from_game_handler,"player_removed_from_game");
 
2121
 
 
2122
void ePlayerNetID::RemoveFromGame()
 
2123
{
 
2124
    // release voter
 
2125
    if ( this->voter_ )
 
2126
        this->voter_->RemoveFromGame();
 
2127
    this->voter_ = 0;
 
2128
 
 
2129
    // log scores
 
2130
    LogScoreDifference();
 
2131
 
 
2132
    if ( sn_GetNetState() != nCLIENT )
 
2133
    {
 
2134
        nameFromClient_ = nameFromServer_;
 
2135
 
 
2136
        nMessage *m=new nMessage(player_removed_from_game);
 
2137
        m->Write(this->ID());
 
2138
        m->BroadCast();
 
2139
 
 
2140
        if ( listID >= 0 ){
 
2141
            if ( IsSpectating() )
 
2142
            {
 
2143
                // get colored player name
 
2144
                tColoredString playerName;
 
2145
                playerName << *this << tColoredString::ColorString(1,.5,.5);
 
2146
 
 
2147
                // announce a generic leave
 
2148
                sn_ConsoleOut( tOutput( "$player_left_spectator", playerName ) );
 
2149
            }
 
2150
 
 
2151
            if ( IsHuman() && sn_GetNetState() == nSERVER && NULL != sn_Connections[Owner()].socket )
 
2152
            {
 
2153
                tString ladder;
 
2154
                ladder << "PLAYER_LEFT " << userName_ << " " << nMachine::GetMachine(Owner()).GetIP() << "\n";
 
2155
                se_SaveToLadderLog(ladder);
 
2156
            }
 
2157
        }
 
2158
    }
 
2159
 
 
2160
    se_PlayerNetIDs.Remove(this, listID);
 
2161
    SetTeamWish( NULL );
 
2162
    SetTeam( NULL );
 
2163
    UpdateTeam();
 
2164
    ControlObject( NULL );
 
2165
    //  currentTeam = NULL;
 
2166
}
 
2167
 
 
2168
bool ePlayerNetID::ActionOnQuit()
 
2169
{
 
2170
    UnregisterWithMachine();
 
2171
 
 
2172
    tControlledPTR< ePlayerNetID > holder( this );
 
2173
 
 
2174
    //  else
 
2175
    {
 
2176
        this->RemoveFromGame();
 
2177
 
 
2178
        se_PlayerNetIDs.Remove(this,listID);
 
2179
 
 
2180
        return true;
 
2181
    }
 
2182
}
 
2183
 
 
2184
void ePlayerNetID::ActionOnDelete()
 
2185
{
 
2186
    UnregisterWithMachine();
 
2187
 
 
2188
    tControlledPTR< ePlayerNetID > holder( this );
 
2189
 
 
2190
    this->RemoveFromGame();
 
2191
 
 
2192
    se_PlayerNetIDs.Remove(this,listID);
 
2193
}
 
2194
 
 
2195
void ePlayerNetID::PrintName(tString &s) const
 
2196
{
 
2197
    s << "ePlayerNetID nr. " << ID() << ", name " << GetName();
 
2198
}
 
2199
 
 
2200
 
 
2201
bool ePlayerNetID::AcceptClientSync() const{
 
2202
    return true;
 
2203
}
 
2204
 
 
2205
void ePlayerNetID::Auth(){
 
2206
    auth = true;
 
2207
    GetScoreFromDisconnectedCopy();
 
2208
}
 
2209
 
 
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");
 
2214
#endif
 
2215
 
 
2216
    return auth;
 
2217
}
 
2218
 
 
2219
// create our voter or find it
 
2220
void ePlayerNetID::CreateVoter()
 
2221
{
 
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 )
 
2224
    {
 
2225
        this->voter_ = eVoter::GetVoter( this->Owner() );
 
2226
    }
 
2227
}
 
2228
 
 
2229
void ePlayerNetID::WriteSync(nMessage &m){
 
2230
    lastSync=tSysTimeFloat();
 
2231
    nNetObject::WriteSync(m);
 
2232
    m.Write(r);
 
2233
    m.Write(g);
 
2234
    m.Write(b);
 
2235
    m.Write(pingCharity);
 
2236
    if ( sn_GetNetState() == nCLIENT )
 
2237
    {
 
2238
        m << nameFromClient_;
 
2239
    }
 
2240
    else
 
2241
    {
 
2242
        m << nameFromServer_;
 
2243
    }
 
2244
 
 
2245
    //if(sn_GetNetState()==nSERVER)
 
2246
    m << ping;
 
2247
 
 
2248
    // pack chat and spectator status together
 
2249
    unsigned short flags = ( chatting_ ? 1 : 0 ) | ( spectating_ ? 2 : 0 );
 
2250
    m << flags;
 
2251
 
 
2252
    m << score;
 
2253
    m << static_cast<unsigned short>(disconnected);
 
2254
 
 
2255
    m << nextTeam;
 
2256
    m << currentTeam;
 
2257
 
 
2258
    m << favoriteNumberOfPlayersPerTeam;
 
2259
    m << nameTeamAfterMe;
 
2260
}
 
2261
 
 
2262
// makes sure the passed string is not longer than the given maximum
 
2263
static void se_CutString( tColoredString & string, int maxLen )
 
2264
{
 
2265
    if (string.Len() > maxLen )
 
2266
    {
 
2267
        string.SetLen(maxLen);
 
2268
        string[string.Len()-1]='\0';
 
2269
        string.RemoveTrailingColor();
 
2270
    }
 
2271
}
 
2272
 
 
2273
static void se_CutString( tString & string, int maxLen )
 
2274
{
 
2275
    se_CutString( reinterpret_cast< tColoredString & >( string ), maxLen );
 
2276
}
 
2277
 
 
2278
static bool se_bugColorOverflow=true;
 
2279
tSettingItem< bool > se_bugColorOverflowColor( "BUG_COLOR_OVERFLOW", se_bugColorOverflow );
 
2280
void Clamp( unsigned short & colorComponent )
 
2281
{
 
2282
    if ( colorComponent > 15 )
 
2283
        colorComponent = 15;
 
2284
}
 
2285
 
 
2286
// function prototype for character testing functions
 
2287
typedef bool TestCharacter( char c );
 
2288
 
 
2289
// strips characters matching a test beginnings and ends of names
 
2290
static void se_StripMatchingEnds( tString & stripper, TestCharacter & beginTester, TestCharacter & endTester )
 
2291
{
 
2292
    int len = stripper.Len() - 1;
 
2293
    int first = 0, last = len;
 
2294
 
 
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;
 
2298
 
 
2299
    // strip everything?
 
2300
    if ( first > last )
 
2301
    {
 
2302
        stripper = "";
 
2303
        return;
 
2304
    }
 
2305
 
 
2306
    // strip
 
2307
    if ( first > 0 || last < stripper.Len() - 1 )
 
2308
        stripper = stripper.SubStr( first, last + 1 - first );
 
2309
}
 
2310
 
 
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 )
 
2314
//{
 
2315
//    se_StripMatchingEnds( stripper, tester, tester );
 
2316
//}
 
2317
 
 
2318
// function wrapper for what may be a macro
 
2319
static bool se_IsSpace( char c )
 
2320
{
 
2321
    return isspace( c );
 
2322
}
 
2323
 
 
2324
// enf of player names should neither be space or :
 
2325
static bool se_IsInvalidNameEnd( char c )
 
2326
{
 
2327
    return isspace( c ) || c == ':' || c == '.';
 
2328
}
 
2329
 
 
2330
// filter name ends
 
2331
static void se_StripNameEnds( tString & name )
 
2332
{
 
2333
    se_StripMatchingEnds( name, se_IsSpace, se_IsInvalidNameEnd );
 
2334
}
 
2335
 
 
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 )
 
2338
{
 
2339
    for (int i = se_PlayerNetIDs.Len()-1; i >= 0; --i )
 
2340
    {
 
2341
        ePlayerNetID * player = se_PlayerNetIDs(i);
 
2342
        if ( player != exception )
 
2343
        {
 
2344
            if ( name == player->GetUserName() )
 
2345
                return true;
 
2346
        }
 
2347
    }
 
2348
 
 
2349
    return false;
 
2350
}
 
2351
 
 
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 );
 
2355
 
 
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 );
 
2360
 
 
2361
void ePlayerNetID::ReadSync(nMessage &m){
 
2362
    nNetObject::ReadSync(m);
 
2363
 
 
2364
    m.Read(r);
 
2365
    m.Read(g);
 
2366
    m.Read(b);
 
2367
 
 
2368
    if ( !se_bugColorOverflow )
 
2369
    {
 
2370
        // clamp color values
 
2371
        Clamp(r);
 
2372
        Clamp(g);
 
2373
        Clamp(b);
 
2374
    }
 
2375
 
 
2376
    m.Read(pingCharity);
 
2377
 
 
2378
    // name as sent from the other end
 
2379
    tString & remoteName = ( sn_GetNetState() == nCLIENT ) ? nameFromServer_ : nameFromClient_;
 
2380
 
 
2381
    // name handling
 
2382
    {
 
2383
        tString oldName = remoteName;
 
2384
 
 
2385
        // read and shorten name, but don't update it yet
 
2386
        m >> remoteName;
 
2387
 
 
2388
        // filter colors
 
2389
        if ( se_filterColorNames )
 
2390
            remoteName = tColoredString::RemoveColors( remoteName );
 
2391
 
 
2392
        // strip whitespace
 
2393
        if ( se_stripNames )
 
2394
            se_StripNameEnds( remoteName );
 
2395
 
 
2396
        se_CutString( remoteName, 16 );
 
2397
 
 
2398
        // zero strings or color code only strings are illegal
 
2399
        if ( !IsLegalPlayerName( remoteName ) )
 
2400
        {
 
2401
            // revert to old name if possible
 
2402
            if ( IsLegalPlayerName( oldName ) )
 
2403
            {
 
2404
                remoteName = oldName;
 
2405
            }
 
2406
            else
 
2407
            {
 
2408
                // or replace it by a default value
 
2409
                remoteName = "Player 1";
 
2410
            }
 
2411
        }
 
2412
    }
 
2413
 
 
2414
    // directly apply name changes sent from the server, they are safe.
 
2415
    if ( sn_GetNetState() != nSERVER )
 
2416
    {
 
2417
        UpdateName();
 
2418
    }
 
2419
 
 
2420
    REAL p;
 
2421
    m >> p;
 
2422
    if (sn_GetNetState()!=nSERVER)
 
2423
        ping=p;
 
2424
 
 
2425
    //  if (!m.End())
 
2426
    {
 
2427
        // read chat and spectator status
 
2428
        unsigned short flags;
 
2429
        m >> flags;
 
2430
 
 
2431
        if (Owner() != ::sn_myNetID)
 
2432
        {
 
2433
            bool newChat = ( ( flags & 1 ) != 0 );
 
2434
            bool newSpectate = ( ( flags & 2 ) != 0 );
 
2435
 
 
2436
            if ( chatting_ != newChat || spectating_ != newSpectate )
 
2437
                lastActivity_ = tSysTimeFloat();
 
2438
            chatting_   = newChat;
 
2439
            spectating_ = newSpectate;
 
2440
        }
 
2441
    }
 
2442
 
 
2443
    //  if (!m.End())
 
2444
    {
 
2445
        if(sn_GetNetState()!=nSERVER)
 
2446
            m >> score;
 
2447
        else{
 
2448
            int s;
 
2449
            m >> s;
 
2450
        }
 
2451
    }
 
2452
 
 
2453
    if (!m.End()){
 
2454
        unsigned short newdisc;
 
2455
        m >> newdisc;
 
2456
 
 
2457
        if (Owner() != ::sn_myNetID && sn_GetNetState()!=nSERVER)
 
2458
            disconnected = newdisc;
 
2459
    }
 
2460
 
 
2461
    if (!m.End())
 
2462
    {
 
2463
        if ( nSERVER != sn_GetNetState() )
 
2464
        {
 
2465
            eTeam *newCurrentTeam, *newNextTeam;
 
2466
 
 
2467
            m >> newNextTeam;
 
2468
            m >> newCurrentTeam;
 
2469
 
 
2470
            // update team
 
2471
            if ( newCurrentTeam != currentTeam )
 
2472
            {
 
2473
                if ( newCurrentTeam )
 
2474
                    newCurrentTeam->AddPlayerDirty( this );
 
2475
                else
 
2476
                    currentTeam->RemovePlayer( this );
 
2477
            }
 
2478
            nextTeam = newNextTeam;
 
2479
        }
 
2480
        else
 
2481
        {
 
2482
            eTeam* t;
 
2483
            m >> t;
 
2484
            m >> t;
 
2485
        }
 
2486
 
 
2487
        m >> favoriteNumberOfPlayersPerTeam;
 
2488
        m >> nameTeamAfterMe;
 
2489
    }
 
2490
    // con << "Player info updated.\n";
 
2491
 
 
2492
    // make sure we did not accidentally overwrite values
 
2493
    // ePlayer::Update();
 
2494
 
 
2495
    // update the team
 
2496
    if ( nSERVER == sn_GetNetState() )
 
2497
    {
 
2498
        if ( nextTeam )
 
2499
            nextTeam->UpdateProperties();
 
2500
 
 
2501
        if ( currentTeam )
 
2502
            currentTeam->UpdateProperties();
 
2503
    }
 
2504
}
 
2505
 
 
2506
 
 
2507
nNOInitialisator<ePlayerNetID> ePlayerNetID_init(201,"ePlayerNetID");
 
2508
 
 
2509
nDescriptor &ePlayerNetID::CreatorDescriptor() const{
 
2510
    return ePlayerNetID_init;
 
2511
}
 
2512
 
 
2513
 
 
2514
 
 
2515
void ePlayerNetID::ControlObject(eNetGameObject *c){
 
2516
    if (bool(object) && c!=object)
 
2517
        ClearObject();
 
2518
 
 
2519
    if (!c)
 
2520
    {
 
2521
        return;
 
2522
    }
 
2523
 
 
2524
 
 
2525
    object=c;
 
2526
    c->team = currentTeam;
 
2527
 
 
2528
    if (bool(object))
 
2529
        object->SetPlayer(this);
 
2530
#ifdef DEBUG
 
2531
    //con << "Player " << name << " controlles new object.\n";
 
2532
#endif
 
2533
 
 
2534
    NewObject();
 
2535
}
 
2536
 
 
2537
void ePlayerNetID::ClearObject(){
 
2538
    if (object)
 
2539
    {
 
2540
        tJUST_CONTROLLED_PTR< eNetGameObject > x=object;
 
2541
        object=NULL;
 
2542
        x->RemoveFromGame();
 
2543
        x->SetPlayer( NULL );
 
2544
    }
 
2545
#ifdef DEBUG
 
2546
    //con << "Player " << name << " controlles nothing.\n";
 
2547
#endif
 
2548
}
 
2549
 
 
2550
// message of day presented to clients logging in
 
2551
tString se_greeting("");
 
2552
static tConfItemLine a_mod("MESSAGE_OF_DAY",se_greeting);
 
2553
 
 
2554
void ePlayerNetID::Greet(){
 
2555
    if (!greeted){
 
2556
        tOutput o;
 
2557
        o.SetTemplateParameter(1, GetName() );
 
2558
        o.SetTemplateParameter(2, sn_programVersion);
 
2559
        o << "$player_welcome";
 
2560
        tString s;
 
2561
        s << o;
 
2562
        s << "\n";
 
2563
        GreetHighscores(s);
 
2564
        s << "\n";
 
2565
        if (se_greeting.Len()>1)
 
2566
            s << se_greeting << "\n";
 
2567
        s << "\n";
 
2568
        //std::cout << s;
 
2569
        sn_ConsoleOut(s,Owner());
 
2570
        greeted=true;
 
2571
    }
 
2572
}
 
2573
 
 
2574
eNetGameObject *ePlayerNetID::Object() const{
 
2575
    return object;
 
2576
}
 
2577
 
 
2578
void se_SaveToLadderLog( tOutput const & out )
 
2579
{
 
2580
    if (sn_GetNetState()!=nCLIENT && !tRecorder::IsPlayingBack())
 
2581
    {
 
2582
        std::ofstream o;
 
2583
        if ( tDirectories::Var().Open(o, "ladderlog.txt", std::ios::app) )
 
2584
            o << out;
 
2585
    }
 
2586
}
 
2587
 
 
2588
void se_SaveToScoreFile(const tOutput &o){
 
2589
    tString s(o);
 
2590
 
 
2591
#ifdef DEBUG
 
2592
    if (sn_GetNetState()!=nCLIENT){
 
2593
#else
 
2594
    if (sn_GetNetState()==nSERVER && !tRecorder::IsPlayingBack()){
 
2595
#endif
 
2596
 
 
2597
        std::ofstream o;
 
2598
        if ( tDirectories::Var().Open(o, "scorelog.txt", std::ios::app) )
 
2599
            o << tColoredString::RemoveColors(s);
 
2600
    }
 
2601
#ifdef DEBUG
 
2602
}
 
2603
#else
 
2604
}
 
2605
#endif
 
2606
 
 
2607
// void ePlayerNetID::SetRubber(REAL rubber2) {rubberstatus = rubber2;}
 
2608
 
 
2609
void ePlayerNetID::AddScore(int points,
 
2610
                            const tOutput& reasonwin,
 
2611
                            const tOutput& reasonlose)
 
2612
{
 
2613
    if (points==0)
 
2614
        return;
 
2615
 
 
2616
    score += points;
 
2617
    if (currentTeam)
 
2618
        currentTeam->AddScore( points );
 
2619
 
 
2620
    tColoredString name;
 
2621
    name << *this << tColoredString::ColorString(1,1,1);
 
2622
 
 
2623
    tOutput message;
 
2624
    message.SetTemplateParameter(1, name);
 
2625
    message.SetTemplateParameter(2, points > 0 ? points : -points);
 
2626
 
 
2627
 
 
2628
    if (points>0)
 
2629
    {
 
2630
        if (reasonwin.IsEmpty())
 
2631
            message << "$player_win_default";
 
2632
        else
 
2633
            message.Append(reasonwin);
 
2634
    }
 
2635
    else
 
2636
    {
 
2637
        if (reasonlose.IsEmpty())
 
2638
            message << "$player_lose_default";
 
2639
        else
 
2640
            message.Append(reasonlose);
 
2641
    }
 
2642
 
 
2643
    sn_ConsoleOut(message);
 
2644
    RequestSync(true);
 
2645
 
 
2646
    se_SaveToScoreFile(message);
 
2647
}
 
2648
 
 
2649
 
 
2650
 
 
2651
 
 
2652
int ePlayerNetID::TotalScore() const
 
2653
{
 
2654
    if ( currentTeam )
 
2655
    {
 
2656
        return score;// + currentTeam->Score() * 5;
 
2657
    }
 
2658
    else
 
2659
    {
 
2660
        return score;
 
2661
    }
 
2662
}
 
2663
 
 
2664
 
 
2665
void ePlayerNetID::SwapPlayersNo(int a,int b){
 
2666
    if (0>a || se_PlayerNetIDs.Len()<=a)
 
2667
        return;
 
2668
    if (0>b || se_PlayerNetIDs.Len()<=b)
 
2669
        return;
 
2670
    if (a==b)
 
2671
        return;
 
2672
 
 
2673
    ePlayerNetID *A=se_PlayerNetIDs(a);
 
2674
    ePlayerNetID *B=se_PlayerNetIDs(b);
 
2675
 
 
2676
    se_PlayerNetIDs(b)=A;
 
2677
    se_PlayerNetIDs(a)=B;
 
2678
    A->listID=b;
 
2679
    B->listID=a;
 
2680
}
 
2681
 
 
2682
 
 
2683
void ePlayerNetID::SortByScore(){
 
2684
    // bubble sort (AAARRGGH! but good for lists that change not much)
 
2685
 
 
2686
    bool inorder=false;
 
2687
    while (!inorder){
 
2688
        inorder=true;
 
2689
        int i;
 
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);
 
2693
                inorder=false;
 
2694
            }
 
2695
    }
 
2696
}
 
2697
 
 
2698
void ePlayerNetID::ResetScore(){
 
2699
    int i;
 
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();
 
2704
    }
 
2705
 
 
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();
 
2710
    }
 
2711
 
 
2712
    ResetScoreDifferences();
 
2713
}
 
2714
 
 
2715
void ePlayerNetID::DisplayScores(){
 
2716
    sr_ResetRenderState(true);
 
2717
 
 
2718
    REAL W=sr_screenWidth;
 
2719
    REAL H=sr_screenHeight;
 
2720
 
 
2721
    REAL MW=400;
 
2722
    REAL MH=(MW*3)/4;
 
2723
 
 
2724
    if(W>MW)
 
2725
        W=MW;
 
2726
 
 
2727
    if(H>MH)
 
2728
        H=MH;
 
2729
 
 
2730
#ifndef DEDICATED
 
2731
    if (sr_glOut){
 
2732
        ::Color(1,1,1);
 
2733
        rTextField c(-.7,.6,10/W,18/H);
 
2734
 
 
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 )
 
2738
        {
 
2739
            if ( eTeam::teams[i]->NumPlayers() > 1 ||
 
2740
                    ( eTeam::teams[i]->NumPlayers() == 1 && eTeam::teams[i]->Player(0)->Score() != eTeam::teams[i]->Score() ) )
 
2741
            {
 
2742
                c << eTeam::Ranking();
 
2743
                c << "\n";
 
2744
                maxPlayers -= ( eTeam::teams.Len() > 6 ? 6 : eTeam::teams.Len() ) + 2;
 
2745
                break;
 
2746
            }
 
2747
        }
 
2748
 
 
2749
        // print player ranking
 
2750
        c << Ranking( maxPlayers );
 
2751
    }
 
2752
#endif
 
2753
}
 
2754
 
 
2755
 
 
2756
tString ePlayerNetID::Ranking( int MAX, bool cut ){
 
2757
    SortByScore();
 
2758
 
 
2759
    tColoredString ret;
 
2760
 
 
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 );
 
2774
        ret << "\n";
 
2775
 
 
2776
        int max = se_PlayerNetIDs.Len();
 
2777
 
 
2778
        // wasting the last line with ... is as stupid if it stands for only
 
2779
        // one player
 
2780
        if ( MAX == max + 1 )
 
2781
            MAX = max;
 
2782
 
 
2783
        if ( max > MAX && MAX > 0 )
 
2784
        {
 
2785
            max = MAX ;
 
2786
        }
 
2787
 
 
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 );
 
2793
 
 
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);
 
2796
 
 
2797
            // however, using the streaming operator is a lot cleaner. The example is left, as it really can be usefull in some situations.
 
2798
            line << *p;
 
2799
            line.SetPos(17, false );
 
2800
            if ( p->Object() && p->Object()->Alive() )
 
2801
            {
 
2802
                line << tColoredString::ColorString(0,1,0) << tOutput("$player_scoretable_alive_yes") << tColoredString::ColorString(1,1,1);
 
2803
            }
 
2804
            else
 
2805
            {
 
2806
                line << tColoredString::ColorString(1,0,0) << tOutput("$player_scoretable_alive_no") << tColoredString::ColorString(1,1,1);
 
2807
            }
 
2808
            line.SetPos(24, cut );
 
2809
            line << p->score;
 
2810
 
 
2811
            if (p->IsActive())
 
2812
            {
 
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 )
 
2818
                {
 
2819
                    //tString teamtemp = p->currentTeam->Name();
 
2820
                    //teamtemp.RemoveHex();
 
2821
                    line << tColoredString::RemoveColors(p->currentTeam->Name());
 
2822
                    line.SetPos(53, cut );
 
2823
                }
 
2824
            }
 
2825
            else
 
2826
                line << tOutput("$player_scoretable_inactive");
 
2827
            ret << line << "\n";
 
2828
        }
 
2829
        if ( max < se_PlayerNetIDs.Len() )
 
2830
        {
 
2831
            ret << "...\n";
 
2832
        }
 
2833
 
 
2834
    }
 
2835
    else
 
2836
        ret << tOutput("$player_scoretable_nobody");
 
2837
    return ret;
 
2838
}
 
2839
 
 
2840
 
 
2841
 
 
2842
tColoredString & operator << (tColoredString &s,const ePlayer &p){
 
2843
    return s << tColoredString::ColorString(p.rgb[0]/15.0,
 
2844
                                            p.rgb[1]/15.0,
 
2845
                                            p.rgb[2]/15.0)
 
2846
           << p.Name();
 
2847
}
 
2848
 
 
2849
tColoredString & operator << (tColoredString &s,const ePlayerNetID &p){
 
2850
    return s << p.GetColoredName();
 
2851
}
 
2852
 
 
2853
void ePlayerNetID::ClearAll(){
 
2854
    for(int i=MAX_PLAYERS-1;i>=0;i--){
 
2855
        ePlayer *local_p=ePlayer::PlayerConfig(i);
 
2856
        if (local_p)
 
2857
            local_p->netPlayer = NULL;
 
2858
    }
 
2859
}
 
2860
 
 
2861
void ePlayerNetID::CompleteRebuild(){
 
2862
    ClearAll();
 
2863
    Update();
 
2864
}
 
2865
 
 
2866
static bool se_VisubleSpectatorsSupported()
 
2867
{
 
2868
    static nVersionFeature se_visibleSpectator(13);
 
2869
    return sn_GetNetState() != nCLIENT || se_visibleSpectator.Supported(0);
 
2870
}
 
2871
 
 
2872
// Update the netPlayer_id list
 
2873
void ePlayerNetID::Update(){
 
2874
#ifdef DEDICATED
 
2875
    if (sr_glOut)
 
2876
#endif
 
2877
    {
 
2878
        for(int i=0; i<MAX_PLAYERS; ++i ){
 
2879
            bool in_game=ePlayer::PlayerIsInGame(i);
 
2880
            ePlayer *local_p=ePlayer::PlayerConfig(i);
 
2881
            tASSERT(local_p);
 
2882
            tCONTROLLED_PTR(ePlayerNetID) &p=local_p->netPlayer;
 
2883
 
 
2884
            if (!p && in_game && ( !local_p->spectate || se_VisubleSpectatorsSupported() ) ) // insert new player
 
2885
            {
 
2886
                p=tNEW(ePlayerNetID) (i);
 
2887
                p->FindDefaultTeam();
 
2888
                p->RequestSync();
 
2889
            }
 
2890
 
 
2891
            if (bool(p) && (!in_game || ( local_p->spectate && !se_VisubleSpectatorsSupported() ) ) && // remove player
 
2892
                    p->Owner() == ::sn_myNetID )
 
2893
            {
 
2894
                p->RemoveFromGame();
 
2895
 
 
2896
                if (p->object)
 
2897
                    p->object->player = NULL;
 
2898
 
 
2899
                p->object = NULL;
 
2900
                p = NULL;
 
2901
            }
 
2902
 
 
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;
 
2910
 
 
2911
                // update spectator status
 
2912
                if ( p->spectating_ != local_p->spectate )
 
2913
                    p->RequestSync();
 
2914
                p->spectating_ = local_p->spectate;
 
2915
 
 
2916
                // update name
 
2917
                tString newName( ePlayer::PlayerConfig(i)->Name() );
 
2918
 
 
2919
                if ( ::sn_GetNetState() != nCLIENT || newName != p->nameFromClient_ )
 
2920
                {
 
2921
                    p->RequestSync();
 
2922
                }
 
2923
 
 
2924
                p->SetName( ePlayer::PlayerConfig(i)->Name() );
 
2925
            }
 
2926
        }
 
2927
 
 
2928
    }
 
2929
    // update the ping charity
 
2930
    int old_c=sn_pingCharityServer;
 
2931
    sn_pingCharityServer=::pingCharity;
 
2932
#ifndef DEDICATED
 
2933
    if (sn_GetNetState()==nCLIENT)
 
2934
#endif
 
2935
        sn_pingCharityServer+=100000;
 
2936
 
 
2937
    int i;
 
2938
    for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
 
2939
        ePlayerNetID *pni=se_PlayerNetIDs(i);
 
2940
        pni->UpdateName();
 
2941
        int new_ps=pni->pingCharity;
 
2942
        new_ps+=int(pni->ping*500);
 
2943
 
 
2944
        if (new_ps< sn_pingCharityServer)
 
2945
            sn_pingCharityServer=new_ps;
 
2946
    }
 
2947
    if (sn_pingCharityServer<0)
 
2948
        sn_pingCharityServer=0;
 
2949
    if (old_c!=sn_pingCharityServer)
 
2950
    {
 
2951
        tOutput o;
 
2952
        o.SetTemplateParameter(1, old_c);
 
2953
        o.SetTemplateParameter(2, sn_pingCharityServer);
 
2954
        o << "$player_pingcharity_changed";
 
2955
        con << o;
 
2956
    }
 
2957
 
 
2958
    // update team assignment
 
2959
    bool assigned = true;
 
2960
    while ( assigned )
 
2961
    {
 
2962
        assigned = false;
 
2963
        for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
 
2964
        {
 
2965
            ePlayerNetID* player = se_PlayerNetIDs(i);
 
2966
 
 
2967
            // only assign new team if it is possible
 
2968
            if ( player->NextTeam() != player->CurrentTeam() &&
 
2969
                    ( !player->NextTeam() || player->NextTeam()->PlayerMayJoin( player ) )
 
2970
               )
 
2971
            {
 
2972
                player->UpdateTeam();
 
2973
                if ( player->NextTeam() == player->CurrentTeam() )
 
2974
                    assigned = true;
 
2975
            }
 
2976
        }
 
2977
    }
 
2978
 
 
2979
    if ( sn_GetNetState() != nCLIENT )
 
2980
    {
 
2981
        for(i=se_PlayerNetIDs.Len()-1;i>=0;i--)
 
2982
        {
 
2983
            ePlayerNetID* player = se_PlayerNetIDs(i);
 
2984
 
 
2985
            // announce unfullfilled wishes
 
2986
            if ( player->NextTeam() != player->CurrentTeam() && player->NextTeam() )
 
2987
            {
 
2988
                tOutput message( "$player_joins_team_wish",
 
2989
                                 player->GetName(),
 
2990
                                 player->NextTeam()->Name() );
 
2991
 
 
2992
                sn_ConsoleOut( message );
 
2993
 
 
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() );
 
2998
            }
 
2999
        }
 
3000
    }
 
3001
 
 
3002
    // update the teams as well
 
3003
    for (i=eTeam::teams.Len()-1; i>=0; --i)
 
3004
    {
 
3005
        eTeam::teams(i)->UpdateProperties();
 
3006
    }
 
3007
 
 
3008
    // get rid of deleted netobjects
 
3009
    nNetObject::ClearAllDeleted();
 
3010
}
 
3011
 
 
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 );
 
3015
 
 
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 );
 
3019
 
 
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 );
 
3023
 
 
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 );
 
3027
 
 
3028
// wait for players to leave chat state
 
3029
bool ePlayerNetID::WaitToLeaveChat()
 
3030
{
 
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;
 
3036
    bool ret = false;
 
3037
 
 
3038
    if ( !lastReturn )
 
3039
    {
 
3040
        // account for non-break pause: give players additional pause time
 
3041
        for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
 
3042
        {
 
3043
            ePlayerNetID* player = se_PlayerNetIDs(i);
 
3044
            if ( dt > 1.0 || !player->chatting_ )
 
3045
            {
 
3046
                player->wait_ += se_playerWaitFraction * dt;
 
3047
                if ( player->wait_ > se_playerWaitMax )
 
3048
                {
 
3049
                    player->wait_ = se_playerWaitMax;
 
3050
                }
 
3051
            }
 
3052
        }
 
3053
 
 
3054
        if ( dt > 1.0 )
 
3055
            lastPlayer = 0;
 
3056
 
 
3057
        dt = 0;
 
3058
    }
 
3059
 
 
3060
    // the chatting player with the most wait seconds left
 
3061
    ePlayerNetID * maxPlayer = 0;
 
3062
    REAL maxWait = .2;
 
3063
 
 
3064
    // iterate over chatting players
 
3065
    for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
 
3066
    {
 
3067
        ePlayerNetID* player = se_PlayerNetIDs(i);
 
3068
        if ( player->CurrentTeam() && player->chatting_ && ( !se_playerWaitTeamleader || player->CurrentTeam()->OldestHumanPlayer() == player ) )
 
3069
        {
 
3070
            // account for waiting time if everyone is to get his time reduced
 
3071
            if ( !se_playerWaitSingle )
 
3072
            {
 
3073
                player->wait_ -= dt;
 
3074
 
 
3075
                // hold back
 
3076
                if ( player->wait_ > 0 )
 
3077
                {
 
3078
                    ret = true;
 
3079
                }
 
3080
                else
 
3081
                {
 
3082
                    player->wait_ = 0;
 
3083
                }
 
3084
            }
 
3085
 
 
3086
            // determine player we'll wait for longest
 
3087
            if (  ( maxPlayer != lastPlayer || NULL == maxPlayer ) && ( player->wait_ > maxWait || player == lastPlayer ) && player->wait_ > 0 )
 
3088
            {
 
3089
                maxWait = player->wait_;
 
3090
                maxPlayer = player;
 
3091
            }
 
3092
        }
 
3093
    }
 
3094
 
 
3095
    // account for waiting time if only one player should get his waiting time reduced
 
3096
    if ( se_playerWaitSingle && maxPlayer )
 
3097
    {
 
3098
        maxPlayer->wait_ -= dt;
 
3099
 
 
3100
        // hold back
 
3101
        if ( maxPlayer->wait_ < 0 )
 
3102
        {
 
3103
            maxPlayer->wait_ = 0;
 
3104
        }
 
3105
    }
 
3106
 
 
3107
    static double lastPrint = -2;
 
3108
 
 
3109
    // print information: who are we waiting for?
 
3110
    if ( maxPlayer && maxPlayer != lastPlayer && tSysTimeFloat() - lastPrint > 1 )
 
3111
    {
 
3112
        sn_ConsoleOut( tOutput( "$gamestate_chat_wait", maxPlayer->GetName(), int(10*maxPlayer->wait_)*.1f ) );
 
3113
        lastPlayer = maxPlayer;
 
3114
    }
 
3115
 
 
3116
    if ( lastPlayer == maxPlayer )
 
3117
    {
 
3118
        lastPrint = tSysTimeFloat();
 
3119
    }
 
3120
 
 
3121
    // store values for future reference
 
3122
    lastReturn = ret;
 
3123
    lastTime = time;
 
3124
 
 
3125
    return ret;
 
3126
}
 
3127
 
 
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 );
 
3131
 
 
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 );
 
3135
 
 
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 );
 
3139
 
 
3140
//removes chatbots and idling players from the game
 
3141
void ePlayerNetID::RemoveChatbots()
 
3142
{
 
3143
    // nothing to be done on the clients
 
3144
    if ( nCLIENT == sn_GetNetState() )
 
3145
        return;
 
3146
 
 
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;
 
3152
 
 
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 )
 
3155
    {
 
3156
        ePlayerNetID *p = se_PlayerNetIDs(i);
 
3157
        if ( p && p->IsHuman() )
 
3158
        {
 
3159
            // time allowed to be idle
 
3160
            REAL idleTime = p->IsChatting() ? se_chatterRemoveTime : se_idleRemoveTime;
 
3161
 
 
3162
            // determine whether the player should have a team
 
3163
            bool shouldHaveTeam = idleTime <= 0 || p->LastActivity() - roundTime < idleTime;
 
3164
            shouldHaveTeam &= !p->IsSpectating();
 
3165
 
 
3166
            tColoredString name;
 
3167
            name << *p << tColoredString::ColorString(1,.5,.5);
 
3168
 
 
3169
            // see to it that the player has or has not a team.
 
3170
            if ( shouldHaveTeam )
 
3171
            {
 
3172
                if ( !p->CurrentTeam() )
 
3173
                {
 
3174
                    p->FindDefaultTeam();
 
3175
                }
 
3176
            }
 
3177
            else
 
3178
            {
 
3179
                if ( p->CurrentTeam() )
 
3180
                {
 
3181
                    p->SetTeam( NULL );
 
3182
                    p->UpdateTeam();
 
3183
                }
 
3184
            }
 
3185
 
 
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 )
 
3188
            {
 
3189
                sn_KickUser( p->Owner(), tOutput( "$network_kill_idle" ) );
 
3190
 
 
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;
 
3194
            }
 
3195
        }
 
3196
    }
 
3197
}
 
3198
 
 
3199
void ePlayerNetID::ThrowOutDisconnected()
 
3200
{
 
3201
    int i;
 
3202
    // find all disconnected players
 
3203
 
 
3204
    for(i=se_PlayerNetIDs.Len()-1;i>=0;i--){
 
3205
        ePlayerNetID *pni=se_PlayerNetIDs(i);
 
3206
        if (pni->disconnected)
 
3207
        {
 
3208
            // remove it from the list of players (so it won't be deleted twice...)
 
3209
            se_PlayerNetIDs.Remove(pni, pni->listID);
 
3210
        }
 
3211
    }
 
3212
 
 
3213
    se_PlayerReferences.ReleaseAll();
 
3214
}
 
3215
 
 
3216
void ePlayerNetID::GetScoreFromDisconnectedCopy()
 
3217
{
 
3218
    int i;
 
3219
    // find a copy
 
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)
 
3223
        {
 
3224
#ifdef DEBUG
 
3225
            con << GetName() << " reconnected.\n";
 
3226
#endif
 
3227
 
 
3228
            pni->RequestSync();
 
3229
            RequestSync();
 
3230
 
 
3231
            score = pni->score;
 
3232
 
 
3233
            ControlObject(pni->Object());
 
3234
            //   object->ePlayer = this;
 
3235
            pni->object = NULL;
 
3236
 
 
3237
            if (bool(object))
 
3238
                chatting_ = true;
 
3239
 
 
3240
            pni->disconnected = false;
 
3241
            se_PlayerNetIDs.Remove(pni, pni->listID);
 
3242
            se_PlayerReferences.Remove( pni );        // really delete it without a message
 
3243
        }
 
3244
    }
 
3245
}
 
3246
 
 
3247
 
 
3248
static bool show_scores=false;
 
3249
static bool ass=true;
 
3250
 
 
3251
void se_AutoShowScores(){
 
3252
    if (ass)
 
3253
        show_scores=true;
 
3254
}
 
3255
 
 
3256
 
 
3257
void se_UserShowScores(bool show){
 
3258
    show_scores=show;
 
3259
}
 
3260
 
 
3261
void se_SetShowScoresAuto(bool a){
 
3262
    ass=a;
 
3263
}
 
3264
 
 
3265
 
 
3266
static void scores(){
 
3267
    if (show_scores){
 
3268
        if ( se_mainGameTimer )
 
3269
            ePlayerNetID::DisplayScores();
 
3270
        else
 
3271
            show_scores = false;
 
3272
    }
 
3273
}
 
3274
 
 
3275
 
 
3276
static rPerFrameTask pf(&scores);
 
3277
 
 
3278
static bool force_small_cons(){
 
3279
    return show_scores;
 
3280
}
 
3281
 
 
3282
static rSmallConsoleCallback sc(&force_small_cons);
 
3283
 
 
3284
static void cd(){
 
3285
    show_scores = false;
 
3286
}
 
3287
 
 
3288
 
 
3289
 
 
3290
static uActionGlobal score("SCORE");
 
3291
 
 
3292
 
 
3293
static bool sf(REAL x){
 
3294
    if (x>0) show_scores = !show_scores;
 
3295
    return true;
 
3296
}
 
3297
 
 
3298
static uActionGlobalFunc saf(&score,&sf);
 
3299
 
 
3300
 
 
3301
static rCenterDisplayCallback c_d(&cd);
 
3302
 
 
3303
tOutput& operator << (tOutput& o, const ePlayerNetID& p)
 
3304
{
 
3305
    tColoredString x;
 
3306
    x << p;
 
3307
    o << x;
 
3308
    return o;
 
3309
}
 
3310
 
 
3311
 
 
3312
 
 
3313
tCallbackString *eCallbackGreeting::anchor = NULL;
 
3314
ePlayerNetID* eCallbackGreeting::greeted = NULL;
 
3315
 
 
3316
tString eCallbackGreeting::Greet(ePlayerNetID* player)
 
3317
{
 
3318
    greeted = player;
 
3319
    return Exec(anchor);
 
3320
}
 
3321
 
 
3322
eCallbackGreeting::eCallbackGreeting(STRINGRETFUNC* f)
 
3323
        :tCallbackString(anchor, f)
 
3324
{
 
3325
}
 
3326
 
 
3327
void ePlayerNetID::GreetHighscores(tString &s){
 
3328
    s << eCallbackGreeting::Greet(this);
 
3329
    //  tOutput o;
 
3330
    //  gHighscoresBase::Greet(this,o);
 
3331
    //  s << o;
 
3332
}
 
3333
 
 
3334
 
 
3335
// *******************
 
3336
// *      chatting_   *
 
3337
// *******************
 
3338
void ePlayerNetID::SetChatting ( ChatFlags flag, bool chatting )
 
3339
{
 
3340
    if ( sn_GetNetState() == nSTANDALONE && flag == ChatFlags_Menu )
 
3341
    {
 
3342
        chatting = false;
 
3343
    }
 
3344
 
 
3345
    if ( chatting )
 
3346
    {
 
3347
        chatFlags_ |= flag;
 
3348
        if ( !chatting_ )
 
3349
            this->RequestSync();
 
3350
 
 
3351
        chatting_ = true;
 
3352
    }
 
3353
    else
 
3354
    {
 
3355
        chatFlags_ &= ~flag;
 
3356
        if ( 0 == chatFlags_ )
 
3357
        {
 
3358
            if ( chatting_ )
 
3359
                this->RequestSync();
 
3360
 
 
3361
            chatting_ = false;
 
3362
        }
 
3363
    }
 
3364
}
 
3365
 
 
3366
// *******************
 
3367
// * team management *
 
3368
// *******************
 
3369
 
 
3370
// put a new player into a default team
 
3371
void ePlayerNetID::FindDefaultTeam( )
 
3372
{
 
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_ )
 
3375
        return;
 
3376
 
 
3377
    static bool recursion = false;
 
3378
    if ( recursion )
 
3379
    {
 
3380
        return;
 
3381
    }
 
3382
 
 
3383
    if ( !IsHuman() )
 
3384
    {
 
3385
        SetTeam( NULL );
 
3386
        return;
 
3387
    }
 
3388
 
 
3389
    recursion = true;
 
3390
 
 
3391
    // find the team with the least number of players on it
 
3392
    eTeam *min = NULL;
 
3393
    for ( int i=eTeam::teams.Len()-1; i>=0; --i )
 
3394
    {
 
3395
        eTeam *t = eTeam::teams( i );
 
3396
        if ( t->IsHuman()  && ( !min || min->NumHumanPlayers() > t->NumHumanPlayers() ) )
 
3397
            min = t;
 
3398
    }
 
3399
 
 
3400
    if ( min &&
 
3401
            eTeam::teams.Len() >= eTeam::minTeams &&
 
3402
            min->PlayerMayJoin( this ) &&
 
3403
            ( !eTeam::NewTeamAllowed() || ( min->NumHumanPlayers() > 0 && min->NumHumanPlayers() < favoriteNumberOfPlayersPerTeam ) )
 
3404
       )
 
3405
        SetTeamWish( min );                             // join the team
 
3406
    else if ( eTeam::NewTeamAllowed() )
 
3407
        CreateNewTeamWish();                    // create a new team
 
3408
 
 
3409
    // yes, if all teams are full and no team creation is allowed, the player stays without team and will not be spawned.
 
3410
 
 
3411
    recursion = false;
 
3412
}
 
3413
 
 
3414
// register me in the given team (callable on the server)
 
3415
void ePlayerNetID::SetTeam( eTeam* newTeam )
 
3416
{
 
3417
    // check if the team change is legal
 
3418
    tASSERT ( !newTeam || nCLIENT !=  sn_GetNetState() );
 
3419
 
 
3420
    SetTeamForce( newTeam );
 
3421
 
 
3422
    if (newTeam && ( !newTeam->PlayerMayJoin( this ) || IsSpectating() ) )
 
3423
    {
 
3424
        tOutput message;
 
3425
        message.SetTemplateParameter( 1, GetName() );
 
3426
        if ( newTeam )
 
3427
            message.SetTemplateParameter(2, newTeam->Name() );
 
3428
        else
 
3429
            message.SetTemplateParameter(2, "NULL");
 
3430
        message << "$player_nojoin_team";
 
3431
 
 
3432
        sn_ConsoleOut( message, Owner() );
 
3433
        // return;
 
3434
    }
 
3435
}
 
3436
 
 
3437
// register me in the given team (callable on the server)
 
3438
void ePlayerNetID::SetTeamForce( eTeam* newTeam )
 
3439
{
 
3440
    // check if the team change is legal
 
3441
    tASSERT ( !newTeam || nCLIENT !=  sn_GetNetState() );
 
3442
 
 
3443
    nextTeam = newTeam;
 
3444
}
 
3445
 
 
3446
// register me in the given team (callable on the server)
 
3447
void ePlayerNetID::UpdateTeam()
 
3448
{
 
3449
    // check if work is needed
 
3450
    if ( nextTeam == currentTeam )
 
3451
    {
 
3452
        return;
 
3453
    }
 
3454
 
 
3455
    // check if the team change is legal
 
3456
    if ( nCLIENT ==  sn_GetNetState() )
 
3457
    {
 
3458
        return;
 
3459
    }
 
3460
 
 
3461
    if ( bool( nextTeam ) && !nextTeam->PlayerMayJoin( this ) )
 
3462
    {
 
3463
        tOutput message;
 
3464
        message.SetTemplateParameter(1, GetName() );
 
3465
        if ( nextTeam )
 
3466
            message.SetTemplateParameter(2, nextTeam->Name() );
 
3467
        else
 
3468
            message.SetTemplateParameter(2, "NULL");
 
3469
        message << "$player_nojoin_team";
 
3470
 
 
3471
        sn_ConsoleOut( message, Owner() );
 
3472
        return;
 
3473
    }
 
3474
 
 
3475
    UpdateTeamForce();
 
3476
}
 
3477
 
 
3478
void ePlayerNetID::UpdateTeamForce()
 
3479
{
 
3480
    // check if work is needed
 
3481
    if ( nextTeam == currentTeam )
 
3482
    {
 
3483
        return;
 
3484
    }
 
3485
 
 
3486
    eTeam *oldTeam = currentTeam;
 
3487
 
 
3488
    if ( nextTeam )
 
3489
        nextTeam->AddPlayer ( this );
 
3490
    else if ( oldTeam )
 
3491
        oldTeam->RemovePlayer( this );
 
3492
 
 
3493
    if( nCLIENT !=  sn_GetNetState() && GetRefcount() > 0 )
 
3494
    {
 
3495
        RequestSync();
 
3496
    }
 
3497
}
 
3498
 
 
3499
// create a new team and join it (on the server)
 
3500
void ePlayerNetID::CreateNewTeam()
 
3501
{
 
3502
    // check if the team change is legal
 
3503
    tASSERT ( nCLIENT !=  sn_GetNetState() );
 
3504
 
 
3505
    if ( !eTeam::NewTeamAllowed() ||
 
3506
            ( bool( currentTeam ) && ( currentTeam->NumHumanPlayers() == 1 ) ) ||
 
3507
            IsSpectating() )
 
3508
    {
 
3509
        tOutput message;
 
3510
        message.SetTemplateParameter(1, GetName() );
 
3511
        message << "$player_nocreate_team";
 
3512
 
 
3513
        sn_ConsoleOut( message, Owner() );
 
3514
 
 
3515
        if ( !currentTeam )
 
3516
            FindDefaultTeam();
 
3517
 
 
3518
        return;
 
3519
    }
 
3520
 
 
3521
    // create the new team and join it
 
3522
    tJUST_CONTROLLED_PTR< eTeam > newTeam = tNEW( eTeam );
 
3523
    nextTeam = newTeam;
 
3524
    nextTeam->AddScore( score );
 
3525
 
 
3526
    // directly if possible
 
3527
    if ( !currentTeam )
 
3528
    {
 
3529
        UpdateTeam();
 
3530
    }
 
3531
}
 
3532
 
 
3533
const unsigned short TEAMCHANGE = 0;
 
3534
const unsigned short NEW_TEAM   = 1;
 
3535
 
 
3536
 
 
3537
// express the wish to be part of the given team (always callable)
 
3538
void ePlayerNetID::SetTeamWish(eTeam* newTeam)
 
3539
{
 
3540
    if ( nCLIENT ==  sn_GetNetState() && Owner() == sn_myNetID )
 
3541
    {
 
3542
        nMessage* m = NewControlMessage();
 
3543
 
 
3544
        (*m) << TEAMCHANGE;
 
3545
        (*m) << newTeam;
 
3546
 
 
3547
        m->BroadCast();
 
3548
    }
 
3549
    else
 
3550
    {
 
3551
        SetTeam( newTeam );
 
3552
 
 
3553
        // directly join if possible to keep counts up to date
 
3554
        if (!currentTeam)
 
3555
            UpdateTeam();
 
3556
    }
 
3557
}
 
3558
 
 
3559
// express the wish to create a new team and join it
 
3560
void ePlayerNetID::CreateNewTeamWish()
 
3561
{
 
3562
    if ( nCLIENT ==  sn_GetNetState() )
 
3563
    {
 
3564
        nMessage* m = NewControlMessage();
 
3565
 
 
3566
        (*m) << NEW_TEAM;
 
3567
 
 
3568
        m->BroadCast();
 
3569
    }
 
3570
    else
 
3571
        CreateNewTeam();
 
3572
 
 
3573
}
 
3574
 
 
3575
// receive the team control wish
 
3576
void ePlayerNetID::ReceiveControlNet(nMessage &m)
 
3577
{
 
3578
    short messageType;
 
3579
    m >> messageType;
 
3580
 
 
3581
    switch (messageType)
 
3582
    {
 
3583
    case NEW_TEAM:
 
3584
        {
 
3585
            CreateNewTeam();
 
3586
 
 
3587
            break;
 
3588
        }
 
3589
    case TEAMCHANGE:
 
3590
        {
 
3591
            eTeam *newTeam;
 
3592
 
 
3593
            m >> newTeam;
 
3594
 
 
3595
            // NULL team probably means that the change target does not
 
3596
            // exist any more. Create a new team instead.
 
3597
            if ( !newTeam )
 
3598
            {
 
3599
                sn_ConsoleOut( tOutput( "$player_joins_team_noex" ), Owner() );
 
3600
                break;
 
3601
            }
 
3602
 
 
3603
            // check if the resulting message is obnoxious
 
3604
            bool redundant = ( nextTeam == newTeam );
 
3605
            bool obnoxious = ( nextTeam != currentTeam || redundant );
 
3606
 
 
3607
            SetTeam( newTeam );
 
3608
 
 
3609
            // announce the change
 
3610
            if ( bool(nextTeam) && !redundant )
 
3611
            {
 
3612
                tOutput message;
 
3613
                message.SetTemplateParameter(1, tColoredString::RemoveColors(GetName()));
 
3614
                message.SetTemplateParameter(2, tColoredString::RemoveColors(nextTeam->Name()) );
 
3615
                message << "$player_joins_team";
 
3616
 
 
3617
                sn_ConsoleOut( message );
 
3618
 
 
3619
                // count it as spam if it is obnoxious
 
3620
                if ( obnoxious )
 
3621
                    chatSpam_.CheckSpam( 4.0, Owner() );
 
3622
            }
 
3623
 
 
3624
            break;
 
3625
        }
 
3626
    default:
 
3627
        {
 
3628
            tASSERT(0);
 
3629
        }
 
3630
    }
 
3631
}
 
3632
 
 
3633
void ePlayerNetID::Color( REAL&a_r, REAL&a_g, REAL&a_b ) const
 
3634
{
 
3635
    if ( ( static_cast<bool>(currentTeam) ) && ( currentTeam->IsHuman() ) )
 
3636
    {
 
3637
        REAL w = 5;
 
3638
        REAL r_w = 2;
 
3639
        REAL g_w = 1;
 
3640
        REAL b_w = 2;
 
3641
 
 
3642
        int r = this->r;
 
3643
        int g = this->g;
 
3644
        int b = this->b;
 
3645
 
 
3646
        // don't tolerate color overflow in a real team
 
3647
        if ( currentTeam->NumPlayers() > 1 )
 
3648
        {
 
3649
            if ( r > 15 )
 
3650
                r = 15;
 
3651
            if ( g > 15 )
 
3652
                g = 15;
 
3653
            if ( b > 15 )
 
3654
                b = 15;
 
3655
        }
 
3656
 
 
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 ) );
 
3660
    }
 
3661
    else
 
3662
    {
 
3663
        a_r = r/15.0;
 
3664
        a_g = g/15.0;
 
3665
        a_b = b/15.0;
 
3666
    }
 
3667
}
 
3668
 
 
3669
void ePlayerNetID::TrailColor( REAL&a_r, REAL&a_g, REAL&a_b ) const
 
3670
{
 
3671
    Color( a_r, a_g, a_b );
 
3672
 
 
3673
    /*
 
3674
    if ( ( static_cast<bool>(currentTeam) ) && ( currentTeam->IsHuman() ) )
 
3675
    {
 
3676
        int w = 6;
 
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 ) );
 
3680
    }
 
3681
    else
 
3682
    {
 
3683
        a_r = r/15.0;
 
3684
        a_g = g/15.0;
 
3685
        a_b = b/15.0;
 
3686
    }
 
3687
    */
 
3688
}
 
3689
 
 
3690
/*
 
3691
void ePlayerNetID::AddRef()
 
3692
{
 
3693
        nNetObject::AddRef();
 
3694
}
 
3695
 
 
3696
void ePlayerNetID::Release()
 
3697
{
 
3698
        nNetObject::Release();
 
3699
}
 
3700
*/
 
3701
 
 
3702
// reads a network ID from the stream, either the number or my user name
 
3703
static unsigned short se_ReadUser( std::istream &s )
 
3704
{
 
3705
    // read name of player to be kicked
 
3706
    tString name;
 
3707
    s >> name;
 
3708
 
 
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 )
 
3712
    {
 
3713
        return num;
 
3714
    }
 
3715
    else
 
3716
    {
 
3717
        // compare the read name with the players' user names
 
3718
        for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
 
3719
        {
 
3720
            ePlayerNetID* p = se_PlayerNetIDs(i);
 
3721
            if ( p && p->GetUserName() == name )
 
3722
            {
 
3723
                int owner = p->Owner();
 
3724
                if ( owner > 0 )
 
3725
                {
 
3726
                    return owner;
 
3727
                }
 
3728
 
 
3729
                con << tOutput( "$network_kick_notfound", name );
 
3730
            }
 
3731
        }
 
3732
    }
 
3733
 
 
3734
    return 0;
 
3735
}
 
3736
 
 
3737
static void se_KickConf(std::istream &s)
 
3738
{
 
3739
    // get user ID
 
3740
    int num = se_ReadUser( s );
 
3741
 
 
3742
    tString reason;
 
3743
    reason.ReadLine(s);
 
3744
 
 
3745
    // and kick.
 
3746
    if ( num > 0 && !s.good() )
 
3747
    {
 
3748
        sn_KickUser( num ,  reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick" );
 
3749
    }
 
3750
    else
 
3751
    {
 
3752
        con << "Usage: KICK <user ID or name> <Reason>\n";
 
3753
        return;
 
3754
    }
 
3755
}
 
3756
 
 
3757
static tConfItemFunc se_kickConf("KICK",&se_KickConf);
 
3758
 
 
3759
 
 
3760
static void se_BanConf(std::istream &s)
 
3761
{
 
3762
    // get user ID
 
3763
    int num = se_ReadUser( s );
 
3764
 
 
3765
    if ( num == 0 && !s.good() )
 
3766
    {
 
3767
        con << "Usage: BAN <user ID or name> <time in minutes(defaults to 60)> <Reason>\n";
 
3768
        return;
 
3769
    }
 
3770
 
 
3771
    // read time to ban
 
3772
    REAL banTime = 60;
 
3773
    s >> banTime;
 
3774
    std::ws(s);
 
3775
 
 
3776
    tString reason;
 
3777
    reason.ReadLine(s);
 
3778
 
 
3779
    // and ban.
 
3780
    if ( num > 0 )
 
3781
    {
 
3782
        nMachine::GetMachine( num ).Ban( banTime * 60, reason );
 
3783
        sn_DisconnectUser( num , reason.Len() > 1 ? static_cast< char const *>( reason ) : "$network_kill_kick" );
 
3784
    }
 
3785
}
 
3786
 
 
3787
static tConfItemFunc se_banConf("BAN",&se_BanConf);
 
3788
 
 
3789
static void Kill_conf(std::istream &s)
 
3790
{
 
3791
    // read name of player to be killed
 
3792
    tString name;
 
3793
    name.ReadLine( s );
 
3794
    int num = name.toInt();
 
3795
 
 
3796
    for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
 
3797
    {
 
3798
        ePlayerNetID* p = se_PlayerNetIDs(i);
 
3799
 
 
3800
        // check whether it's p who should be killed,
 
3801
        // either by comparing the owner or the name.
 
3802
        bool itsHim = false;
 
3803
        if ( num > 0 )
 
3804
            itsHim = ( p->Owner() == num );
 
3805
        else
 
3806
            itsHim = ( p->GetUserName() == name );
 
3807
 
 
3808
        // kill the player's game object
 
3809
        if ( itsHim )
 
3810
        {
 
3811
            if ( p->Object() )
 
3812
                p->Object()->Kill();
 
3813
 
 
3814
            return;
 
3815
        }
 
3816
    }
 
3817
 
 
3818
    tOutput o;
 
3819
    o.SetTemplateParameter( 1, name );
 
3820
    o << "$network_kick_notfound";
 
3821
    con << o;
 
3822
}
 
3823
 
 
3824
static tConfItemFunc kill_conf("KILL",&Kill_conf);
 
3825
 
 
3826
static void players_conf(std::istream &s)
 
3827
{
 
3828
    /*    for ( int i=0; i < se_PlayerNetIDs.Len(); i++ )
 
3829
        {
 
3830
            ePlayerNetID* p = se_PlayerNetIDs(i);
 
3831
            tOutput o;
 
3832
            o << "Player " << p->Owner() << ": " << p->name;
 
3833
            if (p->isLoggedIn())
 
3834
                o << " (logged in)\n";
 
3835
            else
 
3836
                o << " (logged out)\n";
 
3837
            con << o;
 
3838
        } */
 
3839
    for ( int i2 = se_PlayerNetIDs.Len()-1; i2>=0; --i2 )
 
3840
    {
 
3841
        ePlayerNetID* p2 = se_PlayerNetIDs(i2);
 
3842
        tOutput tos;
 
3843
        tos << p2->Owner();
 
3844
        tos << ": ";
 
3845
        tos << p2->GetUserName();
 
3846
        if (p2->isLoggedIn())
 
3847
            tos << " (logged in)";
 
3848
        else
 
3849
            tos << " (logged out)";
 
3850
        tos << "\n";
 
3851
        con << tos;
 
3852
    }
 
3853
}
 
3854
 
 
3855
static tConfItemFunc players("PLAYERS",&players_conf);
 
3856
 
 
3857
static tString sg_url;
 
3858
static tSettingItem< tString > sg_urlConf( "URL", sg_url );
 
3859
 
 
3860
static tString sg_options("Nothing special.");
 
3861
#ifdef DEDICATED
 
3862
static tConfItemLine sg_optionsConf( "SERVER_OPTIONS", sg_options );
 
3863
#endif
 
3864
 
 
3865
class gServerInfoAdmin: public nServerInfoAdmin
 
3866
{
 
3867
public:
 
3868
    gServerInfoAdmin(){};
 
3869
 
 
3870
private:
 
3871
    virtual tString GetUsers()          const
 
3872
    {
 
3873
        tString ret;
 
3874
 
 
3875
        for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
 
3876
    {
 
3877
        ePlayerNetID* p = se_PlayerNetIDs(i);
 
3878
            if ( p->IsHuman() )
 
3879
            {
 
3880
                ret << p->GetName() << "\n";
 
3881
            }
 
3882
        }
 
3883
 
 
3884
        return ret;
 
3885
    }
 
3886
 
 
3887
    virtual tString     GetOptions()    const
 
3888
    {
 
3889
        se_CutString( sg_options, 240 );
 
3890
        return sg_options;
 
3891
    }
 
3892
 
 
3893
    virtual tString GetUrl()            const
 
3894
    {
 
3895
        se_CutString( sg_url, 75 );
 
3896
        return sg_url;
 
3897
    }
 
3898
};
 
3899
 
 
3900
static gServerInfoAdmin sg_serverAdmin;
 
3901
 
 
3902
// ******************************************************************************************
 
3903
// *
 
3904
// *    UpdateName
 
3905
// *
 
3906
// ******************************************************************************************
 
3907
//!
 
3908
//!
 
3909
// ******************************************************************************************
 
3910
 
 
3911
void ePlayerNetID::UpdateName( void )
 
3912
{
 
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);
 
3917
 
 
3918
    // apply client change, stripping excess spaces
 
3919
    if ( sn_GetNetState() != nCLIENT )
 
3920
    {
 
3921
        if ( se_stripNames )
 
3922
            se_StripNameEnds( nameFromClient_ );
 
3923
 
 
3924
        // disallow name changes if there was a kick vote recently
 
3925
        if ( !bool(this->voter_) || voter_->AllowNameChange() || nameFromServer_.Len() <= 1 )
 
3926
        {
 
3927
            nameFromServer_ = nameFromClient_;
 
3928
        }
 
3929
        else if ( nameFromServer_ != nameFromClient_ )
 
3930
        {
 
3931
            // inform victim to avoid complaints
 
3932
            tOutput message( "$player_rename_rejected", nameFromServer_, nameFromClient_ );
 
3933
            sn_ConsoleOut( message, Owner() );
 
3934
            con << message;
 
3935
 
 
3936
            // revert name
 
3937
            nameFromClient_ = nameFromServer_;
 
3938
        }
 
3939
    }
 
3940
 
 
3941
    // remove colors from name
 
3942
    tString newName = tColoredString::RemoveColors( nameFromServer_ );
 
3943
    tString newUserName;
 
3944
    FilterName( newName, newUserName );
 
3945
 
 
3946
    // test if it is already taken, find an alternative name if so.
 
3947
    if ( sn_GetNetState() != nCLIENT && !se_allowImposters && se_IsNameTaken( newUserName, this ) )
 
3948
    {
 
3949
        // Remove possilble trailing digit.
 
3950
        if ( newName.Len() > 2 && isdigit(newName(newName.Len()-2)) )
 
3951
        {
 
3952
            newName = newName.SubStr( 0, newName.Len()-2 );
 
3953
        }
 
3954
 
 
3955
        // append numbers until the name is free
 
3956
        for ( int i=2; i<1000; ++i )
 
3957
        {
 
3958
            tString testName(newName);
 
3959
            testName << i;
 
3960
 
 
3961
            // cut the beginning if the name is too long
 
3962
            if ( testName.Len() > 17 )
 
3963
                testName = testName.SubStr( testName.Len() - 17 );
 
3964
 
 
3965
            FilterName( testName, newUserName );
 
3966
 
 
3967
            if ( !se_IsNameTaken( newUserName, this ) )
 
3968
            {
 
3969
                newName = testName;
 
3970
                break;
 
3971
            }
 
3972
        }
 
3973
 
 
3974
        // rudely overwrite name from client
 
3975
        nameFromServer_ = newName;
 
3976
    }
 
3977
 
 
3978
    // set the colored name to the name from the client, set trailing color to white
 
3979
    coloredName_.Clear();
 
3980
    REAL r,g,b;
 
3981
    Color(r,g,b);
 
3982
    coloredName_ << tColoredString::ColorString(r,g,b) << nameFromServer_;
 
3983
 
 
3984
    if ( name_ != newName )
 
3985
    {
 
3986
        // copy it to the name, removing colors of course
 
3987
        name_ = newName;
 
3988
 
 
3989
        // remove spaces and possibly other nasties for the user name
 
3990
        FilterName( name_, userName_ );
 
3991
 
 
3992
        if (sn_GetNetState()!=nCLIENT)
 
3993
        {
 
3994
            // sync the new name
 
3995
            RequestSync();
 
3996
 
 
3997
            tOutput mess;
 
3998
 
 
3999
            tColoredString printname;
 
4000
            printname << *this << tColoredString::ColorString(.5,1,.5);
 
4001
 
 
4002
            mess.SetTemplateParameter(1, printname);
 
4003
            mess.SetTemplateParameter(2, oldprintname);
 
4004
 
 
4005
            if (oldUserName.Len()<=1 && GetUserName().Len()>=1){
 
4006
 
 
4007
#ifdef KRAWALL_SERVER
 
4008
                if (sn_GetNetState() == nSERVER && Owner() != sn_myNetID )
 
4009
                {
 
4010
                    auth = false;
 
4011
                    nAuthentification::RequestLogin(GetUserName(), Owner(), "$login_request_first");
 
4012
                }
 
4013
#endif
 
4014
 
 
4015
                // print spectating join message (regular join messages are handled by eTeam)
 
4016
                if ( IsSpectating() )
 
4017
                {
 
4018
                    mess << "$player_entered_spectator";
 
4019
                    sn_ConsoleOut(mess);
 
4020
                }
 
4021
 
 
4022
                if ( IsHuman() )
 
4023
                {
 
4024
                    tString ladder;
 
4025
                    ladder << "PLAYER_ENTERED " << userName_ << " " << nMachine::GetMachine(Owner()).GetIP() << "\n";
 
4026
                    se_SaveToLadderLog(ladder);
 
4027
 
 
4028
                    Greet();
 
4029
                }
 
4030
            }
 
4031
            else if (strcmp(oldUserName,GetUserName()))
 
4032
            {
 
4033
#ifdef KRAWALL_SERVER
 
4034
                if (sn_GetNetState() == nSERVER && Owner() != sn_myNetID )
 
4035
                {
 
4036
                    nAuthentification::RequestLogin(GetUserName(), Owner(), "$login_request_namechange");
 
4037
                    auth = false;
 
4038
                    userName_ = oldUserName; // restore the old name until the new one is authenticated
 
4039
                }
 
4040
#endif
 
4041
                mess << "$player_renamed";
 
4042
                sn_ConsoleOut(mess);
 
4043
 
 
4044
                {
 
4045
                    tString ladder;
 
4046
                    ladder << "PLAYER_RENAMED " <<  oldUserName << " "  << userName_ << " " << nMachine::GetMachine(Owner()).GetIP() << "\n";
 
4047
                    se_SaveToLadderLog(ladder);
 
4048
                }
 
4049
            }
 
4050
        }
 
4051
    }
 
4052
}
 
4053
 
 
4054
// filters illegal player characters
 
4055
class ePlayerCharacterFilter
 
4056
{
 
4057
public:
 
4058
    ePlayerCharacterFilter()
 
4059
    {
 
4060
        int i;
 
4061
        filter[0]=0;
 
4062
 
 
4063
        // map all unknown characters to underscores
 
4064
        for (i=255; i>0; --i)
 
4065
        {
 
4066
            filter[i] = '_';
 
4067
        }
 
4068
 
 
4069
        // leave ASCII characters as they are
 
4070
        for (i=126; i>32; --i)
 
4071
        {
 
4072
            filter[i] = i;
 
4073
        }
 
4074
        // but convert uppercase characters to lowercase
 
4075
        for (i='Z'; i>='A'; --i)
 
4076
        {
 
4077
            filter[i] = i + ('a' - 'A');
 
4078
        }
 
4079
 
 
4080
        //! map umlauts and stuff to their base characters
 
4081
        SetMap(0xc0,0xc5,'a');
 
4082
        SetMap(0xd1,0xd6,'o');
 
4083
        SetMap(0xd9,0xdD,'u');
 
4084
        SetMap(0xdf,'s');
 
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');
 
4090
 
 
4091
        // ok, some of those are a bit questionable, but still better than _...
 
4092
        SetMap(161,'!');
 
4093
        SetMap(162,'c');
 
4094
        SetMap(163,'l');
 
4095
        SetMap(165,'y');
 
4096
        SetMap(166,'|');
 
4097
        SetMap(167,'s');
 
4098
        SetMap(168,'"');
 
4099
        SetMap(169,'c');
 
4100
        SetMap(170,'a');
 
4101
        SetMap(171,'"');
 
4102
        SetMap(172,'!');
 
4103
        SetMap(174,'r');
 
4104
        SetMap(176,'o');
 
4105
        SetMap(177,'+');
 
4106
        SetMap(178,'2');
 
4107
        SetMap(179,'3');
 
4108
        SetMap(182,'p');
 
4109
        SetMap(183,'.');
 
4110
        SetMap(185,'1');
 
4111
        SetMap(187,'"');
 
4112
        SetMap(198,'a');
 
4113
        SetMap(199,'c');
 
4114
        SetMap(208,'d');
 
4115
        SetMap(209,'n');
 
4116
        SetMap(215,'x');
 
4117
        SetMap(216,'o');
 
4118
        SetMap(221,'y');
 
4119
        SetMap(222,'p');
 
4120
        SetMap(231,'c');
 
4121
        SetMap(241,'n');
 
4122
        SetMap(247,'/');
 
4123
        SetMap(248,'o');
 
4124
        SetMap(253,'y');
 
4125
        SetMap(254,'p');
 
4126
        SetMap(255,'y');
 
4127
 
 
4128
        //map 0 to o because they look similar
 
4129
        SetMap('0','o');
 
4130
 
 
4131
        // TODO: make this data driven.
 
4132
    }
 
4133
 
 
4134
    char Filter( unsigned char in )
 
4135
    {
 
4136
        return filter[ static_cast< unsigned int >( in )];
 
4137
    }
 
4138
private:
 
4139
    void SetMap( int in1, int in2, unsigned char out)
 
4140
    {
 
4141
        tASSERT( in2 <= 0xff );
 
4142
        tASSERT( 0 <= in1 );
 
4143
        tASSERT( in1 < in2 );
 
4144
        for( int i = in2; i >= in1; --i )
 
4145
            filter[ i ] = out;
 
4146
    }
 
4147
 
 
4148
    void SetMap( unsigned char in, unsigned char out)
 
4149
    {
 
4150
        filter[ static_cast< unsigned int >( in ) ] = out;
 
4151
    }
 
4152
 
 
4153
    char filter[256];
 
4154
};
 
4155
 
 
4156
static bool se_IsUnderscore( char c )
 
4157
{
 
4158
    return c == '_';
 
4159
}
 
4160
 
 
4161
// ******************************************************************************************
 
4162
// *
 
4163
// *    FilterName
 
4164
// *
 
4165
// ******************************************************************************************
 
4166
//!
 
4167
//!             @param  in   input name
 
4168
//!             @param  out  output name cleared to be usable as a username
 
4169
//!
 
4170
// ******************************************************************************************
 
4171
 
 
4172
void ePlayerNetID::FilterName( tString const & in, tString & out )
 
4173
{
 
4174
    int i;
 
4175
    static ePlayerCharacterFilter filter;
 
4176
    out = tColoredString::RemoveColors( in );
 
4177
 
 
4178
    // filter out illegal characters
 
4179
    for ( i = out.Len()-1; i>=0; --i )
 
4180
    {
 
4181
        char & c = out[i];
 
4182
 
 
4183
        c = filter.Filter( c );
 
4184
    }
 
4185
 
 
4186
    // strip leading and trailing unknown characters
 
4187
    se_StripMatchingEnds( out, se_IsUnderscore, se_IsUnderscore );
 
4188
}
 
4189
 
 
4190
// ******************************************************************************************
 
4191
// *
 
4192
// *    FilterName
 
4193
// *
 
4194
// ******************************************************************************************
 
4195
//!
 
4196
//!             @param  in   input name
 
4197
//!             @return      output name cleared to be usable as a username
 
4198
//!
 
4199
// ******************************************************************************************
 
4200
 
 
4201
tString ePlayerNetID::FilterName( tString const & in )
 
4202
{
 
4203
    tString out;
 
4204
    FilterName( in, out );
 
4205
    return out;
 
4206
}
 
4207
 
 
4208
// ******************************************************************************************
 
4209
// *
 
4210
// * SetName
 
4211
// *
 
4212
// ******************************************************************************************
 
4213
//!
 
4214
//!      @param  name    this player's name without colors. to set
 
4215
//!       @return     A reference to this to allow chaining
 
4216
//!
 
4217
// ******************************************************************************************
 
4218
 
 
4219
ePlayerNetID & ePlayerNetID::SetName( tString const & name )
 
4220
{
 
4221
    this->nameFromClient_ = name;
 
4222
    this->nameFromClient_.NetFilter();
 
4223
 
 
4224
    // replace empty name
 
4225
    if ( !IsLegalPlayerName( nameFromClient_ ) )
 
4226
        nameFromClient_ = "Player 1";
 
4227
 
 
4228
    if ( sn_GetNetState() != nCLIENT )
 
4229
        nameFromServer_ = nameFromClient_;
 
4230
 
 
4231
    UpdateName();
 
4232
 
 
4233
    return *this;
 
4234
}
 
4235
 
 
4236
// ******************************************************************************************
 
4237
// *
 
4238
// * SetName
 
4239
// *
 
4240
// ******************************************************************************************
 
4241
//!
 
4242
//!      @param  name    this player's name without colors. to set
 
4243
//!       @return     A reference to this to allow chaining
 
4244
//!
 
4245
// ******************************************************************************************
 
4246
 
 
4247
ePlayerNetID & ePlayerNetID::SetName( char const * name )
 
4248
{
 
4249
    SetName( tString( name ) );
 
4250
    return *this;
 
4251
}
 
4252
 
 
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 );
 
4259
 
 
4260
// *******************************************************************************
 
4261
// *
 
4262
// *    Enemies
 
4263
// *
 
4264
// *******************************************************************************
 
4265
//!
 
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
 
4269
//!
 
4270
// *******************************************************************************
 
4271
 
 
4272
bool ePlayerNetID::Enemies( ePlayerNetID const * a, ePlayerNetID const * b )
 
4273
{
 
4274
    // the client does not need a true answer
 
4275
    if ( sn_GetNetState() == nCLIENT )
 
4276
        return true;
 
4277
 
 
4278
    // no scoring if one of them does not exist
 
4279
    if ( !a || !b )
 
4280
        return false;
 
4281
 
 
4282
    // no scoring for two players from the same IP
 
4283
    if ( !se_allowEnemiesSameIP && a->Owner() != 0 && a->GetMachine() == b->GetMachine() )
 
4284
        return false;
 
4285
 
 
4286
    // no scoring for two players from the same client
 
4287
    if ( !se_allowEnemiesSameClient && a->Owner() != 0 && a->Owner() == b->Owner() )
 
4288
        return false;
 
4289
 
 
4290
    // no objections
 
4291
    return true;
 
4292
}
 
4293
 
 
4294
// *******************************************************************************
 
4295
// *
 
4296
// *    RegisterWithMachine
 
4297
// *
 
4298
// *******************************************************************************
 
4299
//!
 
4300
//!
 
4301
// *******************************************************************************
 
4302
 
 
4303
void ePlayerNetID::RegisterWithMachine( void )
 
4304
{
 
4305
    if ( !registeredMachine_ )
 
4306
    {
 
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();
 
4310
    }
 
4311
}
 
4312
 
 
4313
// *******************************************************************************
 
4314
// *
 
4315
// *    UnregisterWithMachine
 
4316
// *
 
4317
// *******************************************************************************
 
4318
//!
 
4319
//!
 
4320
// *******************************************************************************
 
4321
 
 
4322
void ePlayerNetID::UnregisterWithMachine( void )
 
4323
{
 
4324
    if ( registeredMachine_ )
 
4325
    {
 
4326
        registeredMachine_->RemovePlayer();
 
4327
        registeredMachine_ = 0;
 
4328
    }
 
4329
}
 
4330
 
 
4331
// *******************************************************************************
 
4332
// *
 
4333
// *    DoGetMachine
 
4334
// *
 
4335
// *******************************************************************************
 
4336
//!
 
4337
//!             @return         the machine this object belongs to
 
4338
//!
 
4339
// *******************************************************************************
 
4340
 
 
4341
nMachine & ePlayerNetID::DoGetMachine( void ) const
 
4342
{
 
4343
    // return machine I'm registered at, otherwise whatever the base class thinks
 
4344
    if ( registeredMachine_ )
 
4345
        return *registeredMachine_;
 
4346
    else
 
4347
        return nNetObject::DoGetMachine();
 
4348
}
 
4349
 
 
4350
// *******************************************************************************
 
4351
// *
 
4352
// *    LastActivity
 
4353
// *
 
4354
// *******************************************************************************
 
4355
//!
 
4356
//!             @return
 
4357
//!
 
4358
// *******************************************************************************
 
4359
 
 
4360
REAL ePlayerNetID::LastActivity( void ) const
 
4361
{
 
4362
    return tSysTimeFloat() - lastActivity_;
 
4363
}
 
4364
 
 
4365
// *******************************************************************************
 
4366
// *
 
4367
// *    ResetScoreDifferences
 
4368
// *
 
4369
// *******************************************************************************
 
4370
//!
 
4371
//!
 
4372
// *******************************************************************************
 
4373
 
 
4374
void ePlayerNetID::ResetScoreDifferences( void )
 
4375
{
 
4376
    for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
 
4377
    {
 
4378
        ePlayerNetID* p = se_PlayerNetIDs(i);
 
4379
        if ( bool(p->Object()) && p->IsHuman() )
 
4380
            p->lastScore_ = p->score;
 
4381
    }
 
4382
}
 
4383
 
 
4384
// *******************************************************************************
 
4385
// *
 
4386
// *    LogScoreDifferences
 
4387
// *
 
4388
// *******************************************************************************
 
4389
//!
 
4390
//!
 
4391
// *******************************************************************************
 
4392
 
 
4393
void ePlayerNetID::LogScoreDifferences( void )
 
4394
{
 
4395
    for ( int i = se_PlayerNetIDs.Len()-1; i>=0; --i )
 
4396
    {
 
4397
        ePlayerNetID* p = se_PlayerNetIDs(i);
 
4398
        p->LogScoreDifference();
 
4399
    }
 
4400
}
 
4401
 
 
4402
// *******************************************************************************
 
4403
// *
 
4404
// *    LogScoreDifference
 
4405
// *
 
4406
// *******************************************************************************
 
4407
//!
 
4408
//!
 
4409
// *******************************************************************************
 
4410
 
 
4411
void ePlayerNetID::LogScoreDifference( void )
 
4412
{
 
4413
    if ( lastScore_ > IMPOSSIBLY_LOW_SCORE && IsHuman() )
 
4414
    {
 
4415
        tString ret;
 
4416
        int scoreDifference = score - lastScore_;
 
4417
        lastScore_ = IMPOSSIBLY_LOW_SCORE;
 
4418
        ret << "ROUND_SCORE " << scoreDifference << " " << GetUserName();
 
4419
        if ( currentTeam )
 
4420
            ret << " " << FilterName( currentTeam->Name() );
 
4421
        ret << "\n";
 
4422
        se_SaveToLadderLog( ret );
 
4423
    }
 
4424
}
 
4425
 
 
4426