mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
The client keeps the scene under the player loaded, the scenes the zone file's transitions connect to it, and the global scene (Zone::StreamScenesAroundPosition 0x0108a3f0, TerrainManager::GetSceneAtPos 0x01069010, the connected scenes at 0x01066500; transitions naming a missing scene dropped as Zone::FixupInvalidTransitions 0x010842e0 does). - ZoneScenes (dCommon): the terrain's scene map lookup and the scene graph, shared by the world server and the dashboard. - Objects remember the scene they were placed in (spawners pass theirs on). - ghosting_scenes=1 (world config, off by default): players get the objects of their loaded scenes instead of the ones within the ghosting distances; objects from no scene go by the scene under them. Zones without a scene map keep distance ghosting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
77 lines
3.1 KiB
C++
77 lines
3.1 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
#include "Raw.h"
|
|
#include "ZoneFile.h"
|
|
|
|
/**
|
|
* Which of a zone's scenes the game client keeps loaded, as it decides it (Zone::StreamScenesAroundPosition,
|
|
* 0x0108a3f0 in client 1.10.64): the scene under the player, found in the terrain's scene map
|
|
* (TerrainManager::GetSceneAtPos, 0x01069010), and the scenes connected to it by the zone file's scene transitions
|
|
* (LevelManager's connected scenes, 0x01066500); the global scene (0) always stays. A scene is a scene id: its layers
|
|
* (the audio scenes share their scene's id) come and go with it. Pure, so the world server and the dashboard use the
|
|
* same rules and both can be unit tested.
|
|
*/
|
|
namespace ZoneScenes {
|
|
constexpr uint32_t GLOBAL_SCENE = 0;
|
|
// What the scene map holds where it names no scene; the client reads it as the global scene
|
|
constexpr uint8_t NO_SCENE = 0xFF;
|
|
|
|
// The terrain's scene map, kept apart from the rest of the .raw file (heights, textures and flairs aren't needed)
|
|
class SceneMap {
|
|
public:
|
|
SceneMap() = default;
|
|
explicit SceneMap(const Raw::Raw& raw);
|
|
|
|
// The scene at world position (x, z), as the client finds it: the global scene outside the map or where it has none
|
|
uint32_t SceneAt(float x, float z) const;
|
|
bool Empty() const { return m_Chunks.empty(); }
|
|
|
|
private:
|
|
struct Chunk {
|
|
float minX{}, minZ{}, maxX{}, maxZ{};
|
|
float cellsPerUnitX{}, cellsPerUnitZ{}; // colorMapResolution / the chunk's width in world units
|
|
uint32_t resolution{};
|
|
std::vector<uint8_t> scenes; // resolution * resolution, x major
|
|
};
|
|
std::vector<Chunk> m_Chunks;
|
|
float m_MinX{}, m_MinZ{}, m_MaxX{}, m_MaxZ{};
|
|
};
|
|
|
|
// The scenes each scene is connected to, from the zone file's transitions
|
|
class SceneGraph {
|
|
public:
|
|
SceneGraph() = default;
|
|
/**
|
|
* From a zone's scene transitions: each connects its first two points' scenes (the client's zone keeps them as
|
|
* scene pairs). Transitions naming a scene the zone doesn't have are dropped, as the client's
|
|
* Zone::FixupInvalidTransitions (0x010842e0) does.
|
|
*/
|
|
SceneGraph(const std::vector<ZoneScene>& scenes, const std::vector<SceneTransition>& transitions);
|
|
|
|
// The scenes connected to `scene` (not itself); none for the global scene
|
|
const std::set<uint32_t>& Neighbours(uint32_t scene) const;
|
|
|
|
// What the client keeps loaded with the player in `scene`: the global scene, `scene` and its neighbours
|
|
std::set<uint32_t> Loaded(uint32_t scene) const;
|
|
|
|
const std::set<uint32_t>& Scenes() const { return m_Scenes; }
|
|
|
|
private:
|
|
std::set<uint32_t> m_Scenes;
|
|
std::vector<std::pair<uint32_t, std::set<uint32_t>>> m_Neighbours;
|
|
};
|
|
|
|
/**
|
|
* Whether a player with `loaded` scenes (SceneGraph::Loaded of the scene under them) gets an object: when its scene
|
|
* is loaded. An object's scene is the scene it was placed in (`placedScene`, -1 for none: spawned at run time or a
|
|
* player), else the scene under it (`sceneUnder`).
|
|
*/
|
|
inline bool InLoadedScene(int32_t placedScene, uint32_t sceneUnder, const std::set<uint32_t>& loaded) {
|
|
return loaded.contains(placedScene >= 0 ? static_cast<uint32_t>(placedScene) : sceneUnder);
|
|
}
|
|
}
|