Ejemplo n.º 1
0
/// @copydoc Serializer::SerializeObjectReference()
void BinarySerializer::SerializeObjectReference( const GameObjectType* /*pType*/, GameObjectPtr& rspObject )
{
    if( ShouldSerializeCurrentProperty() )
    {
        HELIUM_ASSERT( m_bAllowObjectReferences );
        if( !m_bAllowObjectReferences )
        {
            HELIUM_TRACE(
                TRACE_ERROR,
                ( TXT( "BinarySerializer: Attempted to serialize an object reference using a serializer with " )
                TXT( "which it is not allowed.\n" ) ) );

            return;
        }

        uint32_t objectIndex;
        SetInvalid( objectIndex );

        GameObject* pObject = rspObject.Get();
        if( pObject )
        {
            objectIndex = ResolveObjectDependency( pObject->GetPath() );
            HELIUM_ASSERT( IsValid( objectIndex ) );
        }

        m_pPropertyStream->Write( &objectIndex, sizeof( objectIndex ), 1 );
    }
}
Ejemplo n.º 2
0
/// @copydoc Serializer::Serialize()
bool BinarySerializer::Serialize( GameObject* pObject )
{
    HELIUM_ASSERT( pObject );

    BeginSerialize( true );

    // Serialize the object type reference.
    const GameObjectType* pType = pObject->GetGameObjectType();
    HELIUM_ASSERT( pType );
    uint32_t typeIndex = ResolveTypeDependency( pType->GetName() );
    HELIUM_ASSERT( IsValid( typeIndex ) );
    m_pPropertyStream->Write( &typeIndex, sizeof( typeIndex ), 1 );

    // Serialize the object template.
    uint32_t templateIndex;
    SetInvalid( templateIndex );

    if( !pObject->IsDefaultTemplate() )
    {
        GameObject* pTemplate = Reflect::AssertCast< GameObject >( pObject->GetTemplate() );
        HELIUM_ASSERT( pTemplate );
        templateIndex = ResolveObjectDependency( pTemplate->GetPath() );
        HELIUM_ASSERT( IsValid( templateIndex ) );
    }

    m_pPropertyStream->Write( &templateIndex, sizeof( templateIndex ), 1 );

    // Serialize the object owner.
    uint32_t ownerIndex;
    SetInvalid( ownerIndex );

    GameObject* pOwner = pObject->GetOwner();
    if( pOwner )
    {
        ownerIndex = ResolveObjectDependency( pOwner->GetPath() );
        HELIUM_ASSERT( IsValid( ownerIndex ) );
    }

    m_pPropertyStream->Write( &ownerIndex, sizeof( ownerIndex ), 1 );

    // Serialize the object properties.
    pObject->Serialize( *this );

    EndSerialize();

    return true;
}
Ejemplo n.º 3
0
/// @name Serializer::SerializeObjectReference()
void GameObjectLoader::Linker::SerializeObjectReference( const GameObjectType* pType, GameObjectPtr& rspObject )
{
    HELIUM_ASSERT( pType );

    uint32_t linkIndex = rspObject.GetLinkIndex();
    rspObject.ClearLinkIndex();

    if( IsInvalid( linkIndex ) )
    {
        return;
    }

    if( linkIndex >= m_linkEntryCount )
    {
        HELIUM_TRACE(
            TRACE_ERROR,
            TXT( "GameObjectLoader: Invalid link index %" ) TPRIu32 TXT( " encountered.  Setting null reference.\n" ),
            linkIndex );

        m_bError = true;

        return;
    }

    GameObject* pObject = m_pLinkEntries[ linkIndex ].spObject;
    if( pObject )
    {
        if( !pObject->IsClass( pType ) )
        {
            HELIUM_TRACE(
                TRACE_ERROR,
                TXT( "GameObjectLoader: GameObject reference \"%s\" is not of the correct type (\"%s\").\n" ),
                *pObject->GetPath().ToString(),
                *pType->GetName() );

            m_bError = true;
        }
        else
        {
            rspObject = pObject;
        }
    }
}
Ejemplo n.º 4
0
/// Update resource precaching for the given object load request.
///
/// @param[in] pRequest  Load request to update.
///
/// @return  True if resource precaching still requires processing, false if not.
bool GameObjectLoader::TickPrecache( LoadRequest* pRequest )
{
    HELIUM_ASSERT( pRequest );
    HELIUM_ASSERT( !( pRequest->stateFlags & LOAD_FLAG_LOADED ) );

    GameObject* pObject = pRequest->spObject;
    if( pObject )
    {
        // Wait for all link dependencies to fully load first.
        DynArray< LinkEntry >& rLinkTable = pRequest->linkTable;
        size_t linkTableSize = rLinkTable.GetSize();
        for( size_t linkIndex = 0; linkIndex < linkTableSize; ++linkIndex )
        {
            LinkEntry& rLinkEntry = rLinkTable[ linkIndex ];
            if( IsValid( rLinkEntry.loadId ) )
            {
                if( !TryFinishLoad( rLinkEntry.loadId, rLinkEntry.spObject ) )
                {
                    return false;
                }

                SetInvalid( rLinkEntry.loadId );
                rLinkEntry.spObject.Release();
            }
        }

        rLinkTable.Resize( 0 );

        // Perform any pre-precaching work (note that we don't precache anything for the default template object for
        // a given type).
        OnPrecacheReady( pObject, pRequest->pPackageLoader );

        if( !pObject->GetAnyFlagSet( GameObject::FLAG_BROKEN ) &&
            !pObject->IsDefaultTemplate() &&
            pObject->NeedsPrecacheResourceData() )
        {
            if( !( pRequest->stateFlags & LOAD_FLAG_PRECACHE_STARTED ) )
            {
                if( !pObject->BeginPrecacheResourceData() )
                {
                    HELIUM_TRACE(
                        TRACE_ERROR,
                        TXT( "GameObjectLoader: Failed to begin precaching object \"%s\".\n" ),
                        *pObject->GetPath().ToString() );

                    pObject->SetFlags( GameObject::FLAG_PRECACHED | GameObject::FLAG_BROKEN );
                    AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_PRECACHED | LOAD_FLAG_ERROR );

                    return true;
                }

                AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_PRECACHE_STARTED );
            }

            if( !pObject->TryFinishPrecacheResourceData() )
            {
                return false;
            }
        }

        pObject->SetFlags( GameObject::FLAG_PRECACHED );
    }

    AtomicOrRelease( pRequest->stateFlags, LOAD_FLAG_PRECACHED );

    return true;
}
Ejemplo n.º 5
0
/// Perform shutdown of the GameObject system.
///
/// This releases all final references to objects and releases all allocated memory.  This should be called during
/// the shutdown process after all types have been unregistered as well as after calling GameObjectType::Shutdown().
///
/// @see GameObjectType::Shutdown()
void GameObject::Shutdown()
{
    HELIUM_TRACE( TraceLevels::Info, TXT( "Shutting down GameObject system.\n" ) );

    GameObject::ReleaseStaticType();

#pragma TODO( "Fix support for casting between Reflect::Object and GameObject once the type systems have been properly integrated." )
#if HELIUM_ENABLE_MEMORY_TRACKING
    ConcurrentHashSet< RefCountProxy< Reflect::Object >* >::ConstAccessor refCountProxyAccessor;
    if( Reflect::ObjectRefCountSupport::GetFirstActiveProxy( refCountProxyAccessor ) )
    {
        HELIUM_TRACE(
            TraceLevels::Error,
            TXT( "%" ) TPRIuSZ TXT( " smart pointer(s) still active during shutdown!\n" ),
            Reflect::ObjectRefCountSupport::GetActiveProxyCount() );

#if 1
        refCountProxyAccessor.Release();
#else
        size_t activeGameObjectCount = 0;
        while( refCountProxyAccessor.IsValid() )
        {
            RefCountProxy< Reflect::Object >* pProxy = *refCountProxyAccessor;
            HELIUM_ASSERT( pProxy );

            GameObject* pGameObject = Reflect::SafeCast< GameObject >( pProxy->GetObject() );
            if( pGameObject )
            {
                ++activeGameObjectCount;
            }

            ++refCountProxyAccessor;
        }

        HELIUM_TRACE(
            TraceLevels::Error,
            TXT( "%" ) TPRIuSZ TXT( " active GameObject smart pointer(s):\n" ),
            activeGameObjectCount );

        Reflect::ObjectRefCountSupport::GetFirstActiveProxy( refCountProxyAccessor );
        while( refCountProxyAccessor.IsValid() )
        {
            RefCountProxy< Reflect::Object >* pProxy = *refCountProxyAccessor;
            HELIUM_ASSERT( pProxy );

            GameObject* pGameObject = Reflect::SafeCast< GameObject >( pProxy->GetObject() );
            if( pGameObject )
            {
                HELIUM_TRACE(
                    TraceLevels::Error,
                    TXT( "- 0x%p: %s (%" ) TPRIu16 TXT( " strong ref(s), %" ) TPRIu16 TXT( " weak ref(s))\n" ),
                    pProxy,
                    ( pGameObject ? *pGameObject->GetPath().ToString() : TXT( "(cleared reference)" ) ),
                    pProxy->GetStrongRefCount(),
                    pProxy->GetWeakRefCount() );
            }

            ++refCountProxyAccessor;
        }
#endif
    }
#endif  // HELIUM_ENABLE_MEMORY_TRACKING

#if !HELIUM_RELEASE
    size_t objectCountActual = sm_objects.GetUsedSize();
    if( objectCountActual != 0 )
    {
        HELIUM_TRACE(
            TraceLevels::Error,
            TXT( "%" ) TPRIuSZ TXT( " object(s) still referenced during shutdown!\n" ),
            objectCountActual );

        size_t objectCount = sm_objects.GetSize();
        for( size_t objectIndex = 0; objectIndex < objectCount; ++objectIndex )
        {
            if( !sm_objects.IsElementValid( objectIndex ) )
            {
                continue;
            }

            GameObject* pObject = sm_objects[ objectIndex ];
            if( !pObject )
            {
                continue;
            }

            HELIUM_TRACE( TraceLevels::Error, TXT( "- %s\n" ), *pObject->GetPath().ToString() );
        }
    }
#endif  // !HELIUM_RELEASE

    sm_objects.Clear();
    sm_wpFirstTopLevelObject.Release();

    delete sm_pNameInstanceIndexMap;
    sm_pNameInstanceIndexMap = NULL;

    delete sm_pEmptyNameInstanceIndexMap;
    sm_pEmptyNameInstanceIndexMap = NULL;

    delete sm_pEmptyInstanceIndexSet;
    sm_pEmptyInstanceIndexSet = NULL;

    sm_serializationBuffer.Clear();
}
Ejemplo n.º 6
0
/// Modify the name, owner, or instance index of this object.
///
/// @param[in] rParameters  Object rename parameters.
///
/// @return  True if this object was renamed successfully, false if not.
///
/// @see GetName(), GetOwner(), GetInstanceIndex()
bool GameObject::Rename( const RenameParameters& rParameters )
{
    Name name = rParameters.name;
    GameObject* pOwner = rParameters.spOwner;
    uint32_t instanceIndex = rParameters.instanceIndex;

    HELIUM_TRACE(
        TraceLevels::Debug,
        TXT("GameObject::Rename(): Renaming object \"%s\" to \"%s\" (Old Owner: \"%s\". New Owner: \"%s\".)\n"),
        *m_name,
        *rParameters.name,
        m_spOwner.ReferencesObject() ? *m_spOwner->GetPath().ToString() : TXT("[none]"),
        rParameters.spOwner.ReferencesObject() ? *rParameters.spOwner->GetPath().ToString() : TXT("[none]"));

    // Only allow setting an empty name if no owner or instance index are given and this object has no children.
    if( name.IsEmpty() )
    {
        HELIUM_ASSERT( !pOwner );
        HELIUM_ASSERT( IsInvalid( instanceIndex ) );
        if( pOwner || IsValid( instanceIndex ) )
        {
            HELIUM_TRACE(
                TraceLevels::Error,
                ( TXT( "GameObject::Rename(): Objects cannot have name information cleared if being assigned an " )
                  TXT( "owner or instance index.\n" ) ) );

            return false;
        }

        HELIUM_ASSERT( !m_wpFirstChild );
        if( m_wpFirstChild )
        {
            HELIUM_TRACE(
                TraceLevels::Error,
                TXT( "GameObject::Rename(): Cannot clear name information for objects with children.\n" ) );

            return false;
        }
    }

    // Don't allow setting the owner to ourself.
    if( pOwner == this )
    {
        HELIUM_TRACE( TraceLevels::Error, TXT( "GameObject::Rename(): Cannot set the owner of an object to itself.\n" ) );

        return false;
    }

    // Don't allow setting the owner to an object with no name information.
    if( pOwner && pOwner->m_name.IsEmpty() )
    {
        HELIUM_TRACE(
            TraceLevels::Error,
            TXT( "GameObject::Rename(): Cannot set the owner of an object to an object with no path information.\n" ) );

        return false;
    }

    if( IsPackage() )
    {
        // Don't allow package objects to be children of non-package objects.
        if( pOwner && !pOwner->IsPackage() )
        {
            HELIUM_TRACE(
                TraceLevels::Error,
                TXT( "GameObject::Rename(): Cannot set a non-package as the owner of a package.\n" ) );

            return false;
        }

        // Don't allow instance indexing for packages.
        if( IsValid( instanceIndex ) )
        {
            HELIUM_TRACE(
                TraceLevels::Error,
                TXT( "GameObject::Rename(): Instance indexing not supported for packages.\n" ) );

            return false;
        }
    }

    // Don't need to do anything if the name, owner, and instance index are not changing.
    if( name == m_name &&
        pOwner == m_spOwner &&
        ( instanceIndex == m_instanceIndex || ( instanceIndex == INSTANCE_INDEX_AUTO && IsValid( m_instanceIndex ) ) ) )
    {
        return true;
    }

    // Hold onto a reference to the current owner until we return from this function.  This is done in case this object
    // has the last strong reference to it, in which case we would encounter a deadlock if clearing its reference while
    // we still have a write lock on the object list (object destruction also requires acquiring a write lock).
    GameObjectPtr spOldOwner = m_spOwner;

    {
        // Acquire a write lock on the object list to prevent objects from being added and removed as well as keep
        // objects from being renamed while this object is being renamed.
        ScopeWriteLock scopeLock( sm_objectListLock );

        // Get the list of children belonging to the new owner.
        GameObjectWPtr& rwpOwnerFirstChild = ( pOwner ? pOwner->m_wpFirstChild : sm_wpFirstTopLevelObject );

        // Don't check for name clashes if we're clearing the object path name information.
        if( !name.IsEmpty() )
        {
            // Resolve name clashes either through the instance index lookup map (if an instance index will be assigned)
            // or through a child object search (if no instance index will be used).
            if( IsValid( instanceIndex ) )
            {
                // Get the instance index map for the requested object name.
                ChildNameInstanceIndexMap& rNameInstanceIndexMap = GetNameInstanceIndexMap();
                HELIUM_ASSERT( sm_pEmptyNameInstanceIndexMap );
                HELIUM_ASSERT( sm_pEmptyInstanceIndexSet );

                sm_pEmptyNameInstanceIndexMap->First() = ( pOwner ? pOwner->GetPath() : GameObjectPath( NULL_NAME ) );
                sm_pEmptyInstanceIndexSet->First() = name;

                ChildNameInstanceIndexMap::Accessor childNameMapAccessor;
                rNameInstanceIndexMap.Insert( childNameMapAccessor, *sm_pEmptyNameInstanceIndexMap );

                NameInstanceIndexMap::Accessor indexSetAccessor;
                childNameMapAccessor->Second().Insert( indexSetAccessor, *sm_pEmptyInstanceIndexSet );

                InstanceIndexSet& rIndexSet = indexSetAccessor->Second();
                InstanceIndexSet::ConstAccessor indexAccessor;

                if( instanceIndex == INSTANCE_INDEX_AUTO )
                {
                    // Pick an unused instance index.
                    instanceIndex = 0;
                    while( !rIndexSet.Insert( indexAccessor, instanceIndex ) )
                    {
                        ++instanceIndex;
                        HELIUM_ASSERT( instanceIndex < INSTANCE_INDEX_AUTO );
                    }
                }
                else
                {
                    // Attempt to acquire the specified instance index.
                    if( !rIndexSet.Insert( indexAccessor, instanceIndex ) )
                    {
                        HELIUM_TRACE(
                            TraceLevels::Error,
                            ( TXT( "GameObject::Rename(): Object already exists with the specified owner (%s), name " )
                              TXT( "(%s), and instance index (%" ) TPRIu32 TXT( ").\n" ) ),
                            ( pOwner ? *pOwner->GetPath().ToString() : TXT( "none" ) ),
                            *name,
                            instanceIndex );

                        return false;
                    }
                }
            }
            else
            {
                // Check each child of the new owner for a name clash.
                for( GameObject* pChild = rwpOwnerFirstChild; pChild != NULL; pChild = pChild->m_wpNextSibling )
                {
                    if( pChild->m_name == name && pChild->m_instanceIndex == instanceIndex )
                    {
                        HELIUM_TRACE(
                            TraceLevels::Error,
                            ( TXT( "GameObject::Rename(): Object already exists with the specified owner (%s) and " )
                              TXT( "name (%s).\n" ) ),
                            ( pOwner ? *pOwner->GetPath().ToString() : TXT( "none" ) ),
                            *name );

                        return false;
                    }
                }
            }
        }

        // Remove any old instance index tracking for the old path name.
        if( IsValid( m_instanceIndex ) )
        {
            GameObjectPath ownerPath = ( spOldOwner ? spOldOwner->GetPath() : GameObjectPath( NULL_NAME ) );

            ChildNameInstanceIndexMap& rNameInstanceIndexMap = GetNameInstanceIndexMap();

            ChildNameInstanceIndexMap::Accessor childMapAccessor;
            HELIUM_VERIFY( rNameInstanceIndexMap.Find( childMapAccessor, ownerPath ) );

            NameInstanceIndexMap& rNameMap = childMapAccessor->Second();
            NameInstanceIndexMap::Accessor nameMapAccessor;
            HELIUM_VERIFY( rNameMap.Find( nameMapAccessor, m_name ) );

            InstanceIndexSet& rIndexSet = nameMapAccessor->Second();
            HELIUM_VERIFY( rIndexSet.Remove( m_instanceIndex ) );

            /*
            if( rIndexSet.IsEmpty() )
            {
                HELIUM_VERIFY( rNameMap.Remove( nameMapAccessor ) );
                if( rNameMap.IsEmpty() )
                {
                    HELIUM_VERIFY( rNameInstanceIndexMap.Remove( childMapAccessor ) );
                }
            }
            */
        }

        // If the owner of this object is changing, remove this object from its old owner's list and add it to the new
        // owner.
        if( spOldOwner.Get() != pOwner || ( m_name.IsEmpty() ? !name.IsEmpty() : name.IsEmpty() ) )
        {
            // Object should not be in any child object lists if its name is empty.
            if( !m_name.IsEmpty() )
            {
                GameObjectWPtr& rwpOldOwnerFirstChild =
                    ( spOldOwner ? spOldOwner->m_wpFirstChild : sm_wpFirstTopLevelObject );

                GameObject* pPreviousChild = NULL;
                GameObject* pChild = rwpOldOwnerFirstChild;
                while( pChild )
                {
                    if( pChild == this )
                    {
                        ( pPreviousChild ? pPreviousChild->m_wpNextSibling : rwpOldOwnerFirstChild ) = m_wpNextSibling;
                        m_wpNextSibling.Release();

                        break;
                    }

                    pPreviousChild = pChild;
                    pChild = pChild->m_wpNextSibling;
                }
            }

            HELIUM_ASSERT( !m_wpNextSibling );

            // Only store the object in a child object list if it is being given a valid name.
            if( !name.IsEmpty() )
            {
                m_wpNextSibling = rwpOwnerFirstChild;
                rwpOwnerFirstChild = this;
            }
        }

        // Set the new path name.
        m_name = name;
        m_spOwner = pOwner;
        m_instanceIndex = instanceIndex;

        // Update path information for this object and its children.
        UpdatePath();
    }

    return true;
}
Ejemplo n.º 7
0
/// @copydoc PackageLoader::TryFinishLoadObject()
bool CachePackageLoader::TryFinishLoadObject(
    size_t requestId,
    GameObjectPtr& rspObject,
    DynamicArray< GameObjectLoader::LinkEntry >& rLinkTable )
{
    HELIUM_ASSERT( requestId < m_loadRequests.GetSize() );
    HELIUM_ASSERT( m_loadRequests.IsElementValid( requestId ) );

    LoadRequest* pRequest = m_loadRequests[ requestId ];
    HELIUM_ASSERT( pRequest );
    if( !( pRequest->flags & LOAD_FLAG_PRELOADED ) )
    {
        return false;
    }

    // Sync on template and owner dependencies.
    GameObjectLoader* pObjectLoader = GameObjectLoader::GetStaticInstance();
    HELIUM_ASSERT( pObjectLoader );

    DynamicArray< size_t >& rInternalLinkTable = pRequest->objectLinkTable;

    if( IsValid( pRequest->templateLinkIndex ) )
    {
        size_t linkLoadId = rInternalLinkTable[ pRequest->templateLinkIndex ];
        if( IsValid( linkLoadId ) && !pObjectLoader->TryFinishLoad( linkLoadId, pRequest->spTemplate ) )
        {
            return false;
        }

        SetInvalid( pRequest->templateLinkIndex );
    }

    if( IsValid( pRequest->ownerLinkIndex ) )
    {
        size_t linkLoadId = rInternalLinkTable[ pRequest->ownerLinkIndex ];
        if( IsValid( linkLoadId ) && !pObjectLoader->TryFinishLoad( linkLoadId, pRequest->spOwner ) )
        {
            return false;
        }

        SetInvalid( pRequest->ownerLinkIndex );
    }

    rspObject = pRequest->spObject;
    GameObject* pObject = rspObject;
    if( pObject && ( pRequest->flags & LOAD_FLAG_ERROR ) )
    {
        pObject->SetFlags( GameObject::FLAG_BROKEN );
    }

    pRequest->spObject.Release();

    size_t linkTableSize = rInternalLinkTable.GetSize();
    rLinkTable.Resize( 0 );
    rLinkTable.Reserve( linkTableSize );
    for( size_t linkIndex = 0; linkIndex < linkTableSize; ++linkIndex )
    {
        GameObjectLoader::LinkEntry* pEntry = rLinkTable.New();
        HELIUM_ASSERT( pEntry );
        pEntry->loadId = rInternalLinkTable[ linkIndex ];
        pEntry->spObject.Release();
    }

    rInternalLinkTable.Resize( 0 );

    HELIUM_ASSERT( IsInvalid( pRequest->asyncLoadId ) );
    HELIUM_ASSERT( !pRequest->pAsyncLoadBuffer );

    pRequest->spType.Release();
    pRequest->spTemplate.Release();
    pRequest->spOwner.Release();
    pRequest->typeLinkTable.Resize( 0 );

    HELIUM_ASSERT( pObject || pRequest->pEntry );
    HELIUM_TRACE(
        TraceLevels::Debug,
        ( TXT( "CachePackageLoader::TryFinishLoadObject(): Load request for \"%s\" (ID: %" ) TPRIuSZ TXT( ") " )
        TXT( "synced.\n" ) ),
        *( pObject ? pObject->GetPath() : pRequest->pEntry->path ).ToString(),
        requestId );

    m_loadRequests.Remove( requestId );
    m_loadRequestPool.Release( pRequest );

    return true;
}