mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
ZoneFilePack reads and writes the editor's packed files: named entries whose data is sd0, one entry for a single file or the zone, terrain, scenes and triggers of a whole map. Every length is checked, data that is not sd0 or has a chunk over 256 KiB is refused, and only a name's base name is used. ZoneFilePush makes a set of what was sent (one file replaces that file in the zone's set), checks it and applies it like any other set. The override store links a set's files instead of moving them, so a file the zone already has can be carried into its next set, and one change at a time is made in an override folder whichever server makes it. The asset manager can read the client's own file past an override. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
113 lines
4.7 KiB
C++
113 lines
4.7 KiB
C++
#pragma once
|
|
|
|
#include <cstdint>
|
|
#include <optional>
|
|
|
|
/**
|
|
* Who may edit a zone's files (docs/ZonePublish.md). A zone has up to MAX_EDITORS assigned editors and at most one
|
|
* lock holder at a time; a set can only be sent, restored or cleared by the account holding the zone's lock. A lock
|
|
* never runs out by itself: its holder gives it up, or an admin takes it away.
|
|
*
|
|
* Pure rules, the same whichever way a request arrives; the caller looks up the account's GM level (now, never from
|
|
* anything the requester says), the zone's holder and whether the account is assigned.
|
|
*/
|
|
namespace ZoneEditLock {
|
|
constexpr uint32_t MAX_EDITORS = 10;
|
|
constexpr uint8_t DEFAULT_PUBLISH_LEVEL = 9;
|
|
constexpr uint8_t DEFAULT_ADMIN_LEVEL = 9;
|
|
|
|
// The numbers are part of what requesters are told; never change them
|
|
enum class eResult : uint8_t {
|
|
OK = 0,
|
|
LEVEL_TOO_LOW = 1, // the account's level is below the level editing needs
|
|
NOT_ASSIGNED = 2, // not one of the zone's editors (and not an admin)
|
|
LOCKED_BY_OTHER = 3, // another account holds the zone's lock
|
|
NO_CHANGE = 4, // nothing to do: already the holder, or nothing was locked
|
|
};
|
|
|
|
struct Levels {
|
|
uint8_t publish = DEFAULT_PUBLISH_LEVEL; // zone_publish_gm_level
|
|
uint8_t admin = DEFAULT_ADMIN_LEVEL; // zone_lock_admin_gm_level
|
|
};
|
|
|
|
struct Actor {
|
|
uint32_t accountId{};
|
|
uint8_t gmLevel{};
|
|
bool assigned{}; // one of the zone's editors
|
|
};
|
|
|
|
inline bool IsAdmin(const Actor& actor, const Levels& levels) {
|
|
return actor.gmLevel >= levels.admin;
|
|
}
|
|
|
|
// Taking the zone's lock; holder: the account holding it now
|
|
inline eResult Take(const Actor& actor, std::optional<uint32_t> holder, const Levels& levels) {
|
|
if (actor.accountId == 0 || actor.gmLevel < levels.publish) return eResult::LEVEL_TOO_LOW;
|
|
if (!actor.assigned && !IsAdmin(actor, levels)) return eResult::NOT_ASSIGNED;
|
|
if (holder && *holder == actor.accountId) return eResult::NO_CHANGE;
|
|
if (holder) return eResult::LOCKED_BY_OTHER;
|
|
return eResult::OK;
|
|
}
|
|
|
|
// Giving the lock up: its holder, or an admin taking it from whoever holds it
|
|
inline eResult Release(const Actor& actor, std::optional<uint32_t> holder, const Levels& levels) {
|
|
if (actor.accountId == 0 || actor.gmLevel < levels.publish) return eResult::LEVEL_TOO_LOW;
|
|
if (!holder) return eResult::NO_CHANGE;
|
|
if (*holder == actor.accountId || IsAdmin(actor, levels)) return eResult::OK;
|
|
return eResult::LOCKED_BY_OTHER;
|
|
}
|
|
|
|
/**
|
|
* Changing the zone's files (a new set, a restore, a clear): only the lock's holder. OK, LEVEL_TOO_LOW,
|
|
* LOCKED_BY_OTHER, or NOT_ASSIGNED when nobody holds the lock (it has to be taken first).
|
|
*/
|
|
inline eResult Change(const Actor& actor, std::optional<uint32_t> holder, const Levels& levels) {
|
|
if (actor.accountId == 0 || actor.gmLevel < levels.publish) return eResult::LEVEL_TOO_LOW;
|
|
if (!holder) return eResult::NOT_ASSIGNED;
|
|
if (*holder != actor.accountId) return eResult::LOCKED_BY_OTHER;
|
|
return eResult::OK;
|
|
}
|
|
|
|
/**
|
|
* Reading the zone's files as they are on the server: an account at the level that is one of the zone's
|
|
* editors, or an admin. OK, LEVEL_TOO_LOW or NOT_ASSIGNED; the lock is not needed to look.
|
|
*/
|
|
inline eResult Read(const Actor& actor, const Levels& levels) {
|
|
if (actor.accountId == 0 || actor.gmLevel < levels.publish) return eResult::LEVEL_TOO_LOW;
|
|
if (!actor.assigned && !IsAdmin(actor, levels)) return eResult::NOT_ASSIGNED;
|
|
return eResult::OK;
|
|
}
|
|
|
|
/**
|
|
* Change, for a requester that is only ever told 0 to 3: an account that could take the lock and hasn't is
|
|
* told LOCKED_BY_OTHER (by nobody: it has to lock the zone first), any other account without the lock what
|
|
* keeps it from taking it.
|
|
*/
|
|
inline eResult ChangeOrWhyNot(const Actor& actor, std::optional<uint32_t> holder, const Levels& levels) {
|
|
const auto result = Change(actor, holder, levels);
|
|
if (result != eResult::NOT_ASSIGNED) return result;
|
|
return Read(actor, levels) == eResult::OK ? eResult::LOCKED_BY_OTHER : eResult::NOT_ASSIGNED;
|
|
}
|
|
|
|
// What happens to the zone's running instances after its files changed
|
|
enum class eReload : uint8_t {
|
|
SKIPPED, // not asked for: the files are read when an instance next starts
|
|
STARTED, // the zone's instances are being replaced now
|
|
HELD, // players are there and they were not to be moved: waits until someone says to
|
|
};
|
|
|
|
inline eReload PlanReload(bool reload, uint32_t players, bool movePlayers) {
|
|
if (!reload) return eReload::SKIPPED;
|
|
return players > 0 && !movePlayers ? eReload::HELD : eReload::STARTED;
|
|
}
|
|
|
|
inline const char* ReloadName(eReload reload) {
|
|
switch (reload) {
|
|
case eReload::SKIPPED: return "skipped";
|
|
case eReload::STARTED: return "started";
|
|
case eReload::HELD: return "held";
|
|
}
|
|
return "skipped";
|
|
}
|
|
}
|