Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 16 Mar 2009 20:15:33 GMT
From:      Dmitry Yashin <yashin.dm@gmail.com>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   ports/132707: New port: games/GHost++, a Warcraft 3 game hosting bot
Message-ID:  <200903162015.n2GKFX9n041050@www.freebsd.org>
Resent-Message-ID: <200903162020.n2GKK1Nf057747@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         132707
>Category:       ports
>Synopsis:       New port: games/GHost++, a Warcraft 3 game hosting bot
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Mar 16 20:20:00 UTC 2009
>Closed-Date:
>Last-Modified:
>Originator:     Dmitry Yashin
>Release:        FreeBSD 7.0-RELEASE
>Organization:
>Environment:
7.0-RELEASE FreeBSD 7.0-RELEASE #0: Sun Feb 24 19:59:52 UTC 2008 root@logan.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC i386
>Description:
GHost++ is a Warcraft 3 game hosting bot. Some features include:

* run on Windows and Linux
* connect to official battle.net and PVPGN realms
* connect to multiple realms at the same time
* host battle.net games and LAN games
* host battle.net games across realms (players from different realms can play in the same game)
* auto refresh games on battle.net
* auto host and start games without admin interaction
* can be controlled via LAN (you only need one set of CD keys)
* host multiple games at the same time
* host melee maps and custom maps
* high speed map downloads (with adjustable maximum download speed)
* host saved games
* record complete replays of hosted games (including allied and private chat from all players)
* determine player pings and countries of origin
* player bans
* player statistics
* kick players from games
* adjustable game latency
* adjustable lag screen
* mute global chat in games
* mute individual players in lobbies and games
* and much more...

http://code.google.com/p/ghostplusplus/

Thx valindar to MySql patch (http://forum.codelain.com/index.php?topic=3429)
>How-To-Repeat:

>Fix:


Patch attached with submission follows:

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	/usr/ports/games/ghost++
#	/usr/ports/games/ghost++/files
#	/usr/ports/games/ghost++/files/patch-StormPortLinux.cpp
#	/usr/ports/games/ghost++/files/patch-bncsutil_makefile
#	/usr/ports/games/ghost++/files/patch-ghostdb.h
#	/usr/ports/games/ghost++/files/patch-ghost.cpp
#	/usr/ports/games/ghost++/files/patch-ghostdbmysql.cpp
#	/usr/ports/games/ghost++/files/patch-ghostdbmysql.h
#	/usr/ports/games/ghost++/files/patch-ghost.cfg
#	/usr/ports/games/ghost++/files/patch-ghost_makefile
#	/usr/ports/games/ghost++/Makefile
#	/usr/ports/games/ghost++/distinfo
#	/usr/ports/games/ghost++/pkg-descr
#	/usr/ports/games/ghost++/pkg-plist
#
echo c - /usr/ports/games/ghost++
mkdir -p /usr/ports/games/ghost++ > /dev/null 2>&1
echo c - /usr/ports/games/ghost++/files
mkdir -p /usr/ports/games/ghost++/files > /dev/null 2>&1
echo x - /usr/ports/games/ghost++/files/patch-StormPortLinux.cpp
sed 's/^X//' >/usr/ports/games/ghost++/files/patch-StormPortLinux.cpp << 'END-of-/usr/ports/games/ghost++/files/patch-StormPortLinux.cpp'
X*** StormLib/stormlib/StormPortLinux.cpp.orig	Fri Mar 13 21:34:36 2009
X--- StormLib/stormlib/StormPortLinux.cpp	Fri Mar 13 21:36:36 2009
X***************
X*** 24,29 ****
X--- 24,30 ----
X  *
X  ********************************************************************/
X  
X+ #define O_LARGEFILE 0100000
X  #ifndef _WIN32
X  #include "StormPort.h"
X  
X***************
X*** 104,111 ****
X          return 0xffffffff;
X      }
X  
X!     struct stat64 fileinfo;
X!     fstat64((intptr_t)hFile, &fileinfo);
X      
X      // Fix by Ladik: If "ulOffSetHigh" is not NULL, it needs to be set 
X      // to higher 32 bits of a file size.
X--- 105,112 ----
X          return 0xffffffff;
X      }
X  
X!     struct stat fileinfo;
X!     fstat((intptr_t)hFile, &fileinfo);
X      
X      // Fix by Ladik: If "ulOffSetHigh" is not NULL, it needs to be set 
X      // to higher 32 bits of a file size.
X***************
X*** 118,129 ****
X  
X  DWORD SetFilePointer(HANDLE hFile, LONG lOffSetLow, LONG *pOffSetHigh, DWORD ulMethod)
X  {
X!     off64_t nFileOffset = (DWORD)lOffSetLow;
X  
X      if(pOffSetHigh != NULL)
X!         nFileOffset |= (*(off64_t *)pOffSetHigh) << 32;
X  
X!     return lseek64((intptr_t)hFile, nFileOffset, ulMethod);
X  }
X  
X  BOOL SetEndOfFile(HANDLE hFile)
X--- 119,130 ----
X  
X  DWORD SetFilePointer(HANDLE hFile, LONG lOffSetLow, LONG *pOffSetHigh, DWORD ulMethod)
X  {
X!     off_t nFileOffset = (DWORD)lOffSetLow;
X  
X      if(pOffSetHigh != NULL)
X!         nFileOffset |= (*(off_t *)pOffSetHigh) << 32;
X  
X!     return lseek((intptr_t)hFile, nFileOffset, ulMethod);
X  }
X  
X  BOOL SetEndOfFile(HANDLE hFile)
END-of-/usr/ports/games/ghost++/files/patch-StormPortLinux.cpp
echo x - /usr/ports/games/ghost++/files/patch-bncsutil_makefile
sed 's/^X//' >/usr/ports/games/ghost++/files/patch-bncsutil_makefile << 'END-of-/usr/ports/games/ghost++/files/patch-bncsutil_makefile'
X*** bncsutil/src/bncsutil/Makefile.orig	Fri Mar 13 21:21:48 2009
X--- bncsutil/src/bncsutil/Makefile	Fri Mar 13 21:22:27 2009
X***************
X*** 6,12 ****
X  CCFLAGS = -Wall -O3 -I ../ -Wno-multichar -fPIC
X  CCOBJ = nls.o pe.o sha1.o stack.o
X  
X! LDFLAGS = -shared -lgmp
X  
X  TARGET = libbncsutil.so
X  
X--- 6,12 ----
X  CCFLAGS = -Wall -O3 -I ../ -Wno-multichar -fPIC
X  CCOBJ = nls.o pe.o sha1.o stack.o
X  
X! LDFLAGS = -shared -lgmp -L/usr/local/lib
X  
X  TARGET = libbncsutil.so
X  
END-of-/usr/ports/games/ghost++/files/patch-bncsutil_makefile
echo x - /usr/ports/games/ghost++/files/patch-ghostdb.h
sed 's/^X//' >/usr/ports/games/ghost++/files/patch-ghostdb.h << 'END-of-/usr/ports/games/ghost++/files/patch-ghostdb.h'
X*** ghost/ghostdb.h.orig	Fri Mar 13 22:01:25 2009
X--- ghost/ghostdb.h	Fri Mar 13 22:04:39 2009
X***************
X*** 66,71 ****
X--- 66,73 ----
X  	virtual string FromCheck( uint32_t ip );
X  	virtual bool FromAdd( uint32_t ip1, uint32_t ip2, string country );
X  	virtual bool DownloadAdd( string map, uint32_t mapsize, string name, string ip, uint32_t spoofed, string spoofedrealm, uint32_t downloadtime );
X+ 	virtual bool LoadIPData( ) { return true; }
X+ 	virtual void Update( ) {  }
X  };
X  
X  //
END-of-/usr/ports/games/ghost++/files/patch-ghostdb.h
echo x - /usr/ports/games/ghost++/files/patch-ghost.cpp
sed 's/^X//' >/usr/ports/games/ghost++/files/patch-ghost.cpp << 'END-of-/usr/ports/games/ghost++/files/patch-ghost.cpp'
X*** ghost/ghost.cpp.orig	Fri Mar 13 21:49:45 2009
X--- ghost/ghost.cpp	Fri Mar 13 21:57:53 2009
X***************
X*** 27,32 ****
X--- 27,33 ----
X  #include "socket.h"
X  #include "ghostdb.h"
X  #include "ghostdbsqlite.h"
X+ #include "ghostdbmysql.h"
X  #include "bnet.h"
X  #include "map.h"
X  #include "packed.h"
X***************
X*** 249,258 ****
X  	m_CRC = new CCRC32( );
X  	m_CRC->Initialize( );
X  	m_CurrentGame = NULL;
X! 	m_DB = new CGHostDBSQLite( CFG );
X  	m_Exiting = false;
X  	m_Enabled = true;
X! 	m_Version = "11.5";
X  	m_HostCounter = 1;
X  	m_AutoHostMaximumGames = 0;
X  	m_AutoHostAutoStartPlayers = 0;
X--- 250,262 ----
X  	m_CRC = new CCRC32( );
X  	m_CRC->Initialize( );
X  	m_CurrentGame = NULL;
X! 	if( CFG->GetString( "db_type", "sqlite3" ) == "mysql" )
X! 	    m_DB = new CGHostDBMySQL( CFG );
X! 	else
X! 	    m_DB = new CGHostDBSQLite( CFG );
X  	m_Exiting = false;
X  	m_Enabled = true;
X! 	m_Version = "11.5 MySQL (FreeBSD)";
X  	m_HostCounter = 1;
X  	m_AutoHostMaximumGames = 0;
X  	m_AutoHostAutoStartPlayers = 0;
X***************
X*** 397,403 ****
X  
X  	// load the iptocountry data
X  
X! 	LoadIPToCountryData( );
X  
X  	// create the admin game
X  
X--- 401,411 ----
X  
X  	// load the iptocountry data
X  
X! 	// We must check if the DB has an error, if it does calling LoadIPData( )
X! 	// using MySQL will crash as the connection object does not exist.
X! 	if( !m_DB->HasError( ) )
X! 	    if( m_DB->LoadIPData( ) )
X! 		LoadIPToCountryData( );
X  
X  	// create the admin game
X  
X***************
X*** 449,454 ****
X--- 457,465 ----
X  		return true;
X  	}
X  
X+ 	if( m_DB )
X+ 	    m_DB->Update( );
X+ 
X  	unsigned int NumFDs = 0;
X  
X  	// take every socket we own and throw it in one giant select statement so we can block on all sockets
END-of-/usr/ports/games/ghost++/files/patch-ghost.cpp
echo x - /usr/ports/games/ghost++/files/patch-ghostdbmysql.cpp
sed 's/^X//' >/usr/ports/games/ghost++/files/patch-ghostdbmysql.cpp << 'END-of-/usr/ports/games/ghost++/files/patch-ghostdbmysql.cpp'
X*** ghost/ghostdbmysql.cpp.orig	Fri Mar 13 22:23:01 2009
X--- ghost/ghostdbmysql.cpp	Fri Feb 13 16:08:00 2009
X***************
X*** 0 ****
X--- 1,841 ----
X+ /*
X+ 
X+    Copyright 2009 Scott Gillespie
X+ 
X+    Licensed under the Apache License, Version 2.0 (the "License");
X+    you may not use this file except in compliance with the License.
X+    You may obtain a copy of the License at
X+ 
X+        http://www.apache.org/licenses/LICENSE-2.0
X+ 
X+    Unless required by applicable law or agreed to in writing, software
X+    distributed under the License is distributed on an "AS IS" BASIS,
X+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
X+    See the License for the specific language governing permissions and
X+    limitations under the License.
X+ 
X+ 
X+ */
X+ 
X+ #include "ghost.h"
X+ #include "util.h"
X+ #include "config.h"
X+ #include "ghostdb.h"
X+ #include "ghostdbmysql.h"
X+ 
X+ #include <mysqld_error.h>
X+ 
X+ //
X+ // CMySQL
X+ //
X+ 
X+ CMySQL :: CMySQL( string db, string server, string user, string pass, int port )
X+ {
X+ 	m_DB = db;
X+ 	m_Server = server;
X+ 	m_User = user;
X+ 	m_Pass = pass;
X+ 	m_Port = port;
X+ 
X+ 	m_Conn = new mysqlpp::Connection(false);
X+ 	Connect( );
X+ }
X+ 
X+ CMySQL :: ~CMySQL( )
X+ {
X+ 	Disconnect( );
X+ }
X+ 
X+ bool CMySQL :: Connect( )
X+ {
X+ 	CONSOLE_Print( "[MySQL++] Connecting to database." );
X+ 
X+ 	// It is necessary to destroy the connection object and recreate it
X+ 	// to re-establish a connection, a quirk of mysql++
X+ 	// This is not necessary the first time but we use the same function
X+ 	delete m_Conn;
X+ 	m_Conn = new mysqlpp::Connection(false);
X+ 
X+ 	if( m_Conn->connect( m_DB.c_str( ), m_Server.c_str( ), m_User.c_str( ), m_Pass.c_str( ), m_Port ) )
X+ 		return true;
X+ 	else
X+ 	{
X+ 		m_ConnError = m_Conn->error( );
X+ 		return false;
X+ 	}
X+ }
X+ 
X+ void CMySQL :: Disconnect( )
X+ {
X+ 	CONSOLE_Print( "[MySQL++] Disconnecting from database." );
X+ 
X+ 	m_Conn->disconnect( );
X+ }
X+ 
X+ bool CMySQL :: Exec( string query )
X+ {
X+ 
X+ 	bool Success = false;
X+ 	if( Connected( ) )
X+ 	{
X+ 		mysqlpp::Query m_Query =  m_Conn->query( query.c_str( ) );
X+ 		
X+ 		if( m_Query.exec( ) )
X+ 			Success = true;
X+ 		else
X+ 		{
X+ 			m_Error = m_Query.error( );
X+ 			m_Errnum = m_Query.errnum( );
X+ 		}
X+ 	}
X+ 
X+ 	return Success;
X+ }
X+ 
X+ bool CMySQL :: Store( string query )
X+ {
X+ 	if( Connected( ) )
X+ 	{
X+ 		mysqlpp::Query m_Query = m_Conn->query( query.c_str( ) );
X+ 
X+ 		if( m_Result = m_Query.store( ) )
X+ 			return true;
X+ 		else
X+ 		{
X+ 			m_Error = m_Query.error( );
X+ 			m_Errnum = m_Query.errnum( );
X+ 			return false;
X+ 		}
X+ 	}
X+ 	return false;
X+ }
X+ 
X+ bool CMySQL :: LoadIPData( )
X+ {
X+ 	if( Store( "SELECT COUNT(*) FROM iptocountry" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult result = GetResult( );
X+ 		if( atoi( result[0][0].c_str( ) ) == 0 )
X+ 			return true;
X+ 		else
X+ 			return false;
X+ 	}
X+ 	else
X+ 		return false;
X+ }
X+ 
X+ //
X+ // CGHostDBMySQL
X+ //
X+ 
X+ CGHostDBMySQL :: CGHostDBMySQL( CConfig *CFG ) : CGHostDB( CFG )
X+ {
X+ 	m_NextPing = GetTime( ) + 5;
X+ 	m_ServerError = false;
X+ 	string db = CFG->GetString( "db_mysql_database", "MYSQL_ERROR" );
X+ 	string hostname = CFG->GetString( "db_mysql_hostname", "MYSQL_ERROR" );
X+ 	string username = CFG->GetString( "db_mysql_username", "MYSQL_ERROR" );
X+ 	string password = CFG->GetString( "db_mysql_password", "MYSQL_ERROR" );
X+ 	string port = CFG->GetString( "db_mysql_port", "MYSQL_ERROR" );
X+ 
X+ 	if( db == "MYSQL_ERROR" || hostname == "MYSQL_ERROR" || username == "MYSQL_ERROR" || password == "MYSQL_ERROR" || port == "MYSQL_ERROR" )
X+ 	{
X+ 		CONSOLE_Print( "[MySQL++] One of/all MySQL config values are not set; fix config file." );
X+ 		m_HasError = true;
X+ 		m_Error = "error getting MySQL config values";
X+ 		return;
X+ 	}
X+ 
X+ 	m_DB = new CMySQL( db, hostname, username, password, atoi( port.c_str( ) ) );
X+ 
X+ 	// Establish the connection
X+ 
X+ 	if( m_DB->Connected( ) )
X+ 		CONSOLE_Print( "[MySQL++] Database connection successful." );
X+ 	else
X+ 	{
X+ 		CONSOLE_Print( "[MySQL++] Database connection failed: " + string( m_DB->GetConnError( ) ) );
X+ 		m_HasError = true;
X+ 		m_Error = "error connecting to database";
X+ 		return;
X+ 	}
X+ 
X+ 	int schemaNumber = 0;
X+ 	// Attempt to get the schema number from the database
X+ 
X+ 	if( m_DB->Store( "SELECT value FROM config WHERE name='schema_number'" ) )
X+ 	{
X+ 		CONSOLE_Print( "[MySQL++] Found schema number." );
X+ 		mysqlpp::StoreQueryResult res = m_DB->GetResult( );
X+ 		schemaNumber = res[0][0];
X+ 	}
X+ 	else if( m_DB->GetErrnum( ) == ER_NO_SUCH_TABLE )
X+ 		CONSOLE_Print( "[MySQL++] Database is empty, creating tables." );
X+ 	else
X+ 	{
X+ 		CONSOLE_Print( "[MySQL++] Failed to get schema number: " + string( m_DB->GetError( ) ) );
X+ 		m_HasError = true;
X+ 		m_Error = "unable to determine if database exists";
X+ 		return;
X+ 	}
X+ 
X+ 	if( schemaNumber == 0 )
X+ 	{
X+ 		// Create the config table
X+ 
X+ 		if( m_DB->Exec( "CREATE TABLE `config` ( `name` VARCHAR( 13 ) NOT NULL, `value` TINYINT NOT NULL, PRIMARY KEY( `name` ) )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Created table `config` successfully." );
X+ 		else
X+ 		{
X+ 			m_HasError = true;
X+ 			m_Error = "error creating `config` table";
X+ 			CONSOLE_Print( "[MySQL++] Failed to create table `config`: " + string( m_DB->GetError( ) ) );
X+ 			return;
X+ 		}
X+ 
X+ 		// Insert the Schema Number in the config table
X+ 
X+ 		if( m_DB->Exec( "INSERT INTO `config` ( `name`, `value` ) VALUES ( 'schema_number', 1 )" ) )
X+ 		{
X+ 			CONSOLE_Print( "[MySQL++] Successfully inserted schema number." );
X+ 			schemaNumber = 1;
X+ 		}
X+ 		else
X+ 		{
X+ 			m_HasError = true;
X+ 			m_Error = "error inserting schema number";
X+ 			CONSOLE_Print( "[MySQL++] Failed to insert schema number: " + string( m_DB->GetError( ) ) );
X+ 			return;
X+ 		}
X+ 
X+ 		// Create the admins table
X+ 
X+ 		if( m_DB->Exec( "CREATE TABLE `admins` ( `id` INTEGER AUTO_INCREMENT, `name` VARCHAR( 15 ) NOT NULL, `server` VARCHAR( 40 ) NOT NULL DEFAULT '', PRIMARY KEY( `id` ) )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Created table `admins` successfully." );
X+ 		else
X+ 		{
X+ 			m_HasError = true;
X+ 			m_Error = "error creating `admins` table";
X+ 			CONSOLE_Print( "[MySQL++] Failed to create table `admins`: " + string( m_DB->GetError( ) ) );
X+ 			return;
X+ 		}
X+ 
X+ 		// Create the bans table
X+ 
X+ 		if( m_DB->Exec( "CREATE TABLE `bans` ( `id` INTEGER AUTO_INCREMENT, `server` VARCHAR( 40 ) NOT NULL, `name` VARCHAR( 15 ) NOT NULL, `ip` VARCHAR( 15 ), `date` DATE NOT NULL, `gamename` VARCHAR( 32 ), `admin` VARCHAR( 15 ) NOT NULL, `reason` VARCHAR( 100 ), PRIMARY KEY( `id` ) )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Created table `bans` successfully." );
X+ 		else
X+ 		{
X+ 			m_HasError = true;
X+ 			m_Error = "error creating `bans` table";
X+ 			CONSOLE_Print( "[MySQL++] Failed to create table `bans`: " + string( m_DB->GetError( ) ) );
X+ 			return;
X+ 		}
X+ 
X+ 		// Create the games table
X+ 
X+ 		if( m_DB->Exec( "CREATE TABLE `games` ( `id` INTEGER AUTO_INCREMENT, `server` VARCHAR( 40 ) NOT NULL, `map` VARCHAR( 40 ) NOT NULL, `datetime` DATETIME NOT NULL, `gamename` VARCHAR( 32 ), `ownername` VARCHAR( 15 ), `duration` INTEGER NOT NULL, `gamestate` TINYINT NOT NULL DEFAULT 0, `creatorname` VARCHAR( 15 ) NOT NULL DEFAULT '', `creatorserver` VARCHAR( 40 ) NOT NULL DEFAULT '', PRIMARY KEY( `id` ) )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Created table `games` successfully." );
X+ 		else
X+ 		{
X+ 			m_HasError = true;
X+ 			m_Error = "error creating `games` table";
X+ 			CONSOLE_Print( "[MySQL++] Failed to create table `games`: " + string( m_DB->GetError( ) ) );
X+ 			return;
X+ 		}
X+ 
X+ 		// Create the gameplayers table
X+ 
X+ 		if( m_DB->Exec( "CREATE TABLE `gameplayers` ( `id` INTEGER AUTO_INCREMENT, `gameid` INTEGER NOT NULL, `name` VARCHAR( 15 ) NOT NULL, `ip` VARCHAR( 15 ) NOT NULL, `spoofed` INTEGER NOT NULL, `reserved` INTEGER NOT NULL, `loadingtime` INTEGER NOT NULL, `left` INTEGER NOT NULL, `leftreason` VARCHAR( 100 ) NOT NULL, `team` TINYINT NOT NULL, `colour` TINYINT NOT NULL, `spoofedrealm` VARCHAR( 40 ) NOT NULL DEFAULT '', PRIMARY KEY( `id` ) )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Created table `gameplayers` successfully." );
X+ 		else
X+ 		{
X+ 			m_HasError = true;
X+ 			m_Error = "error creating `gameplayers` table";
X+ 			CONSOLE_Print( "[MySQL++] Failed to create table `gameplayers`: " + string( m_DB->GetError( ) ) );
X+ 			return;
X+ 		}
X+ 
X+ 		// Create the dotagames table
X+ 
X+ 		if( m_DB->Exec( "CREATE TABLE `dotagames` ( `id` INTEGER AUTO_INCREMENT, `gameid` INTEGER NOT NULL, `winner` TINYINT NOT NULL, `min` INTEGER NOT NULL DEFAULT 0, `sec` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY( `id` ) )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Created table `dotagames` successfully." );
X+ 		else
X+ 		{
X+ 			m_HasError = true;
X+ 			m_Error = "error creating `dotagames` table";
X+ 			CONSOLE_Print( "[MySQL++] Failed to create table `dotagames`: " + string( m_DB->GetError( ) ) );
X+ 			return;
X+ 		}
X+ 
X+ 		// Create the dotaplayers table
X+ 
X+ 		if( m_DB->Exec( "CREATE TABLE `dotaplayers` ( `id` INTEGER AUTO_INCREMENT, `gameid` INTEGER NOT NULL, `colour` TINYINT NOT NULL, `kills` INTEGER NOT NULL, `deaths` INTEGER NOT NULL, `creepkills` INTEGER NOT NULL, `creepdenies` INTEGER NOT NULL, `assists` INTEGER NOT NULL, `gold` INTEGER NOT NULL, `neutralkills` INTEGER NOT NULL, `item1` VARCHAR( 6 ), `item2` VARCHAR( 6 ), `item3` VARCHAR( 6 ), `item4` VARCHAR( 6 ), `item5` VARCHAR( 6 ), `item6` VARCHAR( 6 ), `hero` VARCHAR( 6 ) NOT NULL DEFAULT '', `newcolour` INTEGER NOT NULL DEFAULT 0, `towerkills` INTEGER NOT NULL DEFAULT 0, `raxkills` INTEGER NOT NULL DEFAULT 0, `courierkills` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY( `id` ) )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Created table `dotaplayers` successfully." );
X+ 		else
X+ 		{
X+ 			m_HasError = true;
X+ 			m_Error = "error creating `dotaplayers` table";
X+ 			CONSOLE_Print( "[MySQL++] Failed to create table `dotaplayers`: " + string( m_DB->GetError( ) ) );
X+ 			return;
X+ 		}
X+ 
X+ 		// Create the downloads table
X+ 
X+ 		if( m_DB->Exec( "CREATE TABLE `downloads` ( `id` INTEGER AUTO_INCREMENT, `map` VARCHAR( 40 ) NOT NULL, `mapsize` INTEGER NOT NULL, `datetime` DATETIME NOT NULL, `name` VARCHAR( 15 ) NOT NULL, `ip` VARCHAR( 15 ) NOT NULL, `spoofed` INTEGER( 1 ) NOT NULL, `spoofedrealm` VARCHAR( 40 ) NOT NULL, `downloadtime` INTEGER NOT NULL, PRIMARY KEY( `id` ) )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Created table `downloads` successfully." );
X+ 		else
X+ 		{
X+ 			m_HasError = true;
X+ 			m_Error = "error creating `downloads` table";
X+ 			CONSOLE_Print( "[MySQL++] Failed to create table `downloads`: " + string( m_DB->GetError( ) ) );
X+ 			return;
X+ 		}
X+ 
X+ 		// Create the iptocountry table
X+ 
X+ 		if( m_DB->Exec( "CREATE TABLE `iptocountry` ( `ip1` INTEGER UNSIGNED NOT NULL, `ip2` INTEGER UNSIGNED NOT NULL, `country` VARCHAR( 4 ) NOT NULL, PRIMARY KEY( `ip1`, `ip2` ) )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Successfully created iptocountry table." );
X+ 		else
X+ 			CONSOLE_Print( "[MySQL++] Failed to create iptocountry table: " + string( m_DB->GetError( ) ) );
X+ 
X+ 		// Create index idx_gameid
X+ 
X+ 		if( m_DB->Exec( "CREATE INDEX idx_gameid ON gameplayers ( gameid )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Successfully created index idx_gameid." );
X+ 		else
X+ 			CONSOLE_Print( "[MySQL++] Failed to create index idx_gameid: " + string( m_DB->GetError( ) ) );
X+ 
X+ 		// Create index idx_gameid_colour
X+ 
X+ 		if( m_DB->Exec( "CREATE INDEX idx_gameid_colour ON dotaplayers ( gameid, colour )" ) )
X+ 			CONSOLE_Print( "[MySQL++] Successfully created index idx_gameid_colour." );
X+ 		else
X+ 			CONSOLE_Print( "[MySQL++] Failed to create index idx_gameid_colour: " + string( m_DB->GetError( ) ) );
X+ 	}
X+ }
X+ 
X+ CGHostDBMySQL :: ~CGHostDBMySQL( )
X+ {
X+ 	if( !m_HasError )
X+ 	{
X+ 		delete m_DB;
X+ 	}
X+ }
X+ 
X+ bool CGHostDBMySQL :: FromAdd( uint32_t ip1, uint32_t ip2, string country )
X+ {
X+ 	bool Success = false;
X+ 
X+ 	if( m_DB->Exec( "INSERT INTO `iptocountry` ( `ip1`, `ip2`, `country` ) VALUES ( '" + UTIL_ToString( ip1 ) + "', '" + UTIL_ToString( ip2 ) + "', '" + country + "' )" ) )
X+ 		Success = true;
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error adding iptocountry [" + UTIL_ToString( ip1 ) + " : " + UTIL_ToString( ip2 ) + " : " + country + "] - " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Success;
X+ }
X+ 
X+ bool CGHostDBMySQL :: Begin( )
X+ {
X+ 	if( m_DB->Connected( ) )
X+ 	{
X+ 		if( m_DB->Exec( "START TRANSACTION" ) )
X+ 			return true;
X+ 		else
X+ 		{
X+ 			CONSOLE_Print( "[MySQL++] Error starting transaction: " + string( m_DB->GetError( ) ) );
X+ 			return false;
X+ 		}
X+ 	}
X+ 	else
X+ 	{
X+ 		CONSOLE_Print( "[MySQL++] Not connected." );
X+ 		return false;
X+ 	}
X+ }
X+ 
X+ bool CGHostDBMySQL :: Commit( )
X+ {
X+ 	if( m_DB->Connected( ) )
X+ 	{
X+ 		if( m_DB->Exec( "COMMIT" ) )
X+ 			return true;
X+ 		else
X+ 		{
X+ 			CONSOLE_Print( "[MySQL++] Error committing transaction: " + string( m_DB->GetError( ) ) );
X+ 			return false;
X+ 		}
X+ 	}
X+ 	else
X+ 	{
X+ 		CONSOLE_Print( "[MySQL++] Not connected." );
X+ 		return false;
X+ 	}
X+ }
X+ 
X+ string CGHostDBMySQL :: FromCheck( uint32_t ip )
X+ {
X+ 	string From = "??";
X+ 	if( m_DB->Store( "SELECT country FROM iptocountry WHERE `ip1`<='" + UTIL_ToString( ip ) + "' AND `ip2`>='" + UTIL_ToString( ip ) + "'" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult res = m_DB->GetResult( );
X+ 		if( !res.empty( ) )
X+ 			From = string( res[0][0] );
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error looking up IP: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return From;
X+ }
X+ 
X+ uint32_t CGHostDBMySQL :: AdminCount( string server )
X+ {
X+ 	uint32_t Count = 0;
X+ 
X+ 	if( m_DB->Store( "SELECT COUNT(*) FROM admins WHERE server='" + server + "'" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 		Count = atoi( result[0][0] );
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error counting admins: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Count;
X+ }
X+ 
X+ bool CGHostDBMySQL :: AdminCheck( string server, string user )
X+ {
X+ 	user = sEscape( user );
X+ 	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
X+ 	bool IsAdmin = false;
X+ 
X+ 	if( m_DB->Store( "SELECT * FROM admins WHERE server='" + server + "' AND name='" + user + "'" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 		if( !result.empty( ) )
X+ 			IsAdmin = true;
X+ 	}
X+ 
X+ 	return IsAdmin;
X+ }
X+ 
X+ bool CGHostDBMySQL :: AdminAdd( string server, string user )
X+ {
X+ 	user = sEscape( user );
X+ 	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
X+ 	bool Success = false;
X+ 
X+ 	if( m_DB->Exec( "INSERT INTO admins ( server, name ) VALUES ( '" + server + "', '" + user + "' )" ) )
X+ 		Success = true;
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error inserting row in admins: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Success;
X+ }
X+ 
X+ bool CGHostDBMySQL :: AdminRemove( string server, string user )
X+ {
X+ 	user = sEscape( user );
X+ 	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
X+ 	bool Success = false;
X+ 
X+ 	if( m_DB->Exec( "DELETE FROM admins WHERE server='" + server + "' AND name='" + user + "'" ) )
X+ 		Success = true;
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error deleting row in admins: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Success;
X+ }
X+ 
X+ uint32_t CGHostDBMySQL :: BanCount( string server )
X+ {
X+ 	uint32_t Count = 0;
X+ 
X+ 	if( m_DB->Store( "SELECT COUNT(*) FROM bans WHERE server='" + server + "'" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 		Count = atoi( result[0][0] );
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error counting bans: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Count;
X+ }
X+ 
X+ CDBBan *CGHostDBMySQL :: BanCheck( string server, string user )
X+ {
X+ 	user = sEscape( user );
X+ 	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
X+ 	CDBBan *Ban = NULL;
X+ 
X+ 	if( m_DB->Store( "SELECT ip, date, gamename, admin, reason FROM bans WHERE server='" + server + "' AND name='" + user + "'" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 		if( !result.empty( ) )
X+ 		{
X+ 			Ban = new CDBBan( server, user, string( result[0][0] ), string( result[0][1] ), string( result[0][2] ), string( result[0][3] ), string( result[0][4] ) );
X+ 		}
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error checking a ban: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Ban;
X+ }
X+ 
X+ bool CGHostDBMySQL :: BanAdd( string server, string user, string ip, string gamename, string admin, string reason )
X+ {
X+ 	user = sEscape( user );
X+ 	ip = sEscape( ip );
X+ 	gamename = sEscape( gamename );
X+ 	admin = sEscape( admin );
X+ 	reason = sEscape( reason );
X+ 	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
X+ 	bool Success = false;
X+ 
X+ 	if( m_DB->Exec( "INSERT INTO bans ( server, name, ip, date, gamename, admin, reason ) VALUES ( '" + server + "', '" + user + "', '" + ip + "', CURDATE( ), '" + gamename + "', '" + admin + "', '" + reason + "' )" ) )
X+ 		Success = true;
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error inserting row in bans: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Success;
X+ }
X+ 
X+ bool CGHostDBMySQL :: BanRemove( string server, string user )
X+ {
X+ 	user = sEscape( user );
X+ 	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
X+ 	bool Success = false;
X+ 
X+ 	if( m_DB->Exec( "DELETE FROM bans WHERE server='" + server + "' AND name='" + user + "'" ) )
X+ 		Success = true;
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error deleting row in bans: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Success;
X+ }
X+ 
X+ bool CGHostDBMySQL :: BanRemove( string user )
X+ {
X+ 	user = sEscape( user );
X+ 	transform( user.begin( ), user.end( ), user.begin( ), (int(*)(int))tolower );
X+ 	bool Success = false;
X+ 
X+ 	if( m_DB->Exec( "DELETE FROM bans WHERE name='" + user + "'" ) )
X+ 		Success = true;
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error deleting row in bans: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Success;
X+ }
X+ 
X+ uint32_t CGHostDBMySQL :: GameAdd( string server, string map, string gamename, string ownername, uint32_t duration, uint32_t gamestate, string creatorname, string creatorserver )
X+ {
X+ 	gamename = sEscape( gamename );
X+ 	ownername = sEscape( ownername );
X+ 	creatorname = sEscape( creatorname );
X+ 	creatorserver = sEscape( creatorserver );
X+ 	uint32_t RowID = 0;
X+ 
X+ 	if( m_DB->Exec( "INSERT INTO games ( server, map, datetime, gamename, ownername, duration, gamestate, creatorname, creatorserver ) VALUES ( '" + server + "', '" + map + "', UTC_TIMESTAMP( ), '" + gamename + "', '" + ownername + "', '" + UTIL_ToString( duration ) + "', '" + UTIL_ToString( gamestate ) + "', '" + creatorname + "', '" + creatorserver + "' )" ) )
X+ 	{
X+ 		if( m_DB->Store( "SELECT LAST_INSERT_ID( )" ) )
X+ 		{
X+ 			mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 			if( !result.empty( ) )
X+ 				RowID = atoi( result[0][0] );
X+ 		}
X+ 		else
X+ 			CONSOLE_Print( "[MySQL++] Error getting last insert ID: " + string( m_DB->GetError( ) ) );
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error inserting row in games: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return RowID;
X+ }
X+ 
X+ uint32_t CGHostDBMySQL :: GamePlayerAdd( uint32_t gameid, string name, string ip, uint32_t spoofed, string spoofedrealm, uint32_t reserved, uint32_t loadingtime, uint32_t left, string leftreason, uint32_t team, uint32_t colour )
X+ {
X+ 	name = sEscape( name );
X+ 	spoofedrealm = sEscape( spoofedrealm );
X+ 	leftreason = sEscape( leftreason );
X+ 	transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
X+ 	uint32_t RowID = 0;
X+ 
X+ 	// Turns out MySQL does not recognise "left" in the column list as a column name, instead the command LEFT therefore we need to backtick it
X+ 
X+ 	if( m_DB->Exec( "INSERT INTO gameplayers ( gameid, name, ip, spoofed, reserved, loadingtime, `left`, leftreason, team, colour, spoofedrealm ) VALUES ( '" + UTIL_ToString( gameid ) + "', '" + name + "', '" + ip + "', '" + UTIL_ToString( spoofed ) + "', '" + UTIL_ToString( reserved ) + "', '" + UTIL_ToString( loadingtime ) + "', '" + UTIL_ToString( left ) + "', '" + leftreason + "', '" + UTIL_ToString( team ) + "', '" + UTIL_ToString( colour ) + "', '" + spoofedrealm + "' )" ) )
X+ 	{
X+ 		if( m_DB->Store( "SELECT LAST_INSERT_ID( )" ) )
X+ 		{
X+ 			mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 			if( !result.empty( ) )
X+ 				RowID = atoi( result[0][0] );
X+ 		}
X+ 		else
X+ 			CONSOLE_Print( "[MySQL++] Error getting last insert ID: " + string( m_DB->GetError( ) ) );
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error inserting row in gameplayers: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return RowID;
X+ }
X+ 
X+ uint32_t CGHostDBMySQL :: GamePlayerCount( string name )
X+ {
X+ 	name = sEscape( name );
X+ 	uint32_t Count = 0;
X+ 
X+ 	if( m_DB->Store( "SELECT COUNT(*) FROM gameplayers LEFT JOIN games ON games.id=gameid WHERE name='" + name + "'" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 		Count = atoi( result[0][0] );
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error counting gameplayers: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Count;
X+ }
X+ 
X+ CDBGamePlayerSummary *CGHostDBMySQL :: GamePlayerSummaryCheck( string name )
X+ {
X+ 	if( GamePlayerCount( name ) == 0 )
X+ 		return NULL;
X+ 	name = sEscape( name );
X+ 
X+ 	transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
X+ 	CDBGamePlayerSummary *GamePlayerSummary = NULL;
X+ 
X+ 	if( m_DB->Store( "SELECT MIN(datetime), MAX(datetime), COUNT(*), MIN(loadingtime), AVG(loadingtime), MAX(loadingtime), MIN(`left`/duration)*100, AVG(`left`/duration)*100, MAX(`left`/duration)*100, MIN(duration), AVG(duration), MAX(duration) FROM gameplayers LEFT JOIN games ON games.id=gameid WHERE name='" + name + "'" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 		if( !result.empty( ) )
X+ 		{
X+ 			string FirstGameDateTime = string( result[0][0] );
X+ 			string LastGameDateTime = string( result[0][1] );
X+ 
X+ 			uint32_t TotalGames = atoi( result[0][2] );
X+ 			uint32_t MinLoadingTime = atoi( result[0][3] );
X+ 			uint32_t AvgLoadingTime = atoi( result[0][4] );
X+ 			uint32_t MaxLoadingTime = atoi( result[0][5] );
X+ 			uint32_t MinLeftPercent = atoi( result[0][6] );
X+ 			uint32_t AvgLeftPercent = atoi( result[0][7] );
X+ 			uint32_t MaxLeftPercent = atoi( result[0][8] );
X+ 			uint32_t MinDuration = atoi( result[0][9] );
X+ 			uint32_t AvgDuration = atoi( result[0][10] );
X+ 			uint32_t MaxDuration = atoi( result[0][11] );
X+ 			GamePlayerSummary = new CDBGamePlayerSummary( string( ), name, FirstGameDateTime, LastGameDateTime, TotalGames, MinLoadingTime, AvgLoadingTime, MaxLoadingTime, MinLeftPercent, AvgLeftPercent, MaxLeftPercent, MinDuration, AvgDuration, MaxDuration );
X+ 		}
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error getting gameplayersummary: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return GamePlayerSummary;
X+ }
X+ 
X+ uint32_t CGHostDBMySQL :: DotAGameAdd( uint32_t gameid, uint32_t winner, uint32_t min, uint32_t sec )
X+ {
X+ 	uint32_t RowID = 0;
X+ 
X+ 	if( m_DB->Exec( "INSERT INTO dotagames ( gameid, winner, min, sec ) VALUES ( '" + UTIL_ToString( gameid ) + "', '" + UTIL_ToString( winner ) + "', '" + UTIL_ToString( min ) + "', '" + UTIL_ToString( sec ) + "' )" ) )
X+ 	{
X+ 		if( m_DB->Store( "SELECT LAST_INSERT_ID( )" ) )
X+ 		{
X+ 			mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 			if( !result.empty( ) )
X+ 				RowID = atoi( result[0][0] );
X+ 		}
X+ 		else
X+ 			CONSOLE_Print( "[MySQL++] Error getting last insert ID: " + string( m_DB->GetError( ) ) );
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error inserting row in dotagames: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return RowID;
X+ }
X+ 
X+ uint32_t CGHostDBMySQL :: DotAPlayerAdd( uint32_t gameid, uint32_t colour, uint32_t kills, uint32_t deaths, uint32_t creepkills, uint32_t creepdenies, uint32_t assists, uint32_t gold, uint32_t neutralkills, string item1, string item2, string item3, string item4, string item5, string item6, string hero, uint32_t newcolour, uint32_t towerkills, uint32_t raxkills, uint32_t courierkills )
X+ {
X+ 	uint32_t RowID = 0;
X+ 
X+ 	// We need to convert item1-6 and hero to an empty string if it's contents aren't an item code
X+ 	// We do this because what it contains is "    ", with the spaces being a non-space whitespace character for some reason
X+ 	// MySQL does not like this
X+ 
X+ 	string alphaNum = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
X+ 	if( !( item1.find_first_not_of( alphaNum ) == string::npos ) )
X+ 		item1 = "";
X+ 	if( !( item2.find_first_not_of( alphaNum ) == string::npos ) )
X+ 		item2 = "";
X+ 	if( !( item3.find_first_not_of( alphaNum ) == string::npos ) )
X+ 		item3 = "";
X+ 	if( !( item4.find_first_not_of( alphaNum ) == string::npos ) )
X+ 		item4 = "";
X+ 	if( !( item5.find_first_not_of( alphaNum ) == string::npos ) )
X+ 		item5 = "";
X+ 	if( !( item6.find_first_not_of( alphaNum ) == string::npos ) )
X+ 		item6 = "";
X+ 	if( !( hero.find_first_not_of( alphaNum ) == string::npos ) )
X+ 		hero = "";
X+ 
X+ 	if( m_DB->Exec( "INSERT INTO dotaplayers ( gameid, colour, kills, deaths, creepkills, creepdenies, assists, gold, neutralkills, item1, item2, item3, item4, item5, item6, hero, newcolour, towerkills, raxkills, courierkills ) VALUES ( '" + UTIL_ToString( gameid ) + "', '" + UTIL_ToString( colour ) + "', '" + UTIL_ToString( kills ) + "', '" + UTIL_ToString( deaths ) + "', '" + UTIL_ToString( creepkills ) + "', '" + UTIL_ToString( creepdenies ) + "', '" + UTIL_ToString( assists ) + "', '" + UTIL_ToString( gold ) + "', '" + UTIL_ToString( neutralkills ) + "', '" + item1 + "', '" + item2 + "', '" + item3 + "', '" + item4 + "', '" + item5 + "', '" + item6 + "', '" + hero + "', '" + UTIL_ToString( newcolour ) + "', '" + UTIL_ToString( towerkills ) + "', '" + UTIL_ToString( raxkills ) + "', '" + UTIL_ToString( courierkills ) + "' )" ) )
X+ 	{
X+ 		if( m_DB->Store( "SELECT LAST_INSERT_ID( )" ) )
X+ 		{
X+ 			mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 			if( !result.empty( ) )
X+ 				RowID = atoi( result[0][0] );
X+ 		}
X+ 		else
X+ 			CONSOLE_Print( "[MySQL++] Error getting last insert ID: " + string( m_DB->GetError( ) ) );
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error inserting row in dotaplayers: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return RowID;
X+ }
X+ 
X+ uint32_t CGHostDBMySQL :: DotAPlayerCount( string name )
X+ {
X+ 	name = sEscape( name );
X+ 	uint32_t Count = 0;
X+ 
X+ 	if( m_DB->Store( "SELECT COUNT(dotaplayers.id) FROM gameplayers LEFT JOIN games ON games.id=gameplayers.gameid LEFT JOIN dotaplayers ON dotaplayers.gameid=games.id AND dotaplayers.colour=gameplayers.colour WHERE name='" + name + "'" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 		Count = atoi( result[0][0] );
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error counting dotaplayers: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Count;
X+ }
X+ 
X+ CDBDotAPlayerSummary *CGHostDBMySQL :: DotAPlayerSummaryCheck( string name )
X+ {
X+ 	if( DotAPlayerCount( name ) == 0 )
X+ 		return NULL;
X+ 
X+ 	name = sEscape( name );
X+ 	transform( name.begin( ), name.end( ), name.begin( ), (int(*)(int))tolower );
X+ 	CDBDotAPlayerSummary *DotAPlayerSummary = NULL;
X+ 
X+ 	if( m_DB->Store( "SELECT COUNT(dotaplayers.id), SUM(kills), SUM(deaths), SUM(creepkills), SUM(creepdenies), SUM(assists), SUM(neutralkills), SUM(towerkills), SUM(raxkills), SUM(courierkills) FROM gameplayers LEFT JOIN games ON games.id=gameplayers.gameid LEFT JOIN dotaplayers ON dotaplayers.gameid=games.id AND dotaplayers.colour=gameplayers.colour WHERE name='" + name + "'" ) )
X+ 	{
X+ 		mysqlpp::StoreQueryResult result = m_DB->GetResult( );
X+ 
X+ 		if( !result.empty( ) )
X+ 		{
X+ 			uint32_t TotalGames = atoi( result[0][0] );
X+ 			uint32_t TotalWins = 0;
X+ 			uint32_t TotalLosses = 0;
X+ 			uint32_t TotalKills = atoi( result[0][1] );
X+ 			uint32_t TotalDeaths = atoi( result[0][2] );
X+ 			uint32_t TotalCreepKills = atoi( result[0][3] );
X+ 			uint32_t TotalCreepDenies = atoi( result[0][4] );
X+ 			uint32_t TotalAssists = atoi( result[0][5] );
X+ 			uint32_t TotalNeutralKills = atoi( result[0][6] );
X+ 			uint32_t TotalTowerKills = atoi( result[0][7] );
X+ 			uint32_t TotalRaxKills = atoi( result[0][8] );
X+ 			uint32_t TotalCourierKills = atoi( result[0][9] );
X+ 
X+ 			if( m_DB->Store( "SELECT COUNT(*) FROM gameplayers LEFT JOIN games ON games.id=gameplayers.gameid LEFT JOIN dotaplayers ON dotaplayers.gameid=games.id AND dotaplayers.colour=gameplayers.colour LEFT JOIN dotagames ON games.id=dotagames.gameid WHERE name='" + name + "' AND ((winner=1 AND dotaplayers.newcolour>=1 AND dotaplayers.newcolour<=5) OR (winner=2 AND dotaplayers.newcolour>=7 AND dotaplayers.newcolour<=11))" ) )
X+ 			{
X+ 				mysqlpp::StoreQueryResult resultwins = m_DB->GetResult( );
X+ 
X+ 				if( !resultwins.empty( ) )
X+ 					TotalWins = atoi( resultwins[0][0] );
X+ 			}
X+ 			else
X+ 				CONSOLE_Print( "[MySQL++] Error counting dotaplayersummary wins: " + string( m_DB->GetError( ) ) );
X+ 
X+ 			if( m_DB->Store( "SELECT COUNT(*) FROM gameplayers LEFT JOIN games ON games.id=gameplayers.gameid LEFT JOIN dotaplayers ON dotaplayers.gameid=games.id AND dotaplayers.colour=gameplayers.colour LEFT JOIN dotagames ON games.id=dotagames.gameid WHERE name='" + name + "' AND ((winner=2 AND dotaplayers.newcolour>=1 AND dotaplayers.newcolour<=5) OR (winner=1 AND dotaplayers.newcolour>=7 AND dotaplayers.newcolour<=11))" ) )
X+ 			{
X+ 				mysqlpp::StoreQueryResult resultlosses = m_DB->GetResult( );
X+ 
X+ 				if( !resultlosses.empty( ) )
X+ 					TotalLosses = atoi( resultlosses[0][0] );
X+ 			}
X+ 			else
X+ 				CONSOLE_Print( "[MySQL++] Error counting dotaplayersummary losses: " + string( m_DB->GetError( ) ) );
X+ 
X+ 			DotAPlayerSummary = new CDBDotAPlayerSummary( string( ), name, TotalGames, TotalWins, TotalLosses, TotalKills, TotalDeaths, TotalCreepKills, TotalCreepDenies, TotalAssists, TotalNeutralKills, TotalTowerKills, TotalRaxKills, TotalCourierKills );
X+ 		}
X+ 	}
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error getting dotaplayersummary: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return DotAPlayerSummary;
X+ }
X+ 
X+ bool CGHostDBMySQL :: DownloadAdd( string map, uint32_t mapsize, string name, string ip, uint32_t spoofed, string spoofedrealm, uint32_t downloadtime )
X+ {
X+ 	name = sEscape( name );
X+ 	ip = sEscape( ip );
X+ 	spoofedrealm = sEscape( spoofedrealm );
X+ 	bool Success = false;
X+ 
X+ 	if( m_DB->Exec( "INSERT INTO downloads ( map, mapsize, datetime, name, ip, spoofed, spoofedrealm, downloadtime ) VALUES ( '" + map + "', '" + UTIL_ToString( mapsize ) + "', UTC_TIMESTAMP( ), '" + name + "', '" + ip + "', '" + UTIL_ToString( spoofed ) + "', '" + spoofedrealm + "', '" + UTIL_ToString( downloadtime ) + "' )" ) )
X+ 		Success = true;
X+ 	else
X+ 		CONSOLE_Print( "[MySQL++] Error inserting row in downloads: " + string( m_DB->GetError( ) ) );
X+ 
X+ 	return Success;
X+ }
X+ 
X+ void CGHostDBMySQL :: Update( )
X+ {
X+ 	uint32_t cTime = GetTime( );
X+ 
X+ 	if( cTime >= m_NextPing )
X+ 	{
X+ 		m_NextPing = cTime + 5;
X+ 		if( !m_ServerError )
X+ 		{
X+ 			if( !m_DB->Ping( ) )
X+ 			{
X+ 				CONSOLE_Print( "[MySQL++] Database server not responding, disconnecting..." );
X+ 				m_ServerError = true;
X+ 			}
X+ 		}
X+ 		if( m_ServerError ) // We do not use else as we want to immediately reconnect if the above if sets m_ServerError to true
X+ 		{
X+ 			if( m_DB->Connect( ) )
X+ 			{
X+ 				CONSOLE_Print( "[MySQL++] Connected." );
X+ 				m_ServerError = false;
X+ 			}
X+ 			else
X+ 				CONSOLE_Print( "[MySQL++] Error reconnecting to database server: [" + string( m_DB->GetConnError( ) ) + "]\nRetrying in 5 seconds..." );
X+ 		}
X+ 
X+ 	}
X+ }
X+ 
X+ string CGHostDBMySQL :: sEscape( string s )
X+ {
X+ 	for( unsigned int i = 0; i != s.length( ); i++ )
X+ 	{
X+ 		if( s.at( i ) == '\'' || s.at( i ) == '\"' )
X+ 		{
X+ 			s.insert( i, "\\" );
X+ 			i++;
X+ 		}
X+ 		if( s.at( i ) == '\n' || s.at( i ) == ';' )
X+ 		{
X+ 			s.erase( i, 1 );
X+ 			i--;
X+ 		}
X+ 	}
X+ 	return s;
X+ }
END-of-/usr/ports/games/ghost++/files/patch-ghostdbmysql.cpp
echo x - /usr/ports/games/ghost++/files/patch-ghostdbmysql.h
sed 's/^X//' >/usr/ports/games/ghost++/files/patch-ghostdbmysql.h << 'END-of-/usr/ports/games/ghost++/files/patch-ghostdbmysql.h'
X*** ghost/ghostdbmysql.h.orig	Fri Mar 13 22:22:52 2009
X--- ghost/ghostdbmysql.h	Fri Feb 13 15:21:00 2009
X***************
X*** 0 ****
X--- 1,107 ----
X+ /*
X+ 
X+    Copyright 2009 Scott Gillespie
X+ 
X+    Licensed under the Apache License, Version 2.0 (the "License");
X+    you may not use this file except in compliance with the License.
X+    You may obtain a copy of the License at
X+ 
X+        http://www.apache.org/licenses/LICENSE-2.0
X+ 
X+    Unless required by applicable law or agreed to in writing, software
X+    distributed under the License is distributed on an "AS IS" BASIS,
X+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
X+    See the License for the specific language governing permissions and
X+    limitations under the License.
X+ 
X+ 
X+ */
X+ 
X+ #ifndef GHOSTDBMYSQL_H
X+ #define GHOSTDBMYSQL_H
X+ 
X+ #include <mysql++.h>
X+ 
X+ //
X+ // CMySQL
X+ //
X+ 
X+ class CMySQL
X+ {
X+ private:
X+ 	string m_DB;
X+ 	string m_Server;
X+ 	string m_User;
X+ 	string m_Pass;
X+ 	int m_Port;
X+ 	mysqlpp::Connection *m_Conn;
X+ 	const char* m_Error;
X+ 	int m_Errnum;
X+ 	const char* m_ConnError;
X+ 	mysqlpp::StoreQueryResult m_Result;
X+ 
X+ public:
X+ 	CMySQL( string db, string server, string user, string pass, int port);
X+ 	~CMySQL( );
X+ 
X+ 	bool Connect( );
X+ 	void Disconnect( );
X+ 	bool Connected( ) { return m_Conn->connected( ); }
X+ 	const char* GetError( ) { return m_Error; }
X+ 	const char* GetConnError( ) { return m_ConnError; }
X+ 	int GetErrnum( ) { return m_Errnum; }
X+ 	mysqlpp::StoreQueryResult GetResult( ) { return m_Result; }
X+ 	bool LoadIPData( );
X+ 	bool Ping( ) { return m_Conn->ping( ); }
X+ 
X+ 	bool Exec( string query );
X+ 	bool Store( string query );
X+ 
X+ };
X+ 
X+ //
X+ // CGHostDBMySQL
X+ //
X+ 
X+ class CGHostDBMySQL : public CGHostDB
X+ {
X+ private:
X+ 	CMySQL *m_DB;
X+ 	uint32_t m_NextPing;
X+ 	bool m_ServerError;
X+ 	string sEscape( string s );
X+ 
X+ public:
X+ 	CGHostDBMySQL( CConfig *CFG );
X+ 	virtual ~CGHostDBMySQL( );
X+ 
X+ 	virtual bool Connected( ) { return m_DB->Connected( ); }
X+ 
X+ 	virtual bool FromAdd( uint32_t ip1, uint32_t ip2, string country );
X+ 	virtual bool Begin( );
X+ 	virtual bool Commit( );
X+ 	virtual string FromCheck( uint32_t ip );
X+ 	virtual bool LoadIPData( ) { return m_DB->LoadIPData( ); }
X+ 	virtual uint32_t AdminCount( string server );
X+ 	virtual bool AdminCheck( string server, string user );
X+ 	virtual bool AdminAdd( string server, string user );
X+ 	virtual bool AdminRemove( string server, string user );
X+ 	virtual uint32_t BanCount( string server );
X+ 	virtual CDBBan *BanCheck( string server, string user );
X+ 	virtual bool BanAdd( string server, string user, string ip, string gamename, string admin, string reason );
X+ 	virtual bool BanRemove( string server, string user );
X+ 	virtual bool BanRemove( string user );
X+ 	virtual uint32_t GameAdd( string server, string map, string gamename, string ownername, uint32_t duration, uint32_t gamestate, string creatorname, string creatorserver );
X+ 	virtual uint32_t GamePlayerAdd( uint32_t gameid, string name, string ip, uint32_t spoofed, string spoofedrealm, uint32_t reserved, uint32_t loadingtime, uint32_t left, string leftreason, uint32_t team, uint32_t colour );
X+ 	virtual uint32_t GamePlayerCount( string name );
X+ 	virtual CDBGamePlayerSummary *GamePlayerSummaryCheck( string name );
X+ 	virtual uint32_t DotAGameAdd( uint32_t gameid, uint32_t winner, uint32_t min, uint32_t sec );
X+ 	virtual uint32_t DotAPlayerAdd( uint32_t gameid, uint32_t colour, uint32_t kills, uint32_t deaths, uint32_t creepkills, uint32_t creepdenies, uint32_t assists, uint32_t gold, uint32_t neutralkills, string item1, string item2, string item3, string item4, string item5, string item6, string hero, uint32_t newcolour, uint32_t towerkills, uint32_t raxkills, uint32_t courierkills );
X+ 	virtual uint32_t DotAPlayerCount( string name );
X+ 	virtual CDBDotAPlayerSummary *DotAPlayerSummaryCheck( string name );
X+ 	virtual bool DownloadAdd( string map, uint32_t mapsize, string name, string ip, uint32_t spoofed, string spoofedrealm, uint32_t downloadtime );
X+ 	virtual void Update( );
X+ };
X+ 
X+ #endif
X+ 
END-of-/usr/ports/games/ghost++/files/patch-ghostdbmysql.h
echo x - /usr/ports/games/ghost++/files/patch-ghost.cfg
sed 's/^X//' >/usr/ports/games/ghost++/files/patch-ghost.cfg << 'END-of-/usr/ports/games/ghost++/files/patch-ghost.cfg'
X*** ghost.cfg.orig	Fri Jan 30 01:18:22 2009
X--- ghost.cfg	Thu Feb 12 11:01:00 2009
X***************
X*** 192,205 ****
X  # DATABASE CONFIGURATION #
X  ##########################
X  
X! ### database type (this config value is ignored for now since we only support one type of database)
X  
X! db_type = sqlite3
X  
X  ### sqlite3 database file
X  
X  db_sqlite3_file = ghost.dbs
X  
X  ############################
X  # BATTLE.NET CONFIGURATION #
X  ############################
X--- 192,222 ----
X  # DATABASE CONFIGURATION #
X  ##########################
X  
X! ### database type: sqlite3 or mysql
X  
X! db_type = mysql
X  
X  ### sqlite3 database file
X  
X  db_sqlite3_file = ghost.dbs
X  
X+ ### MySQL connection details
X+ 
X+ ### The database to use
X+ db_mysql_database = ghost
X+ 
X+ ### the server to connect to
X+ db_mysql_hostname = localhost
X+ 
X+ ### the port to connect to (default port is 3306)
X+ db_mysql_port = 3306
X+ 
X+ ### the user to connect as
X+ db_mysql_username = 
X+ 
X+ ### the password to use
X+ db_mysql_password = 
X+ 
X  ############################
X  # BATTLE.NET CONFIGURATION #
X  ############################
END-of-/usr/ports/games/ghost++/files/patch-ghost.cfg
echo x - /usr/ports/games/ghost++/files/patch-ghost_makefile
sed 's/^X//' >/usr/ports/games/ghost++/files/patch-ghost_makefile << 'END-of-/usr/ports/games/ghost++/files/patch-ghost_makefile'
X*** ghost/Makefile.orig	Fri Mar 13 22:06:24 2009
X--- ghost/Makefile	Fri Mar 13 22:11:09 2009
X***************
X*** 4,10 ****
X  CC = gcc
X  DFLAGS =
X  OFLAGS = -O3
X! LFLAGS = -L. -L../bncsutil/src/bncsutil/ -L../StormLib/stormlib/ -lbncsutil -lpthread -ldl -lz -lStorm
X  CFLAGS =
X  
X  ifeq ($(SYSTEM),Darwin)
X--- 4,10 ----
X  CC = gcc
X  DFLAGS =
X  OFLAGS = -O3
X! LFLAGS = -L. -L../bncsutil/src/bncsutil/ -L../StormLib/stormlib/ -L/usr/local/lib/ -L/usr/local/lib/mysql -lmysqlclient -lbncsutil -lc -lpthread -lltdl -lStorm -lmysqlpp
X  CFLAGS =
X  
X  ifeq ($(SYSTEM),Darwin)
X***************
X*** 21,29 ****
X  LFLAGS += -lresolv -lsocket -lnsl
X  endif
X  
X! CFLAGS += $(OFLAGS) $(DFLAGS) -I. -I../bncsutil/src/ -I../StormLib/
X  
X! OBJS = bncsutilinterface.o bnet.o bnetprotocol.o commandpacket.o config.o crc32.o csvparser.o game.o gameplayer.o gameprotocol.o gameslot.o ghost.o ghostdb.o ghostdbsqlite.o language.o map.o packed.o replay.o savegame.o socket.o stats.o statsdota.o util.o
X  COBJS = sqlite3.o
X  PROGS = ./ghost++
X  
X--- 21,29 ----
X  LFLAGS += -lresolv -lsocket -lnsl
X  endif
X  
X! CFLAGS += $(OFLAGS) $(DFLAGS) -I. -I../bncsutil/src/ -I../StormLib/ -I/usr/local/include/mysql -I/usr/local/include/mysql++
X  
X! OBJS = bncsutilinterface.o bnet.o bnetprotocol.o commandpacket.o config.o crc32.o csvparser.o game.o gameplayer.o gameprotocol.o gameslot.o ghost.o ghostdb.o ghostdbmysql.o ghostdbsqlite.o language.o map.o packed.o replay.o savegame.o socket.o stats.o statsdota.o util.o
X  COBJS = sqlite3.o
X  PROGS = ./ghost++
X  
X***************
X*** 58,63 ****
X--- 58,64 ----
X  gameslot.o: ghost.h gameslot.h
X  ghost.o: ghost.h util.h crc32.h csvparser.h config.h language.h socket.h ghostdb.h ghostdbsqlite.h bnet.h map.h packed.h savegame.h gameprotocol.h game.h
X  ghostdb.o: ghost.h config.h ghostdb.h
X+ ghostdbmysql.o: ghost.h util.h config.h ghostdb.h ghostdbmysql.h
X  ghostdbsqlite.o: ghost.h util.h config.h ghostdb.h ghostdbsqlite.h
X  language.o: ghost.h config.h language.h
X  packed.o: ghost.h util.h crc32.h packed.h
END-of-/usr/ports/games/ghost++/files/patch-ghost_makefile
echo x - /usr/ports/games/ghost++/Makefile
sed 's/^X//' >/usr/ports/games/ghost++/Makefile << 'END-of-/usr/ports/games/ghost++/Makefile'
X# New ports collection makefile for:	GHost++
X# Date created:			16 March 2009
X# Whom:				Yashin Dmitry <yashin.dm@gmail.com>
X#
X# $FreeBSD$
X#
X
XPORTNAME=	GHost++
XPORTVERSION=	11.5
XCATEGORIES=	games
XMASTER_SITES=	http://ghostplusplus.googlecode.com/files/
X
XMAINTAINER=	ports@FreeBSD.org
XCOMMENT=	Unofficial Unix version of the GHost++ Warcraft 3 hosting bot with MySql.
X
XDISTNAME=	ghostplusplus_11.5
XUSE_ZIP=	yes
XUSE_GMAKE=	yes
X
XWRKSRC=		${WRKDIR}/ghost
X
XLIB_DEPENDS=	bz2.3:${PORTSDIR}/archivers/bzip2:install \
X		gmp.7:${PORTSDIR}/math/libgmp4:install \
X		mysqlclient.15:${PORTSDIR}/databases/mysql50-client:install \
X		mysqlclient_r.15:${PORTSDIR}/databases/mysql50-client:install \
X		mysqlpp.3:${PORTSDIR}/databases/mysql++3:install
X
Xdo-build:
X	@${ECHO_MSG} "===>  Building for libbncsutil.so"
X	@(cd ${WRKSRC}/bncsutil/src/bncsutil && ${GMAKE})
X	@${ECHO_MSG} "===>  Building for libStorm.so"
X	@(cd ${WRKSRC}/StormLib/stormlib/ && ${GMAKE})
X	@${ECHO_MSG} "===>  Building for ${PKGNAME}"
X	@(cd ${WRKSRC}/ghost/ && ${GMAKE})
X
Xdo-install:
X	mkdir -p /usr/local/include/bncsutil
X	cp ${WRKSRC}/bncsutil/src/bncsutil/*.h /usr/local/include/bncsutil
X	cp ${WRKSRC}/bncsutil/src/bncsutil/libbncsutil.so /usr/local/lib
X	mkdir -p /usr/local/include/StormLib
X	cp ${WRKSRC}/StormLib/stormlib/StormLib.h /usr/local/include/StormLib
X	cp ${WRKSRC}/StormLib/stormlib/StormPort.h /usr/local/include/StormLib
X	cp ${WRKSRC}/StormLib/stormlib/libStorm.so /usr/local/lib
X	mkdir -p /usr/local/etc/ghost++
X	cp ${WRKSRC}/ghost.cfg /usr/local/etc/ghost++/ghost.cfg.simple
X	cp ${WRKSRC}/ghost/ghost++ /usr/local/etc/ghost++
X	@${ECHO_MSG} "===>  Post-installation informations for ${PKGNAME}"
X	@${ECHO_MSG} "      You can find the configuration files and binaries for"
X	@${ECHO_MSG} "      this package in the directory /usr/local/etc/ghost++."
X	@${ECHO_MSG} "      Simple config files is ghost.cfg.simple"
X
X.include <bsd.port.mk>
END-of-/usr/ports/games/ghost++/Makefile
echo x - /usr/ports/games/ghost++/distinfo
sed 's/^X//' >/usr/ports/games/ghost++/distinfo << 'END-of-/usr/ports/games/ghost++/distinfo'
XMD5 (ghostplusplus_11.5.zip) = 840167b9946c1ad51a7fd1913051ea1e
XSHA256 (ghostplusplus_11.5.zip) = 5302da6e0c1ec5a3671224570bfdb201d99c02d8704e09cbfb0fa1dc0b8a0ae0
XSIZE (ghostplusplus_11.5.zip) = 4364025
END-of-/usr/ports/games/ghost++/distinfo
echo x - /usr/ports/games/ghost++/pkg-descr
sed 's/^X//' >/usr/ports/games/ghost++/pkg-descr << 'END-of-/usr/ports/games/ghost++/pkg-descr'
XGHost++ is a Warcraft 3 game hosting bot. Some features include:
X	- connect to official battle.net and PVPGN realms
X	- connect to multiple realms at the same time
X	- auto host and start games without admin interaction
X	- host saved games
X	- player statistics
X	- and much more... 
X
XWWW: http://code.google.com/p/ghostplusplus/
X
XThx valindar to MySql patch (http://forum.codelain.com/index.php?topic=3429)
X
XUnofficial Unix version of the GHost++ Warcraft 3 hosting bot with MySql.
XThx FreeBSD Russian Community in http://forum.lissayra.su
END-of-/usr/ports/games/ghost++/pkg-descr
echo x - /usr/ports/games/ghost++/pkg-plist
sed 's/^X//' >/usr/ports/games/ghost++/pkg-plist << 'END-of-/usr/ports/games/ghost++/pkg-plist'
Xinclude/bncsutil/bncsutil.h
Xinclude/bncsutil/bsha1.h
Xinclude/bncsutil/buffer.h
Xinclude/bncsutil/cdkeydecoder.h
Xinclude/bncsutil/checkrevision.h
Xinclude/bncsutil/debug.h
Xinclude/bncsutil/decodekey.h
Xinclude/bncsutil/file.h
Xinclude/bncsutil/gmp.h
Xinclude/bncsutil/keytables.h
Xinclude/bncsutil/libinfo.h
Xinclude/bncsutil/ms_stdint.h
Xinclude/bncsutil/mutil.h
Xinclude/bncsutil/mutil_types.h
Xinclude/bncsutil/nls.h
Xinclude/bncsutil/oldauth.h
Xinclude/bncsutil/pe.h
Xinclude/bncsutil/sha1.h
Xinclude/bncsutil/stack.h
Xlib/libbncsutil.so
Xinclude/StormLib/StormLib.h
Xinclude/StormLib/StormPort.h
Xlib/libStorm.so
Xetc/ghost++/ghost.cfg.simple
Xetc/ghost++/ghost++
X@dirrm include/bncsutil
X@dirrm include/StormLib
X@exec /sbin/ldconfig -m /usr/local/lib
X@unexec /sbin/ldconfig -R
END-of-/usr/ports/games/ghost++/pkg-plist
exit



>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200903162015.n2GKFX9n041050>