From 0e5917e029cf892de660f0f82b55c8639440b155 Mon Sep 17 00:00:00 2001 From: Alina Lenk Date: Sun, 23 Oct 2022 13:49:44 +0200 Subject: [PATCH] Replace alltemperate and singlepole with latitude bounds See osdn#44038 Signed-off-by: Alina Lenk --- client/packhand.c | 4 +- common/map.c | 4 +- common/map.h | 21 ++-- common/map_types.h | 4 +- common/networking/packets.def | 4 +- data/multiplayer/game.ruleset | 3 +- data/sandbox/game.ruleset | 3 +- server/maphand.c | 4 +- server/rscompat.c | 92 +++++++++++++++++ server/rscompat.h | 4 + server/rssanity.c | 6 -- server/savegame/savecompat.c | 187 ++++++++++++++++++++++++++++++++++ server/settings.c | 81 ++++++++++++--- 13 files changed, 370 insertions(+), 47 deletions(-) diff --git a/client/packhand.c b/client/packhand.c index a90c6501a7..5166df4701 100644 --- a/client/packhand.c +++ b/client/packhand.c @@ -2193,8 +2193,8 @@ void handle_map_info(const struct packet_map_info *packet) wld.map.xsize = packet->xsize; wld.map.ysize = packet->ysize; - wld.map.alltemperate = packet->alltemperate; - wld.map.single_pole = packet->single_pole; + wld.map.north_latitude = packet->north_latitude; + wld.map.south_latitude = packet->south_latitude; if (tileset_map_topo_compatible(packet->topology_id, tileset, &ts_topo) == TOPO_INCOMP_HARD) { tileset_error(LOG_NORMAL, _("Map topology (%s) and tileset (%s) incompatible."), diff --git a/common/map.c b/common/map.c index f49c024eef..ead081d9be 100644 --- a/common/map.c +++ b/common/map.c @@ -170,8 +170,8 @@ void map_init(struct civ_map *imap, bool server_side) imap->xsize = MAP_DEFAULT_LINEAR_SIZE; imap->ysize = MAP_DEFAULT_LINEAR_SIZE; - imap->single_pole = MAP_DEFAULT_SINGLE_POLE; - imap->alltemperate = MAP_DEFAULT_ALLTEMPERATE; + imap->north_latitude = MAP_DEFAULT_NORTH_LATITUDE; + imap->south_latitude = MAP_DEFAULT_SOUTH_LATITUDE; if (server_side) { imap->server.mapsize = MAP_DEFAULT_MAPSIZE; diff --git a/common/map.h b/common/map.h index fe8767ba02..0198cc9c77 100644 --- a/common/map.h +++ b/common/map.h @@ -571,13 +571,14 @@ extern const int DIR_DY[8]; /* Latitude granularity, irrespective of map/generator settings */ #define MAP_MAX_LATITUDE 1000 +#define MAP_MAX_LATITUDE_BOUND (MAP_MAX_LATITUDE) +#define MAP_MIN_LATITUDE_BOUND (-MAP_MAX_LATITUDE) +#define MAP_DEFAULT_NORTH_LATITUDE MAP_MAX_LATITUDE_BOUND +#define MAP_DEFAULT_SOUTH_LATITUDE MAP_MIN_LATITUDE_BOUND + /* Northernmost and southernmost latitude for the given map */ -#define MAP_NORTH_LATITUDE(_nmap) \ - ((_nmap).alltemperate ? (MAP_MAX_LATITUDE / 2) : MAP_MAX_LATITUDE) -#define MAP_SOUTH_LATITUDE(_nmap) \ - ((_nmap).alltemperate \ - ? (MAP_MAX_LATITUDE / 2) \ - : ((_nmap).single_pole ? 0 : (-MAP_MAX_LATITUDE))) +#define MAP_NORTH_LATITUDE(_nmap) ((_nmap).north_latitude) +#define MAP_SOUTH_LATITUDE(_nmap) ((_nmap).south_latitude) /* Maximum and minimum latitude present in the given map */ #define MAP_MAX_REAL_LATITUDE(_nmap) \ @@ -688,14 +689,6 @@ moves. Includes MAP_MAX_LINEAR_SIZE because a map can be non wrapping. */ #define MAP_MIN_FLATPOLES 0 #define MAP_MAX_FLATPOLES 100 -#define MAP_DEFAULT_SINGLE_POLE FALSE -#define MAP_MIN_SINGLE_POLE FALSE -#define MAP_MAX_SINGLE_POLE TRUE - -#define MAP_DEFAULT_ALLTEMPERATE FALSE -#define MAP_MIN_ALLTEMPERATE FALSE -#define MAP_MAX_ALLTEMPERATE TRUE - #define MAP_DEFAULT_TEMPERATURE 50 #define MAP_MIN_TEMPERATURE 0 #define MAP_MAX_TEMPERATURE 100 diff --git a/common/map_types.h b/common/map_types.h index 8938ca72c7..efaaf1e6e4 100644 --- a/common/map_types.h +++ b/common/map_types.h @@ -76,8 +76,8 @@ struct civ_map { struct iter_index *iterate_outwards_indices; int num_iterate_outwards_indices; int xsize, ysize; /* native dimensions */ - bool single_pole; - bool alltemperate; + int north_latitude; + int south_latitude; int num_continents; int num_oceans; /* not updated at the client */ struct tile *tiles; diff --git a/common/networking/packets.def b/common/networking/packets.def index d378d1d79c..ea522abb07 100644 --- a/common/networking/packets.def +++ b/common/networking/packets.def @@ -634,8 +634,8 @@ PACKET_MAP_INFO = 17; sc, lsend XYSIZE ysize; UINT8 topology_id; UINT8 wrap_id; - BOOL alltemperate; - BOOL single_pole; + SINT16 north_latitude; + SINT16 south_latitude; end PACKET_NUKE_TILE_INFO = 18; sc, dsend, lsend, handle-via-fields diff --git a/data/multiplayer/game.ruleset b/data/multiplayer/game.ruleset index 067462f38b..774ca2823f 100644 --- a/data/multiplayer/game.ruleset +++ b/data/multiplayer/game.ruleset @@ -2016,7 +2016,8 @@ set = "wrap", "WRAPX|WRAPY", FALSE "startpos", "SINGLE", FALSE "tinyisles", FALSE, FALSE - "alltemperate", TRUE, FALSE + "northlatitude", 500, FALSE + "southlatitude", 500, FALSE "separatepoles", FALSE, FALSE "huts", 0, FALSE "aifill", 0, FALSE diff --git a/data/sandbox/game.ruleset b/data/sandbox/game.ruleset index 558d15d133..e46ed6bab0 100644 --- a/data/sandbox/game.ruleset +++ b/data/sandbox/game.ruleset @@ -3613,5 +3613,6 @@ set = "techlossrestore", 50, FALSE "multiresearch", "ENABLED", FALSE "tradeworldrelpct", 100, FALSE - "alltemperate", "DISABLED", TRUE + "northlatitude", 1000, TRUE + "southlatitude", -1000, TRUE } diff --git a/server/maphand.c b/server/maphand.c index 73d948cd97..28220bd933 100644 --- a/server/maphand.c +++ b/server/maphand.c @@ -638,8 +638,8 @@ void send_map_info(struct conn_list *dest) minfo.ysize = wld.map.ysize; minfo.topology_id = wld.map.topology_id; minfo.wrap_id = wld.map.wrap_id; - minfo.alltemperate = wld.map.alltemperate; - minfo.single_pole = wld.map.single_pole; + minfo.north_latitude = wld.map.north_latitude; + minfo.south_latitude = wld.map.south_latitude; lsend_packet_map_info(dest, &minfo); } diff --git a/server/rscompat.c b/server/rscompat.c index eb435d27dc..b8ba4d5c1f 100644 --- a/server/rscompat.c +++ b/server/rscompat.c @@ -37,6 +37,7 @@ /* server */ #include "rssanity.h" #include "ruleset.h" +#include "settings.h" #include "rscompat.h" @@ -516,3 +517,94 @@ void rscompat_extra_adjust_3_2(struct extra_type *pextra) "-980")); } } + +/**********************************************************************//** + Determine whether the given setting should be skipped and + rscompat_settings_do_special_handling should be called. +**************************************************************************/ +bool rscompat_setting_needs_special_handling(const char *name) +{ + /* Replaced by 'northlatitude' and 'southlatitude' */ + if (!fc_strcasecmp("alltemperate", name) + || !fc_strcasecmp("singlepole", name)) { + return TRUE; + } + + return FALSE; +} + +/**********************************************************************//** + Special handling for complex server setting changes. +**************************************************************************/ +void rscompat_settings_do_special_handling(struct section_file *file, + const char *section, void (*setdef)(struct setting *pset)) +{ + /* Replace 'alltemperate' and 'singlepole' with appropriate + * 'northlatitude' and 'southlatitude' settings */ + { + bool has_either = FALSE, locks_either = FALSE; + bool alltemperate = FALSE, singlepole = FALSE; + const char *name; + int j; + + for (j = 0; (name = secfile_lookup_str_default(file, NULL, + "%s.set%d.name", + section, j)); j++) { + bool *pval; + + if (!fc_strcasecmp("alltemperate", name)) { + pval = &alltemperate; + } else if (!fc_strcasecmp("singlepole", name)) { + pval = &singlepole; + } else { + /* neither of the settings we care for */ + continue; + } + + has_either = TRUE; + + if (!secfile_lookup_bool(file, pval, "%s.set%d.value", section, j)) { + log_error("Can't read value for setting '%s': %s", name, + secfile_error()); + } + + if (secfile_lookup_bool_default(file, FALSE, + "%s.set%d.lock", section, j)) { + locks_either = TRUE; + } + } + + if (has_either) { + int north_latitude = alltemperate ? 500 : 1000; + int south_latitude = alltemperate ? 500 : (singlepole ? 0 : -1000); + struct setting *pset; + char reject_msg[256], buf[256]; + +#define SET_INT_SETTING(name, value, lock) \ + pset = setting_by_name(name); \ + fc_assert(pset != NULL && setting_type(pset) == SST_INT); \ + \ + if (setting_int_set(pset, value, NULL, reject_msg, \ + sizeof(reject_msg))) { \ + log_normal(_("Ruleset: '%s' has been set to %s."), \ + setting_name(pset), \ + setting_value_name(pset, TRUE, buf, sizeof(buf))); \ + } else { \ + log_error("%s", reject_msg); \ + } \ + \ + setdef(pset); \ + \ + if (lock) { \ + setting_ruleset_lock_set(pset); \ + log_normal(_("Ruleset: '%s' has been locked by the ruleset."), \ + setting_name(pset)); \ + } + + SET_INT_SETTING("northlatitude", north_latitude, locks_either); + SET_INT_SETTING("southlatitude", south_latitude, locks_either); + +#undef SET_INT_SETTING + } + } +} diff --git a/server/rscompat.h b/server/rscompat.h index c4546fe7ba..0849968be1 100644 --- a/server/rscompat.h +++ b/server/rscompat.h @@ -22,6 +22,7 @@ extern "C" { /* server */ #include "ruleset.h" +#include "settings.h" #define RULESET_COMPAT_CAP "+Freeciv-3.1-ruleset" @@ -66,6 +67,9 @@ void rscompat_req_adjust_3_2(const struct rscompat_info *compat, const char **ptype, const char **pname, bool *ppresent, const char *sec_name); void rscompat_extra_adjust_3_2(struct extra_type *pextra); +bool rscompat_setting_needs_special_handling(const char *name); +void rscompat_settings_do_special_handling(struct section_file *file, + const char *section, void (*setdef)(struct setting *pset)); #ifdef __cplusplus } diff --git a/server/rssanity.c b/server/rssanity.c index fad7fe0039..79e2df87db 100644 --- a/server/rssanity.c +++ b/server/rssanity.c @@ -116,12 +116,6 @@ static bool sanity_check_setting_is_game_rule(struct setting *pset) return FALSE; } - if (pset == setting_by_name("alltemperate") - || pset == setting_by_name("singlepole")) { - /* Should be done via world-ranged MinLatitude and MaxLatitude reqs. */ - return FALSE; - } - return TRUE; } diff --git a/server/savegame/savecompat.c b/server/savegame/savecompat.c index 1a59db581b..09ead78f1b 100644 --- a/server/savegame/savecompat.c +++ b/server/savegame/savecompat.c @@ -1927,6 +1927,7 @@ static void compat_load_030200(struct loaddata *loading, /* Server setting migration. */ { if (secfile_lookup_int(loading->file, &set_count, "settings.set_count")) { + int alltemperate_idx = -1, singlepole_idx = -1; bool count_changed = FALSE; gamestart_valid @@ -2071,6 +2072,84 @@ static void compat_load_030200(struct loaddata *loading, set_count++; count_changed = TRUE; } + } else if (!fc_strcasecmp("alltemperate", name)) { + alltemperate_idx = i; + } else if (!fc_strcasecmp("singlepole", name)) { + singlepole_idx = i; + } + } + + if (alltemperate_idx >= 0 || singlepole_idx >= 0) { + int north_latitude, south_latitude; + int north_idx, south_idx; + bool alltemperate, singlepole; + + if (alltemperate_idx < 0 + || !secfile_lookup_bool(loading->file, &alltemperate, + "settings.set%d.value", + alltemperate_idx)) { + /* infer what would've been the ruleset default */ + alltemperate = (wld.map.north_latitude == wld.map.south_latitude); + } + + if (singlepole_idx < 0 + || !secfile_lookup_bool(loading->file, &singlepole, + "settings.set%d.value", + singlepole_idx)) { + /* infer what would've been the ruleset default */ + singlepole = (wld.map.south_latitude >= 0); + } + + /* Note: hard-coding 1000-based latitudes here; if MAX_LATITUDE ever + * changes, that'll have to be handled in later migrations anyway */ + north_latitude = alltemperate ? 500 : 1000; + south_latitude = alltemperate ? 500 : (singlepole ? 0 : -1000); + + /* Replace alltemperate with northlatitude, and singlepole with + * southlatitude. If only one of the two was given, add the other + * at the end. */ + north_idx = (alltemperate_idx < 0) ? set_count : alltemperate_idx; + south_idx = (singlepole_idx < 0) ? set_count : singlepole_idx; + + secfile_replace_str(loading->file, "northlatitude", + "settings.set%d.name", north_idx); + secfile_replace_int(loading->file, north_latitude, + "settings.set%d.value", north_idx); + + secfile_replace_str(loading->file, "southlatitude", + "settings.set%d.name", south_idx); + secfile_replace_int(loading->file, south_latitude, + "settings.set%d.value", south_idx); + + if (gamestart_valid) { + if (alltemperate_idx < 0 + || !secfile_lookup_bool(loading->file, &alltemperate, + "settings.set%d.gamestart", + alltemperate_idx)) { + alltemperate = + (wld.map.north_latitude == wld.map.south_latitude); + } + + if (singlepole_idx < 0 + || !secfile_lookup_bool(loading->file, &singlepole, + "settings.set%d.gamestart", + singlepole_idx)) { + singlepole = (wld.map.south_latitude >= 0); + } + + north_latitude = alltemperate ? 500 : 1000; + south_latitude = alltemperate ? 500 : (singlepole ? 0 : -1000); + + secfile_replace_int(loading->file, north_latitude, + "settings.set%d.gamestart", north_idx); + secfile_replace_int(loading->file, south_latitude, + "settings.set%d.gamestart", south_idx); + } + + if (alltemperate_idx < 0 || singlepole_idx < 0) { + /* only one was given and replaced ~> we added one new entry */ + set_count++; + count_changed = TRUE; } } @@ -2841,6 +2920,114 @@ static void compat_load_dev(struct loaddata *loading) } } } + + /* Convert 'alltemperate' and 'singlepole' into 'northlatitude' and + * 'southlatitude' server settings */ + { + int i; + int set_count = secfile_lookup_int_default(loading->file, 0, + "settings.set_count"); + int alltemperate_idx = -1, singlepole_idx = -1; + + for (i = 0; i < set_count; i++) { + const char *name + = secfile_lookup_str(loading->file, "settings.set%d.name", i); + + if (!name) { + continue; + } + + if (!fc_strcasecmp("northlatitude", name) + || !fc_strcasecmp("southlatitude", name)) { + /* this savegame is recent enough to already have latitude + * settings ~> no change necessary */ + alltemperate_idx = singlepole_idx = -1; + break; + } + + if (!fc_strcasecmp("alltemperate", name)) { + alltemperate_idx = i; + } else if (!fc_strcasecmp("singlepole", name)) { + singlepole_idx = i; + } + } + + if (alltemperate_idx >= 0 || singlepole_idx >= 0) { + int north_latitude, south_latitude; + int north_idx, south_idx; + bool alltemperate, singlepole; + + if (alltemperate_idx < 0 + || !secfile_lookup_bool(loading->file, &alltemperate, + "settings.set%d.value", + alltemperate_idx)) { + /* infer what would've been the ruleset default */ + alltemperate = (wld.map.north_latitude == wld.map.south_latitude); + } + + if (singlepole_idx < 0 + || !secfile_lookup_bool(loading->file, &singlepole, + "settings.set%d.value", + singlepole_idx)) { + /* infer what would've been the ruleset default */ + singlepole = (wld.map.south_latitude >= 0); + } + + /* Note: hard-coding 1000-based latitudes here; if MAX_LATITUDE ever + * changes, that'll have to be handled in later migrations anyway */ + north_latitude = alltemperate ? 500 : 1000; + south_latitude = alltemperate ? 500 : (singlepole ? 0 : -1000); + + /* Replace alltemperate with northlatitude, and singlepole with + * southlatitude. If only one of the two was given, add the other + * at the end. */ + north_idx = (alltemperate_idx < 0) ? set_count : alltemperate_idx; + south_idx = (singlepole_idx < 0) ? set_count : singlepole_idx; + + secfile_replace_str(loading->file, "northlatitude", + "settings.set%d.name", north_idx); + secfile_replace_int(loading->file, north_latitude, + "settings.set%d.value", north_idx); + + secfile_replace_str(loading->file, "southlatitude", + "settings.set%d.name", south_idx); + secfile_replace_int(loading->file, south_latitude, + "settings.set%d.value", south_idx); + + if (secfile_lookup_bool_default(loading->file, FALSE, + "settings.gamestart_valid")) { + if (alltemperate_idx < 0 + || !secfile_lookup_bool(loading->file, &alltemperate, + "settings.set%d.gamestart", + alltemperate_idx)) { + alltemperate = + (wld.map.north_latitude == wld.map.south_latitude); + } + + if (singlepole_idx < 0 + || !secfile_lookup_bool(loading->file, &singlepole, + "settings.set%d.gamestart", + singlepole_idx)) { + singlepole = (wld.map.south_latitude >= 0); + } + + north_latitude = alltemperate ? 500 : 1000; + south_latitude = alltemperate ? 500 : (singlepole ? 0 : -1000); + + secfile_replace_int(loading->file, north_latitude, + "settings.set%d.gamestart", north_idx); + secfile_replace_int(loading->file, south_latitude, + "settings.set%d.gamestart", south_idx); + } + + if (alltemperate_idx < 0 || singlepole_idx < 0) { + /* only one was given and replaced ~> we added one new entry */ + set_count++; + secfile_replace_int(loading->file, set_count, + "settings.set_count"); + } + } + } } /* Version < 3.1.93 */ #endif /* FREECIV_DEV_SAVE_COMPAT_3_2 */ diff --git a/server/settings.c b/server/settings.c index 3e2dbb8024..acaec1fc54 100644 --- a/server/settings.c +++ b/server/settings.c @@ -37,6 +37,7 @@ #include "notify.h" #include "plrhand.h" #include "report.h" +#include "rscompat.h" #include "rssanity.h" #include "setcompat.h" #include "srv_main.h" @@ -189,6 +190,7 @@ static struct { struct setting_list *level[OLEVELS_NUM]; } setting_sorted = { .init = FALSE }; +static void setting_ruleset_setdef(struct setting *pset); static bool setting_ruleset_one(struct section_file *file, const char *name, const char *path, bool compat); @@ -1721,20 +1723,48 @@ static struct setting settings[] = { NULL, NULL, MAP_MIN_FLATPOLES, MAP_MAX_FLATPOLES, MAP_DEFAULT_FLATPOLES) - GEN_BOOL("singlepole", wld.map.single_pole, - SSET_MAP_GEN, SSET_GEOLOGY, SSET_SITUATIONAL, - ALLOW_NONE, ALLOW_BASIC, - N_("Whether there's just one pole generated"), - N_("If this setting is enabled, only one side of the map will have " - "a pole."), NULL, NULL, MAP_DEFAULT_SINGLE_POLE) - - GEN_BOOL("alltemperate", wld.map.alltemperate, - SSET_MAP_GEN, SSET_GEOLOGY, SSET_RARE, ALLOW_NONE, ALLOW_BASIC, - N_("All the map is temperate"), - N_("If this setting is enabled, the temperature will be " - "equivalent everywhere on the map. As a result, the " - "poles won't be generated."), - NULL, NULL, MAP_DEFAULT_ALLTEMPERATE) + GEN_INT("northlatitude", wld.map.north_latitude, + SSET_MAP_GEN, SSET_GEOLOGY, SSET_SITUATIONAL, + ALLOW_NONE, ALLOW_BASIC, + N_("Northernmost latitude"), + /* TRANS: The string between single quotes is a setting name + * and should not be translated. */ + N_("Combined with 'southlatitude', controls what climatic " + "zones exist on the map. Higher values are further north, " + "lower values are further south.\n" + "\n" + "1000 and -1000 gives a full planetary map.\n" + "1000 and 0 gives only a northern hemisphere.\n" + " 500 and 500 gives a map with the same middle-latitude " + "climate everywhere.\n" + " 300 and -300 gives an equatorial map.\n" + "\n" + "In rulesets that support it, latitude may also have certain " + "effects during gameplay."), NULL, + NULL, NULL, + MAP_MIN_LATITUDE_BOUND, MAP_MAX_LATITUDE_BOUND, + MAP_DEFAULT_NORTH_LATITUDE) + GEN_INT("southlatitude", wld.map.south_latitude, + SSET_MAP_GEN, SSET_GEOLOGY, SSET_SITUATIONAL, + ALLOW_NONE, ALLOW_BASIC, + N_("Southernmost latitude"), + /* TRANS: The string between single quotes is a setting name + * and should not be translated. */ + N_("Combined with 'northlatitude', controls what climatic " + "zones exist on the map. Higher values are further north, " + "lower values are further south.\n" + "\n" + "1000 and -1000 gives a full planetary map.\n" + "1000 and 0 gives only a northern hemisphere.\n" + " 500 and 500 gives a map with the same middle-latitude " + "climate everywhere.\n" + " 300 and -300 gives an equatorial map.\n" + "\n" + "In rulesets that support it, latitude may also have certain " + "effects during gameplay."), NULL, + NULL, NULL, + MAP_MIN_LATITUDE_BOUND, MAP_MAX_LATITUDE_BOUND, + MAP_DEFAULT_SOUTH_LATITUDE) GEN_INT("temperature", wld.map.server.temperature, SSET_MAP_GEN, SSET_GEOLOGY, SSET_SITUATIONAL, @@ -4346,10 +4376,18 @@ bool settings_ruleset(struct section_file *file, const char *section, log_verbose("no [%s] section for game settings in %s", section, secfile_name(file)); } else { + bool rscompat_special_handling = FALSE; + for (j = 0; (name = secfile_lookup_str_default(file, NULL, "%s.set%d.name", section, j)); j++) { char path[256]; + if (compat && rscompat_setting_needs_special_handling(name)) { + /* Skip this setting for now; handle it later */ + rscompat_special_handling = TRUE; + continue; + } + fc_snprintf(path, sizeof(path), "%s.set%d", section, j); if (compat) { @@ -4361,6 +4399,11 @@ bool settings_ruleset(struct section_file *file, const char *section, secfile_name(file), name); } } + + if (compat && rscompat_special_handling) { + rscompat_settings_do_special_handling(file, section, + setting_ruleset_setdef); + } } /* Execute all setting actions to consider actions due to the @@ -4381,6 +4424,14 @@ bool settings_ruleset(struct section_file *file, const char *section, return TRUE; } +/***********************************************************************//** + Mark a setting as having been set by the ruleset. +***************************************************************************/ +static inline void setting_ruleset_setdef(struct setting *pset) +{ + pset->setdef = SETDEF_RULESET; +} + /************************************************************************//** Set one setting from the game.ruleset file. ****************************************************************************/ @@ -4557,7 +4608,7 @@ static bool setting_ruleset_one(struct section_file *file, break; } - pset->setdef = SETDEF_RULESET; + setting_ruleset_setdef(pset); } /* set lock */ -- 2.34.1