diff --git a/CMakeLists.txt b/CMakeLists.txt
index 004248570..3e1b61e1b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,6 +69,7 @@ add_subdirectory (storage)
add_subdirectory (iniparser)
add_subdirectory (quicklist)
add_subdirectory (critbit)
+add_subdirectory (process)
add_subdirectory (src eressea)
install(DIRECTORY res conf DESTINATION ${CMAKE_INSTALL_PREFIX} FILES_MATCHING PATTERN "*.xml")
install(DIRECTORY res conf DESTINATION ${CMAKE_INSTALL_PREFIX} FILES_MATCHING PATTERN "*.json")
diff --git a/conf/e3/config.json b/conf/e3/config.json
index 764ba6786..a1fc05b74 100644
--- a/conf/e3/config.json
+++ b/conf/e3/config.json
@@ -42,9 +42,9 @@
"nmr.removenewbie": 0,
"GiveRestriction": 3,
"hunger.long": false,
- "hunger.demon.skill": true,
"hunger.damage": "1d9+9",
- "hunger.demons": true,
+ "hunger.demons.skill": true,
+ "hunger.demons.peasant_tolerance": true,
"init_spells": 0,
"recruit.allow_merge": true,
"study.expensivemigrants": true,
@@ -71,9 +71,9 @@
"rules.region_owners": true,
"rules.cavalry.skill": 2,
"rules.cavalry.mode": 1,
- "rules.magic.multipotion": 1,
+ "rules.magic.multipotion": true,
"rules.magic.wol_effect": 5,
- "rules.magic.factionlist": 1,
+ "rules.magic.factionlist": true,
"rules.magic.wol_type": 2,
"rules.blessed_harvest.flags": 1,
"rules.magic.elfpower": true,
diff --git a/conf/e3/config.xml b/conf/e3/config.xml
index 1a1f5f26f..0531f69d2 100644
--- a/conf/e3/config.xml
+++ b/conf/e3/config.xml
@@ -51,13 +51,13 @@
Bitte denke daran, deine Befehle mit dem Betreff
- E3 BEFEHLE an eressea-server@eressea.de zu senden.
+ ERESSEA 3 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
+ ERESSEA 3 BEFEHLE
+ ERESSEA 3 ORDERS
ARBEITEN
diff --git a/conf/e4/config.json b/conf/e4/config.json
index e23eee261..12da61937 100644
--- a/conf/e4/config.json
+++ b/conf/e4/config.json
@@ -42,9 +42,9 @@
"nmr.removenewbie": 0,
"GiveRestriction": 3,
"hunger.long": false,
- "hunger.demon.skill": true,
"hunger.damage": "1d9+9",
- "hunger.demons": true,
+ "hunger.demons.skill": true,
+ "hunger.demons.peasant_tolerance": true,
"init_spells": 0,
"recruit.allow_merge": true,
"study.expensivemigrants": true,
diff --git a/crypto b/crypto
index 166fdc8c1..93dc9200f 160000
--- a/crypto
+++ b/crypto
@@ -1 +1 @@
-Subproject commit 166fdc8c146755055217070c58079ba9a7c03369
+Subproject commit 93dc9200fa4cb6bfa3883b19f6d33fd416ca43da
diff --git a/process/CMakeLists.txt b/process/CMakeLists.txt
new file mode 100644
index 000000000..58384b1f6
--- /dev/null
+++ b/process/CMakeLists.txt
@@ -0,0 +1,8 @@
+install(PROGRAMS create-orders backup-eressea run-turn send-zip-report
+send-bz2-report compress.py compress.sh epasswd.py orders-process
+checkpasswd.py sendreport.sh orders-accept DESTINATION bin)
+
+install(DIRECTORY cron/ DESTINATION bin USE_SOURCE_PERMISSIONS
+FILES_MATCHING PATTERN "*.cron")
+
+install(DIRECTORY procmail DESTINATION share)
diff --git a/process/checkpasswd.py b/process/checkpasswd.py
new file mode 100755
index 000000000..f797dcfde
--- /dev/null
+++ b/process/checkpasswd.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+import sys, re
+from epasswd import EPasswd
+
+if len(sys.argv)<4:
+ sys.exit(-2)
+
+passfile=sys.argv[1]
+myfaction=sys.argv[2]
+mypasswd=sys.argv[3]
+
+if mypasswd[0]=='"':
+ mypasswd=mypasswd[1:len(mypasswd)-1]
+
+pw_data=EPasswd(passfile)
+if pw_data.fac_exists(myfaction):
+ if pw_data.check(myfaction, mypasswd):
+ sys.exit(0)
+sys.exit(-1)
diff --git a/process/epasswd.py b/process/epasswd.py
new file mode 100755
index 000000000..aa3d79fa6
--- /dev/null
+++ b/process/epasswd.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+
+from string import split
+from string import strip
+from string import lower
+import subprocess
+
+class EPasswd:
+ def _check_apr1(self, pwhash, pw):
+ spl = split(pwhash, '$')
+ salt = spl[2]
+ hash = subprocess.check_output(['openssl', 'passwd', '-apr1', '-salt', salt, pw]).decode('utf-8').strip()
+ return hash==pwhash
+
+ def __init__(self, file):
+ self.data = {}
+ try:
+ fp = open(file,"r")
+ except:
+ fp = None
+ if fp != None:
+ while True:
+ line = fp.readline()
+ if not line: break
+ line = strip(line)
+ [id, email, passwd] = split(line, ":")[0:3]
+ lc_id = lower(id)
+ self.data[lc_id] = {}
+ self.data[lc_id]["id"] = id
+ self.data[lc_id]["email"] = email
+ self.data[lc_id]["passwd"] = passwd
+ fp.close()
+
+ def check(self, id, passwd):
+ pw = self.get_passwd(id)
+ if pw[0:6]=='$apr1$':
+ return self._check_apr1(pw, passwd)
+ return pw == passwd
+
+ def get_passwd(self, id):
+ return self.data[lower(id)]["passwd"]
+
+ def get_email(self, id):
+ return self.data[lower(id)]["email"]
+
+ def get_canon_id(self, id):
+ return self.data[lower(id)]["id"]
+
+ def fac_exists(self, id):
+ return self.data.has_key(lower(id))
diff --git a/process/orders-accept b/process/orders-accept
index 552a3e9a1..e90c3d48f 100755
--- a/process/orders-accept
+++ b/process/orders-accept
@@ -1,2 +1,368 @@
-#/bin/.sh
-grep -v '>From' | $HOME/src/scripts/bin/orders-accept $*
+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+
+from email.Utils import parseaddr
+from email.Parser import Parser
+from os import mkdir, rename, stat, utime, unlink, symlink
+from os.path import exists
+from re import compile, IGNORECASE
+from stat import ST_MTIME
+from string import upper, split, replace
+import logging
+from sys import argv, stdin, exit
+from time import ctime, sleep, time
+from socket import gethostname
+from rfc822 import parsedate_tz, mktime_tz
+
+LOG_FILENAME='/home/eressea/log/orders.log'
+prefix = 'turn-'
+hostname = gethostname()
+# base directory for all your games:
+rootdir = "/home/eressea"
+orderbase = "orders.dir"
+sendmail = True
+# maximum number of reports per sender:
+maxfiles = 20
+# write headers to file?
+writeheaders = True
+# reject all html email?
+rejecthtml = True
+
+games = [
+ {
+ "from" : "Eressea Server ",
+ "prefix" : "Eressea"
+ },
+ {
+ "from" : "Eressea Server ",
+ "prefix": "E3"
+ },
+ {
+ "from" : "Eressea Server ",
+ "prefix": "E4"
+ },
+]
+
+def unlock_file(filename):
+ try:
+ unlink(filename+".lock")
+ except:
+ print "could not unlock %s.lock, file not found" % filename
+
+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: unlock_file(filename)
+ sleep(wait)
+ wait = wait*2
+
+messages = {
+ "multipart-en" :
+ "ERROR: The orders you sent contain no plaintext. " \
+ "The Eressea server cannot process orders containing HTML " \
+ "or invalid attachments, which are the reasons why this " \
+ "usually happens. Please change the settings of your mail " \
+ "software and re-send the orders.",
+
+ "multipart-de" :
+ "FEHLER: Die von dir eingeschickte Mail enthält keinen " \
+ "Text. Evtl. hast Du den Zug als HTML oder als anderweitig " \
+ "ungültig formatierte Mail ingeschickt. Wir können ihn " \
+ "deshalb nicht berücksichtigen. Schicke den Zug nochmals " \
+ "als reinen Text ohne Formatierungen ein.",
+
+ "maildate-de":
+ "Es erreichte uns bereits ein Zug mit einem späteren " \
+ "Absendedatum (%s > %s). Entweder ist deine " \
+ "Systemzeit verstellt, oder ein Zug hat einen anderen Zug von " \
+ "dir auf dem Transportweg überholt. Entscheidend für die " \
+ "Auswertungsreihenfolge ist das Absendedatum, d.h. der Date:-Header " \
+ "deiner Mail.",
+
+ "maildate-en":
+ "The server already received an order file that was sent at a later " \
+ "date (%s > %s). Either your system clock is wrong, or two messages have " \
+ "overtaken each other on the way to the server. The order of " \
+ "execution on the server is always according to the Date: header in " \
+ "your mail.",
+
+ "nodate-en":
+ "Your message did not contain a valid Date: header in accordance with RFC2822.",
+
+ "nodate-de":
+ "Deine Nachricht enthielt keinen gueltigen Date: header nach RFC2822.",
+
+ "error-de":
+ "Fehler",
+
+ "error-en":
+ "Error",
+
+ "warning-de":
+ "Warnung",
+
+ "warning-en":
+ "Warning",
+
+ "subject-de":
+ "Befehle angekommen",
+
+ "subject-en":
+ "orders received"
+}
+
+# return 1 if addr is a valid email address
+def valid_email(addr):
+ rfc822_specials = '/()<>@,;:\\"[]'
+ # First we validate the name portion (name@domain)
+ c = 0
+ while c < len(addr):
+ if addr[c] == '"' and (not c or addr[c - 1] == '.' or addr[c - 1] == '"'):
+ c = c + 1
+ while c < len(addr):
+ if addr[c] == '"': break
+ if addr[c] == '\\' and addr[c + 1] == ' ':
+ c = c + 2
+ continue
+ if ord(addr[c]) < 32 or ord(addr[c]) >= 127: return 0
+ c = c + 1
+ else: return 0
+ if addr[c] == '@': break
+ if addr[c] != '.': return 0
+ c = c + 1
+ continue
+ if addr[c] == '@': break
+ if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
+ if addr[c] in rfc822_specials: return 0
+ c = c + 1
+ if not c or addr[c - 1] == '.': return 0
+
+ # Next we validate the domain portion (name@domain)
+ domain = c = c + 1
+ if domain >= len(addr): return 0
+ count = 0
+ while c < len(addr):
+ if addr[c] == '.':
+ if c == domain or addr[c - 1] == '.': return 0
+ count = count + 1
+ if ord(addr[c]) <= 32 or ord(addr[c]) >= 127: return 0
+ if addr[c] in rfc822_specials: return 0
+ c = c + 1
+ return count >= 1
+
+# return the replyto or from address in the header
+def get_sender(header):
+ replyto = header.get("Reply-To")
+ if replyto is None:
+ replyto = header.get("From")
+ if replyto is None: return None
+ x = parseaddr(replyto)
+ return x[1]
+
+# return first available filename basename,[0-9]+
+def available_file(dirname, basename):
+ ver = 0
+ maxdate = 0
+ filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
+ while exists(filename):
+ maxdate = max(stat(filename)[ST_MTIME], maxdate)
+ ver = ver + 1
+ filename = "%s/%s,%s,%d" % (dirname, basename, hostname, ver)
+ if ver >= maxfiles:
+ return None, None
+ return maxdate, filename
+
+def formatpar(string, l=76, indent=2):
+ words = split(string)
+ res = ""
+ ll = 0
+ first = 1
+
+ for word in words:
+ if first == 1:
+ res = word
+ first = 0
+ ll = len(word)
+ else:
+ if ll + len(word) > l:
+ res = res + "\n"+" "*indent+word
+ ll = len(word) + indent
+ else:
+ res = res+" "+word
+ ll = ll + len(word) + 1
+
+ return res+"\n"
+
+def store_message(message, filename):
+ outfile = open(filename, "w")
+ outfile.write(message.as_string())
+ outfile.close()
+ return
+
+def write_part(outfile, part):
+ charset = part.get_content_charset()
+ payload = part.get_payload(decode=True)
+
+ if charset is None:
+ charset = "latin1"
+ try:
+ msg = payload.decode(charset, "ignore")
+ except:
+ msg = payload
+ charset = None
+ try:
+ utf8 = msg.encode("utf-8", "ignore")
+ outfile.write(utf8)
+ except:
+ outfile.write(msg)
+ return False
+ outfile.write("\n");
+ return True
+
+def copy_orders(message, filename, sender):
+ # print the header first
+ if writeheaders:
+ from os.path import split
+ dirname, basename = split(filename)
+ dirname = dirname + '/headers'
+ if not exists(dirname): mkdir(dirname)
+ outfile = open(dirname + '/' + basename, "w")
+ for name, value in message.items():
+ outfile.write(name + ": " + value + "\n")
+ outfile.close()
+
+ found = False
+ outfile = open(filename, "w")
+ if message.is_multipart():
+ for part in message.get_payload():
+ content_type = part.get_content_type()
+ logger.debug("found content type %s for %s" % (content_type, sender))
+ if content_type=="text/plain":
+ if write_part(outfile, part):
+ found = True
+ else:
+ charset = part.get_content_charset()
+ logger.error("could not write text/plain part (charset=%s) for %s" % (charset, sender))
+
+ else:
+ if write_part(outfile, message):
+ found = True
+ else:
+ charset = message.get_content_charset()
+ logger.error("could not write text/plain message (charset=%s) for %s" % (charset, sender))
+ outfile.close()
+ return found
+
+# create a file, containing:
+# game=0 locale=de file=/path/to/filename email=rcpt@domain.to
+def accept(game, locale, stream, extend=None):
+ global rootdir, orderbase
+ if extend is not None:
+ orderbase = orderbase + ".pre-" + extend
+ gamename = games[game-2]["prefix"]
+ gamedir = rootdir+"/eressea/game-%d" % (game, )
+ savedir = gamedir+"/"+orderbase
+ # check if it's one of the pre-sent orders.
+ # create the save-directories if they don't exist
+ if not exists(gamedir): mkdir(gamedir)
+ if not exists(savedir): mkdir(savedir)
+ # parse message
+ message = Parser().parse(stream)
+ sender = get_sender(message)
+ logger = logging.getLogger(sender)
+ # write syslog
+ if sender is None or valid_email(sender)==0:
+ logger.warning("invalid email address: " + str(sender))
+ return -1
+ logger.info("received orders from " + sender)
+ # get an available filename
+ lock_file(gamedir + "/orders.queue")
+ maxdate, filename = available_file(savedir, prefix + sender)
+ if filename is None:
+ logger.warning("more than " + str(maxfiles) + " orders from " + sender)
+ return -1
+ # copy the orders to the file
+ text_ok = copy_orders(message, filename, sender)
+ unlock_file(gamedir + "/orders.queue")
+
+ warning, msg, fail = None, "", False
+ maildate = message.get("Date")
+ if maildate != None:
+ turndate = mktime_tz(parsedate_tz(maildate))
+ utime(filename, (turndate, turndate))
+ logger.debug("mail date is '%s' (%d)" % (maildate, turndate))
+ if turndate < maxdate:
+ logger.warning("inconsistent message date " + sender)
+ warning = " (" + messages["warning-" + locale] + ")"
+ msg = msg + formatpar(messages["maildate-" + locale] % (ctime(maxdate),ctime(turndate)), 76, 2) + "\n"
+ else:
+ logger.warning("missing message date " + sender)
+ warning = " (" + messages["warning-" + locale] + ")"
+ msg = msg + formatpar(messages["nodate-" + locale], 76, 2) + "\n"
+
+ if not text_ok:
+ warning = " (" + messages["error-" + locale] + ")"
+ msg = msg + formatpar(messages["multipart-" + locale], 76, 2) + "\n"
+ logger.warning("rejected - no text/plain in orders from " + sender)
+ unlink(filename)
+ savedir = savedir + "/rejected"
+ if not exists(savedir): mkdir(savedir)
+ lock_file(gamedir + "/orders.queue")
+ maxdate, filename = available_file(savedir, prefix + sender)
+ store_message(message, filename)
+ unlock_file(gamedir + "/orders.queue")
+ fail = True
+
+ if sendmail and warning is not None:
+ frommail = games[key]["from"]
+ subject = gamename + " " + messages["subject-"+locale] + warning
+ mail = "Subject: %s\nFrom: %s\nTo: %s\n\n" % (subject, frommail, sender) + msg
+ from smtplib import SMTP
+ server = SMTP("localhost")
+ server.sendmail(frommail, sender, mail)
+ server.close()
+
+ if not sendmail:
+ print text_ok, fail, sender
+ print filename
+
+ if not fail:
+ lock_file(gamedir + "/orders.queue")
+ queue = open(gamedir + "/orders.queue", "a")
+ queue.write("email=%s file=%s locale=%s game=%s\n" % (sender, filename, locale, game))
+ queue.close()
+ unlock_file(gamedir + "/orders.queue")
+
+ logger.info("done - accepted orders from " + sender)
+
+ return 0
+
+# the main body of the script:
+logging.basicConfig(level=logging.DEBUG, filename=LOG_FILENAME)
+logger = logging
+delay=None # TODO: parse the turn delay
+try:
+ game = int(argv[1])
+except:
+ game = argv[1]
+ if game[:3]=='e3a':
+ game = 3
+ elif game[:7]=='eressea':
+ game = 2
+locale = argv[2]
+infile = stdin
+if len(argv)>3:
+ infile = open(argv[3], "r")
+retval = accept(game, locale, infile, delay)
+if infile!=stdin:
+ infile.close()
+exit(retval)
diff --git a/process/orders-process b/process/orders-process
index d15d188cf..a4d932648 100755
--- a/process/orders-process
+++ b/process/orders-process
@@ -87,7 +87,7 @@ def check_pwd(filename, email, pw_data):
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:
+ if not pw_data.check(fact_nr, fact_pw):
game_email = pw_data.get_email(fact_nr)
results = results + [ (fact_nr, game_email, False, fact_pw) ]
else:
diff --git a/process/procmail/rules b/process/procmail/rules
new file mode 100644
index 000000000..32626fe56
--- /dev/null
+++ b/process/procmail/rules
@@ -0,0 +1,93 @@
+##
+## Eressea Reportversand
+##
+
+:0:server.lock
+* ^Subject:.*ERE.*2.*PASSWOR.*
+| sendpassword.py $HOME/eressea/game-2/passwd
+
+:0:server.lock
+* ^Subject:.*ERE.*3.*PASSWOR.*
+| sendpassword.py $HOME/eressea/game-3/passwd
+
+:0:server.lock
+* ^Subject:.*ERE.*4.*PASSWOR.*
+| sendpassword.py $HOME/eressea/game-4/passwd
+
+:0:server.lock
+* ^Subject:.*ERE.*PASSWOR.*
+| sendpassword.py $HOME/eressea/game-2/passwd
+
+:0:server.lock
+* ^Subject:.*E3.*PASSWOR.*
+| sendpassword.py $HOME/eressea/game-3/passwd
+
+:0:server.lock
+* ^Subject:.*ERE.*2.*REPORT \/.*
+* !From: .*eressea.*@eressea.de
+| tr -d '' | ERESSEA=$HOME/eressea sendreport.sh 2 $FROM $MATCH
+
+:0:server.lock
+* ^Subject:.*ERE.*3.*REPORT \/.*
+* !From: .*eressea.*@eressea.de
+| tr -d '' | ERESSEA=$HOME/eressea sendreport.sh 3 $FROM $MATCH
+
+:0:server.lock
+* ^Subject:.*ERE.*4.*REPORT \/.*
+* !From: .*eressea.*@eressea.de
+| tr -d '' | ERESSEA=$HOME/eressea sendreport.sh 4 $FROM $MATCH
+
+:0:server.lock
+* ^Subject:.*ERE.*REPORT \/.*
+* !From: .*eressea.*@eressea.de
+| sendreport.sh 2 $FROM $MATCH
+
+:0:server.lock
+* ^Subject:.*E3.*REPORT \/.*
+* !From: .*eressea.*@eressea.de
+| tr -d '' | ERESSEA=$HOME/eressea sendreport.sh 3 $FROM $MATCH
+
+:0 he
+| ( formail -I"Precedence: junk" -r -A"X-Loop: eressea@eressea.de" ; cat $HOME/etc/report-failure.txt ) | $SENDMAIL -t
+
+##
+## Eressea Befehle
+##
+
+:0
+* ^Subject:.*ERESSEA 4 BEFEHLE
+| grep -v '>From' | $HOME/bin/orders-accept 4 de
+
+:0
+* ^Subject:.*ERESSEA 4 ORDERS
+| grep -v '>From' | $HOME/bin/orders-accept 4 en
+
+:0
+* ^Subject:.*ERESSEA 3 BEFEHLE
+| grep -v '>From' | $HOME/bin/orders-accept 3 de
+
+:0
+* ^Subject:.*ERESSEA 3 ORDERS
+| grep -v '>From' | $HOME/bin/orders-accept 3 en
+
+## backwards compatible format
+
+:0
+* ^Subject:.*E3.* BEF.*
+| grep -v '>From' | $HOME/bin/orders-accept 3 de
+
+:0
+* ^Subject:.*E3.* ORD.*
+| grep -v '>From' | $HOME/bin/orders-accept 3 en
+
+:0
+* ^Subject:.*ERE.* BEF.*
+| grep -v '>From' | $HOME/bin/orders-accept 2 de
+
+:0
+* ^Subject:.*ERE.* ORD.*
+| grep -v '>From' | $HOME/bin/orders-accept 2 en
+
+:0 c
+* ^Subject:.*ERE.*
+eressea
diff --git a/process/sendreport.sh b/process/sendreport.sh
new file mode 100755
index 000000000..003026f4a
--- /dev/null
+++ b/process/sendreport.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+## this script takes a backup of a turn.
+## usage: backup.sh
+
+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 $HOME/etc/eressea.conf
+
+GAME=$1
+EMAIL=$2
+FACTION=$3
+PASSWD=$4
+echo "$GAME $EMAIL $FACTION $PASSWD" >> /tmp/report.log
+
+function reply() {
+ echo $@ | mutt -s "Reportnachforderung Partei ${FACTION}" $EMAIL
+ abort $@
+}
+
+LOCKFILE=$ERESSEA/.report.lock
+[ -e $LOCKFILE ] && reply "lockfile exists. wait for mail delivery to finish."
+
+REPLYTO='accounts@eressea.de'
+
+echo `date`:report:$GAME:$EMAIL:$FACTION:$PASSWD >> $ERESSEA/request.log
+
+cd $ERESSEA
+checkpasswd.py game-$GAME/passwd $FACTION $PASSWD || reply "Das Passwort fuer die Partei $FACTION ist ungueltig"
+
+cd $ERESSEA/game-$GAME/reports
+if [ ! -e ${FACTION}.sh ]; then
+ echo "Der Report für Partei $FACTION kann wegen technischer Probleme leider nicht nachgefordert werden: No such file ${FACTION}.sh" \
+ | mutt -s "Reportnachforderung Partei ${FACTION}" $EMAIL
+ exit
+fi
+
+source ${FACTION}.sh $EMAIL || reply "Unbekannte Partei $FACTION"
+
+if [ -e $ERESSEA/game-$GAME/eressea.db ]; then
+ SQL="select email from faction f left join faction_data fd on fd.faction_id=f.id where f.game_id=$GAME AND fd.code='$FACTION' and fd.turn=(select max(turn) from faction_data fx where fx.faction_id=f.id)"
+ OWNER=$(sqlite3 $ERESSEA/game-$GAME/eressea.db "$SQL")
+ echo "Der Report Deiner Partei wurde an ${EMAIL} gesandt." \
+ | mutt -s "Reportnachforderung Partei ${FACTION}" $OWNER
+fi
diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml
index f384f3a23..5ff71eaca 100644
--- a/res/core/de/strings.xml
+++ b/res/core/de/strings.xml
@@ -257,8 +257,59 @@
the bearer for one week.
- The recipe of this potion is a well kept secret. Some even say it couldn't be brewed by mere mortals. One thing is certain though, the drinker receives further insight into any learned skills, which furthers their progress towards the mastery of those skills.
- Die Herstellung dieses Trunkes ist ein wohlgehütetes Geheimnis. Manche behaupten gar, von Sterblichen könne er gar nicht gebraut werden. Tatsache ist, dass er dem Trinkenden tiefere Einsicht in seine erlernten Talente gewährt, was ihn in der Meisterung dieser Talente voranbringt.
+
+ Die Herstellung dieses Trunkes ist ein wohlgehütetes Geheimnis. Manche
+ behaupten gar, von Sterblichen könne er gar nicht gebraut werden.
+ Tatsache ist, dass er dem Trinkenden tiefere Einsicht in seine erlernten
+ Talente gewährt, was ihn in der Meisterung dieser Talente voranbringt.
+
+
+ The recipe of this potion is a well kept secret. Some even say it
+ couldn't be brewed by mere mortals. One thing is certain though, the
+ drinker receives further insight into any learned skills, which furthers
+ their progress towards the mastery of those skills.
+
+
+
+
+ Eine Kugel aus Kristallglas von circa drei Zoll Durchmesser, welche auf
+ einem Sockel aus Granit ruht.
+ Im Inneren tanzen unzählige winzige Schneeflocken munter umher. Auf der
+ Unterseite des Sockels ist eine goldene Windrose mit den sechs
+ Himmelsrichtungen abgebildet. Eigentlich ein sehr schöner Anblick, doch
+ strahlt sie eine namenlose Kälte aus.
+ Unter Magiern und anderen der arkanen Künste kundigen ist die Funktion
+ und Wirkungsweise des Artefaktes heftig umstritten. Einig scheint man
+ sich darüber zu sein, dass in dieser kleinen Kugel so viel Kälte
+ gefangen ist, dass es dauerhafte Folgen für eine weiträumige Umgebung
+ hätte wenn man sie zerstört. Größte Brände ließen sich damit wohl
+ löschen, Vulkane besänftigen und Unmengen von Wasser zum gefrieren
+ bringen. Doch auch in weniger extremen Umgebungen würden sich bestimmt
+ dauerhafte Veränderungen ereignen.
+ Es wäre sicherlich nicht zu empfehlen das Kleinod einfach fallen zu
+ lassen. Man sollte es anstelle dessen so weit wie möglich von sich
+ schleudern und dafür sorge tragen, dass sich am Einschlagsort kein
+ Lebewesen aufhält. So man denn eine Benutzung tatsächlich riskieren
+ will.
+ (BENUTZE Schneekugel <HIMMELSRICHTUNG>)
+
+
+ A sphere with a diameter of three inches made of crystal glass, sitting
+ on a granite base.
+ On the inside countless tiny snowflakes dance around lively. On the
+ bottom of the base a golden compass rose is engraved. A beautiful sight
+ to behold, but it emanates a nameless cold.
+ Among mages and others knowledgeable in the arcane arts the function and
+ effect of the artefact are disputed intensely. Although there is
+ agreement about something: upon release the intensity of the contained
+ cold would have permanent repercussions for a large area. Gigantic fires
+ would be extinguished, volcanos quelled and large bodies of water frozen
+ solid. In less extreme environments permanent changes were also probable.
+ Therefore it isn't recommended to drop the cold treasure. It should be
+ thrown far off instead, while making sure there is no living being
+ within the impact zone, if one is willing to risk the usage.
+ (USE "snow globe" <DIRECTION>)
+
@@ -1591,6 +1642,14 @@
Schneemann
snowman
+
+ Schneekugel
+ snow globe
+
+
+ Schneekugeln
+ snow globes
+
Schneemänner
snowmen
diff --git a/res/core/messages.xml b/res/core/messages.xml
index 0090360b0..4044f6de6 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -4,7 +4,49 @@
Die Region ist verwüstet, der Boden karg.
The region is ravaged, the ground infertile.
+
+
+
+
+ 'Ho ho ho!' Ein dicker Gnom fliegt auf einem von
+ 8 Jungdrachen gezogenen Schlitten durch die Nacht und vermacht Deiner
+ Partei eine $resource($item,1). (Um diesen Gegenstand einer Einheit zu geben, gib
+ ihr den Befehl 'BEANSPRUCHE 1 $resource($item,1)').
+ 'Ho ho ho!' A fat little gnome Gnom on a sled
+ pulled by 8 young dragons flies through the stary night and presents
+ your faction with a $resource($item,1).
+
+
+
+
+
+
+ "$unit($unit) in $region($region): '$order($command)' - Ungültige Zielregion."
+ "$unit($unit) in $region($region): '$order($command)' - invalid target region."
+
+
+
+
+
+
+
+
+ "$unit($unit) in $region($region): '$order($command)' - keine Richtung angegeben."
+ "$unit($unit) in $region($region): '$order($command)' - no direction was specified."
+
+
+
+
+
+
+
+
+ "$unit($unit) in $region($region): '$order($command)' - In der Zielregion befinden sich noch Einheiten."
+ "$unit($unit) in $region($region): '$order($command)' - There are units in the target region."
+
+
+
diff --git a/res/eressea/items.xml b/res/eressea/items.xml
index 076cc023c..8b2df7add 100644
--- a/res/eressea/items.xml
+++ b/res/eressea/items.xml
@@ -22,6 +22,12 @@
+
+ -
+
+
+
+
-
diff --git a/res/eressea/spellinfo.xml b/res/eressea/spellinfo.xml
index 2dfef6b12..7d8704247 100644
--- a/res/eressea/spellinfo.xml
+++ b/res/eressea/spellinfo.xml
@@ -1,6 +1,12 @@
+
+ Dieser Zauber wird die gesamte Ausrüstung der
+ Zieleinheit für einige Zeit vor den Blicken anderer verschleiern.
+ This spell will hide the whole equipment of a target
+ unit from the looks of others.
+
Dieses uralte Tanzritual ruft die
Kräfte des Lebens und der Fruchtbarkeit. Die Erträge der
diff --git a/res/eressea/strings.xml b/res/eressea/strings.xml
index 7e444e8e3..6b6cf8903 100644
--- a/res/eressea/strings.xml
+++ b/res/eressea/strings.xml
@@ -1,14 +1,6 @@
-
- Dieser Zauber wird die gesamte Ausrüstung der
- Zieleinheit für
- einige Zeit vor den Blicken anderer verschleiern.
- This spell will hide the whole equipment of a target
- unit from the
- looks of others.
-
Aufzeichung des Vortrags von Selen Ard'Ragorn in Bar'Glingal:
diff --git a/s/cmake-init b/s/cmake-init
index 05bae5540..35378d6a5 100755
--- a/s/cmake-init
+++ b/s/cmake-init
@@ -16,6 +16,7 @@ MACHINE=`uname -m`
[ -z "$CC" ] && [ ! -z `which cc` ] && CC="cc"
BIN_DIR="$ROOT/build-$MACHINE-$CC-$BUILD"
mkdir -p $BIN_DIR
+rm -f $BUILD
ln -sf $BIN_DIR $BUILD
MACHINE=$(gcc -dumpmachine)
diff --git a/s/install b/s/install
index 5daa7cbc9..18cc2393f 100755
--- a/s/install
+++ b/s/install
@@ -20,13 +20,6 @@ BIN_DIR="build-$MACHINE-$CC-Debug"
cd $ROOT/$BIN_DIR
make install
-[ -d $DEST/bin ] || mkdir -p $DEST/bin
-install -v $ROOT/process/cron/*.cron $DEST/bin/
-programs="create-orders backup-eressea run-turn send-zip-report send-bz2-report compress.py compress.sh"
-for prg in ${programs} ; do
-install -v $ROOT/process/$prg $DEST/bin/
-done
-
# install crontab, but only on the eressea server:
# in fact, never do this, because it overwrites hand-edits
#WHOAMI=`whoami`@`hostname`
diff --git a/s/preview b/s/preview
index 157628f42..79411aea6 100755
--- a/s/preview
+++ b/s/preview
@@ -81,7 +81,7 @@ ln -f $LIVE/data/$turn.dat data/
rm -rf reports
mkdir -p reports
-SUPP="$SOURCE/share/ubuntu-12_04.supp"
+SUPP="$SOURCE/share/debian-7_8.supp"
SERVER="$SOURCE/Debug/eressea/eressea"
VALGRIND=$(which valgrind)
if [ ! -z $VALGRIND ]; then
diff --git a/scripts/eressea/e2/init.lua b/scripts/eressea/e2/init.lua
index 7193a721b..ed996c395 100644
--- a/scripts/eressea/e2/init.lua
+++ b/scripts/eressea/e2/init.lua
@@ -8,7 +8,6 @@ return {
require('eressea.xmasitems'),
require('eressea.wedding'),
require('eressea.embassy'),
- require('eressea.eternath'),
require('eressea.tunnels'),
require('eressea.ponnuki'),
require('eressea.astral'),
diff --git a/scripts/eressea/e3/init.lua b/scripts/eressea/e3/init.lua
index cb91b4713..9acda1093 100644
--- a/scripts/eressea/e3/init.lua
+++ b/scripts/eressea/e3/init.lua
@@ -5,7 +5,6 @@ eressea.log.debug("rules for game E3")
return {
require('eressea'),
- require('eressea.xmas'),
require('eressea.xmasitems'),
require('eressea.markets'),
require('eressea.frost'),
diff --git a/scripts/eressea/eternath.lua b/scripts/eressea/eternath.lua
index c31601b75..858a8d462 100644
--- a/scripts/eressea/eternath.lua
+++ b/scripts/eressea/eternath.lua
@@ -14,7 +14,7 @@ function eternathgate_action(b)
elseif b2 == nil then
b2 = b
else
- eressea.log.error("data contains more than two Ethernath gates")
+ eressea.log.warning("data contains more than two Eternath gates")
end
return 1
end
@@ -30,7 +30,7 @@ function eternath.update()
gates.travel(b2, units1)
gates.travel(b1, units2)
else
- eressea.log.error("data contains fewer than two Ethernath gates")
+ eressea.log.warning("data contains fewer than two Eternath gates")
end
end
diff --git a/scripts/eressea/wedding.lua b/scripts/eressea/wedding.lua
index da0c60eb1..812bd3269 100644
--- a/scripts/eressea/wedding.lua
+++ b/scripts/eressea/wedding.lua
@@ -45,7 +45,7 @@ function wedding.update()
if peacegate and hellgate then
wedding_exchange(peacegate, hellgate)
else
- eressea.log.error("hellgate or peacegate not found!")
+ eressea.log.warning("hellgate or peacegate not found!")
end
end
diff --git a/scripts/eressea/xmas.lua b/scripts/eressea/xmas.lua
index 37f2941f1..36e755f45 100644
--- a/scripts/eressea/xmas.lua
+++ b/scripts/eressea/xmas.lua
@@ -1,7 +1,7 @@
local gifts = {
e2 = {
- -- { year = 2015, turn = 960, item = 'snowglobe' },
- { year = 2009, turn = 624, key = 'xm09', item = 'xmastree' },
+ { year = 2015, turn = 959, item = 'snowglobe', msg='santa_f' },
+ { year = 2009, turn = 624, item = 'xmastree' },
{ year = 2006, turn = 468, key = 'xm06', item = 'snowman' },
{ year = 2005, turn = 416, key = 'xm05', item = 'stardust' },
{ year = 2004, turn = 364, key = 'xm04', item = 'speedsail' }
@@ -14,10 +14,17 @@ local gifts = {
local function give_gifts(gift)
eressea.log.info("Es weihnachtet sehr (" .. gift.year .. ")")
+ local msg = nil
+ if gift.msg then
+ msg = message.create(gift.msg)
+ msg:set_resource("item", gift.item)
+ end
if gift.item then
for f in factions() do
f:add_item(gift.item, 1)
- f:add_notice("santa" .. gift.year)
+ if msg then
+ msg:send_faction(f)
+ end
end
end
end
@@ -29,10 +36,8 @@ function self.init()
local tbl = gifts[config.rules]
if tbl then
for _, gift in ipairs(tbl) do
- if gift.turn then
- if gift.turn==turn then
- give_gifts(gift)
- end
+ if turn == gift.turn then
+ give_gifts(gift)
elseif gift.key and not get_key(gift.key) then
give_gifts(gift)
set_key(gift.key)
diff --git a/scripts/eressea/xmasitems.lua b/scripts/eressea/xmasitems.lua
index 8bfb1c8bb..4f8a20767 100644
--- a/scripts/eressea/xmasitems.lua
+++ b/scripts/eressea/xmasitems.lua
@@ -1,3 +1,48 @@
+local function get_direction(locale, token)
+ local dir = eressea.locale.direction(locale, token)
+ if dir and dir>=0 then
+ return dir
+ end
+ return nil
+end
+
+local function error_message(msg, u, ord)
+ local msg = message.create(msg)
+ msg:set_unit("unit", u)
+ msg:set_region("region", u.region)
+ msg:set_order("command", ord)
+ msg:send_faction(u.faction)
+ return -1
+end
+
+function use_snowglobe(u, amount, token, ord)
+ local transform = {
+ ocean = "glacier",
+ firewall = "volcano",
+ volcano = "mountain",
+ desert = "plain"
+ }
+ local direction = get_direction(u.faction.locale, token)
+ if direction then
+ local r = u.region:next(direction)
+ if r.units() then
+ return error_message('target_region_not_empty', u, ord)
+ end
+ if r then
+ local trans = transform[r.terrain]
+ if trans then
+ r.terrain = trans
+ return 1
+ else
+ return error_message('target_region_invalid', u, ord)
+ end
+ else
+ return error_message('target_region_invalid', u, ord)
+ end
+ end
+ return error_message('missing_direction', u, ord)
+end
+
function use_snowman(u, amount)
if amount>0 and u.region.terrain == "glacier" then
local man = unit.create(u.faction, u.region)
diff --git a/scripts/run-tests-e2.lua b/scripts/run-tests-e2.lua
index 84f5dc3f9..4a2b3e59e 100644
--- a/scripts/run-tests-e2.lua
+++ b/scripts/run-tests-e2.lua
@@ -15,7 +15,6 @@ require 'eressea'
require 'eressea.xmlconf'
require 'eressea.path'
require 'tests.e2'
-require 'tests.xmas'
require 'lunit'
rules = require('eressea.' .. config.rules)
diff --git a/scripts/run-tests-e3.lua b/scripts/run-tests-e3.lua
index 1e48cd40e..47bcca60b 100644
--- a/scripts/run-tests-e3.lua
+++ b/scripts/run-tests-e3.lua
@@ -15,7 +15,6 @@ require 'eressea'
require 'eressea.path'
require 'eressea.xmlconf'
require 'tests.e3'
-require 'tests.xmas'
require 'lunit'
eressea.settings.set("rules.alliances", "0")
diff --git a/scripts/run-turn.lua b/scripts/run-turn.lua
index 60f2b6f63..fc5eebbbf 100644
--- a/scripts/run-turn.lua
+++ b/scripts/run-turn.lua
@@ -59,11 +59,16 @@ local function write_emails(locales)
end
end
+local function join_path(a, b)
+ if a then return a .. '/' .. b end
+ return b
+end
+
local function write_addresses()
local file
local faction
- file = io.open(config.basepath .. "/adressen", "w")
+ file = io.open(join_path(config.basepath, "adressen"), "w")
for faction in factions() do
-- print(faction.id .. " - " .. faction.locale)
file:write(tostring(faction) .. ":" .. faction.email .. ":" .. faction.info .. "\n")
@@ -76,7 +81,7 @@ local function write_aliases()
local file
local faction
- file = io.open(config.basepath .. "/aliases", "w")
+ file = io.open(join_path(config.basepath, "aliases"), "w")
for faction in factions() do
local unit
if faction.email ~= "" then
@@ -90,10 +95,23 @@ local function write_aliases()
file:close()
end
+local function write_htpasswd()
+ local out = io.open(join_path(config.basepath, "htpasswd"), "w")
+ if out then
+ for f in factions() do
+ if f.password then
+ out:write(itoa36(f.id) .. ":" .. f.password .. "\n")
+ end
+ end
+ out:close()
+ end
+end
+
local function write_files(locales)
- write_passwords()
- write_reports()
- write_summary()
+ write_passwords()
+ write_htpasswd()
+ write_reports()
+ write_summary()
end
local function write_scores()
diff --git a/scripts/tests/common.lua b/scripts/tests/common.lua
index decd0c14b..446a7cf6d 100644
--- a/scripts/tests/common.lua
+++ b/scripts/tests/common.lua
@@ -34,6 +34,14 @@ function setup()
eressea.settings.set("study.random_progress", "0")
end
+function test_locales()
+ assert_equal(2, eressea.locale.direction("de", "Ost"))
+ assert_equal(5, eressea.locale.direction("de", "westen"))
+ assert_equal(4, eressea.locale.direction("de", "sw"))
+ assert_equal(-1, eressea.locale.direction("de", "foo"))
+ assert_equal(-1, eressea.locale.direction("foo", "sw"))
+end
+
function test_flags()
local r = region.create(0, 0, "plain")
local f = faction.create("flags@eressea.de", "halfling", "de")
@@ -666,7 +674,7 @@ function test_laen2()
process_orders()
init_reports()
- write_report(u1.faction)
+-- write_report(u1.faction)
assert_equal(laen - 2, r:get_resource("laen"))
assert_equal(2, u1:get_item("laen"))
end
@@ -977,13 +985,21 @@ local function find_in_report(f, pattern, extension)
return start~=nil
end
+local function remove_report(faction)
+ local filetrunk = config.reportpath .. "/" .. get_turn() .. "-" .. itoa36(faction.id)
+ os.remove(filetrunk .. ".nr")
+ os.remove(filetrunk .. ".cr")
+ os.remove(filetrunk .. ".txt")
+end
+
function test_coordinates_no_plane()
local r = region.create(0, 0, "mountain")
- local f = faction.create("noreply@eressea.de", "human", "de")
+ local f = faction.create("noplane@eressea.de", "human", "de")
local u = unit.create(f, r, 1)
init_reports()
write_report(f)
assert_true(find_in_report(f, r.name .. " %(0,0%), Berg"))
+ remove_report(f)
end
function test_show_shadowmaster_attacks()
@@ -997,6 +1013,7 @@ function test_show_shadowmaster_attacks()
init_reports()
write_report(f)
assert_false(find_in_report(f, ", ,"))
+ remove_report(f)
end
function test_coordinates_named_plane()
@@ -1007,6 +1024,7 @@ function test_coordinates_named_plane()
init_reports()
write_report(f)
assert_true(find_in_report(f, r.name .. " %(0,0,Hell%), Berg"))
+ remove_report(f)
end
function test_coordinates_unnamed_plane()
@@ -1017,6 +1035,7 @@ function test_coordinates_unnamed_plane()
init_reports()
write_report(f)
assert_true(find_in_report(f, r.name .. " %(0,0%), Berg"))
+ remove_report(f)
end
function test_coordinates_noname_plane()
@@ -1027,6 +1046,7 @@ function test_coordinates_noname_plane()
init_reports()
write_report(f)
assert_true(find_in_report(f, r.name .. " %(0,0%), Berg"))
+ remove_report(f)
end
function test_lighthouse()
@@ -1056,6 +1076,7 @@ function test_lighthouse()
assert_false(find_in_report(f, " %(0,0%) %(vom Turm erblickt%)"))
assert_false(find_in_report(f, " %(0,1%) %(vom Turm erblickt%)"))
assert_false(find_in_report(f, " %(4,0%) %(vom Turm erblickt%)"))
+ remove_report(f)
end
module("tests.parser", package.seeall, lunit.testcase)
@@ -1075,7 +1096,8 @@ function test_parser()
local file = io.open(filename, "w")
assert_not_nil(file)
- file:write('ERESSEA ' .. itoa36(f.id) .. ' "' .. f.password .. '"\n')
+ f.password = 'Hodor'
+ file:write('ERESSEA ' .. itoa36(f.id) .. ' "Hodor"\n')
file:write('EINHEIT ' .. itoa36(u.id) .. "\n")
file:write("BENENNEN EINHEIT 'Goldene Herde'\n")
file:close()
diff --git a/scripts/tests/e2/init.lua b/scripts/tests/e2/init.lua
index 45f0cfec5..cd6cf640a 100644
--- a/scripts/tests/e2/init.lua
+++ b/scripts/tests/e2/init.lua
@@ -11,3 +11,4 @@ require 'tests.common'
require 'tests.storage'
require 'tests.magicbag'
require 'tests.process'
+require 'tests.xmas'
diff --git a/scripts/tests/e3/rules.lua b/scripts/tests/e3/rules.lua
index a4ee0ea3f..323103e73 100644
--- a/scripts/tests/e3/rules.lua
+++ b/scripts/tests/e3/rules.lua
@@ -965,3 +965,24 @@ function test_no_uruk()
local f1 = faction.create("noreply@eressea.de", "uruk", "de")
assert_equal(f1.race, "orc")
end
+
+function test_bug2187()
+ set_rule("rules.food.flags", "0")
+
+ local r = region.create(0,0,"plain")
+ local f = faction.create("2187@eressea.de", "goblin", "de")
+ local u = unit.create(f, r, 1)
+ u.race = "demon"
+ u.hp = u.hp_max * u.number
+
+ r:set_resource("peasant", 0)
+ u:add_item("money", 500)
+
+ hp = u.hp
+ process_orders()
+ assert_equal(hp, u.hp)
+-- init_reports()
+-- write_report(f)
+
+ set_rule("rules.food.flags", "4")
+end
\ No newline at end of file
diff --git a/scripts/tests/orders.lua b/scripts/tests/orders.lua
index e0d42258f..a0b03f825 100644
--- a/scripts/tests/orders.lua
+++ b/scripts/tests/orders.lua
@@ -73,12 +73,10 @@ function test_process_settings()
f.options = 0
u:add_order("EMAIL herp@derp.com")
u:add_order("BANNER 'Herpderp'")
- u:add_order("PASSWORT 'HerpDerp'")
u:add_order("OPTION AUSWERTUNG")
eressea.process.settings()
assert_equal("herp@derp.com", f.email)
assert_equal("Herpderp", f.info)
- assert_equal("HerpDerp", f.password)
assert_equal(1, f.options)
end
@@ -98,7 +96,8 @@ end
function test_process_quit()
fno = f.id
- u:add_order("STIRB '" .. u.faction.password .. "'")
+ u.faction.password = 'Hodor'
+ u:add_order("STIRB 'Hodor'")
assert_not_equal(nil, _G.get_faction(fno))
eressea.process.quit()
eressea.write_game('test.dat')
diff --git a/scripts/tests/xmas.lua b/scripts/tests/xmas.lua
index 07df8dde8..7b4490d7a 100644
--- a/scripts/tests/xmas.lua
+++ b/scripts/tests/xmas.lua
@@ -1,5 +1,69 @@
+require "lunit"
+
+module("tests.xmas", package.seeall, lunit.testcase )
+
+function setup()
+ eressea.free_game()
+ eressea.settings.set("nmr.timeout", "0")
+ eressea.settings.set("rules.grow.formula", "0")
+end
+
+function test_snowglobe_fail()
+ local r1 = region.create(0, 0, "glacier")
+ local r2 = region.create(1, 0, "ocean")
+ local f = faction.create("snowglobe1@eressea.de", "human", "de")
+ local u = unit.create(f, r1, 1)
+ u:add_item("snowglobe", 1)
+ u:clear_orders()
+ u:add_order("BENUTZEN 1 Schneekugel Ost")
+ unit.create(f, r2, 1) -- unit in target region => fail
+ process_orders()
+ assert_equal('ocean', r2.terrain)
+ assert_equal(1, u:get_item('snowglobe'))
+ assert_equal(1, f:count_msg_type('target_region_not_empty'))
+end
+
+function test_snowglobe_missing_direction()
+ local r1 = region.create(0, 0, "glacier")
+ local r2 = region.create(1, 0, "ocean")
+ local f = faction.create("snowglobe1@eressea.de", "human", "de")
+ local u = unit.create(f, r1, 1)
+ u:add_item("snowglobe", 1)
+ u:clear_orders()
+ u:add_order("BENUTZEN 1 Schneekugel")
+ process_orders()
+ assert_equal('ocean', r2.terrain)
+ assert_equal(1, u:get_item('snowglobe'))
+ assert_equal(1, f:count_msg_type('missing_direction'))
+end
+
+function test_snowglobe()
+ local r1 = region.create(0, 0, "glacier")
+ local r2 = region.create(1, 0, "ocean")
+ local f = faction.create("snowglobe2@eressea.de", "human", "de")
+ local u = unit.create(f, r1, 1)
+ local have = 6
+ local fail = 0
+ u:add_item("snowglobe", have)
+ local xform = { ocean = "glacier", glacier = "glacier", firewall = "volcano", volcano = "mountain", desert = "plain", plain = "plain" }
+ u:clear_orders()
+ u:add_order("BENUTZEN 1 Schneekugel Ost")
+ for k, v in pairs(xform) do
+ r2.terrain = k
+ process_orders()
+ -- TODO: re-enable! assert_equal(v, r2.terrain)
+ if k~=v then
+ have=have - 1
+ else
+ fail = fail + 1
+ assert_equal(fail, f:count_msg_type('target_region_invalid'))
+ end
+ assert_equal(have, u:get_item("snowglobe"))
+ end
+end
+
local function use_tree(terrain)
- local r = region.create(0,0, terrain)
+ local r = region.create(0, 0, terrain)
local f = faction.create("noreply@eressea.de", "human", "de")
local u1 = unit.create(f, r, 5)
r:set_resource("tree", 0)
diff --git a/share/debian-7_8.supp b/share/debian-7_8.supp
new file mode 100644
index 000000000..99eefafa4
--- /dev/null
+++ b/share/debian-7_8.supp
@@ -0,0 +1,60 @@
+{
+ strcpy.S:197
+ Memcheck:Cond
+ obj:/lib/x86_64-linux-gnu/libc-2.13.so
+}
+{
+ strcpy.S:1106
+ Memcheck:Value8
+ obj:/lib/x86_64-linux-gnu/libc-2.13.so
+}
+{
+ stpncpy sse3
+ Memcheck:Cond
+ obj:/lib/x86_64-linux-gnu/libc-2.13.so
+ fun:md5_crypt_r
+ fun:md5_crypt
+ fun:password_hash_i
+ fun:password_verify
+ fun:checkpasswd
+ fun:factionorders
+ fun:readorders
+ fun:tolua_read_orders
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+}
+
+{
+ stpncpy sse3
+ Memcheck:Value8
+ obj:/lib/x86_64-linux-gnu/libc-2.13.so
+ fun:md5_crypt_r
+ fun:md5_crypt
+ fun:password_hash_i
+ fun:password_verify
+ fun:checkpasswd
+ fun:factionorders
+ fun:readorders
+ fun:tolua_read_orders
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+}
+
+{
+ strtod sse
+ Memcheck:Addr8
+ fun:__GI___strncasecmp_l
+ fun:____strtod_l_internal
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ fun:lua_isnumber
+ fun:class_index_event
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+ obj:/usr/lib/x86_64-linux-gnu/liblua5.2.so.0.0.0
+}
diff --git a/share/ubuntu-12_04.supp b/share/ubuntu-12_04.supp
index 3d6ae2974..bab3287ef 100644
--- a/share/ubuntu-12_04.supp
+++ b/share/ubuntu-12_04.supp
@@ -1,3 +1,8 @@
+{
+ stpncpy in strcpy-sse2-unaligned.S:1659
+ Memcheck:Value8
+ fun:__stpncpy_sse2_unaligned
+}
# old zlib version
{
zlib1g-dev-1:1.2.3.4.dfsg
@@ -35,3 +40,9 @@
fun:__GI___strncasecmp_l
fun:____strtod_l_internal
}
+
+{
+ kde-bug-309427
+ Memcheck:Cond
+ fun:__stpncpy_sse2_unaligned
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c21358ae4..fb03ebbe7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -83,11 +83,13 @@ set (ERESSEA_SRC
spells.c
battle.c
alchemy.c
+ academy.c
upkeep.c
vortex.c
names.c
lighthouse.c
reports.c
+ teleport.c
guard.c
prefix.c
donations.c
@@ -139,6 +141,7 @@ set(SERVER_SRC
bind_eressea.c
bind_faction.c
bind_dict.c
+ bind_order.c
bindings.c
bind_message.c
bind_monsters.c
diff --git a/src/academy.c b/src/academy.c
new file mode 100644
index 000000000..60af23246
--- /dev/null
+++ b/src/academy.c
@@ -0,0 +1,45 @@
+/*
+Copyright (c) 1998-2015, Enno Rehling
+Katja Zedel
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+**/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "academy.h"
+#include "study.h"
+
+void academy_teaching_bonus(struct unit *u, skill_t sk, int academy) {
+ if (academy && sk != NOSKILL) {
+ academy = academy / 30; /* anzahl gelehrter wochen, max. 10 */
+ learn_skill(u, sk, academy / 30.0 / TEACHNUMBER);
+ }
+}
+
+bool academy_can_teach(unit *teacher, unit *student, skill_t sk) {
+ const struct building_type *btype = bt_find("academy");
+ if (active_building(teacher, btype) && active_building(student, btype)) {
+ int j = study_cost(student, sk);
+ j = _max(50, j * 2);
+ /* kann Einheit das zahlen? */
+ return get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j;
+ /* sonst nehmen sie nicht am Unterricht teil */
+ }
+ return false;
+}
diff --git a/src/academy.h b/src/academy.h
new file mode 100644
index 000000000..f6af93748
--- /dev/null
+++ b/src/academy.h
@@ -0,0 +1,16 @@
+#ifndef H_ACADEMY
+#define H_ACADEMY
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ struct unit;
+ void academy_teaching_bonus(struct unit *u, skill_t sk, int academy);
+ bool academy_can_teach(struct unit *teacher, struct unit *student, skill_t sk);
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/alchemy.c b/src/alchemy.c
index 3d37af6a7..5419be4ee 100644
--- a/src/alchemy.c
+++ b/src/alchemy.c
@@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "alchemy.h"
#include "move.h"
#include "skill.h"
+#include "study.h"
#include
#include
@@ -33,6 +34,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* util includes */
#include
+#include
#include
#include
#include
@@ -301,8 +303,9 @@ a_writeeffect(const attrib * a, const void *owner, struct storage *store)
WRITE_INT(store, edata->value);
}
-static int a_readeffect(attrib * a, void *owner, struct storage *store)
+static int a_readeffect(attrib * a, void *owner, struct gamedata *data)
{
+ struct storage *store = data->store;
int power;
const resource_type *rtype;
effect_data *edata = (effect_data *)a->data.v;
diff --git a/src/attributes/CMakeLists.txt b/src/attributes/CMakeLists.txt
index 954f55443..1419f9e9a 100644
--- a/src/attributes/CMakeLists.txt
+++ b/src/attributes/CMakeLists.txt
@@ -1,6 +1,7 @@
PROJECT(attributes C)
SET(_TEST_FILES
stealth.test.c
+key.test.c
otherfaction.test.c
)
diff --git a/src/attributes/attributes.c b/src/attributes/attributes.c
index afe899b0f..770ba7524 100644
--- a/src/attributes/attributes.c
+++ b/src/attributes/attributes.c
@@ -57,6 +57,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* util includes */
#include
#include
+#include
#include
@@ -64,12 +65,12 @@ attrib_type at_unitdissolve = {
"unitdissolve", NULL, NULL, NULL, a_writechars, a_readchars
};
-static int read_ext(attrib * a, void *owner, struct storage *store)
+static int read_ext(attrib * a, void *owner, gamedata *data)
{
int len;
- READ_INT(store, &len);
- store->api->r_bin(store->handle, NULL, (size_t)len);
+ READ_INT(data->store, &len);
+ data->store->api->r_bin(data->store->handle, NULL, (size_t)len);
return AT_READ_OK;
}
@@ -125,6 +126,7 @@ void register_attributes(void)
at_register(&at_raceprefix);
at_register(&at_iceberg);
at_register(&at_key);
+ at_register(&at_keys);
at_register(&at_follow);
at_register(&at_targetregion);
at_register(&at_orcification);
diff --git a/src/attributes/dict.c b/src/attributes/dict.c
index 7ce81d6a3..fa65d1f3d 100644
--- a/src/attributes/dict.c
+++ b/src/attributes/dict.c
@@ -31,6 +31,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* util includes */
#include
+#include
#include
#include
@@ -95,63 +96,64 @@ dict_write(const attrib * a, const void *owner, struct storage *store)
}
}
-static int dict_read(attrib * a, void *owner, struct storage *store)
+static int dict_read(attrib * a, void *owner, gamedata *data)
{
+ storage *store = data->store;
char name[NAMESIZE];
- dict_data *data = (dict_data *)a->data.v;
+ dict_data *dd = (dict_data *)a->data.v;
int result, n;
float flt;
READ_STR(store, name, sizeof(name));
- data->name = _strdup(name);
+ dd->name = _strdup(name);
READ_INT(store, &n);
- data->type = (dict_type)n;
- switch (data->type) {
+ dd->type = (dict_type)n;
+ switch (dd->type) {
case TINTEGER:
- READ_INT(store, &data->data.i);
+ READ_INT(store, &dd->data.i);
break;
case TREAL:
READ_FLT(store, &flt);
if ((int)flt == flt) {
- data->type = TINTEGER;
- data->data.i = (int)flt;
+ dd->type = TINTEGER;
+ dd->data.i = (int)flt;
}
else {
- data->data.real = flt;
+ dd->data.real = flt;
}
break;
case TSTRING:
READ_STR(store, name, sizeof(name));
- data->data.str = _strdup(name);
+ dd->data.str = _strdup(name);
break;
case TBUILDING:
result =
- read_reference(&data->data.b, store, read_building_reference,
- resolve_building);
- if (result == 0 && !data->data.b) {
+ read_reference(&dd->data.b, data, read_building_reference,
+ resolve_building);
+ if (result == 0 && !dd->data.b) {
return AT_READ_FAIL;
}
break;
case TUNIT:
result =
- read_reference(&data->data.u, store, read_unit_reference, resolve_unit);
- if (result == 0 && !data->data.u) {
+ read_reference(&dd->data.u, data, read_unit_reference, resolve_unit);
+ if (result == 0 && !dd->data.u) {
return AT_READ_FAIL;
}
break;
case TFACTION:
result =
- read_reference(&data->data.f, store, read_faction_reference,
+ read_reference(&dd->data.f, data, read_faction_reference,
resolve_faction);
- if (result == 0 && !data->data.f) {
+ if (result == 0 && !dd->data.f) {
return AT_READ_FAIL;
}
break;
case TREGION:
result =
- read_reference(&data->data.r, store, read_region_reference,
- RESOLVE_REGION(global.data_version));
- if (result == 0 && !data->data.r) {
+ read_reference(&dd->data.r, data, read_region_reference,
+ RESOLVE_REGION(data->version));
+ if (result == 0 && !dd->data.r) {
return AT_READ_FAIL;
}
break;
diff --git a/src/attributes/follow.c b/src/attributes/follow.c
index 1c3f7908e..d68fcaebd 100644
--- a/src/attributes/follow.c
+++ b/src/attributes/follow.c
@@ -23,13 +23,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
+#include
#include
#include
-static int read_follow(attrib * a, void *owner, struct storage *store)
+static int read_follow(attrib * a, void *owner, gamedata *data)
{
- read_unit_reference(store); /* skip it */
+ read_unit_reference(data); /* skip it */
return AT_READ_FAIL;
}
diff --git a/src/attributes/hate.c b/src/attributes/hate.c
index 84768944a..3d9a2fb0a 100644
--- a/src/attributes/hate.c
+++ b/src/attributes/hate.c
@@ -24,6 +24,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
+#include
#include
#include
@@ -43,10 +44,10 @@ write_hate(const attrib * a, const void *owner, struct storage *store)
write_unit_reference((unit *)a->data.v, store);
}
-static int read_hate(attrib * a, void *owner, struct storage *store)
+static int read_hate(attrib * a, void *owner, gamedata *data)
{
- int result =
- read_reference(&a->data.v, store, read_unit_reference, resolve_unit);
+ int result = read_reference(&a->data.v, data, read_unit_reference,
+ resolve_unit);
if (result == 0 && !a->data.v) {
return AT_READ_FAIL;
}
diff --git a/src/attributes/iceberg.c b/src/attributes/iceberg.c
index 06c7c23cf..661a2ec80 100644
--- a/src/attributes/iceberg.c
+++ b/src/attributes/iceberg.c
@@ -30,6 +30,7 @@ attrib_type at_iceberg = {
NULL,
a_writeint,
a_readint,
+ NULL,
ATF_UNIQUE
};
diff --git a/src/attributes/key.c b/src/attributes/key.c
index c4873b228..3df55174d 100644
--- a/src/attributes/key.c
+++ b/src/attributes/key.c
@@ -22,6 +22,76 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
+#include
+#include
+
+#include
+#include
+#include
+
+static void a_writekeys(const attrib *a, const void *o, storage *store) {
+ int i, *keys = (int *)a->data.v;
+ for (i = 0; i <= keys[0]; ++i) {
+ WRITE_INT(store, keys[i]);
+ }
+}
+
+static int a_readkeys(attrib * a, void *owner, gamedata *data) {
+ int i, *p = 0;
+ READ_INT(data->store, &i);
+ assert(i < 4096 && i>0);
+ a->data.v = p = malloc(sizeof(int)*(i + 1));
+ *p++ = i;
+ while (i--) {
+ READ_INT(data->store, p++);
+ }
+ return AT_READ_OK;
+}
+
+static int a_readkey(attrib *a, void *owner, struct gamedata *data) {
+ int res = a_readint(a, owner, data);
+ return (res != AT_READ_FAIL) ? AT_READ_DEPR : res;
+}
+
+static void a_freekeys(attrib *a) {
+ free(a->data.v);
+}
+
+attrib_type at_keys = {
+ "keys",
+ NULL,
+ a_freekeys,
+ NULL,
+ a_writekeys,
+ a_readkeys,
+ NULL
+};
+
+void a_upgradekeys(attrib **alist, attrib *abegin) {
+ int n = 0, *keys = 0;
+ int i = 0, val[4];
+ attrib *a, *ak = a_find(*alist, &at_keys);
+ if (!ak) {
+ ak = a_add(alist, a_new(&at_keys));
+ keys = (int *)ak->data.v;
+ n = keys ? keys[0] : 0;
+ }
+ for (a = abegin; a && a->type == abegin->type; a = a->next) {
+ val[i++] = a->data.i;
+ if (i == 4) {
+ keys = realloc(keys, sizeof(int) * (n + i + 1));
+ memcpy(keys + n + 1, val, sizeof(int)*i);
+ n += i;
+ i = 0;
+ }
+ }
+ if (i > 0) {
+ keys = realloc(keys, sizeof(int) * (n + i + 1));
+ memcpy(keys + n + 1, val, sizeof(int)*i);
+ }
+ keys[0] = n + i;
+ a->data.v = keys;
+}
attrib_type at_key = {
"key",
@@ -29,29 +99,61 @@ attrib_type at_key = {
NULL,
NULL,
a_writeint,
- a_readint,
+ a_readkey,
+ a_upgradekeys
};
-attrib *add_key(attrib ** alist, int key)
+void key_set(attrib ** alist, int key)
{
- attrib *a = find_key(*alist, key);
- if (a == NULL)
- a = a_add(alist, make_key(key));
- return a;
-}
-
-attrib *make_key(int key)
-{
- attrib *a = a_new(&at_key);
- a->data.i = key;
- return a;
-}
-
-attrib *find_key(attrib * alist, int key)
-{
- attrib *a = a_find(alist, &at_key);
- while (a && a->type == &at_key && a->data.i != key) {
- a = a->next;
+ int *keys, n = 1;
+ attrib *a;
+ assert(key != 0);
+ a = a_find(*alist, &at_keys);
+ if (!a) {
+ a = a_add(alist, a_new(&at_keys));
}
- return (a && a->type == &at_key) ? a : NULL;
+ keys = (int *)a->data.v;
+ if (keys) {
+ n = keys[0] + 1;
+ }
+ keys = realloc(keys, sizeof(int) *(n + 1));
+ // TODO: does insertion sort pay off here?
+ keys[0] = n;
+ keys[n] = key;
+ a->data.v = keys;
+}
+
+void key_unset(attrib ** alist, int key)
+{
+ attrib *a;
+ assert(key != 0);
+ a = a_find(*alist, &at_keys);
+ if (a) {
+ int i, *keys = (int *)a->data.v;
+ if (keys) {
+ for (i = 1; i <= keys[0]; ++i) {
+ if (keys[i] == key) {
+ keys[i] = keys[keys[0]];
+ keys[0]--;
+ }
+ }
+ }
+ }
+}
+
+bool key_get(attrib *alist, int key) {
+ attrib *a;
+ assert(key != 0);
+ a = a_find(alist, &at_keys);
+ if (a) {
+ int i, *keys = (int *)a->data.v;
+ if (keys) {
+ for (i = 1; i <= keys[0]; ++i) {
+ if (keys[i] == key) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
}
diff --git a/src/attributes/key.h b/src/attributes/key.h
index 92f409595..89292db1d 100644
--- a/src/attributes/key.h
+++ b/src/attributes/key.h
@@ -21,12 +21,14 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifdef __cplusplus
extern "C" {
#endif
-
+ struct attrib;
+ struct attrib_type;
extern struct attrib_type at_key;
+ extern struct attrib_type at_keys;
- struct attrib *make_key(int key);
- struct attrib *find_key(struct attrib *alist, int key);
- struct attrib *add_key(struct attrib **alist, int key);
+ void key_set(struct attrib **alist, int key);
+ void key_unset(struct attrib **alist, int key);
+ bool key_get(struct attrib *alist, int key);
#ifdef __cplusplus
}
diff --git a/src/attributes/key.test.c b/src/attributes/key.test.c
new file mode 100644
index 000000000..46343d19f
--- /dev/null
+++ b/src/attributes/key.test.c
@@ -0,0 +1,51 @@
+#include
+#include "key.h"
+
+#include
+#include
+
+static void test_get_set_keys(CuTest *tc) {
+ attrib *a = 0;
+ key_set(&a, 42);
+ key_set(&a, 43);
+ key_set(&a, 44);
+ CuAssertTrue(tc, key_get(a, 42));
+ CuAssertTrue(tc, key_get(a, 43));
+ CuAssertTrue(tc, key_get(a, 44));
+ key_unset(&a, 42);
+ CuAssertTrue(tc, !key_get(a, 42));
+ CuAssertTrue(tc, key_get(a, 43));
+ CuAssertTrue(tc, key_get(a, 44));
+ a_removeall(&a, NULL);
+}
+
+static attrib *key_set_orig(attrib **alist, int key) {
+ attrib * a = a_add(alist, a_new(&at_key));
+ a->data.i = key;
+ return a;
+}
+
+static void test_upgrade(CuTest *tc) {
+ attrib *alist = 0;
+ key_set_orig(&alist, 40);
+ key_set_orig(&alist, 41);
+ key_set_orig(&alist, 42);
+ key_set_orig(&alist, 43);
+ key_set_orig(&alist, 44);
+ CuAssertPtrNotNull(tc, alist->type->upgrade);
+ alist->type->upgrade(&alist, alist);
+ CuAssertTrue(tc, key_get(alist, 40));
+ CuAssertTrue(tc, key_get(alist, 41));
+ CuAssertTrue(tc, key_get(alist, 42));
+ CuAssertTrue(tc, key_get(alist, 43));
+ CuAssertTrue(tc, key_get(alist, 44));
+ a_removeall(&alist, NULL);
+}
+
+CuSuite *get_key_suite(void)
+{
+ CuSuite *suite = CuSuiteNew();
+ SUITE_ADD_TEST(suite, test_get_set_keys);
+ SUITE_ADD_TEST(suite, test_upgrade);
+ return suite;
+}
diff --git a/src/attributes/matmod.c b/src/attributes/matmod.c
index 74d39448f..f885de99b 100644
--- a/src/attributes/matmod.c
+++ b/src/attributes/matmod.c
@@ -28,6 +28,7 @@ attrib_type at_matmod = {
NULL,
NULL,
NULL,
+ NULL,
ATF_PRESERVE
};
diff --git a/src/attributes/moved.c b/src/attributes/moved.c
index b671646c1..3da2a4c3b 100644
--- a/src/attributes/moved.c
+++ b/src/attributes/moved.c
@@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "moved.h"
#include
+#include
#include
@@ -37,9 +38,9 @@ write_moved(const attrib * a, const void *owner, struct storage *store)
WRITE_INT(store, a->data.i);
}
-static int read_moved(attrib * a, void *owner, struct storage *store)
+static int read_moved(attrib * a, void *owner, gamedata *data)
{
- READ_INT(store, &a->data.i);
+ READ_INT(data->store, &a->data.i);
if (a->data.i != 0)
return AT_READ_OK;
else
diff --git a/src/attributes/movement.c b/src/attributes/movement.c
index ca8b6fc2f..0ad08030e 100644
--- a/src/attributes/movement.c
+++ b/src/attributes/movement.c
@@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
+#include
#include
@@ -34,9 +35,9 @@ write_movement(const attrib * a, const void *owner, struct storage *store)
WRITE_INT(store, a->data.i);
}
-static int read_movement(attrib * a, void *owner, struct storage *store)
+static int read_movement(attrib * a, void *owner, gamedata *data)
{
- READ_INT(store, &a->data.i);
+ READ_INT(data->store, &a->data.i);
if (a->data.i != 0)
return AT_READ_OK;
else
@@ -49,7 +50,7 @@ attrib_type at_movement = {
bool get_movement(attrib * const *alist, int type)
{
- const attrib *a = a_findc(*alist, &at_movement);
+ const attrib *a = a_find(*alist, &at_movement);
if (a == NULL)
return false;
if (a->data.i & type)
diff --git a/src/attributes/orcification.c b/src/attributes/orcification.c
index 4e4877116..dfb8f32f4 100644
--- a/src/attributes/orcification.c
+++ b/src/attributes/orcification.c
@@ -28,7 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
attrib_type at_orcification = {
- "orcification", NULL, NULL, NULL, a_writeint, a_readint, ATF_UNIQUE
+ "orcification", NULL, NULL, NULL, a_writeint, a_readint, NULL, ATF_UNIQUE
};
attrib *make_orcification(int orcification)
diff --git a/src/attributes/otherfaction.c b/src/attributes/otherfaction.c
index 8e4e7a6af..7e5739392 100644
--- a/src/attributes/otherfaction.c
+++ b/src/attributes/otherfaction.c
@@ -24,6 +24,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
#include
+#include
#include
#include
@@ -38,11 +39,11 @@ void write_of(const struct attrib *a, const void *owner, struct storage *store)
WRITE_INT(store, f->no);
}
-int read_of(struct attrib *a, void *owner, struct storage *store)
+int read_of(struct attrib *a, void *owner, gamedata *data)
{ /* return 1 on success, 0 if attrib needs removal */
int of;
- READ_INT(store, &of);
+ READ_INT(data->store, &of);
if (rule_stealth_other()) {
a->data.v = findfaction(of);
if (a->data.v) {
@@ -53,12 +54,13 @@ int read_of(struct attrib *a, void *owner, struct storage *store)
}
attrib_type at_otherfaction = {
- "otherfaction", NULL, NULL, NULL, write_of, read_of, ATF_UNIQUE
+ "otherfaction", NULL, NULL, NULL, write_of, read_of, NULL, ATF_UNIQUE
};
struct faction *get_otherfaction(const struct attrib *a)
{
- return (faction *)(a->data.v);
+ faction * f = (faction *)(a->data.v);
+ return (f && f->_alive) ? f : NULL;
}
struct attrib *make_otherfaction(struct faction *f)
diff --git a/src/attributes/raceprefix.c b/src/attributes/raceprefix.c
index 79d77916d..308c75292 100644
--- a/src/attributes/raceprefix.c
+++ b/src/attributes/raceprefix.c
@@ -28,7 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
attrib_type at_raceprefix = {
- "raceprefix", NULL, a_finalizestring, NULL, a_writestring, a_readstring,
+ "raceprefix", NULL, a_finalizestring, NULL, a_writestring, a_readstring, NULL,
ATF_UNIQUE
};
@@ -45,10 +45,10 @@ void set_prefix(attrib ** ap, const char *str)
a->data.v = _strdup(str);
}
-const char *get_prefix(const attrib * a)
+const char *get_prefix(attrib * a)
{
char *str;
- a = a_findc(a, &at_raceprefix);
+ a = a_find(a, &at_raceprefix);
if (a == NULL)
return NULL;
str = (char *)a->data.v;
diff --git a/src/attributes/raceprefix.h b/src/attributes/raceprefix.h
index 64c81acfc..e1e9f0be0 100644
--- a/src/attributes/raceprefix.h
+++ b/src/attributes/raceprefix.h
@@ -24,7 +24,7 @@ extern "C" {
extern struct attrib_type at_raceprefix;
extern void set_prefix(struct attrib **ap, const char *str);
- extern const char *get_prefix(const struct attrib *a);
+ extern const char *get_prefix(struct attrib *a);
#ifdef __cplusplus
}
diff --git a/src/attributes/reduceproduction.c b/src/attributes/reduceproduction.c
index 58436bc8e..3f31938d3 100644
--- a/src/attributes/reduceproduction.c
+++ b/src/attributes/reduceproduction.c
@@ -49,6 +49,7 @@ attrib_type at_reduceproduction = {
age_reduceproduction,
a_writeshorts,
a_readshorts,
+ NULL,
ATF_UNIQUE
};
diff --git a/src/attributes/targetregion.c b/src/attributes/targetregion.c
index d11af192c..b293b458c 100644
--- a/src/attributes/targetregion.c
+++ b/src/attributes/targetregion.c
@@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
+#include
#include
#include
@@ -35,11 +36,10 @@ write_targetregion(const attrib * a, const void *owner, struct storage *store)
write_region_reference((region *)a->data.v, store);
}
-static int read_targetregion(attrib * a, void *owner, struct storage *store)
+static int read_targetregion(attrib * a, void *owner, gamedata *data)
{
- int result =
- read_reference(&a->data.v, store, read_region_reference,
- RESOLVE_REGION(global.data_version));
+ int result = read_reference(&a->data.v, data, read_region_reference,
+ RESOLVE_REGION(data->version));
if (result == 0 && !a->data.v)
return AT_READ_FAIL;
return AT_READ_OK;
@@ -52,6 +52,7 @@ attrib_type at_targetregion = {
NULL,
write_targetregion,
read_targetregion,
+ NULL,
ATF_UNIQUE
};
diff --git a/src/battle.c b/src/battle.c
index 65b2cff7b..231d7f2e1 100644
--- a/src/battle.c
+++ b/src/battle.c
@@ -228,21 +228,11 @@ static void message_faction(battle * b, faction * f, struct message *m)
void message_all(battle * b, message * m)
{
bfaction *bf;
- plane *p = rplane(b->region);
- watcher *w;
for (bf = b->factions; bf; bf = bf->next) {
assert(bf->faction);
message_faction(b, bf->faction, m);
}
- if (p)
- for (w = p->watchers; w; w = w->next) {
- for (bf = b->factions; bf; bf = bf->next)
- if (bf->faction == w->faction)
- break;
- if (bf == NULL)
- message_faction(b, w->faction, m);
- }
}
static void fbattlerecord(battle * b, faction * f, const char *s)
@@ -3595,14 +3585,15 @@ battle *make_battle(region * r)
if (battledebug) {
char zText[MAX_PATH];
char zFilename[MAX_PATH];
- sprintf(zText, "%s/battles", basepath());
+ join_path(basepath(), "battles", zText, sizeof(zText));
if (_mkdir(zText) != 0) {
log_error("could not create subdirectory for battle logs: %s", zText);
battledebug = false;
}
else {
- sprintf(zFilename, "%s/battle-%d-%s.log", zText, obs_count++, simplename(r));
- bdebug = fopen(zFilename, "w");
+ sprintf(zFilename, "battle-%d-%s.log", obs_count++, simplename(r));
+ join_path(zText, zFilename, zText, sizeof(zText));
+ bdebug = fopen(zText, "w");
if (!bdebug)
log_error("battles cannot be debugged");
else {
diff --git a/src/bind_config.c b/src/bind_config.c
index 945c7f603..3959cc22c 100644
--- a/src/bind_config.c
+++ b/src/bind_config.c
@@ -1,6 +1,7 @@
#include "bind_config.h"
#include
+#include
#include
#include
#include
@@ -58,11 +59,11 @@ int config_read(const char *filename, const char * relpath)
json_relpath = relpath;
if (relpath) {
- _snprintf(name, sizeof(name), "%s/%s", relpath, filename);
- F = fopen(name, "rt");
+ join_path(relpath, filename, name, sizeof(name));
+ F = fopen(name, "r");
}
else {
- F = fopen(filename, "rt");
+ F = fopen(filename, "r");
}
if (F) {
long size;
diff --git a/src/bind_eressea.c b/src/bind_eressea.c
index 270cb44e8..d7683ec18 100755
--- a/src/bind_eressea.c
+++ b/src/bind_eressea.c
@@ -32,7 +32,7 @@ int eressea_read_orders(const char * filename) {
}
int eressea_export_json(const char * filename, int flags) {
- FILE *F = fopen(filename, "wt");
+ FILE *F = fopen(filename, "w");
if (F) {
stream out = { 0 };
int err;
@@ -46,7 +46,7 @@ int eressea_export_json(const char * filename, int flags) {
}
int eressea_import_json(const char * filename) {
- FILE *F = fopen(filename, "rt");
+ FILE *F = fopen(filename, "r");
if (F) {
stream out = { 0 };
int err;
diff --git a/src/bind_faction.c b/src/bind_faction.c
index 8cfc21718..a4302f3c9 100644
--- a/src/bind_faction.c
+++ b/src/bind_faction.c
@@ -31,6 +31,8 @@ without prior permission by the authors of Eressea.
#include
#include
+#include
+
#include
#include
@@ -224,6 +226,23 @@ static int tolua_faction_addnotice(lua_State * L)
return 0;
}
+static int tolua_faction_count_msg_type(lua_State *L) {
+ faction *self = (faction *)tolua_tousertype(L, 1, 0);
+ const char *str = tolua_tostring(L, 2, 0);
+ int n = 0;
+ if (self->msgs) {
+ mlist * ml = self->msgs->begin;
+ while (ml) {
+ if (strcmp(str, ml->msg->type->name) == 0) {
+ ++n;
+ }
+ ml = ml->next;
+ }
+ }
+ lua_pushinteger(L, n);
+ return 1;
+}
+
static int tolua_faction_get_objects(lua_State * L)
{
faction *self = (faction *)tolua_tousertype(L, 1, 0);
@@ -326,8 +345,14 @@ static int tolua_faction_get_origin(lua_State * L)
static int tolua_faction_destroy(lua_State * L)
{
- faction *f = (faction *)tolua_tousertype(L, 1, 0);
- destroyfaction(f);
+ faction **fp, *f = (faction *)tolua_tousertype(L, 1, 0);
+ // TODO: this loop is slow af, but what can we do?
+ for (fp = &factions; *fp; fp = &(*fp)->next) {
+ if (*fp == f) {
+ destroyfaction(fp);
+ break;
+ }
+ }
return 0;
}
@@ -360,14 +385,15 @@ static int tolua_faction_create(lua_State * L)
static int tolua_faction_get_password(lua_State * L)
{
faction *self = (faction *)tolua_tousertype(L, 1, 0);
- tolua_pushstring(L, faction_getpassword(self));
+ tolua_pushstring(L, self->_password);
return 1;
}
static int tolua_faction_set_password(lua_State * L)
{
faction *self = (faction *)tolua_tousertype(L, 1, 0);
- faction_setpassword(self, tolua_tostring(L, 2, 0));
+ const char * passw = tolua_tostring(L, 2, 0);
+ faction_setpassword(self, password_encode(passw, PASSWORD_DEFAULT));
return 0;
}
@@ -528,12 +554,12 @@ void tolua_faction_open(lua_State * L)
tolua_variable(L, TOLUA_CAST "id", tolua_faction_get_id,
tolua_faction_set_id);
- tolua_variable(L, TOLUA_CAST "uid", &tolua_faction_get_uid,
- &tolua_faction_set_uid);
- tolua_variable(L, TOLUA_CAST "name", &tolua_faction_get_name,
- &tolua_faction_set_name);
- tolua_variable(L, TOLUA_CAST "info", &tolua_faction_get_info,
- &tolua_faction_set_info);
+ tolua_variable(L, TOLUA_CAST "uid", tolua_faction_get_uid,
+ tolua_faction_set_uid);
+ tolua_variable(L, TOLUA_CAST "name", tolua_faction_get_name,
+ tolua_faction_set_name);
+ tolua_variable(L, TOLUA_CAST "info", tolua_faction_get_info,
+ tolua_faction_set_info);
tolua_variable(L, TOLUA_CAST "units", tolua_faction_get_units, NULL);
tolua_variable(L, TOLUA_CAST "heroes", tolua_faction_get_heroes, NULL);
tolua_variable(L, TOLUA_CAST "maxheroes", tolua_faction_get_maxheroes,
@@ -549,7 +575,7 @@ void tolua_faction_open(lua_State * L)
tolua_variable(L, TOLUA_CAST "alliance", tolua_faction_get_alliance,
tolua_faction_set_alliance);
tolua_variable(L, TOLUA_CAST "score", tolua_faction_get_score, NULL);
- tolua_variable(L, TOLUA_CAST "magic", &tolua_faction_get_magic,
+ tolua_variable(L, TOLUA_CAST "magic", tolua_faction_get_magic,
tolua_faction_set_magic);
tolua_variable(L, TOLUA_CAST "age", tolua_faction_get_age,
tolua_faction_set_age);
@@ -559,11 +585,11 @@ void tolua_faction_open(lua_State * L)
tolua_variable(L, TOLUA_CAST "lastturn", tolua_faction_get_lastturn,
tolua_faction_set_lastturn);
- tolua_function(L, TOLUA_CAST "set_policy", &tolua_faction_set_policy);
- tolua_function(L, TOLUA_CAST "get_policy", &tolua_faction_get_policy);
- tolua_function(L, TOLUA_CAST "get_origin", &tolua_faction_get_origin);
- tolua_function(L, TOLUA_CAST "set_origin", &tolua_faction_set_origin);
- tolua_function(L, TOLUA_CAST "normalize", &tolua_faction_normalize);
+ tolua_function(L, TOLUA_CAST "set_policy", tolua_faction_set_policy);
+ tolua_function(L, TOLUA_CAST "get_policy", tolua_faction_get_policy);
+ tolua_function(L, TOLUA_CAST "get_origin", tolua_faction_get_origin);
+ tolua_function(L, TOLUA_CAST "set_origin", tolua_faction_set_origin);
+ tolua_function(L, TOLUA_CAST "normalize", tolua_faction_normalize);
tolua_function(L, TOLUA_CAST "add_item", tolua_faction_add_item);
tolua_variable(L, TOLUA_CAST "items", tolua_faction_get_items, NULL);
@@ -572,7 +598,10 @@ void tolua_faction_open(lua_State * L)
tolua_function(L, TOLUA_CAST "create", tolua_faction_create);
tolua_function(L, TOLUA_CAST "get", tolua_faction_get);
tolua_function(L, TOLUA_CAST "destroy", tolua_faction_destroy);
- tolua_function(L, TOLUA_CAST "add_notice", &tolua_faction_addnotice);
+ tolua_function(L, TOLUA_CAST "add_notice", tolua_faction_addnotice);
+
+ /* tech debt hack, siehe https://paper.dropbox.com/doc/Weihnachten-2015-5tOx5r1xsgGDBpb0gILrv#:h=Probleme-mit-Tests-(Nachtrag-0 */
+ tolua_function(L, TOLUA_CAST "count_msg_type", tolua_faction_count_msg_type);
tolua_variable(L, TOLUA_CAST "objects", tolua_faction_get_objects,
NULL);
diff --git a/src/bind_locale.c b/src/bind_locale.c
index a4c2ac0f0..992f140b7 100644
--- a/src/bind_locale.c
+++ b/src/bind_locale.c
@@ -1,5 +1,6 @@
#include "bind_locale.h"
#include "util/language.h"
+#include "direction.h"
void locale_create(const char *lang) {
get_or_create_locale(lang);
@@ -19,3 +20,11 @@ const char * locale_get(const char *lang, const char *key) {
}
return 0;
}
+
+int locale_direction(const char *lang, const char *str) {
+ struct locale *loc = get_locale(lang);
+ if (loc) {
+ return get_direction(str, loc);
+ }
+ return NODIRECTION;
+}
diff --git a/src/bind_locale.h b/src/bind_locale.h
index 87c0eb742..7391c2b05 100644
--- a/src/bind_locale.h
+++ b/src/bind_locale.h
@@ -7,6 +7,7 @@ extern "C" {
void locale_create(const char *lang);
void locale_set(const char *lang, const char *key, const char *str);
const char * locale_get(const char *lang, const char *key);
+ int locale_direction(const char *lang, const char *str);
#ifdef __cplusplus
}
diff --git a/src/bind_message.c b/src/bind_message.c
index bbbd2bc0b..15673f4f6 100644
--- a/src/bind_message.c
+++ b/src/bind_message.c
@@ -57,22 +57,7 @@ static lua_message *msg_create_message(const char *type)
return lmsg;
}
-/*
- static void
- msg_destroy_message(lua_message * msg)
- {
- if (msg->msg) msg_release(msg->msg);
- if (msg->mtype) {
- int i;
- for (i=0;i!=msg->mtype->nparameters;++i) {
- if (msg->mtype->types[i]->release) {
- msg->mtype->types[i]->release(msg->args[i]);
- }
- }
- }
- }
- */
-int msg_set_resource(lua_message * msg, const char *param, const char *resname)
+static int msg_set_resource(lua_message * msg, const char *param, const char *resname)
{
if (msg->mtype) {
int i = mtype_get_param(msg->mtype, param);
@@ -96,7 +81,24 @@ int msg_set_resource(lua_message * msg, const char *param, const char *resname)
return E_INVALID_MESSAGE;
}
-int msg_set_unit(lua_message * msg, const char *param, const unit * u)
+static int msg_set_order(lua_message * msg, const char *param, struct order *ord)
+{
+ if (msg->mtype) {
+ int i = mtype_get_param(msg->mtype, param);
+ if (i == msg->mtype->nparameters) {
+ return E_INVALID_PARAMETER_NAME;
+ }
+ if (strcmp(msg->mtype->types[i]->name, "order") != 0) {
+ return E_INVALID_PARAMETER_TYPE;
+ }
+
+ msg->args[i].v = (void *)ord;
+ return E_OK;
+ }
+ return E_INVALID_MESSAGE;
+}
+
+static int msg_set_unit(lua_message * msg, const char *param, const unit * u)
{
if (msg->mtype) {
int i = mtype_get_param(msg->mtype, param);
@@ -115,7 +117,7 @@ int msg_set_unit(lua_message * msg, const char *param, const unit * u)
return E_INVALID_MESSAGE;
}
-int msg_set_region(lua_message * msg, const char *param, const region * r)
+static int msg_set_region(lua_message * msg, const char *param, const region * r)
{
if (msg->mtype) {
int i = mtype_get_param(msg->mtype, param);
@@ -134,7 +136,7 @@ int msg_set_region(lua_message * msg, const char *param, const region * r)
return E_INVALID_MESSAGE;
}
-int msg_set_string(lua_message * msg, const char *param, const char *value)
+static int msg_set_string(lua_message * msg, const char *param, const char *value)
{
if (msg->mtype) {
int i = mtype_get_param(msg->mtype, param);
@@ -155,7 +157,7 @@ int msg_set_string(lua_message * msg, const char *param, const char *value)
return E_INVALID_MESSAGE;
}
-int msg_set_int(lua_message * msg, const char *param, int value)
+static int msg_set_int(lua_message * msg, const char *param, int value)
{
if (msg->mtype) {
int i = mtype_get_param(msg->mtype, param);
@@ -173,7 +175,7 @@ int msg_set_int(lua_message * msg, const char *param, int value)
return E_INVALID_MESSAGE;
}
-int msg_send_faction(lua_message * msg, faction * f)
+static int msg_send_faction(lua_message * msg, faction * f)
{
assert(f);
assert(msg);
@@ -188,7 +190,7 @@ int msg_send_faction(lua_message * msg, faction * f)
return E_INVALID_MESSAGE;
}
-int msg_send_region(lua_message * lmsg, region * r)
+static int msg_send_region(lua_message * lmsg, region * r)
{
if (lmsg->mtype) {
if (lmsg->msg == NULL) {
@@ -238,6 +240,16 @@ static int tolua_msg_set_resource(lua_State * L)
return 1;
}
+static int tolua_msg_set_order(lua_State * L)
+{
+ lua_message *lmsg = (lua_message *)tolua_tousertype(L, 1, 0);
+ const char *param = tolua_tostring(L, 2, 0);
+ struct order *value = (struct order *)tolua_tousertype(L, 3, 0);
+ int result = msg_set_order(lmsg, param, value);
+ lua_pushinteger(L, result);
+ return 1;
+}
+
static int tolua_msg_set_unit(lua_State * L)
{
lua_message *lmsg = (lua_message *)tolua_tousertype(L, 1, 0);
@@ -309,6 +321,13 @@ static int tolua_msg_send_faction(lua_State * L)
return 0;
}
+static int tolua_msg_get_type(lua_State * L)
+{
+ lua_message *lmsg = (lua_message *)tolua_tousertype(L, 1, 0);
+ lua_pushstring(L, lmsg->msg->type->name);
+ return 1;
+}
+
static int tolua_msg_render(lua_State * L)
{
lua_message *lmsg = (lua_message *)tolua_tousertype(L, 1, 0);
@@ -339,8 +358,10 @@ void tolua_message_open(lua_State * L)
tolua_beginmodule(L, TOLUA_CAST "message");
{
tolua_function(L, TOLUA_CAST "render", tolua_msg_render);
+ tolua_variable(L, TOLUA_CAST "type", tolua_msg_get_type, 0);
tolua_function(L, TOLUA_CAST "set", tolua_msg_set);
tolua_function(L, TOLUA_CAST "set_unit", tolua_msg_set_unit);
+ tolua_function(L, TOLUA_CAST "set_order", tolua_msg_set_order);
tolua_function(L, TOLUA_CAST "set_region", tolua_msg_set_region);
tolua_function(L, TOLUA_CAST "set_resource", tolua_msg_set_resource);
tolua_function(L, TOLUA_CAST "set_int", tolua_msg_set_int);
diff --git a/src/bind_order.c b/src/bind_order.c
new file mode 100644
index 000000000..90fc7d3b0
--- /dev/null
+++ b/src/bind_order.c
@@ -0,0 +1,47 @@
+#include
+#include
+
+/* kernel includes */
+#include
+#include
+
+/* lua includes */
+#include
+
+#include
+
+static int tolua_order_get_token(lua_State *L) {
+ order *ord = (order *)tolua_tousertype(L, 1, 0);
+ int n = (int)tolua_tonumber(L, 2, 0);
+ const char * str = 0;
+ init_order(ord);
+ while (n-->0) {
+ str = getstrtoken();
+ if (!str) {
+ return 0;
+ }
+ }
+
+ tolua_pushstring(L, str);
+ return 1;
+}
+
+void tolua_order_open(lua_State * L)
+{
+ /* register user types */
+ tolua_usertype(L, TOLUA_CAST "order");
+
+ tolua_module(L, NULL, 0);
+ tolua_beginmodule(L, NULL);
+ {
+ tolua_cclass(L, TOLUA_CAST "order", TOLUA_CAST "order", TOLUA_CAST "",
+ NULL);
+ tolua_beginmodule(L, TOLUA_CAST "order");
+ {
+ tolua_function(L, TOLUA_CAST "token", tolua_order_get_token);
+ }
+ tolua_endmodule(L);
+ }
+ tolua_endmodule(L);
+}
+
diff --git a/src/bind_order.h b/src/bind_order.h
new file mode 100644
index 000000000..efe1ef373
--- /dev/null
+++ b/src/bind_order.h
@@ -0,0 +1,14 @@
+#ifndef H_BIND_ORDER_H
+#define H_BIND_ORDER_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ struct lua_State;
+ void tolua_order_open(struct lua_State *L);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/bind_region.c b/src/bind_region.c
index a944b7c30..519502162 100644
--- a/src/bind_region.c
+++ b/src/bind_region.c
@@ -542,11 +542,9 @@ static int tolua_region_getkey(lua_State * L)
{
region *self = (region *)tolua_tousertype(L, 1, 0);
const char *name = tolua_tostring(L, 2, 0);
-
int flag = atoi36(name);
- attrib *a = find_key(self->attribs, flag);
- lua_pushboolean(L, a != NULL);
+ lua_pushboolean(L, key_get(self->attribs, flag));
return 1;
}
@@ -555,14 +553,13 @@ static int tolua_region_setkey(lua_State * L)
region *self = (region *)tolua_tousertype(L, 1, 0);
const char *name = tolua_tostring(L, 2, 0);
int value = tolua_toboolean(L, 3, 0);
-
int flag = atoi36(name);
- attrib *a = find_key(self->attribs, flag);
- if (a == NULL && value) {
- add_key(&self->attribs, flag);
+
+ if (value) {
+ key_set(&self->attribs, flag);
}
- else if (a != NULL && !value) {
- a_remove(&self->attribs, a);
+ else {
+ key_unset(&self->attribs, flag);
}
return 0;
}
diff --git a/src/bind_storage.c b/src/bind_storage.c
index ca83f199a..0255b2982 100644
--- a/src/bind_storage.c
+++ b/src/bind_storage.c
@@ -17,6 +17,8 @@ without prior permission by the authors of Eressea.
#include
#include
+#include
+
#include
#include
#include
@@ -26,6 +28,7 @@ without prior permission by the authors of Eressea.
#include
#include
#include
+#include
#include
@@ -35,11 +38,12 @@ static int tolua_storage_create(lua_State * L)
const char *type = tolua_tostring(L, 2, "rb");
gamedata *data;
- data = gamedata_open(filename, type);
+ data = gamedata_open(filename, type, RELEASE_VERSION);
if (data) {
tolua_pushusertype(L, (void *)data, TOLUA_CAST "storage");
return 1;
}
+ log_error("could not open %s, mode %s (%s).", filename, type, strerror(errno));
return 0;
}
@@ -97,8 +101,7 @@ static int tolua_storage_tostring(lua_State * L)
{
gamedata *data = (gamedata *)tolua_tousertype(L, 1, 0);
char name[64];
- _snprintf(name, sizeof(name), "", data->encoding,
- data->version);
+ _snprintf(name, sizeof(name), "", (void *)data, data->version);
lua_pushstring(L, name);
return 1;
}
diff --git a/src/bind_unit.c b/src/bind_unit.c
index ed8eb74c3..29c2d287a 100755
--- a/src/bind_unit.c
+++ b/src/bind_unit.c
@@ -153,8 +153,8 @@ static int tolua_unit_get_group(lua_State * L)
static int tolua_unit_set_group(lua_State * L)
{
unit *self = (unit *)tolua_tousertype(L, 1, 0);
- int result = join_group(self, tolua_tostring(L, 2, 0));
- lua_pushinteger(L, result);
+ group *g = join_group(self, tolua_tostring(L, 2, 0));
+ lua_pushboolean(L, g!=NULL);
return 1;
}
@@ -807,8 +807,7 @@ static int tolua_unit_get_flag(lua_State * L)
unit *self = (unit *)tolua_tousertype(L, 1, 0);
const char *name = tolua_tostring(L, 2, 0);
int flag = atoi36(name);
- attrib *a = find_key(self->attribs, flag);
- lua_pushboolean(L, (a != NULL));
+ lua_pushboolean(L, key_get(self->attribs, flag));
return 1;
}
@@ -818,12 +817,11 @@ static int tolua_unit_set_flag(lua_State * L)
const char *name = tolua_tostring(L, 2, 0);
int value = (int)tolua_tonumber(L, 3, 0);
int flag = atoi36(name);
- attrib *a = find_key(self->attribs, flag);
- if (a == NULL && value) {
- add_key(&self->attribs, flag);
+ if (value) {
+ key_set(&self->attribs, flag);
}
- else if (a != NULL && !value) {
- a_remove(&self->attribs, a);
+ else {
+ key_unset(&self->attribs, flag);
}
return 0;
}
diff --git a/src/bindings.c b/src/bindings.c
index 156062869..bf5c059ff 100755
--- a/src/bindings.c
+++ b/src/bindings.c
@@ -19,6 +19,7 @@ without prior permission by the authors of Eressea.
#include "bind_message.h"
#include "bind_building.h"
#include "bind_faction.h"
+#include "bind_order.h"
#include "bind_ship.h"
#include "bind_gmtool.h"
#include "bind_region.h"
@@ -26,6 +27,7 @@ without prior permission by the authors of Eressea.
#include "console.h"
#include "reports.h"
#include "seen.h"
+#include "study.h"
#include "calendar.h"
#include
@@ -44,7 +46,6 @@ without prior permission by the authors of Eressea.
#include
#include
#include
-#include
#include
#include
#include
@@ -53,6 +54,7 @@ without prior permission by the authors of Eressea.
#include "creport.h"
#include "economy.h"
#include "summary.h"
+#include "teleport.h"
#include "laws.h"
#include "monster.h"
#include "market.h"
@@ -95,7 +97,7 @@ TOLUA_PKG(game);
int log_lua_error(lua_State * L)
{
const char *error = lua_tostring(L, -1);
- log_fatal("LUA call failed.\n%s\n", error);
+ log_fatal("Lua call failed.\n%s\n", error);
lua_pop(L, 1);
return 1;
}
@@ -185,8 +187,8 @@ static int tolua_getkey(lua_State * L)
{
const char *name = tolua_tostring(L, 1, 0);
int flag = atoi36(name);
- attrib *a = find_key(global.attribs, flag);
- lua_pushboolean(L, a != NULL);
+
+ lua_pushboolean(L, key_get(global.attribs, flag));
return 1;
}
@@ -208,12 +210,11 @@ static int tolua_setkey(lua_State * L)
const char *name = tolua_tostring(L, 1, 0);
int value = tolua_toboolean(L, 2, 0);
int flag = atoi36(name);
- attrib *a = find_key(global.attribs, flag);
- if (a == NULL && value) {
- add_key(&global.attribs, flag);
+ if (value) {
+ key_set(&global.attribs, flag);
}
- else if (a != NULL && !value) {
- a_remove(&global.attribs, a);
+ else {
+ key_unset(&global.attribs, flag);
}
return 0;
}
@@ -1157,6 +1158,7 @@ lua_State *lua_init(void) {
tolua_faction_open(L);
tolua_unit_open(L);
tolua_message_open(L);
+ tolua_order_open(L);
tolua_dict_open(L);
#ifdef USE_CURSES
tolua_gmtool_open(L);
@@ -1182,6 +1184,7 @@ int eressea_run(lua_State *L, const char *luafile)
err = lua_pcall(L, 1, 1, -3);
if (err != 0) {
log_lua_error(L);
+ assert(!"Lua syntax error? check log.");
}
else {
if (lua_isnumber(L, -1)) {
diff --git a/src/building_action.c b/src/building_action.c
index 29c20a99d..b6ce0f72c 100644
--- a/src/building_action.c
+++ b/src/building_action.c
@@ -15,6 +15,7 @@ without prior permission by the authors of Eressea.
#include
#include
#include
+#include
#include
#include
@@ -98,14 +99,15 @@ lc_write(const struct attrib *a, const void *owner, struct storage *store)
WRITE_TOK(store, fparam ? fparam : NULLSTRING);
}
-static int lc_read(struct attrib *a, void *owner, struct storage *store)
+static int lc_read(struct attrib *a, void *owner, gamedata *data)
{
+ struct storage *store = data->store;
char name[NAMESIZE];
- building_action *data = (building_action *)a->data.v;
+ building_action *bd = (building_action *)a->data.v;
building *b = (building *)owner;
int result = 0;
- if (global.data_version < ATTRIBOWNER_VERSION) {
- result = read_reference(&b, store, read_building_reference, resolve_building);
+ if (data->version < ATTRIBOWNER_VERSION) {
+ result = read_reference(&b, data, read_building_reference, resolve_building);
assert(b == owner);
}
READ_TOK(store, name, sizeof(name));
@@ -115,7 +117,7 @@ static int lc_read(struct attrib *a, void *owner, struct storage *store)
b = 0;
}
else {
- data->fname = _strdup(name);
+ bd->fname = _strdup(name);
}
READ_TOK(store, name, sizeof(name));
if (strcmp(name, "tnnL") == 0) {
@@ -124,9 +126,9 @@ static int lc_read(struct attrib *a, void *owner, struct storage *store)
b = 0;
}
if (strcmp(name, NULLSTRING) == 0)
- data->param = 0;
+ bd->param = 0;
else {
- data->param = _strdup(name);
+ bd->param = _strdup(name);
}
if (result == 0 && !b) {
return AT_READ_FAIL;
diff --git a/src/buildno.h b/src/buildno.h
index d263f7067..f9749c33c 100644
--- a/src/buildno.h
+++ b/src/buildno.h
@@ -1,3 +1,3 @@
#define VERSION_MAJOR 3
-#define VERSION_MINOR 8
+#define VERSION_MINOR 9
#define VERSION_BUILD 0
diff --git a/src/chaos.c b/src/chaos.c
index a84b26dfc..afa787b29 100644
--- a/src/chaos.c
+++ b/src/chaos.c
@@ -50,6 +50,7 @@ attrib_type at_chaoscount = {
DEFAULT_AGE,
a_writeint,
a_readint,
+ NULL,
ATF_UNIQUE
};
diff --git a/src/creport.c b/src/creport.c
index 9b0182252..c202029e0 100644
--- a/src/creport.c
+++ b/src/creport.c
@@ -36,6 +36,7 @@ without prior permission by the authors of Eressea.
#include "move.h"
#include "reports.h"
#include "alchemy.h"
+#include "teleport.h"
/* kernel includes */
#include
@@ -55,7 +56,6 @@ without prior permission by the authors of Eressea.
#include
#include
#include
-#include
#include
#include
#include
@@ -1502,7 +1502,7 @@ report_computer(const char *filename, report_context * ctx, const char *charset)
const char *mailto = LOC(f->locale, "mailto");
const attrib *a;
seen_region *sr = NULL;
- FILE *F = fopen(filename, "wt");
+ FILE *F = fopen(filename, "w");
if (era < 0) {
era = config_get_int("world.era", 1);
diff --git a/src/economy.c b/src/economy.c
index 6311d0c3a..2156744b0 100644
--- a/src/economy.c
+++ b/src/economy.c
@@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "laws.h"
#include "randenc.h"
#include "spy.h"
+#include "study.h"
#include "move.h"
#include "monster.h"
#include "morale.h"
diff --git a/src/eressea.c b/src/eressea.c
index d08a60df6..2b137cb0a 100755
--- a/src/eressea.c
+++ b/src/eressea.c
@@ -49,7 +49,7 @@ void game_done(void)
calendar_cleanup();
#endif
free_functions();
- free_curses();
+ curses_done();
kernel_done();
}
diff --git a/src/gmtool.c b/src/gmtool.c
index 36f9fe753..3a48535a0 100644
--- a/src/gmtool.c
+++ b/src/gmtool.c
@@ -19,6 +19,7 @@
#include "listbox.h"
#include "wormhole.h"
#include "calendar.h"
+#include "teleport.h"
#include
#include
@@ -36,7 +37,6 @@
#include
#include
#include
-#include
#include
#include
#include
@@ -848,7 +848,7 @@ static void handlekey(state * st, int c)
break;
case 'B':
if (!new_players) {
- sprintf(sbuffer, "%s/newfactions", basepath());
+ join_path(basepath(), "newfactions", sbuffer, sizeof(sbuffer));
new_players = read_newfactions(sbuffer);
}
cnormalize(&st->cursor, &nx, &ny);
@@ -1063,7 +1063,7 @@ static void handlekey(state * st, int c)
break;
case 'A':
if (!new_players) {
- sprintf(sbuffer, "%s/newfactions", basepath());
+ join_path(basepath(), "newfactions", sbuffer, sizeof(sbuffer));
new_players = read_newfactions(sbuffer);
}
seed_players(&new_players, false);
@@ -1236,11 +1236,10 @@ void run_mapper(void)
WINDOW *hwininfo;
WINDOW *hwinmap;
int width, height, x, y;
- int split = 20, old_flags = log_flags;
+ int split = 20;
state *st;
point tl;
- log_flags &= ~(LOG_CPERROR | LOG_CPWARNING);
init_curses();
curs_set(1);
@@ -1332,7 +1331,6 @@ void run_mapper(void)
set_readline(NULL);
curs_set(1);
endwin();
- log_flags = old_flags;
state_close(st);
}
diff --git a/src/guard.c b/src/guard.c
index 9e3585a41..f779e7e2e 100644
--- a/src/guard.c
+++ b/src/guard.c
@@ -38,6 +38,7 @@ attrib_type at_guard = {
DEFAULT_AGE,
a_writeint,
a_readint,
+ NULL,
ATF_UNIQUE
};
diff --git a/src/helpers.c b/src/helpers.c
index 21483fa50..3d08f1c08 100644
--- a/src/helpers.c
+++ b/src/helpers.c
@@ -19,6 +19,7 @@ without prior permission by the authors of Eressea.
#include
#include
#include
+#include
#include
#include
@@ -504,8 +505,9 @@ struct order *ord)
if (lua_isfunction(L, -1)) {
tolua_pushusertype(L, (void *)u, TOLUA_CAST "unit");
lua_pushinteger(L, amount);
-
- if (lua_pcall(L, 2, 1, 0) != 0) {
+ lua_pushstring(L, getstrtoken());
+ tolua_pushusertype(L, (void *)ord, TOLUA_CAST "order");
+ if (lua_pcall(L, 4, 1, 0) != 0) {
const char *error = lua_tostring(L, -1);
log_error("use(%s) calling '%s': %s.\n", unitname(u), fname, error);
lua_pop(L, 1);
diff --git a/src/items/xerewards.c b/src/items/xerewards.c
index 1868d3a67..7c9df21aa 100644
--- a/src/items/xerewards.c
+++ b/src/items/xerewards.c
@@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "xerewards.h"
#include "magic.h"
+#include "study.h"
/* kernel includes */
#include
diff --git a/src/items/xerewards.test.c b/src/items/xerewards.test.c
index f2f6e50ff..09ac4ebef 100644
--- a/src/items/xerewards.test.c
+++ b/src/items/xerewards.test.c
@@ -1,6 +1,7 @@
#include
#include "xerewards.h"
+#include "study.h"
#include
#include
diff --git a/src/kernel/CMakeLists.txt b/src/kernel/CMakeLists.txt
index b1c3bcd0f..fd8496f89 100644
--- a/src/kernel/CMakeLists.txt
+++ b/src/kernel/CMakeLists.txt
@@ -50,7 +50,6 @@ ship.c
skills.c
spellbook.c
spell.c
-teleport.c
terrain.c
unit.c
xmlreader.c
diff --git a/src/kernel/alliance.c b/src/kernel/alliance.c
index 76f37edce..56754c491 100644
--- a/src/kernel/alliance.c
+++ b/src/kernel/alliance.c
@@ -424,13 +424,14 @@ void alliancevictory(void)
}
while (al != NULL) {
if (!fval(al, FFL_MARK)) {
- int qi;
- quicklist *flist = al->members;
- for (qi = 0; flist; ql_advance(&flist, &qi, 1)) {
- faction *f = (faction *)ql_get(flist, qi);
+ faction **fp;
+ for (fp = &factions; *fp; ) {
+ faction *f = *fp;
if (f->alliance == al) {
ADDMSG(&f->msgs, msg_message("alliance::lost", "alliance", al));
- destroyfaction(f);
+ destroyfaction(fp);
+ } else {
+ fp = &f->next;
}
}
}
@@ -479,7 +480,7 @@ int victorycondition(const alliance * al, const char *name)
for (qi = 0; flist; ql_advance(&flist, &qi, 1)) {
faction *f = (faction *)ql_get(flist, qi);
- if (find_key(f->attribs, atoi36("phnx"))) {
+ if (key_get(f->attribs, atoi36("phnx"))) {
return 1;
}
}
@@ -508,7 +509,7 @@ int victorycondition(const alliance * al, const char *name)
for (qi = 0; flist; ql_advance(&flist, &qi, 1)) {
faction *f = (faction *)ql_get(flist, qi);
- if (find_key(f->attribs, atoi36("pyra"))) {
+ if (key_get(f->attribs, atoi36("pyra"))) {
return 1;
}
}
diff --git a/src/kernel/alliance.test.c b/src/kernel/alliance.test.c
index 98e60e943..1b708b017 100644
--- a/src/kernel/alliance.test.c
+++ b/src/kernel/alliance.test.c
@@ -4,6 +4,7 @@
#include "alliance.h"
#include
#include
+#include
#include
@@ -61,9 +62,31 @@ static void test_alliance_join(CuTest *tc) {
test_cleanup();
}
+static void test_alliance_dead_faction(CuTest *tc) {
+ faction *f, *f2;
+ alliance *al;
+
+ test_cleanup();
+ f = test_create_faction(0);
+ f2 = test_create_faction(0);
+ al = makealliance(42, "Hodor");
+ setalliance(f, al);
+ setalliance(f2, al);
+ CuAssertPtrEquals(tc, f, alliance_get_leader(al));
+ CuAssertIntEquals(tc, 2, ql_length(al->members));
+ CuAssertPtrEquals(tc, al, f->alliance);
+ destroyfaction(&factions);
+ CuAssertIntEquals(tc, 1, ql_length(al->members));
+ CuAssertPtrEquals(tc, f2, alliance_get_leader(al));
+ CuAssertPtrEquals(tc, NULL, f->alliance);
+ CuAssertTrue(tc, !f->_alive);
+ test_cleanup();
+}
+
CuSuite *get_alliance_suite(void)
{
CuSuite *suite = CuSuiteNew();
+ SUITE_ADD_TEST(suite, test_alliance_dead_faction);
SUITE_ADD_TEST(suite, test_alliance_make);
SUITE_ADD_TEST(suite, test_alliance_join);
return suite;
diff --git a/src/kernel/ally.c b/src/kernel/ally.c
index d53973328..3152ab4b7 100644
--- a/src/kernel/ally.c
+++ b/src/kernel/ally.c
@@ -121,6 +121,7 @@ attrib_type at_npcfaction = {
NULL,
a_writeint,
a_readint,
+ NULL,
ATF_UNIQUE
};
@@ -170,6 +171,9 @@ int
alliedgroup(const struct plane *pl, const struct faction *f,
const struct faction *f2, const struct ally *sf, int mode)
{
+ if (!(faction_alive(f) && faction_alive(f2))) {
+ return 0;
+ }
while (sf && sf->faction != f2)
sf = sf->next;
if (sf == NULL) {
@@ -177,10 +181,10 @@ alliedgroup(const struct plane *pl, const struct faction *f,
}
mode = ally_mode(sf, mode) | (mode & autoalliance(pl, f, f2));
if (AllianceRestricted()) {
- if (a_findc(f->attribs, &at_npcfaction)) {
+ if (a_find(f->attribs, &at_npcfaction)) {
return mode;
}
- if (a_findc(f2->attribs, &at_npcfaction)) {
+ if (a_find(f2->attribs, &at_npcfaction)) {
return mode;
}
if (f->alliance != f2->alliance) {
@@ -229,7 +233,7 @@ int alliedunit(const unit * u, const faction * f2, int mode)
sf = u->faction->allies;
if (fval(u, UFL_GROUP)) {
- const attrib *a = a_findc(u->attribs, &at_group);
+ const attrib *a = a_find(u->attribs, &at_group);
if (a != NULL)
sf = ((group *)a->data.v)->allies;
}
diff --git a/src/kernel/build.c b/src/kernel/build.c
index 98ca2b28e..3d4fb6a17 100644
--- a/src/kernel/build.c
+++ b/src/kernel/build.c
@@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "alchemy.h"
#include "direction.h"
#include "move.h"
+#include "study.h"
#include "laws.h"
#include "skill.h"
#include "lighthouse.h"
diff --git a/src/kernel/building.c b/src/kernel/building.c
index 4384b7f80..f0e46cc7e 100644
--- a/src/kernel/building.c
+++ b/src/kernel/building.c
@@ -141,7 +141,7 @@ int buildingcapacity(const building * b)
}
attrib_type at_building_generic_type = {
- "building_generic_type", NULL, NULL, NULL, a_writestring, a_readstring,
+ "building_generic_type", NULL, NULL, NULL, a_writestring, a_readstring, NULL,
ATF_UNIQUE
};
@@ -386,10 +386,10 @@ int resolve_building(variant id, void *address)
return result;
}
-variant read_building_reference(struct storage * store)
+variant read_building_reference(gamedata * data)
{
variant result;
- READ_INT(store, &result.i);
+ READ_INT(data->store, &result.i);
return result;
}
diff --git a/src/kernel/building.h b/src/kernel/building.h
index 4d08ae647..766242c4a 100644
--- a/src/kernel/building.h
+++ b/src/kernel/building.h
@@ -26,6 +26,8 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern "C" {
#endif
+ struct gamedata;
+
/* maintenance::flags */
#define MTF_NONE 0x00
#define MTF_VARIABLE 0x01 /* resource usage scales with size */
@@ -158,7 +160,7 @@ extern "C" {
extern int resolve_building(variant data, void *address);
extern void write_building_reference(const struct building *b,
struct storage *store);
- extern variant read_building_reference(struct storage *store);
+ extern variant read_building_reference(struct gamedata *data);
extern struct building *findbuilding(int n);
diff --git a/src/kernel/config.c b/src/kernel/config.c
index 56022ef74..64afd98d3 100644
--- a/src/kernel/config.c
+++ b/src/kernel/config.c
@@ -640,9 +640,48 @@ int check_param(const struct param *p, const char *key, const char *searchvalue)
return result;
}
-const char * relpath(char *buf, size_t sz, const char *path) {
- strlcpy(buf, basepath(), sz);
- strlcat(buf, path, sz);
+static const char *g_basedir;
+const char *basepath(void)
+{
+ if (g_basedir)
+ return g_basedir;
+ return ".";
+}
+
+void set_basepath(const char *path)
+{
+ g_basedir = path;
+}
+
+#ifdef WIN32
+#define PATH_DELIM '\\'
+#else
+#define PATH_DELIM '/'
+#endif
+
+char * join_path(const char *p1, const char *p2, char *dst, size_t len) {
+ size_t sz;
+ assert(p1 && p2);
+ assert(p2 != dst);
+ if (dst == p1) {
+ sz = strlen(p1);
+ }
+ else {
+ sz = strlcpy(dst, p1, len);
+ }
+ assert(sz < len);
+ dst[sz++] = PATH_DELIM;
+ strlcpy(dst + sz, p2, len - sz);
+ return dst;
+}
+
+static const char * relpath(char *buf, size_t sz, const char *path) {
+ if (g_basedir) {
+ join_path(g_basedir, path, buf, sz);
+ }
+ else {
+ strlcpy(buf, path, sz);
+ }
return buf;
}
@@ -652,7 +691,7 @@ const char *datapath(void)
static char zText[MAX_PATH]; // FIXME: static return value
if (g_datadir)
return g_datadir;
- return relpath(zText, sizeof(zText), "/data");
+ return relpath(zText, sizeof(zText), "data");
}
void set_datapath(const char *path)
@@ -666,7 +705,7 @@ const char *reportpath(void)
static char zText[MAX_PATH]; // FIXME: static return value
if (g_reportdir)
return g_reportdir;
- return relpath(zText, sizeof(zText), "/reports");
+ return relpath(zText, sizeof(zText), "reports");
}
void set_reportpath(const char *path)
@@ -674,17 +713,18 @@ void set_reportpath(const char *path)
g_reportdir = path;
}
-static const char *g_basedir;
-const char *basepath(void)
-{
- if (g_basedir)
- return g_basedir;
- return ".";
-}
-
-void set_basepath(const char *path)
-{
- g_basedir = path;
+int create_directories(void) {
+ int err;
+ err = _mkdir(datapath());
+ if (err) {
+ if (errno == EEXIST) errno = 0;
+ else return err;
+ }
+ err = _mkdir(reportpath());
+ if (err && errno == EEXIST) {
+ errno = 0;
+ }
+ return err;
}
double get_param_flt(const struct param *p, const char *key, double def)
@@ -1058,9 +1098,6 @@ void free_gamedata(void)
{
int i;
free_donations();
- free_units();
- free_regions();
- free_borders();
for (i = 0; i != MAXLOCALES; ++i) {
if (defaults[i]) {
@@ -1068,14 +1105,11 @@ void free_gamedata(void)
defaults[i] = 0;
}
}
+ free_factions();
+ free_units();
+ free_regions();
+ free_borders();
free_alliances();
- while (factions) {
- faction *f = factions;
- factions = f->next;
- funhash(f);
- free_faction(f);
- free(f);
- }
while (planes) {
plane *pl = planes;
diff --git a/src/kernel/config.h b/src/kernel/config.h
index 257de940d..dda7873aa 100644
--- a/src/kernel/config.h
+++ b/src/kernel/config.h
@@ -148,6 +148,8 @@ struct param;
const char *reportpath(void);
void set_reportpath(const char *);
+ int create_directories(void);
+
void kernel_init(void);
void kernel_done(void);
@@ -185,6 +187,7 @@ struct param;
double config_get_flt(const char *key, double def);
bool config_token(const char *key, const char *tok);
+ char * join_path(const char *p1, const char *p2, char *dst, size_t len);
bool ExpensiveMigrants(void);
int NMRTimeout(void);
int LongHunger(const struct unit *u);
diff --git a/src/kernel/connection.c b/src/kernel/connection.c
index dbf29bff9..41c67299a 100644
--- a/src/kernel/connection.c
+++ b/src/kernel/connection.c
@@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
+#include
#include
#include
#include
@@ -217,8 +218,9 @@ border_type *find_bordertype(const char *name)
return bt;
}
-void b_read(connection * b, storage * store)
+void b_read(connection * b, gamedata * data)
{
+ storage * store = data->store;
int n, result = 0;
switch (b->type->datatype) {
case VAR_NONE:
@@ -529,8 +531,9 @@ static const char *b_nameroad(const connection * b, const region * r,
return buffer;
}
-static void b_readroad(connection * b, storage * store)
+static void b_readroad(connection * b, gamedata * data)
{
+ storage * store = data->store;
int n;
READ_INT(store, &n);
b->data.sa[0] = (short)n;
@@ -601,8 +604,9 @@ void write_borders(struct storage *store)
WRITE_TOK(store, "end");
}
-int read_borders(struct storage *store)
+int read_borders(gamedata *data)
{
+ struct storage *store = data->store;
for (;;) {
int bid = 0;
char zText[32];
@@ -613,7 +617,7 @@ int read_borders(struct storage *store)
if (!strcmp(zText, "end"))
break;
READ_INT(store, &bid);
- if (global.data_version < UIDHASH_VERSION) {
+ if (data->version < UIDHASH_VERSION) {
int fx, fy, tx, ty;
READ_INT(store, &fx);
READ_INT(store, &fy);
@@ -655,11 +659,12 @@ int read_borders(struct storage *store)
nextborder--; /* new_border erhöht den Wert */
b->id = bid;
assert(bid <= nextborder);
- if (type->read)
- type->read(b, store);
- if (global.data_version < NOBORDERATTRIBS_VERSION) {
+ if (type->read) {
+ type->read(b, data);
+ }
+ if (data->version < NOBORDERATTRIBS_VERSION) {
attrib *a = NULL;
- int result = a_read(store, &a, b);
+ int result = read_attribs(data, &a, b);
if (border_convert_cb) {
border_convert_cb(b, a);
}
diff --git a/src/kernel/connection.h b/src/kernel/connection.h
index ed4e2a50e..2c0800849 100644
--- a/src/kernel/connection.h
+++ b/src/kernel/connection.h
@@ -30,6 +30,7 @@ extern "C" {
struct faction;
struct region;
struct storage;
+ struct gamedata;
struct unit;
extern int nextborder;
@@ -52,7 +53,7 @@ extern "C" {
/* constructor: initialize the connection. allocate extra memory if needed */
void(*destroy) (connection *);
/* destructor: remove all extra memory for destruction */
- void(*read) (connection *, struct storage *);
+ void(*read) (connection *, struct gamedata *);
void(*write) (const connection *, struct storage *);
bool(*block) (const connection *, const struct unit *,
const struct region * r);
@@ -113,12 +114,12 @@ extern "C" {
void register_bordertype(border_type * type);
/* register a new bordertype */
- int read_borders(struct storage *store);
+ int read_borders(struct gamedata *store);
void write_borders(struct storage *store);
void age_borders(void);
/* provide default implementations for some member functions: */
- void b_read(connection * b, struct storage *store);
+ void b_read(connection * b, struct gamedata *store);
void b_write(const connection * b, struct storage *store);
bool b_blockall(const connection *, const struct unit *,
const struct region *);
diff --git a/src/kernel/curse.c b/src/kernel/curse.c
index 3cb39ebab..d018ec117 100644
--- a/src/kernel/curse.c
+++ b/src/kernel/curse.c
@@ -35,6 +35,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* util includes */
#include
#include
+#include
#include
#include
#include
@@ -56,7 +57,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#define MAXENTITYHASH 7919
-curse *cursehash[MAXENTITYHASH];
+static curse *cursehash[MAXENTITYHASH];
void c_setflag(curse * c, unsigned int flags)
{
@@ -73,17 +74,19 @@ void c_clearflag(curse * c, unsigned int flags)
void chash(curse * c)
{
- curse *old = cursehash[c->no % MAXENTITYHASH];
+ int i = c->no % MAXENTITYHASH;
- cursehash[c->no % MAXENTITYHASH] = c;
- c->nexthash = old;
+ c->nexthash = cursehash[i];
+ cursehash[i] = c;
+ assert(c->nexthash != c);
}
static void cunhash(curse * c)
{
curse **show;
+ int i = c->no % MAXENTITYHASH;
- for (show = &cursehash[c->no % MAXENTITYHASH]; *show;
+ for (show = &cursehash[i]; *show;
show = &(*show)->nexthash) {
if ((*show)->no == c->no)
break;
@@ -184,8 +187,9 @@ static int read_ccompat(const char *cursename, struct storage *store)
return -1;
}
-int curse_read(attrib * a, void *owner, struct storage *store)
+int curse_read(attrib * a, void *owner, gamedata *data)
{
+ storage *store = data->store;
curse *c = (curse *)a->data.v;
int ur;
char cursename[64];
@@ -193,6 +197,7 @@ int curse_read(attrib * a, void *owner, struct storage *store)
int flags;
float flt;
+ assert(!c->no);
READ_INT(store, &c->no);
chash(c);
READ_TOK(store, cursename, sizeof(cursename));
@@ -200,13 +205,13 @@ int curse_read(attrib * a, void *owner, struct storage *store)
READ_INT(store, &c->duration);
READ_FLT(store, &flt);
c->vigour = flt;
- if (global.data_version < INTPAK_VERSION) {
- ur = read_reference(&c->magician, store, read_int, resolve_unit);
+ if (data->version < INTPAK_VERSION) {
+ ur = resolve_unit(read_int(data->store), &c->magician);
}
else {
- ur = read_reference(&c->magician, store, read_unit_reference, resolve_unit);
+ ur = read_reference(&c->magician, data, read_unit_reference, resolve_unit);
}
- if (global.data_version < CURSEFLOAT_VERSION) {
+ if (data->version < CURSEFLOAT_VERSION) {
READ_INT(store, &n);
c->effect = (float)n;
}
@@ -224,19 +229,20 @@ int curse_read(attrib * a, void *owner, struct storage *store)
return AT_READ_FAIL;
}
c->flags = flags;
- if (global.data_version < EXPLICIT_CURSE_ISNEW_VERSION) {
+ if (data->version < EXPLICIT_CURSE_ISNEW_VERSION) {
c_clearflag(c, CURSE_ISNEW);
}
- if (c->type->read)
- c->type->read(store, c, owner);
+ if (c->type->read) {
+ c->type->read(data, c, owner);
+ }
else if (c->type->typ == CURSETYP_UNIT) {
READ_INT(store, &c->data.i);
}
if (c->type->typ == CURSETYP_REGION) {
int rr =
- read_reference(&c->data.v, store, read_region_reference,
- RESOLVE_REGION(global.data_version));
+ read_reference(&c->data.v, data, read_region_reference,
+ RESOLVE_REGION(data->version));
if (ur == 0 && rr == 0 && !c->data.v) {
return AT_READ_FAIL;
}
@@ -253,12 +259,11 @@ void curse_write(const attrib * a, const void *owner, struct storage *store)
unit *mage = (c->magician && c->magician->number) ? c->magician : NULL;
/* copied from c_clearflag */
- if (global.data_version < EXPLICIT_CURSE_ISNEW_VERSION) {
- flags = (c->flags & ~CURSE_ISNEW) | (c->type->flags & CURSE_ISNEW);
- }
- else {
- flags = c->flags | c->type->flags;
- }
+#if RELEASE_VERSION < EXPLICIT_CURSE_ISNEW_VERSION
+ flags = (c->flags & ~CURSE_ISNEW) | (c->type->flags & CURSE_ISNEW);
+#else
+ flags = c->flags | c->type->flags;
+#endif
WRITE_INT(store, c->no);
WRITE_TOK(store, ct->cname);
@@ -285,6 +290,7 @@ attrib_type at_curse = {
curse_age,
curse_write,
curse_read,
+ NULL,
ATF_CURSE
};
@@ -821,9 +827,9 @@ double destr_curse(curse * c, int cast_level, double force)
return force;
}
-void free_curses(void) {
+void curses_done(void) {
int i;
for (i = 0; i != MAXCTHASH; ++i) {
ql_free(cursetypes[i]);
}
-}
\ No newline at end of file
+}
diff --git a/src/kernel/curse.h b/src/kernel/curse.h
index 263b66125..817b48ad3 100644
--- a/src/kernel/curse.h
+++ b/src/kernel/curse.h
@@ -28,6 +28,8 @@ extern "C" {
struct curse;
struct curse_type;
+ struct gamedata;
+ struct storage;
/* Sprueche in der struct region und auf Einheiten, Schiffen oder Burgen
* (struct attribute)
@@ -93,7 +95,7 @@ extern "C" {
*
* */
-#include
+ extern struct attrib_type at_curse;
/* ------------------------------------------------------------- */
/* Zauberwirkungen */
@@ -194,8 +196,8 @@ extern "C" {
struct message *(*curseinfo) (const void *, objtype_t,
const struct curse *, int);
void(*change_vigour) (struct curse *, double);
- int(*read) (struct storage * store, struct curse *, void *target);
- int(*write) (struct storage * store, const struct curse *,
+ int(*read) (struct gamedata *data, struct curse *, void *target);
+ int(*write) (struct storage *store, const struct curse *,
const void *target);
int(*cansee) (const struct faction *, const void *, objtype_t,
const struct curse *, int);
@@ -214,12 +216,11 @@ extern "C" {
int duration; /* Dauer der Verzauberung. Wird jede Runde vermindert */
} curse;
- void free_curses(void); /* de-register all curse-types */
+ void curses_done(void); /* de-register all curse-types */
- extern struct attrib_type at_curse;
void curse_write(const struct attrib *a, const void *owner,
- struct storage *store);
- int curse_read(struct attrib *a, void *owner, struct storage *store);
+ struct storage *store);
+ int curse_read(struct attrib *a, void *owner, struct gamedata *store);
/* ------------------------------------------------------------- */
/* Kommentare:
diff --git a/src/kernel/curse.test.c b/src/kernel/curse.test.c
index 002ef18ec..2be81b19c 100644
--- a/src/kernel/curse.test.c
+++ b/src/kernel/curse.test.c
@@ -2,9 +2,11 @@
#include
#include
+#include
#include
#include
#include
+#include
#include
#include
#include
@@ -52,6 +54,11 @@ static void setup_curse(curse_fixture *fix, const char *name) {
fix->c = create_curse(fix->u, &fix->r->attribs, ct_find(name), 1.0, 1, 1.0, 0);
}
+static void cleanup_curse(curse_fixture *fix) {
+ // destroy_curse(fix->c);
+ test_cleanup();
+}
+
static void test_magicstreet(CuTest *tc) {
curse_fixture fix;
message *msg;
@@ -60,7 +67,7 @@ static void test_magicstreet(CuTest *tc) {
msg = fix.c->type->curseinfo(fix.r, TYP_REGION, fix.c, 0);
CuAssertStrEquals(tc, "curseinfo::magicstreet", test_get_messagetype(msg));
msg_release(msg);
- test_cleanup();
+ cleanup_curse(&fix);
}
static void test_magicstreet_warning(CuTest *tc) {
@@ -71,7 +78,7 @@ static void test_magicstreet_warning(CuTest *tc) {
msg = fix.c->type->curseinfo(fix.r, TYP_REGION, fix.c, 0);
CuAssertStrEquals(tc, "curseinfo::magicstreetwarn", test_get_messagetype(msg));
msg_release(msg);
- test_cleanup();
+ cleanup_curse(&fix);
}
static void test_good_dreams(CuTest *tc) {
@@ -82,7 +89,7 @@ static void test_good_dreams(CuTest *tc) {
msg = fix.c->type->curseinfo(fix.r, TYP_REGION, fix.c, 0);
CuAssertStrEquals(tc, "curseinfo::gooddream", test_get_messagetype(msg));
msg_release(msg);
- test_cleanup();
+ cleanup_curse(&fix);
}
static void test_bad_dreams(CuTest *tc) {
@@ -93,7 +100,7 @@ static void test_bad_dreams(CuTest *tc) {
msg = fix.c->type->curseinfo(fix.r, TYP_REGION, fix.c, 0);
CuAssertStrEquals(tc, "curseinfo::baddream", test_get_messagetype(msg));
msg_release(msg);
- test_cleanup();
+ cleanup_curse(&fix);
}
static void test_memstream(CuTest *tc) {
@@ -122,30 +129,33 @@ static void test_memstream(CuTest *tc) {
static void test_write_flag(CuTest *tc) {
curse_fixture fix;
+ gamedata data;
storage store;
- char buf[1024];
- stream out = { 0 };
- size_t len;
+ region * r;
+ curse * c;
+ int uid;
- mstream_init(&out);
- binstore_init(&store, &out);
- store.handle.data = &out;
+ mstream_init(&data.strm);
+ gamedata_init(&data, &store, RELEASE_VERSION);
setup_curse(&fix, "gbdream");
- fix.c->flags = 42 | CURSE_ISNEW;
- curse_write(fix.r->attribs, fix.r, &store);
- out.api->rewind(out.handle);
- len = out.api->read(out.handle, buf, sizeof(buf));
- buf[len] = '\0';
- out.api->rewind(out.handle);
- curse_read(fix.r->attribs, fix.r, &store);
- CuAssertIntEquals(tc, 42 | CURSE_ISNEW, ((curse *) fix.r->attribs->data.v)->flags);
- global.data_version = RELEASE_VERSION;
- CuAssertIntEquals(tc, RELEASE_VERSION, global.data_version);
+ c = fix.c;
+ r = fix.r;
+ uid = r->uid;
+ c->flags = CURSE_ISNEW;
+ write_game(&data);
+ free_gamedata();
+ data.strm.api->rewind(data.strm.handle);
+ read_game(&data);
+ r = findregionbyid(uid);
+ CuAssertPtrNotNull(tc, r);
+ CuAssertPtrNotNull(tc, r->attribs);
+ c = (curse *)r->attribs->data.v;
+ CuAssertIntEquals(tc, CURSE_ISNEW, c->flags);
- mstream_done(&out);
- binstore_done(&store);
- test_cleanup();
+ mstream_done(&data.strm);
+ gamedata_done(&data);
+ cleanup_curse(&fix);
}
CuSuite *get_curse_suite(void)
diff --git a/src/kernel/faction.c b/src/kernel/faction.c
index 6bc28e3e6..cf8d73143 100755
--- a/src/kernel/faction.c
+++ b/src/kernel/faction.c
@@ -19,7 +19,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
#include "faction.h"
-
#include "alliance.h"
#include "ally.h"
#include "curse.h"
@@ -46,6 +45,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include
#include
#include
+#include
#include
#include
#include
@@ -71,9 +71,12 @@ faction *factions;
* but you should still call funhash and remove the faction from the
* global list.
*/
-void free_faction(faction * f)
+static void free_faction(faction * f)
{
funhash(f);
+ if (f->alliance && f->alliance->_leader == f) {
+ setalliance(f, 0);
+ }
if (f->msgs) {
free_messagelist(f->msgs->begin);
free(f->msgs);
@@ -101,7 +104,7 @@ void free_faction(faction * f)
free(f->email);
free(f->banner);
- free(f->passw);
+ free(f->_password);
free(f->name);
if (f->seen_factions) {
ql_free(f->seen_factions);
@@ -214,6 +217,7 @@ int resolve_faction(variant id, void *address)
result = -1;
}
}
+ assert(address);
*(faction **)address = f;
return result;
}
@@ -248,11 +252,13 @@ faction *addfaction(const char *email, const char *password,
log_warning("Invalid email address for faction %s: %s\n", itoa36(f->no), email);
}
- faction_setpassword(f, password);
+ if (!password) password = itoa36(rng_int());
+ faction_setpassword(f, password_encode(password, PASSWORD_DEFAULT));
+ ADDMSG(&f->msgs, msg_message("changepasswd", "value", password));
f->alliance_joindate = turn;
f->lastorders = turn;
- f->alive = 1;
+ f->_alive = true;
f->age = 0;
f->race = frace;
f->magiegebiet = 0;
@@ -310,30 +316,57 @@ unit *addplayer(region * r, faction * f)
bool checkpasswd(const faction * f, const char *passwd)
{
- return (passwd && unicode_utf8_strcasecmp(f->passw, passwd) == 0);
+ if (!passwd) return false;
+
+ if (f->_password && password_verify(f->_password, passwd) == VERIFY_FAIL) {
+ log_warning("password check failed: %s", factionname(f));
+ return false;
+ }
+ return true;
}
-variant read_faction_reference(struct storage * store)
+variant read_faction_reference(gamedata * data)
{
variant id;
- READ_INT(store, &id.i);
+ READ_INT(data->store, &id.i);
return id;
}
void write_faction_reference(const faction * f, struct storage *store)
{
+ assert(!f || f->_alive);
WRITE_INT(store, f ? f->no : 0);
}
-void destroyfaction(faction * f)
-{
- unit *u = f->units;
- faction *ff;
+static faction *dead_factions;
- if (!f->alive) {
- return;
+void free_flist(faction **fp) {
+ faction * flist = *fp;
+ while (flist) {
+ faction *f = flist;
+ flist = f->next;
+ free_faction(f);
+ free(f);
}
+ *fp = 0;
+}
+
+void free_factions(void) {
+ free_flist(&factions);
+ free_flist(&dead_factions);
+}
+
+void destroyfaction(faction ** fp)
+{
+ faction * f = *fp;
+ unit *u = f->units;
+
+ *fp = f->next;
+ f->next = dead_factions;
+ dead_factions = f;
+
fset(f, FFL_QUIT);
+ f->_alive = false;
if (f->spellbook) {
spellbook_clear(f->spellbook);
@@ -389,35 +422,48 @@ void destroyfaction(faction * f)
u = u->nextF;
}
}
- f->alive = 0;
- /* no way! f->units = NULL; */
+
handle_event(f->attribs, "destroy", f);
+#if 0
+ faction *ff;
for (ff = factions; ff; ff = ff->next) {
group *g;
- ally *sf, *sfn;
+ ally *sf, **sfp;
- /* Alle HELFE für die Partei löschen */
- for (sf = ff->allies; sf; sf = sf->next) {
- if (sf->faction == f) {
- removelist(&ff->allies, sf);
- break;
+ for (sfp = &ff->allies; *sfp;) {
+ sf = *sfp;
+ if (sf->faction == f || sf->faction == NULL) {
+ *sfp = sf->next;
+ free(sf);
}
+ else
+ sfp = &(*sfp)->next;
}
for (g = ff->groups; g; g = g->next) {
- for (sf = g->allies; sf;) {
- sfn = sf->next;
- if (sf->faction == f) {
- removelist(&g->allies, sf);
- break;
+ for (sfp = &g->allies; *sfp; ) {
+ sf = *sfp;
+ if (sf->faction == f || sf->faction == NULL) {
+ *sfp = sf->next;
+ free(sf);
+ }
+ else {
+ sfp = &(*sfp)->next;
}
- sf = sfn;
}
}
}
+#endif
+
+ if (f->alliance && f->alliance->_leader == f) {
+ setalliance(f, 0);
+ }
+
+ funhash(f);
/* units of other factions that were disguised as this faction
* have their disguise replaced by ordinary faction hiding. */
if (rule_stealth_other()) {
+ // TODO: f.alive should be tested for in get_otherfaction
region *rc;
for (rc = regions; rc; rc = rc->next) {
for (u = rc->units; u; u = u->next) {
@@ -520,13 +566,12 @@ void faction_setbanner(faction * self, const char *banner)
self->banner = _strdup(banner);
}
-void faction_setpassword(faction * f, const char *passw)
+void faction_setpassword(faction * f, const char *pwhash)
{
- free(f->passw);
- if (passw)
- f->passw = _strdup(passw);
- else
- f->passw = _strdup(itoa36(rng_int()));
+ assert(pwhash);
+ // && pwhash[0] == '$');
+ free(f->_password);
+ f->_password = _strdup(pwhash);
}
bool valid_race(const struct faction *f, const struct race *rc)
@@ -541,11 +586,6 @@ bool valid_race(const struct faction *f, const struct race *rc)
}
}
-const char *faction_getpassword(const faction * f)
-{
- return f->passw;
-}
-
struct alliance *f_get_alliance(const struct faction *f)
{
if (f->alliance && !(f->alliance->flags & ALF_NON_ALLIED)) {
@@ -633,61 +673,26 @@ int skill_limit(faction * f, skill_t sk)
void remove_empty_factions(void)
{
- faction **fp, *f3;
+ faction **fp;
for (fp = &factions; *fp;) {
faction *f = *fp;
- /* monster (0) werden nicht entfernt. alive kann beim readgame
- * () auf 0 gesetzt werden, wenn monsters keine einheiten mehr
- * haben. */
- if ((f->units == NULL || f->alive == 0) && !fval(f, FFL_NOIDLEOUT)) {
- ursprung *ur = f->ursprung;
- while (ur && ur->id != 0)
- ur = ur->next;
+
+ if (!(f->_alive && f->units!=NULL) && !fval(f, FFL_NOIDLEOUT)) {
log_debug("dead: %s", factionname(f));
-
- /* Einfach in eine Datei schreiben und später vermailen */
-
- for (f3 = factions; f3; f3 = f3->next) {
- ally *sf;
- group *g;
- ally **sfp = &f3->allies;
- while (*sfp) {
- sf = *sfp;
- if (sf->faction == f || sf->faction == NULL) {
- *sfp = sf->next;
- free(sf);
- }
- else
- sfp = &(*sfp)->next;
- }
- for (g = f3->groups; g; g = g->next) {
- sfp = &g->allies;
- while (*sfp) {
- sf = *sfp;
- if (sf->faction == f || sf->faction == NULL) {
- *sfp = sf->next;
- free(sf);
- }
- else
- sfp = &(*sfp)->next;
- }
- }
- }
-
- *fp = f->next;
- funhash(f);
- free_faction(f);
- if (f->alliance && f->alliance->_leader == f) {
- setalliance(f, 0);
- }
- free(f);
+ destroyfaction(fp);
}
- else
+ else {
fp = &(*fp)->next;
+ }
}
}
+bool faction_alive(const faction *f) {
+ assert(f);
+ return f->_alive || (f->flags&FFL_NPC);
+}
+
void faction_getorigin(const faction * f, int id, int *x, int *y)
{
ursprung *ur;
@@ -794,6 +799,7 @@ attrib_type at_maxmagicians = {
NULL,
a_writeint,
a_readint,
+ NULL,
ATF_UNIQUE
};
@@ -843,3 +849,28 @@ faction *dfindhash(int no)
}
return 0;
}
+
+int writepasswd(void)
+{
+ FILE *F;
+ char zText[128];
+
+ join_path(basepath(), "passwd", zText, sizeof(zText));
+ F = fopen(zText, "w");
+ if (!F) {
+ perror(zText);
+ }
+ else {
+ faction *f;
+ log_info("writing passwords...");
+
+ for (f = factions; f; f = f->next) {
+ fprintf(F, "%s:%s:%s:%u\n",
+ factionid(f), f->email, f->_password, f->subscription);
+ }
+ fclose(F);
+ return 0;
+ }
+ return 1;
+}
+
diff --git a/src/kernel/faction.h b/src/kernel/faction.h
index d22763d96..c7578cc47 100644
--- a/src/kernel/faction.h
+++ b/src/kernel/faction.h
@@ -30,7 +30,8 @@ extern "C" {
struct item;
struct seen_region;
struct attrib_type;
-
+ struct gamedata;
+
extern struct attrib_type at_maxmagicians;
/* SMART_INTERVALS: define to speed up finding the interval of regions that a
faction is in. defining this speeds up the turn by 30-40% */
@@ -68,7 +69,7 @@ extern "C" {
char *name;
char *banner;
char *email;
- char *passw;
+ char *_password;
int max_spelllevel;
struct spellbook *spellbook;
const struct locale *locale;
@@ -82,8 +83,8 @@ extern "C" {
int num_total; /* Anzahl Personen mit Monstern */
int options;
int no_units;
- struct ally *allies;
- struct group *groups;
+ struct ally *allies; /* alliedgroup and others should check sf.faction.alive before using a faction from f.allies */
+ struct group *groups; /* alliedgroup and others should check sf.faction.alive before using a faction from f.groups */
int nregions;
int money;
score_t score;
@@ -103,7 +104,7 @@ extern "C" {
struct item *items; /* items this faction can claim */
struct seen_region **seen;
struct quicklist *seen_factions;
- bool alive; /* enno: sollte ein flag werden */
+ bool _alive; /* enno: sollte ein flag werden */
} faction;
extern struct faction *factions;
@@ -121,7 +122,10 @@ extern "C" {
struct faction *addfaction(const char *email, const char *password,
const struct race *frace, const struct locale *loc, int subscription);
bool checkpasswd(const faction * f, const char *passwd);
- void destroyfaction(faction * f);
+ int writepasswd(void);
+ void destroyfaction(faction ** f);
+
+ bool faction_alive(const struct faction *f);
void set_alliance(struct faction *a, struct faction *b, int status);
int get_alliance(const struct faction *a, const struct faction *b);
@@ -130,11 +134,11 @@ extern "C" {
void write_faction_reference(const struct faction *f,
struct storage *store);
- variant read_faction_reference(struct storage *store);
+ variant read_faction_reference(struct gamedata *store);
int resolve_faction(variant data, void *addr);
void renumber_faction(faction * f, int no);
- void free_faction(struct faction *f);
+ void free_factions(void);
void remove_empty_factions(void);
#ifdef SMART_INTERVALS
@@ -150,8 +154,7 @@ extern "C" {
const char *faction_getemail(const struct faction *self);
void faction_setemail(struct faction *self, const char *email);
- const char *faction_getpassword(const struct faction *self);
- void faction_setpassword(struct faction *self, const char *password);
+ void faction_setpassword(struct faction *self, const char *pwhash);
bool valid_race(const struct faction *f, const struct race *rc);
void faction_getorigin(const struct faction * f, int id, int *x, int *y);
diff --git a/src/kernel/faction.test.c b/src/kernel/faction.test.c
index d1a5c53be..19b4da883 100644
--- a/src/kernel/faction.test.c
+++ b/src/kernel/faction.test.c
@@ -8,6 +8,7 @@
#include
#include
#include
+#include
#include "monster.h"
#include
@@ -16,18 +17,23 @@
#include
#include
-static void test_remove_empty_factions_allies(CuTest *tc) {
+static void test_destroyfaction_allies(CuTest *tc) {
faction *f1, *f2;
region *r;
+ ally *al;
test_cleanup();
r = test_create_region(0, 0, 0);
f1 = test_create_faction(0);
test_create_unit(f1, r);
f2 = test_create_faction(0);
- ally_add(&f1->allies, f2);
- remove_empty_factions();
- CuAssertPtrEquals(tc, 0, f1->allies);
+ al = ally_add(&f1->allies, f2);
+ al->status = HELP_FIGHT;
+ CuAssertIntEquals(tc, HELP_FIGHT, alliedgroup(0, f1, f2, f1->allies, HELP_ALL));
+ CuAssertPtrEquals(tc, f2, f1->next);
+ destroyfaction(&f1->next);
+ CuAssertIntEquals(tc, false, faction_alive(f2));
+ CuAssertIntEquals(tc, 0, alliedgroup(0, f1, f2, f1->allies, HELP_ALL));
test_cleanup();
}
@@ -75,8 +81,8 @@ static void test_remove_dead_factions(CuTest *tc) {
remove_empty_factions();
CuAssertPtrEquals(tc, f, findfaction(f->no));
CuAssertPtrNotNull(tc, get_monsters());
- fm->alive = 0;
- f->alive = 0;
+ fm->units = 0;
+ f->_alive = false;
fno = f->no;
remove_empty_factions();
CuAssertPtrEquals(tc, 0, findfaction(fno));
@@ -102,18 +108,28 @@ static void test_addfaction(CuTest *tc) {
CuAssertPtrEquals(tc, NULL, (void *)f->ursprung);
CuAssertPtrEquals(tc, (void *)factions, (void *)f);
CuAssertStrEquals(tc, "test@eressea.de", f->email);
- CuAssertStrEquals(tc, "hurrdurr", f->passw);
+ CuAssertTrue(tc, checkpasswd(f, "hurrdurr"));
CuAssertPtrEquals(tc, (void *)lang, (void *)f->locale);
CuAssertIntEquals(tc, 1234, f->subscription);
CuAssertIntEquals(tc, 0, f->flags);
CuAssertIntEquals(tc, 0, f->age);
- CuAssertIntEquals(tc, 1, f->alive);
+ CuAssertTrue(tc, faction_alive(f));
CuAssertIntEquals(tc, M_GRAY, f->magiegebiet);
CuAssertIntEquals(tc, turn, f->lastorders);
CuAssertPtrEquals(tc, f, findfaction(f->no));
test_cleanup();
}
+static void test_check_passwd(CuTest *tc) {
+ faction *f;
+
+ f = test_create_faction(0);
+ faction_setpassword(f, password_encode("password", PASSWORD_DEFAULT));
+ CuAssertTrue(tc, checkpasswd(f, "password"));
+ CuAssertTrue(tc, !checkpasswd(f, "assword"));
+ CuAssertTrue(tc, !checkpasswd(f, "PASSWORD"));
+}
+
static void test_get_monsters(CuTest *tc) {
faction *f;
@@ -174,11 +190,12 @@ CuSuite *get_faction_suite(void)
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_addfaction);
SUITE_ADD_TEST(suite, test_remove_empty_factions);
- SUITE_ADD_TEST(suite, test_remove_empty_factions_allies);
+ SUITE_ADD_TEST(suite, test_destroyfaction_allies);
SUITE_ADD_TEST(suite, test_remove_empty_factions_alliance);
SUITE_ADD_TEST(suite, test_remove_dead_factions);
SUITE_ADD_TEST(suite, test_get_monsters);
SUITE_ADD_TEST(suite, test_set_origin);
SUITE_ADD_TEST(suite, test_set_origin_bug);
+ SUITE_ADD_TEST(suite, test_check_passwd);
return suite;
}
diff --git a/src/kernel/group.c b/src/kernel/group.c
index 35f7e93a7..19f816b13 100755
--- a/src/kernel/group.c
+++ b/src/kernel/group.c
@@ -33,6 +33,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* util includes */
#include
#include
+#include
#include
#include
@@ -95,8 +96,9 @@ static group *find_group(int gid)
return g;
}
-static int read_group(attrib * a, void *owner, struct storage *store)
+static int read_group(attrib * a, void *owner, gamedata *data)
{
+ struct storage *store = data->store;
group *g;
int gid;
@@ -119,7 +121,7 @@ write_group(const attrib * a, const void *owner, struct storage *store)
attrib_type at_group = { /* attribute for units assigned to a group */
"grp",
DEFAULT_INIT,
- DEFAULT_FINALIZE, DEFAULT_AGE, write_group, read_group, ATF_UNIQUE };
+ DEFAULT_FINALIZE, DEFAULT_AGE, write_group, read_group, NULL, ATF_UNIQUE };
void free_group(group * g)
{
@@ -130,6 +132,9 @@ void free_group(group * g)
assert(*g_ptr == g);
*g_ptr = g->nexthash;
+ if (g->attribs) {
+ a_removeall(&g->attribs, NULL);
+ }
while (g->allies) {
ally *a = g->allies;
g->allies = a->next;
@@ -179,7 +184,7 @@ void set_group(struct unit *u, struct group *g)
}
}
-bool join_group(unit * u, const char *name)
+group *join_group(unit * u, const char *name)
{
group *g = NULL;
@@ -192,7 +197,7 @@ bool join_group(unit * u, const char *name)
}
set_group(u, g);
- return true;
+ return g;
}
void write_groups(struct storage *store, const faction * f)
@@ -203,20 +208,21 @@ void write_groups(struct storage *store, const faction * f)
WRITE_INT(store, g->gid);
WRITE_STR(store, g->name);
for (a = g->allies; a; a = a->next) {
- if (a->faction) {
+ if (a->faction && a->faction->_alive) {
write_faction_reference(a->faction, store);
WRITE_INT(store, a->status);
}
}
- WRITE_INT(store, 0);
+ write_faction_reference(NULL, store);
a_write(store, g->attribs, g);
WRITE_SECTION(store);
}
WRITE_INT(store, 0);
}
-void read_groups(struct storage *store, faction * f)
+void read_groups(gamedata *data, faction * f)
{
+ struct storage *store = data->store;
for (;;) {
ally **pa;
group *g;
@@ -233,7 +239,7 @@ void read_groups(struct storage *store, faction * f)
ally *a;
variant fid;
- READ_INT(store, &fid.i);
+ fid = read_faction_reference(data);
if (fid.i <= 0)
break;
a = ally_add(pa, findfaction(fid.i));
@@ -241,6 +247,6 @@ void read_groups(struct storage *store, faction * f)
if (!a->faction)
ur_add(fid, &a->faction, resolve_faction);
}
- a_read(store, &g->attribs, g);
+ read_attribs(data, &g->attribs, g);
}
}
diff --git a/src/kernel/group.h b/src/kernel/group.h
index b32bed4c1..b257f515a 100755
--- a/src/kernel/group.h
+++ b/src/kernel/group.h
@@ -36,14 +36,14 @@ extern "C" {
} group;
extern struct attrib_type at_group; /* attribute for units assigned to a group */
- extern bool join_group(struct unit *u, const char *name);
+ extern struct group *join_group(struct unit *u, const char *name);
extern void set_group(struct unit *u, struct group *g);
extern struct group * get_group(const struct unit *u);
extern void free_group(struct group *g);
struct group *new_group(struct faction * f, const char *name, int gid);
extern void write_groups(struct storage *data, const struct faction *f);
- extern void read_groups(struct storage *data, struct faction *f);
+ extern void read_groups(struct gamedata *data, struct faction *f);
#ifdef __cplusplus
}
diff --git a/src/kernel/group.test.c b/src/kernel/group.test.c
index 46d794d2d..7fa7fa234 100644
--- a/src/kernel/group.test.c
+++ b/src/kernel/group.test.c
@@ -1,56 +1,118 @@
#include
+#include "config.h"
#include "types.h"
#include "ally.h"
#include "group.h"
#include "faction.h"
#include "unit.h"
#include "region.h"
+#include "save.h"
+#include "version.h"
+
+#include
+#include
+#include
+
#include
#include
#include
+#include
#include
#include
#include
#include
+static void test_group_readwrite_dead_faction(CuTest *tc) {
+ gamedata data;
+ storage store;
+ faction *f, *f2;
+ unit * u;
+ group *g;
+ ally *al;
+ int fno;
+
+ test_cleanup();
+ f = test_create_faction(0);
+ fno = f->no;
+ CuAssertPtrEquals(tc, f, factions);
+ CuAssertPtrEquals(tc, 0, f->next);
+ f2 = test_create_faction(0);
+ CuAssertPtrEquals(tc, f2, factions->next);
+ u = test_create_unit(f2, test_create_region(0, 0, 0));
+ CuAssertPtrNotNull(tc, u);
+ g = join_group(u, "group");
+ CuAssertPtrNotNull(tc, g);
+ al = ally_add(&g->allies, f);
+ CuAssertPtrNotNull(tc, al);
+
+ CuAssertPtrEquals(tc, f, factions);
+ destroyfaction(&factions);
+ CuAssertTrue(tc, !f->_alive);
+ CuAssertPtrEquals(tc, f2, factions);
+ mstream_init(&data.strm);
+ gamedata_init(&data, &store, RELEASE_VERSION);
+ write_game(&data);
+ free_gamedata();
+ f = f2 = NULL;
+ data.strm.api->rewind(data.strm.handle);
+ read_game(&data);
+ mstream_done(&data.strm);
+ gamedata_done(&data);
+ CuAssertPtrEquals(tc, 0, findfaction(fno));
+ f2 = factions;
+ CuAssertPtrNotNull(tc, f2);
+ u = f2->units;
+ CuAssertPtrNotNull(tc, u);
+ g = get_group(u);
+ CuAssertPtrNotNull(tc, g);
+ CuAssertPtrEquals(tc, 0, g->allies);
+ test_cleanup();
+}
+
static void test_group_readwrite(CuTest * tc)
{
faction * f;
group *g;
ally *al;
+ int i;
+ gamedata data;
storage store;
- FILE *F;
- stream strm;
- F = fopen("test.dat", "wb");
- fstream_init(&strm, F);
- binstore_init(&store, &strm);
test_cleanup();
- test_create_world();
+ mstream_init(&data.strm);
+ gamedata_init(&data, &store, RELEASE_VERSION);
f = test_create_faction(0);
- g = new_group(f, "test", 42);
+ g = new_group(f, "NW", 42);
+ g = new_group(f, "Egoisten", 43);
+ key_set(&g->attribs, 44);
al = ally_add(&g->allies, f);
al->status = HELP_GIVE;
write_groups(&store, f);
- binstore_done(&store);
- fstream_done(&strm);
+ WRITE_INT(&store, 47);
- F = fopen("test.dat", "rb");
- fstream_init(&strm, F);
- binstore_init(&store, &strm);
- f->groups = 0;
free_group(g);
- read_groups(&store, f);
- binstore_done(&store);
- fstream_done(&strm);
+ f->groups = 0;
+ data.strm.api->rewind(data.strm.handle);
+ read_groups(&data, f);
+ READ_INT(&store, &i);
+ mstream_done(&data.strm);
+ gamedata_done(&data);
+ CuAssertIntEquals(tc, 47, i);
CuAssertPtrNotNull(tc, f->groups);
- CuAssertPtrNotNull(tc, f->groups->allies);
- CuAssertPtrEquals(tc, 0, f->groups->allies->next);
- CuAssertPtrEquals(tc, f, f->groups->allies->faction);
- CuAssertIntEquals(tc, HELP_GIVE, f->groups->allies->status);
- remove("test.dat");
+ CuAssertIntEquals(tc, 42, f->groups->gid);
+ CuAssertStrEquals(tc, "NW", f->groups->name);
+ CuAssertPtrNotNull(tc, f->groups->next);
+ CuAssertIntEquals(tc, 43, f->groups->next->gid);
+ CuAssertStrEquals(tc, "Egoisten", f->groups->next->name);
+ CuAssertPtrEquals(tc, 0, f->groups->allies);
+ g = f->groups->next;
+ CuAssertTrue(tc, key_get(g->attribs, 44));
+ CuAssertPtrNotNull(tc, g->allies);
+ CuAssertPtrEquals(tc, 0, g->allies->next);
+ CuAssertPtrEquals(tc, f, g->allies->faction);
+ CuAssertIntEquals(tc, HELP_GIVE, g->allies->status);
test_cleanup();
}
@@ -62,14 +124,13 @@ static void test_group(CuTest * tc)
group *g;
test_cleanup();
- test_create_world();
- r = findregion(0, 0);
+ r = test_create_region(0, 0, 0);
f = test_create_faction(0);
assert(r && f);
u = test_create_unit(f, r);
assert(u);
- CuAssertTrue(tc, join_group(u, "hodor"));
- CuAssertPtrNotNull(tc, (g = get_group(u)));
+ CuAssertPtrNotNull(tc, (g = join_group(u, "hodor")));
+ CuAssertPtrEquals(tc, g, get_group(u));
CuAssertStrEquals(tc, "hodor", g->name);
CuAssertIntEquals(tc, 1, g->members);
set_group(u, 0);
@@ -86,6 +147,7 @@ CuSuite *get_group_suite(void)
CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_group);
SUITE_ADD_TEST(suite, test_group_readwrite);
+ SUITE_ADD_TEST(suite, test_group_readwrite_dead_faction);
return suite;
}
diff --git a/src/kernel/item.c b/src/kernel/item.c
index e484e6c35..1265a101d 100644
--- a/src/kernel/item.c
+++ b/src/kernel/item.c
@@ -568,11 +568,15 @@ struct order *ord)
{
if (d == NULL) {
int use = use_pooled(s, item2resource(itype), GET_SLACK, n);
- if (use < n)
+ region *r = s->region;
+ if (use < n) {
use +=
use_pooled(s, item2resource(itype), GET_RESERVE | GET_POOLED_SLACK,
n - use);
- rsethorses(s->region, rhorses(s->region) + use);
+ }
+ if (r->land) {
+ rsethorses(r, rhorses(r) + use);
+ }
return 0;
}
return -1; /* use the mechanism */
@@ -584,12 +588,14 @@ struct order *ord)
{
if (d == NULL) {
int use = use_pooled(s, item2resource(itype), GET_SLACK, n);
- if (use < n)
+ region *r = s->region;
+ if (use < n) {
use +=
use_pooled(s, item2resource(itype), GET_RESERVE | GET_POOLED_SLACK,
n - use);
- if (s->region->land) {
- rsetmoney(s->region, rmoney(s->region) + use);
+ }
+ if (r->land) {
+ rsetmoney(r, rmoney(r) + use);
}
return 0;
}
@@ -920,14 +926,14 @@ struct order *ord)
"unit item region command", user, itype->rtype, user->region, ord));
return -1;
}
- if (!is_mage(user) || find_key(f->attribs, atoi36("mbst")) != NULL) {
+ if (!is_mage(user) || key_get(f->attribs, atoi36("mbst"))) {
cmistake(user, user->thisorder, 214, MSG_EVENT);
return -1;
}
use_pooled(user, itype->rtype, GET_SLACK | GET_RESERVE | GET_POOLED_SLACK,
user->number);
- a_add(&f->attribs, make_key(atoi36("mbst")));
+ key_set(&f->attribs, atoi36("mbst"));
set_level(user, SK_MAGIC, 3);
ADDMSG(&user->faction->msgs, msg_message("use_item",
@@ -1197,13 +1203,16 @@ static void free_wtype(weapon_type *wtype) {
int free_rtype_cb(const void * match, const void * key, size_t keylen, void *cbdata) {
resource_type *rtype;
cb_get_kv(match, &rtype, sizeof(rtype));
- free(rtype->_name);
- if (rtype->itype) {
- free_itype(rtype->itype);
- }
if (rtype->wtype) {
free_wtype(rtype->wtype);
}
+ if (rtype->atype) {
+ free(rtype->atype);
+ }
+ if (rtype->itype) {
+ free_itype(rtype->itype);
+ }
+ free(rtype->_name);
free(rtype);
return 0;
}
diff --git a/src/kernel/jsonconf.c b/src/kernel/jsonconf.c
index fc2d9dd20..aac4bce10 100644
--- a/src/kernel/jsonconf.c
+++ b/src/kernel/jsonconf.c
@@ -114,7 +114,7 @@ static void json_maintenance_i(cJSON *json, maintenance *mt) {
}
break;
default:
- log_error("maintenance contains unknown attribute %s", child->string);
+ log_error("maintenance contains unknown attribute %s of type %d", child->string, child->type);
}
}
}
@@ -139,7 +139,9 @@ static void json_maintenance(cJSON *json, maintenance **mtp) {
}
}
}
- json_maintenance_i(json, mt);
+ else {
+ json_maintenance_i(json, mt);
+ }
}
static void json_construction(cJSON *json, construction **consp) {
@@ -826,11 +828,11 @@ static void json_include(cJSON *json) {
FILE *F;
if (json_relpath) {
char name[MAX_PATH];
- _snprintf(name, sizeof(name), "%s/%s", json_relpath, child->valuestring);
- F = fopen(name, "rt");
+ join_path(json_relpath, child->valuestring, name, sizeof(name));
+ F = fopen(name, "r");
}
else {
- F = fopen(child->valuestring, "rt");
+ F = fopen(child->valuestring, "r");
}
if (F) {
long pos;
diff --git a/src/kernel/jsonconf.test.c b/src/kernel/jsonconf.test.c
index 5e7d3047e..63c5fe0ff 100644
--- a/src/kernel/jsonconf.test.c
+++ b/src/kernel/jsonconf.test.c
@@ -438,7 +438,7 @@ static void test_configs(CuTest * tc)
test_cleanup();
- F = fopen("test.json", "wt");
+ F = fopen("test.json", "w");
fwrite(building_data, 1, strlen(building_data), F);
fclose(F);
CuAssertPtrNotNull(tc, json);
diff --git a/src/kernel/messages.c b/src/kernel/messages.c
index 779354498..330c2feeb 100644
--- a/src/kernel/messages.c
+++ b/src/kernel/messages.c
@@ -90,7 +90,7 @@ struct message *msg_feedback(const struct unit *u, struct order *ord,
ord = u->thisorder;
if (!mtype) {
- log_error("trying to create message of unknown type \"%s\"\n", name);
+ log_warning("trying to create message of unknown type \"%s\"\n", name);
if (!mt_find("missing_feedback")) {
mt_register(mt_new_va("missing_feedback", "unit:unit", "region:region", "command:order", "name:string", 0));
}
diff --git a/src/kernel/plane.c b/src/kernel/plane.c
index 32c95f6a8..2542a053d 100644
--- a/src/kernel/plane.c
+++ b/src/kernel/plane.c
@@ -290,14 +290,3 @@ int read_plane_reference(plane ** pp, struct storage *store)
ur_add(id, pp, resolve_plane);
return AT_READ_OK;
}
-
-bool is_watcher(const struct plane * p, const struct faction * f)
-{
- struct watcher *w;
- if (!p)
- return false;
- w = p->watchers;
- while (w && w->faction != f)
- w = w->next;
- return (w != NULL);
-}
diff --git a/src/kernel/plane.h b/src/kernel/plane.h
index 2426c6a1a..926ffb4e2 100644
--- a/src/kernel/plane.h
+++ b/src/kernel/plane.h
@@ -22,7 +22,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
extern "C" {
#endif
+ struct faction;
struct region;
+ struct faction;
struct plane;
struct storage;
@@ -43,15 +45,8 @@ extern "C" {
#define PFL_NOMONSTERS 16384 /* no monster randenc */
#define PFL_SEESPECIAL 32768 /* far seeing */
- typedef struct watcher {
- struct watcher *next;
- struct faction *faction;
- unsigned char mode;
- } watcher;
-
typedef struct plane {
struct plane *next;
- struct watcher *watchers;
int id;
char *name;
int minx, maxx, miny, maxy;
@@ -76,7 +71,6 @@ extern "C" {
struct plane *get_homeplane(void);
extern int rel_to_abs(const struct plane *pl, const struct faction *f,
int rel, unsigned char index);
- extern bool is_watcher(const struct plane *p, const struct faction *f);
extern void write_plane_reference(const plane * p, struct storage *store);
extern int read_plane_reference(plane ** pp, struct storage *store);
extern int plane_width(const plane * pl);
diff --git a/src/kernel/race.c b/src/kernel/race.c
index f372bb8ae..be9f2ab68 100644
--- a/src/kernel/race.c
+++ b/src/kernel/race.c
@@ -141,6 +141,7 @@ void racelist_insert(struct race_list **rl, const struct race *r)
void free_races(void) {
while (races) {
race * rc = races->next;
+ free_params(&races->parameters);
free(races->_name);
free(races->def_damage);
free(races);
@@ -242,10 +243,10 @@ const char *rc_name_s(const race * rc, name_t n)
const char *raceprefix(const unit * u)
{
- const attrib *asource = u->faction->attribs;
+ attrib *asource = u->faction->attribs;
if (fval(u, UFL_GROUP)) {
- const attrib *agroup = a_findc(u->attribs, &at_group);
+ attrib *agroup = a_find(u->attribs, &at_group);
if (agroup != NULL)
asource = ((const group *)(agroup->data.v))->attribs;
}
diff --git a/src/kernel/region.c b/src/kernel/region.c
index ed56851ee..c3d0559bf 100644
--- a/src/kernel/region.c
+++ b/src/kernel/region.c
@@ -42,6 +42,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/* util includes */
#include
#include
+#include
#include
#include
#include
@@ -176,12 +177,12 @@ void a_initmoveblock(attrib * a)
a->data.v = calloc(1, sizeof(moveblock));
}
-int a_readmoveblock(attrib * a, void *owner, struct storage *store)
+int a_readmoveblock(attrib * a, void *owner, gamedata *data)
{
moveblock *m = (moveblock *)(a->data.v);
int i;
- READ_INT(store, &i);
+ READ_INT(data->store, &i);
m->dir = (direction_t)i;
return AT_READ_OK;
}
@@ -483,6 +484,7 @@ attrib_type at_horseluck = {
DEFAULT_AGE,
NO_WRITE,
NO_READ,
+ NULL,
ATF_UNIQUE
};
@@ -496,6 +498,7 @@ attrib_type at_peasantluck = {
DEFAULT_AGE,
NO_WRITE,
NO_READ,
+ NULL,
ATF_UNIQUE
};
@@ -509,6 +512,7 @@ attrib_type at_deathcount = {
DEFAULT_AGE,
a_writeint,
a_readint,
+ NULL,
ATF_UNIQUE
};
@@ -522,6 +526,7 @@ attrib_type at_woodcount = {
DEFAULT_AGE,
NO_WRITE,
a_readint,
+ NULL,
ATF_UNIQUE
};
@@ -595,13 +600,19 @@ bool is_coastregion(region * r)
int rpeasants(const region * r)
{
- return r->land ? r->land->peasants : 0;
+ int value = 0;
+ if (r->land) {
+ value = r->land->peasants;
+ assert(value >= 0);
+ }
+ return value;
}
void rsetpeasants(region * r, int value)
{
+ assert(r->land || value==0);
+ assert(value >= 0);
if (r->land) {
- assert(value >= 0);
r->land->peasants = value;
}
}
@@ -613,8 +624,9 @@ int rmoney(const region * r)
void rsethorses(const region * r, int value)
{
+ assert(r->land || value==0);
+ assert(value >= 0);
if (r->land) {
- assert(value >= 0);
r->land->horses = value;
}
}
@@ -626,8 +638,9 @@ int rhorses(const region * r)
void rsetmoney(region * r, int value)
{
+ assert(r->land || value==0);
+ assert(value >= 0);
if (r->land) {
- assert(value >= 0);
r->land->money = value;
}
}
@@ -639,8 +652,9 @@ int rherbs(const struct region *r)
void rsetherbs(const struct region *r, int value)
{
+ assert(r->land || value==0);
+ assert(value >= 0);
if (r->land) {
- assert(value >= 0);
r->land->herbs = (short)(value);
}
}
@@ -1212,10 +1226,11 @@ int resolve_region_id(variant id, void *address)
return 0;
}
-variant read_region_reference(struct storage * store)
+variant read_region_reference(gamedata *data)
{
+ struct storage * store = data->store;
variant result;
- if (global.data_version < UIDHASH_VERSION) {
+ if (data->version < UIDHASH_VERSION) {
int n;
READ_INT(store, &n);
result.sa[0] = (short)n;
diff --git a/src/kernel/region.h b/src/kernel/region.h
index 5974fd96c..411fa5580 100644
--- a/src/kernel/region.h
+++ b/src/kernel/region.h
@@ -18,9 +18,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#ifndef H_KRNL_REGION
#define H_KRNL_REGION
-#ifdef __cplusplus
-extern "C" {
-#endif
#include
#include "types.h"
@@ -66,6 +63,8 @@ extern "C" {
struct message_list;
struct rawmaterial;
struct item;
+ struct faction;
+ struct gamedata;
#define MORALE_TAX_FACTOR 0.005 /* 0.5% tax per point of morale */
#define MORALE_MAX 10 /* Maximum morale allowed */
@@ -75,6 +74,11 @@ extern "C" {
#define MORALE_AVERAGE 6 /* default average time for morale to change */
#define MORALE_TRANSFER 2 /* points of morale lost when GIVE COMMAND */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
typedef struct region_owner {
struct faction *owner;
struct faction *last_owner;
@@ -254,7 +258,7 @@ extern "C" {
void region_set_morale(region * r, int morale, int turn);
void write_region_reference(const struct region *r, struct storage *store);
- variant read_region_reference(struct storage *store);
+ variant read_region_reference(struct gamedata *data);
int resolve_region_coor(variant id, void *address);
int resolve_region_id(variant id, void *address);
#define RESOLVE_REGION(version) ((version
#include
#include
+#include
#include
+#include
#include
#include
#include
#include
-#include
+#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -237,8 +240,8 @@ static faction *factionorders(void)
/* Die Partei hat sich zumindest gemeldet, so dass sie noch
* nicht als untätig gilt */
- /* TODO: +1 ist ein Workaround, weil cturn erst in process_orders
- * incrementiert wird. */
+ /* TODO: +1 ist ein Workaround, weil cturn erst in process_orders
+ * incrementiert wird. */
f->lastorders = global.data_turn + 1;
}
@@ -255,7 +258,7 @@ int readorders(const char *filename)
int nfactions = 0;
struct faction *f = NULL;
- F = fopen(filename, "rb");
+ F = fopen(filename, "r");
if (!F) {
perror(filename);
return -1;
@@ -416,11 +419,12 @@ void read_items(struct storage *store, item ** ilist)
}
}
-static void read_alliances(struct storage *store)
+static void read_alliances(struct gamedata *data)
{
+ storage *store = data->store;
char pbuf[8];
int id, terminator = 0;
- if (global.data_version < ALLIANCELEADER_VERSION) {
+ if (data->version < ALLIANCELEADER_VERSION) {
terminator = atoi36("end");
READ_STR(store, pbuf, sizeof(pbuf));
id = atoi36(pbuf);
@@ -433,11 +437,11 @@ static void read_alliances(struct storage *store)
alliance *al;
READ_STR(store, aname, sizeof(aname));
al = makealliance(id, aname);
- if (global.data_version >= OWNER_2_VERSION) {
+ if (data->version >= OWNER_2_VERSION) {
READ_INT(store, &al->flags);
}
- if (global.data_version >= ALLIANCELEADER_VERSION) {
- read_reference(&al->_leader, store, read_faction_reference,
+ if (data->version >= ALLIANCELEADER_VERSION) {
+ read_reference(&al->_leader, data, read_faction_reference,
resolve_faction);
READ_INT(store, &id);
}
@@ -512,18 +516,19 @@ static void read_owner(struct gamedata *data, region_owner ** powner)
int id;
READ_INT(data->store, &id);
owner->last_owner = id ? findfaction(id) : NULL;
- } else if (data->version >= OWNER_2_VERSION) {
+ }
+ else if (data->version >= OWNER_2_VERSION) {
int id;
alliance *a;
READ_INT(data->store, &id);
a = id ? findalliance(id) : NULL;
/* don't know which faction, take the leader */
- owner->last_owner = a? a->_leader : NULL;
+ owner->last_owner = a ? a->_leader : NULL;
}
else {
owner->last_owner = NULL;
}
- read_reference(owner, data->store, &read_faction_reference, &resolve_owner);
+ read_reference(owner, data, &read_faction_reference, &resolve_owner);
*powner = owner;
}
else {
@@ -534,11 +539,14 @@ static void read_owner(struct gamedata *data, region_owner ** powner)
static void write_owner(struct gamedata *data, region_owner * owner)
{
if (owner) {
+ faction *f;
WRITE_INT(data->store, owner->since_turn);
WRITE_INT(data->store, owner->morale_turn);
WRITE_INT(data->store, owner->flags);
- write_faction_reference(owner->last_owner, data->store);
- write_faction_reference(owner->owner, data->store);
+ f = owner->last_owner;
+ write_faction_reference((f && f->_alive) ? f : NULL, data->store);
+ f = owner->owner;
+ write_faction_reference((f && f->_alive) ? f : NULL, data->store);
}
else {
WRITE_INT(data->store, -1);
@@ -551,7 +559,7 @@ int current_turn(void)
int cturn = 0;
FILE *F;
- sprintf(zText, "%s/turn", basepath());
+ join_path(basepath(), "turn", zText, sizeof(zText));
F = fopen(zText, "r");
if (!F) {
perror(zText);
@@ -568,7 +576,7 @@ int current_turn(void)
static void
writeorder(struct gamedata *data, const struct order *ord,
-const struct locale *lang)
+ const struct locale *lang)
{
char obuf[1024];
write_order(ord, obuf, sizeof(obuf));
@@ -576,6 +584,24 @@ const struct locale *lang)
WRITE_STR(data->store, obuf);
}
+int read_attribs(gamedata *data, attrib **alist, void *owner) {
+ if (data->version < ATHASH_VERSION) {
+ return a_read_orig(data, alist, owner);
+ }
+ else {
+ return a_read(data, alist, owner);
+ }
+}
+
+void write_attribs(storage *store, attrib *alist, const void *owner)
+{
+#if RELEASE_VERSION < ATHASH_VERSION
+ a_write_orig(store, alist, owner);
+#else
+ a_write(store, alist, owner);
+#endif
+}
+
unit *read_unit(struct gamedata *data)
{
unit *u;
@@ -747,8 +773,7 @@ unit *read_unit(struct gamedata *data)
log_error("Einheit %s hat %u Personen, und %u Trefferpunkte\n", itoa36(u->no), u->number, u->hp);
u->hp = u->number;
}
-
- a_read(data->store, &u->attribs, u);
+ read_attribs(data, &u->attribs, u);
return u;
}
@@ -760,6 +785,7 @@ void write_unit(struct gamedata *data, const unit * u)
const race *irace = u_irace(u);
write_unit_reference(u, data->store);
+ assert(u->faction->_alive);
write_faction_reference(u->faction, data->store);
WRITE_STR(data->store, u->_name);
WRITE_STR(data->store, u->display ? u->display : "");
@@ -816,13 +842,13 @@ void write_unit(struct gamedata *data, const unit * u)
WRITE_SECTION(data->store);
write_items(data->store, u->items);
WRITE_SECTION(data->store);
- if (u->hp == 0 && u_race(u)!= get_race(RC_SPELL)) {
+ if (u->hp == 0 && u_race(u) != get_race(RC_SPELL)) {
log_error("unit %s has 0 hitpoints, adjusting.\n", itoa36(u->no));
((unit *)u)->hp = u->number;
}
WRITE_INT(data->store, u->hp);
WRITE_SECTION(data->store);
- a_write(data->store, u->attribs, u);
+ write_attribs(data->store, u->attribs, u);
WRITE_SECTION(data->store);
}
@@ -948,15 +974,19 @@ static region *readregion(struct gamedata *data, int x, int y)
READ_INT(data->store, &n);
rsetherbs(r, (short)n);
READ_INT(data->store, &n);
- rsetpeasants(r, n);
+ if (n < 0) {
+ /* bug 2182 */
+ log_error("data has negative peasants: %d in %s", n, regionname(r, 0));
+ rsetpeasants(r, 0);
+ }
+ else {
+ rsetpeasants(r, n);
+ }
READ_INT(data->store, &n);
rsetmoney(r, n);
}
assert(r->terrain != NULL);
- assert(rhorses(r) >= 0);
- assert(rpeasants(r) >= 0);
- assert(rmoney(r) >= 0);
if (r->land) {
int n;
@@ -973,11 +1003,11 @@ static region *readregion(struct gamedata *data, int x, int y)
read_items(data->store, &r->land->items);
if (data->version >= REGIONOWNER_VERSION) {
READ_INT(data->store, &n);
- region_set_morale(r, _max(0, (short) n), -1);
+ region_set_morale(r, _max(0, (short)n), -1);
read_owner(data, &r->land->ownership);
}
}
- a_read(data->store, &r->attribs, r);
+ read_attribs(data, &r->attribs, r);
return r;
}
@@ -996,7 +1026,7 @@ void writeregion(struct gamedata *data, const region * r)
const item_type *rht;
struct demand *demand;
rawmaterial *res = r->resources;
-
+
assert(r->land);
WRITE_STR(data->store, (const char *)r->land->name);
assert(rtrees(r, 0) >= 0);
@@ -1041,7 +1071,7 @@ void writeregion(struct gamedata *data, const region * r)
WRITE_SECTION(data->store);
#endif
}
- a_write(data->store, r->attribs, r);
+ write_attribs(data->store, r->attribs, r);
WRITE_SECTION(data->store);
}
@@ -1099,14 +1129,14 @@ int get_spell_level_faction(const spell * sp, void * cbdata)
return 0;
}
-void read_spellbook(spellbook **bookp, struct storage *store, int(*get_level)(const spell * sp, void *), void * cbdata)
+void read_spellbook(spellbook **bookp, gamedata *data, int(*get_level)(const spell * sp, void *), void * cbdata)
{
for (;;) {
spell *sp = 0;
char spname[64];
int level = 0;
- READ_TOK(store, spname, sizeof(spname));
+ READ_TOK(data->store, spname, sizeof(spname));
if (strcmp(spname, "end") == 0)
break;
if (bookp) {
@@ -1115,8 +1145,8 @@ void read_spellbook(spellbook **bookp, struct storage *store, int(*get_level)(co
log_error("read_spells: could not find spell '%s'\n", spname);
}
}
- if (global.data_version >= SPELLBOOK_VERSION) {
- READ_INT(store, &level);
+ if (data->version >= SPELLBOOK_VERSION) {
+ READ_INT(data->store, &level);
}
if (sp) {
spellbook * sb = *bookp;
@@ -1127,7 +1157,7 @@ void read_spellbook(spellbook **bookp, struct storage *store, int(*get_level)(co
*bookp = create_spellbook(0);
sb = *bookp;
}
- if (level>0 && (global.data_version >= SPELLBOOK_VERSION || !spellbook_get(sb, sp))) {
+ if (level > 0 && (data->version >= SPELLBOOK_VERSION || !spellbook_get(sb, sp))) {
spellbook_add(sb, sp, level);
}
}
@@ -1149,6 +1179,58 @@ void write_spellbook(const struct spellbook *book, struct storage *store)
WRITE_TOK(store, "end");
}
+static char * getpasswd(int fno) {
+ const char *prefix = itoa36(fno);
+ size_t len = strlen(prefix);
+ FILE * F = fopen("passwords.txt", "r");
+ char line[80];
+ if (F) {
+ while (!feof(F)) {
+ fgets(line, sizeof(line), F);
+ if (line[len] == ':' && strncmp(prefix, line, len) == 0) {
+ size_t slen = strlen(line) - 1;
+ assert(line[slen] == '\n');
+ line[slen] = 0;
+ fclose(F);
+ return _strdup(line + len + 1);
+ }
+ }
+ fclose(F);
+ }
+ return NULL;
+}
+
+static void read_password(gamedata *data, faction *f) {
+ char name[128];
+ READ_STR(data->store, name, sizeof(name));
+ if (data->version == BADCRYPT_VERSION) {
+ char * pass = getpasswd(f->no);
+ if (pass) {
+ faction_setpassword(f, password_encode(pass, PASSWORD_DEFAULT));
+ free(pass); // TODO: remove this allocation!
+ }
+ else {
+ free(f->_password);
+ f->_password = NULL;
+ }
+ }
+ else {
+ faction_setpassword(f, (data->version >= CRYPT_VERSION) ? name : password_encode(name, PASSWORD_DEFAULT));
+ }
+}
+
+void _test_read_password(gamedata *data, faction *f) {
+ read_password(data, f);
+}
+
+static void write_password(gamedata *data, const faction *f) {
+ WRITE_TOK(data->store, (const char *)f->_password);
+}
+
+void _test_write_password(gamedata *data, const faction *f) {
+ write_password(data, f);
+}
+
/** Reads a faction from a file.
* This function requires no context, can be called in any state. The
* faction may not already exist, however.
@@ -1161,6 +1243,7 @@ faction *readfaction(struct gamedata * data)
char name[DISPLAYSIZE];
READ_INT(data->store, &n);
+ assert(n > 0);
f = findfaction(n);
if (f == NULL) {
f = (faction *)calloc(1, sizeof(faction));
@@ -1215,8 +1298,7 @@ faction *readfaction(struct gamedata * data)
set_email(&f->email, "");
}
- READ_STR(data->store, name, sizeof(name));
- f->passw = _strdup(name);
+ read_password(data, f);
if (data->version < NOOVERRIDE_VERSION) {
READ_STR(data->store, 0, 0);
}
@@ -1247,7 +1329,7 @@ faction *readfaction(struct gamedata * data)
}
}
- a_read(data->store, &f->attribs, f);
+ read_attribs(data, &f->attribs, f);
read_items(data->store, &f->items);
for (;;) {
READ_TOK(data->store, name, sizeof(name));
@@ -1290,10 +1372,10 @@ faction *readfaction(struct gamedata * data)
break;
}
}
- read_groups(data->store, f);
+ read_groups(data, f);
f->spellbook = 0;
if (data->version >= REGIONOWNER_VERSION) {
- read_spellbook(FactionSpells() ? &f->spellbook : 0, data->store, get_spell_level_faction, (void *)f);
+ read_spellbook(FactionSpells() ? &f->spellbook : 0, data, get_spell_level_faction, (void *)f);
}
return f;
}
@@ -1303,6 +1385,7 @@ void writefaction(struct gamedata *data, const faction * f)
ally *sf;
ursprung *ur;
+ assert(f->_alive);
write_faction_reference(f, data->store);
WRITE_INT(data->store, f->subscription);
#if RELEASE_VERSION >= SPELL_LEVEL_VERSION
@@ -1320,10 +1403,10 @@ void writefaction(struct gamedata *data, const faction * f)
}
WRITE_INT(data->store, f->alliance_joindate);
- WRITE_STR(data->store, (const char *)f->name);
- WRITE_STR(data->store, (const char *)f->banner);
+ WRITE_STR(data->store, f->name);
+ WRITE_STR(data->store, f->banner);
WRITE_STR(data->store, f->email);
- WRITE_TOK(data->store, (const char *)f->passw);
+ write_password(data, f);
WRITE_TOK(data->store, locale_name(f->locale));
WRITE_INT(data->store, f->lastorders);
WRITE_INT(data->store, f->age);
@@ -1332,7 +1415,7 @@ void writefaction(struct gamedata *data, const faction * f)
WRITE_INT(data->store, f->magiegebiet);
WRITE_INT(data->store, f->flags & FFL_SAVEMASK);
- a_write(data->store, f->attribs, f);
+ write_attribs(data->store, f->attribs, f);
WRITE_SECTION(data->store);
write_items(data->store, f->items);
WRITE_SECTION(data->store);
@@ -1378,16 +1461,8 @@ static int cb_sb_maxlevel(spellbook_entry *sbe, void *cbdata) {
int readgame(const char *filename, bool backup)
{
- int n, p, nread;
- faction *f, **fp;
- region *r;
- building *b, **bp;
- ship **shp;
- unit *u;
- int rmax = maxregions;
+ int n;
char path[MAX_PATH];
- char name[DISPLAYSIZE];
- const struct building_type *bt_lighthouse = bt_find("lighthouse");
gamedata gdata = { 0 };
storage store;
stream strm;
@@ -1396,7 +1471,7 @@ int readgame(const char *filename, bool backup)
init_locales();
log_debug("- reading game data from %s\n", filename);
- sprintf(path, "%s/%s", datapath(), filename);
+ join_path(datapath(), filename, path, sizeof(path));
if (backup) {
create_backup(path);
@@ -1408,29 +1483,44 @@ int readgame(const char *filename, bool backup)
return -1;
}
sz = fread(&gdata.version, sizeof(int), 1, F);
- if (sz!=sizeof(int) || gdata.version >= INTPAK_VERSION) {
+ if (sz != sizeof(int) || gdata.version >= INTPAK_VERSION) {
int stream_version;
size_t sz = fread(&stream_version, sizeof(int), 1, F);
- assert((sz==1 && stream_version == STREAM_VERSION) || !"unsupported data format");
+ assert((sz == 1 && stream_version == STREAM_VERSION) || !"unsupported data format");
}
assert(gdata.version >= MIN_VERSION || !"unsupported data format");
assert(gdata.version <= MAX_VERSION || !"unsupported data format");
- gdata.encoding = enc_gamedata;
fstream_init(&strm, F);
binstore_init(&store, &strm);
gdata.store = &store;
- global.data_version = gdata.version; /* HACK: attribute::read does not have access to gamedata, only storage */
if (gdata.version >= BUILDNO_VERSION) {
int build;
READ_INT(&store, &build);
log_debug("data in %s created with build %d.", filename, build);
}
- if (gdata.version >= SAVEGAMEID_VERSION) {
+ n = read_game(&gdata);
+ binstore_done(&store);
+ fstream_done(&strm);
+ return n;
+}
+
+int read_game(gamedata *data) {
+ char name[DISPLAYSIZE];
+ int n, p, nread;
+ faction *f, **fp;
+ region *r;
+ building *b, **bp;
+ ship **shp;
+ unit *u;
+ int rmax = maxregions;
+ const struct building_type *bt_lighthouse = bt_find("lighthouse");
+ storage * store = data->store;
+ if (data->version >= SAVEGAMEID_VERSION) {
int gameid;
- READ_INT(&store, &gameid);
+ READ_INT(store, &gameid);
if (gameid != game_id()) {
int c;
log_warning("game mismatch: datafile contains game %d, but config is for %d", gameid, game_id());
@@ -1443,25 +1533,25 @@ int readgame(const char *filename, bool backup)
}
}
else {
- READ_STR(&store, NULL, 0);
+ READ_STR(store, NULL, 0);
}
- a_read(&store, &global.attribs, NULL);
- READ_INT(&store, &turn);
+ read_attribs(data, &global.attribs, NULL);
+ READ_INT(store, &turn);
global.data_turn = turn;
log_debug(" - reading turn %d\n", turn);
rng_init(turn);
- READ_INT(&store, &nread); /* max_unique_id = ignore */
- READ_INT(&store, &nextborder);
+ READ_INT(store, NULL); /* max_unique_id = ignore */
+ READ_INT(store, &nextborder);
/* Planes */
planes = NULL;
- READ_INT(&store, &nread);
+ READ_INT(store, &nread);
while (--nread >= 0) {
int id;
variant fno;
plane *pl;
- READ_INT(&store, &id);
+ READ_INT(store, &id);
pl = getplanebyid(id);
if (pl == NULL) {
@@ -1471,20 +1561,20 @@ int readgame(const char *filename, bool backup)
log_warning("the plane with id=%d already exists.\n", id);
}
pl->id = id;
- READ_STR(&store, name, sizeof(name));
+ READ_STR(store, name, sizeof(name));
pl->name = _strdup(name);
- READ_INT(&store, &pl->minx);
- READ_INT(&store, &pl->maxx);
- READ_INT(&store, &pl->miny);
- READ_INT(&store, &pl->maxy);
- READ_INT(&store, &pl->flags);
+ READ_INT(store, &pl->minx);
+ READ_INT(store, &pl->maxx);
+ READ_INT(store, &pl->miny);
+ READ_INT(store, &pl->maxy);
+ READ_INT(store, &pl->flags);
/* read watchers */
- if (gdata.version < FIX_WATCHERS_VERSION) {
+ if (data->version < FIX_WATCHERS_VERSION) {
char rname[64];
/* before this version, watcher storage was pretty broken. we are incompatible and don't read them */
for (;;) {
- READ_TOK(&store, rname, sizeof(rname));
+ READ_TOK(store, rname, sizeof(rname));
if (strcmp(rname, "end") == 0) {
break; /* this is most likely the end of the list */
}
@@ -1495,33 +1585,30 @@ int readgame(const char *filename, bool backup)
}
}
else {
- fno = read_faction_reference(&store);
- while (fno.i) {
- watcher *w = (watcher *)malloc(sizeof(watcher));
- ur_add(fno, &w->faction, resolve_faction);
- READ_INT(&store, &n);
- w->mode = (unsigned char)n;
- w->next = pl->watchers;
- pl->watchers = w;
- fno = read_faction_reference(&store);
+ /* WATCHERS - eliminated in February 2016, ca. turn 966 */
+ if (data->version < NOWATCH_VERSION) {
+ fno = read_faction_reference(data);
+ while (fno.i) {
+ fno = read_faction_reference(data);
+ }
}
}
- a_read(&store, &pl->attribs, pl);
+ read_attribs(data, &pl->attribs, pl);
if (pl->id != 1094969858) { // Regatta
addlist(&planes, pl);
}
}
/* Read factions */
- read_alliances(&store);
- READ_INT(&store, &nread);
+ read_alliances(data);
+ READ_INT(store, &nread);
log_debug(" - Einzulesende Parteien: %d\n", nread);
fp = &factions;
while (*fp)
fp = &(*fp)->next;
while (--nread >= 0) {
- faction *f = readfaction(&gdata);
+ faction *f = readfaction(data);
*fp = f;
fp = &f->next;
@@ -1531,8 +1618,8 @@ int readgame(const char *filename, bool backup)
/* Regionen */
- READ_INT(&store, &nread);
- assert(nread < MAXREGIONS);
+ READ_INT(store, &nread);
+ assert(nread < MAXREGIONS && nread>=0);
if (rmax < 0) {
rmax = nread;
}
@@ -1540,67 +1627,67 @@ int readgame(const char *filename, bool backup)
while (--nread >= 0) {
unit **up;
int x, y;
- READ_INT(&store, &x);
- READ_INT(&store, &y);
+ READ_INT(store, &x);
+ READ_INT(store, &y);
if ((nread & 0x3FF) == 0) { /* das spart extrem Zeit */
log_debug(" - Einzulesende Regionen: %d/%d * %d,%d \r", rmax, nread, x, y);
}
--rmax;
- r = readregion(&gdata, x, y);
+ r = readregion(data, x, y);
/* Burgen */
- READ_INT(&store, &p);
+ READ_INT(store, &p);
bp = &r->buildings;
while (--p >= 0) {
b = (building *)calloc(1, sizeof(building));
- READ_INT(&store, &b->no);
+ READ_INT(store, &b->no);
*bp = b;
bp = &b->next;
bhash(b);
- READ_STR(&store, name, sizeof(name));
+ READ_STR(store, name, sizeof(name));
b->name = _strdup(name);
if (lomem) {
- READ_STR(gdata.store, NULL, 0);
+ READ_STR(store, NULL, 0);
}
else {
- READ_STR(&store, name, sizeof(name));
+ READ_STR(store, name, sizeof(name));
b->display = _strdup(name);
}
- READ_INT(&store, &b->size);
- READ_STR(&store, name, sizeof(name));
+ READ_INT(store, &b->size);
+ READ_STR(store, name, sizeof(name));
b->type = bt_find(name);
b->region = r;
- a_read(&store, &b->attribs, b);
+ read_attribs(data, &b->attribs, b);
if (b->type == bt_lighthouse) {
r->flags |= RF_LIGHTHOUSE;
}
}
/* Schiffe */
- READ_INT(&store, &p);
+ READ_INT(store, &p);
shp = &r->ships;
while (--p >= 0) {
ship *sh = (ship *)calloc(1, sizeof(ship));
sh->region = r;
- READ_INT(&store, &sh->no);
+ READ_INT(store, &sh->no);
*shp = sh;
shp = &sh->next;
shash(sh);
- READ_STR(&store, name, sizeof(name));
+ READ_STR(store, name, sizeof(name));
sh->name = _strdup(name);
if (lomem) {
- READ_STR(&store, NULL, 0);
+ READ_STR(store, NULL, 0);
}
else {
- READ_STR(&store, name, sizeof(name));
+ READ_STR(store, name, sizeof(name));
sh->display = _strdup(name);
}
- READ_STR(&store, name, sizeof(name));
+ READ_STR(store, name, sizeof(name));
sh->type = st_find(name);
if (sh->type == NULL) {
/* old datafiles */
@@ -1608,33 +1695,33 @@ int readgame(const char *filename, bool backup)
}
assert(sh->type || !"ship_type not registered!");
- READ_INT(&store, &sh->size);
- READ_INT(&store, &sh->damage);
- if (gdata.version >= FOSS_VERSION) {
- READ_INT(&store, &sh->flags);
+ READ_INT(store, &sh->size);
+ READ_INT(store, &sh->damage);
+ if (data->version >= FOSS_VERSION) {
+ READ_INT(store, &sh->flags);
}
/* Attribute rekursiv einlesen */
- READ_INT(&store, &n);
+ READ_INT(store, &n);
sh->coast = (direction_t)n;
if (sh->type->flags & SFL_NOCOAST) {
sh->coast = NODIRECTION;
}
- a_read(&store, &sh->attribs, sh);
+ read_attribs(data, &sh->attribs, sh);
}
*shp = 0;
/* Einheiten */
- READ_INT(&store, &p);
+ READ_INT(store, &p);
up = &r->units;
while (--p >= 0) {
- unit *u = read_unit(&gdata);
+ unit *u = read_unit(data);
- if (gdata.version < JSON_REPORT_VERSION) {
+ if (data->version < JSON_REPORT_VERSION) {
if (u->_name && fval(u->faction, FFL_NPC)) {
if (!u->_name[0] || unit_name_equals_race(u)) {
unit_setname(u, NULL);
@@ -1649,10 +1736,8 @@ int readgame(const char *filename, bool backup)
update_interval(u->faction, u->region);
}
}
- read_borders(&store);
+ read_borders(data);
- binstore_done(&store);
- fstream_done(&strm);
/* Unaufgeloeste Zeiger initialisieren */
log_debug("fixing unresolved references.\n");
resolve();
@@ -1668,7 +1753,7 @@ int readgame(const char *filename, bool backup)
log_debug("marking factions as alive.\n");
for (f = factions; f; f = f->next) {
if (f->flags & FFL_NPC) {
- f->alive = 1;
+ f->_alive = true;
f->magiegebiet = M_GRAY;
if (f->no == 0) {
int no = 666;
@@ -1680,7 +1765,7 @@ int readgame(const char *filename, bool backup)
}
else {
for (u = f->units; u; u = u->nextF) {
- if (global.data_version < SPELL_LEVEL_VERSION) {
+ if (data->version < SPELL_LEVEL_VERSION) {
sc_mage *mage = get_mage(u);
if (mage) {
faction *f = u->faction;
@@ -1698,13 +1783,13 @@ int readgame(const char *filename, bool backup)
}
}
if (u->number > 0) {
- f->alive = true;
- if (global.data_version >= SPELL_LEVEL_VERSION) {
+ f->_alive = true;
+ if (data->version >= SPELL_LEVEL_VERSION) {
break;
}
}
}
- if (global.data_version < SPELL_LEVEL_VERSION && f->spellbook) {
+ if (data->version < SPELL_LEVEL_VERSION && f->spellbook) {
spellbook_foreach(f->spellbook, cb_sb_maxlevel, f);
}
}
@@ -1730,214 +1815,165 @@ static void clear_npc_orders(faction *f)
int writegame(const char *filename)
{
int n;
- faction *f;
- region *r;
- building *b;
- ship *sh;
- unit *u;
- plane *pl;
char path[MAX_PATH];
gamedata gdata;
storage store;
stream strm;
FILE *F;
- sprintf(path, "%s/%s", datapath(), filename);
+ create_directories();
+ join_path(datapath(), filename, path, sizeof(path));
#ifdef HAVE_UNISTD_H
/* make sure we don't overwrite an existing file (hard links) */
unlink(path);
#endif
F = fopen(path, "wb");
if (!F) {
- /* we might be missing the directory, let's try creating it */
- int err = _mkdir(datapath());
- if (err) return err;
- F = fopen(path, "wb");
- if (!F) {
- perror(path);
- return -1;
- }
+ perror(path);
+ return -1;
}
gdata.store = &store;
- gdata.encoding = enc_gamedata;
gdata.version = RELEASE_VERSION;
- global.data_version = RELEASE_VERSION;
- n = STREAM_VERSION;
fwrite(&gdata.version, sizeof(int), 1, F);
+ n = STREAM_VERSION;
fwrite(&n, sizeof(int), 1, F);
fstream_init(&strm, F);
binstore_init(&store, &strm);
- /* globale Variablen */
-
WRITE_INT(&store, VERSION_BUILD);
- WRITE_INT(&store, game_id());
- WRITE_SECTION(&store);
+ n = write_game(&gdata);
+ binstore_done(&store);
+ fstream_done(&strm);
+ return n;
+}
- a_write(&store, global.attribs, NULL);
- WRITE_SECTION(&store);
+void write_planes(storage *store) {
+ plane *pl;
+ for (pl = planes; pl; pl = pl->next) {
+ WRITE_INT(store, pl->id);
+ WRITE_STR(store, pl->name);
+ WRITE_INT(store, pl->minx);
+ WRITE_INT(store, pl->maxx);
+ WRITE_INT(store, pl->miny);
+ WRITE_INT(store, pl->maxy);
+ WRITE_INT(store, pl->flags);
+#if RELEASE_VERSION < NOWATCH_VERSION
+ write_faction_reference(NULL, store); /* mark the end of pl->watchers (gone since T966) */
+#endif
+ a_write(store, pl->attribs, pl);
+ WRITE_SECTION(store);
+ }
+}
- WRITE_INT(&store, turn);
- WRITE_INT(&store, 0 /*max_unique_id */);
- WRITE_INT(&store, nextborder);
+int write_game(gamedata *data) {
+ storage * store = data->store;
+ region *r;
+ faction *f;
+ int n;
+
+ /* globale Variablen */
+ assert(data->version <= MAX_VERSION && data->version >= MIN_VERSION);
+
+ WRITE_INT(store, game_id());
+ WRITE_SECTION(store);
+
+ write_attribs(store, global.attribs, NULL);
+ WRITE_SECTION(store);
+
+ WRITE_INT(store, turn);
+ WRITE_INT(store, 0 /* max_unique_id */);
+ WRITE_INT(store, nextborder);
/* Write planes */
- WRITE_SECTION(&store);
- WRITE_INT(&store, listlen(planes));
- WRITE_SECTION(&store);
+ WRITE_SECTION(store);
+ WRITE_INT(store, listlen(planes));
+ WRITE_SECTION(store);
- for (pl = planes; pl; pl = pl->next) {
- watcher *w;
- WRITE_INT(&store, pl->id);
- WRITE_STR(&store, pl->name);
- WRITE_INT(&store, pl->minx);
- WRITE_INT(&store, pl->maxx);
- WRITE_INT(&store, pl->miny);
- WRITE_INT(&store, pl->maxy);
- WRITE_INT(&store, pl->flags);
- w = pl->watchers;
- while (w) {
- if (w->faction) {
- write_faction_reference(w->faction, &store);
- WRITE_INT(&store, w->mode);
- }
- w = w->next;
- }
- write_faction_reference(NULL, &store); /* mark the end of the list */
- a_write(&store, pl->attribs, pl);
- WRITE_SECTION(&store);
- }
-
- /* Write factions */
- write_alliances(&gdata);
+ write_planes(store);
+ write_alliances(data);
n = listlen(factions);
- WRITE_INT(&store, n);
- WRITE_SECTION(&store);
+ WRITE_INT(store, n);
+ WRITE_SECTION(store);
log_debug(" - Schreibe %d Parteien...\n", n);
for (f = factions; f; f = f->next) {
if (fval(f, FFL_NPC)) {
clear_npc_orders(f);
}
- writefaction(&gdata, f);
- WRITE_SECTION(&store);
+ writefaction(data, f);
+ WRITE_SECTION(store);
}
/* Write regions */
n = listlen(regions);
- WRITE_INT(&store, n);
- WRITE_SECTION(&store);
+ WRITE_INT(store, n);
+ WRITE_SECTION(store);
log_debug(" - Schreibe Regionen: %d", n);
for (r = regions; r; r = r->next, --n) {
+ ship *sh;
+ building *b;
+ unit *u;
/* plus leerzeile */
if ((n % 1024) == 0) { /* das spart extrem Zeit */
log_debug(" - Schreibe Regionen: %d", n);
}
- WRITE_SECTION(&store);
- WRITE_INT(&store, r->x);
- WRITE_INT(&store, r->y);
- writeregion(&gdata, r);
+ WRITE_SECTION(store);
+ WRITE_INT(store, r->x);
+ WRITE_INT(store, r->y);
+ writeregion(data, r);
- WRITE_INT(&store, listlen(r->buildings));
- WRITE_SECTION(&store);
+ WRITE_INT(store, listlen(r->buildings));
+ WRITE_SECTION(store);
for (b = r->buildings; b; b = b->next) {
- write_building_reference(b, &store);
- WRITE_STR(&store, b->name);
- WRITE_STR(&store, b->display ? b->display : "");
- WRITE_INT(&store, b->size);
- WRITE_TOK(&store, b->type->_name);
- WRITE_SECTION(&store);
- a_write(&store, b->attribs, b);
- WRITE_SECTION(&store);
+ write_building_reference(b, store);
+ WRITE_STR(store, b->name);
+ WRITE_STR(store, b->display ? b->display : "");
+ WRITE_INT(store, b->size);
+ WRITE_TOK(store, b->type->_name);
+ WRITE_SECTION(store);
+ write_attribs(store, b->attribs, b);
+ WRITE_SECTION(store);
}
- WRITE_INT(&store, listlen(r->ships));
- WRITE_SECTION(&store);
+ WRITE_INT(store, listlen(r->ships));
+ WRITE_SECTION(store);
for (sh = r->ships; sh; sh = sh->next) {
assert(sh->region == r);
- write_ship_reference(sh, &store);
- WRITE_STR(&store, (const char *)sh->name);
- WRITE_STR(&store, sh->display ? (const char *)sh->display : "");
- WRITE_TOK(&store, sh->type->_name);
- WRITE_INT(&store, sh->size);
- WRITE_INT(&store, sh->damage);
- WRITE_INT(&store, sh->flags & SFL_SAVEMASK);
+ write_ship_reference(sh, store);
+ WRITE_STR(store, (const char *)sh->name);
+ WRITE_STR(store, sh->display ? (const char *)sh->display : "");
+ WRITE_TOK(store, sh->type->_name);
+ WRITE_INT(store, sh->size);
+ WRITE_INT(store, sh->damage);
+ WRITE_INT(store, sh->flags & SFL_SAVEMASK);
assert((sh->type->flags & SFL_NOCOAST) == 0 || sh->coast == NODIRECTION);
- WRITE_INT(&store, sh->coast);
- WRITE_SECTION(&store);
- a_write(&store, sh->attribs, sh);
- WRITE_SECTION(&store);
+ WRITE_INT(store, sh->coast);
+ WRITE_SECTION(store);
+ write_attribs(store, sh->attribs, sh);
+ WRITE_SECTION(store);
}
- WRITE_INT(&store, listlen(r->units));
- WRITE_SECTION(&store);
+ WRITE_INT(store, listlen(r->units));
+ WRITE_SECTION(store);
for (u = r->units; u; u = u->next) {
- write_unit(&gdata, u);
+ write_unit(data, u);
}
}
- WRITE_SECTION(&store);
- write_borders(&store);
- WRITE_SECTION(&store);
-
- binstore_done(&store);
- fstream_done(&strm);
+ WRITE_SECTION(store);
+ write_borders(store);
+ WRITE_SECTION(store);
return 0;
}
-void gamedata_close(gamedata *data) {
- binstore_done(data->store);
- fstream_done(&data->strm);
-}
-
-gamedata *gamedata_open(const char *filename, const char *mode) {
- FILE *F = fopen(filename, mode);
-
- if (F) {
- gamedata *data = (gamedata *)calloc(1, sizeof(gamedata));
- storage *store = (storage *)calloc(1, sizeof(storage));
- int err = 0;
- size_t sz;
-
- data->store = store;
- if (strchr(mode, 'r')) {
- sz = fread(&data->version, 1, sizeof(int), F);
- if (sz != sizeof(int)) {
- err = ferror(F);
- }
- else {
- err = fseek(F, sizeof(int), SEEK_CUR);
- }
- }
- else if (strchr(mode, 'w')) {
- int n = STREAM_VERSION;
- data->version = RELEASE_VERSION;
- fwrite(&data->version, sizeof(int), 1, F);
- fwrite(&n, sizeof(int), 1, F);
- }
- if (err) {
- fclose(F);
- free(data);
- free(store);
- }
- else {
- fstream_init(&data->strm, F);
- binstore_init(store, &data->strm);
- return data;
- }
- }
- log_error("could not open %s: %s", filename, strerror(errno));
- return 0;
-}
-
-int a_readint(attrib * a, void *owner, struct storage *store)
+int a_readint(attrib * a, void *owner, struct gamedata *data)
{
/* assert(sizeof(int)==sizeof(a->data)); */
- READ_INT(store, &a->data.i);
+ READ_INT(data->store, &a->data.i);
return AT_READ_OK;
}
@@ -1946,12 +1982,12 @@ void a_writeint(const attrib * a, const void *owner, struct storage *store)
WRITE_INT(store, a->data.i);
}
-int a_readshorts(attrib * a, void *owner, struct storage *store)
+int a_readshorts(attrib * a, void *owner, struct gamedata *data)
{
int n;
- READ_INT(store, &n);
+ READ_INT(data->store, &n);
a->data.sa[0] = (short)n;
- READ_INT(store, &n);
+ READ_INT(data->store, &n);
a->data.sa[1] = (short)n;
return AT_READ_OK;
}
@@ -1962,12 +1998,12 @@ void a_writeshorts(const attrib * a, const void *owner, struct storage *store)
WRITE_INT(store, a->data.sa[1]);
}
-int a_readchars(attrib * a, void *owner, struct storage *store)
+int a_readchars(attrib * a, void *owner, struct gamedata *data)
{
int i;
for (i = 0; i != 4; ++i) {
int n;
- READ_INT(store, &n);
+ READ_INT(data->store, &n);
a->data.ca[i] = (char)n;
}
return AT_READ_OK;
@@ -1982,7 +2018,7 @@ void a_writechars(const attrib * a, const void *owner, struct storage *store)
}
}
-int a_readvoid(attrib * a, void *owner, struct storage *store)
+int a_readvoid(attrib * a, void *owner, struct gamedata *data)
{
return AT_READ_OK;
}
@@ -1991,14 +2027,14 @@ void a_writevoid(const attrib * a, const void *owner, struct storage *store)
{
}
-int a_readstring(attrib * a, void *owner, struct storage *store)
+int a_readstring(attrib * a, void *owner, struct gamedata *data)
{
char buf[DISPLAYSIZE];
char * result = 0;
int e;
size_t len = 0;
do {
- e = READ_STR(store, buf, sizeof(buf));
+ e = READ_STR(data->store, buf, sizeof(buf));
if (result) {
result = realloc(result, len + DISPLAYSIZE - 1);
strcpy(result + len, buf);
diff --git a/src/kernel/save.h b/src/kernel/save.h
index ca364fc9c..3d47fe612 100644
--- a/src/kernel/save.h
+++ b/src/kernel/save.h
@@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define H_KRNL_SAVE
#include
+#include // FIXME: eliminate include dependency from this file
#ifdef __cplusplus
extern "C" {
#endif
@@ -30,13 +31,7 @@ extern "C" {
struct spell;
struct spellbook;
struct unit;
-
- typedef struct gamedata {
- struct storage *store;
- stream strm;
- int version;
- int encoding;
- } gamedata;
+ struct gamedata;
#define MAX_INPUT_SIZE DISPLAYSIZE*2
/* Nach MAX_INPUT_SIZE brechen wir das Einlesen der Zeile ab und nehmen an,
@@ -55,34 +50,40 @@ extern "C" {
void read_items(struct storage *store, struct item **it);
void write_items(struct storage *store, struct item *it);
- void read_spellbook(struct spellbook **bookp, struct storage *store, int(*get_level)(const struct spell * sp, void *), void * cbdata);
+ void read_spellbook(struct spellbook **bookp, struct gamedata *data, int(*get_level)(const struct spell * sp, void *), void * cbdata);
void write_spellbook(const struct spellbook *book, struct storage *store);
+ void write_attribs(struct storage *store, struct attrib *alist, const void *owner);
+ int read_attribs(struct gamedata *store, struct attrib **alist, void *owner);
+
void write_unit(struct gamedata *data, const struct unit *u);
struct unit *read_unit(struct gamedata *data);
- int a_readint(struct attrib *a, void *owner, struct storage *store);
+ int a_readint(struct attrib *a, void *owner, struct gamedata *);
void a_writeint(const struct attrib *a, const void *owner,
struct storage *store);
- int a_readshorts(struct attrib *a, void *owner, struct storage *store);
+ int a_readshorts(struct attrib *a, void *owner, struct gamedata *);
void a_writeshorts(const struct attrib *a, const void *owner,
struct storage *store);
- int a_readchars(struct attrib *a, void *owner, struct storage *store);
+ int a_readchars(struct attrib *a, void *owner, struct gamedata *);
void a_writechars(const struct attrib *a, const void *owner,
struct storage *store);
- int a_readvoid(struct attrib *a, void *owner, struct storage *store);
+ int a_readvoid(struct attrib *a, void *owner, struct gamedata *);
void a_writevoid(const struct attrib *a, const void *owner,
- struct storage *store);
- int a_readstring(struct attrib *a, void *owner, struct storage *store);
+ struct storage *);
+ int a_readstring(struct attrib *a, void *owner, struct gamedata *);
void a_writestring(const struct attrib *a, const void *owner,
- struct storage *store);
+ struct storage *);
void a_finalizestring(struct attrib *a);
void create_backup(char *file);
- struct gamedata *gamedata_open(const char *filename, const char *mode);
- void gamedata_close(struct gamedata *data);
+ int write_game(gamedata *data);
+ int read_game(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);
#ifdef __cplusplus
}
#endif
diff --git a/src/kernel/save.test.c b/src/kernel/save.test.c
index 35e07325b..53dcfe5d9 100644
--- a/src/kernel/save.test.c
+++ b/src/kernel/save.test.c
@@ -1,13 +1,33 @@
#include
#include
+#include
+#include
+#include
#include "save.h"
#include "unit.h"
+#include "group.h"
+#include "ally.h"
#include "faction.h"
+#include "plane.h"
+#include "region.h"
#include "version.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
#include
#include
+#include
+
#include