mirror of
https://github.com/UOX3DevTeam/UOX3
synced 2026-08-13 12:27:04 -04:00
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>
414 lines
14 KiB
C++
414 lines
14 KiB
C++
#include "ObjectFactory.h"
|
|
#include "uox3.h"
|
|
#include <mutex>
|
|
|
|
#define HASHMAX 2477 // hashmax must be a prime for maximum performce.
|
|
|
|
inline SERIAL HashSerial( SERIAL toHash )
|
|
{
|
|
return toHash%HASHMAX;
|
|
}
|
|
|
|
/** This class is responsible for the creation and destruction of ingame
|
|
objects and should mean that we can take chars[] and items[] out of scope
|
|
*/
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
|
|
ObjectFactory& ObjectFactory::getSingleton( void )
|
|
{
|
|
std::mutex lock;
|
|
std::scoped_lock scope(lock) ;
|
|
static ObjectFactory instance ;
|
|
return instance ;
|
|
}
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
|
|
// Starting Characters with a serial of 1, rather than 0. The UO Client doesn't always respond well to a serial of 0 - 6/21/08
|
|
ObjectFactory::ObjectFactory() : nextPC( 1 ), nextNPC( 1 ), nextItem( BASEITEMSERIAL ), nextMulti( BASEITEMSERIAL )
|
|
{
|
|
}
|
|
ObjectFactory::~ObjectFactory()
|
|
{
|
|
chars.clear();
|
|
multis.clear();
|
|
items.clear();
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - size_t SizeOfObjects( ObjectType toCount )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Returns the size of memory allocated to given object type
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
size_t ObjectFactory::SizeOfObjects( ObjectType toCount )
|
|
{
|
|
size_t toRet = 0;
|
|
switch( toCount )
|
|
{
|
|
default:
|
|
break;
|
|
case OT_ITEM:
|
|
case OT_SPAWNER:
|
|
toRet = items.size() * sizeof( CItem );
|
|
break;
|
|
case OT_MULTI:
|
|
case OT_BOAT:
|
|
toRet = multis.size() * sizeof( CMultiObj );
|
|
break;
|
|
case OT_CHAR:
|
|
toRet = chars.size() * sizeof( CChar );
|
|
break;
|
|
}
|
|
return toRet;
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - size_t CountOfObjects( ObjectType toCount )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Returns the amount of objects that exist of a given object type
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
UI32 ObjectFactory::CountOfObjects( ObjectType toCount )
|
|
{
|
|
UI32 toRet = 0;
|
|
switch( toCount )
|
|
{
|
|
default:
|
|
break;
|
|
case OT_ITEM:
|
|
case OT_SPAWNER:
|
|
toRet = items.size();
|
|
break;
|
|
case OT_MULTI:
|
|
case OT_BOAT:
|
|
toRet = multis.size();
|
|
break;
|
|
case OT_CHAR:
|
|
toRet = chars.size();
|
|
break;
|
|
}
|
|
return toRet;
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - SERIAL ObjectFactory::NextFreeSerial( ObjectType toFind )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Finds the next free serial to use when creating an object of a given type
|
|
//|
|
|
//| Notes - Later on we will track in depth, but this allows us
|
|
//| to keep an eye on the serials in use, and make sure
|
|
//| that we assign them correctly
|
|
//|
|
|
//| For the moment, we'll keep items and chars under a simple system, though
|
|
//| long term we probably want to break it out
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
SERIAL ObjectFactory::NextFreeSerial( ObjectType toFind )
|
|
{
|
|
SERIAL toRet = INVALIDSERIAL;
|
|
switch( toFind )
|
|
{
|
|
default:
|
|
break;
|
|
case OT_ITEM:
|
|
case OT_MULTI:
|
|
case OT_BOAT:
|
|
case OT_SPAWNER:
|
|
toRet = nextItem++;
|
|
break;
|
|
case OT_CHAR:
|
|
toRet = nextNPC++;
|
|
break;
|
|
}
|
|
return toRet;
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - CBaseObject *CreateObject( ObjectType createType )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Create a new object with a serial number, and return it back to the creator
|
|
//| From DFNs, or a PC, by and large.
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
CBaseObject *ObjectFactory::CreateObject( ObjectType createType )
|
|
{
|
|
CBaseObject *created = NULL;
|
|
switch( createType )
|
|
{
|
|
default: break;
|
|
case OT_ITEM: created = new CItem(); break;
|
|
case OT_MULTI: created = new CMultiObj(); break;
|
|
case OT_BOAT: created = new CBoatObj(); break;
|
|
case OT_SPAWNER: created = new CSpawnItem(); break;
|
|
case OT_CHAR: created = new CChar(); break;
|
|
}
|
|
// assign serial here
|
|
if( created != NULL )
|
|
created->SetSerial( NextFreeSerial( createType ) ); // SetSerial() will register our object -
|
|
|
|
return created;
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - CBaseObject *CreateBlankObject( ObjectType createType )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Create a new blank object without a serial number. Serial will be provided
|
|
//| when loading object from worldfiles
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
CBaseObject *ObjectFactory::CreateBlankObject( ObjectType createType )
|
|
{
|
|
CBaseObject *created = NULL;
|
|
switch( createType )
|
|
{
|
|
default: break;
|
|
case OT_ITEM: created = new CItem(); break;
|
|
case OT_MULTI: created = new CMultiObj(); break;
|
|
case OT_BOAT: created = new CBoatObj(); break;
|
|
case OT_SPAWNER: created = new CSpawnItem(); break;
|
|
case OT_CHAR: created = new CChar(); break;
|
|
}
|
|
return created;
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - CBaseObject *FindObject( SERIAL toFind )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Calculate item, multi or character object from provided serial
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
CBaseObject *ObjectFactory::FindObject( SERIAL toFind )
|
|
{
|
|
OBJECTMAP_ITERATOR fIter, fUpper, fEnd;
|
|
CBaseObject * retVal = NULL;
|
|
SERIAL hashValue = HashSerial( toFind );
|
|
if( toFind != INVALIDSERIAL )
|
|
{
|
|
if( toFind >= BASEITEMSERIAL ) // either an item or multi
|
|
{
|
|
fIter = items.find( hashValue );
|
|
fUpper = items.upper_bound( hashValue );
|
|
fEnd = items.end();
|
|
while( fIter != fUpper && fIter != fEnd )
|
|
{
|
|
if( fIter->second->GetSerial() == toFind ) // is this our object?
|
|
{
|
|
retVal = fIter->second;
|
|
break;
|
|
}
|
|
else
|
|
++fIter;
|
|
}
|
|
if( retVal == NULL ) // Not an item! Let's check multis
|
|
{
|
|
fIter = multis.find( hashValue );
|
|
fUpper = multis.upper_bound( hashValue );
|
|
fEnd = multis.end();
|
|
while( fIter != fUpper && fIter != fEnd )
|
|
{
|
|
if( fIter->second->GetSerial() == toFind ) // is this our object?
|
|
{
|
|
retVal = fIter->second;
|
|
break;
|
|
}
|
|
else
|
|
++fIter;
|
|
}
|
|
}
|
|
}
|
|
else // a character of some sort
|
|
{
|
|
fIter = chars.find( hashValue );
|
|
fUpper = chars.upper_bound( hashValue );
|
|
fEnd = chars.end();
|
|
while( fIter != fUpper && fIter != fEnd )
|
|
{
|
|
if( fIter->second->GetSerial() == toFind ) // is this our object?
|
|
{
|
|
retVal = fIter->second;
|
|
break;
|
|
}
|
|
else
|
|
++fIter;
|
|
}
|
|
}
|
|
}
|
|
return retVal;
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - bool DestroyObject( CBaseObject *toDestroy )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - This function unregisters the object and destroys it from memory.
|
|
//| Essentially, it's either a server shutdown or the object has been deleted,
|
|
//| and is no longer part of the world.
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
bool ObjectFactory::DestroyObject( CBaseObject *toDestroy )
|
|
{
|
|
assert( toDestroy != NULL );
|
|
UnregisterObject( toDestroy );
|
|
delete toDestroy;
|
|
toDestroy = NULL;
|
|
return true;
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - bool RegisterObject( CBaseObject *toRegister )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - We want to register an object with the world, that previously
|
|
//| does not exist in our containers
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
bool ObjectFactory::RegisterObject( CBaseObject *toRegister )
|
|
{
|
|
assert( toRegister != NULL );
|
|
return RegisterObject( toRegister, toRegister->GetSerial() );
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - bool RegisterObject( CBaseObject *toRegister, SERIAL toAttach )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - This allows us to register our object with it's attached serial number
|
|
//|
|
|
//| Note that we do not check if this object existed previously, so if you
|
|
//| want to change an object's serial, you would have to Unregister it
|
|
//| and then Register it with the new serial. HOWEVER, that does not guarantee
|
|
//| valid serial numbers and that it's not a duplicate! By and large, we don't
|
|
//| want to call this from anywhere but a world loading routine.
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
bool ObjectFactory::RegisterObject( CBaseObject *toRegister, SERIAL toAttach )
|
|
{
|
|
assert( toRegister != NULL );
|
|
ObjectType reg = toRegister->GetObjType();
|
|
switch( reg )
|
|
{
|
|
default:
|
|
break;
|
|
case OT_MULTI:
|
|
case OT_BOAT:
|
|
if( toAttach >= nextItem )
|
|
nextItem = toAttach + 1;
|
|
multis.insert( std::make_pair( HashSerial( toAttach ), toRegister ) );
|
|
break;
|
|
case OT_ITEM:
|
|
case OT_SPAWNER:
|
|
if( toAttach >= nextItem )
|
|
nextItem = toAttach + 1;
|
|
items.insert( std::make_pair( HashSerial( toAttach ), toRegister ) );
|
|
break;
|
|
case OT_CHAR:
|
|
if( toAttach >= nextNPC )
|
|
nextNPC = toAttach + 1;
|
|
chars.insert( std::make_pair( HashSerial( toAttach ), toRegister ) );
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - bool UnregisterObject( CBaseObject *toRemove )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - We're done with an object, or want to detach it from the world
|
|
//| This function allows us to detach it, but not destroy it
|
|
//| in case we want to attach it elsewise
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
bool ObjectFactory::UnregisterObject( CBaseObject *toRemove )
|
|
{
|
|
assert( toRemove != NULL );
|
|
OBJECTMAP_ITERATOR rIter, rUpper, rEnd;
|
|
SERIAL hashValue = HashSerial( toRemove->GetSerial() );
|
|
switch( toRemove->GetObjType() )
|
|
{
|
|
default:
|
|
throw new std::runtime_error( "Damn, bad bad work here!" );
|
|
break;
|
|
case OT_MULTI:
|
|
case OT_BOAT:
|
|
rIter = multis.find( hashValue );
|
|
rUpper = multis.upper_bound( hashValue );
|
|
rEnd = multis.end();
|
|
break;
|
|
case OT_ITEM:
|
|
case OT_SPAWNER:
|
|
rIter = items.find( hashValue );
|
|
rUpper = items.upper_bound( hashValue );
|
|
rEnd = items.end();
|
|
break;
|
|
case OT_CHAR:
|
|
rIter = chars.find( hashValue );
|
|
rUpper = chars.upper_bound( hashValue );
|
|
rEnd = chars.end();
|
|
break;
|
|
}
|
|
while( rIter != rUpper && rIter != rEnd )
|
|
{
|
|
if( rIter->second == toRemove ) // is this our object?
|
|
{
|
|
// let's remove it from the hash table
|
|
switch( toRemove->GetObjType() )
|
|
{
|
|
case OT_MULTI:
|
|
case OT_BOAT: multis.erase( rIter ); break;
|
|
case OT_ITEM:
|
|
case OT_SPAWNER: items.erase( rIter ); break;
|
|
case OT_CHAR: chars.erase( rIter ); break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
++rIter;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - void GarbageCollect( void )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - This is to deal with dead or weird objects, but I don't think
|
|
//| that we would really need it. No object should be destroyed
|
|
//| except by this object factory anyway. We may want to do lazy
|
|
//| deletes and only do invalidations.
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
void ObjectFactory::GarbageCollect( void )
|
|
{
|
|
// We may not even need to do anything here, really
|
|
}
|
|
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Function - UI32 IterateOver( ObjectType toIterOver, UI32 &b, void *extraData, OBJFUNCTOR( text ) )
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
//| Purpose - Iterate over all objects of a specified object type
|
|
//o-----------------------------------------------------------------------------------------------o
|
|
UI32 ObjectFactory::IterateOver( ObjectType toIterOver, UI32 &b, void *extraData, OBJFUNCTOR( text ) )
|
|
{
|
|
OBJECTMAP_ITERATOR mBegin, mEnd;
|
|
switch( toIterOver )
|
|
{
|
|
case OT_MULTI:
|
|
case OT_BOAT:
|
|
{
|
|
mBegin = multis.begin();
|
|
mEnd = multis.end();
|
|
}
|
|
break;
|
|
case OT_ITEM:
|
|
case OT_SPAWNER:
|
|
{
|
|
mBegin = items.begin();
|
|
mEnd = items.end();
|
|
}
|
|
break;
|
|
case OT_CHAR:
|
|
{
|
|
mBegin = chars.begin();
|
|
mEnd = chars.end();
|
|
}
|
|
break;
|
|
default:
|
|
return 0xFFFFFFFF;
|
|
}
|
|
bool stillContinue = true;
|
|
while( mBegin != mEnd && stillContinue )
|
|
{
|
|
stillContinue = (*text)(mBegin->second, b, extraData );
|
|
++mBegin;
|
|
}
|
|
return 0;
|
|
}
|