// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // A headless simulation at 60 ticks a second. The plain build simulates ten // seconds as fast as it can and prints what happened; the debug build runs in // real time, serving Crafter.Debug, until an agent says quit. import std; import Basic.DebugShim; import Basic.World; #ifdef BASIC_DEBUG_API import Crafter.Thread; import Basic.DebugCommands; #endif namespace Debug = Crafter::Debug; using Clock = std::chrono::steady_clock; namespace { constexpr float dt = 1.0f / 60.0f; #ifdef BASIC_DEBUG_API constexpr bool realTime = true; #else constexpr bool realTime = false; #endif } int main() { Basic::World world; world.Reset(); #ifdef BASIC_DEBUG_API // The session's transport runs on Crafter.Thread's pool. An app that // already uses the pool starts it as usual; this one only needs it here. Crafter::ThreadPool::Start(); Debug::Session debug({ .name = "basic" }); Basic::RegisterDebug(world); std::println("basic: running; drive it with ./build/crafter-debug, stop it with ./build/crafter-debug quit"); #endif const auto tick = std::chrono::duration_cast(std::chrono::duration(dt)); Clock::time_point next = Clock::now(); while (!Debug::QuitRequested() && (realTime || world.Time() < 10.0)) { Debug::Pump(); if (Debug::Advance()) world.Tick(dt); if constexpr (realTime) { next += tick; // After a stop in a debugger, carry on from now instead of // racing to catch up. if (Clock::now() - next > std::chrono::seconds(1)) next = Clock::now(); std::this_thread::sleep_until(next); } } std::println("basic: {:.1f} s simulated; {} balls, {} below the floor", world.Time(), world.Balls().size(), world.Lost()); for (const Basic::Ball& ball : world.Balls()) { if (ball.lost) std::println(" ball {} fell through the floor", ball.id); } }