Skip to content

Commit

Permalink
allow specifying what counts as cheating; closes #572
Browse files Browse the repository at this point in the history
  • Loading branch information
fuzziqersoftware committed Oct 18, 2024
1 parent f8e479b commit 21f1c40
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 39 deletions.
88 changes: 49 additions & 39 deletions src/ChatCommands.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,22 +71,25 @@ static void check_debug_enabled(shared_ptr<Client> c) {
}
}

static void check_cheats_enabled(shared_ptr<Lobby> l, shared_ptr<Client> c) {
if (!l->check_flag(Lobby::Flag::CHEATS_ENABLED) &&
static void check_cheats_enabled(shared_ptr<Lobby> l, shared_ptr<Client> c, bool behavior_is_cheating) {
if (behavior_is_cheating &&
!l->check_flag(Lobby::Flag::CHEATS_ENABLED) &&
!c->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE)) {
throw precondition_failed("$C6This command can\nonly be used in\ncheat mode.");
}
}

static void check_cheats_allowed(shared_ptr<ServerState> s, shared_ptr<Client> c) {
if ((s->cheat_mode_behavior == ServerState::BehaviorSwitch::OFF) &&
static void check_cheats_allowed(shared_ptr<ServerState> s, shared_ptr<Client> c, bool behavior_is_cheating) {
if (behavior_is_cheating &&
(s->cheat_mode_behavior == ServerState::BehaviorSwitch::OFF) &&
!c->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE)) {
throw precondition_failed("$C6Cheats are disabled\non this server.");
}
}

static void check_cheats_allowed(shared_ptr<ServerState> s, shared_ptr<ProxyServer::LinkedSession> ses) {
if ((s->cheat_mode_behavior == ServerState::BehaviorSwitch::OFF) &&
static void check_cheats_allowed(shared_ptr<ServerState> s, shared_ptr<ProxyServer::LinkedSession> ses, bool behavior_is_cheating) {
if (behavior_is_cheating &&
(s->cheat_mode_behavior == ServerState::BehaviorSwitch::OFF) &&
(!ses->login || !ses->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE))) {
throw precondition_failed("$C6Cheats are disabled\non this proxy.");
}
Expand Down Expand Up @@ -972,7 +975,9 @@ static void server_command_cheat(shared_ptr<Client> c, const std::string&) {
l->toggle_flag(Lobby::Flag::CHEATS_ENABLED);
send_text_message_printf(l, "Cheat mode %s", l->check_flag(Lobby::Flag::CHEATS_ENABLED) ? "enabled" : "disabled");

if (!l->check_flag(Lobby::Flag::CHEATS_ENABLED) && !c->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE)) {
if (!l->check_flag(Lobby::Flag::CHEATS_ENABLED) &&
!c->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE) &&
s->cheat_flags.insufficient_minimum_level) {
size_t default_min_level = s->default_min_level_for_game(l->base_version, l->episode, l->difficulty);
if (l->min_level < default_min_level) {
l->min_level = default_min_level;
Expand Down Expand Up @@ -1147,7 +1152,8 @@ static void server_command_meseta(shared_ptr<Client> c, const std::string& args)

static void server_command_secid(shared_ptr<Client> c, const std::string& args) {
auto l = c->require_lobby();
check_cheats_allowed(c->require_server_state(), c);
auto s = c->require_server_state();
check_cheats_allowed(s, c, s->cheat_flags.override_section_id);

uint8_t new_override_section_id;

Expand All @@ -1172,7 +1178,9 @@ static void server_command_secid(shared_ptr<Client> c, const std::string& args)
}

static void proxy_command_secid(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
check_cheats_allowed(ses->require_server_state(), ses);
auto s = ses->require_server_state();
check_cheats_allowed(s, ses, s->cheat_flags.override_section_id);

if (!args[0]) {
ses->config.override_section_id = 0xFF;
send_text_message(ses->client_channel, "$C6Override section ID\nremoved");
Expand All @@ -1194,7 +1202,7 @@ static void server_command_variations(shared_ptr<Client> c, const std::string& a
auto s = c->require_server_state();
auto l = c->require_lobby();
check_is_game(l, false);
check_cheats_allowed(s, c);
check_cheats_allowed(s, c, s->cheat_flags.override_variations);

c->override_variations = make_unique<parray<le_uint32_t, 0x20>>();
c->override_variations->clear(0);
Expand All @@ -1207,7 +1215,7 @@ static void server_command_rand(shared_ptr<Client> c, const std::string& args) {
auto s = c->require_server_state();
auto l = c->require_lobby();
check_is_game(l, false);
check_cheats_allowed(s, c);
check_cheats_allowed(s, c, s->cheat_flags.override_random_seed);

if (!args[0]) {
c->config.clear_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED);
Expand All @@ -1221,7 +1229,8 @@ static void server_command_rand(shared_ptr<Client> c, const std::string& args) {
}

static void proxy_command_rand(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
check_cheats_allowed(ses->require_server_state(), ses);
auto s = ses->require_server_state();
check_cheats_allowed(s, ses, s->cheat_flags.override_random_seed);
if (!args[0]) {
ses->config.clear_flag(Client::Flag::USE_OVERRIDE_RANDOM_SEED);
ses->config.override_random_seed = 0;
Expand Down Expand Up @@ -1292,7 +1301,7 @@ static void server_command_min_level(shared_ptr<Client> c, const std::string& ar
auto s = c->require_server_state();
bool cheats_allowed = (l->check_flag(Lobby::Flag::CHEATS_ENABLED) ||
c->login->account->check_flag(Account::Flag::CHEAT_ANYWHERE));
if (!cheats_allowed) {
if (!cheats_allowed && s->cheat_flags.insufficient_minimum_level) {
size_t default_min_level = s->default_min_level_for_game(l->base_version, l->episode, l->difficulty);
if (new_min_level < default_min_level) {
send_text_message_printf(c, "$C6Cannot set minimum\nlevel below %zu", default_min_level + 1);
Expand Down Expand Up @@ -1342,28 +1351,28 @@ static void server_command_edit(shared_ptr<Client> c, const std::string& args) {

try {
auto p = c->character();
if (tokens.at(0) == "atp" && cheats_allowed) {
if (tokens.at(0) == "atp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.atp = stoul(tokens.at(1));
} else if (tokens.at(0) == "mst" && cheats_allowed) {
} else if (tokens.at(0) == "mst" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.mst = stoul(tokens.at(1));
} else if (tokens.at(0) == "evp" && cheats_allowed) {
} else if (tokens.at(0) == "evp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.evp = stoul(tokens.at(1));
} else if (tokens.at(0) == "hp" && cheats_allowed) {
} else if (tokens.at(0) == "hp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.hp = stoul(tokens.at(1));
} else if (tokens.at(0) == "dfp" && cheats_allowed) {
} else if (tokens.at(0) == "dfp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.dfp = stoul(tokens.at(1));
} else if (tokens.at(0) == "ata" && cheats_allowed) {
} else if (tokens.at(0) == "ata" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.ata = stoul(tokens.at(1));
} else if (tokens.at(0) == "lck" && cheats_allowed) {
} else if (tokens.at(0) == "lck" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.char_stats.lck = stoul(tokens.at(1));
} else if (tokens.at(0) == "meseta" && cheats_allowed) {
} else if (tokens.at(0) == "meseta" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.meseta = stoul(tokens.at(1));
} else if (tokens.at(0) == "exp" && cheats_allowed) {
} else if (tokens.at(0) == "exp" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.experience = stoul(tokens.at(1));
} else if (tokens.at(0) == "level" && cheats_allowed) {
} else if (tokens.at(0) == "level" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
p->disp.stats.level = stoul(tokens.at(1)) - 1;
p->recompute_stats(s->level_table(c->version()));
} else if (((tokens.at(0) == "material") || (tokens.at(0) == "mat")) && !is_v1_or_v2(c->version())) {
} else if (((tokens.at(0) == "material") || (tokens.at(0) == "mat")) && !is_v1_or_v2(c->version()) && (cheats_allowed || !s->cheat_flags.reset_materials)) {
if (tokens.at(1) == "reset") {
const auto& which = tokens.at(2);
if (which == "power") {
Expand Down Expand Up @@ -1420,7 +1429,7 @@ static void server_command_edit(shared_ptr<Client> c, const std::string& args) {
sys->language = new_language;
}
} else if (tokens.at(0) == "secid") {
if (!cheats_allowed && (p->disp.stats.level > 0)) {
if (!cheats_allowed && (p->disp.stats.level > 0) && s->cheat_flags.edit_section_id) {
send_text_message(c, "$C6You cannot change\nyour Section ID\nafter level 1");
return;
}
Expand Down Expand Up @@ -1482,7 +1491,7 @@ static void server_command_edit(shared_ptr<Client> c, const std::string& args) {
p->disp.visual.extra_model = npc;
p->disp.visual.validation_flags |= 0x02;
}
} else if (tokens.at(0) == "tech" && cheats_allowed) {
} else if (tokens.at(0) == "tech" && (cheats_allowed || !s->cheat_flags.edit_stats)) {
uint8_t level = stoul(tokens.at(2)) - 1;
if (tokens.at(1) == "all") {
for (size_t x = 0; x < 0x14; x++) {
Expand Down Expand Up @@ -1853,7 +1862,7 @@ static void server_command_warp(shared_ptr<Client> c, const std::string& args, b
auto s = c->require_server_state();
auto l = c->require_lobby();
check_is_game(l, true);
check_cheats_enabled(l, c);
check_cheats_enabled(l, c, s->cheat_flags.warp);

uint32_t floor = stoul(args, nullptr, 0);
if (!is_warpall && (c->floor == floor)) {
Expand Down Expand Up @@ -1885,7 +1894,7 @@ static void server_command_warpall(shared_ptr<Client> c, const std::string& args

static void proxy_command_warp(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args, bool is_warpall) {
auto s = ses->require_server_state();
check_cheats_allowed(s, ses);
check_cheats_allowed(s, ses, s->cheat_flags.warp);
if (!ses->is_in_game) {
send_text_message(ses->client_channel, "$C6You must be in a\ngame to use this\ncommand");
return;
Expand All @@ -1910,7 +1919,7 @@ static void server_command_next(shared_ptr<Client> c, const std::string&) {
auto s = c->require_server_state();
auto l = c->require_lobby();
check_is_game(l, true);
check_cheats_enabled(l, c);
check_cheats_enabled(l, c, s->cheat_flags.warp);

size_t limit = floor_limit_for_episode(l->episode);
if (limit == 0) {
Expand All @@ -1921,7 +1930,7 @@ static void server_command_next(shared_ptr<Client> c, const std::string&) {

static void proxy_command_next(shared_ptr<ProxyServer::LinkedSession> ses, const std::string&) {
auto s = ses->require_server_state();
check_cheats_allowed(s, ses);
check_cheats_allowed(s, ses, s->cheat_flags.warp);
if (!ses->is_in_game) {
send_text_message(ses->client_channel, "$C6You must be in a\ngame to use this\ncommand");
return;
Expand Down Expand Up @@ -2022,7 +2031,7 @@ static void server_command_infinite_hp(shared_ptr<Client> c, const std::string&)
auto s = c->require_server_state();
auto l = c->require_lobby();
check_is_game(l, true);
check_cheats_enabled(l, c);
check_cheats_enabled(l, c, s->cheat_flags.infinite_hp_tp);

c->config.toggle_flag(Client::Flag::INFINITE_HP_ENABLED);
bool enabled = c->config.check_flag(Client::Flag::INFINITE_HP_ENABLED);
Expand All @@ -2034,7 +2043,7 @@ static void server_command_infinite_hp(shared_ptr<Client> c, const std::string&)

static void proxy_command_infinite_hp(shared_ptr<ProxyServer::LinkedSession> ses, const std::string&) {
auto s = ses->require_server_state();
check_cheats_allowed(s, ses);
check_cheats_allowed(s, ses, s->cheat_flags.infinite_hp_tp);
ses->config.toggle_flag(Client::Flag::INFINITE_HP_ENABLED);
bool enabled = ses->config.check_flag(Client::Flag::INFINITE_HP_ENABLED);
send_text_message_printf(ses->client_channel, "$C6Infinite HP %s", enabled ? "enabled" : "disabled");
Expand All @@ -2048,15 +2057,15 @@ static void server_command_infinite_tp(shared_ptr<Client> c, const std::string&)
auto s = c->require_server_state();
auto l = c->require_lobby();
check_is_game(l, true);
check_cheats_enabled(l, c);
check_cheats_enabled(l, c, s->cheat_flags.infinite_hp_tp);

c->config.toggle_flag(Client::Flag::INFINITE_TP_ENABLED);
send_text_message_printf(c, "$C6Infinite TP %s", c->config.check_flag(Client::Flag::INFINITE_TP_ENABLED) ? "enabled" : "disabled");
}

static void proxy_command_infinite_tp(shared_ptr<ProxyServer::LinkedSession> ses, const std::string&) {
auto s = ses->require_server_state();
check_cheats_allowed(s, ses);
check_cheats_allowed(s, ses, s->cheat_flags.infinite_hp_tp);
ses->config.toggle_flag(Client::Flag::INFINITE_TP_ENABLED);
send_text_message_printf(ses->client_channel, "$C6Infinite TP %s",
ses->config.check_flag(Client::Flag::INFINITE_TP_ENABLED) ? "enabled" : "disabled");
Expand Down Expand Up @@ -2146,7 +2155,8 @@ static void server_command_dropmode(shared_ptr<Client> c, const std::string& arg
}

static void proxy_command_dropmode(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
check_cheats_allowed(ses->require_server_state(), ses);
auto s = ses->require_server_state();
check_cheats_allowed(ses->require_server_state(), ses, s->cheat_flags.proxy_override_drops);

using DropMode = ProxyServer::LinkedSession::DropMode;
if (args.empty()) {
Expand Down Expand Up @@ -2194,7 +2204,7 @@ static void server_command_item(shared_ptr<Client> c, const std::string& args) {
auto s = c->require_server_state();
auto l = c->require_lobby();
check_is_game(l, true);
check_cheats_enabled(l, c);
check_cheats_enabled(l, c, s->cheat_flags.create_items);

ItemData item = s->parse_item_description(c->version(), args);
item.id = l->generate_item_id(c->lobby_client_id);
Expand All @@ -2213,7 +2223,7 @@ static void server_command_item(shared_ptr<Client> c, const std::string& args) {

static void proxy_command_item(shared_ptr<ProxyServer::LinkedSession> ses, const std::string& args) {
auto s = ses->require_server_state();
check_cheats_allowed(s, ses);
check_cheats_allowed(s, ses, s->cheat_flags.create_items);
if (ses->version() == Version::BB_V4) {
send_text_message(ses->client_channel, "$C6This command cannot\nbe used on the proxy\nserver in BB games");
return;
Expand Down Expand Up @@ -2379,7 +2389,7 @@ static void server_command_ep3_replace_assist_card(shared_ptr<Client> c, const s
auto l = c->require_lobby();
check_is_game(l, true);
check_is_ep3(c, true);
check_cheats_enabled(l, c);
check_cheats_enabled(l, c, s->cheat_flags.ep3_replace_assist);

if (l->episode != Episode::EP3) {
throw logic_error("non-Ep3 client in Ep3 game");
Expand Down Expand Up @@ -2431,7 +2441,7 @@ static void server_command_ep3_unset_field_character(shared_ptr<Client> c, const
auto l = c->require_lobby();
check_is_game(l, true);
check_is_ep3(c, true);
check_cheats_enabled(l, c);
check_cheats_enabled(l, c, s->cheat_flags.ep3_unset_field_character);

if (l->episode != Episode::EP3) {
throw logic_error("non-Ep3 client in Ep3 game");
Expand Down
27 changes: 27 additions & 0 deletions src/ServerState.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,27 @@

using namespace std;

CheatFlags::CheatFlags(const phosg::JSON& json) : CheatFlags() {
unordered_set<std::string> enabled_keys;
for (const auto& it : json.as_list()) {
enabled_keys.emplace(it->as_string());
}

this->create_items = enabled_keys.count("CreateItems");
this->edit_section_id = enabled_keys.count("EditSectionID");
this->edit_stats = enabled_keys.count("EditStats");
this->ep3_replace_assist = enabled_keys.count("Ep3ReplaceAssist");
this->ep3_unset_field_character = enabled_keys.count("Ep3UnsetFieldCharacter");
this->infinite_hp_tp = enabled_keys.count("InfiniteHPTP");
this->insufficient_minimum_level = enabled_keys.count("InsufficientMinimumLevel");
this->override_random_seed = enabled_keys.count("OverrideRandomSeed");
this->override_section_id = enabled_keys.count("OverrideSectionID");
this->override_variations = enabled_keys.count("OverrideVariations");
this->proxy_override_drops = enabled_keys.count("ProxyOverrideDrops");
this->reset_materials = enabled_keys.count("ResetMaterials");
this->warp = enabled_keys.count("Warp");
}

ServerState::QuestF960Result::QuestF960Result(const phosg::JSON& json, shared_ptr<const ItemNameIndex> name_index) {
static const array<string, 7> day_names = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
this->meseta_cost = json.get_int("MesetaCost", 0);
Expand Down Expand Up @@ -1207,6 +1228,12 @@ void ServerState::load_config_early() {
} catch (const out_of_range&) {
}

try {
this->cheat_flags = CheatFlags(this->config_json->at("CheatingBehaviors"));
} catch (const out_of_range&) {
this->cheat_flags = CheatFlags();
}

this->update_dependent_server_configs();
}

Expand Down
24 changes: 24 additions & 0 deletions src/ServerState.hh
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,29 @@ struct PortConfiguration {
ServerBehavior behavior;
};

struct CheatFlags {
// This structure describes which behaviors are considered cheating (that is,
// require cheat mode to be enabled or the user to have the CHEAT_ANYWHERE
// account flag). A false value here means that that particular behavior is
// NOT cheating, so cheat mode is NOT required.
bool create_items = true;
bool edit_section_id = true;
bool edit_stats = true;
bool ep3_replace_assist = true;
bool ep3_unset_field_character = true;
bool infinite_hp_tp = true;
bool insufficient_minimum_level = true;
bool override_random_seed = true;
bool override_section_id = true;
bool override_variations = true;
bool proxy_override_drops = true;
bool reset_materials = false;
bool warp = true;

CheatFlags() = default;
explicit CheatFlags(const phosg::JSON& json);
};

struct ServerState : public std::enable_shared_from_this<ServerState> {
enum class RunShellBehavior {
DEFAULT = 0,
Expand Down Expand Up @@ -191,6 +214,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const Map::RareEnemyRates> rare_enemy_rates_challenge;
std::array<std::array<size_t, 4>, 3> min_levels_v4; // Indexed as [episode][difficulty]
std::vector<std::string> bb_required_patches;
CheatFlags cheat_flags;

struct QuestF960Result {
uint32_t meseta_cost = 0;
Expand Down
19 changes: 19 additions & 0 deletions system/config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,25 @@
// available on the proxy server.
"CheatModeBehavior": "OnByDefault",

// Cheat mode behaviors. The keys present in this list determine what the
// server considers to be cheating. If you delete or comment out an item
// here, the server will allow that action even when cheat mode is off.
"CheatingBehaviors": [
"CreateItems", // Use of the $item command
"EditSectionID", // Use of $edit secid
"EditStats", // Use of $edit atp, etc.
"Ep3ReplaceAssist", // Use of $setassist
"Ep3UnsetFieldCharacter", // Use of $unset
"InfiniteHPTP", // Use of $infhp and $inftp
"InsufficientMinimumLevel", // Setting a $minlevel below the default
"OverrideRandomSeed", // Use of $rand
"OverrideSectionID", // Use of $secid
"OverrideVariations", // Use of $variations
"ProxyOverrideDrops", // Use of $dropmode on proxy server
// "ResetMaterials", // Use of $edit mat reset (by default, not cheating)
"Warp", // Use of $warp
],

// Default switch assist behavior. Players can always toggle switch assist
// with the $swa command; this only controls whether it's enabled by default
// for all players or not.
Expand Down
Loading

0 comments on commit 21f1c40

Please sign in to comment.