Project

General

Profile

Feature #2439 ยป 0001-common-Add-world-and-per-player-knowledge-generation.patch

Sweet Tea Dorminy, 11/02/2026 05:47 PM

View differences:

common/game.c
punit->homecity);
}
map_world_unit_changed(punit);
unit_list_remove(unit_tile(punit)->units, punit);
owner = unit_owner(punit);
common/map.c
return pspresent;
}
/* The world generation. It changes whenever something a pathfinder reads
* may have changed: terrain, extras and their owner, tile owner, city
* centers and their owner, diplomatic states, settings, the set of
* players, the units that can transport or are transported, and turn
* and phase boundaries. Tile knowledge has its own per-player counter
* (map_knowledge_changed()). Caches of pathfinding results compare it
* on every lookup; a change that turns out to be irrelevant only costs
* a recomputation. */
static unsigned int world_generation = 0;
/* Per player slot: changes whenever the set of tiles the player knows
* changes. Pathfinding reads tile knowledge only when the parameter is
* not omniscient, so caches of such results compare this as well as
* the world generation. */
static unsigned int knowledge_generation[MAX_NUM_PLAYER_SLOTS];
/*******************************************************************//**
Return whether ptile is a tile of the current map, and not a virtual
tile (tile_virtual_new()), which no pathfinder reads.
***********************************************************************/
static bool map_world_tile_is_real(const struct tile *ptile)
{
return (wld.map.tiles != nullptr
&& ptile >= wld.map.tiles
&& ptile < wld.map.tiles + MAP_INDEX_SIZE);
}
/*******************************************************************//**
Something pathfinding may read has changed, or may have. Issues a new
world generation.
***********************************************************************/
void map_world_changed(void)
{
world_generation++;
}
/*******************************************************************//**
As map_world_changed(), but only when ptile is a real map tile: the
change of a virtual tile changes nothing any pathfinder reads.
@param ptile Tile that changed
***********************************************************************/
void map_world_tile_changed(const struct tile *ptile)
{
if (map_world_tile_is_real(ptile)) {
map_world_changed();
}
}
/*******************************************************************//**
The tile, orders or presence of punit is about to change.
Pathfinding (pf_get_move_scope(), pf_transport_check()) reads the
units on a tile only as possible transports: their owner, type,
orders, transport depth and the transports carrying them. Pathfinders
that read other units (get_zoc, get_TB or is_action_possible
callbacks) do not cache their results. So only a unit that can
transport, or that is transported, changes the world generation.
@param punit Unit about to change
***********************************************************************/
void map_world_unit_changed(const struct unit *punit)
{
if (unit_type_get(punit)->transport_capacity > 0
|| unit_transport_get(punit) != nullptr) {
map_world_changed();
}
}
/*******************************************************************//**
Return the current world generation; see map_world_changed().
***********************************************************************/
unsigned int map_world_generation(void)
{
return world_generation;
}
/*******************************************************************//**
The set of tiles pplayer knows has changed, or may have.
@param pplayer Player whose knowledge changed
***********************************************************************/
void map_knowledge_changed(const struct player *pplayer)
{
knowledge_generation[player_index(pplayer)]++;
}
/*******************************************************************//**
Return the current knowledge generation of pplayer; see
map_knowledge_changed().
@param pplayer Player whose knowledge generation to return
@return Knowledge generation
***********************************************************************/
unsigned int map_knowledge_generation(const struct player *pplayer)
{
return knowledge_generation[player_index(pplayer)];
}
/*******************************************************************//**
Returns TRUE if we are at a stage of the game where the map
has not yet been generated/loaded.
common/map.h
enum direction8 rand_direction(void);
enum direction8 opposite_direction(enum direction8 dir);
void map_world_changed(void);
void map_world_tile_changed(const struct tile *ptile);
void map_world_unit_changed(const struct unit *punit);
unsigned int map_world_generation(void);
void map_knowledge_changed(const struct player *pplayer);
unsigned int map_knowledge_generation(const struct player *pplayer);
#ifdef __cplusplus
}
#endif /* __cplusplus */
common/tile.c
if (BORDERS_DISABLED != game.info.borders
/* City tiles are always owned by the city owner. */
|| (tile_city(ptile) != nullptr || ptile->owner != nullptr)) {
if (ptile->owner != pplayer) {
map_world_tile_changed(ptile);
}
ptile->owner = pplayer;
ptile->claimer = claimer;
}
......
****************************************************************************/
void tile_set_worked(struct tile *ptile, struct city *pcity)
{
struct city *new_center = (pcity != nullptr
&& is_city_center(pcity, ptile)
? pcity : nullptr);
if (tile_city(ptile) != new_center) {
/* A city center appears or goes. Which city works a tile is not
* read by pathfinding. */
map_world_tile_changed(ptile);
}
ptile->worked = pcity;
}
......
tile_city(ptile)->id);
#endif /* 0 */
map_world_tile_changed(ptile);
ptile->terrain = pterrain;
if (ptile->resource != nullptr) {
if (pterrain != nullptr
......
void tile_add_extra(struct tile *ptile, const struct extra_type *pextra)
{
if (pextra != nullptr) {
map_world_tile_changed(ptile);
BV_SET(ptile->extras, extra_index(pextra));
}
}
......
void tile_remove_extra(struct tile *ptile, const struct extra_type *pextra)
{
if (pextra != nullptr) {
map_world_tile_changed(ptile);
BV_CLR(ptile->extras, extra_index(pextra));
if (ptile->resource == pextra) {
ptile->resource = nullptr;
common/unit.c
void free_unit_orders(struct unit *punit)
{
if (punit->has_orders) {
/* unit_has_orders() decides whether a pathfinder may disembark
* from this unit as a transport. */
map_world_unit_changed(punit);
punit->goto_tile = nullptr;
free(punit->orders.list);
punit->orders.list = nullptr;
......
fc_assert_ret_val(!unit_list_search(ptrans->transporting, pcargo), FALSE);
if (force || can_unit_load(pcargo, ptrans)) {
map_world_changed();
pcargo->transporter = ptrans;
unit_list_append(ptrans->transporting, pcargo);
......
}
/* For the server (also safe for the client). */
map_world_changed();
pcargo->transporter = nullptr;
return TRUE;
server/citytools.c
map_clear_border(pcenter);
/* city_thaw_workers_queue() later */
map_world_changed();
pcity->owner = ptaker;
pcity->capital = CAPITAL_NOT;
pcity->acquire_t = CACQ_CONQUEST;
server/cityturn.c
/* If city has a rally point set, give the unit a move order. */
if (pcity->rally_point.length) {
map_world_unit_changed(punit);
punit->has_orders = TRUE;
punit->orders.length = pcity->rally_point.length;
punit->orders.vigilant = pcity->rally_point.vigilant;
server/diplhand.c
max = dst_closest(max, state1->max_state);
map_world_changed();
state1->type = type;
state2->type = type;
state1->max_state = max;
server/edithand.c
conn_list_do_buffer(game.est_connections);
square_iterate(&(wld.map), ptile_center, size - 1, ptile) {
/* Pathfinding reads the extras owner. */
map_world_changed();
ptile->extras_owner = plr_eowner;
edit_tile_extra_handling(ptile, extra_by_number(id), removal, TRUE);
} square_iterate_end;
......
}
if (ptile->extras_owner != eowner) {
/* Pathfinding reads the extras owner. */
map_world_changed();
ptile->extras_owner = eowner;
changed = TRUE;
}
server/maphand.c
**************************************************************************/
void map_set_known(struct tile *ptile, struct player *pplayer)
{
if (!dbv_isset(&pplayer->tile_known, tile_index(ptile))) {
/* Pathfinding reads tile knowledge. */
map_knowledge_changed(pplayer);
}
dbv_set(&pplayer->tile_known, tile_index(ptile));
}
......
**************************************************************************/
void map_clear_known(struct tile *ptile, struct player *pplayer)
{
if (dbv_isset(&pplayer->tile_known, tile_index(ptile))) {
/* Pathfinding reads tile knowledge. */
map_knowledge_changed(pplayer);
}
dbv_clr(&pplayer->tile_known, tile_index(ptile));
}
......
} whole_map_iterate_end;
dbv_init(&pplayer->tile_known, MAP_INDEX_SIZE);
map_knowledge_changed(pplayer);
}
/**********************************************************************//**
......
reality_changed = TRUE;
}
if (extra_owner(ptile) == pplayer) {
map_world_changed();
ptile->extras_owner = nullptr;
reality_changed = TRUE;
}
......
/* This MUST be before potentially recursive call to map_claim_base(),
* so that the recursive call will get new owner == base_loser and
* abort recursion. */
map_world_changed();
ptile->extras_owner = powner;
extra_type_by_cause_iterate(EC_BASE, pextra) {
......
}
} extra_type_by_cause_iterate_end;
map_world_changed();
ptile->extras_owner = pplayer;
}
} else {
server/plrhand.c
map_claim_ownership(ptile, nullptr, nullptr, FALSE);
}
if (extra_owner(ptile) == pplayer) {
map_world_changed();
ptile->extras_owner = nullptr;
}
} whole_map_iterate_end;
......
map_claim_base(ptile, pextra, new_owner, old_owner);
} extra_type_by_cause_iterate_end;
map_world_changed();
ptile->extras_owner = new_owner;
}
}
......
}
/* Do the change */
/* Pathfinding reads diplomatic states. */
map_world_changed();
ds_plrplr2->type = ds_plr2plr->type = new_type;
ds_plrplr2->turns_left = ds_plr2plr->turns_left = 16;
......
if (nullptr == pplayer) {
return nullptr;
}
/* Pathfinding results are keyed by player. */
map_world_changed();
if (allow_ai_type_fallbacking) {
pplayer->savegame_ai_type_name = fc_strdup(ai_tname);
......
/* Save player slot */
pslot = pplayer->slot;
/* Pathfinding results are keyed by player. */
map_world_changed();
log_normal(_("Removing player %s."), player_name(pplayer));
server/scripting/api_server_edit.c
} trade_partners_iterate_end;
}
map_knowledge_changed(pplayer);
dbv_clr(&pplayer->tile_known, tile_index(ptile));
send_tile_info(pplayer->connections, ptile, TRUE);
server/settings.c
****************************************************************************/
void setting_changed(struct setting *pset)
{
/* Settings (borders, restrictinfra, citymindist, ...) are read by
* pathfinding. */
map_world_changed();
pset->setdef = SETDEF_CHANGED;
}
server/srv_main.c
&& state->auto_cancel_turn != game.info.turn) {
state->turns_left--;
if (state->turns_left <= 0) {
map_world_changed();
state->type = DS_PEACE;
state2->type = DS_PEACE;
state->turns_left = 0;
......
"You are now at war with the %s."),
player_name(plr1),
nation_plural_for_player(plr1));
map_world_changed();
state->type = DS_WAR;
state2->type = DS_WAR;
state->turns_left = 0;
......
static void begin_turn(bool is_new_turn)
{
log_debug("Begin turn");
map_world_changed();
event_cache_remove_old();
......
static void begin_phase(bool is_new_phase)
{
log_debug("Begin phase");
map_world_changed();
conn_list_do_buffer(game.est_connections);
......
static void end_phase(void)
{
log_debug("Endphase");
map_world_changed();
/*
* This empties the client Messages window; put this before
......
static void end_turn(void)
{
log_debug("Endturn");
map_world_changed();
/* Hack: Because observer players never get an end-phase packet we send
* one here. */
server/unithand.c
unit_list_remove(old_owner->units, punit);
unit_list_prepend(new_owner->units, punit);
map_world_changed();
punit->owner = new_owner;
/* Activate AI control of the new owner. */
......
return;
}
map_world_unit_changed(punit);
punit->has_orders = TRUE;
punit->orders.length = length;
punit->orders.index = 0;
server/unittools.c
int old_hp = unit_type_get(punit)->hp;
int lvls;
/* A transport's type is read by pathfinding. */
map_world_changed();
punit->utype = to_unit;
/* New type may not have the same veteran system, and we may want to
......
FALSE);
unit_list_prepend(pplayer->units, punit);
map_world_unit_changed(punit);
unit_list_prepend(ptile->units, punit);
unit_make_contact(punit, ptile, nullptr);
if (pcity && !unit_has_type_flag(punit, UTYF_NOHOME)) {
......
/* Remove unit from the source tile. */
fc_assert(unit_tile(punit) == psrctile);
map_world_unit_changed(punit);
#ifndef FREECIV_NDEBUG
bool success =
#endif
    (1-1/1)