#pragma once #include "StarRect.hpp" #include "StarTtlCache.hpp" #include "StarWeightedPool.hpp" #include "StarThread.hpp" #include "StarBTreeDatabase.hpp" #include "StarCelestialTypes.hpp" #include "StarPerlin.hpp" namespace Star { STAR_CLASS(CelestialDatabase); STAR_CLASS(CelestialMasterDatabase); STAR_CLASS(CelestialSlaveDatabase); class CelestialDatabase { public: virtual ~CelestialDatabase(); // The x/y region of usable worlds. RectI xyRange() const; // The maximum number of bodies that can orbit a single system center / // planetary body. Orbital numbers are up to this number of levels // *inclusive*, so planetary orbit numbers would be 1-N, and planetary orbit // "0", in this system, would refer to the center of the planetary system // itself, e.g. a star. In the same way, satellites around a planetary // object are numbered 1-N, and 0 refers to the planetary object itself. int planetOrbitalLevels() const; int satelliteOrbitalLevels() const; // The following methods are allowed to return no information even in the // case of valid coordinates, due to delayed loading. virtual Maybe parameters(CelestialCoordinate const& coordinate) = 0; virtual Maybe name(CelestialCoordinate const& coordinate) = 0; virtual Maybe hasChildren(CelestialCoordinate const& coordinate) = 0; virtual List children(CelestialCoordinate const& coordinate) = 0; virtual List childOrbits(CelestialCoordinate const& coordinate) = 0; // Return all valid system coordinates in the given x/y range. All systems // are guaranteed to have unique x/y coordinates, and are meant to be viewed // from the top in 2d. The z-coordinate is there simpy as a validation // parameter. virtual List scanSystems(RectI const& region, Maybe const& includedTypes = {}) = 0; virtual List> scanConstellationLines(RectI const& region) = 0; // Returns false if part or all of the specified region is not loaded. This // is only relevant for calls to scanSystems and scanConstellationLines, and // does not imply that each individual system in the given region is fully // loaded with all planets moons etc, only that scanSystem and // scanConstellationLines are not waiting on missing data. virtual bool scanRegionFullyLoaded(RectI const& region) = 0; protected: Vec2I chunkIndexFor(CelestialCoordinate const& coordinate) const; Vec2I chunkIndexFor(Vec2I const& systemXY) const; // Returns the chunk indexes for the given region. List chunkIndexesFor(RectI const& region) const; // Returns the region of the given chunk. RectI chunkRegion(Vec2I const& chunkIndex) const; // m_baseInformation should only be modified in the constructor, as it is not // thread protected. CelestialBaseInformation m_baseInformation; }; class CelestialMasterDatabase : public CelestialDatabase { public: CelestialMasterDatabase(Maybe databaseFile = {}); CelestialBaseInformation baseInformation() const; CelestialResponse respondToRequest(CelestialRequest const& requests); // Unload data that has not been used in the configured TTL time, and // periodically commit to the underlying database if it is in use. void cleanupAndCommit(); // Does this coordinate point to a valid existing object? bool coordinateValid(CelestialCoordinate const& coordinate); // Find a planetary or satellite object randomly throughout the entire // celestial space that satisfies the given parameters. May fail to find // anything, though with the defaults this is vanishingly unlikely. Maybe findRandomWorld(unsigned tries = 10, unsigned trySpatialRange = 50, function filter = {}, Maybe seed = {}); // CelestialMasterDatabase always returns actual data, as it does just in // time generation. Maybe parameters(CelestialCoordinate const& coordinate) override; Maybe name(CelestialCoordinate const& coordinate) override; Maybe hasChildren(CelestialCoordinate const& coordinate) override; List children(CelestialCoordinate const& coordinate) override; List childOrbits(CelestialCoordinate const& coordinate) override; List scanSystems(RectI const& region, Maybe const& includedTypes = {}) override; List> scanConstellationLines(RectI const& region) override; bool scanRegionFullyLoaded(RectI const& region) override; // overwrite the celestial parameters for the world at the given celestial coordinate void updateParameters(CelestialCoordinate const& coordinate, CelestialParameters const& parameters); protected: struct SatelliteType { String typeName; Json baseParameters; JsonArray variationParameters; JsonObject orbitParameters; }; struct PlanetaryType { String typeName; float satelliteProbability; size_t maxSatelliteCount; Json baseParameters; JsonArray variationParameters; JsonObject orbitParameters; }; struct SystemType { String typeName; bool constellationCapable; Json baseParameters; JsonArray variationParameters; List orbitRegions; }; struct GenerationInformation { float systemProbability; float constellationProbability; Vec2U constellationLineCountRange; unsigned constellationMaxTries; float maximumConstellationLineLength; float minimumConstellationLineLength; float minimumConstellationMagnitude; float minimumConstellationLineCloseness; Map systemTypes; PerlinD systemTypePerlin; Json systemTypeBins; StringMap planetaryTypes; StringMap satelliteTypes; StringList planetarySuffixes; StringList satelliteSuffixes; WeightedPool systemPrefixNames; WeightedPool systemNames; WeightedPool systemSuffixNames; }; static Maybe orbitRegion( List const& orbitRegions, int planetaryOrbitNumber); typedef std::function&&)>&& UnlockDuringFunction; CelestialChunk const& getChunk(Vec2I const& chunkLocation, UnlockDuringFunction unlockDuring = {}); CelestialChunk produceChunk(Vec2I const& chunkLocation) const; Maybe>> produceSystem( RandomSource& random, Vec3I const& location) const; List produceConstellations( RandomSource& random, List const& constellationCandidates) const; GenerationInformation m_generationInformation; mutable RecursiveMutex m_mutex; HashTtlCache m_chunkCache; BTreeSha256Database m_database; float m_commitInterval; Timer m_commitTimer; }; class CelestialSlaveDatabase : public CelestialDatabase { public: CelestialSlaveDatabase(CelestialBaseInformation baseInformation); // Signal that the given region should be requested from the master database. void signalRegion(RectI const& region); // Signal that the given system should be fully requested from the master // database. void signalSystem(CelestialCoordinate const& system); // There is an internal activity time for chunk requests to live to prevent // repeatedly requesting the same set of chunks. List pullRequests(); void pushResponses(List responses); // Unload data that has not been used in the configured TTL time. void cleanup(); Maybe parameters(CelestialCoordinate const& coordinate) override; Maybe name(CelestialCoordinate const& coordinate) override; Maybe hasChildren(CelestialCoordinate const& coordinate) override; List children(CelestialCoordinate const& coordinate) override; List childOrbits(CelestialCoordinate const& coordinate) override; List scanSystems(RectI const& region, Maybe const& includedTypes = {}) override; List> scanConstellationLines(RectI const& region) override; bool scanRegionFullyLoaded(RectI const& region) override; void invalidateCacheFor(CelestialCoordinate const& coordinate); private: float m_requestTimeout; mutable RecursiveMutex m_mutex; HashTtlCache m_chunkCache; HashMap m_pendingChunkRequests; HashMap m_pendingSystemRequests; }; }