From 7b4b879cc1e3ddc2ee1fcb85710735efeb7f07e4 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 6 Feb 2016 10:56:08 +0100 Subject: [PATCH 01/17] quick addition to a_removeall additional testing for attributes --- src/kernel/save.test.c | 29 +++++++++++++++++++++++++ src/util/attrib.c | 49 +++++++++++++++++++++++++----------------- src/util/attrib.test.c | 18 ++++++++++++++++ tests/score | 1 - tests/score.alliances | 3 --- 5 files changed, 76 insertions(+), 24 deletions(-) delete mode 100644 tests/score delete mode 100644 tests/score.alliances diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c index 4762bf6e0..1b4028694 100644 --- a/src/kernel/save.test.c +++ b/src/kernel/save.test.c @@ -1,5 +1,7 @@ #include #include +#include +#include #include "save.h" #include "unit.h" @@ -61,9 +63,36 @@ static void test_readwrite_unit(CuTest * tc) test_cleanup(); } +static void test_readwrite_attrib(CuTest *tc) { + gamedata *data; + attrib *a = NULL; + const char *path = "attrib.dat"; + test_cleanup(); + data = gamedata_open(path, "wb"); + CuAssertPtrNotNull(tc, data); + add_key(&a, 41); + add_key(&a, 42); + a_write(data->store, a, NULL); + gamedata_close(data); + a_removeall(&a, NULL); + CuAssertPtrEquals(tc, 0, a); + + data = gamedata_open(path, "rb"); + CuAssertPtrNotNull(tc, data); + a_read(data->store, &a, NULL); + gamedata_close(data); + CuAssertPtrNotNull(tc, find_key(a, 41)); + CuAssertPtrNotNull(tc, find_key(a, 42)); + a_removeall(&a, NULL); + + CuAssertIntEquals(tc, 0, remove(path)); + test_cleanup(); +} + CuSuite *get_save_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_readwrite_attrib); SUITE_ADD_TEST(suite, test_readwrite_data); SUITE_ADD_TEST(suite, test_readwrite_unit); return suite; diff --git a/src/util/attrib.c b/src/util/attrib.c index c076b74f2..837f9aff4 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -209,28 +209,37 @@ int a_remove(attrib ** pa, attrib * a) void a_removeall(attrib ** pa, const attrib_type * at) { attrib **pnexttype = pa; - attrib **pnext = NULL; - while (*pnexttype) { - attrib *next = *pnexttype; - if (next->type == at) - break; - pnexttype = &next->nexttype; - pnext = &next->next; - } - if (*pnexttype && (*pnexttype)->type == at) { - attrib *a = *pnexttype; - - *pnexttype = a->nexttype; - if (pnext) { - while (*pnext && (*pnext)->type != at) - pnext = &(*pnext)->next; - *pnext = a->nexttype; + if (!at) { + while (*pnexttype) { + attrib *a = *pnexttype; + *pnexttype = a->next; + a_free(a); } - while (a && a->type == at) { - attrib *ra = a; - a = a->next; - a_free(ra); + } + else { + attrib **pnext = NULL; + while (*pnexttype) { + attrib *a = *pnexttype; + if (a->type == at) + break; + pnexttype = &a->nexttype; + pnext = &a->next; + } + if (*pnexttype && (*pnexttype)->type == at) { + attrib *a = *pnexttype; + + *pnexttype = a->nexttype; + if (pnext) { + while (*pnext && (*pnext)->type != at) + pnext = &(*pnext)->next; + *pnext = a->nexttype; + } + while (a && a->type == at) { + attrib *ra = a; + a = a->next; + a_free(ra); + } } } } diff --git a/src/util/attrib.test.c b/src/util/attrib.test.c index 691087477..76533887d 100644 --- a/src/util/attrib.test.c +++ b/src/util/attrib.test.c @@ -51,6 +51,23 @@ static void test_attrib_remove_self(CuTest * tc) { CuAssertPtrEquals(tc, a, alist); } + +static void test_attrib_removeall(CuTest * tc) { + const attrib_type at_foo = { "foo" }; + const attrib_type at_bar = { "bar" }; + attrib *alist = 0, *a; + a_add(&alist, a_new(&at_foo)); + a = a_add(&alist, a_new(&at_bar)); + a_add(&alist, a_new(&at_foo)); + a_removeall(&alist, &at_foo); + CuAssertPtrEquals(tc, a, alist); + CuAssertPtrEquals(tc, 0, alist->next); + a_add(&alist, a_new(&at_bar)); + a_add(&alist, a_new(&at_foo)); + a_removeall(&alist, NULL); + CuAssertPtrEquals(tc, 0, alist); +} + static void test_attrib_remove(CuTest * tc) { attrib_type at_foo = { "foo" }; @@ -98,6 +115,7 @@ CuSuite *get_attrib_suite(void) SUITE_ADD_TEST(suite, test_attrib_new); SUITE_ADD_TEST(suite, test_attrib_add); SUITE_ADD_TEST(suite, test_attrib_remove); + SUITE_ADD_TEST(suite, test_attrib_removeall); SUITE_ADD_TEST(suite, test_attrib_remove_self); SUITE_ADD_TEST(suite, test_attrib_nexttype); return suite; diff --git a/tests/score b/tests/score deleted file mode 100644 index 5f282702b..000000000 --- a/tests/score +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/tests/score.alliances b/tests/score.alliances deleted file mode 100644 index 003fe8605..000000000 --- a/tests/score.alliances +++ /dev/null @@ -1,3 +0,0 @@ -# alliance:factions:persons:score -1248287:1:0:0 -1490214:1:0:2105 From abc3caa6d173f321fd802a6bf3fc82e9624b399c Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 00:28:23 +0100 Subject: [PATCH 02/17] optimized storage of attribs. save the hash instead of the name and save a few bytes. --- src/kernel/connection.c | 2 +- src/kernel/group.c | 4 +- src/kernel/save.c | 46 ++++++++----- src/kernel/save.h | 3 + src/kernel/save.test.c | 4 +- src/kernel/version.h | 3 +- src/util/attrib.c | 143 ++++++++++++++++++++++++++-------------- src/util/attrib.h | 10 +-- 8 files changed, 140 insertions(+), 75 deletions(-) diff --git a/src/kernel/connection.c b/src/kernel/connection.c index dbf29bff9..257a78af3 100644 --- a/src/kernel/connection.c +++ b/src/kernel/connection.c @@ -659,7 +659,7 @@ int read_borders(struct storage *store) type->read(b, store); if (global.data_version < NOBORDERATTRIBS_VERSION) { attrib *a = NULL; - int result = a_read(store, &a, b); + int result = read_attribs(store, &a, b); if (border_convert_cb) { border_convert_cb(b, a); } diff --git a/src/kernel/group.c b/src/kernel/group.c index 35f7e93a7..7d57511d1 100755 --- a/src/kernel/group.c +++ b/src/kernel/group.c @@ -209,7 +209,7 @@ void write_groups(struct storage *store, const faction * f) } } WRITE_INT(store, 0); - a_write(store, g->attribs, g); + write_attribs(store, g->attribs, g); WRITE_SECTION(store); } WRITE_INT(store, 0); @@ -241,6 +241,6 @@ void read_groups(struct storage *store, faction * f) if (!a->faction) ur_add(fid, &a->faction, resolve_faction); } - a_read(store, &g->attribs, g); + read_attribs(store, &g->attribs, g); } } diff --git a/src/kernel/save.c b/src/kernel/save.c index 7e5112443..c5239d2a5 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -577,6 +577,23 @@ const struct locale *lang) WRITE_STR(data->store, obuf); } +int read_attribs(storage *store, attrib **alist, void *owner) { +#if RELEASE_VERSION < ATHASH_VERSION + return a_read_orig(store, alist, owner); +#else + return a_read(store, alist, owner); +#endif +} + +void write_attribs(storage *store, attrib *alist, const void *owner) +{ +#if RELEASE_VERSION < ATHASH_VERSION + a_write_orig(store, alist, owner); +#else + a_write(store, alist, owner); +#endif +} + unit *read_unit(struct gamedata *data) { unit *u; @@ -748,8 +765,7 @@ unit *read_unit(struct gamedata *data) log_error("Einheit %s hat %u Personen, und %u Trefferpunkte\n", itoa36(u->no), u->number, u->hp); u->hp = u->number; } - - a_read(data->store, &u->attribs, u); + read_attribs(data->store, &u->attribs, u); return u; } @@ -823,7 +839,7 @@ void write_unit(struct gamedata *data, const unit * u) } WRITE_INT(data->store, u->hp); WRITE_SECTION(data->store); - a_write(data->store, u->attribs, u); + write_attribs(data->store, u->attribs, u); WRITE_SECTION(data->store); } @@ -982,7 +998,7 @@ static region *readregion(struct gamedata *data, int x, int y) read_owner(data, &r->land->ownership); } } - a_read(data->store, &r->attribs, r); + read_attribs(data->store, &r->attribs, r); return r; } @@ -1046,7 +1062,7 @@ void writeregion(struct gamedata *data, const region * r) WRITE_SECTION(data->store); #endif } - a_write(data->store, r->attribs, r); + write_attribs(data->store, r->attribs, r); WRITE_SECTION(data->store); } @@ -1253,7 +1269,7 @@ faction *readfaction(struct gamedata * data) } } - a_read(data->store, &f->attribs, f); + read_attribs(data->store, &f->attribs, f); read_items(data->store, &f->items); for (;;) { READ_TOK(data->store, name, sizeof(name)); @@ -1338,7 +1354,7 @@ void writefaction(struct gamedata *data, const faction * f) WRITE_INT(data->store, f->magiegebiet); WRITE_INT(data->store, f->flags & FFL_SAVEMASK); - a_write(data->store, f->attribs, f); + write_attribs(data->store, f->attribs, f); WRITE_SECTION(data->store); write_items(data->store, f->items); WRITE_SECTION(data->store); @@ -1451,7 +1467,7 @@ int readgame(const char *filename, bool backup) else { READ_STR(&store, NULL, 0); } - a_read(&store, &global.attribs, NULL); + read_attribs(&store, &global.attribs, NULL); READ_INT(&store, &turn); global.data_turn = turn; log_debug(" - reading turn %d\n", turn); @@ -1512,7 +1528,7 @@ int readgame(const char *filename, bool backup) fno = read_faction_reference(&store); } } - a_read(&store, &pl->attribs, pl); + read_attribs(&store, &pl->attribs, pl); if (pl->id != 1094969858) { // Regatta addlist(&planes, pl); } @@ -1580,7 +1596,7 @@ int readgame(const char *filename, bool backup) READ_STR(&store, name, sizeof(name)); b->type = bt_find(name); b->region = r; - a_read(&store, &b->attribs, b); + read_attribs(&store, &b->attribs, b); if (b->type == bt_lighthouse) { r->flags |= RF_LIGHTHOUSE; } @@ -1627,7 +1643,7 @@ int readgame(const char *filename, bool backup) if (sh->type->flags & SFL_NOCOAST) { sh->coast = NODIRECTION; } - a_read(&store, &sh->attribs, sh); + read_attribs(&store, &sh->attribs, sh); } *shp = 0; @@ -1777,7 +1793,7 @@ int writegame(const char *filename) WRITE_INT(&store, game_id()); WRITE_SECTION(&store); - a_write(&store, global.attribs, NULL); + write_attribs(&store, global.attribs, NULL); WRITE_SECTION(&store); WRITE_INT(&store, turn); @@ -1807,7 +1823,7 @@ int writegame(const char *filename) w = w->next; } write_faction_reference(NULL, &store); /* mark the end of the list */ - a_write(&store, pl->attribs, pl); + write_attribs(&store, pl->attribs, pl); WRITE_SECTION(&store); } @@ -1852,7 +1868,7 @@ int writegame(const char *filename) WRITE_INT(&store, b->size); WRITE_TOK(&store, b->type->_name); WRITE_SECTION(&store); - a_write(&store, b->attribs, b); + write_attribs(&store, b->attribs, b); WRITE_SECTION(&store); } @@ -1870,7 +1886,7 @@ int writegame(const char *filename) assert((sh->type->flags & SFL_NOCOAST) == 0 || sh->coast == NODIRECTION); WRITE_INT(&store, sh->coast); WRITE_SECTION(&store); - a_write(&store, sh->attribs, sh); + write_attribs(&store, sh->attribs, sh); WRITE_SECTION(&store); } diff --git a/src/kernel/save.h b/src/kernel/save.h index ca364fc9c..10e872955 100644 --- a/src/kernel/save.h +++ b/src/kernel/save.h @@ -58,6 +58,9 @@ extern "C" { void read_spellbook(struct spellbook **bookp, struct storage *store, int(*get_level)(const struct spell * sp, void *), void * cbdata); void write_spellbook(const struct spellbook *book, struct storage *store); + void write_attribs(struct storage *store, struct attrib *alist, const void *owner); + int read_attribs(struct storage *store, struct attrib **alist, void *owner); + void write_unit(struct gamedata *data, const struct unit *u); struct unit *read_unit(struct gamedata *data); diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c index 1b4028694..c2558c876 100644 --- a/src/kernel/save.test.c +++ b/src/kernel/save.test.c @@ -72,14 +72,14 @@ static void test_readwrite_attrib(CuTest *tc) { CuAssertPtrNotNull(tc, data); add_key(&a, 41); add_key(&a, 42); - a_write(data->store, a, NULL); + write_attribs(data->store, a, NULL); gamedata_close(data); a_removeall(&a, NULL); CuAssertPtrEquals(tc, 0, a); data = gamedata_open(path, "rb"); CuAssertPtrNotNull(tc, data); - a_read(data->store, &a, NULL); + read_attribs(data->store, &a, NULL); gamedata_close(data); CuAssertPtrNotNull(tc, find_key(a, 41)); CuAssertPtrNotNull(tc, find_key(a, 42)); diff --git a/src/kernel/version.h b/src/kernel/version.h index c18957530..1dae63f7f 100644 --- a/src/kernel/version.h +++ b/src/kernel/version.h @@ -34,7 +34,8 @@ #define OWNER_3_VERSION 349 /* regions store last owner, not last alliance */ #define ATTRIBOWNER_VERSION 350 /* all attrib_type functions know who owns the attribute */ #define CRYPT_VERSION 351 /* passwords are encrypted */ -#define RELEASE_VERSION CRYPT_VERSION /* current datafile */ +#define ATHASH_VERSION 352 /* attribute-type hash, not name */ +#define RELEASE_VERSION ATHASH_VERSION /* current datafile */ #define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */ #define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */ diff --git a/src/util/attrib.c b/src/util/attrib.c index 837f9aff4..af93e5ad6 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -276,15 +276,79 @@ int a_age(attrib ** p, void *owner) static critbit_tree cb_deprecated = { 0 }; + +typedef struct deprecated_s { + unsigned int hash; + int(*reader)(attrib *, void *, struct storage *); +} deprecated_t; + void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct storage *)) { - char buffer[64]; - size_t len = strlen(name); - len = cb_new_kv(name, len, &reader, sizeof(reader), buffer); - cb_insert(&cb_deprecated, buffer, len); + deprecated_t value; + + value.hash = __at_hashkey(name); + value.reader = reader; + cb_insert(&cb_deprecated, &value, sizeof(value)); } -int a_read(struct storage *store, attrib ** attribs, void *owner) +static int a_read_i(struct storage *store, attrib ** attribs, void *owner, unsigned int key) { + int retval = AT_READ_FAIL; + int(*reader)(attrib *, void *, struct storage *) = 0; + attrib_type *at = at_find(key); + attrib * na = 0; + + if (at) { + reader = at->read; + na = a_new(at); + } + else { + void *match; + if (cb_find_prefix(&cb_deprecated, &key, sizeof(key), &match, 1, 0)>0) { + deprecated_t *value = (deprecated_t *)match; + reader = value->reader; + } + else { + log_error("unknown attribute hash: %u\n", key); + assert(at || !"attribute not registered"); + } + } + if (reader) { + retval = reader(na, owner, store); + if (na) { + switch (retval) { + case AT_READ_OK: + a_add(attribs, na); + break; + case AT_READ_FAIL: + a_free(na); + break; + default: + assert(!"invalid return value"); + break; + } + } + } + else { + assert(!"error: no registered callback can read attribute"); + } + return retval; +} + +int a_read(struct storage *store, attrib ** attribs, void *owner) { + int key, retval = AT_READ_OK; + char zText[128]; + + zText[0] = 0; + key = -1; + READ_INT(store, &key); + while (key > 0 && retval == AT_READ_OK) { + retval = a_read_i(store, attribs, owner, key); + READ_INT(store, &key); + } + return retval; +} + +int a_read_orig(struct storage *store, attrib ** attribs, void *owner) { int key, retval = AT_READ_OK; char zText[128]; @@ -292,52 +356,14 @@ int a_read(struct storage *store, attrib ** attribs, void *owner) zText[0] = 0; key = -1; READ_TOK(store, zText, sizeof(zText)); - if (strcmp(zText, "end") == 0) + if (strcmp(zText, "end") == 0) { return retval; - else + } + else { key = __at_hashkey(zText); - - while (key != -1) { - int(*reader)(attrib *, void *, struct storage *) = 0; - attrib_type *at = at_find(key); - attrib * na = 0; - - if (at) { - reader = at->read; - na = a_new(at); - } - else { - void * kv = 0; - cb_find_prefix(&cb_deprecated, zText, strlen(zText) + 1, &kv, 1, 0); - if (kv) { - cb_get_kv(kv, &reader, sizeof(reader)); - } - else { - fprintf(stderr, "attribute hash: %d (%s)\n", key, zText); - assert(at || !"attribute not registered"); - } - } - if (reader) { - int i = reader(na, owner, store); - if (na) { - switch (i) { - case AT_READ_OK: - a_add(attribs, na); - break; - case AT_READ_FAIL: - retval = AT_READ_FAIL; - a_free(na); - break; - default: - assert(!"invalid return value"); - break; - } - } - } - else { - assert(!"error: no registered callback can read attribute"); - } - + } + while (key > 0) { + retval = a_read_i(store, attribs, owner, key); READ_TOK(store, zText, sizeof(zText)); if (!strcmp(zText, "end")) break; @@ -346,7 +372,24 @@ int a_read(struct storage *store, attrib ** attribs, void *owner) return retval; } -void a_write(struct storage *store, const attrib * attribs, const void *owner) +void a_write(struct storage *store, const attrib * attribs, const void *owner) { + const attrib *na = attribs; + + while (na) { + if (na->type->write) { + assert(na->type->hashkey || !"attribute not registered"); + WRITE_INT(store, na->type->hashkey); + na->type->write(na, owner, store); + na = na->next; + } + else { + na = na->nexttype; + } + } + WRITE_INT(store, 0); +} + +void a_write_orig(struct storage *store, const attrib * attribs, const void *owner) { const attrib *na = attribs; diff --git a/src/util/attrib.h b/src/util/attrib.h index 14adeffb8..705d127d3 100644 --- a/src/util/attrib.h +++ b/src/util/attrib.h @@ -72,11 +72,13 @@ extern "C" { extern int a_remove(attrib ** pa, attrib * at); extern void a_removeall(attrib ** a, const attrib_type * at); extern attrib *a_new(const attrib_type * at); + int a_age(attrib ** attribs, void *owner); - extern int a_age(attrib ** attribs, void *owner); - extern int a_read(struct storage *store, attrib ** attribs, void *owner); - extern void a_write(struct storage *store, const attrib * attribs, - const void *owner); + int a_read_orig(struct storage *store, attrib ** attribs, void *owner); + void a_write_orig(struct storage *store, const attrib * attribs, const void *owner); + + int a_read(struct storage *store, attrib ** attribs, void *owner); + void a_write(struct storage *store, const attrib * attribs, const void *owner); void free_attribs(void); From 018911187671ee4462cfb96b4cf07e4823a8adf7 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 06:43:19 +0100 Subject: [PATCH 03/17] update key attribute api prepare to replace at_key. do not return the internals. add basic test. --- src/attributes/CMakeLists.txt | 1 + src/attributes/iceberg.c | 1 + src/attributes/key.c | 106 ++++++++++++++++++++++++++---- src/attributes/key.h | 6 +- src/attributes/key.test.c | 20 ++++++ src/attributes/matmod.c | 1 + src/attributes/orcification.c | 2 +- src/attributes/otherfaction.c | 2 +- src/attributes/raceprefix.c | 2 +- src/attributes/reduceproduction.c | 1 + src/attributes/targetregion.c | 1 + src/bind_region.c | 15 ++--- src/bind_unit.c | 12 ++-- src/bindings.c | 14 ++-- src/chaos.c | 1 + src/guard.c | 1 + src/kernel/alliance.c | 4 +- src/kernel/ally.c | 1 + src/kernel/building.c | 2 +- src/kernel/curse.c | 1 + src/kernel/faction.c | 1 + src/kernel/group.c | 2 +- src/kernel/item.c | 4 +- src/kernel/region.c | 4 ++ src/kernel/save.test.c | 8 +-- src/kernel/skills.c | 1 + src/laws.c | 3 +- src/magic.c | 5 ++ src/market.c | 2 +- src/spells/alp.c | 1 + src/spells/borders.c | 1 + src/study.c | 2 +- src/test_eressea.c | 1 + src/util/attrib.c | 27 ++++++-- src/util/attrib.h | 2 + src/wormhole.c | 1 + 36 files changed, 200 insertions(+), 59 deletions(-) create mode 100644 src/attributes/key.test.c diff --git a/src/attributes/CMakeLists.txt b/src/attributes/CMakeLists.txt index 954f55443..1419f9e9a 100644 --- a/src/attributes/CMakeLists.txt +++ b/src/attributes/CMakeLists.txt @@ -1,6 +1,7 @@ PROJECT(attributes C) SET(_TEST_FILES stealth.test.c +key.test.c otherfaction.test.c ) diff --git a/src/attributes/iceberg.c b/src/attributes/iceberg.c index 06c7c23cf..661a2ec80 100644 --- a/src/attributes/iceberg.c +++ b/src/attributes/iceberg.c @@ -30,6 +30,7 @@ attrib_type at_iceberg = { NULL, a_writeint, a_readint, + NULL, ATF_UNIQUE }; diff --git a/src/attributes/key.c b/src/attributes/key.c index c4873b228..8e079988f 100644 --- a/src/attributes/key.c +++ b/src/attributes/key.c @@ -22,6 +22,68 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include + +#include +#include + +static void a_writekeys(const attrib *a, const void *o, storage *store) { + int i, *keys = (int *)a->data.v; + for (i = 0; i != keys[0]; ++i) { + WRITE_INT(store, keys[i]); + } +} + +static int a_readkeys(attrib * a, void *owner, struct storage *store) { + int i, *p = 0; + READ_INT(store, &i); + assert(i < 4096 && i>0); + a->data.v = p = malloc(sizeof(int)*(i + 1)); + *p++ = i; + while (--i) { + READ_INT(store, p++); + } + return AT_READ_OK; +} + +static int a_readkey(attrib *a, void *owner, struct storage *store) { + int res = a_readint(a, owner, store); + return (res != AT_READ_FAIL) ? AT_READ_DEPR : res; +} + +attrib_type at_keys = { + "keys", + NULL, + NULL, + NULL, + a_writekeys, + a_readkeys, + NULL +}; + +void a_upgradekeys(attrib **alist, attrib *abegin) { + int n = 0, *keys = 0; + int i = 0, val[4]; + attrib *a, *ak = a_find(*alist, &at_keys); + if (!ak) { + ak = a_add(alist, a_new(&at_keys)); + keys = (int *)ak->data.v; + n = keys ? keys[0] : 0; + } + for (a = abegin; a && a->type == abegin->type; a = a->next) { + val[i++] = a->data.i; + if (i == 4) { + keys = realloc(keys, sizeof(int) * (n + i + 1)); + memcpy(keys + n + 1, val, sizeof(int)*i); + n += i; + } + } + if (i > 0) { + keys = realloc(keys, sizeof(int) * (n + i + 1)); + memcpy(keys + n + 1, val, sizeof(int)*i); + } + keys[0] = n + i; +} attrib_type at_key = { "key", @@ -29,29 +91,51 @@ attrib_type at_key = { NULL, NULL, a_writeint, - a_readint, + a_readkey, + a_upgradekeys }; -attrib *add_key(attrib ** alist, int key) -{ - attrib *a = find_key(*alist, key); - if (a == NULL) - a = a_add(alist, make_key(key)); - return a; -} - -attrib *make_key(int key) +static attrib *make_key(int key) { attrib *a = a_new(&at_key); + assert(key != 0); a->data.i = key; return a; } -attrib *find_key(attrib * alist, int key) +static attrib *find_key(attrib * alist, int key) { attrib *a = a_find(alist, &at_key); + assert(key != 0); while (a && a->type == &at_key && a->data.i != key) { a = a->next; } return (a && a->type == &at_key) ? a : NULL; } + +void key_set(attrib ** alist, int key) +{ + attrib *a; + assert(key != 0); + a = find_key(*alist, key); + if (!a) { + a = a_add(alist, make_key(key)); + } +} + +void key_unset(attrib ** alist, int key) +{ + attrib *a; + assert(key != 0); + a = find_key(*alist, key); + if (a) { + a_remove(alist, a); + } +} + +bool key_get(attrib *alist, int key) { + attrib *a; + assert(key != 0); + a = find_key(alist, key); + return a != NULL; +} diff --git a/src/attributes/key.h b/src/attributes/key.h index 92f409595..31a606be7 100644 --- a/src/attributes/key.h +++ b/src/attributes/key.h @@ -24,9 +24,9 @@ extern "C" { extern struct attrib_type at_key; - struct attrib *make_key(int key); - struct attrib *find_key(struct attrib *alist, int key); - struct attrib *add_key(struct attrib **alist, int key); + void key_set(struct attrib **alist, int key); + void key_unset(struct attrib **alist, int key); + bool key_get(struct attrib *alist, int key); #ifdef __cplusplus } diff --git a/src/attributes/key.test.c b/src/attributes/key.test.c new file mode 100644 index 000000000..3ec321581 --- /dev/null +++ b/src/attributes/key.test.c @@ -0,0 +1,20 @@ +#include +#include "key.h" + +#include +#include + +static void test_get_set_keys(CuTest *tc) { + attrib *a = 0; + key_set(&a, 42); + CuAssertTrue(tc, key_get(a, 42)); + key_unset(&a, 42); + CuAssertTrue(tc, !key_get(a, 42)); +} + +CuSuite *get_key_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_get_set_keys); + return suite; +} diff --git a/src/attributes/matmod.c b/src/attributes/matmod.c index 74d39448f..f885de99b 100644 --- a/src/attributes/matmod.c +++ b/src/attributes/matmod.c @@ -28,6 +28,7 @@ attrib_type at_matmod = { NULL, NULL, NULL, + NULL, ATF_PRESERVE }; diff --git a/src/attributes/orcification.c b/src/attributes/orcification.c index 4e4877116..dfb8f32f4 100644 --- a/src/attributes/orcification.c +++ b/src/attributes/orcification.c @@ -28,7 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ attrib_type at_orcification = { - "orcification", NULL, NULL, NULL, a_writeint, a_readint, ATF_UNIQUE + "orcification", NULL, NULL, NULL, a_writeint, a_readint, NULL, ATF_UNIQUE }; attrib *make_orcification(int orcification) diff --git a/src/attributes/otherfaction.c b/src/attributes/otherfaction.c index 8e4e7a6af..436714c1b 100644 --- a/src/attributes/otherfaction.c +++ b/src/attributes/otherfaction.c @@ -53,7 +53,7 @@ int read_of(struct attrib *a, void *owner, struct storage *store) } attrib_type at_otherfaction = { - "otherfaction", NULL, NULL, NULL, write_of, read_of, ATF_UNIQUE + "otherfaction", NULL, NULL, NULL, write_of, read_of, NULL, ATF_UNIQUE }; struct faction *get_otherfaction(const struct attrib *a) diff --git a/src/attributes/raceprefix.c b/src/attributes/raceprefix.c index 6a150a8e6..308c75292 100644 --- a/src/attributes/raceprefix.c +++ b/src/attributes/raceprefix.c @@ -28,7 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include attrib_type at_raceprefix = { - "raceprefix", NULL, a_finalizestring, NULL, a_writestring, a_readstring, + "raceprefix", NULL, a_finalizestring, NULL, a_writestring, a_readstring, NULL, ATF_UNIQUE }; diff --git a/src/attributes/reduceproduction.c b/src/attributes/reduceproduction.c index 58436bc8e..3f31938d3 100644 --- a/src/attributes/reduceproduction.c +++ b/src/attributes/reduceproduction.c @@ -49,6 +49,7 @@ attrib_type at_reduceproduction = { age_reduceproduction, a_writeshorts, a_readshorts, + NULL, ATF_UNIQUE }; diff --git a/src/attributes/targetregion.c b/src/attributes/targetregion.c index d11af192c..42f78465d 100644 --- a/src/attributes/targetregion.c +++ b/src/attributes/targetregion.c @@ -52,6 +52,7 @@ attrib_type at_targetregion = { NULL, write_targetregion, read_targetregion, + NULL, ATF_UNIQUE }; diff --git a/src/bind_region.c b/src/bind_region.c index a944b7c30..519502162 100644 --- a/src/bind_region.c +++ b/src/bind_region.c @@ -542,11 +542,9 @@ static int tolua_region_getkey(lua_State * L) { region *self = (region *)tolua_tousertype(L, 1, 0); const char *name = tolua_tostring(L, 2, 0); - int flag = atoi36(name); - attrib *a = find_key(self->attribs, flag); - lua_pushboolean(L, a != NULL); + lua_pushboolean(L, key_get(self->attribs, flag)); return 1; } @@ -555,14 +553,13 @@ static int tolua_region_setkey(lua_State * L) region *self = (region *)tolua_tousertype(L, 1, 0); const char *name = tolua_tostring(L, 2, 0); int value = tolua_toboolean(L, 3, 0); - int flag = atoi36(name); - attrib *a = find_key(self->attribs, flag); - if (a == NULL && value) { - add_key(&self->attribs, flag); + + if (value) { + key_set(&self->attribs, flag); } - else if (a != NULL && !value) { - a_remove(&self->attribs, a); + else { + key_unset(&self->attribs, flag); } return 0; } diff --git a/src/bind_unit.c b/src/bind_unit.c index ed8eb74c3..f7b5278dd 100755 --- a/src/bind_unit.c +++ b/src/bind_unit.c @@ -807,8 +807,7 @@ static int tolua_unit_get_flag(lua_State * L) unit *self = (unit *)tolua_tousertype(L, 1, 0); const char *name = tolua_tostring(L, 2, 0); int flag = atoi36(name); - attrib *a = find_key(self->attribs, flag); - lua_pushboolean(L, (a != NULL)); + lua_pushboolean(L, key_get(self->attribs, flag)); return 1; } @@ -818,12 +817,11 @@ static int tolua_unit_set_flag(lua_State * L) const char *name = tolua_tostring(L, 2, 0); int value = (int)tolua_tonumber(L, 3, 0); int flag = atoi36(name); - attrib *a = find_key(self->attribs, flag); - if (a == NULL && value) { - add_key(&self->attribs, flag); + if (value) { + key_set(&self->attribs, flag); } - else if (a != NULL && !value) { - a_remove(&self->attribs, a); + else { + key_unset(&self->attribs, flag); } return 0; } diff --git a/src/bindings.c b/src/bindings.c index 938b75be8..5e05c8b53 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -186,8 +186,8 @@ static int tolua_getkey(lua_State * L) { const char *name = tolua_tostring(L, 1, 0); int flag = atoi36(name); - attrib *a = find_key(global.attribs, flag); - lua_pushboolean(L, a != NULL); + + lua_pushboolean(L, key_get(global.attribs, flag)); return 1; } @@ -209,12 +209,12 @@ static int tolua_setkey(lua_State * L) const char *name = tolua_tostring(L, 1, 0); int value = tolua_toboolean(L, 2, 0); int flag = atoi36(name); - attrib *a = find_key(global.attribs, flag); - if (a == NULL && value) { - add_key(&global.attribs, flag); + bool isset = key_get(global.attribs, flag); + if (value) { + key_set(&global.attribs, flag); } - else if (a != NULL && !value) { - a_remove(&global.attribs, a); + else { + key_unset(&global.attribs, flag); } return 0; } diff --git a/src/chaos.c b/src/chaos.c index a84b26dfc..afa787b29 100644 --- a/src/chaos.c +++ b/src/chaos.c @@ -50,6 +50,7 @@ attrib_type at_chaoscount = { DEFAULT_AGE, a_writeint, a_readint, + NULL, ATF_UNIQUE }; diff --git a/src/guard.c b/src/guard.c index 9e3585a41..f779e7e2e 100644 --- a/src/guard.c +++ b/src/guard.c @@ -38,6 +38,7 @@ attrib_type at_guard = { DEFAULT_AGE, a_writeint, a_readint, + NULL, ATF_UNIQUE }; diff --git a/src/kernel/alliance.c b/src/kernel/alliance.c index 1de816731..56754c491 100644 --- a/src/kernel/alliance.c +++ b/src/kernel/alliance.c @@ -480,7 +480,7 @@ int victorycondition(const alliance * al, const char *name) for (qi = 0; flist; ql_advance(&flist, &qi, 1)) { faction *f = (faction *)ql_get(flist, qi); - if (find_key(f->attribs, atoi36("phnx"))) { + if (key_get(f->attribs, atoi36("phnx"))) { return 1; } } @@ -509,7 +509,7 @@ int victorycondition(const alliance * al, const char *name) for (qi = 0; flist; ql_advance(&flist, &qi, 1)) { faction *f = (faction *)ql_get(flist, qi); - if (find_key(f->attribs, atoi36("pyra"))) { + if (key_get(f->attribs, atoi36("pyra"))) { return 1; } } diff --git a/src/kernel/ally.c b/src/kernel/ally.c index 9a74a2320..3152ab4b7 100644 --- a/src/kernel/ally.c +++ b/src/kernel/ally.c @@ -121,6 +121,7 @@ attrib_type at_npcfaction = { NULL, a_writeint, a_readint, + NULL, ATF_UNIQUE }; diff --git a/src/kernel/building.c b/src/kernel/building.c index 4384b7f80..609b000c8 100644 --- a/src/kernel/building.c +++ b/src/kernel/building.c @@ -141,7 +141,7 @@ int buildingcapacity(const building * b) } attrib_type at_building_generic_type = { - "building_generic_type", NULL, NULL, NULL, a_writestring, a_readstring, + "building_generic_type", NULL, NULL, NULL, a_writestring, a_readstring, NULL, ATF_UNIQUE }; diff --git a/src/kernel/curse.c b/src/kernel/curse.c index 3cb39ebab..8b7a4b51a 100644 --- a/src/kernel/curse.c +++ b/src/kernel/curse.c @@ -285,6 +285,7 @@ attrib_type at_curse = { curse_age, curse_write, curse_read, + NULL, ATF_CURSE }; diff --git a/src/kernel/faction.c b/src/kernel/faction.c index aa04470f0..936c38df2 100755 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -798,6 +798,7 @@ attrib_type at_maxmagicians = { NULL, a_writeint, a_readint, + NULL, ATF_UNIQUE }; diff --git a/src/kernel/group.c b/src/kernel/group.c index 7d57511d1..47adcba98 100755 --- a/src/kernel/group.c +++ b/src/kernel/group.c @@ -119,7 +119,7 @@ write_group(const attrib * a, const void *owner, struct storage *store) attrib_type at_group = { /* attribute for units assigned to a group */ "grp", DEFAULT_INIT, - DEFAULT_FINALIZE, DEFAULT_AGE, write_group, read_group, ATF_UNIQUE }; + DEFAULT_FINALIZE, DEFAULT_AGE, write_group, read_group, NULL, ATF_UNIQUE }; void free_group(group * g) { diff --git a/src/kernel/item.c b/src/kernel/item.c index 190e461f2..178526933 100644 --- a/src/kernel/item.c +++ b/src/kernel/item.c @@ -926,14 +926,14 @@ struct order *ord) "unit item region command", user, itype->rtype, user->region, ord)); return -1; } - if (!is_mage(user) || find_key(f->attribs, atoi36("mbst")) != NULL) { + if (!is_mage(user) || key_get(f->attribs, atoi36("mbst"))) { cmistake(user, user->thisorder, 214, MSG_EVENT); return -1; } use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK, user->number); - a_add(&f->attribs, make_key(atoi36("mbst"))); + key_set(&f->attribs, atoi36("mbst")); set_level(user, SK_MAGIC, 3); ADDMSG(&user->faction->msgs, msg_message("use_item", diff --git a/src/kernel/region.c b/src/kernel/region.c index 80a87552e..04e0c46b3 100644 --- a/src/kernel/region.c +++ b/src/kernel/region.c @@ -483,6 +483,7 @@ attrib_type at_horseluck = { DEFAULT_AGE, NO_WRITE, NO_READ, + NULL, ATF_UNIQUE }; @@ -496,6 +497,7 @@ attrib_type at_peasantluck = { DEFAULT_AGE, NO_WRITE, NO_READ, + NULL, ATF_UNIQUE }; @@ -509,6 +511,7 @@ attrib_type at_deathcount = { DEFAULT_AGE, a_writeint, a_readint, + NULL, ATF_UNIQUE }; @@ -522,6 +525,7 @@ attrib_type at_woodcount = { DEFAULT_AGE, NO_WRITE, a_readint, + NULL, ATF_UNIQUE }; diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c index c2558c876..e1b79b3b9 100644 --- a/src/kernel/save.test.c +++ b/src/kernel/save.test.c @@ -70,8 +70,8 @@ static void test_readwrite_attrib(CuTest *tc) { test_cleanup(); data = gamedata_open(path, "wb"); CuAssertPtrNotNull(tc, data); - add_key(&a, 41); - add_key(&a, 42); + key_set(&a, 41); + key_set(&a, 42); write_attribs(data->store, a, NULL); gamedata_close(data); a_removeall(&a, NULL); @@ -81,8 +81,8 @@ static void test_readwrite_attrib(CuTest *tc) { CuAssertPtrNotNull(tc, data); read_attribs(data->store, &a, NULL); gamedata_close(data); - CuAssertPtrNotNull(tc, find_key(a, 41)); - CuAssertPtrNotNull(tc, find_key(a, 42)); + CuAssertTrue(tc, key_get(a, 41)); + CuAssertTrue(tc, key_get(a, 42)); a_removeall(&a, NULL); CuAssertIntEquals(tc, 0, remove(path)); diff --git a/src/kernel/skills.c b/src/kernel/skills.c index 7ba325dcc..cb0dcddea 100644 --- a/src/kernel/skills.c +++ b/src/kernel/skills.c @@ -59,6 +59,7 @@ attrib_type at_skillmod = { NULL, NULL, /* can't write function pointers */ NULL, /* can't read function pointers */ + NULL, ATF_PRESERVE }; diff --git a/src/laws.c b/src/laws.c index e72472aec..22c0fa1a8 100755 --- a/src/laws.c +++ b/src/laws.c @@ -486,6 +486,7 @@ attrib_type at_germs = { DEFAULT_AGE, a_writeshorts, a_readshorts, + NULL, ATF_UNIQUE }; @@ -2749,7 +2750,7 @@ void sinkships(struct region * r) static attrib_type at_number = { "faction_renum", - NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, ATF_UNIQUE }; diff --git a/src/magic.c b/src/magic.c index 488c9abe3..b8b5005da 100644 --- a/src/magic.c +++ b/src/magic.c @@ -335,6 +335,7 @@ attrib_type at_mage = { NULL, write_mage, read_mage, + NULL, ATF_UNIQUE }; @@ -2416,6 +2417,7 @@ attrib_type at_familiarmage = { age_unit, a_write_unit, read_magician, + NULL, ATF_UNIQUE }; @@ -2426,6 +2428,7 @@ attrib_type at_familiar = { age_unit, a_write_unit, read_familiar, + NULL, ATF_UNIQUE }; @@ -2436,6 +2439,7 @@ attrib_type at_clonemage = { age_unit, a_write_unit, read_magician, + NULL, ATF_UNIQUE }; @@ -2446,6 +2450,7 @@ attrib_type at_clone = { age_unit, a_write_unit, read_clone, + NULL, ATF_UNIQUE }; diff --git a/src/market.c b/src/market.c index 3d96c30e4..efcaf0d98 100644 --- a/src/market.c +++ b/src/market.c @@ -63,7 +63,7 @@ static void free_market(attrib * a) attrib_type at_market = { "script", NULL, free_market, NULL, - NULL, NULL, ATF_UNIQUE + NULL, NULL, NULL, ATF_UNIQUE }; static int rc_luxury_trade(const struct race *rc) diff --git a/src/spells/alp.c b/src/spells/alp.c index d443aa063..e44b52ffa 100644 --- a/src/spells/alp.c +++ b/src/spells/alp.c @@ -96,6 +96,7 @@ static attrib_type at_alp = { alp_verify, alp_write, alp_read, + NULL, ATF_UNIQUE }; diff --git a/src/spells/borders.c b/src/spells/borders.c index c197b93c2..3f107a389 100644 --- a/src/spells/borders.c +++ b/src/spells/borders.c @@ -112,6 +112,7 @@ static attrib_type at_cursewall = { curse_age, cw_write, cw_read, + NULL, ATF_CURSE }; diff --git a/src/study.c b/src/study.c index acce1b6d6..8bfc4f491 100644 --- a/src/study.c +++ b/src/study.c @@ -157,7 +157,7 @@ static void done_learning(struct attrib *a) const attrib_type at_learning = { "learning", - init_learning, done_learning, NULL, NULL, NULL, + init_learning, done_learning, NULL, NULL, NULL, NULL, ATF_UNIQUE }; diff --git a/src/test_eressea.c b/src/test_eressea.c index 4d69ad954..34a8ba141 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -118,6 +118,7 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(monsters); ADD_SUITE(move); ADD_SUITE(piracy); + ADD_SUITE(key); ADD_SUITE(stealth); ADD_SUITE(otherfaction); ADD_SUITE(upkeep); diff --git a/src/util/attrib.c b/src/util/attrib.c index af93e5ad6..3b6f252d4 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -292,7 +292,7 @@ void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct stora } static int a_read_i(struct storage *store, attrib ** attribs, void *owner, unsigned int key) { - int retval = AT_READ_FAIL; + int retval = AT_READ_OK; int(*reader)(attrib *, void *, struct storage *) = 0; attrib_type *at = at_find(key); attrib * na = 0; @@ -313,11 +313,13 @@ static int a_read_i(struct storage *store, attrib ** attribs, void *owner, unsig } } if (reader) { - retval = reader(na, owner, store); + int ret = reader(na, owner, store); if (na) { - switch (retval) { + switch (ret) { + case AT_READ_DEPR: case AT_READ_OK: a_add(attribs, na); + retval = ret; break; case AT_READ_FAIL: a_free(na); @@ -341,11 +343,24 @@ int a_read(struct storage *store, attrib ** attribs, void *owner) { zText[0] = 0; key = -1; READ_INT(store, &key); - while (key > 0 && retval == AT_READ_OK) { - retval = a_read_i(store, attribs, owner, key); + while (key > 0) { + int ret = a_read_i(store, attribs, owner, key); + if (ret == AT_READ_DEPR) { + retval = AT_READ_DEPR; + } READ_INT(store, &key); } - return retval; + if (retval == AT_READ_DEPR) { + /* handle deprecated attributes */ + attrib *a = *attribs; + while (a) { + if (a->type->upgrade) { + a->type->upgrade(attribs, a); + } + a = a->nexttype; + } + } + return AT_READ_OK; } int a_read_orig(struct storage *store, attrib ** attribs, void *owner) diff --git a/src/util/attrib.h b/src/util/attrib.h index 705d127d3..9f421bfef 100644 --- a/src/util/attrib.h +++ b/src/util/attrib.h @@ -56,6 +56,7 @@ extern "C" { /* age returns 0 if the attribute needs to be removed, !=0 otherwise */ void(*write) (const struct attrib *, const void *owner, struct storage *); int(*read) (struct attrib *, void *owner, struct storage *); /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ + void(*upgrade) (struct attrib **alist, struct attrib *a); unsigned int flags; /* ---- internal data, do not modify: ---- */ struct attrib_type *nexthash; @@ -90,6 +91,7 @@ extern "C" { #define AT_READ_OK 0 #define AT_READ_FAIL -1 +#define AT_READ_DEPR 1 /* a deprecated attribute was read, should run a_upgrade */ #define AT_AGE_REMOVE 0 /* remove the attribute after calling age() */ #define AT_AGE_KEEP 1 /* keep the attribute for another turn */ diff --git a/src/wormhole.c b/src/wormhole.c index 684cb8f4d..794dbc836 100644 --- a/src/wormhole.c +++ b/src/wormhole.c @@ -134,6 +134,7 @@ static attrib_type at_wormhole = { wormhole_age, wormhole_write, wormhole_read, + NULL, ATF_UNIQUE }; From dbcae5484e305ce009be9dbba3739baaff367766 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 06:45:28 +0100 Subject: [PATCH 04/17] update test to check more than one key and cleanup. --- src/attributes/key.test.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/attributes/key.test.c b/src/attributes/key.test.c index 3ec321581..e15c50c3b 100644 --- a/src/attributes/key.test.c +++ b/src/attributes/key.test.c @@ -7,9 +7,16 @@ static void test_get_set_keys(CuTest *tc) { attrib *a = 0; key_set(&a, 42); + key_set(&a, 43); + key_set(&a, 44); CuAssertTrue(tc, key_get(a, 42)); + CuAssertTrue(tc, key_get(a, 43)); + CuAssertTrue(tc, key_get(a, 44)); key_unset(&a, 42); CuAssertTrue(tc, !key_get(a, 42)); + CuAssertTrue(tc, key_get(a, 43)); + CuAssertTrue(tc, key_get(a, 44)); + a_removeall(&a, NULL); } CuSuite *get_key_suite(void) From 5e631fd145cc011337db426c7577e417f22f2318 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 07:03:11 +0100 Subject: [PATCH 05/17] replace at_key with at_keys, some bugs found by tests. --- src/attributes/attributes.c | 1 + src/attributes/key.c | 44 ++++++++++++++++++++++++++++++------- src/attributes/key.h | 1 + 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c index afe899b0f..47e5e4cc0 100644 --- a/src/attributes/attributes.c +++ b/src/attributes/attributes.c @@ -125,6 +125,7 @@ void register_attributes(void) at_register(&at_raceprefix); at_register(&at_iceberg); at_register(&at_key); + at_register(&at_keys); at_register(&at_follow); at_register(&at_targetregion); at_register(&at_orcification); diff --git a/src/attributes/key.c b/src/attributes/key.c index 8e079988f..0db121a48 100644 --- a/src/attributes/key.c +++ b/src/attributes/key.c @@ -29,7 +29,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. static void a_writekeys(const attrib *a, const void *o, storage *store) { int i, *keys = (int *)a->data.v; - for (i = 0; i != keys[0]; ++i) { + for (i = 0; i <= keys[0]; ++i) { WRITE_INT(store, keys[i]); } } @@ -40,7 +40,7 @@ static int a_readkeys(attrib * a, void *owner, struct storage *store) { assert(i < 4096 && i>0); a->data.v = p = malloc(sizeof(int)*(i + 1)); *p++ = i; - while (--i) { + while (i--) { READ_INT(store, p++); } return AT_READ_OK; @@ -115,27 +115,55 @@ static attrib *find_key(attrib * alist, int key) void key_set(attrib ** alist, int key) { + int *keys, n = 1; attrib *a; assert(key != 0); - a = find_key(*alist, key); + a = a_find(*alist, &at_keys); if (!a) { - a = a_add(alist, make_key(key)); + a = a_add(alist, a_new(&at_keys)); } + keys = (int *)a->data.v; + if (keys) { + n = keys[0] + 1; + } + keys = realloc(keys, sizeof(int) *(n + 1)); + // TODO: does insertion sort pay off here? + keys[0] = n; + keys[n] = key; + a->data.v = keys; } void key_unset(attrib ** alist, int key) { attrib *a; assert(key != 0); - a = find_key(*alist, key); + a = a_find(*alist, &at_keys); if (a) { - a_remove(alist, a); + int i, *keys = (int *)a->data.v; + if (keys) { + for (i = 1; i <= keys[0]; ++i) { + if (keys[i] == key) { + keys[i] = keys[keys[0]]; + keys[0]--; + } + } + } } } bool key_get(attrib *alist, int key) { attrib *a; assert(key != 0); - a = find_key(alist, key); - return a != NULL; + a = a_find(alist, &at_keys); + if (a) { + int i, *keys = (int *)a->data.v; + if (keys) { + for (i = 1; i <= keys[0]; ++i) { + if (keys[i] == key) { + return true; + } + } + } + } + return false; } diff --git a/src/attributes/key.h b/src/attributes/key.h index 31a606be7..074780763 100644 --- a/src/attributes/key.h +++ b/src/attributes/key.h @@ -23,6 +23,7 @@ extern "C" { #endif extern struct attrib_type at_key; + extern struct attrib_type at_keys; void key_set(struct attrib **alist, int key); void key_unset(struct attrib **alist, int key); From 1201a6b7399e09d69b4a4a59073eb8db8707b286 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 07:06:07 +0100 Subject: [PATCH 06/17] unused variable, bad version check --- src/bindings.c | 1 - src/kernel/save.c | 11 ++++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bindings.c b/src/bindings.c index 5e05c8b53..99458c08d 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -209,7 +209,6 @@ static int tolua_setkey(lua_State * L) const char *name = tolua_tostring(L, 1, 0); int value = tolua_toboolean(L, 2, 0); int flag = atoi36(name); - bool isset = key_get(global.attribs, flag); if (value) { key_set(&global.attribs, flag); } diff --git a/src/kernel/save.c b/src/kernel/save.c index c5239d2a5..3b7c13705 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -578,11 +578,12 @@ const struct locale *lang) } int read_attribs(storage *store, attrib **alist, void *owner) { -#if RELEASE_VERSION < ATHASH_VERSION - return a_read_orig(store, alist, owner); -#else - return a_read(store, alist, owner); -#endif + if (global.data_version < ATHASH_VERSION) { + return a_read_orig(store, alist, owner); + } + else { + return a_read(store, alist, owner); + } } void write_attribs(storage *store, attrib *alist, const void *owner) From 188aa84011699ba1b8c7ac673e7c99971f4eb852 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 08:41:23 +0100 Subject: [PATCH 07/17] create htpasswd files after the turn. do not overwrite old password file (E2!) --- scripts/run-turn.lua | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/scripts/run-turn.lua b/scripts/run-turn.lua index 60f2b6f63..ebf6acfb7 100644 --- a/scripts/run-turn.lua +++ b/scripts/run-turn.lua @@ -59,11 +59,16 @@ local function write_emails(locales) end end +local function join_path(a, b) + if a then return a .. '/' .. b + return b +end + local function write_addresses() local file local faction - file = io.open(config.basepath .. "/adressen", "w") + file = io.open(join_path(config.basepath, "adressen"), "w") for faction in factions() do -- print(faction.id .. " - " .. faction.locale) file:write(tostring(faction) .. ":" .. faction.email .. ":" .. faction.info .. "\n") @@ -76,7 +81,7 @@ local function write_aliases() local file local faction - file = io.open(config.basepath .. "/aliases", "w") + file = io.open(join_path(config.basepath, "aliases"), "w") for faction in factions() do local unit if faction.email ~= "" then @@ -90,10 +95,21 @@ local function write_aliases() file:close() end +local function write_htpasswd() + local out = io.open(join_path(config.basepath, "htpasswd"), "w") + if out then + for f in factions() do + out:write(itoa36(f.id) .. ":" .. f.password .. "\n") + end + out:close() + end +end + local function write_files(locales) - write_passwords() - write_reports() - write_summary() + write_htpasswd() + -- write_passwords() + write_reports() + write_summary() end local function write_scores() From b6e2fc28df2c4af938fa533ff5d36e885f01c623 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 13:56:15 +0100 Subject: [PATCH 08/17] remove unused functions --- src/attributes/key.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/attributes/key.c b/src/attributes/key.c index 0db121a48..12897d453 100644 --- a/src/attributes/key.c +++ b/src/attributes/key.c @@ -95,24 +95,6 @@ attrib_type at_key = { a_upgradekeys }; -static attrib *make_key(int key) -{ - attrib *a = a_new(&at_key); - assert(key != 0); - a->data.i = key; - return a; -} - -static attrib *find_key(attrib * alist, int key) -{ - attrib *a = a_find(alist, &at_key); - assert(key != 0); - while (a && a->type == &at_key && a->data.i != key) { - a = a->next; - } - return (a && a->type == &at_key) ? a : NULL; -} - void key_set(attrib ** alist, int key) { int *keys, n = 1; From 997d81e34adb32e05f3269cecbf538d6045fa823 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 13:56:57 +0100 Subject: [PATCH 09/17] memcpy is in string.h --- src/attributes/key.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/attributes/key.c b/src/attributes/key.c index 12897d453..ed6386d5f 100644 --- a/src/attributes/key.c +++ b/src/attributes/key.c @@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include #include static void a_writekeys(const attrib *a, const void *o, storage *store) { From 87aa7c887e52ed63066fe927ec2ac4bb4a160326 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 14:08:36 +0100 Subject: [PATCH 10/17] fix build --- src/attributes/key.h | 3 ++- src/kernel/save.test.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/attributes/key.h b/src/attributes/key.h index 074780763..89292db1d 100644 --- a/src/attributes/key.h +++ b/src/attributes/key.h @@ -21,7 +21,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #ifdef __cplusplus extern "C" { #endif - + struct attrib; + struct attrib_type; extern struct attrib_type at_key; extern struct attrib_type at_keys; diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c index e1b79b3b9..42d3074ac 100644 --- a/src/kernel/save.test.c +++ b/src/kernel/save.test.c @@ -68,6 +68,7 @@ static void test_readwrite_attrib(CuTest *tc) { attrib *a = NULL; const char *path = "attrib.dat"; test_cleanup(); + global.data_version = RELEASE_VERSION; // FIXME: hack! data = gamedata_open(path, "wb"); CuAssertPtrNotNull(tc, data); key_set(&a, 41); From 5f2dc192dbf0f111a785886866b94658d781847c Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 16:11:32 +0100 Subject: [PATCH 11/17] disable test because valgrind is complaining --- s/travis-build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/s/travis-build b/s/travis-build index 414a70f80..17c17fb10 100755 --- a/s/travis-build +++ b/s/travis-build @@ -19,4 +19,4 @@ inifile s/runtests cd tests ./write-reports.sh -./run-turn.sh +#./run-turn.sh From 652136ee462658b6552285ca5e01712bd608f5b0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 16:32:01 +0100 Subject: [PATCH 12/17] unused variable, kill --- src/util/attrib.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/util/attrib.c b/src/util/attrib.c index 3b6f252d4..b5ea3ad3c 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -338,9 +338,7 @@ static int a_read_i(struct storage *store, attrib ** attribs, void *owner, unsig int a_read(struct storage *store, attrib ** attribs, void *owner) { int key, retval = AT_READ_OK; - char zText[128]; - zText[0] = 0; key = -1; READ_INT(store, &key); while (key > 0) { From ca6448ec2323f7b8f89fbda963033a730e179545 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 17:18:09 +0100 Subject: [PATCH 13/17] re-enable failing valgrind check with more flags --- s/travis-build | 2 +- tests/run-turn.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/s/travis-build b/s/travis-build index 17c17fb10..414a70f80 100755 --- a/s/travis-build +++ b/s/travis-build @@ -19,4 +19,4 @@ inifile s/runtests cd tests ./write-reports.sh -#./run-turn.sh +./run-turn.sh diff --git a/tests/run-turn.sh b/tests/run-turn.sh index 4055613e5..91c79a8df 100755 --- a/tests/run-turn.sh +++ b/tests/run-turn.sh @@ -34,7 +34,7 @@ VALGRIND=`which valgrind` SERVER=../Debug/eressea/eressea if [ -n "$VALGRIND" ]; then SUPP=../share/ubuntu-12_04.supp -SERVER="$VALGRIND --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" +SERVER="$VALGRIND --track-origins=yes --gen-suppressions=all --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" fi echo "running $SERVER" $SERVER -t 184 ../scripts/run-turn.lua From 50f22d7c2f34333ca4f651b1936886d28e58d73f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 18:20:01 +0100 Subject: [PATCH 14/17] debug output --- tests/run-turn.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run-turn.sh b/tests/run-turn.sh index 91c79a8df..2d19e03f7 100755 --- a/tests/run-turn.sh +++ b/tests/run-turn.sh @@ -19,6 +19,7 @@ expr=$2 expect=$3 count=`grep -cE $expr $file` [ $count -eq $expect ] || quit 1 "expected $expect counts of $expr in $file, got $count" +echo "PASS: $expr is $expect" } ROOT=`pwd` @@ -38,6 +39,7 @@ SERVER="$VALGRIND --track-origins=yes --gen-suppressions=all --suppressions=$SUP fi echo "running $SERVER" $SERVER -t 184 ../scripts/run-turn.lua +echo "integration tests" [ -d reports ] || quit 4 "no reports directory created" CRFILE=185-zvto.cr for file in $NEWFILES reports/$CRFILE ; do From 3ee023cdd679e2346805afbe5916d6c5c001342f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 18:30:42 +0100 Subject: [PATCH 15/17] it was all a lua syntax error. good grief. that should go to stderr! --- scripts/run-turn.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/run-turn.lua b/scripts/run-turn.lua index ebf6acfb7..20a8de05b 100644 --- a/scripts/run-turn.lua +++ b/scripts/run-turn.lua @@ -60,7 +60,7 @@ local function write_emails(locales) end local function join_path(a, b) - if a then return a .. '/' .. b + if a then return a .. '/' .. b end return b end @@ -99,7 +99,9 @@ local function write_htpasswd() local out = io.open(join_path(config.basepath, "htpasswd"), "w") if out then for f in factions() do - out:write(itoa36(f.id) .. ":" .. f.password .. "\n") + if f.password then + out:write(itoa36(f.id) .. ":" .. f.password .. "\n") + end end out:close() end From 91ca77bc4815ce446a72732387d8754723993b7f Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 18:34:36 +0100 Subject: [PATCH 16/17] passwd is no longer written --- tests/run-turn.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-turn.sh b/tests/run-turn.sh index 2d19e03f7..622ab68ab 100755 --- a/tests/run-turn.sh +++ b/tests/run-turn.sh @@ -1,4 +1,4 @@ -NEWFILES="data/185.dat datum parteien parteien.full passwd score turn" +NEWFILES="data/185.dat datum parteien parteien.full htpasswd score turn" cleanup () { rm -rf reports $NEWFILES } From 01e864273ab36d6c79f8c5b8f17d50e624627bc6 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 9 Feb 2016 21:34:24 +0100 Subject: [PATCH 17/17] crash with an assert if Lua code has syntax errors. this addresses issue #474 also, correct capitalization of Lua. --- src/bindings.c | 3 ++- src/modules/museum.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bindings.c b/src/bindings.c index 99458c08d..72f14103d 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -96,7 +96,7 @@ TOLUA_PKG(game); int log_lua_error(lua_State * L) { const char *error = lua_tostring(L, -1); - log_fatal("LUA call failed.\n%s\n", error); + log_fatal("Lua call failed.\n%s\n", error); lua_pop(L, 1); return 1; } @@ -1183,6 +1183,7 @@ int eressea_run(lua_State *L, const char *luafile) err = lua_pcall(L, 1, 1, -3); if (err != 0) { log_lua_error(L); + assert(!"Lua syntax error? check log."); } else { if (lua_isnumber(L, -1)) { diff --git a/src/modules/museum.c b/src/modules/museum.c index e1e7d33e5..516bf775f 100644 --- a/src/modules/museum.c +++ b/src/modules/museum.c @@ -194,7 +194,7 @@ void warden_add_give(unit * src, unit * u, const item_type * itype, int n) void create_museum(void) { -#if 0 /* TODO: move this to LUA. It should be possible. */ +#if 0 /* TODO: move this to Lua. It should be possible. */ unsigned int museum_id = hashstring("museum"); plane *museum = getplanebyid(museum_id); region *r;