diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d81c9a5..fcdb957 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,6 +46,7 @@ jobs: sudo ./llvm.sh 18 sudo apt install libstdc++-14-dev build-type: Release + name: ${{ matrix.compiler-name }} ${{ matrix.build-type }} steps: - uses: actions/checkout@v3 @@ -74,6 +75,7 @@ jobs: strategy: matrix: build-type: [ Debug, Release ] + name: MSVC ${{ matrix.build-type }} steps: - uses: actions/checkout@v3 - name: Install Ninja diff --git a/src/obpf/include/obpf/simulator.h b/src/obpf/include/obpf/simulator.h index db7c932..69b0160 100644 --- a/src/obpf/include/obpf/simulator.h +++ b/src/obpf/include/obpf/simulator.h @@ -55,6 +55,11 @@ extern "C" { struct ObpfTetrion** observers; }; + struct ObpfGarbageEvent { + uint8_t num_lines; + uint64_t remaining_frames; + }; + OBPF_EXPORT struct ObpfTetrion* obpf_create_tetrion(uint64_t seed); OBPF_EXPORT struct ObpfTetrion* obpf_create_multiplayer_tetrion(const char* host, uint16_t port); OBPF_EXPORT struct ObpfObserverList obpf_tetrion_get_observers(struct ObpfTetrion const* tetrion); @@ -89,6 +94,10 @@ extern "C" { OBPF_EXPORT uint64_t obpf_tetrion_get_next_frame(struct ObpfTetrion const* tetrion); OBPF_EXPORT void obpf_tetrion_simulate_next_frame(struct ObpfTetrion* tetrion, ObpfKeyState key_state); OBPF_EXPORT void obpf_destroy_tetrion(struct ObpfTetrion const* tetrion); + OBPF_EXPORT uint32_t obpf_garbage_queue_length(struct ObpfTetrion const* tetrion); + OBPF_EXPORT uint32_t obpf_garbage_queue_num_events(struct ObpfTetrion const* tetrion); + OBPF_EXPORT struct ObpfGarbageEvent obpf_garbage_queue_event(struct ObpfTetrion const* tetrion, uint32_t index); + OBPF_EXPORT uint64_t obpf_garbage_delay_frames(void); OBPF_EXPORT uint8_t obpf_tetrion_width(void); OBPF_EXPORT uint8_t obpf_tetrion_height(void); OBPF_EXPORT uint8_t obpf_tetrion_num_invisible_lines(void); diff --git a/src/obpf/simulator.cpp b/src/obpf/simulator.cpp index 30735e3..6b44166 100644 --- a/src/obpf/simulator.cpp +++ b/src/obpf/simulator.cpp @@ -239,15 +239,43 @@ void obpf_destroy_tetrion(ObpfTetrion const* const tetrion) try { spdlog::error("Failed to destroy tetrion: Unknown error"); } -uint8_t obpf_tetrion_width() { +std::uint32_t obpf_garbage_queue_length(ObpfTetrion const* const tetrion) { + return tetrion->garbage_queue_length(); +} + +std::uint32_t obpf_garbage_queue_num_events(ObpfTetrion const* const tetrion) { + return static_cast(tetrion->garbage_queue_num_events()); +} + +ObpfGarbageEvent obpf_garbage_queue_event(ObpfTetrion const* const tetrion, uint32_t const index) try { + auto const event = tetrion->garbage_queue_event(static_cast(index)); + // we do the calculation with signed numbers to simplify the implementation + auto const remaining_frames = static_cast(std::max( + i64{ 0 }, + static_cast(event.frame + ObpfTetrion::garbage_delay_frames) - static_cast(tetrion->next_frame()) + )); + return ObpfGarbageEvent{ + .num_lines = event.num_lines, + .remaining_frames = remaining_frames, + }; +} catch (std::exception const& e) { + + spdlog::error("Failed to fetch garbage queue event: {}", e.what()); + return ObpfGarbageEvent{}; +} catch (...) { + spdlog::error("Failed to fetch garbage queue event: Unknown error"); + return ObpfGarbageEvent{}; +} + +std::uint8_t obpf_tetrion_width() { return uint8_t{ Matrix::width }; } -uint8_t obpf_tetrion_height() { +std::uint8_t obpf_tetrion_height() { return uint8_t{ Matrix::height }; } -uint8_t obpf_tetrion_num_invisible_lines() { +std::uint8_t obpf_tetrion_num_invisible_lines() { return uint8_t{ Matrix::num_invisible_lines }; } @@ -371,6 +399,10 @@ bool obpf_tetrion_is_connected(ObpfTetrion const* tetrion) { return tetrion->is_connected(); } -uint64_t obpf_tetrion_frames_until_game_start(ObpfTetrion const* tetrion) { +std::uint64_t obpf_tetrion_frames_until_game_start(ObpfTetrion const* tetrion) { return tetrion->frames_until_game_start(); } + +std::uint64_t obpf_garbage_delay_frames() { + return ObpfTetrion::garbage_delay_frames; +} diff --git a/src/simulator/include/simulator/tetrion.hpp b/src/simulator/include/simulator/tetrion.hpp index ececc64..4e18889 100644 --- a/src/simulator/include/simulator/tetrion.hpp +++ b/src/simulator/include/simulator/tetrion.hpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include #include "action.hpp" #include "bag.hpp" @@ -24,10 +26,11 @@ struct ObserverTetrion; struct ObpfTetrion { + static constexpr auto garbage_delay_frames = u64{ 10 * 60 }; + private: static constexpr auto spawn_position = Vec2{ 3, 0 }; static constexpr auto spawn_rotation = Rotation::North; - static constexpr auto garbage_delay_frames = u64{ 90 }; ObpfActionHandler m_action_handler = nullptr; void* m_action_handler_user_data = nullptr; @@ -147,6 +150,20 @@ struct ObpfTetrion { return m_start_frame - m_next_frame; } + [[nodiscard]] u32 garbage_queue_length() const { + auto const range = + m_garbage_receive_queue | std::views::transform([](auto const& event) { return event.num_lines; }); + return std::accumulate(range.begin(), range.end(), u32{ 0 }); + } + + [[nodiscard]] usize garbage_queue_num_events() const { + return m_garbage_receive_queue.size(); + } + + [[nodiscard]] GarbageSendEvent garbage_queue_event(usize const index) const { + return m_garbage_receive_queue.at(index); + } + private: void freeze_and_destroy_active_tetromino(); [[nodiscard]] bool is_tetromino_completely_invisible(Tetromino const& tetromino) const; diff --git a/src/simulator/tetrion.cpp b/src/simulator/tetrion.cpp index e2f736e..c0c88b0 100644 --- a/src/simulator/tetrion.cpp +++ b/src/simulator/tetrion.cpp @@ -153,9 +153,18 @@ std::optional ObpfTetrion::simulate_next_frame(KeyState const ++m_next_frame; + while (garbage_lines_to_send > 0 and not m_garbage_receive_queue.empty()) { + --m_garbage_receive_queue.front().num_lines; + if (m_garbage_receive_queue.front().num_lines == 0) { + m_garbage_receive_queue.pop_front(); + } + --garbage_lines_to_send; + } + if (garbage_lines_to_send == 0) { return std::nullopt; } + return std::optional{ GarbageSendEvent{ m_next_frame, garbage_lines_to_send } };