/ [w3loader] / ghost / src / game.cpp
To checkout: svn checkout http://svn.gnu.org.ua/sources/w3loader/ghost/src/game.cpp
Puszcza

Contents of /ghost/src/game.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5 - (show annotations)
Tue Jun 2 19:44:46 2020 UTC (16 months, 3 weeks ago) by zombieteam
File size: 58663 byte(s)
kick when save game
1 /*
2
3 Copyright [2008] [Trevor Hogan]
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16
17 CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/
18
19 */
20
21 #include "ghost.h"
22 #include "util.h"
23 #include "config.h"
24 #include "language.h"
25 #include "socket.h"
26 #include "ghostdb.h"
27 #include "bnet.h"
28 #include "map.h"
29 #include "packed.h"
30 #include "savegame.h"
31 #include "gameplayer.h"
32 #include "gameprotocol.h"
33 #include "game_base.h"
34 #include "game.h"
35 #include "stats.h"
36 #include "statsdota.h"
37 #include "statsw3mmd.h"
38
39 #include <cmath>
40 #include <string.h>
41 #include <time.h>
42
43 //
44 // sorting classes
45 //
46
47 class CGamePlayerSortAscByPing
48 {
49 public:
50 bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
51 {
52 return Player1->GetPing( false ) < Player2->GetPing( false );
53 }
54 };
55
56 class CGamePlayerSortDescByPing
57 {
58 public:
59 bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
60 {
61 return Player1->GetPing( false ) > Player2->GetPing( false );
62 }
63 };
64
65 //
66 // CGame
67 //
68
69 CGame :: CGame( CGHost *nGHost, CMap *nMap, CSaveGame *nSaveGame, uint16_t nHostPort, unsigned char nGameState, string nGameName, string nOwnerName, string nCreatorName, string nCreatorServer ) : CBaseGame( nGHost, nMap, nSaveGame, nHostPort, nGameState, nGameName, nOwnerName, nCreatorName, nCreatorServer )
70 {
71 m_DBBanLast = NULL;
72 m_DBGame = new CDBGame( 0, string( ), m_Map->GetMapPath( ), string( ), string( ), string( ), 0 );
73
74 if( m_Map->GetMapType( ) == "w3mmd" )
75 m_Stats = new CStatsW3MMD( this, m_Map->GetMapStatsW3MMDCategory( ) );
76 else if( m_Map->GetMapType( ) == "dota" )
77 m_Stats = new CStatsDOTA( this );
78 else
79 m_Stats = NULL;
80
81 m_CallableGameAdd = NULL;
82 }
83
84 CGame :: ~CGame( )
85 {
86 if( m_CallableGameAdd && m_CallableGameAdd->GetReady( ) )
87 {
88 if( m_CallableGameAdd->GetResult( ) > 0 )
89 {
90 CONSOLE_Print( "[GAME: " + m_GameName + "] saving player/stats data to database" );
91
92 // store the CDBGamePlayers in the database
93
94 for( vector<CDBGamePlayer *> :: iterator i = m_DBGamePlayers.begin( ); i != m_DBGamePlayers.end( ); i++ )
95 m_GHost->m_Callables.push_back( m_GHost->m_DB->ThreadedGamePlayerAdd( m_CallableGameAdd->GetResult( ), (*i)->GetName( ), (*i)->GetIP( ), (*i)->GetSpoofed( ), (*i)->GetSpoofedRealm( ), (*i)->GetReserved( ), (*i)->GetLoadingTime( ), (*i)->GetLeft( ), (*i)->GetLeftReason( ), (*i)->GetTeam( ), (*i)->GetColour( ) ) );
96
97 // store the stats in the database
98
99 if( m_Stats )
100 m_Stats->Save( m_GHost, m_GHost->m_DB, m_CallableGameAdd->GetResult( ) );
101 }
102 else
103 CONSOLE_Print( "[GAME: " + m_GameName + "] unable to save player/stats data to database" );
104
105 m_GHost->m_DB->RecoverCallable( m_CallableGameAdd );
106 delete m_CallableGameAdd;
107 m_CallableGameAdd = NULL;
108 }
109
110 for( vector<PairedBanCheck> :: iterator i = m_PairedBanChecks.begin( ); i != m_PairedBanChecks.end( ); i++ )
111 m_GHost->m_Callables.push_back( i->second );
112
113 for( vector<PairedBanAdd> :: iterator i = m_PairedBanAdds.begin( ); i != m_PairedBanAdds.end( ); i++ )
114 m_GHost->m_Callables.push_back( i->second );
115
116 for( vector<PairedGPSCheck> :: iterator i = m_PairedGPSChecks.begin( ); i != m_PairedGPSChecks.end( ); i++ )
117 m_GHost->m_Callables.push_back( i->second );
118
119 for( vector<PairedDPSCheck> :: iterator i = m_PairedDPSChecks.begin( ); i != m_PairedDPSChecks.end( ); i++ )
120 m_GHost->m_Callables.push_back( i->second );
121
122 for( vector<CDBBan *> :: iterator i = m_DBBans.begin( ); i != m_DBBans.end( ); i++ )
123 delete *i;
124
125 delete m_DBGame;
126
127 for( vector<CDBGamePlayer *> :: iterator i = m_DBGamePlayers.begin( ); i != m_DBGamePlayers.end( ); i++ )
128 delete *i;
129
130 delete m_Stats;
131
132 // it's a "bad thing" if m_CallableGameAdd is non NULL here
133 // it means the game is being deleted after m_CallableGameAdd was created (the first step to saving the game data) but before the associated thread terminated
134 // rather than failing horribly we choose to allow the thread to complete in the orphaned callables list but step 2 will never be completed
135 // so this will create a game entry in the database without any gameplayers and/or DotA stats
136
137 if( m_CallableGameAdd )
138 {
139 CONSOLE_Print( "[GAME: " + m_GameName + "] game is being deleted before all game data was saved, game data has been lost" );
140 m_GHost->m_Callables.push_back( m_CallableGameAdd );
141 }
142 }
143
144 bool CGame :: Update( void *fd, void *send_fd )
145 {
146 // update callables
147
148 for( vector<PairedBanCheck> :: iterator i = m_PairedBanChecks.begin( ); i != m_PairedBanChecks.end( ); )
149 {
150 if( i->second->GetReady( ) )
151 {
152 CDBBan *Ban = i->second->GetResult( );
153
154 if( Ban )
155 SendAllChat( m_GHost->m_Language->UserWasBannedOnByBecause( i->second->GetServer( ), i->second->GetUser( ), Ban->GetDate( ), Ban->GetAdmin( ), Ban->GetReason( ) ) );
156 else
157 SendAllChat( m_GHost->m_Language->UserIsNotBanned( i->second->GetServer( ), i->second->GetUser( ) ) );
158
159 m_GHost->m_DB->RecoverCallable( i->second );
160 delete i->second;
161 i = m_PairedBanChecks.erase( i );
162 }
163 else
164 i++;
165 }
166
167 for( vector<PairedBanAdd> :: iterator i = m_PairedBanAdds.begin( ); i != m_PairedBanAdds.end( ); )
168 {
169 if( i->second->GetReady( ) )
170 {
171 if( i->second->GetResult( ) )
172 {
173 for( vector<CBNET *> :: iterator j = m_GHost->m_BNETs.begin( ); j != m_GHost->m_BNETs.end( ); j++ )
174 {
175 if( (*j)->GetServer( ) == i->second->GetServer( ) )
176 (*j)->AddBan( i->second->GetUser( ), i->second->GetIP( ), i->second->GetGameName( ), i->second->GetAdmin( ), i->second->GetReason( ) );
177 }
178
179 SendAllChat( m_GHost->m_Language->PlayerWasBannedByPlayer( i->second->GetServer( ), i->second->GetUser( ), i->first ) );
180 }
181
182 m_GHost->m_DB->RecoverCallable( i->second );
183 delete i->second;
184 i = m_PairedBanAdds.erase( i );
185 }
186 else
187 i++;
188 }
189
190 for( vector<PairedGPSCheck> :: iterator i = m_PairedGPSChecks.begin( ); i != m_PairedGPSChecks.end( ); )
191 {
192 if( i->second->GetReady( ) )
193 {
194 CDBGamePlayerSummary *GamePlayerSummary = i->second->GetResult( );
195
196 if( GamePlayerSummary )
197 {
198 if( i->first.empty( ) )
199 SendAllChat( m_GHost->m_Language->HasPlayedGamesWithThisBot( i->second->GetName( ), GamePlayerSummary->GetFirstGameDateTime( ), GamePlayerSummary->GetLastGameDateTime( ), UTIL_ToString( GamePlayerSummary->GetTotalGames( ) ), UTIL_ToString( (float)GamePlayerSummary->GetAvgLoadingTime( ) / 1000, 2 ), UTIL_ToString( GamePlayerSummary->GetAvgLeftPercent( ) ) ) );
200 else
201 {
202 CGamePlayer *Player = GetPlayerFromName( i->first, true );
203
204 if( Player )
205 SendChat( Player, m_GHost->m_Language->HasPlayedGamesWithThisBot( i->second->GetName( ), GamePlayerSummary->GetFirstGameDateTime( ), GamePlayerSummary->GetLastGameDateTime( ), UTIL_ToString( GamePlayerSummary->GetTotalGames( ) ), UTIL_ToString( (float)GamePlayerSummary->GetAvgLoadingTime( ) / 1000, 2 ), UTIL_ToString( GamePlayerSummary->GetAvgLeftPercent( ) ) ) );
206 }
207 }
208 else
209 {
210 if( i->first.empty( ) )
211 SendAllChat( m_GHost->m_Language->HasntPlayedGamesWithThisBot( i->second->GetName( ) ) );
212 else
213 {
214 CGamePlayer *Player = GetPlayerFromName( i->first, true );
215
216 if( Player )
217 SendChat( Player, m_GHost->m_Language->HasntPlayedGamesWithThisBot( i->second->GetName( ) ) );
218 }
219 }
220
221 m_GHost->m_DB->RecoverCallable( i->second );
222 delete i->second;
223 i = m_PairedGPSChecks.erase( i );
224 }
225 else
226 i++;
227 }
228
229 for( vector<PairedDPSCheck> :: iterator i = m_PairedDPSChecks.begin( ); i != m_PairedDPSChecks.end( ); )
230 {
231 if( i->second->GetReady( ) )
232 {
233 CDBDotAPlayerSummary *DotAPlayerSummary = i->second->GetResult( );
234
235 if( DotAPlayerSummary )
236 {
237 string Summary = m_GHost->m_Language->HasPlayedDotAGamesWithThisBot( i->second->GetName( ),
238 UTIL_ToString( DotAPlayerSummary->GetTotalGames( ) ),
239 UTIL_ToString( DotAPlayerSummary->GetTotalWins( ) ),
240 UTIL_ToString( DotAPlayerSummary->GetTotalLosses( ) ),
241 UTIL_ToString( DotAPlayerSummary->GetTotalKills( ) ),
242 UTIL_ToString( DotAPlayerSummary->GetTotalDeaths( ) ),
243 UTIL_ToString( DotAPlayerSummary->GetTotalCreepKills( ) ),
244 UTIL_ToString( DotAPlayerSummary->GetTotalCreepDenies( ) ),
245 UTIL_ToString( DotAPlayerSummary->GetTotalAssists( ) ),
246 UTIL_ToString( DotAPlayerSummary->GetTotalNeutralKills( ) ),
247 UTIL_ToString( DotAPlayerSummary->GetTotalTowerKills( ) ),
248 UTIL_ToString( DotAPlayerSummary->GetTotalRaxKills( ) ),
249 UTIL_ToString( DotAPlayerSummary->GetTotalCourierKills( ) ),
250 UTIL_ToString( DotAPlayerSummary->GetAvgKills( ), 2 ),
251 UTIL_ToString( DotAPlayerSummary->GetAvgDeaths( ), 2 ),
252 UTIL_ToString( DotAPlayerSummary->GetAvgCreepKills( ), 2 ),
253 UTIL_ToString( DotAPlayerSummary->GetAvgCreepDenies( ), 2 ),
254 UTIL_ToString( DotAPlayerSummary->GetAvgAssists( ), 2 ),
255 UTIL_ToString( DotAPlayerSummary->GetAvgNeutralKills( ), 2 ),
256 UTIL_ToString( DotAPlayerSummary->GetAvgTowerKills( ), 2 ),
257 UTIL_ToString( DotAPlayerSummary->GetAvgRaxKills( ), 2 ),
258 UTIL_ToString( DotAPlayerSummary->GetAvgCourierKills( ), 2 ) );
259
260 if( i->first.empty( ) )
261 SendAllChat( Summary );
262 else
263 {
264 CGamePlayer *Player = GetPlayerFromName( i->first, true );
265
266 if( Player )
267 SendChat( Player, Summary );
268 }
269 }
270 else
271 {
272 if( i->first.empty( ) )
273 SendAllChat( m_GHost->m_Language->HasntPlayedDotAGamesWithThisBot( i->second->GetName( ) ) );
274 else
275 {
276 CGamePlayer *Player = GetPlayerFromName( i->first, true );
277
278 if( Player )
279 SendChat( Player, m_GHost->m_Language->HasntPlayedDotAGamesWithThisBot( i->second->GetName( ) ) );
280 }
281 }
282
283 m_GHost->m_DB->RecoverCallable( i->second );
284 delete i->second;
285 i = m_PairedDPSChecks.erase( i );
286 }
287 else
288 i++;
289 }
290
291 return CBaseGame :: Update( fd, send_fd );
292 }
293
294 void CGame :: EventPlayerDeleted( CGamePlayer *player )
295 {
296 CBaseGame :: EventPlayerDeleted( player );
297
298 // record everything we need to know about the player for storing in the database later
299 // since we haven't stored the game yet (it's not over yet!) we can't link the gameplayer to the game
300 // see the destructor for where these CDBGamePlayers are stored in the database
301 // we could have inserted an incomplete record on creation and updated it later but this makes for a cleaner interface
302
303 if( m_GameLoading || m_GameLoaded )
304 {
305 // todotodo: since we store players that crash during loading it's possible that the stats classes could have no information on them
306 // that could result in a DBGamePlayer without a corresponding DBDotAPlayer - just be aware of the possibility
307
308 unsigned char SID = GetSIDFromPID( player->GetPID( ) );
309 unsigned char Team = 255;
310 unsigned char Colour = 255;
311
312 if( SID < m_Slots.size( ) )
313 {
314 Team = m_Slots[SID].GetTeam( );
315 Colour = m_Slots[SID].GetColour( );
316 }
317
318 m_DBGamePlayers.push_back( new CDBGamePlayer( 0, 0, player->GetName( ), player->GetExternalIPString( ), player->GetSpoofed( ) ? 1 : 0, player->GetSpoofedRealm( ), player->GetReserved( ) ? 1 : 0, player->GetFinishedLoading( ) ? player->GetFinishedLoadingTicks( ) - m_StartedLoadingTicks : 0, m_GameTicks / 1000, player->GetLeftReason( ), Team, Colour ) );
319
320 // also keep track of the last player to leave for the !banlast command
321
322 for( vector<CDBBan *> :: iterator i = m_DBBans.begin( ); i != m_DBBans.end( ); i++ )
323 {
324 if( (*i)->GetName( ) == player->GetName( ) )
325 m_DBBanLast = *i;
326 }
327 }
328 }
329
330 void CGame :: EventPlayerAction( CGamePlayer *player, CIncomingAction *action )
331 {
332 if( m_GHost->m_KickGameSaver && !action->GetAction( )->empty( ) && (*action->GetAction( ))[0] == 6 )
333 {
334 SendAllChat( player->GetName( ) + " kicked out when trying to save the game" );
335 player->SetDeleteMe( true );
336 player->SetLeftCode( PLAYERLEAVE_GPROXY );
337 player->SetLeftReason( "Save game not allowed" );
338 delete action;
339 return;
340 }
341
342 CBaseGame :: EventPlayerAction( player, action );
343
344 // give the stats class a chance to process the action
345
346 if( m_Stats && m_Stats->ProcessAction( action ) && m_GameOverTime == 0 )
347 {
348 CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (stats class reported game over)" );
349 SendEndMessage( );
350 m_GameOverTime = GetTime( );
351 }
352 }
353
354 bool CGame :: EventPlayerBotCommand( CGamePlayer *player, string command, string payload )
355 {
356 bool HideCommand = CBaseGame :: EventPlayerBotCommand( player, command, payload );
357
358 // todotodo: don't be lazy
359
360 string User = player->GetName( );
361 string Command = command;
362 string Payload = payload;
363
364 bool AdminCheck = false;
365
366 for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
367 {
368 if( (*i)->GetServer( ) == player->GetSpoofedRealm( ) && (*i)->IsAdmin( User ) )
369 {
370 AdminCheck = true;
371 break;
372 }
373 }
374
375 bool RootAdminCheck = false;
376
377 for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
378 {
379 if( (*i)->GetServer( ) == player->GetSpoofedRealm( ) && (*i)->IsRootAdmin( User ) )
380 {
381 RootAdminCheck = true;
382 break;
383 }
384 }
385
386 if( player->GetSpoofed( ) && ( AdminCheck || RootAdminCheck || IsOwner( User ) ) )
387 {
388 CONSOLE_Print( "[GAME: " + m_GameName + "] admin [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]" );
389
390 if( !m_Locked || RootAdminCheck || IsOwner( User ) )
391 {
392 /*****************
393 * ADMIN COMMANDS *
394 ******************/
395
396 //
397 // !ABORT (abort countdown)
398 // !A
399 //
400
401 // we use "!a" as an alias for abort because you don't have much time to abort the countdown so it's useful for the abort command to be easy to type
402
403 if( ( Command == "abort" || Command == "a" ) && m_CountDownStarted && !m_GameLoading && !m_GameLoaded )
404 {
405 SendAllChat( m_GHost->m_Language->CountDownAborted( ) );
406 m_CountDownStarted = false;
407 }
408
409 //
410 // !ADDBAN
411 // !BAN
412 //
413
414 if( ( Command == "addban" || Command == "ban" ) && !Payload.empty( ) && !m_GHost->m_BNETs.empty( ) )
415 {
416 // extract the victim and the reason
417 // e.g. "Varlock leaver after dying" -> victim: "Varlock", reason: "leaver after dying"
418
419 string Victim;
420 string Reason;
421 stringstream SS;
422 SS << Payload;
423 SS >> Victim;
424
425 if( !SS.eof( ) )
426 {
427 getline( SS, Reason );
428 string :: size_type Start = Reason.find_first_not_of( " " );
429
430 if( Start != string :: npos )
431 Reason = Reason.substr( Start );
432 }
433
434 if( m_GameLoaded )
435 {
436 string VictimLower = Victim;
437 transform( VictimLower.begin( ), VictimLower.end( ), VictimLower.begin( ), (int(*)(int))tolower );
438 uint32_t Matches = 0;
439 CDBBan *LastMatch = NULL;
440
441 // try to match each player with the passed string (e.g. "Varlock" would be matched with "lock")
442 // we use the m_DBBans vector for this in case the player already left and thus isn't in the m_Players vector anymore
443
444 for( vector<CDBBan *> :: iterator i = m_DBBans.begin( ); i != m_DBBans.end( ); i++ )
445 {
446 string TestName = (*i)->GetName( );
447 transform( TestName.begin( ), TestName.end( ), TestName.begin( ), (int(*)(int))tolower );
448
449 if( TestName.find( VictimLower ) != string :: npos )
450 {
451 Matches++;
452 LastMatch = *i;
453
454 // if the name matches exactly stop any further matching
455
456 if( TestName == VictimLower )
457 {
458 Matches = 1;
459 break;
460 }
461 }
462 }
463
464 if( Matches == 0 )
465 SendAllChat( m_GHost->m_Language->UnableToBanNoMatchesFound( Victim ) );
466 else if( Matches == 1 )
467 m_PairedBanAdds.push_back( PairedBanAdd( User, m_GHost->m_DB->ThreadedBanAdd( LastMatch->GetServer( ), LastMatch->GetName( ), LastMatch->GetIP( ), m_GameName, User, Reason ) ) );
468 else
469 SendAllChat( m_GHost->m_Language->UnableToBanFoundMoreThanOneMatch( Victim ) );
470 }
471 else
472 {
473 CGamePlayer *LastMatch = NULL;
474 uint32_t Matches = GetPlayerFromNamePartial( Victim, &LastMatch );
475
476 if( Matches == 0 )
477 SendAllChat( m_GHost->m_Language->UnableToBanNoMatchesFound( Victim ) );
478 else if( Matches == 1 )
479 m_PairedBanAdds.push_back( PairedBanAdd( User, m_GHost->m_DB->ThreadedBanAdd( LastMatch->GetJoinedRealm( ), LastMatch->GetName( ), LastMatch->GetExternalIPString( ), m_GameName, User, Reason ) ) );
480 else
481 SendAllChat( m_GHost->m_Language->UnableToBanFoundMoreThanOneMatch( Victim ) );
482 }
483 }
484
485 //
486 // !ANNOUNCE
487 //
488
489 if( Command == "announce" && !m_CountDownStarted )
490 {
491 if( Payload.empty( ) || Payload == "off" )
492 {
493 SendAllChat( m_GHost->m_Language->AnnounceMessageDisabled( ) );
494 SetAnnounce( 0, string( ) );
495 }
496 else
497 {
498 // extract the interval and the message
499 // e.g. "30 hello everyone" -> interval: "30", message: "hello everyone"
500
501 uint32_t Interval;
502 string Message;
503 stringstream SS;
504 SS << Payload;
505 SS >> Interval;
506
507 if( SS.fail( ) || Interval == 0 )
508 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to announce command" );
509 else
510 {
511 if( SS.eof( ) )
512 CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to announce command" );
513 else
514 {
515 getline( SS, Message );
516 string :: size_type Start = Message.find_first_not_of( " " );
517
518 if( Start != string :: npos )
519 Message = Message.substr( Start );
520
521 SendAllChat( m_GHost->m_Language->AnnounceMessageEnabled( ) );
522 SetAnnounce( Interval, Message );
523 }
524 }
525 }
526 }
527
528 //
529 // !AUTOSAVE
530 //
531
532 if( Command == "autosave" )
533 {
534 if( Payload == "on" )
535 {
536 SendAllChat( m_GHost->m_Language->AutoSaveEnabled( ) );
537 m_AutoSave = true;
538 }
539 else if( Payload == "off" )
540 {
541 SendAllChat( m_GHost->m_Language->AutoSaveDisabled( ) );
542 m_AutoSave = false;
543 }
544 }
545
546 //
547 // !AUTOSTART
548 //
549
550 if( Command == "autostart" && !m_CountDownStarted )
551 {
552 if( Payload.empty( ) || Payload == "off" )
553 {
554 SendAllChat( m_GHost->m_Language->AutoStartDisabled( ) );
555 m_AutoStartPlayers = 0;
556 }
557 else
558 {
559 uint32_t AutoStartPlayers = UTIL_ToUInt32( Payload );
560
561 if( AutoStartPlayers != 0 )
562 {
563 SendAllChat( m_GHost->m_Language->AutoStartEnabled( UTIL_ToString( AutoStartPlayers ) ) );
564 m_AutoStartPlayers = AutoStartPlayers;
565 }
566 }
567 }
568
569 //
570 // !BANLAST
571 //
572
573 if( Command == "banlast" && m_GameLoaded && !m_GHost->m_BNETs.empty( ) && m_DBBanLast )
574 m_PairedBanAdds.push_back( PairedBanAdd( User, m_GHost->m_DB->ThreadedBanAdd( m_DBBanLast->GetServer( ), m_DBBanLast->GetName( ), m_DBBanLast->GetIP( ), m_GameName, User, Payload ) ) );
575
576 //
577 // !CHECK
578 //
579
580 if( Command == "check" )
581 {
582 if( !Payload.empty( ) )
583 {
584 CGamePlayer *LastMatch = NULL;
585 uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
586
587 if( Matches == 0 )
588 SendAllChat( m_GHost->m_Language->UnableToCheckPlayerNoMatchesFound( Payload ) );
589 else if( Matches == 1 )
590 {
591 bool LastMatchAdminCheck = false;
592
593 for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
594 {
595 if( (*i)->GetServer( ) == LastMatch->GetSpoofedRealm( ) && (*i)->IsAdmin( LastMatch->GetName( ) ) )
596 {
597 LastMatchAdminCheck = true;
598 break;
599 }
600 }
601
602 bool LastMatchRootAdminCheck = false;
603
604 for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
605 {
606 if( (*i)->GetServer( ) == LastMatch->GetSpoofedRealm( ) && (*i)->IsRootAdmin( LastMatch->GetName( ) ) )
607 {
608 LastMatchRootAdminCheck = true;
609 break;
610 }
611 }
612
613 SendAllChat( m_GHost->m_Language->CheckedPlayer( LastMatch->GetName( ), LastMatch->GetNumPings( ) > 0 ? UTIL_ToString( LastMatch->GetPing( m_GHost->m_LCPings ) ) + "ms" : "N/A", m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( LastMatch->GetExternalIP( ), true ) ), LastMatchAdminCheck || LastMatchRootAdminCheck ? "Yes" : "No", IsOwner( LastMatch->GetName( ) ) ? "Yes" : "No", LastMatch->GetSpoofed( ) ? "Yes" : "No", LastMatch->GetSpoofedRealm( ).empty( ) ? "N/A" : LastMatch->GetSpoofedRealm( ), LastMatch->GetReserved( ) ? "Yes" : "No" ) );
614 }
615 else
616 SendAllChat( m_GHost->m_Language->UnableToCheckPlayerFoundMoreThanOneMatch( Payload ) );
617 }
618 else
619 SendAllChat( m_GHost->m_Language->CheckedPlayer( User, player->GetNumPings( ) > 0 ? UTIL_ToString( player->GetPing( m_GHost->m_LCPings ) ) + "ms" : "N/A", m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( player->GetExternalIP( ), true ) ), AdminCheck || RootAdminCheck ? "Yes" : "No", IsOwner( User ) ? "Yes" : "No", player->GetSpoofed( ) ? "Yes" : "No", player->GetSpoofedRealm( ).empty( ) ? "N/A" : player->GetSpoofedRealm( ), player->GetReserved( ) ? "Yes" : "No" ) );
620 }
621
622 //
623 // !CHECKBAN
624 //
625
626 if( Command == "checkban" && !Payload.empty( ) && !m_GHost->m_BNETs.empty( ) )
627 {
628 for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
629 m_PairedBanChecks.push_back( PairedBanCheck( User, m_GHost->m_DB->ThreadedBanCheck( (*i)->GetServer( ), Payload, string( ) ) ) );
630 }
631
632 //
633 // !CLEARHCL
634 //
635
636 if( Command == "clearhcl" && !m_CountDownStarted )
637 {
638 m_HCLCommandString.clear( );
639 SendAllChat( m_GHost->m_Language->ClearingHCL( ) );
640 }
641
642 //
643 // !CLOSE (close slot)
644 //
645
646 if( Command == "close" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
647 {
648 // close as many slots as specified, e.g. "5 10" closes slots 5 and 10
649
650 stringstream SS;
651 SS << Payload;
652
653 while( !SS.eof( ) )
654 {
655 uint32_t SID;
656 SS >> SID;
657
658 if( SS.fail( ) )
659 {
660 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input to close command" );
661 break;
662 }
663 else
664 CloseSlot( (unsigned char)( SID - 1 ), true );
665 }
666 }
667
668 //
669 // !CLOSEALL
670 //
671
672 if( Command == "closeall" && !m_GameLoading && !m_GameLoaded )
673 CloseAllSlots( );
674
675 //
676 // !COMP (computer slot)
677 //
678
679 if( Command == "comp" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
680 {
681 // extract the slot and the skill
682 // e.g. "1 2" -> slot: "1", skill: "2"
683
684 uint32_t Slot;
685 uint32_t Skill = 1;
686 stringstream SS;
687 SS << Payload;
688 SS >> Slot;
689
690 if( SS.fail( ) )
691 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to comp command" );
692 else
693 {
694 if( !SS.eof( ) )
695 SS >> Skill;
696
697 if( SS.fail( ) )
698 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to comp command" );
699 else
700 ComputerSlot( (unsigned char)( Slot - 1 ), (unsigned char)Skill, true );
701 }
702 }
703
704 //
705 // !COMPCOLOUR (computer colour change)
706 //
707
708 if( Command == "compcolour" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
709 {
710 // extract the slot and the colour
711 // e.g. "1 2" -> slot: "1", colour: "2"
712
713 uint32_t Slot;
714 uint32_t Colour;
715 stringstream SS;
716 SS << Payload;
717 SS >> Slot;
718
719 if( SS.fail( ) )
720 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to compcolour command" );
721 else
722 {
723 if( SS.eof( ) )
724 CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to compcolour command" );
725 else
726 {
727 SS >> Colour;
728
729 if( SS.fail( ) )
730 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to compcolour command" );
731 else
732 {
733 unsigned char SID = (unsigned char)( Slot - 1 );
734
735 if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && Colour < 12 && SID < m_Slots.size( ) )
736 {
737 if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
738 ColourSlot( SID, Colour );
739 }
740 }
741 }
742 }
743 }
744
745 //
746 // !COMPHANDICAP (computer handicap change)
747 //
748
749 if( Command == "comphandicap" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
750 {
751 // extract the slot and the handicap
752 // e.g. "1 50" -> slot: "1", handicap: "50"
753
754 uint32_t Slot;
755 uint32_t Handicap;
756 stringstream SS;
757 SS << Payload;
758 SS >> Slot;
759
760 if( SS.fail( ) )
761 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to comphandicap command" );
762 else
763 {
764 if( SS.eof( ) )
765 CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to comphandicap command" );
766 else
767 {
768 SS >> Handicap;
769
770 if( SS.fail( ) )
771 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to comphandicap command" );
772 else
773 {
774 unsigned char SID = (unsigned char)( Slot - 1 );
775
776 if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && ( Handicap == 50 || Handicap == 60 || Handicap == 70 || Handicap == 80 || Handicap == 90 || Handicap == 100 ) && SID < m_Slots.size( ) )
777 {
778 if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
779 {
780 m_Slots[SID].SetHandicap( (unsigned char)Handicap );
781 SendAllSlotInfo( );
782 }
783 }
784 }
785 }
786 }
787 }
788
789 //
790 // !COMPRACE (computer race change)
791 //
792
793 if( Command == "comprace" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
794 {
795 // extract the slot and the race
796 // e.g. "1 human" -> slot: "1", race: "human"
797
798 uint32_t Slot;
799 string Race;
800 stringstream SS;
801 SS << Payload;
802 SS >> Slot;
803
804 if( SS.fail( ) )
805 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to comprace command" );
806 else
807 {
808 if( SS.eof( ) )
809 CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to comprace command" );
810 else
811 {
812 getline( SS, Race );
813 string :: size_type Start = Race.find_first_not_of( " " );
814
815 if( Start != string :: npos )
816 Race = Race.substr( Start );
817
818 transform( Race.begin( ), Race.end( ), Race.begin( ), (int(*)(int))tolower );
819 unsigned char SID = (unsigned char)( Slot - 1 );
820
821 if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && !( m_Map->GetMapFlags( ) & MAPFLAG_RANDOMRACES ) && SID < m_Slots.size( ) )
822 {
823 if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
824 {
825 if( Race == "human" )
826 {
827 m_Slots[SID].SetRace( SLOTRACE_HUMAN | SLOTRACE_SELECTABLE );
828 SendAllSlotInfo( );
829 }
830 else if( Race == "orc" )
831 {
832 m_Slots[SID].SetRace( SLOTRACE_ORC | SLOTRACE_SELECTABLE );
833 SendAllSlotInfo( );
834 }
835 else if( Race == "night elf" )
836 {
837 m_Slots[SID].SetRace( SLOTRACE_NIGHTELF | SLOTRACE_SELECTABLE );
838 SendAllSlotInfo( );
839 }
840 else if( Race == "undead" )
841 {
842 m_Slots[SID].SetRace( SLOTRACE_UNDEAD | SLOTRACE_SELECTABLE );
843 SendAllSlotInfo( );
844 }
845 else if( Race == "random" )
846 {
847 m_Slots[SID].SetRace( SLOTRACE_RANDOM | SLOTRACE_SELECTABLE );
848 SendAllSlotInfo( );
849 }
850 else
851 CONSOLE_Print( "[GAME: " + m_GameName + "] unknown race [" + Race + "] sent to comprace command" );
852 }
853 }
854 }
855 }
856 }
857
858 //
859 // !COMPTEAM (computer team change)
860 //
861
862 if( Command == "compteam" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded && !m_SaveGame )
863 {
864 // extract the slot and the team
865 // e.g. "1 2" -> slot: "1", team: "2"
866
867 uint32_t Slot;
868 uint32_t Team;
869 stringstream SS;
870 SS << Payload;
871 SS >> Slot;
872
873 if( SS.fail( ) )
874 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to compteam command" );
875 else
876 {
877 if( SS.eof( ) )
878 CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to compteam command" );
879 else
880 {
881 SS >> Team;
882
883 if( SS.fail( ) )
884 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to compteam command" );
885 else
886 {
887 unsigned char SID = (unsigned char)( Slot - 1 );
888
889 if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) && Team < 12 && SID < m_Slots.size( ) )
890 {
891 if( m_Slots[SID].GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && m_Slots[SID].GetComputer( ) == 1 )
892 {
893 m_Slots[SID].SetTeam( (unsigned char)( Team - 1 ) );
894 SendAllSlotInfo( );
895 }
896 }
897 }
898 }
899 }
900 }
901
902 //
903 // !DBSTATUS
904 //
905
906 if( Command == "dbstatus" )
907 SendAllChat( m_GHost->m_DB->GetStatus( ) );
908
909 //
910 // !DOWNLOAD
911 // !DL
912 //
913
914 if( ( Command == "download" || Command == "dl" ) && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
915 {
916 CGamePlayer *LastMatch = NULL;
917 uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
918
919 if( Matches == 0 )
920 SendAllChat( m_GHost->m_Language->UnableToStartDownloadNoMatchesFound( Payload ) );
921 else if( Matches == 1 )
922 {
923 if( !LastMatch->GetDownloadStarted( ) && !LastMatch->GetDownloadFinished( ) )
924 {
925 unsigned char SID = GetSIDFromPID( LastMatch->GetPID( ) );
926
927 if( SID < m_Slots.size( ) && m_Slots[SID].GetDownloadStatus( ) != 100 )
928 {
929 // inform the client that we are willing to send the map
930
931 CONSOLE_Print( "[GAME: " + m_GameName + "] map download started for player [" + LastMatch->GetName( ) + "]" );
932 Send( LastMatch, m_Protocol->SEND_W3GS_STARTDOWNLOAD( GetHostPID( ) ) );
933 LastMatch->SetDownloadAllowed( true );
934 LastMatch->SetDownloadStarted( true );
935 LastMatch->SetStartedDownloadingTicks( GetTicks( ) );
936 }
937 }
938 }
939 else
940 SendAllChat( m_GHost->m_Language->UnableToStartDownloadFoundMoreThanOneMatch( Payload ) );
941 }
942
943 //
944 // !DROP
945 //
946
947 if( Command == "drop" && m_GameLoaded )
948 StopLaggers( "lagged out (dropped by admin)" );
949
950 //
951 // !END
952 //
953
954 if( Command == "end" && m_GameLoaded )
955 {
956 CONSOLE_Print( "[GAME: " + m_GameName + "] is over (admin ended game)" );
957 StopPlayers( "was disconnected (admin ended game)" );
958 }
959
960 //
961 // !FAKEPLAYER
962 //
963
964 if( Command == "fakeplayer" && !m_CountDownStarted )
965 {
966 if( m_FakePlayerPID == 255 )
967 CreateFakePlayer( );
968 else
969 DeleteFakePlayer( );
970 }
971
972 //
973 // !FPPAUSE
974 //
975
976 if( Command == "fppause" && m_FakePlayerPID != 255 && m_GameLoaded )
977 {
978 BYTEARRAY CRC;
979 BYTEARRAY Action;
980 Action.push_back( 1 );
981 m_Actions.push( new CIncomingAction( m_FakePlayerPID, CRC, Action ) );
982 }
983
984 //
985 // !FPRESUME
986 //
987
988 if( Command == "fpresume" && m_FakePlayerPID != 255 && m_GameLoaded )
989 {
990 BYTEARRAY CRC;
991 BYTEARRAY Action;
992 Action.push_back( 2 );
993 m_Actions.push( new CIncomingAction( m_FakePlayerPID, CRC, Action ) );
994 }
995
996 //
997 // !FROM
998 //
999
1000 if( Command == "from" )
1001 {
1002 string Froms;
1003
1004 for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
1005 {
1006 // we reverse the byte order on the IP because it's stored in network byte order
1007
1008 Froms += (*i)->GetNameTerminated( );
1009 Froms += ": (";
1010 Froms += m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( (*i)->GetExternalIP( ), true ) );
1011 Froms += ")";
1012
1013 if( i != m_Players.end( ) - 1 )
1014 Froms += ", ";
1015
1016 if( ( m_GameLoading || m_GameLoaded ) && Froms.size( ) > 100 )
1017 {
1018 // cut the text into multiple lines ingame
1019
1020 SendAllChat( Froms );
1021 Froms.clear( );
1022 }
1023 }
1024
1025 if( !Froms.empty( ) )
1026 SendAllChat( Froms );
1027 }
1028
1029 //
1030 // !HCL
1031 //
1032
1033 if( Command == "hcl" && !m_CountDownStarted )
1034 {
1035 if( !Payload.empty( ) )
1036 {
1037 if( Payload.size( ) <= m_Slots.size( ) )
1038 {
1039 string HCLChars = "abcdefghijklmnopqrstuvwxyz0123456789 -=,.";
1040
1041 if( Payload.find_first_not_of( HCLChars ) == string :: npos )
1042 {
1043 m_HCLCommandString = Payload;
1044 SendAllChat( m_GHost->m_Language->SettingHCL( m_HCLCommandString ) );
1045 }
1046 else
1047 SendAllChat( m_GHost->m_Language->UnableToSetHCLInvalid( ) );
1048 }
1049 else
1050 SendAllChat( m_GHost->m_Language->UnableToSetHCLTooLong( ) );
1051 }
1052 else
1053 SendAllChat( m_GHost->m_Language->TheHCLIs( m_HCLCommandString ) );
1054 }
1055
1056 //
1057 // !HOLD (hold a slot for someone)
1058 //
1059
1060 if( Command == "hold" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
1061 {
1062 // hold as many players as specified, e.g. "Varlock Kilranin" holds players "Varlock" and "Kilranin"
1063
1064 stringstream SS;
1065 SS << Payload;
1066
1067 while( !SS.eof( ) )
1068 {
1069 string HoldName;
1070 SS >> HoldName;
1071
1072 if( SS.fail( ) )
1073 {
1074 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input to hold command" );
1075 break;
1076 }
1077 else
1078 {
1079 SendAllChat( m_GHost->m_Language->AddedPlayerToTheHoldList( HoldName ) );
1080 AddToReserved( HoldName );
1081 }
1082 }
1083 }
1084
1085 //
1086 // !KICK (kick a player)
1087 //
1088
1089 if( Command == "kick" && !Payload.empty( ) )
1090 {
1091 CGamePlayer *LastMatch = NULL;
1092 uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
1093
1094 if( Matches == 0 )
1095 SendAllChat( m_GHost->m_Language->UnableToKickNoMatchesFound( Payload ) );
1096 else if( Matches == 1 )
1097 {
1098 LastMatch->SetDeleteMe( true );
1099 LastMatch->SetLeftReason( m_GHost->m_Language->WasKickedByPlayer( User ) );
1100
1101 if( !m_GameLoading && !m_GameLoaded )
1102 LastMatch->SetLeftCode( PLAYERLEAVE_LOBBY );
1103 else
1104 LastMatch->SetLeftCode( PLAYERLEAVE_LOST );
1105
1106 if( !m_GameLoading && !m_GameLoaded )
1107 OpenSlot( GetSIDFromPID( LastMatch->GetPID( ) ), false );
1108 }
1109 else
1110 SendAllChat( m_GHost->m_Language->UnableToKickFoundMoreThanOneMatch( Payload ) );
1111 }
1112
1113 //
1114 // !LATENCY (set game latency)
1115 //
1116
1117 if( Command == "latency" )
1118 {
1119 if( Payload.empty( ) )
1120 SendAllChat( m_GHost->m_Language->LatencyIs( UTIL_ToString( m_Latency ) ) );
1121 else
1122 {
1123 m_Latency = UTIL_ToUInt32( Payload );
1124
1125 if( m_Latency <= 20 )
1126 {
1127 m_Latency = 20;
1128 SendAllChat( m_GHost->m_Language->SettingLatencyToMinimum( "20" ) );
1129 }
1130 else if( m_Latency >= 500 )
1131 {
1132 m_Latency = 500;
1133 SendAllChat( m_GHost->m_Language->SettingLatencyToMaximum( "500" ) );
1134 }
1135 else
1136 SendAllChat( m_GHost->m_Language->SettingLatencyTo( UTIL_ToString( m_Latency ) ) );
1137 }
1138 }
1139
1140 //
1141 // !LOCK
1142 //
1143
1144 if( Command == "lock" && ( RootAdminCheck || IsOwner( User ) ) )
1145 {
1146 SendAllChat( m_GHost->m_Language->GameLocked( ) );
1147 m_Locked = true;
1148 }
1149
1150 //
1151 // !MESSAGES
1152 //
1153
1154 if( Command == "messages" )
1155 {
1156 if( Payload == "on" )
1157 {
1158 SendAllChat( m_GHost->m_Language->LocalAdminMessagesEnabled( ) );
1159 m_LocalAdminMessages = true;
1160 }
1161 else if( Payload == "off" )
1162 {
1163 SendAllChat( m_GHost->m_Language->LocalAdminMessagesDisabled( ) );
1164 m_LocalAdminMessages = false;
1165 }
1166 }
1167
1168 //
1169 // !MUTE
1170 //
1171
1172 if( Command == "mute" )
1173 {
1174 CGamePlayer *LastMatch = NULL;
1175 uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
1176
1177 if( Matches == 0 )
1178 SendAllChat( m_GHost->m_Language->UnableToMuteNoMatchesFound( Payload ) );
1179 else if( Matches == 1 )
1180 {
1181 SendAllChat( m_GHost->m_Language->MutedPlayer( LastMatch->GetName( ), User ) );
1182 LastMatch->SetMuted( true );
1183 }
1184 else
1185 SendAllChat( m_GHost->m_Language->UnableToMuteFoundMoreThanOneMatch( Payload ) );
1186 }
1187
1188 //
1189 // !MUTEALL
1190 //
1191
1192 if( Command == "muteall" && m_GameLoaded )
1193 {
1194 SendAllChat( m_GHost->m_Language->GlobalChatMuted( ) );
1195 m_MuteAll = true;
1196 }
1197
1198 //
1199 // !OPEN (open slot)
1200 //
1201
1202 if( Command == "open" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
1203 {
1204 // open as many slots as specified, e.g. "5 10" opens slots 5 and 10
1205
1206 stringstream SS;
1207 SS << Payload;
1208
1209 while( !SS.eof( ) )
1210 {
1211 uint32_t SID;
1212 SS >> SID;
1213
1214 if( SS.fail( ) )
1215 {
1216 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input to open command" );
1217 break;
1218 }
1219 else
1220 OpenSlot( (unsigned char)( SID - 1 ), true );
1221 }
1222 }
1223
1224 //
1225 // !OPENALL
1226 //
1227
1228 if( Command == "openall" && !m_GameLoading && !m_GameLoaded )
1229 OpenAllSlots( );
1230
1231 //
1232 // !OWNER (set game owner)
1233 //
1234
1235 if( Command == "owner" )
1236 {
1237 if( RootAdminCheck || IsOwner( User ) || !GetPlayerFromName( m_OwnerName, false ) )
1238 {
1239 if( !Payload.empty( ) )
1240 {
1241 SendAllChat( m_GHost->m_Language->SettingGameOwnerTo( Payload ) );
1242 m_OwnerName = Payload;
1243 }
1244 else
1245 {
1246 SendAllChat( m_GHost->m_Language->SettingGameOwnerTo( User ) );
1247 m_OwnerName = User;
1248 }
1249 }
1250 else
1251 SendAllChat( m_GHost->m_Language->UnableToSetGameOwner( m_OwnerName ) );
1252 }
1253
1254 //
1255 // !PING
1256 //
1257
1258 if( Command == "ping" )
1259 {
1260 // kick players with ping higher than payload if payload isn't empty
1261 // we only do this if the game hasn't started since we don't want to kick players from a game in progress
1262
1263 uint32_t Kicked = 0;
1264 uint32_t KickPing = 0;
1265
1266 if( !m_GameLoading && !m_GameLoaded && !Payload.empty( ) )
1267 KickPing = UTIL_ToUInt32( Payload );
1268
1269 // copy the m_Players vector so we can sort by descending ping so it's easier to find players with high pings
1270
1271 vector<CGamePlayer *> SortedPlayers = m_Players;
1272 sort( SortedPlayers.begin( ), SortedPlayers.end( ), CGamePlayerSortDescByPing( ) );
1273 string Pings;
1274
1275 for( vector<CGamePlayer *> :: iterator i = SortedPlayers.begin( ); i != SortedPlayers.end( ); i++ )
1276 {
1277 Pings += (*i)->GetNameTerminated( );
1278 Pings += ": ";
1279
1280 if( (*i)->GetNumPings( ) > 0 )
1281 {
1282 Pings += UTIL_ToString( (*i)->GetPing( m_GHost->m_LCPings ) );
1283
1284 if( !m_GameLoading && !m_GameLoaded && !(*i)->GetReserved( ) && KickPing > 0 && (*i)->GetPing( m_GHost->m_LCPings ) > KickPing )
1285 {
1286 (*i)->SetDeleteMe( true );
1287 (*i)->SetLeftReason( "was kicked for excessive ping " + UTIL_ToString( (*i)->GetPing( m_GHost->m_LCPings ) ) + " > " + UTIL_ToString( KickPing ) );
1288 (*i)->SetLeftCode( PLAYERLEAVE_LOBBY );
1289 OpenSlot( GetSIDFromPID( (*i)->GetPID( ) ), false );
1290 Kicked++;
1291 }
1292
1293 Pings += "ms";
1294 }
1295 else
1296 Pings += "N/A";
1297
1298 if( i != SortedPlayers.end( ) - 1 )
1299 Pings += ", ";
1300
1301 if( ( m_GameLoading || m_GameLoaded ) && Pings.size( ) > 100 )
1302 {
1303 // cut the text into multiple lines ingame
1304
1305 SendAllChat( Pings );
1306 Pings.clear( );
1307 }
1308 }
1309
1310 if( !Pings.empty( ) )
1311 SendAllChat( Pings );
1312
1313 if( Kicked > 0 )
1314 SendAllChat( m_GHost->m_Language->KickingPlayersWithPingsGreaterThan( UTIL_ToString( Kicked ), UTIL_ToString( KickPing ) ) );
1315 }
1316
1317 //
1318 // !PRIV (rehost as private game)
1319 //
1320
1321 if( Command == "priv" && !Payload.empty( ) && !m_CountDownStarted && !m_SaveGame )
1322 {
1323 if( Payload.length() < 31 )
1324 {
1325 CONSOLE_Print( "[GAME: " + m_GameName + "] trying to rehost as private game [" + Payload + "]" );
1326 SendAllChat( m_GHost->m_Language->TryingToRehostAsPrivateGame( Payload ) );
1327 m_GameState = GAME_PRIVATE;
1328 m_LastGameName = m_GameName;
1329 m_GameName = Payload;
1330 m_HostCounter = m_GHost->m_HostCounter++;
1331 m_RefreshError = false;
1332 m_RefreshRehosted = true;
1333
1334 for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
1335 {
1336 // unqueue any existing game refreshes because we're going to assume the next successful game refresh indicates that the rehost worked
1337 // this ignores the fact that it's possible a game refresh was just sent and no response has been received yet
1338 // we assume this won't happen very often since the only downside is a potential false positive
1339
1340 (*i)->UnqueueGameRefreshes( );
1341 (*i)->QueueGameUncreate( );
1342 (*i)->QueueEnterChat( );
1343
1344 // we need to send the game creation message now because private games are not refreshed
1345
1346 (*i)->QueueGameCreate( m_GameState, m_GameName, string( ), m_Map, NULL, m_HostCounter );
1347
1348 if( (*i)->GetPasswordHashType( ) != "pvpgn" )
1349 (*i)->QueueEnterChat( );
1350 }
1351
1352 m_CreationTime = GetTime( );
1353 m_LastRefreshTime = GetTime( );
1354 }
1355 else
1356 SendAllChat( m_GHost->m_Language->UnableToCreateGameNameTooLong( Payload ) );
1357 }
1358
1359 //
1360 // !PUB (rehost as public game)
1361 //
1362
1363 if( Command == "pub" && !Payload.empty( ) && !m_CountDownStarted && !m_SaveGame )
1364 {
1365 if( Payload.length() < 31 )
1366 {
1367 CONSOLE_Print( "[GAME: " + m_GameName + "] trying to rehost as public game [" + Payload + "]" );
1368 SendAllChat( m_GHost->m_Language->TryingToRehostAsPublicGame( Payload ) );
1369 m_GameState = GAME_PUBLIC;
1370 m_LastGameName = m_GameName;
1371 m_GameName = Payload;
1372 m_HostCounter = m_GHost->m_HostCounter++;
1373 m_RefreshError = false;
1374 m_RefreshRehosted = true;
1375
1376 for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
1377 {
1378 // unqueue any existing game refreshes because we're going to assume the next successful game refresh indicates that the rehost worked
1379 // this ignores the fact that it's possible a game refresh was just sent and no response has been received yet
1380 // we assume this won't happen very often since the only downside is a potential false positive
1381
1382 (*i)->UnqueueGameRefreshes( );
1383 (*i)->QueueGameUncreate( );
1384 (*i)->QueueEnterChat( );
1385
1386 // the game creation message will be sent on the next refresh
1387 }
1388
1389 m_CreationTime = GetTime( );
1390 m_LastRefreshTime = GetTime( );
1391 }
1392 else
1393 SendAllChat( m_GHost->m_Language->UnableToCreateGameNameTooLong( Payload ) );
1394 }
1395 //
1396 // !REFRESH (turn on or off refresh messages)
1397 //
1398
1399 if( Command == "refresh" && !m_CountDownStarted )
1400 {
1401 if( Payload == "on" )
1402 {
1403 SendAllChat( m_GHost->m_Language->RefreshMessagesEnabled( ) );
1404 m_RefreshMessages = true;
1405 }
1406 else if( Payload == "off" )
1407 {
1408 SendAllChat( m_GHost->m_Language->RefreshMessagesDisabled( ) );
1409 m_RefreshMessages = false;
1410 }
1411 }
1412
1413 //
1414 // !SAY
1415 //
1416
1417 if( Command == "say" && !Payload.empty( ) )
1418 {
1419 for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
1420 (*i)->QueueChatCommand( Payload );
1421
1422 HideCommand = true;
1423 }
1424
1425 //
1426 // !SENDLAN
1427 //
1428
1429 if( Command == "sendlan" && !Payload.empty( ) && !m_CountDownStarted )
1430 {
1431 // extract the ip and the port
1432 // e.g. "1.2.3.4 6112" -> ip: "1.2.3.4", port: "6112"
1433
1434 string IP;
1435 uint32_t Port = 6112;
1436 stringstream SS;
1437 SS << Payload;
1438 SS >> IP;
1439
1440 if( !SS.eof( ) )
1441 SS >> Port;
1442
1443 if( SS.fail( ) )
1444 CONSOLE_Print( "[GAME: " + m_GameName + "] bad inputs to sendlan command" );
1445 else
1446 {
1447 // we send 12 for SlotsTotal because this determines how many PID's Warcraft 3 allocates
1448 // we need to make sure Warcraft 3 allocates at least SlotsTotal + 1 but at most 12 PID's
1449 // this is because we need an extra PID for the virtual host player (but we always delete the virtual host player when the 12th person joins)
1450 // however, we can't send 13 for SlotsTotal because this causes Warcraft 3 to crash when sharing control of units
1451 // nor can we send SlotsTotal because then Warcraft 3 crashes when playing maps with less than 12 PID's (because of the virtual host player taking an extra PID)
1452 // we also send 12 for SlotsOpen because Warcraft 3 assumes there's always at least one player in the game (the host)
1453 // so if we try to send accurate numbers it'll always be off by one and results in Warcraft 3 assuming the game is full when it still needs one more player
1454 // the easiest solution is to simply send 12 for both so the game will always show up as (1/12) players
1455
1456 if( m_SaveGame )
1457 {
1458 // note: the PrivateGame flag is not set when broadcasting to LAN (as you might expect)
1459
1460 uint32_t MapGameType = MAPGAMETYPE_SAVEDGAME;
1461 BYTEARRAY MapWidth;
1462 MapWidth.push_back( 0 );
1463 MapWidth.push_back( 0 );
1464 BYTEARRAY MapHeight;
1465 MapHeight.push_back( 0 );
1466 MapHeight.push_back( 0 );
1467 m_GHost->m_UDPSocket->SendTo( IP, Port, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), 12, 12, m_HostPort, m_HostCounter ) );
1468 }
1469 else
1470 {
1471 // note: the PrivateGame flag is not set when broadcasting to LAN (as you might expect)
1472 // note: we do not use m_Map->GetMapGameType because none of the filters are set when broadcasting to LAN (also as you might expect)
1473
1474 uint32_t MapGameType = MAPGAMETYPE_UNKNOWN0;
1475 m_GHost->m_UDPSocket->SendTo( IP, Port, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, UTIL_CreateByteArray( MapGameType, false ), m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), 12, 12, m_HostPort, m_HostCounter ) );
1476 }
1477 }
1478 }
1479
1480 //
1481 // !SP
1482 //
1483
1484 if( Command == "sp" && !m_CountDownStarted )
1485 {
1486 SendAllChat( m_GHost->m_Language->ShufflingPlayers( ) );
1487 ShuffleSlots( );
1488 }
1489
1490 //
1491 // !START
1492 //
1493
1494 if( Command == "start" && !m_CountDownStarted )
1495 {
1496 // if the player sent "!start force" skip the checks and start the countdown
1497 // otherwise check that the game is ready to start
1498
1499 if( Payload == "force" )
1500 StartCountDown( true );
1501 else
1502 {
1503 if( GetTicks( ) - m_LastPlayerLeaveTicks >= 2000 )
1504 StartCountDown( false );
1505 else
1506 SendAllChat( m_GHost->m_Language->CountDownAbortedSomeoneLeftRecently( ) );
1507 }
1508 }
1509
1510 //
1511 // !SWAP (swap slots)
1512 //
1513
1514 if( Command == "swap" && !Payload.empty( ) && !m_GameLoading && !m_GameLoaded )
1515 {
1516 uint32_t SID1;
1517 uint32_t SID2;
1518 stringstream SS;
1519 SS << Payload;
1520 SS >> SID1;
1521
1522 if( SS.fail( ) )
1523 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #1 to swap command" );
1524 else
1525 {
1526 if( SS.eof( ) )
1527 CONSOLE_Print( "[GAME: " + m_GameName + "] missing input #2 to swap command" );
1528 else
1529 {
1530 SS >> SID2;
1531
1532 if( SS.fail( ) )
1533 CONSOLE_Print( "[GAME: " + m_GameName + "] bad input #2 to swap command" );
1534 else
1535 SwapSlots( (unsigned char)( SID1 - 1 ), (unsigned char)( SID2 - 1 ) );
1536 }
1537 }
1538 }
1539
1540 //
1541 // !SYNCLIMIT
1542 //
1543
1544 if( Command == "synclimit" )
1545 {
1546 if( Payload.empty( ) )
1547 SendAllChat( m_GHost->m_Language->SyncLimitIs( UTIL_ToString( m_SyncLimit ) ) );
1548 else
1549 {
1550 m_SyncLimit = UTIL_ToUInt32( Payload );
1551
1552 if( m_SyncLimit <= 10 )
1553 {
1554 m_SyncLimit = 10;
1555 SendAllChat( m_GHost->m_Language->SettingSyncLimitToMinimum( "10" ) );
1556 }
1557 else if( m_SyncLimit >= 10000 )
1558 {
1559 m_SyncLimit = 10000;
1560 SendAllChat( m_GHost->m_Language->SettingSyncLimitToMaximum( "10000" ) );
1561 }
1562 else
1563 SendAllChat( m_GHost->m_Language->SettingSyncLimitTo( UTIL_ToString( m_SyncLimit ) ) );
1564 }
1565 }
1566
1567 //
1568 // !UNHOST
1569 //
1570
1571 if( Command == "unhost" && !m_CountDownStarted )
1572 m_Exiting = true;
1573
1574 //
1575 // !UNLOCK
1576 //
1577
1578 if( Command == "unlock" && ( RootAdminCheck || IsOwner( User ) ) )
1579 {
1580 SendAllChat( m_GHost->m_Language->GameUnlocked( ) );
1581 m_Locked = false;
1582 }
1583
1584 //
1585 // !UNMUTE
1586 //
1587
1588 if( Command == "unmute" )
1589 {
1590 CGamePlayer *LastMatch = NULL;
1591 uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
1592
1593 if( Matches == 0 )
1594 SendAllChat( m_GHost->m_Language->UnableToMuteNoMatchesFound( Payload ) );
1595 else if( Matches == 1 )
1596 {
1597 SendAllChat( m_GHost->m_Language->UnmutedPlayer( LastMatch->GetName( ), User ) );
1598 LastMatch->SetMuted( false );
1599 }
1600 else
1601 SendAllChat( m_GHost->m_Language->UnableToMuteFoundMoreThanOneMatch( Payload ) );
1602 }
1603
1604 //
1605 // !UNMUTEALL
1606 //
1607
1608 if( Command == "unmuteall" && m_GameLoaded )
1609 {
1610 SendAllChat( m_GHost->m_Language->GlobalChatUnmuted( ) );
1611 m_MuteAll = false;
1612 }
1613
1614 //
1615 // !VIRTUALHOST
1616 //
1617
1618 if( Command == "virtualhost" && !Payload.empty( ) && Payload.size( ) <= 15 && !m_CountDownStarted )
1619 {
1620 DeleteVirtualHost( );
1621 m_VirtualHostName = Payload;
1622 }
1623
1624 //
1625 // !VOTECANCEL
1626 //
1627
1628 if( Command == "votecancel" && !m_KickVotePlayer.empty( ) )
1629 {
1630 SendAllChat( m_GHost->m_Language->VoteKickCancelled( m_KickVotePlayer ) );
1631 m_KickVotePlayer.clear( );
1632 m_StartedKickVoteTime = 0;
1633 }
1634
1635 //
1636 // !W
1637 //
1638
1639 if( Command == "w" && !Payload.empty( ) )
1640 {
1641 // extract the name and the message
1642 // e.g. "Varlock hello there!" -> name: "Varlock", message: "hello there!"
1643
1644 string Name;
1645 string Message;
1646 string :: size_type MessageStart = Payload.find( " " );
1647
1648 if( MessageStart != string :: npos )
1649 {
1650 Name = Payload.substr( 0, MessageStart );
1651 Message = Payload.substr( MessageStart + 1 );
1652
1653 for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
1654 (*i)->QueueChatCommand( Message, Name, true );
1655 }
1656
1657 HideCommand = true;
1658 }
1659 }
1660 else
1661 {
1662 CONSOLE_Print( "[GAME: " + m_GameName + "] admin command ignored, the game is locked" );
1663 SendChat( player, m_GHost->m_Language->TheGameIsLocked( ) );
1664 }
1665 }
1666 else
1667 {
1668 if( !player->GetSpoofed( ) )
1669 CONSOLE_Print( "[GAME: " + m_GameName + "] non-spoofchecked user [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]" );
1670 else
1671 CONSOLE_Print( "[GAME: " + m_GameName + "] non-admin [" + User + "] sent command [" + Command + "] with payload [" + Payload + "]" );
1672 }
1673
1674 /*********************
1675 * NON ADMIN COMMANDS *
1676 *********************/
1677
1678 //
1679 // !CHECKME
1680 //
1681
1682 if( Command == "checkme" )
1683 SendChat( player, m_GHost->m_Language->CheckedPlayer( User, player->GetNumPings( ) > 0 ? UTIL_ToString( player->GetPing( m_GHost->m_LCPings ) ) + "ms" : "N/A", m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( player->GetExternalIP( ), true ) ), AdminCheck || RootAdminCheck ? "Yes" : "No", IsOwner( User ) ? "Yes" : "No", player->GetSpoofed( ) ? "Yes" : "No", player->GetSpoofedRealm( ).empty( ) ? "N/A" : player->GetSpoofedRealm( ), player->GetReserved( ) ? "Yes" : "No" ) );
1684
1685 //
1686 // !STATS
1687 //
1688
1689 if( Command == "stats" && GetTime( ) - player->GetStatsSentTime( ) >= 5 )
1690 {
1691 string StatsUser = User;
1692
1693 if( !Payload.empty( ) )
1694 StatsUser = Payload;
1695
1696 if( player->GetSpoofed( ) && ( AdminCheck || RootAdminCheck || IsOwner( User ) ) )
1697 m_PairedGPSChecks.push_back( PairedGPSCheck( string( ), m_GHost->m_DB->ThreadedGamePlayerSummaryCheck( StatsUser ) ) );
1698 else
1699 m_PairedGPSChecks.push_back( PairedGPSCheck( User, m_GHost->m_DB->ThreadedGamePlayerSummaryCheck( StatsUser ) ) );
1700
1701 player->SetStatsSentTime( GetTime( ) );
1702 }
1703
1704 //
1705 // !STATSDOTA
1706 //
1707
1708 if( Command == "statsdota" && GetTime( ) - player->GetStatsDotASentTime( ) >= 5 )
1709 {
1710 string StatsUser = User;
1711
1712 if( !Payload.empty( ) )
1713 StatsUser = Payload;
1714
1715 if( player->GetSpoofed( ) && ( AdminCheck || RootAdminCheck || IsOwner( User ) ) )
1716 m_PairedDPSChecks.push_back( PairedDPSCheck( string( ), m_GHost->m_DB->ThreadedDotAPlayerSummaryCheck( StatsUser ) ) );
1717 else
1718 m_PairedDPSChecks.push_back( PairedDPSCheck( User, m_GHost->m_DB->ThreadedDotAPlayerSummaryCheck( StatsUser ) ) );
1719
1720 player->SetStatsDotASentTime( GetTime( ) );
1721 }
1722
1723 //
1724 // !VERSION
1725 //
1726
1727 if( Command == "version" )
1728 {
1729 if( player->GetSpoofed( ) && ( AdminCheck || RootAdminCheck || IsOwner( User ) ) )
1730 SendChat( player, m_GHost->m_Language->VersionAdmin( m_GHost->m_Version ) );
1731 else
1732 SendChat( player, m_GHost->m_Language->VersionNotAdmin( m_GHost->m_Version ) );
1733 }
1734
1735 //
1736 // !VOTEKICK
1737 //
1738
1739 if( Command == "votekick" && m_GHost->m_VoteKickAllowed && !Payload.empty( ) )
1740 {
1741 if( !m_KickVotePlayer.empty( ) )
1742 SendChat( player, m_GHost->m_Language->UnableToVoteKickAlreadyInProgress( ) );
1743 else if( m_Players.size( ) == 2 )
1744 SendChat( player, m_GHost->m_Language->UnableToVoteKickNotEnoughPlayers( ) );
1745 else
1746 {
1747 CGamePlayer *LastMatch = NULL;
1748 uint32_t Matches = GetPlayerFromNamePartial( Payload, &LastMatch );
1749
1750 if( Matches == 0 )
1751 SendChat( player, m_GHost->m_Language->UnableToVoteKickNoMatchesFound( Payload ) );
1752 else if( Matches == 1 )
1753 {
1754 if( LastMatch->GetReserved( ) )
1755 SendChat( player, m_GHost->m_Language->UnableToVoteKickPlayerIsReserved( LastMatch->GetName( ) ) );
1756 else
1757 {
1758 m_KickVotePlayer = LastMatch->GetName( );
1759 m_StartedKickVoteTime = GetTime( );
1760
1761 for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
1762 (*i)->SetKickVote( false );
1763
1764 player->SetKickVote( true );
1765 CONSOLE_Print( "[GAME: " + m_GameName + "] votekick against player [" + m_KickVotePlayer + "] started by player [" + User + "]" );
1766 SendAllChat( m_GHost->m_Language->StartedVoteKick( LastMatch->GetName( ), User, UTIL_ToString( (uint32_t)ceil( ( GetNumHumanPlayers( ) - 1 ) * (float)m_GHost->m_VoteKickPercentage / 100 ) - 1 ) ) );
1767 SendAllChat( m_GHost->m_Language->TypeYesToVote( string( 1, m_GHost->m_CommandTrigger ) ) );
1768 }
1769 }
1770 else
1771 SendChat( player, m_GHost->m_Language->UnableToVoteKickFoundMoreThanOneMatch( Payload ) );
1772 }
1773 }
1774
1775 //
1776 // !YES
1777 //
1778
1779 if( Command == "yes" && !m_KickVotePlayer.empty( ) && player->GetName( ) != m_KickVotePlayer && !player->GetKickVote( ) )
1780 {
1781 player->SetKickVote( true );
1782 uint32_t VotesNeeded = (uint32_t)ceil( ( GetNumHumanPlayers( ) - 1 ) * (float)m_GHost->m_VoteKickPercentage / 100 );
1783 uint32_t Votes = 0;
1784
1785 for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
1786 {
1787 if( (*i)->GetKickVote( ) )
1788 Votes++;
1789 }
1790
1791 if( Votes >= VotesNeeded )
1792 {
1793 CGamePlayer *Victim = GetPlayerFromName( m_KickVotePlayer, true );
1794
1795 if( Victim )
1796 {
1797 Victim->SetDeleteMe( true );
1798 Victim->SetLeftReason( m_GHost->m_Language->WasKickedByVote( ) );
1799
1800 if( !m_GameLoading && !m_GameLoaded )
1801 Victim->SetLeftCode( PLAYERLEAVE_LOBBY );
1802 else
1803 Victim->SetLeftCode( PLAYERLEAVE_LOST );
1804
1805 if( !m_GameLoading && !m_GameLoaded )
1806 OpenSlot( GetSIDFromPID( Victim->GetPID( ) ), false );
1807
1808 CONSOLE_Print( "[GAME: " + m_GameName + "] votekick against player [" + m_KickVotePlayer + "] passed with " + UTIL_ToString( Votes ) + "/" + UTIL_ToString( GetNumHumanPlayers( ) ) + " votes" );
1809 SendAllChat( m_GHost->m_Language->VoteKickPassed( m_KickVotePlayer ) );
1810 }
1811 else
1812 SendAllChat( m_GHost->m_Language->ErrorVoteKickingPlayer( m_KickVotePlayer ) );
1813
1814 m_KickVotePlayer.clear( );
1815 m_StartedKickVoteTime = 0;
1816 }
1817 else
1818 SendAllChat( m_GHost->m_Language->VoteKickAcceptedNeedMoreVotes( m_KickVotePlayer, User, UTIL_ToString( VotesNeeded - Votes ) ) );
1819 }
1820
1821 return HideCommand;
1822 }
1823
1824 void CGame :: EventGameStarted( )
1825 {
1826 CBaseGame :: EventGameStarted( );
1827
1828 // record everything we need to ban each player in case we decide to do so later
1829 // this is because when a player leaves the game an admin might want to ban that player
1830 // but since the player has already left the game we don't have access to their information anymore
1831 // so we create a "potential ban" for each player and only store it in the database if requested to by an admin
1832
1833 for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
1834 m_DBBans.push_back( new CDBBan( (*i)->GetJoinedRealm( ), (*i)->GetName( ), (*i)->GetExternalIPString( ), string( ), string( ), string( ), string( ) ) );
1835 }
1836
1837 bool CGame :: IsGameDataSaved( )
1838 {
1839 return m_CallableGameAdd && m_CallableGameAdd->GetReady( );
1840 }
1841
1842 void CGame :: SaveGameData( )
1843 {
1844 CONSOLE_Print( "[GAME: " + m_GameName + "] saving game data to database" );
1845 m_CallableGameAdd = m_GHost->m_DB->ThreadedGameAdd( m_GHost->m_BNETs.size( ) == 1 ? m_GHost->m_BNETs[0]->GetServer( ) : string( ), m_DBGame->GetMap( ), m_GameName, m_OwnerName, m_GameTicks / 1000, m_GameState, m_CreatorName, m_CreatorServer );
1846 }

Send suggestions and bug reports to Sergey Poznyakoff
ViewVC Help
Powered by ViewVC 1.1.20