osb/source/game/StarWorldServer.hpp
Kae 4b0bc220e4 Support for changing the game's timescale
Context-specific (like per-world) timescales can also be added later
2023-07-21 00:58:49 +10:00

387 lines
16 KiB
C++

#ifndef STAR_WORLD_SERVER_HPP
#define STAR_WORLD_SERVER_HPP
#include "StarWorld.hpp"
#include "StarWorldClientState.hpp"
#include "StarCollisionGenerator.hpp"
#include "StarSpawner.hpp"
#include "StarNetPackets.hpp"
#include "StarCellularLighting.hpp"
#include "StarCellularLiquid.hpp"
#include "StarWeather.hpp"
#include "StarInterpolationTracker.hpp"
#include "StarWorldStructure.hpp"
#include "StarLuaRoot.hpp"
#include "StarWorldRenderData.hpp"
#include "StarWarping.hpp"
namespace Star {
STAR_CLASS(Player);
STAR_CLASS(WorldTemplate);
STAR_CLASS(Sky);
STAR_STRUCT(SkyParameters);
STAR_CLASS(DamageManager);
STAR_CLASS(WireProcessor);
STAR_CLASS(EntityMap);
STAR_CLASS(WorldStorage);
STAR_CLASS(FallingBlocksAgent);
STAR_CLASS(DungeonDefinition);
STAR_CLASS(WorldServer);
STAR_CLASS(TileEntity);
STAR_CLASS(UniverseSettings);
STAR_EXCEPTION(WorldServerException, StarException);
// Describes the amount of optional processing that a call to update() in
// WorldServer performs for things like liquid simulation, wiring, sector
// generation etc.
enum class WorldServerFidelity {
Minimum,
Low,
Medium,
High
};
extern EnumMap<WorldServerFidelity> const WorldServerFidelityNames;
class WorldServer : public World {
public:
// Create a new world with the given template, writing new storage file.
WorldServer(WorldTemplatePtr const& worldTemplate, IODevicePtr storage);
// Synonym for WorldServer(make_shared<WorldTemplate>(size), storage);
WorldServer(Vec2U const& size, IODevicePtr storage);
// Load an existing world from the given storage files
WorldServer(IODevicePtr const& storage);
// Load an existing world from the given in-memory chunks
WorldServer(WorldChunks const& chunks);
// Load an existing world from an in-memory representation
~WorldServer();
void setWorldId(String worldId);
String const& worldId() const;
void setUniverseSettings(UniverseSettingsPtr universeSettings);
UniverseSettingsPtr universeSettings() const;
void setReferenceClock(ClockPtr clock);
// Give this world a central structure. If there is a previous central
// structure it is removed first. Returns the structure with transformed
// coordinates.
WorldStructure setCentralStructure(WorldStructure centralStructure);
WorldStructure const& centralStructure() const;
// If there is an active central structure, it is removed and all unmodified
// objects and blocks associated with the structure are removed.
void removeCentralStructure();
void setPlayerStart(Vec2F const& startPosition, bool respawnInWorld = false);
bool spawnTargetValid(SpawnTarget const& spawnTarget) const;
// Returns false if the client id already exists, or the spawn target is
// invalid.
bool addClient(ConnectionId clientId, SpawnTarget const& spawnTarget, bool isLocal);
// Removes client, sends the WorldStopPacket, and returns any pending packets
// for that client
List<PacketPtr> removeClient(ConnectionId clientId);
List<ConnectionId> clientIds() const;
bool hasClient(ConnectionId clientId) const;
RectF clientWindow(ConnectionId clientId) const;
// May return null if a Player is not available or if the client id is not
// valid.
PlayerPtr clientPlayer(ConnectionId clientId) const;
List<EntityId> players() const;
void handleIncomingPackets(ConnectionId clientId, List<PacketPtr> const& packets);
List<PacketPtr> getOutgoingPackets(ConnectionId clientId);
void startFlyingSky(bool enterHyperspace, bool startInWarp);
void stopFlyingSkyAt(SkyParameters const& destination);
void setOrbitalSky(SkyParameters const& destination);
// Defaults to Medium
WorldServerFidelity fidelity() const;
void setFidelity(WorldServerFidelity fidelity);
void update(float dt);
ConnectionId connection() const override;
WorldGeometry geometry() const override;
uint64_t currentStep() const override;
MaterialId material(Vec2I const& position, TileLayer layer) const override;
MaterialHue materialHueShift(Vec2I const& position, TileLayer layer) const override;
ModId mod(Vec2I const& position, TileLayer layer) const override;
MaterialHue modHueShift(Vec2I const& position, TileLayer layer) const override;
MaterialColorVariant colorVariant(Vec2I const& position, TileLayer layer) const override;
LiquidLevel liquidLevel(Vec2I const& pos) const override;
LiquidLevel liquidLevel(RectF const& region) const override;
TileModificationList validTileModifications(TileModificationList const& modificationList, bool allowEntityOverlap) const override;
TileModificationList applyTileModifications(TileModificationList const& modificationList, bool allowEntityOverlap) override;
EntityPtr entity(EntityId entityId) const override;
void addEntity(EntityPtr const& entity) override;
EntityPtr closestEntity(Vec2F const& center, float radius, EntityFilter selector = EntityFilter()) const override;
void forAllEntities(EntityCallback entityCallback) const override;
void forEachEntity(RectF const& boundBox, EntityCallback callback) const override;
void forEachEntityLine(Vec2F const& begin, Vec2F const& end, EntityCallback callback) const override;
void forEachEntityAtTile(Vec2I const& pos, EntityCallbackOf<TileEntity> entityCallback) const override;
EntityPtr findEntity(RectF const& boundBox, EntityFilter entityFilter) const override;
EntityPtr findEntityLine(Vec2F const& begin, Vec2F const& end, EntityFilter entityFilter) const override;
EntityPtr findEntityAtTile(Vec2I const& pos, EntityFilterOf<TileEntity> entityFilter) const override;
bool tileIsOccupied(Vec2I const& pos, TileLayer layer, bool includeEphemeral = false) const override;
void forEachCollisionBlock(RectI const& region, function<void(CollisionBlock const&)> const& iterator) const override;
bool isTileConnectable(Vec2I const& pos, TileLayer layer, bool tilesOnly = false) const override;
bool pointTileCollision(Vec2F const& point, CollisionSet const& collisionSet = DefaultCollisionSet) const override;
bool lineTileCollision(Vec2F const& begin, Vec2F const& end, CollisionSet const& collisionSet = DefaultCollisionSet) const override;
Maybe<pair<Vec2F, Vec2I>> lineTileCollisionPoint(Vec2F const& begin, Vec2F const& end, CollisionSet const& collisionSet = DefaultCollisionSet) const override;
List<Vec2I> collidingTilesAlongLine(Vec2F const& begin, Vec2F const& end, CollisionSet const& collisionSet = DefaultCollisionSet, int maxSize = -1, bool includeEdges = true) const override;
bool rectTileCollision(RectI const& region, CollisionSet const& collisionSet = DefaultCollisionSet) const override;
TileDamageResult damageTiles(List<Vec2I> const& pos, TileLayer layer, Vec2F const& sourcePosition, TileDamage const& tileDamage, Maybe<EntityId> sourceEntity = {}) override;
InteractiveEntityPtr getInteractiveInRange(Vec2F const& targetPosition, Vec2F const& sourcePosition, float maxRange) const override;
bool canReachEntity(Vec2F const& position, float radius, EntityId targetEntity, bool preferInteractive = true) const override;
RpcPromise<InteractAction> interact(InteractRequest const& request) override;
float gravity(Vec2F const& pos) const override;
float windLevel(Vec2F const& pos) const override;
float lightLevel(Vec2F const& pos) const override;
bool breathable(Vec2F const& pos) const override;
float threatLevel() const override;
StringList environmentStatusEffects(Vec2F const& pos) const override;
StringList weatherStatusEffects(Vec2F const& pos) const override;
bool exposedToWeather(Vec2F const& pos) const override;
bool isUnderground(Vec2F const& pos) const override;
bool disableDeathDrops() const override;
List<PhysicsForceRegion> forceRegions() const override;
Json getProperty(String const& propertyName, Json const& def = Json()) const override;
void setProperty(String const& propertyName, Json const& property) override;
void timer(int stepsDelay, WorldAction worldAction) override;
double epochTime() const override;
uint32_t day() const override;
float dayLength() const override;
float timeOfDay() const override;
LuaRootPtr luaRoot() override;
RpcPromise<Vec2F> findUniqueEntity(String const& uniqueId) override;
RpcPromise<Json> sendEntityMessage(Variant<EntityId, String> const& entity, String const& message, JsonArray const& args = {}) override;
bool isTileProtected(Vec2I const& pos) const override;
void setTileProtection(DungeonId dungeonId, bool isProtected);
// used to globally, temporarily disable protection for certain operations
void setTileProtectionEnabled(bool enabled);
void setDungeonGravity(DungeonId dungeonId, Maybe<float> gravity);
void setDungeonBreathable(DungeonId dungeonId, Maybe<bool> breathable);
void setDungeonId(RectI const& tileRegion, DungeonId dungeonId);
// Signal a region to load / generate, returns true if it is now fully loaded
// and generated
bool signalRegion(RectI const& region);
// Immediately generate a given region
void generateRegion(RectI const& region);
// Returns true if a region is fully active without signaling it.
bool regionActive(RectI const& region);
// Queues a microdungeon for placement
RpcPromise<Vec2I> enqueuePlacement(List<BiomeItemDistribution> distributions, Maybe<DungeonId> id);
ServerTile const& getServerTile(Vec2I const& position, bool withSignal = false);
// Gets mutable pointer to server tile and marks it as needing updates to all
// clients.
ServerTile* modifyServerTile(Vec2I const& position, bool withSignal = false);
EntityId loadUniqueEntity(String const& uniqueId);
WorldTemplatePtr worldTemplate() const;
SkyPtr sky() const;
void modifyLiquid(Vec2I const& pos, LiquidId liquid, float quantity, bool additive = false);
void setLiquid(Vec2I const& pos, LiquidId liquid, float level, float pressure);
List<ItemDescriptor> destroyBlock(TileLayer layer, Vec2I const& pos, bool genItems, bool destroyModFirst);
void removeEntity(EntityId entityId, bool andDie);
void updateTileEntityTiles(TileEntityPtr const& object, bool removing = false, bool checkBreaks = true);
bool isVisibleToPlayer(RectF const& region) const;
void activateLiquidRegion(RectI const& region);
void activateLiquidLocation(Vec2I const& location);
// if blocks cascade, we'll need to do a break check across all tile entities
// when the timer next ticks
void requestGlobalBreakCheck();
void setSpawningEnabled(bool spawningEnabled);
// Write all active sectors to disk without unloading them
void sync();
// Copy full world to in memory representation
WorldChunks readChunks();
bool forceModifyTile(Vec2I const& pos, TileModification const& modification, bool allowEntityOverlap);
TileModificationList forceApplyTileModifications(TileModificationList const& modificationList, bool allowEntityOverlap);
DungeonId dungeonId(Vec2I const& pos) const;
bool isPlayerModified(RectI const& region) const;
ItemDescriptor collectLiquid(List<Vec2I> const& tilePositions, LiquidId liquidId);
bool placeDungeon(String const& dungeonName, Vec2I const& position, Maybe<DungeonId> dungeonId = {}, bool forcePlacement = true);
void addBiomeRegion(Vec2I const& position, String const& biomeName, String const& subBlockSelector, int width);
void expandBiomeRegion(Vec2I const& position, int newWidth);
// queue generation of the sectors that will be needed to insert or
// expand a biome region in order to spread processing over time
bool pregenerateAddBiome(Vec2I const& position, int width);
bool pregenerateExpandBiome(Vec2I const& position, int newWidth);
// set the biome at the given position to be the environment biome for the layer
void setLayerEnvironmentBiome(Vec2I const& position);
// for terrestrial worlds only. updates the planet type in the celestial as well as local
// world parameters along with the primary biome and the weather pool
void setPlanetType(String const& planetType, String const& primaryBiomeName);
// used to notify the universe server that the celestial planet type has changed
Maybe<pair<String, String>> pullNewPlanetType();
private:
struct ClientInfo {
ClientInfo(ConnectionId clientId, InterpolationTracker const trackerInit);
List<RectI> monitoringRegions(EntityMapPtr const& entityMap) const;
bool needsDamageNotification(RemoteDamageNotification const& rdn) const;
ConnectionId clientId;
uint64_t skyNetVersion;
uint64_t weatherNetVersion;
WorldClientState clientState;
bool pendingForward;
bool started;
List<PacketPtr> outgoingPackets;
// All slave entities for which the player should be knowledgable about.
HashMap<EntityId, uint64_t> clientSlavesNetVersion;
// Batch send tile updates
HashSet<Vec2I> pendingTileUpdates;
HashSet<Vec2I> pendingLiquidUpdates;
HashSet<pair<Vec2I, TileLayer>> pendingTileDamageUpdates;
HashSet<ServerTileSectorArray::Sector> pendingSectors;
HashSet<ServerTileSectorArray::Sector> activeSectors;
InterpolationTracker interpolationTracker;
};
struct TileEntitySpaces {
List<MaterialSpace> materials;
List<Vec2I> roots;
};
void init(bool firstTime);
// Returns nothing if the processing defined by the given configuration entry
// should not run this tick, if it should run this tick, returns the number
// of ticks since the last run.
Maybe<unsigned> shouldRunThisStep(String const& timingConfiguration);
TileModificationList doApplyTileModifications(TileModificationList const& modificationList, bool allowEntityOverlap, bool ignoreTileProtection = false);
// Queues pending (step based) updates to the given player
void queueUpdatePackets(ConnectionId clientId);
void updateDamage(float dt);
void updateDamagedBlocks(float dt);
// Check for any newly broken entities in this rect
void checkEntityBreaks(RectF const& rect);
// Push modified tile data to each client.
void queueTileUpdates(Vec2I const& pos);
void queueTileDamageUpdates(Vec2I const& pos, TileLayer layer);
void writeNetTile(Vec2I const& pos, NetTile& netTile) const;
void dirtyCollision(RectI const& region);
void freshenCollision(RectI const& region);
Vec2F findPlayerStart(Maybe<Vec2F> firstTry = {});
Vec2F findPlayerSpaceStart(float targetX);
void readMetadata();
void writeMetadata();
float gravityFromTile(ServerTile const& tile) const;
bool isFloatingDungeonWorld() const;
void setupForceRegions();
Json m_serverConfig;
WorldTemplatePtr m_worldTemplate;
WorldStructure m_centralStructure;
Vec2F m_playerStart;
bool m_adjustPlayerStart;
bool m_respawnInWorld;
JsonObject m_worldProperties;
Maybe<pair<String, String>> m_newPlanetType;
UniverseSettingsPtr m_universeSettings;
EntityMapPtr m_entityMap;
ServerTileSectorArrayPtr m_tileArray;
WorldStoragePtr m_worldStorage;
WorldServerFidelity m_fidelity;
Json m_fidelityConfig;
HashSet<Vec2I> m_damagedBlocks;
DamageManagerPtr m_damageManager;
WireProcessorPtr m_wireProcessor;
LuaRootPtr m_luaRoot;
WorldGeometry m_geometry;
uint64_t m_currentStep;
mutable CellularLightIntensityCalculator m_lightIntensityCalculator;
SkyPtr m_sky;
ServerWeather m_weather;
CollisionGenerator m_collisionGenerator;
List<CollisionBlock> m_workingCollisionBlocks;
HashMap<pair<EntityId, uint64_t>, pair<ByteArray, uint64_t>> m_netStateCache;
OrderedHashMap<ConnectionId, shared_ptr<ClientInfo>> m_clientInfo;
GameTimer m_tileEntityBreakCheckTimer;
shared_ptr<LiquidCellEngine<LiquidId>> m_liquidEngine;
FallingBlocksAgentPtr m_fallingBlocksAgent;
Spawner m_spawner;
// Keep track of material spaces and roots registered by tile entities to
// make sure we can cleanly remove them when they change or when the entity
// is removed / uninitialized
HashMap<EntityId, TileEntitySpaces> m_tileEntitySpaces;
List<pair<int, WorldAction>> m_timers;
bool m_needsGlobalBreakCheck;
bool m_generatingDungeon;
HashMap<DungeonId, float> m_dungeonIdGravity;
HashMap<DungeonId, bool> m_dungeonIdBreathable;
Set<DungeonId> m_protectedDungeonIds;
bool m_tileProtectionEnabled;
HashMap<Uuid, pair<ConnectionId, MVariant<ConnectionId, RpcPromiseKeeper<Json>>>> m_entityMessageResponses;
List<PhysicsForceRegion> m_forceRegions;
String m_worldId;
};
}
#endif