mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
refactor(dashboard): routes name zones, objects and phrases through GameText
The routes' own zone name helpers and the CDClient's English zone and leaderboard header copies give way to GameText, so every name comes from the locale in the viewer's language; the Network page's world labels, mail, missions, leaderboards, item info (cached per language) and the settings/vanity help strings too. Reward code 4 and the plaque text name their zone from the locale. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "APIRoutes.h"
|
||||
#include "GameText.h"
|
||||
#include "PropertyAssets.h"
|
||||
#include "OpenApi.h"
|
||||
#include "RouteUtils.h"
|
||||
@@ -40,8 +41,6 @@
|
||||
#include "Sd0.h"
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include "Locale.h"
|
||||
#include "LocaleText.h"
|
||||
#include <bcrypt/BCrypt.hpp>
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
@@ -84,11 +83,7 @@ namespace {
|
||||
std::string WithZoneNames(const std::string& table) {
|
||||
auto json = nlohmann::json::parse(table, nullptr, false);
|
||||
if (json.is_discarded()) return table;
|
||||
const auto& zones = ZoneNames();
|
||||
for (auto& row : json["data"]) {
|
||||
const auto zone = std::to_string(row.value("zone_id", 0));
|
||||
row["zone_name"] = zones.contains(zone) ? zones[zone].get<std::string>() : "Zone " + zone;
|
||||
}
|
||||
for (auto& row : json["data"]) row["zone_name"] = GameText::ZoneName(row.value("zone_id", 0u));
|
||||
return json.dump();
|
||||
}
|
||||
|
||||
@@ -130,7 +125,7 @@ namespace {
|
||||
}
|
||||
|
||||
std::string Localized(const std::string& text) {
|
||||
return LocaleText::Expand(text, [](const std::string& key) -> const std::string& { return Locale::GetPhrase(key); });
|
||||
return GameText::Expand(text);
|
||||
}
|
||||
|
||||
// One mail row for the Mail page and mailboxes: the stored text, the text as the client shows it (locale keys
|
||||
@@ -338,9 +333,9 @@ namespace {
|
||||
row["path"] = points;
|
||||
}
|
||||
const auto prefix = "PropertyTemplate_" + std::to_string(templateId);
|
||||
row["localized_name"] = Locale::GetPhrase(prefix + "_name");
|
||||
row["localized_description"] = Locale::GetPhrase(prefix + "_description");
|
||||
const auto& zones = ZoneNames();
|
||||
row["localized_name"] = GameText::Phrase(prefix + "_name");
|
||||
row["localized_description"] = GameText::Phrase(prefix + "_description");
|
||||
const auto& zones = GameText::ZoneNames();
|
||||
for (const auto* key : { "mapID", "vendorMapID" }) {
|
||||
if (!row.contains(key) || !row[key].is_number_integer()) continue;
|
||||
const auto zone = std::to_string(row[key].get<int64_t>());
|
||||
@@ -1244,7 +1239,7 @@ namespace {
|
||||
{"claimed", info->claimedTime},
|
||||
{"performance_cost", info->performanceCost},
|
||||
{"zone_id", info->zoneId},
|
||||
{"zone_name", ZoneNames().contains(std::to_string(info->zoneId)) ? ZoneNames()[std::to_string(info->zoneId)].get<std::string>() : ""},
|
||||
{"zone_name", GameText::ZoneNames().contains(std::to_string(info->zoneId)) ? GameText::ZoneNames()[std::to_string(info->zoneId)].get<std::string>() : ""},
|
||||
{"models", models}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Announcements.h"
|
||||
#include "GameText.h"
|
||||
#include "AnnouncementSchedule.h"
|
||||
#include "LiveWorld.h"
|
||||
#include "DashboardRoutes.h"
|
||||
@@ -61,7 +62,7 @@ namespace {
|
||||
std::string ZoneList(const std::vector<uint32_t>& zones) {
|
||||
if (zones.empty()) return "every world";
|
||||
std::string text;
|
||||
const auto& names = ZoneNames();
|
||||
const auto& names = GameText::ZoneNames();
|
||||
for (const auto zone : zones) {
|
||||
const auto key = std::to_string(zone);
|
||||
text += (text.empty() ? "" : ", ") + (names.contains(key) ? names[key].get<std::string>() : "zone " + key);
|
||||
@@ -80,7 +81,7 @@ namespace {
|
||||
// Every zone a message can be limited to, for the picker
|
||||
nlohmann::json ZoneOptions() {
|
||||
std::vector<std::pair<uint32_t, std::string>> zones;
|
||||
for (const auto& [id, name] : ZoneNames().items()) {
|
||||
for (const auto& [id, name] : GameText::ZoneNames().items()) {
|
||||
if (const auto zone = GeneralUtils::TryParse<uint32_t>(id); zone && *zone > 0) zones.emplace_back(*zone, name.get<std::string>());
|
||||
}
|
||||
std::sort(zones.begin(), zones.end());
|
||||
@@ -101,7 +102,7 @@ namespace {
|
||||
row.enabled = body.value("enabled", true);
|
||||
row.zones.clear();
|
||||
if (body.contains("zones") && body["zones"].is_array()) {
|
||||
const auto& known = ZoneNames();
|
||||
const auto& known = GameText::ZoneNames();
|
||||
for (const auto& zone : body["zones"]) {
|
||||
if (!zone.is_number_unsigned() || !known.contains(std::to_string(zone.get<uint32_t>()))) { error = "Pick zones from the list"; return false; }
|
||||
if (std::find(row.zones.begin(), row.zones.end(), zone.get<uint32_t>()) == row.zones.end()) row.zones.push_back(zone.get<uint32_t>());
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ChallengeRoutes.h"
|
||||
#include "GameText.h"
|
||||
#include "LiveEventRoutes.h"
|
||||
#include "LiveWorld.h"
|
||||
#include "DashboardRoutes.h"
|
||||
@@ -46,7 +47,7 @@ namespace {
|
||||
std::string ZoneList(const std::vector<uint32_t>& zones) {
|
||||
if (zones.empty()) return "every zone";
|
||||
std::string text;
|
||||
const auto& names = ZoneNames();
|
||||
const auto& names = GameText::ZoneNames();
|
||||
for (const auto zone : zones) {
|
||||
const auto key = std::to_string(zone);
|
||||
text += (text.empty() ? "" : ", ") + (names.contains(key) ? names[key].get<std::string>() : "zone " + key);
|
||||
@@ -257,7 +258,7 @@ namespace {
|
||||
|
||||
nlohmann::json ZoneOptions() {
|
||||
std::vector<std::pair<uint32_t, std::string>> zones;
|
||||
for (const auto& [id, name] : ZoneNames().items()) {
|
||||
for (const auto& [id, name] : GameText::ZoneNames().items()) {
|
||||
if (const auto zone = GeneralUtils::TryParse<uint32_t>(id); zone && *zone > 0) zones.emplace_back(*zone, name.get<std::string>());
|
||||
}
|
||||
std::sort(zones.begin(), zones.end());
|
||||
@@ -284,7 +285,7 @@ namespace {
|
||||
if (challenge.lot < 0 || (challenge.lot > 0 && !ClientAssets::ObjectName(challenge.lot))) return fail("Unknown LOT");
|
||||
challenge.zones.clear();
|
||||
if (body.contains("zones") && body["zones"].is_array()) {
|
||||
const auto& known = ZoneNames();
|
||||
const auto& known = GameText::ZoneNames();
|
||||
for (const auto& zone : body["zones"]) {
|
||||
if (!zone.is_number_unsigned() || !known.contains(std::to_string(zone.get<uint32_t>()))) return fail("Pick zones from the list");
|
||||
if (std::find(challenge.zones.begin(), challenge.zones.end(), zone.get<uint32_t>()) == challenge.zones.end()) challenge.zones.push_back(zone.get<uint32_t>());
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "CharacterProgress.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
@@ -186,10 +187,8 @@ namespace {
|
||||
for (const auto& [zoneId, s] : stats) zoneIds.insert(zoneId);
|
||||
for (const auto& [zoneId, zone] : catalog.zones) if (zone.released) zoneIds.insert(zoneId);
|
||||
zoneIds.erase(0); // character select
|
||||
const auto& names = ZoneNames();
|
||||
nlohmann::json zones = nlohmann::json::array();
|
||||
for (const auto zoneId : zoneIds) {
|
||||
const auto key = std::to_string(zoneId);
|
||||
const auto zone = catalog.zones.find(zoneId);
|
||||
nlohmann::json summary = nlohmann::json::array();
|
||||
if (zone != catalog.zones.end()) {
|
||||
@@ -206,7 +205,7 @@ namespace {
|
||||
}
|
||||
}
|
||||
const auto category = zone != catalog.zones.end() ? zone->second.category : std::nullopt;
|
||||
zones.push_back({ {"id", zoneId}, {"name", names.contains(key) ? names[key] : nlohmann::json("Zone " + key)}, {"visited", visited.contains(zoneId)},
|
||||
zones.push_back({ {"id", zoneId}, {"name", GameText::ZoneName(zoneId)}, {"visited", visited.contains(zoneId)},
|
||||
{"category", category ? nlohmann::json(*category) : nlohmann::json(nullptr)}, {"stats", stats.contains(zoneId) ? stats[zoneId] : nlohmann::json(nullptr)},
|
||||
{"summary", summary} });
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "CharacterTools.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <ctime>
|
||||
|
||||
@@ -72,14 +73,9 @@ namespace {
|
||||
for (auto& inventory : summary["inventories"]) for (auto& item : inventory["items"]) item["name"] = ClientAssets::ItemName(item["lot"].get<LOT>());
|
||||
}
|
||||
|
||||
nlohmann::json ZoneName(uint32_t zone) {
|
||||
const auto& zones = ZoneNames();
|
||||
const auto key = std::to_string(zone);
|
||||
return zones.contains(key) ? zones[key] : nlohmann::json("Zone " + key);
|
||||
}
|
||||
|
||||
nlohmann::json WithZones(nlohmann::json rows) {
|
||||
for (auto& row : rows) row["zone_name"] = ZoneName(row.value("zone_id", 0u));
|
||||
for (auto& row : rows) row["zone_name"] = GameText::ZoneName(row.value("zone_id", 0u));
|
||||
return rows;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ChatRoutes.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <ctime>
|
||||
#include <map>
|
||||
@@ -29,7 +30,7 @@ namespace {
|
||||
}
|
||||
|
||||
nlohmann::json RowJson(const IChatLog::ChatMessage& m, const ChatHistory::Access& access) {
|
||||
const auto& zones = ZoneNames();
|
||||
const auto& zones = GameText::ZoneNames();
|
||||
const auto zone = std::to_string(m.zoneId);
|
||||
auto json = ChatHistory::MessageJson(m, access);
|
||||
json["zone_name"] = zones.contains(zone) ? zones[zone] : nlohmann::json("");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ClientAssets.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
@@ -15,7 +16,6 @@
|
||||
#include "CDClientDatabase.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "Locale.h"
|
||||
#include "dConfig.h"
|
||||
#include "eHTTPMethod.h"
|
||||
#include "GeneralUtils.h"
|
||||
@@ -27,7 +27,7 @@ namespace {
|
||||
constexpr uint32_t MAX_ITEMS_PER_REQUEST = 500;
|
||||
constexpr size_t MAX_LISTING_ENTRIES = 2000;
|
||||
|
||||
std::map<LOT, nlohmann::json> g_ItemCache;
|
||||
std::map<std::pair<std::string, LOT>, nlohmann::json> g_ItemCache; // by language (GameText) and LOT
|
||||
std::map<LOT, std::vector<int>> g_ItemSetsByLot; // lot -> set ids containing it
|
||||
bool g_ItemSetsLoaded = false;
|
||||
|
||||
@@ -95,9 +95,9 @@ namespace {
|
||||
return data;
|
||||
}
|
||||
|
||||
// In the viewer's language (GameText.h)
|
||||
std::string Phrase(const std::string& key) {
|
||||
const auto& phrase = Locale::GetPhrase(key);
|
||||
return phrase;
|
||||
return GameText::Phrase(key);
|
||||
}
|
||||
|
||||
struct Stats {
|
||||
@@ -243,7 +243,7 @@ namespace ClientAssets {
|
||||
}
|
||||
|
||||
std::string ItemName(LOT lot) {
|
||||
const auto& localized = Locale::GetPhrase("Objects_" + std::to_string(lot) + "_name");
|
||||
const auto& localized = GameText::Phrase(GameText::Key("Objects", lot));
|
||||
if (!localized.empty()) return localized;
|
||||
const auto& table = ObjectNameTable();
|
||||
const auto it = table.find(lot);
|
||||
@@ -252,7 +252,8 @@ namespace ClientAssets {
|
||||
}
|
||||
|
||||
nlohmann::json ItemInfo(LOT lot) {
|
||||
if (const auto cached = g_ItemCache.find(lot); cached != g_ItemCache.end()) return cached->second;
|
||||
const auto cacheKey = std::make_pair(GameText::Language(), lot);
|
||||
if (const auto cached = g_ItemCache.find(cacheKey); cached != g_ItemCache.end()) return cached->second;
|
||||
|
||||
nlohmann::json info{ {"lot", lot}, {"name", ItemName(lot)} };
|
||||
|
||||
@@ -284,7 +285,7 @@ namespace ClientAssets {
|
||||
info["stats"] = StatsJson(stats);
|
||||
info["set"] = ItemSet(lot);
|
||||
|
||||
g_ItemCache[lot] = info;
|
||||
g_ItemCache[cacheKey] = info;
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "DashboardRoutes.h"
|
||||
#include "GameText.h"
|
||||
#include "RouteUtils.h"
|
||||
#include "Permissions.h"
|
||||
#include "EmailService.h"
|
||||
@@ -14,7 +15,6 @@
|
||||
#include "Game.h"
|
||||
#include "Database.h"
|
||||
#include "Logger.h"
|
||||
#include "Locale.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "tinyxml2.h"
|
||||
@@ -32,35 +32,12 @@
|
||||
using namespace RouteUtils;
|
||||
|
||||
namespace {
|
||||
std::string ZoneName(int zoneId) {
|
||||
if (zoneId == 0) return "Character Select";
|
||||
const auto& name = Locale::GetPhrase("ZoneTable_" + std::to_string(zoneId) + "_DisplayDescription");
|
||||
return name.empty() ? "Zone " + std::to_string(zoneId) : name;
|
||||
}
|
||||
|
||||
// Built once (worker threads read it too, so never lazily twice)
|
||||
const nlohmann::json& GetZoneNamesJson() {
|
||||
static const nlohmann::json names = [] {
|
||||
nlohmann::json names;
|
||||
names["0"] = "Character Select";
|
||||
for (const auto& key : Locale::GetPhraseIdsWithPrefix("ZoneTable_")) {
|
||||
if (key.find("_DisplayDescription") == std::string::npos) continue;
|
||||
const auto start = std::string("ZoneTable_").length();
|
||||
const auto end = key.find("_DisplayDescription");
|
||||
const auto& name = Locale::GetPhrase(key);
|
||||
if (!name.empty()) names[key.substr(start, end - start)] = name;
|
||||
}
|
||||
return names;
|
||||
}();
|
||||
return names;
|
||||
}
|
||||
|
||||
nlohmann::json ParseCharacterXml(const std::string& xml) {
|
||||
// Every value the template reads has a default, since inja fails on missing variables and
|
||||
// character XML may lack sections (e.g. a character that never finished loading in)
|
||||
nlohmann::json stats{
|
||||
{"coins", 0}, {"universe_score", 0}, {"reputation", 0}, {"gm_level", 0}, {"level", 0},
|
||||
{"last_zone_id", 0}, {"last_zone", ZoneName(0)},
|
||||
{"last_zone_id", 0}, {"last_zone", GameText::ZoneName(0)},
|
||||
{"health", 0}, {"max_health", 0}, {"armor", 0}, {"max_armor", 0}, {"imagination", 0}, {"max_imagination", 0},
|
||||
{"missions_completed", 0}, {"missions_active", 0}, {"total_items", 0}
|
||||
};
|
||||
@@ -79,7 +56,7 @@ namespace {
|
||||
|
||||
auto lastZone = charEl->IntAttribute("lwid", 0);
|
||||
stats["last_zone_id"] = lastZone;
|
||||
stats["last_zone"] = ZoneName(lastZone);
|
||||
stats["last_zone"] = GameText::ZoneName(lastZone);
|
||||
}
|
||||
|
||||
auto* lvlEl = obj->FirstChildElement("lvl");
|
||||
@@ -117,7 +94,7 @@ namespace {
|
||||
std::map<int, std::set<int>> clones;
|
||||
for (auto* l = vl->FirstChildElement("l"); l; l = l->NextSiblingElement("l")) clones[l->IntAttribute("id", 0)].insert(l->IntAttribute("cid", 0));
|
||||
nlohmann::json visited = nlohmann::json::array();
|
||||
for (const auto& [zid, cids] : clones) visited.push_back({ {"id", zid}, {"name", ZoneName(zid)}, {"clones", cids.size()} });
|
||||
for (const auto& [zid, cids] : clones) visited.push_back({ {"id", zid}, {"name", GameText::ZoneName(zid)}, {"clones", cids.size()} });
|
||||
stats["visited_zones"] = visited;
|
||||
}
|
||||
|
||||
@@ -127,7 +104,7 @@ namespace {
|
||||
for (auto* s = zs->FirstChildElement("s"); s; s = s->NextSiblingElement("s")) {
|
||||
auto zid = s->IntAttribute("map", 0);
|
||||
zoneStats.push_back({
|
||||
{"map_id", zid}, {"name", ZoneName(zid)},
|
||||
{"map_id", zid}, {"name", GameText::ZoneName(zid)},
|
||||
{"achievements", s->IntAttribute("ac", 0)},
|
||||
{"coins_collected", s->IntAttribute("cc", 0)},
|
||||
{"enemies_smashed", s->IntAttribute("es", 0)}
|
||||
@@ -207,10 +184,6 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
const nlohmann::json& ZoneNames() {
|
||||
return GetZoneNamesJson();
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr size_t LOG_TAIL_BYTES = 256 * 1024;
|
||||
constexpr size_t LOG_TAIL_LINES = 500;
|
||||
@@ -430,7 +403,7 @@ void RegisterDashboardRoutes() {
|
||||
const std::string selection = report.value("selection", std::string{});
|
||||
report["selection_text"] = selection;
|
||||
if (selection.starts_with("%[") && selection.ends_with("]")) {
|
||||
const auto& phrase = Locale::GetPhrase(selection.substr(2, selection.size() - 3));
|
||||
const auto& phrase = GameText::Phrase(selection.substr(2, selection.size() - 3));
|
||||
if (!phrase.empty()) report["selection_text"] = phrase;
|
||||
}
|
||||
RenderPage(reply, context, "bug_report-view.jinja2", "bug_reports", { {"report", report} });
|
||||
@@ -453,7 +426,7 @@ void RegisterDashboardRoutes() {
|
||||
SimplePage("/tasks", Perm("tasks_view"), "tasks.jinja2", "tasks", "Scheduled tasks: schedules, runs and logs");
|
||||
Route(eHTTPMethod::GET, "/api/zones", Perm("characters_rescue"), "Every zone with its name, for pickers: {zones: [{id, name}]}", [](HTTPReply& reply, const HTTPContext&) {
|
||||
nlohmann::json zones = nlohmann::json::array();
|
||||
for (const auto& [id, name] : GetZoneNamesJson().items()) {
|
||||
for (const auto& [id, name] : GameText::ZoneNames().items()) {
|
||||
if (const auto zone = GeneralUtils::TryParse<uint32_t>(id); zone && *zone > 0) zones.push_back({ {"id", *zone}, {"name", name} });
|
||||
}
|
||||
std::sort(zones.begin(), zones.end(), [](const auto& a, const auto& b) { return a["id"].template get<uint32_t>() < b["id"].template get<uint32_t>(); });
|
||||
@@ -492,7 +465,7 @@ void RegisterDashboardRoutes() {
|
||||
|
||||
// Logs
|
||||
Route(eHTTPMethod::GET, "/activity_log", Perm("logs_activity"), "Activity log", [](HTTPReply& reply, const HTTPContext& context) {
|
||||
RenderPage(reply, context, "activity_log.jinja2", "activity_log", { {"zone_names", GetZoneNamesJson()} });
|
||||
RenderPage(reply, context, "activity_log.jinja2", "activity_log", { {"zone_names", GameText::ZoneNames()} });
|
||||
});
|
||||
SimplePage("/command_log", Perm("logs_command"), "command_log.jinja2", "command_log", "Command log");
|
||||
SimplePage("/audit_log", Perm("logs_audit"), "audit_log.jinja2", "audit_log", "Audit log");
|
||||
@@ -516,7 +489,7 @@ void RegisterDashboardRoutes() {
|
||||
if (const auto zone = GeneralUtils::TryParse<uint32_t>(server.substr(12, server.find('_', 12) - 12))) zoneIds.insert(*zone);
|
||||
}
|
||||
nlohmann::json zones = nlohmann::json::array();
|
||||
for (const auto zone : zoneIds) zones.push_back({ {"id", zone}, {"name", GetZoneNamesJson().value(std::to_string(zone), "")} });
|
||||
for (const auto zone : zoneIds) zones.push_back({ {"id", zone}, {"name", GameText::ZoneNames().value(std::to_string(zone), "")} });
|
||||
nlohmann::json data{ {"servers", servers}, {"server_types", types}, {"selected_server", selected}, {"files", nlohmann::json::array()}, {"bundle_zones", zones} };
|
||||
if (!selected.empty()) {
|
||||
const auto& files = logs.at(selected);
|
||||
|
||||
@@ -5,6 +5,3 @@
|
||||
class HTTPContext;
|
||||
|
||||
void RegisterDashboardRoutes();
|
||||
|
||||
// Zone ID (as a string) -> display name, from the client's locale
|
||||
const nlohmann::json& ZoneNames();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "EconomyPlaces.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -47,10 +48,7 @@ namespace EconomyPlaces {
|
||||
}
|
||||
|
||||
std::string ZoneName(uint32_t zone) {
|
||||
const auto& names = ZoneNames();
|
||||
const auto key = std::to_string(zone);
|
||||
if (names.contains(key) && names[key].is_string()) return names[key].get<std::string>();
|
||||
return "Zone " + key;
|
||||
return GameText::ZoneName(zone);
|
||||
}
|
||||
|
||||
std::optional<IEconomyLedger::PlaceFilter> Parse(std::string_view place) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "EventsCalendar.h"
|
||||
#include "GameText.h"
|
||||
#include "EventSchedule.h"
|
||||
#include "AnnouncementSchedule.h"
|
||||
#include "LevelGating.h"
|
||||
@@ -156,22 +157,17 @@ namespace {
|
||||
GeneralUtils::TryParse<int32_t>(Game::config->GetValue("version_minor")).value_or(ClientVersion::minor) };
|
||||
}
|
||||
|
||||
std::string ZoneName(uint32_t zone) {
|
||||
const auto& names = ZoneNames();
|
||||
const auto key = std::to_string(zone);
|
||||
return names.contains(key) ? names[key].get<std::string>() : "Zone " + key;
|
||||
}
|
||||
|
||||
std::string ZoneList(const std::vector<uint32_t>& zones) {
|
||||
if (zones.empty()) return "every world";
|
||||
std::string text;
|
||||
for (const auto zone : zones) text += (text.empty() ? "" : ", ") + ZoneName(zone);
|
||||
for (const auto zone : zones) text += (text.empty() ? "" : ", ") + GameText::ZoneName(zone);
|
||||
return text;
|
||||
}
|
||||
|
||||
nlohmann::json ZoneOptions() {
|
||||
std::vector<std::pair<uint32_t, std::string>> zones;
|
||||
for (const auto& [id, name] : ZoneNames().items()) {
|
||||
for (const auto& [id, name] : GameText::ZoneNames().items()) {
|
||||
if (const auto zone = GeneralUtils::TryParse<uint32_t>(id); zone && *zone > 0) zones.emplace_back(*zone, name.get<std::string>());
|
||||
}
|
||||
std::sort(zones.begin(), zones.end());
|
||||
@@ -186,14 +182,14 @@ namespace {
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
for (const auto& world : ServerState::g_WorldInstances) {
|
||||
if (!zones.contains(world.mapID)) continue;
|
||||
worlds.push_back({ {"zone", world.mapID}, {"zoneName", ZoneName(world.mapID)}, {"instance", world.instanceID}, {"clone", world.cloneID}, {"players", world.players} });
|
||||
worlds.push_back({ {"zone", world.mapID}, {"zoneName", GameText::ZoneName(world.mapID)}, {"instance", world.instanceID}, {"clone", world.cloneID}, {"players", world.players} });
|
||||
}
|
||||
return worlds;
|
||||
}
|
||||
|
||||
nlohmann::json FeatureJson(const std::string& name, const Feature& feature) {
|
||||
nlohmann::json zones = nlohmann::json::array();
|
||||
for (const auto& [zone, objects] : feature.zones) zones.push_back({ {"id", zone}, {"name", ZoneName(zone)}, {"objects", objects} });
|
||||
for (const auto& [zone, objects] : feature.zones) zones.push_back({ {"id", zone}, {"name", GameText::ZoneName(zone)}, {"objects", objects} });
|
||||
nlohmann::json json{ {"name", name}, {"description", feature.description}, {"zones", zones}, {"version", nullptr}, {"unlocked", false} };
|
||||
if (feature.version) {
|
||||
const auto& [major, current, minor] = *feature.version;
|
||||
@@ -214,7 +210,7 @@ namespace {
|
||||
const auto zones = ZonesOf(feature);
|
||||
if (zones.empty()) return "No zone has objects gated on it; players see client-side changes the next time they log in.";
|
||||
std::string names;
|
||||
for (const auto& [zone, objects] : zones) names += (names.empty() ? "" : ", ") + ZoneName(zone);
|
||||
for (const auto& [zone, objects] : zones) names += (names.empty() ? "" : ", ") + GameText::ZoneName(zone);
|
||||
return "Worlds of " + names + " that are already running keep what they loaded until they are restarted; new logins get the change straight away.";
|
||||
}
|
||||
|
||||
@@ -608,7 +604,7 @@ namespace {
|
||||
}
|
||||
nlohmann::json zones = nlohmann::json::array();
|
||||
if (config.contains("zones") && config["zones"].is_array()) {
|
||||
const auto& known = ZoneNames();
|
||||
const auto& known = GameText::ZoneNames();
|
||||
for (const auto& zone : config["zones"]) {
|
||||
if (!zone.is_number_unsigned() || !known.contains(std::to_string(zone.get<uint32_t>()))) { error = "Pick zones from the list"; return std::nullopt; }
|
||||
if (std::find(zones.begin(), zones.end(), zone) == zones.end()) zones.push_back(zone);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "FeaturedProperties.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
@@ -59,11 +60,6 @@ namespace {
|
||||
return mapId;
|
||||
}
|
||||
|
||||
std::string ZoneName(uint32_t zoneId) {
|
||||
const auto& zones = ZoneNames();
|
||||
const auto key = std::to_string(zoneId);
|
||||
return zones.contains(key) && zones[key].is_string() ? zones[key].get<std::string>() : "Zone " + key;
|
||||
}
|
||||
|
||||
nlohmann::json PropertyJson(const IProperty::Info& info, const std::string& ownerName) {
|
||||
return {
|
||||
@@ -75,7 +71,7 @@ namespace {
|
||||
{"performance_cost", info.performanceCost},
|
||||
{"last_updated", info.lastUpdatedTime},
|
||||
{"zone_id", info.zoneId},
|
||||
{"zone_name", ZoneName(info.zoneId)},
|
||||
{"zone_name", GameText::ZoneName(info.zoneId)},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -144,10 +140,10 @@ namespace {
|
||||
nlohmann::json entry{
|
||||
{"template_id", slot.templateId},
|
||||
{"map_id", slot.mapId},
|
||||
{"zone_name", ZoneName(slot.mapId)},
|
||||
{"zone_name", GameText::ZoneName(slot.mapId)},
|
||||
{"spawn_name", slot.spawnName},
|
||||
{"location", choice.mapId},
|
||||
{"location_name", ZoneName(choice.mapId)},
|
||||
{"location_name", GameText::ZoneName(choice.mapId)},
|
||||
{"mode", HotPropertySlots::ModeName(choice.mode)},
|
||||
{"property_id", nullptr},
|
||||
{"picked", nullptr},
|
||||
@@ -169,7 +165,7 @@ namespace {
|
||||
}
|
||||
|
||||
nlohmann::json worlds = nlohmann::json::array();
|
||||
for (const auto world : news.worlds) worlds.push_back({ {"map_id", world}, {"zone_name", ZoneName(world)} });
|
||||
for (const auto world : news.worlds) worlds.push_back({ {"map_id", world}, {"zone_name", GameText::ZoneName(world)} });
|
||||
return {
|
||||
{"full_auto", settings.fullAuto},
|
||||
{"settings_updated_at", settings.updatedAt},
|
||||
@@ -252,12 +248,12 @@ void FeaturedProperties::RegisterRoutes() {
|
||||
: idValue.is_number_integer() ? std::optional<LWOOBJID>(idValue.get<LWOOBJID>()) : std::nullopt;
|
||||
if (!propertyId) return JsonError(reply, eHTTPStatusCode::BAD_REQUEST, "property_id is required to pick a property");
|
||||
const auto picked = Featurable(*propertyId, *location);
|
||||
if (!picked) return JsonError(reply, eHTTPStatusCode::BAD_REQUEST, "Only an approved public property in " + ZoneName(*location) + " can be picked there");
|
||||
if (!picked) return JsonError(reply, eHTTPStatusCode::BAD_REQUEST, "Only an approved public property in " + GameText::ZoneName(*location) + " can be picked there");
|
||||
for (const auto& other : Database::Get()->GetFeaturedPropertySlots()) {
|
||||
if (other.templateId == slot->templateId || HotPropertySlots::ModeFromInt(other.mode) != eMode::PICKED || other.propertyId != *propertyId) continue;
|
||||
const auto* otherSlot = FindSlot(other.templateId);
|
||||
if (!otherSlot) continue;
|
||||
return JsonError(reply, eHTTPStatusCode::CONFLICT, "That property is already picked for the " + ZoneName(otherSlot->mapId) + " slot");
|
||||
return JsonError(reply, eHTTPStatusCode::CONFLICT, "That property is already picked for the " + GameText::ZoneName(otherSlot->mapId) + " slot");
|
||||
}
|
||||
const auto& [info, ownerName] = *picked;
|
||||
row.propertyId = *propertyId;
|
||||
@@ -266,7 +262,7 @@ void FeaturedProperties::RegisterRoutes() {
|
||||
}
|
||||
|
||||
Database::Get()->SetFeaturedPropertySlot(row);
|
||||
Audit(context, "feature_property", "Top properties slot " + ZoneName(slot->mapId) + " (template " + std::to_string(slot->templateId) + "): " + ZoneName(*location) + ", " + what, target);
|
||||
Audit(context, "feature_property", "Top properties slot " + GameText::ZoneName(slot->mapId) + " (template " + std::to_string(slot->templateId) + "): " + GameText::ZoneName(*location) + ", " + what, target);
|
||||
BroadcastTableChanged("featured_properties", std::to_string(slot->templateId));
|
||||
JsonSuccess(reply, StateJson());
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Inspector.h"
|
||||
#include "GameText.h"
|
||||
#include "MasterPackets.h"
|
||||
|
||||
#include <chrono>
|
||||
@@ -114,9 +115,7 @@ namespace {
|
||||
}
|
||||
|
||||
std::string ZoneName(uint32_t zoneId) {
|
||||
const auto& zones = ZoneNames();
|
||||
const auto zone = std::to_string(zoneId);
|
||||
return zoneId == 0 ? "" : zones.contains(zone) ? zones[zone].get<std::string>() : "Zone " + zone;
|
||||
return zoneId == 0 ? "" : GameText::ZoneName(zoneId);
|
||||
}
|
||||
|
||||
// A session for the page; `state` is the live state, or "ended" for one only in the database
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "InstanceLoad.h"
|
||||
#include "GameText.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "InstanceLimits.h"
|
||||
#include "DashboardRoutes.h"
|
||||
@@ -50,11 +51,6 @@ namespace {
|
||||
return it == ClientCaps().end() ? InstanceLimits::Caps{ 8, 12 } : it->second;
|
||||
}
|
||||
|
||||
std::string ZoneName(uint32_t zone) {
|
||||
const auto& names = ZoneNames();
|
||||
const auto key = std::to_string(zone);
|
||||
return names.contains(key) ? names[key].get<std::string>() : "Zone " + key;
|
||||
}
|
||||
|
||||
nlohmann::json OptionalJson(const std::optional<uint32_t>& value) {
|
||||
return value ? nlohmann::json(*value) : nlohmann::json(nullptr);
|
||||
@@ -114,7 +110,7 @@ namespace InstanceLoad {
|
||||
const auto caps = ClientCapsOf(zone);
|
||||
nlohmann::json points = nlohmann::json::array();
|
||||
for (const auto& s : samples) points.push_back({ {"time", s.time}, {"instance", s.instanceId}, {"clone", s.cloneId}, {"players", s.players} });
|
||||
json["zone"] = { {"id", zone}, {"name", ZoneName(zone)} };
|
||||
json["zone"] = { {"id", zone}, {"name", GameText::ZoneName(zone)} };
|
||||
json["samples"] = points;
|
||||
json["clientCaps"] = { {"soft", caps.soft}, {"hard", caps.hard} };
|
||||
} else {
|
||||
@@ -135,7 +131,7 @@ namespace InstanceLoad {
|
||||
}
|
||||
nlohmann::json list = nlohmann::json::array();
|
||||
for (const auto& [id, summary] : zones) {
|
||||
list.push_back({ {"id", id}, {"name", ZoneName(id)}, {"busiest", summary.busiest}, {"instances", summary.instances}, {"players", summary.players} });
|
||||
list.push_back({ {"id", id}, {"name", GameText::ZoneName(id)}, {"busiest", summary.busiest}, {"instances", summary.instances}, {"players", summary.players} });
|
||||
}
|
||||
json["zones"] = list;
|
||||
}
|
||||
@@ -153,7 +149,7 @@ namespace InstanceLoad {
|
||||
const auto it = limits.find(id);
|
||||
const auto* limit = it == limits.end() ? nullptr : &it->second;
|
||||
const auto effective = InstanceLimits::Effective(limit ? limit->softCap : std::nullopt, limit ? limit->hardCap : std::nullopt, caps);
|
||||
zones.push_back({ {"id", id}, {"name", ZoneName(id)}, {"clientSoft", caps.soft}, {"clientHard", caps.hard},
|
||||
zones.push_back({ {"id", id}, {"name", GameText::ZoneName(id)}, {"clientSoft", caps.soft}, {"clientHard", caps.hard},
|
||||
{"softCap", limit ? OptionalJson(limit->softCap) : nullptr}, {"hardCap", limit ? OptionalJson(limit->hardCap) : nullptr},
|
||||
{"spare", limit ? limit->spareInstances : 0}, {"soft", effective.soft}, {"hard", effective.hard},
|
||||
{"updatedAt", limit ? limit->updatedAt : 0}, {"updatedBy", limit ? limit->updatedBy : ""} });
|
||||
@@ -193,7 +189,7 @@ namespace InstanceLoad {
|
||||
else Database::Get()->SetZoneLimit(limit);
|
||||
const auto effective = InstanceLimits::Effective(limit.softCap, limit.hardCap, client);
|
||||
const auto text = [](const std::optional<uint32_t>& value) { return value ? std::to_string(*value) : std::string("client's"); };
|
||||
Audit(context, "set_zone_limits", ZoneName(zone) + " (" + std::to_string(zone) + "): " + (reset ? std::string("back to the client's caps, no spare instances") :
|
||||
Audit(context, "set_zone_limits", GameText::ZoneName(zone) + " (" + std::to_string(zone) + "): " + (reset ? std::string("back to the client's caps, no spare instances") :
|
||||
"soft cap " + text(limit.softCap) + ", hard cap " + text(limit.hardCap) + ", " + std::to_string(limit.spareInstances) + " spare instance(s)"));
|
||||
BroadcastTableChanged("zone_limits", std::to_string(zone));
|
||||
const bool told = TellMaster();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "LeaderboardRoutes.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
@@ -8,7 +9,7 @@
|
||||
#include "WSRoutes.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "Database.h"
|
||||
#include "Locale.h"
|
||||
#include "GameLabels.h"
|
||||
#include "Game.h"
|
||||
#include "dConfig.h"
|
||||
#include "GeneralUtils.h"
|
||||
@@ -27,7 +28,7 @@ namespace {
|
||||
};
|
||||
|
||||
std::string ActivityName(uint32_t id) {
|
||||
std::string name = Locale::GetPhrase("Activities_" + std::to_string(id) + "_ActivityName");
|
||||
std::string name = GameText::Phrase(GameText::Key("Activities", id, "ActivityName"));
|
||||
if (name.empty()) return "Activity " + std::to_string(id);
|
||||
// The client's locale has Windows-1252 dashes stored as U+0096 ("Foot Race \u0096 Nimbus Station"); make them real dashes
|
||||
for (size_t at = name.find("\xC2\x96"); at != std::string::npos; at = name.find("\xC2\x96", at)) name.replace(at, 2, "\xE2\x80\x93");
|
||||
@@ -57,10 +58,9 @@ namespace {
|
||||
const char* hint = "";
|
||||
};
|
||||
|
||||
// The client's column header for a locale phrase ("RACE_BESTTIME" -> "Best Time"); fallback when the locale lacks it
|
||||
std::string Label(const char* phrase, const char* fallback) {
|
||||
const auto& text = Locale::GetPhrase(phrase);
|
||||
return text.empty() ? fallback : text;
|
||||
// The client's column header for a locale phrase ("RACE_BESTTIME" -> "Best Time"); the key as words when the locale lacks it
|
||||
std::string Label(const char* phrase) {
|
||||
return GameText::Text(phrase, GameLabels::Words(phrase));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,26 +71,26 @@ namespace {
|
||||
*/
|
||||
nlohmann::json Columns(eLeaderboardType type) {
|
||||
using enum eLeaderboardType;
|
||||
std::vector<std::pair<Column, const char*>> columns; // column, English fallback
|
||||
std::vector<Column> columns;
|
||||
switch (type) {
|
||||
case ShootingGallery: columns = { { {"primary", "SCORE", "number"}, "Score" }, { {"secondary", "UI_SG_STREAK", "number"}, "Streak" }, { {"tertiary", "UI_SG_ACCURACY", "percent"}, "Accuracy" } }; break;
|
||||
case Racing: columns = { { {"primary", "RACE_BESTTIME", "laptime", "Less is better"}, "Best Time" }, { {"secondary", "RACE_BESTLAP", "laptime"}, "Best Lap" }, { {"wins", "RACE_NUMWINS", "number"}, "Wins" } }; break;
|
||||
case MonumentRace: columns = { { {"primary", "TIME", "time", "Time taken: less is better"}, "Time" } }; break;
|
||||
case ShootingGallery: columns = { {"primary", "SCORE", "number"}, {"secondary", "UI_SG_STREAK", "number"}, {"tertiary", "UI_SG_ACCURACY", "percent"} }; break;
|
||||
case Racing: columns = { {"primary", "RACE_BESTTIME", "laptime", "Less is better"}, {"secondary", "RACE_BESTLAP", "laptime"}, {"wins", "RACE_NUMWINS", "number"} }; break;
|
||||
case MonumentRace: columns = { {"primary", "TIME", "time", "Time taken: less is better"} }; break;
|
||||
// The foot race scripts save the time left on the race's countdown when the player finishes
|
||||
case FootRace: columns = { { {"primary", "TIME", "time", "Time left on the clock at the finish: more is better"}, "Time" } }; break;
|
||||
case UnusedLeaderboard4: columns = { { {"primary", "POINTS", "number"}, "Points" } }; break;
|
||||
case Survival: columns = { { {"secondary", "TIME", "time"}, "Time" }, { {"primary", "POINTS", "number"}, "Points" } }; break;
|
||||
case SurvivalNS: columns = { { {"primary", "WAVE", "number"}, "Wave" }, { {"secondary", "TIME", "time", "Less is better for the same wave"}, "Time" } }; break;
|
||||
case Donations: columns = { { {"primary", "DONATIONS", "number"}, "Donations" } }; break;
|
||||
default: columns = { { {"primary", "SCORE", "number"}, "Score" } }; break;
|
||||
case FootRace: columns = { {"primary", "TIME", "time", "Time left on the clock at the finish: more is better"} }; break;
|
||||
case UnusedLeaderboard4: columns = { {"primary", "POINTS", "number"} }; break;
|
||||
case Survival: columns = { {"secondary", "TIME", "time"}, {"primary", "POINTS", "number"} }; break;
|
||||
case SurvivalNS: columns = { {"primary", "WAVE", "number"}, {"secondary", "TIME", "time", "Less is better for the same wave"} }; break;
|
||||
case Donations: columns = { {"primary", "DONATIONS", "number"} }; break;
|
||||
default: columns = { {"primary", "SCORE", "number"} }; break;
|
||||
}
|
||||
if (type != Donations) columns.push_back({ {"played", "TIMES_PLAYED", "number"}, "Times Played" });
|
||||
if (type != Donations) columns.push_back({ "played", "TIMES_PLAYED", "number" });
|
||||
// The score the order goes by first: survival ranks by time with classic_survival_scoring (as GetAgsLeaderboard does)
|
||||
const bool byTime = type == Survival && Game::config && Game::config->GetValue("classic_survival_scoring") == "1";
|
||||
const std::string ranksBy = byTime ? "secondary" : "primary";
|
||||
nlohmann::json out = nlohmann::json::array();
|
||||
for (const auto& [column, fallback] : columns) {
|
||||
out.push_back({ {"key", column.key}, {"label", Label(column.phrase, fallback)}, {"format", column.format}, {"hint", column.hint}, {"ranks", column.key == ranksBy} });
|
||||
for (const auto& column : columns) {
|
||||
out.push_back({ {"key", column.key}, {"label", Label(column.phrase)}, {"format", column.format}, {"hint", column.hint}, {"ranks", column.key == ranksBy} });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "LiveEventRoutes.h"
|
||||
#include "GameText.h"
|
||||
#include "LiveWorld.h"
|
||||
#include "DashboardRoutes.h"
|
||||
#include "ClientAssets.h"
|
||||
@@ -48,7 +49,7 @@ namespace {
|
||||
std::string ZoneList(const std::vector<uint32_t>& zones) {
|
||||
if (zones.empty()) return "every world";
|
||||
std::string text;
|
||||
const auto& names = ZoneNames();
|
||||
const auto& names = GameText::ZoneNames();
|
||||
for (const auto zone : zones) {
|
||||
const auto key = std::to_string(zone);
|
||||
text += (text.empty() ? "" : ", ") + (names.contains(key) ? names[key].get<std::string>() : "zone " + key);
|
||||
@@ -58,7 +59,7 @@ namespace {
|
||||
|
||||
nlohmann::json ZoneOptions() {
|
||||
std::vector<std::pair<uint32_t, std::string>> zones;
|
||||
for (const auto& [id, name] : ZoneNames().items()) {
|
||||
for (const auto& [id, name] : GameText::ZoneNames().items()) {
|
||||
if (const auto zone = GeneralUtils::TryParse<uint32_t>(id); zone && *zone > 0) zones.emplace_back(*zone, name.get<std::string>());
|
||||
}
|
||||
std::sort(zones.begin(), zones.end());
|
||||
@@ -277,7 +278,7 @@ namespace {
|
||||
if (event.message.size() > MAX_MESSAGE) { error = "The message is up to 300 characters"; return false; }
|
||||
event.zones.clear();
|
||||
if (body.contains("zones") && body["zones"].is_array()) {
|
||||
const auto& known = ZoneNames();
|
||||
const auto& known = GameText::ZoneNames();
|
||||
for (const auto& zone : body["zones"]) {
|
||||
if (!zone.is_number_unsigned() || zone.get<uint32_t>() == 0 || !known.contains(std::to_string(zone.get<uint32_t>()))) { error = "Pick zones from the list"; return false; }
|
||||
if (std::find(event.zones.begin(), event.zones.end(), zone.get<uint32_t>()) == event.zones.end()) event.zones.push_back(zone.get<uint32_t>());
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "LiveUpdateRoutes.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
@@ -29,7 +30,6 @@ namespace {
|
||||
|
||||
nlohmann::json ToJson(const LiveUpdateStatus& status) {
|
||||
using namespace LiveUpdate;
|
||||
const auto& zones = ZoneNames();
|
||||
nlohmann::json units = nlohmann::json::array();
|
||||
size_t done = 0, failed = 0;
|
||||
for (const auto& unit : status.units) {
|
||||
@@ -37,9 +37,8 @@ namespace {
|
||||
if (unit.state == eUnitState::FAILED) failed++;
|
||||
nlohmann::json row{ {"kind", KindName(unit.kind)}, {"state", StateName(unit.state)}, {"message", unit.message} };
|
||||
if (unit.kind == eUnitKind::WORLD) {
|
||||
const auto zone = std::to_string(unit.zoneId);
|
||||
row["zone"] = unit.zoneId;
|
||||
row["zoneName"] = unit.zoneId == 0 ? "Character Select" : zones.contains(zone) ? zones[zone].get<std::string>() : "Zone " + zone;
|
||||
row["zoneName"] = GameText::ZoneName(unit.zoneId);
|
||||
row["instance"] = unit.instanceId;
|
||||
row["clone"] = unit.cloneId;
|
||||
row["replacement"] = unit.replacement;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "LiveWorld.h"
|
||||
#include "GameText.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "WorldView.h"
|
||||
#include "Permissions.h"
|
||||
@@ -250,13 +251,13 @@ namespace LiveWorld {
|
||||
nlohmann::json worlds = nlohmann::json::array();
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
for (const auto& w : ServerState::g_WorldInstances) {
|
||||
worlds.push_back({ {"mapID", w.mapID}, {"zoneName", w.zoneName}, {"instanceID", w.instanceID}, {"cloneID", w.cloneID}, {"players", w.players},
|
||||
worlds.push_back({ {"mapID", w.mapID}, {"zoneName", GameText::ZoneName(w.mapID)}, {"instanceID", w.instanceID}, {"cloneID", w.cloneID}, {"players", w.players},
|
||||
{"isPrivate", w.isPrivate}, {"ip", w.ip}, {"port", w.port}, {"propertyId", w.propertyId}, {"propertyName", w.propertyName},
|
||||
{"ownerId", w.ownerId}, {"ownerName", w.ownerName}, {"state", w.state} });
|
||||
}
|
||||
// Launched but not connected yet, or shutting down
|
||||
for (const auto& w : ServerState::g_PendingWorlds) {
|
||||
worlds.push_back({ {"mapID", w.mapID}, {"zoneName", w.zoneName}, {"instanceID", w.instanceID}, {"cloneID", w.cloneID}, {"players", 0},
|
||||
worlds.push_back({ {"mapID", w.mapID}, {"zoneName", GameText::ZoneName(w.mapID)}, {"instanceID", w.instanceID}, {"cloneID", w.cloneID}, {"players", 0},
|
||||
{"isPrivate", w.isPrivate}, {"ip", w.ip}, {"port", w.port}, {"propertyId", w.propertyId}, {"propertyName", w.propertyName},
|
||||
{"ownerId", w.ownerId}, {"ownerName", w.ownerName}, {"state", w.state} });
|
||||
}
|
||||
@@ -266,10 +267,8 @@ namespace LiveWorld {
|
||||
Route(eHTTPMethod::GET, "/api/players/online", Perm("players_view"), "Everyone online: character, account, GM level, world and position",
|
||||
[](HTTPReply& reply, const HTTPContext&) {
|
||||
auto players = PlayersJson();
|
||||
const auto& zones = ZoneNames();
|
||||
for (auto& player : players) {
|
||||
const auto zone = std::to_string(player.value("zone", 0));
|
||||
player["zone_name"] = zones.contains(zone) ? zones[zone].get<std::string>() : "Zone " + zone;
|
||||
player["zone_name"] = GameText::ZoneName(player.value("zone", 0u));
|
||||
const auto id = GeneralUtils::TryParse<LWOOBJID>(player.value("id", std::string{})).value_or(0);
|
||||
if (const auto info = Database::Get()->GetCharacterInfo(id)) {
|
||||
player["account_id"] = info->accountId;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "MissionTools.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
@@ -12,7 +13,6 @@
|
||||
#include "WSRoutes.h"
|
||||
#include "CDClientDatabase.h"
|
||||
#include "Database.h"
|
||||
#include "Locale.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "eHTTPMethod.h"
|
||||
|
||||
@@ -72,12 +72,12 @@ namespace MissionCatalog {
|
||||
}
|
||||
|
||||
std::string Name(uint32_t id) {
|
||||
const auto& name = Locale::GetPhrase("Missions_" + std::to_string(id) + "_name");
|
||||
const auto& name = GameText::Phrase(GameText::Key("Missions", id));
|
||||
return name.empty() ? "Mission " + std::to_string(id) : name;
|
||||
}
|
||||
|
||||
std::string TaskText(uint32_t uid) {
|
||||
return Locale::GetPhrase("MissionTasks_" + std::to_string(uid) + "_description");
|
||||
return GameText::Phrase(GameText::Key("MissionTasks", uid, "description"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ModerationTools.h"
|
||||
#include "GameText.h"
|
||||
#include "ChatFilterWords.h"
|
||||
|
||||
#include <ctime>
|
||||
@@ -73,7 +74,7 @@ namespace {
|
||||
nlohmann::json ReportJson(const IModeration::PlayerReport& r, Names& names) {
|
||||
const auto kind = magic_enum::enum_cast<ePlayerReportKind>(r.kind);
|
||||
const auto status = static_cast<ePlayerReportStatus>(r.status);
|
||||
const auto& zones = ZoneNames();
|
||||
const auto& zones = GameText::ZoneNames();
|
||||
const auto zone = std::to_string(r.zoneId);
|
||||
std::string propertyName;
|
||||
if (r.propertyId) {
|
||||
@@ -387,7 +388,7 @@ namespace {
|
||||
query.blockedOnly = allowed;
|
||||
query.newestFirst = true;
|
||||
query.limit = CHECK_MESSAGES;
|
||||
const auto& zones = ZoneNames();
|
||||
const auto& zones = GameText::ZoneNames();
|
||||
nlohmann::json messages = nlohmann::json::array();
|
||||
uint32_t searched = 0;
|
||||
for (const auto& m : Database::Get()->GetChatMessages(query)) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ModeratorHelper.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <condition_variable>
|
||||
@@ -197,11 +198,6 @@ namespace {
|
||||
return info ? info->name : "";
|
||||
}
|
||||
|
||||
std::string ZoneName(uint32_t zoneId) {
|
||||
const auto& zones = ZoneNames();
|
||||
const auto key = std::to_string(zoneId);
|
||||
return zones.contains(key) && zones[key].is_string() ? zones[key].get<std::string>() : "Zone " + key;
|
||||
}
|
||||
|
||||
ModeratorPrompt::ChatLine Line(const IChatLog::ChatMessage& m, uint64_t subjectId, LWOOBJID subjectSender) {
|
||||
return { m.time, m.channel, m.senderName, m.recipientName, m.message, m.blocked, subjectId ? m.id == subjectId : m.senderId == subjectSender };
|
||||
@@ -291,7 +287,7 @@ namespace {
|
||||
item["sent"] = ModeratorPrompt::FormatTime(report->createdAt);
|
||||
item["reporter"] = CharacterName(report->reporterId);
|
||||
item["reported_player"] = report->targetCharacterId ? CharacterName(report->targetCharacterId) : "unknown";
|
||||
if (report->zoneId) item["where"] = ZoneName(report->zoneId);
|
||||
if (report->zoneId) item["where"] = GameText::ZoneName(report->zoneId);
|
||||
if (report->objectLot && (!kind || *kind != ePlayerReportKind::PLAYER)) item["model"] = ClientAssets::ItemName(report->objectLot);
|
||||
if (report->propertyId) {
|
||||
if (const auto property = Database::Get()->GetPropertyInfo(report->propertyId)) item["property"] = property->name;
|
||||
@@ -339,7 +335,7 @@ namespace {
|
||||
item["sender"] = message->senderName;
|
||||
item["channel"] = message->channel;
|
||||
if (!message->recipientName.empty()) item["to"] = message->recipientName;
|
||||
if (message->zoneId) item["where"] = ZoneName(message->zoneId);
|
||||
if (message->zoneId) item["where"] = GameText::ZoneName(message->zoneId);
|
||||
item["when"] = ModeratorPrompt::FormatTime(message->time);
|
||||
item["message"] = message->message;
|
||||
item["stopped_by_filter"] = message->blocked;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "PublicRoutes.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
@@ -79,7 +80,7 @@ namespace {
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
loginUp = ServerState::g_AuthStatus.online;
|
||||
chatUp = ServerState::g_ChatStatus.online;
|
||||
for (const auto& world : ServerState::g_WorldInstances) worlds.push_back({ world.mapID, world.zoneName, world.players });
|
||||
for (const auto& world : ServerState::g_WorldInstances) worlds.push_back({ world.mapID, GameText::ZoneName(world.mapID), world.players });
|
||||
}
|
||||
|
||||
nlohmann::json status{ {"name", ServerName()}, {"online", loginUp}, {"generated_at", now}, {"refresh_seconds", CacheSeconds()} };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ReportRoutes.h"
|
||||
#include "GameText.h"
|
||||
#include "OnceCache.h"
|
||||
#include <deque>
|
||||
#include <future>
|
||||
@@ -385,8 +386,8 @@ namespace {
|
||||
return stats;
|
||||
}
|
||||
|
||||
// Where a zone's files are (its name comes from the locale: GameText::ZoneName)
|
||||
struct ZoneInfo {
|
||||
std::string name;
|
||||
std::string luzPath; // relative to res/maps
|
||||
};
|
||||
|
||||
@@ -414,11 +415,10 @@ namespace {
|
||||
std::map<uint32_t, ZoneInfo> ReadZoneTable() {
|
||||
std::map<uint32_t, ZoneInfo> zones;
|
||||
try {
|
||||
auto result = CDClientDatabase::ExecuteQuery("SELECT zoneID, zoneName, DisplayDescription FROM ZoneTable;");
|
||||
auto result = CDClientDatabase::ExecuteQuery("SELECT zoneID, zoneName FROM ZoneTable;");
|
||||
for (; !result.eof(); result.nextRow()) {
|
||||
const std::string description = result.getStringField("DisplayDescription", "");
|
||||
const auto zoneId = static_cast<uint32_t>(result.getIntField("zoneID", 0));
|
||||
zones.try_emplace(zoneId, ZoneInfo{ description.empty() ? "Zone " + std::to_string(zoneId) : description, result.getStringField("zoneName", "") });
|
||||
zones.try_emplace(zoneId, ZoneInfo{ result.getStringField("zoneName", "") });
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
LOG("Could not read the zone table: %s", ex.what());
|
||||
@@ -959,7 +959,7 @@ void RegisterReportRoutes() {
|
||||
WithItemNames(history);
|
||||
for (auto& hop : history) {
|
||||
const auto zone = GetZoneInfo(hop.value("zone", 0u));
|
||||
hop["zone_name"] = zone ? zone->name : "";
|
||||
hop["zone_name"] = zone ? GameText::ZoneName(hop.value("zone", 0u)) : "";
|
||||
}
|
||||
const auto names = CharacterNames();
|
||||
std::set<std::string> latest;
|
||||
@@ -1066,7 +1066,7 @@ void RegisterReportRoutes() {
|
||||
if (zone.is_null()) {
|
||||
const auto info = GetZoneInfo(zoneId);
|
||||
const bool property = EconomyPlaces::IsPropertyZone(zoneId);
|
||||
zone = { {"zone", zoneId}, {"name", info ? info->name : "Zone " + std::to_string(zoneId)}, {"events", 0}, {"quantity", 0}, {"property", property} };
|
||||
zone = { {"zone", zoneId}, {"name", GameText::ZoneName(zoneId)}, {"events", 0}, {"quantity", 0}, {"property", property} };
|
||||
if (property) zone["properties"] = nlohmann::json::array();
|
||||
}
|
||||
zone["events"] = zone["events"].get<int64_t>() + events;
|
||||
@@ -1107,7 +1107,7 @@ void RegisterReportRoutes() {
|
||||
}
|
||||
const auto info = GetZoneInfo(*zone);
|
||||
nlohmann::json response{
|
||||
{"zone", *zone}, {"name", info ? info->name : ""}, {"from", from}, {"to", to}, {"lot", lot}, {"clone", clone ? nlohmann::json(*clone) : nlohmann::json()},
|
||||
{"zone", *zone}, {"name", info ? GameText::ZoneName(*zone) : ""}, {"from", from}, {"to", to}, {"lot", lot}, {"clone", clone ? nlohmann::json(*clone) : nlohmann::json()},
|
||||
{"cellSize", static_cast<int>(IEconomyLedger::MAP_CELL_SIZE)}, {"lots", lots}, {"cells", Database::Get()->GetMapCells(*zone, clone, *kind, from, to, lot)}
|
||||
};
|
||||
if (EconomyPlaces::IsPropertyZone(*zone)) {
|
||||
@@ -1159,7 +1159,7 @@ void RegisterReportRoutes() {
|
||||
const auto describe = [&owners](nlohmann::json& row) {
|
||||
const auto zoneId = row.value("zone", 0u);
|
||||
const auto zone = GetZoneInfo(zoneId);
|
||||
row["name"] = zone ? zone->name : "Zone " + std::to_string(zoneId);
|
||||
row["name"] = GameText::ZoneName(zoneId);
|
||||
if (!EconomyPlaces::IsPropertyZone(zoneId)) return;
|
||||
const auto info = owners.Info(zoneId, row.value("clone", 0u));
|
||||
row["property"] = true;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "ServerRoutes.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
@@ -365,7 +366,7 @@ void RegisterServerRoutes() {
|
||||
const auto filter = BundleFilter(reply, context);
|
||||
if (!filter) return;
|
||||
const auto files = LogBundle::Select(LogFolder(), DumpFolder(), *filter);
|
||||
const auto& zoneNames = ZoneNames();
|
||||
const auto& zoneNames = GameText::ZoneNames();
|
||||
nlohmann::json list = nlohmann::json::array();
|
||||
uint64_t total = 0;
|
||||
for (const auto& file : files) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "SettingsRoutes.h"
|
||||
#include "GameText.h"
|
||||
#include "MasterPackets.h"
|
||||
#include "Permissions.h"
|
||||
#include "SettingsCatalog.h"
|
||||
@@ -63,7 +64,8 @@ namespace {
|
||||
{"title", info ? info->title : name}, {"section", info ? info->section : "Custom settings"},
|
||||
{"category", section ? section->category : "other"},
|
||||
// Known settings use the catalog's description (the .ini comments are for people editing the file)
|
||||
{"description", info ? info->description : row ? row->description : ""},
|
||||
// Game names in descriptions are locale keys (%[ZoneTable_1000_DisplayDescription]), shown in the viewer's language
|
||||
{"description", GameText::Expand(info ? info->description : row ? row->description : "")},
|
||||
{"type", info ? SettingsCatalog::TypeName(info->type) : "text"}, {"default", info ? info->defaultValue : ""},
|
||||
{"unit", info ? info->unit : ""}, {"format", info ? SettingsCatalog::FormatName(info->format) : "plain"},
|
||||
{"listOf", info ? SettingsCatalog::ListOfName(info->listOf) : "number"}, {"condition", ConditionJson(info ? info->condition : std::nullopt)},
|
||||
@@ -105,7 +107,7 @@ namespace {
|
||||
// Names for the numbers in a list setting: zones, items or reward codes
|
||||
nlohmann::json ListNames(const std::string& of, const std::vector<int64_t>& ids) {
|
||||
nlohmann::json names = nlohmann::json::object();
|
||||
const auto& zones = ZoneNames();
|
||||
const auto& zones = GameText::ZoneNames();
|
||||
for (const auto id : ids) {
|
||||
const auto key = std::to_string(id);
|
||||
if (of == "zone") {
|
||||
@@ -128,14 +130,14 @@ namespace {
|
||||
nlohmann::json options = nlohmann::json::array();
|
||||
if (of == "zone") {
|
||||
std::vector<std::pair<int64_t, std::string>> zones;
|
||||
for (const auto& [id, name] : ZoneNames().items()) {
|
||||
for (const auto& [id, name] : GameText::ZoneNames().items()) {
|
||||
if (const auto zoneId = GeneralUtils::TryParse<int64_t>(id); zoneId && *zoneId > 0) zones.emplace_back(*zoneId, name.get<std::string>());
|
||||
}
|
||||
std::sort(zones.begin(), zones.end());
|
||||
for (const auto& [id, name] : zones) options.push_back({ {"id", id}, {"label", name}, {"detail", ""} });
|
||||
} else if (of == "reward_code") {
|
||||
// What the client does with the two codes that don't just give an item
|
||||
static const std::map<int64_t, std::string> effects{ { 4, "Opens LEGO Club" }, { 30, "Bricks aren't used up in build mode" } };
|
||||
const std::map<int64_t, std::string> effects{ { 4, "Opens " + GameText::ZoneName(1700) }, { 30, "Bricks aren't used up in build mode" } };
|
||||
auto result = CDClientDatabase::ExecuteQuery("SELECT id, code, attachmentLOT FROM RewardCodes ORDER BY id;");
|
||||
for (; !result.eof(); result.nextRow()) {
|
||||
const int64_t id = result.getIntField(0);
|
||||
@@ -375,11 +377,11 @@ void RegisterSettingsRoutes() {
|
||||
nlohmann::json files = nlohmann::json::array();
|
||||
for (const auto& [file, name] : SettingsCatalog::Files()) files.push_back({ {"file", file}, {"name", name} });
|
||||
nlohmann::json categories = nlohmann::json::array();
|
||||
for (const auto& category : SettingsCatalog::Categories()) categories.push_back({ {"id", category.id}, {"name", category.name}, {"description", category.description} });
|
||||
for (const auto& category : SettingsCatalog::Categories()) categories.push_back({ {"id", category.id}, {"name", category.name}, {"description", GameText::Expand(category.description)} });
|
||||
categories.push_back({ {"id", "other"}, {"name", "Custom"}, {"description", "Settings this page doesn't know, e.g. from a modified server."} });
|
||||
nlohmann::json sections = nlohmann::json::array();
|
||||
for (const auto& section : SettingsCatalog::Sections()) {
|
||||
sections.push_back({ {"category", section.category}, {"name", section.name}, {"description", section.description},
|
||||
sections.push_back({ {"category", section.category}, {"name", section.name}, {"description", GameText::Expand(section.description)},
|
||||
{"layout", SettingsCatalog::LayoutName(section.layout)}, {"condition", ConditionJson(section.condition)} });
|
||||
}
|
||||
sections.push_back({ {"category", "other"}, {"name", "Custom settings"}, {"description", ""}, {"layout", "rows"}, {"condition", nullptr} });
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "Showcase.h"
|
||||
#include "GameText.h"
|
||||
#include "Workers.h"
|
||||
|
||||
#include <chrono>
|
||||
@@ -88,24 +89,19 @@ namespace {
|
||||
return info;
|
||||
}
|
||||
|
||||
std::string ZoneName(uint32_t zoneId) {
|
||||
const auto& zones = ZoneNames();
|
||||
const auto key = std::to_string(zoneId);
|
||||
return zones.contains(key) && zones[key].is_string() ? zones[key].get<std::string>() : "Zone " + key;
|
||||
}
|
||||
|
||||
// The zones properties can be claimed in (PropertyTemplate), for the zone filter
|
||||
const nlohmann::json& PropertyZones() {
|
||||
static const nlohmann::json zones = [] {
|
||||
nlohmann::json list = nlohmann::json::array();
|
||||
nlohmann::json PropertyZones() {
|
||||
static const std::vector<uint32_t> ids = [] {
|
||||
std::vector<uint32_t> ids;
|
||||
auto result = CDClientDatabase::ExecuteQuery("SELECT DISTINCT mapID FROM PropertyTemplate ORDER BY mapID;");
|
||||
for (; !result.eof(); result.nextRow()) {
|
||||
const auto id = static_cast<uint32_t>(result.getIntField(0));
|
||||
list.push_back({ {"id", id}, {"name", ZoneName(id)} });
|
||||
}
|
||||
return list;
|
||||
for (; !result.eof(); result.nextRow()) ids.push_back(static_cast<uint32_t>(result.getIntField(0)));
|
||||
return ids;
|
||||
}();
|
||||
return zones;
|
||||
// Named in the viewer's language each time
|
||||
nlohmann::json list = nlohmann::json::array();
|
||||
for (const auto id : ids) list.push_back({ {"id", id}, {"name", GameText::ZoneName(id)} });
|
||||
return list;
|
||||
}
|
||||
|
||||
std::shared_ptr<const std::map<LWOOBJID, IPropertyContents::Model>> Models(LWOOBJID propertyId) {
|
||||
@@ -139,7 +135,7 @@ namespace {
|
||||
{"description", info.description},
|
||||
{"owner_name", owner ? owner->name : ""},
|
||||
{"zone_id", info.zoneId},
|
||||
{"zone_name", ZoneName(info.zoneId)},
|
||||
{"zone_name", GameText::ZoneName(info.zoneId)},
|
||||
{"reputation", info.reputation},
|
||||
{"last_updated", info.lastUpdatedTime},
|
||||
{"models", models}
|
||||
@@ -233,7 +229,7 @@ void RegisterShowcaseRoutes() {
|
||||
{"description", entry.info.description},
|
||||
{"owner_name", entry.ownerName},
|
||||
{"zone_id", entry.info.zoneId},
|
||||
{"zone_name", ZoneName(entry.info.zoneId)},
|
||||
{"zone_name", GameText::ZoneName(entry.info.zoneId)},
|
||||
{"models", entry.modelCount},
|
||||
{"reputation", entry.info.reputation},
|
||||
{"last_updated", entry.info.lastUpdatedTime}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "NetworkView.h"
|
||||
#include "Permissions.h"
|
||||
#include "RouteUtils.h"
|
||||
#include "GameText.h"
|
||||
#include "ServerState.h"
|
||||
#include "ServiceType.h"
|
||||
#include "TrafficHistory.h"
|
||||
@@ -107,13 +108,10 @@ namespace {
|
||||
case ServiceType::DASHBOARD: return "Dashboard";
|
||||
case ServiceType::UGC: return "UGC";
|
||||
case ServiceType::WORLD: {
|
||||
std::string zoneName;
|
||||
{
|
||||
std::lock_guard lock(ServerState::g_StatusMutex);
|
||||
for (const auto& world : ServerState::g_WorldInstances) {
|
||||
if (world.mapID == server.zoneId && world.instanceID == server.instanceId) zoneName = world.zoneName;
|
||||
}
|
||||
}
|
||||
// The zone's name from the client's locale, in the viewer's language (none for a zone it doesn't name)
|
||||
const auto& names = GameText::ZoneNames();
|
||||
const auto known = names.find(std::to_string(server.zoneId));
|
||||
const std::string zoneName = known != names.end() && known->is_string() ? known->get<std::string>() : "";
|
||||
return "World " + std::to_string(server.zoneId) + (zoneName.empty() ? "" : " " + zoneName) + " #" + std::to_string(server.instanceId);
|
||||
}
|
||||
default: return server.key;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "VanityRoutes.h"
|
||||
#include "GameText.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -31,7 +32,7 @@ namespace {
|
||||
|
||||
// The markdown files the game reads, and where players see them
|
||||
const std::vector<std::pair<std::string, std::string>> TEXTS{
|
||||
{ "TESTAMENT.md", "The plaque by the Nimbus Station launch pad (zone 1200)" },
|
||||
{ "TESTAMENT.md", "The plaque by the %[ZoneTable_1200_DisplayDescription] launch pad (zone 1200)" },
|
||||
{ "CREDITS.md", "Shown by the /credits command" },
|
||||
{ "INFO.md", "Shown by the /info command; __VERSION__, __SOURCE__, __LICENSE__ and __TIMESTAMP__ are filled in" },
|
||||
};
|
||||
@@ -180,9 +181,9 @@ void RegisterVanityRoutes() {
|
||||
files.push_back(entry);
|
||||
}
|
||||
nlohmann::json texts = nlohmann::json::array();
|
||||
for (const auto& [name, where] : TEXTS) texts.push_back({ {"name", name}, {"where", where} });
|
||||
for (const auto& [name, where] : TEXTS) texts.push_back({ {"name", name}, {"where", GameText::Expand(where)} });
|
||||
nlohmann::json zones = nlohmann::json::array();
|
||||
for (const auto& [id, name] : ZoneNames().items()) {
|
||||
for (const auto& [id, name] : GameText::ZoneNames().items()) {
|
||||
if (const auto zone = GeneralUtils::TryParse<uint32_t>(id); zone && *zone > 0) zones.push_back({ {"id", *zone}, {"name", name} });
|
||||
}
|
||||
std::sort(zones.begin(), zones.end(), [](const auto& a, const auto& b) { return a["id"].template get<uint32_t>() < b["id"].template get<uint32_t>(); });
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "WorldView.h"
|
||||
#include "GameText.h"
|
||||
#include "PositionHistory.h"
|
||||
#include "WorldScene.h"
|
||||
#include "Scenery.h"
|
||||
@@ -78,11 +79,6 @@ namespace {
|
||||
|
||||
// ---- Zone data ----
|
||||
|
||||
std::string ZoneName(uint32_t zone) {
|
||||
const auto& names = ZoneNames();
|
||||
const auto key = std::to_string(zone);
|
||||
return names.contains(key) ? names[key].get<std::string>() : "Zone " + key;
|
||||
}
|
||||
|
||||
std::optional<std::string> LuzPath(uint32_t zone) {
|
||||
return ZoneLuzPath(zone);
|
||||
@@ -158,7 +154,7 @@ namespace {
|
||||
}
|
||||
|
||||
nlohmann::json json{
|
||||
{"zone", zone}, {"name", ZoneName(zone)},
|
||||
{"zone", zone}, {"name", GameText::ZoneName(zone)},
|
||||
{"spawn", { Round(zoneFile->spawnpoint.x), Round(zoneFile->spawnpoint.y), Round(zoneFile->spawnpoint.z) }},
|
||||
{"scenes", scenes}, {"kinds", kinds},
|
||||
{"objects", { {"lot", lots}, {"kind", kindOf}, {"flags", flags}, {"scene", sceneOf}, {"pos", positions}, {"names", names} }}, {"lotNames", lotNames},
|
||||
@@ -289,7 +285,7 @@ namespace WorldView {
|
||||
nlohmann::json mapKinds = nlohmann::json::array();
|
||||
for (const auto kind : magic_enum::enum_values<IEconomyLedger::eMapEvent>()) mapKinds.push_back({ {"value", static_cast<int>(kind)}, {"name", GameLabels::Name(kind)} });
|
||||
JsonReply(reply, eHTTPStatusCode::OK, {
|
||||
{"zones", ZoneNames()}, {"worlds", worlds}, {"mapKinds", mapKinds}, {"cellSize", IEconomyLedger::MAP_CELL_SIZE},
|
||||
{"zones", GameText::ZoneNames()}, {"worlds", worlds}, {"mapKinds", mapKinds}, {"cellSize", IEconomyLedger::MAP_CELL_SIZE},
|
||||
{"propertyZones", EconomyPlaces::PropertyZones()},
|
||||
{"today", Now() / DAY_SECONDS}, {"now", Now()},
|
||||
{"history", { {"enabled", Setting("position_history", 1) != 0}, {"days", Setting("position_history_days", 3)},
|
||||
@@ -381,7 +377,7 @@ namespace WorldView {
|
||||
const auto from = QueryInt(context, "from", 0);
|
||||
nlohmann::json instances = nlohmann::json::array();
|
||||
for (const auto& i : Database::Get()->GetPositionInstances(zone, from, to)) {
|
||||
instances.push_back({ {"zone", i.zoneId}, {"zoneName", ZoneName(i.zoneId)}, {"instance", i.instanceId}, {"clone", i.cloneId},
|
||||
instances.push_back({ {"zone", i.zoneId}, {"zoneName", GameText::ZoneName(i.zoneId)}, {"instance", i.instanceId}, {"clone", i.cloneId},
|
||||
{"first", i.first}, {"last", i.last}, {"players", i.players} });
|
||||
}
|
||||
JsonReply(reply, eHTTPStatusCode::OK, { {"instances", instances} });
|
||||
@@ -416,7 +412,7 @@ namespace WorldView {
|
||||
}
|
||||
players.push_back({ {"id", std::to_string(first.characterId)}, {"name", CharacterName(first.characterId)}, {"instances", instances}, {"samples", points} });
|
||||
}
|
||||
Audit(context, "view_position_history", ZoneName(zone) + (instance ? " instance " + std::to_string(instance) : "") + ", " +
|
||||
Audit(context, "view_position_history", GameText::ZoneName(zone) + (instance ? " instance " + std::to_string(instance) : "") + ", " +
|
||||
std::to_string((to - from + 59) / 60) + " minute(s) ending " + std::to_string(to));
|
||||
JsonReply(reply, eHTTPStatusCode::OK, {
|
||||
{"zone", zone}, {"instance", instance}, {"from", from}, {"to", to}, {"bucket", bucket}, {"interval", interval}, {"idleSeconds", IDLE_INTERVAL},
|
||||
|
||||
Reference in New Issue
Block a user