diff --git a/Coral.vcxproj b/Coral.vcxproj
index cf959681..0b8b35ea 100644
--- a/Coral.vcxproj
+++ b/Coral.vcxproj
@@ -301,6 +301,7 @@
+
@@ -324,7 +325,6 @@
-
@@ -862,13 +862,13 @@
+
-
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/Core/ThreadPool.h b/Include/Core/ThreadPool.h
new file mode 100644
index 00000000..fcf5cea6
--- /dev/null
+++ b/Include/Core/ThreadPool.h
@@ -0,0 +1,54 @@
+#pragma once
+#include
+#include
+
+namespace CE
+{
+ class ThreadPool final :
+ public EngineSubsystem
+ {
+ friend EngineSubsystem;
+
+ ThreadPool();
+ ~ThreadPool(); // joins all threads
+ public:
+
+ template
+ decltype(auto) Enqueue(F&& callable, A &&...arguments);
+
+ size_t NumberOfThreads() const { return mThreads.size(); }
+
+ private:
+ std::vector mThreads{};
+ std::queue> mTasks{};
+ std::mutex mMutex{};
+ std::condition_variable mCondition{};
+ bool mStopped{};
+ };
+
+ // add new work item to the pool
+ template
+ decltype(auto) ThreadPool::Enqueue(F&& f, Args&&... args)
+ {
+ using return_type = std::invoke_result_t;
+
+ auto task = std::make_shared>(
+ std::bind(std::forward(f), std::forward(args)...)
+ );
+
+ std::future res = task->get_future();
+ {
+ std::unique_lock lock{ mMutex };
+ mTasks.emplace([task]{ (*task)(); });
+ }
+ mCondition.notify_one();
+ return res;
+ }
+
+
+ template
+ bool IsFutureReady(const std::future& f)
+ {
+ return f.valid() && f.wait_for(std::chrono::seconds{}) == std::future_status::ready;
+ }
+}
\ No newline at end of file
diff --git a/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h b/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h
index 8445cbcd..6ce5c2d2 100644
--- a/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h
+++ b/Include/EditorSystems/AssetEditorSystems/ScriptEditorSystem.h
@@ -1,9 +1,8 @@
-#include "Utilities/ASync.h"
#ifdef EDITOR
#pragma once
#include "EditorSystems/AssetEditorSystems/AssetEditorSystem.h"
-#include
+#include
#include "imnodes/imgui_node_editor.h"
#include "Assets/Script.h"
@@ -202,8 +201,10 @@ namespace CE
void InitialiseAllNodesTheUserCanAdd();
std::vector mAllNodesTheUserCanAdd{};
- ASyncThread mNodePopularityCalculateThread{};
- bool mShouldWeStopCountingNodePopularity{};
+
+ // The popularity is stored inside of each node in mAllNodesTheUserCanAdd
+ std::future mNodePopularityCalculations{};
+ std::atomic mShouldWeStopCountingNodePopularity{};
ax::NodeEditor::PinId mPinTheUserRightClicked{};
ax::NodeEditor::PinId mPinTheUserIsTryingToLink{};
diff --git a/Include/EditorSystems/ContentBrowserEditorSystem.h b/Include/EditorSystems/ContentBrowserEditorSystem.h
index 61ccde63..c5ced8b9 100644
--- a/Include/EditorSystems/ContentBrowserEditorSystem.h
+++ b/Include/EditorSystems/ContentBrowserEditorSystem.h
@@ -1,8 +1,9 @@
#ifdef EDITOR
#pragma once
+#include
+
#include "EditorSystems/EditorSystem.h"
#include "Core/AssetManager.h"
-#include "Utilities/ASync.h"
namespace CE
{
@@ -25,7 +26,6 @@ namespace CE
private:
struct ContentFolder
{
- ContentFolder() = default;
ContentFolder(const std::filesystem::path& path, std::string&& name, ContentFolder* parent) :
mActualPath(path),
mFolderName(std::move(name)),
@@ -33,10 +33,10 @@ namespace CE
{}
ContentFolder(const ContentFolder&) = delete;
- ContentFolder(ContentFolder&&) noexcept = default;
+ ContentFolder(ContentFolder&&) noexcept = delete;
ContentFolder& operator=(const ContentFolder&) = delete;
- ContentFolder& operator=(ContentFolder&&) noexcept = default;
+ ContentFolder& operator=(ContentFolder&&) noexcept = delete;
// Will be empty for the root folder
std::filesystem::path mActualPath{};
@@ -46,6 +46,7 @@ namespace CE
ContentFolder* mParent{};
std::vector> mContent{};
};
+
void RequestUpdateToFolderGraph();
void DisplayFolderHierarchyPanel();
@@ -87,11 +88,11 @@ namespace CE
static MetaType Reflect();
REFLECT_AT_START_UP(ContentBrowserEditorSystem);
- ContentFolder mRootFolder{ {}, "All", nullptr };
- std::reference_wrapper mSelectedFolder = mRootFolder;
+ std::unique_ptr mRootFolder = std::make_unique(std::filesystem::path{}, "All", nullptr);
+ std::reference_wrapper mSelectedFolder = *mRootFolder;
std::filesystem::path mSelectedFolderPath{};
- ASyncFuture mPendingRootFolder{};
+ std::future> mPendingRootFolder{};
float mFolderHierarchyPanelWidthPercentage = .25f;
float mContentPanelWidthPercentage = .75f;
diff --git a/Include/EditorSystems/ImporterSystem.h b/Include/EditorSystems/ImporterSystem.h
index 34c14396..daad5343 100644
--- a/Include/EditorSystems/ImporterSystem.h
+++ b/Include/EditorSystems/ImporterSystem.h
@@ -1,14 +1,11 @@
-#include "Utilities/ASync.h"
-#include "Utilities/Time.h"
#ifdef EDITOR
#include "EditorSystems/EditorSystem.h"
#include
#include "Core/AssetManager.h"
-#include "Assets/Core/AssetLoadInfo.h"
#include "Assets/Importers/Importer.h"
-#include "Utilities/MemFunctions.h"
+#include "Utilities/Time.h"
namespace CE
{
@@ -51,7 +48,7 @@ namespace CE
struct ImportFuture
{
ImportRequest mImportRequest{};
- ASyncFuture>> mImportResult{};
+ std::future>> mImportResult{};
};
struct DirToWatch
@@ -113,7 +110,7 @@ namespace CE
std::array mDirectoriesToWatch{};
Cooldown mCheckDirectoryCooldown{ 10.0f };
- ASyncFuture> mChangedFilesInDirectoriesToWatch{};
+ std::future> mChangedFilesInDirectoriesToWatch{};
static inline bool sExcludeDuplicates{};
static inline bool sIgnoreReadOnly = true;
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/ASync.h b/Include/Utilities/ASync.h
deleted file mode 100644
index 0f242efb..00000000
--- a/Include/Utilities/ASync.h
+++ /dev/null
@@ -1,81 +0,0 @@
-#pragma once
-
-namespace CE
-{
- namespace Internal
- {
- struct Job;
- }
-
- // Like a regular thread, except this one
- // does not slow down the main thread.
- class ASyncThread
- {
- public:
- ASyncThread() = default;
- ASyncThread(std::function&& work);
-
- ASyncThread(const ASyncThread&) = delete;
- ASyncThread(ASyncThread&&) noexcept = default;
-
- ASyncThread& operator=(const ASyncThread&) = delete;
- ASyncThread& operator=(ASyncThread&&) noexcept = default;
-
- ~ASyncThread();
-
- bool WasLaunched() const;
-
- void CancelOrJoin();
- void CancelOrDetach();
- void Join();
- void Detach();
-
- private:
- std::shared_ptr mJob{};
- };
-
- template
- class ASyncFuture
- {
- public:
- ASyncFuture() = default;
- ASyncFuture(std::function&& work) :
- mValue(std::make_shared>()),
- mThread(
- [value = mValue, work]
- {
- const_cast>&>(value)->emplace(work());
- }
- )
- {
- }
-
- ASyncFuture(const ASyncFuture&) = delete;
- ASyncFuture(ASyncFuture&&) noexcept = default;
-
- ASyncFuture& operator=(const ASyncFuture&) = delete;
- ASyncFuture& operator=(ASyncFuture&&) noexcept = default;
-
- ASyncThread& GetThread() { return mThread; }
- const ASyncThread& GetThread() const { return mThread; }
-
- bool IsReady() const { return mValue != nullptr && mValue->has_value(); }
-
- T& Get();
-
- private:
- std::shared_ptr> mValue{};
- ASyncThread mThread{};
- };
-
- template
- T& ASyncFuture::Get()
- {
- if (mThread.WasLaunched())
- {
- mThread.Join();
- }
-
- return **mValue;
- }
-}
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