sdl_runtime_compiler git · main
SDL3 game for running and compiling code at runtime
C++ 72.3% C 26.2%git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gzAdd Program-Run pult With Claude
src/entities/pult.hpp | 3 +- src/states/game_state.cpp | 15 +++++++ src/states/game_state.hpp | 3 ++ src/systems/program_system.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++ src/systems/program_system.hpp | 25 +++++++++++ src/systems/pult_system.cpp | 33 ++++++--------- src/systems/pult_system.hpp | 10 ++--- 7 files changed, 157 insertions(+), 26 deletions(-) diff --git a/src/entities/pult.hpp b/src/entities/pult.hpp index 011f9c1..4f96c54 100644 --- a/src/entities/pult.hpp +++ b/src/entities/pult.hpp @@ -17,6 +17,7 @@ enum class PultAction : uint8_t { ScrollEditorDown, ClearEditor, Compile, + RunProgram, }; @@ -26,7 +27,7 @@ struct PultButton final : public SolidBox PultAction action; bool pressed { false }; - bool player_on { false }; // player currently standing on it (edge-detect) + bool player_on { false }; double press_timer { 0.0 }; static constexpr double PRESS_DUR = 0.18; diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp index affbd72..7e9483c 100644 --- a/src/states/game_state.cpp +++ b/src/states/game_state.cpp @@ -37,6 +37,7 @@ GameState::GameState(size_t starting_level) _physics_sys = cpputils::make_unique<PhysicsSystem> (); _projectiles_sys = cpputils::make_unique<ProjectileSystem> (); _compiler_sys = cpputils::make_unique<CompilerSystem> (); + _program_sys = cpputils::make_unique<ProgramSystem> (); _word_anim_sys = cpputils::make_unique<WordAnimSystem> (); _freelook_sys = cpputils::make_unique<FreelookSystem> (); _particle_sys = cpputils::make_unique<ParticleSystem> (); @@ -193,6 +194,9 @@ GameState::pult_DispatchAction(PultAction act) case PultAction::Compile: on_Compile(); break; + case PultAction::RunProgram: + on_Run(); + break; } } @@ -346,6 +350,13 @@ void GameState::on_Compile() { } +void +GameState::on_Run() +{ + _program_sys->run(_ctx.wall_texts, _ctx.world->scale()); +} + + void GameState::on_DebugPrint() { @@ -643,6 +654,10 @@ GameState::update(double dt) } } + { FP_ZONE("update._program_sys"); + _program_sys->update(_ctx.wall_texts, _ctx.world->scale()); + } + { FP_ZONE("update.goals"); _celebration_sys->tick(dt, *_particle_sys); level_TryAdvance(); diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp index 3c8ffe5..a61e501 100644 --- a/src/states/game_state.hpp +++ b/src/states/game_state.hpp @@ -20,6 +20,7 @@ #include "systems/particle_system.hpp" #include "systems/physics_system.hpp" #include "systems/plate_system.hpp" +#include "systems/program_system.hpp" #include "systems/projectile_system.hpp" #include "systems/pult_system.hpp" #include "systems/word_ai_system.hpp" @@ -86,6 +87,7 @@ class GameState : public IState cpputils::unique_ptr<ProjectileSystem> _projectiles_sys; cpputils::unique_ptr<WordAnimSystem> _word_anim_sys; cpputils::unique_ptr<CompilerSystem> _compiler_sys; + cpputils::unique_ptr<ProgramSystem> _program_sys; cpputils::unique_ptr<FreelookSystem> _freelook_sys; cpputils::unique_ptr<PultSystem> _pult_sys; cpputils::unique_ptr<HintSystem> _hint_sys; @@ -108,6 +110,7 @@ class GameState : public IState void on_FreelookStart(); void on_FreelookEnd (); void on_Compile (); + void on_Run (); void on_NumberAdjust (int dt); void on_TextStart (FrameInput&); void on_TextCommit (FrameInput&); diff --git a/src/systems/program_system.cpp b/src/systems/program_system.cpp new file mode 100644 index 0000000..4bb5699 --- /dev/null +++ b/src/systems/program_system.cpp @@ -0,0 +1,94 @@ +#include <SDL3/SDL.h> +#include <algorithm> +#include <unistd.h> +#include <sys/select.h> + +#include "systems/program_system.hpp" +#include "text_3d/text_3d.hpp" +#include "utils/ascii.hpp" + + +static const char* kProgramPath = "/tmp/game_prog"; + + +void +ProgramSystem::run(TextWallList& texts, float world_scale) +{ + // Clear previous output from the Program wall. + texts.erase( + std::remove_if(texts.begin(), texts.end(), + [](const TextWallPtr& wt) { + return wt->wall_position == Wall::position_of(Wall::Role::Program); + }), + texts.end()); + _count = 0; + + if (_pipe) { pclose(_pipe); _pipe = nullptr; } + + // Nothing built yet — tell the player to compile first. + if (access(kProgramPath, X_OK) != 0) { + push_Line(texts, "no program - compile first", world_scale); + return; + } + + // stderr → stdout so runtime messages show up too. + _pipe = popen("LC_ALL=C /tmp/game_prog 2>&1", "r"); + if (!_pipe) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "ProgramSystem: popen failed"); +} + + +void +ProgramSystem::update(TextWallList& texts, float world_scale) +{ + if (!_pipe) return; + + char buf[512]; + int fd = fileno(_pipe); + fd_set set; FD_ZERO(&set); FD_SET(fd, &set); + struct timeval tv{0, 0}; // poll, never block the frame + + while (select(fd + 1, &set, nullptr, nullptr, &tv) > 0) { + if (!fgets(buf, sizeof(buf), _pipe)) { + // EOF — program finished. + pclose(_pipe); + _pipe = nullptr; + if (_count == 0) + push_Line(texts, "(no output)", world_scale); + return; + } + std::string line(buf); + if (!line.empty() && line.back() == '\n') line.pop_back(); + if (!line.empty()) + push_Line(texts, line, world_scale); + + FD_ZERO(&set); FD_SET(fd, &set); + } +} + + +void +ProgramSystem::push_Line(TextWallList& texts, const std::string& line, float world_scale) +{ + std::string clean = ascii_Sanitize(line); + + // Shift existing program-wall lines up so new output appears at the bottom. + for (TextWallPtr& wt : texts) + if (wt->role == Wall::Role::Program) + wt->text3d->pos.y += static_cast<double>(line_height); + + Wall::Transform tf = Wall::get_Transform(Wall::Role::Program, world_scale); + Vec3 pos = tf.position; + pos.y = start_y; + + // Program wall is light ivory → use a dark text colour for contrast. + Text3dPtr t = make_Text3d(clean, Text3d::depth_wall, 0.003f, + pos, Text3d::color_dark_gray); + t->yaw = tf.yaw; + + texts.push_back(cpputils::make_unique<TextWall>(TextWall{ + tf.wall_position, + Wall::Role::Program, + std::move(t) + })); + _count++; +} diff --git a/src/systems/program_system.hpp b/src/systems/program_system.hpp new file mode 100644 index 0000000..b550221 --- /dev/null +++ b/src/systems/program_system.hpp @@ -0,0 +1,25 @@ +#pragma once +#include <string> +#include <cstdio> + +#include "utils/globals.hpp" +#include "wall/text_wall.hpp" + +// ProgramSystem: runs the executable built by CompilerSystem (/tmp/game_prog) +// and streams its stdout/stderr onto the Program wall (+X). Mirrors +// CompilerSystem's non-blocking pipe pattern so it never stalls the frame. +struct ProgramSystem +{ +private: + FILE* _pipe {nullptr}; + int _count {0}; + + void push_Line(TextWallList&, const std::string& line, float world_scale); +public: + static inline float line_height = 0.4f; + static inline float start_y = -WORLD_BOX_SCALE + 1; + + void run (TextWallList&, float world_scale); + void update (TextWallList&, float world_scale); + bool is_Running() const { return _pipe != nullptr; } +}; diff --git a/src/systems/pult_system.cpp b/src/systems/pult_system.cpp index c42ae46..2a6a6c7 100644 --- a/src/systems/pult_system.cpp +++ b/src/systems/pult_system.cpp @@ -36,12 +36,9 @@ PultSystem::init(DynArray<GameObjectPtr>& scenery, }; auto spawn_pult = [&](const std::string& name, - Wall::Role role, float along, - float half_w = Pult::W, - float half_d = Pult::D) -> Pult* + Wall::Role role, float along) -> Pult* { - auto owned = cpputils::make_unique<Pult>(name, make_pult_pos(role, along), - half_w, half_d); + auto owned = cpputils::make_unique<Pult>(name, make_pult_pos(role, along)); Pult* raw = owned.get(); _pults.push_back(raw); scenery.push_back(std::move(owned)); @@ -64,18 +61,18 @@ PultSystem::init(DynArray<GameObjectPtr>& scenery, scenery.push_back(std::move(owned)); }; - // The two buttons sit side-by-side along Z, so this pult is twice as deep - // (its visible width facing the editor wall) and the buttons get a wider gap. - Pult* scroll = spawn_pult("Scroll", Wall::Role::Editor, lane_1, - Pult::W, Pult::D * 2.0f); - add_btn(scroll, "UP", PultAction::ScrollEditorUp, 0.0f, -0.46f); - add_btn(scroll, "DOWN", PultAction::ScrollEditorDown, 0.0f, 0.46f); + Pult* scroll = spawn_pult("Scroll", Wall::Role::Editor, lane_1); + add_btn(scroll, "UP", PultAction::ScrollEditorUp, 0.0f, -0.42f); + add_btn(scroll, "DOWN", PultAction::ScrollEditorDown, 0.0f, 0.42f); - Pult* clr = spawn_pult("Clear", Wall::Role::Editor, lane_2); - add_btn(clr, "CLEAR", PultAction::ClearEditor, 0.0f, 0.0f); + Pult* clr = spawn_pult("Clear", Wall::Role::Editor, lane_2); + add_btn(clr, "CLEAR", PultAction::ClearEditor, 0.0f, 0.0f); Pult* compile = spawn_pult("Compile", Wall::Role::Compiler, lane_3); - add_btn(compile, "RUN", PultAction::Compile, 0.0f, 0.0f); + add_btn(compile, "BUILD", PultAction::Compile, 0.0f, 0.0f); + + Pult* run = spawn_pult("Run", Wall::Role::Program, lane_2); + add_btn(run, "RUN", PultAction::RunProgram, 0.0f, 0.0f); } @@ -126,9 +123,7 @@ player_OnButton(const Player& p, const PultButton& b) if (std::fabs(p.pos.x - b.pos.x) > hx + TOL) return false; if (std::fabs(p.pos.z - b.pos.z) > hz + TOL) return false; - // On/above the top face, not sailing far overhead. No tight lower bound is - // needed: being over the footprint already implies on-top (can't be beside - // it — that space is solid pult), so this only rejects a high fly-over. + // On/above the top face, not sailing far overhead. const double dy = p.pos.y - (b.pos.y + hy); return dy >= -0.10 && dy <= 1.8; } @@ -139,12 +134,10 @@ PultSystem::handle_PlayerContact(const Player& player, const DynArray<PultButton*>& buttons) { std::optional<PultAction> fired; - for (PultButton* btn : buttons) { const bool over = player_OnButton(player, *btn); - // Rising edge only: fire once when the player lands, not every frame - // they keep standing on it. + // Rising edge only: fire once on landing, not every frame stood on it. if (over && !btn->player_on) { btn->press(); if (!fired) fired = btn->action; diff --git a/src/systems/pult_system.hpp b/src/systems/pult_system.hpp index ba1d11b..92d5079 100644 --- a/src/systems/pult_system.hpp +++ b/src/systems/pult_system.hpp @@ -19,16 +19,16 @@ class PultSystem public: void init(DynArray<GameObjectPtr>& scenery, DynArray<PultButton*>& buttons_out, - float world_scale); + float world_scale); std::optional<PultAction> - try_Activate(GameObject* obj, + try_Activate(GameObject* obj, const DynArray<PultButton*>& buttons); std::optional<PultAction> - handle_ProjectileContact(const ProjectileContactEvent& ev, + handle_ProjectileContact(const ProjectileContactEvent& ev, const DynArray<PultButton*>& buttons); std::optional<PultAction> - handle_PlayerContact(const Player& player, - const DynArray<PultButton*>& buttons); + handle_PlayerContact(const Player& player, + const DynArray<PultButton*>& buttons); void update(double dt, const Camera& camera); void draw_Labels(GlRenderer& gl, const Camera& camera, const Light& light) const;