From: Christian Heller Date: Sun, 30 Mar 2014 02:40:58 +0000 (+0200) Subject: Make server config files more readable, their parsing more lenient. X-Git-Tag: tce~792 X-Git-Url: https://plomlompom.com/repos/%7B%7Byoutube_prefix%7D%7D%7B%7Bresult.id%7D%7D?a=commitdiff_plain;h=cc4ed0c49279f08a053a3e3a9a4acba22283a01f;p=plomrogue Make server config files more readable, their parsing more lenient. Completely re-wrote server's config file reading system for this. Just a first step; client's config file system is to follow. Also stumbled upon multiple issues in the meantime, added them to the TODO. --- diff --git a/README b/README index 5f1be55..17a5846 100644 --- a/README +++ b/README @@ -79,23 +79,52 @@ option (like "-s100") to start the recording at the respective turn number. Hacking / server internals and configuration -------------------------------------------- -The movements/actions available to the player and the enemies are defined and -can be changed in ./confserver/map_object_actions. Each entry consists of a -first line of a numerical ID used internally to uniquely identify and manage the -action, a second line of the number of turns the action takes, and a third line -of a string that maps the action to the game logic to do when it is called. -Finally, a delimiter line of "%%" ends the entry. - -The different map object types, i.e. species (including the player's human one) -and item types, can be edited in ./confserver/map_objects. Here the first value -is a numerical ID that represents the object type, the second one describes what -type this object decomposes to when it gets destroyed/killed, the third value is -the ASCII character used to represent the object visually on the map, the fourth -value is the number of hitpoints the object starts with (items are dead and -start with zero hitpoints, anything else moves), the fifth is the string that -names the object in the game log, the sixth defines if the object is consumable -(it is if it is non-zero) and how many hitpoints are gained if it is. Finally, -the same delimiter as for the map object action definitions file follows. +The ./confserver/world file defines the map object types and the actions +available to them. Each object type and action is defined by a multi-line block +wherein each line sets one attribute of the object type or action. + +Here's a typical action definition block: + +ACTION 1 +NAME move +EFFORT 5 + +A line of "ACTION" followed by a number starts an action definition block and +sets the action's id for internal use to 1. The number after "EFFORT" determines +how many turns this action takes for the actor performing it. The string after +"NAME" names the action. Furthermore, if it is one of "move", "pick_up", "drop" +or "use", it matches internal functions described by these strings to this +action. All other names (including "wait") currently are matched to a do-nothing +wait function. + +Here's a typical map object type definition block: + +OBJECT 2 +NAME ZOMBIE +SYMBOL z +LIFEPOINTS 3 +CORPSE_ID 5 +CONSUMABLE 0 + +A line of "OBJECT" followed by a number starts it, and the number sets the +object type's internal id. The number after "CONSUMABLE" defines the object +as consumable (and to so many hitpoints gain). The character after "SYMBOL" is +the one shown on the map to represent to object type. "LIFEPOINTS" is the start +hitpoints value for this object type and defines it as animate if it is +non-zero. The string after "NAME" sets the object type's name. "CORPSE_ID" sets +the id of the object type that objects of this type degrade to if their +hitpoints drop to zero if they start out as inanimate (what is not implemented +yet: or if they are inanimate, but are otherwise crushed). Note that the +"CORPSE_ID" must match the id of an object type defined in the file (before or +after, it may even be the same). + +All this definition block members must be present within a block, but only +"ACTION" / "OBJECT" must be positioned at the respective blocks' first line, +the others may appear in whatever order and even multiple times. If a block is +finished, however, it cannot be re-defined by starting a new block wit the same +object type or action id. Tokens in this config file are separated by +whitespace. Single quotes can be put around string values that are to include +whitespace by themslves. All source files are thoroughly documented to explain more details of plomrogue's internals. The ./roguelike-server executable can be run with a -v diff --git a/TODO b/TODO index 85282de..b90cb76 100644 --- a/TODO +++ b/TODO @@ -7,10 +7,14 @@ BOTH SERVER/CLIENT: - check for return values of *printf() -- make config files format more readable / self-explanatory - - be more strict and humble when allocating memory from the stack +- in textfield_width(), handle too large lines somehow + +- use select() polling where possible for server-client communication + +- may err_line store the processed line internally as it does the line count + SERVER: - implement field of view / line of sight and obstacles for those on the map @@ -22,8 +26,12 @@ SERVER: - for game continuation, replace re-playing of whole record files with loading game state snapshots / save files +- why does an MapObjAct id of 0 fail? + CLIENT: - enable toggling of window borders - make log scrollable + +- implement server-config-file-like parsing for client-config-files, too diff --git a/confserver/map_object_actions b/confserver/map_object_actions deleted file mode 100644 index dc0de83..0000000 --- a/confserver/map_object_actions +++ /dev/null @@ -1,20 +0,0 @@ -1 -1 -wait -%% -2 -5 -move -%% -3 -15 -pick_up -%% -4 -5 -drop -%% -5 -45 -use -%% diff --git a/confserver/map_objects b/confserver/map_objects deleted file mode 100644 index c258e79..0000000 --- a/confserver/map_objects +++ /dev/null @@ -1,49 +0,0 @@ -0 -5 -@ -5 -HUMAN -0 -%% -1 -4 -a -1 -ANT -0 -%% -2 -5 -z -3 -ZOMBIE -0 -%% -3 -6 -S -9 -SHOGGOTH -0 -%% -4 -4 -# -0 -DIRT -0 -%% -5 -4 -% -0 -SKELETON -0 -%% -6 -4 -m -0 -MAGIC MEAT -2 -%% diff --git a/confserver/world b/confserver/world new file mode 100644 index 0000000..01d475a --- /dev/null +++ b/confserver/world @@ -0,0 +1,68 @@ +ACTION 1 +NAME wait +EFFORT 1 + +ACTION 2 +NAME move +EFFORT 5 + +ACTION 3 +NAME pick_up +EFFORT 15 + +ACTION 4 +NAME drop +EFFORT 5 + +ACTION 5 +NAME use +EFFORT 45 + +OBJECT 0 +NAME HUMAN +SYMBOL @ +LIFEPOINTS 5 +CORPSE_ID 5 +CONSUMABLE 0 + +OBJECT 1 +NAME ANT +SYMBOL a +LIFEPOINTS 1 +CORPSE_ID 4 +CONSUMABLE 0 + +OBJECT 2 +NAME ZOMBIE +SYMBOL z +LIFEPOINTS 3 +CORPSE_ID 5 +CONSUMABLE 0 + +OBJECT 3 +NAME SHOGGOTH +SYMBOL S +LIFEPOINTS 9 +CORPSE_ID 6 +CONSUMABLE 0 + +OBJECT 4 +NAME DIRT +SYMBOL # +LIFEPOINTS 0 +CORPSE_ID 4 +CONSUMABLE 0 + +OBJECT 5 +NAME SKELETON +SYMBOL % +LIFEPOINTS 0 +CORPSE_ID 4 +CONSUMABLE 0 + +OBJECT 6 +NAME 'MAGIC MEAT' +SYMBOL m +LIFEPOINTS 0 +CORPSE_ID 4 +CONSUMABLE 3 diff --git a/src/common/err_try_fgets.c b/src/common/err_try_fgets.c index 9912f11..797a442 100644 --- a/src/common/err_try_fgets.c +++ b/src/common/err_try_fgets.c @@ -13,7 +13,7 @@ /* Increments by one on each err_try_fgets() call, servers as a line counter. */ static uint32_t err_try_fgets_counter = 0; -/* Delimiter to use for err_try_fgets()' 'c' test. */ +/* Delimiter to use for err_try_fgets()' 'd' test. */ char * err_try_fgets_delim = ""; @@ -49,8 +49,8 @@ extern void err_line(uint8_t test, char * line, char * intro, char * msg) -extern void err_try_fgets(char * line, uint32_t linemax, FILE * file, - char * context, char * test) +extern char * err_try_fgets(char * line, uint32_t linemax, FILE * file, + char * context, char * test) { char * err_end = "File ended unexpectedly."; char * err_empty = "Hit empty line where non-empty line was expected."; @@ -61,7 +61,7 @@ extern void err_try_fgets(char * line, uint32_t linemax, FILE * file, char * err_uint8 = "Value is too large. Must be 255 or less."; char * f_name = "err_try_fgets()"; line[0] = '\0'; - try_fgets(line, linemax + 1, file, f_name); + char * fgets_return = try_fgets(line, linemax + 1, file, f_name); err_try_fgets_counter++; err_line(strchr(test, '0') && !(strlen(line)), line, context, err_end); err_line(strchr(test, 'n') && line[strlen(line) - 1] != '\n', line, context, @@ -71,7 +71,7 @@ extern void err_try_fgets(char * line, uint32_t linemax, FILE * file, err_line(strchr(test, 's') && strlen(line) > 2, line, context, err_many); err_line(strchr(test, 'd') && strcmp(line, err_try_fgets_delim), line, context, err_delim); - if (strchr(test, 'i')) + if (strchr(test, 'i') || strchr(test, '8')) { err_line(!(strchr(test, 'f')) && strlen(line) < 2, line, context, err_int); @@ -87,4 +87,5 @@ extern void err_try_fgets(char * line, uint32_t linemax, FILE * file, } err_line(strchr(test, '8') && atoi(line) > UINT8_MAX, line, context, err_uint8); + return fgets_return; } diff --git a/src/common/err_try_fgets.h b/src/common/err_try_fgets.h index 6bb07ee..7258a88 100644 --- a/src/common/err_try_fgets.h +++ b/src/common/err_try_fgets.h @@ -45,8 +45,8 @@ extern void err_line(uint8_t test, char * line, char * intro, char * msg); * be '+' or '-'; a '+' or '-' without digits following is invalid) * '8': check for "line" describing an integer smaller than or equal UINT8_MAX */ -extern void err_try_fgets(char * line, uint32_t linemax, FILE * file, - char * context, char * test); +extern char * err_try_fgets(char * line, uint32_t linemax, FILE * file, + char * context, char * test); diff --git a/src/common/readwrite.h b/src/common/readwrite.h index d616a19..4aebdbc 100644 --- a/src/common/readwrite.h +++ b/src/common/readwrite.h @@ -22,7 +22,7 @@ extern void try_fputc(uint8_t c, FILE * file, char * f); /* Wrapper to calling fgetc() and fgets() from function "f". The return code is * returned unless ferror() indicates an error (i.e. to signify an end of file, - * fgetc() may return an EOF and fgets() a NULL. + * fgetc() may return an EOF and fgets() a NULL). */ extern int try_fgetc(FILE * file, char * f); extern char * try_fgets(char * line, int size, FILE * file, char * f); diff --git a/src/server/configfile.c b/src/server/configfile.c new file mode 100644 index 0000000..213b313 --- /dev/null +++ b/src/server/configfile.c @@ -0,0 +1,450 @@ +/* src/server/configfile.c */ + +#define _POSIX_C_SOURCE 200809L /* strdup() */ +#include /* size_t, NULL */ +#include /* FILE, sprintf() */ +#include /* uint8_t, uint32_t */ +#include /* atoi(), free() */ +#include /* strchr(), strcmp(), strdup(), strlen() */ +#include /* access(), F_OK */ +#include "../common/err_try_fgets.h" /* err_line(), err_try_fgets(), + * reset_err_try_fgets_counter() + */ +#include "../common/readwrite.h" /* try_fopen(),try_fclose(),textfile_width() */ +#include "../common/rexit.h" /* exit_err() */ +#include "../common/try_malloc.h" /* try_malloc() */ +#include "cleanup.h" /* set_cleanup_flag(), CLEANUP_MAP_OBJ_DEFS, + * CLEANUP_MAP_OBJ_ACTS + */ +#include "map_object_actions.h" /* struct MapObjAct */ +#include "map_objects.h" /* struct MapObj, struct MapObjDef */ +#include "world.h" /* world global */ + + + +/* Flags defining state of object and action entry reading ((un-)finished, ready + * for starting the reading of a new definition etc. + */ +enum flag +{ + EDIT_STARTED = 0x01, + NAME_SET = 0x02, + EFFORT_SET = 0x04, + CORPSE_ID_SET = 0x04, + SYMBOL_SET = 0x08, + LIFEPOINTS_SET = 0x10, + CONSUMABLE_SET = 0x20, + READY_ACT = NAME_SET | EFFORT_SET, + READY_OBJ = NAME_SET | CORPSE_ID_SET | SYMBOL_SET | LIFEPOINTS_SET + | CONSUMABLE_SET +}; + + + +/* What MapObjDef and MapObjAct structs have in common at their top. Used to + * have common functions run over structs of both types. + */ +struct EntryHead +{ + uint8_t id; + struct EntryHead * next; +}; + + + +/* Many functions working on config file lines / tokens work with these elements + * that only change on line change. Makes sense to pass them over together. + */ +struct Context { + char * line; + char * token0; + char * token1; + char * err_pre; +}; + + + +/* Return next token from "line" or NULL if none is found. Tokens either a) + * start at the first non-whitespace character and end before the next + * whitespace character after that; or b) if the first non-whitespace character + * is a single quote followed by at least one other single quote some time later + * on the line, the token starts after that first single quote and ends before + * the second, with the next token_from_line() call starting its token search + * after that second quote. The only way to return an empty string (instead of + * NULL) as a token is to delimit the token by two succeeding single quotes. + * */ +static char * token_from_line(char * line); + +/* Determines the end of the token_from_line() token. */ +static void set_token_end(char ** start, char ** limit_char); + +/* Get tokens from "context" and, by their order (in the individual context and + * in subsequent calls of this function), interpret them as data to write into + * the MapObjAct / MapObjDef DB. + * + * Individual MapObjDef / MapObjAct 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 context->token0 of "ACTION" or "OBJECT"; or b) a NULL context->token0 is + * passed. This is interpreted as the end of the MapObjDef / MapObjAct DB read, + * so the appropriate cleanup flags are set and test_corpse_ids() is called. + */ +static void tokens_into_entries(struct Context * context); + +/* Start reading a new DB entry of "size" from tokens in "context" if ->token0 + * matches "comparand". Set EDIT_STARTED in "flags" to mark beginning of new + * entry reading. Check that id of new entry in ->token1 has not already been + * used in DB starting at "entry_cmp". + */ +static uint8_t new_entry(struct Context * context, char * comparand, + enum flag * 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 MapObjDef DB fit .id members of + * MapObjDef DB entries. + */ +static void test_corpse_ids(); + +/* Try to read tokens in "context" as members for the entry currently edited, + * which must be either "mod" or "moa". What member of which of the two is set + * depends on which of "object_flags" and "action_flags" has EDIT_STARTED set + * and on the key name of ->token0. Return 1 if interpretation succeeds, else 0. + * + * Note that MapObjAct entries' .name also determines their .func. + */ +static uint8_t set_members(struct Context * context, enum flag * object_flags, + enum flag * action_flags, struct MapObjDef * mod, + struct MapObjAct * moa); + +/* If "context"->token0 fits "comparand", set "element" to value read from + * ->token1 as either string (type: "s"), char ("c") or uint8 ("8"), set + * that element's flag to "flags" and return 1; else return 0. + */ +static uint8_t set_val(struct Context * context, char * comparand, + enum flag * flags, enum flag set_flag, char type, + char * element); + +/* Writes "context"->token1 to "target" only if it describes a proper uint8. */ +static void set_uint8(struct Context * context, uint8_t * target); + +/* If "name" fits "moa"->name, set "moa"->func to "func". (Derives MapObjAct + * .func from .name for set_members(). + */ +static uint8_t try_func_name(struct MapObjAct * moa, + char * name, void (* func) (struct MapObj *)); + + + +static char * token_from_line(char * line) +{ + static char * final_char = NULL; + static char * limit_char = NULL; + char * start = limit_char + 1; + if (line) + { + start = line; + limit_char = start; + final_char = &(line[strlen(line)]); + if ('\n' == *(final_char - 1)) + { + *(--final_char) = '\0'; + } + } + uint8_t empty = 1; + uint32_t i; + for (i = 0; '\0' != start[i]; i++) + { + if (' ' != start[i] && '\t' != start[i]) + { + start = &start[i]; + empty = 0; + break; + } + } + if (empty) + { + return start = NULL; + } + set_token_end(&start, &limit_char); + return start; +} + + + +static void set_token_end(char ** start, char ** limit_char) +{ + char * end_quote = ('\'' == (*start)[0]) ? strchr(*start + 1, '\'') : NULL; + *start = (end_quote) ? *start + 1 : *start; + if (end_quote) + { + *end_quote = '\0'; + *limit_char = end_quote; + return; + } + char * space = strchr(*start, ' '); + char * tab = strchr(*start, '\t'); + space = (!space || (tab && tab < space)) ? tab : space; + if (space) + { + * space = '\0'; + } + *limit_char = strchr(*start, '\0'); +} + + + +static void tokens_into_entries(struct Context * context) +{ + char * str_act = "ACTION"; + char * str_obj = "OBJECT"; + static struct MapObjAct ** moa_p_p = &world.map_obj_acts; + static struct MapObjDef ** mod_p_p = &world.map_obj_defs; + static enum flag action_flags = READY_ACT; + static enum flag object_flags = READY_OBJ; + static struct EntryHead * moa = NULL; + static struct EntryHead * mod = NULL; + if ( !context->token0 + || !strcmp(context->token0,str_act) || !strcmp(context->token0,str_obj)) + { + char * err_fin = "Last definition block not finished yet."; + err_line((action_flags & READY_ACT) ^ READY_ACT, + context->line, context->err_pre, err_fin); + err_line((object_flags & READY_OBJ) ^ READY_OBJ, + context->line, context->err_pre, err_fin); + object_flags = action_flags = READY_OBJ; + write_if_entry(&moa, (struct EntryHead ***) &moa_p_p); + write_if_entry(&mod, (struct EntryHead ***) &mod_p_p); + } + if (!context->token0) + { + set_cleanup_flag(CLEANUP_MAP_OBJECT_ACTS | CLEANUP_MAP_OBJECT_DEFS); + test_corpse_ids(); + return; + } + if (!( new_entry(context, str_act, &action_flags, + sizeof(struct MapObjAct), (struct EntryHead**) &moa, + (struct EntryHead *) world.map_obj_acts) + || new_entry(context, str_obj, &object_flags, + sizeof(struct MapObjDef), (struct EntryHead**) &mod, + (struct EntryHead *) world.map_obj_defs) + || set_members(context, &object_flags, &action_flags, + (struct MapObjDef *) mod, (struct MapObjAct *) moa))) + { + err_line(1, context->line, context->err_pre, "Unknown argument"); + } +} + + + +static uint8_t new_entry(struct Context * context, char * comparand, + enum flag * flags, size_t size, + struct EntryHead ** entry,struct EntryHead * entry_cmp) +{ + char * f_name = "new_entry()"; + char * err_uni = "Declaration of ID already used."; + if (!strcmp(context->token0, comparand)) + { + * flags = EDIT_STARTED; + * entry = try_malloc(size, f_name); + set_uint8(context, &((*entry)->id)); + for (; NULL != entry_cmp; entry_cmp = entry_cmp->next) + { + err_line((*entry)->id == entry_cmp->id, + context->line, context->err_pre, err_uni); + } + return 1; + } + return 0; +} + + + +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 * err_corpse_prefix = "In the object definition DB, one object corpse " + "ID does not reference any known object in the " + "DB. ID of responsible object: "; + char * err_corpse = try_malloc(strlen(err_corpse_prefix) + 3 + 1, f_name); + struct MapObjDef * test_entry_0 = world.map_obj_defs; + for (; test_entry_0; test_entry_0 = test_entry_0->next) + { + uint8_t corpse_id_found = 0; + struct MapObjDef * test_entry_1 = world.map_obj_defs; + 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; + } + } + sprintf(err_corpse, "%s%d", err_corpse_prefix, test_entry_0->id); + exit_err(!corpse_id_found, err_corpse); + } + free(err_corpse); +} + + + +static uint8_t set_members(struct Context * context, enum flag * object_flags, + enum flag * action_flags, struct MapObjDef * mod, + struct MapObjAct * moa) +{ + if ( * action_flags & EDIT_STARTED + && set_val(context, "NAME", action_flags, + NAME_SET, 's', (char *) &moa->name)) + { + if (!( try_func_name(moa, "move", actor_move) + || try_func_name(moa, "pick_up", actor_pick) + || try_func_name(moa, "drop", actor_drop) + || try_func_name(moa, "use", actor_use))) + { + moa->func = actor_wait; + } + *action_flags = *action_flags | NAME_SET; + return 1; + } + else if ( set_val(context, "NAME", object_flags, + NAME_SET, 's', (char *) &mod->name) + || set_val(context, "SYMBOL", object_flags, + SYMBOL_SET, 'c', (char *) &mod->char_on_map) + || set_val(context, "EFFORT", action_flags, + EFFORT_SET, '8', (char *) &moa->effort) + || set_val(context, "LIFEPOINTS", object_flags, + LIFEPOINTS_SET, '8', (char *) &mod->lifepoints) + || set_val(context, "CONSUMABLE", object_flags, + CONSUMABLE_SET, '8', (char *) &mod->consumable) + || set_val(context, "CORPSE_ID", object_flags, + CORPSE_ID_SET, '8', (char *) &mod->corpse_id)) + { + return 1; + } + return 0; +} + + + +static uint8_t set_val(struct Context * context, char * comparand, + enum flag * flags, enum flag set_flag, char type, + char * element) +{ + char * err_out = "Outside appropriate definition's context."; + char * err_singlechar = "Value must be single ASCII character."; + if (!strcmp(context->token0, comparand)) + { + err_line(!(* flags & EDIT_STARTED), + context->line, context->err_pre, err_out); + * flags = * flags | set_flag; + if ('s' == type) + { + * (char **) element = strdup(context->token1); + } + else if ('c' == type) + { + err_line(1 != strlen(context->token1), + context->line, context->err_pre, err_singlechar); + * element = (context->token1)[0]; + } + else if ('8' == type) + { + set_uint8(context, (uint8_t *) element); + } + return 1; + } + return 0; +} + + + +static void set_uint8(struct Context * context, uint8_t * target) +{ + char * err_uint8 = "Value not unsigned decimal number between 0 and 255."; + uint8_t i; + uint8_t is_uint8 = 1; + for (i = 0; '\0' != context->token1[i]; i++) + { + if (i > 2 || '0' > context->token1[i] || '9' < context->token1[i]) + { + is_uint8 = 0; + } + } + if (is_uint8 && atoi(context->token1) > UINT8_MAX) + { + is_uint8 = 0; + } + err_line(!(is_uint8), context->line, context->err_pre, err_uint8); + *target = atoi(context->token1); +} + + + +static uint8_t try_func_name(struct MapObjAct * moa, + char * name, void (* func) (struct MapObj *)) +{ + if (0 == strcmp(moa->name, name)) + { + moa->func = func; + return 1; + } + return 0; +} + + + +extern void read_config_file() { + char * f_name = "read_new_config_file()"; + char * path = world.path_config; + struct Context context; + char * err_pre_prefix = "Failed reading config file: \""; + char * err_pre_affix = "\". "; + context.err_pre = try_malloc(strlen(err_pre_prefix) + strlen(path) + + strlen(err_pre_affix) + 1, f_name); + sprintf(context.err_pre, "%s%s%s", err_pre_prefix, path, err_pre_affix); + exit_err(access(path, F_OK), context.err_pre); + FILE * file = try_fopen(path, "r", f_name); + uint32_t linemax = textfile_width(file); + context.line = try_malloc(linemax + 1, f_name); + reset_err_try_fgets_counter(); + err_line(0 == linemax, context.line, context.err_pre, "File is empty."); + context.token0 = NULL; /* For tokens_into_entries() if while() stagnates. */ + char * err_val = "No value given."; + char * err_many = "Too many values."; + while (err_try_fgets(context.line, linemax + 1, file, context.err_pre, "")) + { + char * line_copy = strdup(context.line); + context.token0 = token_from_line(line_copy); + if (context.token0) + { + err_line(0 == (context.token1 = token_from_line(NULL)), + context.line, context.err_pre, err_val); + err_line(NULL != token_from_line(NULL), + context.line, context.err_pre, err_many); + tokens_into_entries(&context); + context.token0 = NULL; + } + free(line_copy); + } + tokens_into_entries(&context); + try_fclose(file, f_name); + free(context.line); + free(context.err_pre); +} diff --git a/src/server/configfile.h b/src/server/configfile.h new file mode 100644 index 0000000..5fc2a0d --- /dev/null +++ b/src/server/configfile.h @@ -0,0 +1,18 @@ +/* src/server/configfile.h + * + * Reading in the config file of map object and map object action definitions. + */ + +#ifndef CONFIGFILE_H +#define CONFIGFILE_H + + + +/* Parse file at world.path_config into map object and map object action + * definitions at world.map_obj_defs and world.map_obj_acts. + */ +extern void read_config_file(); + + + +#endif diff --git a/src/server/init.c b/src/server/init.c index 3a83c34..9b3f86b 100644 --- a/src/server/init.c +++ b/src/server/init.c @@ -1,13 +1,14 @@ /* src/server/init.c */ -#define _POSIX_C_SOURCE 2 /* getopt(), optarg */ +//#define _POSIX_C_SOURCE 2 /* getopt(), optarg */ +#define _POSIX_C_SOURCE 200809L /* getopt(), optarg, strdup() */ #include "init.h" #include /* global errno, EEXIST */ #include /* NULL */ #include /* uint32_t */ -#include /* sprintf(), fflush() */ -#include /* exit(), free() */ -#include /* atoi(), strlen() */ +#include /* FILE, sprintf(), fflush() */ +#include /* exit(), free(), atoi() */ +#include /* strlen() */ #include /* mkdir() */ #include /* defines pid_t, time_t */ #include /* time() */ @@ -17,15 +18,10 @@ */ #include "../common/rexit.h" /* exit_err() */ #include "../common/try_malloc.h" /* try_malloc() */ -#include "cleanup.h" /* set_cleanup_flag(), CLEANUP_MAP_OBJ_DEFS, - * CLEANUP_MAP_OBJ_ACTS - */ -#include "io.h" /* read_config_file(), struct EntrySkeleton */ +#include "cleanup.h" /* set_cleanup_flag() */ +#include "configfile.h" /* read_config_file() */ #include "map.h" /* init_map() */ -#include "map_object_actions.h" /* struct MapObjAct, read_map_object_action() */ -#include "map_objects.h" /* struct MapObjDef, free_map_objects(), - * add_map_objects(), read_map_object_def() - */ +#include "map_objects.h" /* free_map_objects(), add_map_objects() */ #include "rrand.h" /* rrand() */ #include "run.h" /* obey_msg(), io_loop() */ #include "world.h" /* global world */ @@ -64,16 +60,7 @@ extern void init_map_and_map_objects_configs() world.map.size.y = 64; world.map.dist_orthogonal = 5; world.map.dist_diagonal = 7; - char * err_mod = "No map object definitions file."; - char * err_moa = "No map object actions file."; - exit_err(access(world.path_map_obj_defs, F_OK), err_mod); - exit_err(access(world.path_map_obj_acts, F_OK), err_moa); - read_config_file(world.path_map_obj_defs, CLEANUP_MAP_OBJECT_DEFS, - read_map_object_def, sizeof(struct MapObjDef), - (struct EntrySkeleton **) &world.map_obj_defs); - read_config_file(world.path_map_obj_acts, CLEANUP_MAP_OBJECT_ACTS, - read_map_object_action, sizeof(struct MapObjAct), - (struct EntrySkeleton **) &world.map_obj_acts); + read_config_file(); } diff --git a/src/server/io.c b/src/server/io.c index f318ca6..28c76b8 100644 --- a/src/server/io.c +++ b/src/server/io.c @@ -5,21 +5,17 @@ #include /* PIPE_BUF */ #include /* size_t, NULL */ #include /* uint8_t, uint32_t */ -#include /* defines EOF, FILE, sprintf(), ungetc() */ -#include /* free(), atoi() */ +#include /* defines EOF, FILE, sprintf() */ +#include /* free() */ #include /* strlen(), memcpy() */ #include /* time_t */ #include /* time() */ -#include "../common/err_try_fgets.h" /* err_try_fgets(), err_line(), - * reset_err_try_fgets_counter() - */ +#include "../common/err_try_fgets.h" /* err_line() */ #include "../common/readwrite.h" /* try_fopen(), try_fclose_unlink_rename(), - * try_fwrite(), try_fputc(), try_fgetc(), - * try_fclose() + * try_fwrite(), try_fputc(), try_fgetc() */ -#include "../common/rexit.h" /* exit_trouble() */ #include "../common/try_malloc.h" /* try_malloc() */ -#include "cleanup.h" /* set_cleanup_flag(), enum cleanup_flag */ +#include "cleanup.h" /* set_cleanup_flag() */ #include "map_objects.h" /* structs MapObj, MapObjDef, get_map_obj_def() */ #include "world.h" /* global world */ @@ -223,49 +219,6 @@ static void write_map(FILE * file) -extern void read_config_file(char * path, enum cleanup_flag cleanup, - void (* read) (char *, uint32_t, char *, - struct EntrySkeleton *, FILE *), - size_t size, struct EntrySkeleton ** entry_start) -{ - char * f_name = "init_map_object_defs()"; - char * context_prefix = "Failed reading config file: "; - char context[strlen(context_prefix) + strlen(path) + 1]; - sprintf(context, "%s%s", context_prefix, path); - char * err_uniq = "Declaration of ID already used."; - FILE * file = try_fopen(path, "r", f_name); - uint32_t linemax = textfile_width(file); - char line[linemax + 1]; - reset_err_try_fgets_counter(); - struct EntrySkeleton ** entry_ptr_ptr = entry_start; - while (1) - { - int test_for_end = try_fgetc(file, f_name); - if (EOF == test_for_end || '\n' == test_for_end) - { - break; - } - exit_trouble(EOF == ungetc(test_for_end, file), f_name, "ungetc()"); - err_try_fgets(line, linemax, file, context, "nfi8"); - struct EntrySkeleton * entry = try_malloc(size, f_name); - entry->id = atoi(line); - struct EntrySkeleton * entry_test = * entry_start; - for (; NULL != entry_test; entry_test = entry_test->next) - { - err_line(entry->id == entry_test->id, line, context, err_uniq); - } - read(line, linemax, context, entry, file); - entry->next = NULL; - * entry_ptr_ptr = entry; - entry_ptr_ptr = &entry->next; - err_try_fgets(line, linemax, file, context, "d"); - } - try_fclose(file, f_name); - set_cleanup_flag(cleanup); -} - - - extern char * io_round() { char * f_name = "io_round()"; diff --git a/src/server/io.h b/src/server/io.h index ad1344f..02f8e90 100644 --- a/src/server/io.h +++ b/src/server/io.h @@ -7,28 +7,8 @@ #ifndef IO_H #define IO_H -#include /* size_t */ -#include /* uint8_t, uint32_t */ -#include /* FILE */ -#include "cleanup.h" /* enum cleanup_flag */ - -/* Wrapper to reading in config files from "path" for DB entries of "size - * starting at "entry_start", to be unset by "cleanup" and reading in individual - * entry data line by line via "read"(). Assumes all entries start with the - * items collected in the EntrySkeleton struct. - */ -struct EntrySkeleton -{ - uint8_t id; - struct EntrySkeleton * next; -}; -extern void read_config_file(char * path, enum cleanup_flag cleanup, - void (* read) (char *, uint32_t, char *, - struct EntrySkeleton *, FILE *), - size_t size, struct EntrySkeleton ** entry_start); - /* Return single \0-terminated string read from input queue (world.queue); or, * if queue is empty and world.turn is unequal world.last_update_turn, update * world state file at world.path_worldstate (and update world.last_update_turn diff --git a/src/server/main.c b/src/server/main.c index 9bfddae..3c8829b 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -35,8 +35,7 @@ int main(int argc, char ** argv) exit_err(-1 == test, printf_err); } } - world.path_map_obj_defs = "confserver/map_objects"; - world.path_map_obj_acts = "confserver/map_object_actions"; + world.path_config = "confserver/world"; world.path_worldstate = "server/worldstate"; world.path_out = "server/out"; world.path_in = "server/in"; diff --git a/src/server/map_object_actions.c b/src/server/map_object_actions.c index cd233c9..987f135 100644 --- a/src/server/map_object_actions.c +++ b/src/server/map_object_actions.c @@ -3,14 +3,12 @@ #include "map_object_actions.h" #include /* NULL */ #include /* uint8_t, uint16_t */ -#include /* sprintf(), ungetc() */ -#include /* free(), atoi() */ +#include /* sprintf() */ +#include /* free() */ #include /* strlen(), strcmp(), memcpy(), strncmp() */ -#include "../common/err_try_fgets.h" /* err_try_fgets() */ #include "../common/rexit.h" /* exit_err() */ #include "../common/try_malloc.h" /* try_malloc() */ #include "../common/yx_uint8.h" /* struct yx_uint8 */ -#include "io.h" /* struct EntrySkeleton */ #include "map_objects.h" /* structs MapObj, MapObjDef, get_player(), * set_object_position(), own_map_object(), * get_map_object_def() @@ -25,8 +23,8 @@ static void update_log(char * text); /* If "name" fits "moa"->name, set "moa"->func to "func". */ -static uint8_t try_func_name(struct MapObjAct * moa, - char * name, void (* func) (struct MapObj *)); +//static uint8_t try_func_name(struct MapObjAct * moa, +// char * name, void (* func) (struct MapObj *)); /* One actor "wounds" another actor, decrementing his lifepoints and, if they * reach zero in the process, killing it. Generates appropriate log message. @@ -88,19 +86,6 @@ static void update_log(char * text) -static uint8_t try_func_name(struct MapObjAct * moa, - char * name, void (* func) (struct MapObj *)) -{ - if (0 == strcmp(moa->name, name)) - { - moa->func = func; - return 1; - } - return 0; -} - - - static void actor_hits_actor(struct MapObj * hitter, struct MapObj * hitted) { struct MapObjDef * mod_hitter = get_map_object_def(hitter->type); @@ -222,29 +207,6 @@ static void playerbonus_use(uint8_t no_object, uint8_t wrong_object) -extern void read_map_object_action(char * line, uint32_t linemax,char * context, - struct EntrySkeleton * entry, FILE * file) -{ - char * f_name = "init_map_object_actions()"; - struct MapObjAct * moa = (struct MapObjAct *) entry; - err_try_fgets(line, linemax, file, context, "0nfi8"); - moa->effort = atoi(line); - err_try_fgets(line, linemax, file, context, "0nf"); - line[strlen(line) - 1] = '\0'; - uint8_t len_name = strlen(line) + 1; - moa->name = try_malloc(len_name, f_name); - memcpy(moa->name, line, len_name); - if (!( try_func_name(moa, "move", actor_move) - || try_func_name(moa, "pick_up", actor_pick) - || try_func_name(moa, "drop", actor_drop) - || try_func_name(moa, "use", actor_use))) - { - moa->func = actor_wait; - } -} - - - extern void free_map_object_actions(struct MapObjAct * moa) { if (NULL == moa) diff --git a/src/server/map_object_actions.h b/src/server/map_object_actions.h index 7b5283f..81a3fa6 100644 --- a/src/server/map_object_actions.h +++ b/src/server/map_object_actions.h @@ -8,10 +8,8 @@ #ifndef MAP_OBJECT_ACTIONS_H #define MAP_OBJECT_ACTIONS_H -#include /* uint8_t, uint23_t */ -#include /* FILE */ +#include /* uint8_t */ struct MapObj; -struct EntrySkeleton; @@ -26,12 +24,6 @@ struct MapObjAct -/* Read-in to "entry" multi-line entry from MapObjAct config "file", using - * pre-allocated "line", "linemax" and "context" as input for err_try_fgets(). - */ -extern void read_map_object_action(char * line, uint32_t linemax,char * context, - struct EntrySkeleton * entry, FILE * file); - /* Free MapObjAct * chain starting at "moa". */ extern void free_map_object_actions(struct MapObjAct * moa); diff --git a/src/server/map_objects.c b/src/server/map_objects.c index ae3fbd1..ff062d7 100644 --- a/src/server/map_objects.c +++ b/src/server/map_objects.c @@ -2,11 +2,9 @@ #include "map_objects.h" #include /* NULL */ -#include /* FILE typedef */ #include /* uint8_t, uint16_t */ -#include /* free(), atoi() */ -#include /* strlen(), memcpy(), memset() */ -#include "../common/err_try_fgets.h" /* err_try_fgets() */ +#include /* free() */ +#include /* memset(), strlen() */ #include "../common/rexit.h" /* exit_err() */ #include "../common/try_malloc.h" /* try_malloc() */ #include "../common/yx_uint8.h" /* yx_uint8 struct */ @@ -15,7 +13,7 @@ #include "world.h" /* global world */ #include "yx_uint8.h" /* yx_uint8_cmp() */ -#include "io.h" /* struct EntrySkeleton */ + /* Return pointer to map object of "id" in chain starting at "ptr". */ static struct MapObj * get_map_object(struct MapObj * ptr, uint8_t id); @@ -95,27 +93,6 @@ static void add_map_object(uint8_t type) -extern void read_map_object_def(char * line, uint32_t linemax, char * context, - struct EntrySkeleton * entry, FILE * file) -{ - char * f_name = "init_map_object_defs()"; - struct MapObjDef * mod = (struct MapObjDef *) entry; - err_try_fgets(line, linemax, file, context, "0nfi8"); - mod->corpse_id = atoi(line); - err_try_fgets(line, linemax, file, context, "0nfs"); - mod->char_on_map = line[0]; - err_try_fgets(line, linemax, file, context, "0nfi8"); - mod->lifepoints = atoi(line); - err_try_fgets(line, linemax, file, context, "0nf"); - line[strlen(line) - 1] = '\0'; - mod->name = try_malloc(strlen(line) + 1, f_name); - memcpy(mod->name, line, strlen(line) + 1); - err_try_fgets(line, linemax, file, context, "0nfi8"); - mod->consumable = atoi(line); -} - - - extern void free_map_object_defs(struct MapObjDef * mod_start) { if (NULL == mod_start) diff --git a/src/server/map_objects.h b/src/server/map_objects.h index cf575f3..52d87e1 100644 --- a/src/server/map_objects.h +++ b/src/server/map_objects.h @@ -7,10 +7,10 @@ #ifndef MAP_OBJECTS_H #define MAP_OBJECTS_H -#include /* uint8_t, uint32_t */ -#include /* FILE */ +#include /* uint8_t */ +//#include /* FILE */ #include "../common/yx_uint8.h" /* yx_uint8 structs */ -struct EntrySkeleton; +//struct EntrySkeleton; @@ -43,8 +43,8 @@ struct MapObjDef /* Read-in to "entry" multi-line entry from MapObjDef config "file", using * pre-allocated "line", "linemax" and "context" as input for err_try_fgets(). */ -extern void read_map_object_def(char * line, uint32_t linemax, char * context, - struct EntrySkeleton * entry, FILE * file); +//extern void read_map_object_def(char * line, uint32_t linemax, char * context, +// struct EntrySkeleton * entry, FILE * file); /* Free map object definitions chain starting at "mod_start". */ extern void free_map_object_defs(struct MapObjDef * mod_start); diff --git a/src/server/world.h b/src/server/world.h index 47f7c65..dcd672a 100644 --- a/src/server/world.h +++ b/src/server/world.h @@ -29,8 +29,7 @@ struct World char * path_out; /* File to write server messages into. */ char * path_worldstate; /* File to represent world state to clients.*/ char * path_record; /* Record file from which to read the game history. */ - char * path_map_obj_defs; /* path for map object definitions config file */ - char * path_map_obj_acts; /* path for map object actions config file */ + char * path_config; /* Path for map object (action) definitions file. */ char * tmp_suffix; /* Appended to paths of files for their tmp versions. */ char * queue; /* Stores un-processed messages read from the input file. */ uint32_t queue_size;/* Length of .queue sequence of \0-terminated strings.*/