<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

Reply via email to