diff --git a/process/CMakeLists.txt b/process/CMakeLists.txt index f121e6966..106c4c864 100644 --- a/process/CMakeLists.txt +++ b/process/CMakeLists.txt @@ -1,4 +1,5 @@ -install(PROGRAMS create-orders backup-eressea run-turn send-zip-report +install(PROGRAMS create-orders backup-box backup-onedrive + run-turn send-zip-report send-bz2-report compress.py compress.sh epasswd.py accept-orders.py getemail.py checkpasswd.py sendreport.sh sendreports.sh orders-accept DESTINATION bin) diff --git a/process/backup-eressea b/process/backup-box similarity index 100% rename from process/backup-eressea rename to process/backup-box diff --git a/process/backup-onedrive b/process/backup-onedrive new file mode 100755 index 000000000..f407e244c --- /dev/null +++ b/process/backup-onedrive @@ -0,0 +1,44 @@ +#!/bin/bash +DIR=$(dirname $0) +if [ -z "$ERESSEA" ] ; then + ERESSEA="$HOME/eressea" + echo "The ERESSEA environment variable is not set. Assuming $ERESSEA." +fi +GAME=$1 + +upload() { + onedrive-cli cp "./$1" "Eressea/game-$GAME" +} + +if [ ! -d "$ERESSEA/game-$GAME" ]; then + echo "No such game: game-$GAME." + exit 1 +fi +cd "$ERESSEA/game-$GAME" || exit +TURN=$2 +if [ -z "$TURN" ]; then + TURN=$(cat turn) +fi +if [ ! -e "data/$TURN.dat" ]; then + echo "No data for turn $TURN in game $GAME." + exit 2 +fi +if [ ! -d backup ] ; then + echo "creating missing backup directory for game $GAME." + mkdir -p "$HOME/backup/eressea/game-$GAME" + ln -sf "$HOME/backup/eressea/game-$GAME" backup +fi + +files=("data/$TURN.dat" parteien.full parteien) +if [ -e "orders.$TURN" ]; then + files+=("orders.$TURN") +fi +echo "backup turn $TURN, game $GAME, files: ${files[*]}" +tar cjf "backup/$TURN.tar.bz2" "${files[@]}" +if [ -e reports/reports.txt ] ; then + upload "backup/$TURN.tar.bz2" + echo "backup reports $TURN, game $GAME" + tar cjf "backup/$TURN-reports.tar.bz2" reports eressea.db + upload "backup/$TURN-reports.tar.bz2" +fi + diff --git a/process/cron/run-eressea.cron b/process/cron/run-eressea.cron index c0a432b35..8c9f5c8c2 100755 --- a/process/cron/run-eressea.cron +++ b/process/cron/run-eressea.cron @@ -5,6 +5,13 @@ GAME=$1 [ "$ENABLED" == "no" ] && exit [ -z "$ERESSEA" ] && ERESSEA="$HOME/eressea" +backup() +{ + if [ -x "$BIN/backup-eressea" ] ; then + "$BIN/backup-eressea" "$1" "$2" + fi +} + export ERESSEA eval "$(luarocks path)" export LUA_PATH="${ERESSEA}/server/scripts/?.lua;$LUA_PATH" @@ -37,12 +44,12 @@ if [ ! -s "orders.$TURN" ]; then exit 2 fi -"$BIN/backup-eressea" "$GAME" "$TURN" +backup "$GAME" "$TURN" rm -f execute.lock "$BIN/run-turn" "$GAME" "$TURN" touch execute.lock -let NEXTTURN=$TURN+1 +(( NEXTTURN=TURN+1 )) || true if [ ! -e "$DATA/$NEXTTURN.dat" ]; then echo "server did not create data file $NEXTTURN.dat" exit 5 @@ -51,7 +58,7 @@ if [ ! -s "$REPORTS/reports.txt" ]; then echo "server did not create reports.txt in game $GAME" exit 4 fi -"$BIN/backup-eressea" "$GAME" "$TURN" +backup "$GAME" "$TURN" TURN=$NEXTTURN if [ ! -s "$ERESSEA/game-$GAME/data/$TURN.dat" ]; then echo "server did not create data for turn $TURN in game $GAME" @@ -65,7 +72,7 @@ fi echo "sending reports for game $GAME, turn $TURN" "$BIN/compress.sh" "$GAME" "$TURN" "$BIN/sendreports.sh" "$GAME" -"$BIN/backup-eressea" "$GAME" "$TURN" +backup "$GAME" "$TURN" rm -f test/execute.lock ) | tee -a "$HOME/log/eressea.cron.log" diff --git a/s/git-reset b/s/git-reset index df84d9bef..7531794fc 100755 --- a/s/git-reset +++ b/s/git-reset @@ -1,4 +1,4 @@ -#/bin/sh +#!/bin/sh if [ ! -d .git ] ; then echo "please run this script from the repository's top-level directory" exit 1 diff --git a/s/install b/s/install index dc0e93598..aec97bbdf 100755 --- a/s/install +++ b/s/install @@ -3,7 +3,10 @@ FORCE=0 while getopts f o; do case "${o}" in - f) usage ; FORCE=yes ;; + f) FORCE=yes + ;; + *) usage + ;; esac done @@ -14,13 +17,20 @@ if [ "$FORCE" != "yes" ] ; then exit fi fi -[ -z "$CC" ] && [ ! -z `which clang` ] && CC="clang" -[ -z "$CC" ] && [ ! -z `which gcc` ] && CC="gcc" -[ -z "$CC" ] && [ ! -z `which tcc` ] && CC="tcc" -[ -z "$CC" ] && [ ! -z `which cc` ] && CC="cc" +[ -z "$CC" ] && [ -n "$(which clang)" ] && CC="clang" +[ -z "$CC" ] && [ -n "$(which gcc)" ] && CC="gcc" +[ -z "$CC" ] && [ -n "$(which tcc)" ] && CC="tcc" +[ -z "$CC" ] && [ -n "$(which cc)" ] && CC="cc" BIN_DIR="build" ROOT=$(git rev-parse --show-toplevel) PREFIX=$(dirname "$ROOT")/server -cd "$ROOT" +cd "$ROOT" || exit cmake --install "$BIN_DIR" --prefix "$PREFIX" +if [ ! -e "$PREFIX/bin/backup-eressea" ] ; then + if [ -e ~/.onedrive-cli-token ] ; then + cd "$PREFIX" || exit + ln -sf backup-onedrive backup-eressea + cd - || exit + fi +fi diff --git a/s/preview b/s/preview index 69c46cf90..cc61e28eb 100755 --- a/s/preview +++ b/s/preview @@ -14,17 +14,17 @@ exit 1 } function abort() { -echo $1 -[ -z $2 ] && exit -1 -exit $2 # otherwise +echo "$1" +[ -z "$2" ] && exit 255 +exit "$2" # otherwise } function build() { -assert_dir $SOURCE -cd $SOURCE +assert_dir "$SOURCE" +cd "$SOURCE" || exit rm -rf tolua git fetch -[ -z $1 ] || git checkout $1 +[ -z "$1" ] || git checkout "$1" git pull --rebase -q || abort "failed to update source. do you have local changes?" git submodule update s/cmake-init @@ -32,26 +32,26 @@ s/build > /dev/null || abort "build failed." } function assert_file() { -[ -e $1 ] || abort "missing file: $1" +[ -e "$1" ] || abort "missing file: $1" } function assert_files() { -while [ ! -z $1 ] ; do -assert_file $1 +while [ -n "$1" ] ; do +assert_file "$1" shift done } function assert_dir() { -[ -d $1 ] || abort "missing directory: $1" +[ -d "$1" ] || abort "missing directory: $1" } function setup() { -assert_dir $SOURCE -assert_dir $LIVE -mkdir -p $TESTROOT -assert_dir $TESTROOT -cd $TESTROOT +assert_dir "$SOURCE" +assert_dir "$LIVE" +mkdir -p "$TESTROOT" +assert_dir "$TESTROOT" +cd "$TESTROOT" || exit cat >| eressea.ini <] [-r ] [-s DIR] +Usage: $0 [-hfn] [-d DIR] [-g ] [-r ] [-s DIR] -h print this message -f force overwrite of existing game -g game id @@ -46,29 +46,29 @@ while getopts :d:g:r:s:hfn o; do esac done -[ $game -gt 0 ] || abort "must use a positive integer for game id" -[ -z $SOURCE ] && SOURCE=$ERESSEA/$src -[ -d $SOURCE ] || abort "invalid source directory $SOURCE" -[ -z $rules ] && rules=e$game -[ -z $dir ] && dir=game-$game -[ -z $TOOLS ] && TOOLS=$SOURCE/build -[ -e $TOOLS ] || TOOLS=$SOURCE/bin -[ -z $INIFILE ] && INIFILE=$TOOLS/inifile -[ -e $INIFILE ] || INIFILE=$TOOLS/iniparser/inifile +[ "$game" -gt 0 ] || abort "must use a positive integer for game id" +[ -z "$SOURCE" ] && SOURCE="$ERESSEA/$src" +[ -d "$SOURCE" ] || abort "invalid source directory $SOURCE" +[ -z "$rules" ] && rules="e$game" +[ -z "$dir" ] && dir="game-$game" +[ -z "$TOOLS" ] && TOOLS="$SOURCE/build" +[ -e "$TOOLS" ] || TOOLS="$SOURCE/bin" +[ -z "$INIFILE" ] && INIFILE="$TOOLS/inifile" +[ -e "$INIFILE" ] || INIFILE="$TOOLS/iniparser/inifile" -[ -e $SOURCE/conf/$rules/config.json ] || abort "cannot find conf/$rules/config.json" +[ -e "$SOURCE/conf/$rules/config.json" ] || abort "cannot find conf/$rules/config.json" -cd $ERESSEA -if [ -d $dir ] ; then +cd "$ERESSEA" || exit +if [ -d "$dir" ] ; then [ $force -eq 1 ] || abort "$dir directory exists. Use -f to force" fi -mkdir -p $dir -cd $dir || abort "could not chdir to $dir" +mkdir -p "$dir" +cd "$dir" || abort "could not chdir to $dir" mkdir -p data reports function ini_sec() { if [ $edit -eq 1 ]; then - $INIFILE eressea.ini add $1 + $INIFILE eressea.ini add "$1" else echo "[$1]" >> eressea.ini fi @@ -76,7 +76,7 @@ function ini_sec() { function ini_add() { if [ $edit -eq 1 ]; then - $INIFILE eressea.ini add $1:$2 $3 + $INIFILE eressea.ini add "$1:$2" "$3" else echo "$2 = $3" >> eressea.ini fi @@ -84,7 +84,7 @@ function ini_add() { function ini_start() { if [ -e eressea.ini ]; then - if [ ! -e $INIFILE ] && [ $edit -eq 1 ]; then + if [ ! -e "$INIFILE" ] && [ $edit -eq 1 ]; then abort "missing editor for eressea.ini. use -n to create new file." fi rm -f eressea.ini @@ -98,15 +98,15 @@ function ini_start() { ini_start ini_sec game ini_add game locales de,en -ini_add game id $game +ini_add game id "$game" ini_add game start 1 ini_sec lua -ini_add lua install $SOURCE -ini_add lua rules $rules +ini_add lua install "$SOURCE" +ini_add lua rules "$rules" echo 0 > turn touch newfactions -ln -sf $SOURCE/bin/eressea -ln -sf $SOURCE/scripts/run-turn.lua -ln -sf $SOURCE/scripts/reports.lua -ln -sf $SOURCE/scripts/config.lua +ln -sf "$SOURCE/bin/eressea" . +ln -sf "$SOURCE/scripts/run-turn.lua" . +ln -sf "$SOURCE/scripts/reports.lua" . +ln -sf "$SOURCE/scripts/config.lua" . diff --git a/s/upgrade b/s/upgrade index e7b91196b..8ca07447c 100755 --- a/s/upgrade +++ b/s/upgrade @@ -1,12 +1,12 @@ #!/bin/sh set -e -ROOT=`pwd` -while [ ! -d $ROOT/.git ]; do - ROOT=`dirname $ROOT` +ROOT="$(pwd)" +while [ ! -d "$ROOT/.git" ]; do + ROOT=$(dirname "$ROOT") done -cd $ROOT +cd "$ROOT" || exit git pull --rebase git submodule update s/build diff --git a/src/kernel/gamedata.h b/src/kernel/gamedata.h index c2ac41ae2..879082f2e 100644 --- a/src/kernel/gamedata.h +++ b/src/kernel/gamedata.h @@ -54,8 +54,9 @@ #define FIX_RESOURCES_VERSION 376 /* badly made rawmaterials, bug 2898 */ #define REMOVE_FACTION_AGE_VERSION 377 /* save start_turn, not age, bug 2878 */ #define FORBIDDEN_LAND_VERSION 378 /* forbidden regions (walls) do not store land info */ +#define FIX_SHADOWS_VERSION 379 /* shadowdemon/-master skills, bug 3011 */ -#define RELEASE_VERSION FORBIDDEN_LAND_VERSION /* use for new datafiles */ +#define RELEASE_VERSION FIX_SHADOWS_VERSION /* use for new datafiles */ #define MIN_VERSION UIDHASH_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/kernel/save.c b/src/kernel/save.c index 7b6ebaa3b..c3d24c27b 100644 --- a/src/kernel/save.c +++ b/src/kernel/save.c @@ -1296,6 +1296,27 @@ static int cb_sb_maxlevel(spellbook_entry *sbe, void *cbdata) { return 0; } +void fix_shadows(void) +{ + region *r; + const race *rc_demon = get_race(RC_SHADOW); + const race *rc_lord = get_race(RC_SHADOWLORD); + for (r = regions; r; r = r->next) { + unit *u; + for (u = r->units; u; u = u->next) { + const race *rc = u_race(u); + if (rc == rc_demon) { + int level = get_level(u, SK_STEALTH); + set_level(u, SK_STEALTH, level / 2); + } + else if (rc == rc_lord) { + int level = get_level(u, SK_STEALTH); + set_level(u, SK_STEALTH, level - 1); + } + } + } +} + int readgame(const char *filename) { int n = -2, stream_version; @@ -1331,6 +1352,9 @@ int readgame(const char *filename) log_debug("data in %s created with build %d.", filename, build); } n = read_game(&gdata); + if (gdata.version < FIX_SHADOWS_VERSION) { + fix_shadows(); + } binstore_done(&store); fstream_done(&strm); } diff --git a/src/kernel/save.h b/src/kernel/save.h index 601d1649b..b9a5bda79 100644 --- a/src/kernel/save.h +++ b/src/kernel/save.h @@ -45,3 +45,5 @@ int read_game(struct gamedata *data); /* test-only functions that give access to internal implementation details (BAD) */ void _test_write_password(struct gamedata *data, const struct faction *f); void _test_read_password(struct gamedata *data, struct faction *f); + +void fix_shadows(void); diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c index a2227c055..76beb7d0f 100644 --- a/src/kernel/save.test.c +++ b/src/kernel/save.test.c @@ -15,6 +15,7 @@ #include "race.h" #include "region.h" #include "ship.h" +#include "skill.h" #include "unit.h" #include "version.h" @@ -475,6 +476,22 @@ static void test_version_no(CuTest *tc) { CuAssertIntEquals(tc, 0x10203, version_no("1.2.3-what.is.42")); } +static void test_fix_shadows(CuTest *tc) { + unit *u1, *u2; + + test_setup(); + u1 = test_create_unit(test_create_faction(), test_create_plain(0, 0)); + u_setrace(u1, test_create_race("shadowdemon")); + test_set_skill(u1, SK_STEALTH, 16, 1); + u2 = test_create_unit(test_create_faction(), test_create_plain(1, 0)); + u_setrace(u2, test_create_race("shadowmaster")); + test_set_skill(u2, SK_STEALTH, 16, 1); + fix_shadows(); + CuAssertIntEquals(tc, 8, get_level(u1, SK_STEALTH)); + CuAssertIntEquals(tc, 15, get_level(u2, SK_STEALTH)); + test_teardown(); +} + CuSuite *get_save_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -492,6 +509,7 @@ CuSuite *get_save_suite(void) SUITE_ADD_TEST(suite, test_read_password); SUITE_ADD_TEST(suite, test_read_password_external); SUITE_ADD_TEST(suite, test_version_no); + SUITE_ADD_TEST(suite, test_fix_shadows); return suite; } diff --git a/src/spells.c b/src/spells.c index e7367068d..fbce45c04 100644 --- a/src/spells.c +++ b/src/spells.c @@ -2955,7 +2955,7 @@ static int sp_plague(castorder * co) * Flag: * (SPELLLEVEL) */ -static int sp_summonshadow(castorder * co) +int sp_shadowdemons(castorder * co) { region *r = co_get_region(co); unit *caster = co_get_caster(co); @@ -2967,7 +2967,7 @@ static int sp_summonshadow(castorder * co) u = create_unit(r, caster->faction, number, get_race(RC_SHADOW), 0, NULL, caster); /* Bekommen Tarnung = (Magie+Tarnung)/2 und Wahrnehmung 1. */ - val = get_level(caster, SK_MAGIC) + get_level(caster, SK_STEALTH); + val = (get_level(caster, SK_MAGIC) + get_level(caster, SK_STEALTH)) / 2; set_level(u, SK_STEALTH, val); set_level(u, SK_PERCEPTION, 1); @@ -2986,7 +2986,7 @@ static int sp_summonshadow(castorder * co) * Wirkung: * Diese hoeheren Schattendaemonen sind erheblich gefaehrlicher als die * einfachen Schattendaemonen. Sie haben Tarnung entsprechend dem - * Magietalent des Beschwoerer-1 und Wahrnehmung 5, 75 HP, + * Magietalent des Beschwoerers-1 und Wahrnehmung 5, 75 HP, * Ruestungsschutz 4, Attacke-Bonus 11 und Verteidigungsbonus 13, machen * bei einem Treffer 2d4 Schaden, entziehen einen Staerkepunkt und * entziehen 5 Talenttage in einem zufaelligen Talent. @@ -2995,7 +2995,7 @@ static int sp_summonshadow(castorder * co) * Flag: * (SPELLLEVEL) * */ -static int sp_summonshadowlords(castorder * co) +int sp_shadowlords(castorder * co) { unit *u; region *r = co_get_region(co); @@ -3008,7 +3008,7 @@ static int sp_summonshadowlords(castorder * co) NULL, caster); /* Bekommen Tarnung = Magie und Wahrnehmung 5. */ - set_level(u, SK_STEALTH, get_level(caster, SK_MAGIC)); + set_level(u, SK_STEALTH, get_level(caster, SK_MAGIC) - 1); set_level(u, SK_PERCEPTION, 5); ADDMSG(&caster->faction->msgs, msg_message("summon_effect", "mage amount race", @@ -6224,7 +6224,7 @@ static spelldata spell_functions[] = { { "firewall", sp_firewall, patzer_peasantmob }, { "plague", sp_plague, patzer_peasantmob }, { "chaosrow", sp_chaosrow, 0 }, - { "summonshadow", sp_summonshadow, patzer_peasantmob }, + { "summonshadow", sp_shadowdemons, patzer_peasantmob }, { "undeadhero", sp_undeadhero, 0 }, { "auraleak", sp_auraleak, 0 }, { "draigfumbleshield", sp_fumbleshield, 0 }, @@ -6233,7 +6233,7 @@ static spelldata spell_functions[] = { { "unholypower", sp_unholypower, 0 }, { "deathcloud", sp_deathcloud, patzer_deathcloud }, { "summondragon", sp_summondragon, patzer_peasantmob }, - { "summonshadowlords", sp_summonshadowlords, patzer_peasantmob }, + { "summonshadowlords", sp_shadowlords, patzer_peasantmob }, { "chaossuction", sp_chaossuction, patzer_peasantmob }, /* M_ILLAUN */ { "sparkledream", sp_sparkle, 0 }, diff --git a/src/spells.h b/src/spells.h index 1472353c8..3ab031b0f 100644 --- a/src/spells.h +++ b/src/spells.h @@ -43,3 +43,6 @@ int sp_destroy_magic(struct castorder *co); int sp_rosthauch(struct castorder *co); int sp_sparkle(struct castorder *co); int sp_summon_familiar(struct castorder *co); + +int sp_shadowdemons(struct castorder *co); +int sp_shadowlords(struct castorder *co); diff --git a/src/spells.test.c b/src/spells.test.c index c6469c9ac..68309c032 100644 --- a/src/spells.test.c +++ b/src/spells.test.c @@ -300,6 +300,10 @@ static void test_view_reality(CuTest *tc) { "unit:unit", "region:region", "command:order", MT_NEW_END); mt_create_va(mt_new("viewreality_effect", NULL), "unit:unit", MT_NEW_END); + mt_create_va(mt_new("summon_effect", NULL), + "mage:unit", "amount:int", "race:race", MT_NEW_END); + mt_create_va(mt_new("summonshadow_effect", NULL), + "mage:unit", "number:int", MT_NEW_END); rx = test_create_region(0, TP_RADIUS + 1, NULL); f = test_create_faction(); u = test_create_unit(f, rx); @@ -1384,6 +1388,62 @@ static void test_summon_familiar(CuTest *tc) { CuAssertPtrEquals(tc, rc_special, (race *)u_race(u2)); } +static void test_shadowdemons(CuTest *tc) { + struct region *r; + struct faction *f; + unit *u, *u2; + race *rc; + castorder co; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(); + f->magiegebiet = M_DRAIG; + u = test_create_unit(f, r); + rc = test_create_race("shadowdemon"); + CuAssertPtrEquals(tc, (void *)get_race(RC_SHADOW), rc); + test_set_skill(u, SK_MAGIC, 10, 1); + test_set_skill(u, SK_STEALTH, 6, 1); + test_create_castorder(&co, u, 3, 10., 0, NULL); + + CuAssertIntEquals(tc, co.level, sp_shadowdemons(&co)); + CuAssertPtrNotNull(tc, u2 = u->next); + CuAssertPtrEquals(tc, u->faction, u2->faction); + CuAssertPtrEquals(tc, rc, (race *)u_race(u2)); + CuAssertIntEquals(tc, 100, u2->number); + CuAssertIntEquals(tc, 8, get_level(u2, SK_STEALTH)); + CuAssertIntEquals(tc, 1, get_level(u2, SK_PERCEPTION)); + CuAssertPtrNotNull(tc, test_find_faction_message(f, "summonshadow_effect")); +} + +static void test_shadowlords(CuTest *tc) { + struct region *r; + struct faction *f; + unit *u, *u2; + race *rc; + castorder co; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(); + f->magiegebiet = M_DRAIG; + u = test_create_unit(f, r); + rc = test_create_race("shadowmaster"); + CuAssertPtrEquals(tc, (void *)get_race(RC_SHADOWLORD), rc); + test_set_skill(u, SK_MAGIC, 10, 1); + test_set_skill(u, SK_STEALTH, 6, 1); + test_create_castorder(&co, u, 3, 10., 0, NULL); + + CuAssertIntEquals(tc, co.level, sp_shadowlords(&co)); + CuAssertPtrNotNull(tc, u2 = u->next); + CuAssertPtrEquals(tc, u->faction, u2->faction); + CuAssertPtrEquals(tc, rc, (race *)u_race(u2)); + CuAssertIntEquals(tc, 100, u2->number); + CuAssertIntEquals(tc, 9, get_level(u2, SK_STEALTH)); + CuAssertIntEquals(tc, 5, get_level(u2, SK_PERCEPTION)); + CuAssertPtrNotNull(tc, test_find_faction_message(f, "summon_effect")); +} + CuSuite *get_spells_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -1423,6 +1483,8 @@ CuSuite *get_spells_suite(void) SUITE_ADD_TEST(suite, test_rosthauch); SUITE_ADD_TEST(suite, test_sparkle); SUITE_ADD_TEST(suite, test_summon_familiar); + SUITE_ADD_TEST(suite, test_shadowdemons); + SUITE_ADD_TEST(suite, test_shadowlords); return suite; }