From 4a44bf927b83dae988ebcd99a85480dc720d39fa Mon Sep 17 00:00:00 2001 From: Alina Lenk Date: Sat, 18 May 2024 20:12:52 +0200 Subject: [PATCH 2/7] Client: Keep track of continent and ocean sizes See RM #642 Signed-off-by: Alina Lenk --- ai/tex/texaiworld.c | 2 +- client/packhand.c | 119 +++++++++++++++++++++++++++++++++++++++++++- client/text.c | 19 ++++++- common/map.c | 22 ++++++-- common/map.h | 2 +- common/map_types.h | 18 +++++-- 6 files changed, 169 insertions(+), 13 deletions(-) diff --git a/ai/tex/texaiworld.c b/ai/tex/texaiworld.c index 353fb1e72b..26f9d63ea2 100644 --- a/ai/tex/texaiworld.c +++ b/ai/tex/texaiworld.c @@ -101,7 +101,7 @@ struct civ_map *texai_map_get(void) **************************************************************************/ void texai_map_close(void) { - map_free(&(texai_world.map)); + map_free(&(texai_world.map), TRUE); } /**********************************************************************//** diff --git a/client/packhand.c b/client/packhand.c index a16675a78e..b84f21ee88 100644 --- a/client/packhand.c +++ b/client/packhand.c @@ -2238,7 +2238,7 @@ void handle_map_info(const struct packet_map_info *packet) int ts_topo; if (!map_is_empty()) { - map_free(&(wld.map)); + map_free(&(wld.map), FALSE); free_city_map_index(); } @@ -3119,6 +3119,119 @@ void handle_spaceship_info(const struct packet_spaceship_info *p) } } +/**********************************************************************//** + Returns the location of the adjacency count between the two given + continents or oceans, where ID 0 stands for unknown tiles. +**************************************************************************/ +static inline int *continent_adjacency_count(Continent_id cont1, + Continent_id cont2) +{ + if (cont1 > 0) { + if (cont2 == 0) { + return &wld.map.client.continent_unknown_adj_counts[cont1]; + } + } else if (cont1 < 0) { + if (cont2 == 0) { + return &wld.map.client.ocean_unknown_adj_counts[-cont1]; + } + } else { + if (cont2 > 0) { + return &wld.map.client.continent_unknown_adj_counts[cont2]; + } else if (cont2 < 0) { + return &wld.map.client.ocean_unknown_adj_counts[-cont2]; + } + } + return nullptr; +} + +/**********************************************************************//** + Incrementally update continent information (sizes and adjacency) for the + given tile switching from one continent to another. + + ptile is only used to iterate adjacent tiles, i.e. only its index must + be accurate; everything else may be mid-change. +**************************************************************************/ +static inline void update_continent_cache(const struct tile *ptile, + Continent_id old_cont, + Continent_id new_cont) +{ + /* Update known continents */ + if (new_cont > wld.map.num_continents) { + int i; + + /* Expand sizes array */ + wld.map.continent_sizes = fc_realloc(wld.map.continent_sizes, + (new_cont + 1) * sizeof(*wld.map.continent_sizes)); + + /* Expand unknown tile adjacency counts array */ + wld.map.client.continent_unknown_adj_counts = fc_realloc( + wld.map.client.continent_unknown_adj_counts, + (new_cont + 1) * sizeof(*wld.map.client.continent_unknown_adj_counts) + ); + + /* Fill new spots with zeros */ + for (i = wld.map.num_continents + 1; i <= new_cont; i++) { + wld.map.continent_sizes[i] = 0; + wld.map.client.continent_unknown_adj_counts[i] = 0; + } + + wld.map.num_continents = new_cont; + } else if (new_cont < -wld.map.num_oceans) { + int i; + + /* Expand sizes array */ + wld.map.ocean_sizes = fc_realloc(wld.map.ocean_sizes, + (-new_cont + 1) * sizeof(*wld.map.ocean_sizes)); + + /* Expand unknown tile adjacency counts array */ + wld.map.client.ocean_unknown_adj_counts = fc_realloc( + wld.map.client.ocean_unknown_adj_counts, + (-new_cont + 1) * sizeof(*wld.map.client.ocean_unknown_adj_counts) + ); + + /* Fill new spots with zeros */ + for (i = wld.map.num_oceans + 1; i <= -new_cont; i++) { + wld.map.ocean_sizes[i] = 0; + wld.map.client.ocean_unknown_adj_counts[i] = 0; + } + + wld.map.num_oceans = -new_cont; + } + + /* Decrement old continent/ocean size */ + if (old_cont > 0) { + wld.map.continent_sizes[old_cont]--; + } else if (old_cont < 0) { + wld.map.ocean_sizes[-old_cont]--; + } + + /* Increment new continent/ocean size */ + if (new_cont > 0) { + wld.map.continent_sizes[new_cont]++; + } else if (new_cont < 0) { + wld.map.ocean_sizes[-new_cont]++; + } + + /* Update tile adjacency counts */ + adjc_iterate(&(wld.map), ptile, adj_tile) { + Continent_id adj_cont = tile_continent(adj_tile); + int *padjc_count; + + /* Decrement old adjacency */ + padjc_count = continent_adjacency_count(old_cont, adj_cont); + if (padjc_count) { + fc_assert(*padjc_count > 0); + (*padjc_count)--; + } + + /* Increment new adjacency */ + padjc_count = continent_adjacency_count(new_cont, adj_cont); + if (padjc_count) { + (*padjc_count)++; + } + } adjc_iterate_end; +} + /************************************************************************//** Packet tile_info handler. ****************************************************************************/ @@ -3326,8 +3439,10 @@ void handle_tile_info(const struct packet_tile_info *packet) unit_list_clear(ptile->units); } + if (ptile->continent != packet->continent) { + update_continent_cache(ptile, ptile->continent, packet->continent); + } ptile->continent = packet->continent; - wld.map.num_continents = MAX(ptile->continent, wld.map.num_continents); if (packet->label[0] == '\0') { if (ptile->label != NULL) { diff --git a/client/text.c b/client/text.c index 467cd28114..d1c4e01a46 100644 --- a/client/text.c +++ b/client/text.c @@ -167,14 +167,31 @@ const char *popup_info_text(struct tile *ptile) struct player *plr = client_player(); bool flaggy_unit = (punit != nullptr && !is_flagless_to_player(punit, plr)); + Continent_id cont = tile_continent(ptile); astr_clear(&str); index_to_map_pos(&tile_x, &tile_y, tile_index(ptile)); astr_add_line(&str, _("Location: (%d, %d) [%d]"), - tile_x, tile_y, tile_continent(ptile)); + tile_x, tile_y, cont); index_to_native_pos(&nat_x, &nat_y, tile_index(ptile)); astr_add_line(&str, _("Native coordinates: (%d, %d)"), nat_x, nat_y); + + if (cont > 0) { + int size = get_continent_size(cont); + if (wld.map.client.continent_unknown_adj_counts[cont] > 0) { + astr_add_line(&str, _("Continent size: at least %d"), size); + } else { + astr_add_line(&str, _("Continent size: %d"), size); + } + } else if (cont < 0) { + int size = get_ocean_size(-cont); + if (wld.map.client.ocean_unknown_adj_counts[-cont] > 0) { + astr_add_line(&str, _("Ocean size: at least %d"), size); + } else { + astr_add_line(&str, _("Ocean size: %d"), size); + } + } astr_add_line(&str, _("Latitude: %d"), map_signed_latitude(ptile)); diff --git a/common/map.c b/common/map.c index 15c6f583c5..1a613ded3b 100644 --- a/common/map.c +++ b/common/map.c @@ -198,6 +198,9 @@ void map_init(struct civ_map *imap, bool server_side) imap->server.have_huts = FALSE; imap->server.have_resources = FALSE; imap->server.team_placement = MAP_DEFAULT_TEAM_PLACEMENT; + } else { + imap->client.continent_unknown_adj_counts = nullptr; + imap->client.ocean_unknown_adj_counts = nullptr; } } @@ -537,7 +540,7 @@ void main_map_allocate(void) /*******************************************************************//** Frees the allocated memory of the map. ***********************************************************************/ -void map_free(struct civ_map *fmap) +void map_free(struct civ_map *fmap, bool server_side) { if (fmap->tiles) { /* It is possible that map_init() was called but not map_allocate() */ @@ -568,6 +571,17 @@ void map_free(struct civ_map *fmap) FC_FREE(fmap->lake_surrounders); fmap->num_oceans = 0; } + + if (!server_side) { + if (fmap->client.continent_unknown_adj_counts) { + FC_FREE(fmap->client.continent_unknown_adj_counts); + fmap->num_continents = 0; + } + if (fmap->client.ocean_unknown_adj_counts) { + FC_FREE(fmap->client.ocean_unknown_adj_counts); + fmap->num_oceans = 0; + } + } } /*******************************************************************//** @@ -575,7 +589,7 @@ void map_free(struct civ_map *fmap) ***********************************************************************/ void main_map_free(void) { - map_free(&(wld.map)); + map_free(&(wld.map), is_server()); CALL_FUNC_EACH_AI(map_free); } @@ -797,8 +811,6 @@ int get_continent_size(Continent_id id) { fc_assert_ret_val(id > 0, -1); fc_assert_ret_val(id <= wld.map.num_continents, -1); - /* Client updates num_continents, but not continent_sizes */ - fc_assert_ret_val(is_server(), -1); return wld.map.continent_sizes[id]; } @@ -820,6 +832,8 @@ int get_lake_surrounders(Continent_id id) { fc_assert_ret_val(id < 0, -1); fc_assert_ret_val(id >= -wld.map.num_oceans, -1); + /* Client updates num_oceans, but not lake_surrounders */ + fc_assert_ret_val(is_server(), -1); return wld.map.lake_surrounders[-id]; } diff --git a/common/map.h b/common/map.h index d3f285727e..6de1cb4a0f 100644 --- a/common/map.h +++ b/common/map.h @@ -52,7 +52,7 @@ void map_init(struct civ_map *imap, bool server_side); void map_init_topology(void); void map_allocate(struct civ_map *amap); void main_map_allocate(void); -void map_free(struct civ_map *fmap); +void map_free(struct civ_map *fmap, bool server_side); void main_map_free(void); int map_vector_to_real_distance(int dx, int dy); diff --git a/common/map_types.h b/common/map_types.h index 0452bdc7ec..80ccd8fe8f 100644 --- a/common/map_types.h +++ b/common/map_types.h @@ -81,7 +81,7 @@ struct civ_map { int south_latitude; int num_continents; - int num_oceans; /* Not updated at the client */ + int num_oceans; /* These arrays are indexed by continent number (or negative of the * ocean number) so the 0th element is unused and the array is 1 element * larger than you'd expect. @@ -92,8 +92,8 @@ struct civ_map { * The lake_surrounders array tells which single continent surrounds each * ocean; or -1 if there's more than one adjacent continent. */ - int *continent_sizes; /* Not updated at the client */ - int *ocean_sizes; /* Not updated at the client */ + int *continent_sizes; + int *ocean_sizes; Continent_id *lake_surrounders; /* Not updated at the client */ struct tile *tiles; @@ -125,7 +125,17 @@ struct civ_map { enum team_placement team_placement; } server; - /* Add client side when needed */ + struct { + /* These arrays count how many adjacencies there are between known + * tiles of a given continent or ocean and unknown tiles, i.e. if a + * single known tile is adjacent to multiple unknowns (or vice versa) + * it gets counted multiple times. + * + * If this is 0 for a continent or ocean, we know for sure that its + * size in continent/ocean_sizes is accurate. */ + int *continent_unknown_adj_counts; + int *ocean_unknown_adj_counts; + } client; }; }; -- 2.34.1