diff --git a/Game/Game.vcxproj b/Game/Game.vcxproj
index f112a2b..dc46a10 100644
--- a/Game/Game.vcxproj
+++ b/Game/Game.vcxproj
@@ -48,10 +48,9 @@
true
- $(SolutionDir)Juliet\include\;$(SolutionDir)Game;$(IncludePath)
- $(SolutionDir)\bin\$(Platform)\$(Configuration)\;$(LibraryPath)
$(SolutionDir)\bin\$(Platform)\$(Configuration)\
$(SolutionDir)Intermediate\$(ProjectName)\$(Platform)\$(Configuration)\
+ $(ProjectName)
false
@@ -66,10 +65,15 @@
true
pch.h
stdcpp20
+ $(SolutionDir)Juliet\include\;$(SolutionDir)Game;
Windows
true
+ Juliet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)
+ $(OutDir)$(TargetName)$(TargetExt)
+ $(SolutionDir)\bin\$(Platform)\$(Configuration)\
+ $(OutDir)$(TargetName)_$(Random).pdb
@@ -83,24 +87,21 @@
NDEBUG;CPPDYNAMICLIBRARYTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
true
pch.h
+ $(SolutionDir)Juliet\include\;$(SolutionDir)Game;
Windows
true
true
true
+ $(SolutionDir)\bin\$(Platform)\$(Configuration)\
+ Juliet.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)
-
+
-
-
- {1bbc0b92-e4d8-4838-974b-439c5c501e82}
- Juliet
-
-
diff --git a/Game/dllmain.cpp b/Game/dllmain.cpp
deleted file mode 100644
index 97e8886..0000000
--- a/Game/dllmain.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#include
-#include // TODO: remove because our dll should not be platform dependant
-
-#include
-#include
-
-// Test code
-namespace Game
-{
- struct Door
- {
- DECLARE_ENTITY()
- bool IsOpened;
- };
- DEFINE_ENTITY(Door);
-
- struct Rock
- {
- DECLARE_ENTITY()
- int Health;
- };
- DEFINE_ENTITY(Rock);
-
-} // namespace Game
-
-BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
-{
- using namespace Game;
-
- switch (ul_reason_for_call)
- {
- case DLL_PROCESS_ATTACH:
- {
- // Entity Use case
- InitEntityManager();
- auto& manager = GetEntityManager();
- Door* door = MakeEntity(manager, 10.0f, 2.0f);
- door->IsOpened = true;
-
- Entity* ent = door->Base;
- Door* stillDoor = DownCast(ent);
- Assert(door == stillDoor);
-
- Rock* rock = MakeEntity(manager, 1.f, 2.f);
- rock->Health = 100;
- Assert(door->Base != rock->Base);
-
- printf("Door is %s\n", door->IsOpened ? "Opened" : "Closed");
- printf("Rock has %d health points\n", rock->Health);
-
- // Have to manually free for now because im not using arenas or anything
- free(door->Base);
- free(door);
- free(rock->Base);
- free(rock);
-
- break;
- }
-
- case DLL_THREAD_ATTACH: break;
- case DLL_THREAD_DETACH: break;
- case DLL_PROCESS_DETACH: break;
- }
-
- return TRUE;
-}
diff --git a/Game/game.cpp b/Game/game.cpp
new file mode 100644
index 0000000..182b068
--- /dev/null
+++ b/Game/game.cpp
@@ -0,0 +1,69 @@
+#include
+#include // TODO: remove because our dll should not be platform dependant
+#undef min
+#undef max
+
+#include
+#include
+#include
+#include
+#include
+
+// Test code
+namespace Game
+{
+ struct Door
+ {
+ DECLARE_ENTITY()
+ bool IsOpened;
+ };
+ DEFINE_ENTITY(Door);
+
+ struct Rock
+ {
+ DECLARE_ENTITY()
+ int Health;
+ };
+ DEFINE_ENTITY(Rock);
+
+} // namespace Game
+
+using namespace Juliet;
+
+extern "C" __declspec(dllexport) void __cdecl GameInit()
+{
+ using namespace Game;
+
+ // Entity Use case
+ InitEntityManager();
+ auto& manager = GetEntityManager();
+ Door* door = MakeEntity(manager, 10.0f, 2.0f);
+ door->IsOpened = true;
+
+ Entity* ent = door->Base;
+ Door* stillDoor = DownCast(ent);
+ Assert(door == stillDoor);
+
+ Rock* rock = MakeEntity(manager, 1.f, 2.f);
+ rock->Health = 100;
+ Assert(door->Base != rock->Base);
+
+ printf("Door is %s\n", door->IsOpened ? "Opened" : "Closed");
+ printf("Rock has %d health points\n", rock->Health);
+
+ // Have to manually free for now because im not using arenas or anything
+ free(door->Base);
+ free(door);
+ free(rock->Base);
+ free(rock);
+}
+
+extern "C" __declspec(dllexport) void __cdecl GameShutdown()
+{
+ printf("Shutting down game...\n");
+}
+
+extern "C" __declspec(dllexport) void __cdecl GameUpdate(float deltaTime)
+{
+ printf("Updating game...\n");
+}
diff --git a/Juliet.sln b/Juliet.sln
index 00d6d7b..7f6d579 100644
--- a/Juliet.sln
+++ b/Juliet.sln
@@ -11,6 +11,9 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JulietApp", "JulietApp\Juli
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Game", "Game\Game.vcxproj", "{B7B12DCC-1A69-4371-A9FE-D6E7671497B0}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1BBC0B92-E4D8-4838-974B-439C5C501E82} = {1BBC0B92-E4D8-4838-974B-439C5C501E82}
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/Juliet/Juliet.vcxproj b/Juliet/Juliet.vcxproj
index 2b134ec..fc6b95e 100644
--- a/Juliet/Juliet.vcxproj
+++ b/Juliet/Juliet.vcxproj
@@ -113,16 +113,16 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -152,6 +152,8 @@
+
+
@@ -168,69 +170,20 @@
-
- MultiThreadedDebugDll
- Disabled
- true
- NoListing
- W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\
- false
- W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\
- Default
- Default
- Column
- false
- false
- false
- NotSet
- Fast
- Default
- false
- stdcpp20
- Default
- W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\
- false
- Neither
- W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\
- Cdecl
- Use
- pch.h
- W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\Juliet.pch
- false
- false
- false
- false
- false
- W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\
- true
- true
- false
- Default
- W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\Juliet.tlog\
- true
- false
- Level3
- W:\Classified\Juliet\Intermediate\Juliet\x64\Debug\
- ProgramDatabase
- false
- false
- true
- _DEBUG;JULIET_EXPORT;JULIET_WIN32;_WINDLL;_UNICODE;UNICODE;
- false
- true
- true
- Default
- --target=amd64-pc-windows-msvc
-
+
+
+
+
+
diff --git a/Juliet/include/Core/Common/CoreTypes.h b/Juliet/include/Core/Common/CoreTypes.h
index 85d34de..ddad9b3 100644
--- a/Juliet/include/Core/Common/CoreTypes.h
+++ b/Juliet/include/Core/Common/CoreTypes.h
@@ -24,6 +24,14 @@ struct ByteBuffer
size_t Size;
};
+template
+struct StringBuffer
+{
+ CharType* Data;
+ size_t Size;
+};
+#define StringBufferParam(str) { (str), sizeof(str) }
+
using FunctionPtr = auto (*)(void) -> void;
// Limits
diff --git a/Juliet/include/Core/HAL/Display/Display.h b/Juliet/include/Core/HAL/Display/Display.h
index 0c80edc..2f6f543 100644
--- a/Juliet/include/Core/HAL/Display/Display.h
+++ b/Juliet/include/Core/HAL/Display/Display.h
@@ -1,7 +1,7 @@
#pragma once
#include
-#include
+#include
#include
namespace Juliet
diff --git a/Juliet/include/Core/DynLib/DynamicLibrary.h b/Juliet/include/Core/HAL/DynLib/DynamicLibrary.h
similarity index 81%
rename from Juliet/include/Core/DynLib/DynamicLibrary.h
rename to Juliet/include/Core/HAL/DynLib/DynamicLibrary.h
index 894fbc0..7d6e10e 100644
--- a/Juliet/include/Core/DynLib/DynamicLibrary.h
+++ b/Juliet/include/Core/HAL/DynLib/DynamicLibrary.h
@@ -1,12 +1,10 @@
#pragma once
-#include
+#include
namespace Juliet
{
- struct DynamicLibrary
- {
- };
+ struct DynamicLibrary;
extern JULIET_API DynamicLibrary* LoadDynamicLibrary(const char* filename);
extern JULIET_API FunctionPtr LoadFunction(NonNullPtr lib, const char* functionName);
diff --git a/Juliet/include/Core/HAL/Filesystem/Filesystem.h b/Juliet/include/Core/HAL/Filesystem/Filesystem.h
new file mode 100644
index 0000000..0da2d87
--- /dev/null
+++ b/Juliet/include/Core/HAL/Filesystem/Filesystem.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace Juliet
+{
+ // Returns the path to the application directory
+ extern JULIET_API char * GetBasePath();
+}
\ No newline at end of file
diff --git a/Juliet/include/Core/HotReload/HotReload.h b/Juliet/include/Core/HotReload/HotReload.h
new file mode 100644
index 0000000..b61447c
--- /dev/null
+++ b/Juliet/include/Core/HotReload/HotReload.h
@@ -0,0 +1,36 @@
+#pragma once
+
+namespace Juliet
+{
+ // Fwd Declare
+ struct DynamicLibrary;
+
+ struct HotReloadCode
+ {
+ StringBuffer DLLFullPath;
+ StringBuffer LockFullPath;
+ StringBuffer TransientDLLName;
+
+ uint64 LastWriteTime;
+
+ DynamicLibrary* Dll;
+
+ void** Functions;
+ const char** FunctionNames;
+ uint32 FunctionCount;
+
+ uint32 UniqueID;
+
+ bool IsValid : 1;
+ };
+
+ extern JULIET_API void InitHotReloadCode(HotReloadCode& code, StringBuffer dllName,
+ StringBuffer transientDllName, StringBuffer lockFilename);
+ extern JULIET_API void ShutdownHotReloadCode(HotReloadCode& code);
+
+ extern JULIET_API void LoadCode(HotReloadCode& code);
+ extern JULIET_API void UnloadCode(HotReloadCode& code);
+
+ extern JULIET_API void ReloadCode(HotReloadCode& code);
+ extern JULIET_API bool ShouldReloadCode(const HotReloadCode& code);
+} // namespace Juliet
diff --git a/Juliet/include/Core/Logging/LogManager.h b/Juliet/include/Core/Logging/LogManager.h
index 9fc0e7f..2655932 100644
--- a/Juliet/include/Core/Logging/LogManager.h
+++ b/Juliet/include/Core/Logging/LogManager.h
@@ -1,6 +1,7 @@
#pragma once
#include
+#include
// TODO : Juliet strings
#include
diff --git a/Juliet/include/Core/Memory/Allocator.h b/Juliet/include/Core/Memory/Allocator.h
index a1708bc..2501d54 100644
--- a/Juliet/include/Core/Memory/Allocator.h
+++ b/Juliet/include/Core/Memory/Allocator.h
@@ -1,6 +1,6 @@
#pragma once
-#include
+#include
namespace Juliet
{
@@ -21,8 +21,10 @@ namespace Juliet
template
void SafeFree(Type*& memory)
{
- Assert(memory);
- free(memory);
- memory = nullptr;
+ if (memory)
+ {
+ free(memory);
+ memory = nullptr;
+ }
}
} // namespace Juliet
diff --git a/Juliet/include/Core/Thread/Thread.h b/Juliet/include/Core/Thread/Thread.h
index 6fbc158..9950085 100644
--- a/Juliet/include/Core/Thread/Thread.h
+++ b/Juliet/include/Core/Thread/Thread.h
@@ -5,4 +5,13 @@
namespace Juliet
{
using Thread = std::thread;
+
+ // TODO : Proper wait
+ inline void wait_ms(int milliseconds)
+ {
+ clock_t start_time = clock();
+ while (clock() < start_time + milliseconds)
+ {
+ }
+ }
}
diff --git a/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp b/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp
index 228f9c7..2712249 100644
--- a/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp
+++ b/Juliet/src/Core/HAL/Display/Win32/Win32Window.cpp
@@ -34,7 +34,7 @@ namespace Juliet::Win32
ReleaseDC(state->Handle, state->HDC);
DestroyWindow(state->Handle);
- Free(state);
+ SafeFree(state);
}
window->State = nullptr;
}
diff --git a/Juliet/src/Core/DynLib/Win32/DynamicLibrary.cpp b/Juliet/src/Core/HAL/DynLib/Win32/DynamicLibrary.cpp
similarity index 89%
rename from Juliet/src/Core/DynLib/Win32/DynamicLibrary.cpp
rename to Juliet/src/Core/HAL/DynLib/Win32/DynamicLibrary.cpp
index 9367bfd..c01bf41 100644
--- a/Juliet/src/Core/DynLib/Win32/DynamicLibrary.cpp
+++ b/Juliet/src/Core/HAL/DynLib/Win32/DynamicLibrary.cpp
@@ -1,6 +1,6 @@
#include
-#include
+#include
#include
namespace Juliet
@@ -10,7 +10,7 @@ namespace Juliet
// TODO : Move into string file
// Use portable code + pass the memory array into parameter and not use new
// This is from http://www.rohitab.com/discuss/topic/41257-char-to-lpcwstr/
- wchar_t* UTF8ToWideChar(const char* utf8)
+ static wchar_t* UTF8ToWideChar(const char* utf8)
{
wchar_t* w;
@@ -60,7 +60,7 @@ namespace Juliet
FunctionPtr LoadFunction(NonNullPtr lib, const char* functionName)
{
- auto function = (FunctionPtr)GetProcAddress(reinterpret_cast(lib.Get()), functionName);
+ auto function = reinterpret_cast(GetProcAddress(reinterpret_cast(lib.Get()), functionName));
if (!function)
{
Log(LogLevel::Error, LogCategory::Core, "Failed loading %s", functionName);
diff --git a/Juliet/src/Core/HAL/Filesystem/Filesystem.cpp b/Juliet/src/Core/HAL/Filesystem/Filesystem.cpp
new file mode 100644
index 0000000..c1c07cf
--- /dev/null
+++ b/Juliet/src/Core/HAL/Filesystem/Filesystem.cpp
@@ -0,0 +1,32 @@
+#include
+
+#include
+#include
+#include
+#include
+
+namespace Juliet
+{
+ namespace
+ {
+ char* CachedBasePath = nullptr;
+ }
+ char* GetBasePath()
+ {
+ if (CachedBasePath == nullptr)
+ {
+ CachedBasePath = Platform::GetBasePath();
+ }
+ return CachedBasePath;
+ }
+
+ void InitFilesystem() {}
+
+ void ShutdownFilesystem()
+ {
+ if (CachedBasePath != nullptr)
+ {
+ SafeFree(CachedBasePath);
+ }
+ }
+} // namespace Juliet
diff --git a/Juliet/src/Core/HAL/Filesystem/Filesystem_Platform.h b/Juliet/src/Core/HAL/Filesystem/Filesystem_Platform.h
new file mode 100644
index 0000000..7f2fd12
--- /dev/null
+++ b/Juliet/src/Core/HAL/Filesystem/Filesystem_Platform.h
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace Juliet::Platform
+{
+ extern char* GetBasePath();
+}
\ No newline at end of file
diff --git a/Juliet/src/Core/HAL/Filesystem/Filesystem_Private.h b/Juliet/src/Core/HAL/Filesystem/Filesystem_Private.h
new file mode 100644
index 0000000..8a599bd
--- /dev/null
+++ b/Juliet/src/Core/HAL/Filesystem/Filesystem_Private.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace Juliet
+{
+ extern void InitFilesystem();
+ extern void ShutdownFilesystem();
+}
\ No newline at end of file
diff --git a/Juliet/src/Core/HAL/Filesystem/Win32/Win32Filesystem.cpp b/Juliet/src/Core/HAL/Filesystem/Win32/Win32Filesystem.cpp
new file mode 100644
index 0000000..70c83f3
--- /dev/null
+++ b/Juliet/src/Core/HAL/Filesystem/Win32/Win32Filesystem.cpp
@@ -0,0 +1,90 @@
+#include
+
+#include
+#include
+#include
+
+namespace Juliet::Platform
+{
+ namespace
+ {
+ // TODO : Move into string file
+ // Use portable code + pass the memory array into parameter and not use new
+ // From: https://stackoverflow.com/questions/215963/how-do-you-properly-use-widechartomultibyte
+ char* WideCharToUTF8(wchar_t* wcharStr)
+ {
+ char* result = nullptr;
+ size_t length = WideCharToMultiByte(CP_UTF8, 0, wcharStr, -1, nullptr, 0, nullptr, nullptr);
+ if (length <= 0)
+ {
+ return nullptr;
+ }
+
+ result = new char[length];
+ if (!result)
+ {
+ return nullptr;
+ }
+
+ if (WideCharToMultiByte(CP_UTF8, 0, wcharStr, -1, result, length, nullptr, nullptr) <= 0)
+ {
+ delete[] result;
+ return nullptr;
+ }
+
+ return result;
+ }
+ } // namespace
+
+ char* GetBasePath()
+ {
+ // Allocate a buffer that could fit the module size.
+ // Max Path is a good start but could be bigger if the path include long path prefix
+ StringBuffer buffer{ .Data = nullptr, .Size = MAX_PATH };
+ buffer.Data = static_cast(Calloc(MAX_PATH, sizeof(WCHAR)));
+ if (buffer.Data == nullptr)
+ {
+ return nullptr;
+ }
+
+ size_t moduleFilenameLength = 0;
+ while (true)
+ {
+ moduleFilenameLength = GetModuleFileNameW(nullptr, buffer.Data, static_cast(buffer.Size));
+
+ // If the module filename length is bigger than the buffer size, we need to reallocate a bigger buffer
+ if (moduleFilenameLength >= buffer.Size - 1)
+ {
+ buffer.Size *= 2;
+ buffer.Data = static_cast(Realloc(buffer.Data, buffer.Size * sizeof(WCHAR)));
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if (moduleFilenameLength == 0)
+ {
+ SafeFree(buffer.Data);
+ Log(LogLevel::Error, LogCategory::Core, "Filesystem: Cannot locate executable path");
+ }
+
+ size_t idx = 0;
+ for (idx = moduleFilenameLength - 1; idx > 0; --idx)
+ {
+ if (buffer.Data[idx] == '\\')
+ {
+ break;
+ }
+ }
+
+ Assert(idx > 0 && "Path is not absolute!");
+ buffer.Data[idx + 1] = '\0'; // Chop chop
+
+ // TODO: Add utils to Convert to/from UTF8W
+ char* basePath = WideCharToUTF8(buffer.Data);
+ SafeFree(buffer.Data);
+ return basePath;
+ }
+} // namespace Juliet::Platform
diff --git a/Juliet/src/Core/HotReload/HotReload.cpp b/Juliet/src/Core/HotReload/HotReload.cpp
new file mode 100644
index 0000000..0f02b95
--- /dev/null
+++ b/Juliet/src/Core/HotReload/HotReload.cpp
@@ -0,0 +1,70 @@
+#include
+
+#include
+#include
+#include
+#include
+
+#define MAX_TRIES 100
+
+namespace Juliet
+{
+ void InitHotReloadCode(HotReloadCode& code, StringBuffer dllName,
+ StringBuffer transientDllName, StringBuffer lockFilename)
+ {
+ // Get the app base path and build the dll path from there.
+ const char* basePath = GetBasePath();
+ size_t basePathLength = strlen(basePath);
+
+ // Assign Transient dll path
+ code.TransientDLLName = transientDllName;
+
+ // First allocate all the full path.
+ // TODO: Add path composition into filesystem + string format + string builder
+ const size_t dllFullPathLength = basePathLength + dllName.Size;
+ code.DLLFullPath.Data = static_cast(Calloc(dllFullPathLength, sizeof(char)));
+ int writtenSize = snprintf(code.DLLFullPath.Data, dllFullPathLength, "%s%s", basePath, dllName.Data);
+ if (writtenSize < static_cast(dllFullPathLength) - 1)
+ {
+ SafeFree(code.DLLFullPath.Data);
+ Log(LogLevel::Error, LogCategory::Core, "Cannot create DLL Full Path");
+ return;
+ }
+ code.DLLFullPath.Size = writtenSize + 1;
+
+ // Lock filename path
+ const size_t lockPathLength = basePathLength + lockFilename.Size;
+ code.LockFullPath.Data = static_cast(Calloc(lockPathLength, sizeof(char)));
+ writtenSize = snprintf(code.LockFullPath.Data, lockPathLength, "%s%s", basePath, lockFilename.Data);
+ if (writtenSize < static_cast(lockPathLength) - 1)
+ {
+ code.LockFullPath.Size = 0;
+ SafeFree(code.LockFullPath.Data);
+ Log(LogLevel::Error, LogCategory::Core, "Cannot create lock file full path");
+ return;
+ }
+ code.LockFullPath.Size = writtenSize + 1;
+
+ LoadCode(code);
+ }
+
+ void ShutdownHotReloadCode(HotReloadCode& code)
+ {
+ UnloadCode(code);
+
+ code.DLLFullPath.Size = 0;
+ SafeFree(code.DLLFullPath.Data);
+ code.LockFullPath.Size = 0;
+ SafeFree(code.LockFullPath.Data);
+ }
+
+ void ReloadCode(HotReloadCode& code)
+ {
+ UnloadCode(code);
+ for (uint32 tryItr = 0; !code.IsValid && tryItr < MAX_TRIES; ++tryItr)
+ {
+ LoadCode(code);
+ wait_ms(100);
+ }
+ }
+} // namespace Juliet
diff --git a/Juliet/src/Core/HotReload/Win32/Win32HotReload.cpp b/Juliet/src/Core/HotReload/Win32/Win32HotReload.cpp
new file mode 100644
index 0000000..af1992d
--- /dev/null
+++ b/Juliet/src/Core/HotReload/Win32/Win32HotReload.cpp
@@ -0,0 +1,134 @@
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_ATTEMPT 256
+#define MAX_DLL_ID 32767
+#define TEMP_DLL_BUFFER_SIZE_FOR_ID 6 // MAX_DLL_ID + _
+
+namespace Juliet
+{
+ namespace
+ {
+ FILETIME GetLastWriteTime(const char* filename)
+ {
+ FILETIME lastWriteTime = {};
+
+ WIN32_FILE_ATTRIBUTE_DATA Data;
+ if (GetFileAttributesExA(filename, GetFileExInfoStandard, &Data))
+ {
+ lastWriteTime = Data.ftLastWriteTime;
+ }
+
+ return lastWriteTime;
+ }
+
+ } // namespace
+
+ void LoadCode(HotReloadCode& code)
+ {
+ // TODO : Create and use a TransientAllocator
+ // Create temp dll name
+
+ char* lockFilename = code.LockFullPath.Data;
+ WIN32_FILE_ATTRIBUTE_DATA Ignored;
+ if (!GetFileAttributesExA(lockFilename, GetFileExInfoStandard, &Ignored))
+ {
+ const char* dllName = code.DLLFullPath.Data;
+
+ FILETIME lastWriteTime = GetLastWriteTime(dllName);
+ ULARGE_INTEGER result{ .LowPart = lastWriteTime.dwLowDateTime, .HighPart = lastWriteTime.dwHighDateTime };
+ code.LastWriteTime = result.QuadPart;
+
+ // Create filename for the temp dll until we find a valid id.
+ // This is not infinite, in a big session we could reach the 128 attempts and fail...
+ // We'll see for better later.
+
+ // Get the app base path and build the dll path from there.
+ const char* basePath = GetBasePath();
+ size_t basePathLength = strlen(basePath);
+
+ StringBuffer tempDllPath = {};
+ const size_t tempDllMaxBufferSize = basePathLength + code.TransientDLLName.Size + TEMP_DLL_BUFFER_SIZE_FOR_ID;
+ tempDllPath.Data = static_cast(Calloc(tempDllMaxBufferSize, sizeof(char)));
+
+ char idToStr[TEMP_DLL_BUFFER_SIZE_FOR_ID];
+ for (uint32 attempt = 0; attempt < MAX_ATTEMPT; ++attempt)
+ {
+ // int to char
+ int idLength = snprintf(idToStr, sizeof(idToStr), "%u", code.UniqueID);
+
+ int writtenSize = snprintf(tempDllPath.Data, tempDllMaxBufferSize, "%s%u_%s", basePath, code.UniqueID,
+ code.TransientDLLName.Data);
+ if (writtenSize < static_cast(basePathLength + idLength + code.TransientDLLName.Size))
+ {
+ SafeFree(tempDllPath.Data);
+ Log(LogLevel::Error, LogCategory::Core, "Cannot create temp full path");
+ return;
+ }
+ tempDllPath.Size = writtenSize + 1;
+
+ if (++code.UniqueID >= MAX_DLL_ID)
+ {
+ code.UniqueID = 0;
+ }
+ if (CopyFileA(dllName, tempDllPath.Data, false))
+ {
+ break;
+ }
+ }
+
+ code.Dll = LoadDynamicLibrary(tempDllPath.Data);
+ if (code.Dll)
+ {
+ code.IsValid = true;
+ for (size_t FunctionIndex = 0; FunctionIndex < code.FunctionCount; ++FunctionIndex)
+ {
+ if (auto function = reinterpret_cast(LoadFunction(code.Dll, code.FunctionNames[FunctionIndex])))
+ {
+ code.Functions[FunctionIndex] = function;
+ }
+ else
+ {
+ code.IsValid = false;
+ }
+ }
+ }
+
+ SafeFree(tempDllPath.Data);
+ tempDllPath = {};
+ }
+
+ if (!code.IsValid)
+ {
+ UnloadCode(code);
+ }
+ }
+
+ void UnloadCode(HotReloadCode& code)
+ {
+ code.IsValid = false;
+ UnloadDynamicLibrary(code.Dll);
+ code.Dll = nullptr;
+
+ code.LastWriteTime = 0;
+ ZeroDynArray(code.FunctionCount, code.Functions);
+ }
+
+ bool ShouldReloadCode(const HotReloadCode& code)
+ {
+ ULARGE_INTEGER largeInt = {};
+ FILETIME codeLastWriteTime = {};
+ largeInt.QuadPart = code.LastWriteTime;
+ codeLastWriteTime.dwHighDateTime = largeInt.HighPart;
+ codeLastWriteTime.dwLowDateTime = largeInt.LowPart;
+
+ FILETIME lastWriteTime = GetLastWriteTime(code.DLLFullPath.Data);
+ int compare = CompareFileTime(&lastWriteTime, &codeLastWriteTime);
+ return compare != 0;
+ }
+} // namespace Juliet
diff --git a/Juliet/src/Core/Juliet.cpp b/Juliet/src/Core/Juliet.cpp
index b1d9066..4c53513 100644
--- a/Juliet/src/Core/Juliet.cpp
+++ b/Juliet/src/Core/Juliet.cpp
@@ -2,6 +2,7 @@
#include
#include
+#include
#include
namespace Juliet
@@ -40,6 +41,10 @@ namespace Juliet
void JulietInit(JulietInit_Flags flags)
{
+ // Mandatory systems
+ InitFilesystem();
+
+ // Optional systems
if ((flags | JulietInit_Flags::Display) != JulietInit_Flags::None)
{
InitializeDisplaySystem();
@@ -55,6 +60,8 @@ namespace Juliet
DecrementSystemRefCount(JulietInit_Flags::Display);
ShutdownDisplaySystem();
}
+
+ ShutdownFilesystem();
}
} // namespace Juliet
diff --git a/Juliet/src/Engine/Engine.cpp b/Juliet/src/Engine/Engine.cpp
index 62b5355..969f7c4 100644
--- a/Juliet/src/Engine/Engine.cpp
+++ b/Juliet/src/Engine/Engine.cpp
@@ -37,22 +37,11 @@ namespace Juliet
EngineInstance.Application = nullptr;
}
- // Todo: proper fixed tick
- void wait_ms(int milliseconds)
- {
- clock_t start_time = clock();
- while (clock() < start_time + milliseconds)
- {
- }
- }
-
void RunEngine()
{
while (EngineInstance.Application->IsRunning())
{
EngineInstance.Application->Update();
-
- wait_ms(16);
}
}
} // namespace Juliet
diff --git a/Juliet/src/Graphics/D3D12/DX12CommandList.cpp b/Juliet/src/Graphics/D3D12/DX12CommandList.cpp
index 685f57f..51c2eff 100644
--- a/Juliet/src/Graphics/D3D12/DX12CommandList.cpp
+++ b/Juliet/src/Graphics/D3D12/DX12CommandList.cpp
@@ -392,7 +392,7 @@ namespace Juliet::D3D12
}
}
- Free(commandList->PresentDatas);
+ SafeFree(commandList->PresentDatas);
Free(commandList.Get());
}
diff --git a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp
index b76106a..ea8a874 100644
--- a/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp
+++ b/Juliet/src/Graphics/D3D12/DX12GraphicsDevice.cpp
@@ -1,7 +1,7 @@
#include
-#include
-#include
+#include
+#include
#include
#include
#include
@@ -200,10 +200,10 @@ namespace Juliet::D3D12
}
// Clean allocations
- Free(driver->AvailableCommandLists);
- Free(driver->SubmittedCommandLists);
- //Free(driver->WindowData); // TODO Should free the vector of WindowData, but we have only one for now
- Free(driver->AvailableFences);
+ SafeFree(driver->AvailableCommandLists);
+ SafeFree(driver->SubmittedCommandLists);
+ // Free(driver->WindowData); // TODO Should free the vector of WindowData, but we have only one for now
+ SafeFree(driver->AvailableFences);
if (driver->IndirectDrawCommandSignature)
{
@@ -310,7 +310,7 @@ namespace Juliet::D3D12
Internal::DestroySwapChain(d3d12Driver, d3d12Driver->WindowData);
- Free(d3d12Driver->WindowData);
+ SafeFree(d3d12Driver->WindowData);
d3d12Driver->WindowData = nullptr;
}
diff --git a/JulietApp/EditorMain.cpp b/JulietApp/EditorMain.cpp
deleted file mode 100644
index 9cad5e0..0000000
--- a/JulietApp/EditorMain.cpp
+++ /dev/null
@@ -1 +0,0 @@
-#include "EditorMain.h"
diff --git a/JulietApp/EditorMain.h b/JulietApp/EditorMain.h
deleted file mode 100644
index f363f78..0000000
--- a/JulietApp/EditorMain.h
+++ /dev/null
@@ -1,5 +0,0 @@
-#pragma once
-class EditorMain
-{
-};
-
diff --git a/JulietApp/JulietApp.vcxproj b/JulietApp/JulietApp.vcxproj
index 9c2aff7..3e3db5d 100644
--- a/JulietApp/JulietApp.vcxproj
+++ b/JulietApp/JulietApp.vcxproj
@@ -11,21 +11,70 @@
-
-
-
-
-
-
-
- {b7b12dcc-1a69-4371-a9fe-d6e7671497b0}
- Game
-
{1bbc0b92-e4d8-4838-974b-439c5c501e82}
Juliet
+
+
+
+
+
+ MultiThreadedDebugDll
+ Disabled
+ true
+ NoListing
+ W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\
+ false
+ W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\
+ Default
+ Default
+ Column
+ false
+ false
+ false
+ NotSet
+ Fast
+ Default
+ false
+ stdcpp20
+ Default
+ W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\
+ false
+ Neither
+ W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\
+ Cdecl
+ NotUsing
+ stdafx.h
+ W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\JulietApp.pch
+ false
+ false
+ false
+ false
+ false
+ W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\
+ true
+ true
+ false
+ Default
+ W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\JulietApp.tlog\
+ true
+ false
+ Level3
+ W:\Classified\Juliet\Intermediate\JulietApp\x64\Debug\
+ ProgramDatabase
+ false
+ false
+ true
+ _DEBUG;JULIET_WIN32;_UNICODE;UNICODE;
+ false
+ true
+ true
+ Default
+ --target=amd64-pc-windows-msvc
+
+
17.0
Win32Proj
diff --git a/JulietApp/Editor/EditorMain_win32.cpp b/JulietApp/main.cpp
similarity index 63%
rename from JulietApp/Editor/EditorMain_win32.cpp
rename to JulietApp/main.cpp
index 6b5c458..4ea920a 100644
--- a/JulietApp/Editor/EditorMain_win32.cpp
+++ b/JulietApp/main.cpp
@@ -1,31 +1,52 @@
-#include
+#include
#include
#include
+#include
#include
+#include
#include
#include
#include
-#include
-
-#include
-
-// TODO : Replace with message box from framework + call main and not winmain + subsystem
-#include
#include
+#include
#include
#include
+#include
+#include
+
+// TODO : Replace with message box from framework + call main and not winmain + subsystem
// TODO : Think how to do the draw pipeline.
// Ex: Expose a Draw method ?
// Store a graphics context ?
// For now. Put everything in Update down below.
// Should split update from draw, update should have a != timestep than graphics (60fps or more)
+
+// TODO : Remove main.h. Useless
+// May be remove that Application class, useless too.
+
using namespace Juliet;
-void Win32EditorApplication::Init()
+namespace
{
- Log(LogLevel::Message, LogCategory::Editor, "Initializing Editor Application...");
+ using GameInit_t = void (*)(void);
+ using GameShutdown_t = void (*)(void);
+ using GameUpdate_t = void (*)(float deltaTime);
+ struct GameFunctionTable
+ {
+ GameInit_t Init = nullptr;
+ GameShutdown_t Shutdown = nullptr;
+ GameUpdate_t Update = nullptr;
+ } Game;
+ const char* GameFunctionTable[] = { "GameInit", "GameShutdown", "GameUpdate" };
+} // namespace
+
+void JulietApplication::Init()
+{
+ Log(LogLevel::Message, LogCategory::Editor, "Initializing Juliet Application...");
+
+ Log(LogLevel::Message, LogCategory::Editor, "%s", GetBasePath());
GraphicsConfig config;
GraphicsDevice = CreateGraphicsDevice(config);
@@ -37,18 +58,25 @@ void Win32EditorApplication::Init()
if (Running)
{
AttachToWindow(GraphicsDevice, MainWindow);
- Game = LoadDynamicLibrary("Game.dll");
+ //Game = LoadDynamicLibrary("Game.dll");
+
+ GameCode.Functions = reinterpret_cast(&Game);
+ GameCode.FunctionCount = ArraySize(GameFunctionTable);
+ GameCode.FunctionNames = GameFunctionTable;
+ InitHotReloadCode(GameCode, StringBufferParam("Game.dll"),StringBufferParam("Game_Temp.dll"), StringBufferParam("lock.tmp"));
+ if ((Running = GameCode.IsValid))
+ {
+ Game.Init();
+ }
}
}
-void Win32EditorApplication::Shutdown()
+void JulietApplication::Shutdown()
{
- Log(LogLevel::Message, LogCategory::Editor, "Shutdown Editor Application...");
+ Log(LogLevel::Message, LogCategory::Editor, "Shutting down Juliet Application...");
- if (Game)
- {
- UnloadDynamicLibrary(Game);
- }
+ Game.Shutdown();
+ ShutdownHotReloadCode(GameCode);
if (MainWindow && GraphicsDevice)
{
@@ -64,9 +92,11 @@ void Win32EditorApplication::Shutdown()
{
DestroyGraphicsDevice(GraphicsDevice);
}
+
+ Log(LogLevel::Message, LogCategory::Editor, "Juliet App shutdown Completed");
}
-void Win32EditorApplication::Update()
+void JulietApplication::Update()
{
SystemEvent evt;
while (GetEvent(evt))
@@ -80,6 +110,13 @@ void Win32EditorApplication::Update()
}
}
+ Game.Update(0.0f);
+
+ if (ShouldReloadCode(GameCode))
+ {
+ ReloadCode(GameCode);
+ }
+
// Draw here for now
// 1) Acquire a Command Buffer
CommandList* cmdList = AcquireCommandList(GraphicsDevice, QueueType::Graphics);
@@ -114,24 +151,24 @@ void Win32EditorApplication::Update()
SubmitCommandLists(cmdList);
}
-bool Win32EditorApplication::IsRunning()
+bool JulietApplication::IsRunning()
{
return Running;
}
namespace
{
- Win32EditorApplication EditorApplication;
+ JulietApplication EditorApplication;
}
-Win32EditorApplication& GetEditorApplication()
+JulietApplication& GetEditorApplication()
{
return EditorApplication;
}
int main(int argc, char** argv)
{
- CreateMutex(0, false, L"Local\\Juliet.Editor");
+ CreateMutex(0, false, L"Local\\Juliet.App");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
MessageBox(nullptr, L"An instance of Juliet is already running.", L"Juliet", MB_OK | MB_ICONEXCLAMATION);
diff --git a/JulietApp/Editor/EditorMain_win32.h b/JulietApp/main.h
similarity index 66%
rename from JulietApp/Editor/EditorMain_win32.h
rename to JulietApp/main.h
index 444e586..6b7728b 100644
--- a/JulietApp/Editor/EditorMain_win32.h
+++ b/JulietApp/main.h
@@ -1,8 +1,9 @@
#pragma once
#include
-#include
#include
+#include
+#include
namespace Juliet
{
@@ -10,7 +11,7 @@ namespace Juliet
struct Window;
} // namespace Juliet
-class Win32EditorApplication : public Juliet::IApplication
+class JulietApplication : public Juliet::IApplication
{
protected:
void Init() override;
@@ -21,9 +22,9 @@ class Win32EditorApplication : public Juliet::IApplication
private:
Juliet::Window* MainWindow = {};
Juliet::GraphicsDevice* GraphicsDevice = {};
- Juliet::DynamicLibrary* Game = {};
+ Juliet::HotReloadCode GameCode = {};
bool Running = false;
};
-Win32EditorApplication& GetEditorApplication();
+JulietApplication& GetEditorApplication();
diff --git a/ShellGameBuild.lnk b/ShellGameBuild.lnk
new file mode 100644
index 0000000..e21c4f7
Binary files /dev/null and b/ShellGameBuild.lnk differ
diff --git a/misc/build_game.bat b/misc/build_game.bat
new file mode 100644
index 0000000..521ca27
--- /dev/null
+++ b/misc/build_game.bat
@@ -0,0 +1,16 @@
+@echo off
+
+del /s ..\Game*.pdb > NUL 2> NUL
+
+pushd %~dp0\..\bin\x64\Debug
+echo WAITING FOR PDB > lock.tmp
+popd
+
+pushd %~dp0
+msbuild -p:Random=%random% /p:SolutionDir=..\ ..\Game\Game.vcxproj
+popd
+
+pushd %~dp0\..\bin\x64\Debug
+del lock.tmp
+popd
+
diff --git a/misc/shell.bat b/misc/shell.bat
new file mode 100644
index 0000000..0a7a8a8
--- /dev/null
+++ b/misc/shell.bat
@@ -0,0 +1,8 @@
+@echo off
+
+pushd .
+call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
+popd
+
+set path=W:\Classified\Juliet\misc;%path%
+set _NO_DEBUG_HEAP=1
\ No newline at end of file