diff --git a/dGame/dComponents/ScriptComponent.cpp b/dGame/dComponents/ScriptComponent.cpp index a5acaed1d..dd0b1a604 100644 --- a/dGame/dComponents/ScriptComponent.cpp +++ b/dGame/dComponents/ScriptComponent.cpp @@ -98,11 +98,6 @@ CppScripts::Script* const ScriptComponent::GetScript() { void ScriptComponent::SetScript(const std::string& scriptName) { // Scripts are managed by the CppScripts class and are effecitvely singletons // and they may also be used by other script components so DON'T delete them. - // Edit mode (docs/EditMode.md): the zone control object stays, its zone script does not run - if (Game::zoneManager && Game::zoneManager->GetEditMode() && m_Parent->GetObjectID() == dZoneManager::ZONE_CONTROL_OBJECT_ID) { - m_Script = CppScripts::GetInvalidScript(); - return; - } m_Script = CppScripts::GetScript(m_Parent, scriptName); } diff --git a/dGame/dGameMessages/ZoneMessages.cpp b/dGame/dGameMessages/ZoneMessages.cpp index 25f531bd1..310e727aa 100644 --- a/dGame/dGameMessages/ZoneMessages.cpp +++ b/dGame/dGameMessages/ZoneMessages.cpp @@ -118,8 +118,7 @@ namespace GameMessages { std::vector racingControllers = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::RACING_CONTROL); for (Entity* racingController : racingControllers) { auto* racingComponent = racingController->GetComponent(); - // Edit mode: a race track's zone control doesn't take the GM into a race (or send them out for having no car) - if (racingComponent != nullptr && !Game::zoneManager->GetEditMode()) { + if (racingComponent != nullptr) { racingComponent->OnPlayerLoaded(entity); } } diff --git a/dGame/dUtilities/CMakeLists.txt b/dGame/dUtilities/CMakeLists.txt index 72ba808cb..ef6ed86e6 100644 --- a/dGame/dUtilities/CMakeLists.txt +++ b/dGame/dUtilities/CMakeLists.txt @@ -5,7 +5,6 @@ set(DGAME_DUTILITIES_SOURCES "BrickDatabase.cpp" "CheatDetection.cpp" "DashboardActions.cpp" "EconomyLedger.cpp" - "EditMode.cpp" "DashboardNotify.cpp" "GUID.cpp" "LiveEvents.cpp" diff --git a/dGame/dUtilities/EditMode.cpp b/dGame/dUtilities/EditMode.cpp deleted file mode 100644 index b1f986c3e..000000000 --- a/dGame/dUtilities/EditMode.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "EditMode.h" - -#include - -#include "CharacterComponent.h" -#include "Game.h" -#include "GeneralUtils.h" -#include "dConfig.h" - -eGameMasterLevel EditMode::RequiredLevel() { - const auto level = GeneralUtils::TryParse(Game::config->GetValue("editor_gm_level")).value_or(CharacterComponent::DEFAULT_EDITOR_GM_LEVEL); - return static_cast(std::clamp(level, 1, 9)); -} diff --git a/dGame/dUtilities/EditMode.h b/dGame/dUtilities/EditMode.h deleted file mode 100644 index a10886ada..000000000 --- a/dGame/dUtilities/EditMode.h +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "eGameMasterLevel.h" - -/** - * Edit mode (docs/EditMode.md): a private instance of a zone whose world server constructs none of the zone's level - * objects, spawners or triggers, for editing the zone in the game client. Only the plumbing is here; nothing starts - * such an instance yet. - */ -namespace EditMode { - // Whether an account may be in an edit mode instance - inline bool MayEnter(const eGameMasterLevel accountLevel, const eGameMasterLevel requiredLevel) { - return accountLevel != eGameMasterLevel::CIVILIAN && accountLevel >= requiredLevel; - } - - // The account GM level an edit mode instance lets in: editor_gm_level (9 unless set) - eGameMasterLevel RequiredLevel(); -} diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index de581f770..b84ce6b29 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -86,7 +86,6 @@ #include "DestroyableComponent.h" #include "Game.h" #include "PropertyManagementComponent.h" -#include "EditMode.h" #include "AssetManager.h" #include "ZoneOverrideStore.h" #include "LevelProgressionComponent.h" @@ -245,7 +244,6 @@ int main(int argc, char** argv) { uint32_t cloneID = 0; uint32_t maxClients = 8; uint32_t ourPort = 2007; - bool editMode = false; //Check our arguments: for (int32_t i = 0; (i + 1) < argc; i++) { @@ -260,7 +258,6 @@ int main(int argc, char** argv) { else if (argument == "-clone") cloneID = valOptional.value_or(0); else if (argument == "-maxclients") maxClients = valOptional.value_or(8); else if (argument == "-port") ourPort = valOptional.value_or(2007); - else if (argument == "-editmode") editMode = valOptional.value_or(0) != 0; } Game::config = new dConfig("worldconfig.ini"); @@ -429,8 +426,6 @@ int main(int argc, char** argv) { Profiler::Scope navmesh("Navmesh and physics load", Profiler::Phase::PHYSICS); dpWorld::Initialize(zoneID); } - // A GM's edit mode instance (docs/EditMode.md): master says so when it starts the world - Game::zoneManager->SetEditMode(editMode); Game::zoneManager->Initialize(LWOZONEID(zoneID, g_InstanceID, cloneID)); PacketCapture::SetClone(cloneID); g_CloneID = cloneID; @@ -586,8 +581,7 @@ int main(int argc, char** argv) { DashboardNotify::Flush(); DashboardNotify::SendPlayerPositions(g_InstanceID); MessageInspector::Update(); - // (an edit mode instance has no server objects, so no live events either) - if (!Game::zoneManager->GetEditMode()) LiveEvents::Update(); + LiveEvents::Update(); UgcManifest::Update(); Metrics::StartMeasurement(MetricVariable::UpdateSpawners); @@ -898,17 +892,6 @@ namespace { } else { LOG("User %s authenticated with correct key.", username.GetAsString().c_str()); - // An edit mode instance takes only accounts that may use /editmode, however they found its address - if (Game::zoneManager->GetEditMode()) { - const auto account = Database::Get()->GetAccountInfo(username.GetAsString()); - if (!EditMode::MayEnter(account ? account->maxGmLevel : eGameMasterLevel::CIVILIAN, EditMode::RequiredLevel())) { - LOG("User %s may not enter an edit mode instance; disconnecting them", username.GetAsString().c_str()); - Game::server->Disconnect(it->second.sysAddr, eServerDisconnectIdentifiers::KICK); - g_PendingUsers.erase(it); - return; - } - } - UserManager::Instance()->DeleteUser(masterAddr); //Create our user and send them in: diff --git a/dZoneManager/Level.cpp b/dZoneManager/Level.cpp index 16140fa73..786b8e6a8 100644 --- a/dZoneManager/Level.cpp +++ b/dZoneManager/Level.cpp @@ -47,7 +47,7 @@ Level::Level(Zone* parentZone, const std::string& filepath, int32_t sceneID) { static_cast(header) = chunkHeader; m_ChunkHeaders.insert(std::make_pair(id, header)); } - LoadSceneObjects(levelFile.objects, m_SceneID); + LoadSceneObjects(levelFile.objects); } void Level::MakeSpawner(const SceneObject& obj, int32_t sceneID) { @@ -120,18 +120,7 @@ void Level::MakeSpawner(const SceneObject& obj, int32_t sceneID) { Game::zoneManager->MakeSpawner(spawnInfo); } -void Level::LoadSceneObjects(const std::vector& objects, const int32_t sceneID) { - // Edit mode: the GM's client has the level's objects from its own files, so none is made here. The oldest files - // keep the spawn point in an object, which is still read. - if (Game::zoneManager->GetEditMode()) { - for (const auto& obj : objects) { - if (obj.lot != LOT_MARKER_PLAYER_START) continue; - Game::zoneManager->GetZoneMut()->SetSpawnPos(obj.position); - Game::zoneManager->GetZoneMut()->SetSpawnRot(obj.rotation); - } - return; - } - +void Level::LoadSceneObjects(const std::vector& objects) { CDFeatureGatingTable* featureGatingTable = CDClientManager::GetTable(); CDFeatureGating gating; @@ -192,7 +181,7 @@ void Level::LoadSceneObjects(const std::vector& objects, const int3 } if (obj.lot == 176) { //Spawner - MakeSpawner(obj, sceneID); + MakeSpawner(obj, m_SceneID); } else { //Regular object EntityInfo info; info.spawnerID = 0; @@ -202,7 +191,7 @@ void Level::LoadSceneObjects(const std::vector& objects, const int3 info.rot = obj.rotation; info.settings = obj.settings; info.scale = obj.scale; - info.scene = sceneID; + info.scene = m_SceneID; Game::entityManager->CreateEntity(info); } } diff --git a/dZoneManager/Level.h b/dZoneManager/Level.h index 82173288a..1814aed47 100644 --- a/dZoneManager/Level.h +++ b/dZoneManager/Level.h @@ -20,12 +20,11 @@ public: static void MakeSpawner(const SceneObject& obj, int32_t sceneID = -1); - // Makes the objects and spawners of a level file. In edit mode (dZoneManager::GetEditMode) none: only the player - // start marker's place is taken - static void LoadSceneObjects(const std::vector& objects, int32_t sceneID = -1); - std::map m_ChunkHeaders; private: Zone* m_ParentZone; int32_t m_SceneID = -1; + + //private functions: + void LoadSceneObjects(const std::vector& objects); }; diff --git a/dZoneManager/Zone.cpp b/dZoneManager/Zone.cpp index 6420f02da..34933503b 100644 --- a/dZoneManager/Zone.cpp +++ b/dZoneManager/Zone.cpp @@ -138,8 +138,7 @@ void Zone::LoadZoneIntoMemory() { } } - // Edit mode: no spawner networks (the paths themselves are kept, they are only data) - if (m_FileFormatVersion >= Zone::FileFormatVersion::EarlyAlpha && !Game::zoneManager->GetEditMode()) { + if (m_FileFormatVersion >= Zone::FileFormatVersion::EarlyAlpha) { for (const Path& path : m_Paths) { if (path.pathType != PathType::Spawner) continue; SpawnerInfo info{}; @@ -287,8 +286,7 @@ void Zone::LoadScene(ZoneScene&& zoneScene) { LWOSCENEID lwoSceneID(scene.id, scene.sceneType); std::string luTriggersPath = scene.filename.substr(0, scene.filename.size() - 4) + ".lutriggers"; - // Edit mode: no triggers - if (!Game::zoneManager->GetEditMode() && Game::assetManager->HasFile((m_ZonePath + luTriggersPath).c_str())) LoadLUTriggers(luTriggersPath, scene); + if (Game::assetManager->HasFile((m_ZonePath + luTriggersPath).c_str())) LoadLUTriggers(luTriggersPath, scene); m_Scenes[lwoSceneID] = std::move(scene); } diff --git a/dZoneManager/dZoneManager.cpp b/dZoneManager/dZoneManager.cpp index 1765f3a09..472737b3b 100644 --- a/dZoneManager/dZoneManager.cpp +++ b/dZoneManager/dZoneManager.cpp @@ -57,7 +57,8 @@ void dZoneManager::Initialize(const LWOZONEID& zoneID) { EntityInfo info; info.lot = zoneControlTemplate; - info.id = ZONE_CONTROL_OBJECT_ID; + /* Yep its hardcoded like this in the client too, this exact value. */ + info.id = 0x3FFF'FFFFFFFELL; Entity* zoneControl = Game::entityManager->CreateEntity(info, nullptr, nullptr, true); m_ZoneControlObject = zoneControl; @@ -72,9 +73,7 @@ void dZoneManager::Initialize(const LWOZONEID& zoneID) { LOG("Zone prepared in: %llu ms", (endTime - startTime)); - // The vanity objects are server objects the client's editor would not know - if (!m_EditMode) VanityUtilities::SpawnVanity(); - else LOG("Zone %i is in edit mode: no level objects, spawners, triggers or vanity objects were loaded", zoneID.GetMapID()); + VanityUtilities::SpawnVanity(); } dZoneManager::~dZoneManager() { diff --git a/dZoneManager/dZoneManager.h b/dZoneManager/dZoneManager.h index f9b29a5b6..1a22bca8b 100644 --- a/dZoneManager/dZoneManager.h +++ b/dZoneManager/dZoneManager.h @@ -23,9 +23,6 @@ private: void LoadWorldConfig(); public: - // The zone control object's ID, the same fixed value in the client (LevelManager::LoadZoneControlObject, 0x01082660) - static constexpr LWOOBJID ZONE_CONTROL_OBJECT_ID = 0x3FFF'FFFFFFFELL; - void Initialize(const LWOZONEID& zoneID); ~dZoneManager(); @@ -33,13 +30,6 @@ public: Zone* GetZoneMut() const; const Zone* GetZone() const { return GetZoneMut(); }; bool HasZone() const { return m_pZone != nullptr; }; - - /** - * A GM's edit mode (docs/EditMode.md): the zone's level objects, spawners, spawner networks and triggers are not - * loaded, so the GM's client is the only one that has them. Set before Initialize. - */ - bool GetEditMode() const { return m_EditMode; } - void SetEditMode(bool value) { m_EditMode = value; } void LoadZone(const LWOZONEID& zoneID); //Discard the current zone (if any) and loads a new zone. /* Adds a spawner to the zone with the specified ID. */ @@ -113,7 +103,6 @@ private: bool m_PlayerLoseCoinsOnDeath = false; bool m_DisableSaveLocation = false; bool m_MountsAllowed = true; - bool m_EditMode = false; bool m_PetsAllowed = true; std::map m_Spawners; std::optional m_WorldConfig = std::nullopt; diff --git a/docs/EditMode.md b/docs/EditMode.md deleted file mode 100644 index 538588bdd..000000000 --- a/docs/EditMode.md +++ /dev/null @@ -1,54 +0,0 @@ -# Edit mode (plumbing) - -A private instance of a zone for editing the zone in the game client. The client loads the zone's objects from its own -files; the server never reads the client's files, so in a normal instance its own copies of the same objects are there -too. In an edit mode instance the server constructs none of them. - -**Not exposed yet:** no command or packet from a client starts an edit mode instance. This is what exists for one to use. - -## The flag - -| Where | What | -|---|---| -| World -> master | `MasterPackets::CreatePrivateZone::editMode`, one more byte written only when set (`ZoneInstanceManager::CreatePrivateZone(..., editMode)`) | -| Master | `Instance::GetIsEditMode`; `InstanceManager::CreatePrivateInstance(..., editMode)` | -| Master -> world | The launch argument `-editmode 1` (`StartWorldServer`) | -| World | `dZoneManager::GetEditMode`, set before the zone loads | - -A reload (`/reloadworld`, a zone file change) or a live update replaces a private instance with one that has the same -password and edit mode. A request to join a private instance that is still starting is answered once it is ready. - -## What an edit mode instance loads - -| Loaded | Not loaded | -|---|---| -| The zone file: spawn point, scenes, scene transitions, boundaries, paths (as data) | Level objects (`.lvl`), carver-only blockers | -| The terrain's scene map, the navmesh and the physics world | Spawners (LOT 176) and spawner networks (spawner paths) | -| The level files' revisions (the map checksum sent to the client) | Triggers (`.lutriggers`) | -| The zone control object, with the zone's own template | The zone control object's server script | -| The player, their items, pets and mounts; GM commands | Vanity objects, live event objects | - -The zone control object stays: the client makes its own from the `ZoneTable` template under the same fixed ID -(`LevelManager::LoadZoneControlObject`, 0x01082660), the server's construction must match it, and server code expects -the object. Its script is replaced by one that does nothing. Its other components (an activity, a race) stay but are -idle: a race track's zone control does not take the player into a race. - -The client waits on nothing a level object sends: it reports the level loaded when its own resource queue is empty and -finishes the login on its character and the server's "done loading" (`LWONetworkInterface::Run`, 0x00b32360). - -## The map checksum - -The client compares the checksum the server sends in `LOAD_STATIC_ZONE` with the one of its own files -(`LWONetworkInterface::iSendMessage`, 0x00b35870), and logs out with "invalid map version" when they differ. The -checksum is made of the zone and scene files' revisions. Edit mode does not change that: the server's copy of the zone -must have the revisions the client's has. - -## Who gets in - -The world server of an edit mode instance disconnects accounts below `editor_gm_level` (worldconfig.ini, default 9). -Master never sends anyone to a private instance from a normal transfer or matchmaking. - -## The save's `edit` attribute - -A character's save has `edit="1"` when its account's GM level is at least `editor_gm_level`, `0` for everyone else. -The client takes anything but 0 as the editor being enabled. diff --git a/tests/dGameTests/CMakeLists.txt b/tests/dGameTests/CMakeLists.txt index 08107c4c4..a2e925f81 100644 --- a/tests/dGameTests/CMakeLists.txt +++ b/tests/dGameTests/CMakeLists.txt @@ -7,7 +7,6 @@ set(DGAMETEST_SOURCES "MailPacketTests.cpp" "MatchmakingTests.cpp" "EconomyLedgerTests.cpp" - "EditModeTests.cpp" "PlayerReportsLimitTests.cpp" "SlashCommandPermissionTests.cpp" "StaleSaveGuardTests.cpp" diff --git a/tests/dGameTests/EditModeTests.cpp b/tests/dGameTests/EditModeTests.cpp deleted file mode 100644 index bad510cf6..000000000 --- a/tests/dGameTests/EditModeTests.cpp +++ /dev/null @@ -1,69 +0,0 @@ -// Edit mode (docs/EditMode.md): who may enter, and a level file's objects not being made. -#include "GameDependencies.h" - -#include "EditMode.h" -#include "EntityManager.h" -#include "Level.h" -#include "LevelFile.h" -#include "Zone.h" -#include "GeneralUtils.h" -#include "dConfig.h" -#include "eObjectBits.h" -#include "dZoneManager.h" - -#include - -class EditModeTests : public GameDependenciesTest { -protected: - void SetUp() override { SetUpDependencies(); } - void TearDown() override { TearDownDependencies(); } -}; - -TEST_F(EditModeTests, OnlyEditorAccountsMayEnter) { - EXPECT_TRUE(EditMode::MayEnter(eGameMasterLevel::OPERATOR, eGameMasterLevel::OPERATOR)); - EXPECT_FALSE(EditMode::MayEnter(eGameMasterLevel::DEVELOPER, eGameMasterLevel::OPERATOR)); - EXPECT_FALSE(EditMode::MayEnter(eGameMasterLevel::CIVILIAN, eGameMasterLevel::OPERATOR)); - EXPECT_FALSE(EditMode::MayEnter(eGameMasterLevel::CIVILIAN, eGameMasterLevel::CIVILIAN)); - EXPECT_TRUE(EditMode::MayEnter(eGameMasterLevel::DEVELOPER, eGameMasterLevel::JUNIOR_DEVELOPER)); - - // editor_gm_level, 9 unless set - EXPECT_EQ(EditMode::RequiredLevel(), eGameMasterLevel::OPERATOR); - Game::config->SetDatabaseValues({ { "editor_gm_level", "8" } }, {}); - EXPECT_EQ(EditMode::RequiredLevel(), eGameMasterLevel::DEVELOPER); - Game::config->SetDatabaseValues({ { "editor_gm_level", "0" } }, {}); - EXPECT_EQ(EditMode::RequiredLevel(), eGameMasterLevel::FORUM_MODERATOR); - Game::config->SetDatabaseValues({}, {}); -} - -// In edit mode a level file's objects and spawners are not made; the player start marker still gives the spawn point -TEST_F(EditModeTests, LevelObjectsAreNotMade) { - Game::zoneManager->SetEditMode(true); - ASSERT_TRUE(Game::zoneManager->GetEditMode()); - - std::vector objects; - SceneObject object; - object.id = 1001; - object.lot = 2365; - objects.push_back(object); - SceneObject spawner; - spawner.id = 1002; - spawner.lot = 176; - objects.push_back(spawner); - SceneObject start; - start.id = 1003; - start.lot = LOT_MARKER_PLAYER_START; - start.position = NiPoint3(10.0f, 20.0f, 30.0f); - objects.push_back(start); - - Level::LoadSceneObjects(objects, 4); - - // (a level object's ID gets the client bit when it is made) - for (const LWOOBJID id : { 1001, 1002, 1003 }) { - EXPECT_EQ(Game::entityManager->GetEntity(id), nullptr); - LWOOBJID made = id; - GeneralUtils::SetBit(made, eObjectBits::CLIENT); - EXPECT_EQ(Game::entityManager->GetEntity(made), nullptr); - } - EXPECT_TRUE(Game::zoneManager->GetSpawners().empty()); - EXPECT_EQ(Game::zoneManager->GetZone()->GetSpawnPos(), NiPoint3(10.0f, 20.0f, 30.0f)); -}