polserver/pol-core/pol/uimport.cpp

890 lines
25 KiB
C++

/** @file
*
* @par History
* - 2005/01/23 Shinigami: for_all_mobiles, write_items & write_multis - Tokuno MapDimension doesn't
* fit blocks of 64x64 (WGRID_SIZE)
* - 2007/06/17 Shinigami: added config.world_data_path
* - 2009/09/03 MuadDib: Relocation of account related cpp/h
* Relocation of multi related cpp/h
* - 2009/09/14 MuadDib: All UOX3 Import Code commented out. You can script this.
* - 2009/12/02 Turley: added config.max_tile_id - Tomi
* - 2011/11/28 MuadDib: Removed last of uox referencing code.
*/
#include "uimport.h"
#include <atomic>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <exception>
#include <future>
#include <string>
#include <time.h>
#include "../clib/cfgelem.h"
#include "../clib/cfgfile.h"
#include "../clib/clib.h"
#include "../clib/clib_endian.h"
#include "../clib/fileutil.h"
#include "../clib/logfacility.h"
#include "../clib/passert.h"
#include "../clib/rawtypes.h"
#include "../clib/stlutil.h"
#include "../clib/threadhelp.h"
#include "../clib/timer.h"
#include "../plib/poltype.h"
#include "../plib/systemstate.h"
#include "containr.h"
#include "fnsearch.h"
#include "gameclck.h"
#include "globals/network.h"
#include "globals/object_storage.h"
#include "globals/settings.h"
#include "globals/state.h"
#include "globals/uvars.h"
#include "item/item.h"
#include "item/itemdesc.h"
#include "loaddata.h"
#include "mobile/charactr.h"
#include "mobile/npc.h"
#include "multi/house.h"
#include "multi/multi.h"
#include "objecthash.h"
#include "polvar.h"
#include "regions/resource.h"
#include "savedata.h"
#include "servdesc.h"
#include "startloc.h"
#include "storage.h"
#include "ufunc.h"
#include "uobject.h"
#include "uworld.h"
namespace Pol
{
namespace Module
{
void read_datastore_dat();
} // namespace Module
namespace Plib
{
bool version_greater_or_equal( const std::string& version_have, const std::string& version_need );
bool version_equal( const std::string& version_have, const std::string& version_need );
} // namespace Plib
namespace Core
{
void read_party_dat();
void read_guilds_dat();
/****************** POL Native Files *******************/
// Dave changed 3/8/3 to use objecthash
void read_character( Clib::ConfigElem& elem )
{
CharacterRef chr( new Mobile::Character( elem.remove_ushort( "OBJTYPE" ) ) );
try
{
// note chr->logged_in is true..
chr->readProperties( elem );
// Allows the realm to recognize this char as offline
chr->realm()->add_mobile( *chr, Realms::WorldChangeReason::PlayerLoad );
chr->clear_dirty();
// readProperties gets the serial, so we can't add to the objecthash until now.
objStorageManager.objecthash.Insert( chr.get() );
}
catch ( std::exception& )
{
if ( chr.get() != nullptr )
chr->destroy();
throw;
}
}
// Dave changed 3/8/3 to use objecthash
void read_npc( Clib::ConfigElem& elem )
{
NpcRef npc( new Mobile::NPC( elem.remove_ushort( "OBJTYPE" ), elem ) );
try
{
npc->readProperties( elem );
SetCharacterWorldPosition( npc.get(), Realms::WorldChangeReason::NpcLoad );
npc->clear_dirty();
////HASH
objStorageManager.objecthash.Insert( npc.get() );
////
}
catch ( std::exception& )
{
if ( npc.get() != nullptr )
npc->destroy();
throw;
}
}
// hmm, an interesting idea, what if there was an
// Item::create( ConfigElem& elem ) member,
// which would do this?
// Item would need a constructor (ConfigElem&) also.
// Polymorphism would then take
// care of it, and move the logic into the derived
// classes.
Items::Item* read_item( Clib::ConfigElem& elem )
{
u32 serial;
u32 objtype;
if ( elem.remove_prop( "SERIAL", &serial ) == false )
{
ERROR_PRINTLN( "Item element has no SERIAL property, omitting." );
return nullptr;
}
if ( !IsItem( serial ) )
{
elem.warn_with_line(
"Item element does not have an item serial\n(should be larger than 0x40000000)" );
if ( Plib::systemstate.config.check_integrity )
{
throw std::runtime_error( "Data integrity error" );
}
return nullptr;
}
if ( Plib::systemstate.config.check_integrity )
{
if ( system_find_item( serial ) )
{
ERROR_PRINTLN( "Duplicate item read from datafiles (Serial={:#x})", serial );
throw std::runtime_error( "Data integrity error" );
}
}
if ( elem.remove_prop( "OBJTYPE", &objtype ) == false )
{
ERROR_PRINTLN( "Item (Serial {:#x}) has no OBJTYPE property, omitting.", serial );
return nullptr;
}
if ( gamestate.old_objtype_conversions.count( objtype ) )
objtype = gamestate.old_objtype_conversions[objtype];
Items::Item* item = Items::Item::create( objtype, serial );
if ( item == nullptr )
{
ERROR_PRINTLN( "Unable to create item: objtype={:#x}, serial={:#x}", objtype, serial );
if ( !Plib::systemstate.config.ignore_load_errors )
throw std::runtime_error( "Item::create failed!" );
return nullptr;
}
item->setposition( Pos4d( item->pos().xyz(), find_realm( "britannia" ) ) );
item->readProperties( elem );
item->clear_dirty();
return item;
}
#define USE_PARENT_CONTS 1
using ContStack = std::stack<UContainer*>;
static ContStack parent_conts;
void read_global_item( Clib::ConfigElem& elem, int /*sysfind_flags*/ )
{
u32 container_serial = 0; // defaults to item in the world's top-level
(void)elem.remove_prop( "CONTAINER",
&container_serial ); // therefore we don't need to check the return value
Items::Item* item = read_item( elem );
// dave added 1/15/3, protect against further crash if item is null. Should throw instead?
if ( item == nullptr )
{
elem.warn_with_line( "Error reading item SERIAL or OBJTYPE." );
return;
}
ItemRef itemref( item ); // dave 1/28/3 prevent item from being destroyed before function ends
if ( container_serial == 0 )
{
add_item_to_world( item );
if ( item->isa( UOBJ_CLASS::CLASS_CONTAINER ) )
parent_conts.push( static_cast<UContainer*>( item ) );
}
else
{
if ( IsCharacter( container_serial ) ) // it's equipped on a character
{
Mobile::Character* chr = system_find_mobile( container_serial );
if ( chr != nullptr )
{
equip_loaded_item( chr, item );
}
else
{
defer_item_insertion( item, container_serial );
}
return;
}
Items::Item* cont_item = nullptr;
// bool new_parent_cont = false;
while ( !parent_conts.empty() )
{
UContainer* cont = parent_conts.top();
if ( cont->serial == container_serial )
{
cont_item = cont;
break;
}
parent_conts.pop();
}
if ( cont_item == nullptr )
{
cont_item = system_find_item( container_serial );
// new_parent_cont = true;
}
if ( cont_item )
{
add_loaded_item( cont_item, item );
}
else
{
defer_item_insertion( item, container_serial );
}
}
}
void read_system_vars( Clib::ConfigElem& elem )
{
settingsManager.polvar.DataWrittenBy = elem.remove_string( "CoreVersion" );
settingsManager.polvar.DataWrittenBy99OrLater =
Plib::version_greater_or_equal( settingsManager.polvar.DataWrittenBy, "99.0.0" );
settingsManager.polvar.DataWrittenBy93 =
Plib::version_equal( settingsManager.polvar.DataWrittenBy, "93.0.0" );
stateManager.stored_last_item_serial =
elem.remove_ulong( "LastItemSerialNumber", UINT_MAX ); // dave 3/9/3
stateManager.stored_last_char_serial =
elem.remove_ulong( "LastCharSerialNumber", UINT_MAX ); // dave 3/9/3
}
void read_realms( Clib::ConfigElem& elem )
{
std::string name = elem.remove_string( "Name" );
bool has_decay = elem.remove_bool( "HasDecay", true );
if ( elem.has_prop( "BaseRealm" ) )
{
// this is shadow realm
Realms::Realm* baserealm = find_realm( elem.remove_string( "BaseRealm" ) );
if ( !baserealm )
elem.warn_with_line( "BaseRealm not found." );
else if ( defined_realm( name ) )
elem.warn_with_line( "Realmname already defined" );
else
{
add_realm( name, baserealm, has_decay );
INFO_PRINTLN( "\nShadowrealm {}", name );
}
}
else
{
// these are dynamic settings for base realm
Realms::Realm* realm = find_realm( name );
if ( !realm )
elem.warn_with_line( "Realm not found." );
else
realm->has_decay = has_decay;
}
}
void read_multi( Clib::ConfigElem& elem )
{
pol_serial_t serial = 0;
u32 objtype;
if ( elem.remove_prop( "SERIAL", &serial ) == false )
{
ERROR_PRINTLN( "A Multi has no SERIAL property." );
throw std::runtime_error( "Config File error." );
}
if ( system_find_multi( serial ) || system_find_item( serial ) )
{
ERROR_PRINTLN( "Duplicate item read from datafiles (Serial={:#x})", serial );
throw std::runtime_error( "Data integrity error" );
}
if ( elem.remove_prop( "OBJTYPE", &objtype ) == false )
{
ERROR_PRINTLN( "Multi (Serial {:#x}) has no OBJTYPE property, omitting.", serial );
return;
}
if ( gamestate.old_objtype_conversions.count( objtype ) )
objtype = gamestate.old_objtype_conversions[objtype];
Multi::UMulti* multi = Multi::UMulti::create( Items::find_itemdesc( objtype ), serial );
if ( multi == nullptr )
{
ERROR_PRINTLN( "Unable to create multi: objtype={:#x}, serial={:#x}", objtype, serial );
throw std::runtime_error( "Multi::create failed!" );
}
multi->readProperties( elem );
add_multi_to_world( multi );
}
std::string elapsed( clock_t start, clock_t end )
{
size_t ms = static_cast<size_t>( ( end - start ) * 1000.0 / CLOCKS_PER_SEC );
return Clib::tostring( ms ) + " ms";
}
void slurp( const char* filename, const char* tags, int sysfind_flags )
{
static int num_until_dot = 1000;
if ( Clib::FileExists( filename ) )
{
INFO_PRINT( " {}:", filename );
Clib::ConfigFile cf( filename, tags );
Clib::ConfigElem elem;
Tools::Timer<> timer;
unsigned int nobjects = 0;
while ( cf.read( elem ) )
{
if ( --num_until_dot == 0 )
{
INFO_PRINT( "." );
num_until_dot = 1000;
}
try
{
if ( stricmp( elem.type(), "CHARACTER" ) == 0 )
read_character( elem );
else if ( stricmp( elem.type(), "NPC" ) == 0 )
read_npc( elem );
else if ( stricmp( elem.type(), "ITEM" ) == 0 )
read_global_item( elem, sysfind_flags );
else if ( stricmp( elem.type(), "GLOBALPROPERTIES" ) == 0 )
gamestate.global_properties->readProperties( elem );
else if ( elem.type_is( "SYSTEM" ) )
read_system_vars( elem );
else if ( elem.type_is( "MULTI" ) )
read_multi( elem );
else if ( elem.type_is( "STORAGEAREA" ) )
{
StorageArea* storage_area = gamestate.storage.create_area( elem );
// this will be followed by an item
if ( !cf.read( elem ) )
throw std::runtime_error( "Expected an item to exist after the storagearea." );
storage_area->load_item( elem );
}
else if ( elem.type_is( "REALM" ) )
read_realms( elem );
}
catch ( std::exception& )
{
INFO_PRINTLN( "Failed to load entry starting at line: {}", cf.element_line_start() );
if ( !Plib::systemstate.config.ignore_load_errors )
throw;
}
++nobjects;
}
timer.stop();
INFO_PRINTLN( " {} elements in {} ms.", nobjects, timer.ellapsed() );
}
}
void read_pol_dat()
{
std::string polfile = Plib::systemstate.config.world_data_path + "pol.txt";
slurp( polfile.c_str(), "GLOBALPROPERTIES SYSTEM REALM" );
if ( settingsManager.polvar.DataWrittenBy.empty() )
{
ERROR_PRINTLN(
"CoreVersion not found in {}\n"
"\n"
"{} must contain a section similar to: \n"
"System\n"
"{{\n"
"\tCoreVersion 99\n"
"}}\n"
"\n"
"Ensure that the CoreVersion matches the version that created your data files!",
polfile, polfile );
throw std::runtime_error( "Data file error" );
}
}
void read_objects_dat()
{
slurp( ( Plib::systemstate.config.world_data_path + "objects.txt" ).c_str(),
"CHARACTER NPC ITEM GLOBALPROPERTIES" );
}
void read_pcs_dat()
{
slurp( ( Plib::systemstate.config.world_data_path + "pcs.txt" ).c_str(), "CHARACTER ITEM",
SYSFIND_SKIP_WORLD );
}
void read_pcequip_dat()
{
slurp( ( Plib::systemstate.config.world_data_path + "pcequip.txt" ).c_str(), "ITEM",
SYSFIND_SKIP_WORLD );
}
void read_npcs_dat()
{
slurp( ( Plib::systemstate.config.world_data_path + "npcs.txt" ).c_str(), "NPC ITEM",
SYSFIND_SKIP_WORLD );
}
void read_npcequip_dat()
{
slurp( ( Plib::systemstate.config.world_data_path + "npcequip.txt" ).c_str(), "ITEM",
SYSFIND_SKIP_WORLD );
}
void read_items_dat()
{
slurp( ( Plib::systemstate.config.world_data_path + "items.txt" ).c_str(), "ITEM" );
}
void read_multis_dat()
{
slurp( ( Plib::systemstate.config.world_data_path + "multis.txt" ).c_str(), "MULTI" );
// string multisfile = config.world_data_path + "multis.txt";
// if (FileExists( multisfile ))
// {
// cout << multisfile << ":";
// ConfigFile cf( multisfile, "MULTI" );
// ConfigElem elem;
// while( cf.read( elem ))
// {
// UMulti* multi = read_multi( elem );
// if (multi == nullptr) throw runtime_error( "multi creation returned nullptr!" );
//
// add_multi_to_world( multi );
// }
// }
}
void read_storage_dat()
{
std::string storagefile = Plib::systemstate.config.world_data_path + "storage.txt";
if ( Clib::FileExists( storagefile ) )
{
INFO_PRINT( " {}:", storagefile );
Clib::ConfigFile cf2( storagefile );
gamestate.storage.read( cf2 );
}
}
Items::Item* find_existing_item( u32 objtype, const Pos4d& pos )
{
Pos2d gridp = zone_convert( pos );
for ( auto& item : pos.realm()->getzone_grid( gridp ).items )
{
// FIXME won't find doors which have been perturbed
if ( item->objtype_ == objtype && item->pos() == pos )
{
return item;
}
}
return nullptr;
}
int import_count;
int dupe_count;
void import( Clib::ConfigElem& elem )
{
u32 objtype;
objtype = elem.remove_unsigned( "OBJTYPE" );
if ( objtype > Plib::systemstate.config.max_tile_id )
{
ERROR_PRINTLN( "Importing file: {:#x} is out of range.", objtype );
throw std::runtime_error( "Error while importing file." );
}
Items::Item* item = Items::Item::create( objtype, 0x40000000 ); // dummy serial
if ( item == nullptr )
{
ERROR_PRINTLN( "Unable to import item: objtype={:#x}", objtype );
throw std::runtime_error( "Item::create failed!" );
}
item->readProperties( elem );
if ( find_existing_item( item->objtype_, item->pos() ) )
{
item->destroy();
++dupe_count;
}
else
{
item->serial = GetNewItemSerialNumber();
item->serial_ext = ctBEu32( item->serial );
add_item_to_world( item );
register_with_supporting_multi( item );
++import_count;
}
}
void import_new_data()
{
std::string importfile = Plib::systemstate.config.world_data_path + "import.txt";
if ( Clib::FileExists( importfile ) )
{
Clib::ConfigFile cf( importfile, "ITEM" );
Clib::ConfigElem elem;
while ( cf.read( elem ) )
{
import( elem );
}
unlink( importfile.c_str() );
INFO_PRINTLN( "Import Results: {} imported, {} duplicates.", import_count, dupe_count );
}
}
void rndat( const std::string& basename )
{
std::string datname = Plib::systemstate.config.world_data_path + basename + ".dat";
std::string txtname = Plib::systemstate.config.world_data_path + basename + ".txt";
if ( Clib::FileExists( datname.c_str() ) )
{
rename( datname.c_str(), txtname.c_str() );
}
}
void rename_dat_files()
{
rndat( "pol" );
rndat( "objects" );
rndat( "pcs" );
rndat( "pcequip" );
rndat( "npcs" );
rndat( "npcequip" );
rndat( "items" );
rndat( "multis" );
rndat( "storage" );
rndat( "resource" );
rndat( "guilds" );
rndat( "parties" );
}
int read_data()
{
std::string objectsndtfile = Plib::systemstate.config.world_data_path + "objects.ndt";
std::string storagendtfile = Plib::systemstate.config.world_data_path + "storage.ndt";
stateManager.gflag_in_system_load = true;
if ( Clib::FileExists( objectsndtfile ) )
{
// Display reads "Reading data files..."
ERROR_PRINTLN(
"Error!\n"
"'{} exists. This probably means the system\n"
"exited while writing its state. To avoid loss of data,\n"
"forcing human intervention.",
objectsndtfile );
throw std::runtime_error( "Human intervention required." );
}
if ( Clib::FileExists( storagendtfile ) )
{
ERROR_PRINTLN(
"Error!\n"
"'{} exists. This probably means the system\n"
"exited while writing its state. To avoid loss of data,\n"
"forcing human intervention.",
storagendtfile );
throw std::runtime_error( "Human intervention required." );
}
rename_dat_files();
read_pol_dat();
// POL clock should be paused at this point.
start_gameclock();
SaveContext::last_worldsave_success = read_gameclock(); // set last success to load time
read_objects_dat();
read_pcs_dat();
read_pcequip_dat();
read_npcs_dat();
read_npcequip_dat();
read_items_dat();
read_multis_dat();
read_storage_dat();
read_resources_dat();
read_guilds_dat();
Module::read_datastore_dat();
read_party_dat();
insert_deferred_items();
import_new_data();
// import_wsc();
// dave 3/9/3
if ( stateManager.stored_last_item_serial < GetCurrentItemSerialNumber() )
SetCurrentItemSerialNumber( stateManager.stored_last_item_serial );
if ( stateManager.stored_last_char_serial < GetCurrentCharSerialNumber() )
SetCurrentCharSerialNumber( stateManager.stored_last_char_serial );
while ( !parent_conts.empty() )
parent_conts.pop();
for ( const auto& citr : objStorageManager.objecthash )
{
UObject* obj = citr.second.get();
if ( obj->ismobile() )
{
Mobile::Character* chr = static_cast<Mobile::Character*>( obj );
if ( chr->acct != nullptr )
chr->logged_in( false );
}
}
stateManager.gflag_in_system_load = false;
return 0;
}
void read_starting_locations()
{
Clib::ConfigFile cf( "config/startloc.cfg" );
Clib::ConfigElem elem;
while ( cf.read( elem ) )
{
if ( stricmp( elem.type(), "StartingLocation" ) != 0 )
{
ERROR_PRINTLN( "Unknown element type in startloc.cfg: {}", elem.type() );
throw std::runtime_error( "Error in configuration file." );
}
std::unique_ptr<StartingLocation> loc( new StartingLocation );
loc->city = elem.remove_string( "CITY" );
loc->desc = elem.remove_string( "DESCRIPTION" );
loc->mapid = elem.remove_ushort( "MAPID", 0 );
loc->cliloc_desc = elem.remove_unsigned( "CLILOC", 1075072 );
auto realmstr = elem.remove_string( "REALM", "britannia" );
loc->realm = find_realm( realmstr );
if ( !loc->realm )
{
ERROR_PRINTLN( "Unknown realm in startloc.cfg: '{}' for city {}, description {}", realmstr,
loc->city, loc->desc );
throw std::runtime_error( "Configuration file error in startloc.cfg." );
}
std::string coord;
while ( elem.remove_prop( "Coordinate", &coord ) )
{
int x, y, z;
if ( sscanf( coord.c_str(), "%d,%d,%d", &x, &y, &z ) == 3 )
{
auto pos = Pos3d( Clib::clamp_convert<u16>( x ), Clib::clamp_convert<u16>( y ),
Clib::clamp_convert<s8>( z ) );
pos.crop( loc->realm );
loc->coords.emplace_back( std::move( pos ) );
}
else
{
ERROR_PRINTLN( "Poorly formed coordinate in startloc.cfg: '{}' for city {}, description {}",
coord, loc->city, loc->desc );
throw std::runtime_error( "Configuration file error in startloc.cfg." );
}
}
if ( loc->coords.empty() )
{
ERROR_PRINTLN( "STARTLOC.CFG: StartingLocation ({},{}) has no Coordinate properties.",
loc->city, loc->desc );
throw std::runtime_error( "Configuration file error." );
}
gamestate.startlocations.push_back( loc.release() );
}
if ( gamestate.startlocations.empty() )
throw std::runtime_error(
"STARTLOC.CFG: No starting locations found. Clients will crash on character creation." );
}
ServerDescription::ServerDescription() : name( "" ), port( 0 ), hostname( "" )
{
memset( ip, 0, sizeof ip );
}
size_t ServerDescription::estimateSize() const
{
size_t size = name.capacity() + 4 * sizeof( unsigned char ) /*ip*/
+ sizeof( unsigned short ) /*port*/
+ Clib::memsize( ip_match ) + Clib::memsize( ip_match_mask ) +
Clib::memsize( proxy_match ) + Clib::memsize( proxy_match_mask ) +
hostname.capacity();
for ( const auto& s : acct_match )
size += s.capacity();
return size;
}
void read_gameservers()
{
std::string accttext;
Clib::ConfigFile cf( "config/servers.cfg" );
Clib::ConfigElem elem;
while ( cf.read( elem ) )
{
if ( !elem.type_is( "GameServer" ) )
continue;
std::unique_ptr<ServerDescription> svr( new ServerDescription );
svr->name = elem.remove_string( "NAME" );
std::string iptext;
int ip0, ip1, ip2, ip3;
iptext = elem.remove_string( "IP" );
if ( iptext == "--ip--" )
{
iptext = networkManager.ipaddr_str;
if ( iptext.empty() )
{
INFO_PRINTLN( "Skipping server {} because there is no Internet IP address.", svr->name );
continue;
}
}
else if ( iptext == "--lan--" )
{
iptext = networkManager.lanaddr_str;
if ( iptext.empty() )
{
INFO_PRINTLN( "Skipping server {} because there is no LAN IP address.", svr->name );
continue;
}
}
if ( isdigit( iptext[0] ) )
{
if ( sscanf( iptext.c_str(), "%d.%d.%d.%d", &ip0, &ip1, &ip2, &ip3 ) != 4 )
{
ERROR_PRINTLN( "SERVERS.CFG: Poorly formed IP ({}) for GameServer '{}'.", iptext,
svr->name );
throw std::runtime_error( "Configuration file error." );
}
svr->ip[0] = static_cast<unsigned char>( ip3 );
svr->ip[1] = static_cast<unsigned char>( ip2 );
svr->ip[2] = static_cast<unsigned char>( ip1 );
svr->ip[3] = static_cast<unsigned char>( ip0 );
}
else
{
svr->hostname = iptext;
#ifdef __linux__
/* try to look up */
struct hostent host_ret;
struct hostent* host_result = nullptr;
char tmp_buf[1024];
int my_h_errno;
int res = gethostbyname_r( svr->hostname.c_str(), &host_ret, tmp_buf, sizeof tmp_buf,
&host_result, &my_h_errno );
if ( res == 0 && host_result && host_result->h_addr_list[0] )
{
char* addr = host_result->h_addr_list[0];
svr->ip[0] = addr[3];
svr->ip[1] = addr[2];
svr->ip[2] = addr[1];
svr->ip[3] = addr[0];
/*
struct sockaddr_in saddr;
memcpy( &saddr.sin_addr, he->h_addr_list[0], he->h_length);
server->ip[0] = saddr.sin_addr.S_un.S_un_b.s_b1;
server->ip[1] = saddr.sin_addr.S_un.S_un_b.s_b2;
server->ip[2] = saddr.sin_addr.S_un.S_un_b.s_b3;
server->ip[3] = saddr.sin_addr.S_un.S_un_b.s_b4;
*/
}
else
{
POLLOG_ERRORLN( "Warning: gethostbyname_r failed for server {} ({}): {}", svr->name,
svr->hostname, my_h_errno );
}
#endif
}
svr->port = elem.remove_ushort( "PORT" );
while ( elem.remove_prop( "IPMATCH", &iptext ) )
{
auto delim = iptext.find_first_of( '/' );
if ( delim != std::string::npos )
{
std::string ipaddr_str = iptext.substr( 0, delim );
std::string ipmask_str = iptext.substr( delim + 1 );
unsigned int ipaddr = inet_addr( ipaddr_str.c_str() );
unsigned int ipmask = inet_addr( ipmask_str.c_str() );
svr->ip_match.push_back( ipaddr );
svr->ip_match_mask.push_back( ipmask );
}
else
{
unsigned int ipaddr = inet_addr( iptext.c_str() );
svr->ip_match.push_back( ipaddr );
svr->ip_match_mask.push_back( 0xFFffFFffLu );
}
}
while ( elem.remove_prop( "PROXYMATCH", &iptext ) )
{
auto delim = iptext.find_first_of( '/' );
if ( delim != std::string::npos )
{
std::string ipaddr_str = iptext.substr( 0, delim );
std::string ipmask_str = iptext.substr( delim + 1 );
unsigned int ipaddr = inet_addr( ipaddr_str.c_str() );
unsigned int ipmask = inet_addr( ipmask_str.c_str() );
svr->proxy_match.push_back( ipaddr );
svr->proxy_match_mask.push_back( ipmask );
}
else
{
unsigned int ipaddr = inet_addr( iptext.c_str() );
svr->proxy_match.push_back( ipaddr );
svr->proxy_match_mask.push_back( 0xFFffFFffLu );
}
}
while ( elem.remove_prop( "ACCTMATCH", &accttext ) )
{
svr->acct_match.push_back( accttext );
}
networkManager.servers.push_back( svr.release() );
}
if ( networkManager.servers.empty() )
throw std::runtime_error( "There must be at least one GameServer in SERVERS.CFG." );
}
} // namespace Core
} // namespace Pol