From: Ron Koenderink Date: Sat, 12 Nov 2005 16:53:52 +0000 (+0000) Subject: (init_server, ef_load, xundump): Add ability to customize game X-Git-Tag: PZ5~455 X-Git-Url: http://git.pond.sub.org/?p=empserver;a=commitdiff_plain;h=d8422caa8487dcf56dc7eda09c5eeb7d42fa466e (init_server, ef_load, xundump): Add ability to customize game configurations. The custom game configurations are loaded via text files in the data directory. Closes #752881. --- diff --git a/include/file.h b/include/file.h index da36815ae..753408d94 100644 --- a/include/file.h +++ b/include/file.h @@ -158,6 +158,7 @@ extern int ef_flags(int); extern int ef_byname(char *); extern int ef_byname_from(char *, int *); extern void ef_init(void); +extern int ef_load(void); extern struct empfile empfile[EF_MAX + 1]; diff --git a/include/prototypes.h b/include/prototypes.h index 3e46c6051..2f58ed01c 100644 --- a/include/prototypes.h +++ b/include/prototypes.h @@ -284,6 +284,8 @@ extern int check_plane_ok(struct plnstr *); extern int check_comm_ok(struct comstr *); extern int check_loan_ok(struct lonstr *); extern int check_trade_ok(struct trdstr *); +/* ef_load.c */ +/* in file.h */ /* fsize.c */ extern int fsize(int); extern int blksize(int); @@ -360,6 +362,8 @@ extern int updatetime(time_t *); extern void next_update_time(time_t *, time_t *, time_t *); extern void next_update_check_time(time_t *, time_t *, time_t *); extern int updates_disabled(void); +/* xundump.c */ +extern int xundump(FILE *, char *, int); /* * src/lib/gen/ *.c diff --git a/src/lib/common/Makefile b/src/lib/common/Makefile index 621a09dc1..912482ed1 100644 --- a/src/lib/common/Makefile +++ b/src/lib/common/Makefile @@ -38,13 +38,13 @@ NTLIB = $(SRCDIR)\lib\libcommon.lib OBJS = bestpath.o bridgefall.o check.o damage.o file.o fsize.o \ hap_fact.o hours.o land.o log.o mailbox.o maps.o move.o \ nat.o path.o res_pop.o sectdamage.o \ - stmtch.o tfact.o type.o wantupd.o xy.o + stmtch.o tfact.o type.o wantupd.o xy.o ef_load.o xundump.o NTOBJS = bestpath.obj bridgefall.obj check.obj damage.obj file.obj fsize.obj \ hap_fact.obj hours.obj land.obj log.obj \ mailbox.obj maps.obj move.obj nat.obj path.obj res_pop.obj \ sectdamage.obj stmtch.obj tfact.obj \ - type.obj wantupd.obj xy.obj + type.obj wantupd.obj xy.obj ef_load.obj xundump.obj all: $(LIB) diff --git a/src/lib/common/ef_load.c b/src/lib/common/ef_load.c new file mode 100644 index 000000000..0fd435971 --- /dev/null +++ b/src/lib/common/ef_load.c @@ -0,0 +1,67 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2005, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the + * related information and legal notices. It is expected that any future + * projects/authors will amend these files as needed. + * + * --- + * + * ef_load.c: Load custom game configuration files + * + * Known contributors to this file: + * Ron Koenderink, 2005 + * + */ + +#include +#include + +#include "prototypes.h" +#include "file.h" + +int +ef_load() +{ + struct empfile *ep; + FILE *fp; + int retval = 0; + + for (ep = empfile; ep->name; ep++) { + if (!EF_IS_GAME_STATE(ep->uid) && ep->file) { + if ((fp = fopen(ep->file, "r")) == NULL) { + continue; + } + if (xundump(fp, ep->name, ep->uid) < 0) + retval = -1; + else { + int ch = getc(fp); + if (ch != EOF) { + fprintf(stderr, "%s: Junk after the trailer\n", + ep->file); + retval = -1; + } + } + fclose(fp); + } + } + return retval; +} diff --git a/src/lib/common/xundump.c b/src/lib/common/xundump.c new file mode 100644 index 000000000..87b9c57ca --- /dev/null +++ b/src/lib/common/xundump.c @@ -0,0 +1,469 @@ +/* + * Empire - A multi-player, client/server Internet based war game. + * Copyright (C) 1986-2005, Dave Pare, Jeff Bailey, Thomas Ruschak, + * Ken Stevens, Steve McClure + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * --- + * + * See the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the + * related information and legal notices. It is expected that any future + * projects/authors will amend these files as needed. + * + * --- + * + * xundump.c: Text loading functions based on xdump output + * + * Known contributors to this file: + * Ron Koenderink, 2005 + * + */ + +#include +#include + +#include +#include +#include +#include + +#include "file.h" +#include "nsc.h" +#include "match.h" + +#define MAX_NUM_COLUMNS 256 + +static char *fname = ""; +static int lineno = 0; + +/* + * TODO + * This structure could be replaced with struct valstr. + */ +enum enum_value { + VAL_NOTUSED, + VAL_STRING, + VAL_SYMBOL, + VAL_DOUBLE +}; + +struct value { + enum enum_value v_type; + union { + char *v_string; + double v_double; + } v_field; +}; + +static int +gripe(char *fmt, ...) +{ + va_list ap; + + fprintf(stderr, "%s:%d: ", fname, lineno); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + putc('\n', stderr); + + return -1; +} + +static char * +xuesc(char *buf) +{ + char *src, *dst; + int octal_chr, n; + + dst = buf; + src = buf; + while (*src) { + if (*src == '\\') { + if (sscanf(++src, "%3o%n", &octal_chr, &n) != 1 || n != 3) + return NULL; + *dst++ = (char)octal_chr; + src += 3; + } else + *dst++ = *src++; + } + *dst = '\0'; + return buf; +} + +static int +xuflds(FILE *fp, struct value values[]) +{ + int i, ch; + char sep; + char buf[1024]; + + for (i = 0; i < MAX_NUM_COLUMNS; i++) { + ch = getc(fp); + ungetc(ch, fp); + + switch (ch) { + case '+': case '-': case '.': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (fscanf(fp, "%lg%c", &values[i].v_field.v_double, &sep) != 2) + return gripe("Malformed number in field %d", i + 1); + values[i].v_type = VAL_DOUBLE; + break; + case '"': + if (fscanf(fp, "\"%1023[^ \n]%c", buf, &sep) != 2 + || buf[strlen(buf)-1] != '"') + return gripe("Malformed string in field %d", i + 1); + buf[strlen(buf)-1] = '\0'; + if (!xuesc(buf)) + return gripe("Invalid escape sequence in field %d", + i + 1); + values[i].v_type = VAL_STRING; + values[i].v_field.v_string = strdup(buf); + break; + default: + if (fscanf(fp, "%1023[^ \n]%c", buf, &sep) != 2) { + return gripe("Junk in field %d", i + 1); + } + if (!strcmp(buf, "nil")) { + values[i].v_field.v_string = NULL; + values[i].v_type = VAL_STRING; + } + else { + values[i].v_field.v_string = strdup(buf); + values[i].v_type = VAL_SYMBOL; + } + } + if (sep == '\n') + break; + if (sep != ' ') + return gripe( + "Expected space or newline as field separator found %c", + sep); + } + if (i >= MAX_NUM_COLUMNS) + return gripe("Too many columns"); + if (i == 0) + return gripe("No columns read"); + values[++i].v_type = VAL_NOTUSED; + return i; +} + +static int +xunsymbol(struct castr *ca, char *buf) +{ + struct symbol *symbol = (struct symbol *)empfile[ca->ca_table].cache; + int i; + int value = 0; + char *token; + + if (ca->ca_flags & NSC_BITS) + token = strtok( buf, "|"); + else + token = buf; + + if (!token || token[0] == '\0') + return gripe("Empty symbol value for field %s", ca->ca_name); + + while (token) { + if ((i = stmtch(token, symbol, offsetof(struct symbol, name), + sizeof(struct symbol))) != M_NOTFOUND) { + if (!(ca->ca_flags & NSC_BITS)) + return(symbol[i].value); + value |= symbol[i].value; + break; + } + else + return gripe("Symbol %s was not found for field %s", token, + ca->ca_name); + token = strtok(NULL, "|"); + } + return(value); +} + +static int +has_const(struct castr ca[]) +{ + int i; + + for (i = 0; ca[i].ca_name; i++) { + if (ca[i].ca_flags & NSC_CONST) + return 1; + } + return 0; +} + +static void +xuinitrow(int type, int row) +{ + struct empfile *ep = &empfile[type]; + char *ptr = ep->cache + ep->size * row; + + memset(ptr, 0, ep->size); + + if (ep->init) + ep->init(row, ptr); +} + +static int +xuloadrow(int type, int row, struct value values[]) +{ + int i,j,k; + struct empfile *ep = &empfile[type]; + char *ptr = ep->cache + ep->size * row; + struct castr *ca = ep->cadef; + void *row_ref; + + i = 0; + j = 0; + while (ca[i].ca_type != NSC_NOTYPE && + values[j].v_type != VAL_NOTUSED) { + row_ref = (char *)ptr + ca[i].ca_off; + k = 0; + do { + /* + * TODO + * factor out NSC_CONST comparsion + */ + switch (values[j].v_type) { + case VAL_SYMBOL: + if (ca[i].ca_table == EF_BAD) + return(gripe("Found symbol string %s, but column %s " + "is not symbol or symbol sets", + values[j].v_field.v_string, ca[i].ca_name)); + values[j].v_field.v_double = + (double)xunsymbol(&ca[i], values[j].v_field.v_string); + free(values[i].v_field.v_string); + if (values[j].v_field.v_double < 0.0) + return -1; + /* + * fall through + */ + case VAL_DOUBLE: + switch (ca[i].ca_type) { + case NSC_INT: + if (ca[i].ca_flags & NSC_CONST) { + if (((int *)row_ref)[k] != (int) + values[j].v_field.v_double) + gripe("Field %s must be same, " + "read %d != expected %d", + ca[i].ca_name, + ((int *)row_ref)[k], + (int)values[j].v_field.v_double); + + } else + ((int *)row_ref)[k] = + (int)values[j].v_field.v_double; + break; + case NSC_LONG: + if (ca[i].ca_flags & NSC_CONST) { + if (((long *)row_ref)[k] != (long) + values[j].v_field.v_double) + gripe("Field %s must be same, " + "read %ld != expected %ld", + ca[i].ca_name, + ((long *)row_ref)[k], + (long)values[j].v_field.v_double); + } else + ((long *)row_ref)[k] = (long) + values[j].v_field.v_double; + break; + case NSC_USHORT: + if (ca[i].ca_flags & NSC_CONST) { + if (((unsigned short *)row_ref)[k] != + (unsigned short)values[j].v_field.v_double) + gripe("Field %s must be same, " + "read %d != expected %d", + ca[i].ca_name, + ((unsigned short *)row_ref)[k], + (unsigned short)values[j].v_field.v_double); + } else + ((unsigned short *)row_ref)[k] = (unsigned short) + values[j].v_field.v_double; + break; + case NSC_UCHAR: + if (ca[i].ca_flags & NSC_CONST) { + if (((unsigned char *)row_ref)[k] != (unsigned char) + values[j].v_field.v_double) + gripe("Field %s must be same, " + "read %d != expected %d", + ca[i].ca_name, + ((unsigned char *)row_ref)[k], + (unsigned char)values[j].v_field.v_double); + } else + ((unsigned char *)row_ref)[k] = (unsigned char) + values[j].v_field.v_double; + break; + case NSC_FLOAT: + if (ca[i].ca_flags & NSC_CONST) { + if (((float *)row_ref)[k] != (float) + values[j].v_field.v_double) + gripe("Field %s must be same, " + "read %g != expected %g", + ca[i].ca_name, + ((float *)row_ref)[k], + (float)values[j].v_field.v_double); + } else + ((float *)row_ref)[k] = (float) + values[j].v_field.v_double; + break; + case NSC_STRING: + return gripe("Field %s is a string type, " + "but %lg was read which is a number", + ca[i].ca_name, values[j].v_field.v_double); + default: + return gripe("Field %s's type %d is not supported", + ca[i].ca_name, ca[i].ca_type); + } + break; + case VAL_STRING: + switch(ca[i].ca_type) { + case NSC_STRINGY: + return gripe("Field %s is of NSC_STRINGY type " + "which is not supported", ca[i].ca_name); + case NSC_STRING: + if (ca[i].ca_flags & NSC_CONST) { + if (strcmp(((char **)row_ref)[k], + values[j].v_field.v_string) != 0) + gripe("Field %s must be same, " + "read %s != expected %s", + ca[i].ca_name, + *((char **)row_ref)[k], + *values[j].v_field.v_string); + } else + ((char **)row_ref)[k] = values[j].v_field.v_string; + break; + case NSC_INT: + case NSC_LONG: + case NSC_USHORT: + case NSC_UCHAR: + case NSC_FLOAT: + return gripe("Field %s is a number type %d, " + "but %s was read which is a string", + ca[i].ca_name, ca[i].ca_type, + values[j].v_field.v_string); + default: + return gripe("Field %s's type %d is not supported", + ca[i].ca_name, ca[i].ca_type); + } + break; + case VAL_NOTUSED: + return gripe("Missing column %s in file", ca[i].ca_name); + default: + return gripe("Unknown value type %d", values[j].v_type); + } + k++; + j++; + } while (k < ca[i].ca_len); + i++; + } + if (ca[i].ca_type != NSC_NOTYPE) + return gripe("Missing column %s in file", ca[i].ca_name); + switch (values[j].v_type) { + case VAL_NOTUSED: + break; + case VAL_STRING: + case VAL_SYMBOL: + return gripe("Extra junk after the last column, read %s", + values[j].v_field.v_string); + case VAL_DOUBLE: + return gripe("Extra junk after the last column, read %lg", + values[j].v_field.v_double); + default: + return gripe("Extra junk after the last column, " + "unknown value type %d", values[j].v_type); + } + return 0; +} + +int +xundump(FILE *fp, char *file, int expected_table) +{ + char name[64]; + char sep; + int row, rows, ch; + struct value values[MAX_NUM_COLUMNS + 1]; + int type; + int fixed_rows; + + if (strcmp(fname, file) != 0) { + fname = file; + lineno = 1; + } else + lineno++; + + if (fscanf(fp, "XDUMP %63[^0123456789]%*d%c", name, &sep) != 2) + return gripe("Expected XDUMP header"); + if (sep != '\n') + return gripe("Junk after XDUMP header"); + + if (strlen(name) < 2) + return gripe("Invalid table name in header %s", name); + if (name[strlen(name) - 1] != ' ') + return gripe("Missing space after table name in header %s", + name); + name[strlen(name) - 1] = '\0'; + + type = ef_byname(name); + if (type < 0) + return gripe("Table not found %s", name); + + if (expected_table != EF_BAD && expected_table != type) + return gripe("Incorrect Table expecting %s got %s", + ef_nameof(expected_table), name); + + fixed_rows = has_const(ef_cadef(type)); + + for (row = 0; ; row++) { + lineno++; + ch = getc(fp); + ungetc(ch, fp); + if (ch == EOF) + return gripe("Unexpected EOF"); + if (ch == '/') + break; + /* + * TODO + * Add column count check to the return value of xuflds() + */ + if (xuflds(fp, values) <= 0) + return -1; + else { + if (row >= empfile[type].csize - 1) + return gripe("Too many rows for table %s", name); + empfile[type].fids = row + 1; + if (!fixed_rows) + xuinitrow(type, row); + if (xuloadrow(type, row, values) < 0) + return -1; + } + } + + if (fscanf(fp, "/%d%c", &rows, &sep) != 2) + return gripe("Failed to find number of rows trailer"); + if (row != rows) + return gripe("Number of rows doesn't match between " + "the trailer and what was read"); + if (fixed_rows && row != empfile[type].csize -1) + return gripe("Number of rows doesn't match, and " + "it must for table %s", name); + if (sep != '\n') + return gripe("Junk after number of rows trailer"); + + if (!fixed_rows) + xuinitrow(type, row); + + return 0; +} diff --git a/src/lib/global/file.c b/src/lib/global/file.c index 60c200ab5..892cba271 100644 --- a/src/lib/global/file.c +++ b/src/lib/global/file.c @@ -137,18 +137,18 @@ struct empfile empfile[] = { UNMAPPED_CACHE(struct loststr, EFF_OWNER)}, /* Static game data (configuration) */ - {EF_SECTOR_CHR, "sect chr", NULL, dchr_ca, ARRAY_TABLE(dchr, EFF_CFG)}, - {EF_SHIP_CHR, "ship chr", NULL, mchr_ca, ARRAY_CACHE(mchr, EFF_CFG)}, - {EF_PLANE_CHR, "plane chr", NULL, plchr_ca, ARRAY_CACHE(plchr, EFF_CFG)}, - {EF_LAND_CHR, "land chr", NULL, lchr_ca, ARRAY_CACHE(lchr, EFF_CFG)}, - {EF_NUKE_CHR, "nuke chr", NULL, nchr_ca, ARRAY_CACHE(nchr, EFF_CFG)}, + {EF_SECTOR_CHR, "sect chr", "sect_def", dchr_ca, ARRAY_TABLE(dchr, EFF_CFG)}, + {EF_SHIP_CHR, "ship chr", "ship_def", mchr_ca, ARRAY_CACHE(mchr, EFF_CFG)}, + {EF_PLANE_CHR, "plane chr", "plane_def", plchr_ca, ARRAY_CACHE(plchr, EFF_CFG)}, + {EF_LAND_CHR, "land chr", "land_def", lchr_ca, ARRAY_CACHE(lchr, EFF_CFG)}, + {EF_NUKE_CHR, "nuke chr", "nuke_def", nchr_ca, ARRAY_CACHE(nchr, EFF_CFG)}, {EF_NEWS_CHR, "news chr", NULL, rpt_ca, ARRAY_TABLE(rpt, EFF_CFG)}, {EF_TREATY_FLAGS, "treaty flags", NULL, symbol_ca, PTR_CACHE(treaty_flags, EFF_CFG)}, - {EF_ITEM, "item", NULL, ichr_ca, ARRAY_TABLE(ichr, EFF_CFG)}, - {EF_INFRASTRUCTURE, "infrastructure", NULL, intrchr_ca, + {EF_ITEM, "item", "item_def", ichr_ca, ARRAY_TABLE(ichr, EFF_CFG)}, + {EF_INFRASTRUCTURE, "infrastructure", "infrastructure_def", intrchr_ca, ARRAY_TABLE(intrchr, EFF_CFG)}, - {EF_PRODUCT, "product", NULL, pchr_ca, ARRAY_TABLE(pchr, EFF_CFG)}, + {EF_PRODUCT, "product", "product_def", pchr_ca, ARRAY_TABLE(pchr, EFF_CFG)}, {EF_TABLE, "table", NULL, empfile_ca, ARRAY_TABLE(empfile, EFF_CFG)}, {EF_SHIP_CHR_FLAGS, "ship chr flags", NULL, symbol_ca, PTR_CACHE(ship_chr_flags, EFF_CFG)}, diff --git a/src/server/main.c b/src/server/main.c index 817f8c03a..0e0336784 100644 --- a/src/server/main.c +++ b/src/server/main.c @@ -263,6 +263,8 @@ init_server(void) loc_NTInit(); #endif update_policy_check(); + if (ef_load() < 0) + exit(EXIT_FAILURE); nullify_objects(); global_init(); shutdown_init();