From 4b428c2c37258a6f8e4ef2218cf25fff00dff042 Mon Sep 17 00:00:00 2001 From: Ihnatus Date: Mon, 30 May 2022 01:47:03 +0300 Subject: [PATCH] Optimize create_city() function, allowing to create a city of arbitrary size at once. Stop emitting "city_size_change" signals on city creation. Illegal city_size values in units.ruleset won't be accepted any more. See OSDN#44703. Signed-off-by: Ihnatus --- server/citytools.c | 18 ++++++++++++++---- server/citytools.h | 5 +++-- server/edithand.c | 24 ++++++++++++++++++------ server/gamehand.c | 2 +- server/rscompat.c | 10 ++++++++++ server/rssanity.c | 20 ++++++++++++++++++++ server/scripting/api_server_edit.c | 2 +- server/unithand.c | 12 ++---------- 8 files changed, 69 insertions(+), 24 deletions(-) diff --git a/server/citytools.c b/server/citytools.c index a54c498aa4..048b6f326a 100644 --- a/server/citytools.c +++ b/server/citytools.c @@ -1482,7 +1482,8 @@ void city_build_free_buildings(struct city *pcity) Creates real city. ****************************************************************************/ void create_city(struct player *pplayer, struct tile *ptile, - const char *name, struct player *nationality) + const char *name, struct player *nationality, + citizens size) { struct player *saved_owner = tile_owner(ptile); struct tile *saved_claimer = tile_claimer(ptile); @@ -1493,7 +1494,13 @@ void create_city(struct player *pplayer, struct tile *ptile, log_debug("create_city() %s", name); + fc_assert_ret(size); pcity = create_city_virtual(pplayer, ptile, name); + /* Created a city with 1 DEFAULT_SPECIALIST */ + if (size - 1) { + city_size_set(pcity, size); + city_repair_size(pcity, size - 1); + } /* Remove units no more seen. Do it before city is really put into the * game. */ @@ -1567,6 +1574,7 @@ void create_city(struct player *pplayer, struct tile *ptile, /* Before arranging workers to show unknown land */ pcity->server.vision = vision_new(pplayer, ptile); + conn_list_do_buffer(pplayer->connections); vision_reveal_tiles(pcity->server.vision, game.server.vision_reveal_tiles); city_refresh_vision(pcity); city_list_prepend(pplayer->cities, pcity); @@ -1601,6 +1609,7 @@ void create_city(struct player *pplayer, struct tile *ptile, pcity->server.synced = FALSE; send_city_info(NULL, pcity); + conn_list_do_unbuffer(pplayer->connections); sync_cities(); /* Will also send pwork. */ notify_player(pplayer, ptile, E_CITY_BUILD, ftc_server, @@ -1642,10 +1651,11 @@ void create_city(struct player *pplayer, struct tile *ptile, created. ****************************************************************************/ bool create_city_for_player(struct player *pplayer, struct tile *ptile, - const char *name) + const char *name, citizens size) { if (is_enemy_unit_tile(ptile, pplayer) != NULL - || !city_can_be_built_here(ptile, NULL)) { + || !city_can_be_built_here(ptile, NULL) + || size < 1) { return FALSE; } @@ -1659,7 +1669,7 @@ bool create_city_for_player(struct player *pplayer, struct tile *ptile, } map_show_tile(pplayer, ptile); - create_city(pplayer, ptile, name, pplayer); + create_city(pplayer, ptile, name, pplayer, size); return TRUE; } diff --git a/server/citytools.h b/server/citytools.h index 339af2dea7..8e91f579ab 100644 --- a/server/citytools.h +++ b/server/citytools.h @@ -64,9 +64,10 @@ void remove_dumb_city(struct player *pplayer, struct tile *ptile); void city_build_free_buildings(struct city *pcity); void create_city(struct player *pplayer, struct tile *ptile, - const char *name, struct player *nationality); + const char *name, struct player *nationality, + citizens size); bool create_city_for_player(struct player *pplayer, struct tile *ptile, - const char *name); + const char *name, citizens size); void remove_city(struct city *pcity); struct trade_route *remove_trade_route(struct city *pc1, diff --git a/server/edithand.c b/server/edithand.c index eed7016502..587d5a5b37 100644 --- a/server/edithand.c +++ b/server/edithand.c @@ -663,6 +663,7 @@ void handle_edit_city_create(struct connection *pc, int owner, int tile, struct tile *ptile; struct city *pcity; struct player *pplayer; + int maxsize; ptile = index_to_tile(&(wld.map), tile); if (!ptile) { @@ -680,12 +681,21 @@ void handle_edit_city_create(struct connection *pc, int owner, int tile, "given owner's player id %d is invalid"), tile_link(ptile), owner); return; + } + + if (size <= 0 || size > MAX_CITY_SIZE) { + int new_size = size <= 0 ? 1 : MAX_CITY_SIZE; + notify_conn(pc->self, ptile, E_BAD_COMMAND, ftc_editor, + _("Client requested a wrong city size %d, " + "creating a city of size %d"), + size, new_size); + size = new_size; } conn_list_do_buffer(game.est_connections); - if (!create_city_for_player(pplayer, ptile, NULL)) { + if (!create_city_for_player(pplayer, ptile, NULL, size)) { notify_conn(pc->self, ptile, E_BAD_COMMAND, ftc_editor, /* TRANS: ..." at ." */ _("A city may not be built at %s."), tile_link(ptile)); @@ -695,11 +705,13 @@ void handle_edit_city_create(struct connection *pc, int owner, int tile, } pcity = tile_city(ptile); - - if (size > 1) { - /* FIXME: Slow and inefficient for large size changes. */ - city_change_size(pcity, CLIP(1, size, MAX_CITY_SIZE), pplayer, NULL); - send_city_info(NULL, pcity); + /* Gently note if a city of such a size looks a bit unnatural now */ + maxsize = get_city_bonus(pcity, EFT_SIZE_ADJ); + if (city_size_get(pcity) > maxsize + && get_city_bonus(pcity, EFT_SIZE_UNLIMIT) <= 0) { + notify_conn(pc->self, ptile, E_CITY_AQUEDUCT, ftc_editor, + _("%s needs an improvement to grow beyond size %d."), + city_link(pcity), maxsize); } if (tag > 0) { diff --git a/server/gamehand.c b/server/gamehand.c index 56f19892b6..e395ed2ebf 100644 --- a/server/gamehand.c +++ b/server/gamehand.c @@ -796,7 +796,7 @@ void init_new_game(void) /* Place first city */ if (game.server.start_city) { create_city(pplayer, ptile, city_name_suggestion(pplayer, ptile), - NULL); + NULL, 1); /* Expose visible area. */ map_show_circle(pplayer, ptile, game.server.init_vis_radius_sq); diff --git a/server/rscompat.c b/server/rscompat.c index 00234b169a..056d735c5b 100644 --- a/server/rscompat.c +++ b/server/rscompat.c @@ -384,6 +384,16 @@ void rscompat_postprocess(struct rscompat_info *info) FALSE, "2")); } + /* Unit types fixing */ + unit_type_iterate (utype) { + /* Out of range city_size values tolerated no more */ + if (utype->city_size < 1) { + utype->city_size = 1; + } else if (utype->city_size > MAX_CITY_SIZE) { + utype->city_size = MAX_CITY_SIZE; + } + } unit_type_iterate_end; + /* Make sure that all action enablers added or modified by the * compatibility post processing fulfills all hard action requirements. */ rscompat_enablers_add_obligatory_hard_reqs(); diff --git a/server/rssanity.c b/server/rssanity.c index 12ecf87821..6b83f1c367 100644 --- a/server/rssanity.c +++ b/server/rssanity.c @@ -1002,6 +1002,26 @@ bool sanity_check_ruleset_data(struct rscompat_info *compat) putype->paratroopers_range, UNIT_MAX_PARADROP_RANGE); ok = FALSE; } + if (putype->city_size <= 0 + || putype->city_size > MAX_CITY_SIZE) { + if (compat == NULL || !compat->compat_mode + || compat->version >= RSFORMAT_3_2 + || !utype_can_do_action_result(putype, ACTRES_FOUND_CITY) + || putype->city_size >= 0) { + ruleset_error(LOG_ERROR, + "The city_size of the unit type '%s' is %d. " + "That is out of range. The range is from 1 to %d.", + utype_rule_name(putype), + putype->city_size, MAX_CITY_SIZE); + ok = FALSE; + } else { + ruleset_error(LOG_WARN, + "Out of range city_size %d of the unit type '%s' " + "is changed to 1.", + putype->city_size, utype_rule_name(putype)); + putype->city_size = 1; + } + } } unit_type_iterate_end; memset(&els, 0, sizeof(els)); diff --git a/server/scripting/api_server_edit.c b/server/scripting/api_server_edit.c index 9906ad75d9..794b61160d 100644 --- a/server/scripting/api_server_edit.c +++ b/server/scripting/api_server_edit.c @@ -582,7 +582,7 @@ bool api_edit_create_city(lua_State *L, Player *pplayer, Tile *ptile, LUASCRIPT_CHECK_ARG_NIL(L, ptile, 3, Tile, FALSE); /* TODO: Allow initial citizen to be of nationality other than owner */ - return create_city_for_player(pplayer, ptile, name); + return create_city_for_player(pplayer, ptile, name, 1); } /**********************************************************************//** diff --git a/server/unithand.c b/server/unithand.c index 72750e4def..c2fa3461ce 100644 --- a/server/unithand.c +++ b/server/unithand.c @@ -4061,7 +4061,6 @@ static bool city_build(struct player *pplayer, struct unit *punit, const struct action *paction) { char message[1024]; - int size; struct player *nationality; struct player *towner; const struct unit_type *act_utype; @@ -4081,15 +4080,8 @@ static bool city_build(struct player *pplayer, struct unit *punit, nationality = unit_nationality(punit); - create_city(pplayer, ptile, name, nationality); - size = unit_type_get(punit)->city_size; - if (size > 1) { - struct city *pcity = tile_city(ptile); - - fc_assert_ret_val(pcity != NULL, FALSE); - - city_change_size(pcity, size, nationality, NULL); - } + create_city(pplayer, ptile, name, nationality, + unit_type_get(punit)->city_size); /* May cause an incident even if the target tile is unclaimed. A ruleset * could give everyone a casus belli against the city founder. A rule -- 2.34.1