From 5363a238c0d844ed1003589bd74570df69c6ab6d Mon Sep 17 00:00:00 2001 From: Marko Lindqvist Date: Sun, 19 Dec 2021 23:14:41 +0200 Subject: [PATCH 45/45] Use information from .modpack for client-initiated ruleset selection - Cache information about modpack name and filename mapping - Add dedicated packet for the client to request modpack-specified ruleset - Support both '/read ruleset.serv' and '/rulesetdir ruleset' methods See osdn #43435 Signed-off-by: Marko Lindqvist --- client/connectdlg_common.c | 10 ++--- common/game.c | 3 ++ common/modpack.c | 82 +++++++++++++++++++++++++++++++++++ common/modpack.h | 6 +++ common/networking/packets.def | 4 ++ fc_version | 2 +- server/gamehand.c | 64 +++++++++++++++++++++++---- server/srv_main.c | 3 +- server/stdinhand.c | 12 +++-- server/stdinhand.h | 4 +- 10 files changed, 167 insertions(+), 23 deletions(-) diff --git a/client/connectdlg_common.c b/client/connectdlg_common.c index 93398cac77..bb25a6f3e3 100644 --- a/client/connectdlg_common.c +++ b/client/connectdlg_common.c @@ -752,14 +752,14 @@ void handle_ruleset_choices(const struct packet_ruleset_choices *packet) } /**********************************************************************//** - Called by the GUI code when the user sets the ruleset. The ruleset + Called by the GUI code when the user sets the ruleset. The ruleset passed in here should match one of the strings given to set_rulesets(). **************************************************************************/ void set_ruleset(const char *ruleset) { - char buf[4096]; + struct packet_ruleset_select packet; - fc_snprintf(buf, sizeof(buf), "/read %s%s", ruleset, RULESET_SUFFIX); - log_debug("Executing '%s'", buf); - send_chat(buf); + fc_snprintf(packet.modpack, sizeof(packet.modpack), "%s", ruleset); + + send_packet_ruleset_select(&client.conn, &packet); } diff --git a/common/game.c b/common/game.c index 74ad89bc90..27db3dce00 100644 --- a/common/game.c +++ b/common/game.c @@ -38,6 +38,7 @@ #include "government.h" #include "idex.h" #include "map.h" +#include "modpack.h" #include "multipliers.h" #include "nation.h" #include "packets.h" @@ -442,6 +443,7 @@ static void game_defaults(bool keep_ruleset_value) **************************************************************************/ void game_init(bool keep_ruleset_value) { + modpacks_init(); game_defaults(keep_ruleset_value); player_slots_init(); map_init(&wld.map, is_server()); @@ -480,6 +482,7 @@ void game_free(void) game_ruleset_free(); researches_free(); cm_free(); + modpacks_free(); } /**********************************************************************//** diff --git a/common/modpack.c b/common/modpack.c index 23b55d2d98..480f0bc539 100644 --- a/common/modpack.c +++ b/common/modpack.c @@ -21,8 +21,52 @@ #include "section_file.h" #include "shared.h" +/* common */ +#include "game.h" + #include "modpack.h" +struct modpack_cache_item { + char *modpack_name; + char *filename; +}; + +/* get 'struct modpack_cache_list' and related functions: */ +#define SPECLIST_TAG modpack_cache +#define SPECLIST_TYPE struct modpack_cache_item +#include "speclist.h" + +#define modpack_cache_iterate(mplist, item) \ + TYPED_LIST_ITERATE(struct modpack_cache_item, mplist, item) +#define modpack_cache_iterate_end LIST_ITERATE_END + +static struct modpack_cache_list *modpack_rulesets; + +/************************************************************************//** + Initialize modpacks system +****************************************************************************/ +void modpacks_init(void) +{ + if (is_server()) { + modpack_rulesets = modpack_cache_list_new(); + } +} + +/************************************************************************//** + Free the memory associated with modpacks system +****************************************************************************/ +void modpacks_free(void) +{ + if (is_server()) { + modpack_cache_iterate(modpack_rulesets, item) { + free(item->modpack_name); + free(item->filename); + free(item); + } modpack_cache_iterate_end; + + modpack_cache_list_destroy(modpack_rulesets); + } +} /************************************************************************//** Check modpack file capabilties. @@ -113,3 +157,41 @@ const char *modpack_rulesetdir(struct section_file *sf) return NULL; } + +/************************************************************************//** + Add modpack/ruleset mapping to cache, if modpack has ruleset. + Return name of the modpack, if the mapping exist. +****************************************************************************/ +const char *modpack_cache_ruleset(struct section_file *sf) +{ + const char *mp_name = modpack_has_ruleset(sf); + struct modpack_cache_item *item; + + if (mp_name == NULL) { + return NULL; + } + + fc_assert(sf->name != NULL); + + item = fc_malloc(sizeof(struct modpack_cache_item)); + item->modpack_name = fc_strdup(mp_name); + item->filename = fc_strdup(sf->name); + + modpack_cache_list_append(modpack_rulesets, item); + + return mp_name; +} + +/************************************************************************//** + Find filename by name of the modpack, from the ruleset cache +****************************************************************************/ +const char *modpack_file_from_ruleset_cache(const char *name) +{ + modpack_cache_iterate(modpack_rulesets, item) { + if (!fc_strcasecmp(name, item->modpack_name)) { + return item->filename; + } + } modpack_cache_iterate_end; + + return NULL; +} diff --git a/common/modpack.h b/common/modpack.h index 772565da9f..06da618a24 100644 --- a/common/modpack.h +++ b/common/modpack.h @@ -21,6 +21,12 @@ extern "C" { #define MODPACK_SUFFIX ".modpack" +void modpacks_init(void); +void modpacks_free(void); + +const char *modpack_cache_ruleset(struct section_file *sf); +const char *modpack_file_from_ruleset_cache(const char *name); + struct fileinfo_list *get_modpacks_list(void); const char *modpack_has_ruleset(struct section_file *sf); diff --git a/common/networking/packets.def b/common/networking/packets.def index b452293625..78345edfcd 100644 --- a/common/networking/packets.def +++ b/common/networking/packets.def @@ -1959,6 +1959,10 @@ PACKET_RULESET_CHOICES = 162; sc STRING rulesets[MAX_NUM_RULESETS:ruleset_count][MAX_RULESET_NAME_LENGTH]; end +PACKET_RULESET_SELECT = 171; cs, handle-per-conn + STRING modpack[MAX_RULESET_NAME_LENGTH]; +end + PACKET_GAME_LOAD = 163; sc, lsend, dsend BOOL load_successful; STRING load_filename[MAX_LEN_PACKET]; diff --git a/fc_version b/fc_version index e98eed06d1..173d7bafe9 100755 --- a/fc_version +++ b/fc_version @@ -56,7 +56,7 @@ DEFAULT_FOLLOW_TAG=S3_2 # - No new mandatory capabilities can be added to the release branch; doing # so would break network capability of supposedly "compatible" releases. # -NETWORK_CAPSTRING="+Freeciv.Devel-3.2-2021.Dec.10b" +NETWORK_CAPSTRING="+Freeciv.Devel-3.2-2021.Dec.19" FREECIV_DISTRIBUTOR="" diff --git a/server/gamehand.c b/server/gamehand.c index 369013f0b7..5e53a63866 100644 --- a/server/gamehand.c +++ b/server/gamehand.c @@ -1106,15 +1106,13 @@ static void send_ruleset_choices(struct connection *pc) sf = secfile_load(pfile->fullname, FALSE); if (sf != NULL) { - if (modpack_has_ruleset(sf) != NULL) { - const char *name = modpack_serv_file(sf); + const char *name = modpack_cache_ruleset(sf); - if (name != NULL) { - if (fc_strlcpy(packet.rulesets[i], name, maxlen) < maxlen) { - i++; - } else { - log_verbose("Ruleset name '%s' too long to send to client, skipped", name); - } + if (name != NULL) { + if (fc_strlcpy(packet.rulesets[i], name, maxlen) < maxlen) { + i++; + } else { + log_verbose("Modpack name '%s' too long to send to client, skipped", name); } } @@ -1128,6 +1126,56 @@ static void send_ruleset_choices(struct connection *pc) fileinfo_list_destroy(ruleset_choices); } +/************************************************************************//** + Change ruleset based on modpack. +****************************************************************************/ +void handle_ruleset_select(struct connection *pc, + const struct packet_ruleset_select *packet) +{ + struct section_file *sf; + const char *name; + + if (server_state() != S_S_INITIAL) { + log_warn("Unexpected ruleset selection packet from client"); + return; + } + + if (pc->access_level < ALLOW_HACK) { + log_verbose("Attempt to set ruleset from non-hack level connection"); + } + + name = modpack_file_from_ruleset_cache(packet->modpack); + + if (name == NULL) { + log_error("Modpack \"%s\" not in ruleset cache", packet->modpack); + return; + } + + sf = secfile_load(name, FALSE); + + if (sf == NULL) { + log_error("Failed to load modpack file \"%s\"", name); + return; + } + + name = modpack_serv_file(sf); + + if (name != NULL) { + read_init_script(pc, name, FALSE, FALSE); + } else { + name = modpack_rulesetdir(sf); + + if (name != NULL) { + set_rulesetdir(pc, name, FALSE, 0); + } else { + log_error("Modpack \"%s\" does not contain ruleset at all", + packet->modpack); + } + } + + secfile_destroy(sf); +} + /************************************************************************//** Opens a file specified by the packet and compares the packet values with the file values. Sends an answer to the client once it's done. diff --git a/server/srv_main.c b/server/srv_main.c index 684d6e981e..85a32b0c47 100644 --- a/server/srv_main.c +++ b/server/srv_main.c @@ -2048,7 +2048,8 @@ bool server_packet_input(struct connection *pconn, void *packet, int type) if (S_S_RUNNING != server_state() && type != PACKET_NATION_SELECT_REQ && type != PACKET_PLAYER_READY - && type != PACKET_VOTE_SUBMIT) { + && type != PACKET_VOTE_SUBMIT + && type != PACKET_RULESET_SELECT) { if (S_S_OVER == server_state()) { /* This can happen by accident, so we don't want to print * out lots of error messages. Ie, we use log_debug(). */ diff --git a/server/stdinhand.c b/server/stdinhand.c index 2ecafdc68c..1039d0fe3b 100644 --- a/server/stdinhand.c +++ b/server/stdinhand.c @@ -123,8 +123,6 @@ static bool set_ai_level_named(struct connection *caller, const char *name, static bool set_ai_level(struct connection *caller, const char *name, enum ai_level level, bool check); static bool away_command(struct connection *caller, bool check); -static bool set_rulesetdir(struct connection *caller, char *str, bool check, - int read_recursion); static bool show_command(struct connection *caller, char *str, bool check); static bool show_settings(struct connection *caller, enum command_id called_as, @@ -143,7 +141,7 @@ static bool surrender_command(struct connection *caller, char *str, bool check); static bool handle_stdin_input_real(struct connection *caller, char *str, bool check, int read_recursion); static bool read_init_script_real(struct connection *caller, - char *script_filename, bool from_cmdline, + const char *script_filename, bool from_cmdline, bool check, int read_recursion); static bool reset_command(struct connection *caller, char *arg, bool check, int read_recursion); @@ -1136,7 +1134,7 @@ static bool read_command(struct connection *caller, char *arg, bool check, /**********************************************************************//** Main entry point for reading an init script. **************************************************************************/ -bool read_init_script(struct connection *caller, char *script_filename, +bool read_init_script(struct connection *caller, const char *script_filename, bool from_cmdline, bool check) { return read_init_script_real(caller, script_filename, from_cmdline, @@ -1155,7 +1153,7 @@ bool read_init_script(struct connection *caller, char *script_filename, permissions unless there are other bugs. **************************************************************************/ static bool read_init_script_real(struct connection *caller, - char *script_filename, bool from_cmdline, + const char *script_filename, bool from_cmdline, bool check, int read_recursion) { FILE *script_file; @@ -3881,8 +3879,8 @@ bool load_command(struct connection *caller, const char *filename, bool check, other bad stuff in the directory name, and will only use directories inside the data directories. **************************************************************************/ -static bool set_rulesetdir(struct connection *caller, char *str, bool check, - int read_recursion) +bool set_rulesetdir(struct connection *caller, const char *str, bool check, + int read_recursion) { char filename[512]; const char *pfilename; diff --git a/server/stdinhand.h b/server/stdinhand.h index a8f59942a9..6a210dc2fd 100644 --- a/server/stdinhand.h +++ b/server/stdinhand.h @@ -32,8 +32,10 @@ void cmd_reply(enum command_id cmd, struct connection *caller, bool handle_stdin_input(struct connection *caller, char *str); void set_ai_level_direct(struct player *pplayer, enum ai_level level); -bool read_init_script(struct connection *caller, char *script_filename, +bool read_init_script(struct connection *caller, const char *script_filename, bool from_cmdline, bool check); +bool set_rulesetdir(struct connection *caller, const char *str, bool check, + int read_recursion); struct strvec *get_init_script_choices(void); void show_players(struct connection *caller); -- 2.34.1