#include "ObjectFactory.h" #include "uox3.h" #include #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 = static_cast(items.size()); break; case OT_MULTI: case OT_BOAT: toRet = static_cast(multis.size()); break; case OT_CHAR: toRet = static_cast(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 = nullptr; 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 != nullptr ) 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 = nullptr; 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 = nullptr; 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 == nullptr ) // 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 != nullptr ); UnregisterObject( toDestroy ); delete toDestroy; toDestroy = nullptr; 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 != nullptr ); 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 != nullptr ); 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 != nullptr ); 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; }