Рейтинг темы:
  • 4 Голос(ов) - 5 в среднем
  • 1
  • 2
  • 3
  • 4
  • 5
Ghost++ , GhostOne - Скачать - Установить - Настроить
AFTeRMaTh Написал:Ксандр, агент есть? А то здесь долго ответа ждать что то...
Есть. Если что, отпишу в ПМ (Можно в принципе и через Skype, кликни ссылку на моём юзербаре)
а на какой сервер запускать хочешь? Smile

AFTeRMaTh Написал:Ксандр, ghost and default должны быть одинакого настроены? или можно 1 удалить? а то 2 писать вообще трудно...
Нужны оба (заполнять одинаково)

AFTeRMaTh Написал:А как птс сделать? Поделись опытом
Опыт... Я вчера попробовал и получилось) Правда "Класс" и "Ранг" не робят...

AFTeRMaTh Написал:Скачал, 1.7.1 версия, эта?
Да

AFTeRMaTh Написал:Какой файл отвечает за то, что пишет что Админ зашел на канал? Убрать хочу, забыл где находится
В ghost.cfg и default.cfg
Код:
bot_channeljoingreets =
соответственно "0" выкл, "1" вкл
[Изображение: qvAG.jpg]
Ответ
AFTeRMaTh Написал:1) Да есть один... (если хочешь в ЛС могу отписать сервер)
2)Заполнил
3)Всё же? Как птс включить? Для этого нужно устанавливать БД MySQL?? Просто на Гост Ван вроде встроенная бд существует...
Если не секрет, в лс отпиши что да как
Класс и ранг... В каком смысле? там [D] [C] и т.п. так чтоли? Класс и ранг... не понял что то...
4)Спс за ответы
Может глупый вопрос, но всё же, можно ли компилить без прог, просто через блокнот? или толку 0? Обязательно нужно microsoft visual c++? boost?
Ничего сложного там... Вечерком смогу помочь, брось свой майловский ящик мне в ПМ
[Изображение: qvAG.jpg]
Ответ
Csandr Написал:Есть. Если что, отпишу в ПМ (Можно в принципе и через Skype, кликни ссылку на моём юзербаре)
а на какой сервер запускать хочешь? Smile


Нужны оба (заполнять одинаково)


Опыт... Я вчера попробовал и получилось) Правда "Класс" и "Ранг" не робят...


Да


В ghost.cfg и default.cfg
Код:
bot_channeljoingreets =
соответственно "0" выкл, "1" вкл


1) Да есть один... (если хочешь в ЛС могу отписать сервер)
2)Заполнил
3)Всё же? Как птс включить? Для этого нужно устанавливать БД MySQL?? Просто на Гост Ван вроде встроенная бд существует...
Если не секрет, в лс отпиши что да как
Класс и ранг... В каком смысле? там [D] [C] и т.п. так чтоли? Класс и ранг... не понял что то...
4)Спс за ответы
Может глупый вопрос, но всё же, можно ли компилить без прог, просто через блокнот? или толку 0? Обязательно нужно microsoft visual c++? boost?

Отослал, жду...
Ответ
Csandr Написал:Код не весь...
И так, код -ff инициирует конец игры, но, -ff не инициирует конец игры DotA. Отсюда и получается, что стата не сохраняется, ту бишь киллы, денаи и т.д. Если хочешь сделать чтобы всё робило, учи С++ и дорабатывай, а конкертно дорабатывай инициализацию конца DotA Wink


Это чтот типо ПТС рейтинга? Юзай БРТ, там есть эта функция, а в GhostOne/Ghost++ эту функцию нуно добавлять Smile

Блин я код не могу прикрепить а если тупо копией выложу он всю страницу займет(
Ответ
v1rus Написал:Блин я код не могу прикрепить а если тупо копией выложу он всю страницу займет(

в тег
Код:
[code]
Ответ
[CODE]/*

Copyright [2008] [Trevor Hogan]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/

*/

#include "ghost.h"
#include "util.h"
#include "config.h"
#include "language.h"
#include "socket.h"
#include "ghostdb.h"
#include "bnet.h"
#include "map.h"
#include "packed.h"
#include "savegame.h"
#include "replay.h"
#include "gameplayer.h"
#include "gameprotocol.h"
#include "game_base.h"

#include <cmath>
#include <string.h>
#include <time.h>

#include "next_combination.h"

//
// sorting classes
//

class CGamePlayerSortAscByPing
{
public:
bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
{
return Player1->GetPing( false ) < Player2->GetPing( false );
}
};

class CGamePlayerSortDescByPing
{
public:
bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
{
return Player1->GetPing( false ) > Player2->GetPing( false );
}
};

//
// CBaseGame
//

CBaseGame :: CBaseGame( CGHost *nGHost, CMap *nMap, CSaveGame *nSaveGame, uint16_t nHostPort, unsigned char nGameState, string nGameName, string nOwnerName, string nCreatorName, string nCreatorServer )
{
m_GHost = nGHost;
m_Socket = new CTCPServer( );
m_Protocol = new CGameProtocol( m_GHost );
m_Map = new CMap( *nMap );
m_SaveGame = nSaveGame;

if( m_GHost->m_SaveReplays && !m_SaveGame )
m_Replay = new CReplay( );
else
m_Replay = NULL;

m_Exiting = false;
m_Saving = false;
m_HostPort = nHostPort;
m_GameState = nGameState;
m_VirtualHostPID = 255;
m_FakePlayerPID = 255;
m_WTVPlayerPID = 255;
// wait time of 1 minute = 0 empty actions required
// wait time of 2 minutes = 1 empty action required
// etc...

if( m_GHost->m_ReconnectWaitTime == 0 )
m_GProxyEmptyActions = 0;
else
{
m_GProxyEmptyActions = m_GHost->m_ReconnectWaitTime - 1;

// clamp to 9 empty actions (10 minutes)

if( m_GProxyEmptyActions > 9 )
m_GProxyEmptyActions = 9;
}
m_GameName = nGameName;
wtvprocessid = 0;
AddGameName(m_GameName);
m_OriginalGameName = nGameName;
m_VirtualHostName = m_GHost->m_VirtualHostName;
m_OwnerName = nOwnerName;
m_CreatorName = nCreatorName;
m_CreatorServer = nCreatorServer;
m_HCLCommandString = m_Map->GetMapDefaultHCL( );
m_Server = nCreatorServer;
m_RandomSeed = GetTicks( );
m_GHost->m_HostCounter++;
m_GHost->SaveHostCounter();
if (m_GHost->m_MaxHostCounter>0)
if (m_GHost->m_HostCounter>m_GHost->m_MaxHostCounter)
m_GHost->m_HostCounter = 1;
m_HostCounter = m_GHost->m_HostCounter;
m_Latency = m_GHost->m_Latency;
m_UseDynamicLatency = m_GHost->m_UseDynamicLatency;
m_DynamicLatency = m_Latency;
m_DetourAllMessagesToAdmins = m_GHost->m_DetourAllMessagesToAdmins;
m_NormalCountdown = m_GHost->m_DetourAllMessagesToAdmins;
m_LastDynamicLatencyTicks = 0;
m_LastAdminJoinAndFullTicks = GetTicks();
m_MaxSync = 0;
m_MaxSyncUser = string();
m_SyncLimit = m_GHost->m_SyncLimit;
m_SyncCounter = 0;
m_MaxSyncCounter = 0;
m_GameTicks = 0;
m_CreationTime = GetTime( );
m_LastPingTime = GetTime( );
m_LastRefreshTime = GetTime( );
m_LastDownloadTicks = GetTime( );
m_LastDLCounterTicks = GetTime( );
m_DownloadCounter = 0;
m_DLCounter = 0;
m_LastDownloadCounterResetTicks = GetTicks( );
m_LastAnnounceTime = 0;
m_AnnounceInterval = 0;
m_LastAutoStartTime = GetTime( );
m_AutoStartPlayers = 0;
m_LastCountDownTicks = 0;
m_CountDownCounter = 0;
m_MsgTime = 170;
m_MsgStop = false;
m_StartedLoadingTicks = 0;
m_StartedLoadingTime = 0;
m_StartPlayers = 0;
m_LastLagScreenResetTime = 0;
m_LastActionSentTicks = 0;
m_LastActionLateBy = 0;
m_StartedLaggingTime = 0;
m_LastLagScreenTime = 0;
m_LastReservedSeen = GetTime( );
m_LastActionLateTicks = 0;
m_StartedKickVoteTime = 0;
m_LagScreenTime = 0;
m_GameLoadedTime = 0;
m_GameOverTime = 0;
m_LastPlayerLeaveTicks = 0;
m_MinimumScore = 0.0;
m_MaximumScore = 0.0;
m_SlotInfoChanged = false;
m_Locked = false;
m_RefreshCompleted = false;
m_RefreshMessages = m_GHost->m_RefreshMessages;
m_RefreshError = false;
m_OwnerJoined = false;
m_ShowScoreOf = string();
m_ShowNoteOf = string();
m_DisableStats = false;
m_HCL = false;
m_Listen = false;
m_RootListen = false;
m_MuteAll = false;
m_GameEnded = false;
m_GameEndedTime = 0;
m_GameLoadedMessage = false;
m_AllPlayersWarnChecked = false;
m_LastWarnCheck = 0;
m_MuteLobby = false;
m_CountDownStarted = false;
m_GameEndCountDownStarted = false;
m_GameLoading = false;
m_GameLoaded = false;
m_EndRequested = false;
m_EndRequestedTicks = 0;
if (m_GHost->m_Banning == 1)
m_Bans = true;
else
m_Bans = false;
m_LoadInGame = m_Map->GetMapLoadInGame( );
m_Desynced = false;
m_Lagging = false;
m_EndGameTime = 0;
m_ShowRealSlotCount = m_GHost->m_ShowRealSlotCount;
m_SwitchTime = 0;
m_SwitchNr = 0;
m_Switched = false;
m_CreatorAsFriend = m_GHost->m_addcreatorasfriendonhost;
m_GarenaOnly = false;
m_GameOverCanceled = false;
m_GameOverDiffCanceled = false;
m_CountryCheck = false;
m_CountryCheck2 = false;
m_ProviderCheck = false;
m_ProviderCheck2 = false;
m_ScoreCheck = false;
m_ScoreCheckChecked = false;
m_ScoreCheckScore = 0;
m_ScoreCheckRank = 0;
m_Team1 = 0;
m_Team2 = 0;
m_Team3 = 0;
m_Team4 = 0;
m_TeamDiff = 0;
m_EvenPlayeredTeams = false;
m_BanOn = false;
m_PlayersLeft = 0;
m_Rehost = false;
m_LastPlayerJoined = 255;
m_LastPlayerJoinedTime = GetTime()+5;
m_LastPlayerJoiningTime = GetTime()+5;
m_LastMars = GetTime()-10;
m_LastPlayerWarningTicks = GetTicks();
m_AllSlotsOccupied = false;
m_PingsUpdated = false;
m_AllSlotsAnnounced = false;
m_DownloadOnlyMode = false;
m_AutoSave = m_GHost->m_AutoSave;
m_DoAutoWarns = false;
m_MatchMaking = false;
m_LocalAdminMessages = m_GHost->m_LocalAdminMessages;

if( m_SaveGame )
{
m_EnforceSlots = m_SaveGame->GetSlots( );
m_Slots = m_SaveGame->GetSlots( );

// the savegame slots contain player entries
// we really just want the open/closed/computer entries
// so open all the player slots

for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); i++ )
{
if( (*i).GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && (*i).GetComputer( ) == 0 )
{
(*i).SetPID( 0 );
(*i).SetDownloadStatus( 255 );
(*i).SetSlotStatus( SLOTSTATUS_OPEN );
}
}
}
else
m_Slots = m_Map->GetSlots( );

if( !m_GHost->m_IPBlackListFile.empty( ) )
{
ifstream in;
in.open( m_GHost->m_IPBlackListFile.c_str( ) );

if( in.fail( ) )
CONSOLE_Print( "[GAME: " + m_GameName + "] error loading IP blacklist file [" + m_GHost->m_IPBlackListFile + "]" );
else
{
CONSOLE_Print( "[GAME: " + m_GameName + "] loading IP blacklist file [" + m_GHost->m_IPBlackListFile + "]" );
string Line;

while( !in.eof( ) )
{
getline( in, Line );

// ignore blank lines and comments

if( Line.empty( ) || Line[0] == '#' )
continue;

// remove newlines and partial newlines to help fix issues with Windows formatted files on Linux systems

Line.erase( remove( Line.begin( ), Line.end( ), ' ' ), Line.end( ) );
Line.erase( remove( Line.begin( ), Line.end( ), '\r' ), Line.end( ) );
Line.erase( remove( Line.begin( ), Line.end( ), '\n' ), Line.end( ) );

// ignore lines that don't look like IP addresses

if( Line.find_first_not_of( "1234567890." ) != string :: npos )
continue;

m_IPBlackList.insert( Line );
}

in.close( );

CONSOLE_Print( "[GAME: " + m_GameName + "] loaded " + UTIL_ToString( m_IPBlackList.size( ) ) + " lines from IP blacklist file" );
}
}

m_AutoWarnMarks = m_Map->GetAutoWarnMarks( );

// start listening for connections

if( !m_GHost->m_BindAddress.empty( ) )
CONSOLE_Print( "[GAME: " + m_GameName + "] attempting to bind to address [" + m_GHost->m_BindAddress + "]" );
else
CONSOLE_Print( "[GAME: " + m_GameName + "] attempting to bind to all available addresses" );

if( m_Socket->Listen( m_GHost->m_BindAddress, m_HostPort ) )
CONSOLE_Print( "[GAME: " + m_GameName + "] listening on port " + UTIL_ToString( m_HostPort ) );
else
{
CONSOLE_Print( "[GAME: " + m_GameName + "] error listening on port " + UTIL_ToString( m_HostPort ) );
m_Exiting = true;
}

m_LastReservedSeen = GetTime( );

m_GetMapType = m_Map->GetMapType();
m_GetMapPath = m_Map->GetMapPath();
m_GetMapGameType = m_Map->GetMapGameType();
m_GetMapNumPlayers = m_Map->GetMapNumPlayers();
m_GetMapNumTeams = m_Map->GetMapNumTeams();
m_GetMapOnlyAutoWarnIfMoreThanXPlayers = m_Map->GetMapOnlyAutoWarnIfMoreThanXPlayers();

if (m_GetMapType == "dota" && m_GHost->m_AutoStartDotaGames)
m_AutoStartPlayers = 10;
}

CBaseGame :: ~CBaseGame( )
{
// save replay
// todotodo: put this in a thread

if( m_Replay && ( m_GameLoading || m_GameLoaded ) )
{
time_t Now = time( NULL );
char Time[17];
memset( Time, 0, sizeof( char ) * 17 );
time_t times = Now+ m_GHost->m_ReplayTimeShift;
strftime( Time, sizeof( char ) * 17, "%Y-%m-%d %H-%M", localtime( &times ) );
string MinString = UTIL_ToString( ( m_GameTicks / 1000 ) / 60 );
string SecString = UTIL_ToString( ( m_GameTicks / 1000 ) % 60 );

if( MinString.size( ) == 1 )
MinString.insert( 0, "0" );

if( SecString.size( ) == 1 )
SecString.insert( 0, "0" );

m_Replay->BuildReplay( m_GameName, m_StatString, m_GHost->m_ReplayWar3Version, m_GHost->m_ReplayBuildNumber );
m_Replay->Save( m_GHost->m_TFT, m_GHost->m_ReplayPath + UTIL_FileSafeName( "GHost++ " + string( Time ) + " " + m_GameName + ".w3g" ) );
}

delete m_Socket;
delete m_Protocol;
delete m_Map;
delete m_Replay;

for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); i++ )
delete *i;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
delete *i;

for( vector<CCallableScoreCheck *> :: iterator i = m_ScoreChecks.begin( ); i != m_ScoreChecks.end( ); i++ )
m_GHost->m_Callables.push_back( *i );

for( vector<CCallableWarnCount *> :: iterator i = m_WarnCounts.begin( ); i != m_WarnCounts.end( ); i++ )
m_GHost->m_Callables.push_back( *i );

for( vector<CCallableBanUpdate *> :: iterator i = m_BanUpdates.begin( ); i != m_BanUpdates.end( ); i++ )
m_GHost->m_Callables.push_back( *i );

for( vector<CCallableRunQuery *> :: iterator i = m_RunQueries.begin( ); i != m_RunQueries.end( ); i++ )
m_GHost->m_Callables.push_back( *i );

while( !m_Actions.empty( ) )
{
delete m_Actions.front( );
m_Actions.pop( );
}

m_GameNames.clear();
}

uint32_t CBaseGame :: GetNextTimedActionTicks( )
{
// return the number of ticks (ms) until the next "timed action", which for our purposes is the next game update
// the main GHost++ loop will make sure the next loop update happens at or before this value
// note: there's no reason this function couldn't take into account the game's other timers too but they're far less critical
// warning: this function must take into account when actions are not being sent (e.g. during loading or lagging)

if( !m_GameLoaded || m_Lagging )
return 50;

uint32_t TicksSinceLastUpdate = GetTicks( ) - m_LastActionSentTicks;

if( TicksSinceLastUpdate > m_DynamicLatency - m_LastActionLateBy )
return 0;
else
return m_DynamicLatency - m_LastActionLateBy - TicksSinceLastUpdate;
}

uint32_t CBaseGame :: GetSlotsOccupied( )
{
uint32_t NumSlotsOccupied = 0;

for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); i++ )
{
if( (*i).GetSlotStatus( ) == SLOTSTATUS_OCCUPIED )
NumSlotsOccupied++;
}

return NumSlotsOccupied;
}

uint32_t CBaseGame :: GetSlotsOpen( )
{
uint32_t NumSlotsOpen = 0;

for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); i++ )
{
if( (*i).GetSlotStatus( ) == SLOTSTATUS_OPEN )
NumSlotsOpen++;
}

return NumSlotsOpen;
}

uint32_t CBaseGame :: GetNumPlayers( )
{
uint32_t NumPlayers = GetNumHumanPlayers( );

if( m_FakePlayerPID != 255 )
NumPlayers++;

return NumPlayers;
}

uint32_t CBaseGame :: GetNumHumanPlayers( )
{
uint32_t NumHumanPlayers = 0;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( !(*i)->GetLeftMessageSent( ) )
NumHumanPlayers++;
}

return NumHumanPlayers;
}

string CBaseGame :: GetDescription( )
{
string Description = m_GameName + " : " + m_OwnerName + " : " + UTIL_ToString( GetNumHumanPlayers( ) ) + "/" + UTIL_ToString( m_GameLoading || m_GameLoaded ? m_StartPlayers : m_Slots.size( ) );

if( m_GameLoading || m_GameLoaded )
Description += " : " + UTIL_ToString( ( m_GameTicks / 1000 ) / 60 ) + "m";
else
Description += " : " + UTIL_ToString( ( GetTime( ) - m_CreationTime ) / 60 ) + "m";

return Description;
}

void CBaseGame :: SetAnnounce( uint32_t interval, string message )
{
m_AnnounceInterval = interval;
m_AnnounceMessage = message;
m_LastAnnounceTime = GetTime( );
}

unsigned int CBaseGame :: SetFD( void *fd, void *send_fd, int *nfds )
{
unsigned int NumFDs = 0;

if( m_Socket )
{
m_Socket->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
NumFDs++;
}

for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); i++ )
{
if( (*i)->GetSocket( ) )
{
(*i)->GetSocket( )->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
NumFDs++;
}
}

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetSocket( ) )
{
(*i)->GetSocket( )->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
NumFDs++;
}
}

return NumFDs;
}

bool CBaseGame :: Update( void *fd, void *send_fd )
{
// update callables

for( vector<CCallableWarnCount *> :: iterator i = m_WarnCounts.begin( ); i != m_WarnCounts.end( ); )
{
if( (*i)->GetReady( ) )
{
uint32_t Count = (*i)->GetResult( );
CGamePlayer * Player = GetPlayerFromName((*i)->GetName(), false);

if (Player)
{
// count warns
if (Count > 0)
{
SendChat( Player, m_GHost->m_Language->DisplayWarnsCount( UTIL_ToString(Count) ) );
}
}

m_GHost->m_DB->RecoverCallable( *i );
delete *i;
i = m_WarnCounts.erase( i );
}
else
i++;
}

for( vector<CCallableBanUpdate *> :: iterator i = m_BanUpdates.begin( ); i != m_BanUpdates.end( ); )
{
if( (*i)->GetReady( ) )
{
m_GHost->m_DB->RecoverCallable( *i );
delete *i;
i = m_BanUpdates.erase( i );
}
else
i++;
}

for( vector<CCallableRunQuery *> :: iterator i = m_RunQueries.begin( ); i != m_RunQueries.end( ); )
{
if( (*i)->GetReady( ) )
{
m_GHost->m_DB->RecoverCallable( *i );
delete *i;
i = m_RunQueries.erase( i );
}
else
i++;
}

for( vector<CCallableScoreCheck *> :: iterator i = m_ScoreChecks.begin( ); i != m_ScoreChecks.end( ); )
{
if( (*i)->GetReady( ) )
{
double Score = (*i)->GetResult( );

CGamePlayer * Player = GetPlayerFromName((*i)->GetName( ), false);
if (Player)
{
Player->SetScoreS(UTIL_ToString(Score,2));
Player->SetRankS(UTIL_ToString((*i)->GetRank( )));
Player->SetScore(Score);
}

for( vector<CPotentialPlayer *> :: iterator j = m_Potentials.begin( ); j != m_Potentials.end( ); j++ )
{
if( (*j)->GetJoinPlayer( ) && (*j)->GetJoinPlayer( )->GetName( ) == (*i)->GetName( ) )
{
if (m_MatchMaking && m_AutoStartPlayers != 0)
EventPlayerJoinedWithScore( *j, (*j)->GetJoinPlayer( ), Score );
else
if (m_ScoreCheck)
{
m_ScoreCheckChecked = true;
m_ScoreCheckScore = Score;
m_ScoreCheckRank = (*i)->GetRank( );
EventPlayerJoined(*j, (*j)->GetJoinPlayer( ));
m_ScoreCheckChecked = false;
}
}
}

m_GHost->m_DB->RecoverCallable( *i );
delete *i;
i = m_ScoreChecks.erase( i );
}
else
i++;
}

// update players

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); )
{
if( (*i)->Update( fd ) )
{
EventPlayerDeleted( *i );
delete *i;
i = m_Players.erase( i );
}
else
i++;
}

for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); )
{
if( (*i)->Update( fd ) )
{
// flush the socket (e.g. in case a rejection message is queued)

if( (*i)->GetSocket( ) )
(*i)->GetSocket( )->DoSend( (fd_set *)send_fd );

delete *i;
i = m_Potentials.erase( i );
}
else
i++;
}

// create the virtual host player

// if( !m_GameLoading && !m_GameLoaded && GetNumPlayers( ) < 12 )
uint32_t SlotReq = 12;
if (m_ShowRealSlotCount)
SlotReq = m_Slots.size();
if( !m_GameLoading && !m_GameLoaded && GetNumPlayers( ) < SlotReq && !m_GHost->m_DetourAllMessagesToAdmins )
CreateVirtualHost( );

// unlock the game

if( m_Locked && !GetPlayerFromName( m_OwnerName, false ) )
{
SendAllChat( m_GHost->m_Language->GameUnlocked( ) );
m_Locked = false;
}

// ping every 5 seconds
// changed this to ping during game loading as well to hopefully fix some problems with people disconnecting during loading
// changed this to ping during the game as well

if( GetTime( ) - m_LastPingTime >= 5 )
{
// note: we must send pings to players who are downloading the map because Warcraft III disconnects from the lobby if it doesn't receive a ping every ~90 seconds
// so if the player takes longer than 90 seconds to download the map they would be disconnected unless we keep sending pings
// todotodo: ignore pings received from players who have recently finished downloading the map

SendAll( m_Protocol->SEND_W3GS_PING_FROM_HOST( ) );

// we also broadcast the game to the local network every 5 seconds so we hijack this timer for our nefarious purposes
// however we only want to broadcast if the countdown hasn't started
// see the !sendlan code later in this file for some more information about how this works
// todotodo: should we send a game cancel message somewhere? we'll need to implement a host counter for it to work

if( !m_CountDownStarted )
{
BYTEARRAY MapGameType;

// construct a fixed host counter which will be used to identify players from this "realm" (i.e. LAN)
// the fixed host counter's 4 most significant bits will contain a 4 bit ID (0-15)
// the rest of the fixed host counter will contain the 28 least significant bits of the actual host counter
// since we're destroying 4 bits of information here the actual host counter should not be greater than 2^28 which is a reasonable assumption
// when a player joins a game we can obtain the ID from the received host counter
// note: LAN broadcasts use an ID of 0, battle.net refreshes use an ID of 1-10, the rest are unused

uint32_t FixedHostCounter = m_HostCounter & 0x0FFFFFFF;

// construct the correct W3GS_GAMEINFO packet

uint32_t slotstotal = m_Slots.size( );
uint32_t slotsopen = GetSlotsOpen();
if (slotsopen<2) slotsopen = 2;

if (!m_ShowRealSlotCount)
{
slotsopen = 12;
slotstotal = 12;
}

if( m_SaveGame )
{
MapGameType.push_back( 0 );
MapGameType.push_back( 2 );
MapGameType.push_back( 0 );
MapGameType.push_back( 0 );
BYTEARRAY MapWidth;
MapWidth.push_back( 0 );
MapWidth.push_back( 0 );
BYTEARRAY MapHeight;
MapHeight.push_back( 0 );
MapHeight.push_back( 0 );
if (m_GHost->m_broadcastinlan)
{
m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
m_GHost->m_UDPSocket->SendTo("127.0.0.1", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
}
// if (m_GarenaOnly)
{
//m_GHost->m_UDPSocket->SendTo("127.0.0.1", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), 12, 12, m_HostPort, FixedHostCounter ) );
}
m_GHost->UDPChatSend(m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
}
else
{
MapGameType.push_back( m_Map->GetMapGameType( ) );
MapGameType.push_back( 0 );
MapGameType.push_back( 0 );
MapGameType.push_back( 0 );
if (m_GHost->m_broadcastinlan)
{
m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
m_GHost->m_UDPSocket->SendTo( "127.0.0.1", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
}
// if (m_GarenaOnly)
{
// m_GHost->m_UDPSocket->SendTo( "127.0.0.1", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_LANWar3Version, MapGameType, 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, FixedHostCounter ) );
// m_GHost->m_UDPSocket->SendTo( "192.168.1.2", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_LANWar3Version, MapGameType, 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, FixedHostCounter ) );
}
m_GHost->UDPChatSend(m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, 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, FixedHostCounter ));
}
}

m_LastPingTime = GetTime( );
}

// refresh every 3 seconds

bool m_AutoHostRefresh = (m_autohosted && m_GHost->m_AutoHostLocal);
// don't refresh if we're autohosting locally only
if (!m_AutoHostRefresh )
if( !m_RefreshError && !m_CountDownStarted && m_GameState == GAME_PUBLIC && GetSlotsOpen( ) > 0 && GetTime( )- m_LastRefreshTime >= 3 )
{
// send a game refresh packet to each battle.net connection

bool Refreshed = false;

uint32_t slotstotal = m_Slots.size( );
uint32_t slotsopen = GetSlotsOpen();

for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
{
// don't queue a game refresh message if the queue contains more than 1 packet because they're very low priority

if( (*i)->GetOutPacketsQueued( ) <= 1 )
{
(*i)->QueueGameRefresh( m_GameState, m_GameName, string( ), m_Map, m_SaveGame, GetTime( ) - m_CreationTime, m_HostCounter, slotstotal, slotsopen );
Refreshed = true;
}
}

// only print the "game refreshed" message if we actually refreshed on at least one battle.net server

if( m_RefreshMessages && Refreshed )
SendAllChat( m_GHost->m_Language->GameRefreshed( ) );

m_LastRefreshTime = GetTime( );
}

// send more map data

if( !m_GameLoading && !m_GameLoaded && GetTicks( ) - m_LastDownloadCounterResetTicks >= 1000 )
{
// hackhack: another timer hijack is in progress here
// since the download counter is reset once per second it's a great place to update the slot info if necessary

if( m_SlotInfoChanged )
SendAllSlotInfo( );

// m_DownloadCounter = 0;
m_LastDownloadCounterResetTicks = GetTicks( );
}

/*
if( !m_GameLoading && !m_GameLoaded && GetTicks( ) - m_LastDownloadTicks > 100 )
{
uint32_t Downloaders = 0;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetDownloadStarted( ) && !(*i)->GetDownloadFinished( ) )
{
Downloaders++;

if( m_GHost->m_MaxDownloaders > 0 && Downloaders > m_GHost->m_MaxDownloaders )
break;

// send up to 100 pieces of the map at once so that the download goes faster
// if we wait for each MAPPART packet to be acknowledged by the client it'll take a long time to download
// this is because we would have to wait the round trip time (the ping time) between sending every 1442 bytes of map data
// doing it this way allows us to send at least 140 KB in each round trip interval which is much more reasonable
// the theoretical throughput is [140 KB * 1000 / ping] in KB/sec so someone with 100 ping (round trip ping, not LC ping) could download at 1400 KB/sec
// note: this creates a queue of map data which clogs up the connection when the client is on a slower connection (e.g. dialup)
// in this case any changes to the lobby are delayed by the amount of time it takes to send the queued data (i.e. 140 KB, which could be 30 seconds or more)
// for example, players joining and leaving, slot changes, chat messages would all appear to happen much later for the low bandwidth player
// note: the throughput is also limited by the number of times this code is executed each second
// e.g. if we send the maximum amount (140 KB) 10 times per second the theoretical throughput is 1400 KB/sec
// therefore the maximum throughput is 1400 KB/sec regardless of ping and this value slowly diminishes as the player's ping increases
// in addition to this, the throughput is limited by the configuration value bot_maxdownloadspeed
// in summary: the actual throughput is MIN( 140 * 1000 / ping, 1400, bot_maxdownloadspeed ) in KB/sec assuming only one player is downloading the map

uint32_t MapSize = UTIL_ByteArrayToUInt32( m_Map->GetMapSize( ), false );

while( (*i)->GetLastMapPartSent( ) < (*i)->GetLastMapPartAcked( ) + 1442 * 100 && (*i)->GetLastMapPartSent( ) < MapSize )
{
if( (*i)->GetLastMapPartSent( ) == 0 )
{
// overwrite the "started download ticks" since this is the first time we've sent any map data to the player
// prior to this we've only determined if the player needs to download the map but it's possible we could have delayed sending any data due to download limits

(*i)->SetStartedDownloadingTicks( GetTicks( ) );
}

// limit the download speed if we're sending too much data
// the download counter is the # of map bytes downloaded in the last second (it's reset once per second)

if( m_GHost->m_MaxDownloadSpeed > 0 && m_DownloadCounter > m_GHost->m_MaxDownloadSpeed * 1024 )
break;

Send( *i, m_Protocol->SEND_W3GS_MAPPART( GetHostPID( ), (*i)->GetPID( ), (*i)->GetLastMapPartSent( ), m_Map->GetMapData( ) ) );
(*i)->SetLastMapPartSent( (*i)->GetLastMapPartSent( ) + 1442 );
m_DownloadCounter += 1442;
}
}
}

m_LastDownloadTicks = GetTicks( );
}
*/
// announce every m_AnnounceInterval seconds

if( !m_AnnounceMessage.empty( ) && !m_CountDownStarted && GetTime( ) - m_LastAnnounceTime >= m_AnnounceInterval )
{
{
string msg = m_AnnounceMessage;
string msgl = string();
string :: size_type lstart;
while ((lstart = msg.find("|"))!=string :: npos)
{
msgl = msg.substr(0,lstart);
msg = msg.substr(lstart+1,msg.length()-lstart-1);
SendAllChat(msgl);
}
SendAllChat(msg);
}
m_LastAnnounceTime = GetTime( );
}

// kick players who don't spoof check within 20 seconds when spoof checks are required and the game is autohosted

if( !m_CountDownStarted && m_GHost->m_RequireSpoofChecks && m_GameState == GAME_PUBLIC && !m_GHost->m_AutoHostGameName.empty( ) && m_GHost->m_AutoHostMaximumGames != 0 && m_GHost->m_AutoHostAutoStartPlayers != 0 && m_AutoStartPlayers != 0 )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( !(*i)->GetSpoofed( ) && GetTime( ) - (*i)->GetJoinTime( ) >= 20 )
{
(*i)->SetDeleteMe( true );
(*i)->SetLeftReason( m_GHost->m_Language->WasKickedForNotSpoofChecking( ) );
(*i)->SetLeftCode( PLAYERLEAVE_LOBBY );
OpenSlot( GetSIDFromPID( (*i)->GetPID( ) ), false );
}
}
}

// try to auto start every 10 seconds

if( !m_CountDownStarted && m_AutoStartPlayers != 0 && GetTime( ) - m_LastAutoStartTime >= 10 )
{
StartCountDownAuto( m_GHost->m_RequireSpoofChecks );
m_LastAutoStartTime = GetTime( );
}

// end game countdown every 1000 ms
if( m_GameEndCountDownStarted && GetTicks( ) - m_GameEndLastCountDownTicks >= 1000 )
{
if( m_GameEndCountDownCounter > 0 )
{
// we use a countdown counter rather than a "finish countdown time" here because it might alternately round up or down the count
// this sometimes resulted in a countdown of e.g. "6 5 3 2 1" during my testing which looks pretty dumb
// doing it this way ensures it's always "5 4 3 2 1" but each interval might not be *exactly* the same length

SendAllChat( UTIL_ToString( m_GameEndCountDownCounter ) + ". . ." );
m_GameEndCountDownCounter--;
}
else if( !m_GameLoading && m_GameLoaded )
{
m_GameEndCountDownStarted = false;
StopPlayers( "was disconnected (admin ended game)" );
}

m_GameEndLastCountDownTicks = GetTicks( );
}
// countdown every 500 ms

uint32_t waittime = 500;
if (m_NormalCountdown)
waittime = 1200;

if( m_CountDownStarted && GetTicks( ) - m_LastCountDownTicks >= waittime )
{
if( m_CountDownCounter > 0 )
{
// we use a countdown counter rather than a "finish countdown time" here because it might alternately round up or down the count
// this sometimes resulted in a countdown of e.g. "6 5 3 2 1" during my testing which looks pretty dumb
// doing it this way ensures it's always "5 4 3 2 1" but each interval might not be *exactly* the same length

if (!m_NormalCountdown)
SendAllChat( UTIL_ToString( m_CountDownCounter ) + ". . ." );
m_CountDownCounter--;
}
else if( !m_GameLoading && !m_GameLoaded )
EventGameStarted( );

m_LastCountDownTicks = GetTicks( );
}

// update pings in the GUI
if (GetTime()>m_CreationTime+5 && !m_PingsUpdated)
if (GetTime()>m_LastPlayerJoinedTime+3 && !m_GameLoading && !m_GameLoaded)
{
m_PingsUpdated = true;
m_GHost->UDPChatSend("|lobbyupdate");
}

// check if no one has joined the game for a long time (25 sec default) and rehost
bool LoggedIn = false;
for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
{
if ((*i)->GetServer()==m_Server)
LoggedIn = (*i)->GetLoggedIn();
// the game creation message will be sent on the next refresh
}

if (!(m_autohosted && m_GHost->m_AutoHostLocal))
if (!m_DownloadOnlyMode)
if (m_VirtualHostName!="|cFFC04040Admin")
if (m_GameState==GAME_PUBLIC)
if (m_GHost->m_AutoRehostDelay!=0)
// if (LoggedIn)
if (GetTime()>m_CreationTime+5)
if (GetTime()>m_LastPlayerJoiningTime+m_GHost->m_AutoRehostDelay && !m_GameLoading && !m_GameLoaded && !m_AllSlotsOccupied)
{
/* if (m_AutoStartPlayers!=0)
m_GHost->m_HostCounter++;*/
ReCalculateTeams();
m_GameName = m_GHost->IncGameNr(m_GameName);
m_GHost->m_HostCounter++;
m_GHost->SaveHostCounter();
if (m_GHost->m_MaxHostCounter>0)
if (m_GHost->m_HostCounter>m_GHost->m_MaxHostCounter)
m_GHost->m_HostCounter = 1;
m_HostCounter = m_GHost->m_HostCounter;
m_GHost->m_QuietRehost = true;
m_RefreshError = false;
m_Rehost = true;
AddGameName(m_GameName);
for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
{
(*i)->QueueGameUncreate( );
(*i)->QueueEnterChat( );

// the game creation message will be sent on the next refresh
}
m_CreationTime = GetTime( );
m_LastRefreshTime = GetTime( );
m_LastPlayerJoinedTime = GetTime( )+5;
m_LastPlayerJoiningTime = GetTime( )+5;
}

// check if the lobby is "abandoned" and needs to be closed since it will never start

if(m_VirtualHostName!="|cFFC04040Admin" && !m_GameLoading && !m_GameLoaded && m_AutoStartPlayers == 0 && m_GHost->m_LobbyTimeLimitMax > 0 )
{
// check if we've hit the time limit

if( GetTime( ) - m_CreationTime >= m_GHost->m_LobbyTimeLimitMax * 60 && !m_DownloadOnlyMode )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (lobby time limit hit)" );
return true;
}
}

// check if the lobby is "abandoned" and needs to be closed since it will never start

if( !m_GameLoading && !m_GameLoaded && (m_AutoStartPlayers == 0) && m_GHost->m_LobbyTimeLimit > 0 )
{
// check if there's a player with reserved status in the game

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if (m_AutoStartPlayers == 0) {

if( (*i)->GetReserved( ) )
m_LastReservedSeen = GetTime( );

} else {

// If autostart is on, and there is at least one player, the above for loop will run.
m_LastReservedSeen = GetTime( );
break;

}

// if( (*i)->GetReserved( ) )
// m_LastReservedSeen = GetTime( );
}

// check if we've hit the time limit

if( GetTime( ) - m_LastReservedSeen >= m_GHost->m_LobbyTimeLimit * 60 && !m_DownloadOnlyMode )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (lobby time limit hit)" );
return true;
}
}

// check if the game is loaded

if( m_GameLoading )
{
bool FinishedLoading = true;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
FinishedLoading = (*i)->GetFinishedLoading( );

if( !FinishedLoading )
break;
}

if( FinishedLoading )
{
m_LastActionSentTicks = GetTicks( );
m_GameLoading = false;
m_GameLoaded = true;
EventGameLoaded( );
}
else
{
// reset the "lag" screen (the load-in-game screen) every 30 seconds

if( m_LoadInGame && GetTime( ) - m_LastLagScreenResetTime >= 30 )
{
bool UsingGProxy = false;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetGProxy( ) )
UsingGProxy = true;
}

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetFinishedLoading( ) )
{
// stop the lag screen

for( vector<CGamePlayer *> :: iterator j = m_Players.begin( ); j != m_Players.end( ); j++ )
{
if( !(*j)->GetFinishedLoading( ) )
Send( *i, m_Protocol->SEND_W3GS_STOP_LAG( *j, true ) );
}

// send an empty update
// this resets the lag screen timer but creates a rather annoying problem
// in order to prevent a desync we must make sure every player receives the exact same "desyncable game data" (updates and player leaves) in the exact same order
// unfortunately we cannot send updates to players who are still loading the map, so we buffer the updates to those players (see the else clause a few lines down for the code)
// in addition to this we must ensure any player leave messages are sent in the exact same position relative to these updates so those must be buffered too

if( UsingGProxy && !(*i)->GetGProxy( ) )
{
// we must send empty actions to non-GProxy++ players
// GProxy++ will insert these itself so we don't need to send them to GProxy++ players
// empty actions are used to extend the time a player can use when reconnecting

for( unsigned char j = 0; j < m_GProxyEmptyActions; j++ )
Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
}

Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );

// start the lag screen

Send( *i, m_Protocol->SEND_W3GS_START_LAG( m_Players, true ) );
}
else
{
// buffer the empty update since the player is still loading the map

if( UsingGProxy && !(*i)->GetGProxy( ) )
{
// we must send empty actions to non-GProxy++ players
// GProxy++ will insert these itself so we don't need to send them to GProxy++ players
// empty actions are used to extend the time a player can use when reconnecting

for( unsigned char j = 0; j < m_GProxyEmptyActions; j++ )
(*i)->AddLoadInGameData( m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
}

(*i)->AddLoadInGameData( m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
}
}

// add actions to replay

if( m_Replay )
{
if( UsingGProxy )
{
for( unsigned char i = 0; i < m_GProxyEmptyActions; i++ )
m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
}

m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
}

// Warcraft III doesn't seem to respond to empty actions

/* if( UsingGProxy )
m_SyncCounter += m_GProxyEmptyActions;

m_SyncCounter++; */
m_LastLagScreenResetTime = GetTime( );
}
}
}

// keep track of the largest sync counter (the number of keepalive packets received by each player)
// if anyone falls behind by more than m_SyncLimit keepalives we start the lag screen

if( m_GameLoaded )
{
// calculate the largest sync counter

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetSyncCounter( ) > m_MaxSyncCounter )
m_MaxSyncCounter = (*i)->GetSyncCounter( );
}

// check if anyone has started lagging
// we consider a player to have started lagging if they're more than m_SyncLimit keepalives behind

if( !m_Lagging )
{
string LaggingString;

m_MaxSync = 0;
uint32_t Sync = 0;
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
Sync = m_SyncCounter - (*i)->GetSyncCounter( );
if (Sync > m_MaxSync)
{
m_MaxSync = Sync;
m_MaxSyncUser = (*i)->GetName();
}
if( Sync > m_SyncLimit )
{
(*i)->SetLagging( true );
// calculate drop vote ticks
uint32_t d = 5;
if (m_GHost->m_DropVoteTime<45)
d = (45 * 1000)-m_GHost->m_DropVoteTime*1000 ;

(*i)->SetStartedLaggingTicks( GetTicks( )-d );
m_Lagging = true;
m_StartedLaggingTime = GetTime( );

if( LaggingString.empty( ) )
LaggingString = (*i)->GetName( );
else
LaggingString += ", " + (*i)->GetName( );
}
}

if( m_Lagging )
{
// start the lag screen

CONSOLE_Print( "[GAME: " + m_GameName + "] started lagging on [" + LaggingString + "]" );
SendAll( m_Protocol->SEND_W3GS_START_LAG( m_Players ) );

// reset everyone's drop vote

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
(*i)->SetDropVote( false );

m_LastLagScreenResetTime = GetTime( );

// get current time to use for the drop vote

m_LagScreenTime = GetTime();
}
}

if( m_Lagging )
{
bool UsingGProxy = false;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetGProxy( ) )
UsingGProxy = true;
}

uint32_t WaitTime = 60;

if( UsingGProxy )
WaitTime = ( m_GProxyEmptyActions + 1 ) * 60;

if( GetTime( ) - m_StartedLaggingTime >= WaitTime )
StopLaggers( m_GHost->m_Language->WasAutomaticallyDroppedAfterSeconds( UTIL_ToString( WaitTime ) ) );

// we cannot allow the lag screen to stay up for more than ~65 seconds because Warcraft III disconnects if it doesn't receive an action packet at least this often
// one (easy) solution is to simply drop all the laggers if they lag for more than 60 seconds
// another solution is to reset the lag screen the same way we reset it when using load-in-game
// this is required in order to give GProxy++ clients more time to reconnect

if( GetTime( ) - m_LastLagScreenResetTime >= 60 )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
// stop the lag screen

for( vector<CGamePlayer *> :: iterator j = m_Players.begin( ); j != m_Players.end( ); j++ )
{
if( (*j)->GetLagging( ) )
Send( *i, m_Protocol->SEND_W3GS_STOP_LAG( *j ) );
}

// send an empty update
// this resets the lag screen timer

if( UsingGProxy && !(*i)->GetGProxy( ) )
{
// we must send additional empty actions to non-GProxy++ players
// GProxy++ will insert these itself so we don't need to send them to GProxy++ players
// empty actions are used to extend the time a player can use when reconnecting

for( unsigned char j = 0; j < m_GProxyEmptyActions; j++ )
Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
}

Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );

// start the lag screen

Send( *i, m_Protocol->SEND_W3GS_START_LAG( m_Players ) );
}

// add actions to replay

if( m_Replay )
{
if( UsingGProxy )
{
for( unsigned char i = 0; i < m_GProxyEmptyActions; i++ )
m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
}

m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
}

// Warcraft III doesn't seem to respond to empty actions

/* if( UsingGProxy )
m_SyncCounter += m_GProxyEmptyActions;

m_SyncCounter++; */
m_LastLagScreenResetTime = GetTime( );
}

// check if anyone has stopped lagging normally
// we consider a player to have stopped lagging if they're less than half m_SyncLimit keepalives behind

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetLagging( ) && m_SyncCounter - (*i)->GetSyncCounter( ) < m_SyncLimit / 2 )
{
// stop the lag screen for this player

CONSOLE_Print( "[GAME: " + m_GameName + "] stopped lagging on [" + (*i)->GetName( ) + "]" );
SendAll( m_Protocol->SEND_W3GS_STOP_LAG( *i ) );
(*i)->SetLagging( false );
(*i)->SetStartedLaggingTicks( 0 );
}
}

// check if everyone has stopped lagging

bool Lagging = false;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetLagging( ) )
Lagging = true;
}

m_Lagging = Lagging;

// reset m_LastActionSentTicks because we want the game to stop running while the lag screen is up

m_LastActionSentTicks = GetTicks( );

// keep track of the last lag screen time so we can avoid timing out players

m_LastLagScreenTime = GetTime( );
}
}

// send actions every m_Latency milliseconds
// actions are at the heart of every Warcraft 3 game but luckily we don't need to know their contents to relay them
// we queue player actions in EventPlayerAction then just resend them in batches to all players here

if (!m_GHost->m_newLatency)
if( m_GameLoaded && !m_Lagging && GetTicks( ) - m_LastActionSentTicks >= m_DynamicLatency )
SendAllActions( );

if (m_GHost->m_newLatency)
if( m_GameLoaded && !m_Lagging && GetTicks( ) - m_LastActionSentTicks >= m_DynamicLatency - m_LastActionLateBy )
SendAllActions( );

// update dynamic latency
if (m_UseDynamicLatency)
SetDynamicLatency();
else
m_DynamicLatency = m_Latency;

// expire the end vote

if( m_EndRequested && GetTicks( ) - m_EndRequestedTicks >= 180*1000 )
{
SendAllChat("End request expired." );
m_EndRequested = false;
m_EndRequestedTicks = 0;
}

// expire the rmk vote

if( !m_RmkVotePlayer.empty( ) && GetTime( ) - m_StartedRmkVoteTime >= 180 )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] rmk started by player [" + m_RmkVotePlayer + "] expired" );
SendAllChat("Rmk vote expired." );
m_RmkVotePlayer.clear( );
m_StartedRmkVoteTime = 0;
}

// expire the votekick

if( !m_KickVotePlayer.empty( ) && GetTime( ) - m_StartedKickVoteTime >= 60 )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] votekick against player [" + m_KickVotePlayer + "] expired" );
SendAllChat( m_GHost->m_Language->VoteKickExpired( m_KickVotePlayer ) );
m_KickVotePlayer.clear( );
m_StartedKickVoteTime = 0;
}

// look through each player's warn count and display it to them if > 0

if (m_GameLoadedTime!=0 && !m_AllPlayersWarnChecked)
if (GetTime()>= m_GameLoadedTime + m_GHost->m_InformAboutWarnsPrintout + m_LastWarnCheck)
{
m_AllPlayersWarnChecked = true;

CGamePlayer *player = NULL;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++)
{
if( (*i)->GetDeleteMe() == false && (*i)->GetWarnChecked() == false )
{
player = *i;
}
}

if( player != NULL )
{
m_LastWarnCheck++;
m_AllPlayersWarnChecked = false;
player->SetWarnChecked(true);

m_WarnCounts.push_back( m_GHost->m_DB->ThreadedWarnCount( player->GetName( ), 1 ) );
}
}

// show game start text
// read from gameloaded.txt if available

if (m_GameLoadedTime!=0 && !m_GameLoadedMessage)
if (GetTime()>=m_GameLoadedTime+m_GHost->m_GameLoadedPrintout)
{
CONSOLE_Print("[GAME: " + m_GameName + "] loading gameloaded.txt");
m_GameLoadedMessage = true;
ifstream inn;
inn.open( "gameloaded.txt" );

if( !inn.fail( ) )
{
// don't print more than 8 lines

uint32_t Count = 0;
string Line;

while( !inn.eof( ) && Count < 8 )
{
getline( inn, Line );

if( Line.empty( ) )
SendAllChat( " " );
else
SendAllChat( Line );

if( inn.eof( ) )
break;

Count++;
}

inn.close( );
} else
CONSOLE_Print("[GAME: " + m_GameName + "] gameloaded.txt load failed");

}

if (GetTime()>=m_GameLoadedTime+m_MsgTime && !m_MsgStop && m_GameLoaded) //Если игра загрузилась, если скрипт еще не прошел полный цикл, если прошло уже больше или равно 180 секунд
{
CONSOLE_Print("[GAME: " + m_GameName + "] typing msg");
m_MsgTime += 180; //Вывод текста через 180 сек
m_MsgInc++;
{
SendAllChat( " ################ " );
SendAllChat( "---= Ghost One by TeKeN =---" );
SendAllChat( " ################ " );
m_MsgStop = true;// Останавливаем выполнение условия в следующий раз (в данной игре)
}
}

// check if switch expired
if (m_SwitchTime!=0)
if (GetTime()-m_SwitchTime>60)
{
m_SwitchTime = 0;
CONSOLE_Print("[GAME: " + m_GameName + "] Switch expired");
}

bool lessthanminpercent = false;
bool lessthanminplayers = false;
float remainingpercent = (float)m_Players.size()*100/(float)m_PlayersatStart;

if (!m_GameOverCanceled)
{
// start the gameover timer if world tree/frozen throne has fallen
if (m_GHost->m_gameoverbasefallen>0 && m_GameOverTime == 0 && m_GameEnded)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (a base has fallen)");
m_GameOverTime = GetTime( )-60+m_GHost->m_gameoverbasefallen;
}

// start the gameover timer if we have less than the minimum percent of players remaining.
if (!m_GameEnded && m_GHost->m_gameoverminpercent!=0 && ( !m_GameLoading && m_GameLoaded ))
if (remainingpercent<m_GHost->m_gameoverminpercent)
lessthanminpercent = true;
if (lessthanminpercent && m_GameOverTime == 0)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (less than "+UTIL_ToString(m_GHost->m_gameoverminpercent)+"% )"+" "+string(1,m_GHost->m_CommandTrigger)+"override to cancel" );
SendAllChat("Game over in 60 seconds, "+ UTIL_ToString(remainingpercent)+"% remaining ( < "+UTIL_ToString(m_GHost->m_gameoverminpercent)+"% ) "+string(1,m_GHost->m_CommandTrigger)+"override to cancel");
m_GameOverTime = GetTime( );
}

// start the gameover timer if there's less than m_gameoverminplayers players left (and we had at least 1 leaver)

if (!m_GameEnded && m_GHost->m_gameoverminplayers!=0)
if (m_Players.size( ) < m_GHost->m_gameoverminplayers && m_PlayersatStart>m_Players.size() && ( !m_GameLoading && m_GameLoaded ) )
lessthanminplayers = true;
if (lessthanminplayers && m_GameOverTime == 0)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (one player left)"+" "+string(1, m_GHost->m_CommandTrigger)+"override to cancel" );
SendAllChat("Game over in 60 seconds, "+ UTIL_ToString(m_Players.size())+" remaining ( < "+UTIL_ToString(m_GHost->m_gameoverminplayers)+" ) "+string(1, m_GHost->m_CommandTrigger)+"override to cancel");
m_GameOverTime = GetTime( );
}

if (!m_GameLoading && m_GameLoaded)
if (!m_GameOverDiffCanceled)
if (!m_GameEnded)
if (!lessthanminplayers && !lessthanminpercent)
if (m_Team1<2 || m_Team2<2)
m_GameOverDiffCanceled = true;


// start the gameover timer if team unbalance is greater than m_gameovermaxteamdifference
// ex: m_gameovermaxteamdifference = 2, if one team has -3, start game over.
if (!m_GameLoading && m_GameLoaded)
if (!m_GameOverDiffCanceled)
if (!m_GameEnded)
if (!m_Switched) // disable gameoverteamdifference if someone has switched
if (!lessthanminplayers && !lessthanminpercent)
if (m_GetMapNumTeams==2 && m_GHost->m_gameovermaxteamdifference!=0 && m_Players.size()<m_PlayersatStart && m_Players.size()>1)
{
bool unbalanced = false;

if (m_TeamDiff > m_GHost->m_gameovermaxteamdifference)
unbalanced = true;

if (m_GameOverTime!=0 && !unbalanced)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer stoped (rebalanced team)" );
SendAllChat("Game over averted, team rebalanced");
m_GameOverTime = 0;
}


if (unbalanced && m_GameOverTime==0)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (unbalanced team)" );
SendAllChat("Game over in 120 seconds, rebalance quickly!, max team difference is "+UTIL_ToString(m_GHost->m_gameovermaxteamdifference)+" "+string(1, m_GHost->m_CommandTrigger)+"override to cancel");
m_GameOverTime = GetTime( )+60;
}
}

// finish the gameover timer

if( m_GameOverTime != 0 && GetTime( ) >= m_GameOverTime + 60 )
{
bool AlreadyStopped = true;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( !(*i)->GetDeleteMe( ) )
{
AlreadyStopped = false;
break;
}
}

if( !AlreadyStopped )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (gameover timer finished)" );
StopPlayers( "was disconnected (gameover timer finished)" );
}
}
}

if ( !m_DownloadOnlyMode)
if( m_GHost->m_LobbyTimeLimit && (m_VirtualHostName!="|cFFC04040Admin") && !m_GameLoading && !m_GameLoaded )
{
// is there a player with reserved status in the game?
if (m_Players.size()>0)
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++)
{
if( (*i)->GetReserved( ) )
{
m_LastReservedSeen = GetTime( );
}
}
//check if we have hit the timelimit
if ( GetTime( ) > m_LastReservedSeen + m_GHost->m_LobbyTimeLimit*60 )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (lobby timelimit hit)" );
for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
{
(*i)->QueueChatCommand( "[GAME: " + m_GameName + "] is over (lobby timelimit hit)" );

if( (*i)->GetServer( ) == GetCreatorServer( ) )
(*i)->QueueChatCommand( "[GAME: " + m_GameName + "] is over (lobby timelimit hit)", GetCreatorName( ), true );
}
m_Exiting = true;
}
}

// censor muted process
ProcessCensorMuted();

// check how many slots are unoccupied and announce if needed
if (m_GHost->m_LobbyAnnounceUnoccupied)
if (!m_GameLoaded && !m_GameLoading && m_AutoStartPlayers==0)
if (GetSlotsOpen()!=m_LastSlotsUnoccupied)
{
m_LastSlotsUnoccupied = GetSlotsOpen();
if (m_LastSlotsUnoccupied==1 || m_LastSlotsUnoccupied==2)
SendAllChat("+"+UTIL_ToString(m_LastSlotsUnoccupied));
}


// check if all slots are no longer occupied
if (!m_GameLoaded && !m_GameLoading)
if (m_AllSlotsOccupied)
if (GetSlotsOpen()!=0)
{
m_AllSlotsOccupied = false;
m_AllSlotsAnnounced = false;
}

// if all slots occupied for 3 seconds, announce in the lobby
if (!m_CountDownStarted && !m_GameLoaded && !m_GameLoading)
if (GetSlotsOpen()==0 && m_AllSlotsOccupied && !m_AllSlotsAnnounced)
if (GetTime()-m_SlotsOccupiedTime>3)
{
m_AllSlotsAnnounced = true;
string Pings;
string Pings2;
uint32_t Ping;
bool samecountry=true;
string CN, CNL;

Pings = "All slots occupied. ";
Pings2 = "All slots occupied. ";

// copy the m_Players vector so we can sort by descending ping so it's easier to find players with high pings

vector<CGamePlayer *> SortedPlayers = m_Players;
sort( SortedPlayers.begin( ), SortedPlayers.end( ), CGamePlayerSortDescByPing( ) );

// string FirstC;

for( vector<CGamePlayer *> :: iterator i = SortedPlayers.begin( ); i != SortedPlayers.end( ); i++ )
{
//Pings += (*i)->GetName( );
//Pings += ": ";
bool skipP;

CN = m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( (*i)->GetExternalIP( ), true ) );
if (CNL=="")
CNL=CN;
else
if (CN!=CNL)
samecountry=false;

if( (*i)->GetNumPings( ) > 0 )
{
Ping=(*i)->GetPing( m_GHost->m_LCPings );
if (Ping>5)
{
skipP = false;
Pings += UTIL_ToString( Ping );
Pings += "ms (";
Pings += CN;
Pings += ")";
Pings2 += UTIL_ToString( Ping );
Pings2 += "ms";
} else
{
skipP = true;
}
}
else
{
skipP = false;
Pings += "N/A (";
Pings += CN;
Pings += ")";
}

if( i != SortedPlayers.end( ) - 1 && !skipP)
{
Pings += ", ";
Pings2 += ", ";
}
}
Pings2 += " are all from ("+CNL+")";

if (samecountry)
SendAllChat( Pings2 );
else
SendAllChat( Pings );
}


// check if we're rehosting

if (m_EndGameTime>0)
if (GetTime() - m_EndGameTime>=3)
{
m_Exiting = true;
m_GHost->newGame = true;
}

// end the game if there aren't any players left

if( m_Players.empty( ) && ( m_GameLoading || m_GameLoaded ) )
{
if( !m_Saving )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (no players left)" );
SaveGameData( );
m_Saving = true;
}
else if( IsGameDataSaved( ) )
return true;
}

// accept new connections

if( m_Socket )
{
CTCPSocket *NewSocket = m_Socket->Accept( (fd_set *)fd );

if( NewSocket )
{
// check the IP blacklist

if( m_IPBlackList.find( NewSocket->GetIPString( ) ) == m_IPBlackList.end( ) )
{
if( m_GHost->m_TCPNoDelay )
NewSocket->SetNoDelay( true );

m_Potentials.push_back( new CPotentialPlayer( m_Protocol, this, NewSocket ) );
}
else
{
CONSOLE_Print( "[GAME: " + m_GameName + "] rejected connection from [" + NewSocket->GetIPString( ) + "] due to blacklist" );
delete NewSocket;
}
}

if( m_Socket->HasError( ) )
return true;
}

return m_Exiting;
}

void CBaseGame :: UpdatePost( void *send_fd )
{
// we need to manually call DoSend on each player now because CGamePlayer :: Update doesn't do it
// this is in case player 2 generates a packet for player 1 during the update but it doesn't get sent because player 1 already finished updating
// in reality since we're queueing actions it might not make a big difference but oh well

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetSocket( ) )
(*i)->GetSocket( )->DoSend( (fd_set *)send_fd );
}

for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); i++ )
{
if( (*i)->GetSocket( ) )
(*i)->GetSocket( )->DoSend( (fd_set *)send_fd );
}
}

void CBaseGame :: Send( CGamePlayer *player, BYTEARRAY data )
{
if( player )
player->Send( data );
}

void CBaseGame :: Send( unsigned char PID, BYTEARRAY data )
{
Send( GetPlayerFromPID( PID ), data );
}

void CBaseGame :: Send( BYTEARRAY PIDs, BYTEARRAY data )
{
for( unsigned int i = 0; i < PIDs.size( ); i++ )
Send( PIDs[i], data );
}

void CBaseGame :: SendAlly( unsigned char PID, BYTEARRAY data )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if (m_Slots[GetSIDFromPID((*i)->GetPID())].GetTeam()==m_Slots[GetSIDFromPID(PID)].GetTeam())
(*i)->Send( data );
}
}

void CBaseGame :: SendAdmin( unsigned char PID, BYTEARRAY data )
{
bool isAdmin = false;
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
isAdmin = IsAdmin((*i)->GetName()) || IsRootAdmin((*i)->GetName()) || IsOwner((*i)->GetName());
if (isAdmin)
(*i)->Send( data );
}
}

void CBaseGame :: SendEnemy( unsigned char PID, BYTEARRAY data )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if (m_Slots[GetSIDFromPID((*i)->GetPID())].GetTeam()!=m_Slots[GetSIDFromPID(PID)].GetTeam())
(*i)->Send( data );
}
}

void CBaseGame :: SendAll( BYTEARRAY data )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
(*i)->Send( data );
}

void CBaseGame :: SendAdmin( BYTEARRAY data )
{
bool isAdmin = false;
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
isAdmin = IsAdmin((*i)->GetName()) || IsRootAdmin((*i)->GetName()) || IsOwner((*i)->GetName());
if (isAdmin)
(*i)->Send( data );
}
}

void CBaseGame :: SendChat( unsigned char fromPID, CGamePlayer *player, string message )
{
// send a private message to one player - it'll be marked [Private] in Warcraft 3

if (m_DetourAllMessagesToAdmins)
if (player)
{
bool isAdmin = IsAdmin(player->GetName()) || IsOwner(player->GetName()) || IsRootAdmin(player->GetName());
if (!isAdmin)
return;
}

if( player )
{
if( !m_GameLoading && !m_GameLoaded )
{
if( message.size( ) > 220 )
message = message.substr( 0, 220 );

Send( player, m_Protocol->SEND_W3GS_CHAT_FROM_HOST( fromPID, UTIL_CreateByteArray( player->GetPID( ) ), 16, BYTEARRAY( ), message ) );
}
else
{
unsigned char ExtraFlags[] = { 3, 0, 0, 0 };

// based on my limited testing it seems that the extra flags' first byte contains 3 plus the recipient's colour to denote a private message

unsigned char SID = GetSIDFromPID( player->GetPID( ) );

if( SID < m_Slots.size( ) )
ExtraFlags[0] = 3 + m_Slots[SID].GetColour( );

if( message.size( ) > 120 )
message = me...
Ответ
Здарва Csandr! Ты бы мог мне помоч у меня brtGHost1.7.1 хотел бы настроить pts статистику! и что для этого понадобится?если нетрудно Помоги!

Добавлено через 10 часов 22 минуты
Сори за флуд! хотел бы настроить pts статистику! и что для этого понадобится?если нетрудно Помогите!
Ответ
[quote=v1rus][CODE]/*

Copyright [2008] [Trevor Hogan]

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

CODE PORTED FROM THE ORIGINAL GHOST PROJECT: http://ghost.pwner.org/

*/

#include "ghost.h"
#include "util.h"
#include "config.h"
#include "language.h"
#include "socket.h"
#include "ghostdb.h"
#include "bnet.h"
#include "map.h"
#include "packed.h"
#include "savegame.h"
#include "replay.h"
#include "gameplayer.h"
#include "gameprotocol.h"
#include "game_base.h"

#include <cmath>
#include <string.h>
#include <time.h>

#include "next_combination.h"

//
// sorting classes
//

class CGamePlayerSortAscByPing
{
public:
bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
{
return Player1->GetPing( false ) < Player2->GetPing( false );
}
};

class CGamePlayerSortDescByPing
{
public:
bool operator( ) ( CGamePlayer *Player1, CGamePlayer *Player2 ) const
{
return Player1->GetPing( false ) > Player2->GetPing( false );
}
};

//
// CBaseGame
//

CBaseGame :: CBaseGame( CGHost *nGHost, CMap *nMap, CSaveGame *nSaveGame, uint16_t nHostPort, unsigned char nGameState, string nGameName, string nOwnerName, string nCreatorName, string nCreatorServer )
{
m_GHost = nGHost;
m_Socket = new CTCPServer( );
m_Protocol = new CGameProtocol( m_GHost );
m_Map = new CMap( *nMap );
m_SaveGame = nSaveGame;

if( m_GHost->m_SaveReplays && !m_SaveGame )
m_Replay = new CReplay( );
else
m_Replay = NULL;

m_Exiting = false;
m_Saving = false;
m_HostPort = nHostPort;
m_GameState = nGameState;
m_VirtualHostPID = 255;
m_FakePlayerPID = 255;
m_WTVPlayerPID = 255;
// wait time of 1 minute = 0 empty actions required
// wait time of 2 minutes = 1 empty action required
// etc...

if( m_GHost->m_ReconnectWaitTime == 0 )
m_GProxyEmptyActions = 0;
else
{
m_GProxyEmptyActions = m_GHost->m_ReconnectWaitTime - 1;

// clamp to 9 empty actions (10 minutes)

if( m_GProxyEmptyActions > 9 )
m_GProxyEmptyActions = 9;
}
m_GameName = nGameName;
wtvprocessid = 0;
AddGameName(m_GameName);
m_OriginalGameName = nGameName;
m_VirtualHostName = m_GHost->m_VirtualHostName;
m_OwnerName = nOwnerName;
m_CreatorName = nCreatorName;
m_CreatorServer = nCreatorServer;
m_HCLCommandString = m_Map->GetMapDefaultHCL( );
m_Server = nCreatorServer;
m_RandomSeed = GetTicks( );
m_GHost->m_HostCounter++;
m_GHost->SaveHostCounter();
if (m_GHost->m_MaxHostCounter>0)
if (m_GHost->m_HostCounter>m_GHost->m_MaxHostCounter)
m_GHost->m_HostCounter = 1;
m_HostCounter = m_GHost->m_HostCounter;
m_Latency = m_GHost->m_Latency;
m_UseDynamicLatency = m_GHost->m_UseDynamicLatency;
m_DynamicLatency = m_Latency;
m_DetourAllMessagesToAdmins = m_GHost->m_DetourAllMessagesToAdmins;
m_NormalCountdown = m_GHost->m_DetourAllMessagesToAdmins;
m_LastDynamicLatencyTicks = 0;
m_LastAdminJoinAndFullTicks = GetTicks();
m_MaxSync = 0;
m_MaxSyncUser = string();
m_SyncLimit = m_GHost->m_SyncLimit;
m_SyncCounter = 0;
m_MaxSyncCounter = 0;
m_GameTicks = 0;
m_CreationTime = GetTime( );
m_LastPingTime = GetTime( );
m_LastRefreshTime = GetTime( );
m_LastDownloadTicks = GetTime( );
m_LastDLCounterTicks = GetTime( );
m_DownloadCounter = 0;
m_DLCounter = 0;
m_LastDownloadCounterResetTicks = GetTicks( );
m_LastAnnounceTime = 0;
m_AnnounceInterval = 0;
m_LastAutoStartTime = GetTime( );
m_AutoStartPlayers = 0;
m_LastCountDownTicks = 0;
m_CountDownCounter = 0;
m_MsgTime = 170;
m_MsgStop = false;
m_StartedLoadingTicks = 0;
m_StartedLoadingTime = 0;
m_StartPlayers = 0;
m_LastLagScreenResetTime = 0;
m_LastActionSentTicks = 0;
m_LastActionLateBy = 0;
m_StartedLaggingTime = 0;
m_LastLagScreenTime = 0;
m_LastReservedSeen = GetTime( );
m_LastActionLateTicks = 0;
m_StartedKickVoteTime = 0;
m_LagScreenTime = 0;
m_GameLoadedTime = 0;
m_GameOverTime = 0;
m_LastPlayerLeaveTicks = 0;
m_MinimumScore = 0.0;
m_MaximumScore = 0.0;
m_SlotInfoChanged = false;
m_Locked = false;
m_RefreshCompleted = false;
m_RefreshMessages = m_GHost->m_RefreshMessages;
m_RefreshError = false;
m_OwnerJoined = false;
m_ShowScoreOf = string();
m_ShowNoteOf = string();
m_DisableStats = false;
m_HCL = false;
m_Listen = false;
m_RootListen = false;
m_MuteAll = false;
m_GameEnded = false;
m_GameEndedTime = 0;
m_GameLoadedMessage = false;
m_AllPlayersWarnChecked = false;
m_LastWarnCheck = 0;
m_MuteLobby = false;
m_CountDownStarted = false;
m_GameEndCountDownStarted = false;
m_GameLoading = false;
m_GameLoaded = false;
m_EndRequested = false;
m_EndRequestedTicks = 0;
if (m_GHost->m_Banning == 1)
m_Bans = true;
else
m_Bans = false;
m_LoadInGame = m_Map->GetMapLoadInGame( );
m_Desynced = false;
m_Lagging = false;
m_EndGameTime = 0;
m_ShowRealSlotCount = m_GHost->m_ShowRealSlotCount;
m_SwitchTime = 0;
m_SwitchNr = 0;
m_Switched = false;
m_CreatorAsFriend = m_GHost->m_addcreatorasfriendonhost;
m_GarenaOnly = false;
m_GameOverCanceled = false;
m_GameOverDiffCanceled = false;
m_CountryCheck = false;
m_CountryCheck2 = false;
m_ProviderCheck = false;
m_ProviderCheck2 = false;
m_ScoreCheck = false;
m_ScoreCheckChecked = false;
m_ScoreCheckScore = 0;
m_ScoreCheckRank = 0;
m_Team1 = 0;
m_Team2 = 0;
m_Team3 = 0;
m_Team4 = 0;
m_TeamDiff = 0;
m_EvenPlayeredTeams = false;
m_BanOn = false;
m_PlayersLeft = 0;
m_Rehost = false;
m_LastPlayerJoined = 255;
m_LastPlayerJoinedTime = GetTime()+5;
m_LastPlayerJoiningTime = GetTime()+5;
m_LastMars = GetTime()-10;
m_LastPlayerWarningTicks = GetTicks();
m_AllSlotsOccupied = false;
m_PingsUpdated = false;
m_AllSlotsAnnounced = false;
m_DownloadOnlyMode = false;
m_AutoSave = m_GHost->m_AutoSave;
m_DoAutoWarns = false;
m_MatchMaking = false;
m_LocalAdminMessages = m_GHost->m_LocalAdminMessages;

if( m_SaveGame )
{
m_EnforceSlots = m_SaveGame->GetSlots( );
m_Slots = m_SaveGame->GetSlots( );

// the savegame slots contain player entries
// we really just want the open/closed/computer entries
// so open all the player slots

for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); i++ )
{
if( (*i).GetSlotStatus( ) == SLOTSTATUS_OCCUPIED && (*i).GetComputer( ) == 0 )
{
(*i).SetPID( 0 );
(*i).SetDownloadStatus( 255 );
(*i).SetSlotStatus( SLOTSTATUS_OPEN );
}
}
}
else
m_Slots = m_Map->GetSlots( );

if( !m_GHost->m_IPBlackListFile.empty( ) )
{
ifstream in;
in.open( m_GHost->m_IPBlackListFile.c_str( ) );

if( in.fail( ) )
CONSOLE_Print( "[GAME: " + m_GameName + "] error loading IP blacklist file [" + m_GHost->m_IPBlackListFile + "]" );
else
{
CONSOLE_Print( "[GAME: " + m_GameName + "] loading IP blacklist file [" + m_GHost->m_IPBlackListFile + "]" );
string Line;

while( !in.eof( ) )
{
getline( in, Line );

// ignore blank lines and comments

if( Line.empty( ) || Line[0] == '#' )
continue;

// remove newlines and partial newlines to help fix issues with Windows formatted files on Linux systems

Line.erase( remove( Line.begin( ), Line.end( ), ' ' ), Line.end( ) );
Line.erase( remove( Line.begin( ), Line.end( ), '\r' ), Line.end( ) );
Line.erase( remove( Line.begin( ), Line.end( ), '\n' ), Line.end( ) );

// ignore lines that don't look like IP addresses

if( Line.find_first_not_of( "1234567890." ) != string :: npos )
continue;

m_IPBlackList.insert( Line );
}

in.close( );

CONSOLE_Print( "[GAME: " + m_GameName + "] loaded " + UTIL_ToString( m_IPBlackList.size( ) ) + " lines from IP blacklist file" );
}
}

m_AutoWarnMarks = m_Map->GetAutoWarnMarks( );

// start listening for connections

if( !m_GHost->m_BindAddress.empty( ) )
CONSOLE_Print( "[GAME: " + m_GameName + "] attempting to bind to address [" + m_GHost->m_BindAddress + "]" );
else
CONSOLE_Print( "[GAME: " + m_GameName + "] attempting to bind to all available addresses" );

if( m_Socket->Listen( m_GHost->m_BindAddress, m_HostPort ) )
CONSOLE_Print( "[GAME: " + m_GameName + "] listening on port " + UTIL_ToString( m_HostPort ) );
else
{
CONSOLE_Print( "[GAME: " + m_GameName + "] error listening on port " + UTIL_ToString( m_HostPort ) );
m_Exiting = true;
}

m_LastReservedSeen = GetTime( );

m_GetMapType = m_Map->GetMapType();
m_GetMapPath = m_Map->GetMapPath();
m_GetMapGameType = m_Map->GetMapGameType();
m_GetMapNumPlayers = m_Map->GetMapNumPlayers();
m_GetMapNumTeams = m_Map->GetMapNumTeams();
m_GetMapOnlyAutoWarnIfMoreThanXPlayers = m_Map->GetMapOnlyAutoWarnIfMoreThanXPlayers();

if (m_GetMapType == "dota" && m_GHost->m_AutoStartDotaGames)
m_AutoStartPlayers = 10;
}

CBaseGame :: ~CBaseGame( )
{
// save replay
// todotodo: put this in a thread

if( m_Replay && ( m_GameLoading || m_GameLoaded ) )
{
time_t Now = time( NULL );
char Time[17];
memset( Time, 0, sizeof( char ) * 17 );
time_t times = Now+ m_GHost->m_ReplayTimeShift;
strftime( Time, sizeof( char ) * 17, "%Y-%m-%d %H-%M", localtime( &times ) );
string MinString = UTIL_ToString( ( m_GameTicks / 1000 ) / 60 );
string SecString = UTIL_ToString( ( m_GameTicks / 1000 ) % 60 );

if( MinString.size( ) == 1 )
MinString.insert( 0, "0" );

if( SecString.size( ) == 1 )
SecString.insert( 0, "0" );

m_Replay->BuildReplay( m_GameName, m_StatString, m_GHost->m_ReplayWar3Version, m_GHost->m_ReplayBuildNumber );
m_Replay->Save( m_GHost->m_TFT, m_GHost->m_ReplayPath + UTIL_FileSafeName( "GHost++ " + string( Time ) + " " + m_GameName + ".w3g" ) );
}

delete m_Socket;
delete m_Protocol;
delete m_Map;
delete m_Replay;

for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); i++ )
delete *i;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
delete *i;

for( vector<CCallableScoreCheck *> :: iterator i = m_ScoreChecks.begin( ); i != m_ScoreChecks.end( ); i++ )
m_GHost->m_Callables.push_back( *i );

for( vector<CCallableWarnCount *> :: iterator i = m_WarnCounts.begin( ); i != m_WarnCounts.end( ); i++ )
m_GHost->m_Callables.push_back( *i );

for( vector<CCallableBanUpdate *> :: iterator i = m_BanUpdates.begin( ); i != m_BanUpdates.end( ); i++ )
m_GHost->m_Callables.push_back( *i );

for( vector<CCallableRunQuery *> :: iterator i = m_RunQueries.begin( ); i != m_RunQueries.end( ); i++ )
m_GHost->m_Callables.push_back( *i );

while( !m_Actions.empty( ) )
{
delete m_Actions.front( );
m_Actions.pop( );
}

m_GameNames.clear();
}

uint32_t CBaseGame :: GetNextTimedActionTicks( )
{
// return the number of ticks (ms) until the next "timed action", which for our purposes is the next game update
// the main GHost++ loop will make sure the next loop update happens at or before this value
// note: there's no reason this function couldn't take into account the game's other timers too but they're far less critical
// warning: this function must take into account when actions are not being sent (e.g. during loading or lagging)

if( !m_GameLoaded || m_Lagging )
return 50;

uint32_t TicksSinceLastUpdate = GetTicks( ) - m_LastActionSentTicks;

if( TicksSinceLastUpdate > m_DynamicLatency - m_LastActionLateBy )
return 0;
else
return m_DynamicLatency - m_LastActionLateBy - TicksSinceLastUpdate;
}

uint32_t CBaseGame :: GetSlotsOccupied( )
{
uint32_t NumSlotsOccupied = 0;

for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); i++ )
{
if( (*i).GetSlotStatus( ) == SLOTSTATUS_OCCUPIED )
NumSlotsOccupied++;
}

return NumSlotsOccupied;
}

uint32_t CBaseGame :: GetSlotsOpen( )
{
uint32_t NumSlotsOpen = 0;

for( vector<CGameSlot> :: iterator i = m_Slots.begin( ); i != m_Slots.end( ); i++ )
{
if( (*i).GetSlotStatus( ) == SLOTSTATUS_OPEN )
NumSlotsOpen++;
}

return NumSlotsOpen;
}

uint32_t CBaseGame :: GetNumPlayers( )
{
uint32_t NumPlayers = GetNumHumanPlayers( );

if( m_FakePlayerPID != 255 )
NumPlayers++;

return NumPlayers;
}

uint32_t CBaseGame :: GetNumHumanPlayers( )
{
uint32_t NumHumanPlayers = 0;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( !(*i)->GetLeftMessageSent( ) )
NumHumanPlayers++;
}

return NumHumanPlayers;
}

string CBaseGame :: GetDescription( )
{
string Description = m_GameName + " : " + m_OwnerName + " : " + UTIL_ToString( GetNumHumanPlayers( ) ) + "/" + UTIL_ToString( m_GameLoading || m_GameLoaded ? m_StartPlayers : m_Slots.size( ) );

if( m_GameLoading || m_GameLoaded )
Description += " : " + UTIL_ToString( ( m_GameTicks / 1000 ) / 60 ) + "m";
else
Description += " : " + UTIL_ToString( ( GetTime( ) - m_CreationTime ) / 60 ) + "m";

return Description;
}

void CBaseGame :: SetAnnounce( uint32_t interval, string message )
{
m_AnnounceInterval = interval;
m_AnnounceMessage = message;
m_LastAnnounceTime = GetTime( );
}

unsigned int CBaseGame :: SetFD( void *fd, void *send_fd, int *nfds )
{
unsigned int NumFDs = 0;

if( m_Socket )
{
m_Socket->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
NumFDs++;
}

for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); i++ )
{
if( (*i)->GetSocket( ) )
{
(*i)->GetSocket( )->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
NumFDs++;
}
}

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetSocket( ) )
{
(*i)->GetSocket( )->SetFD( (fd_set *)fd, (fd_set *)send_fd, nfds );
NumFDs++;
}
}

return NumFDs;
}

bool CBaseGame :: Update( void *fd, void *send_fd )
{
// update callables

for( vector<CCallableWarnCount *> :: iterator i = m_WarnCounts.begin( ); i != m_WarnCounts.end( ); )
{
if( (*i)->GetReady( ) )
{
uint32_t Count = (*i)->GetResult( );
CGamePlayer * Player = GetPlayerFromName((*i)->GetName(), false);

if (Player)
{
// count warns
if (Count > 0)
{
SendChat( Player, m_GHost->m_Language->DisplayWarnsCount( UTIL_ToString(Count) ) );
}
}

m_GHost->m_DB->RecoverCallable( *i );
delete *i;
i = m_WarnCounts.erase( i );
}
else
i++;
}

for( vector<CCallableBanUpdate *> :: iterator i = m_BanUpdates.begin( ); i != m_BanUpdates.end( ); )
{
if( (*i)->GetReady( ) )
{
m_GHost->m_DB->RecoverCallable( *i );
delete *i;
i = m_BanUpdates.erase( i );
}
else
i++;
}

for( vector<CCallableRunQuery *> :: iterator i = m_RunQueries.begin( ); i != m_RunQueries.end( ); )
{
if( (*i)->GetReady( ) )
{
m_GHost->m_DB->RecoverCallable( *i );
delete *i;
i = m_RunQueries.erase( i );
}
else
i++;
}

for( vector<CCallableScoreCheck *> :: iterator i = m_ScoreChecks.begin( ); i != m_ScoreChecks.end( ); )
{
if( (*i)->GetReady( ) )
{
double Score = (*i)->GetResult( );

CGamePlayer * Player = GetPlayerFromName((*i)->GetName( ), false);
if (Player)
{
Player->SetScoreS(UTIL_ToString(Score,2));
Player->SetRankS(UTIL_ToString((*i)->GetRank( )));
Player->SetScore(Score);
}

for( vector<CPotentialPlayer *> :: iterator j = m_Potentials.begin( ); j != m_Potentials.end( ); j++ )
{
if( (*j)->GetJoinPlayer( ) && (*j)->GetJoinPlayer( )->GetName( ) == (*i)->GetName( ) )
{
if (m_MatchMaking && m_AutoStartPlayers != 0)
EventPlayerJoinedWithScore( *j, (*j)->GetJoinPlayer( ), Score );
else
if (m_ScoreCheck)
{
m_ScoreCheckChecked = true;
m_ScoreCheckScore = Score;
m_ScoreCheckRank = (*i)->GetRank( );
EventPlayerJoined(*j, (*j)->GetJoinPlayer( ));
m_ScoreCheckChecked = false;
}
}
}

m_GHost->m_DB->RecoverCallable( *i );
delete *i;
i = m_ScoreChecks.erase( i );
}
else
i++;
}

// update players

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); )
{
if( (*i)->Update( fd ) )
{
EventPlayerDeleted( *i );
delete *i;
i = m_Players.erase( i );
}
else
i++;
}

for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); )
{
if( (*i)->Update( fd ) )
{
// flush the socket (e.g. in case a rejection message is queued)

if( (*i)->GetSocket( ) )
(*i)->GetSocket( )->DoSend( (fd_set *)send_fd );

delete *i;
i = m_Potentials.erase( i );
}
else
i++;
}

// create the virtual host player

// if( !m_GameLoading && !m_GameLoaded && GetNumPlayers( ) < 12 )
uint32_t SlotReq = 12;
if (m_ShowRealSlotCount)
SlotReq = m_Slots.size();
if( !m_GameLoading && !m_GameLoaded && GetNumPlayers( ) < SlotReq && !m_GHost->m_DetourAllMessagesToAdmins )
CreateVirtualHost( );

// unlock the game

if( m_Locked && !GetPlayerFromName( m_OwnerName, false ) )
{
SendAllChat( m_GHost->m_Language->GameUnlocked( ) );
m_Locked = false;
}

// ping every 5 seconds
// changed this to ping during game loading as well to hopefully fix some problems with people disconnecting during loading
// changed this to ping during the game as well

if( GetTime( ) - m_LastPingTime >= 5 )
{
// note: we must send pings to players who are downloading the map because Warcraft III disconnects from the lobby if it doesn't receive a ping every ~90 seconds
// so if the player takes longer than 90 seconds to download the map they would be disconnected unless we keep sending pings
// todotodo: ignore pings received from players who have recently finished downloading the map

SendAll( m_Protocol->SEND_W3GS_PING_FROM_HOST( ) );

// we also broadcast the game to the local network every 5 seconds so we hijack this timer for our nefarious purposes
// however we only want to broadcast if the countdown hasn't started
// see the !sendlan code later in this file for some more information about how this works
// todotodo: should we send a game cancel message somewhere? we'll need to implement a host counter for it to work

if( !m_CountDownStarted )
{
BYTEARRAY MapGameType;

// construct a fixed host counter which will be used to identify players from this "realm" (i.e. LAN)
// the fixed host counter's 4 most significant bits will contain a 4 bit ID (0-15)
// the rest of the fixed host counter will contain the 28 least significant bits of the actual host counter
// since we're destroying 4 bits of information here the actual host counter should not be greater than 2^28 which is a reasonable assumption
// when a player joins a game we can obtain the ID from the received host counter
// note: LAN broadcasts use an ID of 0, battle.net refreshes use an ID of 1-10, the rest are unused

uint32_t FixedHostCounter = m_HostCounter & 0x0FFFFFFF;

// construct the correct W3GS_GAMEINFO packet

uint32_t slotstotal = m_Slots.size( );
uint32_t slotsopen = GetSlotsOpen();
if (slotsopen<2) slotsopen = 2;

if (!m_ShowRealSlotCount)
{
slotsopen = 12;
slotstotal = 12;
}

if( m_SaveGame )
{
MapGameType.push_back( 0 );
MapGameType.push_back( 2 );
MapGameType.push_back( 0 );
MapGameType.push_back( 0 );
BYTEARRAY MapWidth;
MapWidth.push_back( 0 );
MapWidth.push_back( 0 );
BYTEARRAY MapHeight;
MapHeight.push_back( 0 );
MapHeight.push_back( 0 );
if (m_GHost->m_broadcastinlan)
{
m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
m_GHost->m_UDPSocket->SendTo("127.0.0.1", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
}
// if (m_GarenaOnly)
{
//m_GHost->m_UDPSocket->SendTo("127.0.0.1", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), 12, 12, m_HostPort, FixedHostCounter ) );
}
m_GHost->UDPChatSend(m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), MapWidth, MapHeight, m_GameName, "Varlock", GetTime( ) - m_CreationTime, "Save\\Multiplayer\\" + m_SaveGame->GetFileNameNoPath( ), m_SaveGame->GetMagicNumber( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
}
else
{
MapGameType.push_back( m_Map->GetMapGameType( ) );
MapGameType.push_back( 0 );
MapGameType.push_back( 0 );
MapGameType.push_back( 0 );
if (m_GHost->m_broadcastinlan)
{
m_GHost->m_UDPSocket->Broadcast( 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
m_GHost->m_UDPSocket->SendTo( "127.0.0.1", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, m_Map->GetMapGameFlags( ), m_Map->GetMapWidth( ), m_Map->GetMapHeight( ), m_GameName, "Varlock", GetTime( ) - m_CreationTime, m_Map->GetMapPath( ), m_Map->GetMapCRC( ), slotstotal, slotsopen, m_HostPort, FixedHostCounter ) );
}
// if (m_GarenaOnly)
{
// m_GHost->m_UDPSocket->SendTo( "127.0.0.1", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_LANWar3Version, MapGameType, 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, FixedHostCounter ) );
// m_GHost->m_UDPSocket->SendTo( "192.168.1.2", 6112, m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_LANWar3Version, MapGameType, 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, FixedHostCounter ) );
}
m_GHost->UDPChatSend(m_Protocol->SEND_W3GS_GAMEINFO( m_GHost->m_TFT, m_GHost->m_LANWar3Version, MapGameType, 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, FixedHostCounter ));
}
}

m_LastPingTime = GetTime( );
}

// refresh every 3 seconds

bool m_AutoHostRefresh = (m_autohosted && m_GHost->m_AutoHostLocal);
// don't refresh if we're autohosting locally only
if (!m_AutoHostRefresh )
if( !m_RefreshError && !m_CountDownStarted && m_GameState == GAME_PUBLIC && GetSlotsOpen( ) > 0 && GetTime( )- m_LastRefreshTime >= 3 )
{
// send a game refresh packet to each battle.net connection

bool Refreshed = false;

uint32_t slotstotal = m_Slots.size( );
uint32_t slotsopen = GetSlotsOpen();

for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
{
// don't queue a game refresh message if the queue contains more than 1 packet because they're very low priority

if( (*i)->GetOutPacketsQueued( ) <= 1 )
{
(*i)->QueueGameRefresh( m_GameState, m_GameName, string( ), m_Map, m_SaveGame, GetTime( ) - m_CreationTime, m_HostCounter, slotstotal, slotsopen );
Refreshed = true;
}
}

// only print the "game refreshed" message if we actually refreshed on at least one battle.net server

if( m_RefreshMessages && Refreshed )
SendAllChat( m_GHost->m_Language->GameRefreshed( ) );

m_LastRefreshTime = GetTime( );
}

// send more map data

if( !m_GameLoading && !m_GameLoaded && GetTicks( ) - m_LastDownloadCounterResetTicks >= 1000 )
{
// hackhack: another timer hijack is in progress here
// since the download counter is reset once per second it's a great place to update the slot info if necessary

if( m_SlotInfoChanged )
SendAllSlotInfo( );

// m_DownloadCounter = 0;
m_LastDownloadCounterResetTicks = GetTicks( );
}

/*
if( !m_GameLoading && !m_GameLoaded && GetTicks( ) - m_LastDownloadTicks > 100 )
{
uint32_t Downloaders = 0;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetDownloadStarted( ) && !(*i)->GetDownloadFinished( ) )
{
Downloaders++;

if( m_GHost->m_MaxDownloaders > 0 && Downloaders > m_GHost->m_MaxDownloaders )
break;

// send up to 100 pieces of the map at once so that the download goes faster
// if we wait for each MAPPART packet to be acknowledged by the client it'll take a long time to download
// this is because we would have to wait the round trip time (the ping time) between sending every 1442 bytes of map data
// doing it this way allows us to send at least 140 KB in each round trip interval which is much more reasonable
// the theoretical throughput is [140 KB * 1000 / ping] in KB/sec so someone with 100 ping (round trip ping, not LC ping) could download at 1400 KB/sec
// note: this creates a queue of map data which clogs up the connection when the client is on a slower connection (e.g. dialup)
// in this case any changes to the lobby are delayed by the amount of time it takes to send the queued data (i.e. 140 KB, which could be 30 seconds or more)
// for example, players joining and leaving, slot changes, chat messages would all appear to happen much later for the low bandwidth player
// note: the throughput is also limited by the number of times this code is executed each second
// e.g. if we send the maximum amount (140 KB) 10 times per second the theoretical throughput is 1400 KB/sec
// therefore the maximum throughput is 1400 KB/sec regardless of ping and this value slowly diminishes as the player's ping increases
// in addition to this, the throughput is limited by the configuration value bot_maxdownloadspeed
// in summary: the actual throughput is MIN( 140 * 1000 / ping, 1400, bot_maxdownloadspeed ) in KB/sec assuming only one player is downloading the map

uint32_t MapSize = UTIL_ByteArrayToUInt32( m_Map->GetMapSize( ), false );

while( (*i)->GetLastMapPartSent( ) < (*i)->GetLastMapPartAcked( ) + 1442 * 100 && (*i)->GetLastMapPartSent( ) < MapSize )
{
if( (*i)->GetLastMapPartSent( ) == 0 )
{
// overwrite the "started download ticks" since this is the first time we've sent any map data to the player
// prior to this we've only determined if the player needs to download the map but it's possible we could have delayed sending any data due to download limits

(*i)->SetStartedDownloadingTicks( GetTicks( ) );
}

// limit the download speed if we're sending too much data
// the download counter is the # of map bytes downloaded in the last second (it's reset once per second)

if( m_GHost->m_MaxDownloadSpeed > 0 && m_DownloadCounter > m_GHost->m_MaxDownloadSpeed * 1024 )
break;

Send( *i, m_Protocol->SEND_W3GS_MAPPART( GetHostPID( ), (*i)->GetPID( ), (*i)->GetLastMapPartSent( ), m_Map->GetMapData( ) ) );
(*i)->SetLastMapPartSent( (*i)->GetLastMapPartSent( ) + 1442 );
m_DownloadCounter += 1442;
}
}
}

m_LastDownloadTicks = GetTicks( );
}
*/
// announce every m_AnnounceInterval seconds

if( !m_AnnounceMessage.empty( ) && !m_CountDownStarted && GetTime( ) - m_LastAnnounceTime >= m_AnnounceInterval )
{
{
string msg = m_AnnounceMessage;
string msgl = string();
string :: size_type lstart;
while ((lstart = msg.find("|"))!=string :: npos)
{
msgl = msg.substr(0,lstart);
msg = msg.substr(lstart+1,msg.length()-lstart-1);
SendAllChat(msgl);
}
SendAllChat(msg);
}
m_LastAnnounceTime = GetTime( );
}

// kick players who don't spoof check within 20 seconds when spoof checks are required and the game is autohosted

if( !m_CountDownStarted && m_GHost->m_RequireSpoofChecks && m_GameState == GAME_PUBLIC && !m_GHost->m_AutoHostGameName.empty( ) && m_GHost->m_AutoHostMaximumGames != 0 && m_GHost->m_AutoHostAutoStartPlayers != 0 && m_AutoStartPlayers != 0 )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( !(*i)->GetSpoofed( ) && GetTime( ) - (*i)->GetJoinTime( ) >= 20 )
{
(*i)->SetDeleteMe( true );
(*i)->SetLeftReason( m_GHost->m_Language->WasKickedForNotSpoofChecking( ) );
(*i)->SetLeftCode( PLAYERLEAVE_LOBBY );
OpenSlot( GetSIDFromPID( (*i)->GetPID( ) ), false );
}
}
}

// try to auto start every 10 seconds

if( !m_CountDownStarted && m_AutoStartPlayers != 0 && GetTime( ) - m_LastAutoStartTime >= 10 )
{
StartCountDownAuto( m_GHost->m_RequireSpoofChecks );
m_LastAutoStartTime = GetTime( );
}

// end game countdown every 1000 ms
if( m_GameEndCountDownStarted && GetTicks( ) - m_GameEndLastCountDownTicks >= 1000 )
{
if( m_GameEndCountDownCounter > 0 )
{
// we use a countdown counter rather than a "finish countdown time" here because it might alternately round up or down the count
// this sometimes resulted in a countdown of e.g. "6 5 3 2 1" during my testing which looks pretty dumb
// doing it this way ensures it's always "5 4 3 2 1" but each interval might not be *exactly* the same length

SendAllChat( UTIL_ToString( m_GameEndCountDownCounter ) + ". . ." );
m_GameEndCountDownCounter--;
}
else if( !m_GameLoading && m_GameLoaded )
{
m_GameEndCountDownStarted = false;
StopPlayers( "was disconnected (admin ended game)" );
}

m_GameEndLastCountDownTicks = GetTicks( );
}
// countdown every 500 ms

uint32_t waittime = 500;
if (m_NormalCountdown)
waittime = 1200;

if( m_CountDownStarted && GetTicks( ) - m_LastCountDownTicks >= waittime )
{
if( m_CountDownCounter > 0 )
{
// we use a countdown counter rather than a "finish countdown time" here because it might alternately round up or down the count
// this sometimes resulted in a countdown of e.g. "6 5 3 2 1" during my testing which looks pretty dumb
// doing it this way ensures it's always "5 4 3 2 1" but each interval might not be *exactly* the same length

if (!m_NormalCountdown)
SendAllChat( UTIL_ToString( m_CountDownCounter ) + ". . ." );
m_CountDownCounter--;
}
else if( !m_GameLoading && !m_GameLoaded )
EventGameStarted( );

m_LastCountDownTicks = GetTicks( );
}

// update pings in the GUI
if (GetTime()>m_CreationTime+5 && !m_PingsUpdated)
if (GetTime()>m_LastPlayerJoinedTime+3 && !m_GameLoading && !m_GameLoaded)
{
m_PingsUpdated = true;
m_GHost->UDPChatSend("|lobbyupdate");
}

// check if no one has joined the game for a long time (25 sec default) and rehost
bool LoggedIn = false;
for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
{
if ((*i)->GetServer()==m_Server)
LoggedIn = (*i)->GetLoggedIn();
// the game creation message will be sent on the next refresh
}

if (!(m_autohosted && m_GHost->m_AutoHostLocal))
if (!m_DownloadOnlyMode)
if (m_VirtualHostName!="|cFFC04040Admin")
if (m_GameState==GAME_PUBLIC)
if (m_GHost->m_AutoRehostDelay!=0)
// if (LoggedIn)
if (GetTime()>m_CreationTime+5)
if (GetTime()>m_LastPlayerJoiningTime+m_GHost->m_AutoRehostDelay && !m_GameLoading && !m_GameLoaded && !m_AllSlotsOccupied)
{
/* if (m_AutoStartPlayers!=0)
m_GHost->m_HostCounter++;*/
ReCalculateTeams();
m_GameName = m_GHost->IncGameNr(m_GameName);
m_GHost->m_HostCounter++;
m_GHost->SaveHostCounter();
if (m_GHost->m_MaxHostCounter>0)
if (m_GHost->m_HostCounter>m_GHost->m_MaxHostCounter)
m_GHost->m_HostCounter = 1;
m_HostCounter = m_GHost->m_HostCounter;
m_GHost->m_QuietRehost = true;
m_RefreshError = false;
m_Rehost = true;
AddGameName(m_GameName);
for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
{
(*i)->QueueGameUncreate( );
(*i)->QueueEnterChat( );

// the game creation message will be sent on the next refresh
}
m_CreationTime = GetTime( );
m_LastRefreshTime = GetTime( );
m_LastPlayerJoinedTime = GetTime( )+5;
m_LastPlayerJoiningTime = GetTime( )+5;
}

// check if the lobby is "abandoned" and needs to be closed since it will never start

if(m_VirtualHostName!="|cFFC04040Admin" && !m_GameLoading && !m_GameLoaded && m_AutoStartPlayers == 0 && m_GHost->m_LobbyTimeLimitMax > 0 )
{
// check if we've hit the time limit

if( GetTime( ) - m_CreationTime >= m_GHost->m_LobbyTimeLimitMax * 60 && !m_DownloadOnlyMode )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (lobby time limit hit)" );
return true;
}
}

// check if the lobby is "abandoned" and needs to be closed since it will never start

if( !m_GameLoading && !m_GameLoaded && (m_AutoStartPlayers == 0) && m_GHost->m_LobbyTimeLimit > 0 )
{
// check if there's a player with reserved status in the game

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if (m_AutoStartPlayers == 0) {

if( (*i)->GetReserved( ) )
m_LastReservedSeen = GetTime( );

} else {

// If autostart is on, and there is at least one player, the above for loop will run.
m_LastReservedSeen = GetTime( );
break;

}

// if( (*i)->GetReserved( ) )
// m_LastReservedSeen = GetTime( );
}

// check if we've hit the time limit

if( GetTime( ) - m_LastReservedSeen >= m_GHost->m_LobbyTimeLimit * 60 && !m_DownloadOnlyMode )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (lobby time limit hit)" );
return true;
}
}

// check if the game is loaded

if( m_GameLoading )
{
bool FinishedLoading = true;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
FinishedLoading = (*i)->GetFinishedLoading( );

if( !FinishedLoading )
break;
}

if( FinishedLoading )
{
m_LastActionSentTicks = GetTicks( );
m_GameLoading = false;
m_GameLoaded = true;
EventGameLoaded( );
}
else
{
// reset the "lag" screen (the load-in-game screen) every 30 seconds

if( m_LoadInGame && GetTime( ) - m_LastLagScreenResetTime >= 30 )
{
bool UsingGProxy = false;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetGProxy( ) )
UsingGProxy = true;
}

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetFinishedLoading( ) )
{
// stop the lag screen

for( vector<CGamePlayer *> :: iterator j = m_Players.begin( ); j != m_Players.end( ); j++ )
{
if( !(*j)->GetFinishedLoading( ) )
Send( *i, m_Protocol->SEND_W3GS_STOP_LAG( *j, true ) );
}

// send an empty update
// this resets the lag screen timer but creates a rather annoying problem
// in order to prevent a desync we must make sure every player receives the exact same "desyncable game data" (updates and player leaves) in the exact same order
// unfortunately we cannot send updates to players who are still loading the map, so we buffer the updates to those players (see the else clause a few lines down for the code)
// in addition to this we must ensure any player leave messages are sent in the exact same position relative to these updates so those must be buffered too

if( UsingGProxy && !(*i)->GetGProxy( ) )
{
// we must send empty actions to non-GProxy++ players
// GProxy++ will insert these itself so we don't need to send them to GProxy++ players
// empty actions are used to extend the time a player can use when reconnecting

for( unsigned char j = 0; j < m_GProxyEmptyActions; j++ )
Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
}

Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );

// start the lag screen

Send( *i, m_Protocol->SEND_W3GS_START_LAG( m_Players, true ) );
}
else
{
// buffer the empty update since the player is still loading the map

if( UsingGProxy && !(*i)->GetGProxy( ) )
{
// we must send empty actions to non-GProxy++ players
// GProxy++ will insert these itself so we don't need to send them to GProxy++ players
// empty actions are used to extend the time a player can use when reconnecting

for( unsigned char j = 0; j < m_GProxyEmptyActions; j++ )
(*i)->AddLoadInGameData( m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
}

(*i)->AddLoadInGameData( m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
}
}

// add actions to replay

if( m_Replay )
{
if( UsingGProxy )
{
for( unsigned char i = 0; i < m_GProxyEmptyActions; i++ )
m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
}

m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
}

// Warcraft III doesn't seem to respond to empty actions

/* if( UsingGProxy )
m_SyncCounter += m_GProxyEmptyActions;

m_SyncCounter++; */
m_LastLagScreenResetTime = GetTime( );
}
}
}

// keep track of the largest sync counter (the number of keepalive packets received by each player)
// if anyone falls behind by more than m_SyncLimit keepalives we start the lag screen

if( m_GameLoaded )
{
// calculate the largest sync counter

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetSyncCounter( ) > m_MaxSyncCounter )
m_MaxSyncCounter = (*i)->GetSyncCounter( );
}

// check if anyone has started lagging
// we consider a player to have started lagging if they're more than m_SyncLimit keepalives behind

if( !m_Lagging )
{
string LaggingString;

m_MaxSync = 0;
uint32_t Sync = 0;
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
Sync = m_SyncCounter - (*i)->GetSyncCounter( );
if (Sync > m_MaxSync)
{
m_MaxSync = Sync;
m_MaxSyncUser = (*i)->GetName();
}
if( Sync > m_SyncLimit )
{
(*i)->SetLagging( true );
// calculate drop vote ticks
uint32_t d = 5;
if (m_GHost->m_DropVoteTime<45)
d = (45 * 1000)-m_GHost->m_DropVoteTime*1000 ;

(*i)->SetStartedLaggingTicks( GetTicks( )-d );
m_Lagging = true;
m_StartedLaggingTime = GetTime( );

if( LaggingString.empty( ) )
LaggingString = (*i)->GetName( );
else
LaggingString += ", " + (*i)->GetName( );
}
}

if( m_Lagging )
{
// start the lag screen

CONSOLE_Print( "[GAME: " + m_GameName + "] started lagging on [" + LaggingString + "]" );
SendAll( m_Protocol->SEND_W3GS_START_LAG( m_Players ) );

// reset everyone's drop vote

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
(*i)->SetDropVote( false );

m_LastLagScreenResetTime = GetTime( );

// get current time to use for the drop vote

m_LagScreenTime = GetTime();
}
}

if( m_Lagging )
{
bool UsingGProxy = false;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetGProxy( ) )
UsingGProxy = true;
}

uint32_t WaitTime = 60;

if( UsingGProxy )
WaitTime = ( m_GProxyEmptyActions + 1 ) * 60;

if( GetTime( ) - m_StartedLaggingTime >= WaitTime )
StopLaggers( m_GHost->m_Language->WasAutomaticallyDroppedAfterSeconds( UTIL_ToString( WaitTime ) ) );

// we cannot allow the lag screen to stay up for more than ~65 seconds because Warcraft III disconnects if it doesn't receive an action packet at least this often
// one (easy) solution is to simply drop all the laggers if they lag for more than 60 seconds
// another solution is to reset the lag screen the same way we reset it when using load-in-game
// this is required in order to give GProxy++ clients more time to reconnect

if( GetTime( ) - m_LastLagScreenResetTime >= 60 )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
// stop the lag screen

for( vector<CGamePlayer *> :: iterator j = m_Players.begin( ); j != m_Players.end( ); j++ )
{
if( (*j)->GetLagging( ) )
Send( *i, m_Protocol->SEND_W3GS_STOP_LAG( *j ) );
}

// send an empty update
// this resets the lag screen timer

if( UsingGProxy && !(*i)->GetGProxy( ) )
{
// we must send additional empty actions to non-GProxy++ players
// GProxy++ will insert these itself so we don't need to send them to GProxy++ players
// empty actions are used to extend the time a player can use when reconnecting

for( unsigned char j = 0; j < m_GProxyEmptyActions; j++ )
Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );
}

Send( *i, m_Protocol->SEND_W3GS_INCOMING_ACTION( queue<CIncomingAction *>( ), 0 ) );

// start the lag screen

Send( *i, m_Protocol->SEND_W3GS_START_LAG( m_Players ) );
}

// add actions to replay

if( m_Replay )
{
if( UsingGProxy )
{
for( unsigned char i = 0; i < m_GProxyEmptyActions; i++ )
m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
}

m_Replay->AddTimeSlot( 0, queue<CIncomingAction *>( ) );
}

// Warcraft III doesn't seem to respond to empty actions

/* if( UsingGProxy )
m_SyncCounter += m_GProxyEmptyActions;

m_SyncCounter++; */
m_LastLagScreenResetTime = GetTime( );
}

// check if anyone has stopped lagging normally
// we consider a player to have stopped lagging if they're less than half m_SyncLimit keepalives behind

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetLagging( ) && m_SyncCounter - (*i)->GetSyncCounter( ) < m_SyncLimit / 2 )
{
// stop the lag screen for this player

CONSOLE_Print( "[GAME: " + m_GameName + "] stopped lagging on [" + (*i)->GetName( ) + "]" );
SendAll( m_Protocol->SEND_W3GS_STOP_LAG( *i ) );
(*i)->SetLagging( false );
(*i)->SetStartedLaggingTicks( 0 );
}
}

// check if everyone has stopped lagging

bool Lagging = false;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetLagging( ) )
Lagging = true;
}

m_Lagging = Lagging;

// reset m_LastActionSentTicks because we want the game to stop running while the lag screen is up

m_LastActionSentTicks = GetTicks( );

// keep track of the last lag screen time so we can avoid timing out players

m_LastLagScreenTime = GetTime( );
}
}

// send actions every m_Latency milliseconds
// actions are at the heart of every Warcraft 3 game but luckily we don't need to know their contents to relay them
// we queue player actions in EventPlayerAction then just resend them in batches to all players here

if (!m_GHost->m_newLatency)
if( m_GameLoaded && !m_Lagging && GetTicks( ) - m_LastActionSentTicks >= m_DynamicLatency )
SendAllActions( );

if (m_GHost->m_newLatency)
if( m_GameLoaded && !m_Lagging && GetTicks( ) - m_LastActionSentTicks >= m_DynamicLatency - m_LastActionLateBy )
SendAllActions( );

// update dynamic latency
if (m_UseDynamicLatency)
SetDynamicLatency();
else
m_DynamicLatency = m_Latency;

// expire the end vote

if( m_EndRequested && GetTicks( ) - m_EndRequestedTicks >= 180*1000 )
{
SendAllChat("End request expired." );
m_EndRequested = false;
m_EndRequestedTicks = 0;
}

// expire the rmk vote

if( !m_RmkVotePlayer.empty( ) && GetTime( ) - m_StartedRmkVoteTime >= 180 )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] rmk started by player [" + m_RmkVotePlayer + "] expired" );
SendAllChat("Rmk vote expired." );
m_RmkVotePlayer.clear( );
m_StartedRmkVoteTime = 0;
}

// expire the votekick

if( !m_KickVotePlayer.empty( ) && GetTime( ) - m_StartedKickVoteTime >= 60 )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] votekick against player [" + m_KickVotePlayer + "] expired" );
SendAllChat( m_GHost->m_Language->VoteKickExpired( m_KickVotePlayer ) );
m_KickVotePlayer.clear( );
m_StartedKickVoteTime = 0;
}

// look through each player's warn count and display it to them if > 0

if (m_GameLoadedTime!=0 && !m_AllPlayersWarnChecked)
if (GetTime()>= m_GameLoadedTime + m_GHost->m_InformAboutWarnsPrintout + m_LastWarnCheck)
{
m_AllPlayersWarnChecked = true;

CGamePlayer *player = NULL;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++)
{
if( (*i)->GetDeleteMe() == false && (*i)->GetWarnChecked() == false )
{
player = *i;
}
}

if( player != NULL )
{
m_LastWarnCheck++;
m_AllPlayersWarnChecked = false;
player->SetWarnChecked(true);

m_WarnCounts.push_back( m_GHost->m_DB->ThreadedWarnCount( player->GetName( ), 1 ) );
}
}

// show game start text
// read from gameloaded.txt if available

if (m_GameLoadedTime!=0 && !m_GameLoadedMessage)
if (GetTime()>=m_GameLoadedTime+m_GHost->m_GameLoadedPrintout)
{
CONSOLE_Print("[GAME: " + m_GameName + "] loading gameloaded.txt");
m_GameLoadedMessage = true;
ifstream inn;
inn.open( "gameloaded.txt" );

if( !inn.fail( ) )
{
// don't print more than 8 lines

uint32_t Count = 0;
string Line;

while( !inn.eof( ) && Count < 8 )
{
getline( inn, Line );

if( Line.empty( ) )
SendAllChat( " " );
else
SendAllChat( Line );

if( inn.eof( ) )
break;

Count++;
}

inn.close( );
} else
CONSOLE_Print("[GAME: " + m_GameName + "] gameloaded.txt load failed");

}

if (GetTime()>=m_GameLoadedTime+m_MsgTime && !m_MsgStop && m_GameLoaded) //Если игра загрузилась, если скрипт еще не прошел полный цикл, если прошло уже больше или равно 180 секунд
{
CONSOLE_Print("[GAME: " + m_GameName + "] typing msg");
m_MsgTime += 180; //Вывод текста через 180 сек
m_MsgInc++;
{
SendAllChat( " ################ " );
SendAllChat( "---= Ghost One by TeKeN =---" );
SendAllChat( " ################ " );
m_MsgStop = true;// Останавливаем выполнение условия в следующий раз (в данной игре)
}
}

// check if switch expired
if (m_SwitchTime!=0)
if (GetTime()-m_SwitchTime>60)
{
m_SwitchTime = 0;
CONSOLE_Print("[GAME: " + m_GameName + "] Switch expired");
}

bool lessthanminpercent = false;
bool lessthanminplayers = false;
float remainingpercent = (float)m_Players.size()*100/(float)m_PlayersatStart;

if (!m_GameOverCanceled)
{
// start the gameover timer if world tree/frozen throne has fallen
if (m_GHost->m_gameoverbasefallen>0 && m_GameOverTime == 0 && m_GameEnded)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (a base has fallen)");
m_GameOverTime = GetTime( )-60+m_GHost->m_gameoverbasefallen;
}

// start the gameover timer if we have less than the minimum percent of players remaining.
if (!m_GameEnded && m_GHost->m_gameoverminpercent!=0 && ( !m_GameLoading && m_GameLoaded ))
if (remainingpercent<m_GHost->m_gameoverminpercent)
lessthanminpercent = true;
if (lessthanminpercent && m_GameOverTime == 0)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (less than "+UTIL_ToString(m_GHost->m_gameoverminpercent)+"% )"+" "+string(1,m_GHost->m_CommandTrigger)+"override to cancel" );
SendAllChat("Game over in 60 seconds, "+ UTIL_ToString(remainingpercent)+"% remaining ( < "+UTIL_ToString(m_GHost->m_gameoverminpercent)+"% ) "+string(1,m_GHost->m_CommandTrigger)+"override to cancel");
m_GameOverTime = GetTime( );
}

// start the gameover timer if there's less than m_gameoverminplayers players left (and we had at least 1 leaver)

if (!m_GameEnded && m_GHost->m_gameoverminplayers!=0)
if (m_Players.size( ) < m_GHost->m_gameoverminplayers && m_PlayersatStart>m_Players.size() && ( !m_GameLoading && m_GameLoaded ) )
lessthanminplayers = true;
if (lessthanminplayers && m_GameOverTime == 0)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (one player left)"+" "+string(1, m_GHost->m_CommandTrigger)+"override to cancel" );
SendAllChat("Game over in 60 seconds, "+ UTIL_ToString(m_Players.size())+" remaining ( < "+UTIL_ToString(m_GHost->m_gameoverminplayers)+" ) "+string(1, m_GHost->m_CommandTrigger)+"override to cancel");
m_GameOverTime = GetTime( );
}

if (!m_GameLoading && m_GameLoaded)
if (!m_GameOverDiffCanceled)
if (!m_GameEnded)
if (!lessthanminplayers && !lessthanminpercent)
if (m_Team1<2 || m_Team2<2)
m_GameOverDiffCanceled = true;


// start the gameover timer if team unbalance is greater than m_gameovermaxteamdifference
// ex: m_gameovermaxteamdifference = 2, if one team has -3, start game over.
if (!m_GameLoading && m_GameLoaded)
if (!m_GameOverDiffCanceled)
if (!m_GameEnded)
if (!m_Switched) // disable gameoverteamdifference if someone has switched
if (!lessthanminplayers && !lessthanminpercent)
if (m_GetMapNumTeams==2 && m_GHost->m_gameovermaxteamdifference!=0 && m_Players.size()<m_PlayersatStart && m_Players.size()>1)
{
bool unbalanced = false;

if (m_TeamDiff > m_GHost->m_gameovermaxteamdifference)
unbalanced = true;

if (m_GameOverTime!=0 && !unbalanced)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer stoped (rebalanced team)" );
SendAllChat("Game over averted, team rebalanced");
m_GameOverTime = 0;
}


if (unbalanced && m_GameOverTime==0)
{
CONSOLE_Print( "[GAME: " + m_GameName + "] gameover timer started (unbalanced team)" );
SendAllChat("Game over in 120 seconds, rebalance quickly!, max team difference is "+UTIL_ToString(m_GHost->m_gameovermaxteamdifference)+" "+string(1, m_GHost->m_CommandTrigger)+"override to cancel");
m_GameOverTime = GetTime( )+60;
}
}

// finish the gameover timer

if( m_GameOverTime != 0 && GetTime( ) >= m_GameOverTime + 60 )
{
bool AlreadyStopped = true;

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( !(*i)->GetDeleteMe( ) )
{
AlreadyStopped = false;
break;
}
}

if( !AlreadyStopped )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (gameover timer finished)" );
StopPlayers( "was disconnected (gameover timer finished)" );
}
}
}

if ( !m_DownloadOnlyMode)
if( m_GHost->m_LobbyTimeLimit && (m_VirtualHostName!="|cFFC04040Admin") && !m_GameLoading && !m_GameLoaded )
{
// is there a player with reserved status in the game?
if (m_Players.size()>0)
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++)
{
if( (*i)->GetReserved( ) )
{
m_LastReservedSeen = GetTime( );
}
}
//check if we have hit the timelimit
if ( GetTime( ) > m_LastReservedSeen + m_GHost->m_LobbyTimeLimit*60 )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (lobby timelimit hit)" );
for( vector<CBNET *> :: iterator i = m_GHost->m_BNETs.begin( ); i != m_GHost->m_BNETs.end( ); i++ )
{
(*i)->QueueChatCommand( "[GAME: " + m_GameName + "] is over (lobby timelimit hit)" );

if( (*i)->GetServer( ) == GetCreatorServer( ) )
(*i)->QueueChatCommand( "[GAME: " + m_GameName + "] is over (lobby timelimit hit)", GetCreatorName( ), true );
}
m_Exiting = true;
}
}

// censor muted process
ProcessCensorMuted();

// check how many slots are unoccupied and announce if needed
if (m_GHost->m_LobbyAnnounceUnoccupied)
if (!m_GameLoaded && !m_GameLoading && m_AutoStartPlayers==0)
if (GetSlotsOpen()!=m_LastSlotsUnoccupied)
{
m_LastSlotsUnoccupied = GetSlotsOpen();
if (m_LastSlotsUnoccupied==1 || m_LastSlotsUnoccupied==2)
SendAllChat("+"+UTIL_ToString(m_LastSlotsUnoccupied));
}


// check if all slots are no longer occupied
if (!m_GameLoaded && !m_GameLoading)
if (m_AllSlotsOccupied)
if (GetSlotsOpen()!=0)
{
m_AllSlotsOccupied = false;
m_AllSlotsAnnounced = false;
}

// if all slots occupied for 3 seconds, announce in the lobby
if (!m_CountDownStarted && !m_GameLoaded && !m_GameLoading)
if (GetSlotsOpen()==0 && m_AllSlotsOccupied && !m_AllSlotsAnnounced)
if (GetTime()-m_SlotsOccupiedTime>3)
{
m_AllSlotsAnnounced = true;
string Pings;
string Pings2;
uint32_t Ping;
bool samecountry=true;
string CN, CNL;

Pings = "All slots occupied. ";
Pings2 = "All slots occupied. ";

// copy the m_Players vector so we can sort by descending ping so it's easier to find players with high pings

vector<CGamePlayer *> SortedPlayers = m_Players;
sort( SortedPlayers.begin( ), SortedPlayers.end( ), CGamePlayerSortDescByPing( ) );

// string FirstC;

for( vector<CGamePlayer *> :: iterator i = SortedPlayers.begin( ); i != SortedPlayers.end( ); i++ )
{
//Pings += (*i)->GetName( );
//Pings += ": ";
bool skipP;

CN = m_GHost->m_DBLocal->FromCheck( UTIL_ByteArrayToUInt32( (*i)->GetExternalIP( ), true ) );
if (CNL=="")
CNL=CN;
else
if (CN!=CNL)
samecountry=false;

if( (*i)->GetNumPings( ) > 0 )
{
Ping=(*i)->GetPing( m_GHost->m_LCPings );
if (Ping>5)
{
skipP = false;
Pings += UTIL_ToString( Ping );
Pings += "ms (";
Pings += CN;
Pings += ")";
Pings2 += UTIL_ToString( Ping );
Pings2 += "ms";
} else
{
skipP = true;
}
}
else
{
skipP = false;
Pings += "N/A (";
Pings += CN;
Pings += ")";
}

if( i != SortedPlayers.end( ) - 1 && !skipP)
{
Pings += ", ";
Pings2 += ", ";
}
}
Pings2 += " are all from ("+CNL+")";

if (samecountry)
SendAllChat( Pings2 );
else
SendAllChat( Pings );
}


// check if we're rehosting

if (m_EndGameTime>0)
if (GetTime() - m_EndGameTime>=3)
{
m_Exiting = true;
m_GHost->newGame = true;
}

// end the game if there aren't any players left

if( m_Players.empty( ) && ( m_GameLoading || m_GameLoaded ) )
{
if( !m_Saving )
{
CONSOLE_Print( "[GAME: " + m_GameName + "] is over (no players left)" );
SaveGameData( );
m_Saving = true;
}
else if( IsGameDataSaved( ) )
return true;
}

// accept new connections

if( m_Socket )
{
CTCPSocket *NewSocket = m_Socket->Accept( (fd_set *)fd );

if( NewSocket )
{
// check the IP blacklist

if( m_IPBlackList.find( NewSocket->GetIPString( ) ) == m_IPBlackList.end( ) )
{
if( m_GHost->m_TCPNoDelay )
NewSocket->SetNoDelay( true );

m_Potentials.push_back( new CPotentialPlayer( m_Protocol, this, NewSocket ) );
}
else
{
CONSOLE_Print( "[GAME: " + m_GameName + "] rejected connection from [" + NewSocket->GetIPString( ) + "] due to blacklist" );
delete NewSocket;
}
}

if( m_Socket->HasError( ) )
return true;
}

return m_Exiting;
}

void CBaseGame :: UpdatePost( void *send_fd )
{
// we need to manually call DoSend on each player now because CGamePlayer :: Update doesn't do it
// this is in case player 2 generates a packet for player 1 during the update but it doesn't get sent because player 1 already finished updating
// in reality since we're queueing actions it might not make a big difference but oh well

for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if( (*i)->GetSocket( ) )
(*i)->GetSocket( )->DoSend( (fd_set *)send_fd );
}

for( vector<CPotentialPlayer *> :: iterator i = m_Potentials.begin( ); i != m_Potentials.end( ); i++ )
{
if( (*i)->GetSocket( ) )
(*i)->GetSocket( )->DoSend( (fd_set *)send_fd );
}
}

void CBaseGame :: Send( CGamePlayer *player, BYTEARRAY data )
{
if( player )
player->Send( data );
}

void CBaseGame :: Send( unsigned char PID, BYTEARRAY data )
{
Send( GetPlayerFromPID( PID ), data );
}

void CBaseGame :: Send( BYTEARRAY PIDs, BYTEARRAY data )
{
for( unsigned int i = 0; i < PIDs.size( ); i++ )
Send( PIDs[i], data );
}

void CBaseGame :: SendAlly( unsigned char PID, BYTEARRAY data )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if (m_Slots[GetSIDFromPID((*i)->GetPID())].GetTeam()==m_Slots[GetSIDFromPID(PID)].GetTeam())
(*i)->Send( data );
}
}

void CBaseGame :: SendAdmin( unsigned char PID, BYTEARRAY data )
{
bool isAdmin = false;
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
isAdmin = IsAdmin((*i)->GetName()) || IsRootAdmin((*i)->GetName()) || IsOwner((*i)->GetName());
if (isAdmin)
(*i)->Send( data );
}
}

void CBaseGame :: SendEnemy( unsigned char PID, BYTEARRAY data )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
if (m_Slots[GetSIDFromPID((*i)->GetPID())].GetTeam()!=m_Slots[GetSIDFromPID(PID)].GetTeam())
(*i)->Send( data );
}
}

void CBaseGame :: SendAll( BYTEARRAY data )
{
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
(*i)->Send( data );
}

void CBaseGame :: SendAdmin( BYTEARRAY data )
{
bool isAdmin = false;
for( vector<CGamePlayer *> :: iterator i = m_Players.begin( ); i != m_Players.end( ); i++ )
{
isAdmin = IsAdmin((*i)->GetName()) || IsRootAdmin((*i)->GetName()) || IsOwner((*i)->GetName());
if (isAdmin)
(*i)->Send( data );
}
}

void CBaseGame :: SendChat( unsigned char fromPID, CGamePlayer *player, string message )
{
// send a private message to one player - it'll be marked [Private] in Warcraft 3

if (m_DetourAllMessagesToAdmins)
if (player)
{
bool isAdmin = IsAdmin(player->GetName()) || IsOwner(player->GetName()) || IsRootAdmin(player->GetName());
if (!isAdmin)
return;
}

if( player )
{
if( !m_GameLoading && !m_GameLoaded )
{
if( message.size( ) > 220 )
message = message.substr( 0, 220 );

Send( player, m_Protocol->SEND_W3GS_CHAT_FROM_HOST( fromPID, UTIL_CreateByteArray( player->GetPID( ) ), 16, BYTEARRAY( ), message ) );
}
else
{
unsigned char ExtraFlags[] = { 3, 0, 0, 0 };

// based on my limited testing it seems that the extra flags' first byte contains 3 plus the recipient's colour to denote a private message

unsigned char SID = GetSIDFromPID( player->GetPID( ) );

if( SID < m_Slots.size( ) )
ExtraFlags[0] = 3 + m_Slots[SID].GetColour( );

if( message.size( ) > 120 )
...
[Изображение: qvAG.jpg]
Ответ
Csandr дружище! мог бы мне объяснить кое какие веши! вот ну хорчу поставить например - Csandr 10 игр,5 лив, 15000pts (С+)

а у меня пишет Csandr 10 игр,5 лив, класс: Убийца. очков: 1.50308

и вот пишу !top там выходет старые статы как их обновить!?
и на MySQL передти неполучается!
спс за внимание
Ответ
Zadrot Написал:Csandr дружище! мог бы мне объяснить кое какие веши! вот ну хорчу поставить например - Csandr 10 игр,5 лив, 15000pts (С+)

а у меня пишет Csandr 10 игр,5 лив, класс: Убийца. очков: 1.50308

и вот пишу !top там выходет старые статы как их обновить!?
и на MySQL передти неполучается!
спс за внимание
На мой предыдущий первый вопрос не ответил... А хочешь, чтоб на твои отвечал... :dc:


Zadrot Написал:Csandr дружище! мог бы мне объяснить кое какие веши! вот ну хорчу поставить например - Csandr 10 игр,5 лив, 15000pts (С+)

а у меня пишет Csandr 10 игр,5 лив, класс: Убийца. очков: 1.50308
ставь MySQL туда интегрируй SQL запросы (они лежат в корне БРТ бота)
Насчет 15000pts (С+) - нуно будет в сырцах добавлять (чтобы присвоить определённым очкам букву, плюс/минус)

Zadrot Написал:вот пишу !top там выходет старые статы как их обновить!?
Я тебе написал по этому поводу...
Версия бота?

Zadrot Написал:и на MySQL переЙти не_получается!
В смысле? Создать БД MySQL не можешь? Или выполнить SQL запросы не можешь?
[Изображение: qvAG.jpg]
Ответ


Возможно похожие темы ...
Тема Автор Ответы Просмотры Последний пост
  Где скачать варик для battele.net? Gambit_J 24 9,402 01-31-2012, 01:54 PM
Последний пост: Steep_0

Перейти к форуму:


Пользователи, просматривающие эту тему: 1 Гость(ей)