From d9c65743932de4ee5d6b10d22165ddf696b39a54 Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Fri, 6 Jan 2023 22:04:59 +0200 Subject: [PATCH 34/34] Send city original owner information to client This information is available in the full city package only. We don't have that information stored for vision sites, to send to players other than current owner. Even with the full package, only the original owner get the full knowledge. See osdn #46402 Signed-off-by: Marko Lindqvist --- ai/default/daieffects.c | 2 +- client/packhand.c | 18 +++++++++--------- common/city.c | 5 ++++- common/city.h | 3 ++- common/networking/packets.def | 1 + common/player.c | 7 ++++--- common/requirements.c | 14 ++++++++++++-- server/citytools.c | 35 ++++++++++++++++++++++++++++++++--- server/citytools.h | 5 ++++- server/cityturn.c | 4 ++-- server/diplomats.c | 1 + server/plrhand.c | 5 +++-- server/savegame/savegame3.c | 8 ++++++-- 13 files changed, 81 insertions(+), 27 deletions(-) diff --git a/ai/default/daieffects.c b/ai/default/daieffects.c index 91b63fb1ea..557c91f287 100644 --- a/ai/default/daieffects.c +++ b/ai/default/daieffects.c @@ -814,7 +814,7 @@ bool dai_can_requirement_be_met_in_city(const struct requirement *preq, case VUT_CITYSTATUS: /* FIXME: update */ - if (pcity == NULL) { + if (pcity == NULL || pcity->original == NULL) { return preq->present; } if (preq->present) { diff --git a/client/packhand.c b/client/packhand.c index 927846d596..f10dd09ef5 100644 --- a/client/packhand.c +++ b/client/packhand.c @@ -689,17 +689,17 @@ void handle_city_info(const struct packet_city_info *packet) ptile = city_tile(pcity); if (NULL == ptile) { - /* invisible worked city */ + /* Invisible worked city */ city_list_remove(invisible.cities, pcity); city_is_new = TRUE; pcity->tile = pcenter; ptile = pcenter; pcity->owner = powner; - pcity->original = powner; + pcity->original = player_by_number(packet->original); } else if (city_owner(pcity) != powner) { - /* Remember what were the worked tiles. The server won't - * send to us again. */ + /* Remember what were the worked tiles. The server won't + * send them to us again. */ city_tile_iterate_skip_free_worked(city_map_radius_sq_get(pcity), ptile, pworked, _index, _x, _y) { if (pcity == tile_worked(pworked)) { @@ -1174,7 +1174,7 @@ void handle_city_short_info(const struct packet_city_short_info *packet) pcity->tile = pcenter; pcity->owner = powner; - pcity->original = powner; + pcity->original = NULL; whole_map_iterate(&(wld.map), wtile) { if (wtile->worked == pcity) { @@ -3161,7 +3161,7 @@ void handle_tile_info(const struct packet_tile_info *packet) if (NULL == pwork) { char named[MAX_LEN_CITYNAME]; - /* new unseen ("invisible") city, or before city_info */ + /* New unseen ("invisible") city, or before city_info */ fc_snprintf(named, sizeof(named), "%06u", packet->worked); pwork = create_city_virtual(invisible.placeholder, NULL, named); @@ -3173,11 +3173,11 @@ void handle_tile_info(const struct packet_tile_info *packet) log_debug("(%d,%d) invisible city %d, %s", TILE_XY(ptile), pwork->id, city_name_get(pwork)); } else if (NULL == city_tile(pwork)) { - /* old unseen ("invisible") city, or before city_info */ + /* Old unseen ("invisible") city, or before city_info */ if (NULL != powner && city_owner(pwork) != powner) { - /* update placeholder with current owner */ + /* Update placeholder with current owner */ pwork->owner = powner; - pwork->original = powner; + pwork->original = NULL; } } else { /* We have a real (not invisible) city record for this ID, but diff --git a/common/city.c b/common/city.c index 439b9351b2..a4fa280679 100644 --- a/common/city.c +++ b/common/city.c @@ -3342,7 +3342,10 @@ struct city *create_city_virtual(struct player *pplayer, pcity->tile = ptile; fc_assert_ret_val(NULL != pplayer, NULL); /* No unowned cities! */ pcity->owner = pplayer; - pcity->original = pplayer; + + if (is_server()) { + pcity->original = pplayer; + } /* City structure was allocated with fc_calloc(), so contents are initially * zero. There is no need to initialize it a second time. */ diff --git a/common/city.h b/common/city.h index c915110d68..aac8205607 100644 --- a/common/city.h +++ b/common/city.h @@ -306,7 +306,8 @@ struct city { char *name; struct tile *tile; /* May be NULL, should check! */ struct player *owner; /* Cannot be NULL. */ - struct player *original; /* Cannot be NULL. */ + struct player *original; /* Often NULL in client, + * can be NULL on server after player removal */ int id; int style; enum capital_type capital; diff --git a/common/networking/packets.def b/common/networking/packets.def index fd0dd04b04..6474dc1e76 100644 --- a/common/networking/packets.def +++ b/common/networking/packets.def @@ -707,6 +707,7 @@ PACKET_CITY_INFO = 31; sc, lsend, is-game-info, force, cancel(PACKET_CITY_SHORT_ TILE tile; PLAYER owner; + PLAYER original; CITIZENS size; UINT8 city_radius_sq; UINT8 style; diff --git a/common/player.c b/common/player.c index 68d0a998c1..44c1fef116 100644 --- a/common/player.c +++ b/common/player.c @@ -761,8 +761,9 @@ void player_destroy(struct player *pplayer) * by the removed player. */ city_list_iterate(aplayer->cities, pcity) { if (pcity->original == pplayer) { - /* Current owner is a sane value, at least */ - pcity->original = aplayer; + /* Unknown origin is better than giving baseless + * benefits to current owner */ + pcity->original = NULL; } } city_list_iterate_end; } @@ -820,7 +821,7 @@ int player_number(const struct player *pplayer) Return struct player pointer for the given player index. You can retrieve players that are not in the game (with IDs larger than - player_count). An out-of-range player request will return NULL. + player_count). An out-of-range player request will return NULL. ***********************************************************************/ struct player *player_by_number(const int player_id) { diff --git a/common/requirements.c b/common/requirements.c index c2ad111aa4..53cbf21d1e 100644 --- a/common/requirements.c +++ b/common/requirements.c @@ -3545,7 +3545,7 @@ is_originalowner_req_active(const struct req_context *context, switch (req->range) { case REQ_RANGE_CITY: - if (context->city == NULL) { + if (context->city == NULL || context->city->original == NULL) { return TRI_MAYBE; } if (player_nation(context->city->original) == nation) { @@ -4519,18 +4519,28 @@ is_citystatus_req_active(const struct req_context *context, case CITYS_OWNED_BY_ORIGINAL: switch (req->range) { case REQ_RANGE_CITY: + if (context->city->original == NULL) { + return TRI_MAYBE; + } return BOOL_TO_TRISTATE(city_owner(context->city) == context->city->original); case REQ_RANGE_TRADEROUTE: { bool found = (city_owner(context->city) == context->city->original); + bool maybe = FALSE; trade_partners_iterate(context->city, trade_partner) { - if (city_owner(trade_partner) == trade_partner->original) { + if (trade_partner->original == NULL) { + maybe = TRUE; + } else if (city_owner(trade_partner) == trade_partner->original) { found = TRUE; break; } } trade_partners_iterate_end; + if (!found && maybe) { + return TRI_MAYBE; + } + return BOOL_TO_TRISTATE(found); } case REQ_RANGE_LOCAL: diff --git a/server/citytools.c b/server/citytools.c index e583ab4460..513bef35ef 100644 --- a/server/citytools.c +++ b/server/citytools.c @@ -986,7 +986,7 @@ static void reestablish_city_trade_routes(struct city *pcity) announce_trade_route_removal(pcity, partner, FALSE); } - /* refresh regardless; either it lost a trade route or the trade + /* Refresh regardless; either it lost a trade route or the trade * route revenue changed. */ city_refresh(partner); send_city_info(city_owner(partner), partner); @@ -2235,6 +2235,7 @@ void broadcast_city_info(struct city *pcity) if (!send_city_suppressed || pplayer != powner) { if (can_player_see_city_internals(pplayer, pcity)) { update_dumb_city(pplayer, pcity); + packet.original = city_original_owner(pcity, pplayer); lsend_packet_city_info(pplayer->connections, &packet, FALSE); lsend_packet_city_nationalities(pplayer->connections, &nat_packet, FALSE); lsend_packet_city_rally_point(pplayer->connections, &rally_packet, FALSE); @@ -2253,6 +2254,7 @@ void broadcast_city_info(struct city *pcity) } players_iterate_end; /* Send to global observers. */ + packet.original = city_original_owner(pcity, NULL); conn_list_iterate(game.est_connections, pconn) { if (conn_is_global_observer(pconn)) { send_packet_city_info(pconn, &packet, FALSE); @@ -2399,8 +2401,8 @@ void send_city_info_at_tile(struct player *pviewer, struct conn_list *dest, webp_ptr = NULL; } - if (powner && powner == pviewer) { - /* send info to owner */ + if (powner != NULL && powner == pviewer) { + /* Send info to owner */ /* This case implies powner non-NULL which means pcity non-NULL */ if (!send_city_suppressed) { /* Wait that city has been rearranged, if it's currently @@ -2412,6 +2414,7 @@ void send_city_info_at_tile(struct player *pviewer, struct conn_list *dest, update_dumb_city(powner, pcity); package_city(pcity, &packet, &nat_packet, &rally_packet, webp_ptr, routes, FALSE); + packet.original = city_original_owner(pcity, pviewer); lsend_packet_city_info(dest, &packet, FALSE); lsend_packet_city_nationalities(dest, &nat_packet, FALSE); lsend_packet_city_rally_point(dest, &rally_packet, FALSE); @@ -2421,6 +2424,7 @@ void send_city_info_at_tile(struct player *pviewer, struct conn_list *dest, } traderoute_packet_list_iterate_end; if (dest == powner->connections) { /* HACK: send also a copy to global observers. */ + packet.original = city_original_owner(pcity, NULL); conn_list_iterate(game.est_connections, pconn) { if (conn_is_global_observer(pconn)) { send_packet_city_info(pconn, &packet, FALSE); @@ -2475,6 +2479,8 @@ void send_city_info_at_tile(struct player *pviewer, struct conn_list *dest, /************************************************************************//** Fill city info packet with information about given city. + This can't set 'original', as it depends on who is about to receive + the package whether they know true value of that. ****************************************************************************/ void package_city(struct city *pcity, struct packet_city_info *packet, struct packet_city_nationalities *nat_packet, @@ -2490,6 +2496,7 @@ void package_city(struct city *pcity, struct packet_city_info *packet, packet->id = pcity->id; packet->owner = player_number(city_owner(pcity)); + packet->tile = tile_index(city_tile(pcity)); sz_strlcpy(packet->name, city_name_get(pcity)); @@ -3602,3 +3609,25 @@ void city_add_improvement_with_gov_notice(struct city *pcity, city_add_improvement(pcity, pimprove); } } + +/************************************************************************//** + Return city's original owner id, as known by specified player. + NULL known_for is expected to mean global observer. +****************************************************************************/ +int city_original_owner(const struct city *pcity, + const struct player *known_for) +{ + if (pcity->original == NULL) { + /* Nobody knows */ + return MAX_NUM_PLAYER_SLOTS; + } + + if (pcity->original != known_for + || known_for == NULL) { + /* Players know what they have built themselves, + * global observer knows everything. */ + return player_number(pcity->original); + } + + return MAX_NUM_PLAYER_SLOTS; +} diff --git a/server/citytools.h b/server/citytools.h index 355f265b70..939f273a5b 100644 --- a/server/citytools.h +++ b/server/citytools.h @@ -132,4 +132,7 @@ void city_add_improvement_with_gov_notice(struct city *pcity, const struct impr_type *pimprove, const char *format); -#endif /* FC__CITYTOOLS_H */ +int city_original_owner(const struct city *pcity, + const struct player *known_for); + +#endif /* FC__CITYTOOLS_H */ diff --git a/server/cityturn.c b/server/cityturn.c index 20efa26c31..cc27e0ac94 100644 --- a/server/cityturn.c +++ b/server/cityturn.c @@ -3328,9 +3328,9 @@ int city_incite_cost(struct player *pplayer, struct city *pcity) if (!game.info.citizen_nationality) { if (city_owner(pcity) != pcity->original) { if (pplayer == pcity->original) { - cost /= 2; /* buy back: 50% price reduction */ + cost /= 2; /* Buy back: 50% price reduction */ } else { - cost = cost * 2 / 3; /* buy conquered: 33% price reduction */ + cost = cost * 2 / 3; /* Buy conquered: 33% price reduction */ } } } diff --git a/server/diplomats.c b/server/diplomats.c index 30c3aeaa3e..6fcd118778 100644 --- a/server/diplomats.c +++ b/server/diplomats.c @@ -389,6 +389,7 @@ bool diplomat_investigate(struct player *pplayer, struct unit *pdiplomat, webp_ptr, routes, TRUE); /* We need to force to send the packet to ensure the client will receive * something and popup the city dialog. */ + city_packet.original = city_original_owner(pcity, pplayer); lsend_packet_city_info(pplayer->connections, &city_packet, TRUE); lsend_packet_city_nationalities(pplayer->connections, &nat_packet, TRUE); lsend_packet_city_rally_point(pplayer->connections, &rally_packet, TRUE); diff --git a/server/plrhand.c b/server/plrhand.c index 59d710f331..db2cfa5825 100644 --- a/server/plrhand.c +++ b/server/plrhand.c @@ -170,9 +170,10 @@ void kill_player(struct player *pplayer) /* Transfer back all cities not originally owned by player to their rightful owners, if they are still around */ save_palace = game.server.savepalace; - game.server.savepalace = FALSE; /* moving it around is dumb */ + game.server.savepalace = FALSE; /* Moving it around is dumb */ city_list_iterate_safe(pplayer->cities, pcity) { - if (pcity->original != pplayer && pcity->original->is_alive) { + if (pcity->original != pplayer && pcity->original != NULL + && pcity->original->is_alive) { /* Transfer city to original owner, kill all its units outside of a radius of 3, give verbose messages of every unit transferred, and raze buildings according to raze chance (also removes palace) */ diff --git a/server/savegame/savegame3.c b/server/savegame/savegame3.c index 7e79a2ca02..13983f37ae 100644 --- a/server/savegame/savegame3.c +++ b/server/savegame/savegame3.c @@ -5497,8 +5497,12 @@ static void sg_save_player_cities(struct savedata *saving, secfile_insert_int(saving->file, pcity->id, "%s.id", buf); - secfile_insert_int(saving->file, player_number(pcity->original), - "%s.original", buf); + if (pcity->original != NULL) { + secfile_insert_int(saving->file, player_number(pcity->original), + "%s.original", buf); + } else { + secfile_insert_int(saving->file, -1, "%s.original", buf); + } secfile_insert_int(saving->file, city_size_get(pcity), "%s.size", buf); j = 0; -- 2.39.0