diff options
| author | Jonathan Bradley <jcb@pikum.xyz> | 2025-11-07 11:43:56 -0500 |
|---|---|---|
| committer | Jonathan Bradley <jcb@pikum.xyz> | 2025-11-07 12:26:15 -0500 |
| commit | c3c4d351f8ccfc678aaa2de458326a11884aef19 (patch) | |
| tree | 11c74d89da371667858f17139df55896a3ade67c | |
| parent | 5da699cff8a39bf1dc41b2d302c3e9c202496971 (diff) | |
pke: split up Game_Main and pass plugins to init
| -rw-r--r-- | editor/editor.cpp | 1 | ||||
| -rw-r--r-- | src/game.cpp | 335 | ||||
| -rw-r--r-- | src/game.hpp | 10 | ||||
| -rw-r--r-- | src/window.cpp | 45 | ||||
| -rw-r--r-- | tests/pke-test-load-unload.cpp | 2 |
5 files changed, 214 insertions, 179 deletions
diff --git a/editor/editor.cpp b/editor/editor.cpp index 2c66c8c..2a53b61 100644 --- a/editor/editor.cpp +++ b/editor/editor.cpp @@ -2233,7 +2233,6 @@ void PkeEditor_RecordImGui() { RecordImGuiEntityTypes(); RecordImGuiAssets(); RecordImGuiPlayerInput(); - Game_RecordImGui(); } } diff --git a/src/game.cpp b/src/game.cpp index c4a023d..01aba10 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -31,44 +31,12 @@ #include <fstream> #include <thread> -const char *levelName = "demo-level"; +void Game_Init(); +void Game_Tick(double delta); +void Game_Teardown(); +void Game_RecordImGui(); -const int64_t consoleBufferCount = 30; -const int64_t consoleLineLength = 128; -char consoleBuffer[consoleBufferCount][consoleLineLength]; -int64_t consoleBufferIndex = 0; -void Game_RecordImGui() { - static bool scrollToBottom = true; - if (!ImGui::Begin("Console", &pkeSettings.editorSettings.isShowingConsole)) { - ImGui::End(); - return; - } - ImVec2 region = ImGui::GetContentRegionAvail(); - region.y -= 27; - if (ImGui::BeginListBox("##ConsoleHistory", region)) { - for (int64_t i = consoleBufferIndex + 1; i < consoleBufferCount; ++i) { - ImGui::Text("%s", consoleBuffer[i]); - } - for (int64_t i = 0; i < consoleBufferIndex; ++i) { - ImGui::Text("%s", consoleBuffer[i]); - } - if (scrollToBottom) ImGui::SetScrollHereY(1); - scrollToBottom = false; - ImGui::EndListBox(); - } - ImGui::Separator(); - if (ImGui::InputText("##ConsoleInput", consoleBuffer[consoleBufferIndex], consoleLineLength, ImGuiInputTextFlags_EnterReturnsTrue)) { - // TODO parse and execute. - scrollToBottom = true; - consoleBufferIndex = (consoleBufferIndex + 1) % consoleBufferCount; - memset(consoleBuffer[consoleBufferIndex], '\0', consoleLineLength); - } - auto focusedFlags = (ImGuiFocusedFlags_ChildWindows); - if (ImGui::IsWindowFocused(focusedFlags) && !ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemHovered() && !ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsMouseClicked(ImGuiMouseButton_Left, true)) { - ImGui::SetKeyboardFocusHere(-1); - } - ImGui::End(); -} +const char *levelName = "demo-level"; void Game_Tick(double delta) { pk_mem_bucket_reset(pkeSettings.mem_bkt.game_transient); @@ -132,161 +100,171 @@ void Game_Tick(double delta) { window_tick_late(delta); } -void Game_Main(PKEWindowProperties windowProps, const char *executablePath) { +void pke_game_main_init(PKEWindowProperties windowProps, const char *executablePath, PKEPluginInterface *plugins, unsigned int n_plugins) { + fprintf(stdout, "[%s] pke_game_main_init Entering.\n", __FILE__); + unsigned int u; + pkeSettings.executablePath = executablePath; - fprintf(stdout, "Game_Main Entering\n"); - try { - Game_Init(); - pk_ev_init(pkeSettings.mem_bkt.game); - PkeThreads_Init(); - AM_Init(); - ECS_Init(); - Physics_Init(); - pke_audio_init(); - PkeCamera_Init(); - pke_level_init(); - pke_scene_master_init(); - CreateWindow(windowProps); - EntityType_Init(); - pke_input_init(); - FontType_Init(); - pke_ui_init(); - pke_ui_init_bindings(); - PkeProject_Load(pkeSettings.args.projectPath); - if (pkeSettings.args.pluginPath != nullptr) { - PkePlugin_Load(pkeSettings.args.pluginPath); - } - for (long i = 0; i < LoadedPkePlugins.next; ++i) { - if (LoadedPkePlugins[i].OnInit != nullptr) { - LoadedPkePlugins[i].OnInit(); - } + + Game_Init(); + pk_ev_init(pkeSettings.mem_bkt.game); + PkeThreads_Init(); + AM_Init(); + ECS_Init(); + Physics_Init(); + pke_audio_init(); + PkeCamera_Init(); + pke_level_init(); + pke_scene_master_init(); + CreateWindow(windowProps); + EntityType_Init(); + pke_input_init(); + FontType_Init(); + pke_ui_init(); + pke_ui_init_bindings(); + PkeProject_Load(pkeSettings.args.projectPath); + if (pkeSettings.args.pluginPath != nullptr) { + PkePlugin_Load(pkeSettings.args.pluginPath); + } + for (u = 0; u < n_plugins; ++u) { + pk_arr_append_t<PKEPluginInterface>(&LoadedPkePlugins, plugins[u]); + } + for (u = 0; u < LoadedPkePlugins.next; ++u) { + if (LoadedPkePlugins[u].OnInit != nullptr) { + LoadedPkePlugins[u].OnInit(); } + } - // - // at this point, everything is loaded or initialized. - // + fprintf(stdout, "[%s] pke_game_main_init Exiting.\n", __FILE__); +} - // if we were passed only a scene name, create a faux level. - if (!pkeSettings.args.levelName) { - // TODO uuids - pke_level *lvl = pke_level_create("faux-level", pk_uuid_zed, pk_uuid_zed); - fprintf(stdout, "[Game_Main] Creating faux level.\n"); - pkeSettings.rt.activeLevel = lvl; - } +void pke_game_main_load() { + fprintf(stdout, "[%s] pke_game_main_load Entering.\n", __FILE__); - if (pkeSettings.args.levelName == nullptr && pkeSettings.args.sceneName != nullptr) { - scene_instance si{}; - pke_scene *scene; - std::ifstream f(pkeSettings.args.sceneName); - if (!f.is_open()) { - fprintf(stdout, "[Game_Main] Did not find scene by name specified in arg: '%s'\n", pkeSettings.args.sceneName); - goto GAME_SHUTDOWN; - } - fprintf(stdout, "[Game_Main] loading scene from arg (expecting path): %s\n", pkeSettings.args.sceneName); - srlztn_deserialize_helper *h = pke_deserialize_init(pkeSettings.rt.activeLevel, pkeSettings.mem_bkt.game_transient); - // 2025-09-09 JCB Scenes no longer contain anything so I'm not sure there's a reason to create one here. - // spit-balling here, maybe "scene" files should be assets and not much more. - scene = pke_scene_create(pkeSettings.args.sceneName); - pke_deserialize_scene_from_stream(f, h); - pke_deserialize_scene(h); - pke_deserialize_teardown(h); - si.scene_handle = scene->scene_handle; - pk_arr_append_t(&pkeSettings.rt.activeLevel->scene_instances, si); + // if we were passed only a scene name, create a faux level. + if (!pkeSettings.args.levelName) { + // TODO uuids + pke_level *lvl = pke_level_create("faux-level", pk_uuid_zed, pk_uuid_zed); + fprintf(stdout, "[%s] pke_game_main_load Creating faux level.\n", __FILE__); + pkeSettings.rt.activeLevel = lvl; + } + + if (pkeSettings.args.levelName == nullptr && pkeSettings.args.sceneName != nullptr) { + scene_instance si{}; + pke_scene *scene; + std::ifstream f(pkeSettings.args.sceneName); + if (!f.is_open()) { + fprintf(stdout, "[%s] pke_game_main_load Did not find scene by name specified in arg: '%s'\n", __FILE__, pkeSettings.args.sceneName); + exit(1); } + fprintf(stdout, "[%s] pke_game_main_load loading scene from arg (expecting path): %s\n", __FILE__, pkeSettings.args.sceneName); + srlztn_deserialize_helper *h = pke_deserialize_init(pkeSettings.rt.activeLevel, pkeSettings.mem_bkt.game_transient); + // 2025-09-09 JCB Scenes no longer contain anything so I'm not sure there's a reason to create one here. + // spit-balling here, maybe "scene" files should be assets and not much more. + scene = pke_scene_create(pkeSettings.args.sceneName); + pke_deserialize_scene_from_stream(f, h); + pke_deserialize_scene(h); + pke_deserialize_teardown(h); + si.scene_handle = scene->scene_handle; + pk_arr_append_t(&pkeSettings.rt.activeLevel->scene_instances, si); + } - GameTimePoint lastTimePoint = pkeSettings.steadyClock.now(); - double deltaTillNextRender = pkeSettings.deltaPerFrame; - GameTimePoint lastLogTimePoint = pkeSettings.steadyClock.now(); - int64_t tickCount = 0; - int64_t renderCount = 0; + fprintf(stdout, "[%s] pke_game_main_load Exiting.\n", __FILE__); +} - int64_t nsAhead = 0.0; +void pke_game_main_run() { + fprintf(stdout, "[%s] pke_game_main_run Entering.\n", __FILE__); - while (pkeSettings.isGameRunning) { + GameTimePoint lastTimePoint = pkeSettings.steadyClock.now(); + double deltaTillNextRender = pkeSettings.deltaPerFrame; + GameTimePoint lastLogTimePoint = pkeSettings.steadyClock.now(); + int64_t tickCount = 0; + int64_t renderCount = 0; - glfwPollEvents(); + int64_t nsAhead = 0.0; - int64_t nsAheadHolder = 0.0; - if (nsAhead > 0) { - nsAheadHolder = nsAhead; - std::this_thread::sleep_for(GameTimeDuration(nsAhead)); - nsAhead = 0; - } - if (vidMode.refreshRate != pkeSettings.targetFPS) { - pkeSettings.targetFPS = vidMode.refreshRate; - pkeSettings.deltaPerFrame = 1 / double(pkeSettings.targetFPS); - } + while (pkeSettings.isGameRunning) { - GameTimePoint currentTimePoint = pkeSettings.steadyClock.now(); - double deltaThisTick = ((currentTimePoint - lastTimePoint).count() - nsAheadHolder) / NANO_DENOM_DOUBLE; - deltaThisTick = std::min(deltaThisTick, pkeSettings.minimumDeltaPerFrame); - lastTimePoint = currentTimePoint; - - deltaTillNextRender -= deltaThisTick; - bool shouldRender = pkeSettings.graphicsSettings.isFramerateUnlocked - || pkeSettings.graphicsSettings.isWaitingForVsync - || deltaTillNextRender <= 0.0; - - if (shouldRender == false && (deltaTillNextRender > 0.0 && deltaTillNextRender - (deltaThisTick * 2.0) <= 0.0)) { - /* - * We are ahead of the render schedule - * && the current tick's speed would put us behind schedule next tick. - * Simulate the extra time we are ahead and prepare to sleep the difference - * before the next tick. - */ - nsAhead = std::floor(deltaTillNextRender * NANO_DENOM_DOUBLE); - deltaThisTick += deltaTillNextRender; - shouldRender = true; - } + glfwPollEvents(); - tickCount += 1; - Game_Tick(deltaThisTick); - - pkeSettings.rt.was_framebuffer_resized = false; - if (shouldRender) { - Render(); - renderCount += 1; - double msBehind = deltaTillNextRender * -1000; - int64_t behindCount = 0; - while (deltaTillNextRender < pkeSettings.deltaPerFrame) { - behindCount += 1; - deltaTillNextRender += pkeSettings.deltaPerFrame; - } - if (behindCount > 2) { - fprintf(stderr, "[PKE::main] late render - simulated ahead: %fms - delta behind: %fms - missed frames:%ld\n", nsAheadHolder / (NANO_DENOM_DOUBLE / 1000), msBehind, behindCount - 1); - fflush(stderr); - } - } + int64_t nsAheadHolder = 0.0; + if (nsAhead > 0) { + nsAheadHolder = nsAhead; + std::this_thread::sleep_for(GameTimeDuration(nsAhead)); + nsAhead = 0; + } + if (vidMode.refreshRate != pkeSettings.targetFPS) { + pkeSettings.targetFPS = vidMode.refreshRate; + pkeSettings.deltaPerFrame = 1 / double(pkeSettings.targetFPS); + } - pkeSettings.stats.last_deltas[0] = pkeSettings.stats.last_deltas[1]; - pkeSettings.stats.last_deltas[1] = pkeSettings.stats.last_deltas[2]; - pkeSettings.stats.last_deltas[2] = deltaThisTick; - pkeSettings.stats.tick_rate = 3.L / (pkeSettings.stats.last_deltas[0] + pkeSettings.stats.last_deltas[1] + pkeSettings.stats.last_deltas[2]); - if ((currentTimePoint - lastLogTimePoint).count() > std::chrono::nanoseconds::period::den) { - lastLogTimePoint = currentTimePoint; - fprintf(stdout, "TPS: ~%.03f - actual:%ld - presents:%ld\n", pkeSettings.stats.tick_rate, tickCount, renderCount); - fflush(stdout); - tickCount = 0; - renderCount = 0; - } + GameTimePoint currentTimePoint = pkeSettings.steadyClock.now(); + double deltaThisTick = ((currentTimePoint - lastTimePoint).count() - nsAheadHolder) / NANO_DENOM_DOUBLE; + deltaThisTick = std::min(deltaThisTick, pkeSettings.minimumDeltaPerFrame); + lastTimePoint = currentTimePoint; - pkeSettings.isGameRunning = !glfwWindowShouldClose(window); + deltaTillNextRender -= deltaThisTick; + bool shouldRender = pkeSettings.graphicsSettings.isFramerateUnlocked + || pkeSettings.graphicsSettings.isWaitingForVsync + || deltaTillNextRender <= 0.0; + + if (shouldRender == false && (deltaTillNextRender > 0.0 && deltaTillNextRender - (deltaThisTick * 2.0) <= 0.0)) { + /* + * We are ahead of the render schedule + * && the current tick's speed would put us behind schedule next tick. + * Simulate the extra time we are ahead and prepare to sleep the difference + * before the next tick. + */ + nsAhead = std::floor(deltaTillNextRender * NANO_DENOM_DOUBLE); + deltaThisTick += deltaTillNextRender; + shouldRender = true; } - vkDeviceWaitIdle(vkDevice); + tickCount += 1; + Game_Tick(deltaThisTick); + + pkeSettings.rt.was_framebuffer_resized = false; + if (shouldRender) { + Render(); + renderCount += 1; + double msBehind = deltaTillNextRender * -1000; + int64_t behindCount = 0; + while (deltaTillNextRender < pkeSettings.deltaPerFrame) { + behindCount += 1; + deltaTillNextRender += pkeSettings.deltaPerFrame; + } + if (behindCount > 2) { + fprintf(stderr, "[PKE::main] late render - simulated ahead: %fms - delta behind: %fms - missed frames:%ld\n", nsAheadHolder / (NANO_DENOM_DOUBLE / 1000), msBehind, behindCount - 1); + fflush(stderr); + } + } + + pkeSettings.stats.last_deltas[0] = pkeSettings.stats.last_deltas[1]; + pkeSettings.stats.last_deltas[1] = pkeSettings.stats.last_deltas[2]; + pkeSettings.stats.last_deltas[2] = deltaThisTick; + pkeSettings.stats.tick_rate = 3.L / (pkeSettings.stats.last_deltas[0] + pkeSettings.stats.last_deltas[1] + pkeSettings.stats.last_deltas[2]); + if ((currentTimePoint - lastLogTimePoint).count() > std::chrono::nanoseconds::period::den) { + lastLogTimePoint = currentTimePoint; + fprintf(stdout, "TPS: ~%.03f - actual:%ld - presents:%ld\n", pkeSettings.stats.tick_rate, tickCount, renderCount); + fflush(stdout); + tickCount = 0; + renderCount = 0; + } - } catch (const std::exception &exc) { - fprintf(stderr, "Game_Main EXCEPTION: %s\n", exc.what()); - } catch (const char *err) { - fprintf(stderr, "Game_Main UNHANDLED EXCEPTION: %s\n", err); - } catch (...) { - fprintf(stderr, "Game_Main UNHANDLED EXCEPTION\n"); + pkeSettings.isGameRunning = !glfwWindowShouldClose(window); } -GAME_SHUTDOWN: + + fprintf(stdout, "[%s] pke_game_main_run Exiting.\n", __FILE__); +} + +void pke_game_main_teardown() { + fprintf(stdout, "[%s] pke_game_main_teardown Entering.\n", __FILE__); + fprintf(stdout, "Game_Main SHUTDOWN INITIATED\n"); #ifndef NDEBUG // TODO debug print buckets before shutdown #endif + vkDeviceWaitIdle(vkDevice); for (long i = 0; i < LoadedPkePlugins.next; ++i) { if (LoadedPkePlugins[i].OnTeardown) { LoadedPkePlugins[i].OnTeardown(); @@ -310,17 +288,26 @@ GAME_SHUTDOWN: pk_ev_teardown(); Game_Teardown(); // TODO debug print buckets after shutdown - fprintf(stdout, "Game_Main Exiting\n"); + + fprintf(stdout, "[%s] pke_game_main_teardown Exiting.\n", __FILE__); +} + +void Game_Main(PKEWindowProperties windowProps, const char *executablePath) { + + pke_game_main_init(windowProps, executablePath, nullptr, 0); + + pke_game_main_load(); + + pke_game_main_run(); + + pke_game_main_teardown(); + } void Game_Init() { pkeSettings.mem_bkt.game = pk_mem_bucket_create("game", 1UL << 26, PK_MEMBUCKET_FLAG_NONE); pkeSettings.mem_bkt.game_transient = pk_mem_bucket_create("game-transient", 1UL << 26, PK_MEMBUCKET_FLAG_TRANSIENT); pk_mem_bucket_set_client_mem_bucket(pkeSettings.mem_bkt.game); - - for (uint64_t i = 0; i < consoleBufferCount; ++i) { - memset(consoleBuffer[i], '\0', consoleLineLength); - } } void Game_Teardown() { diff --git a/src/game.hpp b/src/game.hpp index b879364..b599c24 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -1,12 +1,14 @@ #ifndef PKE_GAME_HPP #define PKE_GAME_HPP +#include "plugin-types.hpp" #include "window-types.hpp" void Game_Main(PKEWindowProperties windowProps, const char *executablePath); -void Game_Init(); -void Game_Tick(double delta); -void Game_Teardown(); -void Game_RecordImGui(); + +void pke_game_main_init(PKEWindowProperties windowProps, const char *executablePath, PKEPluginInterface *interfaces = nullptr, unsigned int n_interfaces = 0); +void pke_game_main_load(); +void pke_game_main_run(); +void pke_game_main_teardown(); #endif /* PKE_GAME_HPP */ diff --git a/src/window.cpp b/src/window.cpp index 7a627d5..b93d8ab 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -165,6 +165,12 @@ UniformBufferObject UBO{ VkDeviceMemory uniformBufferMemory; VkDeviceSize paddedUboBufferSize; +// console +const int64_t consoleBufferCount = 30; +const int64_t consoleLineLength = 128; +char consoleBuffer[consoleBufferCount][consoleLineLength]; +int64_t consoleBufferIndex = 0; + // public VkBuffer *UniformBuffers; DebugHitbox pkeDebugHitbox{}; @@ -3511,6 +3517,39 @@ void pkvk_transition_image_layout(VkCommandBuffer command_buffer, vkCmdPipelineBarrier(command_buffer, src_stage_mask, dst_stage_mask, 0, 0, VK_NULL_HANDLE, 0, VK_NULL_HANDLE, 1, &barrier); } +void RecordImGui() { + static bool scrollToBottom = true; + if (!ImGui::Begin("Console", &pkeSettings.editorSettings.isShowingConsole)) { + ImGui::End(); + return; + } + ImVec2 region = ImGui::GetContentRegionAvail(); + region.y -= 27; + if (ImGui::BeginListBox("##ConsoleHistory", region)) { + for (int64_t i = consoleBufferIndex + 1; i < consoleBufferCount; ++i) { + ImGui::Text("%s", consoleBuffer[i]); + } + for (int64_t i = 0; i < consoleBufferIndex; ++i) { + ImGui::Text("%s", consoleBuffer[i]); + } + if (scrollToBottom) ImGui::SetScrollHereY(1); + scrollToBottom = false; + ImGui::EndListBox(); + } + ImGui::Separator(); + if (ImGui::InputText("##ConsoleInput", consoleBuffer[consoleBufferIndex], consoleLineLength, ImGuiInputTextFlags_EnterReturnsTrue)) { + // TODO parse and execute. + scrollToBottom = true; + consoleBufferIndex = (consoleBufferIndex + 1) % consoleBufferCount; + memset(consoleBuffer[consoleBufferIndex], '\0', consoleLineLength); + } + auto focusedFlags = (ImGuiFocusedFlags_ChildWindows); + if (ImGui::IsWindowFocused(focusedFlags) && !ImGui::IsAnyItemFocused() && !ImGui::IsAnyItemHovered() && !ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsMouseClicked(ImGuiMouseButton_Left, true)) { + ImGui::SetKeyboardFocusHere(-1); + } + ImGui::End(); +} + void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { uint32_t i, counter; bool b; @@ -3933,10 +3972,12 @@ void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { } } // TODO move this 'any' check to happen on plugin load and store in global? + // Note that drawing imgui requires a plugin is intentional if (any) { ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); + RecordImGui(); for (uint32_t i = 0; i < LoadedPkePlugins.next; ++i) { if (LoadedPkePlugins[i].OnImGuiRender != nullptr) { LoadedPkePlugins[i].OnImGuiRender(); @@ -4214,6 +4255,10 @@ void CreateWindow(PKEWindowProperties wp) { CreateImGui(); DetermineMonitor(); + + for (uint64_t i = 0; i < consoleBufferCount; ++i) { + memset(consoleBuffer[i], '\0', consoleLineLength); + } } void DestroyWindow() { diff --git a/tests/pke-test-load-unload.cpp b/tests/pke-test-load-unload.cpp index 61d901c..27c588a 100644 --- a/tests/pke-test-load-unload.cpp +++ b/tests/pke-test-load-unload.cpp @@ -23,6 +23,8 @@ extern pk_arr_t<EntityHandle> entitiesYetToBeRemoved; static pk_membucket *bkt; static pke_level *level_01; +void Game_Tick(double); + void print_entities_marked_for_removal() { bool b; pk_iter_t<Entity_Base*> iter{}; |
