Merge origin/main into experimental: behaviors, raw terrain scene lookups, property model fixes, player count

Main's model behaviors (every rotation, DoDamage, OnTimer, OnStartup, MoveBackToStart, moves along the model's
local axes), raw terrain reading with scene lookups and scene tools, property model and brick by brick fixes and the
chat server's player count fix, merged onto this branch's structures:

- game messages stay structs; the angular velocity messages join them and the handler sets a message's target
- strips own their speed again and the block names, moves and rotations stay in the shared action table
  (PropertyBehaviorActions, which the dashboard's behavior player reads; it now lists rotations too)
- the zone keeps reading its file through ZoneFile and takes the boundaries from it, then loads the raw terrain as main
  does; the old dNavigation terrain reader is gone
- the show all response stays a struct and logs a player count mismatch as main does
- the property management component tells models who sent a chat message

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-11-10 10:57:45 -06:00
56 changed files with 2418 additions and 438 deletions

View File

@@ -11,9 +11,10 @@
#include "VanityUtilities.h"
#include "WorldConfig.h"
#include "CDZoneTableTable.h"
#include <algorithm>
#include <chrono>
#include <set>
#include "eObjectBits.h"
#include "CDZoneTableTable.h"
#include "AssetManager.h"
#include <ranges>
@@ -63,6 +64,9 @@ void dZoneManager::Initialize(const LWOZONEID& zoneID) {
m_pZone->Initalize();
// Build the scene graph after zone is loaded
BuildSceneGraph();
endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
LoadWorldConfig();
@@ -306,3 +310,83 @@ void dZoneManager::LoadWorldConfig() {
LOG_DEBUG("Loaded WorldConfig into memory");
}
LWOSCENEID dZoneManager::GetSceneIDFromPosition(const NiPoint3& position) const {
if (!m_pZone) return LWOSCENEID_INVALID;
const auto sceneID = m_pZone->GetZoneRaw().GetSceneIDAt(position.x, position.z);
return sceneID ? LWOSCENEID(*sceneID, 0) : LWOSCENEID_INVALID;
}
void dZoneManager::BuildSceneGraph() {
if (!m_pZone) return;
// Clear any existing adjacency list
m_SceneAdjacencyList.clear();
// Initialize adjacency list with all scenes
const auto& scenes = m_pZone->GetScenes();
for (const auto& [sceneID, sceneRef] : scenes) {
if (sceneRef.sceneType != eSceneType::General) continue;
m_SceneAdjacencyList.try_emplace(sceneID, std::vector<LWOSCENEID>());
}
// Build adjacency list from scene transitions
const auto& transitions = m_pZone->GetSceneTransitions();
for (const auto& transition : transitions) {
// Each transition has multiple points, each pointing to a scene
// We need to determine which scenes this transition connects
// Group transition points by their scene IDs to find unique connections
std::set<LWOSCENEID> connectedScenes;
for (const auto& point : transition.points) {
if (point.sceneID != LWOSCENEID_INVALID && m_SceneAdjacencyList.contains(point.sceneID)) {
connectedScenes.insert(point.sceneID);
}
}
// Create bidirectional edges between all scenes in this transition
// (transitions typically connect two scenes, but can be more complex)
std::vector<LWOSCENEID> sceneList(connectedScenes.begin(), connectedScenes.end());
for (size_t i = 0; i < sceneList.size(); ++i) {
for (size_t j = 0; j < sceneList.size(); ++j) {
if (i != j) {
LWOSCENEID fromScene = sceneList[i];
LWOSCENEID toScene = sceneList[j];
// Add edge if it doesn't already exist
auto& adjacentScenes = m_SceneAdjacencyList[fromScene];
if (std::find(adjacentScenes.begin(), adjacentScenes.end(), toScene) == adjacentScenes.end()) {
adjacentScenes.push_back(toScene);
}
}
}
}
}
// Scene 0 (global scene) is always loaded and adjacent to all other scenes
LWOSCENEID globalScene = LWOSCENEID(0, 0);
for (auto& [sceneID, adjacentScenes] : m_SceneAdjacencyList) {
if (sceneID != globalScene) {
// Add global scene to this scene's adjacency list if not already present
if (std::find(adjacentScenes.begin(), adjacentScenes.end(), globalScene) == adjacentScenes.end()) {
adjacentScenes.push_back(globalScene);
}
// Add this scene to global scene's adjacency list if not already present
auto& globalAdjacent = m_SceneAdjacencyList[globalScene];
if (std::find(globalAdjacent.begin(), globalAdjacent.end(), sceneID) == globalAdjacent.end()) {
globalAdjacent.push_back(sceneID);
}
}
}
}
std::vector<LWOSCENEID> dZoneManager::GetAdjacentScenes(LWOSCENEID sceneID) const {
auto it = m_SceneAdjacencyList.find(sceneID);
if (it != m_SceneAdjacencyList.end()) {
return it->second;
}
return std::vector<LWOSCENEID>();
}