24 #include "../clib/Program/ProgramConfig.h" 25 #include "../clib/cfgelem.h" 26 #include "../clib/cfgfile.h" 27 #include "../clib/clib.h" 28 #include "../clib/clib_endian.h" 29 #include "../clib/esignal.h" 30 #include "../clib/fileutil.h" 31 #include "../clib/logfacility.h" 32 #include "../clib/passert.h" 33 #include "../clib/rawtypes.h" 34 #include "../clib/threadhelp.h" 35 #include "../clib/timer.h" 36 #include "../plib/systemstate.h" 98 chr->readProperties( elem );
108 catch ( std::exception& )
110 if ( chr.get() != nullptr )
130 npc->readProperties( elem );
139 catch ( std::exception& )
141 if ( npc.
get() != nullptr )
158 if ( elem.
remove_prop(
"SERIAL", &serial ) == false )
160 ERROR_PRINT <<
"Item element has no SERIAL property, omitting.\n";
167 "Item element does not have an item serial\n(should be larger than 0x40000000)" );
170 throw std::runtime_error(
"Data integrity error" );
179 ERROR_PRINT.Format(
"Duplicate item read from datafiles (Serial=0x{:X})\n" ) << serial;
180 throw std::runtime_error(
"Data integrity error" );
183 if ( elem.
remove_prop(
"OBJTYPE", &objtype ) == false )
185 ERROR_PRINT.Format(
"Item (Serial 0x{:X}) has no OBJTYPE property, omitting." ) << serial;
192 if ( item ==
nullptr )
194 ERROR_PRINT.Format(
"Unable to create item: objtype=0x{:X}, serial=0x{:X}" )
195 << objtype << serial;
197 throw std::runtime_error(
"Item::create failed!" );
203 item->readProperties( elem );
210 #define USE_PARENT_CONTS 1 225 u32 container_serial = 0;
231 if ( item ==
nullptr )
238 if ( container_serial == 0 )
241 if ( item->
isa( UOBJ_CLASS::CLASS_CONTAINER ) )
242 parent_conts.push( static_cast<UContainer*>( item ) );
249 if ( chr !=
nullptr )
262 while ( !parent_conts.empty() )
265 if ( cont->
serial == container_serial )
276 if ( cont_item ==
nullptr )
313 INFO_PRINT <<
"\nShadowrealm " << name <<
"\n";
327 if ( elem.
remove_prop(
"SERIAL", &serial ) == false )
329 ERROR_PRINT <<
"A Multi has no SERIAL property.\n";
330 throw std::runtime_error(
"Config File error." );
334 ERROR_PRINT.Format(
"Duplicate item read from datafiles (Serial=0x{:X})\n" ) << serial;
335 throw std::runtime_error(
"Data integrity error" );
337 if ( elem.
remove_prop(
"OBJTYPE", &objtype ) == false )
339 ERROR_PRINT.Format(
"Multi (Serial 0x{:X}) has no OBJTYPE property, omitting." ) << serial;
346 if ( multi ==
nullptr )
348 ERROR_PRINT.Format(
"Unable to create multi: objtype=0x{:X}, serial=0x{:X}\n" )
349 << objtype << serial;
350 throw std::runtime_error(
"Multi::create failed!" );
352 multi->readProperties( elem );
357 std::string
elapsed( clock_t start, clock_t end )
359 size_t ms =
static_cast<size_t>( ( end - start ) * 1000.0 / CLOCKS_PER_SEC );
363 void slurp(
const char* filename,
const char* tags,
int sysfind_flags )
365 static int num_until_dot = 1000;
375 unsigned int nobjects = 0;
376 while ( cf.
read( elem ) )
378 if ( --num_until_dot == 0 )
381 num_until_dot = 1000;
385 if ( stricmp( elem.
type(),
"CHARACTER" ) == 0 )
387 else if ( stricmp( elem.
type(),
"NPC" ) == 0 )
389 else if ( stricmp( elem.
type(),
"ITEM" ) == 0 )
391 else if ( stricmp( elem.
type(),
"GLOBALPROPERTIES" ) == 0 )
393 else if ( elem.
type_is(
"SYSTEM" ) )
395 else if ( elem.
type_is(
"MULTI" ) )
397 else if ( elem.
type_is(
"STORAGEAREA" ) )
401 if ( !cf.
read( elem ) )
402 throw std::runtime_error(
"Expected an item to exist after the storagearea." );
406 else if ( elem.
type_is(
"REALM" ) )
409 catch ( std::exception& )
427 slurp( polfile.c_str(),
"GLOBALPROPERTIES SYSTEM REALM" );
432 <<
"CoreVersion not found in " << polfile <<
"\n\n" 433 << polfile <<
" must contain a section similar to: \n" 436 <<
"\tCoreVersion 99\n" 438 <<
"Ensure that the CoreVersion matches the version that created your data files!\n";
439 throw std::runtime_error(
"Data file error" );
446 "CHARACTER NPC ITEM GLOBALPROPERTIES" );
511 unsigned short wx, wy;
513 for (
auto& item : realm->
zone[wx][wy].
items )
516 if ( item->objtype_ == objtype && item->x == x && item->y == y && item->z == z )
530 objtype = elem.remove_unsigned(
"OBJTYPE" );
533 ERROR_PRINT.Format(
"Importing file: 0x{:X} is out of range.\n" ) << objtype;
534 throw std::runtime_error(
"Error while importing file." );
539 if ( item ==
nullptr )
541 ERROR_PRINT.Format(
"Unable to import item: objtype=0x{:X}\n" ) << objtype;
542 throw std::runtime_error(
"Item::create failed!" );
572 while ( cf.
read( elem ) )
576 unlink( importfile.c_str() );
577 INFO_PRINT <<
"Import Results: " << import_count <<
" imported, " << dupe_count
582 void rndat(
const std::string& basename )
589 rename( datname.c_str(), txtname.c_str() );
619 <<
"'" << objectsndtfile <<
" exists. This probably means the system\n" 620 <<
"exited while writing its state. To avoid loss of data,\n" 621 <<
"forcing human intervention.\n";
622 throw std::runtime_error(
"Human intervention required." );
627 <<
"'" << storagendtfile <<
" exists. This probably means the system\n" 628 <<
"exited while writing its state. To avoid loss of data,\n" 629 <<
"forcing human intervention.\n";
630 throw std::runtime_error(
"Human intervention required." );
670 while ( !parent_conts.empty() )
675 citr != citrend; ++citr )
677 UObject* obj = ( *citr ).second.get();
682 if ( chr->
acct !=
nullptr )
707 objects( &_objects ),
709 pcequip( &_pcequip ),
711 npcequip( &_npcequip ),
714 storage( &_storage ),
715 resource( &_resource ),
717 datastore( &_datastore ),
735 <<
"# In addition to PC data, this also contains hair, beards, death shrouds," <<
pf_endl 736 <<
"# and backpacks, but not the contents of each backpack." <<
pf_endl <<
"#" <<
pf_endl 739 pcequip() <<
"#" << pf_endl <<
"# PCEQUIP.TXT: Player-Character Equipment Data" << pf_endl <<
"#" 741 <<
"# This file can be deleted to wipe all items held/equipped by characters" 743 <<
"# Note that hair, beards, empty backpacks, and death shrouds are in PCS.TXT." 744 << pf_endl <<
"#" << pf_endl <<
pf_endl;
746 npcs() <<
"#" << pf_endl <<
"# NPCS.TXT: Nonplayer-Character Data" << pf_endl <<
"#" << pf_endl
747 <<
"# If you delete this file to perform an NPC wipe," << pf_endl
748 <<
"# be sure to also delete NPCEQUIP.TXT" << pf_endl <<
"#" << pf_endl <<
pf_endl;
750 npcequip() <<
"#" << pf_endl <<
"# NPCEQUIP.TXT: Nonplayer-Character Equipment Data" << pf_endl
751 <<
"#" << pf_endl <<
"# Delete this file along with NPCS.TXT to perform an NPC wipe" 752 << pf_endl <<
"#" << pf_endl <<
pf_endl;
754 items() <<
"#" << pf_endl <<
"# ITEMS.TXT: Item data" << pf_endl <<
"#" << pf_endl
755 <<
"# This file also contains ship and house components (doors, planks etc)" << pf_endl
758 multis() <<
"#" << pf_endl <<
"# MULTIS.TXT: Ship and House data" << pf_endl <<
"#" << pf_endl
759 <<
"# Deleting this file will not properly wipe houses and ships," << pf_endl
760 <<
"# because doors, planks, and tillermen will be left in the world." << pf_endl <<
"#" 764 <<
"# STORAGE.TXT: Contains bank boxes, vendor inventories, and other data." << pf_endl
766 <<
"# This file can safely be deleted to wipe bank boxes and vendor inventories." 767 << pf_endl <<
"# Note that scripts may use this for other types of storage as well" 768 << pf_endl <<
"#" << pf_endl <<
pf_endl;
770 resource() <<
"#" << pf_endl <<
"# RESOURCE.TXT: Resource System Data" << pf_endl <<
"#" 773 guilds() <<
"#" << pf_endl <<
"# GUILDS.TXT: Guild Data" << pf_endl <<
"#" << pf_endl <<
pf_endl;
775 datastore() <<
"#" << pf_endl <<
"# DATASTORE.TXT: DataStore Data" << pf_endl <<
"#" << pf_endl
777 party() <<
"#" << pf_endl <<
"# PARTIES.TXT: Party Data" << pf_endl <<
"#" << pf_endl <<
pf_endl;
800 if ( SaveContext::finished.valid() )
803 SaveContext::finished.wait();
812 sw() <<
"}" << pf_endl <<
pf_endl;
819 <<
"\tCoreVersionString\t" << POL_VERSION_ID <<
pf_endl <<
"\tCompileDateTime\t" 831 if ( realm->is_shadowrealm )
844 if ( item ==
nullptr || item->
orphan() )
854 item->
x = item->
y = item->
z = 0;
861 UObject* obj = objitr.second.get();
879 UObject* obj = objitr.second.get();
900 unsigned wgridx = realm->grid_width();
901 unsigned wgridy = realm->grid_height();
903 for (
unsigned wx = 0; wx < wgridx; ++wx )
905 for (
unsigned wy = 0; wy < wgridy; ++wy )
907 for (
const auto& item : realm->zone[wx][wy].items )
909 if ( item->itemdesc().save_on_exit && item->saveonexit() )
921 UObject* obj = objitr.second.get();
928 if ( chr->has_gotten_item() )
939 unsigned wgridx = realm->grid_width();
940 unsigned wgridy = realm->grid_height();
942 for (
unsigned wx = 0; wx < wgridx; ++wx )
944 for (
unsigned wy = 0; wy < wgridy; ++wy )
946 for (
auto& multi : realm->zone[wx][wy].multis )
951 if ( house !=
nullptr )
961 multi->clear_dirty();
968 bool commit(
const std::string& basename )
973 const char* bakfile_c = bakfile.c_str();
974 const char* datfile_c = datfile.c_str();
975 const char* ndtfile_c = ndtfile.c_str();
982 if ( unlink( bakfile_c ) )
986 << bakfile_c << strerror( err ) << err;
993 if ( rename( datfile_c, bakfile_c ) )
996 POLLOG_ERROR.Format(
"Unable to rename {} to {}: {} ({})\n" )
997 << datfile_c << bakfile_c << strerror( err ) << err;
1004 if ( rename( ndtfile_c, datfile_c ) )
1007 POLLOG_ERROR.Format(
"Unable to rename {} to {}: {} ({})\n" )
1008 << ndtfile_c << datfile_c << strerror( err ) << err;
1025 int write_data(
unsigned int& dirty_writes,
unsigned int& clean_writes,
long long& elapsed_ms )
1030 dirty_writes = clean_writes = 0;
1043 auto critical_promise = std::make_shared<std::promise<bool>>();
1044 auto critical_future = critical_promise->get_future();
1045 SaveContext::finished = std::async( std::launch::async, [&, critical_promise]() ->
bool {
1050 std::vector<std::future<bool>> critical_parts;
1054 sc.pol() <<
"#" << pf_endl <<
"# Created by Version: " << POL_VERSION_ID << pf_endl
1055 <<
"# Mobiles: " << get_mobile_count() << pf_endl
1056 <<
"# Top-level Items: " << get_toplevel_item_count() << pf_endl <<
"#" 1057 << pf_endl << pf_endl;
1059 write_system_data( sc.pol );
1060 write_global_properties( sc.pol );
1061 write_shadow_realms( sc.pol );
1073 write_items( sc.items );
1085 write_characters( sc );
1089 POLLOG_ERROR <<
"failed to store character datafile!\n";
1109 write_multis( sc.multis );
1121 gamestate.storage.print( sc.storage );
1133 write_resources_dat( sc.resource );
1137 POLLOG_ERROR <<
"failed to store resource datafile!\n";
1145 write_guilds( sc.guilds );
1157 Module::write_datastore( sc.datastore );
1159 Module::commit_datastore();
1163 POLLOG_ERROR <<
"failed to store datastore datafile!\n";
1171 write_party( sc.party );
1180 for (
auto& task : critical_parts )
1183 critical_promise->set_value( result );
1189 critical_promise->set_value(
false );
1206 critical_future.wait();
1224 elapsed_ms = timer.ellapsed();
1235 while ( cf.
read( elem ) )
1237 if ( stricmp( elem.
type(),
"StartingLocation" ) != 0 )
1239 ERROR_PRINT <<
"Unknown element type in startloc.cfg: " << elem.
type() <<
"\n";
1240 throw std::runtime_error(
"Error in configuration file." );
1251 while ( elem.
remove_prop(
"Coordinate", &coord ) )
1254 if ( sscanf( coord.c_str(),
"%d,%d,%d", &x, &y, &z ) == 3 )
1256 loc->coords.push_back(
1257 Coordinate( static_cast<u16>( x ), static_cast<u16>( y ), static_cast<s8>( z ) ) );
1261 ERROR_PRINT <<
"Poorly formed coordinate in startloc.cfg: '" << coord <<
"' for city " 1262 << loc->city <<
", description " << loc->desc <<
"\n";
1263 throw std::runtime_error(
"Configuration file error in startloc.cfg." );
1266 if ( loc->coords.size() == 0 )
1268 ERROR_PRINT <<
"STARTLOC.CFG: StartingLocation (" << loc->city <<
"," << loc->desc
1269 <<
") has no Coordinate properties." 1271 throw std::runtime_error(
"Configuration file error." );
1277 throw std::runtime_error(
1278 "STARTLOC.CFG: No starting locations found. Clients will crash on character creation." );
1283 memset(
ip, 0,
sizeof ip );
1288 size_t size =
name.capacity() + 4 *
sizeof(
unsigned char )
1289 +
sizeof(
unsigned short )
1290 + 3 *
sizeof(
unsigned int* ) +
ip_match.capacity() *
sizeof(
unsigned int ) +
1291 3 *
sizeof(
unsigned int* ) +
ip_match_mask.capacity() *
sizeof(
unsigned int ) +
1292 3 *
sizeof( std::string* ) +
hostname.capacity();
1294 size += s.capacity();
1300 std::string accttext;
1305 while ( cf.
read( elem ) )
1307 if ( !elem.
type_is(
"GameServer" ) )
1315 int ip0, ip1, ip2, ip3;
1317 if ( iptext ==
"--ip--" )
1322 INFO_PRINT <<
"Skipping server " << svr->name
1323 <<
" because there is no Internet IP address.\n";
1327 else if ( iptext ==
"--lan--" )
1332 INFO_PRINT <<
"Skipping server " << svr->name <<
" because there is no LAN IP address.\n";
1337 if ( isdigit( iptext[0] ) )
1339 if ( sscanf( iptext.c_str(),
"%d.%d.%d.%d", &ip0, &ip1, &ip2, &ip3 ) != 4 )
1341 ERROR_PRINT <<
"SERVERS.CFG: Poorly formed IP (" << iptext <<
") for GameServer '" 1342 << svr->name <<
"'.\n";
1343 throw std::runtime_error(
"Configuration file error." );
1345 svr->ip[0] =
static_cast<unsigned char>( ip3 );
1346 svr->ip[1] =
static_cast<unsigned char>( ip2 );
1347 svr->ip[2] =
static_cast<unsigned char>( ip1 );
1348 svr->ip[3] =
static_cast<unsigned char>( ip0 );
1352 svr->hostname = iptext;
1356 struct hostent host_ret;
1357 struct hostent* host_result =
nullptr;
1360 int res = gethostbyname_r( svr->hostname.c_str(), &host_ret, tmp_buf,
sizeof tmp_buf,
1361 &host_result, &my_h_errno );
1362 if ( res == 0 && host_result && host_result->h_addr_list[0] )
1364 char* addr = host_result->h_addr_list[0];
1365 svr->ip[0] = addr[3];
1366 svr->ip[1] = addr[2];
1367 svr->ip[2] = addr[1];
1368 svr->ip[3] = addr[0];
1380 POLLOG_ERROR.Format(
"Warning: gethostbyname_r failed for server {} ({}): {}\n" )
1381 << svr->name << svr->hostname << my_h_errno;
1390 auto delim = iptext.find_first_of(
'/' );
1391 if ( delim != std::string::npos )
1393 std::string ipaddr_str = iptext.substr( 0, delim );
1394 std::string ipmask_str = iptext.substr( delim + 1 );
1395 unsigned int ipaddr = inet_addr( ipaddr_str.c_str() );
1396 unsigned int ipmask = inet_addr( ipmask_str.c_str() );
1397 svr->ip_match.push_back( ipaddr );
1398 svr->ip_match_mask.push_back( ipmask );
1402 unsigned int ipaddr = inet_addr( iptext.c_str() );
1403 svr->ip_match.push_back( ipaddr );
1404 svr->ip_match_mask.push_back( 0xFFffFFffLu );
1408 while ( elem.
remove_prop(
"ACCTMATCH", &accttext ) )
1410 svr->acct_match.push_back( accttext );
1416 throw std::runtime_error(
"There must be at least one GameServer in SERVERS.CFG." );
void register_with_supporting_multi(Item *item)
static Item * create(u32 objtype, u32 serial=0)
size_t estimateSize() const
void read_system_vars(Clib::ConfigElem &elem)
static std::atomic< unsigned int > dirty_writes
bool defined_realm(const std::string &name)
void warn_with_line(const std::string &errmsg) const
void write_items(Clib::StreamWriter &sw_items)
std::string remove_string(const char *propname)
void add_item_to_world(Items::Item *item)
void SetCurrentCharSerialNumber(u32 serial)
static void ready()
blocks till possible last commit finishes
void get_prop(const char *propname, unsigned int *plong) const
void add_loaded_item(Items::Item *cont_item, Items::Item *item)
std::string world_data_path
threadhelp::TaskThreadPool task_thread_pool
void SetCharacterWorldPosition(Mobile::Character *chr, Realms::WorldChangeReason reason)
void write_system_data(Clib::StreamWriter &sw)
unsigned int stored_last_char_serial
std::string decint(unsigned short v)
void printWornItems(Clib::StreamWriter &sw_pc, Clib::StreamWriter &sw_equip) const
hs::const_iterator begin() const
void write_account_data()
unsigned int remove_ulong(const char *propname)
std::string elapsed(clock_t start, clock_t end)
void read_global_item(Clib::ConfigElem &elem, int)
StorageArea * create_area(const std::string &name)
void insert_deferred_items()
unsigned current_incremental_save
std::vector< unsigned int > ip_match
std::future< bool > checked_push(const msg &msg)
returns a future which will be set once the msg is processed
void force_backtrace(bool complete)
std::unique_ptr< Core::PropertyList > global_properties
bool gflag_in_system_load
unsigned short DataWrittenBy
static std::shared_future< bool > finished
const ItemDesc & find_itemdesc(unsigned int objtype)
void load_incremental_indexes()
bool incremental_saves_disabled
void write_shadow_realms(Clib::StreamWriter &sw)
void add_multi_to_world(Multi::UMulti *multi)
void read_incremental_saves()
void write_npcs(SaveContext &sc)
virtual void readProperties(Clib::ConfigElem &elem) POL_OVERRIDE
void read_npc(Clib::ConfigElem &elem)
Multi::UMulti * system_find_multi(u32 serial)
static std::atomic< unsigned int > clean_writes
const unsigned SYSFIND_SKIP_WORLD
void read_resources_dat()
void write_multis(Clib::StreamWriter &ofs)
std::vector< Realms::Realm * > Realms
u32 GetNewItemSerialNumber(void)
NetworkManager networkManager
Items::Item * find_existing_item(u32 objtype, u16 x, u16 y, s8 z, Realms::Realm *realm)
void read_character(Clib::ConfigElem &elem)
unsigned incremental_save_count
virtual void flush_file() POL_OVERRIDE
void commit_incremental_saves()
static ContStack parent_conts
void AcceptHouseCommit(Mobile::Character *chr, bool accept)
ref_ptr< Mobile::Character > CharacterRef
void write_party(Clib::StreamWriter &sw)
virtual void printOn(Clib::StreamWriter &sw) const POL_OVERRIDE
unsigned int stored_last_item_serial
void equip_loaded_item(Mobile::Character *chr, Items::Item *item)
std::stack< UContainer * > ContStack
static UMulti * create(const Items::ItemDesc &descriptor, u32 serial=0)
SettingsManager settingsManager
Items::Item * read_item(Clib::ConfigElem &elem)
u32 GetCurrentItemSerialNumber(void)
StateManager stateManager
void zone_convert(unsigned short x, unsigned short y, unsigned short *wx, unsigned short *wy, const Realms::Realm *realm)
Mobile::Character * system_find_mobile(u32 serial)
void write_global_properties(Clib::StreamWriter &sw)
void write_datastore(Clib::StreamWriter &sw)
bool Insert(UObject *obj)
bool remove_prop(const char *propname, std::string *value)
ref_ptr< Items::Item > ItemRef
void read_shadow_realms(Clib::ConfigElem &elem)
virtual void init(const std::string &filepath) POL_OVERRIDE
void read(Clib::ConfigFile &cf)
bool IsCharacter(u32 serial)
Items::Item * system_find_item(u32 serial)
bool type_is(const char *name) const
u32 GetCurrentCharSerialNumber(void)
OldObjtypeConversions old_objtype_conversions
virtual void printOn(Clib::StreamWriter &) const
ObjectStorageManager objStorageManager
void read_multi(Clib::ConfigElem &elem)
void SetCurrentItemSerialNumber(u32 serial)
unsigned short remove_ushort(const char *propname)
virtual class UHouse * as_house() POL_OVERRIDE
Realms::Realm * find_realm(const std::string &name)
hs::const_iterator end() const
void write_guilds(Clib::StreamWriter &sw)
void read_datastore_dat()
std::vector< std::string > acct_match
bool isa(UOBJ_CLASS uobj_class) const
void start_gameclock()
The functions below deal with reading and updating the gameclock. The state is protected by a mutex...
bool passert_shutdown_due_to_assertion
void load_item(Clib::ConfigElem &elem)
void write_characters(SaveContext &sc)
void defer_item_insertion(Items::Item *item, pol_serial_t container_serial)
bool FileExists(const char *filename)
StartingLocations startlocations
bool read(ConfigElem &elem)
int write_data(unsigned int &dirty_writes, unsigned int &clean_writes, long long &elapsed_ms)
const char * type() const
bool commit(const std::string &basename)
bool IsWaitingForAccept() const
void add_realm(const std::string &name, Realms::Realm *base)
void slurp(const char *filename, const char *tags, int sysfind_flags=0)
unsigned get_save_index(pol_serial_t serial)
unsigned remove_unsigned(const char *propname)
std::atomic< bool > exit_signalled
void register_deleted_serials()
static std::string build_datetime()
std::vector< unsigned int > ip_match_mask
void read_starting_locations()
void WriteGottenItem(Mobile::Character *chr, Items::Item *item, Clib::StreamWriter &sw)
void rndat(const std::string &basename)