Hiện bộ gõ=phím F8
PRESS F9 to turn on/off the unicode typing method.
Bộ gõ Tiếng Việt kiểu VNI đã mặc định bật, bấm F8 để hiện trạng thái bộ gõ và hiệu chỉnh khi cần.Latest topics
Thống Kê/Statistic
Edit version Ghost++
3 posters
ProGamingEXP - PROFESSIONAL GAMING EXPERIENCE :: Game Mode Section - Nơi hỗ trợ về map & gameplay :: Gameplay discussion
Page 1 of 1
Edit version Ghost++
I want to ask how where to replace !version ghost + +
version: Ghost + + ([You must be registered and logged in to see this link.]
version: Ghost + + ([You must be registered and logged in to see this link.]
balanarursa- New Member
- Posts : 4
Join date : 2013-01-10
Re: Edit version Ghost++
which replaced that version can be replaced?
ghost.cpp
ghost.cpp
/*
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
[You must be registered and logged in to see this link.]
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: [You must be registered and logged in to see this link.]
*/
#include "ghost.h"
#include "util.h"
#include "crc32.h"
#include "sha1.h"
#include "csvparser.h"
#include "config.h"
#include "language.h"
#include "socket.h"
#include "ghostdb.h"
#include "ghostdbsqlite.h"
#include "ghostdbmysql.h"
#include "bnet.h"
#include "map.h"
#include "packed.h"
#include "savegame.h"
#include "gameplayer.h"
#include "gameprotocol.h"
#include "gpsprotocol.h"
#include "game_base.h"
#include "game.h"
#include "game_admin.h"
#include
#include
#ifdef WIN32
#include// for WSAIoctl
#endif
#define __STORMLIB_SELF__
#include
/*
#include "ghost.h"
#include "util.h"
#include "crc32.h"
#include "sha1.h"
#include "csvparser.h"
#include "config.h"
#include "language.h"
#include "socket.h"
#include "commandpacket.h"
#include "ghostdb.h"
#include "ghostdbsqlite.h"
#include "ghostdbmysql.h"
#include "bncsutilinterface.h"
#include "warden.h"
#include "bnlsprotocol.h"
#include "bnlsclient.h"
#include "bnetprotocol.h"
#include "bnet.h"
#include "map.h"
#include "packed.h"
#include "savegame.h"
#include "replay.h"
#include "gameslot.h"
#include "gameplayer.h"
#include "gameprotocol.h"
#include "gpsprotocol.h"
#include "game_base.h"
#include "game.h"
#include "game_admin.h"
#include "stats.h"
#include "statsdota.h"
#include "sqlite3.h"
*/
#ifdef WIN32
#include
#include
#endif
#include
#ifndef WIN32
#include
#endif
#ifdef __APPLE__
#include
#endif
string gCFGFile;
string gLogFile;
uint32_t gLogMethod;
ofstream *gLog = NULL;
CGHost *gGHost = NULL;
uint32_t GetTime( )
{
return GetTicks( ) / 1000;
}
uint32_t GetTicks( )
{
#ifdef WIN32
// don't use GetTickCount anymore because it's not accurate enough (~16ms resolution)
// don't use QueryPerformanceCounter anymore because it isn't guaranteed to be strictly increasing on some systems and thus requires "smoothing" code
// use timeGetTime instead, which typically has a high resolution (5ms or more) but we request a lower resolution on startup
return timeGetTime( );
#elif __APPLE__
uint64_t current = mach_absolute_time( );
static mach_timebase_info_data_t info = { 0, 0 };
// get timebase info
if( info.denom == 0 )
mach_timebase_info( &info );
uint64_t elapsednano = current * ( info.numer / info.denom );
// convert ns to ms
return elapsednano / 1e6;
#else
uint32_t ticks;
struct timespec t;
clock_gettime( CLOCK_MONOTONIC, &t );
ticks = t.tv_sec * 1000;
ticks += t.tv_nsec / 1000000;
return ticks;
#endif
}
void SignalCatcher2( int s )
{
CONSOLE_Print( "[!!!] caught signal " + UTIL_ToString( s ) + ", exiting NOW" );
if( gGHost )
{
if( gGHost->m_Exiting )
exit( 1 );
else
gGHost->m_Exiting = true;
}
else
exit( 1 );
}
void SignalCatcher( int s )
{
// signal( SIGABRT, SignalCatcher2 );
signal( SIGINT, SignalCatcher2 );
CONSOLE_Print( "[!!!] caught signal " + UTIL_ToString( s ) + ", exiting nicely" );
if( gGHost )
gGHost->m_ExitingNice = true;
else
exit( 1 );
}
void CONSOLE_Print( string message )
{
cout << message << endl;
// logging
if( !gLogFile.empty( ) )
{
if( gLogMethod == 1 )
{
ofstream Log;
Log.open( gLogFile.c_str( ), ios :: app );
if( !Log.fail( ) )
{
time_t Now = time( NULL );
string Time = asctime( localtime( &Now ) );
// erase the newline
Time.erase( Time.size( ) - 1 );
Log << "[" << Time << "] " << message << endl;
Log.close( );
}
}
else if( gLogMethod == 2 )
{
if( gLog && !gLog->fail( ) )
{
time_t Now = time( NULL );
string Time = asctime( localtime( &Now ) );
// erase the newline
Time.erase( Time.size( ) - 1 );
*gLog << "[" << Time << "] " << message << endl;
gLog->flush( );
}
}
}
}
void DEBUG_Print( string message )
{
cout << message << endl;
}
void DEBUG_Print( BYTEARRAY b )
{
cout << "{ ";
for( unsigned int i = 0; i < b.size( ); i++ )
cout << hex << (int)b[i] << " ";
cout << "}" << endl;
}
//
// main
//
int main( int argc, char **argv )
{
gCFGFile = "ghost.cfg";
if( argc > 1 && argv[1] )
gCFGFile = argv[1];
// read config file
CConfig CFG;
CFG.Read( "default.cfg" );
CFG.Read( gCFGFile );
gLogFile = CFG.GetString( "bot_log", string( ) );
gLogMethod = CFG.GetInt( "bot_logmethod", 1 );
if( !gLogFile.empty( ) )
{
if( gLogMethod == 1 )
{
// log method 1: open, append, and close the log for every message
// this works well on Linux but poorly on Windows, particularly as the log file grows in size
// the log file can be edited/moved/deleted while GHost++ is running
}
else if( gLogMethod == 2 )
{
// log method 2: open the log on startup, flush the log for every message, close the log on shutdown
// the log file CANNOT be edited/moved/deleted while GHost++ is running
gLog = new ofstream( );
gLog->open( gLogFile.c_str( ), ios :: app );
}
}
CONSOLE_Print( "[GHOST] starting up" );
if( !gLogFile.empty( ) )
{
if( gLogMethod == 1 )
CONSOLE_Print( "[GHOST] using log method 1, logging is enabled and [" + gLogFile + "] will not be locked" );
else if( gLogMethod == 2 )
{
if( gLog->fail( ) )
CONSOLE_Print( "[GHOST] using log method 2 but unable to open [" + gLogFile + "] for appending, logging is disabled" );
else
CONSOLE_Print( "[GHOST] using log method 2, logging is enabled and [" + gLogFile + "] is now locked" );
}
}
else
CONSOLE_Print( "[GHOST] no log file specified, logging is disabled" );
// catch SIGABRT and SIGINT
// signal( SIGABRT, SignalCatcher );
signal( SIGINT, SignalCatcher );
#ifndef WIN32
// disable SIGPIPE since some systems like OS X don't define MSG_NOSIGNAL
signal( SIGPIPE, SIG_IGN );
#endif
#ifdef WIN32
// initialize timer resolution
// attempt to set the resolution as low as possible from 1ms to 5ms
unsigned int TimerResolution = 0;
for( unsigned int i = 1; i <= 5; i++ )
{
if( timeBeginPeriod( i ) == TIMERR_NOERROR )
{
TimerResolution = i;
break;
}
else if( i < 5 )
CONSOLE_Print( "[GHOST] error setting Windows timer resolution to " + UTIL_ToString( i ) + " milliseconds, trying a higher resolution" );
else
{
CONSOLE_Print( "[GHOST] error setting Windows timer resolution" );
return 1;
}
}
CONSOLE_Print( "[GHOST] using Windows timer with resolution " + UTIL_ToString( TimerResolution ) + " milliseconds" );
#elif __APPLE__
// not sure how to get the resolution
#else
// print the timer resolution
struct timespec Resolution;
if( clock_getres( CLOCK_MONOTONIC, &Resolution ) == -1 )
CONSOLE_Print( "[GHOST] error getting monotonic timer resolution" );
else
CONSOLE_Print( "[GHOST] using monotonic timer with resolution " + UTIL_ToString( (double)( Resolution.tv_nsec / 1000 ), 2 ) + " microseconds" );
#endif
#ifdef WIN32
// initialize winsock
CONSOLE_Print( "[GHOST] starting winsock" );
WSADATA wsadata;
if( WSAStartup( MAKEWORD( 2, 2 ), &wsadata ) != 0 )
{
CONSOLE_Print( "[GHOST] error starting winsock" );
return 1;
}
// increase process priority
CONSOLE_Print( "[GHOST] setting process priority to \"above normal\"" );
SetPriorityClass( GetCurrentProcess( ), ABOVE_NORMAL_PRIORITY_CLASS );
#endif
// initialize ghost
gGHost = new CGHost( &CFG );
while( 1 )
{
// block for 50ms on all sockets - if you intend to perform any timed actions more frequently you should change this
// that said it's likely we'll loop more often than this due to there being data waiting on one of the sockets but there aren't any guarantees
if( gGHost->Update( 50000 ) )
break;
}
// shutdown ghost
CONSOLE_Print( "[GHOST] shutting down" );
delete gGHost;
gGHost = NULL;
#ifdef WIN32
// shutdown winsock
CONSOLE_Print( "[GHOST] shutting down winsock" );
WSACleanup( );
// shutdown timer
timeEndPeriod( TimerResolution );
#endif
if( gLog )
{
if( !gLog->fail( ) )
gLog->close( );
delete gLog;
}
return 0;
}
//
// CGHost
//
CGHost :: CGHost( CConfig *CFG )
{
m_UDPSocket = new CUDPSocket( );
m_UDPSocket->SetBroadcastTarget( CFG->GetString( "udp_broadcasttarget", string( ) ) );
m_UDPSocket->SetDontRoute( CFG->GetInt( "udp_dontroute", 0 ) == 0 ? false : true );
m_ReconnectSocket = NULL;
m_GPSProtocol = new CGPSProtocol( );
m_CRC = new CCRC32( );
m_CRC->Initialize( );
m_SHA = new CSHA1( );
m_CurrentGame = NULL;
string DBType = CFG->GetString( "db_type", "sqlite3" );
CONSOLE_Print( "[GHOST] opening primary database" );
if( DBType == "mysql" )
{
#ifdef GHOST_MYSQL
m_DB = new CGHostDBMySQL( CFG );
#else
CONSOLE_Print( "[GHOST] warning - this binary was not compiled with MySQL database support, using SQLite database instead" );
m_DB = new CGHostDBSQLite( CFG );
#endif
}
else
m_DB = new CGHostDBSQLite( CFG );
CONSOLE_Print( "[GHOST] opening secondary (local) database" );
m_DBLocal = new CGHostDBSQLite( CFG );
// get a list of local IP addresses
// this list is used elsewhere to determine if a player connecting to the bot is local or not
CONSOLE_Print( "[GHOST] attempting to find local IP addresses" );
#ifdef WIN32
// use a more reliable Windows specific method since the portable method doesn't always work properly on Windows
// code stolen from: [You must be registered and logged in to see this link.]
SOCKET sd = WSASocket( AF_INET, SOCK_DGRAM, 0, 0, 0, 0 );
if( sd == SOCKET_ERROR )
CONSOLE_Print( "[GHOST] error finding local IP addresses - failed to create socket (error code " + UTIL_ToString( WSAGetLastError( ) ) + ")" );
else
{
INTERFACE_INFO InterfaceList[20];
unsigned long nBytesReturned;
if( WSAIoctl( sd, SIO_GET_INTERFACE_LIST, 0, 0, &InterfaceList, sizeof(InterfaceList), &nBytesReturned, 0, 0 ) == SOCKET_ERROR )
CONSOLE_Print( "[GHOST] error finding local IP addresses - WSAIoctl failed (error code " + UTIL_ToString( WSAGetLastError( ) ) + ")" );
else
{
int nNumInterfaces = nBytesReturned / sizeof(INTERFACE_INFO);
for( int i = 0; i < nNumInterfaces; i++ )
{
sockaddr_in *pAddress;
pAddress = (sockaddr_in *)&(InterfaceList[i].iiAddress);
CONSOLE_Print( "[GHOST] local IP address #" + UTIL_ToString( i + 1 ) + " is [" + string( inet_ntoa( pAddress->sin_addr ) ) + "]" );
m_LocalAddresses.push_back( UTIL_CreateByteArray( (uint32_t)pAddress->sin_addr.s_addr, false ) );
}
}
closesocket( sd );
}
#else
// use a portable method
char HostName[255];
if( gethostname( HostName, 255 ) == SOCKET_ERROR )
CONSOLE_Print( "[GHOST] error finding local IP addresses - failed to get local hostname" );
else
{
CONSOLE_Print( "[GHOST] local hostname is [" + string( HostName ) + "]" );
struct hostent *HostEnt = gethostbyname( HostName );
if( !HostEnt )
CONSOLE_Print( "[GHOST] error finding local IP addresses - gethostbyname failed" );
else
{
for( int i = 0; HostEnt->h_addr_list[i] != NULL; i++ )
{
struct in_addr Address;
memcpy( &Address, HostEnt->h_addr_list[i], sizeof(struct in_addr) );
CONSOLE_Print( "[GHOST] local IP address #" + UTIL_ToString( i + 1 ) + " is [" + string( inet_ntoa( Address ) ) + "]" );
m_LocalAddresses.push_back( UTIL_CreateByteArray( (uint32_t)Address.s_addr, false ) );
}
}
}
#endif
m_Language = NULL;
m_Exiting = false;
m_ExitingNice = false;
m_Enabled = true;
m_Version = "17.0";
m_HostCounter = 1;
m_AutoHostMaximumGames = CFG->GetInt( "autohost_maxgames", 0 );
m_AutoHostAutoStartPlayers = CFG->GetInt( "autohost_startplayers", 0 );
m_AutoHostGameName = CFG->GetString( "autohost_gamename", string( ) );
m_AutoHostOwner = CFG->GetString( "autohost_owner", string( ) );
m_LastAutoHostTime = GetTime( );
m_AutoHostMatchMaking = false;
m_AutoHostMinimumScore = 0.0;
m_AutoHostMaximumScore = 0.0;
m_AllGamesFinished = false;
m_AllGamesFinishedTime = 0;
m_TFT = CFG->GetInt( "bot_tft", 1 ) == 0 ? false : true;
if( m_TFT )
CONSOLE_Print( "[GHOST] acting as Warcraft III: The Frozen Throne" );
else
CONSOLE_Print( "[GHOST] acting as Warcraft III: Reign of Chaos" );
m_HostPort = CFG->GetInt( "bot_hostport", 6112 );
m_Reconnect = CFG->GetInt( "bot_reconnect", 1 ) == 0 ? false : true;
m_ReconnectPort = CFG->GetInt( "bot_reconnectport", 6114 );
m_DefaultMap = CFG->GetString( "bot_defaultmap", "map" );
m_AdminGameCreate = CFG->GetInt( "admingame_create", 0 ) == 0 ? false : true;
m_AdminGamePort = CFG->GetInt( "admingame_port", 6113 );
m_AdminGamePassword = CFG->GetString( "admingame_password", string( ) );
m_AdminGameMap = CFG->GetString( "admingame_map", string( ) );
m_LANWar3Version = CFG->GetInt( "lan_war3version", 24 );
m_ReplayWar3Version = CFG->GetInt( "replay_war3version", 24 );
m_ReplayBuildNumber = CFG->GetInt( "replay_buildnumber", 6059 );
SetConfigs( CFG );
// load the battle.net connections
// we're just loading the config data and creating the CBNET classes here, the connections are established later (in the Update function)
for( uint32_t i = 1; i < 10; i++ )
{
string Prefix;
if( i == 1 )
Prefix = "bnet_";
else
Prefix = "bnet" + UTIL_ToString( i ) + "_";
string Server = CFG->GetString( Prefix + "server", string( ) );
string ServerAlias = CFG->GetString( Prefix + "serveralias", string( ) );
string CDKeyROC = CFG->GetString( Prefix + "cdkeyroc", string( ) );
string CDKeyTFT = CFG->GetString( Prefix + "cdkeytft", string( ) );
string CountryAbbrev = CFG->GetString( Prefix + "countryabbrev", "USA" );
string Country = CFG->GetString( Prefix + "country", "United States" );
string Locale = CFG->GetString( Prefix + "locale", "system" );
uint32_t LocaleID;
if( Locale == "system" )
{
#ifdef WIN32
LocaleID = GetUserDefaultLangID( );
#else
LocaleID = 1033;
#endif
}
else
LocaleID = UTIL_ToUInt32( Locale );
string UserName = CFG->GetString( Prefix + "username", string( ) );
string UserPassword = CFG->GetString( Prefix + "password", string( ) );
string FirstChannel = CFG->GetString( Prefix + "firstchannel", "The Void" );
string RootAdmin = CFG->GetString( Prefix + "rootadmin", string( ) );
string BNETCommandTrigger = CFG->GetString( Prefix + "commandtrigger", "!" );
if( BNETCommandTrigger.empty( ) )
BNETCommandTrigger = "!";
bool HoldFriends = CFG->GetInt( Prefix + "holdfriends", 1 ) == 0 ? false : true;
bool HoldClan = CFG->GetInt( Prefix + "holdclan", 1 ) == 0 ? false : true;
bool PublicCommands = CFG->GetInt( Prefix + "publiccommands", 1 ) == 0 ? false : true;
string BNLSServer = CFG->GetString( Prefix + "bnlsserver", string( ) );
int BNLSPort = CFG->GetInt( Prefix + "bnlsport", 9367 );
int BNLSWardenCookie = CFG->GetInt( Prefix + "bnlswardencookie", 0 );
unsigned char War3Version = CFG->GetInt( Prefix + "custom_war3version", 24 );
BYTEARRAY EXEVersion = UTIL_ExtractNumbers( CFG->GetString( Prefix + "custom_exeversion", string( ) ), 4 );
BYTEARRAY EXEVersionHash = UTIL_ExtractNumbers( CFG->GetString( Prefix + "custom_exeversionhash", string( ) ), 4 );
string PasswordHashType = CFG->GetString( Prefix + "custom_passwordhashtype", string( ) );
string PVPGNRealmName = CFG->GetString( Prefix + "custom_pvpgnrealmname", "PvPGN Realm" );
uint32_t MaxMessageLength = CFG->GetInt( Prefix + "custom_maxmessagelength", 200 );
if( Server.empty( ) )
break;
if( CDKeyROC.empty( ) )
{
CONSOLE_Print( "[GHOST] missing " + Prefix + "cdkeyroc, skipping this battle.net connection" );
continue;
}
if( m_TFT && CDKeyTFT.empty( ) )
{
CONSOLE_Print( "[GHOST] missing " + Prefix + "cdkeytft, skipping this battle.net connection" );
continue;
}
if( UserName.empty( ) )
{
CONSOLE_Print( "[GHOST] missing " + Prefix + "username, skipping this battle.net connection" );
continue;
}
if( UserPassword.empty( ) )
{
CONSOLE_Print( "[GHOST] missing " + Prefix + "password, skipping this battle.net connection" );
continue;
}
CONSOLE_Print( "[GHOST] found battle.net connection #" + UTIL_ToString( i ) + " for server [" + Server + "]" );
if( Locale == "system" )
{
#ifdef WIN32
CONSOLE_Print( "[GHOST] using system locale of " + UTIL_ToString( LocaleID ) );
#else
CONSOLE_Print( "[GHOST] unable to get system locale, using default locale of 1033" );
#endif
}
m_BNETs.push_back( new CBNET( this, Server, ServerAlias, BNLSServer, (uint16_t)BNLSPort, (uint32_t)BNLSWardenCookie, CDKeyROC, CDKeyTFT, CountryAbbrev, Country, LocaleID, UserName, UserPassword, FirstChannel, RootAdmin, BNETCommandTrigger[0], HoldFriends, HoldClan, PublicCommands, War3Version, EXEVersion, EXEVersionHash, PasswordHashType, PVPGNRealmName, MaxMessageLength, i ) );
}
if( m_BNETs.empty( ) )
CONSOLE_Print( "[GHOST] warning - no battle.net connections found in config file" );
// extract common.j and blizzard.j from War3Patch.mpq if we can
// these two files are necessary for calculating "map_crc" when loading maps so we make sure to do it before loading the default map
// see CMap :: Load for more information
ExtractScripts( );
// load the default maps (note: make sure to run ExtractScripts first)
if( m_DefaultMap.size( ) < 4 || m_DefaultMap.substr( m_DefaultMap.size( ) - 4 ) != ".cfg" )
{
m_DefaultMap += ".cfg";
CONSOLE_Print( "[GHOST] adding \".cfg\" to default map -> new default is [" + m_DefaultMap + "]" );
}
CConfig MapCFG;
MapCFG.Read( m_MapCFGPath + m_DefaultMap );
m_Map = new CMap( this, &MapCFG, m_MapCFGPath + m_DefaultMap );
if( !m_AdminGameMap.empty( ) )
{
if( m_AdminGameMap.size( ) < 4 || m_AdminGameMap.substr( m_AdminGameMap.size( ) - 4 ) != ".cfg" )
{
m_AdminGameMap += ".cfg";
CONSOLE_Print( "[GHOST] adding \".cfg\" to default admin game map -> new default is [" + m_AdminGameMap + "]" );
}
CONSOLE_Print( "[GHOST] trying to load default admin game map" );
CConfig AdminMapCFG;
AdminMapCFG.Read( m_MapCFGPath + m_AdminGameMap );
m_AdminMap = new CMap( this, &AdminMapCFG, m_MapCFGPath + m_AdminGameMap );
if( !m_AdminMap->GetValid( ) )
{
CONSOLE_Print( "[GHOST] default admin game map isn't valid, using hardcoded admin game map instead" );
delete m_AdminMap;
m_AdminMap = new CMap( this );
}
}
else
{
CONSOLE_Print( "[GHOST] using hardcoded admin game map" );
m_AdminMap = new CMap( this );
}
m_AutoHostMap = new CMap( *m_Map );
m_SaveGame = new CSaveGame( );
// load the iptocountry data
LoadIPToCountryData( );
// create the admin game
if( m_AdminGameCreate )
{
CONSOLE_Print( "[GHOST] creating admin game" );
m_AdminGame = new CAdminGame( this, m_AdminMap, NULL, m_AdminGamePort, 0, "GHost++ Admin Game", m_AdminGamePassword );
if( m_AdminGamePort == m_HostPort )
CONSOLE_Print( "[GHOST] warning - admingame_port and bot_hostport are set to the same value, you won't be able to host any games" );
}
else
m_AdminGame = NULL;
if( m_BNETs.empty( ) && !m_AdminGame )
CONSOLE_Print( "[GHOST] warning - no battle.net connections found and no admin game created" );
#ifdef GHOST_MYSQL
CONSOLE_Print( "[GHOST] GHost++ Version " + m_Version + " (with MySQL support)" );
#else
CONSOLE_Print( "[GHOST] GHost++ Version " + m_Version + " (without MySQL support)" );
#endif
}
CGHost :: ~CGHost( )
{
delete m_UDPSocket;
delete m_ReconnectSocket;
for( vector:: iterator i = m_ReconnectSockets.begin( ); i != m_ReconnectSockets.end( ); i++ )
delete *i;
delete m_GPSProtocol;
delete m_CRC;
delete m_SHA;
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
delete *i;
delete m_CurrentGame;
delete m_AdminGame;
for( vector:: iterator i = m_Games.begin( ); i != m_Games.end( ); i++ )
delete *i;
delete m_DB;
delete m_DBLocal;
// warning: we don't delete any entries of m_Callables here because we can't be guaranteed that the associated threads have terminated
// this is fine if the program is currently exiting because the OS will clean up after us
// but if you try to recreate the CGHost object within a single session you will probably leak resources!
if( !m_Callables.empty( ) )
CONSOLE_Print( "[GHOST] warning - " + UTIL_ToString( m_Callables.size( ) ) + " orphaned callables were leaked (this is not an error)" );
delete m_Language;
delete m_Map;
delete m_AdminMap;
delete m_AutoHostMap;
delete m_SaveGame;
}
bool CGHost :: Update( long usecBlock )
{
// todotodo: do we really want to shutdown if there's a database error? is there any way to recover from this?
if( m_DB->HasError( ) )
{
CONSOLE_Print( "[GHOST] database error - " + m_DB->GetError( ) );
return true;
}
if( m_DBLocal->HasError( ) )
{
CONSOLE_Print( "[GHOST] local database error - " + m_DBLocal->GetError( ) );
return true;
}
// try to exit nicely if requested to do so
if( m_ExitingNice )
{
if( !m_BNETs.empty( ) )
{
CONSOLE_Print( "[GHOST] deleting all battle.net connections in preparation for exiting nicely" );
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
delete *i;
m_BNETs.clear( );
}
if( m_CurrentGame )
{
CONSOLE_Print( "[GHOST] deleting current game in preparation for exiting nicely" );
delete m_CurrentGame;
m_CurrentGame = NULL;
}
if( m_AdminGame )
{
CONSOLE_Print( "[GHOST] deleting admin game in preparation for exiting nicely" );
delete m_AdminGame;
m_AdminGame = NULL;
}
if( m_Games.empty( ) )
{
if( !m_AllGamesFinished )
{
CONSOLE_Print( "[GHOST] all games finished, waiting 60 seconds for threads to finish" );
CONSOLE_Print( "[GHOST] there are " + UTIL_ToString( m_Callables.size( ) ) + " threads in progress" );
m_AllGamesFinished = true;
m_AllGamesFinishedTime = GetTime( );
}
else
{
if( m_Callables.empty( ) )
{
CONSOLE_Print( "[GHOST] all threads finished, exiting nicely" );
m_Exiting = true;
}
else if( GetTime( ) - m_AllGamesFinishedTime >= 60 )
{
CONSOLE_Print( "[GHOST] waited 60 seconds for threads to finish, exiting anyway" );
CONSOLE_Print( "[GHOST] there are " + UTIL_ToString( m_Callables.size( ) ) + " threads still in progress which will be terminated" );
m_Exiting = true;
}
}
}
}
// update callables
for( vector:: iterator i = m_Callables.begin( ); i != m_Callables.end( ); )
{
if( (*i)->GetReady( ) )
{
m_DB->RecoverCallable( *i );
delete *i;
i = m_Callables.erase( i );
}
else
i++;
}
// create the GProxy++ reconnect listener
if( m_Reconnect )
{
if( !m_ReconnectSocket )
{
m_ReconnectSocket = new CTCPServer( );
if( m_ReconnectSocket->Listen( m_BindAddress, m_ReconnectPort ) )
CONSOLE_Print( "[GHOST] listening for GProxy++ reconnects on port " + UTIL_ToString( m_ReconnectPort ) );
else
{
CONSOLE_Print( "[GHOST] error listening for GProxy++ reconnects on port " + UTIL_ToString( m_ReconnectPort ) );
delete m_ReconnectSocket;
m_ReconnectSocket = NULL;
m_Reconnect = false;
}
}
else if( m_ReconnectSocket->HasError( ) )
{
CONSOLE_Print( "[GHOST] GProxy++ reconnect listener error (" + m_ReconnectSocket->GetErrorString( ) + ")" );
delete m_ReconnectSocket;
m_ReconnectSocket = NULL;
m_Reconnect = false;
}
}
unsigned int NumFDs = 0;
// take every socket we own and throw it in one giant select statement so we can block on all sockets
int nfds = 0;
fd_set fd;
fd_set send_fd;
FD_ZERO( &fd );
FD_ZERO( &send_fd );
// 1. all battle.net sockets
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
NumFDs += (*i)->SetFD( &fd, &send_fd, &nfds );
// 2. the current game's server and player sockets
if( m_CurrentGame )
NumFDs += m_CurrentGame->SetFD( &fd, &send_fd, &nfds );
// 3. the admin game's server and player sockets
if( m_AdminGame )
NumFDs += m_AdminGame->SetFD( &fd, &send_fd, &nfds );
// 4. all running games' player sockets
for( vector:: iterator i = m_Games.begin( ); i != m_Games.end( ); i++ )
NumFDs += (*i)->SetFD( &fd, &send_fd, &nfds );
// 5. the GProxy++ reconnect socket(s)
if( m_Reconnect && m_ReconnectSocket )
{
m_ReconnectSocket->SetFD( &fd, &send_fd, &nfds );
NumFDs++;
}
for( vector:: iterator i = m_ReconnectSockets.begin( ); i != m_ReconnectSockets.end( ); i++ )
{
(*i)->SetFD( &fd, &send_fd, &nfds );
NumFDs++;
}
// before we call select we need to determine how long to block for
// previously we just blocked for a maximum of the passed usecBlock microseconds
// however, in an effort to make game updates happen closer to the desired latency setting we now use a dynamic block interval
// note: we still use the passed usecBlock as a hard maximum
for( vector:: iterator i = m_Games.begin( ); i != m_Games.end( ); i++ )
{
if( (*i)->GetNextTimedActionTicks( ) * 1000 < usecBlock )
usecBlock = (*i)->GetNextTimedActionTicks( ) * 1000;
}
// always block for at least 1ms just in case something goes wrong
// this prevents the bot from sucking up all the available CPU if a game keeps asking for immediate updates
// it's a bit ridiculous to include this check since, in theory, the bot is programmed well enough to never make this mistake
// however, considering who programmed it, it's worthwhile to do it anyway
if( usecBlock < 1000 )
usecBlock = 1000;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = usecBlock;
struct timeval send_tv;
send_tv.tv_sec = 0;
send_tv.tv_usec = 0;
#ifdef WIN32
select( 1, &fd, NULL, NULL, &tv );
select( 1, NULL, &send_fd, NULL, &send_tv );
#else
select( nfds + 1, &fd, NULL, NULL, &tv );
select( nfds + 1, NULL, &send_fd, NULL, &send_tv );
#endif
if( NumFDs == 0 )
{
// we don't have any sockets (i.e. we aren't connected to battle.net maybe due to a lost connection and there aren't any games running)
// select will return immediately and we'll chew up the CPU if we let it loop so just sleep for 50ms to kill some time
MILLISLEEP( 50 );
}
bool AdminExit = false;
bool BNETExit = false;
// update current game
if( m_CurrentGame )
{
if( m_CurrentGame->Update( &fd, &send_fd ) )
{
CONSOLE_Print( "[GHOST] deleting current game [" + m_CurrentGame->GetGameName( ) + "]" );
delete m_CurrentGame;
m_CurrentGame = NULL;
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
(*i)->QueueGameUncreate( );
(*i)->QueueEnterChat( );
}
}
else if( m_CurrentGame )
m_CurrentGame->UpdatePost( &send_fd );
}
// update admin game
if( m_AdminGame )
{
if( m_AdminGame->Update( &fd, &send_fd ) )
{
CONSOLE_Print( "[GHOST] deleting admin game" );
delete m_AdminGame;
m_AdminGame = NULL;
AdminExit = true;
}
else if( m_AdminGame )
m_AdminGame->UpdatePost( &send_fd );
}
// update running games
for( vector:: iterator i = m_Games.begin( ); i != m_Games.end( ); )
{
if( (*i)->Update( &fd, &send_fd ) )
{
CONSOLE_Print( "[GHOST] deleting game [" + (*i)->GetGameName( ) + "]" );
EventGameDeleted( *i );
delete *i;
i = m_Games.erase( i );
}
else
{
(*i)->UpdatePost( &send_fd );
i++;
}
}
// update battle.net connections
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->Update( &fd, &send_fd ) )
BNETExit = true;
}
// update GProxy++ reliable reconnect sockets
if( m_Reconnect && m_ReconnectSocket )
{
CTCPSocket *NewSocket = m_ReconnectSocket->Accept( &fd );
if( NewSocket )
m_ReconnectSockets.push_back( NewSocket );
}
for( vector:: iterator i = m_ReconnectSockets.begin( ); i != m_ReconnectSockets.end( ); )
{
if( (*i)->HasError( ) || !(*i)->GetConnected( ) || GetTime( ) - (*i)->GetLastRecv( ) >= 10 )
{
delete *i;
i = m_ReconnectSockets.erase( i );
continue;
}
(*i)->DoRecv( &fd );
string *RecvBuffer = (*i)->GetBytes( );
BYTEARRAY Bytes = UTIL_CreateByteArray( (unsigned char *)RecvBuffer->c_str( ), RecvBuffer->size( ) );
// a packet is at least 4 bytes
if( Bytes.size( ) >= 4 )
{
if( Bytes[0] == GPS_HEADER_CONSTANT )
{
// bytes 2 and 3 contain the length of the packet
uint16_t Length = UTIL_ByteArrayToUInt16( Bytes, false, 2 );
if( Length >= 4 )
{
if( Bytes.size( ) >= Length )
{
if( Bytes[1] == CGPSProtocol :: GPS_RECONNECT && Length == 13 )
{
unsigned char PID = Bytes[4];
uint32_t ReconnectKey = UTIL_ByteArrayToUInt32( Bytes, false, 5 );
uint32_t LastPacket = UTIL_ByteArrayToUInt32( Bytes, false, 9 );
// look for a matching player in a running game
CGamePlayer *Match = NULL;
for( vector:: iterator j = m_Games.begin( ); j != m_Games.end( ); j++ )
{
if( (*j)->GetGameLoaded( ) )
{
CGamePlayer *Player = (*j)->GetPlayerFromPID( PID );
if( Player && Player->GetGProxy( ) && Player->GetGProxyReconnectKey( ) == ReconnectKey )
{
Match = Player;
break;
}
}
}
if( Match )
{
// reconnect successful!
*RecvBuffer = RecvBuffer->substr( Length );
Match->EventGProxyReconnect( *i, LastPacket );
i = m_ReconnectSockets.erase( i );
continue;
}
else
{
(*i)->PutBytes( m_GPSProtocol->SEND_GPSS_REJECT( REJECTGPS_NOTFOUND ) );
(*i)->DoSend( &send_fd );
delete *i;
i = m_ReconnectSockets.erase( i );
continue;
}
}
else
{
(*i)->PutBytes( m_GPSProtocol->SEND_GPSS_REJECT( REJECTGPS_INVALID ) );
(*i)->DoSend( &send_fd );
delete *i;
i = m_ReconnectSockets.erase( i );
continue;
}
}
}
else
{
(*i)->PutBytes( m_GPSProtocol->SEND_GPSS_REJECT( REJECTGPS_INVALID ) );
(*i)->DoSend( &send_fd );
delete *i;
i = m_ReconnectSockets.erase( i );
continue;
}
}
else
{
(*i)->PutBytes( m_GPSProtocol->SEND_GPSS_REJECT( REJECTGPS_INVALID ) );
(*i)->DoSend( &send_fd );
delete *i;
i = m_ReconnectSockets.erase( i );
continue;
}
}
(*i)->DoSend( &send_fd );
i++;
}
// autohost
if( !m_AutoHostGameName.empty( ) && m_AutoHostMaximumGames != 0 && m_AutoHostAutoStartPlayers != 0 && GetTime( ) - m_LastAutoHostTime >= 30 )
{
// copy all the checks from CGHost :: CreateGame here because we don't want to spam the chat when there's an error
// instead we fail silently and try again soon
if( !m_ExitingNice && m_Enabled && !m_CurrentGame && m_Games.size( ) < m_MaxGames && m_Games.size( ) < m_AutoHostMaximumGames )
{
if( m_AutoHostMap->GetValid( ) )
{
string GameName = m_AutoHostGameName + " #" + UTIL_ToString( m_HostCounter );
if( GameName.size( ) <= 31 )
{
CreateGame( m_AutoHostMap, GAME_PUBLIC, false, GameName, m_AutoHostOwner, m_AutoHostOwner, m_AutoHostServer, false );
if( m_CurrentGame )
{
m_CurrentGame->SetAutoStartPlayers( m_AutoHostAutoStartPlayers );
if( m_AutoHostMatchMaking )
{
if( !m_Map->GetMapMatchMakingCategory( ).empty( ) )
{
if( !( m_Map->GetMapOptions( ) & MAPOPT_FIXEDPLAYERSETTINGS ) )
CONSOLE_Print( "[GHOST] autohostmm - map_matchmakingcategory [" + m_Map->GetMapMatchMakingCategory( ) + "] found but matchmaking can only be used with fixed player settings, matchmaking disabled" );
else
{
CONSOLE_Print( "[GHOST] autohostmm - map_matchmakingcategory [" + m_Map->GetMapMatchMakingCategory( ) + "] found, matchmaking enabled" );
m_CurrentGame->SetMatchMaking( true );
m_CurrentGame->SetMinimumScore( m_AutoHostMinimumScore );
m_CurrentGame->SetMaximumScore( m_AutoHostMaximumScore );
}
}
else
CONSOLE_Print( "[GHOST] autohostmm - map_matchmakingcategory not found, matchmaking disabled" );
}
}
}
else
{
CONSOLE_Print( "[GHOST] stopped auto hosting, next game name [" + GameName + "] is too long (the maximum is 31 characters)" );
m_AutoHostGameName.clear( );
m_AutoHostOwner.clear( );
m_AutoHostServer.clear( );
m_AutoHostMaximumGames = 0;
m_AutoHostAutoStartPlayers = 0;
m_AutoHostMatchMaking = false;
m_AutoHostMinimumScore = 0.0;
m_AutoHostMaximumScore = 0.0;
}
}
else
{
CONSOLE_Print( "[GHOST] stopped auto hosting, map config file [" + m_AutoHostMap->GetCFGFile( ) + "] is invalid" );
m_AutoHostGameName.clear( );
m_AutoHostOwner.clear( );
m_AutoHostServer.clear( );
m_AutoHostMaximumGames = 0;
m_AutoHostAutoStartPlayers = 0;
m_AutoHostMatchMaking = false;
m_AutoHostMinimumScore = 0.0;
m_AutoHostMaximumScore = 0.0;
}
}
m_LastAutoHostTime = GetTime( );
}
return m_Exiting || AdminExit || BNETExit;
}
void CGHost :: EventBNETConnecting( CBNET *bnet )
{
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->ConnectingToBNET( bnet->GetServer( ) ) );
if( m_CurrentGame )
m_CurrentGame->SendAllChat( m_Language->ConnectingToBNET( bnet->GetServer( ) ) );
}
void CGHost :: EventBNETConnected( CBNET *bnet )
{
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->ConnectedToBNET( bnet->GetServer( ) ) );
if( m_CurrentGame )
m_CurrentGame->SendAllChat( m_Language->ConnectedToBNET( bnet->GetServer( ) ) );
}
void CGHost :: EventBNETDisconnected( CBNET *bnet )
{
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->DisconnectedFromBNET( bnet->GetServer( ) ) );
if( m_CurrentGame )
m_CurrentGame->SendAllChat( m_Language->DisconnectedFromBNET( bnet->GetServer( ) ) );
}
void CGHost :: EventBNETLoggedIn( CBNET *bnet )
{
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->LoggedInToBNET( bnet->GetServer( ) ) );
if( m_CurrentGame )
m_CurrentGame->SendAllChat( m_Language->LoggedInToBNET( bnet->GetServer( ) ) );
}
void CGHost :: EventBNETGameRefreshed( CBNET *bnet )
{
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->BNETGameHostingSucceeded( bnet->GetServer( ) ) );
if( m_CurrentGame )
m_CurrentGame->EventGameRefreshed( bnet->GetServer( ) );
}
void CGHost :: EventBNETGameRefreshFailed( CBNET *bnet )
{
if( m_CurrentGame )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
(*i)->QueueChatCommand( m_Language->UnableToCreateGameTryAnotherName( bnet->GetServer( ), m_CurrentGame->GetGameName( ) ) );
if( (*i)->GetServer( ) == m_CurrentGame->GetCreatorServer( ) )
(*i)->QueueChatCommand( m_Language->UnableToCreateGameTryAnotherName( bnet->GetServer( ), m_CurrentGame->GetGameName( ) ), m_CurrentGame->GetCreatorName( ), true );
}
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->BNETGameHostingFailed( bnet->GetServer( ), m_CurrentGame->GetGameName( ) ) );
m_CurrentGame->SendAllChat( m_Language->UnableToCreateGameTryAnotherName( bnet->GetServer( ), m_CurrentGame->GetGameName( ) ) );
// we take the easy route and simply close the lobby if a refresh fails
// it's possible at least one refresh succeeded and therefore the game is still joinable on at least one battle.net (plus on the local network) but we don't keep track of that
// we only close the game if it has no players since we support game rehosting (via !priv and !pub in the lobby)
if( m_CurrentGame->GetNumHumanPlayers( ) == 0 )
m_CurrentGame->SetExiting( true );
m_CurrentGame->SetRefreshError( true );
}
}
void CGHost :: EventBNETConnectTimedOut( CBNET *bnet )
{
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->ConnectingToBNETTimedOut( bnet->GetServer( ) ) );
if( m_CurrentGame )
m_CurrentGame->SendAllChat( m_Language->ConnectingToBNETTimedOut( bnet->GetServer( ) ) );
}
void CGHost :: EventBNETWhisper( CBNET *bnet, string user, string message )
{
if( m_AdminGame )
{
m_AdminGame->SendAdminChat( "[W: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
if( m_CurrentGame )
m_CurrentGame->SendLocalAdminChat( "[W: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
for( vector:: iterator i = m_Games.begin( ); i != m_Games.end( ); i++ )
(*i)->SendLocalAdminChat( "[W: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
}
}
void CGHost :: EventBNETChat( CBNET *bnet, string user, string message )
{
if( m_AdminGame )
{
m_AdminGame->SendAdminChat( "[L: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
if( m_CurrentGame )
m_CurrentGame->SendLocalAdminChat( "[L: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
for( vector:: iterator i = m_Games.begin( ); i != m_Games.end( ); i++ )
(*i)->SendLocalAdminChat( "[L: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
}
}
void CGHost :: EventBNETEmote( CBNET *bnet, string user, string message )
{
if( m_AdminGame )
{
m_AdminGame->SendAdminChat( "[E: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
if( m_CurrentGame )
m_CurrentGame->SendLocalAdminChat( "[E: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
for( vector:: iterator i = m_Games.begin( ); i != m_Games.end( ); i++ )
(*i)->SendLocalAdminChat( "[E: " + bnet->GetServerAlias( ) + "] [" + user + "] " + message );
}
}
void CGHost :: EventGameDeleted( CBaseGame *game )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
(*i)->QueueChatCommand( m_Language->GameIsOver( game->GetDescription( ) ) );
if( (*i)->GetServer( ) == game->GetCreatorServer( ) )
(*i)->QueueChatCommand( m_Language->GameIsOver( game->GetDescription( ) ), game->GetCreatorName( ), true );
}
}
void CGHost :: ReloadConfigs( )
{
CConfig CFG;
CFG.Read( "default.cfg" );
CFG.Read( gCFGFile );
SetConfigs( &CFG );
}
void CGHost :: SetConfigs( CConfig *CFG )
{
// this doesn't set EVERY config value since that would potentially require reconfiguring the battle.net connections
// it just set the easily reloadable values
m_LanguageFile = CFG->GetString( "bot_language", "language.cfg" );
delete m_Language;
m_Language = new CLanguage( m_LanguageFile );
m_Warcraft3Path = UTIL_AddPathSeperator( CFG->GetString( "bot_war3path", "C:\\Program Files\\Warcraft III\\" ) );
m_BindAddress = CFG->GetString( "bot_bindaddress", string( ) );
m_ReconnectWaitTime = CFG->GetInt( "bot_reconnectwaittime", 3 );
m_MaxGames = CFG->GetInt( "bot_maxgames", 5 );
string BotCommandTrigger = CFG->GetString( "bot_commandtrigger", "!" );
if( BotCommandTrigger.empty( ) )
BotCommandTrigger = "!";
m_CommandTrigger = BotCommandTrigger[0];
m_MapCFGPath = UTIL_AddPathSeperator( CFG->GetString( "bot_mapcfgpath", string( ) ) );
m_SaveGamePath = UTIL_AddPathSeperator( CFG->GetString( "bot_savegamepath", string( ) ) );
m_MapPath = UTIL_AddPathSeperator( CFG->GetString( "bot_mappath", string( ) ) );
m_SaveReplays = CFG->GetInt( "bot_savereplays", 0 ) == 0 ? false : true;
m_ReplayPath = UTIL_AddPathSeperator( CFG->GetString( "bot_replaypath", string( ) ) );
m_VirtualHostName = CFG->GetString( "bot_virtualhostname", "|cFF4080C0GHost" );
m_HideIPAddresses = CFG->GetInt( "bot_hideipaddresses", 0 ) == 0 ? false : true;
m_CheckMultipleIPUsage = CFG->GetInt( "bot_checkmultipleipusage", 1 ) == 0 ? false : true;
if( m_VirtualHostName.size( ) > 15 )
{
m_VirtualHostName = "|cFF4080C0GHost";
CONSOLE_Print( "[GHOST] warning - bot_virtualhostname is longer than 15 characters, using default virtual host name" );
}
m_SpoofChecks = CFG->GetInt( "bot_spoofchecks", 2 );
m_RequireSpoofChecks = CFG->GetInt( "bot_requirespoofchecks", 0 ) == 0 ? false : true;
m_ReserveAdmins = CFG->GetInt( "bot_reserveadmins", 1 ) == 0 ? false : true;
m_RefreshMessages = CFG->GetInt( "bot_refreshmessages", 0 ) == 0 ? false : true;
m_AutoLock = CFG->GetInt( "bot_autolock", 0 ) == 0 ? false : true;
m_AutoSave = CFG->GetInt( "bot_autosave", 0 ) == 0 ? false : true;
m_AllowDownloads = CFG->GetInt( "bot_allowdownloads", 0 );
m_PingDuringDownloads = CFG->GetInt( "bot_pingduringdownloads", 0 ) == 0 ? false : true;
m_MaxDownloaders = CFG->GetInt( "bot_maxdownloaders", 3 );
m_MaxDownloadSpeed = CFG->GetInt( "bot_maxdownloadspeed", 100 );
m_LCPings = CFG->GetInt( "bot_lcpings", 1 ) == 0 ? false : true;
m_AutoKickPing = CFG->GetInt( "bot_autokickping", 400 );
m_BanMethod = CFG->GetInt( "bot_banmethod", 1 );
m_IPBlackListFile = CFG->GetString( "bot_ipblacklistfile", "ipblacklist.txt" );
m_LobbyTimeLimit = CFG->GetInt( "bot_lobbytimelimit", 10 );
m_Latency = CFG->GetInt( "bot_latency", 100 );
m_SyncLimit = CFG->GetInt( "bot_synclimit", 50 );
m_VoteKickAllowed = CFG->GetInt( "bot_votekickallowed", 1 ) == 0 ? false : true;
m_VoteKickPercentage = CFG->GetInt( "bot_votekickpercentage", 100 );
if( m_VoteKickPercentage > 100 )
{
m_VoteKickPercentage = 100;
CONSOLE_Print( "[GHOST] warning - bot_votekickpercentage is greater than 100, using 100 instead" );
}
m_MOTDFile = CFG->GetString( "bot_motdfile", "motd.txt" );
m_GameLoadedFile = CFG->GetString( "bot_gameloadedfile", "gameloaded.txt" );
m_GameOverFile = CFG->GetString( "bot_gameoverfile", "gameover.txt" );
m_LocalAdminMessages = CFG->GetInt( "bot_localadminmessages", 1 ) == 0 ? false : true;
m_TCPNoDelay = CFG->GetInt( "tcp_nodelay", 0 ) == 0 ? false : true;
m_MatchMakingMethod = CFG->GetInt( "bot_matchmakingmethod", 1 );
}
void CGHost :: ExtractScripts( )
{
string PatchMPQFileName = m_Warcraft3Path + "War3Patch.mpq";
HANDLE PatchMPQ;
if( SFileOpenArchive( PatchMPQFileName.c_str( ), 0, MPQ_OPEN_FORCE_MPQ_V1, &PatchMPQ ) )
{
CONSOLE_Print( "[GHOST] loading MPQ file [" + PatchMPQFileName + "]" );
HANDLE SubFile;
// common.j
if( SFileOpenFileEx( PatchMPQ, "Scripts\\common.j", 0, &SubFile ) )
{
uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
if( FileLength > 0 && FileLength != 0xFFFFFFFF )
{
char *SubFileData = new char[FileLength];
DWORD BytesRead = 0;
if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) )
{
CONSOLE_Print( "[GHOST] extracting Scripts\\common.j from MPQ file to [" + m_MapCFGPath + "common.j]" );
UTIL_FileWrite( m_MapCFGPath + "common.j", (unsigned char *)SubFileData, BytesRead );
}
else
CONSOLE_Print( "[GHOST] warning - unable to extract Scripts\\common.j from MPQ file" );
delete [] SubFileData;
}
SFileCloseFile( SubFile );
}
else
CONSOLE_Print( "[GHOST] couldn't find Scripts\\common.j in MPQ file" );
// blizzard.j
if( SFileOpenFileEx( PatchMPQ, "Scripts\\blizzard.j", 0, &SubFile ) )
{
uint32_t FileLength = SFileGetFileSize( SubFile, NULL );
if( FileLength > 0 && FileLength != 0xFFFFFFFF )
{
char *SubFileData = new char[FileLength];
DWORD BytesRead = 0;
if( SFileReadFile( SubFile, SubFileData, FileLength, &BytesRead ) )
{
CONSOLE_Print( "[GHOST] extracting Scripts\\blizzard.j from MPQ file to [" + m_MapCFGPath + "blizzard.j]" );
UTIL_FileWrite( m_MapCFGPath + "blizzard.j", (unsigned char *)SubFileData, BytesRead );
}
else
CONSOLE_Print( "[GHOST] warning - unable to extract Scripts\\blizzard.j from MPQ file" );
delete [] SubFileData;
}
SFileCloseFile( SubFile );
}
else
CONSOLE_Print( "[GHOST] couldn't find Scripts\\blizzard.j in MPQ file" );
SFileCloseArchive( PatchMPQ );
}
else
CONSOLE_Print( "[GHOST] warning - unable to load MPQ file [" + PatchMPQFileName + "] - error code " + UTIL_ToString( GetLastError( ) ) );
}
void CGHost :: LoadIPToCountryData( )
{
ifstream in;
in.open( "ip-to-country.csv" );
if( in.fail( ) )
CONSOLE_Print( "[GHOST] warning - unable to read file [ip-to-country.csv], iptocountry data not loaded" );
else
{
CONSOLE_Print( "[GHOST] started loading [ip-to-country.csv]" );
// the begin and commit statements are optimizations
// we're about to insert ~4 MB of data into the database so if we allow the database to treat each insert as a transaction it will take a LONG time
// todotodo: handle begin/commit failures a bit more gracefully
if( !m_DBLocal->Begin( ) )
CONSOLE_Print( "[GHOST] warning - failed to begin local database transaction, iptocountry data not loaded" );
else
{
unsigned char Percent = 0;
string Line;
string IP1;
string IP2;
string Country;
CSVParser parser;
// get length of file for the progress meter
in.seekg( 0, ios :: end );
uint32_t FileLength = in.tellg( );
in.seekg( 0, ios :: beg );
while( !in.eof( ) )
{
getline( in, Line );
if( Line.empty( ) )
continue;
parser << Line;
parser >> IP1;
parser >> IP2;
parser >> Country;
m_DBLocal->FromAdd( UTIL_ToUInt32( IP1 ), UTIL_ToUInt32( IP2 ), Country );
// it's probably going to take awhile to load the iptocountry data (~10 seconds on my 3.2 GHz P4 when using SQLite3)
// so let's print a progress meter just to keep the user from getting worried
unsigned char NewPercent = (unsigned char)( (float)in.tellg( ) / FileLength * 100 );
if( NewPercent != Percent && NewPercent % 10 == 0 )
{
Percent = NewPercent;
CONSOLE_Print( "[GHOST] iptocountry data: " + UTIL_ToString( Percent ) + "% loaded" );
}
}
if( !m_DBLocal->Commit( ) )
CONSOLE_Print( "[GHOST] warning - failed to commit local database transaction, iptocountry data not loaded" );
else
CONSOLE_Print( "[GHOST] finished loading [ip-to-country.csv]" );
}
in.close( );
}
}
void CGHost :: CreateGame( CMap *map, unsigned char gameState, bool saveGame, string gameName, string ownerName, string creatorName, string creatorServer, bool whisper )
{
if( !m_Enabled )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetServer( ) == creatorServer )
(*i)->QueueChatCommand( m_Language->UnableToCreateGameDisabled( gameName ), creatorName, whisper );
}
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->UnableToCreateGameDisabled( gameName ) );
return;
}
if( gameName.size( ) > 31 )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetServer( ) == creatorServer )
(*i)->QueueChatCommand( m_Language->UnableToCreateGameNameTooLong( gameName ), creatorName, whisper );
}
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->UnableToCreateGameNameTooLong( gameName ) );
return;
}
if( !map->GetValid( ) )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetServer( ) == creatorServer )
(*i)->QueueChatCommand( m_Language->UnableToCreateGameInvalidMap( gameName ), creatorName, whisper );
}
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->UnableToCreateGameInvalidMap( gameName ) );
return;
}
if( saveGame )
{
if( !m_SaveGame->GetValid( ) )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetServer( ) == creatorServer )
(*i)->QueueChatCommand( m_Language->UnableToCreateGameInvalidSaveGame( gameName ), creatorName, whisper );
}
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->UnableToCreateGameInvalidSaveGame( gameName ) );
return;
}
string MapPath1 = m_SaveGame->GetMapPath( );
string MapPath2 = map->GetMapPath( );
transform( MapPath1.begin( ), MapPath1.end( ), MapPath1.begin( ), (int(*)(int))tolower );
transform( MapPath2.begin( ), MapPath2.end( ), MapPath2.begin( ), (int(*)(int))tolower );
if( MapPath1 != MapPath2 )
{
CONSOLE_Print( "[GHOST] path mismatch, saved game path is [" + MapPath1 + "] but map path is [" + MapPath2 + "]" );
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetServer( ) == creatorServer )
(*i)->QueueChatCommand( m_Language->UnableToCreateGameSaveGameMapMismatch( gameName ), creatorName, whisper );
}
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->UnableToCreateGameSaveGameMapMismatch( gameName ) );
return;
}
if( m_EnforcePlayers.empty( ) )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetServer( ) == creatorServer )
(*i)->QueueChatCommand( m_Language->UnableToCreateGameMustEnforceFirst( gameName ), creatorName, whisper );
}
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->UnableToCreateGameMustEnforceFirst( gameName ) );
return;
}
}
if( m_CurrentGame )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetServer( ) == creatorServer )
(*i)->QueueChatCommand( m_Language->UnableToCreateGameAnotherGameInLobby( gameName, m_CurrentGame->GetDescription( ) ), creatorName, whisper );
}
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->UnableToCreateGameAnotherGameInLobby( gameName, m_CurrentGame->GetDescription( ) ) );
return;
}
if( m_Games.size( ) >= m_MaxGames )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetServer( ) == creatorServer )
(*i)->QueueChatCommand( m_Language->UnableToCreateGameMaxGamesReached( gameName, UTIL_ToString( m_MaxGames ) ), creatorName, whisper );
}
if( m_AdminGame )
m_AdminGame->SendAllChat( m_Language->UnableToCreateGameMaxGamesReached( gameName, UTIL_ToString( m_MaxGames ) ) );
return;
}
CONSOLE_Print( "[GHOST] creating game [" + gameName + "]" );
if( saveGame )
m_CurrentGame = new CGame( this, map, m_SaveGame, m_HostPort, gameState, gameName, ownerName, creatorName, creatorServer );
else
m_CurrentGame = new CGame( this, map, NULL, m_HostPort, gameState, gameName, ownerName, creatorName, creatorServer );
// todotodo: check if listening failed and report the error to the user
if( m_SaveGame )
{
m_CurrentGame->SetEnforcePlayers( m_EnforcePlayers );
m_EnforcePlayers.clear( );
}
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( whisper && (*i)->GetServer( ) == creatorServer )
{
// note that we send this whisper only on the creator server
if( gameState == GAME_PRIVATE )
(*i)->QueueChatCommand( m_Language->CreatingPrivateGame( gameName, ownerName ), creatorName, whisper );
else if( gameState == GAME_PUBLIC )
(*i)->QueueChatCommand( m_Language->CreatingPublicGame( gameName, ownerName ), creatorName, whisper );
}
else
{
// note that we send this chat message on all other bnet servers
if( gameState == GAME_PRIVATE )
(*i)->QueueChatCommand( m_Language->CreatingPrivateGame( gameName, ownerName ) );
else if( gameState == GAME_PUBLIC )
(*i)->QueueChatCommand( m_Language->CreatingPublicGame( gameName, ownerName ) );
}
if( saveGame )
(*i)->QueueGameCreate( gameState, gameName, string( ), map, m_SaveGame, m_CurrentGame->GetHostCounter( ) );
else
(*i)->QueueGameCreate( gameState, gameName, string( ), map, NULL, m_CurrentGame->GetHostCounter( ) );
}
if( m_AdminGame )
{
if( gameState == GAME_PRIVATE )
m_AdminGame->SendAllChat( m_Language->CreatingPrivateGame( gameName, ownerName ) );
else if( gameState == GAME_PUBLIC )
m_AdminGame->SendAllChat( m_Language->CreatingPublicGame( gameName, ownerName ) );
}
// if we're creating a private game we don't need to send any game refresh messages so we can rejoin the chat immediately
// unfortunately this doesn't work on PVPGN servers because they consider an enterchat message to be a gameuncreate message when in a game
// so don't rejoin the chat if we're using PVPGN
if( gameState == GAME_PRIVATE )
{
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetPasswordHashType( ) != "pvpgn" )
(*i)->QueueEnterChat( );
}
}
// hold friends and/or clan members
for( vector:: iterator i = m_BNETs.begin( ); i != m_BNETs.end( ); i++ )
{
if( (*i)->GetHoldFriends( ) )
(*i)->HoldFriends( m_CurrentGame );
if( (*i)->GetHoldClan( ) )
(*i)->HoldClan( m_CurrentGame );
}
}
balanarursa- New Member
- Posts : 4
Join date : 2013-01-10
Re: Edit version Ghost++
like where to replace:
Version: Ghost + + Version 1.17 ([You must be registered and logged in to see this link.]
not just version 1.17 only.
how to change all this:
Version: Ghost + + Version 1.17 ([You must be registered and logged in to see this link.]
-------------------------------------------------- --------
I do not have a Garena id.
Version: Ghost + + Version 1.17 ([You must be registered and logged in to see this link.]
not just version 1.17 only.
how to change all this:
Version: Ghost + + Version 1.17 ([You must be registered and logged in to see this link.]
-------------------------------------------------- --------
I do not have a Garena id.
balanarursa- New Member
- Posts : 4
Join date : 2013-01-10
Re: Edit version Ghost++
No need to recompile, just go to language.cfg and you will find "Version", there you can change it to whatever you want.
Re
yeah i downloaded which mysql do i have to use ? 2.5 ? is it just ghost one ? because i nromaly host with ghost++ cuase i use multiple hostbots ^^ and the gamelistscript is included ?
Tadashi- New Member
- Posts : 8
Join date : 2013-02-24
Re: Edit version Ghost++
use the latest for mysql db, it's ghostone. You haven't discovered the package (extracted from GenBot .7z file?) gamelist php is included.
Re: Edit version Ghost++
i mean the patch is installed i already have the official php script but i wanted to modify the ghost folder, so your version include the gamelist patch ? yes or no ?
Tadashi- New Member
- Posts : 8
Join date : 2013-02-24
Re: Edit version Ghost++
lol but is laggy like hell ^^ It laggs during the game ping of 400 and normal bots with ghost++ of 23 ^^
Tadashi- New Member
- Posts : 8
Join date : 2013-02-24
Re: Edit version Ghost++
Maybe you turned LC style on ghost++?
Here we have it off for actual ping. e.g. 400 is actual ping due to your location (where are you living?), not 200 with LC style.
# use LC style pings (divide actual pings by two)
bot_lcpings = 0
We are testing 6 bots (=>max 10 games) at a time as to see how it could work now.
Normally we host 4 bots, max 7 games at once since broadband here is with 11 Mbps upload. It should be higher for more game quality but as said, we are testing if 6 bots can be handled.
Also, about 40 Garena rooms are connected. Maybe the number of rooms should be reduced.
Here we have it off for actual ping. e.g. 400 is actual ping due to your location (where are you living?), not 200 with LC style.
# use LC style pings (divide actual pings by two)
bot_lcpings = 0
We are testing 6 bots (=>max 10 games) at a time as to see how it could work now.
Normally we host 4 bots, max 7 games at once since broadband here is with 11 Mbps upload. It should be higher for more game quality but as said, we are testing if 6 bots can be handled.
Also, about 40 Garena rooms are connected. Maybe the number of rooms should be reduced.
Similar topics
» edit ghost.exe
» Strange Invalid version error after changing to bnet version 26
» help about compile ghost
» Gen Modded Ghost One and Boost 1.52?
» Patching & Recompiling Ghost One
» Strange Invalid version error after changing to bnet version 26
» help about compile ghost
» Gen Modded Ghost One and Boost 1.52?
» Patching & Recompiling Ghost One
ProGamingEXP - PROFESSIONAL GAMING EXPERIENCE :: Game Mode Section - Nơi hỗ trợ về map & gameplay :: Gameplay discussion
Page 1 of 1
Permissions in this forum:
You cannot reply to topics in this forum
|
|
Tue Nov 24, 2015 6:08 am by kingsamurie
» Strange Invalid version error after changing to bnet version 26
Wed Oct 07, 2015 9:26 am by Gen
» Perfect-G 412KANAKO Gallery
Wed Sep 23, 2015 11:32 pm by Guest
» Fully Naked Pics Of Jessica Gomes
Tue Jul 07, 2015 5:57 pm by Guest
» The One Tree Hill Season 3
Sun Jun 07, 2015 11:27 am by Guest
» The One Tree Hill Season 3
Sun Jun 07, 2015 11:20 am by Guest
» When i host i get this error using Gen MOdded Ghostone(Latest ver)
Mon Apr 21, 2014 12:00 am by kingsamurie
» When i type !map on any kind of map i get this error
Sun Apr 20, 2014 11:59 pm by kingsamurie
» Players get kicked
Thu Oct 17, 2013 1:51 am by keisersoze
» Where to find Games!
Tue Oct 15, 2013 11:22 am by kokkis