-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathOrder.cpp
More file actions
155 lines (135 loc) · 5.86 KB
/
Order.cpp
File metadata and controls
155 lines (135 loc) · 5.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// This file is part of ClassicAPI.
//
// ClassicAPI is free software: you can redistribute it and/or modify it under the terms
// of the GNU Lesser General Public License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// ClassicAPI is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with
// ClassicAPI. If not, see <https://www.gnu.org/licenses/>.
// `GetSavedCharacterOrder(realm)` / `SetSavedCharacterOrder(realm, order)`
// — glue-only globals for persisting the character-select reorder UI
// across sessions. Storage lives in the shared per-account settings
// file via `Settings::Account` (`WTF\Account\<account>\ClassicAPI.txt`),
// keyed by `CharacterOrder.<realm>=<opaque order>` lines.
//
// The `<order>` payload is opaque to this module — the Lua caller
// decides the encoding (the current GlueXML patch uses pipe-delimited
// character names).
//
// Account scoping is implicit via the file path (the engine writes
// `VAR_ACCOUNT_NAME_PTR` after auth response, so it's populated by the
// time the user lands on character-select). Realm scoping is explicit
// via the prefixed key: multiple realms under one account each get an
// independent line. Vanilla 1.12 glue has no general-purpose persistence
// API — only the autologin-saturated `GetSavedAccountName`/`Set` pair —
// so we layer onto our own settings file format.
//
// Registered on the **glue** Lua state only. In-world Lua sees a nil
// global; `/script GetSavedCharacterOrder("X")` from inside the world
// will error, by design — per TODO #94 the binding should not exist
// outside the screens that need it.
#include "Game.h"
#include "settings/Account.h"
#include <map>
#include <ostream>
#include <string>
namespace Charlist::Order {
namespace {
constexpr const char *kKeyPrefix = "CharacterOrder.";
// In-memory mirror of the on-disk file's `CharacterOrder.*` lines for
// the currently-loaded account. The `Settings::Account` registry owns
// the load/save cycle and the account-change detection; this module
// just exposes Reset/Serialize/Parse callbacks so the registry knows
// how to round-trip our slice of the file.
std::map<std::string, std::string> g_orders;
// Account-settings registry hooks ---------------------------------------
void SettingsReset() {
g_orders.clear();
}
void SettingsSerialize(std::ostream &out) {
for (const auto &kv : g_orders) {
if (kv.second.empty())
continue; // skip cleared entries
out << kKeyPrefix << kv.first << '=' << kv.second << '\n';
}
}
bool SettingsParse(const std::string &key, const std::string &value) {
constexpr std::size_t prefixLen = 15; // strlen("CharacterOrder.")
if (key.compare(0, prefixLen, kKeyPrefix) != 0)
return false;
std::string realm = key.substr(prefixLen);
if (realm.empty())
return true; // claim the key even if malformed — drop the value
g_orders[std::move(realm)] = value;
return true;
}
const Settings::Account::AutoRegister _accountSettings{
&SettingsReset, &SettingsSerialize, &SettingsParse};
// Lua surface -----------------------------------------------------------
// CR/LF inside either arg would corrupt the line-oriented file format
// on the next save (a key with an embedded newline would parse as two
// half-lines on reload). Reject up-front rather than sanitize — the
// Lua caller can do its own escaping if it ever has multi-line names.
bool HasFileBreakChars(const char *s) {
for (const char *p = s; *p != '\0'; ++p)
if (*p == '\r' || *p == '\n')
return true;
return false;
}
// `GetSavedCharacterOrder(realm) -> string`. Always returns a string
// (never nil): empty for unknown realm, missing/non-string arg,
// pre-login (no account name), or unreadable file. Per TODO #94 the
// Lua side relies on `""` as the "first-run / cleared" sentinel.
int __fastcall Script_GetSavedCharacterOrder(void *L) {
Settings::Account::EnsureLoaded();
if (!Game::Lua::IsString(L, 1)) {
Game::Lua::PushString(L, "");
return 1;
}
const char *realm = Game::Lua::ToString(L, 1);
if (realm == nullptr || realm[0] == '\0') {
Game::Lua::PushString(L, "");
return 1;
}
const auto it = g_orders.find(realm);
Game::Lua::PushString(L,
it != g_orders.end() ? it->second.c_str() : "");
return 1;
}
// `SetSavedCharacterOrder(realm, order)`. No return value. Empty
// `order` clears the realm's entry (removes the line on next save).
// Silent no-op when `realm` is missing/empty, when either arg
// contains a CR/LF, or when the account name isn't available yet.
int __fastcall Script_SetSavedCharacterOrder(void *L) {
Settings::Account::EnsureLoaded();
if (!Game::Lua::IsString(L, 1) || !Game::Lua::IsString(L, 2))
return 0;
const char *realm = Game::Lua::ToString(L, 1);
const char *order = Game::Lua::ToString(L, 2);
if (realm == nullptr || realm[0] == '\0')
return 0;
if (order == nullptr)
order = "";
if (HasFileBreakChars(realm) || HasFileBreakChars(order))
return 0;
if (order[0] == '\0') {
g_orders.erase(realm);
} else {
g_orders[realm] = order;
}
Settings::Account::Save();
return 0;
}
void RegisterGlue() {
Game::Lua::RegisterGlueFunction("GetSavedCharacterOrder",
&Script_GetSavedCharacterOrder);
Game::Lua::RegisterGlueFunction("SetSavedCharacterOrder",
&Script_SetSavedCharacterOrder);
}
const Game::GlueModuleAutoRegister _autoreg{&RegisterGlue};
} // namespace
} // namespace Charlist::Order