uox3/source/cServerDefinitions.cpp
Xoduz 166d3b0119 Numerous changes with focus on feature and stability parity across Windows, Linux and MacOS platforms
Added new Makefile that handles compiling UOX3 and Spidermonkey on both Linux and MacOS (punt)
Added VS solution (SpiderMonkey.sln) and VC++ project files for compiling SpiderMonkey on Windows (Xuri)
Updated SpiderMonkey from v1.6.0 to v1.7.0
Added optimization flag -O2 for release build in CMakeLists.txt in project root (Xuri)
Added StringUtility file with general common string manipulation functions (punt)
Updated RandomNum function to use a "seedless" random number generator from C++11 instead of the old C rand() function. (punt)
Overall code cleanup to remove/replace platform-dependent code (punt)
Replaced potentially unsafe usage of C string stuff like sprintf, vsprintf, vsnprintf, strncat, strcpy and strlen throughout the code with calls to a format() function containing only a single, safe use of vsnprintf, ensuring there's a single place of failure if there's anything to fix and and making it easy to potentially replace this with std::format from C++20 when the time comes. (punt)
Replaced usage of char in many places with std::string (punt)
Started process of replacing UString usage with functions provided through StringUtility instead (punt)
Replaced platform specific fileIO handling in Windows/Linux with cross-platform C++17 standard std::filesystem (punt)
Replaced platform specific time handling by using cross-platform chrono library (punt)
Removed UOX namespace to reduce complexity (punt)
Elimitated template code approach to singletons for more modern c++ constructs, removing dependices in the process (punt)
Removed ODBC support; it hasn't been touched since the initial implementation in 2008, and there is a lack of someone to maintain the code. (punt)
Removed some platform specific files like uoxlinux.h, which is no longer required (punt)
Removed legacy crash protection code, and removed support for cluox (punt)
Removed legacy "support" for Borland compiler (punt)
Removed legacy VC++ 6 Workspace/Project files, VS2005 Solution/Project files, as these are no longer supported (Xuri)
Removed legacy BUILD folder with outdated compilation instructions (Xuri)
Removed old Changelog file (merged into Changelog.txt) (Xuri)
Fixed a pointer bug for items in multis on world load (punt)
Fixed a dictionary related issue that caused segmentation faults on Linux/MacOS (punt)
Fixed an issue with callbacks to JS scripts that caused segmentation faults on Linux/MacOS (punt)
Paths in uox.ini should now load properly on all platforms regardless of whether those paths use slashes or backslashes, and whether or not they end in a slash/backslash. (punt)
Moved to c++17 style of threading, and got rid of threadsafeobject.cpp/h (punt)
Replaced parsing of UOX.INI tags with a system that's more easily maintainable (punt)
Added some new commands to js/commands/custom/misc-cmd.js (Xuri)
	cont		// Targeted item will be made a container, set to nondecay and movable 2
	endfight	// Targeted character (and character being fought) will stop fighting
	getmulti	// Get multiObject for targeted item
	finditem	// Find item at layer X
	movespeed	// Set movement speed of target player (0x0 Normal, 0x1 Mounted, 0x2 Slow (walk only), 0x3 Hybrid ("jog"?), 0x4 Frozen)
Added new HTML based documentation in docs folder, and removed many old legacy docs that are either incorporated into this document already or no longer relevant for the current UOX3 version (Xuri)

Co-Authored-By: Charles Kerr <punt1959@users.noreply.github.com>
2020-09-07 18:09:55 +08:00

591 lines
14 KiB
C++

#include "uox3.h"
#include "cServerDefinitions.h"
#include "ssection.h"
#include "scriptc.h"
#include <filesystem>
#include "StringUtility.hpp"
CServerDefinitions *FileLookup;
std::string CurrentWorkingDir( void )
{
return std::filesystem::current_path().string();
}
std::string BuildPath( const std::string &extra )
{
std::string temp = CurrentWorkingDir();
temp += "/" + extra;
return temp;
}
std::string ShortDirectory( std::string sPath )
{
std::string building = "";
size_t iFound = 0xFFFFFFFF;
for( iFound = sPath.length() - 1; iFound >= 0 && iFound < sPath.length(); --iFound )
{
if( sPath[iFound] == '/' )
break;
}
if( iFound >= sPath.length() )
return sPath;
else
{
for( size_t i = iFound + 1; i < sPath.length(); ++i )
building += sPath[i];
}
return building;
}
const std::string dirnames[NUM_DEFS] =
{
"items",
"npc",
"create",
"regions",
"misc",
"skills",
"location",
"menus",
"spells",
"newbie",
"titles",
"advance",
"house",
"colors",
"spawn",
"html",
"race",
"weather",
"harditems",
"command",
"msgboard",
"carve",
"creatures",
"maps"
};
std::multimap<UI32,ADDMENUITEM> g_mmapAddMenuMap;
CServerDefinitions::CServerDefinitions() : defaultPriority( 0 )
{
Console.PrintSectionBegin();
Console << "Loading server scripts..." << myendl;
Console << " o Clearing AddMenuMap entries(" << static_cast<UI64>(g_mmapAddMenuMap.size()) << ")" << myendl;
g_mmapAddMenuMap.clear();
ScriptListings.resize( NUM_DEFS );
ReloadScriptObjects();
Console.PrintSectionBegin();
}
CServerDefinitions::CServerDefinitions( const char *indexfilename ) : defaultPriority( 0 )
{
Console.PrintSectionBegin();
Console << "Loading server scripts...." << myendl;
ScriptListings.resize( NUM_DEFS );
ReloadScriptObjects();
Console.PrintSectionBegin();
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool Reload( void )
//| Date - 04/17/2002
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Reload the dfn files.
//| Changes - 04042004 - Added the code to clear out the
//| Auto-AddMenu items so there isn't any duplication in the
//| multimap
//o-----------------------------------------------------------------------------------------------o
bool CServerDefinitions::Reload( void )
{
// We need to clear out the AddMenuItem Map
g_mmapAddMenuMap.clear();
//
Cleanup();
ScriptListings.clear();
ScriptListings.resize( NUM_DEFS );
ReloadScriptObjects();
return true;
}
void CServerDefinitions::Cleanup( void )
{
std::vector< VECSCRIPTLIST >::iterator slIter;
for( slIter = ScriptListings.begin(); slIter != ScriptListings.end(); ++slIter )
{
VECSCRIPTLIST& toDel = (*slIter);
for( size_t j = 0; j < toDel.size(); ++j )
{
if( toDel[j] == NULL )
continue;
delete toDel[j];
toDel[j] = NULL;
}
}
}
CServerDefinitions::~CServerDefinitions()
{
Cleanup();
}
bool CServerDefinitions::Dispose( DEFINITIONCATEGORIES toDispose )
{
bool retVal = false;
if( toDispose != NUM_DEFS )
{
VECSCRIPTLIST& toDel = ScriptListings[toDispose];
for( VECSCRIPTLIST_CITERATOR dIter = toDel.begin(); dIter != toDel.end(); ++dIter )
{
Script *toDelete = (*dIter);
if( toDelete != NULL )
{
retVal = true;
delete toDelete;
}
}
toDel.clear();
}
return retVal;
}
ScriptSection *CServerDefinitions::FindEntry( std::string toFind, DEFINITIONCATEGORIES typeToFind )
{
ScriptSection *rvalue = NULL;
if( !toFind.empty() && typeToFind != NUM_DEFS )
{
UString tUFind = UString( toFind ).upper();
VECSCRIPTLIST& toDel = ScriptListings[typeToFind];
for( VECSCRIPTLIST_CITERATOR dIter = toDel.begin(); dIter != toDel.end(); ++dIter )
{
Script *toCheck = (*dIter);
if( toCheck != NULL )
{
rvalue = toCheck->FindEntry( tUFind );
if( rvalue != NULL )
break;
}
}
}
return rvalue;
}
ScriptSection *CServerDefinitions::FindEntrySubStr( std::string toFind, DEFINITIONCATEGORIES typeToFind )
{
ScriptSection *rvalue = NULL;
if( !toFind.empty() && typeToFind != NUM_DEFS )
{
VECSCRIPTLIST& toDel = ScriptListings[typeToFind];
for( VECSCRIPTLIST_CITERATOR dIter = toDel.begin(); dIter != toDel.end(); ++dIter )
{
Script *toCheck = (*dIter);
if( toCheck != NULL )
{
rvalue = toCheck->FindEntrySubStr( toFind );
if( rvalue != NULL )
break;
}
}
}
return rvalue;
}
const std::string defExt = ".dfn";
struct PrioScan
{
std::string filename;
SI16 priority;
PrioScan() : filename( "" ), priority( 0 )
{
}
PrioScan( const char *toUse, SI16 mPrio ) : filename( toUse ), priority( mPrio )
{
}
};
inline bool operator==( const PrioScan& x, const PrioScan& y )
{
return ( x.priority == y.priority );
}
inline bool operator<( const PrioScan& x, const PrioScan& y )
{
return ( x.priority < y.priority );
}
inline bool operator>( const PrioScan& x, const PrioScan& y )
{
return ( x.priority > y.priority );
}
void CServerDefinitions::LoadDFNCategory( DEFINITIONCATEGORIES toLoad )
{
CleanPriorityMap();
defaultPriority = 0;
UI08 wasPriod = 2;
BuildPriorityMap( toLoad, wasPriod );
cDirectoryListing fileList( toLoad, defExt );
fileList.Flatten( true );
STRINGLIST *shortListing = fileList.FlattenedShortList();
STRINGLIST *longListing = fileList.FlattenedList();
std::vector< PrioScan > mSort;
for( size_t i = 0; i < shortListing->size(); ++i )
{
mSort.push_back( PrioScan( (*longListing)[i].c_str(), GetPriority( (*shortListing)[i].c_str() ) ) );
}
if( !mSort.empty() )
{
std::sort( mSort.begin(), mSort.end() );
Console.print( format("Section %20s : %6i", dirnames[toLoad].c_str(), 0 ));
size_t iTotal = 0;
Console.TurnYellow();
std::vector< PrioScan >::const_iterator mIter;
for( mIter = mSort.begin(); mIter != mSort.end(); ++mIter )
{
Console.print( "\b\b\b\b\b\b" );
ScriptListings[toLoad].push_back( new Script( (*mIter).filename, toLoad, false ) );
iTotal += ScriptListings[toLoad].back()->NumEntries();
Console.print( format("%6i", iTotal) );
}
Console.print( format("\b\b\b\b\b\b%6i", CountOfEntries( toLoad )) );
Console.TurnNormal();
Console.print( " entries" );
switch( wasPriod )
{
case 0: Console.PrintSpecial( CGREEN, "prioritized" ); break; // prioritized
case 1:
Console.PrintSpecial( CGREEN, "done" );
break; // file exist, no section
default:
case 2:
Console.PrintSpecial( CBLUE, "done" );
break; // no file
};
}
}
void CServerDefinitions::ReloadScriptObjects( void )
{
Console << myendl;
for( SI32 sCtr = 0; sCtr < NUM_DEFS; ++sCtr )
{
LoadDFNCategory( (DEFINITIONCATEGORIES)sCtr );
}
CleanPriorityMap();
}
size_t CServerDefinitions::CountOfEntries( DEFINITIONCATEGORIES typeToFind )
{
size_t sumEntries = 0;
VECSCRIPTLIST *toScan = &(ScriptListings[typeToFind]);
if( toScan == NULL )
return 0;
for( VECSCRIPTLIST_CITERATOR cIter = toScan->begin(); cIter != toScan->end(); ++cIter )
sumEntries += (*cIter)->NumEntries();
return sumEntries;
}
size_t CServerDefinitions::CountOfFiles( DEFINITIONCATEGORIES typeToFind )
{
return ScriptListings[typeToFind].size();
}
Script * CServerDefinitions::FirstScript( DEFINITIONCATEGORIES typeToFind )
{
Script *retScript = NULL;
slIter = ScriptListings[typeToFind].begin();
if( !FinishedScripts( typeToFind ) )
retScript = (*slIter);
return retScript;
}
Script * CServerDefinitions::NextScript( DEFINITIONCATEGORIES typeToFind )
{
Script *retScript = NULL;
if( !FinishedScripts( typeToFind ) )
{
++slIter;
if( !FinishedScripts( typeToFind ) )
retScript = (*slIter);
}
return retScript;
}
bool CServerDefinitions::FinishedScripts( DEFINITIONCATEGORIES typeToFind )
{
return ( slIter == ScriptListings[typeToFind].end() );
}
void CServerDefinitions::CleanPriorityMap( void )
{
priorityMap.clear();
}
void CServerDefinitions::BuildPriorityMap( DEFINITIONCATEGORIES category, UI08& wasPrioritized )
{
cDirectoryListing priorityFile( category, "priority.nfo", false );
STRINGLIST *longList = priorityFile.List();
if( !longList->empty() )
{
std::string filename = (*longList)[0];
// Do we have any priority informat?
if( FileExists( filename ) ) // the file exists, so perhaps we do
{
Script *prio = new Script( filename, category, false ); // generate a script for it
if( prio != NULL ) // successfully made a script
{
UString tag;
UString data;
ScriptSection *prioInfo = prio->FindEntry( "PRIORITY" ); // find the priority entry
if( prioInfo != NULL )
{
for( tag = prioInfo->First(); !prioInfo->AtEnd(); tag = prioInfo->Next() ) // keep grabbing priority info
{
data = prioInfo->GrabData();
if( tag.upper() == "DEFAULTPRIORITY" )
defaultPriority = data.toShort();
else
{
std::string filenametemp = tag.lower();
priorityMap[filenametemp] = data.toShort();
}
}
wasPrioritized = 0;
}
else
wasPrioritized = 1;
delete prio; // remove script
prio = NULL;
}
else
wasPrioritized = 2;
return;
}
}
#if defined( UOX_DEBUG_MODE )
// Console.Warning( "Failed to open priority.nfo for reading in %s DFN", dirnames[category].c_str() );
#endif
wasPrioritized = 2;
}
void CServerDefinitions::DisplayPriorityMap( void )
{
Console << "Dumping map... " << myendl;
std::map< std::string, SI16 >::const_iterator p = priorityMap.begin();
while( p != priorityMap.end() )
{
Console << p->first << " : " << p->second << myendl;
++p;
}
Console << "Dumped" << myendl;
}
SI16 CServerDefinitions::GetPriority( const char *file )
{
SI16 retVal = defaultPriority;
UString lowername = UString( file ).lower();
std::map< std::string, SI16 >::const_iterator p = priorityMap.find( lowername );
if( p != priorityMap.end() )
retVal = p->second;
return retVal;
}
bool cDirectoryListing::PushDir( DEFINITIONCATEGORIES toMove )
{
std::string filePath = cwmWorldState->ServerData()->Directory( CSDDP_DEFS );
filePath += dirnames[toMove];
return PushDir( filePath );
}
bool cDirectoryListing::PushDir( std::string toMove )
{
std::string cwd = CurrentWorkingDir();
dirs.push( cwd );
auto path = std::filesystem::path( toMove );
if( std::filesystem::exists( path ) )
{
std::filesystem::current_path( path );
currentDir = toMove;
replaceSlash( toMove );
shortCurrentDir = ShortDirectory( toMove );
return true;
}
else
{
Console.error(format( "DFN directory %s does not exist", toMove.c_str()) );
Shutdown( FATAL_UOX3_DIR_NOT_FOUND );
}
return false;
}
void cDirectoryListing::PopDir( void )
{
if( dirs.empty() )
{
Console.error( "cServerDefinition::PopDir called, but dirs is empty" );
Shutdown( FATAL_UOX3_DIR_NOT_FOUND );
}
else
{
auto path = std::filesystem::path( dirs.top() );
if( std::filesystem::exists( path ) )
{
std::filesystem::current_path( path );
}
dirs.pop();
}
}
cDirectoryListing::cDirectoryListing( bool recurse ) : extension( ".dfn" ), doRecursion( recurse )
{
}
cDirectoryListing::cDirectoryListing( const std::string &dir, const std::string &extent, bool recurse ) : doRecursion( recurse )
{
Extension( extent );
Retrieve( dir );
}
cDirectoryListing::cDirectoryListing( DEFINITIONCATEGORIES dir, const std::string &extent, bool recurse ) : doRecursion( recurse )
{
Extension( extent );
Retrieve( dir );
}
cDirectoryListing::~cDirectoryListing()
{
while( !dirs.empty() )
{
auto path = std::filesystem::path( dirs.top() );
if( std::filesystem::exists( path ))
{
std::filesystem::current_path( path );
}
dirs.pop();
}
}
void cDirectoryListing::Retrieve( const std::string &dir )
{
bool dirSet = PushDir( dir );
InternalRetrieve();
if( dirSet )
PopDir();
}
void cDirectoryListing::Retrieve( DEFINITIONCATEGORIES dir )
{
bool dirSet = PushDir( dir );
InternalRetrieve();
if( dirSet )
PopDir();
}
STRINGLIST *cDirectoryListing::List( void )
{
return &filenameList;
}
STRINGLIST *cDirectoryListing::ShortList( void )
{
return &shortList;
}
STRINGLIST *cDirectoryListing::FlattenedList( void )
{
return &flattenedFull;
}
STRINGLIST *cDirectoryListing::FlattenedShortList( void )
{
return &flattenedShort;
}
void cDirectoryListing::InternalRetrieve( void )
{
std::string filePath;
auto path = std::filesystem::current_path();
for( const auto& entry : std::filesystem::directory_iterator( path ))
{
auto name = entry.path().filename().string();
filePath = entry.path().string();
auto ext = entry.path().extension();
if( std::filesystem::is_regular_file( entry ))
{
if( !extension.empty() )
{
if( entry.path().extension().string() != extension )
{
name = "";
}
}
if( !name.empty() )
{
shortList.push_back( name );
filenameList.push_back( filePath );
}
}
else if( std::filesystem::is_directory(entry) && doRecursion )
{
subdirectories.push_back( cDirectoryListing( name, extension, doRecursion ));
}
}
}
void cDirectoryListing::Extension( const std::string &extent )
{
extension = extent;
}
void cDirectoryListing::Flatten( bool isParent )
{
ClearFlatten();
std::string temp;
STRINGLIST_ITERATOR sIter;
for( sIter = filenameList.begin(); sIter != filenameList.end(); ++sIter )
{
flattenedFull.push_back( (*sIter) );
if( isParent )
temp = "";
else
{
temp = shortCurrentDir;
temp += "/";
}
temp += (*sIter);
flattenedShort.push_back( temp );
}
DIRLIST_ITERATOR dIter;
for( dIter = subdirectories.begin(); dIter != subdirectories.end(); ++dIter )
{
(*dIter).Flatten( false );
STRINGLIST *shortFlat = (*dIter).FlattenedShortList();
STRINGLIST *longFlat = (*dIter).FlattenedList();
for( size_t k = 0; k < longFlat->size(); ++k )
{
flattenedFull.push_back( (*longFlat)[k] );
if( isParent )
temp = "";
else
{
temp = shortCurrentDir;
temp += "/";
}
temp += (*shortFlat)[k];
flattenedShort.push_back( temp );
}
(*dIter).ClearFlatten();
}
}
void cDirectoryListing::ClearFlatten( void )
{
flattenedFull.clear();
flattenedShort.clear();
}