diff options
Diffstat (limited to 'Runtime/BaseClasses')
39 files changed, 8241 insertions, 0 deletions
diff --git a/Runtime/BaseClasses/BaseObject.cpp b/Runtime/BaseClasses/BaseObject.cpp new file mode 100644 index 0000000..1f4c88a --- /dev/null +++ b/Runtime/BaseClasses/BaseObject.cpp @@ -0,0 +1,1393 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#include "BaseObject.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Utilities/dynamic_bitset.h" +#include "Runtime/Utilities/CStringHash.h" +#include "Runtime/Utilities/InitializeAndCleanup.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Profiler/MemoryProfilerStats.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "EventManager.h" +#include "EventIDs.h" +#if ENABLE_MONO +#include "Runtime/Mono/MonoIncludes.h" +#endif + +#include "Runtime/Scripting/ScriptingUtility.h" + +#if defined(__MWERKS__) +#include <hash_map> +#endif + +#include "Configuration/UnityConfigure.h" +#if THREADED_LOADING +#include "Runtime/Threads/ThreadSpecificValue.h" +#include "Runtime/Threads/ProfilerMutex.h" +#endif + +#if !UNITY_EXTERNAL_TOOL +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Profiler/MemoryProfiler.h" +#endif +#include "Runtime/Allocator/MemoryManager.h" + +using namespace std; + +#define SHOW_REGISTERED_CLASS_INFO 0 + + +#if THREADED_LOADING +#define CHECK_IN_MAIN_THREAD DebugAssertIf(!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID())); +#define DEBUG_CHECK_IN_MAIN_THREAD AssertIf(!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID())); +#else +#define CHECK_IN_MAIN_THREAD +#define DEBUG_CHECK_IN_MAIN_THREAD +#endif + + +static bool IsDerivedFromRTTI (const Object::RTTI* klass, const Object::RTTI* derivedFrom) +{ + const Object::RTTI* i = klass; + while (i) + { + if (derivedFrom == i) + return true; + + i = i->base; + } + return false; +} + +struct RegisterClassCallbackStruct +{ + RegisterClassCallback* registerClass; + RegisterClassCallback* initClassEarly; + RegisterClassCallback* initClass; + RegisterClassCallback* postInitClass; + RegisterClassCallback* cleanupClass; + + RegisterClassCallbackStruct() + { + registerClass = initClassEarly = initClass = postInitClass = cleanupClass = NULL; + } +}; + +typedef UNITY_VECTOR(kMemPermanent, RegisterClassCallbackStruct) RegisterClassCallbacks; + +Object::IDToPointerMap* Object::ms_IDToPointer = NULL; +UInt32* Object::ms_IsDerivedFromBitMap = NULL; +unsigned Object::ms_MaxClassID = 0; +#if USE_NEW_IS_DERIVED_FROM +UInt32 gClassIDMask[32]; +UInt32* Object::ms_ClassIDMask = 0; +UInt32* Object::ms_ClassIsDerivedFrom = 0; +#endif + +static RegisterClassCallbacks* gRegisterClassCallbacks = NULL; + +#if defined(__MWERKS__) +#error("Metrowerks should not be used") +#endif + +#if DEBUGMODE +RegisteredClassSet* gVerifyRegisteredClasses = NULL; +#endif + +typedef map<char*, SInt32, smaller_cstring> StringToClassIDMap; +typedef pair<const SInt32, Object::RTTI> SInt32RTTIPair; +typedef map<SInt32, Object::RTTI, less<SInt32>, STL_ALLOCATOR(kMemPermanent, SInt32RTTIPair) > RTTIMap; + +static StringToClassIDMap* gStringToClassID = NULL; +static RTTIMap* gRTTI = NULL; +static dynamic_bitset* gRegisteredClassIDs = NULL; +static dynamic_bitset* gIsDerivedFromBitMap = NULL; +static Object::ObjectDestroyCallbackFunction* gDestroyedCallbackFunc = NULL; + +static int* gBaseObjectManagerContainer = NULL; + +namespace BaseObjectManager +{ + void StaticInitialize() + { + gBaseObjectManagerContainer = UNITY_NEW_AS_ROOT(int, kMemBaseObject, "Managers", "BaseObjectManager"); + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + gStringToClassID = UNITY_NEW(StringToClassIDMap, kMemBaseObject); + gRTTI = UNITY_NEW(RTTIMap, kMemBaseObject); + gRegisteredClassIDs = UNITY_NEW(dynamic_bitset, kMemBaseObject); + gIsDerivedFromBitMap = UNITY_NEW(dynamic_bitset, kMemBaseObject); + Object::StaticInitialize(); + } + void StaticDestroy() + { + Object::StaticDestroy(); + UNITY_DELETE(gStringToClassID, kMemBaseObject); + UNITY_DELETE(gRTTI, kMemBaseObject); + UNITY_DELETE(gRegisteredClassIDs, kMemBaseObject); + UNITY_DELETE(gIsDerivedFromBitMap, kMemBaseObject); +#if DEBUGMODE + UNITY_DELETE(gVerifyRegisteredClasses, kMemBaseObject); // allocated on first access +#endif + UNITY_DELETE(gBaseObjectManagerContainer, kMemBaseObject); + } +} + +static RegisterRuntimeInitializeAndCleanup s_BaseObjectManagerCallbacks(BaseObjectManager::StaticInitialize, BaseObjectManager::StaticDestroy); + +#if UNITY_EDITOR +static Object::ObjectDirtyCallbackFunction* gSetDirtyCallbackFunc = NULL; +#endif + +PROFILER_INFORMATION (gObjectCreationMutexLockInfo, "Object.CreateObject mutex lock", kProfilerLoading) + +#if THREADED_LOADING +Mutex gCreateObjectMutex; +#if DEBUGMODE +static UNITY_TLS_VALUE(int) gCheckObjectCreationMutex; +#endif +#endif + +void LockObjectCreation () +{ + #if THREADED_LOADING + LOCK_MUTEX (gCreateObjectMutex, gObjectCreationMutexLockInfo); + #if DEBUGMODE + ++gCheckObjectCreationMutex; + #endif + #endif +} + +void UnlockObjectCreation () +{ + #if THREADED_LOADING + gCreateObjectMutex.Unlock(); + #if DEBUGMODE + --gCheckObjectCreationMutex; + #endif + #endif +} + + +static SInt32 gLowestInstanceID = -10; +static bool gDisableImmediateDestruction = false; + +#if UNITY_EDITOR +InstanceIDResolveCallback* gInstanceIDResolveCallback = NULL; +const void* gInstanceIDResolveContext = NULL; + +void SetInstanceIDResolveCallback (InstanceIDResolveCallback callback, const void* context) +{ + gInstanceIDResolveCallback = callback; + gInstanceIDResolveContext = context; +} +#endif + +void InstanceIDToLocalSerializedObjectIdentifier (SInt32 id, LocalSerializedObjectIdentifier& localIdentifier) +{ + #if UNITY_EDITOR + // Early out if referenced object is null + if (id == 0) + { + localIdentifier.localSerializedFileIndex = 0; + localIdentifier.localIdentifierInFile = 0; + return; + } + + if (gInstanceIDResolveCallback == NULL) + { + GetPersistentManager ().InstanceIDToLocalSerializedObjectIdentifierInternal (id, localIdentifier); + return; + } + else + { + gInstanceIDResolveCallback (id, localIdentifier, const_cast<void*>(gInstanceIDResolveContext)); + } + #else + GetPersistentManager ().InstanceIDToLocalSerializedObjectIdentifierInternal (id, localIdentifier); + #endif +} + +void LocalSerializedObjectIdentifierToInstanceID (const LocalSerializedObjectIdentifier& localIdentifier, SInt32& instanceID) +{ + GetPersistentManager ().LocalSerializedObjectIdentifierToInstanceIDInternal (localIdentifier, instanceID); +} + +Object* ReadObjectFromPersistentManager (int id) +{ + if (id == 0) + return NULL; + else + { + // In the Player it is not possible to call MakeObjectPersistent, + // thus instance id's that are positive are the only ones that can be loaded from disk + #if !UNITY_EDITOR + if (id < 0) + { + #if DEBUGMODE + //AssertIf(GetPersistentManager ().ReadObject (id)); + #endif + return NULL; + } + #endif + + Object* o = GetPersistentManager ().ReadObject (id); + return o; + } +} + +void DestroyWithoutLoadingButDontDestroyFromFile (int instanceID) +{ + GetPersistentManager ().MakeObjectUnpersistent (instanceID, kDontDestroyFromFile); + UnloadObject(Object::IDToPointer(instanceID)); +} + +void DestroySingleObject (Object* o) +{ + if (o == NULL) + return; + + if (o->IsPersistent()) + GetPersistentManager ().MakeObjectUnpersistent (o->GetInstanceID (), kDestroyFromFile); + + // Lock changes to IDToPointer so that we can safely lookup pointers using IDToPointerThreadSafe + LockObjectCreation(); + + delete_object_internal (o); + + UnlockObjectCreation(); +} + + +Object::Object (MemLabelId label, ObjectCreationMode mode) +{ + Assert(label.label < (1 << kMemLabelBits)); + m_MemLabel = label.label; + m_InstanceID = 0; + m_EventIndex = NULL; + + #if ENABLE_SCRIPTING + m_MonoReference = 0; + m_ScriptingObjectPointer = SCRIPTING_NULL; + #endif + + #if !UNITY_RELEASE + m_AwakeCalled = 0; + m_ResetCalled = 0; + m_AwakeThreadedCalled = 0; + m_AwakeDidLoadThreadedCalled = 0; + #endif + + + #if UNITY_EDITOR + m_DirtyIndex = 0; + m_FileIDHint = 0; + #endif + m_HideFlags = 0; + m_TemporaryFlags = 0; + m_IsPersistent = false; + + DebugAssert(GetMemoryManager().GetAllocator(GetMemoryLabel())->Contains(this)); + m_IsRootOwner = GetMemoryManager().GetAllocator(GetMemoryLabel())->GetProfilerHeader(this) != NULL; + #if UNITY_WINRT + m_TemporaryUnusedAssetsFlags = 0; + #endif +} + +void Object::CalculateCachedClassID (Object* obj) +{ + Assert(obj->GetClassIDVirtualInternal() < (1 << kCachedClassIDBits)); + obj->m_CachedClassID = obj->GetClassIDVirtualInternal(); +} + +void Object::InsertObjectInMap( Object* obj ) +{ + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + Assert (ms_IDToPointer->find (obj->GetInstanceID ()) == ms_IDToPointer->end ()); + ms_IDToPointer->insert (make_pair (obj->GetInstanceID (), obj)); + + PROFILER_REGISTER_OBJECT(obj); +} + +void Object::RegisterInstanceID (Object* obj) +{ + CHECK_IN_MAIN_THREAD + + LockObjectCreation(); + Assert (obj != NULL); + AssertIf(obj->m_InstanceID == 0); + InsertObjectInMap (obj); + + UnlockObjectCreation(); +} + +void Object::RegisterInstanceIDNoLock (Object* obj) +{ + CHECK_IN_MAIN_THREAD + Assert (obj != NULL); + AssertIf (obj->m_InstanceID == 0); + CalculateCachedClassID (obj); + InsertObjectInMap (obj); +} + + +Object* Object::AllocateAndAssignInstanceID (Object* obj) +{ + CHECK_IN_MAIN_THREAD + AssertIf(obj->m_InstanceID != 0); + + LockObjectCreation(); + + // Create a new unique instanceID for this Object. + // The created id will be negative beginning with -1 + // Ids loaded from a file will be positive beginning with 1 + gLowestInstanceID-=2; + obj->SetInstanceID (gLowestInstanceID); + AssertIf (obj->GetInstanceID () & 1); + + CalculateCachedClassID (obj); + InsertObjectInMap (obj); + + UnlockObjectCreation(); + + obj->SetDirty(); + + return obj; +} + +Object* Object::AllocateAndAssignInstanceIDNoLock (Object* obj) +{ + CHECK_IN_MAIN_THREAD + AssertIf(obj->m_InstanceID != 0); + + // Create a new unique instanceID for this Object. + // The created id will be negative beginning with -1 + // Ids loaded from a file will be positive beginning with 1 + gLowestInstanceID-=2; + obj->SetInstanceID (gLowestInstanceID); + AssertIf (obj->GetInstanceID () & 1); + + CalculateCachedClassID (obj); + + InsertObjectInMap (obj); + + obj->SetDirty(); + + return obj; +} + +enum { kMonoObjectCachedPtrOffset = 12 }; + + +// This must be executed on the main thread +void delete_object_internal_step1 (Object* object) +{ + PROFILER_UNREGISTER_OBJECT(object); + +#if !UNITY_RELEASE + object->CheckCorrectAwakeUsage(); +#endif + +#if THREADED_LOADING && DEBUGMODE + Assert(gCheckObjectCreationMutex >= 1); +#endif + + // Send destroy message & clear event index + if (object->m_EventIndex != NULL) + { + GetEventManager().InvokeEvent(object->m_EventIndex, object, kWillDestroyEvent); + GetEventManager().RemoveEvent(object->m_EventIndex); + object->m_EventIndex = NULL; + } + + // Remove this objects instanceID from the table. + AssertIf (Object::ms_IDToPointer->find (object->GetInstanceID ()) == Object::ms_IDToPointer->end ()); + Object::ms_IDToPointer->erase (object->GetInstanceID ()); + + if (gDestroyedCallbackFunc) + gDestroyedCallbackFunc (object->GetInstanceID ()); + + object->m_InstanceID = 0; +} + +Object::~Object () +{ + // Ensure PreCleanupObject was called + #if DEBUGMODE + Assert(m_InstanceID == 0); + #endif + +#if ENABLE_MONO //if unity3.5 succesfully shipped with this assert it may be removed. here to verify an assumption made in a refactor. + Assert((m_MonoReference==0) == (m_ScriptingObjectPointer==NULL)); +#endif + +#if ENABLE_SCRIPTING + if (m_ScriptingObjectPointer) + SetCachedScriptingObject(SCRIPTING_NULL); +#endif +} + +bool Object::MainThreadCleanup () +{ + AssertString("MainThreadCleanup is not implemented for this class. See DoesClassRequireMainThreadDeallocation"); + return false; +} + +void Object::SetIsPersistent( bool p ) +{ + PROFILER_CHANGE_PERSISTANCY(GetInstanceID(), m_IsPersistent, p); + m_IsPersistent = p; +} + +#if ENABLE_SCRIPTING + +#if UNITY_PS3 || UNITY_XENON + extern "C" char* GC_clear_stack(char*); +#endif + +void Object::SetupWeakHandle () +{ + if (m_MonoReference != 0) + { + register ScriptingObjectPtr object = scripting_gchandle_get_target(m_MonoReference); + UInt32 weakref = scripting_gchandle_weak_new (object); + SetCachedScriptingObject(SCRIPTING_NULL); + +#if UNITY_PS3 || UNITY_XENON + // we need to make sure the object doesn't continue living on the stack. // fixes http://fogbugz.unity3d.com/default.asp?444901#1065992062 + GC_clear_stack((char*)object); +#endif + object = SCRIPTING_NULL; + m_MonoReference = weakref; + } +} + +bool Object::RevertWeakHandle () +{ + if (m_MonoReference != 0) + { + ScriptingObjectPtr target = scripting_gchandle_get_target (m_MonoReference); + scripting_gchandle_free(m_MonoReference); + m_MonoReference = 0; + if (target) + { + SetCachedScriptingObject(target); +#if UNITY_WINRT + // Restore cached ptr for managed object, not sure why we don't do this for Mono as well, because we reset cachedPtr in SetupWeakHandle + // But on Mono it seems cachedPtr persists ?! + // Maybe it's related to cachedPtr optimization + register ScriptingObjectOfType<Object> instance(m_ScriptingObjectPointer); + instance.SetCachedPtr(this); +#endif + } + return target != SCRIPTING_NULL; + } + else + return false; +} + + +void Object::SetCachedScriptingObject (ScriptingObjectPtr object) +{ + if (object) + { + AssertIf(m_MonoReference != 0); + m_MonoReference = scripting_gchandle_new (object); + m_ScriptingObjectPointer = object; + return; + } + + if (m_ScriptingObjectPointer == SCRIPTING_NULL) + { + AssertString("Dont do this"); + return; + } + + register ScriptingObjectOfType<Object> instance(m_ScriptingObjectPointer); + instance.SetCachedPtr(0); + + scripting_gchandle_free (m_MonoReference); + m_MonoReference = 0; + + m_ScriptingObjectPointer = SCRIPTING_NULL; +#if UNITY_WINRT + instance = ScriptingObjectPtr(SCRIPTING_NULL); +#else + instance = SCRIPTING_NULL; +#endif +} +#endif + +void Object::RegisterDestroyedCallback (ObjectDestroyCallbackFunction* callback) +{ + gDestroyedCallbackFunc = callback; +} + +// Register base class +void Object::RegisterClass () +{ + RegisterClass (ClassID (Object), -1, "Object", sizeof (Object), NULL, true); +} + +void Object::RegisterClass (int inClassID, int inBaseClass, const string& inName, int byteSize, FactoryFunction* inFunc, bool isAbstract) +{ + if (ClassIDToRTTI (inClassID)) + return; + + // Store ClassID -> RTTI + AssertIf (gRTTI->find (inClassID) != gRTTI->end ()); + RTTIMap::iterator baseClass = gRTTI->find (inBaseClass); + AssertIf (baseClass == gRTTI->end () && inBaseClass != -1); + Object::RTTI& classInfo = (*gRTTI)[inClassID]; + classInfo.base = baseClass == gRTTI->end () ? NULL : &baseClass->second; + classInfo.factory = inFunc; + classInfo.className = inName; + classInfo.classID = inClassID; + classInfo.isAbstract = isAbstract; + classInfo.size = byteSize; + + // Store String -> ClassID + AssertIf (gStringToClassID->find (const_cast<char*> (inName.c_str ())) != gStringToClassID->end ()); + (*gStringToClassID)[const_cast<char*> (classInfo.className.c_str ())] = inClassID; +} + +Object* Object::Produce (int classID, int instanceID, MemLabelId memLabel, ObjectCreationMode mode) +{ + // Object is already loaded assert + AssertIf (mode == kCreateObjectDefault && IDToPointer (instanceID) != NULL); + AssertIf (instanceID == 0 && mode == kCreateObjectFromNonMainThread); + #if THREADED_LOADING + AssertIf (!Thread::EqualsCurrentThreadID(GetPersistentManager().GetMainThreadID()) && mode != kCreateObjectFromNonMainThread); + #endif + AssertIf (instanceID & 1); + + // Find the appropriate Factory. + RTTIMap::iterator i; + i = gRTTI->find (classID); + if (i == gRTTI->end () || i->second.factory == NULL) + { + return NULL; + } + + Object* o; + if (instanceID != 0) + { + o = i->second.factory (memLabel, mode); + if (o == NULL) + return NULL; + o->SetInstanceID(instanceID); + CalculateCachedClassID (o); + + // Register instanceID and set dirty + if (mode == kCreateObjectDefault) + { + RegisterInstanceID(o); + o->SetDirty(); + } + else if (mode == kCreateObjectDefaultNoLock) + { + RegisterInstanceIDNoLock(o); + o->SetDirty(); + } + + return o; + } + else + { + AssertIf(mode != kCreateObjectDefaultNoLock && mode != kCreateObjectDefault); + o = i->second.factory (memLabel, mode); + + if (mode == kCreateObjectDefaultNoLock) + AllocateAndAssignInstanceIDNoLock(o); + else + AllocateAndAssignInstanceID(o); + return o; + } +} + +void Object::CheckInstanceIDsLoaded (SInt32* instanceIDs, int size) +{ + for (int i=0;i<size;i++) + { + if (ms_IDToPointer->count (instanceIDs[i])) + instanceIDs[i] = 0; + } +} + +#if !UNITY_RELEASE +Object* Object::IDToPointer (int inInstanceID) +{ + DEBUG_CHECK_IN_MAIN_THREAD + + return Object::IDToPointerNoThreadCheck (inInstanceID); +} +#endif + +Object* Object::IDToPointerThreadSafe (int inInstanceID) +{ + LockObjectCreation(); + Object* obj = Object::IDToPointerNoThreadCheck (inInstanceID); + UnlockObjectCreation(); + return obj; +} + +Object* Object::IDToPointerNoThreadCheck (int inInstanceID) +{ + if( !ms_IDToPointer) return NULL; + + IDToPointerMap::const_iterator i = ms_IDToPointer->find (inInstanceID); + if (i != ms_IDToPointer->end ()) + { + return i->second; + } + else + { + return NULL; + } +} + +#if THREADED_LOADING +Object* InstanceIDToObjectThreadSafe (int instanceID) +{ + if (Thread::EqualsCurrentThreadID (GetPersistentManager().GetMainThreadID())) + return PPtr<Object> (instanceID); + else + { + Object* obj = Object::IDToPointerThreadSafe(instanceID); + if (obj == NULL) + return GetPersistentManager().ReadObjectThreaded(instanceID); + else + return obj; + } +} +#endif + +void Object::FindAllDerivedClasses (int classID, vector<SInt32>* derivedClasses, bool onlyNonAbstract) +{ + AssertIf (derivedClasses == NULL); + RTTIMap::iterator i; + for (i=gRTTI->begin ();i!=gRTTI->end ();i++) + { + if (IsDerivedFromClassID (i->first, classID) && (!onlyNonAbstract || !i->second.isAbstract)) + derivedClasses->push_back (i->first); + } +} + +struct GetConstFirst +{ + template<typename T1, typename T2> + const T1& operator()(const std::pair<T1, T2>& p) const { + return p.first; + } +}; + +struct GetConstSecond +{ + template<typename T1, typename T2> + const T2& operator()(const std::pair<T1, T2>& p) const { + return p.second; + } +}; + +struct IsDerivedFromClass +{ + IsDerivedFromClass(int classID): m_ClassID(classID) {} + bool operator()(const Object::IDToPointerMap::value_type& el) const + { + return el.second->IsDerivedFrom (m_ClassID); + } +private: + int m_ClassID; +}; + +inline int DerivedObjectCount(const Object::IDToPointerMap& objmap, int classID) +{ + return std::count_if(objmap.begin(), objmap.end(), IsDerivedFromClass(classID)); +} + +template<typename ObjectGetter, typename Container, typename Predicate> +inline int FindAllDerivedObjectsImpl (const Object::IDToPointerMap& objmap, int classID, + ObjectGetter getter, Container* derivedObjects, bool sorted, Predicate pred) +{ + if (NULL == derivedObjects) + return DerivedObjectCount(objmap, classID); + + int count = 0; + for (typename Object::IDToPointerMap::const_iterator i = objmap.begin(); + i != objmap.end(); ++i) + { + if (i->second->IsDerivedFrom (classID)) + { + derivedObjects->push_back (getter(*i)); + count++; + } + } + + if (sorted && count) + std::sort(derivedObjects->begin(), derivedObjects->end(), pred); + + return count; +} + +int Object::FindAllDerivedObjects (int classID, vector<SInt32>* derivedObjects, bool sorted) +{ + return FindAllDerivedObjectsImpl (*ms_IDToPointer, classID, + GetConstFirst(), derivedObjects, sorted, std::less<SInt32>()); +} + +struct CompareInstanceID +{ + bool operator () (const Object* lhs, const Object* rhs) const + { + return lhs->GetInstanceID() < rhs->GetInstanceID(); + } +}; + +int Object::FindObjectsOfType (int classID, vector<Object*>* derivedObjects, bool sorted) +{ + return FindAllDerivedObjectsImpl (*ms_IDToPointer, classID, + GetConstSecond(), derivedObjects, sorted, CompareInstanceID()); +} + +int Object::FindObjectsOfType (int classID, dynamic_array<Object*>* derivedObjects, bool sorted) +{ + int count = 0; + IDToPointerMap::iterator i; + for (i=ms_IDToPointer->begin ();i!=ms_IDToPointer->end ();i++) + { + if (i->second->IsDerivedFrom (classID)) + { + if (derivedObjects != NULL) + derivedObjects->push_back (i->second); + count++; + } + } + + + if (sorted && derivedObjects != NULL) + { + CompareInstanceID compare; + sort(derivedObjects->begin(), derivedObjects->end(), compare); + } + + + return count; +} + + +const std::string& Object::ClassIDToString (int ID) +{ + static std::string emptyString; + RTTIMap::iterator i = gRTTI->find (ID); + if (i == gRTTI->end ()) + return emptyString; + else + return i->second.className; +} + +int Object::StringToClassID (const string& classString) +{ + StringToClassIDMap::iterator i; + i = gStringToClassID->find (const_cast<char*> (classString.c_str ())); + if (i == gStringToClassID->end ()) + return -1; + else + return i->second; +} + +int Object::StringToClassIDCaseInsensitive (const string& classString) +{ + StringToClassIDMap::iterator i; + string lowerClass = ToLower(classString); + for (StringToClassIDMap::iterator i = gStringToClassID->begin(); i!=gStringToClassID->end(); i++) + { + if (ToLower(string(i->first)) == lowerClass) + return i->second; + } + return -1; +} + +int Object::StringToClassID (const char* classString) +{ + StringToClassIDMap::iterator i; + i = gStringToClassID->find (const_cast<char*> (classString)); + if (i == gStringToClassID->end ()) + return -1; + else + return i->second; +} + +const std::string& Object::GetClassName () const +{ + return Object::ClassIDToString (GetClassID ()); +} + +int Object::GetSuperClassID (int classID) +{ + RTTIMap::iterator i = gRTTI->find (classID); + AssertIf (i == gRTTI->end ()); + if (i->second.base) + return i->second.base->classID; + else + return ClassID (Object); +} + +Object::RTTI* Object::ClassIDToRTTI (int classID) +{ + RTTIMap::iterator i = gRTTI->find (classID); + if (i == gRTTI->end ()) + return NULL; + else + return &i->second; +} + +struct BuildClassInfo +{ + Object::RTTI* klass; + Object::RTTI* base; + UInt32 id; + UInt32 subclasses; + UInt32 level; + UInt32 newId; + UInt32 subClassIdAssign; + void Clear(){ + base = 0; + klass = 0; + id = (UInt32)-1; + subclasses = 0; + level = 0xffffffff; + newId = -1; + subClassIdAssign = 0; + } + void Init(Object::RTTI* klass, Object::RTTI* base) + { + this->klass = klass; + this->base = base; + } + bool operator <( const BuildClassInfo& o) const + { + return level < o.level; + } +}; +int bitsRequired(int subclasses) +{ + int r = 0; + while(subclasses) + { + r++; + subclasses >>= 1; + } + return r; +} + +void Object::StaticInitialize() +{ + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + Object::ms_IDToPointer = UNITY_NEW(Object::IDToPointerMap (1024 * 128), kMemBaseObject); +} + +void Object::StaticDestroy() +{ + UNITY_DELETE(Object::ms_IDToPointer,kMemBaseObject); +} + +void Object::InitializeAllClasses () +{ + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + + if (gRegisterClassCallbacks == NULL) + return; + +#if SHOW_REGISTERED_CLASS_INFO + int registeredClasses = 0; +#endif + + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + // The callback is the RegisterClass function defined in ObjectDefines.h + // It calls the static Object::RegisterClass function which sets up the rtti system + for (int i=0;i<callbacks.size ();i++) + { + if (callbacks[i].registerClass) + { + callbacks[i].registerClass (); +#if SHOW_REGISTERED_CLASS_INFO + ++registeredClasses; +#endif + } + } + +#if SHOW_REGISTERED_CLASS_INFO + printf_console ("Object::InitializeAllClasses: %d total, %d registered\n", callbacks.size(), registeredClasses); +#endif + + AssertIf (gRTTI->empty ()); + + // Setup ms_IsDerivedFrom lookup bitmap + if (UNITY_EDITOR) + { + ms_MaxClassID = (--gRTTI->end ())->first + 1; + Assert(kLargestEditorClassID == ms_MaxClassID); + } + else + { + ms_MaxClassID = kLargestRuntimeClassID; + } + + gIsDerivedFromBitMap->resize (ms_MaxClassID * ms_MaxClassID, false); + ms_IsDerivedFromBitMap = (UInt32*) gIsDerivedFromBitMap->m_bits; + gRegisteredClassIDs->resize (ms_MaxClassID, false); + for (int i=0;i<ms_MaxClassID;i++) + { + RTTIMap::iterator iRTTI = gRTTI->find (i); + (*gRegisteredClassIDs)[i] = iRTTI != gRTTI->end (); + if ((*gRegisteredClassIDs)[i]) + { + for (int j=0;j<ms_MaxClassID;j++) + { + RTTIMap::iterator jRTTI = gRTTI->find (j); + if (jRTTI != gRTTI->end ()) + (*gIsDerivedFromBitMap)[i * ms_MaxClassID + j] = IsDerivedFromRTTI (&iRTTI->second, &jRTTI->second); + } + } + } + +#if USE_NEW_IS_DERIVED_FROM + ms_ClassIDMask = &gClassIDMask[0]; + ms_ClassIsDerivedFrom = new UInt32[ms_MaxClassID]; + memset(ms_ClassIsDerivedFrom, 0xffffffff, sizeof(UInt32) * ms_MaxClassID); + ms_ClassIsDerivedFrom[0] = 1; + BuildClassInfo* info = (BuildClassInfo*)alloca(sizeof(BuildClassInfo) * ms_MaxClassID); + for(int i = 0; i < ms_MaxClassID; ++i) + info[i].Clear(); + int maxSubClasses[32]; + int bitShift[32]; + memset(maxSubClasses, 0, sizeof(maxSubClasses)); + memset(bitShift, 0, sizeof(bitShift)); + //search for the base class + typedef RTTIMap::iterator itr; + for(itr i = gRTTI->begin(); i != gRTTI->end(); ++i) + { + Object::RTTI& r = i->second; + SInt32 oldId = i->first; + info[oldId].Init(&r, r.base); + info[oldId].id = oldId; + int level = 0; + Object::RTTI* base = r.base; + while(base) + { + ++level; + base = base->base; + } + info[oldId].level = level; + if(r.base) + info[r.base->classID].subclasses++; + } + std::sort(&info[0], ms_MaxClassID + &info[0]); + for(int i = 0; i < ms_MaxClassID; ++i) + { + int level = info[i].level; + maxSubClasses[level] = info[i].subclasses > maxSubClasses[level] ? info[i].subclasses : maxSubClasses[level]; + } + int totalBits = 1; + UInt32 mask = 1; + bitShift[0] = 0; + gClassIDMask[0] = 1; + for(int i = 1; i < 32; ++i) + { + int bitsReq = bitsRequired(maxSubClasses[i-1]); + int bitsReq1 = bitsReq; + while(bitsReq--) + mask = (mask<<1) | 1; + + bitShift[i] = totalBits; + totalBits += bitsReq1; + AssertIf(totalBits > 32 - CLASS_ID_MASK_BITS); //OUT OF BITS + gClassIDMask[i] = mask; + } +#define VERIFY_CLASS_IDS 0 +#if VERIFY_CLASS_IDS + std::set<UInt32> ClassIdSet; +#endif + int lastIndex = 0; + for(int i = 0; i < ms_MaxClassID; ++i) + { + Object::RTTI* klass = info[i].klass; + Object::RTTI* base = info[i].base; + int level = info[i].level; + int parentId = 0; + int parentIndex = -1; + int parentSubIndex = 1; + if(base) + { + for(int j = 0; j < ms_MaxClassID; ++j) + { + if(info[j].klass == base) + { + parentIndex = j; + break; + } + } + AssertIf(parentIndex >= i); + parentId = info[parentIndex].newId & CLASS_ID_MASK_IDS; + AssertIf(-1 == parentId); + parentSubIndex = info[parentIndex].subClassIdAssign++; + } + else + { + parentSubIndex = 1; + if(i != 0) + { + lastIndex = i; + break; + } + } + int shift = bitShift[level]; + int id = (parentSubIndex << shift) | parentId; + int fullId = id | (level<<(32-CLASS_ID_MASK_BITS)); +#if VERIFY_CLASS_IDS + AssertIf(ClassIdSet.find(fullId) != ClassIdSet.end() || ClassIdSet.find(id) != ClassIdSet.end() ); // DUPE CLASSID. should never happen + ClassIdSet.insert(id); + ClassIdSet.insert(fullId); +#endif + info[i].newId = fullId; + ms_ClassIsDerivedFrom[info[i].id] = fullId; + AssertIf(info[i].subClassIdAssign != 0); + info[i].subClassIdAssign = 1; + } +#endif +} + +void Object::CallInitializeClass() +{ + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + + // Call the IntializeClass function for classes that registered for it (IMPLEMENT_CLASS_HAS_INIT) + // This is done after all classes are registered and the rtti setup + // so that the rtti system can be used insie InitializeClass () + for (int i=0;i<callbacks.size ();i++) + { + if (callbacks[i].initClass) + { + callbacks[i].initClass (); + } + } +} + +void Object::CallPostInitializeClass() +{ + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + + // Call the PostIntializeClass function for classes that registered for it (IMPLEMENT_CLASS_HAS_POSTINIT) + // This is done after all classes are registered and the rtti setup + // so that the rtti system can be used inside PostInitializeClass () + for (int i=0;i<callbacks.size ();i++) + { + if (callbacks[i].postInitClass) + { + callbacks[i].postInitClass (); + } + } +} + +void Object::AddEvent (EventCallback* callback, void* userData) +{ + m_EventIndex = GetEventManager().AddEvent(callback, userData, m_EventIndex); +} + +void Object::RemoveEvent (EventCallback* callback, void* userData) +{ + m_EventIndex = GetEventManager().RemoveEvent(m_EventIndex, callback, userData); +} + +bool Object::HasEvent (EventCallback* callback, const void* userData) const +{ + return EventManager::HasEvent(m_EventIndex, callback, userData); +} + +void Object::InvokeEvent (int eventType) +{ + EventManager::InvokeEvent(m_EventIndex, this, eventType); +} + +bool IsObjectAvailable (int instanceID) +{ + Object* temp = Object::IDToPointer (instanceID); + if (temp != NULL) + return true; + + return GetPersistentManager ().IsObjectAvailable (instanceID); +} +#if !USE_NEW_IS_DERIVED_FROM +#if !UNITY_RELEASE + +bool Object::IsDerivedFromClassID (int classID, int compareClass) +{ + if (classID >= ms_MaxClassID || classID < 0) + { + char buffy[512]; + sprintf (buffy, "The class with classID: %d out of bounds", classID); + AssertString (buffy); + return false; + } + if (compareClass >= ms_MaxClassID || compareClass < 0) + { + /* + char buffy[512]; + sprintf (buffy, "The compare class with classID: %d out of bounds", compareClass); + AssertString (buffy); + */ + return false; + } + + AssertIf (classID >= ms_MaxClassID || classID < 0); + AssertIf (compareClass >= ms_MaxClassID || compareClass < 0); + + + if (!(*gRegisteredClassIDs)[classID]) + { + char buffy[512]; + sprintf (buffy, "The class with classID: %d is not registered (see ClassIDs.h)", classID); + AssertString (buffy); + + } + + // When doing classID stripping + #if !ALLOW_CLASS_ID_STRIPPING + if (!(*gRegisteredClassIDs)[compareClass]) + { + char buffy[512]; + sprintf (buffy, "The class with classID: %d is not registered (see ClassIDs.h)", compareClass); + AssertString (buffy); + } + #endif + + int index = classID * ms_MaxClassID + compareClass; + int block = index >> 5; + int bit = index - (block << 5); + return (ms_IsDerivedFromBitMap[block]) & (1 << bit); +} + +#endif +#endif + +INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL (Object, EXPORTDLL); + +template<class TransferFunction> +void Object::Transfer (TransferFunction& transfer) +{ +#if UNITY_EDITOR + if (!transfer.IsSerializingForGameRelease() && SerializePrefabIgnoreProperties(transfer)) + { + UInt32 flags = m_HideFlags; + transfer.Transfer(flags, "m_ObjectHideFlags", kHideInEditorMask); + m_HideFlags = flags; + } + + if (transfer.GetFlags () & kSerializeDebugProperties) + { + SInt32 instanceID = GetInstanceID (); + transfer.Transfer (instanceID, "m_InstanceID"); + + LocalIdentifierInFileType fileID; + if (IsPersistent ()) + fileID = GetPersistentManager ().GetLocalFileID (instanceID); + else + fileID = GetFileIDHint (); + + transfer.Transfer (fileID, "m_LocalIdentfierInFile"); + } +#endif +} + +#if UNITY_EDITOR + +void Object::RegisterDirtyCallback (ObjectDirtyCallbackFunction* callback) +{ + gSetDirtyCallbackFunc = callback; +} + +Object::ObjectDirtyCallbackFunction* Object::GetDirtyCallback () +{ + return gSetDirtyCallbackFunc; +} + +void Object::SetDirty () +{ + // When we run out of dirty indices, make sure it stays at 1 + m_DirtyIndex++; + if (m_DirtyIndex == 0) + m_DirtyIndex = 1; + + if (gSetDirtyCallbackFunc) + gSetDirtyCallbackFunc (this); + + #if !UNITY_RELEASE + m_DEBUGCLASSID = GetClassID (); + #endif +} + +void Object::ClearPersistentDirty () +{ + m_DirtyIndex = 0; +} + +void Object::SetPersistentDirtyIndex (UInt32 dirtyValue) +{ + m_DirtyIndex = dirtyValue; +} + + +#endif + +#if DEBUGMODE +void AddVerifyClassRegistration (int classID) +{ + if (gVerifyRegisteredClasses == NULL) + { + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + gVerifyRegisteredClasses = UNITY_NEW(RegisteredClassSet, kMemManager)(); + } + gVerifyRegisteredClasses->insert(classID); +} +const RegisteredClassSet& GetVerifyClassRegistration () +{ + if (gVerifyRegisteredClasses == NULL) + { + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + gVerifyRegisteredClasses = UNITY_NEW(RegisteredClassSet, kMemManager)(); + } + return *gVerifyRegisteredClasses; +} + +#endif + + +void RegisterInitializeClassCallback (int classID, + RegisterClassCallback* registerClass, + RegisterClassCallback* initClass, + RegisterClassCallback* postInitClass, + RegisterClassCallback* cleanupClass) +{ + if (gRegisterClassCallbacks == NULL) + { + SET_ALLOC_OWNER(gBaseObjectManagerContainer); + gRegisterClassCallbacks = UNITY_NEW(RegisterClassCallbacks,kMemBaseObject); + } + if (gRegisterClassCallbacks->size () <= classID) + gRegisterClassCallbacks->resize (classID + 1); + + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + if (callbacks[classID].registerClass != NULL || callbacks[classID].initClass != NULL || callbacks[classID].postInitClass != NULL || callbacks[classID].cleanupClass != NULL) + { + char buffer[512]; + sprintf (buffer, "ClassID: %d is already registered. ClassID's have to be unique", classID); + FatalErrorString (buffer); + AssertBreak(false); + } + callbacks[classID].registerClass = registerClass; + callbacks[classID].initClass = initClass; + callbacks[classID].postInitClass = postInitClass; + callbacks[classID].cleanupClass = cleanupClass; +} + +void Object::CleanupAllClasses () +{ + AssertIf(!ms_IDToPointer->empty()); + + if (!gRegisterClassCallbacks) + return; + + RegisterClassCallbacks& callbacks = *gRegisterClassCallbacks; + for (int i=0;i<callbacks.size ();i++) + { + if (callbacks[i].cleanupClass) + callbacks[i].cleanupClass (); + } + + UNITY_DELETE(gRegisterClassCallbacks, kMemBaseObject); +} + +void SetDisableImmediateDestruction (bool disable) +{ + gDisableImmediateDestruction = disable; +} + +bool GetDisableImmediateDestruction () +{ + return gDisableImmediateDestruction; +} + +void delete_object_internal (Object* p) +{ + if (!p) + return; + + delete_object_internal_step1 (p); + delete_object_internal_step2 (p); +} + +// This can be execute on any thread. +void delete_object_internal_step2 (Object* p) +{ + MemLabelId label = p->GetMemoryLabel(); + p->~Object (); + UNITY_FREE(label, p); +} + +void UnloadObject (Object* p) +{ + if (!p) + return; + + LockObjectCreation(); + delete_object_internal(p); + UnlockObjectCreation(); +} + +void Object::DoneLoadingManagers() +{ + // We are done loading managers. Start instance IDs from a high constant value here, + // so new managers and built-in resources can be added without changed instanceIDs + // used by the content. + if (gLowestInstanceID > -10000) + { + gLowestInstanceID = -10000; + } +} + +MemLabelId Object::GetMemoryLabel() const +{ + MemLabelIdentifier id = (MemLabelIdentifier)m_MemLabel; + MemLabelId label(id, NULL); + if(m_IsRootOwner) + label.SetRootHeader(GET_ALLOC_HEADER((void*)this, label)); + return label; +} + +int Object::GetRuntimeMemorySize() const +{ +#if ENABLE_MEM_PROFILER + return GetMemoryProfiler()->GetRelatedMemorySize((void*)this); +#else + return 0; +#endif +} + + +#if !UNITY_RELEASE + +void Object::CheckCorrectAwakeUsage() +{ + // check only if saw that object already to allow delayed awake and immediate destroy + if ( m_AwakeCalled == 0 ) + AssertStringObject(Format("Awake has not been called '%s' (%s). Figure out where the object gets created and call AwakeFromLoad correctly.", GetName(), GetClassName().c_str()), this); + + if ( m_ResetCalled == 0 ) + AssertStringObject(Format("Reset has not been called '%s' (%s). Figure out where the object gets created and call Reset correctly.", GetName(), GetClassName().c_str()), this); + + if ( m_AwakeThreadedCalled && !m_AwakeDidLoadThreadedCalled ) + AssertStringObject(Format("AwakeFromLoadThreaded has not been called '%s' (%s). Figure out where the object gets created and call AwakeFromLoadThreaded correctly.", GetName(), GetClassName().c_str()), this); +} + + +#endif // !UNITY_RELEASE diff --git a/Runtime/BaseClasses/BaseObject.h b/Runtime/BaseClasses/BaseObject.h new file mode 100644 index 0000000..52613be --- /dev/null +++ b/Runtime/BaseClasses/BaseObject.h @@ -0,0 +1,1162 @@ +#ifndef BASEOBJECT_H +#define BASEOBJECT_H + +#include "Runtime/Utilities/LogAssert.h" +#include "Runtime/Serialize/SerializeUtility.h" +#include "Runtime/Serialize/SerializationMetaFlags.h" +#include "Configuration/UnityConfigure.h" +#include "Runtime/Utilities/Prefetch.h" +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include "Runtime/Misc/Allocator.h" +#include "Runtime/Utilities/dynamic_array.h" + +#include <string> +#include <vector> + +#include "Runtime/Utilities/dense_hash_map.h" +#include "Runtime/Utilities/HashFunctions.h" + +#include "Runtime/BaseClasses/ClassIDs.h" + +class ProxyTransfer; +class SafeBinaryRead; +template<bool kSwap> +class StreamedBinaryRead; +template<bool kSwap> +class StreamedBinaryWrite; +class RemapPPtrTransfer; +class TypeTree; +class Object; +struct EventEntry; +#if SUPPORT_TEXT_SERIALIZATION +class YAMLRead; +class YAMLWrite; +#endif + +#include "ObjectDefines.h" +#include <string> +#include <typeinfo> + +//#define DefineClassID( x, classID ) +//#define ClassID( x ) + +// Every non-abstract class that is derived from object has to place this inside the class Declaration +// (REGISTER_DERIVED_CLASS (Foo, Object)) + +// Every abstract class that is derived from object has to place this inside the class Declaration +// (REGISTER_DERIVED_ABSTRACT_CLASS (Foo, Object)) + +//In the cpp file of every object derived class you have to place eg. IMPLEMENT_CLASS (Foo) +//#define IMPLEMENT_CLASS(x) +// or IMPLEMENT_CLASS_HAS_INIT (x) which will call the static class Function InitializeClass (); on startup. + +using std::string; + +template<class T> +class PPtr +{ + SInt32 m_InstanceID; + #if !UNITY_RELEASE + mutable T* m_DEBUGPtr; + #endif + + protected: + + inline void AssignObject (const Object* o); + + private: + static string s_TypeString; + + public: + + static const char* GetTypeString (); + static bool IsAnimationChannel () { return false; } + static bool MightContainPPtr () { return true; } + static bool AllowTransferOptimization () { return false; } + + template<class TransferFunction> + void Transfer (TransferFunction& transfer); + + // Assignment + explicit PPtr (int instanceID) + { + m_InstanceID = instanceID; + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + } + PPtr (const T* o) { AssignObject (o); } + PPtr (const PPtr<T>& o) + { + m_InstanceID = o.m_InstanceID; + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + } + + PPtr () + { + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + m_InstanceID = 0; + } + + PPtr& operator = (const T* o) { AssignObject (o); return *this; } + PPtr& operator = (const PPtr<T>& o) + { + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + m_InstanceID = o.m_InstanceID; return *this; + } + + void SetInstanceID (int instanceID) { m_InstanceID = instanceID; } + int GetInstanceID ()const { return m_InstanceID; } + + // Comparison + bool operator < (const PPtr& p)const { return m_InstanceID < p.m_InstanceID; } + bool operator == (const PPtr& p)const { return m_InstanceID == p.m_InstanceID; } + bool operator != (const PPtr& p)const { return m_InstanceID != p.m_InstanceID; } + + // MSVC gets confused whether it should use operator bool(), or operator T* with implicit + // comparison to NULL. So we add explicit functions and use them instead. + bool IsNull() const; + bool IsValid() const; + + operator T* () const; + T* operator -> () const; + T& operator * () const; +}; + +template<class T> +class ImmediatePtr +{ + mutable intptr_t m_Ptr; + #if !UNITY_RELEASE + mutable T* m_DEBUGPtr; + #endif + + void AssignInstanceID (int instanceID) + { + AssertIf (instanceID & 1); m_Ptr = instanceID | 1; AssertIf ((m_Ptr & 1) == 0); + #if !UNITY_RELEASE + m_DEBUGPtr = NULL; + #endif + } + void AssignObject (const T* o) + { + m_Ptr = (intptr_t)o; AssertIf (m_Ptr & 1); + #if !UNITY_RELEASE + m_DEBUGPtr = const_cast<T*>(o); + #endif + } + void Load () const + { + AssertIf ((m_Ptr & 1) == 0); + T* loaded = PPtr<T> (m_Ptr & (~1)); + m_Ptr = (intptr_t)(loaded); + AssertIf (m_Ptr & 1); + #if !UNITY_RELEASE + m_DEBUGPtr = loaded; + #endif + } + + inline T* GetPtr () const + { + if ((m_Ptr & 1) == 0) + { + return (T*)(m_Ptr); + } + else + { + Load (); + return (T*)(m_Ptr); + } + } + + static string s_TypeString; + + public: + + bool IsLoaded () const; + + static const char* GetTypeString (); + static bool IsAnimationChannel () { return false; } + static bool MightContainPPtr () { return true; } + static bool AllowTransferOptimization () { return false; } + + template<class TransferFunction> + void Transfer (TransferFunction& transfer); + + // Assignment + ImmediatePtr (const T* o) { AssignObject (o); } + ImmediatePtr (const ImmediatePtr<T>& o) { m_Ptr = o.m_Ptr; } + ImmediatePtr () { m_Ptr = 0; } + + ImmediatePtr& operator = (const T* o) { AssignObject (o); return *this; } + + void SetInstanceID (int instanceID) { AssignInstanceID (instanceID); } + int GetInstanceID ()const + { + if ((m_Ptr & 1) == 0 && m_Ptr != 0) + { + T* o = (T*)(m_Ptr); + SInt32 instanceID = o->GetInstanceID (); + AssertIf (instanceID & 1); + return instanceID; + } + else + return m_Ptr & (~1); + } + + inline bool operator == (const T* p)const { return GetPtr () == p; } + inline bool operator != (const T* p)const { return GetPtr () != p; } + + inline operator T* () const { return GetPtr (); } + inline T* operator -> () const { T* o = GetPtr (); AssertIf (o == NULL); return o; } + inline T& operator * () const { T* o = GetPtr (); AssertIf (o == NULL); ANALYSIS_ASSUME(o); return *o; } +}; + +template<typename T> class PtrToType; +template<typename T> class PtrToType<T*> +{ +public: + typedef T value_type; +}; + +template<class T, class U> +T dynamic_pptr_cast (U* ptr) +{ + typedef typename PtrToType<T>::value_type Type; + T castedPtr = (T)(ptr); + if (castedPtr && castedPtr->IsDerivedFrom ( Type::GetClassIDStatic ())) + return castedPtr; + else + return NULL; +} + +template<class T, class U> +T dynamic_pptr_cast (const PPtr<U>& ptr) +{ + U* o = ptr; + return dynamic_pptr_cast<T> (o); +} + +template<class T> inline +T dynamic_instanceID_cast (int instanceID) +{ + Object* o = PPtr<Object> (instanceID); + return dynamic_pptr_cast<T> (o); +} + +template<class T, class U> +PPtr<T> assert_pptr_cast (const PPtr<U>& ptr) +{ + #if DEBUGMODE + U* u = ptr; + AssertIf (dynamic_pptr_cast<U*> (u) == NULL && u != NULL); + #endif + return PPtr<T> (ptr.GetInstanceID ()); +} + +// Enables boost::mem_fn to use PPtr properly, needed for boost::bind +template<typename T> inline T * get_pointer(PPtr<T> const & p) +{ + return p; +} + + +enum ObjectCreationMode +{ + // Create the object from the main thread in a perfectly normal way + kCreateObjectDefault = 0, + // Create the object from another thread. Might assign an instance ID but will not register with IDToPointer map. + // Objects created like this, need to call, AwakeFromLoadThraded, and Object::RegisterInstanceID and AwakeFromLoad (kDidLoadThreaded); from the main thread + kCreateObjectFromNonMainThread = 1, + // Create the object and register the instance id but do not lock the object + // creation mutex because the code calling it already called LockObjectCreation mutex. + kCreateObjectDefaultNoLock = 2 +}; + + +enum AwakeFromLoadMode +{ + // This is the default, usually called from the inspector or various serialization methods + kDefaultAwakeFromLoad = 0, + // The object was loaded from disk + kDidLoadFromDisk = 1 << 0, + // The object was loaded from a loading thread (in almost all cases through loading from disk asynchronously) + kDidLoadThreaded = 1 << 1, + // Object was instantiated and is now gettings it's first Awake function or it was created from code and gets the Awake function called + kInstantiateOrCreateFromCodeAwakeFromLoad = 1 << 2, + // GameObject was made active or a component was added to an active game object + kActivateAwakeFromLoad = 1 << 3, + + kDefaultAwakeFromLoadInvalid = -1 +}; + +class BaseAllocator; + +enum ObjectDeleteMode +{ + kUnknownMode = 0 +}; + +class EXPORT_COREMODULE Object +{ + protected: + virtual ~Object (); + + public: + + Object (MemLabelId label, ObjectCreationMode mode); + + + /// By default the destructor might get executed on another thread. + /// This lets us distribute large level unloads to another thread and thus avoid hiccups. + /// Some classes need to deallocate resources on the main thread. + /// You can implement this function to delete the resources. + /// + /// The destructor will still be called in this case, thus you need to ensure that values are set to NULL. + /// MainThreadCleanup is only called if the deallocations are done on another thread. + /// Thus the destructor needs to handle the case where it is not called correctly. + /// + /// If you override the function return true, since it indicates that the class requires the function to be called. + /// SA: DoesClassRequireMainThreadDeallocation + virtual bool MainThreadCleanup (); + + /// To destroy objects use delete_object instead of delete operator + /// The default way to destroy objects is using the DestroyObject Function, which also destroys the object from it's file + /// Must be protected by LockObjectCreation / UnlockObjectCreation + friend void delete_object_internal_step1 (Object* p); + friend void delete_object_internal_step2 (Object* p); + + /// AwakeFromLoad is called after an object was read using Transfer (Either from disk or a vector) + /// This means it is called after the inspector has been updated, after it is loaded from disk, after a prefab has been modified or + /// the animation system has changed values behind your back. + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode) + { + #if !UNITY_RELEASE + m_AwakeCalled = 1; + + if( awakeMode & kDidLoadThreaded ) + m_AwakeDidLoadThreadedCalled = 1; + #endif + } + + virtual void AwakeFromLoadThreaded () + { + #if !UNITY_RELEASE + m_AwakeThreadedCalled = 1; + m_AwakeCalled = 0; + m_AwakeDidLoadThreadedCalled = 0; + #endif + } + + /// For Subclasses: Makes sure that persistent variables are correct and if not corrects them + /// It is called after Prefab propagation, SafeBinaryRead and PropertyEditor changes. + /// It is called before AwakeFromLoad + virtual void CheckConsistency () { } + + /// Override Reset in order to setup default values for the Object + + /// The difference between setting up default values in the constructor + /// and Reset is that Reset is only called when the editor creates a new object + /// or when the object uses SerializeSafeBinary read. + /// Thus Reset can be used as a performance optimization for touching variables which are serialized only once during load. + /// * All variables that are serialized and Reset in the Reset function, do not have to be initialized in the constructor* + /// Reset functions might get called from different threads during loading, thus they may not derefence other objects, in that case use SmartReset. + /// You can always rely on that AwakeFromLoad is called after Reset has been called. + virtual void Reset () + { + #if !UNITY_RELEASE + m_ResetCalled = 1; + m_AwakeCalled = 0; + m_AwakeThreadedCalled = 0; + m_AwakeDidLoadThreadedCalled = 0; + #endif + } + + // Smart Reset is called when Reset is selected or when AddComponent is called and a new ScriptableObject is created. + // If you want to for example adjust a collider bounding volume by the renderers mesh, use SmartReset, you can not use Reset for this! + virtual void SmartReset () + { + #if !UNITY_RELEASE + m_ResetCalled = 1; + m_AwakeCalled = 0; + m_AwakeThreadedCalled = 0; + m_AwakeDidLoadThreadedCalled = 0; + #endif + } + +#if !UNITY_RELEASE + // use it to check AwakeFromLoad/AwakeFromLoadThreaded/Reset/SmartReset were correctly called + void CheckCorrectAwakeUsage(); + + // hacks to set debug flags in cases you REALLY know what you are doing + + // call when you don't want to Reset in case of object fully inited and don't need to be reset to default state + // e.g. if you de-serialize object - you don't need reset + inline void HackSetResetWasCalled() { m_ResetCalled = 1; } + + // call when AwakeFromLoad has some side-effects so you need to postpone that call for indefinite time + // e.g. AudioClip will try to load sound in AwakeFromLoad, so you better do this only when needed + inline void HackSetAwakeWasCalled() { m_AwakeCalled = 1; } + + // same as HackSetAwakeWasCalled but for Awake with kDidLoadThreaded param + inline void HackSetAwakeDidLoadThreadedWasCalled () { m_AwakeDidLoadThreadedCalled = true; } + #else + inline void HackSetResetWasCalled() {} + inline void HackSetAwakeWasCalled() {} + inline void HackSetAwakeDidLoadThreadedWasCalled() {} + #endif + + /// Get and set the name + virtual char const* GetName () const { return ""; }; + virtual void SetName (char const* /*name*/) { } + void SetNameCpp (const std::string& name) { SetName(name.c_str()); } + + #if UNITY_EDITOR + /// Return true if you want the inspector to automatically refresh without SetDirty being called. + virtual bool HasDebugmodeAutoRefreshInspector () { return false; } + virtual void WarnInstantiateDisallowed () {} + #endif + + /// Returns the classID of the class + static int GetClassIDStatic () { return ClassID (Object); } + + // Is the class sealed (No other class can inherit from it) + // A sealed class can perform a GetComponent call faster, + // since it can compare the ClassID directly instead of using the RTTI system. + static bool IsSealedClass () { return false; } + + /// Returns true if the class is abstract + static bool IsAbstract () { return true; } + + /// Creates an object of type classID. + /// if instanceID is 0 a unique id will be generated if its non 0 the object will have the specified instanceID + static Object* Produce (int classID, int instanceID = 0, MemLabelId = kMemBaseObject, ObjectCreationMode mode = kCreateObjectDefault); + + + // Static initializa and destroy for BaseObject + static void StaticInitialize(); + static void StaticDestroy(); + + /// Registers instance id with IDToPointerMap + /// useful for thread loading with delayed activation from main thread + /// Can only be called from main thead + static void RegisterInstanceID (Object* obj); + static void RegisterInstanceIDNoLock (Object* obj); + + /// Allocates new instanceID and registers it with IDToPointerMap + /// Can only be called from main thead + static Object* AllocateAndAssignInstanceID (Object* obj); + static Object* AllocateAndAssignInstanceIDNoLock (Object* obj); + + #if UNITY_EDITOR + virtual void CloneAdditionalEditorProperties (Object& /*source*/) { } + + /// Can assign variable allows you to do additional type checking when assiging a variable + /// in the property inspector. + /// Eg. MonoBehaviours checks if a monobehaviour can be assigned based on the actual Mono class + virtual bool CanAssignMonoVariable (const char* /*property*/, Object* /*object*/) { return false; } + + #endif + + virtual bool ShouldIgnoreInGarbageDependencyTracking () { return false; } + + /// Gets the class ID + ClassIDType GetClassID () const { Assert(m_CachedClassID != 0); return (ClassIDType)m_CachedClassID; } + /// Gets the instance ID + int GetInstanceID () const { AssertIf(m_InstanceID == 0); return m_InstanceID; } + bool IsInstanceIDCreated () const { return m_InstanceID != 0; } + + /// Is this instance derived from compareClassID + bool IsDerivedFrom (int compareClassID)const { return IsDerivedFromClassID (GetClassID (), compareClassID); } + + #if UNITY_EDITOR + + /// Has this object been synced with the PersistentManager + bool IsPersistentDirty () const { return m_DirtyIndex != 0; } + + void SetPersistentDirtyIndex (UInt32 dirtyIndex); + UInt32 GetPersistentDirtyIndex () { return m_DirtyIndex; } + + ////@TODO: Rename this to SetPersistentDirty + + /// Whenever variables that are being serialized in Transfer change, SetDirty () should be called + /// This will allow tracking of objects that have changed since the last saving to disk or over the network + void SetDirty (); + + /// This method can be called if you need to unload an object from memory even if it's dirty. + void ClearPersistentDirty (); + + // Callback support for callbacks when SetDirty is called + typedef void ObjectDirtyCallbackFunction (Object* ptr); + static void RegisterDirtyCallback (ObjectDirtyCallbackFunction* callback); + static ObjectDirtyCallbackFunction* GetDirtyCallback (); + + void SetFileIDHint (LocalIdentifierInFileType hint) { m_FileIDHint = hint; } + LocalIdentifierInFileType GetFileIDHint () const { return m_FileIDHint; } + + #else + void SetDirty () { } + void ClearPersistentDirty () { } + + #endif + + + // The name of the class + const std::string& GetClassName () const; + + enum + { + kHideInHierarchy = 1 << 0, + kHideInspector = 1 << 1, + kDontSave = 1 << 2, + kNotEditable = 1 << 3, + kHideAndDontSave = kDontSave | kHideInHierarchy | kNotEditable + }; + + int GetHideFlags () const { return m_HideFlags; } + bool TestHideFlag (int mask) const { return (m_HideFlags & mask) == mask; } + bool TestHideFlagAny (int mask) const { return (m_HideFlags & mask) != 0; } + + virtual void SetHideFlags (int flags) { m_HideFlags = flags; } + void SetHideFlagsObjectOnly (int flags) { Assert(flags < (1 << kHideFlagsBits)); m_HideFlags = flags; } + + /// You must document all usage here in order to provide clear overview and avoid overlaps + /// - Transform root calculation for animation component, when binding animation states (Runtime only) + void SetTemporaryFlags (int flags) { Assert(flags < (1 << kTemporaryFlagsBits)); m_TemporaryFlags = flags; } + int GetTemporaryFlags () const { return m_TemporaryFlags; } +#if UNITY_WINRT + /// Used by WinRT's GarbageCollectSharedAssets + void SetTemporaryUnusedAssetsFlags (int flags) { m_TemporaryUnusedAssetsFlags = flags; } + int GetTemporaryUnusedAssetsFlags () const { return m_TemporaryUnusedAssetsFlags; } +#endif + +#if ENABLE_SCRIPTING + int GetGCHandle () const { return m_MonoReference; } +#endif + + /// Overall memory allocated for this object. Should calculate any memory allocated by other subystems as well. + /// For example if OpenGL allocates memory for a texture it must return how much memory we "think" OpenGL will allocate for the texture. + virtual int GetRuntimeMemorySize () const; + + /// Is this object persistent? + bool IsPersistent () const { return m_IsPersistent; } + + typedef InstanceIdToObjectPtrHashMap IDToPointerMap; + + /// How many objects are there in memory? + static IDToPointerMap::size_type GetLoadedObjectCount () { return ms_IDToPointer->size (); } + + // Finds the pointer to the object referenced by instanceID (NULL if none found in memory) + static Object* IDToPointer (int inInstanceID); + static Object* IDToPointerThreadSafe (int inInstanceID); + + /// This function may not be called unless you use LockObjectCreation / UnlockObjectCreation from another thread first... + /// If you don't know 100% what you are doing use: IDToPointerThreadSafe instead + static Object* IDToPointerNoThreadCheck (int inInstanceID); + + /// Finds out if classID is derived from compareClassID + static bool IsDerivedFromClassID (int classID, int derivedFromClassID); + + /// Returns the super Class ID of classID. + /// if classID doesnt have any super Class it will return ClassID (Object) + static int GetSuperClassID (int classID); + + /// Returns all classIDs that are derived from ClassID + static void FindAllDerivedClasses (int classID, std::vector<SInt32>* allDerivedClasses, bool returnOnlyNonAbstractClasses = true); + /// Returns how many objects are derived from classID + /// If allDerivedObjects != NULL, adds all derived object instanceIDs to the container + static int FindAllDerivedObjects (int classID, std::vector<SInt32>* derivedObjects, bool sorted = false); + + static int FindObjectsOfType (int classID, std::vector<Object*>* derivedObjects, bool sorted = false); + template<class T> + static int FindObjectsOfType (std::vector<T*>* derivedObjects) + { + std::vector<Object*>* casted = reinterpret_cast<std::vector<Object*>*> (derivedObjects); + return FindObjectsOfType (T::GetClassIDStatic (), casted); + } + + static int FindObjectsOfType (int classID, dynamic_array<Object*>* derivedObjects, bool sorted = false); + template<class T> + static int FindObjectsOfType (dynamic_array<T*>* derivedObjects) + { + dynamic_array<Object*>* casted = reinterpret_cast<dynamic_array<Object*>*> (derivedObjects); + return FindObjectsOfType (T::GetClassIDStatic (), casted); + } + + template<class T> + static int FindObjectsOfTypeSorted (std::vector<T*>* derivedObjects) + { + std::vector<Object*>* casted = reinterpret_cast<std::vector<Object*>*> (derivedObjects); + return FindObjectsOfType (T::GetClassIDStatic (), casted, true); + } + + + + /// Get the class name from the classID + static const std::string& ClassIDToString (int classID); + /// Get the classID from the class name, returns -1 if no classID was found + static int StringToClassID (const std::string& classString); + static int StringToClassIDCaseInsensitive (const std::string& classString); + static int StringToClassID (const char* classString); + + /// Callback support for callbacks when an object is destroyed + typedef void ObjectDestroyCallbackFunction (int instanceID); + static void RegisterDestroyedCallback (ObjectDestroyCallbackFunction* callback); + + /// Sets up the rtti for all classes that are derived from Object and + /// use the macro IMPLEMENT_CLASS or IMPLEMENT_CLASS_HAS_INIT + /// Calls the static function InitializeClass on every class that used + /// IMPLEMENT_CLASS_HAS_INIT instead of IMPLEMENT_CLASS + static void InitializeAllClasses (); + static void CallInitializeClassEarly(); + static void CallInitializeClass(); + static void CallPostInitializeClass(); + + static void CleanupAllClasses (); + + /// Checks if an array of instance id's are loaded. + /// If an instanceID is loaded it is set to 0. + static void CheckInstanceIDsLoaded (SInt32* instanceIDs, int size); + + + typedef Object* FactoryFunction (MemLabelId label, ObjectCreationMode mode); + struct RTTI + { + RTTI* base;// super rtti class + Object::FactoryFunction* factory;// the factory function of the class + int classID;// the class ID of the class + std::string className;// the name of the class + int size;// sizeof (Class) + bool isAbstract;// is the class Abstract? + }; + + /// Returns the RTTI information for a classID + static RTTI* ClassIDToRTTI (int classID); + + MemLabelId GetMemoryLabel () const; + + static void DoneLoadingManagers (); + + static IDToPointerMap& GetIDToPointerMapInternal () { return *ms_IDToPointer; } + + virtual int GetClassIDVirtualInternal () const { AssertString("Bad"); return ClassID(Object); } + void PreCleanupObject (); + + // Generic Event callback support. + typedef void EventCallback (void* userData, void* sender, int eventType); + + void AddEvent (EventCallback* callback, void* userData); + void RemoveEvent (EventCallback* callback, void* userData); + bool HasEvent (EventCallback* callback, const void* userData) const; + void InvokeEvent (int eventType); + +private: + + static UInt32* ms_IsDerivedFromBitMap; + static unsigned ms_MaxClassID; + static IDToPointerMap* ms_IDToPointer; + static UInt32* ms_ClassIDMask; + static UInt32* ms_ClassIsDerivedFrom; + + SInt32 m_InstanceID; + + enum Bits + { + kMemLabelBits = 13, + kIsRootOwnerBits = 1, + kTemporaryFlagsBits = 1, + kHideFlagsBits = 4, + kIsPersistentBits = 1, + kCachedClassIDBits = 12 + }; + + + UInt32 m_MemLabel : kMemLabelBits; // 13 bits + UInt32 m_IsRootOwner : kIsRootOwnerBits; // 14 bits + UInt32 m_TemporaryFlags: kTemporaryFlagsBits; // 15 bits + UInt32 m_HideFlags : kHideFlagsBits; // 19 bits + UInt32 m_IsPersistent : kIsPersistentBits; // 20 bits + UInt32 m_CachedClassID : kCachedClassIDBits; // 32 bits + + EventEntry* m_EventIndex; + + + #if !UNITY_RELEASE + UInt32 m_DEBUGCLASSID:16; + UInt32 m_AwakeCalled:1; + UInt32 m_ResetCalled:1; + UInt32 m_AwakeThreadedCalled:1; + UInt32 m_AwakeDidLoadThreadedCalled:1; + #endif + + #if ENABLE_MONO + UInt32 m_MonoReference; + #elif UNITY_FLASH + SInt32 m_MonoReference; + #elif UNITY_WINRT + SInt32 m_MonoReference; + UInt32 m_TemporaryUnusedAssetsFlags; + #endif + #if ENABLE_SCRIPTING + ScriptingObjectPtr m_ScriptingObjectPointer; + #endif + + #if UNITY_EDITOR + UInt32 m_DirtyIndex; + LocalIdentifierInFileType m_FileIDHint; + #endif + + public: + + #if ENABLE_SCRIPTING + void SetupWeakHandle (); + bool RevertWeakHandle (); + + void SetCachedScriptingObject (ScriptingObjectPtr cachedPointer); + ScriptingObjectPtr GetCachedScriptingObject () { return m_ScriptingObjectPointer; } + #endif + +private: + + static void CalculateCachedClassID (Object* obj); + static void InsertObjectInMap (Object* obj); + + void SetIsPersistent (bool p); + + Object (const Object& o); // Disallow copy constructor + Object& operator = (const Object& o); // Disallow assignment + + void SetInstanceID (int inID) { m_InstanceID = inID; } + + protected: + + static void RegisterClass (int inClassID, int inBaseClass, const std::string& inName, int size, FactoryFunction* inFunc, bool isAbstract); + + static Object* PRODUCE (MemLabelId /*label*/, ObjectCreationMode /*mode*/) { AssertString ("Can't produce abstract class"); return NULL; } + + template<class TransferFunction> + void Transfer (TransferFunction& transfer); + + public: + + /// Returns whether or not the class needs one typetree per object, not per classID + /// Having a per object typetree makes serialization considerably slower because safeBinaryTransfer is always used + /// Since no TypeTree can be generated before reading the object. + /// The File size will also increase because the typetree is not shared among the same classes. + /// It is used for example in PythonBehaviour + /// Also for one class you have to always returns true or always false. + virtual bool GetNeedsPerObjectTypeTree () const { return false; } + + // Sets up RTTI, the object factory (Produce) and string <-> classID + // conversion. RegisterClass() has to be called once for every class + // derived from object, before any Objects are allocated + static void RegisterClass (); + + // Required by serialization + virtual void VirtualRedirectTransfer (StreamedBinaryWrite<false>&){ AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (StreamedBinaryRead<false>&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (RemapPPtrTransfer&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (ProxyTransfer&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } +#if SUPPORT_SERIALIZED_TYPETREES + virtual void VirtualRedirectTransfer (StreamedBinaryRead<true>&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (SafeBinaryRead&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (StreamedBinaryWrite<true>&){ AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } +#endif +#if SUPPORT_TEXT_SERIALIZATION + virtual void VirtualRedirectTransfer (YAMLRead&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualRedirectTransfer (YAMLWrite&) { AssertString ("Serialization not implemented for type " + Object::ClassIDToString (GetClassID ())); } + virtual void VirtualStrippedRedirectTransfer (YAMLWrite& t) { VirtualRedirectTransfer(t); } +#endif +#if ENABLE_SERIALIZATION_BY_CODEGENERATION + virtual void DoLivenessCheck (RemapPPtrTransfer&) { AssertString ("DoLivenessCheck not implemented for type " + Object::ClassIDToString (GetClassID ())); } +#endif + static const char* GetClassStringStatic (){ return "Object"; } + static const char* GetPPtrTypeString (){ return "PPtr<Object>"; } + + friend class PersistentManager; + friend class SerializedFile; +}; + +struct LocalSerializedObjectIdentifier +{ + SInt32 localSerializedFileIndex; + #if LOCAL_IDENTIFIER_IN_FILE_SIZE == 64 + UInt64 localIdentifierInFile; + #else + SInt32 localIdentifierInFile; + #endif + + LocalSerializedObjectIdentifier() + { + localIdentifierInFile = 0; + localSerializedFileIndex = 0; + } +}; + +typedef void InstanceIDResolveCallback (SInt32 id, LocalSerializedObjectIdentifier& localIdentifier, void* context); +void SetInstanceIDResolveCallback (InstanceIDResolveCallback* callback, const void* context = NULL); + +void EXPORT_COREMODULE InstanceIDToLocalSerializedObjectIdentifier (SInt32 id, LocalSerializedObjectIdentifier& localIdentifier); +void EXPORT_COREMODULE LocalSerializedObjectIdentifierToInstanceID (const LocalSerializedObjectIdentifier& fileID, SInt32& memoryID); +EXPORT_COREMODULE Object* ReadObjectFromPersistentManager (int instanceID); + +#if THREADED_LOADING +EXPORT_COREMODULE Object* InstanceIDToObjectThreadSafe (int instanceID); +#else +# define InstanceIDToObjectThreadSafe PPtr<Object> +#endif + +// This is used by the build game process. When writing for game release +// we want to null all pptrs that can't be loaded anymore. +// And when building default resources (culls all external references) +enum { kWriteNULLWhenNotLoaded = 1 << 0, kConstrainedExternalReferences = 1 << 1 }; +void SetSerializeWritePPtrFlags (int flags, const std::set<string>& paths); + +void EXPORT_COREMODULE SetDisableImmediateDestruction (bool disable); +bool EXPORT_COREMODULE GetDisableImmediateDestruction (); + + +/// Returns if the object can possibly be loaded or is already in memory, without actually performing the loading. +bool IsObjectAvailable (int instanceID); + +//Implementation +#if UNITY_RELEASE +# if UNITY_PS3 + __attribute__((always_inline)) inline Object* Object::IDToPointer (int inInstanceID) +#else + inline Object* Object::IDToPointer (int inInstanceID) +# endif +{ + if( !ms_IDToPointer ) return NULL; + IDToPointerMap::const_iterator i = ms_IDToPointer->find (inInstanceID); + if (i != ms_IDToPointer->end ()) + return i->second; + else + return NULL; +} +#endif + +template<class T> +inline void PPtr<T>::AssignObject (const Object* o) +{ + if (o == NULL) + m_InstanceID = 0; + else + m_InstanceID = o->GetInstanceID (); + #if !UNITY_RELEASE + m_DEBUGPtr = (T*) (o); + #endif +} + +template<class T> inline +PPtr<T>::operator T* () const +{ + if (GetInstanceID () == 0) + return NULL; + + Object* temp = Object::IDToPointer (GetInstanceID ()); + if (temp == NULL) + temp = ReadObjectFromPersistentManager (GetInstanceID ()); + + #if !UNITY_RELEASE + m_DEBUGPtr = (T*) (temp); + #endif + + #if DEBUGMODE || UNITY_EDITOR + T* casted = dynamic_pptr_cast<T*> (temp); + if (casted == temp) + return casted; + else + { + ErrorStringObject ("PPtr cast failed when dereferencing! Casting from " + temp->GetClassName () + " to " + T::GetClassStringStatic () + "!", temp); + return casted; + } + #else + return static_cast<T*> (temp); + #endif +} + +template<class T> inline +T* PPtr<T>::operator -> () const +{ + Object* temp = Object::IDToPointer (GetInstanceID ()); + if (temp == NULL) + temp = ReadObjectFromPersistentManager (GetInstanceID ()); + + #if !UNITY_RELEASE + m_DEBUGPtr = (T*) (temp); + #endif + + #if DEBUGMODE || !GAMERELEASE + T* casted = dynamic_pptr_cast<T*> (temp); + if (casted != NULL) + return casted; + else + { + if (temp != NULL) + { + ErrorStringObject ("PPtr cast failed when dereferencing! Casting from " + temp->GetClassName () + " to " + T::GetClassStringStatic () + "!", temp); + } + else + { + ErrorString ("Dereferencing NULL PPtr!"); + } + return casted; + } + #else + return static_cast<T*> (temp); + #endif +} + +template<class T> inline +T& PPtr<T>::operator * () const +{ + Object* temp = Object::IDToPointer (GetInstanceID ()); + if (temp == NULL) + temp = ReadObjectFromPersistentManager (GetInstanceID ()); + + #if !UNITY_RELEASE + m_DEBUGPtr = (T*) (temp); + #endif + + #if DEBUGMODE || !GAMERELEASE + T* casted = dynamic_pptr_cast<T*> (temp); + if (casted != NULL) + return *casted; + else + { + if (temp != NULL) + { + ErrorStringObject ("PPtr cast failed when dereferencing! Casting from " + temp->GetClassName () + " to " + T::GetClassStringStatic () + "!", temp); + } + else + { + ErrorString ("Dereferencing NULL PPtr!"); + } + ANALYSIS_ASSUME(casted); + return *casted; + } + #else + return *static_cast<T*> (temp); + #endif +} + +template<class T> inline +bool PPtr<T>::IsNull() const +{ + T* casted = *this; + return casted == NULL; +} + +template<class T> inline +bool PPtr<T>::IsValid() const +{ + T* casted = *this; + return casted != NULL; +} + +template<class T> +string PPtr<T>::s_TypeString; + +template<class T> inline +const char* PPtr<T>::GetTypeString () +{ + return T::GetPPtrTypeString (); +} + +template<class T> +template<class TransferFunction> inline +void PPtr<T>::Transfer (TransferFunction& transfer) +{ + LocalSerializedObjectIdentifier localIdentifier; + + if (transfer.NeedsInstanceIDRemapping ()) + { + AssertIf (!transfer.IsWriting () && !transfer.IsReading ()); + + if (transfer.IsReading ()) + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + LocalSerializedObjectIdentifierToInstanceID (localIdentifier, m_InstanceID); + } + else if (transfer.IsWriting ()) + { + InstanceIDToLocalSerializedObjectIdentifier (m_InstanceID, localIdentifier); + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + else + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + } + else + { + transfer.Transfer (m_InstanceID, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } +} + +template<class T> inline +bool ImmediatePtr<T>::IsLoaded () const +{ + if (m_Ptr & 1) + { + return Object::IDToPointer(m_Ptr & (~1)) != NULL; + } + else + { + AssertIf(Object::IDToPointer(GetInstanceID()) == NULL); + return true; + } +} +template<class T> +string ImmediatePtr<T>::s_TypeString; + +template<class T> inline +const char* ImmediatePtr<T>::GetTypeString () +{ + if(s_TypeString.empty()) + { + SET_ALLOC_OWNER(NULL); + s_TypeString = string ("PPtr<") + T::GetClassStringStatic () + ">"; + } + return s_TypeString.c_str (); +} + +template<class T> +template<class TransferFunction> inline +void ImmediatePtr<T>::Transfer (TransferFunction& transfer) +{ + LocalSerializedObjectIdentifier localIdentifier; + + if (transfer.NeedsInstanceIDRemapping ()) + { + AssertIf (!transfer.IsWriting () && !transfer.IsReading ()); + + if (transfer.IsReading ()) + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + SInt32 instanceID; + LocalSerializedObjectIdentifierToInstanceID (localIdentifier, instanceID); + AssignInstanceID (instanceID); + } + else if (transfer.IsWriting ()) + { + InstanceIDToLocalSerializedObjectIdentifier (GetInstanceID (), localIdentifier); + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + else + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + } + else + { + if (transfer.IsReading ()) + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + SetInstanceID (localIdentifier.localSerializedFileIndex); + } + else if (transfer.IsWriting ()) + { + localIdentifier.localSerializedFileIndex = GetInstanceID (); + localIdentifier.localIdentifierInFile = 0; + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + AssertIf (localIdentifier.localSerializedFileIndex != GetInstanceID ()); + } + else + { + transfer.Transfer (localIdentifier.localSerializedFileIndex, "m_FileID", kHideInEditorMask); + transfer.Transfer (localIdentifier.localIdentifierInFile, "m_PathID", kHideInEditorMask); + } + } +} +#if UNITY_PS3 +#define USE_NEW_IS_DERIVED_FROM 1 +#else +#define USE_NEW_IS_DERIVED_FROM 0 +#endif + +#if !USE_NEW_IS_DERIVED_FROM +#if UNITY_RELEASE +inline bool Object::IsDerivedFromClassID (int classID, int compareClass) +{ + AssertIf (classID >= ms_MaxClassID || classID < 0); + AssertIf (compareClass >= ms_MaxClassID || compareClass < 0); + int index = classID * ms_MaxClassID + compareClass; + int block = index >> 5; + int bit = index - (block << 5); + return (ms_IsDerivedFromBitMap[block]) & (1 << bit); +} +#endif +#else +#define CLASS_ID_MASK_BITS 4 +#define CLASS_ID_MASK_IDS 0x0fffffff +inline bool Object::IsDerivedFromClassID(int klass, int base) +{ + int klassId = ms_ClassIsDerivedFrom[klass]; + int baseId = ms_ClassIsDerivedFrom[base]; + int mask = ms_ClassIDMask[ 0xf&(baseId >> (32-CLASS_ID_MASK_BITS))]; + return (klassId&mask) == (baseId&CLASS_ID_MASK_IDS); +} +#endif + +void LockObjectCreation (); +void UnlockObjectCreation (); + +// Destroys a Object removing from memory and disk when needed. +// Might load the object as part of destruction which is probably unwanted. +// @TODO: Refactor code to not do that +void EXPORT_COREMODULE DestroySingleObject (Object* o); +void UnloadObject (Object* o); + +/// Destroys the object if it is loaded. (Will not load the object from disk if it is not loaded at the moment) +/// Will remove it from any remapping tables +/// Will not removed it from the actual serialized file, with the assumption that the file will be unloaded from disk later. +void DestroyWithoutLoadingButDontDestroyFromFile (int instanceID); + +#if DEBUGMODE +typedef std::set<int, std::less<int>, STL_ALLOCATOR(kMemPermanent,int) > VerifyRegisteredClass; +void EXPORT_COREMODULE AddVerifyClassRegistration (int classID); +typedef std::set<int, std::less<int>, STL_ALLOCATOR(kMemBaseObject, int) > RegisteredClassSet; +const RegisteredClassSet& GetVerifyClassRegistration (); +#endif + +/// Helper to create object correctly from code. Will call Reset and AwakeFromLoad +template <typename T> T* CreateObjectFromCode( AwakeFromLoadMode awakeMode=kInstantiateOrCreateFromCodeAwakeFromLoad, MemLabelId label = kMemBaseObject ) +{ + Assert(Object::ClassIDToRTTI(T::GetClassIDStatic()) != NULL); + T* obj = NEW_OBJECT_USING_MEMLABEL(T, label); + SET_ALLOC_OWNER(obj); + obj->Reset(); + obj->AwakeFromLoad(awakeMode); + return obj; +} + +template<typename T> +inline T* ResetAndAwake (T* object) +{ + object->Reset(); + object->AwakeFromLoad (kDefaultAwakeFromLoad); + return object; +} + +void delete_object_internal (Object* p); +void delete_object_internal_step1 (Object* object); +void delete_object_internal_step2 (Object* object); + +#endif diff --git a/Runtime/BaseClasses/BitField.h b/Runtime/BaseClasses/BitField.h new file mode 100644 index 0000000..1ead092 --- /dev/null +++ b/Runtime/BaseClasses/BitField.h @@ -0,0 +1,26 @@ +#ifndef BITFIELD_H +#define BITFIELD_H +#include "Runtime/Serialize/SerializeUtility.h" + +struct BitField +{ + DECLARE_SERIALIZE_OPTIMIZE_TRANSFER (BitField) + UInt32 m_Bits; +}; + +enum { kPreUnity2UnusedLayerMask = 1 << 5 }; + +template<class TransferFunc> +void BitField::Transfer (TransferFunc& transfer) +{ + transfer.SetVersion (2); + transfer.Transfer (m_Bits, "m_Bits", kHideInEditorMask | kGenerateBitwiseDifferences); + + if (transfer.IsOldVersion(1)) + { + if (m_Bits & kPreUnity2UnusedLayerMask) + m_Bits |= 0xFFFF << 16; + } +} + +#endif diff --git a/Runtime/BaseClasses/ClassIDs.h b/Runtime/BaseClasses/ClassIDs.h new file mode 100644 index 0000000..aa991e2 --- /dev/null +++ b/Runtime/BaseClasses/ClassIDs.h @@ -0,0 +1,240 @@ +#ifndef CLASSIDS_H_ +#define CLASSIDS_H_ + +#define ClassID(x) CLASS_##x +#define DefineClassID(x,classID) ClassID(x) = classID, + +// Runtime classIDs are kept intentionally small. +enum ClassIDType +{ +DefineClassID (Undefined, -1) +DefineClassID (Object, 0) +DefineClassID (GameObject, 1) +DefineClassID (Component, 2) +DefineClassID (LevelGameManager, 3) +DefineClassID (Transform, 4) +DefineClassID (TimeManager, 5) +DefineClassID (GlobalGameManager, 6) +DefineClassID (Behaviour, 8) +DefineClassID (GameManager, 9) +DefineClassID (AudioManager, 11) +DefineClassID (ParticleAnimator, 12) +DefineClassID (InputManager, 13) +DefineClassID (EllipsoidParticleEmitter, 15) +DefineClassID (Pipeline, 17) +DefineClassID (EditorExtension, 18) +DefineClassID (Physics2DSettings, 19) +DefineClassID (Camera, 20) +DefineClassID (Material, 21) +DefineClassID (MeshRenderer, 23) +DefineClassID (Renderer, 25) +DefineClassID (ParticleRenderer, 26) +DefineClassID (Texture, 27) +DefineClassID (Texture2D, 28) +DefineClassID (SceneSettings, 29) +DefineClassID (GraphicsSettings, 30) +DefineClassID (MeshFilter, 33) +DefineClassID (OcclusionPortal, 41) +DefineClassID (Mesh, 43) +DefineClassID (Skybox, 45) +DefineClassID (QualitySettings, 47) +DefineClassID (Shader, 48) +DefineClassID (TextAsset, 49) +DefineClassID (Rigidbody2D, 50) +DefineClassID (Physics2DManager, 51) +DefineClassID (Collider2D, 53) +DefineClassID (Rigidbody, 54) +DefineClassID (PhysicsManager, 55) +DefineClassID (Collider, 56) +DefineClassID (Joint, 57) +DefineClassID (CircleCollider2D, 58) +DefineClassID (HingeJoint, 59) +DefineClassID (PolygonCollider2D, 60) +DefineClassID (BoxCollider2D, 61) +DefineClassID (PhysicsMaterial2D, 62) +DefineClassID (MeshCollider, 64) +DefineClassID (BoxCollider, 65) +DefineClassID (SpriteCollider2D, 66) +DefineClassID (EdgeCollider2D, 68) +DefineClassID (PolygonColliderBase2D, 69) +DefineClassID (ComputeShader, 72) +DefineClassID (AnimationClip, 74) +DefineClassID (ConstantForce, 75) +DefineClassID (WorldParticleCollider, 76) +DefineClassID (TagManager, 78) +DefineClassID (AudioListener, 81) +DefineClassID (AudioSource, 82) +DefineClassID (AudioClip, 83) +DefineClassID (RenderTexture, 84) +DefineClassID (MeshParticleEmitter, 87) +DefineClassID (ParticleEmitter, 88) +DefineClassID (Cubemap, 89) +DefineClassID (Avatar, 90) +DefineClassID (AnimatorController, 91) +DefineClassID (GUILayer, 92) +DefineClassID (RuntimeAnimatorController, 93) +DefineClassID (ScriptMapper, 94) +DefineClassID (Animator, 95) +DefineClassID (TrailRenderer, 96) +DefineClassID (DelayedCallManager, 98) +DefineClassID (TextMesh, 102) +DefineClassID (RenderSettings, 104) +DefineClassID (Light, 108) +DefineClassID (CGProgram, 109) +DefineClassID (BaseAnimationTrack, 110) +DefineClassID (Animation, 111) +DefineClassID (MonoBehaviour, 114) +DefineClassID (MonoScript, 115) +DefineClassID (MonoManager, 116) +DefineClassID (Texture3D, 117) +DefineClassID (NewAnimationTrack, 118) +DefineClassID (Projector, 119) +DefineClassID (LineRenderer, 120) +DefineClassID (Flare, 121) +DefineClassID (Halo, 122) +DefineClassID (LensFlare, 123) +DefineClassID (FlareLayer, 124) +DefineClassID (HaloLayer, 125) +DefineClassID (NavMeshLayers, 126) +DefineClassID (HaloManager, 127) +DefineClassID (Font, 128) +DefineClassID (PlayerSettings, 129) +DefineClassID (NamedObject, 130) +DefineClassID (GUITexture, 131) +DefineClassID (GUIText, 132) +DefineClassID (GUIElement, 133) +DefineClassID (PhysicMaterial, 134) +DefineClassID (SphereCollider, 135) +DefineClassID (CapsuleCollider, 136) +DefineClassID (SkinnedMeshRenderer, 137) +DefineClassID (FixedJoint, 138) +DefineClassID (RaycastCollider, 140) +DefineClassID (BuildSettings, 141) +DefineClassID (AssetBundle, 142) +DefineClassID (CharacterController, 143) +DefineClassID (CharacterJoint, 144) +DefineClassID (SpringJoint, 145) +DefineClassID (WheelCollider, 146) +DefineClassID (ResourceManager, 147) +DefineClassID (NetworkView, 148) +DefineClassID (NetworkManager, 149) +DefineClassID (PreloadData, 150) +DefineClassID (MovieTexture, 152) +DefineClassID (ConfigurableJoint, 153) +DefineClassID (TerrainCollider, 154) +DefineClassID (MasterServerInterface, 155) +DefineClassID (TerrainData, 156) +DefineClassID (LightmapSettings, 157) +DefineClassID (WebCamTexture, 158) +DefineClassID (EditorSettings, 159) +DefineClassID (InteractiveCloth, 160) +DefineClassID (ClothRenderer, 161) +DefineClassID (EditorUserSettings, 162) +DefineClassID (SkinnedCloth, 163) +DefineClassID (AudioReverbFilter, 164) +DefineClassID (AudioHighPassFilter, 165) +DefineClassID (AudioChorusFilter, 166) +DefineClassID (AudioReverbZone, 167) +DefineClassID (AudioEchoFilter, 168) +DefineClassID (AudioLowPassFilter, 169) +DefineClassID (AudioDistortionFilter, 170) +DefineClassID (AudioBehaviour, 180) +DefineClassID (AudioFilter, 181) +DefineClassID (WindZone, 182) +DefineClassID (Cloth, 183) +DefineClassID (SubstanceArchive, 184) +DefineClassID (ProceduralMaterial, 185) +DefineClassID (ProceduralTexture, 186) +DefineClassID (OffMeshLink, 191) +DefineClassID (OcclusionArea, 192) +DefineClassID (Tree, 193) +DefineClassID (NavMesh, 194) +DefineClassID (NavMeshAgent, 195) +DefineClassID (NavMeshSettings, 196) +DefineClassID (LightProbes, 197) +DefineClassID (ParticleSystem, 198) +DefineClassID (ParticleSystemRenderer, 199) +DefineClassID (LODGroup, 205) +DefineClassID (BlendTree, 206) +DefineClassID (Motion, 207) +DefineClassID (NavMeshObstacle, 208) +DefineClassID (TerrainInstance, 210) + +DefineClassID (SpriteRenderer, 212) +DefineClassID (Sprite, 213) +DefineClassID (CachedSpriteAtlas, 214) + +DefineClassID (LightProbeGroup, 220) +DefineClassID (AnimatorOverrideController, 221) + +DefineClassID (Joint2D, 230) +DefineClassID (SpringJoint2D, 231) +DefineClassID (DistanceJoint2D, 232) +DefineClassID (HingeJoint2D, 233) +DefineClassID (SliderJoint2D, 234) +// Reserved 235-238 for new joints. +//DefineClassID (WheelJoint2D, 235) +//DefineClassID (FrictionJoint2D, 236) +//DefineClassID (PulleyJoint2D, 237) +//DefineClassID (GearJoint2D, 238) + +kLargestRuntimeClassID, + +DefineClassID (SmallestEditorClassID, 1000) +DefineClassID (Prefab, 1001) +DefineClassID (EditorExtensionImpl, 1002) +DefineClassID (AssetImporter, 1003) +DefineClassID (AssetDatabase, 1004) +DefineClassID (Mesh3DSImporter, 1005) +DefineClassID (TextureImporter, 1006) +DefineClassID (ShaderImporter, 1007) +DefineClassID (ComputeShaderImporter, 1008) +DefineClassID (AvatarMask, 1011) +DefineClassID (AudioImporter, 1020) +DefineClassID (HierarchyState, 1026) +DefineClassID (GUIDSerializer, 1027) +DefineClassID (AssetMetaData, 1028) +DefineClassID (DefaultAsset, 1029) +DefineClassID (DefaultImporter, 1030) +DefineClassID (TextScriptImporter, 1031) +DefineClassID (SceneAsset, 1032) +DefineClassID (NativeFormatImporter, 1034) +DefineClassID (MonoImporter, 1035) +DefineClassID (AssetServerCache, 1037) +DefineClassID (LibraryAssetImporter, 1038) +DefineClassID (ModelImporter, 1040) +DefineClassID (FBXImporter, 1041) +DefineClassID (TrueTypeFontImporter, 1042) +DefineClassID (MovieImporter, 1044) +DefineClassID (EditorBuildSettings, 1045) +DefineClassID (DDSImporter, 1046) +DefineClassID (InspectorExpandedState, 1048) +DefineClassID (AnnotationManager, 1049) +DefineClassID (MonoAssemblyImporter, 1050) +DefineClassID (EditorUserBuildSettings, 1051) +DefineClassID (PVRImporter, 1052) +DefineClassID (Transition, 1101) +DefineClassID (State, 1102) +DefineClassID (HumanTemplate, 1105) +DefineClassID (StateMachine, 1107) +DefineClassID (PreviewAssetType, 1108) +DefineClassID (SubstanceImporter, 1112) + +kLargestEditorClassID, + +kClassIdOutOfHierarchy = 100000, + +DefineClassID (int, kClassIdOutOfHierarchy) +DefineClassID (bool, kClassIdOutOfHierarchy + 1) +DefineClassID (float, kClassIdOutOfHierarchy + 2) +DefineClassID (MonoObject, kClassIdOutOfHierarchy + 3) +DefineClassID (Collision, kClassIdOutOfHierarchy + 4) +DefineClassID (Vector3f, kClassIdOutOfHierarchy + 5) +DefineClassID (RootMotionData, kClassIdOutOfHierarchy + 6) +DefineClassID (Collision2D, kClassIdOutOfHierarchy + 7) +}; + +//make sure people dont accidentally define classids in other files: +#undef DefineClassID + +#endif diff --git a/Runtime/BaseClasses/ClassRegistration.cpp b/Runtime/BaseClasses/ClassRegistration.cpp new file mode 100644 index 0000000..80b44ec --- /dev/null +++ b/Runtime/BaseClasses/ClassRegistration.cpp @@ -0,0 +1,267 @@ +#include "UnityPrefix.h" +#include "ClassRegistration.h" +#include "Runtime/BaseClasses/BaseObject.h" +#include "Runtime/Modules/ModuleRegistration.h" + +// IPhone platform with stripping overwrites the "RegisterAllClasses" function. +// See GenerateRegisterClassesForStripping in MonoInternalCallGenerator.cs. +// This is why the iPhone builds overwrite the function name here. +#if UNITY_IPHONE +#define RegisterAllClasses RegisterAllClassesIPhone +#endif + +using namespace std; +#if DEBUGMODE +static void VerifyThatAllClassesHaveBeenRegistered(const RegisteredClassSet& explicitlyRegistered); +#endif + +#define RESERVE_CLASSID(klass,classID) ValidateRegisteredClassID (context,classID, #klass); +#define RESERVE_DEPRECATED_CLASSID(klass,classID) ValidateRegisteredClassID (context,classID, #klass); + +void ValidateRegisteredClassID (ClassRegistrationContext& context, int classID, const char* className) +{ +#if DEBUGMODE + RegisteredClassSet& explicitlyRegistered = *reinterpret_cast<RegisteredClassSet*> (context.explicitlyRegistered); + bool didNotExist = explicitlyRegistered.insert(classID).second; + + if (!didNotExist) FatalErrorString(Format("ClassID %d conflicts with that of another class. Please resolve the conflict. (%s)", classID, className)); +#endif +} + + +#if DEBUGMODE +void RegisterDeprecatedClassIDs (ClassRegistrationContext& context) +{ + /// DO NOT REMOVE CLASS IDS FROM THIS LIST TO MAKE ROOM FOR A NEW ONE. IT WILL RESULT IN CLASSID CONFLICTS ON OLD PROJECTS + + RESERVE_DEPRECATED_CLASSID (BehaviourManager, 7) // Removed in Unity 3.2 + RESERVE_DEPRECATED_CLASSID (Filter, 16) // Removed ages ago + RESERVE_DEPRECATED_CLASSID (PipelineManager, 31) // Removed in Unity 3.2 + RESERVE_DEPRECATED_CLASSID (BaseBehaviourManager, 34) // Removed in Unity 3.2 + RESERVE_DEPRECATED_CLASSID (LateBehaviourManager, 35) // Removed in Unity 3.2 + RESERVE_DEPRECATED_CLASSID (FixedBehaviourManager, 46) // Removed in Unity 3.2 + RESERVE_DEPRECATED_CLASSID (UpdateManager, 63) // Removed in Unity 3.2 + RESERVE_DEPRECATED_CLASSID (RenderLayer, 67) // Intermediate abstract class refactored away post-Unity 3.2 + RESERVE_DEPRECATED_CLASSID (AnimationTrack2, 112) + RESERVE_DEPRECATED_CLASSID (ResourceManagerOLD, 113) + RESERVE_DEPRECATED_CLASSID (GooballCollider, 77) + RESERVE_DEPRECATED_CLASSID (VertexSnapper, 79) + RESERVE_DEPRECATED_CLASSID (LightManager, 85) // -> changed id DO NOT REUSE + //RESERVE_DEPRECATED_CLASSID (PreloadManager, 90) // Now used by Avatar + //RESERVE_DEPRECATED_CLASSID (ScaleFilter, 91) // deprecated pre 1.0, now used by AnimatorController + //RESERVE_DEPRECATED_CLASSID (TextureRect, 93) // Now used by RuntimeAnimatorController + //RESERVE_DEPRECATED_CLASSID (MotorJoint, 95) // Now used by Animator + RESERVE_DEPRECATED_CLASSID (Decal, 97) // Pre 1.0 + RESERVE_DEPRECATED_CLASSID (EulerRotationMotor, 139) + RESERVE_DEPRECATED_CLASSID (ParticleCloudColor, 103) // Pre 1.0 + RESERVE_DEPRECATED_CLASSID (TextScript, 105) + RESERVE_DEPRECATED_CLASSID (VertexProgram, 106) + RESERVE_DEPRECATED_CLASSID (FragmentProgram, 107) + RESERVE_DEPRECATED_CLASSID (GooStickyness, 151) + RESERVE_DEPRECATED_CLASSID (ClothAnimator, 99) // PRE 1.0 + RESERVE_DEPRECATED_CLASSID (PatchRenderer, 100) // PRE 1.0 + RESERVE_DEPRECATED_CLASSID (Stretcher, 101) // PRE 1.0 + RESERVE_DEPRECATED_CLASSID (AudioManager, 80) // -> changed id DO NOT REUSE + //RESERVE_DEPRECATED_CLASSID (AxisRenderer, 1008) // REMOVED in 2.0, now used by ComputeShaderImporter + RESERVE_DEPRECATED_CLASSID (BBoxRenderer, 1009) // REMOVED in 2.0 + RESERVE_DEPRECATED_CLASSID (CopyTransform, 1010) // REMOVED in 2.1 + //RESERVE_DEPRECATED_CLASSID (DotRenderer, 1011) // REMOVED in 2.0, now used by AvatarMask + RESERVE_DEPRECATED_CLASSID (SphereRenderer, 1012) // Not here anymore + RESERVE_DEPRECATED_CLASSID (WireRenderer, 1024) // Removed in 2.0 + RESERVE_DEPRECATED_CLASSID (AnimationManager, 71) // Removed in 4.3 +} + +void RegisterReservedClassIDs (ClassRegistrationContext& context) +{ + RESERVE_CLASSID (PreviewAssetType, 1108); + RESERVE_CLASSID (GUITransform, 187) + RESERVE_CLASSID (GUIButton, 188) + RESERVE_CLASSID (GUIGroup, 189) + RESERVE_CLASSID (GUIComponent, 190) + RESERVE_CLASSID (GUICanvas, 219) + RESERVE_CLASSID (GUIToggle, 200) + RESERVE_CLASSID (GUIImage, 201) + RESERVE_CLASSID (GUILabel, 202) + RESERVE_CLASSID (GUISlider, 211) + RESERVE_CLASSID (GUITextField, 204) + RESERVE_CLASSID (GUIKeyboardControl, 210) + RESERVE_CLASSID (InWorldGUI, 209) + RESERVE_CLASSID (GUICamera, 217) + RESERVE_CLASSID (TextureAtlas, 203) +} + +static void VerifyThatAllClassesHaveBeenRegistered(const RegisteredClassSet& explicitlyRegistered) +{ + const RegisteredClassSet& classes = GetVerifyClassRegistration (); + + for (RegisteredClassSet::const_iterator i=classes.begin();i != classes.end();++i) + { + if (explicitlyRegistered.count (*i) == 0) + { + FatalErrorString(Format("ClassID %d has not been registered but is included in the build. You must add the class to RegisterAllClasses.", *i)); + } + } + + Assert(classes == explicitlyRegistered); +} +#endif + +void RegisterAllClasses() +{ + ClassRegistrationContext context; +#if DEBUGMODE + RegisteredClassSet explicitlyRegistered; + context.explicitlyRegistered = &explicitlyRegistered; +#endif + + REGISTER_CLASS (GameObject) + REGISTER_CLASS (Component) + REGISTER_CLASS (LevelGameManager) + REGISTER_CLASS (Transform) + REGISTER_CLASS (TimeManager) + REGISTER_CLASS (GlobalGameManager) + REGISTER_CLASS (Behaviour) + REGISTER_CLASS (GameManager) + REGISTER_CLASS (ParticleAnimator) + REGISTER_CLASS (InputManager) + REGISTER_CLASS (EllipsoidParticleEmitter) + REGISTER_CLASS (Pipeline) + REGISTER_CLASS (EditorExtension) + REGISTER_CLASS (Camera) + REGISTER_CLASS (Material) + REGISTER_CLASS (Mesh) + REGISTER_CLASS (MeshRenderer) + REGISTER_CLASS (MeshFilter) + REGISTER_CLASS (Renderer) + REGISTER_CLASS (ParticleRenderer) + REGISTER_CLASS (Texture) + REGISTER_CLASS (Texture2D) + REGISTER_CLASS (SceneSettings) + REGISTER_CLASS (OcclusionPortal) + REGISTER_CLASS (Skybox) + REGISTER_CLASS (QualitySettings) + REGISTER_CLASS (Shader) + REGISTER_CLASS (TextAsset) + REGISTER_CLASS (ComputeShader) + REGISTER_CLASS (WorldParticleCollider) + REGISTER_CLASS (TagManager) + REGISTER_CLASS (RenderTexture) + REGISTER_CLASS (MeshParticleEmitter) + REGISTER_CLASS (ParticleEmitter) + REGISTER_CLASS (Cubemap) + REGISTER_CLASS (GUILayer) + REGISTER_CLASS (ScriptMapper) + REGISTER_CLASS (TrailRenderer) + REGISTER_CLASS (DelayedCallManager) + REGISTER_CLASS (TextMesh) + REGISTER_CLASS (Light) + REGISTER_CLASS (CGProgram) + REGISTER_CLASS (LightProbes) + REGISTER_CLASS (ResourceManager) + REGISTER_CLASS (Texture3D) + REGISTER_CLASS (Projector) + REGISTER_CLASS (LineRenderer) + REGISTER_CLASS (Flare) + REGISTER_CLASS (Halo) + REGISTER_CLASS (LensFlare) + REGISTER_CLASS (FlareLayer) + REGISTER_CLASS (HaloLayer) + REGISTER_CLASS (HaloManager) + REGISTER_CLASS (PreloadData) + REGISTER_CLASS (LightmapSettings) + REGISTER_CLASS (RenderSettings) + REGISTER_CLASS (NamedObject) + REGISTER_CLASS (GUIText) + REGISTER_CLASS (GUITexture) + REGISTER_CLASS (Font) + REGISTER_CLASS (GUIElement) + REGISTER_CLASS (SkinnedMeshRenderer) + REGISTER_CLASS (BuildSettings) + REGISTER_CLASS (AssetBundle) + REGISTER_CLASS (OcclusionArea) + REGISTER_CLASS (ParticleSystem) + REGISTER_CLASS (ParticleSystemRenderer) + REGISTER_CLASS (GraphicsSettings) + REGISTER_CLASS (PlayerSettings) + REGISTER_CLASS (SubstanceArchive) + REGISTER_CLASS (ProceduralMaterial) + REGISTER_CLASS (ProceduralTexture) + REGISTER_CLASS (LODGroup) + REGISTER_CLASS (LightProbeGroup) + REGISTER_CLASS (WindZone) + +#if ENABLE_SCRIPTING + REGISTER_CLASS (MonoScript) + REGISTER_CLASS (MonoManager) + REGISTER_CLASS (MonoBehaviour) +#endif + +#if ENABLE_NETWORK + REGISTER_CLASS (NetworkView) + REGISTER_CLASS (NetworkManager) + REGISTER_CLASS (MasterServerInterface) +#endif + +#if ENABLE_SPRITES + REGISTER_CLASS (SpriteRenderer) + REGISTER_CLASS (Sprite) + #if UNITY_EDITOR + REGISTER_CLASS (CachedSpriteAtlas) + #endif +#endif + + RegisterAllAvailableModuleClasses (context); + + // Editor Only classes following: +#if UNITY_EDITOR + REGISTER_CLASS (EditorSettings) + REGISTER_CLASS (EditorUserSettings) + REGISTER_CLASS (Prefab) + REGISTER_CLASS (EditorExtensionImpl) + REGISTER_CLASS (AssetImporter) + REGISTER_CLASS (AssetDatabase) + REGISTER_CLASS (Mesh3DSImporter) + REGISTER_CLASS (TextureImporter) + REGISTER_CLASS (ShaderImporter) + REGISTER_CLASS (ComputeShaderImporter) + REGISTER_CLASS (AudioImporter) + REGISTER_CLASS (GUIDSerializer) + REGISTER_CLASS (AssetMetaData) + REGISTER_CLASS (DefaultAsset) + REGISTER_CLASS (DefaultImporter) + REGISTER_CLASS (TextScriptImporter) + REGISTER_CLASS (SceneAsset) + REGISTER_CLASS (NativeFormatImporter) + REGISTER_CLASS (MonoImporter) + REGISTER_CLASS (MonoAssemblyImporter) + REGISTER_CLASS (AssetServerCache) + REGISTER_CLASS (LibraryAssetImporter) + REGISTER_CLASS (ModelImporter) + REGISTER_CLASS (FBXImporter) + REGISTER_CLASS (TrueTypeFontImporter) + REGISTER_CLASS (EditorBuildSettings) + REGISTER_CLASS (DDSImporter) + REGISTER_CLASS (InspectorExpandedState) + REGISTER_CLASS (AnnotationManager) + REGISTER_CLASS (EditorUserBuildSettings) + REGISTER_CLASS (PVRImporter) + REGISTER_CLASS (HierarchyState) + REGISTER_CLASS (Transition) + REGISTER_CLASS (State) + REGISTER_CLASS (HumanTemplate) + REGISTER_CLASS (StateMachine) + REGISTER_CLASS (AvatarMask) + REGISTER_CLASS (BlendTree) + REGISTER_CLASS (SubstanceImporter) + +#if !UNITY_LINUX + REGISTER_CLASS (MovieImporter) +#endif +#endif // UNITY_EDITOR + +#if DEBUGMODE + VerifyThatAllClassesHaveBeenRegistered(explicitlyRegistered); + RegisterDeprecatedClassIDs(context); + RegisterReservedClassIDs(context); +#endif +} diff --git a/Runtime/BaseClasses/ClassRegistration.h b/Runtime/BaseClasses/ClassRegistration.h new file mode 100644 index 0000000..cf016db --- /dev/null +++ b/Runtime/BaseClasses/ClassRegistration.h @@ -0,0 +1,22 @@ +#pragma once +#include "ClassIDs.h" + +struct ClassRegistrationContext +{ + void* explicitlyRegistered; +}; + +#if DEBUGMODE +#define REGISTER_CLASS(x) \ +{ \ + extern void RegisterClass_##x(); \ + RegisterClass_##x(); \ + ValidateRegisteredClassID(context, ClassID(x), #x); \ +} +#else +#define REGISTER_CLASS(x) \ +{ extern void RegisterClass_##x(); RegisterClass_##x(); } +#endif + + +EXPORT_COREMODULE void ValidateRegisteredClassID (ClassRegistrationContext& context, int classID, const char* className); diff --git a/Runtime/BaseClasses/CleanupManager.cpp b/Runtime/BaseClasses/CleanupManager.cpp new file mode 100644 index 0000000..7adb1e7 --- /dev/null +++ b/Runtime/BaseClasses/CleanupManager.cpp @@ -0,0 +1,65 @@ +#include "UnityPrefix.h" +#if UNITY_EDITOR +#include "CleanupManager.h" +#include "Runtime/Misc/GameObjectUtility.h" +#include "Runtime/Graphics/Transform.h" +#include <algorithm> + +using namespace std; + +void CleanupManager::MarkForDeletion( PPtr<Unity::Component> comp, std::string const& reason ) +{ + // Ignore component that is already marked for deletion + list<struct MarkedComponent>::iterator a = std::find(m_markedComponents.begin(), m_markedComponents.end(), static_cast<Unity::Component*>(comp)); + if (a != m_markedComponents.end()) + return; + + struct MarkedComponent marker; + m_markedComponents.push_back (marker); + m_markedComponents.back ().component = comp; + m_markedComponents.back ().reason = reason; +} + +void CleanupManager::Flush() +{ + while (m_markedComponents.size() > 0) + { + struct MarkedComponent& marked_component = m_markedComponents.front (); + PPtr<Unity::Component> comp = marked_component.component; + + Unity::Component* compPtr = comp; + if (compPtr) + { + LogString(Format("%s component deleted: %s", comp->GetClassName ().c_str (), marked_component.reason.c_str())); + if (marked_component.component->GetGameObjectPtr () != NULL) + { + DestroyObjectHighLevel (comp); + } + else + { + // if the component is a transform remove the references + if (comp->GetClassID () == ClassID(Transform)) + { + DestroyTransformComponentAndChildHierarchy(static_cast<Transform&>(*comp)); + } + + DestroySingleObject (comp); + } + } + + m_markedComponents.pop_front (); + } +} + +static CleanupManager* singleton = NULL; +CleanupManager& GetCleanupManager () +{ + if (singleton == NULL) + { + singleton = new CleanupManager(); + } + + return *singleton; +} + +#endif
\ No newline at end of file diff --git a/Runtime/BaseClasses/CleanupManager.h b/Runtime/BaseClasses/CleanupManager.h new file mode 100644 index 0000000..23c76c8 --- /dev/null +++ b/Runtime/BaseClasses/CleanupManager.h @@ -0,0 +1,37 @@ +#pragma once + +#if UNITY_EDITOR + +#include "Runtime/BaseClasses/GameObject.h" + +#include <string> +#include <list> + +class CleanupManager +{ +private: + struct MarkedComponent { + PPtr<Unity::Component> component; + std::string reason; + + bool operator==(PPtr<Unity::Component> const& comp) + { + return this->component == comp; + } + }; + +public: + CleanupManager() {} + + void MarkForDeletion(PPtr<Unity::Component> comp, std::string const& reason); + void Flush(); + + static void DidDestroyObjectNotification (Object* comp, void* userData); + +private: + std::list<struct MarkedComponent> m_markedComponents; +}; + +CleanupManager& GetCleanupManager (); + +#endif
\ No newline at end of file diff --git a/Runtime/BaseClasses/Cursor.cpp b/Runtime/BaseClasses/Cursor.cpp new file mode 100644 index 0000000..3fb45ca --- /dev/null +++ b/Runtime/BaseClasses/Cursor.cpp @@ -0,0 +1,152 @@ +#include "Cursor.h" + +#include "Runtime/GfxDevice/GfxDevice.h" +#include "Runtime/Graphics/RenderTexture.h" +#include "Runtime/Graphics/RenderBufferManager.h" +#include "Runtime/Camera/ImageFilters.h" +#include "Runtime/Graphics/ScreenManager.h" +#include "Runtime/Graphics/Image.h" +#include "Runtime/Camera/RenderManager.h" +#include "Runtime/Camera/CameraUtil.h" +#include "Runtime/Camera/RenderLayers/GUITexture.h" +#include "Runtime/Input/InputManager.h" +#include "Runtime/Misc/PlayerSettings.h" + +namespace Cursors +{ +void RenderSoftwareCursor () +{ + Texture2D* softCursor = GetSoftwareCursor(); + if (softCursor && GetScreenManager().GetShowCursor()) + { + DeviceMVPMatricesState preserveMVP; + SetupPixelCorrectCoordinates(); + + Vector2f pos = GetInputManager().GetMousePosition(); + Vector2f hotSpotOffset = GetCursorHotspot(); + + pos.x -= hotSpotOffset.x; + pos.y += hotSpotOffset.y; + + // the color is set to 0.5f because the gui-texture shader multiplies the vertex color by 2 for some reason + // pos is floored to match the behaviour of the hardware cursors + DrawGUITexture (Rectf ((int)pos.x, (int)pos.y, softCursor->GetGLWidth(), -softCursor->GetGLHeight()), softCursor, ColorRGBAf(0.5f, 0.5f, 0.5f, 0.5f)); + } +} + +#if !PLATFORM_SUPPORTS_HARDWARE_CURSORS + + +typedef UnityCursor<int> SoftCursor; +typedef CursorManager<SoftCursor> SoftCursorManager; + +template<> SoftCursorManager* SoftCursorManager::s_CursorManager = NULL; + +static SoftCursor GenerateCursor (Texture2D* texture, Vector2f hotSpot) +{ + // if this is null you are doing it wrong + assert(texture); + + SoftCursor c; + c.sCursor = texture; + c.hotspot = hotSpot; + return c; +} + +void SetCursor (Texture2D* texture, Vector2f hotSpot, CursorMode forceHardware) +{ + SoftCursorManager& manager = SoftCursorManager::Instance(); + if (!texture) + { + manager.m_CurrentCursor = manager.m_DefaultCursor; + return; + } + + // try and find the cursor in the cache + SoftCursorManager::CursorCache::iterator found = manager.m_CursorCache.find (texture->GetTextureID()); + SoftCursor cursorToSet; + bool shouldGenerateCursor = true; + if (manager.m_CursorCache.end() != found) + { + // see if old hotspot + // is the same as the one requested now... + // if it's not then delete the old cursor and recreate it! + cursorToSet = found->second; + + if (!CompareApproximately (hotSpot.x, cursorToSet.hotspot.x) + || !CompareApproximately (hotSpot.y, cursorToSet.hotspot.y)) + { + manager.m_CursorCache.erase(found); + } + else + { + shouldGenerateCursor = false; + } + } + + if (shouldGenerateCursor) + { + cursorToSet = GenerateCursor (texture, hotSpot); + manager.m_CursorCache[texture->GetTextureID()] = cursorToSet; + } + + manager.m_CurrentCursor = cursorToSet; +} + +Texture2D* GetSoftwareCursor() +{ + return SoftCursorManager::Instance().m_CurrentCursor.sCursor; +} + +Vector2f GetCursorHotspot() +{ + return SoftCursorManager::Instance().m_CurrentCursor.hotspot; +} + +void InitializeCursors(Texture2D* defaultCursorTexture, Vector2f defaultCursorHotSpot) +{ + SoftCursorManager& manager = SoftCursorManager::Instance (); + if (defaultCursorTexture) + { + manager.m_DefaultCursor = GenerateCursor (defaultCursorTexture, defaultCursorHotSpot); + manager.m_CurrentCursor = manager.m_DefaultCursor; + manager.m_UsingBuiltinDefaultCursor = true; + } +} + +void CleanupCursors() +{ + SoftCursorManager::Instance().Cleanup (); +} + +// needed for windows linkage with hardware cursors disabled +#if UNITY_WIN +void ResetCursor () +{} + +bool HandleMouseCursor (UINT message, LPARAM lParam) +{ + return false; +} + +HCURSOR GetHardwareCursor () +{ + return NULL; +} +#endif + +// needed for osx linkage with hardware cursors disabled +#if UNITY_OSX +void ResetCursor () +{} + +NSCursor* GetCurrentCursor () +{ + return NULL; +} +#endif + +#endif //!PLATFORM_SUPPORTS_HARDWARE_CURSORS + +}; //namespace + diff --git a/Runtime/BaseClasses/Cursor.h b/Runtime/BaseClasses/Cursor.h new file mode 100644 index 0000000..b0072fb --- /dev/null +++ b/Runtime/BaseClasses/Cursor.h @@ -0,0 +1,105 @@ +#pragma once + +#include "UnityPrefix.h" +#include "Runtime/Graphics/Texture2D.h" +#include "Runtime/Math/Vector2.h" + +#include <string> + +#define PLATFORM_SUPPORTS_HARDWARE_CURSORS (!UNITY_PEPPER && ((UNITY_WIN && !UNITY_WINRT) || UNITY_OSX || UNITY_LINUX || UNITY_FLASH)) + +enum CursorMode +{ + kAutoHardwareCursor = 0, + kHardwareCursorOff = 1 +}; + +#if UNITY_OSX + #ifdef __OBJC__ + @class NSCursor; + #else + typedef struct objc_object NSCursor; + #endif +#endif + +namespace Cursors +{ + +template <typename T> +struct UnityCursor +{ + UnityCursor () + { + hCursor = NULL; + sCursor = NULL; + } + T hCursor; + PPtr<Texture2D> sCursor; + Vector2f hotspot; + + typedef T HCursorType; +}; + +template <typename T> +struct CursorManager +{ + T m_DefaultCursor; + T m_CurrentCursor; + + bool m_UsingBuiltinDefaultCursor; + + typedef std::map<TextureID, T > CursorCache; + CursorCache m_CursorCache; + + Texture2D* GetSoftwareCursor () + { + return m_CurrentCursor.sCursor; + } + + Vector2f GetCursorHotspot () + { + return m_CurrentCursor.hotspot; + } + + typename T::HCursorType GetHardwareCursor () + { + return m_CurrentCursor.hCursor; + } + + static CursorManager<T>* s_CursorManager; + static CursorManager<T>& Instance () + { + if (s_CursorManager == NULL) + { + s_CursorManager = new CursorManager<T>(); + } + + return *s_CursorManager; + } + + static void Cleanup () + { + delete s_CursorManager; + s_CursorManager = NULL; + } +}; + +void SetCursor (Texture2D* texture, Vector2f hotSpot, CursorMode forceHardware); +void RenderSoftwareCursor (); +Texture2D* GetSoftwareCursor (); +Vector2f GetCursorHotspot (); +void InitializeCursors (Texture2D* defaultCursorTexture, Vector2f defaultCursorHotSpot); +void CleanupCursors (); + +#if UNITY_WIN +void ResetCursor (); +// returns true if the event is 'handled' WM_SETCURSOR in this case +bool HandleMouseCursor (UINT message, LPARAM lParam); +HCURSOR GetHardwareCursor (); +#endif + +#if UNITY_OSX +void ResetCursor (); +NSCursor* GetHardwareCursor (); +#endif +}; diff --git a/Runtime/BaseClasses/EditorExtension.cpp b/Runtime/BaseClasses/EditorExtension.cpp new file mode 100644 index 0000000..a55c92b --- /dev/null +++ b/Runtime/BaseClasses/EditorExtension.cpp @@ -0,0 +1,95 @@ +#include "UnityPrefix.h" +#include "EditorExtension.h" + +#if !GAMERELEASE + +#include "Editor/Src/EditorExtensionImpl.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/TransferUtility.h" +#include "Runtime/Serialize/SerializationMetaFlags.h" +#include "Runtime/Utilities/dynamic_bitset.h" +#include "Editor/Src/Prefabs/Prefab.h" +#include "Editor/Src/Prefabs/PrefabBackwardsCompatibility.h" + +EditorExtension::EditorExtension (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ +} + +EditorExtension::~EditorExtension () +{ + Assert(m_DeprecatedExtensionPtr.GetInstanceID() == 0); +} + +bool EditorExtension::IsPrefabParent () const +{ + Prefab* prefab = m_Prefab; + return prefab != NULL && prefab->IsPrefabParent(); +} + +template<class TransferFunction> +void EditorExtension::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + if (!transfer.IsSerializingForGameRelease ()) + { + if (SerializePrefabIgnoreProperties(transfer)) + { + transfer.Transfer (m_PrefabParentObject, "m_PrefabParentObject", kHideInEditorMask | kIgnoreWithInspectorUndoMask); + transfer.Transfer (m_Prefab, "m_PrefabInternal", kHideInEditorMask | kIgnoreWithInspectorUndoMask); + } + + if (transfer.IsReadingBackwardsCompatible()) + transfer.Transfer (m_DeprecatedExtensionPtr, "m_ExtensionPtr", kHideInEditorMask | kIgnoreWithInspectorUndoMask); + } +} + +PPtr<EditorExtensionImpl> GetDeprecatedExtensionPtrIfExists (const Object& o) +{ + EditorExtension* extension = dynamic_pptr_cast<EditorExtension*> (&o); + if (extension) + return extension->m_DeprecatedExtensionPtr; + else + return NULL; +} + +void EditorExtension::PatchPrefabBackwardsCompatibility () +{ + if (m_DeprecatedExtensionPtr.IsValid ()) + { + m_Prefab = m_DeprecatedExtensionPtr->m_DataTemplate; + if (m_DeprecatedExtensionPtr->m_TemplateFather.IsValid()) + m_PrefabParentObject = m_DeprecatedExtensionPtr->m_TemplateFather; + + if (m_Prefab.IsValid() && m_PrefabParentObject.IsValid() && m_DeprecatedExtensionPtr->m_Object == PPtr<EditorExtension> (this)) + ReadOldPrefabFormat (*m_Prefab, *this, *m_PrefabParentObject, *m_DeprecatedExtensionPtr); + + ///@TODO: DESTROY!!! + /// DestroyObject(m_DeprecatedExtensionPtr) + } + m_DeprecatedExtensionPtr = NULL; + +} + + +void EditorExtension::AwakeFromLoad (AwakeFromLoadMode mode) +{ + Super::AwakeFromLoad(mode); + PatchPrefabBackwardsCompatibility (); +} + +IMPLEMENT_OBJECT_SERIALIZE (EditorExtension) +IMPLEMENT_CLASS (EditorExtension) +INSTANTIATE_TEMPLATE_TRANSFER (EditorExtension) + +#else + +IMPLEMENT_CLASS (EditorExtension) + +EditorExtension::~EditorExtension () +{ + +} + +#endif + diff --git a/Runtime/BaseClasses/EditorExtension.h b/Runtime/BaseClasses/EditorExtension.h new file mode 100644 index 0000000..5e2830a --- /dev/null +++ b/Runtime/BaseClasses/EditorExtension.h @@ -0,0 +1,58 @@ +#ifndef EDITOREXTENSION_H +#define EDITOREXTENSION_H + +#include "BaseObject.h" +class TypeTree; +class Prefab; +class EditorExtensionImpl; + +#if UNITY_EDITOR + +class EXPORT_COREMODULE EditorExtension : public Object +{ + public: + + PPtr<EditorExtension> m_PrefabParentObject; + PPtr<Prefab> m_Prefab; + + PPtr<EditorExtensionImpl> m_DeprecatedExtensionPtr; + + REGISTER_DERIVED_ABSTRACT_CLASS (EditorExtension, Object) + DECLARE_OBJECT_SERIALIZE (EditorExtension) + + EditorExtension (MemLabelId label, ObjectCreationMode mode); + // ~EditorExtension (); declared-by-macro + + friend PPtr<EditorExtensionImpl> GetDeprecatedExtensionPtrIfExists (const Object& o); + + virtual bool IsPrefabParent () const; + + PPtr<Prefab> GetPrefab () { return m_Prefab; } + PPtr<EditorExtension> GetPrefabParentObject () { return m_PrefabParentObject; } + + virtual void AwakeFromLoad (AwakeFromLoadMode mode); + + void PatchPrefabBackwardsCompatibility (); + + + //std::string ExtractDeprecatedNameString (); +}; + +#else + +class EXPORT_COREMODULE EditorExtension : public Object +{ + public: + + EditorExtension (MemLabelId label, ObjectCreationMode mode) : Super(label, mode) {} + // virtual ~EditorExtension (); declared-by-macro + + REGISTER_DERIVED_CLASS (EditorExtension, Object) + + virtual bool IsPrefabParent () const { return false; } +}; + + +#endif + +#endif diff --git a/Runtime/BaseClasses/EventIDs.h b/Runtime/BaseClasses/EventIDs.h new file mode 100644 index 0000000..a37acdb --- /dev/null +++ b/Runtime/BaseClasses/EventIDs.h @@ -0,0 +1,9 @@ +#pragma once + +enum EventIDs +{ + kBecameVisibleEvent, + kBecameInvisibleEvent, + kWillDestroyEvent, + kAnimatorClearEvent, +};
\ No newline at end of file diff --git a/Runtime/BaseClasses/EventManager.cpp b/Runtime/BaseClasses/EventManager.cpp new file mode 100644 index 0000000..365faf0 --- /dev/null +++ b/Runtime/BaseClasses/EventManager.cpp @@ -0,0 +1,216 @@ +#include "UnityPrefix.h" +#include "EventManager.h" +#include "Runtime/Utilities/InitializeAndCleanup.h" + +////@TODO: Assert on recursive calls... + +EventManager* EventManager::s_Instance = NULL; + +EventManager& GetEventManager () +{ + return *EventManager::s_Instance; +} + +void EventManager::StaticInitialize() +{ + s_Instance = UNITY_NEW(EventManager,kMemManager); +} + +void EventManager::StaticDestroy() +{ + UNITY_DELETE(s_Instance, kMemManager); +} + +static RegisterRuntimeInitializeAndCleanup s_EventManagerCallbacks(EventManager::StaticInitialize, EventManager::StaticDestroy); + +EventManager::EventManager () +: m_EventPool (false, "EventManager", sizeof(EventEntry), 1024 * 4) +#if DEBUGMODE +, m_InvokingEventList(NULL) +, m_InvokingEventActiveNode(NULL) +#endif +{ +} + +EventManager::EventIndex EventManager::AddEvent (EventCallback* callback, void* userData, EventIndex previousIndex) +{ + if (previousIndex == NULL) + { + EventIndex event = (EventIndex)m_EventPool.Allocate(); + event->userData = userData; + event->callback = callback; + event->next = NULL; + + return event; + } + else + { + EventIndex event = (EventIndex)m_EventPool.Allocate(); + event->callback = callback; + event->userData = userData; + event->next = previousIndex; + + return event; + } +} + +/// Removes all events with the event index. +void EventManager::RemoveEvent (EventIndex index) +{ + #if DEBUGMODE + // We can not delete the event which we are currently invoking + Assert (m_InvokingEventList != index); + #endif + + while (index != NULL) + { + EventIndex next = index->next; + m_EventPool.Deallocate(index); + index = next; + } +} + +bool EventManager::HasEvent (const EventIndex index, EventCallback* callback, const void* userData) +{ + EventIndex curIndex = index; + while (curIndex != NULL) + { + if (curIndex->callback == callback && curIndex->userData == userData) + return true; + + curIndex = curIndex->next; + } + return false; +} + + +/// Removes an event with a specific callback & userData +/// Returns the new event or null if no events in that index exist anymore. +EventManager::EventIndex EventManager::RemoveEvent (EventIndex index, EventCallback* callback, void* userData) +{ + EventIndex previousIndex = NULL; + EventIndex curEvent = index; + while (curEvent != NULL) + { + if (curEvent->callback == callback && curEvent->userData == userData) + { + // While invoking we are allowed to remove the event being invoked itself but no other events on the same chain. + #if DEBUGMODE + Assert (m_InvokingEventList != index || m_InvokingEventActiveNode == curEvent); + #endif + + EventIndex nextEvent = curEvent->next; + m_EventPool.Deallocate(curEvent); + + if (previousIndex) + previousIndex->next = nextEvent; + + if (index == curEvent) + return nextEvent; + else + return index; + } + + previousIndex = curEvent; + + curEvent = curEvent->next; + } + + return index; +} + +void EventManager::InvokeEvent (EventIndex index, void* senderUserData, int eventType) +{ + #if DEBUGMODE + GetEventManager().m_InvokingEventList = index; + #endif + + while (index != NULL) + { + EventIndex next = index->next; + + #if DEBUGMODE + GetEventManager().m_InvokingEventActiveNode = index; + #endif + + index->callback(index->userData, senderUserData, eventType); + index = next; + } + + #if DEBUGMODE + GetEventManager().m_InvokingEventList = NULL; + GetEventManager().m_InvokingEventActiveNode = NULL; + #endif +} + + +#if ENABLE_UNIT_TESTS + +#include "External/UnitTest++/src/UnitTest++.h" + + +struct LoggingCounter +{ + int counter; +}; + +void LoggingCallback (void* userData, void* sender, int type) +{ + LoggingCounter* logging = (LoggingCounter*)userData; + + logging->counter++; +} + +SUITE (EventsManagerTest) +{ +TEST (EventsManager_EventsSimple) +{ + EventManager manager; + + LoggingCounter counter1; + counter1.counter = 0; + + EventManager::EventIndex index = manager.AddEvent (LoggingCallback, &counter1, NULL); + manager.InvokeEvent(index, NULL, 0); + CHECK_EQUAL(1, counter1.counter); +} + +// Test chaining (But not duplicating) +TEST (EventsManager_EventsChaining) +{ + EventManager manager; + + LoggingCounter counter1; + counter1.counter = 0; + LoggingCounter counter2; + counter2.counter = 0; + LoggingCounter counter3; + counter3.counter = 0; + + EventManager::EventIndex index; + + // Add chained event (add one duplicate which should not be added or invoked) + index = manager.AddEvent (LoggingCallback, &counter1, NULL); + index = manager.AddEvent (LoggingCallback, &counter2, index); + index = manager.AddEvent (LoggingCallback, &counter3, index); + + manager.InvokeEvent(index, NULL, 0); + CHECK_EQUAL(1, counter1.counter); + CHECK_EQUAL(1, counter2.counter); + CHECK_EQUAL(1, counter3.counter); + + // Remove 1 chained event + index = manager.RemoveEvent (index, LoggingCallback, &counter2); + counter1.counter = 0; + counter2.counter = 0; + counter3.counter = 0; + + manager.InvokeEvent(index, NULL, 0); + CHECK_EQUAL(1, counter1.counter); + CHECK_EQUAL(0, counter2.counter); + CHECK_EQUAL(1, counter3.counter); +} +} + +#endif + diff --git a/Runtime/BaseClasses/EventManager.h b/Runtime/BaseClasses/EventManager.h new file mode 100644 index 0000000..2f0b813 --- /dev/null +++ b/Runtime/BaseClasses/EventManager.h @@ -0,0 +1,59 @@ +#pragma once + +#include "Runtime/Utilities/MemoryPool.h" + +typedef void EventCallback (void* userData, void* sender, int eventType); + + +// Small event entry. Keep this tight. +struct EventEntry +{ + void* userData; + EventEntry* next; + EventCallback* callback; +}; + +class EventManager +{ +public: + typedef EventEntry* EventIndex; + +private: + ////@TODO: Memory pool has a minimum size of 32 bytes. This one fits in 12. WTF??? + MemoryPool m_EventPool; + + static EventManager* s_Instance; + friend EventManager& GetEventManager (); + + #if DEBUGMODE + EventIndex m_InvokingEventList; + EventIndex m_InvokingEventActiveNode; + #endif + +public: + EventManager (); + + static void StaticInitialize (); + static void StaticDestroy (); + + /// Adds an event + /// If there is already a previous event registered, it will chain them. + /// The reference to the event is the returned eventIndex + EventIndex AddEvent (EventCallback* callback, void* userData, EventIndex previousIndex); + + /// Removes all events with the event index. + void RemoveEvent (EventIndex index); + + /// Removes an event with a specific callback & userData + /// Returns the new event or null if no events in that index exist anymore. + /// AddEvent and RemoveEvent calls must be balanced. + EventIndex RemoveEvent (EventIndex index, EventCallback* callback, void* userData); + + /// Does the event with that specific callback and userData exist? + static bool HasEvent (const EventIndex index, EventCallback* callback, const void* userData); + + static void InvokeEvent (EventIndex index, void* sender, int eventType); +}; + +EventManager& GetEventManager (); + diff --git a/Runtime/BaseClasses/GameManager.cpp b/Runtime/BaseClasses/GameManager.cpp new file mode 100644 index 0000000..0003b1f --- /dev/null +++ b/Runtime/BaseClasses/GameManager.cpp @@ -0,0 +1,58 @@ +#include "UnityPrefix.h" +#include "GameManager.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "ManagerContext.h" + +GameManager::~GameManager () +{ + for (int i=0;i<ManagerContext::kManagerCount;i++) + { + if (GetManagerContext().m_Managers[i] == this) + SetManagerPtrInContext(i, NULL); + } +} + +LevelGameManager::~LevelGameManager () { } +GlobalGameManager::~GlobalGameManager () { } + +template<class TransferFunction> +void LevelGameManager::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); +} + +template<class TransferFunction> +void GlobalGameManager::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); +} + +char const* GlobalGameManager::GetName () const +{ + return GetClassName ().c_str (); +} + +GameManager* GetGameManagerIfExists (int index) +{ + GameManager* manager = static_cast<GameManager*> (GetManagerPtrFromContext(index)); + Assert(manager == dynamic_pptr_cast<GameManager*> (GetManagerPtrFromContext(index))); + return manager; +} + +LevelGameManager::LevelGameManager(MemLabelId label, ObjectCreationMode mode) : Super(label, mode) +{ } + +GlobalGameManager::GlobalGameManager(MemLabelId label, ObjectCreationMode mode) : Super(label, mode) +{ } + + + +IMPLEMENT_CLASS (LevelGameManager) +IMPLEMENT_CLASS (GlobalGameManager) +IMPLEMENT_CLASS (GameManager) + +IMPLEMENT_OBJECT_SERIALIZE (LevelGameManager) +IMPLEMENT_OBJECT_SERIALIZE (GlobalGameManager) + +INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED (LevelGameManager) +INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED (GlobalGameManager) diff --git a/Runtime/BaseClasses/GameManager.h b/Runtime/BaseClasses/GameManager.h new file mode 100644 index 0000000..8d194c3 --- /dev/null +++ b/Runtime/BaseClasses/GameManager.h @@ -0,0 +1,69 @@ +#ifndef GAMEMANAGER_H +#define GAMEMANAGER_H + +#include "EditorExtension.h" + +/// Any game manager (eg. AudioManager, dynamicsmanager) that needs serialization +/// has to derive from either LevelGameManager or GlobalGameManager. +/// Every level contains its own GameManager for that Level (eg. Scene, PhysicsManager) +/// LevelGameManagers are destroyed and reloaded from the new scene when loading a new scene. +/// GlobalGameManagers are singletons and loaded on +/// startup of the gameplayer/editor (eg. InputManager, TagManager) + +class EXPORT_COREMODULE GameManager : public Object +{ + public: + + REGISTER_DERIVED_ABSTRACT_CLASS (GameManager, Object) + GameManager(MemLabelId label, ObjectCreationMode mode) : Super(label, mode) { } +// virtual ~GameManager (); + + ///@TODO: Get rid of this. I am not sure why this is not just done in the destructor / cleanup class + virtual void NetworkOnApplicationQuit () { AssertString("not implemented"); } + virtual void NetworkUpdate () { AssertString("not implemented"); } +}; + + +class EXPORT_COREMODULE LevelGameManager : public GameManager +{ + public: + + virtual char const* GetName () const { return GetClassName().c_str (); } + + REGISTER_DERIVED_ABSTRACT_CLASS (LevelGameManager, GameManager) + DECLARE_OBJECT_SERIALIZE (GameManager) + + LevelGameManager(MemLabelId label, ObjectCreationMode mode); + +// virtual ~LevelGameManager (); +}; + + +class EXPORT_COREMODULE GlobalGameManager : public GameManager +{ + public: + + REGISTER_DERIVED_ABSTRACT_CLASS (GlobalGameManager, GameManager) + DECLARE_OBJECT_SERIALIZE (GlobalGameManager) + + GlobalGameManager(MemLabelId label, ObjectCreationMode mode); + +// virtual ~GlobalGameManager (); + + virtual char const* GetName () const; +}; + +GameManager* GetGameManagerIfExists (int index); + +inline GameManager* CreateGameManager (int classID) +{ + Object* o = Object::Produce (classID); + o->Reset (); + o->AwakeFromLoad(kDefaultAwakeFromLoad); + o->SetNameCpp (Object::ClassIDToString (classID)); + return static_cast<GameManager*> (o); +} + +#define CALL_MANAGER_IF_EXISTS(x,func) { GameManager* _manager = GetGameManagerIfExists(x); if (_manager) _manager->func; } + +#endif diff --git a/Runtime/BaseClasses/GameObject.cpp b/Runtime/BaseClasses/GameObject.cpp new file mode 100644 index 0000000..833052a --- /dev/null +++ b/Runtime/BaseClasses/GameObject.cpp @@ -0,0 +1,1189 @@ +#include "UnityPrefix.h" +#include "GameObject.h" +#include "CleanupManager.h" +#include "Runtime/Serialize/AwakeFromLoadQueue.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/SerializationMetaFlags.h" +#include "Tags.h" +#include "MessageHandler.h" +#include "Runtime/Misc/ReproductionLog.h" +#include "Runtime/Utilities/Utility.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Graphics/Texture2D.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Misc/GameObjectUtility.h" +#include "Runtime/Misc/ComponentRequirement.h" +#include "Runtime/Mono/MonoBehaviour.h" +#include "Runtime/Containers/ConstantStringSerialization.h" +#include "Runtime/Profiler/Profiler.h" +#if UNITY_EDITOR +#include "Editor/Src/BuildPipeline/BuildTargetPlatformSpecific.h" +#include "Editor/Src/Utility/StaticEditorFlags.h" +#endif +#if UNITY_WII +#include <rvlaux/clib.h> +#endif + +using namespace std; + +namespace Unity +{ + +PROFILER_INFORMATION (gActivateGameObjectProfiler, "GameObject.Activate", kProfilerScripts) +PROFILER_INFORMATION (gDeactivateGameObjectProfiler, "GameObject.Deactivate", kProfilerScripts) + +Unity::GameObject::DestroyGOCallbackFunction* Unity::GameObject::s_GameObjectDestroyedCallback = NULL; +Unity::GameObject::SetGONameFunction* Unity::GameObject::s_SetGONameCallback = NULL; +MessageForwarders* Unity::GameObject::s_RegisteredMessageForwarders = NULL; +MessageHandler* Unity::GameObject::s_MessageHandler = NULL; + +GameObject::GameObject (MemLabelId label, ObjectCreationMode mode) + : Super(label, mode), +// m_Component (GameObject::Container::allocator_type (*baseAllocator)), + m_ActiveGONode (this) +{ + m_SupportedMessages = 0; + m_IsDestroying = false; + m_IsActivating = false; + m_Tag = 0; + m_IsActive = false; + m_IsActiveCached = -1; + + #if UNITY_EDITOR + m_IsOldVersion = false; + m_StaticEditorFlags = 0; + m_IsMarkedVisible = kSelfVisible; + #endif +} + +void GameObject::Reset () +{ + Super::Reset (); + m_Layer = kDefaultLayer; + m_Tag = 0; + #if UNITY_EDITOR + m_StaticEditorFlags = 0; + m_TagString = TagToString (m_Tag); + m_NavMeshLayer = 0; + #endif +} + +GameObject::~GameObject () +{ + Assert(!m_ActiveGONode.IsInList()); +} + +void GameObject::WillDestroyGameObject () +{ + Assert(!m_IsDestroying); + m_IsDestroying = true; + + // Find a component with the requested ID + Container::const_iterator i; + Container::const_iterator end = m_Component.end (); + for (i=m_Component.begin ();i != end; ++i) + { + Component& com = *i->second; + com.WillDestroyComponent(); + } +} + +void GameObject::AwakeFromLoad(AwakeFromLoadMode awakeMode) +{ + #if SUPPORT_LOG_ORDER_TRACE + if (IsActive() && RunningReproduction()) + { + if (SUPPORT_LOG_ORDER_TRACE == 2) + { + LogString(Format("AwakeFromLoad %s (%s) [%d]", GetName(), GetClassName().c_str(), GetInstanceID())); + } + else + { + LogString(Format("AwakeFromLoad %s (%s)", GetName(), GetClassName().c_str())); + } + } + #endif + + Super::AwakeFromLoad (awakeMode); + SetSupportedMessagesDirty (); + UpdateActiveGONode (); + + if (s_SetGONameCallback) + s_SetGONameCallback(this); + + #if UNITY_EDITOR + // When we are modifying the game object active state from the inspector + // We need to Activate / Deactivate the relevant components + // This never happens in the player. + if (awakeMode == kDefaultAwakeFromLoad) + ActivateAwakeRecursively(); + #endif + +} + +int GameObject::CountDerivedComponents (int compareClassID)const +{ + int count = 0; + Container::const_iterator i; + for (i=m_Component.begin ();i != m_Component.end (); ++i) + count += Object::IsDerivedFromClassID (i->first, compareClassID); + return count; +} + + +Component* GameObject::FindConflictingComponentPtr (int classID) const +{ + const vector_set<int>& conflicts = FindConflictingComponents(classID); + if (conflicts.empty()) + return NULL; + + for (Container::const_iterator i = m_Component.begin(); i != m_Component.end(); ++i) + { + for (vector_set<int>::const_iterator c = conflicts.begin(); c != conflicts.end(); ++c) + { + if (Object::IsDerivedFromClassID(i->first, *c)) + return i->second; + } + } + + return NULL; +} + + +void GameObject::SetName (char const* name) +{ + m_Name.assign(name, GetMemoryLabel()); + if (s_SetGONameCallback) + s_SetGONameCallback(this); + SetDirty (); +} + +void GameObject::UpdateActiveGONode() +{ + m_ActiveGONode.RemoveFromList(); + if (IsActive()) + { + if (m_Tag != 0) + GetGameObjectManager().m_TaggedNodes.push_back(m_ActiveGONode); + else + GetGameObjectManager().m_ActiveNodes.push_back(m_ActiveGONode); + } +} + +void GameObject::MarkActiveRecursively (bool state) +{ + Transform &transform = GetComponent (Transform); + for (Transform::iterator i=transform.begin ();i != transform.end ();i++) + (*i)->GetGameObject().MarkActiveRecursively (state); + + m_IsActive = state; + SetDirty(); +} + +void GameObject::ActivateAwakeRecursivelyInternal (DeactivateOperation deactivateOperation, AwakeFromLoadQueue &queue) +{ + if (m_IsActivating) + { + ErrorStringObject("GameObject is already being activated or deactivated.", this); + return; + } + bool state; + bool changed; + m_IsActivating = true; + if (m_IsActiveCached != -1) + { + bool oldState = m_IsActiveCached; + m_IsActiveCached = -1; + state = IsActive(); + changed = oldState != state; + } + else + { + state = IsActive(); + changed = true; + } + + Transform *transform = QueryComponent (Transform); + if (transform) + { + // use a loop by index rather than a iterator, as the children can adjust + // the child list during the Awake call, and invalidate the iterator + for (int i = 0; i < transform->GetChildrenCount(); i++) + transform->GetChild(i).GetGameObject().ActivateAwakeRecursivelyInternal (deactivateOperation, queue); + } + + if (changed) + { + for (int i=0;i<m_Component.size ();i++) + { + Component& component = *m_Component[i].second; + if (state) + { + AssertIf (&*component.m_GameObject != this); + component.SetGameObjectInternal (this); + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + queue.Add(*m_Component[i].second); + else + component.AwakeFromLoad (kActivateAwakeFromLoad); + } + else + component.Deactivate (deactivateOperation); + } + + if (state) + UpdateActiveGONode (); + else + m_ActiveGONode.RemoveFromList(); + } + m_IsActivating = false; +} + +void GameObject::ActivateAwakeRecursively (DeactivateOperation deactivateOperation) +{ + AwakeFromLoadQueue queue (kMemTempAlloc); + ActivateAwakeRecursivelyInternal (deactivateOperation, queue); + queue.AwakeFromLoad (kActivateAwakeFromLoad); +} + +void GameObject::SetActiveRecursivelyDeprecated (bool state) +{ + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion3_5_a1)) + { + if (IsPrefabParent ()) + { + ErrorString(Format("Prefab GameObject's can not be made active! (%s)", GetName())); + return; + } + + // First Mark all objects as active + MarkActiveRecursively (state); + + // Then awake them. + ActivateAwakeRecursively (); + } + else + { + // Old versions used to mark active and awake each object after another. + // That would cause problems with colliders being created twice, once without the active + // parent rigibdodies, and once with, thus causing the unnecessary creation and destruction + // of static colliders (slow). So we fixed it (see above), but we keep the old behaviour for + // legacy content. + Transform &transform = GetComponent (Transform); + for (Transform::iterator i=transform.begin ();i != transform.end ();i++) + (*i)->GetGameObject().SetActiveRecursivelyDeprecated (state); + + if (state) + Activate(); + else + Deactivate(); + } +} + +void GameObject::AddComponentInternal (Component* com) +{ + AssertIf (com == NULL); + { + m_Component.push_back (std::make_pair (com->GetClassID (), ImmediatePtr<Component> (com))); + } + // Make sure it isn't already added to another GO + Assert ( com->m_GameObject.GetInstanceID() == 0 || com->GetGameObjectPtr() == this ); + + com->SetHideFlags(GetHideFlags()); + com->m_GameObject = this; + + if (IsActive ()) + com->AwakeFromLoad (kActivateAwakeFromLoad); + else + com->AwakeFromLoad (kDefaultAwakeFromLoad); + + com->SetDirty (); + SetDirty (); + + SendMessage(kDidAddComponent, com, ClassID (Component)); + + SetSupportedMessagesDirty (); +} + +Component* GameObject::QueryComponentExactTypeImplementation (int classID) const +{ + // Find a component with the requested ID + Container::const_iterator i; + Container::const_iterator end = m_Component.end (); + for (i=m_Component.begin ();i != end; ++i) + { + if (i->first == classID) + return i->second; + } + + return NULL; +} + + +Component* GameObject::QueryComponentImplementation (int classID) const +{ + // Find a component with the requested ID + Container::const_iterator i; + Container::const_iterator end = m_Component.end (); + for (i=m_Component.begin ();i != end; ++i) + { + if (Object::IsDerivedFromClassID (i->first, classID)) + return i->second; + } + + return NULL; +} + +void GameObject::RemoveComponentAtIndex (int index) +{ + Container::iterator i = m_Component.begin () + index; + + Component* com = i->second; + AssertIf (com == NULL); + + m_Component.erase (i); + com->m_GameObject = NULL; + + com->SetDirty (); + SetDirty (); + SetSupportedMessagesDirty (); +} + +void GameObject::SetComponentAtIndexInternal (PPtr<Component> component, int index) +{ + m_Component[index].first = component->GetClassID(); + m_Component[index].second.SetInstanceID(component.GetInstanceID()); +} + + +void GameObject::SetSupportedMessagesDirty () +{ + Assert(!IsDestroying()); + + int oldSupportedMessage = m_SupportedMessages; + m_SupportedMessages = 0; + if (IsDestroying ()) + return; + + GetSupportedMessagesRecalculate (); + if (oldSupportedMessage != m_SupportedMessages) + { + for (Container::iterator i=m_Component.begin ();i != m_Component.end (); ++i) + if (i->second) + i->second->SupportedMessagesDidChange (m_SupportedMessages); + } +} + +void GameObject::GetSupportedMessagesRecalculate () +{ + Assert(!IsDestroying()); + + m_SupportedMessages = 0; + for (Container::iterator i=m_Component.begin ();i != m_Component.end (); ++i) + if (i->second) + m_SupportedMessages |= i->second->CalculateSupportedMessages (); +} + +Component* GameObject::GetComponentPtrAtIndex (int i)const +{ + return m_Component[i].second; +} + +int GameObject::GetComponentIndex (Component *component) +{ + Assert(!IsDestroying()); + + for (int i = 0; i < GetComponentCount (); i++) + { + if (&GetComponentAtIndex (i) == component) + return i; + } + + return -1; +} + + +void GameObject::SwapComponents (int index1, int index2) +{ + AssertIf (index1 > m_Component.size() || index1 < 0); + AssertIf (index2 > m_Component.size() || index2 < 0); + + ComponentPair tmp = m_Component[index1]; + m_Component[index1] = m_Component[index2]; + m_Component[index2] = tmp; + + Component* comp1 = m_Component[index1].second; + Component* comp2 = m_Component[index2].second; + if (comp1 && comp1->IsDerivedFrom(ClassID(Behaviour))) + { + Behaviour* beh = static_cast<Behaviour*>(comp1); + if (beh->GetEnabled()) + { + beh->SetEnabled (false); + beh->SetEnabled (true); + } + } + if (comp2 && comp2->IsDerivedFrom(ClassID(Behaviour))) + { + Behaviour* beh = static_cast<Behaviour*>(comp2); + if (beh->GetEnabled()) + { + beh->SetEnabled (false); + beh->SetEnabled (true); + } + } + SetDirty(); +} + +bool GameObject::IsActive () const +{ + if (m_IsActiveCached != -1) + return m_IsActiveCached; + + // For pre 4.0 content activate state is the same as m_IsActive. + if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + m_IsActiveCached = m_IsActive; + else + { + // Calculate active state based on the hierarchy + m_IsActiveCached = m_IsActive && !(IsPersistent() || IsPrefabParent()); + Transform *trs = QueryComponent (Transform); + if (trs) + { + Transform *parent = GetComponent (Transform).GetParent(); + if (parent) + m_IsActiveCached = m_IsActiveCached && parent->GetGameObject().IsActive(); + } + } + + return m_IsActiveCached; +} + +bool GameObject::IsActiveIgnoreImplicitPrefab () +{ + // This function does not make sense to be called for old content. + Assert (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)); + + Transform *trs = QueryComponent (Transform); + if (trs) + { + Transform *parent = GetComponent (Transform).GetParent(); + if (parent) + return m_IsActive && parent->GetGameObject().IsActiveIgnoreImplicitPrefab(); + } + + return m_IsActive; +} + +void GameObject::Activate () +{ + if (IsActive()) + return; + + PROFILER_AUTO(gActivateGameObjectProfiler, this); + + SetDirty (); + + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + { + m_IsActive = true; + ActivateAwakeRecursively (); + // After AwakeFromLoad, 'this' could have been destroyed (if user is Destroying in OnEnable or Awake) + // So do not access it any further + } + else + { + if (IsPrefabParent ()) + { + ErrorString(Format("Prefab GameObject's can not be made active! (%s)", GetName())); + return; + } + + if (IS_CONTENT_NEWER_OR_SAME(kUnityVersion3_3_a1) && IsPersistent ()) + { + ErrorString(Format("GameObjects stored in assets can not be made active! (%s)", GetName())); + return; + } + + m_IsActive = true; + m_IsActiveCached = m_IsActive; + for (int i=0;i<m_Component.size ();i++) + { + Component& component = *m_Component[i].second; + AssertIf (&*component.m_GameObject != this); + component.m_GameObject = this; + component.AwakeFromLoad (kActivateAwakeFromLoad); + } + + UpdateActiveGONode (); + } + +} + +void GameObject::Deactivate (DeactivateOperation operation) +{ + PROFILER_AUTO(gDeactivateGameObjectProfiler, this) + + if (!IsActive()) + { + if (m_IsActive) + { + m_IsActive = false; + SetDirty (); + } + return; + } + + m_IsActive = false; + + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + ActivateAwakeRecursively (operation); + else + { + m_IsActiveCached = m_IsActive; + for (int i=0;i<m_Component.size ();i++) + { + Component& com = *m_Component[i].second; + com.Deactivate (operation); + } + + m_ActiveGONode.RemoveFromList(); + } + + SetDirty (); +} + +void GameObject::SetSelfActive (bool state) +{ + if (state) + Activate(); + else + Deactivate(kNormalDeactivate); +} + +void GameObject::AddComponentInternal (GameObject& gameObject, Component& clone) +{ + SET_ALLOC_OWNER(&gameObject); + Assert(clone.m_GameObject == NULL); + gameObject.m_Component.push_back(make_pair(clone.GetClassID(), &clone)); + clone.m_GameObject = &gameObject; +} + +void GameObject::RemoveComponentFromGameObjectInternal (Component& clone) +{ + GameObject* go = clone.GetGameObjectPtr(); + if (go == NULL) + return; + + int index = go->GetComponentIndex(&clone); + if (index == -1) + return; + + go->m_Component.erase(go->m_Component.begin() + index); + clone.m_GameObject = NULL; +} + +void GameObject::CheckConsistency () +{ + Super::CheckConsistency (); + + + // Remove Components from m_Component if they vanished without deactivating. + // (eg. class hierarchy changed and the class doesn't exist anymore when loading from disk) + int i = 0; + while (i < m_Component.size ()) + { + // Use is object available instead of normal comparison so that we dont load the object + // which might already trigger the component to query for other components. + int CurComponentInstanceID = m_Component[i].second.GetInstanceID (); + if (!IsObjectAvailable (CurComponentInstanceID)) + { + ErrorStringObject (Format("Component %s could not be loaded when loading game object. Cleaning up!", ClassIDToString(m_Component[i].first).c_str()), this); + m_Component.erase (m_Component.begin () + i); + } + else + i++; + } + + // Preload all the components! + // This is necessary to avoid recursion and awake functions calling remove component, + // When we are removing the wrong ones anyway. + i = 0; + while (i < m_Component.size ()) + { + Component* com = m_Component[i].second; + UNUSED(com); + i++; + } + + // Remove Components with wrong gameobject ptrs + i = 0; + while (i < m_Component.size ()) + { + Component* com = m_Component[i].second; + if (com && com->GetGameObjectPtr () == this) + { + i++; + continue; + } + + if (com) + { + if (com->GetGameObjectPtr () == NULL) + { + com->SetGameObjectInternal (this); + ErrorStringObject ("Component (" + com->GetClassName () + ") has a broken GameObject reference. Fixing!", this); + continue; + } + else + { + ErrorStringObject ("Failed to load component (" + com->GetClassName () + ")! Removing it!", this); + com->SetHideFlags(kHideAndDontSave); + } + } + else + { + ErrorStringObject ("Failed to load component (" + Object::ClassIDToString (m_Component[i].first) + ")! Removing it!", this); + } + + m_Component.erase (m_Component.begin () + i); + } + + // make sure we always have at least one transform on a gameobject + int transformCount = 0; + for (int i = 0; i < m_Component.size (); ++i) + { + if(m_Component[i].first == ClassID (Transform)) + transformCount++; + + if (transformCount > 1) + { + // More than one transform on object. If it's a scene object (transient), + // remove the extraneous transform. For prefabs (persistent), touching the + // transform hierarchy will lead to all kinds of troubles so we just leave + // it be. + if (!IsPersistent ()) + { + Transform* com = static_cast<Transform*> (&*m_Component[i].second); + com->SetParent (NULL); + + RemoveComponentAtIndex (i); + --i; + + GameObject* dummyObject = CreateObjectFromCode<GameObject> (); + dummyObject->SetName ("!! ORPHAN TRANSFORM !!"); + dummyObject->AddComponentInternal (com); + + ErrorStringObject ("Object has multiple transform components. Created dummy GameObject and added transform to it!", this); + } + else + { + ErrorStringObject ("Object has multiple transform components!", this); + } + } + } + if(transformCount == 0) + { + ErrorStringObject (Format("Transform component could not be found on game object. Adding one!"), this); + AddComponentUnchecked(*this,ClassID(Transform), NULL, NULL); + } + +#if UNITY_EDITOR + if (m_IsOldVersion && m_IsActive && !IsPersistent() && !IsActiveIgnoreImplicitPrefab()) + WarningStringObject ("GameObject is active but a parent is inactive. Active state is now inherited. Change the parenting to get back the old behaviour!", this); +#endif + + SetSupportedMessagesDirty (); +} + +void GameObject::SetLayer (int layer) +{ + if (layer >= 0 && layer < 32) + { + m_Layer = layer; + MessageData data; + SendMessageAny (kLayerChanged, data); + SetDirty (); + } + else + ErrorString ("A game object can only be in one layer. The layer needs to be in the range [0...31]"); +} + +void GameObject::SetTag (UInt32 tag) +{ + #if UNITY_EDITOR + m_TagString = TagToString (tag); + #endif + + m_Tag = tag; + UpdateActiveGONode(); + + AssertIf (tag != -1 && tag != m_Tag); + AssertIf (tag == -1 && m_Tag != 0xFFFF); + MessageData data; + SendMessageAny (kLayerChanged, data); + SetDirty (); +} + +void GameObject::SetHideFlags (int flags) +{ + SetHideFlagsObjectOnly(flags); + for (int i=0;i<m_Component.size ();i++) + { + Component& com = *m_Component[i].second; + com.SetHideFlags(flags); + } +} + +template<class TransferFunction> +void GameObject::TransferComponents (TransferFunction& transfer) +{ + Container* components_to_serialize = &m_Component; + + // When cloning objects for prefabs and instantiate, we don't use serialization to duplicate the hierarchy, + // we duplicate the hierarchy directly + if (!SerializePrefabIgnoreProperties(transfer)) + return; + +#if UNITY_EDITOR + Container filtered_components; + if (transfer.IsWritingGameReleaseData ()) + { + components_to_serialize = &filtered_components; + for (Container::iterator i = m_Component.begin(); i != m_Component.end(); i++) + { + if (IsClassSupportedOnBuildTarget(i->first, transfer.GetBuildingTarget().platform)) + filtered_components.push_back (*i); + } + } +#endif + + transfer.Transfer (*components_to_serialize, "m_Component", kHideInEditorMask | kStrongPPtrMask | kIgnoreWithInspectorUndoMask); +} + + + + + +template<class TransferFunction> +void GameObject::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + transfer.SetVersion (4); + TransferComponents(transfer); + + TRANSFER (m_Layer); + + #if GAMERELEASE + TransferConstantString(m_Name, "m_Name", kNoTransferFlags, GetMemoryLabel(), transfer); + TRANSFER (m_Tag); + transfer.Transfer (m_IsActive, "m_IsActive"); + #else + + #if UNITY_EDITOR + if (transfer.IsVersionSmallerOrEqual (3)) + m_IsOldVersion = true; + #endif + + if (transfer.IsOldVersion (3) || transfer.IsCurrentVersion ()) + { + TransferConstantString(m_Name, "m_Name", kNoTransferFlags, GetMemoryLabel(), transfer); + + if (transfer.IsSerializingForGameRelease ()) + { + TRANSFER (m_Tag); + if (transfer.IsReading ()) + m_TagString = TagToString (m_Tag); + + transfer.Transfer (m_IsActive, "m_IsActive"); + } + else + { + transfer.Transfer (m_TagString, "m_TagString"); + if (transfer.IsReading ()) + m_Tag = StringToTagAddIfUnavailable (m_TagString); + + transfer.Transfer (m_Icon, "m_Icon", kNoTransferFlags); + transfer.Transfer (m_NavMeshLayer, "m_NavMeshLayer", kHideInEditorMask); + + transfer.Transfer (m_StaticEditorFlags, "m_StaticEditorFlags", kNoTransferFlags | kGenerateBitwiseDifferences); + + // Read deprecated static flag and set it up as m_StaticEditorFlags + if (transfer.IsReadingBackwardsCompatible ()) + { + bool isStatic = false; + transfer.Transfer (isStatic, "m_IsStatic", kNoTransferFlags); + if (isStatic) + m_StaticEditorFlags = 0xFFFFFFFF; + } + transfer.Transfer (m_IsActive, "m_IsActive", kHideInEditorMask); + } + } + else if (transfer.IsOldVersion (2)) + { + TRANSFER (m_TagString); + m_Tag = StringToTag (m_TagString); + transfer.Transfer (m_IsActive, "m_IsActive"); + } + else if (transfer.IsOldVersion (1)) + { + TRANSFER (m_Tag); + m_TagString = TagToString (m_Tag); + transfer.Transfer (m_IsActive, "m_IsActive"); + } + #endif + + // Make sure that old prefabs are always active. + if (transfer.IsVersionSmallerOrEqual (3) && IsPersistent() && IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + m_IsActive = true; +} + +bool GameObject::GetIsStaticDeprecated () +{ +#if UNITY_EDITOR + return m_StaticEditorFlags != 0; +#else + return false; +#endif +} + +void GameObject::SetIsStaticDeprecated(bool s) +{ +#if UNITY_EDITOR + m_StaticEditorFlags = s ? 0xFFFFFFFF : 0; + SetDirty(); +#endif +} + +bool GameObject::IsStaticBatchable () const +{ +#if UNITY_EDITOR + return AreStaticEditorFlagsSet (kBatchingStatic); +#else + return false; +#endif +} + +#if UNITY_EDITOR + +bool GameObject::AreStaticEditorFlagsSet (StaticEditorFlags flags) const +{ + return (m_StaticEditorFlags & (UInt32)flags) != 0; +} + +StaticEditorFlags GameObject::GetStaticEditorFlags () const +{ + return (StaticEditorFlags)m_StaticEditorFlags; +} + +void GameObject::SetStaticEditorFlags (StaticEditorFlags flags) +{ + m_StaticEditorFlags = (UInt32)flags; + SetDirty(); +} + +void GameObject::SetIcon (PPtr<Texture2D> icon) +{ + if (m_Icon != icon) + { + m_Icon = icon; + SetDirty (); + } +} + +PPtr<Texture2D> GameObject::GetIcon () const +{ + return m_Icon; +} +#endif + +void GameObject::RegisterDestroyedCallback (DestroyGOCallbackFunction* callback) +{ + s_GameObjectDestroyedCallback = callback; +} + +void GameObject::InvokeDestroyedCallback (GameObject* go) +{ + if (s_GameObjectDestroyedCallback) + s_GameObjectDestroyedCallback (go); +} + +void GameObject::RegisterSetGONameCallback (SetGONameFunction* callback) +{ + s_SetGONameCallback = callback; +} + +static int GetHighestGOComponentClassID () +{ + static int highestGOComponentClassID = 0; + if (highestGOComponentClassID != 0) + return highestGOComponentClassID; + + vector<SInt32> classes; + Object::FindAllDerivedClasses (ClassID (Component), &classes, false); + for (int i=0;i<classes.size ();i++) + highestGOComponentClassID = max<int> (highestGOComponentClassID, classes[i]); + + return highestGOComponentClassID; +} + +void GameObject::RegisterMessageHandler (int classID, const MessageIdentifier& messageIdentifier, + MessagePtr message, int typeId) +{ + Assert(s_RegisteredMessageForwarders); + s_RegisteredMessageForwarders->resize (max(classID, GetHighestGOComponentClassID ()) + 1); + (*s_RegisteredMessageForwarders)[classID].RegisterMessageCallback (messageIdentifier.messageID, message, typeId); +} + +void GameObject::RegisterAllMessagesHandler (int classID, MessagePtr message, CanHandleMessagePtr canHandleNotification) +{ + Assert(s_RegisteredMessageForwarders); + s_RegisteredMessageForwarders->resize (max(classID, GetHighestGOComponentClassID ()) + 1); + (*s_RegisteredMessageForwarders)[classID].RegisterAllMessagesCallback (message, canHandleNotification); +} + +static void PropagateNotificationsToDerivedClasses (MessageForwarders& notifications) +{ + vector<SInt32> classes; + Object::FindAllDerivedClasses (ClassID (Object), &classes, false); + int highestClassID = 0; + for (unsigned i=0;i<classes.size ();i++) + highestClassID = max<int> (classes[i], highestClassID); + + notifications.resize (highestClassID + 1); + + for (int classID=0;classID<notifications.size ();classID++) + { + if (Object::ClassIDToRTTI (classID) == NULL) + continue; + + int superClassID = Object::GetSuperClassID (classID); + while (superClassID != ClassID (Object)) + { + notifications[classID].AddBaseMessages (notifications[superClassID]); + superClassID = Object::GetSuperClassID (superClassID); + } + } +} + +void GameObject::InitializeMessageHandlers () +{ + Assert(s_MessageHandler && s_RegisteredMessageForwarders); + PropagateNotificationsToDerivedClasses (*s_RegisteredMessageForwarders); + s_MessageHandler->Initialize (*s_RegisteredMessageForwarders); + s_RegisteredMessageForwarders->clear (); +} + +void GameObject::InitializeMessageIdentifiers () +{ + Assert(s_MessageHandler == NULL); + s_MessageHandler = UNITY_NEW(MessageHandler,kMemNewDelete); + s_RegisteredMessageForwarders = UNITY_NEW(MessageForwarders,kMemNewDelete); + GetMessageHandler ().InitializeMessageIdentifiers (); +} + +void GameObject::InitializeClass () +{ + GameObjectManager::StaticInitialize(); +} + +void GameObject::CleanupClass () +{ + GameObjectManager::StaticDestroy(); + UNITY_DELETE(s_MessageHandler,kMemNewDelete); + UNITY_DELETE(s_RegisteredMessageForwarders,kMemNewDelete); +} + +bool CheckMessageDataType (int messageIdentifier, MessageData& data) +{ + return GameObject::GetMessageHandler ().MessageIDToParameter (messageIdentifier) == data.type; +} + +void GameObject::SendMessageAny (const MessageIdentifier& messageIdentifier, MessageData& messageData) +{ + int messageID = messageIdentifier.messageID; + AssertIf (messageIdentifier.messageID == -1); + #if DEBUGMODE + if (!CheckMessageDataType (messageID, messageData)) + AssertString ("The messageData sent has an incorrect type."); + #endif + + for (int i=0;i<m_Component.size ();i++) + { + int classID = m_Component[i].first; + if (s_MessageHandler->HasMessageCallback (classID, messageID)) + { + Component& component = *m_Component[i].second; + s_MessageHandler->HandleMessage (&component, classID, messageID, messageData); + } + } +} + +bool GameObject::WillHandleMessage (const MessageIdentifier& messageIdentifier) +{ + int messageID = messageIdentifier.messageID; + AssertIf (messageIdentifier.messageID == -1); + + for (Container::iterator i=m_Component.begin ();i != m_Component.end ();i++) + { + int classID = i->first; + if (s_MessageHandler->HasMessageCallback (classID, messageID)) + { + Component& component = *i->second; + if (s_MessageHandler->WillHandleMessage (&component, classID, messageID)) + return true; + } + } + return false; +} + +void GameObject::TransformParentHasChanged () +{ + // Reactivate transform hieararchy, but only if it has been activated before, + // otherwise we change activation order. + if (m_IsActiveCached != -1) + ActivateAwakeRecursively (); +} + +void SendMessageDirect (Object& target, const MessageIdentifier& messageIdentifier, MessageData& messageData) +{ + int classID = target.GetClassID(); + if (GameObject::GetMessageHandler ().HasMessageCallback (classID, messageIdentifier.messageID)) + { + GameObject::GetMessageHandler ().HandleMessage (&target, classID, messageIdentifier.messageID, messageData); + } +} + +MessageHandler& GameObject::GetMessageHandler () +{ + Assert(s_MessageHandler); + return *s_MessageHandler; +} + + +char const* Component::GetName () const +{ + if (m_GameObject) + return m_GameObject->m_Name.c_str(); + else + return GetClassName().c_str(); +} + +void Component::SetName (char const* name) +{ + if (m_GameObject) + m_GameObject->SetName (name); +} + +Component::Component (MemLabelId label, ObjectCreationMode mode) : Super(label, mode) +{ + m_GameObject = NULL; +} + +Component::~Component () +{ +} + +void Component::SendMessageAny (const MessageIdentifier& messageID, MessageData& messageData) +{ + GameObject* go = GetGameObjectPtr (); + if (go) + go->SendMessageAny (messageID, messageData); +} + +void Component::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad(awakeMode); + #if SUPPORT_LOG_ORDER_TRACE + if (IsActive() && RunningReproduction()) + { + if (SUPPORT_LOG_ORDER_TRACE == 2) + { + LogString(Format("AwakeFromLoad %s (%s) [%d]", GetName(), GetClassName().c_str(), GetInstanceID())); + } + else + { + LogString(Format("AwakeFromLoad %s (%s)", GetName(), GetClassName().c_str())); + } + } + #endif + + // Force load the game object. This is in order to prevent ImmediatePtrs not being dereferenced after loading. + // Which can cause a crash in Resources.UnloadUnusedAssets() + // Resources.Load used to store incorrect preload data which made this trigger. + GameObject* dereferenceGameObject = m_GameObject; + UNUSED(dereferenceGameObject); +} + +template<class TransferFunction> +void Component::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + + if (SerializePrefabIgnoreProperties(transfer)) + transfer.Transfer (m_GameObject, "m_GameObject", kHideInEditorMask | kStrongPPtrMask | kIgnoreWithInspectorUndoMask); +} + +void Component::CheckConsistency () +{ + Super::CheckConsistency (); + GameObject* go = GetGameObjectPtr (); + if (go) + { + for (int i = 0; i < go->GetComponentCount(); i++) + { + if (&go->GetComponentAtIndex(i) == this) + return; + } + + ErrorStringObject (Format("CheckConsistency: GameObject does not reference component %s. Fixing.", GetClassName().c_str()), go); + go->AddComponentInternal (this); + } + + // MonoBehaviours are allowed to exists without a game object + if (IsDerivedFrom(ClassID(Behaviour))) + { + return; + } + + #if UNITY_EDITOR + if (m_GameObject == NULL) + { + GetCleanupManager ().MarkForDeletion (this, "GameObject pointer is invalid"); + } + #endif +} + +GameObjectManager* GameObjectManager::s_Instance = NULL; +void GameObjectManager::StaticInitialize() +{ + Assert(GameObjectManager::s_Instance == NULL); + GameObjectManager::s_Instance = UNITY_NEW(GameObjectManager,kMemBaseObject); +} + +void GameObjectManager::StaticDestroy() +{ + Assert(GameObjectManager::s_Instance); + UNITY_DELETE(GameObjectManager::s_Instance,kMemBaseObject); +} + +GameObjectManager& GetGameObjectManager() +{ + Assert(GameObjectManager::s_Instance); + return *GameObjectManager::s_Instance; +} + +IMPLEMENT_OBJECT_SERIALIZE (GameObject) +IMPLEMENT_OBJECT_SERIALIZE (Component) + +IMPLEMENT_CLASS_HAS_INIT (GameObject) +IMPLEMENT_CLASS (Component) + +INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(GameObject) +INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(Component) + +} + +// Hack to make register class work with name spaces. Optimally IMPLEMENT_CLASS / IMPLEMENT_OBJECT_SERIALIZE +// could be moved out of the namespace but that gives compile errors on gcc +void RegisterClass_Component () { Unity::RegisterClass_Component(); } +void RegisterClass_GameObject () { Unity::RegisterClass_GameObject(); } diff --git a/Runtime/BaseClasses/GameObject.h b/Runtime/BaseClasses/GameObject.h new file mode 100644 index 0000000..ca4d376 --- /dev/null +++ b/Runtime/BaseClasses/GameObject.h @@ -0,0 +1,535 @@ +#ifndef GAMEOBJECT_H +#define GAMEOBJECT_H + +#include <vector> +#include "EditorExtension.h" +#include "Runtime/Utilities/LogAssert.h" +#include "MessageIdentifier.h" +#include "MessageHandler.h" +#include "BitField.h" +#include "Runtime/Utilities/LinkedList.h" +#include "Runtime/Containers/ConstantString.h" + +#if UNITY_EDITOR +#include "Editor/Src/Utility/StaticEditorFlags.h" +#endif + +class GameManager; +class MessageHandler; +class Texture2D; +class AwakeFromLoadQueue; +typedef UNITY_SET(kMemTempAlloc, Object*) TempSelectionSet; + +namespace Unity +{ + +/// A GameObject is basically a bag of GOComponents. +/// GOComponents are added and removed at runtime. +/// This allows GameObject to be composed of an arbitrary amount of GOComponents. + +/// You can query for any GOCOmponents using +/// QueryComponent and GetComponent +/// This will Ask every of the component in the GO if it is derived from the wanted class +/// eg. from inside a Component you can query for a Transform: +/// Transform* t = QueryComponent (Transform); +/// If there is no Transform inside the GameObject, NULL is returned. +/// The difference between QueryComponent and GetComponent is that +/// QueryComponent returns a ptr, GetComponent returns a reference +/// but asserts if no matching component could be found +/// Also you are not allowed to Query for Component classes +/// that are in the GameObject more than once. + +/// Querys for a component class, aksing every component if it is derived from wanted class +#define QueryComponent(x) GetGameObject ().QueryComponentT<x> (ClassID (x)) +/// Same as above only that it returns a reference and asserts if no component derived from x can be found +#define GetComponent(x) GetGameObject ().GetComponentT<x> (ClassID (x)) + +/// Also GameObjects support messaging. +/// MessageIdentifier kTransformChanged ("TransformChanged"); +/// MessageIdentifier kTestMessage ("Test", ClassID (float)); +/// In order to receive a message +/// Register the notification inside the InitializeClass of the class +/// Renderer::InitializeClass () +/// { +/// REGISTER_NOTIFICATION_VOID (Renderer, kTransformChanged, TransformChanged); +/// REGISTER_NOTIFICATION (Renderer, kTestMessage, TestMessage, float); +/// } +/// bool Renderer::TransformChanged () { ... } +/// bool Renderer::TestMessage (float f) { ... } + +/// In order to send a message use: +/// SendMessage (kTransformChanged); +/// SendMessage (kTestMessage, 0.1f, ClassID (float)); + +class Component; +class GameObject; + + +enum DeactivateOperation +{ + kNormalDeactivate = 0, + // Deactivate was called + kDeprecatedDeactivateToggleForLevelLoad = 1, + + // Deactivate was called because the component will be destroyed + kWillDestroySingleComponentDeactivate = 2, + // Deactivate was called because the entire game object will be destroyed + kWillDestroyGameObjectDeactivate = 3 +}; + +class EXPORT_COREMODULE GameObject : public EditorExtension +{ + public: + + typedef std::pair<SInt32, ImmediatePtr<Component> > ComponentPair; + typedef UNITY_VECTOR(kMemBaseObject, ComponentPair) Container; + + GameObject (MemLabelId label, ObjectCreationMode mode); + // ~GameObject (); declared-by-macro + + REGISTER_DERIVED_CLASS (GameObject, EditorExtension) + DECLARE_OBJECT_SERIALIZE (GameObject) + + /// An GameObject can either be active or inactive (Template GameObjects are always inactive) + /// If an GameObject is active/inactive all its components have the same state as well. + /// (Components that are not added to a gameobject are always inactive) + /// Querying and messaging still works for inactive GameObjects and Components + void Activate (); + + /// Deactiates the game object and thus all it's components. + void Deactivate (DeactivateOperation operation = kNormalDeactivate); + + bool IsActiveIgnoreImplicitPrefab (); + bool IsActive () const; + bool IsSelfActive () const { return m_IsActive; } + void SetSelfActive (bool state); + + /// Set the GameObject Layer. + /// This is used for collisions and messaging + void SetLayer (int layerIndex); + int GetLayer () const { return m_Layer; } + UInt32 GetLayerMask () const { return 1 << m_Layer; } + + /// Set the Tag of the gameobject + UInt32 GetTag () const { return m_Tag; } + void SetTag (UInt32 tag); + + // Adds a new Component to the GameObject. + // Using the PersistentObject interface so that Components, + // which are not loaded at the moment can be added. + // Use GameObjectUtility instead, you must invoke specific callbacks etc. + void AddComponentInternal (Component* component); + + // Removes a Component from the GameObject. + void RemoveComponentAtIndex (int index); + + int GetComponentCount () const { return m_Component.size (); } + Component& GetComponentAtIndex (int i) const; + Component* GetComponentPtrAtIndex (int i) const; + bool GetComponentAtIndexIsLoaded (int i) const; + int GetComponentClassIDAtIndex (int i) const; + int CountDerivedComponents (int compareClassID)const; + + /// Checks if GameObject has any components conflicting with the specified classID. + bool HasConflictingComponents (int classID) const { return FindConflictingComponentPtr (classID) != NULL; } + + /// Find the first conflicting component classID for the specified classID. + Component* FindConflictingComponentPtr (int classID) const; + + /// Swap two components in the vector. + void SwapComponents (int index1, int index2); + + /// Get the index of a component. + int GetComponentIndex (Component *component); + + /// Send a message identified by messageName to all components if they can handle it + void SendMessageAny (const MessageIdentifier& messageID, MessageData& messageData); + + /// Send a message identified by messageName to all components if they can handle it + template<class T> + void SendMessage (const MessageIdentifier& messageID, T messageData, int classId); + + /// Will this message be handled by any component in the gameobject? + bool WillHandleMessage (const MessageIdentifier& messageID); + + // Use the QueryComponent macro + // Gives back a component by its classID. + // If the GameObject doesnt have such a Component, NULL is returned. + template<class T> + T* QueryComponentT (int inClassID) const; + + // Use the GetComponent macro + // Gives back a component by its classID. + // If the GameObject doesnt have such a Component, the function will assert + template<class T> + T& GetComponentT (int inClassID) const; + + // Use the GetComponent macro + // Gives back a component by its classID. + // If the GameObject doesnt have such a Component, the function will assert + template<class T> + T& GetComponentExactTypeT (int inClassID) const; + + const GameObject& GetGameObject ()const { return *this; } + GameObject& GetGameObject () { return *this; } + + virtual char const* GetName () const { return m_Name.c_str(); } + virtual void SetName (char const* name); + + // Deprecated + void SetActiveRecursivelyDeprecated (bool state); + bool GetIsStaticDeprecated (); + void SetIsStaticDeprecated (bool s); + + #if UNITY_EDITOR + bool AreStaticEditorFlagsSet (StaticEditorFlags flags) const; + StaticEditorFlags GetStaticEditorFlags () const; + void SetStaticEditorFlags (StaticEditorFlags flags); + #endif + + //@TODO: When we rewrite static batching in C++ fix this up + bool IsStaticBatchable () const; + + // Callback functions + typedef void DestroyGOCallbackFunction (GameObject* go); + static void RegisterDestroyedCallback (DestroyGOCallbackFunction* callback); + static void InvokeDestroyedCallback (GameObject* go); + + typedef void SetGONameFunction (GameObject* go); + static void RegisterSetGONameCallback (SetGONameFunction* callback); + + /// Registers an message callback. Used by the REGISTER_NOTIFICATION macros + typedef void (*MessagePtr)(void* receiver, int messageIndex, MessageData& data); + typedef bool (*CanHandleMessagePtr)(void* receiver, int messageIndex, MessageData& data); + static void RegisterMessageHandler (int classID, const MessageIdentifier& messageIdentifier, MessagePtr functor, int typeId); + static void RegisterAllMessagesHandler (int classID, MessagePtr message, CanHandleMessagePtr canHandleNotification); + + virtual void Reset (); + + static void InitializeClass (); + static void CleanupClass (); + + // Initializes the message system + static void InitializeMessageHandlers (); + static void InitializeMessageIdentifiers (); + // Returns the message handler + static class MessageHandler& GetMessageHandler (); + + // Internally used during object destruction to prevent double deletion etc. + bool IsDestroying () const { return m_IsDestroying; } + bool IsActivating () const { return m_IsActivating; } + + void WillDestroyGameObject (); + + + inline UInt32 GetSupportedMessages (); + void SetSupportedMessagesDirty (); + + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + virtual void SetHideFlags (int flags); + + virtual void CheckConsistency (); + + + static void AddComponentInternal (GameObject& gameObject, Component& clone); + static void RemoveComponentFromGameObjectInternal (Component& clone); + + #if UNITY_EDITOR + enum + { + kNotVisible = 0, + kSelfVisible = 1, + kVisibleAsChild = 2, + }; + int IsMarkedVisible () const { return m_IsMarkedVisible; } + void SetMarkedVisible (int marked) { m_IsMarkedVisible = marked; } + void SetIcon (PPtr<Texture2D> icon); + + // Get the custom icon for this gameobject. Does not scan components for their icons. + PPtr<Texture2D> GetIcon () const; + + UInt32 GetNavMeshLayer () const { return m_NavMeshLayer; } + void SetNavMeshLayer (UInt32 layer) { m_NavMeshLayer = layer; SetDirty(); } + #endif + + // Internal functions that you should never call unless you really understand all side effects. + void SetActiveBitInternal (bool value) { m_IsActive = value; } + + void SetComponentAtIndexInternal (PPtr<Component> component, int index); + + void UpdateActiveGONode(); + + void TransformParentHasChanged (); + + Container& GetComponentContainerInternal () { return m_Component; } + + void ActivateAwakeRecursively (DeactivateOperation deactivateOperation = kNormalDeactivate); + void ActivateAwakeRecursivelyInternal (DeactivateOperation deactivateOperation, AwakeFromLoadQueue &queue); + + Component* QueryComponentImplementation (int classID) const; + Component* QueryComponentExactTypeImplementation (int classID) const; + +private: + void GetSupportedMessagesRecalculate (); + + void MarkActiveRecursively (bool state); + + template <class TransferFunction> + void TransferComponents(TransferFunction& transfer); + + Container m_Component; + + UInt32 m_Layer; + UInt16 m_Tag; + bool m_IsActive; + mutable SInt8 m_IsActiveCached; + UInt8 m_IsDestroying; //// OPTIMIZE THIS INTO A COMMON BITMASK! + UInt8 m_IsActivating; + + UInt32 m_SupportedMessages; + + ConstantString m_Name; + + #if UNITY_EDITOR + UInt32 m_StaticEditorFlags; + UnityStr m_TagString; + int m_IsMarkedVisible; + PPtr<Texture2D> m_Icon; + UInt32 m_NavMeshLayer; + bool m_IsOldVersion; + #endif + + ListNode<GameObject> m_ActiveGONode; + + static DestroyGOCallbackFunction* s_GameObjectDestroyedCallback; + static SetGONameFunction* s_SetGONameCallback; + static MessageForwarders* s_RegisteredMessageForwarders; + static MessageHandler* s_MessageHandler; + + friend class Component; +}; + +class EXPORT_COREMODULE Component : public EditorExtension +{ + private: + + ImmediatePtr<GameObject> m_GameObject; + + public: + + + DECLARE_OBJECT_SERIALIZE (Component) + REGISTER_DERIVED_CLASS (Component, EditorExtension) + + Component (MemLabelId label, ObjectCreationMode mode); + // ~Component (); declared-by-macro + + // Returns a reference to the GameObject holding this component + GameObject& GetGameObject () { return *m_GameObject; } + const GameObject& GetGameObject () const { return *m_GameObject; } + GameObject* GetGameObjectPtr () { return m_GameObject; } + GameObject* GetGameObjectPtr () const { return m_GameObject; } + + /// Send a message identified by messageName to every components of the gameobject + /// that can handle it + void SendMessageAny (const MessageIdentifier& messageID, MessageData& messageData); + + template<class T> + void SendMessage (const MessageIdentifier& messageID, T messageData, int classId); + void SendMessage (const MessageIdentifier& messageID); + + /// Is this component active? + /// A component is always inactive if its not attached to a gameobject + /// A component is always inactive if its gameobject is inactive + /// If its a datatemplate, the gameobject and its components are always set to be inactive + bool IsActive () const; + + virtual char const* GetName () const; + virtual void SetName (char const* name); + + virtual UInt32 CalculateSupportedMessages () { return 0; } + virtual void SupportedMessagesDidChange (int /*newMask*/) { } + + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode); + + // Invoke any callbacks prior to component destruction. + virtual void WillDestroyComponent () { } + + /// Deactivate will be called just before the Component is going to be removed from a GameObject + /// It can still communicate with other components at this point. + /// Deactivate will only be called when the component is remove from the GameObject, + /// not if the object is persistet to disk and removed from memory + /// Deactivate will only be called if the GameObject the Component is being removed from is active + /// YOU CAN NOT RELY ON IsActive returning false inside Deactivate + virtual void Deactivate (DeactivateOperation /*operation*/) { } + + virtual void CheckConsistency (); + + #if UNITY_EDITOR + // Some components always go together, e.g. when removing one you have + // to remove other. Example, ParticleSystem and ParticleSystemRenderer. + // Override and return class ID of that "dependent" component. + virtual int GetCoupledComponentClassID() const { return -1; } + #endif + + public: + + int GetGameObjectInstanceID () const { return m_GameObject.GetInstanceID(); } + + /// SetGameObject is called whenever the GameObject of a component changes. + void SetGameObjectInternal (const GameObject* go) { m_GameObject = go; } + + friend class GameObject; +}; + +typedef List< ListNode<GameObject> > GameObjectList; + +// A fast lookup for all tagged and active game objects +class GameObjectManager +{ +public: + static void StaticInitialize(); + static void StaticDestroy(); + // Nodes that are tagged and active + GameObjectList m_TaggedNodes; + // Nodes that are just active + // (If you want to get all active nodes you need to go through tagged and active nodes) + GameObjectList m_ActiveNodes; + + static GameObjectManager* s_Instance; +}; +GameObjectManager& GetGameObjectManager(); + + +template<class T> inline +T* GameObject::QueryComponentT (int compareClassID) const +{ + Component* com; + if (T::IsSealedClass()) + com = QueryComponentExactTypeImplementation(compareClassID); + else + com = QueryComponentImplementation (compareClassID); + DebugAssertIf (com != dynamic_pptr_cast<Component*> (com)); + return static_cast<T*> (com); +} + +template<class T> inline +T& GameObject::GetComponentT (int compareClassID) const +{ + Component* com; + if (T::IsSealedClass()) + com = QueryComponentExactTypeImplementation(compareClassID); + else + com = QueryComponentImplementation (compareClassID); + DebugAssertIf (dynamic_pptr_cast<T*> (com) == NULL); + return *static_cast<T*> (com); +} + +inline Component& GameObject::GetComponentAtIndex (int i)const +{ + return *m_Component[i].second; +} + +inline bool GameObject::GetComponentAtIndexIsLoaded (int i)const +{ + return m_Component[i].second.IsLoaded(); +} + +inline int GameObject::GetComponentClassIDAtIndex (int i) const +{ + return m_Component[i].first; +} + +inline bool Component::IsActive () const +{ + GameObject* go = m_GameObject; + return go != NULL && go->IsActive (); +} + +#define IMPLEMENT_SENDMESSAGE_FOR_CLASS(ClassName) \ + template<class T> inline \ + void ClassName::SendMessage (const MessageIdentifier& messageID, \ + T messageData, int classId) \ + { \ + MessageData data; \ + data.SetData (messageData, classId); \ + SendMessageAny (messageID, data); \ + } + +IMPLEMENT_SENDMESSAGE_FOR_CLASS (Component) +IMPLEMENT_SENDMESSAGE_FOR_CLASS (GameObject) + +inline UInt32 GameObject::GetSupportedMessages () +{ + return m_SupportedMessages; +} + +inline void Component::SendMessage (const MessageIdentifier& messageID) +{ + MessageData data; + SendMessageAny (messageID, data); +} + +void SendMessageDirect (Object& target, const MessageIdentifier& messageIdentifier, MessageData& messageData); + +// Compares the MessageData's type with the method signatures expected parameter type +bool EXPORT_COREMODULE CheckMessageDataType (int messageIdentifier, MessageData& data); + +/// Creates a wrapper that calls a function with no parameter +#define REGISTER_MESSAGE_VOID(ClassType,NotificationName,Function) \ +struct FunctorImpl_##ClassType##_##NotificationName { \ + static void Call (void* object, int, MessageData&) { \ + ClassType* castedObject = reinterpret_cast<ClassType*> (object); \ + castedObject->Function (); \ + } \ +}; \ +GameObject::RegisterMessageHandler (ClassID (ClassType), NotificationName, \ + FunctorImpl_##ClassType##_##NotificationName::Call, 0) + +#if DEBUGMODE +#define CHECK_MSG_DATA_TYPE \ +if (!CheckMessageDataType (messageID, messageData)) \ + DebugStringToFile ("Check message data", 0, __FILE__, 0, kAssert); +#else +#define CHECK_MSG_DATA_TYPE +#endif + +/// Creates a wrapper thats sends the specified DataType to the member functions +#define REGISTER_MESSAGE(ClassType,NotificationName,Function,DataType) \ +struct FunctorImpl_##ClassType##_##NotificationName { \ + static void Call (void* object, int messageID, MessageData& messageData) { \ + CHECK_MSG_DATA_TYPE \ + DataType data = messageData.GetData<DataType> (); \ + ClassType* castedObject = reinterpret_cast<ClassType*> (object); \ + castedObject->Function (data); \ + } \ +}; \ +GameObject::RegisterMessageHandler (ClassID (ClassType), \ + NotificationName, \ + FunctorImpl_##ClassType##_##NotificationName::Call, \ + ClassID (DataType)) + + +/// Creates a wrapper thats sends the specified DataType to the member functions +#define REGISTER_MESSAGE_PTR(ClassType,NotificationName,Function,DataType) \ +struct FunctorImpl_##ClassType##_##NotificationName { \ + static void Call (void* object, int messageID, MessageData& messageData) { \ + CHECK_MSG_DATA_TYPE \ + DataType* data = messageData.GetData<DataType*> (); \ + ClassType* castedObject = reinterpret_cast<ClassType*> (object); \ + castedObject->Function (data); \ + } \ +}; \ +GameObject::RegisterMessageHandler (ClassID (ClassType), \ + NotificationName, \ + FunctorImpl_##ClassType##_##NotificationName::Call, \ + ClassID (DataType)) + + +} + +using namespace Unity; + +#endif + diff --git a/Runtime/BaseClasses/GameObjectTests.cpp b/Runtime/BaseClasses/GameObjectTests.cpp new file mode 100644 index 0000000..9c0362f --- /dev/null +++ b/Runtime/BaseClasses/GameObjectTests.cpp @@ -0,0 +1,274 @@ +#include "UnityPrefix.h" + +#if ENABLE_UNIT_TESTS +#include "External/UnitTest++/src/UnitTest++.h" +#include "Runtime/Misc/GameObjectUtility.h" +#include "Runtime/BaseClasses/SupportedMessageOptimization.h" +#include "Runtime/Testing/Testing.h" + +class GameObjectFixture +{ +protected: + GameObject* NewGameObject() + { + return NEW_OBJECT_RESET_AND_AWAKE(GameObject); + } + + Unity::Component* NewComponent() + { + return NEW_OBJECT_RESET_AND_AWAKE(Unity::Component); + } +}; + +SUITE (GameObjectTests) +{ + TEST_FIXTURE (GameObjectFixture, AddandRemoveComponentTest) + { + GameObject* go = NewGameObject(); + + Unity::Component* component = NewComponent(); + go->AddComponentInternal(component); + CHECK_EQUAL (go->GetComponentCount(), 1); + + // Delete by RemoveComponentFromGameObjectInternal(). + go->RemoveComponentFromGameObjectInternal(*component); + CHECK_EQUAL (go->GetComponentCount(), 0); + + go->AddComponentInternal(component); + CHECK_EQUAL (go->GetComponentCount(), 1); + + // Delete by RemoveComponentAtIndex(). + go->RemoveComponentAtIndex(0); + CHECK_EQUAL (go->GetComponentCount(), 0); + + go->AddComponentInternal(component); + go->AwakeFromLoad (kDefaultAwakeFromLoad); + DestroyObjectHighLevel(go); + } + + TEST_FIXTURE (GameObjectFixture, HideFlagTest) + { + GameObject* go = NewGameObject(); + + Unity::Component* component = NewComponent(); + go->AddComponentInternal(component); + CHECK_EQUAL (go->GetComponentCount(), 1); + + int hideFlag = 2; + go->SetHideFlags(hideFlag); + + CHECK_EQUAL (go->GetHideFlags(), hideFlag); + + for(int i = 0; i < go->GetComponentCount() ; i++) + { + CHECK_EQUAL (go->GetComponentAtIndex(i).GetHideFlags(), hideFlag); + } + + // Add another component, it should have the same hide flag as the game object. + Unity::Component* component1 = NewComponent(); + go->AddComponentInternal(component1); + CHECK_EQUAL (go->GetComponentCount(), 2); + + for(int i = 0; i < go->GetComponentCount() ; i++) + { + CHECK_EQUAL (go->GetComponentAtIndex(i).GetHideFlags(), hideFlag); + } + + DestroyObjectHighLevel(go); + } + + TEST_FIXTURE (GameObjectFixture, NameTest) + { + GameObject* go = NewGameObject(); + + AddComponents(*go, "Transform", "MeshRenderer", "MeshFilter", NULL); + + const char* name = "Test"; + go->SetName(name); + + CHECK_EQUAL (go->GetName(), name); + + for(int i = 0; i < go->GetComponentCount() ; i++) + { + CHECK_EQUAL (go->GetComponentAtIndex(i).GetName(), name); + } + + // Set invalid value. + // We will not test NULL as it's rejected by UI. + go->SetName(""); + + CHECK_EQUAL (go->GetName(), ""); + + for(int i = 0; i < go->GetComponentCount() ; i++) + { + CHECK_EQUAL (go->GetComponentAtIndex(i).GetName(), ""); + } + + DestroyObjectHighLevel(go); + } + + TEST_FIXTURE (GameObjectFixture, QueryComponentTest) + { + GameObject* go = NewGameObject(); + + AddComponents(*go, "Transform", "MeshRenderer", "MeshFilter", NULL); + + // Go for QueryComponentExactTypeImplementation(). + Transform* transform = go->QueryComponentT<Transform>(ClassID(Transform)); + CHECK (transform != NULL); + + // Go for QueryComponentImplementation(). + Unity::Component* component = go->QueryComponentT<Unity::Component>(ClassID(Component)); + CHECK (component != NULL); + + DestroyObjectHighLevel(go); + } + + TEST_FIXTURE (GameObjectFixture, SwapComponentTest) + { + GameObject* go = NewGameObject(); + + AddComponents(*go, "Transform", "MeshRenderer", "MeshFilter", NULL); + + Unity::Component* component = go->GetComponentPtrAtIndex(0); + go->SwapComponents(0, 1); + + CHECK_EQUAL (go->GetComponentIndex(component), 1); + + DestroyObjectHighLevel(go); + } +} + +SUITE (ComponentTests) +{ + TEST (GameObjectTest) + { + GameObject& go = CreateGameObject ("TestGameObject", "Transform", "MeshRenderer", NULL); + + Unity::Component& component = go.GetComponentAtIndex(0); + + CHECK(component.GetGameObjectPtr() == &go); + } + + TEST_FIXTURE (GameObjectFixture, ActiveTest) + { + GameObject* go = NewGameObject(); + go->Activate(); + + Unity::Component* component = NewComponent(); + CHECK (!component->IsActive()); + + go->AddComponentInternal(component); + CHECK (component->IsActive()); + } + + TEST_FIXTURE (GameObjectFixture, NameTest) + { + GameObject* go = NewGameObject(); + + Unity::Component* component = NewComponent(); + CHECK_EQUAL (component->GetName(), component->GetClassName()); + + go->AddComponentInternal(component); + CHECK_EQUAL (component->GetName(), go->GetName()); + + const char* name = "TestComponent"; + component->SetName(name); + + CHECK_EQUAL(go->GetName(), name); + } + + TEST_FIXTURE (GameObjectFixture, CheckConsistencyTest) + { + GameObject* go = NewGameObject(); + + Unity::Component* component = NewComponent(); + component->SetGameObjectInternal(go); + + CHECK_EQUAL(go->GetComponentCount(), 0); + + EXPECT (Error, "GameObject does not reference component"); + component->CheckConsistency(); + + CHECK_EQUAL(go->GetComponentCount(), 1); + } + + TEST (GameObjectMessagesCheckTest) + { +#if !UNITY_RELEASE + { + GameObject& go = CreateGameObject ("test", "Transform", NULL); + CHECK_EQUAL(go.GetSupportedMessages(), 0); + + //Add a Tree component + Unity::Component *cmp1 = AddComponentUnchecked (go, 193, NULL, NULL); + CHECK_EQUAL(go.GetSupportedMessages(), (int) kHasOnWillRenderObject); + + go.Deactivate(); + CHECK_EQUAL(go.GetSupportedMessages(), (int) kHasOnWillRenderObject); + + //Add a NavMeshObstablce component + Unity::Component *cmp2 = AddComponentUnchecked (go, 208, NULL, NULL); + CHECK_EQUAL(go.GetSupportedMessages(), (kHasOnWillRenderObject | kSupportsVelocityChanged | kSupportsTransformChanged)); + go.Activate(); + CHECK_EQUAL(go.GetSupportedMessages(), (kHasOnWillRenderObject | kSupportsVelocityChanged | kSupportsTransformChanged)); + DestroyObjectHighLevel(cmp1); + CHECK_EQUAL(go.GetSupportedMessages(), (kSupportsVelocityChanged | kSupportsTransformChanged)); + go.Deactivate(); + DestroyObjectHighLevel(cmp2); + + CHECK_EQUAL(go.GetSupportedMessages(), 0); + DestroyObjectHighLevel(&go); + } +#endif + } + + TEST (AwakeFromLoadCheckTest) + { + // tests to check if checks of AwakeFromLoad behavior are working + // uncomment them when in doubt + #if !UNITY_RELEASE + // 1. simple object creation + /* + { + GameObject* obj = NEW_OBJECT(GameObject); + obj->CheckCorrectAwakeUsage(); + obj->CheckCorrectAwakeUsage(); + } + */ + + // 2. enforce awake after reset + /* + { + GameObject* obj = NEW_OBJECT(GameObject); + obj->AwakeFromLoad(kDefaultAwakeFromLoad); + obj->CheckCorrectAwakeUsage(); + obj->CheckCorrectAwakeUsage(); + obj->Reset(); + obj->CheckCorrectAwakeUsage(); + obj->CheckCorrectAwakeUsage(); + } + */ + + // 3. check hacks are working + { + GameObject* obj = NEW_OBJECT(GameObject); + obj->Reset(); + obj->HackSetAwakeWasCalled(); + obj->CheckCorrectAwakeUsage(); + obj->CheckCorrectAwakeUsage(); + DestroyObjectHighLevel(obj); + } + + { + GameObject* obj = NEW_OBJECT(GameObject); + obj->Reset(); + obj->AwakeFromLoad(kInstantiateOrCreateFromCodeAwakeFromLoad); + obj->CheckCorrectAwakeUsage(); + DestroyObjectHighLevel(obj); + } +#endif + } +} + +#endif diff --git a/Runtime/BaseClasses/IsPlaying.cpp b/Runtime/BaseClasses/IsPlaying.cpp new file mode 100644 index 0000000..184e830 --- /dev/null +++ b/Runtime/BaseClasses/IsPlaying.cpp @@ -0,0 +1,14 @@ +#include "UnityPrefix.h" +#include "IsPlaying.h" + +#if GAMERELEASE +static bool gIsWorldPlaying = true; +#else +static bool gIsWorldPlaying = false; +#endif + +bool IsWorldPlaying () { return gIsWorldPlaying; } +void SetIsWorldPlaying (bool isPlaying) +{ + gIsWorldPlaying = isPlaying; +} diff --git a/Runtime/BaseClasses/IsPlaying.h b/Runtime/BaseClasses/IsPlaying.h new file mode 100644 index 0000000..740ffb8 --- /dev/null +++ b/Runtime/BaseClasses/IsPlaying.h @@ -0,0 +1,9 @@ +#ifndef ISPLAYING_H +#define ISPLAYING_H + +/// Is the world playmode? (Otherwise we are in editor mode) +bool EXPORT_COREMODULE IsWorldPlaying (); + +void SetIsWorldPlaying (bool isPlaying); + +#endif diff --git a/Runtime/BaseClasses/ManagerContext.cpp b/Runtime/BaseClasses/ManagerContext.cpp new file mode 100644 index 0000000..1824a84 --- /dev/null +++ b/Runtime/BaseClasses/ManagerContext.cpp @@ -0,0 +1,107 @@ +#include "UnityPrefix.h" +#include "ManagerContext.h" +#include "BaseObject.h" +#include "Configuration/UnityConfigure.h" + +ManagerContext::ManagerContext () +{ + for (int i=0;i<kManagerCount;i++) + m_Managers[i] = NULL; +} + +void ManagerContext::InitializeClasses () +{ + for (int i=0;i<kManagerCount;i++) + { + m_ManagerClassIDs[i] = -1; + #if DEBUGMODE + m_ManagerNames[i] = "Not initialized"; + #endif + } + +#if DEBUGMODE + #define INIT_MANAGER_CLASS(x) Assert(m_ManagerClassIDs[k##x] == -1); m_ManagerClassIDs[k##x] = Object::StringToClassID (#x); m_ManagerNames[k##x] = #x; +#else + #define INIT_MANAGER_CLASS(x) m_ManagerClassIDs[k##x] = Object::StringToClassID (#x); +#endif + + INIT_MANAGER_CLASS (PlayerSettings) + INIT_MANAGER_CLASS (InputManager) + INIT_MANAGER_CLASS (TagManager) + INIT_MANAGER_CLASS (AudioManager) + INIT_MANAGER_CLASS (ScriptMapper) + INIT_MANAGER_CLASS (MonoManager) + INIT_MANAGER_CLASS (GraphicsSettings) + INIT_MANAGER_CLASS (TimeManager) + INIT_MANAGER_CLASS (DelayedCallManager) + INIT_MANAGER_CLASS (PhysicsManager) + INIT_MANAGER_CLASS (BuildSettings) + INIT_MANAGER_CLASS (QualitySettings) + INIT_MANAGER_CLASS (ResourceManager) + INIT_MANAGER_CLASS (NetworkManager) + INIT_MANAGER_CLASS (MasterServerInterface) + INIT_MANAGER_CLASS (NavMeshLayers) + #if ENABLE_2D_PHYSICS + INIT_MANAGER_CLASS (Physics2DSettings) + #endif + + INIT_MANAGER_CLASS (SceneSettings) + INIT_MANAGER_CLASS (RenderSettings) + INIT_MANAGER_CLASS (HaloManager) + INIT_MANAGER_CLASS (LightmapSettings) + INIT_MANAGER_CLASS (NavMeshSettings) + +#if UNITY_EDITOR + for (int i=0;i<kManagerCount;i++) + { + Assert (m_ManagerClassIDs[i] != -1); + } + + std::vector<SInt32> allDerivedClasses; + Object::FindAllDerivedClasses(Object::StringToClassID("GameManager"), &allDerivedClasses); + if (allDerivedClasses.size() != kManagerCount) + { + ErrorString("Number of GameManager classes does not match number of game managers registered."); + } +#endif + +} + +static ManagerContext gContext; + +Object& GetManagerFromContext (int index) +{ +#if DEBUGMODE + + if( index >= ManagerContext::kManagerCount ) + FatalErrorString( "GetManagerFromContext: index for managers table is out of bounds" ); + + if( gContext.m_Managers[index] == NULL ) + { + char const* managerName = gContext.m_ManagerNames[ index ]; + FatalErrorString( Format("GetManagerFromContext: pointer to object of manager '%s' is NULL (table index %d)", managerName, index) ); + } +#endif + + return *gContext.m_Managers[index]; +} + +void ManagerContextInitializeClasses() +{ + gContext.InitializeClasses(); +} + +Object* GetManagerPtrFromContext (int index) +{ + return gContext.m_Managers[index]; +} + +void SetManagerPtrInContext(int index, Object* ptr) +{ + gContext.m_Managers[index] = ptr; +} + +const ManagerContext& GetManagerContext () +{ + return gContext; +} diff --git a/Runtime/BaseClasses/ManagerContext.h b/Runtime/BaseClasses/ManagerContext.h new file mode 100644 index 0000000..af4de7b --- /dev/null +++ b/Runtime/BaseClasses/ManagerContext.h @@ -0,0 +1,61 @@ +#ifndef MANAGERCONTEXT_H +#define MANAGERCONTEXT_H + +class Object; +struct ManagerContext; + +EXPORT_COREMODULE Object& GetManagerFromContext (int index); +EXPORT_COREMODULE Object* GetManagerPtrFromContext (int index); +void SetManagerPtrInContext(int index, Object* ptr); +void ManagerContextInitializeClasses(); + +#define GET_MANAGER(x) x& Get##x () { return reinterpret_cast<x&> (GetManagerFromContext (ManagerContext::k##x)); } +#define GET_MANAGER_PTR(x) x* Get##x##Ptr () { return reinterpret_cast<x*> (GetManagerPtrFromContext (ManagerContext::k##x)); } + +const ManagerContext& GetManagerContext (); + +struct ManagerContext +{ + enum Managers + { + // Global managers + kPlayerSettings = 0, + kInputManager, + kTagManager, + kAudioManager, + kScriptMapper, + kMonoManager, + kGraphicsSettings, + kTimeManager, + kDelayedCallManager, + kPhysicsManager, + kBuildSettings, + kQualitySettings, + kResourceManager, + kNetworkManager, + kMasterServerInterface, + kNavMeshLayers, + #if ENABLE_2D_PHYSICS + kPhysics2DSettings, + #endif + kGlobalManagerCount, + + // Level managers + kSceneSettings = kGlobalManagerCount, + kRenderSettings, + kHaloManager, + kLightmapSettings, + kNavMeshSettings, + kManagerCount + }; + + ManagerContext (); + Object* m_Managers[kManagerCount]; + int m_ManagerClassIDs[kManagerCount]; + #if DEBUGMODE + const char* m_ManagerNames[kManagerCount]; + #endif + void InitializeClasses (); +}; + +#endif diff --git a/Runtime/BaseClasses/ManagerContextLoading.cpp b/Runtime/BaseClasses/ManagerContextLoading.cpp new file mode 100644 index 0000000..76345d3 --- /dev/null +++ b/Runtime/BaseClasses/ManagerContextLoading.cpp @@ -0,0 +1,285 @@ +#include "UnityPrefix.h" +#include "ManagerContextLoading.h" +#include "Runtime/Profiler/Profiler.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/Serialize/SerializedFile.h" +#include "Runtime/Misc/GameObjectUtility.h" +#include "Runtime/BaseClasses/ManagerContext.h" +#include "Runtime/Serialize/AwakeFromLoadQueue.h" +#include "Runtime/BaseClasses/GameManager.h" +#include "Runtime/Utilities/vector_map.h" +#include "Runtime/Core/Callbacks/GlobalCallbacks.h" + + +PROFILER_INFORMATION(gCollectGameManagers, "CollectGameManagers", kProfilerLoading) + +void CollectLevelGameManagers (InstanceIDArray& outputObjects) +{ + PROFILER_AUTO(gCollectGameManagers,NULL); + const ManagerContext& context = GetManagerContext (); + for (int i=0;i<ManagerContext::kManagerCount;i++) + { + #if !UNITY_EDITOR + // In players we might have stripped all managers + if (!context.m_Managers[i]) + continue; + #endif + + Object& object = *context.m_Managers[i]; + if (object.IsDerivedFrom (ClassID (LevelGameManager))) + { + AssertIf (object.IsPersistent () || object.TestHideFlag (Object::kDontSave)); + outputObjects.push_back(object.GetInstanceID ()); + } + } +} + +void DestroyLevelManagers () +{ + InstanceIDArray loadedLevelManagers; + CollectLevelGameManagers (loadedLevelManagers); + for (InstanceIDArray::iterator i=loadedLevelManagers.begin ();i != loadedLevelManagers.end ();++i) + { + Object* o = Object::IDToPointer (*i); + AssertIf (o == NULL || o->IsPersistent ()); + DestroyObjectHighLevel (o); + } +} + + +/// Setup all managers to be called for a given mode. +/// When we're in edit mode, the Input, Dynamics, Fixed, Animation & Behaviour managers don't get +/// Updated. In Play mode, everything runs. +/// @param mode kPlayMode or kEditMode + + + +void RemoveDuplicateGameManagers () +{ + const ManagerContext& context = GetManagerContext (); + + for (int i=0;i<ManagerContext::kManagerCount;i++) + { + Assert(GetManagerPtrFromContext(i) != NULL); + } + + vector<GameManager*> managers; + Object::FindObjectsOfType (&managers); + + // Remove all managers that are not in the manager context! + for (int m=0;m<managers.size ();m++) + { + bool isUsed = false; + for (int i=0;i<ManagerContext::kManagerCount;i++) + { + if ((Object*)managers[m] == context.m_Managers[i]) + isUsed = true; + } + + if (!isUsed) + { + Object* obj = managers[m]; + FatalErrorIf (obj->IsPersistent ()); + ErrorString (Format("Removing duplicate game manager (%s)!", obj->GetClassName().c_str())); + FatalErrorIf (PPtr<Object> (managers[m])->IsPersistent ()); + DestroyObjectHighLevel (managers[m]); + } + } + + ErrorIf (Object::FindAllDerivedObjects (ClassID (GameManager), NULL) != ManagerContext::kManagerCount); +} + + +typedef vector_map<SInt32, SInt32> ClassIDToInstanceID; +static void ExtractGlobalManagers (const std::string& path, ClassIDToInstanceID& managers) +{ + GetPersistentManager ().Lock(); + SerializedFile* stream = GetPersistentManager ().GetSerializedFileInternal(path); + if (stream == NULL) + { + GetPersistentManager ().Unlock(); + return; + } + + vector<LocalIdentifierInFileType> sourceFileIDs; + stream->GetAllFileIDs(&sourceFileIDs); + + for (int i=0;i<sourceFileIDs.size ();i++) + { + LocalIdentifierInFileType fileID = sourceFileIDs[i]; + SInt32 classID = stream->GetClassID (fileID); + + if (Object::IsDerivedFromClassID (classID, ClassID (GlobalGameManager))) + { + SInt32 instanceID = GetPersistentManager().GetInstanceIDFromPathAndFileID (path, fileID); + managers.push_unsorted (classID, instanceID); + } + } + + managers.sort(); + + GetPersistentManager ().Unlock(); +} + +static Object* LoadManager (const ClassIDToInstanceID& managers, int classID) +{ + ClassIDToInstanceID::const_iterator i = managers.find(classID); + if (i == managers.end()) + return NULL; + + Object* obj = dynamic_instanceID_cast<Object*> (i->second); + Assert(dynamic_pptr_cast<GlobalGameManager*> (obj) != NULL); + + return obj; +} + + +string PlayerLoadSettingsAndInput(const std::string& dataFile) +{ + ClassIDToInstanceID managers; + ExtractGlobalManagers (dataFile, managers); + + ManagerContext::Managers loadManagers[] = + { + ManagerContext::kPlayerSettings, + ManagerContext::kInputManager, + ManagerContext::kBuildSettings, + ManagerContext::kGraphicsSettings, + ManagerContext::kQualitySettings, + }; + + const ManagerContext& ctx = GetManagerContext(); + for( int i = 0; i < sizeof(loadManagers)/sizeof(loadManagers[0]); ++i ) + { + int index = loadManagers[i]; + int classID = ctx.m_ManagerClassIDs[index]; + SetManagerPtrInContext(index, LoadManager(managers, classID)); + if (ctx.m_Managers[index] == NULL || !ctx.m_Managers[index]->IsDerivedFrom(classID)) + return Format("Could..... not preload global game manager #%i i=%i", index,i); + } + + return string(); +} + +string PlayerLoadGlobalManagers (const char* dataFile) +{ + ClassIDToInstanceID managers; + ExtractGlobalManagers (dataFile, managers); + + // Load all game managers! All global game managers are coming first in the main data + // (Global game managers have to be loaded before selecting the screen resolution. + // ProjectSettings i used by screen selector and RenderManager, InputManager by screen switching) + for (int i=0;i<ManagerContext::kGlobalManagerCount;i++) + { + int classID = GetManagerContext().m_ManagerClassIDs[i]; + + Object* manager = NULL; + + // Manager is dead-code stripped + if (classID != -1) + { + // Try to load manager + manager = LoadManager(managers, classID); + + // Manager could not be loaded, create it from code instead + if (classID != -1 && manager == NULL) + { + manager = CreateGameManager (classID); + printf_console("Loading manager failed, creating from code %d\n", classID); + } + } + + // Assign manager as soon as it is created. + SetManagerPtrInContext(i, manager); + } + + std::string resetError = ResetManagerContextFromLoaded(); + if( !resetError.empty() ) + return Format("PlayerLoadGlobalManagers: %s\n", resetError.c_str()); + + GetPersistentManager().DoneLoadingManagers(); + + return string(); +} + +string ResetManagerContextFromLoaded () +{ + GlobalCallbacks::Get().managersWillBeReloadedHack.Invoke(); + + string error; + const ManagerContext& context = GetManagerContext (); + + vector<GameManager*> allManagers; + Object::FindObjectsOfType (&allManagers); + + for (int i=0;i<ManagerContext::kManagerCount;i++) + { + SetManagerPtrInContext(i, NULL); + + if (context.m_ManagerClassIDs[i] == -1) + continue; + + vector<GameManager*> specificManagers; + for (int j=0;j<allManagers.size();j++) + { + if (allManagers[j]->IsDerivedFrom(context.m_ManagerClassIDs[i])) + { + specificManagers.push_back(allManagers[j]); + } + } + + if (specificManagers.size () == 1) + SetManagerPtrInContext(i, specificManagers[0]); + else if (specificManagers.size () == 0) + { + // missing global managers are serious errors + if( i < ManagerContext::kGlobalManagerCount ) + error += " Missing " + Object::ClassIDToString (context.m_ManagerClassIDs[i]); + } + else + { + // missing global managers are serious errors + if( i < ManagerContext::kGlobalManagerCount ) + error += " Too many instances of " + Object::ClassIDToString (context.m_ManagerClassIDs[i]); + } + } + return error; +} + +void LoadManagers (AwakeFromLoadQueue& awakeFromLoadQueue) +{ + AwakeFromLoadMode awakeMode = (AwakeFromLoadMode)(kDidLoadFromDisk | kDidLoadThreaded); + + awakeFromLoadQueue.PersistentManagerAwakeFromLoad(kManagersQueue, awakeMode, NULL); + awakeFromLoadQueue.ClearQueue(kManagersQueue); + + // Get all managers that are in memory + map<int, set<GameManager*> > managers; + vector<GameManager*> temp; + Object::FindObjectsOfType (&temp); + for (int i=0;i<temp.size ();i++) + managers[temp[i]->GetClassID ()].insert (temp[i]); + + const ManagerContext& context = GetManagerContext(); + // Load the managers in the order defined by the context. + // Create new managers if necessary. + for (int i=0;i<ManagerContext::kManagerCount;i++) + { + SetManagerPtrInContext(i, NULL); + + if (context.m_ManagerClassIDs[i] == -1) + continue; + + const set<GameManager*>& manager = managers[context.m_ManagerClassIDs[i]]; + + if (manager.size () == 1) + SetManagerPtrInContext(i, *manager.begin ()); + else if (manager.size () == 0) + SetManagerPtrInContext(i, CreateGameManager (context.m_ManagerClassIDs[i])); + else + { + ErrorString("Multiple managers are loaded of type: " + Object::ClassIDToString(context.m_ManagerClassIDs[i])); + SetManagerPtrInContext(i, *manager.begin ()); + } + } +} diff --git a/Runtime/BaseClasses/ManagerContextLoading.h b/Runtime/BaseClasses/ManagerContextLoading.h new file mode 100644 index 0000000..f8187d7 --- /dev/null +++ b/Runtime/BaseClasses/ManagerContextLoading.h @@ -0,0 +1,17 @@ +#ifndef _MANAGERCONTEXT_LOADING_H_ +#define _MANAGERCONTEXT_LOADING_H_ + +#include "Runtime/Utilities/dynamic_array.h" + +typedef dynamic_array<int> InstanceIDArray; +class AwakeFromLoadQueue; + +void CollectLevelGameManagers (InstanceIDArray& outputObjects); +void DestroyLevelManagers (); +void RemoveDuplicateGameManagers (); +std::string PlayerLoadSettingsAndInput(const std::string& dataFile); +std::string PlayerLoadGlobalManagers (const char* dataFile); +std::string ResetManagerContextFromLoaded (); +void LoadManagers (AwakeFromLoadQueue& awakeFromLoadQueue); + +#endif
\ No newline at end of file diff --git a/Runtime/BaseClasses/MessageHandler.cpp b/Runtime/BaseClasses/MessageHandler.cpp new file mode 100644 index 0000000..71ef06e --- /dev/null +++ b/Runtime/BaseClasses/MessageHandler.cpp @@ -0,0 +1,196 @@ +#include "UnityPrefix.h" +#include "MessageHandler.h" +#include "Runtime/Utilities/LogAssert.h" +#include <map> +#include <list> + +using namespace std; + +MessageForwarder::MessageForwarder () +{ + m_GeneralMessage = NULL; + m_CanHandleGeneralMessage = NULL; +} + +void MessageForwarder::HandleMessage (void* receiver, int messageID, MessageData& messageData) +{ + MessageCallback messagePtr = m_GeneralMessage; + if (messageID < m_SupportedMessages.size () && m_SupportedMessages[messageID] != NULL) + messagePtr = m_SupportedMessages[messageID]; + + AssertIf (messagePtr == NULL); + messagePtr (receiver, messageID, messageData); +} + +bool MessageForwarder::HasMessageCallback (const MessageIdentifier& identifier) +{ + if (identifier.messageID < m_SupportedMessages.size () && m_SupportedMessages[identifier.messageID]) + return true; + else + return m_GeneralMessage != NULL && (identifier.options & MessageIdentifier::kSendToScripts); +} + +bool MessageForwarder::WillHandleMessage (void* receiver, const MessageIdentifier& identifier) +{ + if (identifier.messageID < m_SupportedMessages.size () && m_SupportedMessages[identifier.messageID]) + return true; + + if (m_GeneralMessage && (identifier.options & MessageIdentifier::kSendToScripts)) + { + AssertIf (m_CanHandleGeneralMessage == NULL); + MessageData data; + return m_CanHandleGeneralMessage (receiver, identifier.messageID, data); + } + return false; +} + +int MessageForwarder::GetExpectedParameter (int messageID) +{ + if (messageID < m_SupportedMessages.size ()) + return m_SupportedMessagesParameter[messageID]; + else + return 0; +} + +void MessageForwarder::RegisterMessageCallback (int messageID, MessageCallback message, int classId) +{ + AssertIf (messageID == -1); + if (messageID >= m_SupportedMessages.size ()) + { + m_SupportedMessages.resize (messageID + 1, NULL); + m_SupportedMessagesParameter.resize (messageID + 1, 0); + } + m_SupportedMessages[messageID] = message; + m_SupportedMessagesParameter[messageID] = classId; +} + +void MessageForwarder::RegisterAllMessagesCallback (MessageCallback message, CanHandleMessageCallback canHandle) +{ + m_GeneralMessage = message; + m_CanHandleGeneralMessage = canHandle; +} + +void MessageForwarder::AddBaseMessages (const MessageForwarder& baseClass) +{ + int maxsize = max (m_SupportedMessages.size (), baseClass.m_SupportedMessages.size ()); + m_SupportedMessages.resize (maxsize, NULL); + m_SupportedMessagesParameter.resize (maxsize, 0); + for (int i=0;i<m_SupportedMessages.size ();i++) + { + if (m_SupportedMessages[i] == NULL && i < baseClass.m_SupportedMessages.size ()) + { + m_SupportedMessages[i] = baseClass.m_SupportedMessages[i]; + m_SupportedMessagesParameter[i] = baseClass.m_SupportedMessagesParameter[i]; + } + } + + if (m_GeneralMessage == NULL) + m_GeneralMessage = baseClass.m_GeneralMessage; +} + +void MessageHandler::InitializeMessageIdentifiers () +{ + MessageIdentifier::RegisteredMessages& identifiers = MessageIdentifier::GetRegisteredMessages (); + MessageIdentifier::SortedMessages sortedMessages = MessageIdentifier::GetSortedMessages(false); + + m_MessageIDToIdentifier.clear (); + m_MessageNameToIndex.clear (); + + // Build m_MessageIDToName and m_MessageNameToIndex + // by copying the data from the sorted messages map + m_MessageIDToIdentifier.resize (sortedMessages.size ()); + int index = 0; + MessageIdentifier::SortedMessages::iterator i; + for (i = sortedMessages.begin ();i != sortedMessages.end ();i++) + { + m_MessageNameToIndex[i->first] = index; + m_MessageIDToIdentifier[index] = i->second; + m_MessageIDToIdentifier[index].messageID = index; + index++; + } + + + // Setup the messageIDs of all registered message Identifiers + MessageIdentifier::RegisteredMessages::iterator j; + for (j = identifiers.begin ();j != identifiers.end ();j++) + { + AssertIf (*j == NULL); + MessageIdentifier& identifier = **j; + if (m_MessageNameToIndex.count (identifier.messageName)) + { + identifier.messageID = m_MessageNameToIndex[identifier.messageName]; + } + } +} + +void MessageHandler::Initialize (const MessageForwarders& receivers) +{ + m_Forwarder = receivers; + m_MessageCount = m_MessageNameToIndex.size (); + m_ClassCount = receivers.size (); + + // Precalculate supported messages + m_SupportedMessages.resize (m_ClassCount * m_MessageCount); + for (int c=0;c<m_ClassCount;c++) + { + for (int m=0;m<m_MessageCount;m++) + { + bool hasCallback = m_Forwarder[c].HasMessageCallback (m_MessageIDToIdentifier[m]); + if (hasCallback) + { + // Check if the parameter is correct and print an error if they dont match + int wantedParameter = m_Forwarder[c].GetExpectedParameter (m); + int messageParameter = m_MessageIDToIdentifier[m].parameterClassId; + if (wantedParameter != 0 && messageParameter != wantedParameter) + { + char buffy[4096]; + char const format[] = "The message: %s in the class with " + "classID: %d uses a parameter type " + "that is different from the " + "message's parameter type: %d != %d."; + sprintf (buffy, format, m_MessageIDToIdentifier[m].messageName, + c, wantedParameter, messageParameter); + ErrorString (buffy); + hasCallback = false; + } + } + m_SupportedMessages[m * m_ClassCount + c] = hasCallback; + } + } +} + +bool MessageHandler::WillHandleMessage (void* receiver, int classID, int messageID) +{ + AssertIf (!HasMessageCallback (classID, messageID)); + return m_Forwarder[classID].WillHandleMessage (receiver, m_MessageIDToIdentifier[messageID]); +} + +int MessageHandler::MessageNameToID (const string& name) +{ + MessageNameToIndex::iterator i = m_MessageNameToIndex.find (name); + if (i == m_MessageNameToIndex.end ()) + return -1; + else + return i->second; +} + +const char* MessageHandler::MessageIDToName (int messageID) +{ + AssertIf (messageID < 0); + AssertIf (messageID >= m_MessageIDToIdentifier.size ()); + return m_MessageIDToIdentifier[messageID].messageName; +} + +int MessageHandler::MessageIDToParameter (int messageID) +{ + AssertIf (messageID < 0); + AssertIf (messageID >= m_MessageIDToIdentifier.size ()); + return m_MessageIDToIdentifier[messageID].parameterClassId; +} + +MessageIdentifier MessageHandler::MessageIDToMessageIdentifier (int messageID) +{ + AssertIf (messageID < 0); + AssertIf (messageID >= m_MessageIDToIdentifier.size ()); + return m_MessageIDToIdentifier[messageID]; +} diff --git a/Runtime/BaseClasses/MessageHandler.h b/Runtime/BaseClasses/MessageHandler.h new file mode 100644 index 0000000..5d0fa46 --- /dev/null +++ b/Runtime/BaseClasses/MessageHandler.h @@ -0,0 +1,112 @@ +#ifndef MESSAGEHANDLER_H +#define MESSAGEHANDLER_H + +#include <vector> +#include <map> +#include <string> +#include "Runtime/Utilities/dynamic_bitset.h" +#include "Runtime/Misc/Allocator.h" +#include "MessageIdentifier.h" + +/* + DOCUMENT_______________________________________ +*/ + +class MessageForwarder +{ + typedef void (*MessageCallback)(void* Receiver, int messageID, MessageData& data); + typedef bool (*CanHandleMessageCallback)(void* Receiver, int messageID, MessageData& data); + std::vector<MessageCallback> m_SupportedMessages; + std::vector<int> m_SupportedMessagesParameter; + MessageCallback m_GeneralMessage; + CanHandleMessageCallback m_CanHandleGeneralMessage; + + public: + + MessageForwarder (); + + // Returns true if a message callback exists for the class and the messageID + bool HasMessageCallback (const MessageIdentifier& messageID); + + // Returns true a message callback exists and the message will actually be handled. + // This is used to find out if a message will *actually* be forwared, eg. HasMessageCallback will always return true + // for eg. ScriptBehaviours which checks at runtime if the message is supported by the script. + bool WillHandleMessage (void* receiver, const MessageIdentifier& messageID); + + /// Calls the message + /// the notification can be handled using CanHandleNotification + void HandleMessage (void* receiver, int messageID, MessageData& notificationData); + + void RegisterMessageCallback (int messageID, MessageCallback message, int classId); + void RegisterAllMessagesCallback (MessageCallback message, CanHandleMessageCallback canHandleMessage); + + /// Returns the parameter that the receiver expects from a message. If + /// the method doesn't expect a parameter, is not supported, or uses a + /// general message handler, 0 is returned. + int GetExpectedParameter (int messageID); + + /// Adds all messages that baseMessages contains but this MessageReceiver does not handle yet. + /// AddBaseNotifications is used to implement derivation by calling AddBaseNotifications for all base classes. + void AddBaseMessages (const MessageForwarder& baseMessages); +}; + +typedef std::vector<MessageForwarder, STL_ALLOCATOR_ALIGNED(kMemPermanent, MessageForwarder, 8) > MessageForwarders; + +class MessageHandler +{ + dynamic_bitset m_SupportedMessages; + MessageForwarders m_Forwarder; + int m_ClassCount; + int m_MessageCount; + + typedef std::vector<MessageIdentifier> MessageIDToIdentifier; + MessageIDToIdentifier m_MessageIDToIdentifier; + typedef std::map<std::string, int> MessageNameToIndex; + MessageNameToIndex m_MessageNameToIndex; + + public: + + /// Initializes all message forwarders and precalculates the supporetedmessages bit array + void Initialize (const MessageForwarders& receivers); + + // Generates the messageIndices for all MessageIdentifier's + // Gets the list of all message identifiers which are created by constructor of MessageIdentifier + // Sorts the messages by name and builds the MessageNameToIndex and m_MessageIDToIdentifier maps. + void InitializeMessageIdentifiers (); + + // Returns true if a message callback exists for the class and the messageID + bool HasMessageCallback (int classID, int messageID) { return m_SupportedMessages.test (messageID * m_ClassCount + classID); } + + // Returns true a message callback exists and the message will actually be handled. + // This is used to find out if a message will *actually* be forwared, eg. HasMessageCallback will always return true + // for eg. ScriptBehaviours which checks at runtime if the message is supported by the script. + bool WillHandleMessage (void* receiver, int classID, int messageID); + + /// Forwards a message to the appropriate MessageForwarder + void HandleMessage (void* receiver, int classID, int messageID, MessageData& messageData) + { + AssertIf (classID >= m_ClassCount); + SET_ALLOC_OWNER(NULL); + m_Forwarder[classID].HandleMessage (receiver, messageID, messageData); + } + + void SetMessageEnabled (int classID, int messageID, bool enabled) + { + // You are probably doing something wrong if you enable/disable a message twice + DebugAssertIf(m_SupportedMessages[messageID * m_ClassCount + classID] == enabled); + m_SupportedMessages[messageID * m_ClassCount + classID] = enabled; + } + + // Converts a message name to an ID if the name is not registered returns -1 + int MessageNameToID (const std::string& name); + // Converts a messageID to its message name. The messageID has to exist + const char* MessageIDToName (int messageID); + // Converts a messageID to its parameter eg. ClassID (float). The messageID has to exist + int MessageIDToParameter (int messageID); + MessageIdentifier MessageIDToMessageIdentifier (int messageID); + + // Returns the number of registered messages + int GetMessageCount () { return m_MessageCount; } +}; + +#endif diff --git a/Runtime/BaseClasses/MessageIdentifier.cpp b/Runtime/BaseClasses/MessageIdentifier.cpp new file mode 100644 index 0000000..fc55509 --- /dev/null +++ b/Runtime/BaseClasses/MessageIdentifier.cpp @@ -0,0 +1,86 @@ +#include "UnityPrefix.h" +#include "MessageIdentifier.h" +using namespace std; + +MessageIdentifier::RegisteredMessages* gRegisteredMessageIdentifiers = NULL; + +MessageIdentifier::MessageIdentifier (const char* name, Options opts, int classId, const char* scriptParamName) +{ + AssertIf (name == NULL); + + messageName = name; + parameterClassId = classId; + scriptParameterName = scriptParamName; + messageID = -1; + options = (int)opts; + if (gRegisteredMessageIdentifiers == NULL) + gRegisteredMessageIdentifiers = new RegisteredMessages(); + + gRegisteredMessageIdentifiers->push_back (this); +} + +MessageIdentifier::RegisteredMessages& MessageIdentifier::GetRegisteredMessages () +{ + AssertIf (gRegisteredMessageIdentifiers == NULL); + return *gRegisteredMessageIdentifiers; +} + +MessageIdentifier::SortedMessages MessageIdentifier::GetSortedMessages (bool notificationMessages) +{ + // Build sorted Messages map. Which contains all messages sorted by name. + SortedMessages sortedMessages; + RegisteredMessages& messages = GetRegisteredMessages(); + RegisteredMessages::iterator j; + for (j = messages.begin ();j != messages.end ();j++) + { + MessageIdentifier& identifier = **j; + AssertIf (identifier.messageName == NULL); + + bool usesNotifications = identifier.options & kUseNotificationManager; + if (usesNotifications != notificationMessages) + continue; + + SortedMessages::iterator found = sortedMessages.find (identifier.messageName); + if (found == sortedMessages.end ()) + { + sortedMessages.insert (make_pair (string (identifier.messageName), identifier)); + } + else + { + if (identifier.parameterClassId != found->second.parameterClassId) + { + string error = "There are conflicting definitions of the message: "; + error += identifier.messageName; + error += ". The parameter of one message has to be the same across all definitions of that message."; + ErrorString (error); + } + + if (identifier.scriptParameterName != found->second.scriptParameterName) + { + string error = "There are conflicting definitions of the message: "; + error += identifier.messageName; + error += ". The parameter of one message has to be the same across all definitions of that message."; + ErrorString (error); + } + + if (identifier.options != found->second.options) + { + string error = "There are conflicting options of the message: "; + error += identifier.messageName; + ErrorString (error); + } + } + } + return sortedMessages; +} + +void MessageIdentifier::Cleanup () +{ + delete gRegisteredMessageIdentifiers; +} + +#include "Runtime/Dynamics/Collider.h" +#undef MESSAGE_IDENTIFIER +#define MESSAGE_IDENTIFIER(n,p) const EXPORT_COREMODULE MessageIdentifier n p +#include "MessageIdentifiers.h" + diff --git a/Runtime/BaseClasses/MessageIdentifier.h b/Runtime/BaseClasses/MessageIdentifier.h new file mode 100644 index 0000000..9469ef8 --- /dev/null +++ b/Runtime/BaseClasses/MessageIdentifier.h @@ -0,0 +1,109 @@ +#ifndef MESSAGEIDENTIFIER_H +#define MESSAGEIDENTIFIER_H + +#include <list> +#include <typeinfo> +#include "Runtime/Utilities/LogAssert.h" +#include "Runtime/Misc/Allocator.h" +#include <map> +#include "Runtime/Allocator/MemoryMacros.h" +#include "Runtime/Scripting/Backend/ScriptingTypes.h" +#include "Runtime/BaseClasses/ObjectDefines.h" +#include "Runtime/BaseClasses/ClassIDs.h" + +struct MessageData +{ + int type; +private: + // Note: on Metro WinRT types cannot be located in union, so don't use union! + intptr_t data; + ScriptingObjectPtr scriptingObjectData; +public: + MessageData () : scriptingObjectData(SCRIPTING_NULL) + { + data = 0; type = 0; + } + + template<class T> + void SetData (T inData, int classId) + { + // Check if SetData is used instead of SetScriptingObjectData + Assert (type != ClassID (MonoObject)); + AssertIf (sizeof (T) > sizeof (data)); // increase the data size + *reinterpret_cast<T*> (&data) = *reinterpret_cast<T*> (&inData); + type = classId; + } + + template<class T> + T GetData () + { + // Check if GetData is used instead of GetScriptingObjectData + Assert (type != ClassID (MonoObject)); + return *reinterpret_cast<T*> (&data); + } + + intptr_t& GetGenericDataRef () + { + // Check if GetGenericDataRef is used instead of GetScriptingObjectData + Assert (type != ClassID (MonoObject)); + return data; + } + + void SetScriptingObjectData (ScriptingObjectPtr inData) + { + scriptingObjectData = inData; + type = ClassID (MonoObject); + } + + ScriptingObjectPtr GetScriptingObjectData () + { + Assert (type == ClassID (MonoObject)); + return scriptingObjectData; + } + +}; + +// usage: +// MessageIdentifier kOnTransformChanged ("OnTransformChanged", MessageIdentifier::kDontSendToScripts); +// MessageIdentifier kOnTransformChanged ("OnTransformChanged", MessageIdentifier::kDontSendToScripts, ClassID (int)); + + +class MessageIdentifier +{ + public: + + enum Options { kDontSendToScripts = 0, kSendToScripts = 1 << 0, kUseNotificationManager = 1 << 1, kDontSendToDisabled = 1 << 2 }; + + const char* messageName; + const char* scriptParameterName; + int messageID; + int parameterClassId; + int options; + + /// Place the MessageIdentifier as a global variable! + /// The constructor must be called before InitializeEngine + explicit MessageIdentifier (const char* name, Options opt, int classId = 0, const char* scriptParamName = NULL); + + typedef std::list<MessageIdentifier*, STL_ALLOCATOR(kMemPermanent, MessageIdentifier*) > RegisteredMessages; + typedef std::map<std::string, MessageIdentifier> SortedMessages; + + // All registered message identifiers + static RegisteredMessages& GetRegisteredMessages (); + // Sorted list of message identifiers + // - Duplicate message identifiers are ignored + // - Only notification messages are returned if onlyNotificationMessages otherwise only normal messages are returned (See kUseNotificationManager) + static SortedMessages GetSortedMessages (bool onlyNotificationMessages); + + static void Cleanup (); + + // Only to be used by subclasses + MessageIdentifier () + { + messageID = -1; scriptParameterName = NULL; messageName = NULL; + options = kSendToScripts; + } +}; + +#include "MessageIdentifiers.h" + +#endif diff --git a/Runtime/BaseClasses/MessageIdentifiers.h b/Runtime/BaseClasses/MessageIdentifiers.h new file mode 100644 index 0000000..3d285ce --- /dev/null +++ b/Runtime/BaseClasses/MessageIdentifiers.h @@ -0,0 +1,86 @@ +#ifndef MESSAGE_IDENTIFIER +#define MESSAGE_IDENTIFIER(n,p) const extern EXPORT_COREMODULE MessageIdentifier n +#endif + +// NOTE: Whether messages are sent to scripts at all or sent to disabled scripts is determined on a per callback basis here, +// make sure you set MessageIdentifier::Options appropriately for your message, leaving out kDontSendToDisabled will +// mean that script code in the callback function will be executed regardless of whether the script is enabled or not. + +MESSAGE_IDENTIFIER(kEnterTrigger, ("OnTriggerEnter", MessageIdentifier::kSendToScripts, ClassID (Collider))); +MESSAGE_IDENTIFIER(kExitTrigger, ("OnTriggerExit", MessageIdentifier::kSendToScripts, ClassID (Collider))); +MESSAGE_IDENTIFIER(kStayTrigger, ("OnTriggerStay", MessageIdentifier::kSendToScripts, ClassID (Collider))); +MESSAGE_IDENTIFIER(kEnterContact, ("OnCollisionEnter", MessageIdentifier::kSendToScripts, ClassID (Collision))); +MESSAGE_IDENTIFIER(kExitContact, ("OnCollisionExit", MessageIdentifier::kSendToScripts, ClassID (Collision))); +MESSAGE_IDENTIFIER(kStayContact, ("OnCollisionStay", MessageIdentifier::kSendToScripts, ClassID (Collision))); +MESSAGE_IDENTIFIER(kCollisionEnter2D, ("OnCollisionEnter2D", MessageIdentifier::kSendToScripts, ClassID (Collision2D))); +MESSAGE_IDENTIFIER(kCollisionExit2D, ("OnCollisionExit2D", MessageIdentifier::kSendToScripts, ClassID (Collision2D))); +MESSAGE_IDENTIFIER(kCollisionStay2D, ("OnCollisionStay2D", MessageIdentifier::kSendToScripts, ClassID (Collision2D))); +MESSAGE_IDENTIFIER(kTriggerEnter2D, ("OnTriggerEnter2D", MessageIdentifier::kSendToScripts, ClassID (Collider2D))); +MESSAGE_IDENTIFIER(kTriggerExit2D, ("OnTriggerExit2D", MessageIdentifier::kSendToScripts, ClassID (Collider2D))); +MESSAGE_IDENTIFIER(kTriggerStay2D, ("OnTriggerStay2D", MessageIdentifier::kSendToScripts, ClassID (Collider2D))); +MESSAGE_IDENTIFIER(kJointBreak, ("OnJointBreak", MessageIdentifier::kSendToScripts, ClassID (float))); +MESSAGE_IDENTIFIER(kTransformChanged, ("OnTransformChanged", MessageIdentifier::kDontSendToScripts, ClassID (int))); +MESSAGE_IDENTIFIER(kParticleCollisionEvent, ("OnParticleCollision", MessageIdentifier::kSendToScripts, ClassID (GameObject))); +MESSAGE_IDENTIFIER(kDidModifyValidity, ("OnDidModifyMeshValidity", MessageIdentifier::kDontSendToScripts)); +MESSAGE_IDENTIFIER(kDidDeleteMesh, ("OnDidModifyMeshDelete", MessageIdentifier::kDontSendToScripts, ClassID (Mesh))); +MESSAGE_IDENTIFIER(kDidModifyBounds, ("OnDidModifyMeshBounds", MessageIdentifier::kDontSendToScripts)); +MESSAGE_IDENTIFIER(kDidModifyMesh, ("OnDidModifyMesh", MessageIdentifier::kDontSendToScripts)); +MESSAGE_IDENTIFIER(kLayerChanged, ("OnLayersChanged", MessageIdentifier::kDontSendToScripts)); +MESSAGE_IDENTIFIER(kDestroyedComponentNotification, ("OnDestroyedComponent", MessageIdentifier::kUseNotificationManager)); +MESSAGE_IDENTIFIER(kDidAddComponent , ("OnDidAddComponent", MessageIdentifier::kDontSendToScripts, ClassID (Component))); +MESSAGE_IDENTIFIER(kBecameVisible, ("OnBecameVisible", MessageIdentifier::kSendToScripts)); +MESSAGE_IDENTIFIER(kBecameInvisible, ("OnBecameInvisible", MessageIdentifier::kSendToScripts)); +MESSAGE_IDENTIFIER(kOnWillRenderObject, ("OnWillRenderObject", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled))); +MESSAGE_IDENTIFIER(kPreCull, ("OnPreCull", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled))); +MESSAGE_IDENTIFIER(kPostRender, ("OnPostRender", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled))); +MESSAGE_IDENTIFIER(kPreRender, ("OnPreRender", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled))); +MESSAGE_IDENTIFIER(kControllerColliderHit, ("OnControllerColliderHit", MessageIdentifier::kSendToScripts, ClassID (MonoObject))); +MESSAGE_IDENTIFIER(kAnimatorMove, ("OnAnimatorMove", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled))); +MESSAGE_IDENTIFIER(kAnimatorMoveBuiltin, ("OnAnimatorMoveBuiltin", MessageIdentifier::kDontSendToScripts, ClassID(RootMotionData))); +MESSAGE_IDENTIFIER(kAnimatorIK, ("OnAnimatorIK", (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts|MessageIdentifier::kDontSendToDisabled), ClassID (int))); +MESSAGE_IDENTIFIER(kForceRecreateCollider, ("ForceRecreateCollider", MessageIdentifier::kDontSendToScripts)); +MESSAGE_IDENTIFIER(kRecreateRigidBodyDependencies2D, ("RecreateRigidBodyDependencies2D", MessageIdentifier::kDontSendToScripts, ClassID (Rigidbody2D))); +MESSAGE_IDENTIFIER(kServerInitialized, ("OnServerInitialized", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkPlayer")); +MESSAGE_IDENTIFIER(kPlayerConnected, ("OnPlayerConnected", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkPlayer")); +MESSAGE_IDENTIFIER(kConnectedToServer, ("OnConnectedToServer", MessageIdentifier::kSendToScripts)); +MESSAGE_IDENTIFIER(kPlayerDisconnected, ("OnPlayerDisconnected", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkPlayer")); +MESSAGE_IDENTIFIER(kDisconnectedFromServer, ("OnDisconnectedFromServer", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkDisconnection")); +MESSAGE_IDENTIFIER(kConnectionAttemptFailed, ("OnFailedToConnect", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkConnectionError")); +MESSAGE_IDENTIFIER(kMasterServerConnectionAttemptFailed, ("OnFailedToConnectToMasterServer", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkConnectionError")); +MESSAGE_IDENTIFIER(kDisconnectedFromMasterServer, ("OnDisconnectedFromMasterServer", MessageIdentifier::kSendToScripts, ClassID (int), "NetworkDisconnection")); +MESSAGE_IDENTIFIER(kMasterServerEvent, ("OnMasterServerEvent", MessageIdentifier::kSendToScripts, ClassID (int), "MasterServerEvent")); +MESSAGE_IDENTIFIER(kLevelWasLoaded, ("OnLevelWasLoaded", MessageIdentifier::kSendToScripts, ClassID (int))); +MESSAGE_IDENTIFIER(kPlayerPause, ("OnApplicationPause", MessageIdentifier::kSendToScripts, ClassID (bool))); +MESSAGE_IDENTIFIER(kPlayerFocus, ("OnApplicationFocus", MessageIdentifier::kSendToScripts, ClassID (bool))); +MESSAGE_IDENTIFIER(kPlayerQuit, ("OnApplicationQuit", MessageIdentifier::kSendToScripts)); +MESSAGE_IDENTIFIER(kTerrainChanged, ("OnTerrainChanged", MessageIdentifier::kSendToScripts, ClassID (int), "TerrainChangedFlags")); // Investigate getting rid of these +MESSAGE_IDENTIFIER(kSetLightmapIndex, ("SetLightmapIndex", MessageIdentifier::kSendToScripts, ClassID (int))); // +MESSAGE_IDENTIFIER(kShiftLightmapIndex, ("ShiftLightmapIndex", MessageIdentifier::kSendToScripts, ClassID (int))); // +MESSAGE_IDENTIFIER(kDidModifyAnimatorController, ("OnDidModifyAnimatorController", MessageIdentifier::kDontSendToScripts)); +MESSAGE_IDENTIFIER(kDidModifyMotion, ("OnDidModifyMotion", MessageIdentifier::kDontSendToScripts)); +MESSAGE_IDENTIFIER(kDidVelocityChange, ("OnDidVelocityChange", MessageIdentifier::kDontSendToDisabled, ClassID (Vector3f))); +MESSAGE_IDENTIFIER(kDidModifyAvatar, ("OnDidModifyAvatar", MessageIdentifier::kDontSendToScripts)); + +#if ENABLE_NEW_EVENT_SYSTEM + +#ifndef SEND_TO_SCRIPTS +#define SEND_TO_SCRIPTS (MessageIdentifier::Options)(MessageIdentifier::kSendToScripts | MessageIdentifier::kDontSendToDisabled) +#endif + +MESSAGE_IDENTIFIER(kOnMouseEnterEvent, ("OnMouseEnterEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnMouseExitEvent, ("OnMouseExitEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnMouseMoveEvent, ("OnMouseMoveEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnPressEvent, ("OnPressEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnReleaseEvent, ("OnReleaseEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnDragEvent, ("OnDragEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnDropEvent, ("OnDropEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnDragEnterEvent, ("OnDragEnterEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnDragExitEvent, ("OnDragExitEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnClickEvent, ("OnClickEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnKeyEvent, ("OnKeyEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnSelectEvent, ("OnSelectEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnDeselectEvent, ("OnDeselectEvent", SEND_TO_SCRIPTS)); +MESSAGE_IDENTIFIER(kOnScrollEvent, ("OnScrollEvent", SEND_TO_SCRIPTS)); +//MESSAGE_IDENTIFIER(kOnShowTooltip, ("OnShowTooltip", SEND_TO_SCRIPTS)); +//MESSAGE_IDENTIFIER(kOnHideTooltip, ("OnHideTooltip", SEND_TO_SCRIPTS)); +#endif diff --git a/Runtime/BaseClasses/NamedObject.cpp b/Runtime/BaseClasses/NamedObject.cpp new file mode 100644 index 0000000..41acc3a --- /dev/null +++ b/Runtime/BaseClasses/NamedObject.cpp @@ -0,0 +1,33 @@ +#include "UnityPrefix.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "NamedObject.h" +#include "Runtime/Containers/ConstantStringSerialization.h" + +NamedObject::NamedObject (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ +} + +NamedObject::~NamedObject () +{ +} + +template<class TransferFunction> +void NamedObject::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); + TransferConstantString(m_Name, "m_Name", kHideInEditorMask, GetMemoryLabel(), transfer); +} + +void NamedObject::SetName (char const* name) +{ + if (strcmp (m_Name.c_str (), name) != 0) + { + m_Name.assign (name, GetMemoryLabel()); + SetDirty (); + } +} + +IMPLEMENT_CLASS (NamedObject) +IMPLEMENT_OBJECT_SERIALIZE (NamedObject) +INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(NamedObject)
\ No newline at end of file diff --git a/Runtime/BaseClasses/NamedObject.h b/Runtime/BaseClasses/NamedObject.h new file mode 100644 index 0000000..7355dbc --- /dev/null +++ b/Runtime/BaseClasses/NamedObject.h @@ -0,0 +1,23 @@ +#ifndef NAMEDOBJECT_H +#define NAMEDOBJECT_H + +#include "EditorExtension.h" +#include "Runtime/Containers/ConstantString.h" + +class EXPORT_COREMODULE NamedObject : public EditorExtension +{ + public: + + virtual char const* GetName () const { return m_Name.c_str (); } + virtual void SetName (char const* name); + + REGISTER_DERIVED_ABSTRACT_CLASS (NamedObject, EditorExtension) + DECLARE_OBJECT_SERIALIZE (NamedObject) + + NamedObject (MemLabelId label, ObjectCreationMode mode); + protected: + + ConstantString m_Name; +}; + +#endif diff --git a/Runtime/BaseClasses/ObjectDefines.h b/Runtime/BaseClasses/ObjectDefines.h new file mode 100644 index 0000000..71550e0 --- /dev/null +++ b/Runtime/BaseClasses/ObjectDefines.h @@ -0,0 +1,202 @@ +#ifndef OBJECTDEFINES_H +#define OBJECTDEFINES_H + +#include "Runtime/Allocator/BaseAllocator.h" +#include "Configuration/UnityConfigure.h" +#include "Runtime/Allocator/MemoryMacros.h" + +#define NEW_OBJECT(class_) reinterpret_cast<class_*> (::Object::AllocateAndAssignInstanceID( UNITY_NEW_AS_ROOT ( class_(kMemBaseObject, kCreateObjectDefault), kMemBaseObject, NULL, NULL) )) +#define NEW_OBJECT_USING_MEMLABEL(class_, memlabel_) reinterpret_cast<class_*> (::Object::AllocateAndAssignInstanceID( UNITY_NEW_AS_ROOT ( class_(memlabel_, kCreateObjectDefault), memlabel_, NULL, NULL) )) +#define NEW_OBJECT_MAIN_THREAD(class_) reinterpret_cast<class_*> (::Object::AllocateAndAssignInstanceID(UNITY_NEW_AS_ROOT ( class_(kMemBaseObject, kCreateObjectDefault), kMemBaseObject, NULL, NULL) )) +#define NEW_OBJECT_FULL(class_,param) UNITY_NEW_AS_ROOT( class_(kMemBaseObject, param), kMemBaseObject, NULL, NULL) + +#define NEW_OBJECT_RESET_AND_AWAKE(class_) ResetAndAwake (NEW_OBJECT (class_)) + +// Every non-abstract class that is derived from object has to place this inside the class Declaration +// (REGISTER_DERIVED_CLASS (Foo, Object)) +#define REGISTER_DERIVED_CLASS(x, d) \ +public: \ + virtual int GetClassIDVirtualInternal () const; \ + static int GetClassIDStatic () { return ClassID (x); } \ + static const char* GetClassStringStatic (){ return #x; } \ + static const char* GetPPtrTypeString () { return "PPtr<"#x">"; } \ + static bool IsAbstract () { return false; }\ + static Object* PRODUCE (MemLabelId label, ObjectCreationMode mode) { return UNITY_NEW_AS_ROOT( x (label, mode), label, NULL, NULL); } \ + typedef d Super; \ + static void RegisterClass (); \ + protected: \ + ~x (); \ + public: + + +// Every abstract class that is derived from object has to place this inside the class Declaration +// (REGISTER_DERIVED_ABSTRACT_CLASS (Foo, Object)) +#define REGISTER_DERIVED_ABSTRACT_CLASS(x, d) \ + public: \ + virtual int GetClassIDVirtualInternal () const; \ + static int GetClassIDStatic () { return ClassID (x); } \ + static Object* PRODUCE (MemLabelId, ObjectCreationMode) { AssertString ("Can't produce abstract class"); return NULL; } \ + static bool IsAbstract () { return true; }\ + static const char* GetClassStringStatic (){ return #x; } \ + static const char* GetPPtrTypeString () { return "PPtr<"#x">"; } \ + typedef d Super; \ + static void RegisterClass (); \ + protected: \ + ~x (); \ + public: + +typedef void RegisterClassCallback (); +void EXPORT_COREMODULE RegisterInitializeClassCallback (int classID, + RegisterClassCallback* registerClass, + RegisterClassCallback* initClass, + RegisterClassCallback* postInitClass, + RegisterClassCallback* cleanupClass); + + +#if DEBUGMODE +#define VERIFY_OBJECT_IS_REGISTERED(x) \ +struct IMPLEMENT_CONSTRUCTOR_CLASS##x { IMPLEMENT_CONSTRUCTOR_CLASS##x () { AddVerifyClassRegistration (ClassID (x)); } }; \ +IMPLEMENT_CONSTRUCTOR_CLASS##x gVAR_CONSTRUCTOR_CLASS##x; + +#else +#define VERIFY_OBJECT_IS_REGISTERED(x) +#endif + +#define IMPLEMENT_CLASS_FULL(x, INIT, POSTINIT, CLEANUP) \ +VERIFY_OBJECT_IS_REGISTERED(x) \ +void RegisterClass_##x () { RegisterInitializeClassCallback (ClassID (x), x::RegisterClass, INIT, POSTINIT, CLEANUP); } \ +void x::RegisterClass () \ +{ \ + Assert(!Super::IsSealedClass ()); \ + if (Object::ClassIDToRTTI (Super::GetClassIDStatic ()) == NULL) \ + Super::RegisterClass (); \ + Object::RegisterClass (ClassID (x), Super::GetClassIDStatic (), #x, sizeof (x), PRODUCE, IsAbstract ()); \ +} \ +int x::GetClassIDVirtualInternal () const { return ClassID (x); } + +#define IMPLEMENT_CLASS(x) IMPLEMENT_CLASS_FULL(x, NULL, NULL, NULL) +#define IMPLEMENT_CLASS_HAS_INIT(x) IMPLEMENT_CLASS_FULL(x, x::InitializeClass, NULL, x::CleanupClass) +#define IMPLEMENT_CLASS_HAS_POSTINIT(x) IMPLEMENT_CLASS_FULL(x, x::InitializeClass, x::PostInitializeClass, x::CleanupClass) +#define IMPLEMENT_CLASS_INIT_ONLY(x) IMPLEMENT_CLASS_FULL(x, x::InitializeClass, NULL, NULL) + +// Should be placed in every serializable object derived class (DECLARE_OBJECT_SERIALIZE (Transform)) +#if UNITY_EDITOR + #define DECLARE_OBJECT_SERIALIZE(x) \ + static const char* GetTypeString () { return GetClassStringStatic(); } \ + static bool IsAnimationChannel () { return false; } \ + static bool MightContainPPtr () { return true; } \ + static bool AllowTransferOptimization () { return false; } \ + template<class TransferFunction> void Transfer (TransferFunction& transfer); \ + virtual void VirtualRedirectTransfer (ProxyTransfer& transfer); \ + virtual void VirtualRedirectTransfer (SafeBinaryRead& transfer); \ + virtual void VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer);\ + virtual void VirtualRedirectTransfer (StreamedBinaryWrite<true>& transfer);\ + virtual void VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer); \ + virtual void VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer); \ + virtual void VirtualRedirectTransfer (RemapPPtrTransfer& transfer); \ + virtual void VirtualRedirectTransfer (YAMLRead& transfer); \ + virtual void VirtualRedirectTransfer (YAMLWrite& transfer); +#elif SUPPORT_SERIALIZED_TYPETREES + #define DECLARE_OBJECT_SERIALIZE(x) \ + static const char* GetTypeString () { return GetClassStringStatic(); } \ + static bool IsAnimationChannel () { return false; } \ + static bool MightContainPPtr () { return true; } \ + static bool AllowTransferOptimization () { return false; } \ + template<class TransferFunction> void Transfer (TransferFunction& transfer); \ + virtual void VirtualRedirectTransfer (ProxyTransfer& transfer); \ + virtual void VirtualRedirectTransfer (SafeBinaryRead& transfer); \ + virtual void VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer); \ + virtual void VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer); \ + virtual void VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer); \ + virtual void VirtualRedirectTransfer (RemapPPtrTransfer& transfer); +#else + #define DECLARE_OBJECT_SERIALIZE(x) \ + static const char* GetTypeString () { return GetClassStringStatic(); } \ + static bool IsAnimationChannel () { return false; } \ + static bool MightContainPPtr () { return true; } \ + static bool AllowTransferOptimization () { return false; } \ + template<class TransferFunction> void Transfer (TransferFunction& transfer); \ + virtual void VirtualRedirectTransfer (ProxyTransfer& transfer); \ + virtual void VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer); \ + virtual void VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer); \ + virtual void VirtualRedirectTransfer (RemapPPtrTransfer& transfer); +#endif + +// Has to be placed in the cpp file of a serializable class (IMPLEMENT_OBJECT_SERIALIZE (Transform)) +// you also have to #include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" + +#if UNITY_EDITOR // Editor needs to support swapped endian writing, player doesnt. + +#define INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, decl) \ +template decl void x::Transfer(ProxyTransfer& transfer); \ +template decl void x::Transfer(SafeBinaryRead& transfer); \ +template decl void x::Transfer(StreamedBinaryRead<false>& transfer); \ +template decl void x::Transfer(StreamedBinaryRead<true>& transfer); \ +template decl void x::Transfer(StreamedBinaryWrite<false>& transfer); \ +template decl void x::Transfer(StreamedBinaryWrite<true>& transfer); \ +template decl void x::Transfer(RemapPPtrTransfer& transfer); \ +template decl void x::Transfer(YAMLRead& transfer); \ +template decl void x::Transfer(YAMLWrite& transfer); + +#define IMPLEMENT_OBJECT_SERIALIZE(x) \ +void x::VirtualRedirectTransfer (ProxyTransfer& transfer) { transfer.Transfer (*this, "Base"); } \ +void x::VirtualRedirectTransfer (SafeBinaryRead& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \ +void x::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \ +void x::VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \ +void x::VirtualRedirectTransfer (RemapPPtrTransfer& transfer) { transfer.Transfer (*this, "Base"); } \ +void x::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer) { transfer.Transfer (*this, "Base"); } \ +void x::VirtualRedirectTransfer (StreamedBinaryWrite<true>& transfer) { transfer.Transfer (*this, "Base"); } \ +void x::VirtualRedirectTransfer (YAMLRead& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \ +void x::VirtualRedirectTransfer (YAMLWrite& transfer) { transfer.Transfer (*this, "Base"); } \ + +#elif SUPPORT_SERIALIZED_TYPETREES +#define INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, decl) \ +template decl void x::Transfer(ProxyTransfer& transfer); \ +template decl void x::Transfer(SafeBinaryRead& transfer); \ +template decl void x::Transfer(StreamedBinaryRead<false>& transfer); \ +template decl void x::Transfer(StreamedBinaryRead<true>& transfer); \ +template decl void x::Transfer(StreamedBinaryWrite<false>& transfer); \ +template decl void x::Transfer(RemapPPtrTransfer& transfer); + + #define IMPLEMENT_OBJECT_SERIALIZE(x) \ + void x::VirtualRedirectTransfer (ProxyTransfer& transfer) { transfer.Transfer (*this, "Base"); } \ + void x::VirtualRedirectTransfer (SafeBinaryRead& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \ + void x::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \ + void x::VirtualRedirectTransfer (StreamedBinaryRead<true>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \ + void x::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer) { transfer.Transfer (*this, "Base"); } \ + void x::VirtualRedirectTransfer (RemapPPtrTransfer& transfer) { transfer.Transfer (*this, "Base"); } \ + +#else +#define INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, decl) \ +template decl void x::Transfer(ProxyTransfer& transfer); \ +template decl void x::Transfer(StreamedBinaryRead<false>& transfer); \ +template decl void x::Transfer(StreamedBinaryWrite<false>& transfer); \ +template decl void x::Transfer(RemapPPtrTransfer& transfer); + + #define IMPLEMENT_OBJECT_SERIALIZE(x) \ + void x::VirtualRedirectTransfer (ProxyTransfer& transfer) { transfer.Transfer (*this, "Base"); } \ + void x::VirtualRedirectTransfer (StreamedBinaryRead<false>& transfer) { SET_ALLOC_OWNER(this); transfer.Transfer (*this, "Base"); } \ + void x::VirtualRedirectTransfer (StreamedBinaryWrite<false>& transfer) { transfer.Transfer (*this, "Base"); } \ + void x::VirtualRedirectTransfer (RemapPPtrTransfer& transfer) { transfer.Transfer (*this, "Base"); } \ + +#endif + +#if UNITY_WIN +#define EXPORTDLL __declspec(dllexport) +#elif UNITY_OSX +#define EXPORTDLL __attribute__((visibility("default"))) +#else +#define EXPORTDLL +#endif + +#define INSTANTIATE_TEMPLATE_TRANSFER(x) INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, ) +#define INSTANTIATE_TEMPLATE_TRANSFER_EXPORTED(x) INSTANTIATE_TEMPLATE_TRANSFER_WITH_DECL(x, EXPORTDLL) + + +// Use this to make a Generic C++ GET/SET function: GET_SET (float, Velocity, m_Velocity) +// Implements GetVelocity, SetVelocity +#define GET_SET(TYPE,PROP_NAME,VAR_NAME) void Set##PROP_NAME (TYPE val) { VAR_NAME = val; } const TYPE Get##PROP_NAME () const {return (const TYPE)VAR_NAME; } +#define GET_SET_DIRTY(TYPE,PROP_NAME,VAR_NAME) void Set##PROP_NAME (TYPE val) { VAR_NAME = val; SetDirty(); } const TYPE Get##PROP_NAME () const {return (const TYPE)VAR_NAME; } +#define GET_SET_COMPARE_DIRTY(TYPE,PROP_NAME,VAR_NAME) void Set##PROP_NAME (TYPE val) { if ((TYPE)VAR_NAME == val) return; VAR_NAME = val; SetDirty(); } const TYPE Get##PROP_NAME () const {return (const TYPE)VAR_NAME; } + +#endif diff --git a/Runtime/BaseClasses/RefCounted.h b/Runtime/BaseClasses/RefCounted.h new file mode 100644 index 0000000..cbe2934 --- /dev/null +++ b/Runtime/BaseClasses/RefCounted.h @@ -0,0 +1,36 @@ +#pragma once + +#include "Configuration/UnityConfigure.h" +#include "Runtime/Mono/MonoIncludes.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" + +class TrackedReferenceBase +{ +public: + int m_MonoObjectReference; + + TrackedReferenceBase () + { + m_MonoObjectReference = 0; + } + + ~TrackedReferenceBase () + { +#if ENABLE_SCRIPTING + if (m_MonoObjectReference) + { + ScriptingObjectPtr target = scripting_gchandle_get_target (m_MonoObjectReference); + if (target) + { + void* nativePointer = 0; + MarshallNativeStructIntoManaged(nativePointer,target); + target = SCRIPTING_NULL; + } + + scripting_gchandle_free (m_MonoObjectReference); + m_MonoObjectReference = 0; + } +#endif + } +}; diff --git a/Runtime/BaseClasses/SupportedMessageOptimization.h b/Runtime/BaseClasses/SupportedMessageOptimization.h new file mode 100644 index 0000000..b5a6501 --- /dev/null +++ b/Runtime/BaseClasses/SupportedMessageOptimization.h @@ -0,0 +1,17 @@ +#ifndef SUPPORTEDMESSAGEOPTIMIZATION_H +#define SUPPORTEDMESSAGEOPTIMIZATION_H + +enum +{ + kHasCollisionStay = 1 << 0, + kHasCollisionEnterExit = 1 << 1, + kWantsCollisionData = 1 << 2, + kSupportsTransformChanged = 1 << 3, + kHasOnWillRenderObject = 1 << 4, + kSupportsVelocityChanged = 1 << 5, + kHasOnAnimatorMove = 1 << 6, + kHasOnAnimatorIK = 1 << 7, + kHasCollision2D = 1 << 8 +}; + +#endif diff --git a/Runtime/BaseClasses/Tags.cpp b/Runtime/BaseClasses/Tags.cpp new file mode 100644 index 0000000..3a56c6f --- /dev/null +++ b/Runtime/BaseClasses/Tags.cpp @@ -0,0 +1,604 @@ +#include "UnityPrefix.h" +#include "Tags.h" +#include "ManagerContext.h" +#include "GameManager.h" +#include "Runtime/Filters/Renderer.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Threads/Mutex.h" +#include "Runtime/Utilities/algorithm_utility.h" +#include "Runtime/Utilities/BitUtility.h" +#include "Runtime/Utilities/InitializeAndCleanup.h" +#include "Runtime/Utilities/LogAssert.h" +#include "Runtime/Utilities/GUID.h" +#include "External/MurmurHash/MurmurHash2.h" +#include <vector> + +static const char* kDefaultSortingLayerName = "Default"; + +// ------------------------------------------------------------------- + + +static Object* GetTagManagerPtr (); + + +typedef std::pair<const std::string, UInt32> StringUInt32Pair; +typedef std::map<std::string, UInt32, std::less<std::string>, STL_ALLOCATOR(kMemPermanent, StringUInt32Pair) > StringToUnsigned; + + +static StringToUnsigned* gStringToTag; +static UnsignedToString* gTagToString; +static int* gTagManagerContainer; + +static StringToUnsigned* gStringToMask; +static std::string gMaskToString[32]; +static std::string gEmpty; + +namespace LayerTagManager +{ + void StaticInitialize() + { + gTagManagerContainer = UNITY_NEW_AS_ROOT(int, kMemResource, "LayerTagManager", ""); + SET_ALLOC_OWNER(gTagManagerContainer); + gStringToTag = UNITY_NEW(StringToUnsigned,kMemResource); + gTagToString = UNITY_NEW(UnsignedToString,kMemResource); + gStringToMask = UNITY_NEW(StringToUnsigned,kMemResource); + } + void StaticDestroy() + { + UNITY_DELETE(gStringToTag, kMemResource); + UNITY_DELETE(gTagToString, kMemResource); + UNITY_DELETE(gStringToMask, kMemResource); + for(int i = 0; i < 32; i++) + gMaskToString[i] = std::string(); + gEmpty = std::string(); + UNITY_DELETE(gTagManagerContainer, kMemResource); + } +} + +static RegisterRuntimeInitializeAndCleanup s_LayerTagManagerCallbacks(LayerTagManager::StaticInitialize, LayerTagManager::StaticDestroy); + + +static void RegisterTag (UInt32 tag, const std::string& name); + + +void RegisterTag (UInt32 tag, const std::string& name) +{ + SET_ALLOC_OWNER(gTagManagerContainer); + if (!gStringToTag->insert (make_pair (name, tag)).second && !name.empty ()) + LogStringObject ("Default GameObject Tag: " + name + " already registered", GetTagManagerPtr ()); + + if (!gTagToString->insert (make_pair (tag, name)).second) + LogStringObject ("Default GameObject Tag for name: " + name + " already registered", GetTagManagerPtr ()); +} + +// In the editor we might add / remove tags from the gStringToTag array. +// And we might do this from the loading thread and main thread at the same time. + +// In the player this map is completely fixed, thus it even if two threads access the data at the same time, the data always stays constant +#if UNITY_EDITOR + +Mutex gTagToStringMutex; + +UInt32 StringToTagAddIfUnavailable (const std::string& tag) +{ + Mutex::AutoLock lock(gTagToStringMutex); + StringToUnsigned::iterator i = gStringToTag->find (tag); + if (i == gStringToTag->end ()) + { + SET_ALLOC_OWNER(gTagManagerContainer); + int nextTagID = last_iterator(*gTagToString)->first + 1; + gTagToString->insert(make_pair(nextTagID, tag)); + gStringToTag->insert(make_pair(tag, nextTagID)); + return nextTagID; + } + else + return i->second; +} + +#endif + + +UInt32 StringToTag (const std::string& tag) +{ + #if UNITY_EDITOR + Mutex::AutoLock lock(gTagToStringMutex); + #endif + + StringToUnsigned::iterator i = gStringToTag->find (tag); + if (i == gStringToTag->end ()) + return -1; + else + return i->second; +} + +const std::string& TagToString (UInt32 tag) +{ + #if UNITY_EDITOR + Mutex::AutoLock lock(gTagToStringMutex); + #endif + UnsignedToString::iterator i = gTagToString->find (tag); + if (i == gTagToString->end ()) + { + static std::string empty; + return empty; + } + else + return i->second; +} + +UInt32 StringToLayerMask (const std::string& tag) +{ + StringToUnsigned::iterator i = gStringToMask->find (tag); + if (i == gStringToMask->end ()) + return 0; + else + return 1 << i->second; +} + +UInt32 StringToLayer (const std::string& tag) +{ + StringToUnsigned::iterator i = gStringToMask->find (tag); + if (i == gStringToMask->end ()) + return -1; + else + return i->second; +} + +const std::string& LayerToString (UInt32 layer) +{ + if (layer >= 32) + { + ErrorString("Layer index out of bounds"); + return gEmpty; + } + return gMaskToString[layer]; +} + +const std::string& LayerMaskToString (UInt32 layerMask) +{ + Assert (IsPowerOfTwo (layerMask)); + if (layerMask == 0) + return gEmpty; + int layer = AnyBitFromMask (layerMask); + return gMaskToString[layer]; +} + +void RegisterLayer (UInt32 tag, const std::string& name) +{ + SET_ALLOC_OWNER(gTagManagerContainer); + if (!gStringToMask->insert (make_pair (name, tag)).second && !name.empty ()) + LogStringObject ("Default GameObject BitMask: " + name + " already registered", GetTagManagerPtr ()); + + if (gMaskToString[tag].empty ()) + gMaskToString[tag] = name; + else + LogStringObject ("Default GameObject BitMask for name: " + name + " already registered", GetTagManagerPtr ()); +} + +UnsignedToString GetTags () +{ + // Cull out all empty string tags. (The user is allowed to add those but we dont want them to show up!) + UnsignedToString tags; + for (UnsignedToString::iterator i=gTagToString->begin ();i != gTagToString->end ();i++) + { + if (!i->second.empty ()) + tags.insert (make_pair (i->first, i->second)); + } + return tags; +} + + +// ------------------------------------------------------------------- + +struct SortingLayerEntry +{ + DECLARE_SERIALIZE_NO_PPTR (SortingLayerEntry) + + SortingLayerEntry() : userID(1), uniqueID(1), locked(false) { } + UnityStr name; + UInt32 userID; + UInt32 uniqueID; + bool locked; +}; + +template<class TransferFunc> +void SortingLayerEntry::Transfer (TransferFunc& transfer) +{ + TRANSFER (name); + TRANSFER (userID); + TRANSFER (uniqueID); + TRANSFER_EDITOR_ONLY (locked); + transfer.Align(); +} + +class TagManager : public GlobalGameManager +{ +public: + DECLARE_OBJECT_SERIALIZE (TagManager) + REGISTER_DERIVED_CLASS (TagManager, GlobalGameManager) + + TagManager (MemLabelId label, ObjectCreationMode mode) : Super(label, mode), m_DefaultLayerIndex(0) {} + + bool ShouldIgnoreInGarbageDependencyTracking () { return true; } + + virtual void Update () { } + // virtual ~TagManager () { } declared-by-macro + + void AddDefaultLayerIfNeeded(); + void FindDefaultLayerIndex(); + + std::vector<SortingLayerEntry> m_SortingLayers; + int m_DefaultLayerIndex; +}; + +TagManager::~TagManager () +{ +} + +void TagManager::FindDefaultLayerIndex() +{ + m_DefaultLayerIndex = 0; + for (size_t i = 0, n = m_SortingLayers.size(); i != n; ++i) + { + if (m_SortingLayers[i].userID == 0) + { + m_DefaultLayerIndex = i; + break; + } + } +} + +void TagManager::AddDefaultLayerIfNeeded() +{ + // do we have a default layer? + for (size_t i = 0, n = m_SortingLayers.size(); i != n; ++i) + { + if (m_SortingLayers[i].userID == 0) + return; + } + + // no default layer, add one in front + SortingLayerEntry layer; + layer.name = kDefaultSortingLayerName; + layer.uniqueID = 0; + layer.userID = 0; + m_SortingLayers.insert(m_SortingLayers.begin(), layer); + m_DefaultLayerIndex = 0; +} + + + +void RegisterDefaultTagsAndLayerMasks () +{ + SET_ALLOC_OWNER(gTagManagerContainer); + gStringToTag->clear (); gTagToString->clear (); + gStringToMask->clear (); + for (int i=0;i<32;i++) + gMaskToString[i].clear (); + if (GetTagManagerPtr()) + { + TagManager& tags = (TagManager&)GetTagManager(); + tags.m_SortingLayers.clear(); + // add "Default" sorting layer + tags.m_SortingLayers.push_back(SortingLayerEntry()); + SortingLayerEntry& layer = tags.m_SortingLayers[0]; + layer.name = kDefaultSortingLayerName; + layer.userID = 0; + layer.uniqueID = 0; + tags.m_DefaultLayerIndex = 0; + } + + RegisterTag (kUntagged, "Untagged"); + RegisterTag (kRespawnTag, "Respawn"); + RegisterTag (kFinishTag, "Finish"); + RegisterTag (kEditorOnlyTag, "EditorOnly"); + RegisterTag (kMainCameraTag, "MainCamera"); + RegisterTag (kGameControllerTag, "GameController"); + RegisterTag (kPlayerTag, "Player"); + + RegisterLayer (kDefaultLayer, "Default"); + RegisterLayer (kNoFXLayer, "TransparentFX"); + RegisterLayer (kIgnoreRaycastLayer, "Ignore Raycast"); + RegisterLayer (kWaterLayer, "Water"); +} + +template<class TransferFunction> +void TagManager::Transfer (TransferFunction& transfer) +{ + std::vector<UnityStr> tags; + + // Build tags array + if (transfer.IsWriting ()) + { + UnsignedToString::iterator begin = gTagToString->lower_bound (kFirstUserTag); + UnsignedToString::iterator end = gTagToString->upper_bound (kLastUserTag); + for (UnsignedToString::iterator i=begin;i != end;i++) + tags.push_back (i->second); + if (tags.empty () || !tags.back ().empty ()) + tags.push_back (""); + } + else if (transfer.IsReading ()) + { + RegisterDefaultTagsAndLayerMasks (); + } + + TRANSFER_SIMPLE (tags); + + // Register tags we've read (if there actually was tag data in the stream). + if (transfer.DidReadLastProperty ()) + { + for (int i=0;i<tags.size ();i++) + RegisterTag (kFirstUserTag + i, tags[i]); + } + + // Build bitnames array + UnityStr bitnames[32]; + for (int i=0;i<32;i++) + { + char name[64]; + bool editable = i >= kUserLayer; + if (editable) + sprintf (name, "User Layer %d", i); + else + sprintf (name, "Builtin Layer %d", i); + + bitnames[i] = LayerToString (i); + transfer.Transfer (bitnames[i], name, editable ? kNoTransferFlags : kNotEditableMask); + + if (transfer.DidReadLastProperty ()) + { + if (i >= kUserLayer) + RegisterLayer (i, bitnames[i]); + } + } + + // Sorting layers + TRANSFER (m_SortingLayers); + if (!transfer.IsWriting () && transfer.IsReading()) + { + AddDefaultLayerIfNeeded(); + FindDefaultLayerIndex(); + } +} + +IMPLEMENT_CLASS (TagManager) +IMPLEMENT_OBJECT_SERIALIZE (TagManager) + +Object& GetTagManager () +{ + return GetManagerFromContext (ManagerContext::kTagManager); +} + +static Object* GetTagManagerPtr () +{ + return GetManagerPtrFromContext (ManagerContext::kTagManager); +} + + + +// ------------------------------------------------------------------- + + + +UnityStr GetSortingLayerName(int index) +{ + TagManager& tags = (TagManager&)GetTagManager(); + if (index < 0 || index >= tags.m_SortingLayers.size()) + return UnityStr(); + return tags.m_SortingLayers[index].name; +} + +UnityStr GetSortingLayerNameFromValue(int layerValue) +{ + TagManager& tags = (TagManager&)GetTagManager(); + int index = tags.m_DefaultLayerIndex + layerValue; + if (index < 0 || index >= tags.m_SortingLayers.size()) + return UnityStr(); + return tags.m_SortingLayers[index].name; +} + + +int GetSortingLayerUserID(int index) +{ + TagManager& tags = (TagManager&)GetTagManager(); + if (index < 0 || index >= tags.m_SortingLayers.size()) + return 0; + return tags.m_SortingLayers[index].userID; +} + +int GetSortingLayerUniqueIDFromValue(int layerValue) +{ + TagManager& tags = (TagManager&)GetTagManager(); + int index = tags.m_DefaultLayerIndex + layerValue; + if (index < 0 || index >= tags.m_SortingLayers.size()) + return 0; + return tags.m_SortingLayers[index].uniqueID; +} + + +int GetSortingLayerUserIDFromValue(int layerValue) +{ + TagManager& tags = (TagManager&)GetTagManager(); + int index = tags.m_DefaultLayerIndex + layerValue; + if (index < 0 || index >= tags.m_SortingLayers.size()) + return 0; + return tags.m_SortingLayers[index].userID; +} + +int GetSortingLayerIndexFromValue(int layerValue) +{ + TagManager& tags = (TagManager&)GetTagManager(); + int index = tags.m_DefaultLayerIndex + layerValue; + if (index < 0 || index >= tags.m_SortingLayers.size()) + index = 0; + return index; +} + + + +int GetSortingLayerUniqueID(int index) +{ + TagManager& tags = (TagManager&)GetTagManager(); + Assert (index >= 0 && index < tags.m_SortingLayers.size()); + return tags.m_SortingLayers[index].uniqueID; +} + +UnityStr GetSortingLayerNameFromUniqueID(int id) +{ + if (id == 0) + return UnityStr(kDefaultSortingLayerName); + + TagManager& tags = (TagManager&)GetTagManager(); + for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i) + if (tags.m_SortingLayers[i].uniqueID == id) + return tags.m_SortingLayers[i].name; + + return "<unknown layer>"; +} + +int GetSortingLayerValueFromUniqueID(int id) +{ + if (id == 0) + return 0; + + TagManager& tags = (TagManager&)GetTagManager(); + for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i) + if (tags.m_SortingLayers[i].uniqueID == id) + return i - tags.m_DefaultLayerIndex; + + return 0; // unknown layer: treat as if no layer is assigned +} + +int GetSortingLayerValueFromUserID(int id) +{ + if (id == 0) + return 0; + + TagManager& tags = (TagManager&)GetTagManager(); + for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i) + if (tags.m_SortingLayers[i].userID == id) + return i - tags.m_DefaultLayerIndex; + + return 0; // unknown layer: treat as if no layer is assigned +} + +int GetSortingLayerValueFromName(const UnityStr& name) +{ + TagManager& tags = (TagManager&)GetTagManager(); + if (name.empty()) + return 0; + + for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i) + if (tags.m_SortingLayers[i].name == name) + return i - tags.m_DefaultLayerIndex; + + return 0; // unknown layer: treat as default layer +} + + +#if UNITY_EDITOR +void AddSortingLayer() +{ + TagManager& tags = (TagManager&)GetTagManager(); + SortingLayerEntry s; + + // internal ID should be quite unique; generate a GUID and hash to and integer + UnityGUID guid; + guid.Init(); + s.uniqueID = MurmurHash2A(&guid.data, sizeof(guid.data), 0x8f37154b); + s.uniqueID |= 1; // make sure it's never zero + + // user-visible ID: smallest unused one + int id = 1; + while (true) + { + bool gotIt = false; + for (size_t i = 0; i < tags.m_SortingLayers.size(); ++i) + { + if (tags.m_SortingLayers[i].userID == id) + { + gotIt = true; + break; + } + } + if (!gotIt) + break; + ++id; + } + + s.name = Format("New Layer %d", id); + s.userID = id; + s.locked = false; + + tags.m_SortingLayers.push_back (s); + tags.SetDirty(); +} + + +void UpdateSortingLayersOrder() +{ + TagManager& tags = (TagManager&)GetTagManager(); + tags.FindDefaultLayerIndex(); + + vector<SInt32> objs; + Object::FindAllDerivedObjects (ClassID (Renderer), &objs); + for (size_t i = 0, n = objs.size(); i != n; ++i) + { + Renderer* r = PPtr<Renderer> (objs[i]); + r->SetupSortingOverride(); + } +} + + +void SetSortingLayerName(int index, const std::string& name) +{ + TagManager& tags = (TagManager&)GetTagManager(); + Assert (index >= 0 && index < tags.m_SortingLayers.size()); + tags.m_SortingLayers[index].name = name; + tags.SetDirty(); +} + +void SwapSortingLayers(int idx1, int idx2) +{ + TagManager& tags = (TagManager&)GetTagManager(); + Assert (idx1 >= 0 && idx1 < tags.m_SortingLayers.size()); + Assert (idx2 >= 0 && idx2 < tags.m_SortingLayers.size()); + std::swap(tags.m_SortingLayers[idx1], tags.m_SortingLayers[idx2]); + UpdateSortingLayersOrder(); + tags.SetDirty(); +} + + +void SetSortingLayerLocked(int index, bool locked) +{ + TagManager& tags = (TagManager&)GetTagManager(); + Assert (index >= 0 && index < tags.m_SortingLayers.size()); + tags.m_SortingLayers[index].locked = locked; + tags.SetDirty(); +} + +bool GetSortingLayerLocked(int index) +{ + TagManager& tags = (TagManager&)GetTagManager(); + if (index < 0 || index >= tags.m_SortingLayers.size()) + return false; + return tags.m_SortingLayers[index].locked; +} + +bool IsSortingLayerDefault(int index) +{ + TagManager& tags = (TagManager&)GetTagManager(); + return index == tags.m_DefaultLayerIndex; +} + + +int g_LockedPickingLayers = 0; + +#endif // #if UNITY_EDITOR + + +int GetSortingLayerCount() +{ + TagManager& tags = (TagManager&)GetTagManager(); + return tags.m_SortingLayers.size(); +} diff --git a/Runtime/BaseClasses/Tags.h b/Runtime/BaseClasses/Tags.h new file mode 100644 index 0000000..9d790a9 --- /dev/null +++ b/Runtime/BaseClasses/Tags.h @@ -0,0 +1,104 @@ +#pragma once + +#include <map> +#include <string> +#include "Runtime/Misc/Allocator.h" + +class Object; + +enum BitMasks +{ + // Can't modify these without breaking backwards compatibility! + kDefaultLayer = 0, + kNoFXLayer = 1, + kIgnoreRaycastLayer = 2, + kIgnoreCollisionLayer = 3, + kWaterLayer = 4, + kNumLayers = 32, + + kDefaultLayerMask = 1 << kDefaultLayer, + kNoFXLayerMask = 1 << kNoFXLayer, + kIgnoreRaycastMask = 1 << kIgnoreRaycastLayer, + kIgnoreCollisionMask = 1 << kIgnoreCollisionLayer, + kPreUnity2UnusedLayer = 1 << 5, + + kUserLayer = 8, +}; + +enum Tags +{ + kUntagged = 0, + kRespawnTag = 1, + kFinishTag = 2, + kEditorOnlyTag = 3, + kMainCameraTag = 5, + kPlayerTag = 6, + kGameControllerTag = 7, + kFirstUserTag = 20000, + kLastUserTag = 30000, + kUndefinedTag = -1 +}; + +// converts tag to string +UInt32 StringToTag (const std::string& tag); +UInt32 StringToTagAddIfUnavailable (const std::string& name); +const std::string& TagToString (UInt32 tag); + +// Converts between layer [0..31] and string +UInt32 StringToLayerMask (const std::string& layerName); +const std::string& LayerToString (UInt32 layer); +UInt32 StringToLayer (const std::string& layer); +// Converts a layer mask (1 << [0..31]) to a string +const std::string& LayerMaskToString (UInt32 mask); + +void RegisterLayer (UInt32 layer, const std::string& name); + +typedef std::pair<const UInt32, std::string> UInt32StringPair; +typedef std::map<UInt32, std::string, std::less<UInt32>, STL_ALLOCATOR(kMemPermanent, UInt32StringPair) > UnsignedToString; +UnsignedToString GetTags (); + +void RegisterDefaultTagsAndLayerMasks (); + +Object& GetTagManager (); + + +// ------------------------------------------------------------------- +// Global sorting layers: +// +// Defined globally, and can be reordered in the inspector. The drawing order is as shown in the inspector. +// Internally each global sorting layer has "unique ID" (GUID hashed into an int), and in-editor Renderers that want to +// use them refer to the layer by this ID. +// +// @TODO: +// * Do we need this "user friendly ID"? + +int GetSortingLayerCount(); +UnityStr GetSortingLayerName(int index); +UnityStr GetSortingLayerNameFromUniqueID(int id); +int GetSortingLayerUniqueID(int index); +int GetSortingLayerUserID(int index); + +UnityStr GetSortingLayerNameFromValue(int layerValue); +int GetSortingLayerUserIDFromValue(int layerValue); +int GetSortingLayerUniqueIDFromValue(int layerValue); +int GetSortingLayerIndexFromValue(int layerValue); + +// these return final sorting layer values +// (i.e. zero is always "default" - the returned value can be negative or positive) +int GetSortingLayerValueFromUniqueID(int id); +int GetSortingLayerValueFromUserID(int id); +int GetSortingLayerValueFromName(const UnityStr& name); + + +#if UNITY_EDITOR +void SetSortingLayerName(int index, const std::string& name); +void AddSortingLayer(); +void UpdateSortingLayersOrder(); +void SetSortingLayerLocked(int index, bool locked); +bool GetSortingLayerLocked(int index); +bool IsSortingLayerDefault(int index); +void SwapSortingLayers(int idx1, int idx2); + +extern int g_LockedPickingLayers; + +#endif // #if UNITY_EDITOR diff --git a/Runtime/BaseClasses/TagsTests.cpp b/Runtime/BaseClasses/TagsTests.cpp new file mode 100644 index 0000000..38085d4 --- /dev/null +++ b/Runtime/BaseClasses/TagsTests.cpp @@ -0,0 +1,109 @@ +#include "UnityPrefix.h" + +#if ENABLE_UNIT_TESTS + +#include "Tags.h" +#include "Runtime/Testing/Testing.h" + +SUITE (TagsTests) +{ + TEST (StringToTag_TagToString_WithEmptyString_IsIdentityOperation) + { + CHECK_EQUAL ("", TagToString (StringToTag (""))); + } + + TEST (StringToTag_TagToString_WithDefaultTag_IsIdentityOperation) + { + CHECK_EQUAL ("Untagged", TagToString (StringToTag ("Untagged"))); + } + +# if UNITY_EDITOR + TEST (SortingLayer_UserID_Works) + { + CHECK_EQUAL (1, GetSortingLayerCount()); // only default layer initially + + // add 3 layers + AddSortingLayer(); + AddSortingLayer(); + AddSortingLayer(); + SetSortingLayerName(1, "A"); + SetSortingLayerName(2, "B"); + SetSortingLayerName(3, "C"); + const int idA = GetSortingLayerUniqueID(1); + const int idB = GetSortingLayerUniqueID(2); + const int idC = GetSortingLayerUniqueID(3); + + // now the order is: Default, A, B, C + + // they should get 1,2,3 user IDs assigned + CHECK_EQUAL(1, GetSortingLayerUserID(1)); + CHECK_EQUAL(2, GetSortingLayerUserID(2)); + CHECK_EQUAL(3, GetSortingLayerUserID(3)); + CHECK_EQUAL(0, GetSortingLayerUserIDFromValue(0)); + CHECK_EQUAL(1, GetSortingLayerUserIDFromValue(1)); + CHECK_EQUAL(2, GetSortingLayerUserIDFromValue(2)); + CHECK_EQUAL(3, GetSortingLayerUserIDFromValue(3)); + + // find their values by user IDs + CHECK_EQUAL(1, GetSortingLayerValueFromUserID(1)); + CHECK_EQUAL(2, GetSortingLayerValueFromUserID(2)); + CHECK_EQUAL(3, GetSortingLayerValueFromUserID(3)); + + // find their values by names + CHECK_EQUAL(1, GetSortingLayerValueFromName("A")); + CHECK_EQUAL(2, GetSortingLayerValueFromName("B")); + CHECK_EQUAL(3, GetSortingLayerValueFromName("C")); + + // check all the above for default layer + CHECK_EQUAL(0, GetSortingLayerUserID(0)); + CHECK_EQUAL(0, GetSortingLayerValueFromUserID(0)); + CHECK_EQUAL(0, GetSortingLayerValueFromName("")); + CHECK_EQUAL(0, GetSortingLayerValueFromName("Default")); + CHECK_EQUAL(0, GetSortingLayerValueFromUniqueID(0)); + + // reorder layers into: B, A, Default, C + SwapSortingLayers (0, 2); + + // check user IDs + CHECK_EQUAL(2, GetSortingLayerUserID(0)); // B + CHECK_EQUAL(1, GetSortingLayerUserID(1)); + CHECK_EQUAL(3, GetSortingLayerUserID(3)); + + CHECK_EQUAL(2, GetSortingLayerUserIDFromValue(-2)); // B + CHECK_EQUAL(1, GetSortingLayerUserIDFromValue(-1)); // A + CHECK_EQUAL(0, GetSortingLayerUserIDFromValue(0)); // Default + CHECK_EQUAL(3, GetSortingLayerUserIDFromValue(1)); // C + + // find values by names + CHECK_EQUAL(-2, GetSortingLayerValueFromName("B")); + CHECK_EQUAL(-1, GetSortingLayerValueFromName("A")); + CHECK_EQUAL(1, GetSortingLayerValueFromName("C")); + + // check all the above for default layer + CHECK_EQUAL(0, GetSortingLayerUserID(2)); + CHECK_EQUAL(0, GetSortingLayerValueFromUserID(0)); + CHECK_EQUAL(0, GetSortingLayerValueFromName("")); + CHECK_EQUAL(0, GetSortingLayerValueFromName("Default")); + CHECK_EQUAL(0, GetSortingLayerValueFromUniqueID(0)); + + RegisterDefaultTagsAndLayerMasks (); // cleanup + } +# endif // if UNITY_EDITOR + +#if UNITY_EDITOR + + TEST (StringToTagAddIfUnavailable_WithNewTag_SetsUpMappings) + { + UInt32 tag = StringToTagAddIfUnavailable ("foobar"); + + CHECK_EQUAL (tag, StringToTag ("foobar")); + CHECK_EQUAL ("foobar", TagToString (tag)); + + // Cleanup. + RegisterDefaultTagsAndLayerMasks (); + } + +#endif // UNITY_EDITOR +} + +#endif // ENABLE_UNIT_TESTS |