<URL: http://bugs.freeciv.org/Ticket/Display.html?id=39678 >
This adds checking of requirement list sanity to ruleset load. S2_1 version. Currently outputs only number of conflicting requirement type (not very user-friendly error message). Functionality to access requirement type names outside requirements.c is needed before this can be fixed (and this should be fixed before committing) - ML
diff -Nurd -X.diff_ignore freeciv/common/effects.c freeciv/common/effects.c --- freeciv/common/effects.c 2007-08-21 21:41:47.000000000 +0300 +++ freeciv/common/effects.c 2007-09-07 03:38:15.000000000 +0300 @@ -1019,3 +1019,21 @@ } requirement_list_iterate_end; } +/************************************************************************** + Iterate through all the effects in cache, and call callback for each. + This is currently not very generic implementation, as we have only one user; + ruleset sanity checking. If any callback returns FALSE, there is no + further checking and this will return FALSE. +**************************************************************************/ +bool iterate_effect_cache(iec_cb cb) +{ + assert(cb != NULL); + + effect_list_iterate(ruleset_cache.tracker, peffect) { + if (!cb(peffect)) { + return FALSE; + } + } effect_list_iterate_end; + + return TRUE; +} diff -Nurd -X.diff_ignore freeciv/common/effects.h freeciv/common/effects.h --- freeciv/common/effects.h 2007-08-13 22:23:46.000000000 +0300 +++ freeciv/common/effects.h 2007-09-07 02:22:43.000000000 +0300 @@ -223,5 +223,7 @@ Impr_type_id ai_find_source_building(struct player *pplayer, enum effect_type effect_type); -#endif /* FC__EFFECTS_H */ +typedef bool (*iec_cb)(const struct effect*); +bool iterate_effect_cache(iec_cb cb); +#endif /* FC__EFFECTS_H */ diff -Nurd -X.diff_ignore freeciv/server/ruleset.c freeciv/server/ruleset.c --- freeciv/server/ruleset.c 2007-09-01 04:51:56.000000000 +0300 +++ freeciv/server/ruleset.c 2007-09-07 03:35:49.000000000 +0300 @@ -32,6 +32,7 @@ #include "nation.h" #include "packets.h" #include "registry.h" +#include "requirements.h" #include "shared.h" #include "support.h" #include "tech.h" @@ -3265,11 +3266,128 @@ } /************************************************************************** + Check if requirement list is free of conflicting requirements. + max_tiles is number of tiles that can provide requirement. Value -1 + disables checking based on number of tiles. + + Returns TRUE iff everything ok. +**************************************************************************/ +static bool sanity_check_req_list(const struct requirement_list *preqs, + int max_tiles, + const char *list_for) +{ + int reqs_of_type[REQ_LAST]; + int i; + + /* Initialize requirement counters */ + for (i = 0; i < REQ_LAST; i++) { + reqs_of_type[i] = 0; + } + + requirement_list_iterate(preqs, preq) { + int rc; + + /* Add to counter */ + reqs_of_type[preq->source.type]++; + rc = reqs_of_type[preq->source.type]; + + if (rc > 1) { + /* Multiple requirements of same the type */ + switch (preq->source.type) { + case REQ_GOV: + case REQ_NATION: + case REQ_UNITTYPE: + case REQ_UNITCLASS: + case REQ_OUTPUTTYPE: + case REQ_SPECIALIST: + case REQ_MINSIZE: /* Breaks nothing, but has no sense either */ + /* There can be only one requirement of these types + * Requirements might be identical, but we consider multiple + * declarations error anyway. */ + + /* TODO: Get real requirement type text instead of just type number. */ + freelog(LOG_ERROR, + "%s: Requirement list has multiple %d type requirements", + list_for, preq->source.type); + return FALSE; + break; + + case REQ_SPECIAL: + case REQ_TERRAIN: + /* There can be only up to max_tiles requirements of these types */ + if (max_tiles != 1 && rc > max_tiles) { + freelog(LOG_ERROR, + "%s: Requirement list has more %d type requirements than " + "can ever be fullfilled.", + list_for, preq->source.type); + return FALSE; + } + break; + + case REQ_NONE: + case REQ_TECH: + case REQ_BUILDING: + case REQ_UNITFLAG: + /* Can have multiple requirements of these types */ + break; + } + } + } requirement_list_iterate_end; + + return TRUE; +} + +/************************************************************************** + Check that requirement vector and negated requirements vector do not have + confliciting requirements. + + Returns TRUE iff everything ok. +**************************************************************************/ +static bool sanity_check_req_nreq_list(const struct requirement_list *preqs, + const struct requirement_list *pnreqs, + int one_tile, + const char *list_for) +{ + /* Check internal sanity of requirement list */ + if (!sanity_check_req_list(preqs, one_tile, list_for)) { + return FALSE; + } + + /* There is no pnreqs in all cases */ + if (pnreqs != NULL) { + /* Check sanity between reqs and nreqs */ + requirement_list_iterate(preqs, preq) { + requirement_list_iterate(pnreqs, pnreq) { + if (are_requirements_equal(preq, pnreq)) { + freelog(LOG_ERROR, + "%s: Identical requirement in requirements and negated requirements."); + return FALSE; + } + } requirement_list_iterate_end; + } requirement_list_iterate_end; + } + + return TRUE; +} + +/************************************************************************** + Sanity check callback for iterating effects cache. +**************************************************************************/ +static bool effect_sanity_cb(const struct effect *peffect) +{ + int one_tile = -1; /* TODO: Determine correct value from effect. + * -1 disables checking */ + + return sanity_check_req_nreq_list(peffect->reqs, peffect->nreqs, one_tile, + effect_type_name(peffect->type)); +} + +/************************************************************************** Some more sanity checking once all rulesets are loaded. These check for some cross-referencing which was impossible to do while only one party was loaded in load_ruleset_xxx() - Returns TRUE if everything ok. + Returns TRUE iff everything ok. **************************************************************************/ static bool sanity_check_ruleset_data(void) { @@ -3340,5 +3458,10 @@ } } unit_type_iterate_end; + /* Check requirement lists against conflicting requirements */ + if (!iterate_effect_cache(effect_sanity_cb)) { + exit(EXIT_FAILURE); + } + return TRUE; }
_______________________________________________ Freeciv-dev mailing list Freeciv-dev@gna.org https://mail.gna.org/listinfo/freeciv-dev