Freeciv-3.3: /fast/freeciv/nightly/main/server/ruleset/rscompat.c Source File

Freeciv-3.3

Loading...
Searching...
No Matches
rscompat.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18/* ANSI */
19#ifdef HAVE_STRING_H
20#include <string.h>
21#endif
22
23/* utility */
24#include "capability.h"
25#include "log.h"
26#include "registry.h"
27
28/* common */
29#include "actions.h"
30#include "effects.h"
31#include "fc_types.h"
32#include "game.h"
33#include "movement.h"
34#include "requirements.h"
35#include "unittype.h"
36
37/* server */
38#include "rssanity.h"
39#include "ruleload.h"
40#include "settings.h"
41
42#include "rscompat.h"
43
44#define enough_new_user_flags(_new_flags_, _name_, \
45 _LAST_USER_FLAG_, _LAST_USER_FLAG_PREV_) \
46FC_STATIC_ASSERT((ARRAY_SIZE(_new_flags_) \
47 <= _LAST_USER_FLAG_ - _LAST_USER_FLAG_PREV_), \
48 not_enough_new_##_name_##_user_flags)
49
50#define UTYF_LAST_USER_FLAG_3_2 UTYF_USER_FLAG_50
51
52static int first_free_unit_type_user_flag(void);
53
54/**********************************************************************/
57 void rscompat_init_info(struct rscompat_info *info)
58{
59 memset(info, 0, sizeof(*info));
60}
61
62/**********************************************************************/
66 int rscompat_check_capabilities(struct section_file *file,
67 const char *filename,
68 const struct rscompat_info *info)
69{
70 const char *datafile_options;
71 bool ok = FALSE;
72 int format;
73
74 if (!(datafile_options = secfile_lookup_str(file, "datafile.options"))) {
75 log_fatal("\"%s\": ruleset capability problem:", filename);
76 ruleset_error(NULL, LOG_ERROR, "%s", secfile_error());
77
78 return 0;
79 }
80
81 if (info->compat_mode) {
82 /* Check alternative capstr first, so that when we do the main capstr check,
83 * we already know that failures there are fatal (error message correct, can return
84 * immediately) */
85
86 if (has_capabilities(RULESET_COMPAT_CAP, datafile_options)
87 && has_capabilities(datafile_options, RULESET_COMPAT_CAP)) {
88 ok = TRUE;
89 }
90 }
91
92 if (!ok) {
93 if (!has_capabilities(RULESET_CAPABILITIES, datafile_options)) {
94 log_fatal("\"%s\": ruleset datafile appears incompatible:", filename);
95 log_fatal(" datafile options: %s", datafile_options);
96 log_fatal(" supported options: %s", RULESET_CAPABILITIES);
97 ruleset_error(NULL, LOG_ERROR, "Capability problem");
98
99 return 0;
100 }
101 if (!has_capabilities(datafile_options, RULESET_CAPABILITIES)) {
102 log_fatal("\"%s\": ruleset datafile claims required option(s)"
103 " that we don't support:", filename);
104 log_fatal(" datafile options: %s", datafile_options);
105 log_fatal(" supported options: %s", RULESET_CAPABILITIES);
106 ruleset_error(NULL, LOG_ERROR, "Capability problem");
107
108 return 0;
109 }
110 }
111
112 if (!secfile_lookup_int(file, &format, "datafile.format_version")) {
113 log_error("\"%s\": lacking legal format_version field", filename);
114 ruleset_error(NULL, LOG_ERROR, "%s", secfile_error());
115
116 return 0;
117 } else if (format == 0) {
118 log_error("\"%s\": Illegal format_version value", filename);
119 ruleset_error(NULL, LOG_ERROR, "Format version error");
120 }
121
122 return format;
123}
124/**********************************************************************/
131 bool rscompat_check_cap_and_version(struct section_file *file,
132 const char *filename,
133 const struct rscompat_info *info)
134{
135 int format_version;
136
137 fc_assert_ret_val(info->version > 0, FALSE);
138
139 format_version = rscompat_check_capabilities(file, filename, info);
140 if (format_version <= 0) {
141 /* Already logged in rscompat_check_capabilities */
142 return FALSE;
143 }
144
145 if (format_version != info->version) {
146 log_fatal("\"%s\": ruleset datafile format version differs from"
147 " other ruleset datafile(s):", filename);
148 log_fatal(" datafile format version: %d", format_version);
149 log_fatal(" expected format version: %d", info->version);
150 ruleset_error(NULL, LOG_ERROR, "Inconsistent format versions");
151
152 return FALSE;
153 }
154
155 return TRUE;
156}
157
158/**********************************************************************/
165static bool
166rscompat_enabler_add_obligatory_hard_reqs(struct action_enabler *ae)
167{
168 struct req_vec_problem *problem;
169
170 struct action *paction = enabler_get_action(ae);
171 /* Some changes requires starting to process an action's enablers from
172 * the beginning. */
173 bool needs_restart = FALSE;
174
175 while ((problem = action_enabler_suggest_repair(ae)) != NULL) {
176 /* A hard obligatory requirement is missing. */
177
178 int i;
179
180 if (problem->num_suggested_solutions == 0) {
181 /* Didn't get any suggestions about how to solve this. */
182
183 log_error("While adding hard obligatory reqs to action enabler"
184 " for %s: %s"
185 " Don't know how to fix it."
186 " Dropping it.",
187 action_rule_name(paction), problem->description);
188 ae->ruledit_disabled = TRUE;
189
190 req_vec_problem_free(problem);
191 return TRUE;
192 }
193
194 /* Sanity check. */
195 fc_assert_ret_val(problem->num_suggested_solutions > 0,
196 needs_restart);
197
198 /* Only append is supported for upgrade */
199 for (i = 0; i < problem->num_suggested_solutions; i++) {
200 if (problem->suggested_solutions[i].operation != RVCO_APPEND) {
201 /* A problem that isn't caused by missing obligatory hard
202 * requirements has been detected.
203 *
204 * Probably an old requirement that contradicted a hard requirement
205 * that wasn't documented by making it obligatory. In that case all
206 * suggested solutions has been applied to the enabler creating a
207 * new copy for each possible fulfillment of the new obligatory hard
208 * requirement.
209 *
210 * If another copy of the original enabler has survived this isn't
211 * an error. It probably isn't event an indication of a potential
212 * problem.
213 *
214 * If no possible solution survives the enabler was never in use
215 * because the action it self would have blocked it. In that case
216 * this is an error. */
217
218 log_warn("While adding hard obligatory reqs to action enabler"
219 " for %s: %s"
220 " Dropping it.",
221 action_rule_name(paction), problem->description);
222 ae->ruledit_disabled = TRUE;
223 req_vec_problem_free(problem);
224 return TRUE;
225 }
226 }
227
228 for (i = 0; i < problem->num_suggested_solutions; i++) {
229 struct action_enabler *new_enabler;
230
231 /* There can be more than one suggestion to apply. In that case both
232 * are applied to their own copy. The original should therefore be
233 * kept for now. */
234 new_enabler = action_enabler_copy(ae);
235
236 /* Apply the solution. */
237 if (!req_vec_change_apply(&problem->suggested_solutions[i],
238 action_enabler_vector_by_number,
239 new_enabler)) {
240 log_error("While adding hard obligatory reqs to action enabler"
241 " for %s: %s"
242 "Failed to apply solution %s."
243 " Dropping it.",
244 action_rule_name(paction), problem->description,
245 req_vec_change_translation(
246 &problem->suggested_solutions[i],
247 action_enabler_vector_by_number_name));
248 new_enabler->ruledit_disabled = TRUE;
249 req_vec_problem_free(problem);
250 return TRUE;
251 }
252
253 if (problem->num_suggested_solutions - 1 == i) {
254 /* The last modification is to the original enabler. */
255 ae->action = new_enabler->action;
256 ae->ruledit_disabled = new_enabler->ruledit_disabled;
257 requirement_vector_copy(&ae->actor_reqs,
258 &new_enabler->actor_reqs);
259 requirement_vector_copy(&ae->target_reqs,
260 &new_enabler->target_reqs);
261 FC_FREE(new_enabler);
262 } else {
263 /* Register the new enabler */
264 action_enabler_add(new_enabler);
265
266 /* This changes the number of action enablers. */
267 needs_restart = TRUE;
268 }
269 }
270
271 req_vec_problem_free(problem);
272
273 if (needs_restart) {
274 /* May need to apply future upgrades to the copies too. */
275 return TRUE;
276 }
277 }
278
279 return needs_restart;
280}
281
282/**********************************************************************/
286 void rscompat_enablers_add_obligatory_hard_reqs(void)
287{
288 log_normal("action enablers: adding obligatory hard requirements.");
289 log_warn("More than one way to fulfill a new obligatory hard requirement"
290 " may exist."
291 " In that case the enabler is copied so each alternative"
292 " solution is applied to a copy of the enabler."
293 " If an action enabler becomes self contradicting after applying"
294 " a solution it is dropped."
295 " Note that other copies of the original enabler may have"
296 " survived even if one copy is dropped.");
297
298 action_iterate(act_id) {
299 bool restart_enablers_for_action;
300
301 /* RSFORMAT_3_3 */
302 if (action_has_result(action_by_number(act_id), ACTRES_COLLECT_RANSOM)) {
303 action_enabler_list_iterate(action_enablers_for_action(act_id), ae) {
304 requirement_vector_append(&ae->target_reqs,
305 req_from_str("PlayerState", "Player",
306 FALSE, TRUE, TRUE,
307 "Barbarian"));
308 } action_enabler_list_iterate_end;
309 }
310
311 do {
312 restart_enablers_for_action = FALSE;
313 action_enabler_list_iterate(action_enablers_for_action(act_id), ae) {
314 if (ae->ruledit_disabled) {
315 /* Ignore disabled enablers */
316 continue;
317 }
318 if (rscompat_enabler_add_obligatory_hard_reqs(ae)) {
319 /* Something important, probably the number of action enablers
320 * for this action, changed. Start over again on this action's
321 * enablers. */
322 restart_enablers_for_action = TRUE;
323 break;
324 }
325 } action_enabler_list_iterate_end;
326 } while (restart_enablers_for_action);
327
328 } action_iterate_end;
329}
330
331/**********************************************************************/
341 bool rscompat_names(struct rscompat_info *info)
342{
343 if (info->version < RSFORMAT_3_3) {
344 int first_free;
345 int i;
346
347 /* Some unit type flags moved to the ruleset between 3.2 and 3.3.
348 * Add them back as user flags.
349 * XXX: ruleset might not need all of these, and may have enough
350 * flags of its own that these additional ones prevent conversion. */
351 const struct {
352 const char *name;
353 const char *helptxt;
354 } new_flags_33[] = {
355 { N_("NoVeteran"), N_("May acquire veteran status.") },
356 { N_("CanEscape"), N_("Can try to escape stack death.") }
357 };
358
359 enough_new_user_flags(new_flags_33, unit_type,
360 UTYF_LAST_USER_FLAG, UTYF_LAST_USER_FLAG_3_2);
361
362 /* Unit type flags. */
363 first_free = first_free_unit_type_user_flag() + UTYF_USER_FLAG_1;
364
365 for (i = 0; i < ARRAY_SIZE(new_flags_33); i++) {
366 if (UTYF_USER_FLAG_1 + MAX_NUM_USER_UNIT_FLAGS <= first_free + i) {
367 /* Can't add the user unit type flags. */
368 ruleset_error(NULL, LOG_ERROR,
369 "Can't upgrade the ruleset. Not enough free unit type "
370 "user flags to add user flags for the unit type flags "
371 "that used to be hardcoded.");
372 return FALSE;
373 }
374 /* Shouldn't be possible for valid old ruleset to have flag names that
375 * clash with these ones */
376 if (unit_type_flag_id_by_name(new_flags_33[i].name, fc_strcasecmp)
377 != unit_type_flag_id_invalid()) {
378 ruleset_error(NULL, LOG_ERROR,
379 "Ruleset had illegal user unit type flag '%s'",
380 new_flags_33[i].name);
381 return FALSE;
382 }
383 set_user_unit_type_flag_name(first_free + i,
384 new_flags_33[i].name,
385 new_flags_33[i].helptxt);
386 }
387 }
388
389 /* No errors encountered. */
390 return TRUE;
391}
392
393/**********************************************************************/
396 static bool effect_list_compat_cb(struct effect *peffect, void *data)
397{
398 /* Go to the next effect. */
399 return TRUE;
400}
401
402/**********************************************************************/
405 void rscompat_postprocess(struct rscompat_info *info)
406{
407 struct action_enabler *enabler;
408 struct requirement e_req;
409
410 if (!info->compat_mode || info->version >= RSFORMAT_CURRENT) {
411 /* There isn't anything here that should be done outside of compat
412 * mode. */
413 return;
414 }
415
416 unit_type_iterate(ptype) {
417 if (utype_has_role(ptype, L_BARBARIAN_LEADER)) {
418 BV_SET(ptype->flags, UTYF_PROVIDES_RANSOM);
419 }
420 } unit_type_iterate_end;
421
422 enabler = action_enabler_new();
423 enabler->action = ACTION_GAIN_VETERANCY;
424 e_req = req_from_str("UnitFlag", "Local", FALSE, FALSE, FALSE,
425 "NoVeteran");
426 requirement_vector_append(&enabler->actor_reqs, e_req);
427 action_enabler_add(enabler);
428
429 enabler = action_enabler_new();
430 enabler->action = ACTION_ESCAPE;
431 e_req = req_from_str("UnitFlag", "Local", FALSE, TRUE, FALSE,
432 "CanEscape");
433 requirement_vector_append(&enabler->actor_reqs, e_req);
434 action_enabler_add(enabler);
435
436 /* Upgrade existing effects. Done before new effects are added to prevent
437 * the new effects from being upgraded by accident. */
438 iterate_effect_cache(effect_list_compat_cb, info);
439
440 /* Old hardcoded behavior always had tech leakage enabled,
441 * thought limited by game.info.tech_leakage setting. */
442 effect_new(EFT_TECH_LEAKAGE, 1, nullptr);
443
444 /* Make sure that all action enablers added or modified by the
445 * compatibility post processing fulfills all hard action requirements. */
446 rscompat_enablers_add_obligatory_hard_reqs();
447
448 /* The ruleset may need adjustments it didn't need before compatibility
449 * post processing.
450 *
451 * If this isn't done a user of ruleset compatibility that ends up using
452 * the rules risks bad rules. A user that saves the ruleset rather than
453 * using it risks an unexpected change on the next load and save. */
454 autoadjust_ruleset_data();
455}
456
457/**********************************************************************/
461 static int first_free_unit_type_user_flag(void)
462{
463 int flag;
464
465 /* Find the first unused user defined unit type flag. */
466 for (flag = 0; flag < MAX_NUM_USER_UNIT_FLAGS; flag++) {
467 if (unit_type_flag_id_name_cb(flag + UTYF_USER_FLAG_1) == NULL) {
468 return flag;
469 }
470 }
471
472 /* All unit type user flags are taken. */
473 return MAX_NUM_USER_UNIT_FLAGS;
474}
475
476/**********************************************************************/
479 const char *rscompat_utype_flag_name_3_3(const char *old_name)
480{
481 if (!strcasecmp("Settlers", old_name)) {
482 return "Workers";
483 }
484
485 return old_name;
486}
action_enabler_suggest_repair
struct req_vec_problem * action_enabler_suggest_repair(const struct action_enabler *enabler)
Definition actions.c:1892
action_enabler_vector_by_number_name
const char * action_enabler_vector_by_number_name(req_vec_num_in_item vec)
Definition actions.c:2069
action_rule_name
const char * action_rule_name(const struct action *action)
Definition actions.c:1191
action_enabler_add
void action_enabler_add(struct action_enabler *enabler)
Definition actions.c:1496
action_enabler_new
struct action_enabler * action_enabler_new(void)
Definition actions.c:1450
action_enabler_copy
struct action_enabler * action_enabler_copy(const struct action_enabler *original)
Definition actions.c:1481
action_enablers_for_action
struct action_enabler_list * action_enablers_for_action(action_id action)
Definition actions.c:1529
action_enabler_vector_by_number
struct requirement_vector * action_enabler_vector_by_number(const void *enabler, req_vec_num_in_item number)
Definition actions.c:2045
actions.h
enabler_get_action
#define enabler_get_action(_enabler_)
Definition actions.h:177
action_by_number
static struct action * action_by_number(action_id act_id)
Definition actions.h:390
action_has_result
#define action_has_result(_act_, _res_)
Definition actions.h:175
action_enabler_list_iterate_end
#define action_enabler_list_iterate_end
Definition actions.h:185
action_iterate_end
#define action_iterate_end
Definition actions.h:209
action_enabler_list_iterate
#define action_enabler_list_iterate(action_enabler_list, aenabler)
Definition actions.h:183
action_iterate
#define action_iterate(_act_)
Definition actions.h:205
BV_SET
#define BV_SET(bv, bit)
Definition bitvector.h:81
has_capabilities
bool has_capabilities(const char *us, const char *them)
Definition capability.c:88
capability.h
incite_cost
char * incite_cost
Definition comments.c:74
iterate_effect_cache
bool iterate_effect_cache(iec_cb cb, void *data)
Definition effects.c:1321
effect_new
struct effect * effect_new(enum effect_type type, int value, struct multiplier *pmul)
Definition effects.c:186
effects.h
fc_types.h
N_
#define N_(String)
Definition fcintl.h:69
game.h
name
const char * name
Definition inputfile.c:127
log.h
log_warn
#define log_warn(message,...)
Definition log.h:105
fc_assert_ret_val
#define fc_assert_ret_val(condition, val)
Definition log.h:194
log_fatal
#define log_fatal(message,...)
Definition log.h:100
log_normal
#define log_normal(message,...)
Definition log.h:107
LOG_ERROR
@ LOG_ERROR
Definition log.h:30
log_error
#define log_error(message,...)
Definition log.h:103
FC_FREE
#define FC_FREE(ptr)
Definition mem.h:41
movement.h
registry.h
secfile_error
const char * secfile_error(void)
Definition section_file.c:36
secfile_lookup_int
bool secfile_lookup_int(const struct section_file *secfile, int *ival, const char *path,...)
Definition registry_ini.c:1978
secfile_lookup_str
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
Definition registry_ini.c:2180
req_vec_change_apply
bool req_vec_change_apply(const struct req_vec_change *modification, requirement_vector_by_number getter, const void *parent_item)
Definition requirements.c:6388
req_vec_change_translation
const char * req_vec_change_translation(const struct req_vec_change *change, const requirement_vector_namer namer)
Definition requirements.c:6315
req_from_str
struct requirement req_from_str(const char *type, const char *range, bool survives, bool present, bool quiet, const char *value)
Definition requirements.c:1048
req_vec_problem_free
void req_vec_problem_free(struct req_vec_problem *issue)
Definition requirements.c:6485
requirements.h
UTYF_LAST_USER_FLAG_3_2
#define UTYF_LAST_USER_FLAG_3_2
Definition rscompat.c:50
rscompat_enabler_add_obligatory_hard_reqs
static bool rscompat_enabler_add_obligatory_hard_reqs(struct action_enabler *ae)
Definition rscompat.c:166
rscompat_names
bool rscompat_names(struct rscompat_info *info)
Definition rscompat.c:341
rscompat_postprocess
void rscompat_postprocess(struct rscompat_info *info)
Definition rscompat.c:405
rscompat_utype_flag_name_3_3
const char * rscompat_utype_flag_name_3_3(const char *old_name)
Definition rscompat.c:479
rscompat_check_capabilities
int rscompat_check_capabilities(struct section_file *file, const char *filename, const struct rscompat_info *info)
Definition rscompat.c:66
rscompat_enablers_add_obligatory_hard_reqs
void rscompat_enablers_add_obligatory_hard_reqs(void)
Definition rscompat.c:286
effect_list_compat_cb
static bool effect_list_compat_cb(struct effect *peffect, void *data)
Definition rscompat.c:396
first_free_unit_type_user_flag
static int first_free_unit_type_user_flag(void)
Definition rscompat.c:461
rscompat_init_info
void rscompat_init_info(struct rscompat_info *info)
Definition rscompat.c:57
rscompat_check_cap_and_version
bool rscompat_check_cap_and_version(struct section_file *file, const char *filename, const struct rscompat_info *info)
Definition rscompat.c:131
enough_new_user_flags
#define enough_new_user_flags(_new_flags_, _name_, _LAST_USER_FLAG_, _LAST_USER_FLAG_PREV_)
Definition rscompat.c:44
rscompat.h
RULESET_COMPAT_CAP
#define RULESET_COMPAT_CAP
Definition rscompat.h:27
autoadjust_ruleset_data
bool autoadjust_ruleset_data(void)
Definition rssanity.c:1551
rssanity.h
ruleload.h
ruleset_error
#define ruleset_error(logger, level, format,...)
Definition ruleload.h:59
RULESET_CAPABILITIES
#define RULESET_CAPABILITIES
Definition ruleload.h:24
RSFORMAT_CURRENT
#define RSFORMAT_CURRENT
Definition ruleload.h:38
RSFORMAT_3_3
#define RSFORMAT_3_3
Definition ruleload.h:37
settings.h
ARRAY_SIZE
#define ARRAY_SIZE(x)
Definition shared.h:85
action_enabler
Definition actions.h:165
action
Definition actions.h:106
effect
Definition effects.h:40
req_vec_problem
Definition requirements.h:295
requirement
Definition requirements.h:81
rscompat_info
Definition rscompat.h:30
rscompat_info::version
int version
Definition rscompat.h:33
rscompat_info::compat_mode
bool compat_mode
Definition rscompat.h:31
section_file
Definition section_file.h:36
unit_type
Definition unittype.h:507
fc_strcasecmp
int fc_strcasecmp(const char *str0, const char *str1)
Definition support.c:186
TRUE
#define TRUE
Definition support.h:46
FALSE
#define FALSE
Definition support.h:47
utype_has_role
bool utype_has_role(const struct unit_type *punittype, int role)
Definition unittype.c:199
unit_type_flag_id_name_cb
const char * unit_type_flag_id_name_cb(enum unit_type_flag_id flag)
Definition unittype.c:1905
set_user_unit_type_flag_name
void set_user_unit_type_flag_name(enum unit_type_flag_id id, const char *name, const char *helptxt)
Definition unittype.c:1876
unittype.h
MAX_NUM_USER_UNIT_FLAGS
#define MAX_NUM_USER_UNIT_FLAGS
Definition unittype.h:338
UTYF_LAST_USER_FLAG
#define UTYF_LAST_USER_FLAG
Definition unittype.h:337
unit_type_iterate
#define unit_type_iterate(_p)
Definition unittype.h:859
unit_type_iterate_end
#define unit_type_iterate_end
Definition unittype.h:866