From 9d8e173003bf7a9d86753acdd140a63ed39dc687 Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Tue, 3 Oct 2023 20:02:45 +0300 Subject: [PATCH 26/26] Make playertile extras dynamic bitvector It was a static one, always reserving memory for freeciv's internal maximum number of extras. See osdn #48798 Signed-off-by: Marko Lindqvist --- server/legacysave.c | 284 ++++++++++++++++++++++++++++++++++------- server/maphand.c | 14 ++- server/maphand.h | 2 +- server/savegame2.c | 301 +++++++++++++++++++++++++++++++++++++++----- server/savegame3.c | 97 ++++++++++++-- server/unittools.c | 19 +-- 6 files changed, 615 insertions(+), 102 deletions(-) diff --git a/server/legacysave.c b/server/legacysave.c index 6de49cf8f3..ea2090b7a2 100644 --- a/server/legacysave.c +++ b/server/legacysave.c @@ -228,8 +228,10 @@ static const char num_chars[] = static bool load_river_overlay = FALSE; -static void set_savegame_special(struct tile *ptile, bv_extras *extras, - char ch, const enum tile_special_type *idx); +static void set_savegame_special_dbv(struct tile *ptile, struct dbv *extras, + char ch, const enum tile_special_type *idx); +static void set_savegame_special_bv(struct tile *ptile, bv_extras *extras, + char ch, const enum tile_special_type *idx); static void game_load_internal(struct section_file *file); @@ -694,17 +696,17 @@ static void map_load_rivers_overlay(struct section_file *file, const enum tile_special_type *special_order, int num_special_types) { - /* used by set_savegame_special */ + /* Used by set_savegame_special_bv() */ load_river_overlay = TRUE; if (special_order) { special_halfbyte_iterate(j, num_special_types) { - char buf[16]; /* enough for sprintf() below */ + char buf[16]; /* Enough for sprintf() below */ sprintf (buf, "map.spe%02d_%%03d", j); LOAD_MAP_DATA(ch, nat_y, ptile, secfile_lookup_str(file, buf, nat_y), - set_savegame_special(ptile, &ptile->extras, ch, special_order + 4 * j)); + set_savegame_special_bv(ptile, &ptile->extras, ch, special_order + 4 * j)); } special_halfbyte_iterate_end; } else { /* Get the bits of the special flags which contain the river special @@ -712,7 +714,7 @@ static void map_load_rivers_overlay(struct section_file *file, FC_STATIC_ASSERT(S_LAST <= 32, S_LAST_too_big); LOAD_MAP_DATA(ch, line, ptile, secfile_lookup_str(file, "map.n%03d", line), - set_savegame_special(ptile, &ptile->extras, ch, default_specials + 8)); + set_savegame_special_bv(ptile, &ptile->extras, ch, default_specials + 8)); } load_river_overlay = FALSE; @@ -725,9 +727,129 @@ static void map_load_rivers_overlay(struct section_file *file, in four to a character in hex notation. 'index' is a mapping of savegame bit -> special bit. S_LAST is used to mark unused savegame bits. ****************************************************************************/ -static void set_savegame_special(struct tile *ptile, bv_extras *extras, - char ch, - const enum tile_special_type *idx) +static void set_savegame_special_dbv(struct tile *ptile, struct dbv *extras, + char ch, + const enum tile_special_type *idx) +{ + int i, bin; + const char *pch = strchr(hex_chars, ch); + + if (!pch || ch == '\0') { + log_error("Unknown hex value: '%c' (%d)", ch, ch); + bin = 0; + } else { + bin = pch - hex_chars; + } + + for (i = 0; i < 4; i++) { + enum tile_special_type sp = idx[i]; + + if (sp == S_LAST) { + continue; + } + if (load_river_overlay && sp != S_OLD_RIVER) { + continue; + } + + if (bin & (1 << i)) { + /* Pre 2.2 savegames have fortresses and airbases as part of specials */ + if (sp == S_OLD_FORTRESS) { + struct base_type *pbase; + + pbase = get_base_by_gui_type(BASE_GUI_FORTRESS, NULL, NULL); + if (pbase) { + dbv_set(extras, extra_index(base_extra_get(pbase))); + } + } else if (sp == S_OLD_AIRBASE) { + struct base_type *pbase; + + pbase = get_base_by_gui_type(BASE_GUI_AIRBASE, NULL, NULL); + if (pbase) { + dbv_set(extras, extra_index(base_extra_get(pbase))); + } + } else if (sp == S_OLD_ROAD) { + struct road_type *proad; + + proad = road_by_compat_special(ROCO_ROAD); + if (proad) { + dbv_set(extras, extra_index(road_extra_get(proad))); + } + } else if (sp == S_OLD_RAILROAD) { + struct road_type *proad; + + proad = road_by_compat_special(ROCO_RAILROAD); + if (proad) { + dbv_set(extras, extra_index(road_extra_get(proad))); + } + } else if (sp == S_OLD_RIVER) { + struct road_type *proad; + + proad = road_by_compat_special(ROCO_RIVER); + if (proad) { + dbv_set(extras, extra_index(road_extra_get(proad))); + } + } else { + struct extra_type *pextra = NULL; + enum extra_cause cause = EC_COUNT; + + /* Converting from old hardcoded specials to as sensible extra as we can */ + switch (sp) { + case S_IRRIGATION: + case S_FARMLAND: + /* If old savegame has both irrigation and farmland, EC_IRRIGATION + * gets applied twice, which hopefully has the correct result. */ + cause = EC_IRRIGATION; + break; + case S_MINE: + cause = EC_MINE; + break; + case S_POLLUTION: + cause = EC_POLLUTION; + break; + case S_HUT: + cause = EC_HUT; + break; + case S_FALLOUT: + cause = EC_FALLOUT; + break; + default: + pextra = extra_type_by_rule_name(special_rule_name(sp)); + break; + } + + if (cause != EC_COUNT) { + struct tile *vtile = tile_virtual_new(ptile); + + /* Do not let the extras already set to the real tile mess with setup + * of the player tiles if that's what we're doing. */ + dbv_to_bv(vtile->extras.vec, extras); + + /* It's ok not to know which player or which unit originally built the extra - + * in the rules used when specials were saved these could not have made any + * difference. */ + pextra = next_extra_for_tile(vtile, cause, NULL, NULL); + + tile_virtual_destroy(vtile); + } + + if (pextra) { + dbv_set(extras, extra_index(pextra)); + } + } + } + } +} + +/**************************************************************************** + Complicated helper function for loading specials from a savegame. + + 'ch' gives the character loaded from the savegame. Specials are packed + in four to a character in hex notation. 'index' is a mapping of + savegame bit -> special bit. S_LAST is used to mark unused savegame bits. +****************************************************************************/ +static void set_savegame_special_bv(struct tile *ptile, bv_extras *extras, + char ch, + const enum tile_special_type *idx) { int i, bin; const char *pch = strchr(hex_chars, ch); @@ -842,12 +964,45 @@ static void set_savegame_special(struct tile *ptile, bv_extras *extras, Helper function for loading bases from a savegame. 'ch' gives the character loaded from the savegame. Bases are packed - in four to a character in hex notation. 'index' is a mapping of + in four to a character in hex notation. 'index' is a mapping of + savegame bit -> base bit. +****************************************************************************/ +static void set_savegame_bases_dbv(struct dbv *extras, + char ch, + struct base_type **idx) +{ + int i, bin; + const char *pch = strchr(hex_chars, ch); + + if (!pch || ch == '\0') { + log_error("Unknown hex value: '%c' (%d)", ch, ch); + bin = 0; + } else { + bin = pch - hex_chars; + } + + for (i = 0; i < 4; i++) { + struct base_type *pbase = idx[i]; + + if (pbase == NULL) { + continue; + } + if (bin & (1 << i)) { + dbv_set(extras, extra_index(base_extra_get(pbase))); + } + } +} + +/**************************************************************************** + Helper function for loading bases from a savegame. + + 'ch' gives the character loaded from the savegame. Bases are packed + in four to a character in hex notation. 'index' is a mapping of savegame bit -> base bit. ****************************************************************************/ -static void set_savegame_bases(bv_extras *extras, - char ch, - struct base_type **idx) +static void set_savegame_bases_bv(bv_extras *extras, + char ch, + struct base_type **idx) { int i, bin; const char *pch = strchr(hex_chars, ch); @@ -877,17 +1032,52 @@ static void set_savegame_bases(bv_extras *extras, ch is the character read from the map, and n is the number of the special (0 for special_1, 1 for special_2). ****************************************************************************/ -static void set_savegame_old_resource(struct extra_type **r, - const struct terrain *terrain, - bv_extras *extras, - char ch, int n) +static void set_savegame_old_resource_dbv(struct extra_type **r, + const struct terrain *terrain, + struct dbv *extras, + char ch, int n) +{ + fc_assert_ret(n == 0 || n == 1); + + /* If resource is already set to non-NULL or there is no resource found + * in this half-byte, then abort */ + if (*r || !(ascii_hex2bin (ch, 0) & 0x1) || !terrain->resources[0]) { + return; + } + + /* Note that we must handle the case of special_2 set when there is + * only one resource defined for the terrain (example, Shields on + * Grassland terrain where both resources are identical) */ + if (n == 0 || !terrain->resources[1]) { + *r = terrain->resources[0]; + } else { + *r = terrain->resources[1]; + } + + if ((*r) != NULL) { + dbv_set(extras, extra_index(*r)); + } +} + +/**************************************************************************** + Complicated helper function for reading resources from a savegame. + This reads resources saved in the specials bitvector. + ch is the character read from the map, and n is the number of the special + (0 for special_1, 1 for special_2). +****************************************************************************/ +static void set_savegame_old_resource_bv(struct extra_type **r, + const struct terrain *terrain, + bv_extras *extras, + char ch, int n) { fc_assert_ret(n == 0 || n == 1); + /* If resource is already set to non-NULL or there is no resource found * in this half-byte, then abort */ if (*r || !(ascii_hex2bin (ch, 0) & 0x1) || !terrain->resources[0]) { return; } + /* Note that we must handle the case of special_2 set when there is * only one resource defined for the terrain (example, Shields on * Grassland terrain where both resources are identical) */ @@ -963,34 +1153,36 @@ static void map_load(struct section_file *file, LOAD_MAP_DATA(ch, nat_y, ptile, secfile_lookup_str(file, buf, nat_y), - set_savegame_special(ptile, &ptile->extras, - ch, special_order + 4 * j)); + set_savegame_special_bv(ptile, &ptile->extras, + ch, special_order + 4 * j)); } special_halfbyte_iterate_end; } else { /* get 4-bit segments of 16-bit "special" field. */ LOAD_MAP_DATA(ch, nat_y, ptile, secfile_lookup_str(file, "map.l%03d", nat_y), - set_savegame_special(ptile, &ptile->extras, - ch, default_specials + 0)); + set_savegame_special_bv(ptile, &ptile->extras, + ch, default_specials + 0)); LOAD_MAP_DATA(ch, nat_y, ptile, secfile_lookup_str(file, "map.u%03d", nat_y), - set_savegame_special(ptile, &ptile->extras, - ch, default_specials + 4)); + set_savegame_special_bv(ptile, &ptile->extras, + ch, default_specials + 4)); LOAD_MAP_DATA(ch, nat_y, ptile, secfile_lookup_str(file, "map.n%03d", nat_y), - set_savegame_special(ptile, &ptile->extras, - ch, default_specials + 8)); + set_savegame_special_bv(ptile, &ptile->extras, + ch, default_specials + 8)); LOAD_MAP_DATA(ch, nat_y, ptile, secfile_lookup_str(file, "map.f%03d", nat_y), - set_savegame_special(ptile, &ptile->extras, - ch, default_specials + 12)); + set_savegame_special_bv(ptile, &ptile->extras, + ch, default_specials + 12)); /* Setup resources (from half-bytes 1 and 3 of old savegames) */ LOAD_MAP_DATA(ch, nat_y, ptile, secfile_lookup_str(file, "map.l%03d", nat_y), - set_savegame_old_resource(&ptile->resource, ptile->terrain, &ptile->extras, ch, 0)); + set_savegame_old_resource_bv(&ptile->resource, ptile->terrain, + &ptile->extras, ch, 0)); LOAD_MAP_DATA(ch, nat_y, ptile, secfile_lookup_str(file, "map.n%03d", nat_y), - set_savegame_old_resource(&ptile->resource, ptile->terrain, &ptile->extras, ch, 1)); + set_savegame_old_resource_bv(&ptile->resource, ptile->terrain, + &ptile->extras, ch, 1)); } /* after the resources are loaded, indicate those currently valid */ @@ -1021,7 +1213,7 @@ static void map_load(struct section_file *file, LOAD_MAP_DATA(ch, nat_y, ptile, secfile_lookup_str_default(file, zeroline, buf, nat_y), - set_savegame_bases(&ptile->extras, ch, base_order + 4 * j)); + set_savegame_bases_bv(&ptile->extras, ch, base_order + 4 * j)); } bases_halfbyte_iterate_end; } } @@ -2849,34 +3041,34 @@ static void player_load_vision(struct player *plr, int plrno, sprintf (buf, "player%d.map_spe%02d_%%03d", plrno, j); LOAD_MAP_DATA(ch, vnat_y, ptile, secfile_lookup_str(file, buf, vnat_y), - set_savegame_special(ptile, &map_get_player_tile(ptile, plr)->extras, - ch, special_order + 4 * j)); + set_savegame_special_dbv(ptile, &map_get_player_tile(ptile, plr)->extras, + ch, special_order + 4 * j)); } special_halfbyte_iterate_end; } else { /* get 4-bit segments of 12-bit "special" field. */ LOAD_MAP_DATA(ch, vnat_y, ptile, secfile_lookup_str(file, "player%d.map_l%03d", plrno, vnat_y), - set_savegame_special(ptile, &map_get_player_tile(ptile, plr)->extras, - ch, default_specials + 0)); + set_savegame_special_dbv(ptile, &map_get_player_tile(ptile, plr)->extras, + ch, default_specials + 0)); LOAD_MAP_DATA(ch, vnat_y, ptile, secfile_lookup_str(file, "player%d.map_u%03d", plrno, vnat_y), - set_savegame_special(ptile, &map_get_player_tile(ptile, plr)->extras, - ch, default_specials + 4)); + set_savegame_special_dbv(ptile, &map_get_player_tile(ptile, plr)->extras, + ch, default_specials + 4)); LOAD_MAP_DATA(ch, vnat_y, ptile, secfile_lookup_str_default (file, NULL, "player%d.map_n%03d", plrno, vnat_y), - set_savegame_special(ptile, &map_get_player_tile(ptile, plr)->extras, - ch, default_specials + 8)); + set_savegame_special_dbv(ptile, &map_get_player_tile(ptile, plr)->extras, + ch, default_specials + 8)); LOAD_MAP_DATA(ch, vnat_y, ptile, secfile_lookup_str(file, "map.l%03d", vnat_y), - set_savegame_old_resource(&map_get_player_tile(ptile, plr)->resource, - ptile->terrain, - &map_get_player_tile(ptile, plr)->extras, ch, 0)); + set_savegame_old_resource_dbv(&map_get_player_tile(ptile, plr)->resource, + ptile->terrain, + &map_get_player_tile(ptile, plr)->extras, ch, 0)); LOAD_MAP_DATA(ch, vnat_y, ptile, secfile_lookup_str(file, "map.n%03d", vnat_y), - set_savegame_old_resource(&map_get_player_tile(ptile, plr)->resource, - ptile->terrain, - &map_get_player_tile(ptile, plr)->extras, ch, 1)); + set_savegame_old_resource_dbv(&map_get_player_tile(ptile, plr)->resource, + ptile->terrain, + &map_get_player_tile(ptile, plr)->extras, ch, 1)); } if (has_capability("bases", savefile_options)) { @@ -2897,8 +3089,8 @@ static void player_load_vision(struct player *plr, int plrno, LOAD_MAP_DATA(ch, vnat_y, ptile, secfile_lookup_str_default(file, zeroline, buf, vnat_y), - set_savegame_bases(&map_get_player_tile(ptile, plr)->extras, - ch, base_order + 4 * j)); + set_savegame_bases_dbv(&map_get_player_tile(ptile, plr)->extras, + ch, base_order + 4 * j)); } bases_halfbyte_iterate_end; } else { /* Already loaded fortresses and airbases as part of specials */ diff --git a/server/maphand.c b/server/maphand.c index fdf14d5b8d..826a10f87f 100644 --- a/server/maphand.c +++ b/server/maphand.c @@ -535,7 +535,7 @@ void send_tile_info(struct conn_list *dest, struct tile *ptile, : MAX_EXTRA_TYPES; if (pplayer != NULL) { - info.extras = map_get_player_tile(ptile, pplayer)->extras; + dbv_to_bv(info.extras.vec, &(map_get_player_tile(ptile, pplayer)->extras)); } else { info.extras = ptile->extras; } @@ -571,7 +571,7 @@ void send_tile_info(struct conn_list *dest, struct tile *ptile, ? extra_number(plrtile->resource) : MAX_EXTRA_TYPES; - info.extras = plrtile->extras; + dbv_to_bv(info.extras.vec, &(plrtile->extras)); /* Labels never change, so they are not subject to fog of war */ if (ptile->label != NULL) { @@ -1256,7 +1256,7 @@ static void player_tile_init(struct tile *ptile, struct player *pplayer) plrtile->owner = NULL; plrtile->extras_owner = NULL; plrtile->site = NULL; - BV_CLR_ALL(plrtile->extras); + dbv_init(&(plrtile->extras), extra_count()); if (!game.server.last_updated_year) { plrtile->last_updated = game.info.turn; } else { @@ -1278,6 +1278,8 @@ static void player_tile_free(struct tile *ptile, struct player *pplayer) if (plrtile->site != NULL) { vision_site_destroy(plrtile->site); } + + dbv_free(&(plrtile->extras)); } /**************************************************************************** @@ -1328,16 +1330,16 @@ bool update_player_tile_knowledge(struct player *pplayer, struct tile *ptile) struct player_tile *plrtile = map_get_player_tile(ptile, pplayer); if (plrtile->terrain != ptile->terrain - || !BV_ARE_EQUAL(plrtile->extras, ptile->extras) + || !bv_match_dbv(&(plrtile->extras), ptile->extras.vec) || plrtile->resource != ptile->resource || plrtile->owner != tile_owner(ptile) || plrtile->extras_owner != extra_owner(ptile)) { plrtile->terrain = ptile->terrain; extra_type_iterate(pextra) { if (player_knows_extra_exist(pplayer, pextra, ptile)) { - BV_SET(plrtile->extras, extra_number(pextra)); + dbv_set(&(plrtile->extras), extra_number(pextra)); } else { - BV_CLR(plrtile->extras, extra_number(pextra)); + dbv_clr(&(plrtile->extras), extra_number(pextra)); } } extra_type_iterate_end; plrtile->resource = ptile->resource; diff --git a/server/maphand.h b/server/maphand.h index 0c239527e6..557e6e47eb 100644 --- a/server/maphand.h +++ b/server/maphand.h @@ -32,7 +32,7 @@ struct player_tile { struct terrain *terrain; /* NULL for unknown tiles */ struct player *owner; /* NULL for unowned */ struct player *extras_owner; - bv_extras extras; + struct dbv extras; /* If you build a city with an unknown square within city radius the square stays unknown. However, we still have to keep count diff --git a/server/savegame2.c b/server/savegame2.c index c70fbec6dc..e4d4d7e0bc 100644 --- a/server/savegame2.c +++ b/server/savegame2.c @@ -292,12 +292,20 @@ static int unquote_block(const char *const quoted_, void *dest, static void worklist_load(struct section_file *file, int wlist_max_length, struct worklist *pwl, const char *path, ...); static void unit_ordering_apply(void); -static void sg_extras_set(bv_extras *extras, char ch, struct extra_type **idx); -static void sg_special_set(struct tile *ptile, bv_extras *extras, char ch, - const enum tile_special_type *idx, - bool rivers_overlay); -static void sg_bases_set(bv_extras *extras, char ch, struct base_type **idx); -static void sg_roads_set(bv_extras *extras, char ch, struct road_type **idx); +static void sg_extras_set_dbv(struct dbv *extras, char ch, + struct extra_type **idx); +static void sg_extras_set_bv(bv_extras *extras, char ch, + struct extra_type **idx); +static void sg_special_set_dbv(struct tile *ptile, struct dbv *extras, char ch, + const enum tile_special_type *idx, + bool rivers_overlay); +static void sg_special_set_bv(struct tile *ptile, bv_extras *extras, char ch, + const enum tile_special_type *idx, + bool rivers_overlay); +static void sg_bases_set_dbv(struct dbv *extras, char ch, struct base_type **idx); +static void sg_bases_set_bv(bv_extras *extras, char ch, struct base_type **idx); +static void sg_roads_set_dbv(struct dbv *extras, char ch, struct road_type **idx); +static void sg_roads_set_bv(bv_extras *extras, char ch, struct road_type **idx); static struct extra_type *char2resource(char c); static int char2num(char ch); static struct terrain *char2terrain(char ch); @@ -801,7 +809,41 @@ static void unit_ordering_apply(void) in four to a character in hex notation. 'index' is a mapping of savegame bit -> base bit. ****************************************************************************/ -static void sg_extras_set(bv_extras *extras, char ch, struct extra_type **idx) +static void sg_extras_set_dbv(struct dbv *extras, char ch, + struct extra_type **idx) +{ + int i, bin; + const char *pch = strchr(hex_chars, ch); + + if (!pch || ch == '\0') { + log_sg("Unknown hex value: '%c' (%d)", ch, ch); + bin = 0; + } else { + bin = pch - hex_chars; + } + + for (i = 0; i < 4; i++) { + struct extra_type *pextra = idx[i]; + + if (pextra == NULL) { + continue; + } + if ((bin & (1 << i)) + && (wld.map.server.have_huts || !is_extra_caused_by(pextra, EC_HUT))) { + dbv_set(extras, extra_index(pextra)); + } + } +} + +/************************************************************************//** + Helper function for loading extras from a savegame. + + 'ch' gives the character loaded from the savegame. Extras are packed + in four to a character in hex notation. 'index' is a mapping of + savegame bit -> base bit. +****************************************************************************/ +static void sg_extras_set_bv(bv_extras *extras, char ch, + struct extra_type **idx) { int i, bin; const char *pch = strchr(hex_chars, ch); @@ -826,16 +868,152 @@ static void sg_extras_set(bv_extras *extras, char ch, struct extra_type **idx) } } -/**************************************************************************** +/************************************************************************//** + Complicated helper function for loading specials from a savegame. + + 'ch' gives the character loaded from the savegame. Specials are packed + in four to a character in hex notation. 'index' is a mapping of + savegame bit -> special bit. S_LAST is used to mark unused savegame bits. +****************************************************************************/ +static void sg_special_set_dbv(struct tile *ptile, struct dbv *extras, char ch, + const enum tile_special_type *idx, + bool rivers_overlay) +{ + int i, bin; + const char *pch = strchr(hex_chars, ch); + + if (!pch || ch == '\0') { + log_sg("Unknown hex value: '%c' (%d)", ch, ch); + bin = 0; + } else { + bin = pch - hex_chars; + } + + for (i = 0; i < 4; i++) { + enum tile_special_type sp = idx[i]; + + if (sp == S_LAST) { + continue; + } + if (rivers_overlay && sp != S_OLD_RIVER) { + continue; + } + + if (sp == S_HUT && !wld.map.server.have_huts) { + /* It would be logical to have this in the saving side - + * really not saving the huts in the first place, BUT + * 1) They have been saved by older versions, so we + * have to deal with such savegames. + * 2) This makes scenario author less likely to lose + * one's work completely after carefully placing huts + * and then saving with 'have_huts' disabled. */ + continue; + } + + if (bin & (1 << i)) { + if (sp == S_OLD_ROAD) { + struct road_type *proad; + + proad = road_by_compat_special(ROCO_ROAD); + if (proad) { + BV_SET(*extras, extra_index(road_extra_get(proad))); + } + } else if (sp == S_OLD_RAILROAD) { + struct road_type *proad; + + proad = road_by_compat_special(ROCO_RAILROAD); + if (proad) { + BV_SET(*extras, extra_index(road_extra_get(proad))); + } + } else if (sp == S_OLD_RIVER) { + struct road_type *proad; + + proad = road_by_compat_special(ROCO_RIVER); + if (proad) { + BV_SET(*extras, extra_index(road_extra_get(proad))); + } + } else { + struct extra_type *pextra = NULL; + enum extra_cause cause = EC_COUNT; + + /* Converting from old hardcoded specials to as sensible extra as we can */ + switch (sp) { + case S_IRRIGATION: + case S_FARMLAND: + /* If old savegame has both irrigation and farmland, EC_IRRIGATION + * gets applied twice, which hopefully has the correct result. */ + cause = EC_IRRIGATION; + break; + case S_MINE: + cause = EC_MINE; + break; + case S_POLLUTION: + cause = EC_POLLUTION; + break; + case S_HUT: + cause = EC_HUT; + break; + case S_FALLOUT: + cause = EC_FALLOUT; + break; + default: + pextra = extra_type_by_rule_name(special_rule_name(sp)); + break; + } + + if (cause != EC_COUNT) { + struct tile *vtile = tile_virtual_new(ptile); + struct terrain *pterr = tile_terrain(vtile); + + /* Do not let the extras already set to the real tile mess with setup + * of the player tiles if that's what we're doing. */ + dbv_to_bv(vtile->extras.vec, extras); + + /* It's ok not to know which player or which unit originally built the extra - + * in the rules used when specials were saved these could not have made any + * difference. */ + /* Can't use next_extra_for_tile() as it works for buildable extras only. */ + + if ((cause != EC_IRRIGATION || pterr->irrigation_result == pterr) + && (cause != EC_MINE || pterr->mining_result == pterr) + && (cause != EC_BASE || pterr->base_time != 0) + && (cause != EC_ROAD || pterr->road_time != 0)) { + extra_type_by_cause_iterate(cause, candidate) { + if (!tile_has_extra(vtile, candidate)) { + if ((!is_extra_caused_by(candidate, EC_BASE) + || tile_city(vtile) != NULL + || extra_base_get(candidate)->border_sq <= 0) + && are_reqs_active(NULL, tile_owner(vtile), NULL, NULL, vtile, + NULL, NULL, NULL, NULL, NULL, &candidate->reqs, + RPT_POSSIBLE)) { + pextra = candidate; + break; + } + } + } extra_type_by_cause_iterate_end; + } + + tile_virtual_destroy(vtile); + } + + if (pextra) { + dbv_set(extras, extra_index(pextra)); + } + } + } + } +} + +/************************************************************************//** Complicated helper function for loading specials from a savegame. 'ch' gives the character loaded from the savegame. Specials are packed in four to a character in hex notation. 'index' is a mapping of savegame bit -> special bit. S_LAST is used to mark unused savegame bits. ****************************************************************************/ -static void sg_special_set(struct tile *ptile, bv_extras *extras, char ch, - const enum tile_special_type *idx, - bool rivers_overlay) +static void sg_special_set_bv(struct tile *ptile, bv_extras *extras, char ch, + const enum tile_special_type *idx, + bool rivers_overlay) { int i, bin; const char *pch = strchr(hex_chars, ch); @@ -969,7 +1147,39 @@ static void sg_special_set(struct tile *ptile, bv_extras *extras, char ch, in four to a character in hex notation. 'index' is a mapping of savegame bit -> base bit. ****************************************************************************/ -static void sg_bases_set(bv_extras *extras, char ch, struct base_type **idx) +static void sg_bases_set_dbv(struct dbv *extras, char ch, + struct base_type **idx) +{ + int i, bin; + const char *pch = strchr(hex_chars, ch); + + if (!pch || ch == '\0') { + log_sg("Unknown hex value: '%c' (%d)", ch, ch); + bin = 0; + } else { + bin = pch - hex_chars; + } + + for (i = 0; i < 4; i++) { + struct base_type *pbase = idx[i]; + + if (pbase == NULL) { + continue; + } + if (bin & (1 << i)) { + dbv_set(extras, extra_index(base_extra_get(pbase))); + } + } +} + +/************************************************************************//** + Helper function for loading bases from a savegame. + + 'ch' gives the character loaded from the savegame. Bases are packed + in four to a character in hex notation. 'index' is a mapping of + savegame bit -> base bit. +****************************************************************************/ +static void sg_bases_set_bv(bv_extras *extras, char ch, struct base_type **idx) { int i, bin; const char *pch = strchr(hex_chars, ch); @@ -1000,7 +1210,38 @@ static void sg_bases_set(bv_extras *extras, char ch, struct base_type **idx) in four to a character in hex notation. 'index' is a mapping of savegame bit -> road bit. ****************************************************************************/ -static void sg_roads_set(bv_extras *extras, char ch, struct road_type **idx) +static void sg_roads_set_dbv(struct dbv *extras, char ch, struct road_type **idx) +{ + int i, bin; + const char *pch = strchr(hex_chars, ch); + + if (!pch || ch == '\0') { + log_sg("Unknown hex value: '%c' (%d)", ch, ch); + bin = 0; + } else { + bin = pch - hex_chars; + } + + for (i = 0; i < 4; i++) { + struct road_type *proad = idx[i]; + + if (proad == NULL) { + continue; + } + if (bin & (1 << i)) { + dbv_set(extras, extra_index(road_extra_get(proad))); + } + } +} + +/************************************************************************//** + Helper function for loading roads from a savegame. + + 'ch' gives the character loaded from the savegame. Roads are packed + in four to a character in hex notation. 'index' is a mapping of + savegame bit -> road bit. +****************************************************************************/ +static void sg_roads_set_bv(bv_extras *extras, char ch, struct road_type **idx) { int i, bin; const char *pch = strchr(hex_chars, ch); @@ -2014,7 +2255,8 @@ static void sg_load_map_tiles_extras(struct loaddata *loading) /* Load extras. */ halfbyte_iterate_extras(j, loading->extra.size) { - LOAD_MAP_CHAR(ch, ptile, sg_extras_set(&ptile->extras, ch, loading->extra.order + 4 * j), + LOAD_MAP_CHAR(ch, ptile, sg_extras_set_bv(&ptile->extras, + ch, loading->extra.order + 4 * j), loading->file, "map.e%02d_%04d", j); } halfbyte_iterate_extras_end; } @@ -2029,8 +2271,8 @@ static void sg_load_map_tiles_bases(struct loaddata *loading) /* Load bases. */ halfbyte_iterate_bases(j, loading->base.size) { - LOAD_MAP_CHAR(ch, ptile, sg_bases_set(&ptile->extras, ch, - loading->base.order + 4 * j), + LOAD_MAP_CHAR(ch, ptile, sg_bases_set_bv(&ptile->extras, ch, + loading->base.order + 4 * j), loading->file, "map.b%02d_%04d", j); } halfbyte_iterate_bases_end; } @@ -2045,8 +2287,8 @@ static void sg_load_map_tiles_roads(struct loaddata *loading) /* Load roads. */ halfbyte_iterate_roads(j, loading->road.size) { - LOAD_MAP_CHAR(ch, ptile, sg_roads_set(&ptile->extras, ch, - loading->road.order + 4 * j), + LOAD_MAP_CHAR(ch, ptile, sg_roads_set_bv(&ptile->extras, ch, + loading->road.order + 4 * j), loading->file, "map.r%02d_%04d", j); } halfbyte_iterate_roads_end; } @@ -2075,9 +2317,9 @@ static void sg_load_map_tiles_specials(struct loaddata *loading, * the rivers overlay but no other specials. Scenarios that encode things * this way should have the "riversoverlay" capability. */ halfbyte_iterate_special(j, loading->special.size) { - LOAD_MAP_CHAR(ch, ptile, sg_special_set(ptile, &ptile->extras, ch, - loading->special.order + 4 * j, - rivers_overlay), + LOAD_MAP_CHAR(ch, ptile, sg_special_set_bv(ptile, &ptile->extras, ch, + loading->special.order + 4 * j, + rivers_overlay), loading->file, "map.spe%02d_%04d", j); } halfbyte_iterate_special_end; } @@ -4661,24 +4903,25 @@ static void sg_load_player_vision(struct loaddata *loading, /* Load player map (extras). */ halfbyte_iterate_extras(j, loading->extra.size) { LOAD_MAP_CHAR(ch, ptile, - sg_extras_set(&map_get_player_tile(ptile, plr)->extras, - ch, loading->extra.order + 4 * j), + sg_extras_set_dbv(&(map_get_player_tile(ptile, plr)->extras), + ch, loading->extra.order + 4 * j), loading->file, "player%d.map_e%02d_%04d", plrno, j); } halfbyte_iterate_extras_end; } else { /* Load player map (specials). */ halfbyte_iterate_special(j, loading->special.size) { LOAD_MAP_CHAR(ch, ptile, - sg_special_set(ptile, &map_get_player_tile(ptile, plr)->extras, - ch, loading->special.order + 4 * j, FALSE), + sg_special_set_dbv(ptile, + &(map_get_player_tile(ptile, plr)->extras), + ch, loading->special.order + 4 * j, FALSE), loading->file, "player%d.map_spe%02d_%04d", plrno, j); } halfbyte_iterate_special_end; /* Load player map (bases). */ halfbyte_iterate_bases(j, loading->base.size) { LOAD_MAP_CHAR(ch, ptile, - sg_bases_set(&map_get_player_tile(ptile, plr)->extras, - ch, loading->base.order + 4 * j), + sg_bases_set_dbv(&(map_get_player_tile(ptile, plr)->extras), + ch, loading->base.order + 4 * j), loading->file, "player%d.map_b%02d_%04d", plrno, j); } halfbyte_iterate_bases_end; @@ -4687,8 +4930,8 @@ static void sg_load_player_vision(struct loaddata *loading, /* 2.5.0 or newer */ halfbyte_iterate_roads(j, loading->road.size) { LOAD_MAP_CHAR(ch, ptile, - sg_roads_set(&map_get_player_tile(ptile, plr)->extras, - ch, loading->road.order + 4 * j), + sg_roads_set_dbv(&(map_get_player_tile(ptile, plr)->extras), + ch, loading->road.order + 4 * j), loading->file, "player%d.map_r%02d_%04d", plrno, j); } halfbyte_iterate_roads_end; } diff --git a/server/savegame3.c b/server/savegame3.c index 0dec35d568..29cf529771 100644 --- a/server/savegame3.c +++ b/server/savegame3.c @@ -305,9 +305,12 @@ static void worklist_save(struct section_file *file, int max_length, const char *path, ...); static void unit_ordering_calc(void); static void unit_ordering_apply(void); -static void sg_extras_set(bv_extras *extras, char ch, struct extra_type **idx); -static char sg_extras_get(bv_extras extras, struct extra_type *presource, - const int *idx); +static void sg_extras_set_dbv(struct dbv *extras, char ch, struct extra_type **idx); +static void sg_extras_set_bv(bv_extras *extras, char ch, struct extra_type **idx); +static char sg_extras_get_dbv(struct dbv *extras, struct extra_type *presource, + const int *idx); +static char sg_extras_get_bv(bv_extras extras, struct extra_type *presource, + const int *idx); static char num2char(unsigned int num); static int char2num(char ch); static struct terrain *char2terrain(char ch); @@ -1087,7 +1090,41 @@ static void unit_ordering_apply(void) in four to a character in hex notation. 'index' is a mapping of savegame bit -> base bit. ****************************************************************************/ -static void sg_extras_set(bv_extras *extras, char ch, struct extra_type **idx) +static void sg_extras_set_dbv(struct dbv *extras, char ch, + struct extra_type **idx) +{ + int i, bin; + const char *pch = strchr(hex_chars, ch); + + if (!pch || ch == '\0') { + log_sg("Unknown hex value: '%c' (%d)", ch, ch); + bin = 0; + } else { + bin = pch - hex_chars; + } + + for (i = 0; i < 4; i++) { + struct extra_type *pextra = idx[i]; + + if (pextra == NULL) { + continue; + } + if ((bin & (1 << i)) + && (wld.map.server.have_huts || !is_extra_caused_by(pextra, EC_HUT))) { + dbv_set(extras, extra_index(pextra)); + } + } +} + +/************************************************************************//** + Helper function for loading extras from a savegame. + + 'ch' gives the character loaded from the savegame. Extras are packed + in four to a character in hex notation. 'index' is a mapping of + savegame bit -> base bit. +****************************************************************************/ +static void sg_extras_set_bv(bv_extras *extras, char ch, + struct extra_type **idx) { int i, bin; const char *pch = strchr(hex_chars, ch); @@ -1118,8 +1155,41 @@ static void sg_extras_set(bv_extras *extras, char ch, struct extra_type **idx) Extras are packed in four to a character in hex notation. 'index' specifies which set of extras are included in this character. ****************************************************************************/ -static char sg_extras_get(bv_extras extras, struct extra_type *presource, - const int *idx) +static char sg_extras_get_dbv(struct dbv *extras, struct extra_type *presource, + const int *idx) +{ + int i, bin = 0; + int max = dbv_bits(extras); + + for (i = 0; i < 4; i++) { + int extra = idx[i]; + + if (extra < 0) { + break; + } + + if (extra < max + && (dbv_isset(extras, extra) + /* An invalid resource, a resource that can't exist at the tile's + * current terrain, isn't in the bit extra vector. Save it so it + * can return if the tile's terrain changes to something it can + * exist on. */ + || extra_by_number(extra) == presource)) { + bin |= (1 << i); + } + } + + return hex_chars[bin]; +} + +/************************************************************************//** + Helper function for saving extras into a savegame. + + Extras are packed in four to a character in hex notation. 'index' + specifies which set of extras are included in this character. +****************************************************************************/ +static char sg_extras_get_bv(bv_extras extras, struct extra_type *presource, + const int *idx) { int i, bin = 0; @@ -2723,7 +2793,8 @@ static void sg_load_map_tiles_extras(struct loaddata *loading) /* Load extras. */ halfbyte_iterate_extras(j, loading->extra.size) { - LOAD_MAP_CHAR(ch, ptile, sg_extras_set(&ptile->extras, ch, loading->extra.order + 4 * j), + LOAD_MAP_CHAR(ch, ptile, sg_extras_set_bv(&ptile->extras, ch, + loading->extra.order + 4 * j), loading->file, "map.e%02d_%04d", j); } halfbyte_iterate_extras_end; @@ -2764,7 +2835,7 @@ static void sg_save_map_tiles_extras(struct savedata *saving) mod[l] = 4 * j + l; } } - SAVE_MAP_CHAR(ptile, sg_extras_get(ptile->extras, ptile->resource, mod), + SAVE_MAP_CHAR(ptile, sg_extras_get_bv(ptile->extras, ptile->resource, mod), saving->file, "map.e%02d_%04d", j); } halfbyte_iterate_extras_end; } @@ -6328,8 +6399,8 @@ static void sg_load_player_vision(struct loaddata *loading, /* Load player map (extras). */ halfbyte_iterate_extras(j, loading->extra.size) { LOAD_MAP_CHAR(ch, ptile, - sg_extras_set(&map_get_player_tile(ptile, plr)->extras, - ch, loading->extra.order + 4 * j), + sg_extras_set_dbv(&(map_get_player_tile(ptile, plr)->extras), + ch, loading->extra.order + 4 * j), loading->file, "player%d.map_e%02d_%04d", plrno, j); } halfbyte_iterate_extras_end; @@ -6633,9 +6704,9 @@ static void sg_save_player_vision(struct savedata *saving, } SAVE_MAP_CHAR(ptile, - sg_extras_get(map_get_player_tile(ptile, plr)->extras, - map_get_player_tile(ptile, plr)->resource, - mod), + sg_extras_get_dbv(&(map_get_player_tile(ptile, plr)->extras), + map_get_player_tile(ptile, plr)->resource, + mod), saving->file, "player%d.map_e%02d_%04d", plrno, j); } halfbyte_iterate_extras_end; diff --git a/server/unittools.c b/server/unittools.c index 10db2c4621..e1fd63fb9f 100644 --- a/server/unittools.c +++ b/server/unittools.c @@ -2892,13 +2892,18 @@ bool do_paradrop(struct unit *punit, struct tile *ptile, /* Only take in account values from player map. */ const struct player_tile *plrtile = map_get_player_tile(ptile, pplayer); - if (NULL == plrtile->site - && !is_native_to_class(unit_class_get(punit), plrtile->terrain, - &(plrtile->extras))) { - notify_player(pplayer, ptile, E_BAD_COMMAND, ftc_server, - _("This unit cannot paradrop into %s."), - terrain_name_translation(plrtile->terrain)); - return FALSE; + if (NULL == plrtile->site) { + bv_extras fbv; + + dbv_to_bv(fbv.vec, &(plrtile->extras)); + + if (!is_native_to_class(unit_class_get(punit), plrtile->terrain, + &fbv)) { + notify_player(pplayer, ptile, E_BAD_COMMAND, ftc_server, + _("This unit cannot paradrop into %s."), + terrain_name_translation(plrtile->terrain)); + return FALSE; + } } if (NULL != plrtile->site -- 2.40.1