From: Christian Heller Date: Sun, 13 Jul 2014 21:49:13 +0000 (+0200) Subject: Server: Read in former "config" data as normal server god commands. X-Git-Tag: tce~707 X-Git-Url: https://plomlompom.com/repos/ledger?a=commitdiff_plain;h=891ba8fbca53d920f6b3704827fa6b8aee737de4;p=plomrogue Server: Read in former "config" data as normal server god commands. Re-wrote and re-factored lots of stuff to facilitate this. --- diff --git a/confserver/world b/confserver/world index 1bbb10c..a399aee 100644 --- a/confserver/world +++ b/confserver/world @@ -1,78 +1,86 @@ MAP_LENGTH 64 PLAYER_TYPE 0 -ACTION 1 -NAME wait -EFFORT 1 +THING_ACTION 1 +TA_EFFORT 1 +TA_NAME wait -ACTION 2 -NAME move -EFFORT 5 +THING_ACTION 2 +TA_EFFORT 5 +TA_NAME move -ACTION 3 -NAME pick_up -EFFORT 15 +THING_ACTION 3 +TA_EFFORT 15 +TA_NAME pick_up -ACTION 4 -NAME drop -EFFORT 5 +THING_ACTION 4 +TA_EFFORT 5 +TA_NAME drop -ACTION 5 -NAME use -EFFORT 45 +THING_ACTION 5 +TA_EFFORT 45 +TA_NAME use -THINGTYPE 0 -NAME HUMAN -SYMBOL @ -LIFEPOINTS 5 -CORPSE_ID 5 -CONSUMABLE 0 -START_NUMBER 1 +THING_TYPE 0 +TT_START_NUMBER 1 +TT_LIFEPOINTS 5 +TT_SYMBOL @ +TT_NAME HUMAN +TT_CONSUMABLE 0 -THINGTYPE 1 -NAME ANT -SYMBOL a -LIFEPOINTS 1 -CORPSE_ID 4 -CONSUMABLE 0 -START_NUMBER 27 +THING_TYPE 1 +TT_START_NUMBER 27 +TT_LIFEPOINTS 1 +TT_SYMBOL a +TT_NAME ANT +TT_CONSUMABLE 0 -THINGTYPE 2 -NAME ZOMBIE -SYMBOL z -LIFEPOINTS 3 -CORPSE_ID 5 -CONSUMABLE 0 -START_NUMBER 9 +THING_TYPE 2 +TT_START_NUMBER 9 +TT_LIFEPOINTS 3 +TT_SYMBOL z +TT_NAME ZOMBIE +TT_CONSUMABLE 0 -THINGTYPE 3 -NAME SHOGGOTH -SYMBOL S -LIFEPOINTS 9 -CORPSE_ID 6 -CONSUMABLE 0 -START_NUMBER 3 +THING_TYPE 3 +TT_START_NUMBER 3 +TT_LIFEPOINTS 9 +TT_SYMBOL S +TT_NAME SHOGGOTH +TT_CONSUMABLE 0 -THINGTYPE 4 -NAME DIRT -SYMBOL # -LIFEPOINTS 0 -CORPSE_ID 4 -CONSUMABLE 0 -START_NUMBER 9 +THING_TYPE 4 +TT_START_NUMBER 9 +TT_LIFEPOINTS 0 +TT_SYMBOL # +TT_NAME DIRT +TT_CONSUMABLE 0 -THINGTYPE 5 -NAME SKELETON -SYMBOL % -LIFEPOINTS 0 -CORPSE_ID 4 -CONSUMABLE 0 -START_NUMBER 9 +THING_TYPE 5 +TT_START_NUMBER 9 +TT_LIFEPOINTS 0 +TT_SYMBOL % +TT_NAME SKELETON +TT_CONSUMABLE 0 -THINGTYPE 6 -NAME 'MAGIC MEAT' -SYMBOL m -LIFEPOINTS 0 -CORPSE_ID 4 -CONSUMABLE 3 -START_NUMBER 1 +THING_TYPE 6 +TT_START_NUMBER 1 +TT_LIFEPOINTS 0 +TT_SYMBOL m +TT_NAME 'MAGIC MEAT' +TT_CONSUMABLE 3 + +THING_TYPE 0 +TT_CORPSE_ID 5 +THING_TYPE 1 +TT_CORPSE_ID 4 +THING_TYPE 2 +TT_CORPSE_ID 5 +THING_TYPE 3 +TT_CORPSE_ID 6 +THING_TYPE 4 +TT_CORPSE_ID 4 +THING_TYPE 5 +TT_CORPSE_ID 4 +THING_TYPE 6 +TT_CORPSE_ID 4 diff --git a/src/client/command_db.c b/src/client/command_db.c index b9d10a6..a7523b7 100644 --- a/src/client/command_db.c +++ b/src/client/command_db.c @@ -6,13 +6,12 @@ #include /* uint8_t */ #include /* free() */ #include /* strcmp(), strdup() */ -#include "../common/parse_file.h" /* EDIT_STARTED,parse_init_entry(), - * parse_id_uniq(), parse_unknown_arg(), - * parsetest_too_many_values(), parse_file(), - * parse_and_reduce_to_readyflag(), - * parse_flagval() - */ #include "array_append.h" /* array_append() */ +#include "parse.h" /* EDIT_STARTED, parse_init_entry(), parse_id_uniq(), + * parse_unknown_arg(), parsetest_too_many_values(), + * parse_file(), parse_and_reduce_to_readyflag(), + * parse_flagval() + */ #include "world.h" /* global world */ #include "cleanup.h" /* set_cleanup_flag() */ diff --git a/src/client/control.c b/src/client/control.c index 04f0c94..17a7632 100644 --- a/src/client/control.c +++ b/src/client/control.c @@ -181,8 +181,8 @@ static char * build_server_message_with_argument(struct Command * cmd) { char * f_name = "build_server_message_with_argument()"; uint8_t command_size = strlen(cmd->server_msg); - char * arg_str; - uint8_t arg_size; + char * arg_str = ""; + uint8_t arg_size = 0; if ('i' == cmd->arg) { arg_size = 3; diff --git a/src/client/interface_conf.c b/src/client/interface_conf.c index 853fdaa..aba5e3b 100644 --- a/src/client/interface_conf.c +++ b/src/client/interface_conf.c @@ -9,13 +9,7 @@ #include /* FILE, sprintf() */ #include /* strchr(), strcmp(), strdup(), strlen() */ #include /* optarg, getopt() */ -#include "../common/parse_file.h" /* EDIT_STARTED, parse_file(),parse_flagval(), - * token_from_line(), parsetest_singlechar(), - * parse_and_reduce_to_readyflag(), - * parsetest_defcontext(),parse_unknown_arg(), - * parsetest_too_many_values(), - * parse_id_uniq(), parse_init_entry() - */ +#include "../common/parse_file.h" /* token_from_line(),parsetset_singlechar() */ #include "../common/readwrite.h" /* atomic_write_start(), atomic_write_finish(), * detect_atomic_leftover(), try_fwrite() */ @@ -26,6 +20,11 @@ #include "command_db.h" /* get_command() */ #include "keybindings.h" /* KeyBinding, KeyBindingDB, get_command_to_keycode()*/ #include "map.h" /* map_center() */ +#include "parse.h" /* EDIT_STARTED, parse_file(), parse_flagval(), + * parse_and_reduce_to_readyflag(), parse_id_uniq() + * parsetest_defcontext(), parse_unknown_arg(), + * parsetest_too_many_values(), parse_init_entry() + */ #include "wincontrol.h" /* toggle_window() */ #include "windows.h" /* Win, free_winDB(), make_v_screen_and_init_win_sizes() */ #include "world.h" /* global world */ diff --git a/src/client/parse.c b/src/client/parse.c new file mode 100644 index 0000000..2767c46 --- /dev/null +++ b/src/client/parse.c @@ -0,0 +1,120 @@ +/* src/client/parse.c */ + +#define _POSIX_C_SOURCE 200809L /* strdup() */ +#include "parse.h" +#include /* size_t, NULL */ +#include /* FILE, snprintf() */ +#include /* uint8_t, uint32_t */ +#include /* free() */ +#include /* strdup(), strlen() */ +#include /* access(), F_OK */ +#include "../common/parse_file.h" /* set_err_line_options(), err_line_inc(), + * err_line_zero(), token_from_line() + */ +#include "../common/readwrite.h" /* try_fopen(),try_fclose(),textfile_width() */ +#include "../common/rexit.h" /* exit_err(), exit_trouble() */ +#include "../common/try_malloc.h" /* try_malloc() */ + + + +extern void parse_file(char * path, void (* token_to_entry) (char *, char *)) +{ + char * f_name = "read_new_config_file()"; + char * prefix = "Failed reading config file: \""; + char * affix = "\". "; + size_t size = strlen(prefix) + strlen(path) + strlen(affix) + 1; + char * errline_intro = try_malloc(size, f_name); + int test = snprintf(errline_intro, size, "%s%s%s", prefix, path, affix); + exit_trouble(test < 0, f_name, "snprintf()"); + exit_err(access(path, F_OK), errline_intro); + FILE * file = try_fopen(path, "r", f_name); + uint32_t linemax = textfile_width(file); + char * errline_line = try_malloc(linemax + 1, f_name); + set_err_line_options(errline_intro, errline_line, 1); + err_line_zero(); + err_line(0 == linemax, "File is empty."); + char * token0 = NULL; /* For final token_to_entry() if while() stagnates. */ + char * token1 = NULL; + char * err_val = "No value given."; + while (try_fgets(errline_line, linemax + 1, file, f_name)) + { + err_line_inc(); + // err_line(UINT32_MAX == err_line_count, "Line reaches max lines limit."); + char * line_copy = strdup(errline_line); + token0 = token_from_line(line_copy); + if (token0) + { + err_line(0 == (token1 = token_from_line(NULL)), err_val); + token_to_entry(token0, token1); + token0 = NULL; + } + free(line_copy); + } + token_to_entry(token0, token1); + try_fclose(file, f_name); + free(errline_line); + free(errline_intro); +} + + + +extern void parsetest_defcontext(uint8_t flags) +{ + err_line(!(flags & EDIT_STARTED),"Outside appropriate definition context."); +} + + + +extern void parsetest_too_many_values() +{ + err_line(NULL != token_from_line(NULL), "Too many values."); +} + + + +extern void parse_id_uniq(int test) +{ + err_line(0 != test, "Declaration of ID already used."); +} + + + +extern void parse_unknown_arg() +{ + err_line(1, "Unknown argument."); +} + + + +extern char * parse_init_entry(uint8_t * flags, size_t size) +{ + char * f_name = "parse_init_entry()"; + *flags = EDIT_STARTED; + char * p = try_malloc(size, f_name); + memset(p, 0, size); + return p; +} + + + +extern uint8_t parse_flagval(char * token0, char * token1, char * comparand, + uint8_t * flags, uint8_t set_flag, char type, + char * element) +{ + if (parse_val(token0, token1, comparand, type, element)) + { + parsetest_defcontext(*flags); + *flags = *flags | set_flag; + return 1; + } + return 0; +} + + + +extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag) +{ + char * err_fin = "Last definition block not finished yet."; + err_line((*flags & ready_flag) ^ ready_flag, err_fin); + *flags = ready_flag; +} diff --git a/src/client/parse.h b/src/client/parse.h new file mode 100644 index 0000000..33a9687 --- /dev/null +++ b/src/client/parse.h @@ -0,0 +1,55 @@ +/* src/client/parse.h + * + * Routines for file parsing. + */ + +#ifndef PARSE_H +#define PARSE_H + +#include /* size_t */ +#include /* uint8_t */ + + + +enum parse_flags +{ + EDIT_STARTED = 0x01 +}; + + + +/* Parse file at "path" by passing each line's first two tokens to + * "token_to_entry". Ignore empty lines. Non-empty lines must feature at least + * two tokens as delimited either be whitespace or single quotes (to allow for + * tokens featuring whitespaces). When EOF is reached, token_to_entry() is + * called a last time with a first token of NULL. + */ +extern void parse_file(char * path, void ( *token_to_entry) (char *, char *)); + +/* Calls err_line() with fitting message if EDIT_STARTED not set in "flags". */ +extern void parsetest_defcontext(uint8_t flags); + +/* Ensure token_from_line() does not find any more tokens on the line. */ +extern void parsetest_too_many_values(); + +/* Trigger err_line() with "Unknown argument" message. */ +extern void parse_unknown_arg(); + +/* If "test"!=0 call err_line() with "Declaration of ID already used" message.*/ +extern void parse_id_uniq(int test); + +/* Set "flags"=EDIT_STARTED and return pointer to new zeroed array of "size". */ +extern char * parse_init_entry(uint8_t * flags, size_t size); + +/* Wrapper to parse_val() that sets "flags" to "flags"|"set_flag" on success. */ +extern uint8_t parse_flagval(char * token0, char * token1, char * comparand, + uint8_t * flags, uint8_t set_flag, char type, + char * element); + +/* Check "ready_flag" is set in "flags", re-set "flags" to "ready_flag" only. */ +extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag); + + + +#endif + diff --git a/src/common/parse_file.c b/src/common/parse_file.c index 8ce20ad..82ba4e3 100644 --- a/src/common/parse_file.c +++ b/src/common/parse_file.c @@ -7,8 +7,6 @@ #include /* int16_t,uint8_t,uint32_t, INT16_MIN, UINT{8,16,32}_MAX */ #include /* atoi(), free() */ #include /* strchr, strcmp(), strdup(), strlen() */ -#include /* access(), F_OK */ -#include "readwrite.h" /* try_fopen(), try_fclose(), textfile_width() */ #include "rexit.h" /* exit_err(), exit_trouble() */ #include "try_malloc.h" /* try_malloc() */ @@ -49,53 +47,26 @@ static void set_token_end(char ** start, char ** limit_char) -extern void parse_file(char * path, void (* token_to_entry) (char *, char *)) +extern void set_err_line_options(char * intro, char * line, uint8_t exit) { - char * f_name = "read_new_config_file()"; - char * prefix = "Failed reading config file: \""; - char * affix = "\". "; - size_t size = strlen(prefix) + strlen(path) + strlen(affix) + 1; - char * errline_intro = try_malloc(size, f_name); - int test = snprintf(errline_intro, size, "%s%s%s", prefix, path, affix); - exit_trouble(test < 0, f_name, "snprintf()"); - exit_err(access(path, F_OK), errline_intro); - FILE * file = try_fopen(path, "r", f_name); - uint32_t linemax = textfile_width(file); - char * errline_line = try_malloc(linemax + 1, f_name); - set_err_line_options(errline_intro, errline_line, 0, 1); - err_line(0 == linemax, "File is empty."); - char * token0 = NULL; /* For final token_to_entry() if while() stagnates. */ - char * token1 = NULL; - char * err_val = "No value given."; - while (try_fgets(errline_line, linemax + 1, file, f_name)) - { - err_line_count++; - err_line(UINT32_MAX == err_line_count, "Line reaches max lines limit."); - char * line_copy = strdup(errline_line); - token0 = token_from_line(line_copy); - if (token0) - { - err_line(0 == (token1 = token_from_line(NULL)), err_val); - token_to_entry(token0, token1); - token0 = NULL; - } - free(line_copy); - } - token_to_entry(token0, token1); - try_fclose(file, f_name); - free(errline_line); - free(errline_intro); + err_line_line = line; + err_line_intro = intro; + err_line_exit = exit; } -extern void set_err_line_options(char * intro, char * line, uint32_t count, - uint8_t exit) +extern void err_line_inc() { - err_line_count = count; - err_line_line = line; - err_line_intro = intro; - err_line_exit = exit; + err_line_count++; + err_line(UINT32_MAX == err_line_count, "Line reaches max lines limit."); +} + + + +extern void err_line_zero() +{ + err_line_count = 0; } @@ -108,7 +79,7 @@ extern uint8_t err_line(uint8_t test, char * msg) } char * f_name = "err_line()"; char * prefix = " Offending line "; - char * affix = ":\n"; + char * affix = ": "; size_t size = strlen(err_line_intro) + strlen(msg) + strlen(prefix) + 10 /* strlen for uint32_t representations */ + strlen(affix) + strlen(err_line_line) + 1; @@ -208,13 +179,6 @@ extern uint8_t parsetest_int(char * string, char type) -extern void parsetest_defcontext(uint8_t flags) -{ - err_line(!(flags & EDIT_STARTED),"Outside appropriate definition context."); -} - - - extern uint8_t parsetest_singlechar(char * string) { return err_line(1 !=strlen(string),"Value must be single ASCII character."); @@ -222,38 +186,6 @@ extern uint8_t parsetest_singlechar(char * string) -extern void parsetest_too_many_values() -{ - err_line(NULL != token_from_line(NULL), "Too many values."); -} - - - -extern void parse_id_uniq(int test) -{ - err_line(0 != test, "Declaration of ID already used."); -} - - - -extern void parse_unknown_arg() -{ - err_line(1, "Unknown argument."); -} - - - -extern char * parse_init_entry(uint8_t * flags, size_t size) -{ - char * f_name = "parse_init_entry()"; - *flags = EDIT_STARTED; - char * p = try_malloc(size, f_name); - memset(p, 0, size); - return p; -} - - - extern uint8_t parse_val(char * token0, char * token1, char * comparand, char type, char * element) { @@ -261,11 +193,15 @@ extern uint8_t parse_val(char * token0, char * token1, char * comparand, { if ('s' == type) { + free(* (char **) element); * (char **) element = strdup(token1); } - else if ('c' == type && !parsetest_singlechar(token1)) + else if ('c' == type) { - *element = (token1)[0]; + if (!parsetest_singlechar(token1)) + { + *element = (token1)[0]; + } } else if (!parsetest_int(token1, type)) { @@ -290,27 +226,3 @@ extern uint8_t parse_val(char * token0, char * token1, char * comparand, } return 0; } - - - -extern uint8_t parse_flagval(char * token0, char * token1, char * comparand, - uint8_t * flags, uint8_t set_flag, char type, - char * element) -{ - if (parse_val(token0, token1, comparand, type, element)) - { - parsetest_defcontext(*flags); - *flags = *flags | set_flag; - return 1; - } - return 0; -} - - - -extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag) -{ - char * err_fin = "Last definition block not finished yet."; - err_line((*flags & ready_flag) ^ ready_flag, err_fin); - *flags = ready_flag; -} diff --git a/src/common/parse_file.h b/src/common/parse_file.h index 4192dac..7d7cfbd 100644 --- a/src/common/parse_file.h +++ b/src/common/parse_file.h @@ -1,36 +1,23 @@ /* src/common/parse_file.h * - * Tools for parsing config files. + * Tools for parsing files. */ #ifndef PARSE_FILE_H #define PARSE_FILE_H -#include /* size_t */ #include /* uint8_t */ -enum parse_flags -{ - EDIT_STARTED = 0x01 -}; - - - -/* Parse file at "path" by passing each line's first two tokens to - * "token_to_entry". Ignore empty lines. Non-empty lines must feature at least - * two tokens as delimited either be whitespace or single quotes (to allow for - * tokens featuring whitespaces). When EOF is reached, token_to_entry() is - * called a last time with a first token of NULL. - */ -extern void parse_file(char * path, void ( *token_to_entry) (char *, char *)); - /* Set err_line() options: "intro" message, char array used to store analyzed - * lines ("line"), line start "count", whether to "exit" on error message. + * lines ("line"), and whether to "exit" on error message. */ -extern void set_err_line_options(char * intro, char * line, uint32_t count, - uint8_t exit); +extern void set_err_line_options(char * intro, char * line, uint8_t exit); + +/* Increment and reset (to zero) err_line() line counter. */ +extern void err_line_inc(); +extern void err_line_zero(); /* If "test", output "msg", faulty line, its number and exit if so defined by * set_err_line_options(), else return 1 on "test" and 0 if "test" is 0. @@ -56,21 +43,6 @@ extern uint8_t parsetest_int(char * string, char type); /* Test for "string" to be of length 1 (excluding "\0"). Return 1 on failure. */ extern uint8_t parsetest_singlechar(char * string); -/* Calls err_line() with fitting message if EDIT_STARTED not set in "flags". */ -extern void parsetest_defcontext(uint8_t flags); - -/* Ensure token_from_line() does not find any more tokens on the line. */ -extern void parsetest_too_many_values(); - -/* Trigger err_line() with "Unknown argument" message. */ -extern void parse_unknown_arg(); - -/* If "test"!=0 call err_line() with "Declaration of ID already used" message.*/ -extern void parse_id_uniq(int test); - -/* Set "flags"=EDIT_STARTED and return pointer to new zeroed array of "size". */ -extern char * parse_init_entry(uint8_t * flags, size_t size); - /* If "token0" fits "comparand", set "element" to value read from "token1" as * string (type: "s"), char ("c") uint8 ("8"), uint16 ("u"), uint32 ("U") or * int16 ("i"), and return 1; else 0. @@ -78,14 +50,6 @@ extern char * parse_init_entry(uint8_t * flags, size_t size); extern uint8_t parse_val(char * token0, char * token1, char * comparand, char type, char * element); -/* Wrapper to parse_val() that sets "flags" to "flags"|"set_flag" on success. */ -extern uint8_t parse_flagval(char * token0, char * token1, char * comparand, - uint8_t * flags, uint8_t set_flag, char type, - char * element); - -/* Check "ready_flag" is set in "flags", re-set "flags" to "ready_flag" only. */ -extern void parse_and_reduce_to_readyflag(uint8_t * flags, uint8_t ready_flag); - #endif diff --git a/src/server/configfile.c b/src/server/configfile.c deleted file mode 100644 index ca1b07f..0000000 --- a/src/server/configfile.c +++ /dev/null @@ -1,322 +0,0 @@ -/* src/server/configfile.c */ - -#include /* size_t, NULL */ -#include /* snprintf() */ -#include /* uint8_t */ -#include /* atoi(), free() */ -#include /* strcmp() */ -#include "../common/parse_file.h" /* EDIT_STARTED, parsetest_int(),parse_file(), - * parsetest_too_many_values(),parse_id_uniq() - * parse_unknown_arg(), parse_init_entry(), - * parse_and_reduce_to_readyflag(), - * parse_flagval() - */ -#include "../common/rexit.h" /* exit_err(), exit_trouble() */ -#include "../common/try_malloc.h" /* try_malloc() */ -#include "cleanup.h" /* set_cleanup_flag(), CLEANUP_THING_TYPES, - * CLEANUP_THING_ACTIONS - */ -#include "hardcoded_strings.h" /* s */ -#include "thing_actions.h" /* ThingAction */ -#include "things.h" /* Thing, ThingType */ -#include "world.h" /* world global */ - - - -/* Flags defining state of thing type and action entry reading ((un-)finished / - * ready for starting the reading of a new definition etc.) - */ -enum flag -{ - HEIGHT_SET = 0x02, - WIDTH_SET = 0x04, - NAME_SET = 0x02, - EFFORT_SET = 0x04, - CORPSE_ID_SET = 0x04, - SYMBOL_SET = 0x08, - LIFEPOINTS_SET = 0x10, - CONSUMABLE_SET = 0x20, - START_N_SET = 0x40, - READY_ACTION = NAME_SET | EFFORT_SET, - READY_THING = NAME_SET | CORPSE_ID_SET | SYMBOL_SET | LIFEPOINTS_SET - | CONSUMABLE_SET | START_N_SET -}; - - - -/* What ThingType and ThingAction structs have in common at their top. Use this - * to allow same functions run over structs of both types. - */ -struct EntryHead -{ - uint8_t id; - struct EntryHead * next; -}; - - - -/* Interpret "token0" and "token1" as data to write into the ThingAction / - * ThingType DB. - * - * Individual ThingType / ThingAction DB entries are put together line by line - * before being written. Writing only happens after all necessary members of an - * entry have been assembled, and when additionally a) a new entry is started by - * a "token0" of "ACTION" or "THINGTYPE"; or b) "token0" is NULL. - * - * Also check against the line parse_file() read tokens from having more tokens. - */ -static void tokens_into_entries(char * token0, char * token1); - -/* Start reading a new DB entry of "size" from tokens if "token0" matches - * "comparand". Set EDIT_STARTED in "flags" to mark beginning of new entry - * reading. Check that "token1" id of new entry has not already been used in DB - * starting at "entry_cmp". - */ -static uint8_t start_entry(char * token0, char * token1, char * comparand, - uint8_t * flags, size_t size, - struct EntryHead ** entry, - struct EntryHead * entry_cmp); - -/* Write DB entry pointed to by "entry" to its appropriate location. */ -static void write_if_entry(struct EntryHead ** entry, - struct EntryHead *** entry_p_p_p); - -/* Ensure that all .corpse_id members in the ThingType DB fit .id members of - * ThingType DB entries. - */ -static void test_corpse_ids(); - -/* If "token0" matches "comparand", set world.player_type to int in "token1". */ -static uint8_t set_player_type(char * token0, char * comparand, char * token1); - -/* If "token0" matches "comparand", set world.map.length to int in "token1". */ -static uint8_t set_map_length(char * token0, char * comparand, char * token1); - -/* Try to read tokens as members for the definition currently edited, which may - * be "tt" or "ta". What member of which of the two is set depends on which of - * the flags has EDIT_STARTED set and on the key name in "token0". Return 1 if - * interpretation succeeds, else 0. - * - * Note that ThingAction entries' .name also determines their .func. - */ -static uint8_t set_members(char * token0, char * token1, - uint8_t * thing_flags, uint8_t * action_flags, - struct ThingType * tt, struct ThingAction * ta); - -/* If "name" fits "ta"->name, set "ta"->func to "func". (Derives ThingAction - * .func from .name for set_members(). - */ -static uint8_t try_func_name(struct ThingAction * ta, - char * name, void (* func) (struct Thing *)); - - - -static void tokens_into_entries(char * token0, char * token1) -{ - char * str_action = "ACTION"; - char * str_thing = "THINGTYPE"; - char * str_player = "PLAYER_TYPE"; - char * str_map_length = "MAP_LENGTH"; - static struct ThingAction ** ta_p_p = &world.thing_actions; - static struct ThingType ** tt_p_p = &world.thing_types; - static uint8_t action_flags = READY_ACTION; - static uint8_t thing_flags = READY_THING; - static struct EntryHead * ta = NULL; - static struct EntryHead * tt = NULL; - if (!token0 || !strcmp(token0, str_action) || !strcmp(token0, str_thing) - || !strcmp(token0, str_player)) - { - parse_and_reduce_to_readyflag(&action_flags, READY_ACTION); - parse_and_reduce_to_readyflag(&thing_flags, READY_THING); - write_if_entry(&ta, (struct EntryHead ***) &ta_p_p); - write_if_entry(&tt, (struct EntryHead ***) &tt_p_p); - } - if (token0) - { - parsetest_too_many_values(); - if (start_entry(token0, token1, str_action, &action_flags, - sizeof(struct ThingAction), (struct EntryHead**) &ta, - (struct EntryHead *) world.thing_actions)) - { - err_line(0 == atoi(token1), "Value must not be 0."); - } - else if (!( start_entry(token0, token1, str_thing, &thing_flags, - sizeof(struct ThingType), - (struct EntryHead**) &tt, - (struct EntryHead *) world.thing_types) - || set_player_type(token0, str_player, token1) - || set_map_length(token0, str_map_length, token1) - || set_members(token0, token1, &thing_flags, &action_flags, - (struct ThingType *) tt, - (struct ThingAction *) ta))) - { - parse_unknown_arg(); - } - } -} - - - -static uint8_t start_entry(char * token0, char * token1, char * comparand, - uint8_t * flags, size_t size, - struct EntryHead ** entry, - struct EntryHead * entry_cmp) -{ - if (strcmp(token0, comparand)) - { - return 0; - } - *entry = (struct EntryHead *) parse_init_entry(flags, size); - parsetest_int(token1, '8'); - (*entry)-> id = atoi(token1); - for (; NULL != entry_cmp; entry_cmp = entry_cmp->next) - { - parse_id_uniq((*entry)->id == entry_cmp->id); - } - return 1; -} - - - -static void write_if_entry(struct EntryHead ** entry, - struct EntryHead *** entry_p_p_p) -{ - if (*entry) - { - (*entry)->next = NULL; - **entry_p_p_p = *entry; - *entry_p_p_p = &((*entry)->next); - *entry = NULL; /* So later runs of this don't re-append same entry. */ - } -} - - - -static void test_corpse_ids() -{ - char * f_name = "test_corpse_ids()"; - char * prefix = "In the thing types DB, one thing corpse ID does not " - "reference any known thing type in the DB. ID of " - "responsible thing type: "; - size_t size = strlen(prefix) + 3 + 1; /* 3: uint8_t representation strlen */ - char * err_corpse = try_malloc(size, f_name); - struct ThingType * test_entry_0 = world.thing_types; - for (; test_entry_0; test_entry_0 = test_entry_0->next) - { - uint8_t corpse_id_found = 0; - struct ThingType * test_entry_1 = world.thing_types; - for (; test_entry_1; test_entry_1 = test_entry_1->next) - { - if (test_entry_0->corpse_id == test_entry_1->id) - { - corpse_id_found = 1; - } - } - int test = snprintf(err_corpse, size, "%s%d", prefix, test_entry_0->id); - exit_trouble(test < 0, f_name, "snprintf()"); - exit_err(!corpse_id_found, err_corpse); - } - free(err_corpse); -} - - - -static uint8_t set_player_type(char * token0, char * comparand, char * token1) -{ - if (strcmp(token0, comparand)) - { - return 0; - } - parsetest_int(token1, '8'); - world.player_type = atoi(token1); - return 1; -} - - - -static uint8_t set_map_length(char * token0, char * comparand, char * token1) -{ - if (strcmp(token0, comparand)) - { - return 0; - } - parsetest_int(token1, 'i'); - int test = atoi(token1) > 256 || atoi(token1) < 1; - err_line(test, "Value must be >= 1 and <= 256."); - world.map.length = atoi(token1); - return 1; -} - - - -static uint8_t set_members(char * token0, char * token1, uint8_t * thing_flags, - uint8_t * action_flags, - struct ThingType * tt, struct ThingAction * ta) -{ - if ( *action_flags & EDIT_STARTED - && parse_flagval(token0, token1, "NAME", action_flags, - NAME_SET, 's', (char *) &ta->name)) - { - if (!( try_func_name(ta, s[S_CMD_MOVE], actor_move) - || try_func_name(ta, s[S_CMD_PICKUP], actor_pick) - || try_func_name(ta, s[S_CMD_DROP], actor_drop) - || try_func_name(ta, s[S_CMD_USE], actor_use))) - { - ta->func = actor_wait; - } - *action_flags = *action_flags | NAME_SET; - return 1; - } - else if ( parse_flagval(token0, token1, "NAME", thing_flags, - NAME_SET, 's', (char *) &tt->name) - || parse_flagval(token0, token1, "SYMBOL", thing_flags, - SYMBOL_SET, 'c', (char *) &tt->char_on_map) - || parse_flagval(token0, token1, "EFFORT", action_flags, - EFFORT_SET, '8', (char *) &ta->effort) - || parse_flagval(token0, token1, "START_NUMBER", thing_flags, - START_N_SET, '8', (char *) &tt->start_n) - || parse_flagval(token0, token1, "LIFEPOINTS", thing_flags, - LIFEPOINTS_SET, '8', (char *) &tt->lifepoints) - || parse_flagval(token0, token1, "CONSUMABLE", thing_flags, - CONSUMABLE_SET, '8', (char *) &tt->consumable) - || parse_flagval(token0, token1, "CORPSE_ID", thing_flags, - CORPSE_ID_SET, '8', (char *) &tt->corpse_id)) - { - return 1; - } - return 0; -} - - - -static uint8_t try_func_name(struct ThingAction * ta, char * name, - void (* func) (struct Thing *)) -{ - if (0 == strcmp(ta->name, name)) - { - ta->func = func; - return 1; - } - return 0; -} - - - -extern void read_config_file() -{ - parse_file(s[S_PATH_CONFIG], tokens_into_entries); - exit_err(!world.map.length, "Map size not defined in config file."); - uint8_t player_type_is_valid = 0; - struct ThingType * tt; - for (tt = world.thing_types; NULL != tt; tt = tt->next) - { - if (world.player_type == tt->id) - { - player_type_is_valid = 1; - break; - } - } - exit_err(!player_type_is_valid, "No valid thing type set for player."); - set_cleanup_flag(CLEANUP_THING_ACTIONS | CLEANUP_THING_TYPES); - test_corpse_ids(); -} diff --git a/src/server/configfile.h b/src/server/configfile.h deleted file mode 100644 index d80a2ba..0000000 --- a/src/server/configfile.h +++ /dev/null @@ -1,18 +0,0 @@ -/* src/server/configfile.h - * - * Reading in the config file of thing types and thing actions. - */ - -#ifndef CONFIGFILE_H -#define CONFIGFILE_H - - - -/* Parse thing type / action definitons file nto thing type and thing action - * definitions at world.thing_types and world.thing_actions. - */ -extern void read_config_file(); - - - -#endif diff --git a/src/server/god_commands.c b/src/server/god_commands.c new file mode 100644 index 0000000..efb607d --- /dev/null +++ b/src/server/god_commands.c @@ -0,0 +1,426 @@ +/* src/server/god_commands.c */ + +#include "god_commands.h" +#include /* NULL */ +#include /* uint8_t */ +#include /* atoi(), free() */ +#include /* strcmp() */ +#include /* F_OK, access(), unlink() */ +#include "../common/parse_file.h" /* err_line(), parse_val(), parsetest_int() */ +#include "../common/rexit.h" /* exit_trouble() */ +#include "cleanup.h" /* unset_cleanup_flag() */ +#include "field_of_view.h" /* build_fov_map() */ +#include "hardcoded_strings.h" /* s */ +#include "init.h" /* remake_world() */ +#include "map.h" /* remake_map() */ +#include "thing_actions.h" /* ThingAction, actor_wait(), actor_move(), + * actor_use(), actor_pickup(), actor_drop() + */ +#include "things.h" /* Thing, ThingType, add_thing(), get_thing(), own_thing(), + * free_things() + */ +#include "world.h" /* world */ + + + +/* Parse/apply god command in "tok0"/tok1" to manipulate a ThingType*/ +static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1); + +/* If "name" fits "ta"->name, set "ta"->func to "func". (Derives ThingAction + * .func from .name for set_members(). + */ +static uint8_t try_func_name(struct ThingAction * ta, char * name, + void (* func) (struct Thing *)); + +/* Parse/apply god command in "tok0"/"tok1" to manipulate a ThingAction. */ +static uint8_t parse_thingaction_manipulation(char * tok0, char * tok1); + +/* Parse/apply god command in "tok0"/"tok1" oo setting "t"'s thing type. */ +static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t); + +/* Parse/apply god command in "tok0"/"tok1" on setting up thing "t". */ +static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t); + +/* Parse/apply god command in "tok0"/"tok1" on positioning a thing "t". */ +static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t); + +/* Parse/apply god command in "tok0"/"tok1" on "t" owning another thing. */ +static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t); + +/* Parse/apply god command in "tok0"/"tok1" to manipulate a Thing. */ +static uint8_t parse_thing_manipulation(char * tok0, char * tok1); + +/* Performs parse_world_active()'s world activation legality tests. */ +static uint8_t world_may_be_set_active(); + +/* Parse/apply god command in "tok0"/"tok1" on toggling world.exists. Unset if + * argument is 0 and unlink worldstate file, but only set it on positive + * argument if it is not already set and a thing action of name S_CMD_WAIT, a + * player thing and a map are defined. On setting it, rebuild all FOVs. + */ +static uint8_t parse_world_active(char * tok0, char * tok1); + +/* Parse/apply god command in "tok0"/"tok1" to reset world.map.length. On + * re-set, set world.exists to 0, remove all things and free world.map.cells + */ +static uint8_t set_map_length(char * tok0, char * tok1); + + + +static uint8_t parse_thingtype_manipulation(char * tok0, char * tok1) +{ + static struct ThingType * tt = NULL; + if (!tt && + ( !strcmp(tok0, s[S_CMD_TT_CONSUM]) || !strcmp(tok0, s[S_CMD_TT_SYMB]) + || !strcmp(tok0, s[S_CMD_TT_STARTN]) || !strcmp(tok0, s[S_CMD_TT_NAME]) + || !strcmp(tok0, s[S_CMD_TT_CORPS]) || !strcmp(tok0, s[S_CMD_TT_HP]))) + { + err_line(1, "No thing type defined to manipulate yet."); + return 1; + } + uint8_t id; + if ( parse_val(tok0,tok1,s[S_CMD_TT_CONSUM],'8',(char *) &tt->consumable) + || parse_val(tok0,tok1,s[S_CMD_TT_HP],'8',(char *) &tt->lifepoints) + || parse_val(tok0,tok1,s[S_CMD_TT_STARTN],'8',(char *) &tt->start_n) + || parse_val(tok0,tok1,s[S_CMD_TT_SYMB],'c',(char *) &tt->char_on_map) + || parse_val(tok0,tok1,s[S_CMD_TT_NAME],'s',(char *) &tt->name)); + else if (parse_val(tok0, tok1, s[S_CMD_TT_CORPS],'8',(char *)&id)) + { + if (!get_thing_type(id)) + { + err_line(1, "Corpse ID belongs to no known thing type."); + return 1; + } + tt->corpse_id = id; + } + else if (parse_val(tok0, tok1, s[S_CMD_THINGTYPE], '8', (char *) &id)) + { + tt = get_thing_type(id); + if (!tt) + { + tt = add_thing_type(id); + } + } + else + { + return 0; + } + return 1; +} + + + +static uint8_t try_func_name(struct ThingAction * ta, char * name, + void (* func) (struct Thing *)) +{ + if (0 == strcmp(ta->name, name)) + { + ta->func = func; + return 1; + } + return 0; +} + + + +static uint8_t parse_thingaction_manipulation(char * tok0, char * tok1) +{ + static struct ThingAction * ta = NULL; + if (!ta && + (!strcmp(tok0, s[S_CMD_TA_EFFORT]) || !strcmp(tok0, s[S_CMD_TA_NAME]))) + { + err_line(1, "No thing action defined to manipulate yet."); + return 1; + } + uint8_t id; + if (parse_val(tok0, tok1, s[S_CMD_TA_EFFORT],'8',(char *) &ta->effort)); + else if (parse_val(tok0, tok1, s[S_CMD_TA_NAME], 's', (char *) &ta->name)) + { + if (!( try_func_name(ta, s[S_CMD_MOVE], actor_move) + || try_func_name(ta, s[S_CMD_PICKUP], actor_pick) + || try_func_name(ta, s[S_CMD_WAIT], actor_wait) + || try_func_name(ta, s[S_CMD_DROP], actor_drop) + || try_func_name(ta, s[S_CMD_USE], actor_use))) + { + err_line(1, "Invalid action function name."); + return 1; + } /* Legal worlds have at least one thing action for waiting. */ + if (world.exists) + { + world.exists = 0 != get_thing_action_id_by_name(s[S_CMD_WAIT]); + } + } + else if (parse_val(tok0, tok1, s[S_CMD_THINGACTION], '8', (char *) &id)) + { + ta = get_thing_action(id); + if (!ta) + { + ta = add_thing_action(id); + } + } + else + { + return 0; + } + return 1; +} + + + +static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t) +{ + uint8_t type; + if (parse_val(tok0, tok1, s[S_CMD_T_TYPE], '8', (char *) &type)) + { + struct ThingType * tt = get_thing_type(type); + if (!err_line(!tt, "Thing type does not exist.")) + { + t->type = type; + } + return 1; + } + return 0; +} + + + +static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t) +{ + uint8_t command; + if (parse_val(tok0, tok1, s[S_CMD_T_COMMAND], '8', (char *) &command)) + { + if (!command) + { + t->command = command; + return 1; + } + struct ThingAction * ta = world.thing_actions; + for (; ta && command != ta->id; ta = ta->next); + if (!err_line(!ta, "Thing action does not exist.")) + { + t->command = command; + } + return 1; + } + return 0; +} + + + +static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t) +{ + char axis = 0; + if (!strcmp(tok0, s[S_CMD_T_POSY])) + { + axis = 'y'; + } + else if (!strcmp(tok0, s[S_CMD_T_POSX])) + { + axis = 'x'; + } + if (axis && !parsetest_int(tok1, '8')) + { + uint8_t length = atoi(tok1); + char * err = "Position is outside of map."; + if (!err_line(length >= world.map.length, err)) + { + if ('y' == axis) + { + t->pos.y = length; + } + else if ('x' == axis) + { + t->pos.x = length; + } + free(t->fov_map); + if (world.exists && t->lifepoints) + { + t->fov_map = build_fov_map(t); + } + } + return 1; + } + return 0; +} + + + +static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t) +{ + uint8_t id; + if (parse_val(tok0, tok1, s[S_CMD_T_CARRIES], '8', (char *) &id)) + { + if (!err_line(id == t->id, "Thing cannot carry itself.")) + { + struct Thing * o = get_thing(world.things, id, 0); + if (!err_line(!o, "Thing not available for carrying.")) + { + own_thing(&(t->owns), &world.things, id); + o->pos = t->pos; + } + } + return 1; + } + return 0; +} + + + +static uint8_t parse_thing_manipulation(char * tok0, char * tok1) +{ + static struct Thing * t = NULL; + if (!t && + ( !strcmp(tok0, s[S_CMD_T_PROGRESS]) || !strcmp(tok0, s[S_CMD_T_TYPE]) + || !strcmp(tok0, s[S_CMD_T_CARRIES]) || !strcmp(tok0, s[S_CMD_T_POSY]) + || !strcmp(tok0, s[S_CMD_T_POSY]) || !strcmp(tok0, s[S_CMD_T_ARGUMENT]) + || !strcmp(tok0, s[S_CMD_T_HP]) || !strcmp(tok0, s[S_CMD_T_COMMAND]))) + { + err_line(1, "No thing defined to manipulate yet."); + return 1; + } + uint8_t id; + if ( parse_thing_type(tok0, tok1, t) + || parse_thing_command(tok0, tok1, t) + || parse_val(tok0,tok1, s[S_CMD_T_ARGUMENT], '8', (char *)&t->arg) + || parse_val(tok0,tok1, s[S_CMD_T_PROGRESS], '8', (char *)&t->progress) + || parse_val(tok0,tok1, s[S_CMD_T_HP], '8', (char *) &t->lifepoints) + || parse_position(tok0, tok1, t) + || parse_carry(tok0, tok1, t)); + else if (parse_val(tok0, tok1, s[S_CMD_THING], 'i', (char *) &id)) + { + t = get_thing(world.things, id, 1); + char * err = "No thing type found to initialize new thing."; + if (!t && !err_line(NULL == world.thing_types, err)) + { + t = add_thing(id, world.thing_types->id, 0, 0); + if (world.exists && t->lifepoints) + { + t->fov_map = build_fov_map(t); + } + } + } + else + { + return 0; + } + return 1; +} + + + +static uint8_t world_may_be_set_active() +{ + if (!get_thing_action_id_by_name(s[S_CMD_WAIT])) + { + err_line(1, "No thing action of name 'wait' found."); + return 0; + } + if (!get_player()) + { + err_line(1, "No un-owned player thing (of id=0) found."); + return 0; + } + if (!world.map.cells) + { + err_line(1, "No map found."); + return 0; + } + return 1; +} + + + +static uint8_t parse_world_active(char * tok0, char * tok1) +{ + char * f_name = "parse_world_active()"; + if (!strcmp(tok0, s[S_CMD_WORLD_ACTIVE]) && !parsetest_int(tok1, '8')) + { + if (!parsetest_int(tok1, '8')) + { + uint8_t argument = atoi(tok1); + if (!argument) + { + if (!access(s[S_PATH_WORLDSTATE], F_OK)) + { + int test = unlink(s[S_PATH_WORLDSTATE]); + exit_trouble(-1 == test, f_name, "unlink()"); + } + world.exists = 0; + } + else if (world.exists) + { + err_line(1, "World already active."); + } + else if (world_may_be_set_active()) + { + struct Thing * ti; + for (ti = world.things; ti; ti = ti->next) + { + if (ti->lifepoints) + { + if (ti->fov_map) + { + free(ti->fov_map); + } + ti->fov_map = build_fov_map(ti); + } + } + world.exists = 1; + } + return 1; + } + } + return 0; +} + + + +static uint8_t set_map_length(char * tok0, char * tok1) +{ + if (!strcmp(tok0, s[S_CMD_MAPLENGTH]) && !parsetest_int(tok1, 'u')) + { + uint16_t argument = atoi(tok1); + if (argument < 1 || argument > 256) + { + err_line(1, "Value must be >= 1 and <= 256."); + return 1; + } + world.exists = 0; + free_things(world.things); + free(world.map.cells); + world.map.cells = NULL; /* Since remake_map() runs free() on this. */ + world.map.length = argument; + return 1; + } + return 0; +} + + + +extern uint8_t parse_god_command_1arg(char * tok0, char * tok1) +{ + if ( parse_thingtype_manipulation(tok0, tok1) + || parse_thingaction_manipulation(tok0, tok1) + || parse_thing_manipulation(tok0, tok1) + || set_map_length(tok0,tok1) + || parse_val(tok0,tok1,s[S_CMD_SEED_RAND],'U', (char *)&world.seed) + || parse_val(tok0,tok1,s[S_CMD_TURN],'u',(char *)&world.turn) + || parse_val(tok0,tok1,s[S_CMD_PLAYTYPE],'8',(char *)&world.player_type) + || parse_world_active(tok0, tok1)); + else if (parse_val(tok0,tok1,s[S_CMD_SEED_MAP],'U',(char *)&world.seed_map)) + + { + remake_map(); + } + else if (parse_val(tok0, tok1, s[S_CMD_MAKE_WORLD],'U',(char *)&world.seed)) + { + uint8_t test = remake_world(); + err_line(1 == test, "No player type with start number of >0 defined."); + err_line(2 == test, "No thing action with name 'wait' defined."); + } + else + { + return 0; + } + return 1; +} diff --git a/src/server/god_commands.h b/src/server/god_commands.h new file mode 100644 index 0000000..87af27e --- /dev/null +++ b/src/server/god_commands.h @@ -0,0 +1,19 @@ +/* src/server/god_commands.h + * + * God commands and their interpretation by the server. + */ + + + +#ifndef GOD_COMMANDS_H +#define GOD_COMMANDS_H + +#include /* uint8_t */ + + +/* Parse/apply god command "tok0" with argument "tok1". */ +extern uint8_t parse_god_command_1arg(char * tok1, char * tok2); + + + +#endif diff --git a/src/server/hardcoded_strings.c b/src/server/hardcoded_strings.c index e26264a..4094478 100644 --- a/src/server/hardcoded_strings.c +++ b/src/server/hardcoded_strings.c @@ -4,7 +4,7 @@ -char * s[26]; +char * s[38]; @@ -17,23 +17,35 @@ extern void init_strings() s[S_PATH_RECORD] = "record"; s[S_PATH_SAVE] = "savefile"; s[S_CMD_MAKE_WORLD] = "MAKE_WORLD"; - s[S_CMD_DO_FOV] = "BUILD_FOVS"; + s[S_CMD_WORLD_ACTIVE] = "WORLD_ACTIVE"; s[S_CMD_SEED_MAP] = "SEED_MAP"; s[S_CMD_SEED_RAND] = "SEED_RANDOMNESS"; s[S_CMD_TURN] = "TURN"; s[S_CMD_THING] = "THING"; - s[S_CMD_TYPE] = "TYPE"; - s[S_CMD_POS_Y] = "POS_Y"; - s[S_CMD_POS_X] = "POS_X"; - s[S_CMD_COMMAND] = "COMMAND"; - s[S_CMD_ARGUMENT] = "ARGUMENT"; - s[S_CMD_PROGRESS] = "PROGRESS"; - s[S_CMD_LIFEPOINTS] = "LIFEPOINTS"; - s[S_CMD_CARRIES] = "CARRIES"; + s[S_CMD_T_TYPE] = "T_TYPE"; + s[S_CMD_T_POSY] = "T_POSY"; + s[S_CMD_T_POSX] = "T_POSX"; + s[S_CMD_T_COMMAND] = "T_COMMAND"; + s[S_CMD_T_ARGUMENT] = "T_ARGUMENT"; + s[S_CMD_T_PROGRESS] = "T_PROGRESS"; + s[S_CMD_T_HP] = "T_LIFEPOINTS"; + s[S_CMD_T_CARRIES] = "T_CARRIES"; s[S_CMD_WAIT] = "wait"; s[S_CMD_MOVE] = "move"; s[S_CMD_PICKUP] = "pick_up"; s[S_CMD_DROP] = "drop"; s[S_CMD_USE] = "use"; s[S_FCN_SPRINTF] = "sprintf()"; + s[S_CMD_THINGTYPE] = "THING_TYPE"; + s[S_CMD_TT_CONSUM] = "TT_CONSUMABLE"; + s[S_CMD_TT_STARTN] = "TT_START_NUMBER"; + s[S_CMD_TT_HP] = "TT_LIFEPOINTS"; + s[S_CMD_TT_SYMB] = "TT_SYMBOL"; + s[S_CMD_TT_NAME] = "TT_NAME"; + s[S_CMD_TT_CORPS] = "TT_CORPSE_ID"; + s[S_CMD_THINGACTION] = "THING_ACTION"; + s[S_CMD_TA_EFFORT] = "TA_EFFORT"; + s[S_CMD_TA_NAME] = "TA_NAME"; + s[S_CMD_MAPLENGTH] = "MAP_LENGTH"; + s[S_CMD_PLAYTYPE] = "PLAYER_TYPE"; } diff --git a/src/server/hardcoded_strings.h b/src/server/hardcoded_strings.h index db4be5a..fd9ceb8 100644 --- a/src/server/hardcoded_strings.h +++ b/src/server/hardcoded_strings.h @@ -17,30 +17,42 @@ enum string_num S_PATH_RECORD, S_PATH_SAVE, S_CMD_MAKE_WORLD, - S_CMD_DO_FOV, + S_CMD_WORLD_ACTIVE, S_CMD_SEED_MAP, S_CMD_SEED_RAND, S_CMD_TURN, S_CMD_THING, - S_CMD_TYPE, - S_CMD_POS_Y, - S_CMD_POS_X, - S_CMD_COMMAND, - S_CMD_ARGUMENT, - S_CMD_PROGRESS, - S_CMD_LIFEPOINTS, - S_CMD_CARRIES, + S_CMD_T_TYPE, + S_CMD_T_POSY, + S_CMD_T_POSX, + S_CMD_T_COMMAND, + S_CMD_T_ARGUMENT, + S_CMD_T_PROGRESS, + S_CMD_T_HP, + S_CMD_T_CARRIES, S_CMD_WAIT, S_CMD_MOVE, S_CMD_PICKUP, S_CMD_DROP, S_CMD_USE, - S_FCN_SPRINTF + S_FCN_SPRINTF, + S_CMD_THINGTYPE, + S_CMD_TT_CONSUM, + S_CMD_TT_STARTN, + S_CMD_TT_HP, + S_CMD_TT_SYMB, + S_CMD_TT_NAME, + S_CMD_TT_CORPS, + S_CMD_THINGACTION, + S_CMD_TA_EFFORT, + S_CMD_TA_NAME, + S_CMD_MAPLENGTH, + S_CMD_PLAYTYPE }; extern void init_strings(); -extern char * s[26]; +extern char * s[38]; diff --git a/src/server/init.c b/src/server/init.c index dd6ee15..16ab4df 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -11,7 +11,8 @@ #include /* mkdir() */ #include /* defines pid_t, time_t */ #include /* time() */ -#include /* optarg, getopt(), access(), unlink(), getpid() */ +#include /* optarg, getopt(), access(), getpid() */ +#include "../common/parse_file.h" /* err_line_zero(), err_line_inc() */ #include "../common/readwrite.h" /* try_fopen(), try_fclose(), textfile_width(), * try_fgets(), try_fwrite(), * detect_atomic_leftover() @@ -23,7 +24,7 @@ #include "hardcoded_strings.h" /* s */ #include "map.h" /* remake_map() */ #include "things.h" /* Thing, ThingType, free_things(), add_things(), - * get_player() + * get_thing_id_action_id_by_name() */ #include "run.h" /* obey_msg(), io_loop() */ #include "world.h" /* global world */ @@ -31,11 +32,50 @@ +/* Pass to obey_msg() lines from file at "path", on "record" write to same. Do + * not pass lines that consist only of a newline character. Transform newline + * in the line passed to \0. + */ +static void obey_lines_from_file(char * path, uint8_t record); + /* Replay game from record file up to the turn named in world.replay, then turn * over to manual replay via io_loop(). */ static void replay_game(); +/* Return 1 if the type defined by world.player_type has a .start_n of 0. + * Return 2 if no thing action with .name of s[S_CMD_WAIT] is defined. + * Else, return 0. + */ +static uint8_t world_cannot_be_made(); + + +static void obey_lines_from_file(char * path, uint8_t record) +{ + char * f_name = "obey_lines_from_file()"; + FILE * file = try_fopen(path, "r", f_name); + uint32_t linemax = textfile_width(file); + char * line = try_malloc(linemax + 1, f_name); + while (NULL != try_fgets(line, linemax + 1, file, f_name)) + { + if (strlen(line)) + { + if (strcmp("\n", line)) + { + char * nl = strchr(line, '\n'); + if (nl) + { + *nl = '\0'; + } + obey_msg(line, record, 1); + } + err_line_inc(); + } + } + free(line); + try_fclose(file, f_name); +} + static void replay_game() @@ -48,7 +88,8 @@ static void replay_game() while ( world.turn < world.replay && NULL != try_fgets(line, linemax + 1, file, f_name)) { - obey_msg(line, 0); + obey_msg(line, 0, 1); + err_line_inc(); } uint8_t end = 0; while (!io_loop()) @@ -58,7 +99,8 @@ static void replay_game() end = (NULL == try_fgets(line, linemax + 1, file, f_name)); if (!end) { - obey_msg(line, 0); + obey_msg(line, 0, 1); + err_line_inc(); } } } @@ -68,6 +110,31 @@ static void replay_game() +static uint8_t world_cannot_be_made() +{ + uint8_t player_will_be_generated = 0; + struct ThingType * tt; + for (tt = world.thing_types; NULL != tt; tt = tt->next) + { + if (world.player_type == tt->id) + { + player_will_be_generated = 0 < tt->start_n; + break; + } + } + if (!player_will_be_generated) + { + return 1; + } + if (!get_thing_action_id_by_name(s[S_CMD_WAIT])) + { + return 2; + } + return 0; +} + + + extern void obey_argv(int argc, char * argv[]) { int opt; @@ -120,14 +187,17 @@ extern void setup_server_io() -extern void remake_world() +extern uint8_t remake_world() { - char * f_name = "remake_world()"; + uint8_t test = world_cannot_be_made(); + if (test) + { + return test; + } free(world.log); world.log = NULL; /* thing_actions.c's update_log() checks for this. */ world.seed_map = world.seed; free_things(world.things); - world.do_update = 1; remake_map(); struct ThingType * tt; for (tt = world.thing_types; NULL != tt; tt = tt->next) @@ -145,17 +215,15 @@ extern void remake_world() add_things(tt->id, tt->start_n); } } - set_cleanup_flag(CLEANUP_THINGS); struct Thing * t; for (t = world.things; NULL != t; t = t->next) { t->fov_map = t->lifepoints ? build_fov_map(t) : NULL; } - if (!world.replay && !access(s[S_PATH_RECORD], F_OK)) - { - exit_trouble(unlink(s[S_PATH_RECORD]), f_name, "unlink()"); - } world.turn = 1; + world.do_update = 1; + world.exists = 1; + return 0; } @@ -165,35 +233,29 @@ extern void run_game() char * f_name = "run_game()"; detect_atomic_leftover(s[S_PATH_SAVE]); detect_atomic_leftover(s[S_PATH_RECORD]); + err_line_zero(); if (world.replay) { replay_game(); return; } - char * path_savefile = s[S_PATH_SAVE]; - if (!access(path_savefile, F_OK)) + if (!access(s[S_PATH_SAVE], F_OK)) { - FILE * file = try_fopen(path_savefile, "r", f_name); - uint32_t linemax = textfile_width(file); - char * line = try_malloc(linemax + 1, f_name); - while (NULL != try_fgets(line, linemax + 1, file, f_name)) - { - if (strlen(line) && strcmp("\n", line)) - { - obey_msg(line, 0); - } - } - free(line); - try_fclose(file, f_name); + obey_lines_from_file(s[S_PATH_SAVE], 0); } else { + char * err = "No world config file from which to start a new world."; + exit_err(access(s[S_PATH_CONFIG], F_OK), err); + obey_lines_from_file(s[S_PATH_CONFIG], 1); + err_line_zero(); char * command = s[S_CMD_MAKE_WORLD]; char * msg = try_malloc(strlen(command) + 1 + 11 + 1, f_name); int test = sprintf(msg, "%s %d", command, (int) time(NULL)); exit_trouble(test < 0, f_name, s[S_FCN_SPRINTF]); - obey_msg(msg, 1); + obey_msg(msg, 1, 1); free(msg); } + err_line_zero(); io_loop(); } diff --git a/src/server/init.h b/src/server/init.h index cadacf3..e18b3bd 100644 --- a/src/server/init.h +++ b/src/server/init.h @@ -6,7 +6,7 @@ #ifndef INIT_H #define INIT_H -#include /* uint32_t */ +#include /* uint8_t */ @@ -17,22 +17,23 @@ extern void obey_argv(int argc, char * argv[]); extern void setup_server_io(); /* Dissolves old game world if it exists, generates a new one from world.seed. - * Unlinks any pre-existing record file. - * - * Thing (action) definitions read in from server config directory are not - * affected. The map is populated accordingly. world.turn is set to 1, as is - * world.do_update, so that io_round() is told to update the worldstate file. + * The map is populated according to world.thing_types start numbers. world.turn + * is set to 1, as is .exists and .do_update, so that io_round() is told to + * update the worldstate file. Returns 0 on success, and if the world cannot be + * generated 1 since there is no player type or it has .n_start of 0, 2 if no + * "wait" thing action is defined. */ -extern void remake_world(); +extern uint8_t remake_world(); /* Create a game world state, then enter play or replay mode. * * If replay mode is called for, try for the record file and follow its commands + up to the turn specified by the user, then enter manual replay. Otherwise, * start into play mode after having either recreated a game world state from - * the savefile, or, if none exists, having created a new world with the - * MAKE_WORLD command. Manual replay as well as play mode take place inside - * io_loop(). + * the savefile, or, if none exists, having created a new world with first + * following the commands from the world config file, then running the + * MAKE_WORLD command. Manual replay as well as manual play mode take place + * inside io_loop(). */ extern void run_game(); diff --git a/src/server/io.c b/src/server/io.c index 00afb3c..317c088 100644 --- a/src/server/io.c +++ b/src/server/io.c @@ -6,9 +6,9 @@ #include /* PIPE_BUF */ #include /* size_t, NULL */ #include /* uint8_t, uint16_t, uint32_t */ -#include /* defines EOF, FILE, sprintf() */ +#include /* defines EOF, FILE, sprintf(), fprintf() */ #include /* free() */ -#include /* strlen(), memcpy(), memset() */ +#include /* strlen(), memcpy(), memset(), strchr() */ #include /* time_t */ #include /* time(), nanosleep() */ #include "../common/readwrite.h" /* atomic_write_start(), atomic_write_finish(), @@ -20,13 +20,16 @@ #include "field_of_view.h" /* VISIBLE */ #include "hardcoded_strings.h" /* s */ #include "map.h" /* yx_to_map_pos() */ -#include "things.h" /* Thing, ThingType, get_thing_type(), get_player() */ +#include "things.h" /* Thing, ThingType, ThingAction, get_thing_type(), + * get_player() + */ #include "world.h" /* global world */ /* Write to "file" god commands (one per line) to recreate thing "t". */ static void write_key_value(FILE * file, char * key, uint32_t value); +static void write_key_string(FILE * file, char * key, char * string); /* Write to "file" \n-delimited line of "key" + space + "value" as string. */ static void write_thing(FILE * file, struct Thing * t); @@ -80,6 +83,26 @@ static void write_key_value(FILE * file, char * key, uint32_t value) +static void write_key_string(FILE * file, char * key, char * string) +{ + char * f_name = "write_key_string()"; + try_fwrite(key, strlen(key), 1, file, f_name); + try_fputc(' ', file, f_name); + uint8_t contains_space = NULL != strchr(string, ' '); + if (contains_space) + { + try_fputc('\'', file, f_name); + } + try_fwrite(string, strlen(string), 1, file, f_name); + if (contains_space) + { + try_fputc('\'', file, f_name); + } + try_fputc('\n', file, f_name); +} + + + static void write_thing(FILE * file, struct Thing * t) { char * f_name = "write_thing()"; @@ -89,16 +112,16 @@ static void write_thing(FILE * file, struct Thing * t) write_thing(file, o); } write_key_value(file, s[S_CMD_THING], t->id); - write_key_value(file, s[S_CMD_TYPE], t->type); - write_key_value(file, s[S_CMD_POS_Y], t->pos.y); - write_key_value(file, s[S_CMD_POS_X], t->pos.x); - write_key_value(file, s[S_CMD_COMMAND], t->command); - write_key_value(file, s[S_CMD_ARGUMENT], t->arg); - write_key_value(file, s[S_CMD_PROGRESS], t->progress); - write_key_value(file, s[S_CMD_LIFEPOINTS], t->lifepoints); + write_key_value(file, s[S_CMD_T_TYPE], t->type); + write_key_value(file, s[S_CMD_T_POSY], t->pos.y); + write_key_value(file, s[S_CMD_T_POSX], t->pos.x); + write_key_value(file, s[S_CMD_T_COMMAND], t->command); + write_key_value(file, s[S_CMD_T_ARGUMENT], t->arg); + write_key_value(file, s[S_CMD_T_PROGRESS], t->progress); + write_key_value(file, s[S_CMD_T_HP], t->lifepoints); for (o = t->owns; o; o = o->next) { - write_key_value(file, s[S_CMD_CARRIES], o->id); + write_key_value(file, s[S_CMD_T_CARRIES], o->id); } try_fputc('\n', file, f_name); } @@ -196,7 +219,7 @@ static void update_worldstate_file() } atomic_write_finish(file, s[S_PATH_WORLDSTATE], path_tmp); set_cleanup_flag(CLEANUP_WORLDSTATE); - char * dot = ".\n";; + char * dot = ".\n"; try_fwrite(dot, strlen(dot), 1, world.file_out, f_name); fflush(world.file_out); } @@ -329,7 +352,34 @@ extern void save_world() char * f_name = "save_world()"; char * path_tmp; FILE * file = atomic_write_start(s[S_PATH_SAVE], &path_tmp); - write_key_value(file, s[S_CMD_DO_FOV], 0); + write_key_value(file, s[S_CMD_MAPLENGTH], world.map.length); + write_key_value(file, s[S_CMD_PLAYTYPE], world.player_type); + try_fputc('\n', file, f_name); + struct ThingAction * ta; + for (ta = world.thing_actions; ta; ta = ta->next) + { + write_key_value(file, s[S_CMD_THINGACTION], ta->id); + write_key_value(file, s[S_CMD_TA_EFFORT], ta->effort); + write_key_string(file, s[S_CMD_TA_NAME], ta->name); + try_fputc('\n', file, f_name); + } + struct ThingType * tt; + for (tt = world.thing_types; tt; tt = tt->next) + { + write_key_value(file, s[S_CMD_THINGTYPE], tt->id); + write_key_value(file, s[S_CMD_TT_STARTN], tt->start_n); + write_key_value(file, s[S_CMD_TT_HP], tt->lifepoints); + int test = fprintf(file, "%s %c\n", s[S_CMD_TT_SYMB], tt->char_on_map); + exit_trouble(test < 0, f_name, "fprintf()"); + write_key_string(file, s[S_CMD_TT_NAME], tt->name); + write_key_value(file, s[S_CMD_TT_CONSUM], tt->consumable); + try_fputc('\n', file, f_name); + } + for (tt = world.thing_types; tt; tt = tt->next) + { + write_key_value(file, s[S_CMD_THINGTYPE], tt->id); + write_key_value(file, s[S_CMD_TT_CORPS], tt->corpse_id); + } try_fputc('\n', file, f_name); write_key_value(file, s[S_CMD_SEED_MAP], world.seed_map); write_key_value(file, s[S_CMD_SEED_RAND], world.seed); @@ -340,6 +390,6 @@ extern void save_world() { write_thing(file, t); } - write_key_value(file, s[S_CMD_DO_FOV], 1); + write_key_value(file, s[S_CMD_WORLD_ACTIVE], 1); atomic_write_finish(file, s[S_PATH_SAVE], path_tmp); } diff --git a/src/server/main.c b/src/server/main.c index 33e5391..c8ca499 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -4,7 +4,6 @@ #include /* exit() */ #include "../common/rexit.h" /* exit_err, set_cleanup_func() */ #include "cleanup.h" /* set_cleanup_flag(), cleanup() */ -#include "configfile.h" /* read_config_file() */ #include "hardcoded_strings.h" /* s */ #include "init.h" /* run_game(), obey_argv(), obey_argv(), setup_server_io() */ #include "world.h" /* struct World */ @@ -31,16 +30,14 @@ int main(int argc, char ** argv) if (world.replay) { test = printf("Replay mode. Auto-replaying up to turn %d.\n", - world.replay); + world.replay); exit_err(-1 == test, printf_err); } } + world.map.length = 64; /* Just a sane default value. */ - /* Init config file and server i/o files. */ - read_config_file(); + /* Init server i/o, Enter play or replay mode loops, then leave properly. */ setup_server_io(); - - /* Enter play or replay mode loops, then leave properly. */ run_game(); cleanup(); exit(EXIT_SUCCESS); diff --git a/src/server/run.c b/src/server/run.c index 394081c..8251d06 100644 --- a/src/server/run.c +++ b/src/server/run.c @@ -5,11 +5,11 @@ #include /* NULL */ #include /* uint8_t, uint16_t, uint32_t */ #include /* FILE, printf(), fflush() */ -#include /* free(), atoi() */ -#include /* strlen(), strcmp() strncmp(), strdup() */ +#include /* free() */ +#include /* strlen(), strcmp(), strncmp(), strdup() */ #include /* access() */ #include "../common/parse_file.h" /* set_err_line_options(), token_from_line(), - * err_line() + * err_line(), err_line_inc(), parse_val() */ #include "../common/readwrite.h" /* try_fopen(), try_fcose(), try_fwrite(), * try_fgets(), textfile_width(), try_fputc() @@ -17,55 +17,32 @@ #include "../common/rexit.h" /* exit_trouble(), exit_err() */ #include "../common/try_malloc.h" /* try_malloc() */ #include "ai.h" /* ai() */ -#include "cleanup.h" /* set_cleanup_flag(), unset_cleanup_flag() */ -#include "field_of_view.h" /* build_fov_map() */ +#include "cleanup.h" /* unset_cleanup_flag() */ +#include "god_commands.h" /* parse_god_command_1arg() */ #include "hardcoded_strings.h" /* s */ -#include "init.h" /* remake_world() */ #include "io.h" /* io_round(), save_world() */ -#include "map.h" /* remake_map() */ -#include "thing_actions.h" /* ThingAction */ -#include "things.h" /* Thing, get_thing(), own_thing(), add_thing(), - * get_thing_action_id_by_name(), get_player() - */ +#include "things.h" /* Thing, get_thing_action_id_by_name(), get_player() */ #include "world.h" /* world */ -/* Parse/apply god command in "tok0"/"tok1" on "t" owning another thing. */ -static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t); - -/* Parse/apply god commansd in "tok0"/"tok1" on positioning a thing "t". */ -static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t); - -/* Parse/apply god command in "tok0"/"tok1" oo setting "t"'s thing type. */ -static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t); - -/* Parse/apply god command in "tok0"/"tok1" on setting up thing "t". */ -static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t); - -/* Parse/apply god command on enabling/disabling generation of fields of view on - * god commands that may affect them, via static global "do_fov". On enabling, - * (re-)generate all animate things' fields of view. - */ -static uint8_t parse_do_fov(char * tok0, char * tok1); +/* If "string" and "comparand" match in string, set "c_to_set" to value." */ +static uint8_t set_char_by_string_comparison(char * string, char * comparand, + char * c_to_set, char value); -/* Parse/apply god command in "tok0"/"tok1" manipulating a thing's state. */ -static uint8_t parse_thing_manipulation(char * tok0, char * tok1); +/* Return 1 on world.exists, else 0 and err_line() appropriate error message. */ +static uint8_t player_commands_allowed(); /* Parse player command "tok0" with no argument to player action, comment on * invalidity of non-zero "tok1" (but do not abort in that case). */ static uint8_t parse_player_command_0arg(char * tok0, char * tok1); -/* If "string" and "comparand" match in string, set "c_to_set" to value." */ -static uint8_t set_char_by_string_comparison(char * string, char * comparand, - char * c_to_set, char value); - /* Parse player command "tok0" with one argument "tok1" to player action. */ static uint8_t parse_player_command_1arg(char * tok0, char * tok1); -/* Parse/apply commadn "tok0" with argument "tok1" and test the line for further - * tokens, commenting on their invalidity (but don't abort on findingthem). +/* Parse/apply command "tok0" with argument "tok1" and test the line for further + * tokens, commenting on their invalidity (but don't abort on finding them). */ static uint8_t parse_command_1arg(char * tok0, char * tok1); @@ -84,120 +61,13 @@ static void turn_over(); -/* Do god commands to create / position things generate their fields of view? */ -static uint8_t do_fov = 0; - - - -static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t) -{ - uint8_t id; - if (parse_val(tok0, tok1, s[S_CMD_CARRIES], '8', (char *) &id)) - { - if (!err_line(id == t->id, "Thing cannot carry itself.")) - { - struct Thing * o = get_thing(world.things, id, 0); - if (!err_line(!o, "Thing cannot carry thing that does not exist.")) - { - own_thing(&(t->owns), &world.things, id); - o->pos = t->pos; - } - } - return 1; - } - return 0; -} - - - -static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t) -{ - char axis = 0; - if (!strcmp(tok0, s[S_CMD_POS_Y])) - { - axis = 'y'; - } - else if (!strcmp(tok0, s[S_CMD_POS_X])) - { - axis = 'x'; - } - if (axis && !parsetest_int(tok1, '8')) - { - uint8_t length = atoi(tok1); - char * err = "Position is outside of map."; - if (!err_line(length >= world.map.length, err)) - { - if ('y' == axis) - { - t->pos.y = length; - } - else if ('x' == axis) - { - t->pos.x = length; - } - free(t->fov_map); - t->fov_map= do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map; - } - return 1; - } - return 0; -} - - - -static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t) -{ - uint8_t type; - if (parse_val(tok0, tok1, s[S_CMD_TYPE], '8', (char *) &type)) - { - struct ThingType * tt = world.thing_types; - for (; NULL != tt && type != tt->id; tt = tt->next); - if (!err_line(!tt, "Thing type does not exist.")) - { - t->type = type; - } - return 1; - } - return 0; -} - - -static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t) -{ - uint8_t command; - if (parse_val(tok0, tok1, s[S_CMD_COMMAND], '8', (char *) &command)) - { - if (!command) - { - t->command = command; - return 1; - } - struct ThingAction * ta = world.thing_actions; - for (; NULL != ta && command != ta->id; ta = ta->next); - if (!err_line(!ta, "Thing action does not exist.")) - { - t->command = command; - } - return 1; - } - return 0; -} - - - -static uint8_t parse_do_fov(char * tok0, char * tok1) +static uint8_t set_char_by_string_comparison(char * string, char * comparand, + char * c_to_set, char value) { - if (parse_val(tok0, tok1, s[S_CMD_DO_FOV], '8', (char *) &do_fov)) + if (!strcmp(string, comparand)) { - if (do_fov) - { - struct Thing * ti; - for (ti = world.things; ti; ti = ti->next) - { - ti->fov_map = ti->lifepoints ? build_fov_map(ti) : ti->fov_map; - } - } + * c_to_set = value; return 1; } return 0; @@ -205,30 +75,11 @@ static uint8_t parse_do_fov(char * tok0, char * tok1) -static uint8_t parse_thing_manipulation(char * tok0, char * tok1) +static uint8_t player_commands_allowed() { - uint8_t id; - static struct Thing * t = NULL; - if (t && ( parse_thing_type(tok0, tok1, t) - || parse_thing_command(tok0, tok1, t) - || parse_val(tok0, tok1, s[S_CMD_ARGUMENT], '8', (char *)&t->arg) - || parse_val(tok0,tok1,s[S_CMD_PROGRESS],'8',(char *)&t->progress) - || parse_val(tok0, tok1, s[S_CMD_LIFEPOINTS], '8', - (char *) &t->lifepoints) - || parse_position(tok0, tok1, t) - || parse_carry(tok0, tok1, t))); - else if (parse_val(tok0, tok1, s[S_CMD_THING], '8', (char *) &id)) - { - t = get_thing(world.things, id, 1); - if (!t) - { - t = add_thing(id, 0, 0); - set_cleanup_flag(CLEANUP_THINGS); - t->fov_map= do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map; - } - } - else + if (!world.exists) { + err_line(1, "No world exists in which to run player commands."); return 0; } return 1; @@ -241,23 +92,13 @@ static uint8_t parse_player_command_0arg(char * tok0, char * tok1) struct Thing * player = get_player(); if (!strcmp(tok0, s[S_CMD_WAIT]) || !strcmp(tok0, s[S_CMD_PICKUP])) { - player->command = get_thing_action_id_by_name(tok0); - player->arg = 0; - turn_over(); - err_line (NULL != tok1, "No arguments expected, ignoring arguments."); - return 1; - } - return 0; -} - - - -static uint8_t set_char_by_string_comparison(char * string, char * comparand, - char * c_to_set, char value) -{ - if (!strcmp(string, comparand)) - { - * c_to_set = value; + if (player_commands_allowed()) + { + player->command = get_thing_action_id_by_name(tok0); + player->arg = 0; + turn_over(); + err_line (NULL != tok1, "No arguments expected, ignoring them."); + } return 1; } return 0; @@ -268,14 +109,14 @@ static uint8_t set_char_by_string_comparison(char * string, char * comparand, static uint8_t parse_player_command_1arg(char * tok0, char * tok1) { struct Thing * player = get_player(); - if ( - parse_val(tok0, tok1, s[S_CMD_DROP], '8', (char *) &player->arg) - || parse_val(tok0, tok1, s[S_CMD_USE], '8', (char *) &player->arg)) + if ( ( parse_val(tok0, tok1, s[S_CMD_DROP], '8', (char *) &player->arg) + || parse_val(tok0, tok1, s[S_CMD_USE], '8', (char *) &player->arg)) + && player_commands_allowed()) { player->command = get_thing_action_id_by_name(tok0); turn_over(); } - else if (!strcmp(tok0, s[S_CMD_MOVE])) + else if (!strcmp(tok0, s[S_CMD_MOVE]) && player_commands_allowed()) { char dir = '\0'; if (!( set_char_by_string_comparison(tok1, "east", &dir, 'd') @@ -303,20 +144,8 @@ static uint8_t parse_player_command_1arg(char * tok0, char * tok1) static uint8_t parse_command_1arg(char * tok0, char * tok1) { char * tok2 = token_from_line(NULL); - if ( parse_thing_manipulation(tok0, tok1) - || parse_player_command_1arg(tok0, tok1) - || parse_val(tok0, tok1, s[S_CMD_SEED_RAND], 'U', (char *) &world.seed) - || parse_val(tok0, tok1, s[S_CMD_TURN], 'u', (char *) &world.turn) - || parse_do_fov(tok0, tok1)); - else if (parse_val(tok0,tok1,s[S_CMD_SEED_MAP],'U',(char *)&world.seed_map)) - - { - remake_map(); - } - else if (parse_val(tok0, tok1, s[S_CMD_MAKE_WORLD],'U',(char *)&world.seed)) - { - remake_world(); - } + if ( parse_player_command_1arg(tok0, tok1) + || parse_god_command_1arg(tok0, tok1)); else { return 0; @@ -373,11 +202,8 @@ static void turn_over() ai(thing); } thing->progress++; - struct ThingAction * ta = world.thing_actions; - while (ta->id != thing->command) - { - ta = ta->next; - } + + struct ThingAction * ta = get_thing_action(thing->command); if (thing->progress == ta->effort) { ta->func(thing); @@ -415,10 +241,20 @@ static void record_msg(char * msg) -extern void obey_msg(char * msg, uint8_t do_record) +extern void obey_msg(char * msg, uint8_t do_record, uint8_t do_verbose) { - set_err_line_options("Trouble with message: ", msg, 0, 0); + char * f_name = "obey_msg()"; + if (world.is_verbose && do_verbose) + { + exit_trouble(-1 == printf("Input: %s\n", msg), f_name, "printf()"); + } + set_err_line_options("Trouble with message: ", msg, 0); char * msg_copy = strdup(msg); + if (msg[0] == 'm') + { + int a = 5; + a = a; + } char * tok0 = token_from_line(msg_copy); if (NULL != tok0) { @@ -426,7 +262,10 @@ extern void obey_msg(char * msg, uint8_t do_record) if ( parse_player_command_0arg(tok0, tok1) || (tok1 && parse_command_1arg(tok0, tok1))) { - world.do_update = 1; + if (world.exists) + { + world.do_update = 1; + } if (do_record) { save_world(); @@ -436,7 +275,7 @@ extern void obey_msg(char * msg, uint8_t do_record) return; } } - err_line(1, "Unknown command or bad number of tokens."); + err_line(1, "Unknown command/argument or bad number of tokens."); free(msg_copy); } @@ -475,7 +314,7 @@ extern uint8_t io_loop() free(msg); return 0; } - obey_msg(msg, 1); + obey_msg(msg, 1, 0); free(msg); } } diff --git a/src/server/run.h b/src/server/run.h index 07ca902..400f079 100644 --- a/src/server/run.h +++ b/src/server/run.h @@ -11,9 +11,10 @@ /* Try parsing "msg" into a command to apply, and apply it. Record commands to - * the file at world.path_record if "do_record" is set. + * the file at world.path_record if "do_record" is set, and output them to + * stdout if "do_verbose" and world.is_verbose are set. */ -extern void obey_msg(char * msg, uint8_t do_record); +extern void obey_msg(char * msg, uint8_t do_record, uint8_t do_verbose); /* Loop for receiving commands via io_round() and acting on them. Exits with 1 * on "QUIT" command. In replay mode, exits with 0 on each non-"QUIT" command. diff --git a/src/server/thing_actions.c b/src/server/thing_actions.c index b153ff8..e91f576 100644 --- a/src/server/thing_actions.c +++ b/src/server/thing_actions.c @@ -5,13 +5,13 @@ #include /* uint8_t, uint16_t */ #include /* sprintf() */ #include /* free() */ -#include /* strlen(), strcmp(), memcpy(), strncmp() */ -#include "../common/rexit.h" /* exit_err(), exit_trouble() */ +#include /* strlen(), memcpy(), strncmp() */ +#include "../common/rexit.h" /* exit_trouble() */ #include "../common/try_malloc.h" /* try_malloc() */ #include "../common/yx_uint8.h" /* struct yx_uint8 */ #include "field_of_view.h" /* build_fov_map() */ #include "hardcoded_strings.h" /* s */ -#include "things.h" /* structs Thing, ThingType, get_player(), own_thing(), +#include "things.h" /* Thing, ThingType, get_player(), own_thing(), * set_thing_position(), get_thing_type() */ #include "map.h" /* is_passable() */ @@ -238,36 +238,6 @@ static void playerbonus_use(uint8_t no_thing, uint8_t wrong_thing) -extern void free_thing_actions(struct ThingAction * ta) -{ - if (NULL == ta) - { - return; - } - free(ta->name); - free_thing_actions(ta->next); - free(ta); -} - - - -extern uint8_t get_thing_action_id_by_name(char * name) -{ - struct ThingAction * ta = world.thing_actions; - while (NULL != ta) - { - if (0 == strcmp(ta->name, name)) - { - break; - } - ta = ta->next; - } - exit_err(NULL == ta, "get_thing_action_id_by_name() did not find action."); - return ta->id; -} - - - extern void actor_wait(struct Thing * t) { if (t == get_player()) diff --git a/src/server/thing_actions.h b/src/server/thing_actions.h index 861f5c2..28857e7 100644 --- a/src/server/thing_actions.h +++ b/src/server/thing_actions.h @@ -8,28 +8,10 @@ #ifndef THING_ACTIONS_H #define THING_ACTIONS_H -#include /* uint8_t */ struct Thing; -struct ThingAction -{ - uint8_t id; /* identifies action in Thing.command; therefore must be >0 */ - struct ThingAction * next; - void (* func) (struct Thing *); /* function called after .effort turns */ - char * name; /* human-readable identifier */ - uint8_t effort; /* how many turns the action takes */ -}; - - - -/* Free ThingAction * chain starting at "ta". */ -extern void free_thing_actions(struct ThingAction * ta); - -/* Return world.thing_actions ThingAction.id for "name". */ -extern uint8_t get_thing_action_id_by_name(char * name); - /* Actor "t" does nothing. */ extern void actor_wait(struct Thing * t); diff --git a/src/server/things.c b/src/server/things.c index 9425f9c..6df9b18 100644 --- a/src/server/things.c +++ b/src/server/things.c @@ -1,148 +1,274 @@ /* src/server/things.c */ +#define _POSIX_C_SOURCE 200809L /* strdup() */ #include "things.h" -#include /* NULL */ -#include /* uint8_t, uint16_t, UINT8_MAX, UINT16_MAX */ +#include /* NULL, size_t */ +#include /* uint8_t, uint16_t, int16_t, UINT8_MAX, UINT16_MAX */ #include /* free() */ -#include /* memset(), strlen() */ -#include "../common/rexit.h" /* exit_err(), exit_trouble() */ +#include /* memset(), strcmp(), strdup() */ +#include "../common/rexit.h" /* exit_err() */ #include "../common/try_malloc.h" /* try_malloc() */ #include "../common/yx_uint8.h" /* yx_uint8 */ +#include "cleanup.h" /* set_cleanup_flag() */ #include "hardcoded_strings.h" /* s */ #include "map.h" /* is_passable() */ #include "rrand.h" /* rrand() */ -#include "world.h" /* global world */ +#include "thing_actions.h" /* actor_wait */ +#include "world.h" /* world */ #include "yx_uint8.h" /* yx_uint8_cmp() */ +/* Used to treat structs Thing, ThingType and ThingAction the same. */ +struct NextAndId +{ + struct NextAndId * next; + uint8_t id; +}; + + -/* Return lowest unused id for new thing. */ -static uint8_t get_lowest_unused_id(); +/* Return lowest unused id for new thing ("sel"==0), thing type ("sel"==1) or + * thing action ("sel"==2). + */ +static uint8_t get_unused_id(uint8_t sel); +/* To linked list of NextAndId structs (or rather structs whose start region is + * compatible to it) starting at "start", add newly allocated element of + * "n_size" and an ID that is either "id" or, if "id" is <= UINT8_MAX and >= + * "id_start", get ID from get_unused_id("struct_id"). + */ +static struct NextAndId * add_to_struct_list(size_t n_size, uint8_t start_id, + int16_t id, uint8_t struct_id, + struct NextAndId ** start); -static uint8_t get_lowest_unused_id() + +static uint8_t get_unused_id(uint8_t sel) { uint8_t i = 0; while (1) { - if (!get_thing(world.things, i, 1)) + if ( (0 == sel && !get_thing(world.things, i, 1)) + || (1 == sel && !get_thing_type(i)) + || (2 == sel && !get_thing_action(i))) { return i; } - exit_err(i == UINT8_MAX, "No unused ID available to add new thing."); + exit_err(i == UINT8_MAX, "No unused ID available to add to ID list."); i++; } } -extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep) +static struct NextAndId * add_to_struct_list(size_t n_size, uint8_t start_id, + int16_t id, uint8_t struct_id, + struct NextAndId ** start) { - while (1) + char * f_name = "add_to_struct_list()"; + struct NextAndId * nai = try_malloc(n_size, f_name); + memset(nai, 0, n_size); + nai->id = (start_id<=id && id<=UINT8_MAX) ? id : get_unused_id(struct_id); + struct NextAndId ** nai_ptr_ptr = start; + for (; NULL != * nai_ptr_ptr; nai_ptr_ptr = &(*nai_ptr_ptr)->next); + *nai_ptr_ptr = nai; + return nai; +} + + + +extern struct ThingAction * add_thing_action(int16_t id) +{ + struct ThingAction * ta; + ta = (struct ThingAction *) add_to_struct_list(sizeof(struct ThingAction), + 1, id, 2, + (struct NextAndId **) + &world.thing_actions); + set_cleanup_flag(CLEANUP_THING_ACTIONS); + ta->name = strdup(s[S_CMD_WAIT]); + ta->effort = 1; + ta->func = actor_wait; + return ta; +} + + + +extern struct ThingType * add_thing_type(int16_t id) +{ + struct ThingType * tt; + tt = (struct ThingType *) add_to_struct_list(sizeof(struct ThingType), + 0, id, 1, + (struct NextAndId **) + &world.thing_types); + set_cleanup_flag(CLEANUP_THING_TYPES); + tt->name = strdup("(none)"); + return tt; +} + + + +extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t y, uint8_t x) +{ + struct Thing * t; + t = (struct Thing *) add_to_struct_list(sizeof(struct Thing), 0, id, 0, + (struct NextAndId **) &world.things); + struct ThingType * tt = get_thing_type(type); + set_cleanup_flag(CLEANUP_THINGS); + t->type = tt->id; + t->lifepoints = tt->lifepoints; + t->pos.y = y; + t->pos.x = x; + return t; +} + + + +extern void free_thing_actions(struct ThingAction * ta) +{ + if (NULL == ta) { - if (NULL == ptr || id == ptr->id) - { - return ptr; - } - if (deep) - { - struct Thing * owned_thing = get_thing(ptr->owns, id, 1); - if (NULL != owned_thing) - { - return ptr; - } - } - ptr = ptr->next; + return; } + free_thing_actions(ta->next); + free(ta->name); + free(ta); } -extern void free_thing_types(struct ThingType * tt_start) +extern void free_thing_types(struct ThingType * tt) { - if (NULL == tt_start) + if (NULL == tt) { return; } - free_thing_types(tt_start->next); - free(tt_start->name); - free(tt_start); + free_thing_types(tt->next); + free(tt->name); + free(tt); } -extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t find_pos) +extern void free_things(struct Thing * t) { - char * f_name = "add_thing()"; - struct ThingType * tt = get_thing_type(type); - struct Thing * t = try_malloc(sizeof(struct Thing), f_name); - memset(t, 0, sizeof(struct Thing)); - t->id = (0 <= id && id <= UINT8_MAX) ? id : get_lowest_unused_id(); - t->type = tt->id; - t->lifepoints = tt->lifepoints; - char * err = "Space to put thing on too hard to find. Map too small?"; - uint16_t i = 0; - memset(&(t->pos), 0, sizeof(struct yx_uint8)); - while (find_pos) + if (NULL == t) { - struct yx_uint8 pos; - for (pos.y = pos.x = 0; 0 == is_passable(pos); i++) + return; + } + free_things(t->owns); + free_things(t->next); + free(t->fov_map); + free(t); + if (t == world.things) /* So add_things()' NULL-delimited thing */ + { /* iteration loop does not iterate over */ + world.things = NULL; /* freed memory when called the first time */ + } /* after world re-seeding. */ +} + + + +extern struct ThingAction * get_thing_action(uint8_t id) +{ + struct ThingAction * ta = world.thing_actions; + for (; NULL != ta && id != ta->id; ta = ta->next); + return ta; +} + + + +extern struct ThingType * get_thing_type(uint8_t id) +{ + struct ThingType * tt = world.thing_types; + for (; NULL != tt && id != tt->id; tt = tt->next); + return tt; +} + + + +extern uint8_t get_thing_action_id_by_name(char * name) +{ + struct ThingAction * ta = world.thing_actions; + while (NULL != ta) + { + if (0 == strcmp(ta->name, name)) { - exit_err(UINT16_MAX == i, err); - pos.y = rrand() % world.map.length; - pos.x = rrand() % world.map.length; + break; } - struct Thing * t_ptr; - uint8_t clear = 1; - for (t_ptr = world.things; t_ptr != NULL; t_ptr = t_ptr->next) + ta = ta->next; + } + if (!ta) + { + return 0; + } + return ta->id; +} + + + +extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep) +{ + while (1) + { + if (NULL == ptr || id == ptr->id) { - if (yx_uint8_cmp(&pos, &t_ptr->pos) && 0 != t_ptr->lifepoints) - { - clear = 0; - break; - } + return ptr; } - if (1 == clear) + if (deep) { - t->pos = pos; - break; + struct Thing * owned_thing = get_thing(ptr->owns, id, 1); + if (NULL != owned_thing) + { + return ptr; + } } + ptr = ptr->next; } - struct Thing ** t_ptr_ptr = &world.things; - for (; NULL != * t_ptr_ptr; t_ptr_ptr = &(*t_ptr_ptr)->next); - * t_ptr_ptr = t; - return t; } -extern void add_things(uint8_t type, uint8_t n) +extern struct Thing * get_player() { - uint8_t i; - for (i = 0; i < n; i++) - { - add_thing(-1, type, 1); - } + return get_thing(world.things, 0, 0); } -extern void free_things(struct Thing * t_start) +extern void add_things(uint8_t type, uint8_t n) { - if (NULL == t_start) + uint8_t i; + for (i = 0; i < n; i++) { - return; + struct yx_uint8 pos; + while (1) + { + char * err = "Space to put thing on too hard to find." + "Map too small?"; + uint16_t i_pos = 0; + for (pos.y = pos.x = 0; 0 == is_passable(pos); i_pos++) + { + exit_err(UINT16_MAX == i_pos, err); + pos.y = rrand() % world.map.length; + pos.x = rrand() % world.map.length; + } + struct Thing * t; + uint8_t clear = 1; + for (t = world.things; t; t = t->next) + { + if (yx_uint8_cmp(&pos, &t->pos) && 0 != t->lifepoints) + { + clear = 0; + break; + } + } + if (1 == clear) + { + break; + } + } + add_thing(-1, type, pos.y, pos.x); } - free_things(t_start->owns); - free_things(t_start->next); - free(t_start->fov_map); - free(t_start); - if (t_start == world.things) /* So add_things()' NULL-delimited thing */ - { /* iteration loop does not iterate over */ - world.things = NULL; /* freed memory when called the first time */ - } /* after world re-seeding. */ } @@ -178,29 +304,6 @@ extern void own_thing(struct Thing ** target, struct Thing ** source, -extern struct Thing * get_player() -{ - return get_thing(world.things, 0, 1); -} - - - -extern struct ThingType * get_thing_type(uint8_t id) -{ - char * f_name = "get_thing_type()"; - struct ThingType * tt = world.thing_types; - for (; NULL != tt && id != tt->id; tt = tt->next); - char * err_intro = "Requested thing type of unused ID "; - uint16_t size = strlen(err_intro) + 3 + 1 + 1; - char * err = try_malloc(size, f_name); - exit_trouble(sprintf(err,"%s%d.",err_intro,id) < 0,f_name,s[S_FCN_SPRINTF]); - exit_err(NULL == tt, err); - free(err); - return tt; -} - - - extern void set_thing_position(struct Thing * t, struct yx_uint8 pos) { t->pos = pos; diff --git a/src/server/things.h b/src/server/things.h index 889374a..6f7d124 100644 --- a/src/server/things.h +++ b/src/server/things.h @@ -1,24 +1,24 @@ /* src/server/things.h * - * Structs for things and their type definitions, and routines to initialize - * these and load and save them from/to files. + * Structs for things and their type and action definitions, and routines to + * initialize these. */ #ifndef THINGS_H #define THINGS_H -#include /* uint8_t */ +#include /* uint8_t, int16_t */ #include "../common/yx_uint8.h" /* yx_uint8 structs */ struct Thing { - struct Thing * next; /* pointer to next one in things chain */ + struct Thing * next; + uint8_t id; /* individual thing's unique identifier */ struct Thing * owns; /* chain of things owned / in inventory */ struct yx_uint8 pos; /* coordinate on map */ uint8_t * fov_map; /* map of the thing's field of view */ - uint8_t id; /* individual thing's unique identifier */ uint8_t type; /* ID of appropriate thing definition */ uint8_t lifepoints; /* 0: thing is inanimate; >0: hitpoints */ uint8_t command; /* thing's current action; 0 if none */ @@ -28,8 +28,8 @@ struct Thing struct ThingType { - uint8_t id; /* thing type identifier / sets .type */ struct ThingType * next; + uint8_t id; /* thing type identifier / sets .type */ char char_on_map; /* thing symbol to appear on map */ char * name; /* string to describe thing in game log */ uint8_t corpse_id; /* type to change thing into upon destruction */ @@ -38,37 +38,65 @@ struct ThingType uint8_t start_n; /* how many of these does the map start with? */ }; +struct ThingAction +{ + struct ThingAction * next; + uint8_t id; /* identifies action in Thing.command; therefore must be >0 */ + void (* func) (struct Thing *); /* function called after .effort turns */ + char * name; /* human-readable identifier */ + uint8_t effort; /* how many turns the action takes */ +}; -/* Return thing of "id" in chain at "ptr", search inventories too if "deep". */ -extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep); -/* Free thing types chain starting at "tt_start". */ -extern void free_thing_types(struct ThingType * tt_start); +/* Add thing action of "id" to world.thing_actions, with .name defaulting to + * s[S_CMD_WAIT], .func to actor_wait() and .effort to 1. If "id" is not >= 1 + * and <= UINT8_MAX, use lowest unused id. Return thing action. + */ +extern struct ThingAction * add_thing_action(int16_t id); -/* Add thing of "id" and "type" to map on random passable position (positions - * which contain an actor are not deemed passable) if "find_pos", else on y=0, - * x=0. If "id" is >= 0 and <= UINT8_MAX, use lowest unused id. Return thing. +/* Add thing type of "id", with .corpse_id defaulting to "id" to + * world.thing_types, .name to "(none)" and the remaining values to 0. If "id" + * is not >= 0 and <= UINT8_MAX, use lowest unused id. Return thing type. */ -extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t find_pos); +extern struct ThingType * add_thing_type(int16_t id); -/* Add thing(s) ("n": how many?) of "type" to map on random position(s). New - * animate things are never placed in the same square with other animate ones. +/* Add thing of "id" and "type" on position of "y"/x" to world.things.If "id" is + * not >= 0 and <= UINT8_MAX, use lowest unused id. Return thing. */ -extern void add_things(uint8_t type, uint8_t n); +extern struct Thing * add_thing(int16_t id, uint8_t type, uint8_t y, uint8_t x); -/* Free things in things chain starting at "t_start. */ -extern void free_things(struct Thing * t_start); +/* Free ThingAction/ThingType/Thing * chain starting at "ta"/"tt"/"t". */ +extern void free_thing_actions(struct ThingAction * ta); +extern void free_thing_types(struct ThingType * tt); +extern void free_things(struct Thing * t); -/* Move thing of "id" from "source" inventory to "target" inventory. */ -extern void own_thing(struct Thing ** target, struct Thing ** source, - uint8_t id); +/* Return pointer to ThingAction/ThingType of "id", or NULL if none found. */ +extern struct ThingAction * get_thing_action(uint8_t id); +extern struct ThingType * get_thing_type(uint8_t id); -/* Get pointer to the Thing struct that represents the player. */ +/* Return world.thing_actions ThingAction.id for "name" or 0 if none found. */ +extern uint8_t get_thing_action_id_by_name(char * name); + +/* Return thing of "id" in chain at "ptr", search inventories too if "deep". + * Return NULL if nothing found. + */ +extern struct Thing * get_thing(struct Thing * ptr, uint8_t id, uint8_t deep); + +/* Get pointer to the non-owend Thing struct that represents the player, or NULL + * if none found. + */ extern struct Thing * get_player(); -/* Get pointer to the thing type of identifier "def_id". */ -extern struct ThingType * get_thing_type(uint8_t id); +/* Add thing(s) ("n": how many?) of "type" to map on random passable + * position(s). New animate things are never placed in the same square with + * other animate ones. + */ +extern void add_things(uint8_t type, uint8_t n); + +/* Move thing of "id" from "source" inventory to "target" inventory. */ +extern void own_thing(struct Thing ** target, struct Thing ** source, + uint8_t id); /* Move not only "t" to "pos", but also all things owned by it. */ extern void set_thing_position(struct Thing * t, struct yx_uint8 pos); diff --git a/src/server/world.h b/src/server/world.h index 22af12c..6e941e6 100644 --- a/src/server/world.h +++ b/src/server/world.h @@ -31,7 +31,8 @@ struct World uint32_t seed_map; /* Map seed. */ uint16_t replay; /* Turn up to which to replay game. No replay if zero. */ uint16_t turn; /* Current game turn. */ - uint16_t do_update; /* Update worldstate file if !0. */ + uint8_t do_update; /* Update worldstate file if !0. */ + uint8_t exists; /* If !0, remake_world() has been run successfully. */ uint8_t player_type; /* Thing type that player will start as. */ uint8_t is_verbose; /* Should server send debugging info to stdout? */ };