diff --git a/scripts/run-turn.lua b/scripts/run-turn.lua index 60f2b6f63..20a8de05b 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 end + 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,23 @@ 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 + if f.password then + out:write(itoa36(f.id) .. ":" .. f.password .. "\n") + end + 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() 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/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/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..ed6386d5f 100644 --- a/src/attributes/key.c +++ b/src/attributes/key.c @@ -22,6 +22,69 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #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 +92,61 @@ attrib_type at_key = { NULL, NULL, a_writeint, - a_readint, + a_readkey, + a_upgradekeys }; -attrib *add_key(attrib ** alist, int key) +void key_set(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) -{ - attrib *a = a_new(&at_key); - a->data.i = key; - return a; -} - -attrib *find_key(attrib * alist, int key) -{ - attrib *a = a_find(alist, &at_key); - while (a && a->type == &at_key && a->data.i != key) { - a = a->next; + int *keys, n = 1; + attrib *a; + assert(key != 0); + a = a_find(*alist, &at_keys); + if (!a) { + a = a_add(alist, a_new(&at_keys)); } - return (a && a->type == &at_key) ? a : NULL; + 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 = 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) { + keys[i] = keys[keys[0]]; + keys[0]--; + } + } + } + } +} + +bool key_get(attrib *alist, int key) { + attrib *a; + assert(key != 0); + 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 92f409595..89292db1d 100644 --- a/src/attributes/key.h +++ b/src/attributes/key.h @@ -21,12 +21,14 @@ 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; - 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..e15c50c3b --- /dev/null +++ b/src/attributes/key.test.c @@ -0,0 +1,27 @@ +#include +#include "key.h" + +#include +#include + +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) +{ + 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..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; } @@ -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,11 @@ 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); + 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; } @@ -1184,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/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/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/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 35f7e93a7..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) { @@ -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/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.c b/src/kernel/save.c index 7e5112443..3b7c13705 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -577,6 +577,24 @@ const struct locale *lang) WRITE_STR(data->store, obuf); } +int read_attribs(storage *store, attrib **alist, void *owner) { + 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) +{ +#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 +766,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 +840,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 +999,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 +1063,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 +1270,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 +1355,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 +1468,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 +1529,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 +1597,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 +1644,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 +1794,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 +1824,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 +1869,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 +1887,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 4762bf6e0..42d3074ac 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,37 @@ 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(); + global.data_version = RELEASE_VERSION; // FIXME: hack! + data = gamedata_open(path, "wb"); + CuAssertPtrNotNull(tc, data); + key_set(&a, 41); + key_set(&a, 42); + 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); + read_attribs(data->store, &a, NULL); + gamedata_close(data); + CuAssertTrue(tc, key_get(a, 41)); + CuAssertTrue(tc, key_get(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/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/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/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/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; 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 c076b74f2..b5ea3ad3c 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); + } } } } @@ -267,15 +276,92 @@ 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_OK; + 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) { + int ret = reader(na, owner, store); + if (na) { + switch (ret) { + case AT_READ_DEPR: + case AT_READ_OK: + a_add(attribs, na); + retval = ret; + 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; + + key = -1; + READ_INT(store, &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); + } + 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) { int key, retval = AT_READ_OK; char zText[128]; @@ -283,52 +369,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; @@ -337,7 +385,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..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; @@ -72,11 +73,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); @@ -88,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/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/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 }; diff --git a/tests/run-turn.sh b/tests/run-turn.sh index 4055613e5..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 } @@ -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` @@ -34,10 +35,11 @@ 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 +echo "integration tests" [ -d reports ] || quit 4 "no reports directory created" CRFILE=185-zvto.cr for file in $NEWFILES reports/$CRFILE ; do 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