From fc299a25593642911aeeb653e291e03be77d507c Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 26 Aug 2026 11:54:26 +0200 Subject: [PATCH] feat(core): declare per-tick world simulation orchestrator interface --- core/include/vf/WorldSim.h | 173 +++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 core/include/vf/WorldSim.h diff --git a/core/include/vf/WorldSim.h b/core/include/vf/WorldSim.h new file mode 100644 index 0000000..9d4e901 --- /dev/null +++ b/core/include/vf/WorldSim.h @@ -0,0 +1,173 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "World.h" +#include "Math.h" +#include "Block.h" +#include "TimeOfDay.h" +#include "Weather.h" +#include "Farming.h" +#include "BiomeGen.h" +#include "RandomTick.h" +#include "BlockTick.h" +#include "Container.h" +#include "Circuit.h" +#include "Hopper.h" +#include "Dispenser.h" +#include "Minecart.h" +#include "Rail.h" +#include "Mob.h" + +namespace vf { + +// Climate carries its own vf::BlockChange that collides with the one in +// Protocol.h. Kept out of this header (forward declared, held by pointer) so a +// translation unit can pull WorldSim together with ChunkSync/Protocol. +class Climate; + +// Mirror of ClimateConfig's tunables, so callers can shape the climate without +// pulling Climate.h (and its clashing BlockChange) into their translation unit. +struct WorldClimateSettings { + int seaLevel = 26; + float heightLapse = 0.006f; + float latitudeLapse = 0.0009f; + float freezeTemperature = 0.15f; + float meltTemperature = 0.5f; + float frostSpreadTemperature = 0.30f; + int maxSnowDepth = 8; + int packThreshold = 4; +}; + +struct WorldSimConfig { + uint32_t worldSeed = 1u; + + // Farming random ticks dispatched by the WorldSim's own selector. + int randomTicksPerChunk = 3; + // Vanilla block random ticks handled by the reused BlockTickScheduler. + int blockRandomTicksPerChunk = 3; + + long long weatherIntervalTicks = 1; + long long climateIntervalTicks = 20; + long long logisticsIntervalTicks = 1; + + float mobDt = 1.0f / 20.0f; + + WorldTick startTick = 0; + + TimeOfDayConfig time; + WeatherConfig weather; + WorldClimateSettings climate; + FarmingConfig farming; + BiomeGenParams biome; + CartConfig cart; + CreatureConfig creature; +}; + +// Deterministic orchestrator that folds the standalone simulation modules into +// one step. Every tick advances the clock, periodically the weather and the +// regional climate, dispatches random ticks to farming and vanilla block +// behaviour, runs redstone, logistics (hoppers/dispensers), minecarts and +// creatures, then collects the block cells that changed inside the active +// region as a dirty feed for chunk replication. Given the same seed and the +// same inputs, two WorldSims produce byte-identical world state. +class WorldSim { +public: + WorldSim(World& world, const Vec3i& regionOrigin, const Vec3i& regionDims, + const WorldSimConfig& config = {}); + ~WorldSim(); + + WorldSim(const WorldSim&) = delete; + WorldSim& operator=(const WorldSim&) = delete; + WorldSim(WorldSim&&) = delete; + WorldSim& operator=(WorldSim&&) = delete; + + void step(long long ticks = 1); + + long long currentTick() const { return tickCounter; } + const Vec3i& regionOrigin() const { return origin; } + const Vec3i& regionDims() const { return dims; } + + const TimeOfDay& time() const { return clock; } + const Weather& weather() const { return sky; } + Farming& farming() { return crops; } + const Farming& farming() const { return crops; } + RedstoneCircuit& circuit() { return *redstone; } + ContainerStore& containers() { return store; } + const ContainerStore& containers() const { return store; } + + // Climate readouts exposed without leaking Climate.h into callers. + int snowDepthAt(const Vec3i& pos) const; + int iceAgeAt(const Vec3i& pos) const; + float temperatureAt(int gx, int gy, int gz) const; + Precipitation currentPrecipitation() const; + + void addMinecart(const Minecart& cart); + std::vector& minecarts() { return carts; } + const std::vector& minecarts() const { return carts; } + void setPoweredRailPredicate(std::function predicate); + + void addCreature(const Creature& creature); + std::vector& creatures() { return mobs; } + const std::vector& creatures() const { return mobs; } + void setPlayer(const Vec3& pos); + void clearPlayer(); + + // Global cells whose block changed versus the last clearDirty (or since + // construction), in a deterministic sorted order. + const std::vector& dirtyBlocks() const { return dirtyList; } + void clearDirty(); + + uint64_t regionHash() const; + +private: + void runFarmingRandomTicks(); + void runClimate(); + void runLogistics(); + void runMinecarts(); + void runCreatures(); + void recomputeDirty(); + + int localIndexOf(int gx, int gy, int gz) const; + Block baselineBlock(int localIdx) const { return baseline[static_cast(localIdx)]; } + + World& world; + Vec3i origin; + Vec3i dims; + + long long tickCounter; + long long weatherInterval; + long long climateInterval; + long long logisticsInterval; + float mobDt; + + TimeOfDay clock; + Weather sky; + BiomeGen biomes; + std::unique_ptr weatherClimate; + Farming crops; + RandomTick randomTick; + BlockTickScheduler scheduler; + ContainerStore store; + std::unique_ptr redstone; + std::unordered_map hopperStates; + std::unordered_map dispenserStates; + + CartConfig cartConfig; + CreatureConfig creatureConfig; + std::vector carts; + std::function poweredRail; + + std::vector mobs; + Vec3 player; + bool hasPlayer = false; + + std::vector baseline; + std::vector dirtyList; +}; + +} // namespace vf