From 51836a1c45564c87ca409cbddd8dc0c2ba721557 Mon Sep 17 00:00:00 2001 From: Alina Lenk Date: Sun, 27 Mar 2022 13:23:23 +0200 Subject: [PATCH 2/2] Sanity check: Only allow 'singlepole' requirements when also checking 'alltemperate' See osdn #44181 Signed-off-by: Alina Lenk --- server/rscompat.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ server/rscompat.h | 5 ++++ server/rssanity.c | 55 ++++++++++++++++++++++++++++++++++++++ server/ruleset.c | 5 ++++ 4 files changed, 133 insertions(+) diff --git a/server/rscompat.c b/server/rscompat.c index c4cf214e62..2e7608cdca 100644 --- a/server/rscompat.c +++ b/server/rscompat.c @@ -36,6 +36,7 @@ /* server */ #include "rssanity.h" #include "ruleset.h" +#include "settings.h" #include "rscompat.h" @@ -2048,6 +2049,73 @@ const char *rscompat_req_name_3_1(const char *type, return old_name; } +/**********************************************************************//** + Modify requirement vectors in ways that require looking at the entire + vector, rather than just individual requirements. +**************************************************************************/ +void rscompat_req_vec_adjust_3_1(struct rscompat_info *compat, + struct requirement_vector *preqs, + int *reqs_count, + const char *filename, const char *sec, + const char *sub, const char *rfor) +{ + char buf[1024]; + + /* Add a missing 'alltemperate' server setting requirement to any vector + * including a 'singlepole' requirement. + * See also sanity_check_req_vec_singlepole() in rssanity.c + * + * Note that this *does* change the semantics of the given vector, so we + * must inform the user of this change. */ + if (compat->compat_mode && compat->version < RSFORMAT_3_1) { + /* heuristic: the only non-conjunctive requirement vectors are + * improvement obsoletion requirements */ + bool conjunctive = BOOL_VAL(fc_strcasecmp(sub, "obsolete_by")); + bool need_alltemperate_req = FALSE; + + requirement_vector_iterate(preqs, preq) { + server_setting_id id; + struct setting *pset; + + if (preq->source.kind != VUT_SERVERSETTING) { + continue; + } + + id = ssetv_setting_get(preq->source.value.ssetval); + fc_assert_ret(server_setting_exists(id)); + pset = setting_by_number(id); + + if (pset == setting_by_name("singlepole")) { + need_alltemperate_req = TRUE; + } else if (pset == setting_by_name("alltemperate") + && XOR(conjunctive, preq->present)) { + need_alltemperate_req = FALSE; + break; + } + } requirement_vector_iterate_end; + + if (need_alltemperate_req) { + struct requirement new_req = req_from_values(VUT_SERVERSETTING, + REQ_RANGE_WORLD, FALSE, !conjunctive, FALSE, + ssetv_by_rule_name("alltemperate")); + + requirement_vector_append(preqs, new_req); + (*reqs_count)++; + + if (compat->log_cb != NULL) { + fc_snprintf(buf, sizeof(buf), + "%s: Added 'alltemperate' server setting requirement to" + " requirement vector %s.%s (for %s) so that current" + " 'singlepole' requirement is only relevant when" + " 'alltemperate' is disabled. This likely changes the" + " semantics; make sure the new rules are sensible.", + filename, sec, sub, rfor); + compat->log_cb(buf); + } + } + } +} + /**********************************************************************//** Replace deprecated unit type flag names with currently valid ones. **************************************************************************/ diff --git a/server/rscompat.h b/server/rscompat.h index 82354acc33..41353747be 100644 --- a/server/rscompat.h +++ b/server/rscompat.h @@ -64,6 +64,11 @@ bool rscompat_auto_attack_3_1(struct rscompat_info *compat, const char *rscompat_req_type_name_3_1(const char *old_type); const char *rscompat_req_name_3_1(const char *type, const char *old_name); +void rscompat_req_vec_adjust_3_1(struct rscompat_info *compat, + struct requirement_vector *preqs, + int *reqs_count, + const char *filename, const char *sec, + const char *sub, const char *rfor); const char *rscompat_utype_flag_name_3_1(struct rscompat_info *info, const char *old_type); const char *rscompat_combat_bonus_name_3_1(struct rscompat_info *compat, diff --git a/server/rssanity.c b/server/rssanity.c index 8ee21b9762..222c6e6308 100644 --- a/server/rssanity.c +++ b/server/rssanity.c @@ -394,6 +394,57 @@ static bool sanity_check_req_set(int reqs_of_type[], int local_reqs_of_type[], return TRUE; } +/**********************************************************************//** + Helper function: Sanity check potential 'singlepole' server setting + requirement in a requirement vector. + 'conjunctive' should be TRUE if the vector is an AND vector (all + requirements must be active), FALSE if it's a disjunctive (OR) vector. + + Returns TRUE iff everything ok. +**************************************************************************/ +static bool +sanity_check_req_vec_singlepole(const struct requirement_vector *preqs, + bool conjunctive, const char *list_for) +{ + bool has_singlepole_req = FALSE; + + requirement_vector_iterate(preqs, preq) { + server_setting_id id; + struct setting *pset; + + if (preq->source.kind != VUT_SERVERSETTING) { + continue; + } + + id = ssetv_setting_get(preq->source.value.ssetval); + fc_assert_ret_val(server_setting_exists(id), FALSE); + pset = setting_by_number(id); + + if (pset == setting_by_name("singlepole")) { + has_singlepole_req = TRUE; + } else if (pset == setting_by_name("alltemperate") + && XOR(conjunctive, preq->present)) { + return TRUE; + } + } requirement_vector_iterate_end; + + if (!has_singlepole_req) { + /* all good */ + return TRUE; + } + + if (conjunctive) { + log_error("%s: Requirement list containing 'singlepole' server" + " setting requirement must also have negated (!present)" + " 'alltemperate' requirement", list_for); + } else { + log_error("%s: Disjunctive requirement list containing 'singlepole'" + " server setting requirement must also have present" + " 'alltemperate' requirement", list_for); + } + return FALSE; +} + /**********************************************************************//** Sanity check requirement vector, including whether it's free of conflicting requirements. @@ -429,6 +480,10 @@ static bool sanity_check_req_vec(const struct requirement_vector *preqs, } } requirement_vector_iterate_end; + if (!sanity_check_req_vec_singlepole(preqs, conjunctive, list_for)) { + return FALSE; + } + problem = req_vec_suggest_repair(preqs, req_vec_vector_number, preqs); if (problem != NULL) { log_error("%s: %s.", list_for, problem->description); diff --git a/server/ruleset.c b/server/ruleset.c index 16022d6823..b05069fd75 100644 --- a/server/ruleset.c +++ b/server/ruleset.c @@ -772,6 +772,11 @@ struct requirement_vector *lookup_req_list(struct section_file *file, requirement_vector_append(&reqs_list, req); } + if (compat->compat_mode) { + rscompat_req_vec_adjust_3_1(compat, &reqs_list, &j, + filename, sec, sub, rfor); + } + if (j > MAX_NUM_REQS) { ruleset_error(LOG_ERROR, "Too many (%d) requirements for %s. Max is %d", j, rfor, MAX_NUM_REQS); -- 2.17.1