From 2cc21b265d957cf5e100f39fde4dbf953475e0b6 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Tue, 22 Apr 2014 21:42:32 -0700 Subject: [PATCH 01/18] remove GM command, remove gm-attributes from data when writing. --- src/bind_faction.c | 9 +- src/laws.c | 4 - src/modules/gmcmd.c | 675 +------------------------------------------- src/util/attrib.c | 2 +- 4 files changed, 12 insertions(+), 678 deletions(-) diff --git a/src/bind_faction.c b/src/bind_faction.c index 0dfcb360e..0c7a7dfed 100644 --- a/src/bind_faction.c +++ b/src/bind_faction.c @@ -375,7 +375,14 @@ static int tolua_faction_set_locale(lua_State * L) { faction *self = (faction *) tolua_tousertype(L, 1, 0); const char *name = tolua_tostring(L, 2, 0); - self->locale = find_locale(name); + const struct locale *loc = find_locale(name); + if (loc) { + self->locale = loc; + } + else { + tolua_pushstring(L, "invalid locale"); + return 1; + } return 0; } diff --git a/src/laws.c b/src/laws.c index 7521b8426..00a88568a 100755 --- a/src/laws.c +++ b/src/laws.c @@ -4521,10 +4521,6 @@ void init_processor(void) add_proc_region(p, &enter_1, "Betreten (1. Versuch)"); add_proc_order(p, K_USE, &use_cmd, 0, "Benutzen"); - if (!global.disabled[K_GM]) { - add_proc_global(p, &gmcommands, "GM Kommandos"); - } - p += 10; /* in case it has any effects on alliance victories */ add_proc_order(p, K_GIVE, &give_control_cmd, 0, "GIB KOMMANDO"); diff --git a/src/modules/gmcmd.c b/src/modules/gmcmd.c index 581ad27ee..12845df9d 100644 --- a/src/modules/gmcmd.c +++ b/src/modules/gmcmd.c @@ -54,65 +54,25 @@ #include #include -/** - ** at_permissions - **/ - -static void mistake(const unit * u, struct order *ord, const char *comment) -{ - if (!is_monsters(u->faction)) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "mistake", - "error", comment)); - } -} - -static void -write_permissions(const attrib * a, const void *owner, struct storage *store) -{ - a_write(store, (attrib *) a->data.v, owner); -} - static int read_permissions(attrib * a, void *owner, struct storage *store) { attrib *attr = NULL; a_read(store, &attr, NULL); - a->data.v = attr; + a_free(attr); return AT_READ_OK; } struct attrib_type at_permissions = { "GM:permissions", NULL, NULL, NULL, - write_permissions, read_permissions, + NULL, read_permissions, ATF_UNIQUE }; -attrib *make_atpermissions(void) -{ - return a_new(&at_permissions); -} - -/** - ** GM: CREATE - **/ - -static void -write_gmcreate(const attrib * a, const void *owner, struct storage *store) -{ - const item_type *itype = (const item_type *)a->data.v; - assert(itype); - WRITE_TOK(store, resourcename(itype->rtype, 0)); -} - static int read_gmcreate(attrib * a, void *owner, struct storage *store) { char zText[32]; READ_TOK(store, zText, sizeof(zText)); - a->data.v = it_find(zText); - if (a->data.v == NULL) { - log_error("unknown itemtype %s in gmcreate attribute\n", zText); - return AT_READ_FAIL; - } return AT_READ_OK; } @@ -120,640 +80,11 @@ static int read_gmcreate(attrib * a, void *owner, struct storage *store) attrib_type at_gmcreate = { "GM:create", NULL, NULL, NULL, - write_gmcreate, read_gmcreate + NULL, read_gmcreate }; -attrib *make_atgmcreate(const struct item_type * itype) -{ - attrib *a = a_new(&at_gmcreate); - a->data.v = (void *)itype; - return a; -} - -static void gm_create(const void *tnext, struct unit *u, struct order *ord) -{ - int i; - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (permissions) - permissions = (attrib *) permissions->data.v; - if (!permissions) - return; - i = getint(); - - if (i > 0) { - const char *iname = getstrtoken(); - const item_type *itype = finditemtype(iname, u->faction->locale); - if (itype == NULL) { - mistake(u, ord, "unknown item."); - } else { - attrib *a = a_find(permissions, &at_gmcreate); - - while (a && a->type == &at_gmcreate && a->data.v != (void *)itype) - a = a->next; - if (a) - i_change(&u->items, itype, i); - else - mistake(u, ord, "your faction cannot create this item."); - } - } -} - -static bool has_permission(const attrib * permissions, unsigned int key) -{ - return (find_key((attrib *) permissions->data.v, key) || - find_key((attrib *) permissions->data.v, atoi36("master"))); -} - -/** - ** GM: GATE - ** requires: permission-key "gmgate" - **/ -static void gm_gate(const void *tnext, struct unit * u, struct order *ord) -{ - const struct plane *pl = rplane(u->region); - int id = getid(); - int x = rel_to_abs(pl, u->faction, getint(), 0); - int y = rel_to_abs(pl, u->faction, getint(), 1); - building *b = findbuilding(id); - region *r; - - pnormalize(&x, &y, pl); - r = findregion(x, y); - if (b == NULL || r == NULL || pl != rplane(b->region) || pl != rplane(r)) { - mistake(u, ord, "the unit cannot transform this building."); - return; - } else { - /* checking permissions */ - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (permissions && has_permission(permissions, atoi36("gmgate"))) { - remove_triggers(&b->attribs, "timer", &tt_gate); - remove_triggers(&b->attribs, "create", &tt_unguard); - if (r != b->region) { - add_trigger(&b->attribs, "timer", trigger_gate(b, r)); - add_trigger(&b->attribs, "create", trigger_unguard(b)); - fset(b, BLD_UNGUARDED); - } - } - } -} - -/** - ** GM: TERRAFORM - ** requires: permission-key "gmterf" - **/ -static void gm_terraform(const void *tnext, struct unit *u, struct order *ord) -{ - const struct plane *p = rplane(u->region); - int x = rel_to_abs(p, u->faction, getint(), 0); - int y = rel_to_abs(p, u->faction, getint(), 1); - const char *c = getstrtoken(); - variant token; - void **tokens = get_translations(u->faction->locale, UT_TERRAINS); - region *r; - pnormalize(&x, &y, p); - r = findregion(x, y); - - if (r == NULL || p != rplane(r)) { - mistake(u, ord, "region is in another plane."); - return; - } else { - /* checking permissions */ - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmterf"))) - return; - } - - if (findtoken(*tokens, c, &token) != E_TOK_NOMATCH) { - const terrain_type *terrain = (const terrain_type *)token.v; - terraform_region(r, terrain); - } -} - -/** - ** GM: TELEPORT - ** requires: permission-key "gmtele" - **/ -static void gm_teleport(const void *tnext, struct unit *u, struct order *ord) -{ - const struct plane *p = rplane(u->region); - unit *to = findunit(getid()); - int x = rel_to_abs(p, u->faction, getint(), 0); - int y = rel_to_abs(p, u->faction, getint(), 1); - region *r = findregion(x, y); - - if (r == NULL || p != rplane(r)) { - mistake(u, ord, "region is in another plane."); - } else if (to == NULL) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", - "")); - } else if (rplane(to->region) != rplane(r) && !ucontact(to, u)) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_no_contact", - "target", to)); - } else { - /* checking permissions */ - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmtele"))) { - mistake(u, ord, "permission denied."); - } else - move_unit(to, r, NULL); - } -} - -/** - ** GM: TELL PLANE - ** requires: permission-key "gmmsgr" - **/ -static void gm_messageplane(const void *tnext, struct unit *gm, struct order *ord) -{ - const struct plane *p = rplane(gm->region); - const char *zmsg = getstrtoken(); - if (p == NULL) { - mistake(gm, ord, "In diese Ebene kann keine Nachricht gesandt werden."); - } else { - /* checking permissions */ - attrib *permissions = a_find(gm->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmmsgr"))) { - mistake(gm, ord, "permission denied."); - } else { - message *msg = msg_message("msg_event", "string", zmsg); - faction *f; - region *r; - for (f = factions; f; f = f->next) { - freset(f, FFL_SELECT); - } - for (r = regions; r; r = r->next) { - unit *u; - if (rplane(r) != p) - continue; - for (u = r->units; u; u = u->next) - if (!fval(u->faction, FFL_SELECT)) { - f = u->faction; - fset(f, FFL_SELECT); - add_message(&f->msgs, msg); - } - } - msg_release(msg); - } - } -} - -static void -gm_messagefaction(const void *tnext, struct unit *gm, struct order *ord) -{ - int n = getid(); - faction *f = findfaction(n); - const char *msg = getstrtoken(); - plane *p = rplane(gm->region); - attrib *permissions = a_find(gm->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmmsgr"))) { - mistake(gm, ord, "permission denied."); - return; - } - if (f != NULL) { - region *r; - for (r = regions; r; r = r->next) - if (rplane(r) == p) { - unit *u; - for (u = r->units; u; u = u->next) - if (u->faction == f) { - add_message(&f->msgs, msg_message("msg_event", "string", msg)); - return; - } - } - } - mistake(gm, ord, "cannot send messages to this faction."); -} - -/** - ** GM: TELL REGION - ** requires: permission-key "gmmsgr" - **/ -static void gm_messageregion(const void *tnext, struct unit *u, struct order *ord) -{ - const struct plane *p = rplane(u->region); - int x = rel_to_abs(p, u->faction, getint(), 0); - int y = rel_to_abs(p, u->faction, getint(), 1); - const char *msg = getstrtoken(); - region *r = findregion(x, y); - - if (r == NULL || p != rplane(r)) { - mistake(u, ord, "region is in another plane."); - } else { - /* checking permissions */ - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmmsgr"))) { - mistake(u, ord, "permission denied."); - } else { - add_message(&r->msgs, msg_message("msg_event", "string", msg)); - } - } -} - -/** - ** GM: KILL UNIT - ** requires: permission-key "gmkill" - **/ -static void gm_killunit(const void *tnext, struct unit *u, struct order *ord) -{ - const struct plane *p = rplane(u->region); - unit *target = findunit(getid()); - const char *msg = getstrtoken(); - region *r = target->region; - - if (r == NULL || p != rplane(r)) { - mistake(u, ord, "region is in another plane."); - } else { - /* checking permissions */ - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmkill"))) { - mistake(u, ord, "permission denied."); - } else { - scale_number(target, 0); - ADDMSG(&target->faction->msgs, msg_message("killedbygm", - "region unit string", r, target, msg)); - } - } -} - -/** - ** GM: KILL FACTION - ** requires: permission-key "gmmsgr" - **/ -static void gm_killfaction(const void *tnext, struct unit *u, struct order *ord) -{ - int n = getid(); - faction *f = findfaction(n); - const char *msg = getstrtoken(); - plane *p = rplane(u->region); - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmkill"))) { - mistake(u, ord, "permission denied."); - return; - } - if (f != NULL) { - region *r; - for (r = regions; r; r = r->next) - if (rplane(r) == p) { - unit *target; - for (target = r->units; target; target = target->next) { - if (target->faction == f) { - scale_number(target, 0); - ADDMSG(&target->faction->msgs, msg_message("killedbygm", - "region unit string", r, target, msg)); - return; - } - } - } - } - mistake(u, ord, "cannot remove a unit from this faction."); -} - -/** - ** GM: TELL - ** requires: permission-key "gmmsgr" - **/ -static void gm_messageunit(const void *tnext, struct unit *u, struct order *ord) -{ - const struct plane *p = rplane(u->region); - unit *target = findunit(getid()); - const char *msg = getstrtoken(); - region *r; - - if (target == NULL) { - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", - "")); - return; - } - - r = target->region; - - if (r == NULL || p != rplane(r)) { - mistake(u, ord, "region is in another plane."); - } else { - /* checking permissions */ - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmmsgu"))) { - mistake(u, ord, "permission denied."); - } else { - add_message(&target->faction->msgs, - msg_message("regionmessage", "region sender string", r, u, msg)); - } - } -} - -/** - ** GM: GIVE - ** requires: permission-key "gmgive" - **/ -static void gm_give(const void *tnext, struct unit *u, struct order *ord) -{ - unit *to = findunit(getid()); - int num = getint(); - const item_type *itype = finditemtype(getstrtoken(), u->faction->locale); - - if (to == NULL || rplane(to->region) != rplane(u->region)) { - /* unknown or in another plane */ - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", - "")); - } else if (itype == NULL || i_get(u->items, itype) == 0) { - /* unknown or not enough */ - mistake(u, ord, "invalid item or item not found."); - } else { - /* checking permissions */ - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmgive"))) { - mistake(u, ord, "permission denied."); - } else { - int i = i_get(u->items, itype); - if (i < num) - num = i; - if (num) { - i_change(&u->items, itype, -num); - i_change(&to->items, itype, num); - } - } - } -} - -/** - ** GM: TAKE - ** requires: permission-key "gmtake" - **/ -static void gm_take(const void *tnext, struct unit *u, struct order *ord) -{ - unit *to = findunit(getid()); - int num = getint(); - const item_type *itype = finditemtype(getstrtoken(), u->faction->locale); - - if (to == NULL || rplane(to->region) != rplane(u->region)) { - /* unknown or in another plane */ - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", - "")); - } else if (itype == NULL || i_get(to->items, itype) == 0) { - /* unknown or not enough */ - mistake(u, ord, "invalid item or item not found."); - } else { - /* checking permissions */ - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmtake"))) { - mistake(u, ord, "permission denied."); - } else { - int i = i_get(to->items, itype); - if (i < num) - num = i; - if (num) { - i_change(&to->items, itype, -num); - i_change(&u->items, itype, num); - } - } - } -} - -/** - ** GM: SKILL - ** requires: permission-key "gmskil" - **/ -static void gm_skill(const void *tnext, struct unit *u, struct order *ord) -{ - unit *to = findunit(getid()); - skill_t skill = findskill(getstrtoken(), u->faction->locale); - int num = getint(); - - if (to == NULL || rplane(to->region) != rplane(u->region)) { - /* unknown or in another plane */ - ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "feedback_unit_not_found", - "")); - } else if (skill == NOSKILL || skill == SK_MAGIC || skill == SK_ALCHEMY) { - /* unknown or not enough */ - mistake(u, ord, "unknown skill, or skill cannot be raised."); - } else if (num < 0 || num > 30) { - /* sanity check failed */ - mistake(u, ord, "invalid value."); - } else { - /* checking permissions */ - attrib *permissions = a_find(u->faction->attribs, &at_permissions); - if (!permissions || !has_permission(permissions, atoi36("gmskil"))) { - mistake(u, ord, "permission denied."); - } else { - set_level(to, skill, num); - } - } -} - -static void * g_keys; -static void * g_root; -static void * g_tell; -static void * g_kill; - void register_gmcmd(void) { at_register(&at_gmcreate); at_register(&at_permissions); - add_command(&g_root, &g_keys, "gm", NULL); - add_command(&g_keys, NULL, "terraform", &gm_terraform); - add_command(&g_keys, NULL, "create", &gm_create); - add_command(&g_keys, NULL, "gate", &gm_gate); - add_command(&g_keys, NULL, "give", &gm_give); - add_command(&g_keys, NULL, "take", &gm_take); - add_command(&g_keys, NULL, "teleport", &gm_teleport); - add_command(&g_keys, NULL, "skill", &gm_skill); - add_command(&g_keys, &g_tell, "tell", NULL); - add_command(&g_tell, NULL, "region", &gm_messageregion); - add_command(&g_tell, NULL, "unit", &gm_messageunit); - add_command(&g_tell, NULL, "plane", &gm_messageplane); - add_command(&g_tell, NULL, "faction", &gm_messagefaction); - add_command(&g_keys, &g_kill, "kill", NULL); - add_command(&g_kill, NULL, "unit", &gm_killunit); - add_command(&g_kill, NULL, "faction", &gm_killfaction); -} - -/* - * execute gm-commands for all units in the game - */ - -void gmcommands(void) -{ - region **rp = ®ions; - while (*rp) { - region *r = *rp; - unit **up = &r->units; - while (*up) { - unit *u = *up; - struct order *ord; - for (ord = u->orders; ord; ord = ord->next) { - if (get_keyword(ord) == K_GM) { - do_command(&g_root, u, ord); - } - } - if (u == *up) - up = &u->next; - } - if (*rp == r) - rp = &r->next; - } -} - -#define EXTENSION 10000 - -faction *gm_addquest(const char *email, const char *name, int radius, - unsigned int flags) -{ - plane *pl; - watcher *w = calloc(sizeof(watcher), 1); - region *center; - bool invalid = false; - int minx, miny, maxx, maxy, cx, cy; - int x; - faction *f; - - /* GM playfield */ - do { - minx = ((rng_int() % (2 * EXTENSION)) - EXTENSION); - miny = ((rng_int() % (2 * EXTENSION)) - EXTENSION); - for (x = 0; !invalid && x <= radius * 2; ++x) { - int y; - for (y = 0; !invalid && y <= radius * 2; ++y) { - region *r = findregion(minx + x, miny + y); - if (r) - invalid = true; - } - } - } while (invalid); - maxx = minx + 2 * radius; - cx = minx + radius; - maxy = miny + 2 * radius; - cy = miny + radius; - pl = create_new_plane(rng_int(), name, minx, maxx, miny, maxy, flags); - center = new_region(cx, cy, pl, 0); - for (x = 0; x <= 2 * radius; ++x) { - int y; - for (y = 0; y <= 2 * radius; ++y) { - region *r = findregion(minx + x, miny + y); - if (!r) { - r = new_region(minx + x, miny + y, pl, 0); - } - freset(r, RF_ENCOUNTER); - if (distance(r, center) == radius) { - terraform_region(r, newterrain(T_FIREWALL)); - } else if (r == center) { - terraform_region(r, newterrain(T_PLAIN)); - } else { - terraform_region(r, newterrain(T_OCEAN)); - } - } - } - - /* watcher: */ - f = gm_addfaction(email, pl, center); - w->faction = f; - w->mode = see_unit; - w->next = pl->watchers; - pl->watchers = w; - - return f; -} - -faction *gm_addfaction(const char *email, plane * p, region * r) -{ - attrib *a; - unit *u; - faction *f = calloc(1, sizeof(faction)); - - assert(p != NULL); - - /* GM faction */ - a_add(&f->attribs, make_key(atoi36("quest"))); - f->banner = _strdup("quest faction"); - f->name = _strdup("quest faction"); - f->passw = _strdup(itoa36(rng_int())); - if (set_email(&f->email, email) != 0) { - log_error("Invalid email address for faction %s: %s\n", itoa36(f->no), email); - } - f->race = new_race[RC_TEMPLATE]; - f->age = 0; - f->lastorders = turn; - f->alive = true; - f->locale = default_locale; - f->options = - want(O_COMPRESS) | want(O_REPORT) | want(O_COMPUTER) | want(O_ADRESSEN); - { - faction *xist; - int id = atoi36("gm00") - 1; - do { - xist = findfaction(++id); - } while (xist); - - f->no = id; - addlist(&factions, f); - fhash(f); - } - - /* generic permissions */ - a = a_add(&f->attribs, a_new(&at_permissions)); - if (a) { - attrib *ap = (attrib *) a->data.v; - const char *keys[] = - { "gmterf", "gmtele", "gmgive", "gmskil", "gmtake", "gmmsgr", "gmmsgu", - "gmgate", 0 }; - const char **key_p = keys; - while (*key_p) { - add_key(&ap, atoi36(*key_p)); - ++key_p; - } - a_add(&ap, make_atgmcreate(resource2item(r_silver))); - - a->data.v = ap; - } - - /* one initial unit */ - u = create_unit(r, f, 1, new_race[RC_TEMPLATE], 1, "quest master", NULL); - u->irace = new_race[RC_GNOME]; - - return f; -} - -plane *gm_addplane(int radius, unsigned int flags, const char *name) -{ - region *center; - plane *pl; - bool invalid = false; - int minx, miny, maxx, maxy, cx, cy; - int x; - - /* GM playfield */ - do { - minx = (rng_int() % (2 * EXTENSION)) - EXTENSION; - miny = (rng_int() % (2 * EXTENSION)) - EXTENSION; - for (x = 0; !invalid && x <= radius * 2; ++x) { - int y; - for (y = 0; !invalid && y <= radius * 2; ++y) { - region *r = findregion(minx + x, miny + y); - if (r) - invalid = true; - } - } - } while (invalid); - maxx = minx + 2 * radius; - cx = minx + radius; - maxy = miny + 2 * radius; - cy = miny + radius; - pl = create_new_plane(rng_int(), name, minx, maxx, miny, maxy, flags); - center = new_region(cx, cy, pl, 0); - for (x = 0; x <= 2 * radius; ++x) { - int y; - for (y = 0; y <= 2 * radius; ++y) { - region *r = findregion(minx + x, miny + y); - if (!r) - r = new_region(minx + x, miny + y, pl, 0); - freset(r, RF_ENCOUNTER); - if (distance(r, center) == radius) { - terraform_region(r, newterrain(T_FIREWALL)); - } else if (r == center) { - terraform_region(r, newterrain(T_PLAIN)); - } else { - terraform_region(r, newterrain(T_OCEAN)); - } - } - } - return pl; } diff --git a/src/util/attrib.c b/src/util/attrib.c index 197fea808..8c8bf9d35 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -321,7 +321,7 @@ int a_read(struct storage *store, attrib ** attribs, void *owner) } } } else { - assert(!"fehler: keine laderoutine für attribut"); + assert(!"error: no registered callback can read attribute"); } READ_TOK(store, zText, sizeof(zText)); From cdcc8ae1d32e2bdae1681934db617c0c6324866e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 23 Apr 2014 21:36:34 -0700 Subject: [PATCH 02/18] draft documentation, chapter overview and introduction --- .gitignore | 1 + doc/chapters.md | 27 ++++++++++++++++ doc/introduction.md | 79 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 doc/chapters.md create mode 100644 doc/introduction.md diff --git a/.gitignore b/.gitignore index 191be8658..39abdc995 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ game-e3/reports/ *.log *.log.* tags +Thumbs.db diff --git a/doc/chapters.md b/doc/chapters.md new file mode 100644 index 000000000..7db67fcf2 --- /dev/null +++ b/doc/chapters.md @@ -0,0 +1,27 @@ +# Eressea Rules + +© Copyright Enno Rehling 2014 + +## Contents + +1. Introduction +2. First Turn by Example +3. The Economy: Recruiting, Upkeep and Silver +4. Studying Skills +5. Movement and Seafaring +6. Combat +7. Alliances and Groups +7. Resources and Production +8. Magic and Alchemy +9. Castles and other buildings +10. Races +11. List of Skills +12. List of Buildings +12. List of Items +13. Appendices and Tables + +## Acknowledgments + +This game would not be possible without the work of Russell Wallace, who created the Atlantis PBEM in 1993. I would also like to thank Alex Schröder, who wrote the first German translation of Atlantis. Christian Schlittchen, Katja Zedel and Henning Peters started Eressea, and invited me to be a part of their team and become their friends in the process. Ingo Wilken, Benjamin Bärmann, Stefan Reich, and Martin Hershoff have contributed code to the server, and valuable design input. Many of the features are a direct result of entertaining debates on the eressea-kom discussion list, and I would like to thank them and the entire community for their support and encouragement over the years. + +Enno Rehling, April 2014 diff --git a/doc/introduction.md b/doc/introduction.md new file mode 100644 index 000000000..e21374a84 --- /dev/null +++ b/doc/introduction.md @@ -0,0 +1,79 @@ +# Introduction + +Eressea is a play-by-email strategy role-playing game that simulates a fantasy world. Unlike most video games, there are are no fancy graphics, but everything that happens is communicated by text. + +As a player, you are in control of a number of characters, which are referred to as units, which combine to make a faction. Your faction competes with other factions over the resources of the world that you explore together. Sometimes this leads to war, and sometimes to friendship between factions, and eventually it all adds up to a rich history of the world, with its own heroes and legends. + +Eressea is different from most games in many respects, but most of all in that there is no declared winner. There is not even a goal, other than to enjoy yourself, and set your own goals. Do you want to monopolize a natural resource? Become the strongest military force? Create a democratic government for the factions in your part of the world? Collect trophies from slain dragons? Map the entire globe? Those are all valid goals to set for yourself, but you might find your own. + +### How to play + +Eressea is played in weekly turns. Every weekend, you receive a textual report containing your units, the part of the world that they can observe, and any events that happened since the last report. + +Based on that report, players send an email to the game's server that contains orders for all of their units. A week later, the server evaluates the orders from all players, updates the state of the world, and sends out new reports. + +A (slightly simplified) report may look like this: + + Report for Eressea, Sunday, 13. April 2014, 23:25 + + It is the first week of the month of harvest moon in + the 1. year of the fourth age. It is summer. + + Guardians (xin8), elves/draig (guardian@gmx.de) + + Your faction has 4 people in 1 unit. + + Vetkan (10,1), highland, 1012 peasants, 13156 silver, + 17 horses. To the northwest lies the forests of + Buviken (9,2), to the northeast the mountains of Rebus + (10,2), to the east the plain of Setutvul (-9,1), to + the southeast the mountains of Vithil (-9,0), to the + southwest the forests of Posotmetid (10,0) and to the + west the forests of Cesartapun (9,1) + + The local market offers silk and mandrakes. + + Statistics for Vetkan (10,1): + + Peasant wages: 11 silver + Recruits: 25 peasants + + Pelenth (opw4), size 10, fortification. + + * Fighters of Pelenth (r6b0), 4 elves, + aggressive, skills: melee 5, has: platemail, + sword, 500 silver. + + + Osswid the Destroyer (wtmj), Vikings (atnf), 1 + dwarf, has: 1 iron. + +What you see here are two units, Osswid (wtmj) and the Fighters of Pelenth (r6b0). The fighters are our own unit, but Osswid belongs to the faction Vikings (atnf). Every unit, faction or other object in the game is identified by a four-letter code, and it is common to append it to the name in parentheses. The fort of Pelenth (opw4) is a building, and our fighters are inside, which causes them to be indented a little more, and gives them some benefits including additional protection in combat. + +While Osswid is a single dwarf, our unit of fighters consists of several elves. Units can any number of men, which are always of the same race. Each unit can learn any number of skills, and our Fighters are skilled in melee combat at a respectable level 5. They own a sword and one platemail, which means that one of them will be well equipped when going into battle, while the other three will fight with their bare hands. The unit also has 500 pieces of silver, the currency of the game. + +Since he isn't of our own faction, we cannot tell what skills Osswid has, but we can tell that he's unarmed. + +We see all this because one of our units is in the region, the highland of Vetkan (10,1). Regions do not have a four-letter identifier, but are instead identified by their coordinates on the map, which makes it easier to visualize the world. + +The region of Vetkan is surrounded by a number of other regions on six sides, since Eressea's Map uses a hexagonal grid. Every region has a terrain, and we can see highland, mountain, plain and forest regions in our vicinity. Each terrain has different qualities: For example, mountains are good for mining iron ore and quarries, while plains are fertile and forests produce lumber. Each region is inhabited by a peasant population, and you can see that Vetkan has a population of 1012. These peasants are the basis of the region's economy, and they own a combined 13,156 silver that we could try to acquire through taxation or other means. + +Example orders for this faction may look like this: + + ERESSEA xin8 "password" + UNIT r6b0 + STUDY perception + GIVE wtmj 100 silver + GIVE TEMP 1 150 silver + GIVE TEMP 2 150 silver + MAKE TEMP 1 + RECRUIT 1 + MOVE W + END + MAKE TEMP 2 + RECRUIT 1 + MOVE E + END + +Every set of orders begins with the faction's id and password, and is followed by orders for one or more units. A unit's orders begin with the word UNIT followed by the unit's id, and one or more orders. In this case, we are giving the unit an order to STUDY the perception skill, and to GIVE silver to three other units. STUDY is a standard action, and each unit can perform exactly one per turn. If you do not specify a standard action, then the unit will usually repeat the action from the previous week. GIVE is a free action, and all units can perform as many free actions in a week as they like. + +The MAKE TEMP order is special: It creates a new unit with a temporary id (in this case, we're calling them TEMP 1 and TEMP 2). The server will replace this with a unique id by the time it sends the next report, but until then, we can address the new unit by this temporary identifier, for example to give it the silver that it requires for recruiting new members. RECRUIT is another example of a free action, while MOVE is a standard action. The MAKE TEMP order is technically given by the unit whose orders preceded it, and the new unit's orders are finished with the END keyword. From b875ec872bf4a6f5ad418639b0489aa24d022bcc Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 23 Apr 2014 21:48:21 -0700 Subject: [PATCH 03/18] Do not forget a chapter of orders. --- doc/chapters.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/chapters.md b/doc/chapters.md index 7db67fcf2..4cd51251e 100644 --- a/doc/chapters.md +++ b/doc/chapters.md @@ -15,10 +15,11 @@ 8. Magic and Alchemy 9. Castles and other buildings 10. Races -11. List of Skills -12. List of Buildings -12. List of Items -13. Appendices and Tables +11. List of Orders +12. List of Skills +13. List of Buildings +14. List of Items +15. Appendices and Tables ## Acknowledgments From a485f655621705fd6d2cbef2e5eaa342aecdae08 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 24 Apr 2014 09:14:29 +0200 Subject: [PATCH 04/18] new config files --- cutest | 2 +- res/adamantium.xml | 36 ++++++++++++++++++++++++++++++++++++ res/config-e3a.xml | 1 + res/config-eressea.xml | 1 + res/eressea/items.xml | 32 -------------------------------- 5 files changed, 39 insertions(+), 33 deletions(-) create mode 100644 res/adamantium.xml diff --git a/cutest b/cutest index d83cec09a..788659594 160000 --- a/cutest +++ b/cutest @@ -1 +1 @@ -Subproject commit d83cec09a52835274ab8ed4849de16fb8658982a +Subproject commit 788659594ef87e9f497b8039da764182adfd2943 diff --git a/res/adamantium.xml b/res/adamantium.xml new file mode 100644 index 000000000..315a8d66c --- /dev/null +++ b/res/adamantium.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/config-e3a.xml b/res/config-e3a.xml index 07a3b8eab..22c3bd645 100644 --- a/res/config-e3a.xml +++ b/res/config-e3a.xml @@ -30,6 +30,7 @@ + diff --git a/res/config-eressea.xml b/res/config-eressea.xml index d06464759..579e7e94a 100644 --- a/res/config-eressea.xml +++ b/res/config-eressea.xml @@ -31,6 +31,7 @@ + diff --git a/res/eressea/items.xml b/res/eressea/items.xml index a1efc9620..8fe36ee1b 100644 --- a/res/eressea/items.xml +++ b/res/eressea/items.xml @@ -164,36 +164,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From c1b840b0b2c9e8dd6b8ce39a6a91f92ecf07e7ea Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 24 Apr 2014 14:41:04 +0200 Subject: [PATCH 05/18] remove -Cef, parse any arguments as a file to run. new xml config file, work in progress, xinclude is broken --- game-e3/catalog.xml | 6 +++--- game-e3/runtests.lua | 3 +++ src/bindings.c | 31 ++++++++++--------------------- src/bindings.h | 2 +- src/main.c | 14 ++------------ src/util/xml.c | 2 +- 6 files changed, 20 insertions(+), 38 deletions(-) create mode 100644 game-e3/runtests.lua diff --git a/game-e3/catalog.xml b/game-e3/catalog.xml index 93cda3505..4c55dadd5 100644 --- a/game-e3/catalog.xml +++ b/game-e3/catalog.xml @@ -6,11 +6,11 @@ + rewritePrefix="config/core/" /> + rewritePrefix="config/game/" /> + rewritePrefix="config/default/" /> diff --git a/game-e3/runtests.lua b/game-e3/runtests.lua new file mode 100644 index 000000000..9a167d039 --- /dev/null +++ b/game-e3/runtests.lua @@ -0,0 +1,3 @@ +require "setup" +read_xml("config.xml", "catalog.xml") +run_tests() diff --git a/src/bindings.c b/src/bindings.c index 7a8e99e3a..6b45bfb2f 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -995,8 +995,8 @@ static int tolua_get_spells(lua_State * L) int tolua_read_xml(lua_State * L) { - const char *filename = tolua_tostring(L, 1, 0); - const char *catalog = tolua_tostring(L, 2, 0); + const char *filename = tolua_tostring(L, 1, "config.xml"); + const char *catalog = tolua_tostring(L, 2, "catalog.xml"); init_data(filename, catalog); return 0; } @@ -1196,7 +1196,7 @@ lua_State *lua_init(void) { return L; } -int eressea_run(lua_State *L, const char *luafile, const char *entry_point) +int eressea_run(lua_State *L, const char *luafile) { int err = 0; @@ -1206,28 +1206,17 @@ int eressea_run(lua_State *L, const char *luafile, const char *entry_point) log_debug("executing script %s\n", luafile); lua_getglobal(L, "dofile"); lua_pushstring(L, luafile); - err = lua_pcall(L, 1, 0, 0); + err = lua_pcall(L, 1, 1, 0); if (err != 0) { log_lua_error(L); abort(); - return err; - } - } - if (entry_point) { - if (strcmp("console", entry_point)==0) { - return lua_console(L); - } - lua_getglobal(L, entry_point); - if (lua_isfunction(L, -1)) { - log_debug("calling entry-point: %s\n", entry_point); - err = lua_pcall(L, 0, 1, 0); - if (err != 0) { - log_lua_error(L); + } else { + if (lua_isnumber(L, -1)) { + err = (int)lua_tonumber(L, -1); } - return err; - } else { - log_error("unknown entry-point: %s\n", entry_point); + lua_pop(L, 1); } + return err; } - return 0; + return lua_console(L); } diff --git a/src/bindings.h b/src/bindings.h index 1494a1d7c..d1b880609 100755 --- a/src/bindings.h +++ b/src/bindings.h @@ -29,7 +29,7 @@ extern "C" { void lua_done(struct lua_State *L); struct lua_State *lua_init(void); - int eressea_run(struct lua_State *L, const char *luafile, const char *entry_point); + int eressea_run(struct lua_State *L, const char *luafile); #ifdef __cplusplus } diff --git a/src/main.c b/src/main.c index 2246dc389..3729be52e 100644 --- a/src/main.c +++ b/src/main.c @@ -39,7 +39,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. static const char *logfile= "eressea.log"; static const char *luafile = 0; -static const char *entry_point = NULL; static const char *inifile = "eressea.ini"; static int memdebug = 0; @@ -51,8 +50,6 @@ static void parse_config(const char *filename) log_debug("reading from configuration file %s\n", filename); memdebug = iniparser_getint(d, "eressea:memcheck", memdebug); - entry_point = iniparser_getstring(d, "eressea:run", entry_point); - luafile = iniparser_getstring(d, "eressea:load", luafile); /* only one value in the [editor] section */ force_color = iniparser_getint(d, "editor:color", force_color); @@ -99,7 +96,7 @@ static int parse_args(int argc, char **argv, int *exitcode) for (i = 1; i != argc; ++i) { if (argv[i][0] != '-') { - return usage(argv[0], argv[i]); + luafile = argv[i]; } else if (argv[i][1] == '-') { /* long format */ if (strcmp(argv[i] + 2, "version") == 0) { printf("\n%s PBEM host\n" @@ -117,18 +114,12 @@ static int parse_args(int argc, char **argv, int *exitcode) } else { const char *arg; switch (argv[i][1]) { - case 'C': - entry_point = "console"; - break; case 'f': i = get_arg(argc, argv, 2, i, &luafile, 0); break; case 'l': i = get_arg(argc, argv, 2, i, &logfile, 0); break; - case 'e': - i = get_arg(argc, argv, 2, i, &entry_point, 0); - break; case 't': i = get_arg(argc, argv, 2, i, &arg, 0); turn = atoi(arg); @@ -137,7 +128,6 @@ static int parse_args(int argc, char **argv, int *exitcode) verbosity = 0; break; case 'r': - entry_point = "run_turn"; i = get_arg(argc, argv, 2, i, &arg, 0); turn = atoi(arg); break; @@ -270,7 +260,7 @@ int main(int argc, char **argv) register_spells(); bind_monsters(L); - err = eressea_run(L, luafile, entry_point); + err = eressea_run(L, luafile); if (err) { log_error("server execution failed with code %d\n", err); return err; diff --git a/src/util/xml.c b/src/util/xml.c index c97460912..b1647838e 100644 --- a/src/util/xml.c +++ b/src/util/xml.c @@ -113,7 +113,7 @@ int read_xml(const char *filename, const char *catalog) xmlLoadCatalog(catalog); } #ifdef XML_PARSE_XINCLUDE - doc = xmlReadFile(filename, NULL, XML_PARSE_XINCLUDE); + doc = xmlReadFile(filename, NULL, XML_PARSE_XINCLUDE|XML_PARSE_NONET|XML_PARSE_PEDANTIC); #else doc = xmlParseFile(filename); #endif From bf8d37e4f8d035abf8508d225374545dcdc74948 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 24 Apr 2014 05:43:09 -0700 Subject: [PATCH 06/18] reworded acknowledgements --- doc/chapters.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/chapters.md b/doc/chapters.md index 4cd51251e..02ddeec0d 100644 --- a/doc/chapters.md +++ b/doc/chapters.md @@ -23,6 +23,6 @@ ## Acknowledgments -This game would not be possible without the work of Russell Wallace, who created the Atlantis PBEM in 1993. I would also like to thank Alex Schröder, who wrote the first German translation of Atlantis. Christian Schlittchen, Katja Zedel and Henning Peters started Eressea, and invited me to be a part of their team and become their friends in the process. Ingo Wilken, Benjamin Bärmann, Stefan Reich, and Martin Hershoff have contributed code to the server, and valuable design input. Many of the features are a direct result of entertaining debates on the eressea-kom discussion list, and I would like to thank them and the entire community for their support and encouragement over the years. +This game would not be possible without the work of Russell Wallace, who created the Atlantis PBEM in 1993. I would also like to thank Alex Schröder, who wrote the first German translation of Atlantis. Christian Schlittchen, Katja Zedel and Henning Peters started Eressea, and invited me to be a part of their team and become their friends in the process. Ingo Wilken, Benjamin Bärmann, Stefan Reich, and Martin Hershoff have contributed code and valuable design input to the server. Many advanced features are a direct result of entertaining debates on the eressea-kom and atlantisdev discussion lists, and I would like to thank them and the entire community for their support and encouragement over the years. Enno Rehling, April 2014 From ac1149add53e568fad6d6ccb7bed61168154298d Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 24 Apr 2014 14:47:44 +0200 Subject: [PATCH 07/18] this catalog does not work. why? --- game-e3/catalog.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/game-e3/catalog.xml b/game-e3/catalog.xml index 4c55dadd5..93cda3505 100644 --- a/game-e3/catalog.xml +++ b/game-e3/catalog.xml @@ -6,11 +6,11 @@ + rewritePrefix="../core/res/" /> + rewritePrefix="../res/e3a/" /> + rewritePrefix="../res/" /> From 921ab4947eebab7676214d313696c9c800597c54 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 24 Apr 2014 06:24:35 -0700 Subject: [PATCH 08/18] fixing xml config for e2 and e3 --- res/catalog-e3a.xml => game-e2/catalog.xml | 8 +- res/config-eressea.xml => game-e2/config.xml | 66 +++---- game-e2/runtests.lua | 2 + game-e2/setup.lua | 3 +- game-e3/catalog.xml | 6 +- game-e3/runtests.lua | 1 - game-e3/setup.lua | 3 +- res/catalog-eressea.xml | 10 - res/config-e3a.xml | 190 ------------------- src/bindings.c | 21 +- src/util/xml.c | 27 ++- 11 files changed, 75 insertions(+), 262 deletions(-) rename res/catalog-e3a.xml => game-e2/catalog.xml (56%) rename res/config-eressea.xml => game-e2/config.xml (64%) create mode 100644 game-e2/runtests.lua delete mode 100644 res/catalog-eressea.xml delete mode 100644 res/config-e3a.xml diff --git a/res/catalog-e3a.xml b/game-e2/catalog.xml similarity index 56% rename from res/catalog-e3a.xml rename to game-e2/catalog.xml index 96dbf8d9d..a6c2882c8 100644 --- a/res/catalog-e3a.xml +++ b/game-e2/catalog.xml @@ -5,6 +5,12 @@ + + diff --git a/res/config-eressea.xml b/game-e2/config.xml similarity index 64% rename from res/config-eressea.xml rename to game-e2/config.xml index 579e7e94a..72c1563ab 100644 --- a/res/config-eressea.xml +++ b/game-e2/config.xml @@ -1,37 +1,37 @@ - + - - - + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - - - - - + + + + + @@ -44,11 +44,11 @@ - - - - - + + + + + diff --git a/game-e2/runtests.lua b/game-e2/runtests.lua new file mode 100644 index 000000000..423094391 --- /dev/null +++ b/game-e2/runtests.lua @@ -0,0 +1,2 @@ +require "setup" +run_tests() diff --git a/game-e2/setup.lua b/game-e2/setup.lua index d9dea3541..ccd43719d 100644 --- a/game-e2/setup.lua +++ b/game-e2/setup.lua @@ -10,6 +10,5 @@ for idx, path in pairs(paths) do package.path = srcpath .. '/' .. path .. ';' .. package.path end -read_xml(respath..'/config-eressea.xml', respath..'/catalog-eressea.xml') - +assert(read_xml()) require "init" diff --git a/game-e3/catalog.xml b/game-e3/catalog.xml index 93cda3505..3d82919cd 100644 --- a/game-e3/catalog.xml +++ b/game-e3/catalog.xml @@ -4,13 +4,13 @@ "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"> - + diff --git a/game-e3/runtests.lua b/game-e3/runtests.lua index 9a167d039..423094391 100644 --- a/game-e3/runtests.lua +++ b/game-e3/runtests.lua @@ -1,3 +1,2 @@ require "setup" -read_xml("config.xml", "catalog.xml") run_tests() diff --git a/game-e3/setup.lua b/game-e3/setup.lua index e93f64a48..ccd43719d 100644 --- a/game-e3/setup.lua +++ b/game-e3/setup.lua @@ -10,6 +10,5 @@ for idx, path in pairs(paths) do package.path = srcpath .. '/' .. path .. ';' .. package.path end -read_xml(respath..'/config-e3a.xml', respath..'/catalog-e3a.xml') - +assert(read_xml()) require "init" diff --git a/res/catalog-eressea.xml b/res/catalog-eressea.xml deleted file mode 100644 index 96dbf8d9d..000000000 --- a/res/catalog-eressea.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/res/config-e3a.xml b/res/config-e3a.xml deleted file mode 100644 index 22c3bd645..000000000 --- a/res/config-e3a.xml +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - eressea-server@eressea.de - eressea-server@eressea.de - - - Bitte denke daran, deine Befehle mit dem Betreff - E3 BEFEHLE an eressea-server@eressea.de zu senden. - Remember to send your orders to - eressea-server@eressea.de with the subject E3 ORDERS. - - - E3 BEFEHLE - E3 ORDERS - - - diff --git a/src/bindings.c b/src/bindings.c index 6b45bfb2f..3bd2db543 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -462,9 +462,14 @@ static int tolua_init_reports(lua_State * L) static int tolua_write_report(lua_State * L) { faction *f = (faction *) tolua_tousertype(L, 1, 0); - time_t ltime = time(0); - int result = write_reports(f, ltime); - tolua_pushnumber(L, (lua_Number) result); + if (f) { + time_t ltime = time(0); + int result = write_reports(f, ltime); + tolua_pushnumber(L, (lua_Number)result); + } + else { + tolua_pushstring(L, "function expects a faction, got nil"); + } return 1; } @@ -997,8 +1002,8 @@ int tolua_read_xml(lua_State * L) { const char *filename = tolua_tostring(L, 1, "config.xml"); const char *catalog = tolua_tostring(L, 2, "catalog.xml"); - init_data(filename, catalog); - return 0; + lua_pushinteger(L, init_data(filename, catalog)); + return 1; } typedef struct event_args { @@ -1204,9 +1209,13 @@ int eressea_run(lua_State *L, const char *luafile) /* run the main script */ if (luafile) { log_debug("executing script %s\n", luafile); + + lua_getglobal(L, "debug"); + lua_getfield(L, -1, "traceback"); + lua_remove(L, -2); lua_getglobal(L, "dofile"); lua_pushstring(L, luafile); - err = lua_pcall(L, 1, 1, 0); + err = lua_pcall(L, 1, 1, -3); if (err != 0) { log_lua_error(L); abort(); diff --git a/src/util/xml.c b/src/util/xml.c index b1647838e..0755e7331 100644 --- a/src/util/xml.c +++ b/src/util/xml.c @@ -108,29 +108,28 @@ int read_xml(const char *filename, const char *catalog) { xml_reader *reader = xmlReaders; xmlDocPtr doc; + int result; if (catalog) { xmlLoadCatalog(catalog); } -#ifdef XML_PARSE_XINCLUDE - doc = xmlReadFile(filename, NULL, XML_PARSE_XINCLUDE|XML_PARSE_NONET|XML_PARSE_PEDANTIC); -#else - doc = xmlParseFile(filename); -#endif + doc = xmlReadFile(filename, NULL, XML_PARSE_XINCLUDE | XML_PARSE_NONET | XML_PARSE_PEDANTIC | XML_PARSE_COMPACT); if (doc == NULL) { log_error("could not open '%s'\n", filename); return -1; } - xmlXIncludeProcess(doc); - - while (reader != NULL) { - int i = reader->callback(doc); - if (i != 0) { - return i; - } - reader = reader->next; + result = xmlXIncludeProcessFlags(doc, XML_PARSE_XINCLUDE | XML_PARSE_NONET | XML_PARSE_PEDANTIC | XML_PARSE_COMPACT); + if (result >= 0) { + while (reader != NULL) { + int i = reader->callback(doc); + if (i != 0) { + return i; + } + reader = reader->next; + } + result = 0; } xmlFreeDoc(doc); - return 0; + return result; } From 1588d31a7e5668a35cd862a9d2a1a8021ecbc827 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 24 Apr 2014 15:39:14 +0200 Subject: [PATCH 09/18] fix test runner --- game-e2/eressea.ini | 1 - src/CMakeLists.txt | 12 ++++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/game-e2/eressea.ini b/game-e2/eressea.ini index 8e4eff430..f92fcd87e 100644 --- a/game-e2/eressea.ini +++ b/game-e2/eressea.ini @@ -1,6 +1,5 @@ [eressea] base = . -load = setup.lua report = reports verbose = 0 lomem = 0 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0aacc8d76..fde5e11f0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -152,7 +152,15 @@ target_link_libraries(test_eressea ) add_test(server test_eressea) -add_test(NAME E3 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e3 COMMAND $ -e run_tests) -add_test(NAME E2 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e2 COMMAND $ -e run_tests) +add_test( + NAME E3 + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e3 + COMMAND $ runtests.lua + ) +add_test( + NAME E2 + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/game-e2 + COMMAND $ runtests.lua + ) install(TARGETS eressea DESTINATION bin) From 35c2baafbe7c0905b52e4c2b77c412f2a2ee3b1e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 25 Apr 2014 07:47:23 +0200 Subject: [PATCH 10/18] do not read turn file for turn 0 --- core/scripts/init.lua | 4 ++-- scripts/e3a/rules.lua | 2 +- src/kernel/config.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/scripts/init.lua b/core/scripts/init.lua index 83bcc4f63..b184887c3 100755 --- a/core/scripts/init.lua +++ b/core/scripts/init.lua @@ -4,7 +4,7 @@ require "resources" function run_editor() local turn = get_turn() - if turn==0 then + if turn<0 then turn = read_turn() set_turn(turn) end @@ -28,7 +28,7 @@ function run_turn() require(config.game .. ".main") local turn = get_turn() - if turn==0 then + if turn<0 then turn = read_turn() set_turn(turn) end diff --git a/scripts/e3a/rules.lua b/scripts/e3a/rules.lua index 2d137d2d0..0c5e9be85 100644 --- a/scripts/e3a/rules.lua +++ b/scripts/e3a/rules.lua @@ -8,7 +8,7 @@ function item_canuse(u, iname) end end if iname=="rep_crossbow" then - -- only dwarves and halflings allowed to use towershield + -- only dwarves and halflings allowed to use repeating crossbow return race=="dwarf" or race=="halfling" end if iname=="scale" then diff --git a/src/kernel/config.c b/src/kernel/config.c index e1647d7e4..5c285bec6 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -98,7 +98,7 @@ FILE *updatelog; const struct race *new_race[MAXRACES]; bool sqlpatch = false; bool battledebug = false; -int turn = 0; +int turn = -1; int NewbieImmunity(void) { From 0c06c86fb91ef3e748493a10827f406fc160ce7c Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 24 Apr 2014 23:36:54 -0700 Subject: [PATCH 11/18] do not kill the wrong nonstarters --- core/scripts/multis.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/scripts/multis.lua b/core/scripts/multis.lua index ad0b77783..e7157a40f 100644 --- a/core/scripts/multis.lua +++ b/core/scripts/multis.lua @@ -1,6 +1,6 @@ function kill_nonstarters() for f in factions() do - if f.lastturn==1 then + if f.lastturn==0 then kill_faction(f, true) end end From 94e8a65d4f6c9af3a3d13b5f7cee0f8bd79f278c Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Fri, 25 Apr 2014 08:41:52 +0200 Subject: [PATCH 12/18] processing scripts and tools --- scripts/run-turn.lua | 2 + scripts/setup.lua | 15 ++++++ scripts/tools/build.lua | 100 ++++++++++++++++++++++++++++++++++++ scripts/tools/oceanfill.lua | 11 ++++ scripts/tools/reimburse.lua | 37 +++++++++++++ scripts/tools/reports.lua | 4 ++ 6 files changed, 169 insertions(+) create mode 100644 scripts/run-turn.lua create mode 100644 scripts/setup.lua create mode 100644 scripts/tools/build.lua create mode 100644 scripts/tools/oceanfill.lua create mode 100644 scripts/tools/reimburse.lua create mode 100644 scripts/tools/reports.lua diff --git a/scripts/run-turn.lua b/scripts/run-turn.lua new file mode 100644 index 000000000..615d9eeab --- /dev/null +++ b/scripts/run-turn.lua @@ -0,0 +1,2 @@ +require "setup" +run_turn() diff --git a/scripts/setup.lua b/scripts/setup.lua new file mode 100644 index 000000000..116ee94d4 --- /dev/null +++ b/scripts/setup.lua @@ -0,0 +1,15 @@ +local srcpath = config.source_dir +local respath = srcpath .. '/res' +local paths = { + 'scripts/?.lua', + 'core/scripts/?.lua', + 'lunit/?.lua' +} + +for idx, path in pairs(paths) do + package.path = srcpath .. '/' .. path .. ';' .. package.path +end + +read_xml() + +require "init" diff --git a/scripts/tools/build.lua b/scripts/tools/build.lua new file mode 100644 index 000000000..1e8c6f5d5 --- /dev/null +++ b/scripts/tools/build.lua @@ -0,0 +1,100 @@ +function new_faction(email, race, lang, r) + f = faction.create(email, race, lang) + u = unit.create(f, r, 10) + u:add_item("log", 5); + u:add_item("horse", 2); + u:add_item("silver", 1000); + u:add_item("adamantium", 1); +end + +function get_homes(f) + homes={} + for u in f.units do + table.insert(homes, u.region) + end + return homes +end + +if eressea~=nil then + eressea.free_game() + eressea.read_game("game4.dat") + homes = get_homes(get_faction("xin8")) +else + -- running in the lua interpreter, not eressea. fake it. + new_faction = print + eressea = { ['write_game'] = function(s) print("writing " .. s) end } + homes = { "Andune", "Bedap", "Curtis", "Dovre" } +end + +local f=assert(io.open("factions", "r")) +line=f:read("*line") +players = {} +emails = {} +patrons = {} +nplayers = 0 +while line~=nil do + fields = {} + line:gsub("([^\t]*)\t*", function(c) table.insert(fields, c) end) + line=f:read("*line") + email = fields[1] + if fields[2]=='yes' then + table.insert(patrons, email) + else + table.insert(emails, email) + end + if fields[3]=='German' then lang='de' else lang='en' end + race=string.gsub(fields[4], "/.*", ''):lower() + players[email] = { ['lang'] = lang, ['race'] = race } + nplayers = nplayers + 1 +end + +for k, r in ipairs(homes) do + print(k, r) +end +npatrons = #patrons +print(#homes .. " regions.") +print(nplayers .. " players.") +print(npatrons .. " patrons.") + +maxfactions = 20 +selected = {} +if maxfactions > nplayers then maxfactions = nplayers end +while maxfactions > 0 do + if npatrons > 0 then + email = patrons[npatrons] + patrons[npatrons] = nil + npatrons = npatrons - 1 + else + local np = #emails + local i = math.random(np) + email = emails[i] + emails[i] = emails[np] + emails[np] = nil + end + local player = players[email] + player.email = email + table.insert(selected, player) + maxfactions = maxfactions - 1 +end + +-- random shuffle +for j=1,#selected do + k = math.random(j) + if k ~= j then + local temp = selected[j] + selected[j] = selected[k] + selected[k] = temp + end +end + +print('## players') +for k, player in ipairs(selected) do + local r = homes[1 + k % #homes] + new_faction(player.email, player.race, player.lang, r) + print(player.email) +end +eressea.write_game("game4.dat") +print("## no faction") +for i, email in ipairs(emails) do + print(email) +end diff --git a/scripts/tools/oceanfill.lua b/scripts/tools/oceanfill.lua new file mode 100644 index 000000000..8025897f6 --- /dev/null +++ b/scripts/tools/oceanfill.lua @@ -0,0 +1,11 @@ +p = plane.get(0) +w, h = p:size() +print(p, w, h) +for x=0,w-1 do + for y=0,h-1 do + r = get_region(x,y) + if r==nil then + r = region.create(x, y, "ocean") + end + end +end diff --git a/scripts/tools/reimburse.lua b/scripts/tools/reimburse.lua new file mode 100644 index 000000000..9877f0649 --- /dev/null +++ b/scripts/tools/reimburse.lua @@ -0,0 +1,37 @@ +require "config" + +function main() + for f in factions() do + if f.race=="demon" then + for u in f.units do + u.building.size = 2 + u.building.name = u.region.name .. " Keep" + u.name = "Lord " .. u.region.name + end + else + u = f.units() + u:add_item("money", 1000-u:get_item("money")) + u:add_item("adamantium", 1-u:get_item("adamantium")) + end + end + for r in regions() do for u in r.units do + print(u) + things = "" + comma = "" + for i in u.items do + things = things .. comma .. u:get_item(i) .. " " .. i + comma = ", " + end + print(' - ' .. things) + end end +end + +if eressea==nil then + print("this script is part of eressea") +else + config.read() + eressea.read_game('0.dat') + main() + eressea.write_game('0.dat') + print('done') +end diff --git a/scripts/tools/reports.lua b/scripts/tools/reports.lua new file mode 100644 index 000000000..127d9fc46 --- /dev/null +++ b/scripts/tools/reports.lua @@ -0,0 +1,4 @@ +read_xml() +eressea.read_game('0.dat') +init_reports() +write_reports() From 535a9e6e5376cd8a78e5d774c42378ea46702727 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 26 Apr 2014 12:24:51 -0700 Subject: [PATCH 13/18] fix number of units in the report header --- src/kernel/config.c | 43 ++++++++++++++++++------- src/kernel/config.h | 9 +++--- src/kernel/faction.c | 2 +- src/kernel/unit.c | 76 ++++++++++++++++++++++++-------------------- src/report.c | 10 ++++-- 5 files changed, 88 insertions(+), 52 deletions(-) diff --git a/src/kernel/config.c b/src/kernel/config.c index 5c285bec6..41e994e5c 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -1196,19 +1196,40 @@ void update_lighthouse(building * lh) int count_all(const faction * f) { #ifndef NDEBUG - int n = 0; - unit *u; - for (u = f->units; u; u = u->nextF) { - if (playerrace(u_race(u))) { - n += u->number; - assert(f == u->faction); + unit *u; + int np = 0, n = 0; + for (u = f->units; u; u = u->nextF) { + assert(f == u->faction); + n += u->number; + if (playerrace(u_race(u))) { + np += u->number; + } + } + if (f->num_people != np) { + log_error("# of people in %s is != num_people: %d should be %d.\n", factionid(f), f->num_people, np); + } + if (f->num_total != n) { + log_error("# of men in %s is != num_total: %d should be %d.\n", factionid(f), f->num_total, n); } - } - if (f->num_people != n) { - log_error("# of people in %s is != num_people: %d should be %d.\n", factionid(f), f->num_people, n); - } #endif - return f->num_people; + return (f->flags & FFL_NPC) ? f->num_total : f->num_people; +} + +int count_units(const faction * f) +{ +#ifndef NDEBUG + unit *u; + int n = 0, np = 0; + for (u = f->units; u; u = u->nextF) { + ++n; + if (playerrace(u_race(u))) ++np; + } + n = (f->flags & FFL_NPC) ? n : np; + if (f->no_units && n != f->no_units) { + log_warning("# of units in %s is != no_units: %d should be %d.\n", factionid(f), f->no_units, n); + } +#endif + return n; } int count_migrants(const faction * f) diff --git a/src/kernel/config.h b/src/kernel/config.h index f4b2ceff6..b7c7e053c 100644 --- a/src/kernel/config.h +++ b/src/kernel/config.h @@ -256,9 +256,10 @@ extern "C" { extern int rule_alliance_limit(void); extern int rule_faction_limit(void); - extern int count_all(const struct faction *f); - extern int count_migrants(const struct faction *f); - extern int count_maxmigrants(const struct faction *f); + int count_units(const struct faction * f); + int count_all(const struct faction *f); + int count_migrants(const struct faction *f); + int count_maxmigrants(const struct faction *f); extern bool has_limited_skills(const struct unit *u); extern const struct race *findrace(const char *, const struct locale *); @@ -449,7 +450,7 @@ extern "C" { extern struct attrib_type at_guard; extern void free_gamedata(void); #if 1 /* disable to count all units */ -# define count_unit(u) playerrace(u_race(u)) +# define count_unit(u) (u->number>0 && playerrace(u_race(u))) #else # define count_unit(u) 1 #endif diff --git a/src/kernel/faction.c b/src/kernel/faction.c index 456cc3133..34d7071e2 100755 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -112,7 +112,7 @@ faction *get_monsters(void) if (!monsters) { faction *f; for (f = factions; f; f = f->next) { - if (f->flags & FFL_NPC) { + if ((f->flags & FFL_NPC) && !(f->flags & FFL_DEFENDER)) { return monsters = f; } } diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 1be61c138..568d8161f 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1070,43 +1070,51 @@ struct building *inside_building(const struct unit *u) void u_setfaction(unit * u, faction * f) { - int cnt = u->number; + int cnt = u->number; + if (u->faction == f) + return; + if (u->faction) { + if (count_unit(u)) { + --u->faction->no_units; + } + set_number(u, 0); + join_group(u, NULL); + free_orders(&u->orders); + set_order(&u->thisorder, NULL); - if (u->faction == f) - return; - if (u->faction) { - set_number(u, 0); - if (count_unit(u)) - --u->faction->no_units; - join_group(u, NULL); - free_orders(&u->orders); - set_order(&u->thisorder, NULL); + if (u->nextF) { + u->nextF->prevF = u->prevF; + } + if (u->prevF) { + u->prevF->nextF = u->nextF; + } + else { + u->faction->units = u->nextF; + } + } - if (u->nextF) - u->nextF->prevF = u->prevF; - if (u->prevF) - u->prevF->nextF = u->nextF; - else - u->faction->units = u->nextF; - } + if (f != NULL) { + if (f->units) { + f->units->prevF = u; + } + u->prevF = NULL; + u->nextF = f->units; + f->units = u; + } + else { + u->nextF = NULL; + } - if (f != NULL) { - if (f->units) - f->units->prevF = u; - u->prevF = NULL; - u->nextF = f->units; - f->units = u; - } else - u->nextF = NULL; - - u->faction = f; - if (u->region) - update_interval(f, u->region); - if (cnt && f) { - set_number(u, cnt); - if (count_unit(u)) - ++f->no_units; - } + u->faction = f; + if (u->region) { + update_interval(f, u->region); + } + if (cnt) { + set_number(u, cnt); + } + if (f && count_unit(u)) { + ++f->no_units; + } } /* vorsicht Sprüche können u->number == RS_FARVISION haben! */ diff --git a/src/report.c b/src/report.c index 415897f3a..261d0a2cc 100644 --- a/src/report.c +++ b/src/report.c @@ -2199,8 +2199,14 @@ report_plaintext(const char *filename, report_context * ctx, f->num_people = no_people; } #else - no_units = f->no_units; - no_people = f->num_people; + no_units = count_units(f); + no_people = count_all(f); + if (f->flags & FFL_NPC) { + no_people = f->num_total; + } + else { + no_people = f->num_people; + } #endif m = msg_message("nr_population", "population units", no_people, no_units); nr_render(m, f->locale, buf, sizeof(buf), f); From 75b7ac30643a77f5c46a92f6160e141087a15ab0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 27 Apr 2014 02:59:02 +0200 Subject: [PATCH 14/18] new turn processing script, add adamantium to config --- res/e3a/strings.xml | 31 +++++++++++++++++++++++++++++++ scripts/tools/reimburse.lua | 6 +++--- scripts/{ => tools}/run-turn.lua | 0 3 files changed, 34 insertions(+), 3 deletions(-) rename scripts/{ => tools}/run-turn.lua (100%) diff --git a/res/e3a/strings.xml b/res/e3a/strings.xml index 3b368a28e..9e6e83bd8 100644 --- a/res/e3a/strings.xml +++ b/res/e3a/strings.xml @@ -7,6 +7,37 @@ _a: including article (ein Troll, a troll) --> + + Adamantium + adamantium + + + Adamantium + adamantium + + + Adamantium + adamantium + + + + Adamantiumaxt + adamantium axe + + + Adamantiumäxte + adamantium axes + + + + Adamantiumrüstung + adamantium plate + + + Adamantiumrüstungen + adamantium plates + + des dritten Zeitalters diff --git a/scripts/tools/reimburse.lua b/scripts/tools/reimburse.lua index 9877f0649..80838fb83 100644 --- a/scripts/tools/reimburse.lua +++ b/scripts/tools/reimburse.lua @@ -1,12 +1,12 @@ -require "config" - function main() for f in factions() do if f.race=="demon" then + f.flags = 2147484672 for u in f.units do u.building.size = 2 u.building.name = u.region.name .. " Keep" u.name = "Lord " .. u.region.name + u:add_item("money", 1000-u:get_item("money")) end else u = f.units() @@ -29,7 +29,7 @@ end if eressea==nil then print("this script is part of eressea") else - config.read() + read_xml() eressea.read_game('0.dat') main() eressea.write_game('0.dat') diff --git a/scripts/run-turn.lua b/scripts/tools/run-turn.lua similarity index 100% rename from scripts/run-turn.lua rename to scripts/tools/run-turn.lua From ce347a95c91a89eaff084ce75ede4459c7a79edd Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 27 Apr 2014 03:03:14 +0200 Subject: [PATCH 15/18] order processing and other scripts --- process/compress.py | 72 +++++++++++++ process/orders-accept | 2 + process/orders-process | 218 ++++++++++++++++++++++++++++++++++++++++ process/orders.cron | 16 +++ process/send-bz2-report | 35 +++++++ process/send-zip-report | 46 +++++++++ 6 files changed, 389 insertions(+) create mode 100755 process/compress.py create mode 100755 process/orders-accept create mode 100755 process/orders-process create mode 100755 process/orders.cron create mode 100755 process/send-bz2-report create mode 100755 process/send-zip-report diff --git a/process/compress.py b/process/compress.py new file mode 100755 index 000000000..539f1a032 --- /dev/null +++ b/process/compress.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +from sys import argv, exit +from string import join +from os import access, R_OK +from os import system + +gamename='Eressea' + +if(len(argv) >= 3): + gamename=argv[2] + +template="""#!/bin/bash +#PATH=$PATH:$HOME/bin + +addr=%(email)s +[ $# -ge 1 ] && addr=$1 +[ -z $addr ] || send-%(compression)s-report $addr '%(gamename)s Report #%(turn)s' %(files)s +""" + +turn = argv[1] +try: + infile = file("reports.txt", "r") +except: + print "%s: reports.txt file does not exist" % (argv[0], ) + exit(0) + +for line in infile.readlines(): + settings = line[:-1].split(":") + options = { "turn" : turn} + options["gamename"] = gamename + for setting in settings: + try: + key, value = setting.split("=") + options[key] = value + except: + print "Invalid input line", line + if not options.has_key("reports"): + continue + reports = options["reports"].split(",") +# reports = reports + [ "iso.cr" ] + prefix = "%(turn)s-%(faction)s." % options + files=[] + if options["compression"]=="zip": + output = prefix+"zip" + files = [output] + if (access(output, R_OK)): + pass + else: + parameters = [] + for extension in reports: + filename = "%s%s" % (prefix, extension) + if (access(filename, R_OK)): + parameters = parameters + [ filename ] + system("zip %s -q -m -j -1 %s" % (output, join(parameters," "))) + else: + for extension in reports: + if extension!='': + filename = "%s%s" % (prefix, extension) + output = "%s%s.bz2" % (prefix, extension) + files = files+[output] + if access(filename, R_OK): + if (access(output, R_OK)): + #print output, "exists, skipping" + continue + system("bzip2 %s" % filename) + #print files + options["files"] = join(files, " ") + batch = file("%s.sh" % options["faction"], "w") + batch.write(template % options) + batch.close() +infile.close() diff --git a/process/orders-accept b/process/orders-accept new file mode 100755 index 000000000..552a3e9a1 --- /dev/null +++ b/process/orders-accept @@ -0,0 +1,2 @@ +#/bin/.sh +grep -v '>From' | $HOME/src/scripts/bin/orders-accept $* diff --git a/process/orders-process b/process/orders-process new file mode 100755 index 000000000..d15d188cf --- /dev/null +++ b/process/orders-process @@ -0,0 +1,218 @@ +#!/usr/bin/env python +# -*- coding: iso-8859-1 -*- + +from os import unlink, symlink, rename, popen, tmpfile +from os.path import exists +from re import compile, IGNORECASE +from string import split, join, upper, strip +from sys import argv, exit +from time import sleep, time, ctime +from syslog import openlog, closelog, syslog + +from epasswd import EPasswd + +def pwd_get_email(faction, pwd, pwdfile=None): + return None + +def splitfilename(filename): + from os.path import split + return split(filename) + +def unlock_file(filename): + try: + unlink(filename+".lock") + except: + print "could not unlock %s.lock, file not found" % filename + raise + +def lock_file(filename): + i = 0 + wait = 1 + if not exists(filename): + file=open(filename, "w") + file.close() + while True: + try: + symlink(filename, filename+".lock") + return + except: + i = i+1 + if i == 5: + raise + sleep(wait) + wait = wait*2 + +messages = { +"subject-de": "Befehle angekommen", +"subject-en": "orders received", + +"validate-en": "Validating", +"validate-de": "Verarbeite", + +"faction-en": "Faction", +"faction-de": "Partei", + +"unknown-de": "WARNUNG: Die Partei ist nicht bekannt, oder das Passwort falsch!", +"unknown-en": "WARNING: This faction is unknown, or the password is incorrect!", + +"warning-de": "Warnung", +"warning-en": "Warning", + +"error-de": "Fehler", +"error-en": "Error", +} + + +# base directory for all your games: +rootdir = "/home/eressea/eressea" +frommail = "Eressea Server " +orderbase = "orders.dir" +sendmail = True +maxlines = 25 +echeck_cmd = "/home/eressea/echeck/echeck.sh" + +# regular expression that finds the start of a faction +fact_re = compile("^\s*(eressea|vinyambar|partei|faction)\s+([a-zA-Z0-9]+)\s+\"?([^\"]*)\"?", IGNORECASE) + +def check_pwd(filename, email, pw_data): + results = [] + try: + file = open(filename, "r") + except: + print "could not open file", filename + return results + for line in file.readlines(): + mo = fact_re.search(strip(line)) + if mo != None: + fact_nr = str(mo.group(2)) + fact_pw = str(mo.group(3)) + if pw_data.fac_exists(fact_nr): + if pw_data.check(fact_nr, fact_pw) == 0: + game_email = pw_data.get_email(fact_nr) + results = results + [ (fact_nr, game_email, False, fact_pw) ] + else: + game_email = pw_data.get_email(fact_nr) + results = results + [ (fact_nr, game_email, True, fact_pw) ] + else: + results = results + [ (fact_nr, None, False, fact_pw) ] + return results + +def echeck(filename, locale, rules): + dirname, filename = splitfilename(filename) + stream = popen("%s %s %s %s %s" % (echeck_cmd, locale, filename, dirname, rules), 'r') + lines = stream.readlines() + if len(lines)==0: + stream.close() + return None + if len(lines)>maxlines: + mail = join(lines[:maxlines-3] + ["...", "\n"] + lines[-3:], '') + else: + mail = join(lines[:maxlines], '') + stream.close() + return mail + +## the main body of the script +game = int(argv[1]) + +basedir = rootdir + "/game-%d" % (game, ) +queuename = basedir + "/orders.queue" +if not exists(queuename): + exit(0) + +# parse the queue file - +#print "connecting to SMTP..." +from smtplib import SMTP +try: + server = SMTP("localhost") +except: + print "could not connect to SMTP server" + exit(0) +#print "reading password file..." +pw_data = EPasswd(basedir + "/passwd") + +#print "reading orders.queue..." +# move the queue file to a save space while locking it: +try: + lock_file(queuename) +except: + exit(0) +queuefile = open(queuename, "r") +lines = queuefile.readlines() +queuefile.close() + +# copy to a temp file + +tname="/tmp/orders.queue.%s" % str(time()) +try: + lock_file(tname) +except: + exit(0) +tmpfile=open(tname, "w") +for line in lines: + tmpfile.write(line) +tmpfile.close() + +openlog("orders") + +unlink(queuename) +try: + unlock_file(queuename) +except: + pass + +for line in lines: + tokens = split(line[:-1], ' ') + dict = {} + for token in tokens: + name, value = split(token, '=') + dict[name] = value + + email = dict["email"] + locale = dict["locale"] + game = int(dict["game"]) + file = dict["file"] + gamename='[E%d]' % game + rules='e%d' % game + warning = "" + failed = True + results = check_pwd(file, email, pw_data) + logfile = open(basedir+"/zug.log", "a") + dirname, filename = splitfilename(file) + msg = messages["validate-"+locale] + " " + filename + "\n\n" + for faction, game_email, success, pwd in results: + msg = msg + messages["faction-"+locale] + " " + faction + "\n" + if success: failed = False + else: msg = msg + messages["unknown-"+locale] + "\n" + msg = msg + "\n" + logfile.write("%s:%s:%s:%s:%s:%s\n" % (ctime(time()), email, game_email, faction, pwd, success)) + logfile.close() + + if failed: + warning = " (" + messages["warning-" + locale] + ")" + syslog("failed - no valid password in " + file) + else: + result = echeck(file, locale, rules) + if email=='eressea': + print result + continue + elif result is None: + # echeck did not finish + msg = msg + "Echeck was killed. Your turn was accepted, but could not be verified.\n" + warning = " (" + messages["warning-" + locale] + ")" + syslog("process - echeck got killed, " + file) + else: + msg = msg + result + syslog("process - checked orders in " + file) + + subject = gamename + " " + messages["subject-" + locale] + warning + msg = "Subject: %s\nFrom: %s\nTo: %s\nContent-Type: text/plain; charset=utf-8\n\n" % (subject, frommail, email) + msg + try: + server.sendmail(frommail, email, msg) + except: + syslog("failed - cannot send to " + email) + +server.close() + +closelog() +unlink(tname) +unlock_file(tname) diff --git a/process/orders.cron b/process/orders.cron new file mode 100755 index 000000000..be573fe86 --- /dev/null +++ b/process/orders.cron @@ -0,0 +1,16 @@ +#!/bin/bash + +## this script processes incoming order files. +# files are delivered into an incoming queue by procmail, then cron runs +# this here script to make a non-blocking syntax check and reject or +# accept the order file. + +for GAME in $* +do + if [ "$GAME" == "eressea" ]; then GAME=2 ; fi + if [ "$GAME" == "e3a" ]; then GAME=3 ; fi + if [ -e $HOME/eressea/game-$GAME/orders.queue ] + then + $HOME/bin/orders-process $GAME + fi +done diff --git a/process/send-bz2-report b/process/send-bz2-report new file mode 100755 index 000000000..733b5c6be --- /dev/null +++ b/process/send-bz2-report @@ -0,0 +1,35 @@ +#!/bin/bash +if [ -z $ERESSEA ]; then + echo "You have to define the \$ERESSEA environment variable to run $0" + exit -2 +fi +source $HOME/bin/functions.sh +source $ERESSEA/etc/eressea.conf + +TEMPLATE=report-mail.txt +if [ "$1" == "-Lde" ] +then + TEMPLATE=report-mail.de.txt + shift +fi + +if [ "$1" == "-Lde" ] +then + TEMPLATE=report-mail.en.txt + shift +fi + +EMAIL=$1 +SUBJECT=$2 +shift 2 + +ATTACHMENTS="" +while [ $# -gt 0 ] +do + if [ -e "$1" ]; then + ATTACHMENTS="-a $1 $ATTACHMENTS" + fi + shift +done + +cat $ERESSEA/etc/$TEMPLATE | mutt -F $ERESSEA/etc/muttrc -s "$SUBJECT" $ATTACHMENTS -- $EMAIL diff --git a/process/send-zip-report b/process/send-zip-report new file mode 100755 index 000000000..fb068c33f --- /dev/null +++ b/process/send-zip-report @@ -0,0 +1,46 @@ +#!/bin/bash +if [ -z $ERESSEA ]; then + ERESSEA=`echo $PWD |sed -e 's/\/game.*//'` + echo "Assuming that ERESSEA=$ERESSEA" +fi +if [ ! -f reports.txt ]; then + echo "need to run $0 from the report direcory" + exit -2 +fi + +PWD=$(pwd) +GAME=$(dirname $PWD) + +TEMPLATE=report-mail.txt +if [ "$1" == "-Lde" ] +then + TEMPLATE=report-mail.de.txt + shift +fi + +if [ "$1" == "-Len" ] +then + TEMPLATE=report-mail.en.txt + shift +fi + +if [ -e $GAME/$TEMPLATE ]; then +TEMPLATE=$GAME/$TEMPLATE +else +TEMPLATE=$ERESSEA/etc/$TEMPLATE +fi + +if [ ! -e $TEMPLATE ]; then + echo "no such email template: $TEMPLATE" + exit -3 +fi + +while [ -e /tmp/.stopped ] ; do + echo "waiting 2 minutes for lockfile in /tmp/.stopped to clear" + sleep 120 +done +mutt -F $ERESSEA/etc/muttrc -s "$2" -a "$3" -- $1 < $TEMPLATE + +if [ $? -ne 0 ] ; then + echo "Sending failed for email/report: $2/$3" +fi From 1318c8976ab8201d116896e6643fb45527b496c2 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 3 May 2014 19:53:59 +0200 Subject: [PATCH 16/18] e4 startup --- res/e4/main.lua | 61 ++++++++++++++++++++++++++++++++++++++++++++++ res/e4/modules.lua | 4 +++ 2 files changed, 65 insertions(+) create mode 100644 res/e4/main.lua create mode 100644 res/e4/modules.lua diff --git a/res/e4/main.lua b/res/e4/main.lua new file mode 100644 index 000000000..813e0f55a --- /dev/null +++ b/res/e4/main.lua @@ -0,0 +1,61 @@ +require "multis" +require "e3a.frost" + +function process(orders) + local confirmed_multis = { } + local suspected_multis = { } + + if open_game(get_turn())~=0 then + print("could not read game") + return -1 + end + init_summary() + + -- run the turn: + if read_orders(orders) ~= 0 then + print("could not read " .. orders) + return -1 + end + + -- plan_monsters() + local mon = get_faction(666) + if mon ~= nil then + mon.lastturn = get_turn() + end + + if nmr_check(config.maxnmrs or 30)~=0 then + return -1 + end + + process_orders() + if xmas2009~=nil then + xmas2009() + end + + -- create new monsters: + spawn_dragons() + spawn_undead() + + if get_turn()>=config.kill_after then + kill_nonstarters() + end + -- post-turn updates: + update_guards() + update_scores() + frost.update() + + local localechange = { en = { "L46o" } } + change_locales(localechange) + + -- use newfactions file to place out new players + -- autoseed(config.basepath .. "/newfactions", false) + + write_files(config.locales) + + file = "" .. get_turn() .. ".dat" + if eressea.write_game(file)~=0 then + print("could not write game") + return -1 + end + return 0 +end diff --git a/res/e4/modules.lua b/res/e4/modules.lua new file mode 100644 index 000000000..39a1d7ca0 --- /dev/null +++ b/res/e4/modules.lua @@ -0,0 +1,4 @@ +require "spells" +require "e3a.rules" +require "e3a.markets" +require "e3a.frost" From cb458db9a1b89721be4a47157d34c285977d2aef Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 4 May 2014 22:49:06 +0200 Subject: [PATCH 17/18] updating eressea.db, not with a fixed game-id --- src/bind_sqlite.c | 5 +- src/kernel/config.c | 8 +- src/kernel/config.h | 1 + src/kernel/sqlite.c | 232 +++++++++++++++++++++++--------------------- 4 files changed, 130 insertions(+), 116 deletions(-) diff --git a/src/bind_sqlite.c b/src/bind_sqlite.c index 6f6c5ea29..b164068aa 100644 --- a/src/bind_sqlite.c +++ b/src/bind_sqlite.c @@ -15,16 +15,17 @@ without prior permission by the authors of Eressea. #include "bind_unit.h" #include "bindings.h" +#include #include #include #define LTYPE_DB TOLUA_CAST "db" -extern int db_update_factions(sqlite3 * db, bool force); +extern int db_update_factions(sqlite3 * db, bool force, int game); static int tolua_db_update_factions(lua_State * L) { sqlite3 *db = (sqlite3 *) tolua_tousertype(L, 1, 0); - db_update_factions(db, tolua_toboolean(L, 2, 0)); + db_update_factions(db, tolua_toboolean(L, 2, 0), global.game_id); return 0; } diff --git a/src/kernel/config.c b/src/kernel/config.c index 41e994e5c..6822d42d0 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -93,6 +93,7 @@ struct settings global = { "Eressea", /* gamename */ }; +bool lomem = false; FILE *logfile; FILE *updatelog; const struct race *new_race[MAXRACES]; @@ -1578,10 +1579,6 @@ void freestrlist(strlist * s) } } -/* - Meldungen und Fehler ------------------------------------------------- */ - -bool lomem = false; - /* - Namen der Strukturen -------------------------------------- */ typedef char name[OBJECTIDSIZE + 1]; static name idbuf[8]; @@ -3168,7 +3165,6 @@ void load_inifile(dictionary * d) make_locales(str); /* excerpt from [config] (the rest is used in bindings.c) */ - game_name = iniparser_getstring(d, "config:game", game_name); - + global.game_id = iniparser_getint(d, "config:game_id", 0); global.inifile = d; } diff --git a/src/kernel/config.h b/src/kernel/config.h index b7c7e053c..885684d99 100644 --- a/src/kernel/config.h +++ b/src/kernel/config.h @@ -393,6 +393,7 @@ extern "C" { void *vm_state; float producexpchance; int cookie; + int game_id; int data_version; /* TODO: eliminate in favor of gamedata.version */ struct _dictionary_ *inifile; diff --git a/src/kernel/sqlite.c b/src/kernel/sqlite.c index cb9e8b903..a84cea6b2 100644 --- a/src/kernel/sqlite.c +++ b/src/kernel/sqlite.c @@ -129,117 +129,133 @@ db_update_email(sqlite3 * db, const faction * f, const db_faction * dbstate, return SQLITE_OK; } - -int db_update_factions(sqlite3 * db, bool force) -{ - int game_id = 6; - const char sql_select[] = - "SELECT faction.id, faction.email_id, faction.code, email.email, faction.password_md5, faction.name, faction.lastturn FROM email, faction" - " WHERE email.id=faction.email_id AND faction.game_id=? AND (lastturn IS NULL OR lastturn>?)"; - sqlite3_stmt *stmt_select = stmt_cache_get(db, sql_select); - faction *f; - int res; - - res = sqlite3_bind_int(stmt_select, 1, game_id); - SQL_EXPECT(res, SQLITE_OK); - res = sqlite3_bind_int(stmt_select, 2, turn - 2); - SQL_EXPECT(res, SQLITE_OK); - for (;;) { - sqlite3_uint64 id_faction; - int lastturn; - +/* +int db_get_game(sqlite3 *db) { + int game_id = 0; + const char sql_game[] = + "SELECT id FROM game WHERE name=?"; + sqlite3_stmt *stmt_game = stmt_cache(db, sql_game); + res = sqlite3_bind_text(stmt_game, 1, gamename, -1, SQLITE_TRANSIENT); + SQL_EXPECT(res, SQLITE_OK); res = sqlite3_step(stmt_select); - if (res != SQLITE_ROW) - break; - - id_faction = sqlite3_column_int64(stmt_select, 0); - lastturn = sqlite3_column_int(stmt_select, 6); - f = get_faction_by_id((int)id_faction); - - if (f == NULL || !f->alive) { - if (lastturn == 0) { - const char sql_update[] = "UPDATE faction SET lastturn=? WHERE id=?"; - sqlite3_stmt *stmt = stmt_cache_get(db, sql_update); - - lastturn = f ? f->lastorders : turn - 1; - sqlite3_bind_int(stmt, 1, lastturn); - sqlite3_bind_int64(stmt, 2, id_faction); - res = sqlite3_step(stmt); - SQL_EXPECT(res, SQLITE_DONE); - } - } else { - md5_state_t ms; - md5_byte_t digest[16]; - int i; - char passwd_md5[MD5_LENGTH_0]; - sqlite3_uint64 id_email; - bool update = force; - db_faction dbstate; - const char *no_b36; - - fset(f, FFL_MARK); - dbstate.id_faction = id_faction; - dbstate.id_email = sqlite3_column_int64(stmt_select, 1); - no_b36 = (const char *)sqlite3_column_text(stmt_select, 2); - dbstate.no = no_b36 ? atoi36(no_b36) : -1; - dbstate.email = (const char *)sqlite3_column_text(stmt_select, 3); - dbstate.passwd_md5 = (const char *)sqlite3_column_text(stmt_select, 4); - dbstate.name = (const char *)sqlite3_column_text(stmt_select, 5); - - id_email = dbstate.id_email; - res = db_update_email(db, f, &dbstate, force, &id_email); - SQL_EXPECT(res, SQLITE_OK); - - md5_init(&ms); - md5_append(&ms, (md5_byte_t *) f->passw, (int)strlen(f->passw)); - md5_finish(&ms, digest); - for (i = 0; i != 16; ++i) - sprintf(passwd_md5 + 2 * i, "%.02x", digest[i]); - - if (!update) { - update = ((id_email != 0 && dbstate.id_email != id_email) - || dbstate.no != f->no || dbstate.passwd_md5 == NULL - || strcmp(passwd_md5, dbstate.passwd_md5) != 0 || dbstate.name == NULL - || strncmp(f->name, dbstate.name, MAX_FACTION_NAME) != 0); - } - if (update) { - const char sql_update_faction[] = - "UPDATE faction SET email_id=?, password_md5=?, code=?, name=?, firstturn=? WHERE id=?"; - sqlite3_stmt *stmt_update_faction = - stmt_cache_get(db, sql_update_faction); - - res = sqlite3_bind_int64(stmt_update_faction, 1, id_email); - SQL_EXPECT(res, SQLITE_OK); - res = - sqlite3_bind_text(stmt_update_faction, 2, passwd_md5, MD5_LENGTH, - SQLITE_TRANSIENT); - SQL_EXPECT(res, SQLITE_OK); - res = - sqlite3_bind_text(stmt_update_faction, 3, no_b36, -1, - SQLITE_TRANSIENT); - SQL_EXPECT(res, SQLITE_OK); - res = - sqlite3_bind_text(stmt_update_faction, 4, f->name, -1, - SQLITE_TRANSIENT); - SQL_EXPECT(res, SQLITE_OK); - res = sqlite3_bind_int(stmt_update_faction, 5, turn - f->age); - SQL_EXPECT(res, SQLITE_OK); - res = sqlite3_bind_int64(stmt_update_faction, 6, f->subscription); - SQL_EXPECT(res, SQLITE_OK); - res = sqlite3_step(stmt_update_faction); - SQL_EXPECT(res, SQLITE_DONE); - } + return game_id; +} +*/ +int db_update_factions(sqlite3 * db, bool force, int game_id) +{ + const char sql_select[] = + "SELECT faction.id, faction.email_id, faction.code, email.email, faction.password_md5, faction.name, faction.lastturn FROM email, faction" + " WHERE email.id=faction.email_id AND faction.game_id=? AND (lastturn IS NULL OR lastturn>?)"; + sqlite3_stmt *stmt_select = stmt_cache_get(db, sql_select); + faction *f; + int res; + + res = sqlite3_bind_int(stmt_select, 1, game_id); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_bind_int(stmt_select, 2, turn - 2); + SQL_EXPECT(res, SQLITE_OK); + for (;;) { + sqlite3_uint64 id_faction; + int lastturn; + const char * no_b36; + + res = sqlite3_step(stmt_select); + if (res != SQLITE_ROW) + break; + + id_faction = sqlite3_column_int64(stmt_select, 0); + lastturn = sqlite3_column_int(stmt_select, 6); + no_b36 = (const char *)sqlite3_column_text(stmt_select, 2); + f = get_faction_by_id((int)id_faction); + if (!f) { + int no = atoi36(no_b36); + f = findfaction(no); + if (f) { + f->subscription = (int)id_faction; + } + } + if (!f || !f->alive) { + if (lastturn == 0) { + const char sql_update[] = "UPDATE faction SET lastturn=? WHERE id=?"; + sqlite3_stmt *stmt = stmt_cache_get(db, sql_update); + + lastturn = f ? f->lastorders : turn - 1; + sqlite3_bind_int(stmt, 1, lastturn); + sqlite3_bind_int64(stmt, 2, id_faction); + res = sqlite3_step(stmt); + SQL_EXPECT(res, SQLITE_DONE); + } + } else { + md5_state_t ms; + md5_byte_t digest[16]; + int i; + char passwd_md5[MD5_LENGTH_0]; + sqlite3_uint64 id_email; + bool update = force; + db_faction dbstate; + + fset(f, FFL_MARK); + dbstate.id_faction = id_faction; + dbstate.id_email = sqlite3_column_int64(stmt_select, 1); + dbstate.no = no_b36 ? atoi36(no_b36) : -1; + dbstate.email = (const char *)sqlite3_column_text(stmt_select, 3); + dbstate.passwd_md5 = (const char *)sqlite3_column_text(stmt_select, 4); + dbstate.name = (const char *)sqlite3_column_text(stmt_select, 5); + + id_email = dbstate.id_email; + res = db_update_email(db, f, &dbstate, force, &id_email); + SQL_EXPECT(res, SQLITE_OK); + + md5_init(&ms); + md5_append(&ms, (md5_byte_t *) f->passw, (int)strlen(f->passw)); + md5_finish(&ms, digest); + for (i = 0; i != 16; ++i) + sprintf(passwd_md5 + 2 * i, "%.02x", digest[i]); + + if (!update) { + update = ((id_email != 0 && dbstate.id_email != id_email) + || dbstate.no != f->no || dbstate.passwd_md5 == NULL + || strcmp(passwd_md5, dbstate.passwd_md5) != 0 || dbstate.name == NULL + || strncmp(f->name, dbstate.name, MAX_FACTION_NAME) != 0); + } + if (update) { + const char sql_update_faction[] = + "UPDATE faction SET email_id=?, password_md5=?, code=?, name=?, firstturn=? WHERE id=?"; + sqlite3_stmt *stmt_update_faction = + stmt_cache_get(db, sql_update_faction); + + res = sqlite3_bind_int64(stmt_update_faction, 1, id_email); + SQL_EXPECT(res, SQLITE_OK); + res = + sqlite3_bind_text(stmt_update_faction, 2, passwd_md5, MD5_LENGTH, + SQLITE_TRANSIENT); + SQL_EXPECT(res, SQLITE_OK); + res = + sqlite3_bind_text(stmt_update_faction, 3, no_b36, -1, + SQLITE_TRANSIENT); + SQL_EXPECT(res, SQLITE_OK); + res = + sqlite3_bind_text(stmt_update_faction, 4, f->name, -1, + SQLITE_TRANSIENT); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_bind_int(stmt_update_faction, 5, turn - f->age); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_bind_int64(stmt_update_faction, 6, f->subscription); + SQL_EXPECT(res, SQLITE_OK); + res = sqlite3_step(stmt_update_faction); + SQL_EXPECT(res, SQLITE_DONE); + } + } } - } - - for (f = factions; f; f = f->next) { - if (!fval(f, FFL_MARK)) { - log_error("%s (sub=%d, email=%s) has no entry in the database\n", factionname(f), f->subscription, f->email); - } else { - freset(f, FFL_MARK); + + for (f = factions; f; f = f->next) { + if (!fval(f, FFL_MARK)) { + log_error("%s (sub=%d, email=%s) has no entry in the database\n", factionname(f), f->subscription, f->email); + } else { + freset(f, FFL_MARK); + } } - } - return SQLITE_OK; + return SQLITE_OK; } int db_update_scores(sqlite3 * db, bool force) From 19a21772e483acd69caef7e13932fa1b609cdee0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 4 May 2014 15:54:56 -0700 Subject: [PATCH 18/18] stop tests from crashing --- cutest | 2 +- s/runtests | 4 ++-- src/bindings.c | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cutest b/cutest index 788659594..d83cec09a 160000 --- a/cutest +++ b/cutest @@ -1 +1 @@ -Subproject commit 788659594ef87e9f497b8039da764182adfd2943 +Subproject commit d83cec09a52835274ab8ed4849de16fb8658982a diff --git a/s/runtests b/s/runtests index 1ad5a0b0a..c56619db1 100755 --- a/s/runtests +++ b/s/runtests @@ -18,7 +18,7 @@ fi echo $ROOT $ROOT/$BIN_DIR/eressea/test_eressea pushd $ROOT/game-e2 -$ROOT/$BIN_DIR/eressea/eressea -e run_tests +$ROOT/$BIN_DIR/eressea/eressea runtests.lua cd $ROOT/game-e3 -$ROOT/$BIN_DIR/eressea/eressea -e run_tests +$ROOT/$BIN_DIR/eressea/eressea runtests.lua popd diff --git a/src/bindings.c b/src/bindings.c index 3bd2db543..3d8a83388 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -1218,7 +1218,6 @@ int eressea_run(lua_State *L, const char *luafile) err = lua_pcall(L, 1, 1, -3); if (err != 0) { log_lua_error(L); - abort(); } else { if (lua_isnumber(L, -1)) { err = (int)lua_tonumber(L, -1);