diff --git a/Include/Assets/Level.h b/Include/Assets/Level.h index 3c166797..3cd797de 100644 --- a/Include/Assets/Level.h +++ b/Include/Assets/Level.h @@ -33,9 +33,13 @@ namespace CE Level& operator=(const Level&) = delete; void CreateFromWorld(const World& world); - World CreateWorld(bool callBeginPlayImmediately) const; + void LoadIntoWorld(World& world) const; - static World CreateDefaultWorld(); + // Will never return nullptr + std::unique_ptr CreateWorld(bool callBeginPlayImmediately) const; + + // Will never return nullptr + static std::unique_ptr CreateDefaultWorld(); protected: void OnSave(AssetSaveInfo& saveInfo) const override; @@ -71,7 +75,7 @@ namespace CE // when loading the level. Instead of // discarding the world, we return this // one on the first call to CreateWorld - mutable std::optional mWorld{}; + mutable std::unique_ptr mWorld{}; mutable std::optional mSerializedWorld{}; friend ReflectAccess; diff --git a/Include/Assets/Script.h b/Include/Assets/Script.h index 65e625f8..9f8ccdf5 100644 --- a/Include/Assets/Script.h +++ b/Include/Assets/Script.h @@ -96,9 +96,10 @@ namespace CE // so if this ever changes, you'll know exactly where you need to start fixing things. static constexpr bool sIsTypeIdTheSameAsNameHash = true; - // Each component script secretly gets assigned a field of type entt::entity with this name. - // Upon constructing the component, the correct entity id is assigned. + // Each component script secretly gets assigned a field of type entt::entity and World* with these names. + // Upon constructing the component, the correct entity id and world is assigned. static constexpr Name sNameOfOwnerField = "Owner"_Name; + static constexpr Name sNameOfWorldField = "World"_Name; void CollectErrors(ScriptErrorInserter inserter) const; diff --git a/Include/Components/GridSpawnerComponent.h b/Include/Components/GridSpawnerComponent.h index 6fbbea3d..98808827 100644 --- a/Include/Components/GridSpawnerComponent.h +++ b/Include/Components/GridSpawnerComponent.h @@ -10,10 +10,10 @@ namespace CE { public: void OnConstruct(World&, entt::entity owner); - void OnBeginPlay(World&, entt::entity); + void OnBeginPlay(World& world, entt::entity); - void ClearGrid(); - void SpawnGrid(); + void ClearGrid(World& world); + void SpawnGrid(World& world); std::vector> mTiles{}; diff --git a/Include/EditorSystems/ThumbnailEditorSystem.h b/Include/EditorSystems/ThumbnailEditorSystem.h index 1ad0aa72..c19ac8fe 100644 --- a/Include/EditorSystems/ThumbnailEditorSystem.h +++ b/Include/EditorSystems/ThumbnailEditorSystem.h @@ -12,7 +12,7 @@ namespace CE class Texture; class World; - using GetThumbnailRet = std::variant, World>; + using GetThumbnailRet = std::variant, std::unique_ptr>; class ThumbnailEditorSystem final : public EditorSystem @@ -72,9 +72,7 @@ namespace CE struct CurrentlyGenerating { - CurrentlyGenerating(World&& world, WeakAssetHandle<> forAsset); - - World mWorld; + std::unique_ptr mWorld{}; WeakAssetHandle<> mForAsset{}; FrameBuffer mFrameBuffer{ sGeneratedThumbnailResolution }; Timer mNumSecondsSinceLastRequested{}; diff --git a/Include/Scripting/ScriptPin.h b/Include/Scripting/ScriptPin.h index 64c8dbad..e219d88c 100644 --- a/Include/Scripting/ScriptPin.h +++ b/Include/Scripting/ScriptPin.h @@ -1,17 +1,10 @@ #pragma once #include "ScriptIds.h" -#include "imnodes/imgui_node_editor.h" -#include "Meta/MetaAny.h" -#include "Meta/MetaTypeTraits.h" #include "Meta/Fwd/MetaFuncFwd.h" +#include "Meta/MetaTypeTraits.h" #include "Scripting/ScriptErrors.h" -namespace CE -{ - struct MetaFuncNamedParam; -} - namespace CE { enum class ScriptPinKind : bool diff --git a/Include/Utilities/Imgui/WorldInspect.h b/Include/Utilities/Imgui/WorldInspect.h index 1424c154..fb71a869 100644 --- a/Include/Utilities/Imgui/WorldInspect.h +++ b/Include/Utilities/Imgui/WorldInspect.h @@ -21,7 +21,7 @@ namespace CE The provided doUndoStack must remain alive for the duration of this objects lifetime. */ - WorldInspectHelper(World&& worldThatHasNotYetBegunPlay); + WorldInspectHelper(std::unique_ptr worldThatHasNotYetBegunPlay); ~WorldInspectHelper(); /* diff --git a/Include/Utilities/Reflect/ReflectComponentType.h b/Include/Utilities/Reflect/ReflectComponentType.h index f68982ed..0e46d33c 100644 --- a/Include/Utilities/Reflect/ReflectComponentType.h +++ b/Include/Utilities/Reflect/ReflectComponentType.h @@ -36,12 +36,10 @@ namespace CE MetaFunc& addComponentFunc = entityType.AddFunc( [](MetaFunc::DynamicArgs args, MetaFunc::RVOBuffer) -> FuncResult { - World* world = World::TryGetWorldAtTopOfStack(); - ASSERT(world != nullptr && "Reached a scripting context without pushing a world"); + World& world = *static_cast(args[0].GetData()); + const entt::entity entity = *static_cast(args[1].GetData()); - entt::entity entity = *static_cast(args[0].GetData()); - - Registry& reg = world->GetRegistry(); + Registry& reg = world.GetRegistry(); if (reg.HasComponent(entity)) { @@ -61,7 +59,7 @@ namespace CE }, Internal::GetAddComponentFuncName(type.GetName()), MetaFunc::Return{ isEmpty ? MakeTypeTraits() : MakeTypeTraits() }, - MetaFunc::Params{ { MakeTypeTraits(), "Entity" } }); + MetaFunc::Params{ { MakeTypeTraits() }, { MakeTypeTraits() } }); if (type.GetProperties().Has(Props::sIsScriptableTag)) { diff --git a/Include/World/EventManager.h b/Include/World/EventManager.h index bafd9d6e..45145778 100644 --- a/Include/World/EventManager.h +++ b/Include/World/EventManager.h @@ -29,8 +29,6 @@ namespace CE MetaFunc::DynamicArgs argsProvided, std::span argFormsProvided); - // mWorld needs to be updated in World::World(World&&), so we give access to World to do so. - friend class World; std::reference_wrapper mWorld; std::unordered_map> mBoundEvents{}; diff --git a/Include/World/Physics.h b/Include/World/Physics.h index a10caf42..755655f2 100644 --- a/Include/World/Physics.h +++ b/Include/World/Physics.h @@ -52,8 +52,6 @@ namespace CE static MetaType Reflect(); REFLECT_AT_START_UP(Physics); - // mWorld needs to be updated in World::World(World&&), so we give access to World to do so. - friend class World; std::reference_wrapper mWorld; BVHS mBVHs; diff --git a/Include/World/Registry.h b/Include/World/Registry.h index ed485d40..5d554475 100644 --- a/Include/World/Registry.h +++ b/Include/World/Registry.h @@ -157,9 +157,6 @@ namespace CE void CallEndPlayEventsForEntity(entt::sparse_set& storage, entt::entity entity, const BoundEvent& endPlayEvent); - // mWorld needs to be updated in World::World(World&&), so we give access to World to do so. - friend World; - std::reference_wrapper mWorld; entt::registry mRegistry{}; @@ -217,8 +214,6 @@ namespace CE } }(); - World::PushWorld(mWorld); - if constexpr (isEmpty) { mRegistry.emplace(toEntity, std::forward(additionalArgs)...); @@ -236,8 +231,6 @@ namespace CE events.mOnBeginPlay->mFunc.get().InvokeUncheckedUnpacked(GetWorld(), toEntity); } } - - World::PopWorld(); } else { @@ -271,8 +264,6 @@ namespace CE } } - World::PopWorld(); - return component; } } @@ -307,7 +298,6 @@ namespace CE template void Registry::RemoveComponent(entt::entity fromEntity) { - World::PushWorld(mWorld); static constexpr TypeId componentClassTypeId = MakeStrippedTypeId(); entt::sparse_set* storage = Storage(componentClassTypeId); ASSERT(storage != nullptr); @@ -335,7 +325,6 @@ namespace CE } storage->erase(fromEntity); - World::PopWorld(); } template @@ -358,8 +347,6 @@ namespace CE return; } - World::PushWorld(mWorld); - if constexpr (sIsReflectable) { static std::optional endPlayEvent = @@ -379,7 +366,6 @@ namespace CE { if (!storage->contains(fromEntity)) { - World::PopWorld(); return; } @@ -388,7 +374,6 @@ namespace CE } storage->remove(fromEntity); - World::PopWorld(); } template diff --git a/Include/World/World.h b/Include/World/World.h index 07a9e04b..9a336463 100644 --- a/Include/World/World.h +++ b/Include/World/World.h @@ -18,14 +18,15 @@ namespace CE { public: World(bool beginPlayImmediately); - World(World&& other) noexcept; - World(const World&) = delete; - ~World(); + World(World&&) = delete; + World(const World&) = delete; - World& operator=(World&& other) noexcept; + World& operator=(World&&) noexcept = delete; World& operator=(const World&) = delete; + ~World(); + void Tick(float deltaTime); void Render(FrameBuffer* renderTarget = nullptr); @@ -75,11 +76,6 @@ namespace CE void RequestEndplay(); - static void PushWorld(World& world); - static void PopWorld(); - - static World* TryGetWorldAtTopOfStack(); - /** * \brief Will request a transition to a different level. * diff --git a/Include/World/WorldViewport.h b/Include/World/WorldViewport.h index 501921d6..b77661bc 100644 --- a/Include/World/WorldViewport.h +++ b/Include/World/WorldViewport.h @@ -23,8 +23,6 @@ namespace CE glm::vec3 ScreenToWorldPlane(glm::vec2 screenPosition, float planeHeight) const; private: - // mWorld needs to be updated in World::World(World&&), so we give access to World to do so. - friend class World; std::reference_wrapper mWorld; // In pixels diff --git a/Source/Assets/Level.cpp b/Source/Assets/Level.cpp index bf225626..ca7d9c18 100644 --- a/Source/Assets/Level.cpp +++ b/Source/Assets/Level.cpp @@ -44,7 +44,7 @@ CE::Level::Level(std::string_view name) : CE::Level::Level(AssetLoadInfo& loadInfo) : Asset(loadInfo), - mWorld(false) + mWorld(std::make_unique(false)) { BinaryGSONObject savedData{}; savedData.LoadFromBinary(loadInfo.GetStream()); @@ -178,7 +178,7 @@ void CE::Level::OnSave(AssetSaveInfo& saveInfo) const { if (!mSerializedWorld.has_value()) { - if (!mWorld.has_value()) + if (mWorld == nullptr) { LOG(LogAssets, Error, "Cannot save level {}, mSerializedComponents and mWorld are null", GetName()); @@ -213,49 +213,49 @@ void CE::Level::CreateFromWorld(const World& world) mSerializedWorld.emplace(Archiver::Serialize(world)); } -CE::World CE::Level::CreateWorld(const bool callBeginPlayImmediately) const +void CE::Level::LoadIntoWorld(World& world) const { - if (mWorld.has_value()) + if (!mSerializedWorld.has_value()) { - World world = std::move(*mWorld); - mWorld.reset(); + LOG(LogAssets, Warning, "Failed to load {} into world, mSerializedWorld was null", GetName()); + return; + } + Archiver::Deserialize(world, *mSerializedWorld); +} + +std::unique_ptr CE::Level::CreateWorld(const bool callBeginPlayImmediately) const +{ + if (mWorld != nullptr) + { if (!mSerializedWorld.has_value()) { - mSerializedWorld.emplace(Archiver::Serialize(world)); + mSerializedWorld.emplace(Archiver::Serialize(*mWorld)); } if (callBeginPlayImmediately) { - world.BeginPlay(); + mWorld->BeginPlay(); } - return world; + return std::move(mWorld); } - World world{ false }; - - if (mSerializedWorld.has_value()) - { - Archiver::Deserialize(world, *mSerializedWorld); - } - else - { - LOG(LogAssets, Warning, "mWorld and mSerializedWorld were both null for {}", GetName()); - } + std::unique_ptr world = std::make_unique(false); + LoadIntoWorld(*world); if (callBeginPlayImmediately) { - world.BeginPlay(); + world->BeginPlay(); } return world; } -CE::World CE::Level::CreateDefaultWorld() +std::unique_ptr CE::Level::CreateDefaultWorld() { - World world{ false }; - Registry& reg = world.GetRegistry(); + std::unique_ptr world = std::make_unique(false); + Registry& reg = world->GetRegistry(); { const entt::entity camera = reg.Create(); diff --git a/Source/Assets/Script.cpp b/Source/Assets/Script.cpp index a72df196..8ff2f261 100644 --- a/Source/Assets/Script.cpp +++ b/Source/Assets/Script.cpp @@ -16,6 +16,16 @@ #include "Utilities/Reflect/ReflectAssetType.h" #include "Utilities/Reflect/ReflectComponentType.h" +// We've hardcoded our scripts to always have a +// While fields normally dont support ptr/ref types, +// we can make a little wrapper around our pointer +struct WorldPointer +{ + CE::World* mData{}; + static CE::MetaType Reflect(); + REFLECT_AT_START_UP(WorldPointer); +}; + CE::Script::Script(std::string_view name) : Asset(name, MakeTypeId