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 static void test_readwrite_data(CuTest * tc) @@ -15,19 +35,17 @@ static void test_readwrite_data(CuTest * tc) const char *filename = "test.dat"; char path[MAX_PATH]; test_cleanup(); - sprintf(path, "%s/%s", datapath(), filename); CuAssertIntEquals(tc, 0, writegame(filename)); CuAssertIntEquals(tc, 0, readgame(filename, false)); - CuAssertIntEquals(tc, RELEASE_VERSION, global.data_version); + join_path(datapath(), filename, path, sizeof(path)); CuAssertIntEquals(tc, 0, remove(path)); test_cleanup(); } static void test_readwrite_unit(CuTest * tc) { - const char *filename = "test.dat"; - char path[MAX_PATH]; - gamedata *data; + gamedata data; + storage store; struct unit *u; struct region *r; struct faction *f; @@ -38,32 +56,263 @@ static void test_readwrite_unit(CuTest * tc) f = test_create_faction(0); fno = f->no; u = test_create_unit(f, r); - _mkdir(datapath()); - sprintf(path, "%s/%s", datapath(), filename); - - data = gamedata_open(path, "wb"); - CuAssertPtrNotNull(tc, data); // TODO: intermittent test - write_unit(data, u); - gamedata_close(data); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + write_unit(&data, u); + + data.strm.api->rewind(data.strm.handle); free_gamedata(); f = test_create_faction(0); renumber_faction(f, fno); - data = gamedata_open(path, "rb"); - u = read_unit(data); - gamedata_close(data); + gamedata_init(&data, &store, RELEASE_VERSION); + u = read_unit(&data); + mstream_done(&data.strm); + gamedata_done(&data); CuAssertPtrNotNull(tc, u); CuAssertPtrEquals(tc, f, u->faction); CuAssertPtrEquals(tc, 0, u->region); - CuAssertIntEquals(tc, 0, remove(path)); test_cleanup(); } +static void test_readwrite_attrib(CuTest *tc) { + gamedata data; + storage store; + attrib *a = NULL; + + test_cleanup(); + key_set(&a, 41); + key_set(&a, 42); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + write_attribs(data.store, a, NULL); + a_removeall(&a, NULL); + CuAssertPtrEquals(tc, 0, a); + + data.strm.api->rewind(data.strm.handle); + read_attribs(&data, &a, NULL); + mstream_done(&data.strm); + gamedata_done(&data); + CuAssertTrue(tc, key_get(a, 41)); + CuAssertTrue(tc, key_get(a, 42)); + a_removeall(&a, NULL); + + test_cleanup(); +} + +static void test_readwrite_dead_faction_group(CuTest *tc) { + faction *f, *f2; + unit * u; + group *g; + ally *al; + int fno; + gamedata data; + storage store; + + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + + 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); + write_game(&data); + free_gamedata(); + f = f2 = NULL; + data.strm.api->rewind(data.strm.handle); + read_game(&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); + mstream_done(&data.strm); + gamedata_done(&data); + test_cleanup(); +} + +static void test_readwrite_dead_faction_regionowner(CuTest *tc) { + faction *f; + region *r; + gamedata data; + storage store; + + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + + test_cleanup(); + config_set("rules.region_owners", "1"); + f = test_create_faction(0); + test_create_unit(f, r = test_create_region(0, 0, 0)); + region_set_owner(r, f, turn); + destroyfaction(&factions); + CuAssertTrue(tc, !f->_alive); + remove_empty_units(); + write_game(&data); + free_gamedata(); + f = NULL; + data.strm.api->rewind(data.strm.handle); + read_game(&data); + mstream_done(&data.strm); + gamedata_done(&data); + f = factions; + CuAssertPtrEquals(tc, 0, f); + r = regions; + CuAssertPtrNotNull(tc, r); + CuAssertPtrEquals(tc, 0, region_get_owner(r)); + test_cleanup(); +} + +static void test_readwrite_dead_faction_changefaction(CuTest *tc) { + gamedata data; + storage store; + faction *f, *f2; + region *r; + trigger *tr; + unit * u; + + test_cleanup(); + f = test_create_faction(0); + f2 = test_create_faction(0); + u = test_create_unit(f2, r = test_create_region(0, 0, 0)); + tr = trigger_changefaction(u, f); + add_trigger(&u->attribs, "timer", trigger_timeout(10, tr)); + CuAssertPtrNotNull(tc, a_find(u->attribs, &at_eventhandler)); + destroyfaction(&factions); + CuAssertTrue(tc, !f->_alive); + remove_empty_units(); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + write_game(&data); + free_gamedata(); + f = NULL; + data.strm.api->rewind(data.strm.handle); + read_game(&data); + mstream_done(&data.strm); + gamedata_done(&data); + f = factions; + CuAssertPtrNotNull(tc, f); + r = regions; + CuAssertPtrNotNull(tc, r); + u = r->units; + CuAssertPtrNotNull(tc, u); + CuAssertPtrEquals(tc, 0, a_find(u->attribs, &at_eventhandler)); + test_cleanup(); +} + +static void test_readwrite_dead_faction_createunit(CuTest *tc) { + gamedata data; + storage store; + faction *f, *f2; + region *r; + trigger *tr; + unit * u; + + test_cleanup(); + f = test_create_faction(0); + f2 = test_create_faction(0); + u = test_create_unit(f2, r = test_create_region(0, 0, 0)); + tr = trigger_createunit(r, f, f->race, 1); + add_trigger(&u->attribs, "timer", trigger_timeout(10, tr)); + CuAssertPtrNotNull(tc, a_find(u->attribs, &at_eventhandler)); + destroyfaction(&factions); + CuAssertTrue(tc, !f->_alive); + remove_empty_units(); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + write_game(&data); + free_gamedata(); + f = NULL; + data.strm.api->rewind(data.strm.handle); + read_game(&data); + mstream_done(&data.strm); + gamedata_done(&data); + f = factions; + CuAssertPtrNotNull(tc, f); + r = regions; + CuAssertPtrNotNull(tc, r); + u = r->units; + CuAssertPtrNotNull(tc, u); + CuAssertPtrEquals(tc, 0, a_find(u->attribs, &at_eventhandler)); + test_cleanup(); +} + +static void test_read_password(CuTest *tc) { + gamedata data; + storage store; + faction *f; + + f = test_create_faction(0); + faction_setpassword(f, password_encode("secret", PASSWORD_DEFAULT)); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + _test_write_password(&data, f); + data.strm.api->rewind(data.strm.handle); + _test_read_password(&data, f); + mstream_done(&data.strm); + gamedata_done(&data); + CuAssertTrue(tc, checkpasswd(f, "secret")); +} + +static void test_read_password_external(CuTest *tc) { + gamedata data; + storage store; + const char *pwfile = "passwords.txt"; + faction *f; + FILE * F; + + remove(pwfile); + f = test_create_faction(0); + faction_setpassword(f, password_encode("secret", PASSWORD_DEFAULT)); + CuAssertPtrNotNull(tc, f->_password); + mstream_init(&data.strm); + gamedata_init(&data, &store, RELEASE_VERSION); + WRITE_TOK(data.store, (const char *)f->_password); + WRITE_TOK(data.store, (const char *)f->_password); + data.strm.api->rewind(data.strm.handle); + data.version = BADCRYPT_VERSION; + _test_read_password(&data, f); + CuAssertPtrEquals(tc, 0, f->_password); + F = fopen(pwfile, "wt"); + fprintf(F, "%s:secret\n", itoa36(f->no)); + fclose(F); + _test_read_password(&data, f); + CuAssertPtrNotNull(tc, f->_password); + mstream_done(&data.strm); + gamedata_done(&data); + CuAssertTrue(tc, checkpasswd(f, "secret")); + CuAssertIntEquals(tc, 0, remove(pwfile)); +} + CuSuite *get_save_suite(void) { CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_readwrite_attrib); SUITE_ADD_TEST(suite, test_readwrite_data); SUITE_ADD_TEST(suite, test_readwrite_unit); + SUITE_ADD_TEST(suite, test_readwrite_dead_faction_createunit); + SUITE_ADD_TEST(suite, test_readwrite_dead_faction_changefaction); + SUITE_ADD_TEST(suite, test_readwrite_dead_faction_regionowner); + DISABLE_TEST(suite, test_readwrite_dead_faction_group); + SUITE_ADD_TEST(suite, test_read_password); + SUITE_ADD_TEST(suite, test_read_password_external); return suite; } diff --git a/src/kernel/skills.c b/src/kernel/skills.c index 7ba325dcc..cb0dcddea 100644 --- a/src/kernel/skills.c +++ b/src/kernel/skills.c @@ -59,6 +59,7 @@ attrib_type at_skillmod = { NULL, NULL, /* can't write function pointers */ NULL, /* can't read function pointers */ + NULL, ATF_PRESERVE }; diff --git a/src/kernel/spell.test.c b/src/kernel/spell.test.c index 23e227ebe..0c0beb6ce 100644 --- a/src/kernel/spell.test.c +++ b/src/kernel/spell.test.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -24,6 +25,7 @@ static void test_create_a_spell(CuTest * tc) static void test_create_duplicate_spell(CuTest * tc) { spell *sp; + /* FIXME: this test emits ERROR messages (duplicate spells), inject a logger to verify that */ test_cleanup(); CuAssertPtrEquals(tc, 0, find_spell("testspell")); @@ -36,6 +38,7 @@ static void test_create_duplicate_spell(CuTest * tc) static void test_create_spell_with_id(CuTest * tc) { spell *sp; + /* FIXME: this test emits ERROR messages (duplicate spells), inject a logger to verify that */ test_cleanup(); CuAssertPtrEquals(tc, 0, find_spellbyid(42)); diff --git a/src/kernel/unit.c b/src/kernel/unit.c index d82ad47ed..e5746b11e 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -28,7 +28,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "curse.h" #include "item.h" #include "move.h" -#include "monster.h" #include "order.h" #include "plane.h" #include "race.h" @@ -52,6 +51,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -116,9 +116,9 @@ unit *findunitr(const region * r, int n) { unit *u; /* findunit regional! */ - assert(n>0); + assert(n > 0); u = ufindhash(n); - return (u && u->region==r)?u:0; + return (u && u->region == r) ? u : 0; } // TODO: deprecated, replace with findunit(n) @@ -443,8 +443,9 @@ int ualias(const unit * u) return a->data.i; } -int a_readprivate(attrib * a, void *owner, struct storage *store) +int a_readprivate(attrib * a, void *owner, gamedata *data) { + struct storage *store = data->store; char lbuf[DISPLAYSIZE]; READ_STR(store, lbuf, sizeof(lbuf)); a->data.v = _strdup(lbuf); @@ -579,9 +580,9 @@ void a_writesiege(const attrib * a, const void *owner, struct storage *store) write_building_reference(b, store); } -int a_readsiege(attrib * a, void *owner, struct storage *store) +int a_readsiege(attrib * a, void *owner, gamedata *data) { - int result = read_reference(&a->data.v, store, read_building_reference, + int result = read_reference(&a->data.v, data, read_building_reference, resolve_building); if (result == 0 && !a->data.v) { return AT_READ_FAIL; @@ -656,7 +657,7 @@ bool ucontact(const unit * u, const unit * u2) /* Explizites KONTAKTIERE */ for (ru = a_find(u->attribs, &at_contact); ru && ru->type == &at_contact; - ru = ru->next) { + ru = ru->next) { if (((unit *)ru->data.v) == u2) { return true; } @@ -698,10 +699,10 @@ int resolve_unit(variant id, void *address) return 0; } -variant read_unit_reference(struct storage * store) +variant read_unit_reference(gamedata *data) { variant var; - READ_INT(store, &var.i); + READ_INT(data->store, &var.i); return var; } @@ -830,7 +831,7 @@ bool can_leave(unit * u) rule_leave = config_get_int("rules.move.owner_leave", 0); - if (rule_leave!=0 && u->building && u == building_owner(u->building)) { + if (rule_leave != 0 && u->building && u == building_owner(u->building)) { return false; } return true; @@ -1001,7 +1002,7 @@ void transfermen(unit * u, unit * dst, int n) set_leftship(dst, sh); dst->flags |= u->flags & (UFL_LONGACTION | UFL_NOTMOVING | UFL_HUNGER | UFL_MOVED | - UFL_ENTER); + UFL_ENTER); if (u->attribs) { transfer_curse(u, dst, n); } @@ -1128,30 +1129,6 @@ void set_number(unit * u, int count) u->number = (unsigned short)count; } -bool learn_skill(unit * u, skill_t sk, double learn_chance) -{ - skill *sv = u->skills; - if (learn_chance < 1.0 && rng_int() % 10000 >= learn_chance * 10000) - if (!chance(learn_chance)) - return false; - while (sv != u->skills + u->skill_size) { - assert(sv->weeks > 0); - if (sv->id == sk) { - if (sv->weeks <= 1) { - sk_set(sv, sv->level + 1); - } - else { - sv->weeks--; - } - return true; - } - ++sv; - } - sv = add_skill(u, sk); - sk_set(sv, 1); - return true; -} - void remove_skill(unit * u, skill_t sk) { skill *sv = u->skills; @@ -1280,7 +1257,8 @@ static int att_modification(const unit * u, skill_t sk) bool allied = alliedunit(c->magician, u->faction, HELP_GUARD); if (allied) { if (effect > bonus) bonus = effect; - } else { + } + else { if (effect < malus) malus = effect; } } @@ -1329,7 +1307,7 @@ int eff_skill(const unit * u, const skill *sv, const region *r) { assert(u); if (!r) r = u->region; - if (sv && sv->level>0) { + if (sv && sv->level > 0) { int mlevel = sv->level + get_modifier(u, sv->id, sv->level, r, false); if (mlevel > 0) { @@ -1418,10 +1396,10 @@ void default_name(const unit *u, char name[], int len) { static const char * prefix[MAXLOCALES]; int i = locale_index(lang); /*if (!prefix[i]) {*/ - prefix[i] = LOC(lang, "unitdefault"); - if (!prefix[i]) { - prefix[i] = parameters[P_UNIT]; - } + prefix[i] = LOC(lang, "unitdefault"); + if (!prefix[i]) { + prefix[i] = parameters[P_UNIT]; + } /*}*/ result = prefix[i]; } @@ -1463,7 +1441,7 @@ unit *create_unit(region * r, faction * f, int number, const struct race *urace, assert(urace); if (f) { - assert(f->alive); + assert(faction_alive(f)); u_setfaction(u, f); if (f->locale) { @@ -1733,7 +1711,7 @@ void scale_number(unit * u, int n) } if (u->number > 0) { for (a = a_find(u->attribs, &at_effect); a && a->type == &at_effect; - a = a->next) { + a = a->next) { effect_data *data = (effect_data *)a->data.v; int snew = data->value / u->number * n; if (n) { @@ -1828,7 +1806,7 @@ void remove_empty_units_in_region(region * r) if (u->number) { faction *f = u->faction; - if (f == NULL || !f->alive) { + if (f == NULL || !faction_alive(f)) { set_number(u, 0); } } @@ -1889,25 +1867,6 @@ bool unit_can_study(const unit *u) { return !((u_race(u)->flags & RCF_NOLEARN) || fval(u, UFL_WERE)); } -static double produceexp_chance(void) { - return config_get_flt("study.from_use", 1.0 / 3); -} - -void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double)) -{ - if (n != 0 && (is_monsters(u->faction) || playerrace(u_race(u)))) { - double chance = produceexp_chance(); - if (chance > 0.0F) { - learn(u, sk, (n * chance) / u->number); - } - } -} - -void produceexp(struct unit *u, skill_t sk, int n) -{ - produceexp_ex(u, sk, n, learn_skill); -} - /* ID's für Einheiten und Zauber */ int newunitid(void) { diff --git a/src/kernel/unit.h b/src/kernel/unit.h index e2dac5abe..2a74e4fbb 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -30,7 +30,7 @@ extern "C" { struct skill; struct item; struct sc_mage; - + struct gamedata; #define UFL_DEAD (1<<0) #define UFL_ISNEW (1<<1) /* 2 */ #define UFL_LONGACTION (1<<2) /* 4 */ @@ -162,9 +162,6 @@ extern "C" { struct skill *unit_skill(const struct unit *u, skill_t id); bool has_skill(const unit * u, skill_t sk); int effskill(const struct unit *u, skill_t sk, const struct region *r); - int SkillCap(skill_t sk); - void produceexp(struct unit *u, skill_t sk, int n); - void produceexp_ex(struct unit *u, skill_t sk, int n, bool (*learn)(unit *, skill_t, double)); void set_level(struct unit *u, skill_t id, int level); int get_level(const struct unit *u, skill_t id); @@ -186,7 +183,7 @@ extern "C" { /* see resolve.h */ int resolve_unit(variant data, void *address); void write_unit_reference(const struct unit *u, struct storage *store); - variant read_unit_reference(struct storage *store); + variant read_unit_reference(struct gamedata *data); bool leave(struct unit *u, bool force); bool can_leave(struct unit *u); @@ -209,8 +206,6 @@ extern "C" { void u_setfaction(struct unit *u, struct faction *f); void set_number(struct unit *u, int count); - bool learn_skill(struct unit *u, skill_t sk, double chance); - int invisible(const struct unit *target, const struct unit *viewer); void free_unit(struct unit *u); diff --git a/src/kernel/unit.test.c b/src/kernel/unit.test.c index e72f4caed..807a27327 100644 --- a/src/kernel/unit.test.c +++ b/src/kernel/unit.test.c @@ -86,7 +86,7 @@ static void test_remove_units_with_dead_faction(CuTest *tc) { u = test_create_unit(test_create_faction(test_create_race("human")), findregion(0, 0)); uid = u->no; - u->faction->alive = false; + u->faction->_alive = false; remove_empty_units_in_region(u->region); CuAssertPtrEquals(tc, 0, findunit(uid)); CuAssertIntEquals(tc, 0, u->number); @@ -341,32 +341,6 @@ static void test_age_familiar(CuTest *tc) { test_cleanup(); } -static CuTest *g_tc; - -static bool cb_learn_one(unit *u, skill_t sk, double chance) { - CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); - CuAssertDblEquals(g_tc, 0.5 / u->number, chance, 0.01); - return false; -} - -static bool cb_learn_two(unit *u, skill_t sk, double chance) { - CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); - CuAssertDblEquals(g_tc, 2 * 0.5 / u->number, chance, 0.01); - return false; -} - -static void test_produceexp(CuTest *tc) { - unit *u; - - g_tc = tc; - test_cleanup(); - u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); - config_set("study.from_use", "0.5"); - produceexp_ex(u, SK_ALCHEMY, 1, cb_learn_one); - produceexp_ex(u, SK_ALCHEMY, 2, cb_learn_two); - test_cleanup(); -} - static void test_inside_building(CuTest *tc) { unit *u; building *b; @@ -433,7 +407,6 @@ CuSuite *get_unit_suite(void) SUITE_ADD_TEST(suite, test_skill_familiar); SUITE_ADD_TEST(suite, test_age_familiar); SUITE_ADD_TEST(suite, test_inside_building); - SUITE_ADD_TEST(suite, test_produceexp); SUITE_ADD_TEST(suite, test_limited_skills); return suite; } diff --git a/src/kernel/version.h b/src/kernel/version.h index 19c564c1e..cfab2d52e 100644 --- a/src/kernel/version.h +++ b/src/kernel/version.h @@ -32,10 +32,11 @@ #define EXPLICIT_CURSE_ISNEW_VERSION 347 /* CURSE_ISNEW is not reset in read/write, but in age() */ #define SPELL_LEVEL_VERSION 348 /* f->max_spelllevel gets stored, not calculated */ #define OWNER_3_VERSION 349 /* regions store last owner, not last alliance */ -#define ATTRIBOWNER_VERSION 350 /* all attrib_type functions know who owns the attribute */ - -#define RELEASE_VERSION ATTRIBOWNER_VERSION /* current datafile */ +#define ATTRIBOWNER_VERSION 351 /* all attrib_type functions know who owns the attribute */ +#define BADCRYPT_VERSION 351 /* passwords are encrypted, poorly */ +#define ATHASH_VERSION 352 /* attribute-type hash, not name */ +#define NOWATCH_VERSION 353 /* plane->watchers is gone */ +#define CRYPT_VERSION 354 /* passwords are encrypted */ +#define RELEASE_VERSION NOWATCH_VERSION /* current datafile */ #define MIN_VERSION INTPAK_VERSION /* minimal datafile we support */ #define MAX_VERSION RELEASE_VERSION /* change this if we can need to read the future datafile, and we can do so */ - -#define STREAM_VERSION 2 /* internal encoding of binary files */ diff --git a/src/laws.c b/src/laws.c index 5ba995e72..79889e7da 100755 --- a/src/laws.c +++ b/src/laws.c @@ -36,6 +36,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "study.h" #include "wormhole.h" #include "prefix.h" +#include "teleport.h" #include "calendar.h" #include "guard.h" @@ -59,7 +60,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include -#include #include #include /* for volcanoes in emigration (needs a flag) */ #include @@ -79,6 +79,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -485,6 +486,7 @@ attrib_type at_germs = { DEFAULT_AGE, a_writeshorts, a_readshorts, + NULL, ATF_UNIQUE }; @@ -709,20 +711,22 @@ void immigration(void) } /* Genereate some (0-6 depending on the income) peasants out of nothing */ /* if less than 50 are in the region and there is space and no monster or demon units in the region */ - int peasants = rpeasants(r); - int income = wage(r, NULL, NULL, turn) - maintenance_cost(NULL); - if (repopulate && r->land && (peasants < repopulate) && maxworkingpeasants(r) > (peasants + 30) * 2 && income >= 0) { - int badunit = 0; - unit *u; - for (u = r->units; u; u = u->next) { - if (!playerrace(u_race(u)) || u_race(u) == get_race(RC_DAEMON)) { - badunit = 1; - break; + if (repopulate) { + int peasants = rpeasants(r); + int income = wage(r, NULL, NULL, turn) - maintenance_cost(NULL) + 1; + if (income >= 0 && r->land && (peasants < repopulate) && maxworkingpeasants(r) >(peasants + 30) * 2) { + int badunit = 0; + unit *u; + for (u = r->units; u; u = u->next) { + if (!playerrace(u_race(u)) || u_race(u) == get_race(RC_DAEMON)) { + badunit = 1; + break; + } + } + if (badunit == 0) { + peasants += (int)(rng_double()*income); + rsetpeasants(r, peasants); } - } - if (badunit == 0) { - peasants += (int)(rng_double() * (income + 1)); - rsetpeasants(r, peasants); } } } @@ -733,9 +737,6 @@ void nmr_warnings(void) faction *f, *fa; #define FRIEND (HELP_GUARD|HELP_MONEY) for (f = factions; f; f = f->next) { - if (f->age <= 1) { - ADDMSG(&f->msgs, msg_message("changepasswd", "value", f->passw)); - } if (!fval(f, FFL_NOIDLEOUT) && turn > f->lastorders) { ADDMSG(&f->msgs, msg_message("nmr_warning", "")); if (turn - f->lastorders == NMRTimeout() - 1) { @@ -785,14 +786,14 @@ void demographics(void) } for (r = regions; r; r = r->next) { - ++r->age; /* also oceans. no idea why we didn't always do that */ + ++r->age; /* also oceans. no idea why we didn't always do that */ live(r); if (!fval(r->terrain, SEA_REGION)) { /* die Nachfrage nach Produkten steigt. */ struct demand *dmd; if (r->land) { - int plant_rules = config_get_int("rules.grow.formula", 0); + int plant_rules = config_get_int("rules.grow.formula", 2); for (dmd = r->land->demands; dmd; dmd = dmd->next) { if (dmd->value > 0 && dmd->value < MAXDEMAND) { float rise = DMRISE; @@ -818,11 +819,11 @@ void demographics(void) } } horses(r); - if (plant_rules == 0) { /* E1 */ + if (plant_rules == 2) { /* E2 */ growing_trees(r, current_season, last_weeks_season); growing_herbs(r, current_season, last_weeks_season); } - else { /* E3 */ + else if (plant_rules==1) { /* E3 */ growing_trees_e3(r, current_season, last_weeks_season); } } @@ -1220,42 +1221,45 @@ static void nmr_death(faction * f) static void remove_idle_players(void) { - faction *f; + faction **fp; log_info(" - beseitige Spieler, die sich zu lange nicht mehr gemeldet haben..."); - for (f = factions; f; f = f->next) { + for (fp = &factions; *fp;) { + faction *f = *fp; if (fval(f, FFL_NOIDLEOUT)) { f->lastorders = turn; } if (NMRTimeout() > 0 && turn - f->lastorders >= NMRTimeout()) { nmr_death(f); - destroyfaction(f); - continue; - } - if (turn != f->lastorders) { + destroyfaction(fp); + } else if (turn != f->lastorders) { char info[256]; sprintf(info, "%d Einheiten, %d Personen, %d Silber", f->no_units, f->num_total, f->money); } + fp = &f->next; } log_info(" - beseitige Spieler, die sich nach der Anmeldung nicht gemeldet haben..."); age = calloc(_max(4, turn + 1), sizeof(int)); - for (f = factions; f; f = f->next) + for (fp = &factions; *fp;) { + faction *f = *fp; if (!is_monsters(f)) { if (RemoveNMRNewbie() && !fval(f, FFL_NOIDLEOUT)) { if (f->age >= 0 && f->age <= turn) ++age[f->age]; if (f->age == 2 || f->age == 3) { if (f->lastorders == turn - 2) { - destroyfaction(f); ++dropouts[f->age - 2]; + destroyfaction(fp); continue; } } } } + fp = &f->next; + } } void quit(void) @@ -1264,7 +1268,7 @@ void quit(void) while (*fptr) { faction *f = *fptr; if (f->flags & FFL_QUIT) { - destroyfaction(f); + destroyfaction(fptr); } else { ++f->age; @@ -1272,8 +1276,6 @@ void quit(void) ADDMSG(&f->msgs, msg_message("newbieimmunity", "turns", NewbieImmunity() - f->age - 1)); } - } - if (*fptr == f) { fptr = &f->next; } } @@ -2167,16 +2169,13 @@ int password_cmd(unit * u, struct order *ord) } } } - free(u->faction->passw); if (!pwok) { cmistake(u, ord, 283, MSG_EVENT); - u->faction->passw = _strdup(itoa36(rng_int())); - } - else { - u->faction->passw = _strdup(pwbuf); + strlcpy(pwbuf, itoa36(rng_int()), sizeof(pwbuf)); } + faction_setpassword(u->faction, password_encode(pwbuf, PASSWORD_DEFAULT)); ADDMSG(&u->faction->msgs, msg_message("changepasswd", - "value", u->faction->passw)); + "value", pwbuf)); return 0; } @@ -2751,7 +2750,7 @@ void sinkships(struct region * r) static attrib_type at_number = { "faction_renum", - NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, ATF_UNIQUE }; @@ -3019,74 +3018,75 @@ int renumber_cmd(unit * u, order * ord) return 0; } -static building *age_building(building * b) -{ - const struct building_type *bt_blessed; - const struct curse_type *ct_astralblock; +/* blesses stone circles create an astral protection in the astral region + * above the shield, which prevents chaos suction and other spells. + * The shield is created when a magician enters the blessed stone circle, + * and lasts for as long as his skill level / 2 is, at no mana cost. + * + * TODO: this would be nicer in a btype->age function, but we don't have it. + */ +static void age_stonecircle(building *b) { const struct resource_type *rtype = get_resourcetype(R_UNICORN); + region *r = b->region; + unit *u, *mage = NULL; - bt_blessed = bt_find("blessedstonecircle"); - ct_astralblock = ct_find("astralblock"); - - /* blesses stone circles create an astral protection in the astral region - * above the shield, which prevents chaos suction and other spells. - * The shield is created when a magician enters the blessed stone circle, - * and lasts for as long as his skill level / 2 is, at no mana cost. - * - * TODO: this would be nicer in a btype->age function, but we don't have it. - */ - if (rtype && ct_astralblock && bt_blessed && b->type == bt_blessed) { - region *r = b->region; - region *rt = r_standard_to_astral(r); - unit *u, *mage = NULL; - - /* step 1: give unicorns to people in the building, - * find out if there's a magician in there. */ - for (u = r->units; u; u = u->next) { - if (b == u->building && inside_building(u)) { - if ((u_race(u)->ec_flags & ECF_KEEP_ITEM) == 0) { - int n, unicorns = 0; - for (n = 0; n != u->number; ++n) { - if (chance(0.02)) { - i_change(&u->items, rtype->itype, 1); - ++unicorns; - } - if (unicorns) { - ADDMSG(&u->faction->msgs, msg_message("scunicorn", - "unit amount rtype", - u, unicorns, rtype)); - } + /* step 1: give unicorns to people in the building, + * find out if there's a magician in there. */ + for (u = r->units; u; u = u->next) { + if (b == u->building && inside_building(u)) { + if (!mage && is_mage(u)) { + mage = u; + } + if (rtype && (u_race(u)->ec_flags & ECF_KEEP_ITEM) == 0) { + int n, unicorns = 0; + for (n = 0; n != u->number; ++n) { + if (chance(0.02)) { + i_change(&u->items, rtype->itype, 1); + ++unicorns; + } + if (unicorns) { + ADDMSG(&u->faction->msgs, msg_message("scunicorn", + "unit amount rtype", + u, unicorns, rtype)); } - } - if (mage == NULL && is_mage(u)) { - mage = u; } } } + } - /* if there's a magician, and a connection to astral space, create the - * curse. */ - if (rt && !fval(rt->terrain, FORBIDDEN_REGION) && mage != NULL) { + /* step 2: if there's a magician, and a connection to astral space, create the + * curse. */ + if (get_astralplane()) { + region *rt = r_standard_to_astral(r); + if (mage && rt && !fval(rt->terrain, FORBIDDEN_REGION)) { + const struct curse_type *ct_astralblock = ct_find("astralblock"); curse *c = get_curse(rt->attribs, ct_astralblock); - if (c == NULL) { - if (mage != NULL) { - int sk = effskill(mage, SK_MAGIC, 0); - float effect = 100; - /* the mage reactivates the circle */ - c = create_curse(mage, &rt->attribs, ct_astralblock, - (float)_max(1, sk), _max(1, sk / 2), effect, 0); - ADDMSG(&r->msgs, - msg_message("astralshield_activate", "region unit", r, mage)); - } + if (!c) { + int sk = effskill(mage, SK_MAGIC, 0); + float effect = 100; + /* the mage reactivates the circle */ + c = create_curse(mage, &rt->attribs, ct_astralblock, + (float)_max(1, sk), _max(1, sk / 2), effect, 0); + ADDMSG(&r->msgs, + msg_message("astralshield_activate", "region unit", r, mage)); } - else if (mage != NULL) { + else { int sk = effskill(mage, SK_MAGIC, 0); c->duration = _max(c->duration, sk / 2); c->vigour = _max(c->vigour, (float)sk); } } } +} +static building *age_building(building * b) +{ + const struct building_type *bt_blessed; + + bt_blessed = bt_find("blessedstonecircle"); + if (bt_blessed && b->type == bt_blessed) { + age_stonecircle(b); + } a_age(&b->attribs, b); handle_event(b->attribs, "timer", b); @@ -3432,10 +3432,13 @@ void update_long_order(unit * u) static int use_item(unit * u, const item_type * itype, int amount, struct order *ord) { int i; - int target = read_unitid(u->faction, u->region); + int target = -1; + + if (itype->useonother) { + target = read_unitid(u->faction, u->region); + } i = get_pooled(u, itype->rtype, GET_DEFAULT, amount); - if (amount > i) { /* TODO: message? eg. "not enough %, using only %" */ amount = i; @@ -3445,20 +3448,16 @@ static int use_item(unit * u, const item_type * itype, int amount, struct order } if (target == -1) { - int result; - if (itype->use == NULL) { - return EUNUSABLE; + if (itype->use) { + int result = itype->use(u, itype, amount, ord); + if (result > 0) { + use_pooled(u, itype->rtype, GET_DEFAULT, result); + } + return result; } - result = itype->use ? itype->use(u, itype, amount, ord) : EUNUSABLE; - if (result > 0) { - use_pooled(u, itype->rtype, GET_DEFAULT, result); - } - return result; + return EUNUSABLE; } else { - if (itype->useonother == NULL) { - return EUNUSABLE; - } return itype->useonother(u, target, itype, amount, ord); } } @@ -4256,33 +4255,6 @@ static void maintain_buildings_1(region * r) maintain_buildings(r, false); } -/** warn about passwords that are not US ASCII. - * even though passwords are technically UTF8 strings, the server receives - * them as part of the Subject of an email when reports are requested. - * This means that we need to limit them to ASCII characters until that - * mechanism has been changed. - */ -static int warn_password(void) -{ - faction *f = factions; - while (f) { - bool pwok = true; - const char *c = f->passw; - while (*c && pwok) { - if (!isalnum((unsigned char)*c)) - pwok = false; - c++; - } - if (!pwok) { - free(f->passw); - f->passw = _strdup(itoa36(rng_int())); - ADDMSG(&f->msgs, msg_message("illegal_password", "newpass", f->passw)); - } - f = f->next; - } - return 0; -} - void init_processor(void) { int p; @@ -4464,31 +4436,6 @@ void processorders(void) /* immer ausführen, wenn neue Sprüche dazugekommen sind, oder sich * Beschreibungen geändert haben */ update_spells(); - warn_password(); -} - -int writepasswd(void) -{ - FILE *F; - char zText[128]; - - sprintf(zText, "%s/passwd", basepath()); - 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->passw, f->subscription); - } - fclose(F); - return 0; - } - return 1; } void update_subscriptions(void) @@ -4496,8 +4443,7 @@ void update_subscriptions(void) FILE *F; char zText[MAX_PATH]; - strlcpy(zText, basepath(), sizeof(zText)); - strlcat(zText, "/subscriptions", sizeof(zText)); + join_path(basepath(), "subscriptions", zText, sizeof(zText)); F = fopen(zText, "r"); if (F == NULL) { log_warning(0, "could not open %s.\n", zText); @@ -4517,17 +4463,6 @@ void update_subscriptions(void) } } fclose(F); - - sprintf(zText, "subscriptions.%u", turn); - F = fopen(zText, "w"); - if (F) { - faction *f; - for (f = factions; f != NULL; f = f->next) { - fprintf(F, "%s:%u:%s:%s:%u:\n", - itoa36(f->no), f->subscription, f->email, dbrace(f->race), f->lastorders); - } - fclose(F); - } } bool diff --git a/src/laws.h b/src/laws.h index e245f3c01..6c8633536 100755 --- a/src/laws.h +++ b/src/laws.h @@ -38,7 +38,6 @@ extern "C" { extern int dropouts[2]; extern int *age; - int writepasswd(void); void demographics(void); void immigration(void); void update_guards(void); diff --git a/src/laws.test.c b/src/laws.test.c index 91b04b257..64f65663c 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1061,6 +1061,7 @@ static void test_ally_cmd(CuTest *tc) { test_cleanup(); u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); f = test_create_faction(0); + u->faction->locale = lang = get_or_create_locale("de"); locale_setstring(lang, parameters[P_NOT], "NICHT"); locale_setstring(lang, parameters[P_GUARD], "BEWACHE"); @@ -1105,8 +1106,9 @@ static void test_nmr_warnings(CuTest *tc) { CuAssertIntEquals(tc, 0, f1->age); nmr_warnings(); CuAssertPtrNotNull(tc, f1->msgs); - CuAssertPtrNotNull(tc, test_find_messagetype(f1->msgs, "changepasswd")); + CuAssertPtrNotNull(tc, test_find_messagetype(f1->msgs, "nmr_warning")); CuAssertPtrNotNull(tc, f2->msgs); + CuAssertPtrNotNull(tc, f2->msgs->begin); CuAssertPtrNotNull(tc, test_find_messagetype(f2->msgs, "nmr_warning")); CuAssertPtrNotNull(tc, test_find_messagetype(f2->msgs, "nmr_warning_final")); test_cleanup(); @@ -1227,6 +1229,8 @@ static void test_show_without_item(CuTest *tc) test_cleanup(); loc = get_or_create_locale("de"); + locale_setstring(loc, parameters[P_ANY], "ALLE"); + init_parameters(loc); r = test_create_region(0, 0, test_create_terrain("testregion", LAND_REGION)); f = test_create_faction(test_create_race("human")); @@ -1295,6 +1299,46 @@ static void test_immigration(CuTest * tc) test_cleanup(); } +static void test_demon_hunger(CuTest * tc) +{ + const resource_type *rtype; + region *r; + race *rc; + faction *f; + unit *u; + message* msg; + + test_cleanup(); + test_create_world(); + r = findregion(0, 0); + rc = test_create_race("demon"); + f = test_create_faction(rc); + u = test_create_unit(f, r); + u->hp = 999; + + config_set("hunger.demons.peasant_tolerance", "1"); + + rtype = get_resourcetype(R_SILVER); + i_change(&u->items, rtype->itype, 30); + scale_number(u, 1); + rsetpeasants(r, 0); + + get_food(r); + + CuAssertIntEquals(tc, 20, i_get(u->items, rtype->itype)); + CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "malnourish")); + + config_set("hunger.demons.peasant_tolerance", "0"); + + get_food(r); + + CuAssertIntEquals(tc, 10, i_get(u->items, rtype->itype)); + msg = test_get_last_message(u->faction->msgs); + CuAssertStrEquals(tc, "malnourish", test_get_messagetype(msg)); + + test_cleanup(); +} + CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -1354,6 +1398,7 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_name_ship); SUITE_ADD_TEST(suite, test_show_without_item); SUITE_ADD_TEST(suite, test_immigration); + SUITE_ADD_TEST(suite, test_demon_hunger); return suite; } diff --git a/src/locale.pkg b/src/locale.pkg index 9a2c420de..b14dc9261 100644 --- a/src/locale.pkg +++ b/src/locale.pkg @@ -7,5 +7,6 @@ module eressea { void locale_create @ create(const char *lang); void locale_set @ set(const char *lang, const char *key, const char *str); const char * locale_get @ get(const char *lang, const char *key); + int locale_direction @ direction(const char *lang, const char *str); } } diff --git a/src/locale.pkg.c b/src/locale.pkg.c index 34517584f..1792b742e 100644 --- a/src/locale.pkg.c +++ b/src/locale.pkg.c @@ -20,7 +20,6 @@ LUALIB_API int luaopen_locale (lua_State* tolua_S); #undef tolua_reg_types #define tolua_reg_types tolua_reg_types_locale -#include "bind_tolua.h" #include "bind_locale.h" /* function to register type */ @@ -113,6 +112,35 @@ static int tolua_locale_eressea_locale_get00(lua_State* tolua_S) #endif } +/* function: locale_direction */ +static int tolua_locale_eressea_locale_direction00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isstring(tolua_S,1,0,&tolua_err) || + !tolua_isstring(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + const char* lang = ((const char*) tolua_tostring(tolua_S,1,0)); + const char* str = ((const char*) tolua_tostring(tolua_S,2,0)); + { + int tolua_ret = (int) locale_direction(lang,str); + tolua_pushnumber(tolua_S,(lua_Number)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'direction'.",&tolua_err); + return 0; +#endif +} + /* Open lib function */ LUALIB_API int luaopen_locale (lua_State* tolua_S) { @@ -127,6 +155,7 @@ LUALIB_API int luaopen_locale (lua_State* tolua_S) tolua_function(tolua_S,"create",tolua_locale_eressea_locale_create00); tolua_function(tolua_S,"set",tolua_locale_eressea_locale_set00); tolua_function(tolua_S,"get",tolua_locale_eressea_locale_get00); + tolua_function(tolua_S,"direction",tolua_locale_eressea_locale_direction00); tolua_endmodule(tolua_S); tolua_endmodule(tolua_S); tolua_endmodule(tolua_S); diff --git a/src/magic.c b/src/magic.c index dc6d6e9dc..d24612e06 100644 --- a/src/magic.c +++ b/src/magic.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "magic.h" #include "skill.h" +#include "study.h" #include "laws.h" #include @@ -41,7 +42,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include -#include #include #include #include @@ -55,7 +55,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include -#include +#include #include #include #include @@ -68,6 +68,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include +#include #include /* libc includes */ @@ -129,16 +130,17 @@ typedef struct icastle_data { int time; } icastle_data; -static int a_readicastle(attrib * a, void *owner, struct storage *store) +static int a_readicastle(attrib * a, void *owner, struct gamedata *data) { - icastle_data *data = (icastle_data *)a->data.v; + storage *store = data->store; + icastle_data *idata = (icastle_data *)a->data.v; char token[32]; READ_TOK(store, token, sizeof(token)); - if (global.data_version < ATTRIBOWNER_VERSION) { + if (data->version < ATTRIBOWNER_VERSION) { READ_INT(store, NULL); } - READ_INT(store, &data->time); - data->type = bt_find(token); + READ_INT(store, &idata->time); + idata->type = bt_find(token); return AT_READ_OK; } @@ -255,8 +257,9 @@ int get_spell_level_mage(const spell * sp, void * cbdata) return sbe ? sbe->level : 0; } -static int read_mage(attrib * a, void *owner, struct storage *store) +static int read_mage(attrib * a, void *owner, struct gamedata *data) { + storage *store = data->store; int i, mtype; sc_mage *mage = (sc_mage *)a->data.v; char spname[64]; @@ -292,10 +295,10 @@ static int read_mage(attrib * a, void *owner, struct storage *store) } } if (mage->magietyp == M_GRAY) { - read_spellbook(&mage->spellbook, store, get_spell_level_mage, mage); + read_spellbook(&mage->spellbook, data, get_spell_level_mage, mage); } else { - read_spellbook(0, store, 0, mage); + read_spellbook(0, data, 0, mage); } return AT_READ_OK; } @@ -336,6 +339,7 @@ attrib_type at_mage = { NULL, write_mage, read_mage, + NULL, ATF_UNIQUE }; @@ -362,8 +366,9 @@ sc_mage *get_mage(const unit * u) * Spruch zu seiner List-of-known-spells hinzugefügt werden. */ -static int read_seenspell(attrib * a, void *owner, struct storage *store) +static int read_seenspell(attrib * a, void *owner, struct gamedata *data) { + storage *store = data->store; int i; spell *sp = 0; char token[32]; @@ -374,7 +379,7 @@ static int read_seenspell(attrib * a, void *owner, struct storage *store) sp = find_spellbyid((unsigned int)i); } else { - if (global.data_version < UNIQUE_SPELLS_VERSION) { + if (data->version < UNIQUE_SPELLS_VERSION) { READ_INT(store, 0); /* ignore mtype */ } sp = find_spell(token); @@ -1127,8 +1132,13 @@ double magic_resistance(unit * target) curse *c; const curse_type * ct_goodresist = 0, *ct_badresist = 0; const resource_type *rtype; - double probability = u_race(target)->magres; + const race *rc = u_race(target); + double probability = rc->magres; + const plane *pl = rplane(target->region); + if (rc == get_race(RC_HIRNTOETER) && !pl) { + probability /= 2; + } assert(target->number > 0); /* Magier haben einen Resistenzbonus vom Magietalent * 5% */ probability += effskill(target, SK_MAGIC, 0) * 0.05; @@ -1865,7 +1875,7 @@ static int addparam_building(const char *const param[], spllprm ** spobjp) static int addparam_region(const char *const param[], spllprm ** spobjp, const unit * u, -order * ord, plane * pl) +order * ord) { assert(param[0]); if (param[1] == 0) { @@ -1874,6 +1884,7 @@ order * ord, plane * pl) return -1; } else { + plane *pl = getplanebyid(0); int tx = atoi((const char *)param[0]), ty = atoi((const char *)param[1]); int x = rel_to_abs(pl, u->faction, tx, 0); int y = rel_to_abs(pl, u->faction, ty, 1); @@ -1987,9 +1998,8 @@ static spellparameter *add_spellparameter(region * target_r, unit * u, ++c; break; case 'r': - /* Parameter sind zwei Regionskoordinaten */ - /* this silly thing only works in the normal plane! */ - j = addparam_region(param + i, &spobj, u, ord, get_normalplane()); + /* Parameter sind zwei Regionskoordinaten innerhalb der "normalen" Plane */ + j = addparam_region(param + i, &spobj, u, ord); ++c; break; case 'b': @@ -2276,10 +2286,10 @@ static int resolve_familiar(variant data, void *addr) return result; } -static int read_familiar(attrib * a, void *owner, struct storage *store) +static int read_familiar(attrib * a, void *owner, struct gamedata *data) { int result = - read_reference(&a->data.v, store, read_unit_reference, resolve_familiar); + read_reference(&a->data.v, data, read_unit_reference, resolve_familiar); if (result == 0 && a->data.v == NULL) { return AT_READ_FAIL; } @@ -2360,10 +2370,10 @@ static int resolve_clone(variant data, void *addr) return result; } -static int read_clone(attrib * a, void *owner, struct storage *store) +static int read_clone(attrib * a, void *owner, struct gamedata *data) { int result = - read_reference(&a->data.v, store, read_unit_reference, resolve_clone); + read_reference(&a->data.v, data, read_unit_reference, resolve_clone); if (result == 0 && a->data.v == NULL) { return AT_READ_FAIL; } @@ -2387,10 +2397,10 @@ static int resolve_mage(variant data, void *addr) return result; } -static int read_magician(attrib * a, void *owner, struct storage *store) +static int read_magician(attrib * a, void *owner, struct gamedata *data) { int result = - read_reference(&a->data.v, store, read_unit_reference, resolve_mage); + read_reference(&a->data.v, data, read_unit_reference, resolve_mage); if (result == 0 && a->data.v == NULL) { return AT_READ_FAIL; } @@ -2412,6 +2422,7 @@ attrib_type at_familiarmage = { age_unit, a_write_unit, read_magician, + NULL, ATF_UNIQUE }; @@ -2422,6 +2433,7 @@ attrib_type at_familiar = { age_unit, a_write_unit, read_familiar, + NULL, ATF_UNIQUE }; @@ -2432,6 +2444,7 @@ attrib_type at_clonemage = { age_unit, a_write_unit, read_magician, + NULL, ATF_UNIQUE }; @@ -2442,6 +2455,7 @@ attrib_type at_clone = { age_unit, a_write_unit, read_clone, + NULL, ATF_UNIQUE }; diff --git a/src/magic.test.c b/src/magic.test.c index 6ed4d81a9..ad698625a 100644 --- a/src/magic.test.c +++ b/src/magic.test.c @@ -1,7 +1,10 @@ #include #include "magic.h" +#include "teleport.h" +#include +#include #include #include #include @@ -386,6 +389,8 @@ void test_multi_cast(CuTest *tc) { u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); u->faction->locale = lang = get_or_create_locale("de"); + locale_setstring(lang, parameters[P_ANY], "ALLE"); + init_parameters(lang); locale_setstring(lang, mkname("spell", sp->sname), "Feuerball"); CuAssertStrEquals(tc, "Feuerball", spell_name(sp, lang)); set_level(u, SK_MAGIC, 10); @@ -402,6 +407,25 @@ void test_multi_cast(CuTest *tc) { test_cleanup(); } +static void test_magic_resistance(CuTest *tc) { + unit *u; + race *rc; + + test_cleanup(); + rc = test_create_race("human"); + u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0)); + CuAssertDblEquals(tc, rc->magres, magic_resistance(u), 0.01); + rc->magres = 1.0; + CuAssertDblEquals_Msg(tc, "magic resistance is capped at 0.9", 0.9, magic_resistance(u), 0.01); + rc = test_create_race("braineater"); + rc->magres = 1.0; + u_setrace(u, rc); + CuAssertDblEquals_Msg(tc, "brain eaters outside astral space have 50% magres", 0.5, magic_resistance(u), 0.01); + u->region->_plane = get_astralplane(); + CuAssertDblEquals_Msg(tc, "brain eaters in astral space have full magres", 0.9, magic_resistance(u), 0.01); + test_cleanup(); +} + CuSuite *get_magic_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -417,5 +441,6 @@ CuSuite *get_magic_suite(void) SUITE_ADD_TEST(suite, test_set_main_combatspell); SUITE_ADD_TEST(suite, test_set_post_combatspell); SUITE_ADD_TEST(suite, test_hasspell); + SUITE_ADD_TEST(suite, test_magic_resistance); return suite; } diff --git a/src/main.c b/src/main.c index 6eac28470..f96c04d2e 100644 --- a/src/main.c +++ b/src/main.c @@ -130,7 +130,7 @@ static int get_arg(int argc, char **argv, size_t len, int index, const char **re static int parse_args(int argc, char **argv, int *exitcode) { - int i; + int i, log_stderr = 0; for (i = 1; i != argc; ++i) { char *argi = argv[i]; @@ -210,6 +210,10 @@ static int parse_args(int argc, char **argv, int *exitcode) break; } + if (log_stderr) { + log_to_file(log_stderr | LOG_FLUSH | LOG_BRIEF, stderr); + } + return 0; } @@ -284,10 +288,6 @@ int main(int argc, char **argv) int err = 0; lua_State *L; setup_signal_handler(); - /* parse args once to read config file location */ - if (parse_args(argc, argv, &err) != 0) { - return err; - } /* ini file sets defaults for arguments*/ parse_config(inifile); if (!global.inifile) { @@ -296,7 +296,7 @@ int main(int argc, char **argv) /* parse arguments again, to override ini file */ parse_args(argc, argv, &err); - log_open(logfile); + log_open(logfile, LOG_CPERROR | LOG_CPWARNING | LOG_CPDEBUG | LOG_FLUSH); locale_init(); #ifdef CRTDBG diff --git a/src/market.c b/src/market.c index 3d96c30e4..efcaf0d98 100644 --- a/src/market.c +++ b/src/market.c @@ -63,7 +63,7 @@ static void free_market(attrib * a) attrib_type at_market = { "script", NULL, free_market, NULL, - NULL, NULL, ATF_UNIQUE + NULL, NULL, NULL, ATF_UNIQUE }; static int rc_luxury_trade(const struct race *rc) diff --git a/src/modules/arena.c b/src/modules/arena.c index 6b76a3434..97488218a 100644 --- a/src/modules/arena.c +++ b/src/modules/arena.c @@ -48,6 +48,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -248,10 +249,10 @@ write_hurting(const attrib * a, const void *owner, struct storage *store) WRITE_INT(store, b->no); } -static int read_hurting(attrib * a, void *owner, struct storage *store) +static int read_hurting(attrib * a, void *owner, struct gamedata *data) { int i; - READ_INT(store, &i); + READ_INT(data->store, &i); a->data.v = (void *)findbuilding(i); if (a->data.v == NULL) { log_error("temple of pain is broken\n"); @@ -448,10 +449,10 @@ static void caldera_write(const trigger * t, struct storage *store) write_building_reference(b, store); } -static int caldera_read(trigger * t, struct storage *store) +static int caldera_read(trigger * t, struct gamedata *data) { int rb = - read_reference(&t->data.v, store, read_building_reference, + read_reference(&t->data.v, data, read_building_reference, resolve_building); if (rb == 0 && !t->data.v) { return AT_READ_FAIL; diff --git a/src/modules/gmcmd.c b/src/modules/gmcmd.c index 02bd2b889..f450cd67f 100644 --- a/src/modules/gmcmd.c +++ b/src/modules/gmcmd.c @@ -35,6 +35,8 @@ /* util includes */ #include +#include + #include /* libc includes */ @@ -43,18 +45,18 @@ #include #include -static int read_permissions(attrib * a, void *owner, struct storage *store) +static int read_permissions(attrib * a, void *owner, struct gamedata *data) { assert(!a); - a_read(store, &a, owner); + a_read(data, &a, owner); a_remove(&a, a); return AT_READ_OK; } -static int read_gmcreate(attrib * a, void *owner, struct storage *store) +static int read_gmcreate(attrib * a, void *owner, struct gamedata *data) { char zText[32]; - READ_TOK(store, zText, sizeof(zText)); + READ_TOK(data->store, zText, sizeof(zText)); return AT_READ_OK; } diff --git a/src/modules/museum.c b/src/modules/museum.c index e1e7d33e5..5ad9092a4 100644 --- a/src/modules/museum.c +++ b/src/modules/museum.c @@ -43,6 +43,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -79,11 +80,11 @@ struct storage *store) } static int -a_readmuseumgivebackcookie(attrib * a, void *owner, struct storage *store) +a_readmuseumgivebackcookie(attrib * a, void *owner, gamedata *data) { museumgivebackcookie *gbc = (museumgivebackcookie *)a->data.v; - READ_INT(store, &gbc->warden_no); - READ_INT(store, &gbc->cookie); + READ_INT(data->store, &gbc->warden_no); + READ_INT(data->store, &gbc->cookie); return AT_READ_OK; } @@ -121,11 +122,11 @@ struct storage *store) write_items(store, gb->items); } -static int a_readmuseumgiveback(attrib * a, void *owner, struct storage *store) +static int a_readmuseumgiveback(attrib * a, void *owner, struct gamedata *data) { museumgiveback *gb = (museumgiveback *)a->data.v; - READ_INT(store, &gb->cookie); - read_items(store, &gb->items); + READ_INT(data->store, &gb->cookie); + read_items(data->store, &gb->items); return AT_READ_OK; } @@ -194,7 +195,7 @@ void warden_add_give(unit * src, unit * u, const item_type * itype, int n) void create_museum(void) { -#if 0 /* TODO: move this to LUA. It should be possible. */ +#if 0 /* TODO: move this to Lua. It should be possible. */ unsigned int museum_id = hashstring("museum"); plane *museum = getplanebyid(museum_id); region *r; diff --git a/src/modules/score.c b/src/modules/score.c index ed8f4e458..74eb0898b 100644 --- a/src/modules/score.c +++ b/src/modules/score.c @@ -152,7 +152,7 @@ void score(void) allscores = 1; } - sprintf(path, "%s/score", basepath()); + join_path(basepath(), "score", path, sizeof(path)); scoreFP = fopen(path, "w"); if (scoreFP) { const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf, 0 }; @@ -177,7 +177,7 @@ void score(void) alliance *a; const item_type *token = it_find("conquesttoken"); - sprintf(path, "%s/score.alliances", basepath()); + join_path(basepath(), "score.alliances", path, sizeof(path)); scoreFP = fopen(path, "w"); if (scoreFP) { const unsigned char utf8_bom[4] = { 0xef, 0xbb, 0xbf, 0 }; diff --git a/src/modules/xmas.c b/src/modules/xmas.c index 1aa8aaba7..4b90af68e 100644 --- a/src/modules/xmas.c +++ b/src/modules/xmas.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -47,10 +48,10 @@ static void xmasgate_write(const trigger * t, struct storage *store) WRITE_TOK(store, itoa36(b->no)); } -static int xmasgate_read(trigger * t, struct storage *store) +static int xmasgate_read(trigger * t, struct gamedata *data) { int bc = - read_reference(&t->data.v, store, read_building_reference, + read_reference(&t->data.v, data, read_building_reference, resolve_building); if (bc == 0 && !t->data.v) { return AT_READ_FAIL; diff --git a/src/monsters.c b/src/monsters.c index b48cb7c82..40f42f2f1 100644 --- a/src/monsters.c +++ b/src/monsters.c @@ -26,6 +26,7 @@ #include "monster.h" #include "laws.h" #include "keyword.h" +#include "study.h" /* triggers includes */ #include @@ -677,7 +678,7 @@ static order *plan_dragon(unit * u) } } - if (move) { + if (move && (!ta || chance(0.1))) { /* dragon gets bored and looks for a different place to go */ ta = set_new_dragon_target(u, u->region, DRAGON_RANGE); } diff --git a/src/monsters.test.c b/src/monsters.test.c index 9fce059f2..c619ecf2a 100644 --- a/src/monsters.test.c +++ b/src/monsters.test.c @@ -13,6 +13,7 @@ #include "monster.h" #include "guard.h" #include "skill.h" +#include "study.h" #include diff --git a/src/move.c b/src/move.c index 04a50b45d..e602b073c 100644 --- a/src/move.c +++ b/src/move.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "move.h" #include "laws.h" #include "reports.h" +#include "study.h" #include "alchemy.h" #include "travelthru.h" #include "vortex.h" @@ -44,13 +45,13 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include -#include #include #include #include #include +#include "teleport.h" #include "direction.h" #include "calendar.h" #include "skill.h" @@ -59,6 +60,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -122,7 +124,7 @@ get_followers(unit * target, region * r, const region_list * route_end, unit *uf; for (uf = r->units; uf; uf = uf->next) { if (fval(uf, UFL_FOLLOWING) && !fval(uf, UFL_NOTMOVING)) { - const attrib *a = a_findc(uf->attribs, &at_follow); + const attrib *a = a_find(uf->attribs, &at_follow); if (a && a->data.v == target) { follower *fnew = (follower *)malloc(sizeof(follower)); fnew->uf = uf; @@ -154,8 +156,9 @@ static int shiptrail_age(attrib * a, void *owner) return (t->age > 0) ? AT_AGE_KEEP : AT_AGE_REMOVE; } -static int shiptrail_read(attrib * a, void *owner, struct storage *store) +static int shiptrail_read(attrib * a, void *owner, struct gamedata *data) { + storage *store = data->store; int n; traveldir *t = (traveldir *)(a->data.v); diff --git a/src/move.test.c b/src/move.test.c index c4e9944c9..21c717137 100644 --- a/src/move.test.c +++ b/src/move.test.c @@ -35,11 +35,10 @@ static void test_ship_not_allowed_in_coast(CuTest * tc) ship_type *stype; test_cleanup(); - test_create_world(); - ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO); otype = test_create_terrain("ocean", SEA_REGION | SAIL_INTO); stype = test_create_shiptype("derp"); + free(stype->coasts); stype->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *)); r1 = test_create_region(0, 0, ttype); @@ -50,6 +49,7 @@ static void test_ship_not_allowed_in_coast(CuTest * tc) CuAssertIntEquals(tc, SA_NO_COAST, check_ship_allowed(sh, r1)); stype->coasts[0] = ttype; CuAssertIntEquals(tc, SA_COAST, check_ship_allowed(sh, r1)); + test_cleanup(); } typedef struct move_fixture { @@ -68,7 +68,6 @@ static void setup_harbor(move_fixture *mf) { unit *u; test_cleanup(); - test_create_world(); ttype = test_create_terrain("glacier", LAND_REGION | ARCTIC_REGION | WALK_INTO | SAIL_INTO); btype = test_create_buildingtype("harbour"); diff --git a/src/piracy.test.c b/src/piracy.test.c index dee326980..419f73073 100644 --- a/src/piracy.test.c +++ b/src/piracy.test.c @@ -47,6 +47,7 @@ static void setup_pirate(unit **pirate, int p_r_flags, int p_rc_flags, const cha if (v_shiptype) { st_boat = st_get_or_create(v_shiptype); u_set_ship(*victim, test_create_ship((*victim)->region, st_boat)); + free(st_boat->coasts); st_boat->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *)); st_boat->coasts[0] = vterrain; st_boat->coasts[1] = 0; @@ -59,6 +60,7 @@ static void setup_pirate(unit **pirate, int p_r_flags, int p_rc_flags, const cha if (p_shiptype) { st_boat = st_get_or_create(p_shiptype); u_set_ship(*pirate, test_create_ship((*pirate)->region, st_boat)); + free(st_boat->coasts); st_boat->coasts = (struct terrain_type **)calloc(2, sizeof(struct terrain_type *)); st_boat->coasts[0] = vterrain; st_boat->coasts[1] = 0; diff --git a/src/randenc.c b/src/randenc.c index d948ea270..26a2e6b4a 100644 --- a/src/randenc.c +++ b/src/randenc.c @@ -25,6 +25,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "move.h" #include "alchemy.h" #include "chaos.h" +#include "study.h" /* kernel includes */ #include @@ -963,7 +964,7 @@ static void demon_skillchanges(void) if (fval(u, UFL_HUNGER)) { /* hungry demons only go down, never up in skill */ - int rule_hunger = config_get_int("hunger.demon.skill", 0) != 0; + int rule_hunger = config_get_int("hunger.demons.skill", 0) != 0; if (rule_hunger) { upchance = 0; downchance = 15; diff --git a/src/report.c b/src/report.c index 7b04e020f..15c5f1b2c 100644 --- a/src/report.c +++ b/src/report.c @@ -42,6 +42,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "upkeep.h" #include "vortex.h" #include "calendar.h" +#include "teleport.h" /* kernel includes */ #include @@ -65,7 +66,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include -#include #include #include #include @@ -1403,7 +1403,7 @@ report_template(const char *filename, report_context * ctx, const char *charset) const resource_type *rsilver = get_resourcetype(R_SILVER); faction *f = ctx->f; region *r; - FILE *F = fopen(filename, "wt"); + FILE *F = fopen(filename, "w"); stream strm = { 0 }, *out = &strm; seen_region *sr = NULL; char buf[8192], *bufp; @@ -1427,7 +1427,7 @@ report_template(const char *filename, report_context * ctx, const char *charset) newline(out); newline(out); - sprintf(buf, "%s %s \"%s\"", LOC(f->locale, "ERESSEA"), factionid(f), f->passw); + sprintf(buf, "%s %s \"password\"", LOC(f->locale, "ERESSEA"), factionid(f)); rps_nowrap(out, buf); newline(out); newline(out); @@ -2067,7 +2067,7 @@ const char *charset) unsigned char op; int maxh, bytes, ix = want(O_STATISTICS); int wants_stats = (f->options & ix); - FILE *F = fopen(filename, "wt"); + FILE *F = fopen(filename, "w"); stream strm = { 0 }, *out = &strm; seen_region *sr = NULL; char buf[8192]; @@ -2112,9 +2112,6 @@ const char *charset) if (f->age <= 2) { const char *s; - RENDER(f, buf, sizeof(buf), ("newbie_password", "password", f->passw)); - newline(out); - centre(out, buf, true); s = locale_getstring(f->locale, "newbie_info_1"); if (s) { newline(out); diff --git a/src/reports.c b/src/reports.c index 99b9f755c..379b35429 100644 --- a/src/reports.c +++ b/src/reports.c @@ -354,7 +354,7 @@ const char **illusion) bt_illusion = bt_find("illusioncastle"); if (bt_illusion && b->type == bt_illusion) { - const attrib *a = a_findc(b->attribs, &at_icastle); + const attrib *a = a_find(b->attribs, &at_icastle); if (a != NULL) { *illusion = buildingtype(icastle_type(a), b, b->size); } @@ -1385,17 +1385,9 @@ static void prepare_reports(void) for (r = regions; r; r = r->next) { unit *u; - plane *p = rplane(r); reorder_units(r); - if (p) { - watcher *w = p->watchers; - for (; w; w = w->next) { - faction_add_seen(w->faction, r, w->mode); - } - } - /* Region owner get always the Lighthouse report */ if (bt_lighthouse && config_token("rules.region_owner_pay_building", bt_lighthouse->_name)) { for (b = rbuildings(r); b; b = b->next) { @@ -1464,8 +1456,6 @@ static region *lastregion(faction * f) /* we continue from the best region and look for travelthru etc. */ for (r = f->last->next; r; r = r->next) { - plane *p = rplane(r); - /* search the region for travelthru-attributes: */ if (fval(r, RF_TRAVELUNIT)) { travelthru_map(r, cb_set_last, f); @@ -1474,9 +1464,6 @@ static region *lastregion(faction * f) continue; if (check_leuchtturm(r, f)) f->last = r; - if (p && is_watcher(p, f)) { - f->last = r; - } } return f->last->next; #else @@ -1540,16 +1527,6 @@ static void prepare_report(struct report_context *ctx, faction *f) ctx->last = lastregion(f); } -static void mkreportdir(const char *rpath) { - if (_mkdir(rpath) != 0) { - if (_access(rpath, 0) < 0) { - log_error("could not create reports directory %s: %s", rpath, strerror(errno)); - abort(); - } - } - errno = 0; -} - int write_reports(faction * f, time_t ltime) { unsigned int backup = 1, maxbackup = 128 * 1000; @@ -1557,24 +1534,24 @@ int write_reports(faction * f, time_t ltime) struct report_context ctx; const char *encoding = "UTF-8"; report_type *rtype; - const char *path = reportpath(); if (noreports) { return false; } prepare_report(&ctx, f); get_addresses(&ctx); - mkreportdir(path); // FIXME: too many mkdir calls! init_reports is enough log_debug("Reports for %s:", factionname(f)); for (rtype = report_types; rtype != NULL; rtype = rtype->next) { if (f->options & rtype->flag) { int error; do { - char filename[MAX_PATH]; - sprintf(filename, "%s/%d-%s.%s", reportpath(), turn, factionid(f), + char filename[32]; + char path[MAX_PATH]; + sprintf(filename, "%d-%s.%s", turn, factionid(f), rtype->extension); + join_path(reportpath(), filename, path, sizeof(path)); error = 0; - if (rtype->write(filename, &ctx, encoding) == 0) { + if (rtype->write(path, &ctx, encoding) == 0) { gotit = true; } if (errno) { @@ -1635,14 +1612,8 @@ static void check_messages_exist(void) { int init_reports(void) { check_messages_exist(); - + create_directories(); prepare_reports(); - { - if (_access(reportpath(), 0) != 0) { - return 0; - } - } - mkreportdir(reportpath()); return 0; } @@ -1659,8 +1630,7 @@ int reports(void) report_donations(); remove_empty_units(); - mkreportdir(rpath); // FIXME: init_reports already does this? - sprintf(path, "%s/reports.txt", rpath); + join_path(rpath, "reports.txt", path, sizeof(path)); mailit = fopen(path, "w"); if (mailit == NULL) { log_error("%s could not be opened!\n", path); @@ -1676,15 +1646,6 @@ int reports(void) if (mailit) fclose(mailit); free_seen(); -#ifdef GLOBAL_REPORT - { - const char *str = config_get("globalreport"); - if (str != NULL) { - sprintf(path, "%s/%s.%u.cr", reportpath(), str, turn); - global_report(path); - } - } -#endif return retval; } diff --git a/src/reports.test.c b/src/reports.test.c index 0955e8a6f..e00e50a8b 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -253,6 +253,7 @@ static void test_write_unit(CuTest *tc) { race *rc; struct locale *lang; char buffer[1024]; + /* FIXME: test emits ERROR: no translation for combat status status_aggressive in locale de */ test_cleanup(); rc = rc_get_or_create("human"); @@ -261,6 +262,7 @@ static void test_write_unit(CuTest *tc) { locale_setstring(lang, "nr_skills", "Talente"); locale_setstring(lang, "skill::sailing", "Segeln"); locale_setstring(lang, "skill::alchemy", "Alchemie"); + locale_setstring(lang, "status_aggressive", "aggressiv"); init_skills(lang); u = test_create_unit(test_create_faction(rc), test_create_region(0, 0, 0)); u->faction->locale = lang; @@ -270,15 +272,15 @@ static void test_write_unit(CuTest *tc) { unit_setid(u, 1); bufunit(u->faction, u, 0, 0, buffer, sizeof(buffer)); - CuAssertStrEquals(tc, "Hodor (1), 1 human, status_aggressive.", buffer); + CuAssertStrEquals(tc, "Hodor (1), 1 human, aggressiv.", buffer); set_level(u, SK_SAILING, 1); bufunit(u->faction, u, 0, 0, buffer, sizeof(buffer)); - CuAssertStrEquals(tc, "Hodor (1), 1 human, status_aggressive, Talente: Segeln 1.", buffer); + CuAssertStrEquals(tc, "Hodor (1), 1 human, aggressiv, Talente: Segeln 1.", buffer); set_level(u, SK_ALCHEMY, 1); bufunit(u->faction, u, 0, 0, buffer, sizeof(buffer)); - CuAssertStrEquals(tc, "Hodor (1), 1 human, status_aggressive, Talente: Segeln 1, Alchemie 2.", buffer); + CuAssertStrEquals(tc, "Hodor (1), 1 human, aggressiv, Talente: Segeln 1, Alchemie 2.", buffer); f = test_create_faction(0); f->locale = get_or_create_locale("de"); @@ -403,7 +405,8 @@ static void test_write_spell_syntax(CuTest *tc) { set_parameter(spell, "kc+"); check_spell_syntax(tc, "kc+", &spell, " ZAUBERE \"Testzauber\" ( REGION | EINHEIT [ ...] | SCHIFF \n [ ...] | BURG [ ...] )"); - + spellbook_clear(spell.spb); + free(spell.spb); test_cleanup(); } diff --git a/src/seen.h b/src/seen.h index de600d353..1f0197cc7 100644 --- a/src/seen.h +++ b/src/seen.h @@ -43,7 +43,7 @@ extern "C" { struct seen_region *next; struct region *r; seen_t mode; - bool disbelieves; + bool disbelieves; /* potion of truth */ } seen_region; struct seen_region **seen_init(void); diff --git a/src/spells.c b/src/spells.c index 1445598d9..ceb66d69f 100644 --- a/src/spells.c +++ b/src/spells.c @@ -22,6 +22,7 @@ #include "direction.h" #include "randenc.h" #include "monster.h" +#include "teleport.h" #include #include @@ -49,7 +50,6 @@ #include #include #include -#include #include #include #include @@ -62,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -2866,9 +2867,10 @@ static curse *mk_deathcloud(unit * mage, region * r, double force, int duration) #define COMPAT_DEATHCLOUD #ifdef COMPAT_DEATHCLOUD -static int dc_read_compat(struct attrib *a, void *target, struct storage * store) +static int dc_read_compat(struct attrib *a, void *target, gamedata *data) /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ { + struct storage *store = data->store; region *r = NULL; unit *u; variant var; @@ -3091,9 +3093,9 @@ static int sp_summonshadowlords(castorder * co) static bool chaosgate_valid(const connection * b) { - const attrib *a = a_findc(b->from->attribs, &at_direction); + const attrib *a = a_find(b->from->attribs, &at_direction); if (!a) - a = a_findc(b->to->attribs, &at_direction); + a = a_find(b->to->attribs, &at_direction); if (!a) return false; return true; @@ -3146,7 +3148,7 @@ static int sp_chaossuction(castorder * co) unit *mage = co->magician.u; int cast_level = co->level; - if (getplane(r) != get_normalplane()) { + if (rplane(r)) { /* Der Zauber funktioniert nur in der materiellen Welt. */ cmistake(mage, co->order, 190, MSG_MAGIC); return 0; @@ -5436,8 +5438,9 @@ int sp_fetchastral(castorder * co) region *rt = co_get_region(co); /* region to which we are fetching */ region *ro = NULL; /* region in which the target is */ - if (rplane(rt) != get_normalplane()) { - ADDMSG(&mage->faction->msgs, msg_feedback(mage, co->order, "error190", "")); + if (rplane(rt)) { + /* Der Zauber funktioniert nur in der materiellen Welt. */ + cmistake(mage, co->order, 190, MSG_MAGIC); return 0; } diff --git a/src/spells/alp.c b/src/spells/alp.c index d443aa063..61c60df03 100644 --- a/src/spells/alp.c +++ b/src/spells/alp.c @@ -25,6 +25,7 @@ /* util includes */ #include #include +#include #include #include @@ -76,12 +77,12 @@ alp_write(const attrib * a, const void *owner, struct storage *store) write_unit_reference(ad->target, store); } -static int alp_read(attrib * a, void *owner, struct storage *store) +static int alp_read(attrib * a, void *owner, struct gamedata *data) { alp_data *ad = (alp_data *)a->data.v; - int rm = read_reference(&ad->mage, store, read_unit_reference, resolve_unit); + int rm = read_reference(&ad->mage, data, read_unit_reference, resolve_unit); int rt = - read_reference(&ad->target, store, read_unit_reference, resolve_unit); + read_reference(&ad->target, data, read_unit_reference, resolve_unit); if (rt == 0 && rm == 0 && (!ad->target || !ad->mage)) { /* the target or mage disappeared. */ return AT_READ_FAIL; @@ -96,6 +97,7 @@ static attrib_type at_alp = { alp_verify, alp_write, alp_read, + NULL, ATF_UNIQUE }; diff --git a/src/spells/borders.c b/src/spells/borders.c index c197b93c2..2c222a9dd 100644 --- a/src/spells/borders.c +++ b/src/spells/borders.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -51,8 +52,9 @@ typedef struct bresolve { static int resolve_buddy(variant data, void *addr); -static int cw_read(attrib * a, void *target, storage * store) +static int cw_read(attrib * a, void *target, gamedata *data) { + storage *store = data->store; bresolve *br = calloc(sizeof(bresolve), 1); curse *c = (curse *)a->data.v; wallcurse *wc = (wallcurse *)c->data.v; @@ -112,6 +114,7 @@ static attrib_type at_cursewall = { curse_age, cw_write, cw_read, + NULL, ATF_CURSE }; @@ -171,15 +174,15 @@ static void wall_destroy(connection * b) free(b->data.v); } -static void wall_read(connection * b, storage * store) +static void wall_read(connection * b, gamedata * data) { static wall_data dummy; wall_data *fd = b->data.v ? (wall_data *)b->data.v : &dummy; - read_reference(&fd->mage, store, read_unit_reference, resolve_unit); - READ_INT(store, &fd->force); - if (global.data_version >= NOBORDERATTRIBS_VERSION) { - READ_INT(store, &fd->countdown); + read_reference(&fd->mage, data, read_unit_reference, resolve_unit); + READ_INT(data->store, &fd->force); + if (data->version >= NOBORDERATTRIBS_VERSION) { + READ_INT(data->store, &fd->countdown); } fd->active = true; } diff --git a/src/spells/flyingship.c b/src/spells/flyingship.c index e8574db2b..c58ec270c 100644 --- a/src/spells/flyingship.c +++ b/src/spells/flyingship.c @@ -11,6 +11,8 @@ #include #include +#include + #include #include @@ -95,11 +97,11 @@ int sp_flying_ship(castorder * co) return cast_level; } -static int flyingship_read(storage * store, curse * c, void *target) +static int flyingship_read(gamedata * data, curse * c, void *target) { ship *sh = (ship *)target; c->data.v = sh; - if (global.data_version < FOSS_VERSION) { + if (data->version < FOSS_VERSION) { sh->flags |= SF_FLYING; return 0; } diff --git a/src/spells/flyingship.test.c b/src/spells/flyingship.test.c index b3d03f01b..98e524926 100644 --- a/src/spells/flyingship.test.c +++ b/src/spells/flyingship.test.c @@ -44,19 +44,23 @@ static void test_flyingship(CuTest * tc) sh1 = test_create_ship(r, shipType1); par_data.data.sh = sh1; - test_create_castorder(&co, u, 10, 10.0, 0, &par); + test_create_castorder(&co, u, 10, 10.0, 0, &par); CuAssertTrue(tc, !flying_ship(sh1)); CuAssertIntEquals(tc, 10, sp_flying_ship(&co)); CuAssertTrue(tc, flying_ship(sh1)); + co.par = 0; + free_castorder(&co); sh2 = test_create_ship(r, shipType2); par_data.data.sh = sh2; - test_create_castorder(&co, u, 10, 10.0, 0, &par); + test_create_castorder(&co, u, 10, 10.0, 0, &par); CuAssertTrue(tc, !flying_ship(sh2)); CuAssertIntEquals(tc, 0, sp_flying_ship(&co)); CuAssertTrue(tc, !flying_ship(sh2)); + co.par = 0; + free_castorder(&co); test_cleanup(); } diff --git a/src/spells/unitcurse.c b/src/spells/unitcurse.c index 73925a55a..c29935479 100644 --- a/src/spells/unitcurse.c +++ b/src/spells/unitcurse.c @@ -25,6 +25,7 @@ #include /* util includes */ +#include #include #include #include @@ -307,10 +308,10 @@ static struct curse_type ct_oldrace = { * C_SKILL */ -static int read_skill(struct storage *store, curse * c, void *target) +static int read_skill(gamedata *data, curse * c, void *target) { int skill; - READ_INT(store, &skill); + READ_INT(data->store, &skill); c->data.i = skill; return 0; } diff --git a/src/spy.c b/src/spy.c index e25ff1e85..9b8fe4601 100644 --- a/src/spy.c +++ b/src/spy.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "laws.h" #include "move.h" #include "reports.h" +#include "study.h" /* kernel includes */ #include diff --git a/src/spy.test.c b/src/spy.test.c index 00a1767d0..b843306f1 100644 --- a/src/spy.test.c +++ b/src/spy.test.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include @@ -90,7 +91,8 @@ static void setup_sabotage(void) { test_cleanup(); lang = get_or_create_locale("de"); locale_setstring(lang, parameters[P_SHIP], "SCHIFF"); - test_create_world(); + locale_setstring(lang, parameters[P_ANY], "ALLE"); + init_parameters(lang); init_locales(); } @@ -100,7 +102,7 @@ static void test_sabotage_self(CuTest *tc) { order *ord; setup_sabotage(); - r = test_create_region(0, 0, NULL); + r = test_create_region(0, 0, 0); assert(r); u = test_create_unit(test_create_faction(NULL), r); assert(u && u->faction && u->region == r); @@ -122,7 +124,7 @@ static void test_sabotage_other_fail(CuTest *tc) { message *msg; setup_sabotage(); - r = test_create_region(0, 0, NULL); + r = test_create_region(0, 0, 0); assert(r); u = test_create_unit(test_create_faction(NULL), r); u2 = test_create_unit(test_create_faction(NULL), r); @@ -151,7 +153,7 @@ static void test_sabotage_other_success(CuTest *tc) { order *ord; setup_sabotage(); - r = test_create_region(0, 0, NULL); + r = test_create_region(0, 0, 0); assert(r); u = test_create_unit(test_create_faction(NULL), r); u2 = test_create_unit(test_create_faction(NULL), r); diff --git a/src/sqlite.c b/src/sqlite.c index 9eecb2fa8..d45ca0cf3 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -123,7 +123,7 @@ static void update_faction(sqlite3 *db, const faction *f) { "INSERT INTO faction_data (faction_id, code, name, email, lang, turn)" " VALUES (?, ?, ?, ?, ?, ?)"; sqlite3_stmt *stmt = 0; - strncpy(code, itoa36(f->no), sizeof(code)-1); + strncpy(code, itoa36(f->no), sizeof(code)); sqlite3_prepare_v2(db, sql, -1, &stmt, 0); sqlite3_bind_int(stmt, 1, f->subscription); sqlite3_bind_text(stmt, 2, code, -1, SQLITE_STATIC); diff --git a/src/stdafx.h b/src/stdafx.h deleted file mode 100644 index f6db3cac1..000000000 --- a/src/stdafx.h +++ /dev/null @@ -1 +0,0 @@ -/* empty, only used in non-msvc builds */ diff --git a/src/study.c b/src/study.c index acce1b6d6..a69dff705 100644 --- a/src/study.c +++ b/src/study.c @@ -23,7 +23,9 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "study.h" #include "move.h" +#include "monster.h" #include "alchemy.h" +#include "academy.h" #include #include @@ -47,6 +49,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include /* libc includes */ @@ -57,8 +60,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include -#define TEACHNUMBER 10 - static skill_t getskill(const struct locale *lang) { char token[128]; @@ -157,7 +158,7 @@ static void done_learning(struct attrib *a) const attrib_type at_learning = { "learning", - init_learning, done_learning, NULL, NULL, NULL, + init_learning, done_learning, NULL, NULL, NULL, NULL, ATF_UNIQUE }; @@ -206,7 +207,6 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, n = _min(n, nteaching); if (n != 0) { - const struct building_type *btype = bt_find("academy"); int index = 0; if (teach == NULL) { @@ -227,21 +227,18 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, } teach->value += n; - /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und - * Student auch in unterschiedlichen Gebaeuden stehen duerfen */ - if (active_building(teacher, btype) && active_building(student, btype)) { - int j = study_cost(student, sk); - j = _max(50, j * 2); - /* kann Einheit das zahlen? */ - if (get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j) { + if (student->building && teacher->building == student->building) { + /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und + * Student auch in unterschiedlichen Gebaeuden stehen duerfen */ + if (academy_can_teach(teacher, student, sk)) { /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ teach->value += (n / 30) * 10; /* learning erhoehen */ - /* Lehrer zusaetzlich +1 Tag pro Schueler. */ - if (academy) + /* Lehrer zusaetzlich +1 Tag pro Schueler. */ + if (academy) { *academy += n; - } /* sonst nehmen sie nicht am Unterricht teil */ + } + } } - /* Teaching ist die Anzahl Leute, denen man noch was beibringen kann. Da * hier nicht n verwendet wird, werden die Leute gezaehlt und nicht die * effektiv gelernten Tage. -> FALSCH ? (ENNO) @@ -482,9 +479,8 @@ int teach_cmd(unit * u, struct order *ord) replace_order(&u->orders, ord, new_order); free_order(new_order); /* parse_order & set_order have each increased the refcount */ } - if (academy && sk != NOSKILL) { - academy = academy / 30; /* anzahl gelehrter wochen, max. 10 */ - learn_skill(u, sk, academy / 30.0 / TEACHNUMBER); + if (academy) { + academy_teaching_bonus(u, sk, academy); } return 0; } @@ -803,3 +799,59 @@ int study_cmd(unit * u, order * ord) return 0; } + +static double produceexp_chance(void) { + return config_get_flt("study.from_use", 1.0 / 3); +} + +void produceexp_ex(struct unit *u, skill_t sk, int n, bool(*learn)(unit *, skill_t, double)) +{ + if (n != 0 && (is_monsters(u->faction) || playerrace(u_race(u)))) { + double chance = produceexp_chance(); + if (chance > 0.0F) { + learn(u, sk, (n * chance) / u->number); + } + } +} + +void produceexp(struct unit *u, skill_t sk, int n) +{ + produceexp_ex(u, sk, n, learn_skill); +} + +#ifndef NO_TESTS +static learn_fun inject_learn_fun = 0; + +void inject_learn(learn_fun fun) { + inject_learn_fun = fun; +} +#endif + +bool learn_skill(unit * u, skill_t sk, double learn_chance) +{ + skill *sv = u->skills; +#ifndef NO_TESTS + if (inject_learn_fun) { + return inject_learn_fun(u, sk, learn_chance); + } +#endif + if (learn_chance < 1.0 && rng_int() % 10000 >= learn_chance * 10000) + if (!chance(learn_chance)) + return false; + while (sv != u->skills + u->skill_size) { + assert(sv->weeks > 0); + if (sv->id == sk) { + if (sv->weeks <= 1) { + sk_set(sv, sv->level + 1); + } + else { + sv->weeks--; + } + return true; + } + ++sv; + } + sv = add_skill(u, sk); + sk_set(sv, 1); + return true; +} diff --git a/src/study.h b/src/study.h index 1feb55921..a0c638fa2 100644 --- a/src/study.h +++ b/src/study.h @@ -26,14 +26,25 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. extern "C" { #endif - extern int teach_cmd(struct unit *u, struct order *ord); - extern int study_cmd(struct unit *u, struct order *ord); + struct unit; - extern magic_t getmagicskill(const struct locale *lang); - extern bool is_migrant(struct unit *u); - extern int study_cost(struct unit *u, skill_t talent); + int teach_cmd(struct unit *u, struct order *ord); + int study_cmd(struct unit *u, struct order *ord); + magic_t getmagicskill(const struct locale *lang); + bool is_migrant(struct unit *u); + int study_cost(struct unit *u, skill_t talent); + +#ifndef NO_TESTS + typedef bool(*learn_fun)(struct unit *u, skill_t sk, double ch); + void inject_learn(learn_fun fun); +#endif + bool learn_skill(struct unit *u, skill_t sk, double chance); + + void produceexp(struct unit *u, skill_t sk, int n); + void produceexp_ex(struct unit *u, skill_t sk, int n, bool(*learn)(struct unit *, skill_t, double)); #define MAXTEACHERS 20 +#define TEACHNUMBER 10 typedef struct teaching_info { struct unit *teachers[MAXTEACHERS]; int value; @@ -41,6 +52,7 @@ extern "C" { extern const struct attrib_type at_learning; + #ifdef __cplusplus } #endif diff --git a/src/study.test.c b/src/study.test.c index 4b30ae842..bd14096bf 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -5,7 +5,11 @@ #include #include #include +#include #include +#include +#include +#include #include #include #include @@ -20,6 +24,17 @@ typedef struct { unit *teachers[2]; } study_fixture; +static void setup_locale(struct locale *lang) { + int i; + for (i = 0; i < MAXSKILLS; ++i) { + if (!locale_getstring(lang, mkname("skill", skillnames[i]))) + locale_setstring(lang, mkname("skill", skillnames[i]), skillnames[i]); + } + locale_setstring(lang, parameters[P_ANY], "ALLE"); + init_parameters(lang); + init_skills(lang); +} + static void setup_study(study_fixture *fix, skill_t sk) { struct region * r; struct faction *f; @@ -29,11 +44,10 @@ static void setup_study(study_fixture *fix, skill_t sk) { test_cleanup(); config_set("study.random_progress", "0"); test_create_world(); - r = test_create_region(0, 0, 0); + r = findregion(0, 0); f = test_create_faction(0); lang = get_or_create_locale(locale_name(f->locale)); - locale_setstring(lang, mkname("skill", skillnames[sk]), skillnames[sk]); - init_skills(lang); + setup_locale(lang); fix->u = test_create_unit(f, r); assert(fix->u); fix->u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[sk]); @@ -45,6 +59,7 @@ static void setup_study(study_fixture *fix, skill_t sk) { fix->teachers[1] = test_create_unit(f, r); assert(fix->teachers[1]); fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + test_clear_messages(f); } static void test_study_no_teacher(CuTest *tc) { @@ -78,12 +93,10 @@ static void test_study_with_teacher(CuTest *tc) { static void test_study_with_bad_teacher(CuTest *tc) { study_fixture fix; skill *sv; - message *msg; setup_study(&fix, SK_CROSSBOW); teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); - CuAssertPtrNotNull(tc, msg = test_get_last_message(fix.u->faction->msgs)); - CuAssertStrEquals(tc, "teach_asgood", test_get_messagetype(msg)); + CuAssertPtrNotNull(tc, test_find_messagetype(fix.u->faction->msgs, "teach_asgood")); study_cmd(fix.u, fix.u->thisorder); CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); @@ -91,11 +104,139 @@ static void test_study_with_bad_teacher(CuTest *tc) { test_cleanup(); } +static void test_study_bug_2194(CuTest *tc) { + unit *u, *u1, *u2; + struct locale * loc; + building * b; + + test_cleanup(); + random_source_inject_constant(0.0); + init_resources(); + loc = get_or_create_locale("de"); + setup_locale(loc); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 2); + set_level(u, SK_CROSSBOW, TEACHDIFFERENCE); + u->faction->locale = loc; + u1 = test_create_unit(u->faction, u->region); + scale_number(u1, 17); + u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); + u2 = test_create_unit(u->faction, u->region); + scale_number(u2, 3); + u2->thisorder = create_order(K_STUDY, loc, skillnames[SK_MAGIC]); + u->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u1->no), itoa36(u2->no)); + b = test_create_building(u->region, test_create_buildingtype("academy")); + b->size = 22; + u_set_building(u, b); + u_set_building(u1, b); + u_set_building(u2, b); + i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); + i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); + b->flags = BLD_WORKING; + teach_cmd(u, u->thisorder); + CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "teach_asgood")); + test_cleanup(); +} + +static CuTest *g_tc; + +static bool cb_learn_one(unit *u, skill_t sk, double chance) { + CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); + CuAssertDblEquals(g_tc, 0.5 / u->number, chance, 0.01); + return false; +} + +static bool cb_learn_two(unit *u, skill_t sk, double chance) { + CuAssertIntEquals(g_tc, SK_ALCHEMY, sk); + CuAssertDblEquals(g_tc, 2 * 0.5 / u->number, chance, 0.01); + return false; +} + +static void test_produceexp(CuTest *tc) { + unit *u; + + g_tc = tc; + test_cleanup(); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + config_set("study.from_use", "0.5"); + produceexp_ex(u, SK_ALCHEMY, 1, cb_learn_one); + produceexp_ex(u, SK_ALCHEMY, 2, cb_learn_two); + test_cleanup(); +} + +#define MAXLOG 4 +typedef struct log_entry { + unit *u; + skill_t sk; + double ch; +} log_entry; + +static log_entry log_learners[MAXLOG]; +static int log_size; + +static bool log_learn(unit *u, skill_t sk, double ch) { + if (log_size < MAXLOG) { + log_entry * entry = &log_learners[log_size++]; + entry->u = u; + entry->sk = sk; + entry->ch = ch; + } + return true; +} + +static void test_academy_building(CuTest *tc) { + unit *u, *u1, *u2; + struct locale * loc; + building * b; + message * msg; + + test_cleanup(); + mt_register(mt_new_va("teach_asgood", "unit:unit", "region:region", "command:order", "student:unit", 0)); + + random_source_inject_constant(0.0); + init_resources(); + loc = get_or_create_locale("de"); + setup_locale(loc); + u = test_create_unit(test_create_faction(0), test_create_region(0, 0, 0)); + scale_number(u, 2); + set_level(u, SK_CROSSBOW, TEACHDIFFERENCE); + u->faction->locale = loc; + u1 = test_create_unit(u->faction, u->region); + scale_number(u1, 15); + u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); + u2 = test_create_unit(u->faction, u->region); + scale_number(u2, 5); + u2->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); + set_level(u2, SK_CROSSBOW, 1); + u->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u1->no), itoa36(u2->no)); + b = test_create_building(u->region, test_create_buildingtype("academy")); + b->size = 22; + u_set_building(u, b); + u_set_building(u1, b); + u_set_building(u2, b); + i_change(&u1->items, get_resourcetype(R_SILVER)->itype, 50); + i_change(&u2->items, get_resourcetype(R_SILVER)->itype, 50); + b->flags = BLD_WORKING; + inject_learn(log_learn); + teach_cmd(u, u->thisorder); + inject_learn(0); + CuAssertPtrNotNull(tc, msg = test_find_messagetype(u->faction->msgs, "teach_asgood")); + CuAssertPtrEquals(tc, u, (unit *)(msg)->parameters[0].v); + CuAssertPtrEquals(tc, u2, (unit *)(msg)->parameters[3].v); + CuAssertPtrEquals(tc, u, log_learners[0].u); + CuAssertIntEquals(tc, SK_CROSSBOW, log_learners[0].sk); + CuAssertDblEquals(tc, 0.05, log_learners[0].ch, 0.001); + test_cleanup(); +} + CuSuite *get_study_suite(void) { CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_study_no_teacher); SUITE_ADD_TEST(suite, test_study_with_teacher); SUITE_ADD_TEST(suite, test_study_with_bad_teacher); + SUITE_ADD_TEST(suite, test_produceexp); + SUITE_ADD_TEST(suite, test_academy_building); + DISABLE_TEST(suite, test_study_bug_2194); return suite; } diff --git a/src/summary.c b/src/summary.c index 94ae0b6e7..51c549cf3 100644 --- a/src/summary.c +++ b/src/summary.c @@ -89,7 +89,7 @@ int update_nmrs(void) if (fval(f, FFL_ISNEW)) { ++newplayers; } - else if (!fval(f, FFL_NOIDLEOUT) && f->alive) { + else if (!fval(f, FFL_NOIDLEOUT)) { int nmr = turn - f->lastorders + 1; if (nmr < 0 || nmr > NMRTimeout()) { log_error("faction %s has %d NMRS\n", factionid(f), nmr); @@ -152,7 +152,7 @@ static void writeturn(void) char zText[MAX_PATH]; FILE *f; - sprintf(zText, "%s/datum", basepath()); + join_path(basepath(), "datum", zText, sizeof(zText)); f = fopen(zText, "w"); if (!f) { perror(zText); @@ -160,7 +160,7 @@ static void writeturn(void) } fputs(gamedate2(default_locale), f); fclose(f); - sprintf(zText, "%s/turn", basepath()); + join_path(basepath(), "turn", zText, sizeof(zText)); f = fopen(zText, "w"); if (!f) { perror(zText); @@ -178,10 +178,10 @@ void report_summary(summary * s, summary * o, bool full) char zText[MAX_PATH]; if (full) { - sprintf(zText, "%s/parteien.full", basepath()); + join_path(basepath(), "parteien.full", zText, sizeof(zText)); } else { - sprintf(zText, "%s/parteien", basepath()); + join_path(basepath(), "parteien", zText, sizeof(zText)); } F = fopen(zText, "w"); if (!F) { @@ -370,7 +370,7 @@ summary *make_summary(void) f->nregions = 0; f->num_total = 0; f->money = 0; - if (f->alive && f->units) { + if (f->units) { s->factions++; /* Problem mit Monsterpartei ... */ if (!is_monsters(f)) { diff --git a/src/kernel/teleport.c b/src/teleport.c similarity index 93% rename from src/kernel/teleport.c rename to src/teleport.c index 59b18ade8..5b7f71831 100644 --- a/src/kernel/teleport.c +++ b/src/teleport.c @@ -21,19 +21,19 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "teleport.h" /* kernel includes */ -#include "equipment.h" -#include "unit.h" -#include "region.h" -#include "race.h" -#include "skill.h" -#include "terrain.h" -#include "faction.h" -#include "plane.h" +#include +#include +#include +#include +#include +#include +#include /* util includes */ #include #include +#include "skill.h" #include "monster.h" /* libc includes */ @@ -96,8 +96,7 @@ region_list *astralregions(const region * r, bool(*valid) (const region *)) region *r_standard_to_astral(const region * r) { - if (rplane(r) != get_normalplane()) - return NULL; + assert(!is_astral(r)); return tpregion(r); } @@ -109,9 +108,9 @@ region *r_astral_to_standard(const region * r) assert(is_astral(r)); x = (r->x - TE_CENTER_X) * TP_DISTANCE; y = (r->y - TE_CENTER_Y) * TP_DISTANCE; - pnormalize(&x, &y, get_normalplane()); + pnormalize(&x, &y, NULL); r2 = findregion(x, y); - if (r2 == NULL || rplane(r2) != get_normalplane()) + if (r2 == NULL || rplane(r2)) return NULL; return r2; @@ -168,11 +167,6 @@ void spawn_braineaters(float chance) } } -plane *get_normalplane(void) -{ - return NULL; -} - bool is_astral(const region * r) { plane *pl = get_astralplane(); diff --git a/src/kernel/teleport.h b/src/teleport.h similarity index 97% rename from src/kernel/teleport.h rename to src/teleport.h index d0e8f86a9..cc0a3f143 100644 --- a/src/kernel/teleport.h +++ b/src/teleport.h @@ -31,7 +31,6 @@ extern "C" { extern bool inhabitable(const struct region *r); extern bool is_astral(const struct region *r); extern struct plane *get_astralplane(void); - extern struct plane *get_normalplane(void); void create_teleport_plane(void); void set_teleport_plane_regiontypes(void); diff --git a/src/test_eressea.c b/src/test_eressea.c index f3d4b71ae..658f20b9b 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -58,10 +58,6 @@ bool list = false; int RunAllTests(int argc, char *argv[]) { - int flags = log_flags; - - log_flags = LOG_FLUSH | LOG_CPERROR; - /* self-test */ ADD_SUITE(tests); ADD_SUITE(callback); @@ -79,7 +75,9 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(base36); ADD_SUITE(bsdstring); ADD_SUITE(functions); + ADD_SUITE(gamedata); ADD_SUITE(parser); + ADD_SUITE(password); ADD_SUITE(umlaut); ADD_SUITE(unicode); ADD_SUITE(strings); @@ -121,6 +119,7 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(monsters); ADD_SUITE(move); ADD_SUITE(piracy); + ADD_SUITE(key); ADD_SUITE(stealth); ADD_SUITE(otherfaction); ADD_SUITE(upkeep); @@ -145,7 +144,6 @@ int RunAllTests(int argc, char *argv[]) suites = s; } printf("\ntest summary: %d tests, %d failed\n", summary->count, summary->failCount); - log_flags = flags; fail_count = summary->failCount; CuSuiteDelete(summary); game_done(); @@ -155,7 +153,7 @@ int RunAllTests(int argc, char *argv[]) } int main(int argc, char ** argv) { - log_stderr = 0; + log_to_file(LOG_CPERROR, stderr); ++argv; --argc; if (argc > 0 && strcmp("--list", argv[0]) == 0) { diff --git a/src/tests.c b/src/tests.c index eb03820a8..f8a0c2a7f 100644 --- a/src/tests.c +++ b/src/tests.c @@ -5,6 +5,7 @@ #include "prefix.h" #include +#include #include #include #include @@ -45,14 +46,19 @@ struct race *test_create_race(const char *name) struct region *test_create_region(int x, int y, const terrain_type *terrain) { - region *r = new_region(x, y, NULL, 0); + region *r = findregion(x, y); + if (!r) { + r = new_region(x, y, findplane(x, y), 0); + } if (!terrain) { terrain_type *t = get_or_create_terrain("plain"); t->size = 1000; fset(t, LAND_REGION|CAVALRY_REGION|FOREST_REGION); terraform_region(r, t); - } else + } + else { terraform_region(r, terrain); + } rsettrees(r, 0, 0); rsettrees(r, 1, 0); rsettrees(r, 2, 0); @@ -64,6 +70,7 @@ struct region *test_create_region(int x, int y, const terrain_type *terrain) struct faction *test_create_faction(const struct race *rc) { faction *f = addfaction("nobody@eressea.de", NULL, rc ? rc : test_create_race("human"), default_locale, 0); + test_clear_messages(f); return f; } @@ -78,6 +85,7 @@ void test_cleanup(void) { int i; + free_gamedata(); free_terrains(); free_resources(); free_config(); @@ -89,7 +97,6 @@ void test_cleanup(void) free_shiptypes(); free_races(); free_spellbooks(); - free_gamedata(); free_seen(); free_prefixes(); mt_clear(); @@ -102,7 +109,7 @@ void test_cleanup(void) if (errno) { int error = errno; errno = 0; - log_error("errno: %d", error); + log_error("errno: %d (%s)", error, strerror(error)); } random_source_reset(); @@ -147,11 +154,13 @@ ship_type * test_create_shiptype(const char * name) stype->construction->skill = SK_SHIPBUILDING; } + if (stype->coasts) { + free(stype->coasts); + } stype->coasts = - (terrain_type **)malloc(sizeof(terrain_type *)*2); - stype->coasts[0] = get_or_create_terrain("plain"); + (terrain_type **)malloc(sizeof(terrain_type *) * 2); + stype->coasts[0] = test_create_terrain("plain", LAND_REGION | FOREST_REGION | WALK_INTO | CAVALRY_REGION | SAIL_INTO | FLY_INTO); stype->coasts[1] = NULL; - if (default_locale) { locale_setstring(default_locale, name, name); } @@ -254,6 +263,12 @@ void test_create_world(void) struct locale * loc; loc = get_or_create_locale("de"); + + locale_setstring(loc, parameters[P_SHIP], "SCHIFF"); + locale_setstring(loc, parameters[P_ANY], "ALLE"); + init_parameters(loc); + + locale_setstring(loc, "status_aggressive", "aggressiv"); locale_setstring(loc, keyword(K_RESERVE), "RESERVIEREN"); locale_setstring(loc, "money", "SILBER"); init_resources(); diff --git a/src/triggers/changefaction.c b/src/triggers/changefaction.c index eb212b769..13785dfb9 100644 --- a/src/triggers/changefaction.c +++ b/src/triggers/changefaction.c @@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -79,14 +80,21 @@ static void changefaction_write(const trigger * t, struct storage *store) { changefaction_data *td = (changefaction_data *)t->data.v; write_unit_reference(td->unit, store); - write_faction_reference(td->faction, store); + write_faction_reference(td->faction->_alive ? td->faction : NULL, store); } -static int changefaction_read(trigger * t, struct storage *store) +static int changefaction_read(trigger * t, gamedata *data) { + variant var; changefaction_data *td = (changefaction_data *)t->data.v; - read_reference(&td->unit, store, read_unit_reference, resolve_unit); - read_reference(&td->faction, store, read_faction_reference, resolve_faction); + + read_reference(&td->unit, data, read_unit_reference, resolve_unit); + var = read_faction_reference(data); + if (var.i == 0) { + return AT_READ_FAIL; + } + ur_add(var, &td->faction, resolve_faction); + // read_reference(&td->faction, store, read_faction_reference, resolve_faction); return AT_READ_OK; } diff --git a/src/triggers/changerace.c b/src/triggers/changerace.c index d4937cc9f..0e7fc997e 100644 --- a/src/triggers/changerace.c +++ b/src/triggers/changerace.c @@ -27,6 +27,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -86,12 +87,12 @@ static void changerace_write(const trigger * t, struct storage *store) write_race_reference(td->irace, store); } -static int changerace_read(trigger * t, struct storage *store) +static int changerace_read(trigger * t, gamedata *data) { changerace_data *td = (changerace_data *)t->data.v; - read_reference(&td->u, store, read_unit_reference, resolve_unit); - td->race = (const struct race *)read_race_reference(store).v; - td->irace = (const struct race *)read_race_reference(store).v; + read_reference(&td->u, data, read_unit_reference, resolve_unit); + td->race = (const struct race *)read_race_reference(data->store).v; + td->irace = (const struct race *)read_race_reference(data->store).v; return AT_READ_OK; } diff --git a/src/triggers/clonedied.c b/src/triggers/clonedied.c index 7a91ea514..37693fdbf 100644 --- a/src/triggers/clonedied.c +++ b/src/triggers/clonedied.c @@ -29,6 +29,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -68,10 +69,10 @@ static void clonedied_write(const trigger * t, struct storage *store) write_unit_reference(u, store); } -static int clonedied_read(trigger * t, struct storage *store) +static int clonedied_read(trigger * t, gamedata *data) { int result = - read_reference(&t->data.v, store, read_unit_reference, resolve_unit); + read_reference(&t->data.v, data, read_unit_reference, resolve_unit); if (result == 0 && t->data.v == NULL) { return AT_READ_FAIL; } diff --git a/src/triggers/createcurse.c b/src/triggers/createcurse.c index 07e8a370e..d0d65571b 100644 --- a/src/triggers/createcurse.c +++ b/src/triggers/createcurse.c @@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include #include @@ -93,30 +94,30 @@ static void createcurse_write(const trigger * t, struct storage *store) WRITE_INT(store, td->men); } -static int createcurse_read(trigger * t, struct storage *store) +static int createcurse_read(trigger * t, gamedata *data) { createcurse_data *td = (createcurse_data *)t->data.v; char zText[128]; float flt; - read_reference(&td->mage, store, read_unit_reference, resolve_unit); - read_reference(&td->target, store, read_unit_reference, resolve_unit); + read_reference(&td->mage, data, read_unit_reference, resolve_unit); + read_reference(&td->target, data, read_unit_reference, resolve_unit); - READ_TOK(store, zText, sizeof(zText)); + READ_TOK(data->store, zText, sizeof(zText)); td->type = ct_find(zText); - READ_FLT(store, &flt); + READ_FLT(data->store, &flt); td->vigour = flt; - READ_INT(store, &td->duration); - if (global.data_version < CURSEFLOAT_VERSION) { + READ_INT(data->store, &td->duration); + if (data->version < CURSEFLOAT_VERSION) { int n; - READ_INT(store, &n); + READ_INT(data->store, &n); td->effect = (float)n; } else { - READ_FLT(store, &flt); + READ_FLT(data->store, &flt); td->effect = flt; } - READ_INT(store, &td->men); + READ_INT(data->store, &td->men); return AT_READ_OK; } diff --git a/src/triggers/createunit.c b/src/triggers/createunit.c index 642e598f0..6e98901ca 100644 --- a/src/triggers/createunit.c +++ b/src/triggers/createunit.c @@ -31,6 +31,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -82,30 +83,37 @@ static int createunit_handle(trigger * t, void *data) static void createunit_write(const trigger * t, struct storage *store) { createunit_data *td = (createunit_data *)t->data.v; - write_faction_reference(td->f, store); + write_faction_reference(td->f->_alive ? td->f : NULL, store); write_region_reference(td->r, store); write_race_reference(td->race, store); WRITE_INT(store, td->number); } -static int createunit_read(trigger * t, struct storage *store) +static int createunit_read(trigger * t, gamedata *data) { createunit_data *td = (createunit_data *)t->data.v; - - int uc = - read_reference(&td->f, store, read_faction_reference, resolve_faction); - int rc = - read_reference(&td->r, store, read_region_reference, - RESOLVE_REGION(global.data_version)); - td->race = (const struct race *)read_race_reference(store).v; - - if (uc == 0 && rc == 0) { - if (!td->f || !td->r) - return AT_READ_FAIL; + variant var; + int result = AT_READ_OK; + var = read_faction_reference(data); + if (var.i > 0) { + td->f = findfaction(var.i); + if (!td->f) { + ur_add(var, &td->f, resolve_faction); + } } - READ_INT(store, &td->number); + else { + result = AT_READ_FAIL; + } + // read_reference(&td->f, store, read_faction_reference, resolve_faction); - return AT_READ_OK; + read_reference(&td->r, data, read_region_reference, + RESOLVE_REGION(data->version)); + td->race = (const struct race *)read_race_reference(data->store).v; + if (!td->race) { + result = AT_READ_FAIL; + } + READ_INT(data->store, &td->number); + return result; } trigger_type tt_createunit = { diff --git a/src/triggers/gate.c b/src/triggers/gate.c index 44d26e0ef..2a9c5693d 100644 --- a/src/triggers/gate.c +++ b/src/triggers/gate.c @@ -13,7 +13,7 @@ #include #include "gate.h" -/* kernel includes */ + /* kernel includes */ #include #include #include @@ -22,6 +22,7 @@ /* util includes */ #include #include +#include #include #include @@ -72,15 +73,15 @@ static void gate_write(const trigger * t, struct storage *store) write_region_reference(r, store); } -static int gate_read(trigger * t, struct storage *store) +static int gate_read(trigger * t, gamedata *data) { gate_data *gd = (gate_data *)t->data.v; int bc = - read_reference(&gd->gate, store, read_building_reference, resolve_building); + read_reference(&gd->gate, data, read_building_reference, resolve_building); int rc = - read_reference(&gd->target, store, read_region_reference, - RESOLVE_REGION(global.data_version)); + read_reference(&gd->target, data, read_region_reference, + RESOLVE_REGION(data->version)); if (bc == 0 && rc == 0) { if (!gd->gate || !gd->target) diff --git a/src/triggers/giveitem.c b/src/triggers/giveitem.c index 02161775b..d4a56162b 100644 --- a/src/triggers/giveitem.c +++ b/src/triggers/giveitem.c @@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -82,15 +83,15 @@ static void giveitem_write(const trigger * t, struct storage *store) WRITE_TOK(store, td->itype->rtype->_name); } -static int giveitem_read(trigger * t, struct storage *store) +static int giveitem_read(trigger * t, gamedata *data) { giveitem_data *td = (giveitem_data *)t->data.v; char zText[128]; - int result = read_reference(&td->u, store, read_unit_reference, resolve_unit); + int result = read_reference(&td->u, data, read_unit_reference, resolve_unit); - READ_INT(store, &td->number); - READ_TOK(store, zText, sizeof(zText)); + READ_INT(data->store, &td->number); + READ_TOK(data->store, zText, sizeof(zText)); td->itype = it_find(zText); assert(td->itype); diff --git a/src/triggers/killunit.c b/src/triggers/killunit.c index 866914835..23275a6af 100644 --- a/src/triggers/killunit.c +++ b/src/triggers/killunit.c @@ -27,6 +27,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -58,10 +59,10 @@ static void killunit_write(const trigger * t, struct storage *store) write_unit_reference(u, store); } -static int killunit_read(trigger * t, struct storage *store) +static int killunit_read(trigger * t, gamedata *data) { - int result = - read_reference(&t->data.v, store, read_unit_reference, resolve_unit); + int result = read_reference(&t->data.v, data, read_unit_reference, + resolve_unit); if (result == 0 && t->data.v == NULL) { return AT_READ_FAIL; } diff --git a/src/triggers/removecurse.c b/src/triggers/removecurse.c index 94819843a..d44f9dc22 100644 --- a/src/triggers/removecurse.c +++ b/src/triggers/removecurse.c @@ -28,6 +28,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include @@ -77,12 +78,16 @@ static void removecurse_write(const trigger * t, struct storage *store) WRITE_INT(store, td->curse ? td->curse->no : 0); } -static int removecurse_read(trigger * t, struct storage *store) +static variant read_curse_reference(struct gamedata *data) { + return read_int(data->store); +} + +static int removecurse_read(trigger * t, gamedata *data) { removecurse_data *td = (removecurse_data *)t->data.v; - read_reference(&td->target, store, read_unit_reference, resolve_unit); - read_reference(&td->curse, store, read_int, resolve_curse); + read_reference(&td->target, data, read_unit_reference, resolve_unit); + read_reference(&td->curse, data, read_curse_reference, resolve_curse); return AT_READ_OK; } diff --git a/src/triggers/shock.c b/src/triggers/shock.c index 95e7b61b6..75904e346 100644 --- a/src/triggers/shock.c +++ b/src/triggers/shock.c @@ -33,6 +33,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include #include #include @@ -125,10 +126,10 @@ static void shock_write(const trigger * t, struct storage *store) } } -static int shock_read(trigger * t, struct storage *store) +static int shock_read(trigger * t, gamedata *data) { int result = - read_reference(&t->data.v, store, read_unit_reference, resolve_unit); + read_reference(&t->data.v, data, read_unit_reference, resolve_unit); if (result == 0 && t->data.v == NULL) { return AT_READ_FAIL; } diff --git a/src/triggers/shock.test.c b/src/triggers/shock.test.c index eae2a5004..77bc192dc 100644 --- a/src/triggers/shock.test.c +++ b/src/triggers/shock.test.c @@ -24,6 +24,8 @@ static void test_shock(CuTest *tc) { CuAssertIntEquals(tc, 2, u->hp); CuAssertIntEquals(tc, 2, get_spellpoints(u)); CuAssertPtrNotNull(tc, test_find_messagetype(u->faction->msgs, "shock")); + t_free(tt); + free(tt); test_cleanup(); } @@ -41,6 +43,8 @@ static void test_shock_low(CuTest *tc) { tt->type->handle(tt, u); CuAssertIntEquals(tc, 1, u->hp); CuAssertIntEquals(tc, 1, get_spellpoints(u)); + t_free(tt); + free(tt); test_cleanup(); } diff --git a/src/triggers/timeout.c b/src/triggers/timeout.c index 8f1778f21..06fa18363 100644 --- a/src/triggers/timeout.c +++ b/src/triggers/timeout.c @@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include #include +#include #include #include @@ -72,11 +73,11 @@ static void timeout_write(const trigger * t, struct storage *store) write_triggers(store, td->triggers); } -static int timeout_read(trigger * t, struct storage *store) +static int timeout_read(trigger * t, gamedata *data) { timeout_data *td = (timeout_data *)t->data.v; - READ_INT(store, &td->timer); - read_triggers(store, &td->triggers); + READ_INT(data->store, &td->timer); + read_triggers(data, &td->triggers); if (td->timer > 20) { trigger *tr = td->triggers; log_warning("there is a timeout lasting for another %d turns\n", td->timer); diff --git a/src/triggers/unguard.c b/src/triggers/unguard.c index 49cb6fe25..e6471f977 100644 --- a/src/triggers/unguard.c +++ b/src/triggers/unguard.c @@ -22,6 +22,7 @@ /* util includes */ #include #include +#include #include #include @@ -48,10 +49,9 @@ static void unguard_write(const trigger * t, struct storage *store) write_building_reference((building *)t->data.v, store); } -static int unguard_read(trigger * t, struct storage *store) +static int unguard_read(trigger * t, gamedata *data) { - int rb = - read_reference(&t->data.v, store, read_building_reference, + int rb = read_reference(&t->data.v, data, read_building_reference, resolve_building); if (rb == 0 && !t->data.v) { return AT_READ_FAIL; diff --git a/src/triggers/unitmessage.c b/src/triggers/unitmessage.c index df227ed24..2d869fc49 100644 --- a/src/triggers/unitmessage.c +++ b/src/triggers/unitmessage.c @@ -20,6 +20,7 @@ without prior permission by the authors of Eressea. #include #include #include +#include #include #include #include @@ -84,17 +85,17 @@ static void unitmessage_write(const trigger * t, struct storage *store) WRITE_INT(store, td->level); } -static int unitmessage_read(trigger * t, struct storage *store) +static int unitmessage_read(trigger * t, gamedata *data) { unitmessage_data *td = (unitmessage_data *)t->data.v; char zText[256]; - int result = - read_reference(&td->target, store, read_unit_reference, resolve_unit); - READ_TOK(store, zText, sizeof(zText)); + int result = read_reference(&td->target, data, read_unit_reference, + resolve_unit); + READ_TOK(data->store, zText, sizeof(zText)); td->string = _strdup(zText); - READ_INT(store, &td->type); - READ_INT(store, &td->level); + READ_INT(data->store, &td->type); + READ_INT(data->store, &td->level); if (result == 0 && td->target == NULL) { return AT_READ_FAIL; diff --git a/src/upkeep.c b/src/upkeep.c index 193c68480..5d114c74e 100644 --- a/src/upkeep.c +++ b/src/upkeep.c @@ -271,7 +271,7 @@ void get_food(region * r) peasantfood = 0; } if (hungry > 0) { - bool demon_hunger = config_get_int("hunger.demons", 0) != 0; + bool demon_hunger = config_get_int("hunger.demons.peasant_tolerance", 0) == 0; if (demon_hunger) { /* demons who don't feed are hungry */ if (hunger(hungry, u)) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 249b1c61c..6fc0b5dea 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -2,7 +2,9 @@ project(util C) SET(_TEST_FILES base36.test.c +gamedata.test.c parser.test.c +password.test.c attrib.test.c strings.test.c bsdstring.test.c @@ -21,13 +23,16 @@ dice.c event.c filereader.c functions.c +gamedata.c goodies.c +gamedata.c language.c lists.c log.c message.c nrmessage.c parser.c +password.c rand.c resolve.c strings.c diff --git a/src/util/attrib.c b/src/util/attrib.c index 9db46836a..841bf9559 100644 --- a/src/util/attrib.c +++ b/src/util/attrib.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "log.h" #include "storage.h" +#include #include #include @@ -101,13 +102,6 @@ attrib *a_find(attrib * a, const attrib_type * at) return a; } -const attrib *a_findc(const attrib * a, const attrib_type * at) -{ - while (a && a->type != at) - a = a->nexttype; - return a; -} - static attrib *a_insert(attrib * head, attrib * a) { attrib **pa; @@ -216,28 +210,37 @@ int a_remove(attrib ** pa, attrib * a) void a_removeall(attrib ** pa, const attrib_type * at) { attrib **pnexttype = pa; - attrib **pnext = NULL; - while (*pnexttype) { - attrib *next = *pnexttype; - if (next->type == at) - break; - pnexttype = &next->nexttype; - pnext = &next->next; - } - if (*pnexttype && (*pnexttype)->type == at) { - attrib *a = *pnexttype; - - *pnexttype = a->nexttype; - if (pnext) { - while (*pnext && (*pnext)->type != at) - pnext = &(*pnext)->next; - *pnext = a->nexttype; + if (!at) { + while (*pnexttype) { + attrib *a = *pnexttype; + *pnexttype = a->next; + a_free(a); } - while (a && a->type == at) { - attrib *ra = a; - a = a->next; - a_free(ra); + } + else { + attrib **pnext = NULL; + while (*pnexttype) { + attrib *a = *pnexttype; + if (a->type == at) + break; + pnexttype = &a->nexttype; + pnext = &a->next; + } + if (*pnexttype && (*pnexttype)->type == at) { + attrib *a = *pnexttype; + + *pnexttype = a->nexttype; + if (pnext) { + while (*pnext && (*pnext)->type != at) + pnext = &(*pnext)->next; + *pnext = a->nexttype; + } + while (a && a->type == at) { + attrib *ra = a; + a = a->next; + a_free(ra); + } } } } @@ -274,69 +277,109 @@ int a_age(attrib ** p, void *owner) static critbit_tree cb_deprecated = { 0 }; -void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct storage *)) + +typedef struct deprecated_s { + unsigned int hash; + int(*reader)(attrib *, void *, struct gamedata *); +} deprecated_t; + +void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct gamedata *)) { - char buffer[64]; - size_t len = strlen(name); - len = cb_new_kv(name, len, &reader, sizeof(reader), buffer); - cb_insert(&cb_deprecated, buffer, len); + deprecated_t value; + + value.hash = __at_hashkey(name); + value.reader = reader; + cb_insert(&cb_deprecated, &value, sizeof(value)); } -int a_read(struct storage *store, attrib ** attribs, void *owner) +static int a_read_i(gamedata *data, attrib ** attribs, void *owner, unsigned int key) { + int retval = AT_READ_OK; + int(*reader)(attrib *, void *, struct gamedata *) = 0; + attrib_type *at = at_find(key); + attrib * na = 0; + + if (at) { + reader = at->read; + na = a_new(at); + } + else { + void *match; + if (cb_find_prefix(&cb_deprecated, &key, sizeof(key), &match, 1, 0)>0) { + deprecated_t *value = (deprecated_t *)match; + reader = value->reader; + } + else { + log_error("unknown attribute hash: %u\n", key); + assert(at || !"attribute not registered"); + } + } + if (reader) { + int ret = reader(na, owner, data); + if (na) { + switch (ret) { + case AT_READ_DEPR: + case AT_READ_OK: + a_add(attribs, na); + retval = ret; + break; + case AT_READ_FAIL: + a_free(na); + break; + default: + assert(!"invalid return value"); + break; + } + } + } + else { + assert(!"error: no registered callback can read attribute"); + } + return retval; +} + +int a_read(gamedata *data, attrib ** attribs, void *owner) { + struct storage *store = data->store; + int key, retval = AT_READ_OK; + + key = -1; + READ_INT(store, &key); + while (key > 0) { + int ret = a_read_i(data, attribs, owner, key); + if (ret == AT_READ_DEPR) { + retval = AT_READ_DEPR; + } + READ_INT(store, &key); + } + if (retval == AT_READ_DEPR) { + /* handle deprecated attributes */ + attrib *a = *attribs; + while (a) { + if (a->type->upgrade) { + a->type->upgrade(attribs, a); + } + a = a->nexttype; + } + } + return AT_READ_OK; +} + +int a_read_orig(gamedata *data, attrib ** attribs, void *owner) { int key, retval = AT_READ_OK; char zText[128]; zText[0] = 0; key = -1; - READ_TOK(store, zText, sizeof(zText)); - if (strcmp(zText, "end") == 0) + READ_TOK(data->store, zText, sizeof(zText)); + if (strcmp(zText, "end") == 0) { return retval; - else + } + else { key = __at_hashkey(zText); - - while (key != -1) { - int(*reader)(attrib *, void *, struct storage *) = 0; - attrib_type *at = at_find(key); - attrib * na = 0; - - if (at) { - reader = at->read; - na = a_new(at); - } - else { - void * kv = 0; - cb_find_prefix(&cb_deprecated, zText, strlen(zText) + 1, &kv, 1, 0); - if (kv) { - cb_get_kv(kv, &reader, sizeof(reader)); - } - else { - fprintf(stderr, "attribute hash: %d (%s)\n", key, zText); - assert(at || !"attribute not registered"); - } - } - if (reader) { - int i = reader(na, owner, store); - if (na) { - switch (i) { - case AT_READ_OK: - a_add(attribs, na); - break; - case AT_READ_FAIL: - retval = AT_READ_FAIL; - a_free(na); - break; - default: - assert(!"invalid return value"); - break; - } - } - } - else { - assert(!"error: no registered callback can read attribute"); - } - - READ_TOK(store, zText, sizeof(zText)); + } + while (key > 0) { + retval = a_read_i(data, attribs, owner, key); + READ_TOK(data->store, zText, sizeof(zText)); if (!strcmp(zText, "end")) break; key = __at_hashkey(zText); @@ -344,7 +387,24 @@ int a_read(struct storage *store, attrib ** attribs, void *owner) return retval; } -void a_write(struct storage *store, const attrib * attribs, const void *owner) +void a_write(struct storage *store, const attrib * attribs, const void *owner) { + const attrib *na = attribs; + + while (na) { + if (na->type->write) { + assert(na->type->hashkey || !"attribute not registered"); + WRITE_INT(store, na->type->hashkey); + na->type->write(na, owner, store); + na = na->next; + } + else { + na = na->nexttype; + } + } + WRITE_INT(store, 0); +} + +void a_write_orig(struct storage *store, const attrib * attribs, const void *owner) { const attrib *na = attribs; diff --git a/src/util/attrib.h b/src/util/attrib.h index 2703de0cb..b41ac2bcf 100644 --- a/src/util/attrib.h +++ b/src/util/attrib.h @@ -55,7 +55,8 @@ extern "C" { int(*age) (struct attrib *, void *owner); /* age returns 0 if the attribute needs to be removed, !=0 otherwise */ void(*write) (const struct attrib *, const void *owner, struct storage *); - int(*read) (struct attrib *, void *owner, struct storage *); /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ + int(*read) (struct attrib *, void *owner, struct gamedata *); /* return AT_READ_OK on success, AT_READ_FAIL if attrib needs removal */ + void(*upgrade) (struct attrib **alist, struct attrib *a); unsigned int flags; /* ---- internal data, do not modify: ---- */ struct attrib_type *nexthash; @@ -63,21 +64,22 @@ extern "C" { } attrib_type; extern void at_register(attrib_type * at); - extern void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct storage *)); + extern void at_deprecate(const char * name, int(*reader)(attrib *, void *, struct gamedata *)); extern attrib *a_select(attrib * a, const void *data, bool(*compare) (const attrib *, const void *)); extern attrib *a_find(attrib * a, const attrib_type * at); - extern const attrib *a_findc(const attrib * a, const attrib_type * at); extern attrib *a_add(attrib ** pa, attrib * at); extern int a_remove(attrib ** pa, attrib * at); extern void a_removeall(attrib ** a, const attrib_type * at); extern attrib *a_new(const attrib_type * at); + int a_age(attrib ** attribs, void *owner); - extern int a_age(attrib ** attribs, void *owner); - extern int a_read(struct storage *store, attrib ** attribs, void *owner); - extern void a_write(struct storage *store, const attrib * attribs, - const void *owner); + int a_read_orig(struct gamedata *data, attrib ** attribs, void *owner); + void a_write_orig(struct storage *store, const attrib * attribs, const void *owner); + + int a_read(struct gamedata *data, attrib ** attribs, void *owner); + void a_write(struct storage *store, const attrib * attribs, const void *owner); void free_attribs(void); @@ -89,6 +91,7 @@ extern "C" { #define AT_READ_OK 0 #define AT_READ_FAIL -1 +#define AT_READ_DEPR 1 /* a deprecated attribute was read, should run a_upgrade */ #define AT_AGE_REMOVE 0 /* remove the attribute after calling age() */ #define AT_AGE_KEEP 1 /* keep the attribute for another turn */ diff --git a/src/util/attrib.test.c b/src/util/attrib.test.c index 691087477..0f189015a 100644 --- a/src/util/attrib.test.c +++ b/src/util/attrib.test.c @@ -49,6 +49,24 @@ static void test_attrib_remove_self(CuTest * tc) { CuAssertPtrEquals(tc, 0, alist->nexttype); CuAssertIntEquals(tc, 1, a_remove(&alist, alist)); CuAssertPtrEquals(tc, a, alist); + a_removeall(&alist, NULL); +} + + +static void test_attrib_removeall(CuTest * tc) { + const attrib_type at_foo = { "foo" }; + const attrib_type at_bar = { "bar" }; + attrib *alist = 0, *a; + a_add(&alist, a_new(&at_foo)); + a = a_add(&alist, a_new(&at_bar)); + a_add(&alist, a_new(&at_foo)); + a_removeall(&alist, &at_foo); + CuAssertPtrEquals(tc, a, alist); + CuAssertPtrEquals(tc, 0, alist->next); + a_add(&alist, a_new(&at_bar)); + a_add(&alist, a_new(&at_foo)); + a_removeall(&alist, NULL); + CuAssertPtrEquals(tc, 0, alist); } static void test_attrib_remove(CuTest * tc) @@ -98,6 +116,7 @@ CuSuite *get_attrib_suite(void) SUITE_ADD_TEST(suite, test_attrib_new); SUITE_ADD_TEST(suite, test_attrib_add); SUITE_ADD_TEST(suite, test_attrib_remove); + SUITE_ADD_TEST(suite, test_attrib_removeall); SUITE_ADD_TEST(suite, test_attrib_remove_self); SUITE_ADD_TEST(suite, test_attrib_nexttype); return suite; diff --git a/src/util/event.c b/src/util/event.c index 5784a116a..ad5a9e725 100644 --- a/src/util/event.c +++ b/src/util/event.c @@ -21,6 +21,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* util includes */ #include "attrib.h" +#include "gamedata.h" #include "log.h" #include "storage.h" @@ -42,26 +43,27 @@ void write_triggers(struct storage *store, const trigger * t) WRITE_TOK(store, "end"); } -int read_triggers(struct storage *store, trigger ** tp) +int read_triggers(struct gamedata *data, trigger ** tp) { for (;;) { trigger_type *ttype; char zText[128]; - READ_TOK(store, zText, sizeof(zText)); + READ_TOK(data->store, zText, sizeof(zText)); if (!strcmp(zText, "end")) break; ttype = tt_find(zText); assert(ttype || !"unknown trigger-type"); *tp = t_new(ttype); if (ttype->read) { - int i = ttype->read(*tp, store); + int i = ttype->read(*tp, data); switch (i) { case AT_READ_OK: tp = &(*tp)->next; break; case AT_READ_FAIL: t_free(*tp); + free(*tp); *tp = NULL; break; default: @@ -106,6 +108,7 @@ int handle_triggers(trigger ** triggers, void *param) if (t->type->handle(t, param) != 0) { *tp = t->next; t_free(t); + free(t); } else tp = &t->next; @@ -143,14 +146,15 @@ write_handler(const attrib * a, const void *owner, struct storage *store) write_triggers(store, hi->triggers); } -static int read_handler(attrib * a, void *owner, struct storage *store) +static int read_handler(attrib * a, void *owner, gamedata *data) { + struct storage *store = data->store; char zText[128]; handler_info *hi = (handler_info *)a->data.v; READ_TOK(store, zText, sizeof(zText)); hi->event = _strdup(zText); - read_triggers(store, &hi->triggers); + read_triggers(data, &hi->triggers); if (hi->triggers != NULL) { return AT_READ_OK; } @@ -258,6 +262,7 @@ const trigger_type * tt) if (t->type == tt) { *tp = t->next; t_free(t); + free(t); } else tp = &t->next; diff --git a/src/util/event.h b/src/util/event.h index 2fb5f4806..ad4ca373d 100644 --- a/src/util/event.h +++ b/src/util/event.h @@ -27,6 +27,7 @@ extern "C" { struct attrib; struct trigger; struct storage; + struct gamedata; typedef struct trigger_type { const char *name; @@ -34,7 +35,7 @@ extern "C" { void(*finalize) (struct trigger *); int(*handle) (struct trigger *, void *); void(*write) (const struct trigger *, struct storage * store); - int(*read) (struct trigger *, struct storage * store); + int(*read) (struct trigger *, struct gamedata * store); struct trigger_type *next; } trigger_type; @@ -73,7 +74,7 @@ extern "C" { /* functions for making complex triggers: */ void free_triggers(trigger * triggers); /* release all these triggers */ void write_triggers(struct storage *store, const trigger * t); - int read_triggers(struct storage *store, trigger ** tp); + int read_triggers(struct gamedata *data, trigger ** tp); int handle_triggers(trigger ** triggers, void *data); extern struct attrib_type at_eventhandler; diff --git a/src/util/gamedata.c b/src/util/gamedata.c new file mode 100644 index 000000000..cd3fa01a7 --- /dev/null +++ b/src/util/gamedata.c @@ -0,0 +1,73 @@ +#include + +#include "gamedata.h" +#include "log.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +void gamedata_done(gamedata *data) { + binstore_done(data->store); +} + +void gamedata_init(gamedata *data, storage *store, int version) { + data->version = version; + data->store = store; + binstore_init(data->store, &data->strm); +} + +int gamedata_openfile(gamedata *data, const char *filename, const char *mode, int version) { + FILE *F = fopen(filename, mode); + if (F) { + int err = 0; + + if (strchr(mode, 'r')) { + size_t sz; + sz = fread(&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; + fwrite(&version, sizeof(int), 1, F); + fwrite(&n, sizeof(int), 1, F); + } + if (err) { + log_error("could not open %s: %s", filename, strerror(errno)); + fclose(F); + } + else { + storage *store = malloc(sizeof(storage)); + fstream_init(&data->strm, F); + gamedata_init(data, store, version); + } + return err; + } + return errno; +} + +gamedata *gamedata_open(const char *filename, const char *mode, int version) { + gamedata *data = (gamedata *)calloc(1, sizeof(gamedata)); + if (gamedata_openfile(data, filename, mode, version) != 0) { + free(data); + return NULL; + } + return data; +} + +void gamedata_close(gamedata *data) { + gamedata_done(data); + fstream_done(&data->strm); + free(data->store); +} diff --git a/src/util/gamedata.h b/src/util/gamedata.h new file mode 100644 index 000000000..e75f7d681 --- /dev/null +++ b/src/util/gamedata.h @@ -0,0 +1,25 @@ +#pragma once + +#ifndef _GAMEDATA_H +#define _GAMEDATA_H + +#include + +struct storage; + +typedef struct gamedata { + struct storage *store; + stream strm; + int version; +} gamedata; + +void gamedata_init(gamedata *data, struct storage *store, int version); +void gamedata_done(gamedata *data); + +void gamedata_close(gamedata *data); +gamedata *gamedata_open(const char *filename, const char *mode, int version); +int gamedata_openfile(gamedata *data, const char *filename, const char *mode, int version); + +#define STREAM_VERSION 2 /* internal encoding of binary files */ + +#endif diff --git a/src/util/gamedata.test.c b/src/util/gamedata.test.c new file mode 100644 index 000000000..c58a8ddd0 --- /dev/null +++ b/src/util/gamedata.test.c @@ -0,0 +1,27 @@ +#include +#include "gamedata.h" + +#include +#include +#include + +static void test_gamedata(CuTest * tc) +{ + gamedata *data; + data = gamedata_open("test.dat", "wb", 0); + CuAssertPtrNotNull(tc, data); + gamedata_close(data); + free(data); + data = gamedata_open("test.dat", "rb", 0); + CuAssertPtrNotNull(tc, data); + gamedata_close(data); + free(data); + CuAssertIntEquals(tc, 0, remove("test.dat")); +} + +CuSuite *get_gamedata_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_gamedata); + return suite; +} diff --git a/src/util/log.c b/src/util/log.c index 3b7ec50a0..4afee68b4 100644 --- a/src/util/log.c +++ b/src/util/log.c @@ -24,29 +24,34 @@ without prior permission by the authors of Eressea. /* TODO: set from external function */ int log_flags = LOG_FLUSH | LOG_CPERROR | LOG_CPWARNING | LOG_CPDEBUG; -int log_stderr = LOG_FLUSH | LOG_CPERROR | LOG_CPWARNING; #ifdef STDIO_CP static int stdio_codepage = STDIO_CP; #else static int stdio_codepage = 0; #endif -static FILE *logfile; + +typedef struct log_t { + void(*log)(void *data, int level, const char *module, const char *format, va_list args); + void *data; + int flags; + struct log_t *next; +} log_t; + +static log_t *loggers; + +log_t *log_create(int flags, void *data, log_fun call) { + log_t *lgr = malloc(sizeof(log_t)); + lgr->log = call; + lgr->flags = flags; + lgr->data = data; + lgr->next = loggers; + loggers = lgr; + return lgr; +} #define MAXLENGTH 4096 /* because I am lazy, CP437 output is limited to this many chars */ #define LOG_MAXBACKUPS 5 -void log_flush(void) -{ - if (logfile) fflush(logfile); -} - -void log_puts(const char *str) -{ - fflush(stdout); - if (logfile) { - fputs(str, logfile); - } -} static int cp_convert(const char *format, char *buffer, size_t length, int codepage) @@ -100,27 +105,146 @@ void log_rotate(const char *filename, int maxindex) } } -void log_open(const char *filename) +static const char *log_prefix(int level) { + const char * prefix = "ERROR"; + if (level == LOG_CPWARNING) prefix = "WARNING"; + else if (level == LOG_CPDEBUG) prefix = "DEBUG"; + else if (level == LOG_CPINFO) prefix = "INFO"; + return prefix; +} + +static int check_dupe(const char *format, int type) { - if (logfile) { - log_close(); + static int last_type; /* STATIC_XCALL: used across calls */ + static char last_message[32]; /* STATIC_XCALL: used across calls */ + static int dupes = 0; /* STATIC_XCALL: used across calls */ + if (strncmp(last_message, format, sizeof(last_message)) == 0) { + ++dupes; + return 1; } - log_rotate(filename, LOG_MAXBACKUPS); - logfile = fopen(filename, "a"); - if (logfile) { - /* Get UNIX-style time and display as number and string. */ - time_t ltime; - time(<ime); - fprintf(logfile, "===\n=== Logfile started at %s===\n", ctime(<ime)); + if (dupes) { + fprintf(stderr, "%s: last message repeated %d times\n", log_prefix(last_type), + dupes + 1); + dupes = 0; + } + strlcpy(last_message, format, sizeof(last_message)); + last_type = type; + return 0; +} + +static void _log_write(FILE * stream, int codepage, const char *format, va_list args) +{ + if (codepage) { + char buffer[MAXLENGTH]; + char converted[MAXLENGTH]; + + vsnprintf(buffer, sizeof(buffer), format, args); + if (cp_convert(buffer, converted, MAXLENGTH, codepage) == 0) { + fputs(converted, stream); + } + else { + /* fall back to non-converted output */ + vfprintf(stream, format, args); + } + } + else { + vfprintf(stream, format, args); } } +static void log_stdio(void *data, int level, const char *module, const char *format, va_list args) { + FILE *out = (FILE *)data; + int codepage = (out == stderr || out == stdout) ? stdio_codepage : 0; + const char *prefix = log_prefix(level); + size_t len = strlen(format); + + fprintf(out, "%s: ", prefix); + + _log_write(out, codepage, format, args); + if (format[len - 1] != '\n') { + fputc('\n', out); + } +} + +log_t *log_to_file(int flags, FILE *out) { + return log_create(flags, out, log_stdio); +} + +static void log_write(int flags, const char *module, const char *format, va_list args) { + log_t *lg; + for (lg = loggers; lg; lg = lg->next) { + int level = flags & LOG_LEVELS; + if (lg->flags & level) { + int dupe = 0; + if (lg->flags & LOG_BRIEF) { + dupe = check_dupe(format, level); + } + if (dupe == 0) { + lg->log(lg->data, level, NULL, format, args); + } + } + } +} + + +void log_fatal(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_write(LOG_CPERROR, NULL, format, args); + va_end(args); +} + +void log_error(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_write(LOG_CPERROR, NULL, format, args); + va_end(args); +} + +void log_warning(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_write(LOG_CPWARNING, NULL, format, args); + va_end(args); +} + +void log_debug(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_write(LOG_CPDEBUG, NULL, format, args); + va_end(args); +} + +void log_info(const char *format, ...) +{ + va_list args; + va_start(args, format); + log_write(LOG_CPINFO, NULL, format, args); + va_end(args); +} + +void log_printf(FILE * io, const char *format, ...) +{ + va_list args; + va_start(args, format); + log_write(LOG_CPINFO, NULL, format, args); + va_end(args); +} + +static FILE *logfile; + void log_close(void) { - if (!logfile || logfile == stderr || logfile == stdout) - return; + while (loggers) { + log_t *lgr = loggers; + loggers = lgr->next; + free(lgr); + } if (logfile) { - /* Get UNIX-style time and display as number and string. */ time_t ltime; time(<ime); fprintf(logfile, "===\n=== Logfile closed at %s===\n\n", ctime(<ime)); @@ -129,223 +253,23 @@ void log_close(void) logfile = 0; } -static int check_dupe(const char *format, const char *type) +log_t *log_open(const char *filename, int log_flags) { - static const char *last_type; /* STATIC_XCALL: used across calls */ - static char last_message[32]; /* STATIC_XCALL: used across calls */ - static int dupes = 0; /* STATIC_XCALL: used across calls */ - if (strncmp(last_message, format, sizeof(last_message)) == 0) { - ++dupes; - return 1; + log_rotate(filename, LOG_MAXBACKUPS); + logfile = fopen(filename, "a"); + if (logfile) { + /* Get UNIX-style time and display as number and string. */ + time_t ltime; + time(<ime); + fprintf(logfile, "===\n=== Logfile started at %s===\n", ctime(<ime)); + return log_create(log_flags, logfile, log_stdio); } - if (dupes) { - if (log_flags & LOG_CPERROR) { - fprintf(stderr, "%s: last message repeated %d times\n", last_type, - dupes + 1); - } - dupes = 0; - } - strlcpy(last_message, format, sizeof(last_message)); - last_type = type; return 0; } -static void _log_write(FILE * stream, int codepage, const char * prefix, const char *format, va_list args) +int log_level(log_t * log, int flags) { - if (stream) { - fprintf(stream, "%s: ", prefix); - if (codepage) { - char buffer[MAXLENGTH]; - char converted[MAXLENGTH]; - - vsnprintf(buffer, sizeof(buffer), format, args); - if (cp_convert(buffer, converted, MAXLENGTH, codepage) == 0) { - fputs(converted, stream); - } - else { - /* fall back to non-converted output */ - vfprintf(stream, format, args); - } - } - else { - vfprintf(stream, format, args); - } - } -} - -static void _log_writeln(FILE * stream, int codepage, const char * prefix, const char *format, va_list args) -{ - size_t len = strlen(format); - _log_write(stream, codepage, prefix, format, args); - if (format[len - 1] != '\n') { - fputc('\n', stream); - } -} -void log_debug(const char *format, ...) -{ - const char * prefix = "DEBUG"; - const int mask = LOG_CPDEBUG; - - /* write to the logfile, always */ - if (logfile && (log_flags & mask)) { - va_list args; - va_start(args, format); - _log_writeln(logfile, 0, prefix, format, args); - va_end(args); - } - - /* write to stderr, if that's not the logfile already */ - if (logfile != stderr && (log_stderr & mask)) { - int dupe = check_dupe(format, prefix); - if (!dupe) { - va_list args; - va_start(args, format); - _log_writeln(stderr, stdio_codepage, prefix, format, args); - va_end(args); - } - } - if (log_flags & LOG_FLUSH) { - log_flush(); - } -} - -void log_warning(const char *format, ...) -{ - const char * prefix = "WARNING"; - const int mask = LOG_CPWARNING; - - /* write to the logfile, always */ - if (logfile && (log_flags & mask)) { - va_list args; - va_start(args, format); - _log_writeln(logfile, 0, prefix, format, args); - va_end(args); - } - - /* write to stderr, if that's not the logfile already */ - if (logfile != stderr && (log_stderr & mask)) { - int dupe = check_dupe(format, prefix); - if (!dupe) { - va_list args; - va_start(args, format); - _log_writeln(stderr, stdio_codepage, prefix, format, args); - va_end(args); - } - } - if (log_flags & LOG_FLUSH) { - log_flush(); - } -} - -void log_error(const char *format, ...) -{ - const char * prefix = "ERROR"; - const int mask = LOG_CPERROR; - - /* write to the logfile, always */ - if (logfile && (log_flags & mask)) { - va_list args; - va_start(args, format); - _log_writeln(logfile, 0, prefix, format, args); - va_end(args); - } - - /* write to stderr, if that's not the logfile already */ - if (logfile != stderr && (log_stderr & mask)) { - int dupe = check_dupe(format, prefix); - if (!dupe) { - va_list args; - va_start(args, format); - _log_writeln(stderr, stdio_codepage, prefix, format, args); - va_end(args); - } - } - if (log_flags & LOG_FLUSH) { - log_flush(); - } -} - -void log_fatal(const char *format, ...) -{ - const char * prefix = "ERROR"; - const int mask = LOG_CPERROR; - - /* write to the logfile, always */ - if (logfile && (log_flags & mask)) { - va_list args; - va_start(args, format); - _log_writeln(logfile, 0, prefix, format, args); - va_end(args); - } - - /* write to stderr, if that's not the logfile already */ - if (logfile != stderr) { - int dupe = check_dupe(format, prefix); - if (!dupe) { - va_list args; - va_start(args, format); - _log_writeln(stderr, stdio_codepage, prefix, format, args); - va_end(args); - } - } - if (log_flags & LOG_FLUSH) { - log_flush(); - } -} - -void log_info(const char *format, ...) -{ - const char * prefix = "INFO"; - const int mask = LOG_CPINFO; - - /* write to the logfile, always */ - if (logfile && (log_flags & mask)) { - va_list args; - va_start(args, format); - _log_writeln(logfile, 0, prefix, format, args); - va_end(args); - } - - /* write to stderr, if that's not the logfile already */ - if (logfile != stderr && (log_stderr & mask)) { - int dupe = check_dupe(format, prefix); - if (!dupe) { - va_list args; - va_start(args, format); - _log_writeln(stderr, stdio_codepage, prefix, format, args); - va_end(args); - } - } - if (log_flags & LOG_FLUSH) { - log_flush(); - } -} - -void log_printf(FILE * io, const char *format, ...) -{ - const char * prefix = "INFO"; - const int mask = LOG_CPINFO; - - /* write to the logfile, always */ - if (logfile && (log_flags & mask)) { - int codepage = (logfile == stderr || logfile == stdout) ? stdio_codepage : 0; - va_list args; - va_start(args, format); - _log_write(logfile, codepage, prefix, format, args); - va_end(args); - } - - /* write to io, if that's not the logfile already */ - if (logfile != io && (log_stderr & mask)) { - int dupe = check_dupe(format, prefix); - if (!dupe) { - va_list args; - va_start(args, format); - _log_write(io, stdio_codepage, prefix, format, args); - va_end(args); - } - } - if (log_flags & LOG_FLUSH) { - log_flush(); - } + int old = log->flags; + log->flags = flags; + return old; } diff --git a/src/util/log.h b/src/util/log.h index 40a667de4..56adfa1c3 100644 --- a/src/util/log.h +++ b/src/util/log.h @@ -15,11 +15,18 @@ without prior permission by the authors of Eressea. extern "C" { #endif #include - extern void log_open(const char *filename); - extern void log_close(void); - extern void log_flush(void); +#include + + struct log_t; + + typedef void(*log_fun)(void *data, int level, const char *module, const char *format, va_list args); + + struct log_t * log_open(const char *filename, int flags); + struct log_t * log_create(int flags, void *data, log_fun call); + struct log_t * log_to_file(int flags, FILE *out); + int log_level(struct log_t *log, int flags); + void log_close(void); - /* use macros above instead of these: */ extern void log_fatal(const char *format, ...); extern void log_error(const char *format, ...); extern void log_warning(const char *format, ...); @@ -27,14 +34,14 @@ extern "C" { extern void log_info(const char *format, ...); extern void log_printf(FILE * ios, const char *format, ...); -#define LOG_FLUSH 0x01 +#define LOG_CPERROR 0x01 #define LOG_CPWARNING 0x02 -#define LOG_CPERROR 0x04 -#define LOG_CPINFO 0x08 -#define LOG_CPDEBUG 0x10 +#define LOG_CPINFO 0x04 +#define LOG_CPDEBUG 0x08 +#define LOG_LEVELS 0x0F +#define LOG_FLUSH 0x10 +#define LOG_BRIEF 0x20 - extern int log_flags; - extern int log_stderr; #ifdef __cplusplus } #endif diff --git a/src/util/password.c b/src/util/password.c new file mode 100644 index 000000000..7b1bb9901 --- /dev/null +++ b/src/util/password.c @@ -0,0 +1,132 @@ +#include +#include "password.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#define MAXSALTLEN 32 // maximum length in characters of any salt +#define SALTLEN 8 // length of salts we generate + +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int n = (N); \ + while (n-- > 0 && buflen > 0) \ + { \ + *cp++ = itoa64[w & 0x3f]; \ + --buflen; \ + w >>= 6; \ + } \ + } while (0) + + +char *password_gensalt(char *salt, size_t salt_len) { + size_t buflen = salt_len-1; + char *cp = salt; + while (buflen) { + unsigned long ul = genrand_int32() & (unsigned long)time(0); + b64_from_24bit((char)(ul & 0xFF), (char)((ul >> 8) & 0xff), (char)((ul >> 16) & 0xFF), 4); + } + salt[salt_len-1] = 0; + return salt; +} + +bool password_is_implemented(int algo) { + return algo == PASSWORD_PLAINTEXT;// || algo == PASSWORD_BCRYPT || algo == PASSWORD_NOCRYPT || algo == PASSWORD_MD5 || algo == PASSWORD_APACHE_MD5; +} + +static const char * password_hash_i(const char * passwd, const char *input, int algo, char *result, size_t len) { + if (algo == PASSWORD_BCRYPT) { + char salt[MAXSALTLEN]; + char setting[40]; + if (!input) { + input = password_gensalt(salt, MAXSALTLEN); + } + if (_crypt_gensalt_blowfish_rn("$2y$", 5, input, strlen(input), setting, sizeof(setting)) == NULL) { + return NULL; + } + if (_crypt_blowfish_rn(passwd, setting, result, len) == NULL) { + return NULL; + } + return result; + } + else if (algo == PASSWORD_PLAINTEXT) { + _snprintf(result, len, "%s", passwd); + return result; + } + else if (algo == PASSWORD_NOCRYPT) { + _snprintf(result, len, "$0$%s", passwd); + return result; + } + else if (password_is_implemented(algo)) { + char salt[MAXSALTLEN]; + assert(passwd); + if (input) { + const char * dol = strchr(input, '$'); + size_t salt_len; + if (dol) { + assert(dol > input && dol[0] == '$'); + salt_len = dol - input; + } + else { + salt_len = strlen(input); + } + assert(salt_len < MAXSALTLEN); + memcpy(salt, input, salt_len); + salt[salt_len] = 0; + } else { + input = password_gensalt(salt, SALTLEN); + } + if (algo == PASSWORD_MD5) { + return md5_crypt_r(passwd, input, result, len); + } + else if (algo == PASSWORD_APACHE_MD5) { + apr_md5_encode(passwd, input, result, len); + return result; + } + } + return NULL; +} + +const char * password_encode(const char * passwd, int algo) { + static char result[64]; // TODO: static result buffers are bad mojo! + if (algo < 0) algo = PASSWORD_DEFAULT; + return password_hash_i(passwd, 0, algo, result, sizeof(result)); +} + +int password_verify(const char * pwhash, const char * passwd) { + char hash[64]; + int algo = PASSWORD_PLAINTEXT; + char *pos; + const char *result; + assert(passwd); + assert(pwhash); + if (pwhash[0] == '$') { + algo = pwhash[1]; + } + if (!password_is_implemented(algo)) { + return VERIFY_UNKNOWN; + } + if (algo == PASSWORD_PLAINTEXT) { + return (strcmp(passwd, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL; + } else if (algo == PASSWORD_BCRYPT) { + char sample[200]; + _crypt_blowfish_rn(passwd, pwhash, sample, sizeof(sample)); + return (strcmp(sample, pwhash) == 0) ? VERIFY_OK : VERIFY_FAIL; + } + pos = strchr(pwhash+2, '$'); + assert(pos && pos[0] == '$'); + pos = strchr(pos, '$')+1; + result = password_hash_i(passwd, pos, algo, hash, sizeof(hash)); + if (strcmp(pwhash, result) == 0) { + return VERIFY_OK; + } + return VERIFY_FAIL; +} diff --git a/src/util/password.h b/src/util/password.h new file mode 100644 index 000000000..90912cd9d --- /dev/null +++ b/src/util/password.h @@ -0,0 +1,17 @@ +#pragma once + +#define PASSWORD_PLAINTEXT 0 +#define PASSWORD_NOCRYPT '0' +#define PASSWORD_MD5 '1' +#define PASSWORD_BCRYPT '2' // not implemented +#define PASSWORD_APACHE_MD5 'a' +#define PASSWORD_SHA256 '5' // not implemented +#define PASSWORD_SHA512 '6' // not implemented +#define PASSWORD_DEFAULT PASSWORD_PLAINTEXT + +#define VERIFY_OK 0 // password matches hash +#define VERIFY_FAIL 1 // password is wrong +#define VERIFY_UNKNOWN 2 // hashing algorithm not supported +int password_verify(const char *hash, const char *passwd); +const char * password_encode(const char *passwd, int algo); +bool password_is_implemented(int algo); diff --git a/src/util/password.test.c b/src/util/password.test.c new file mode 100644 index 000000000..067aebbed --- /dev/null +++ b/src/util/password.test.c @@ -0,0 +1,67 @@ +#include +#include "password.h" +#include +#include + +static void test_passwords(CuTest *tc) { + const char *hash, *expect; + expect = "$apr1$FqQLkl8g$.icQqaDJpim4BVy.Ho5660"; + if (password_is_implemented(PASSWORD_APACHE_MD5)) { + CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "Hodor")); + hash = password_encode("Hodor", PASSWORD_APACHE_MD5); + CuAssertPtrNotNull(tc, hash); + CuAssertIntEquals(tc, 0, strncmp(hash, expect, 6)); + } else { + CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "Hodor")); + } + + expect = "$1$ZouUn04i$yNnT1Oy8azJ5V.UM9ppP5/"; + if (password_is_implemented(PASSWORD_MD5)) { + CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "jollygood")); + hash = password_encode("jollygood", PASSWORD_MD5); + CuAssertPtrNotNull(tc, hash); + CuAssertIntEquals(tc, 0, strncmp(hash, expect, 3)); + } else { + CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "jollygood")); + } + + expect = "password"; + if (password_is_implemented(PASSWORD_PLAINTEXT)) { + hash = password_encode("password", PASSWORD_PLAINTEXT); + CuAssertPtrNotNull(tc, hash); + CuAssertStrEquals(tc, hash, expect); + CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "password")); + CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(expect, "arseword")); + } else { + CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "password")); + } + + expect = "$0$password"; + if (password_is_implemented(PASSWORD_NOCRYPT)) { + hash = password_encode("password", PASSWORD_NOCRYPT); + CuAssertPtrNotNull(tc, hash); + CuAssertStrEquals(tc, hash, expect); + CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "password")); + CuAssertIntEquals(tc, VERIFY_FAIL, password_verify(expect, "arseword")); + } else { + CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "password")); + } + + expect = "$2y$05$RJ8qAhu.foXyJLdc2eHTLOaK4MDYn3/v4HtOVCq0Plv2yxcrEB7Wm"; + if (password_is_implemented(PASSWORD_BCRYPT)) { + CuAssertIntEquals(tc, VERIFY_OK, password_verify(expect, "Hodor")); + hash = password_encode("Hodor", PASSWORD_BCRYPT); + CuAssertPtrNotNull(tc, hash); + CuAssertIntEquals(tc, 0, strncmp(hash, expect, 7)); + } else { + CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify(expect, "Hodor")); + } + + CuAssertIntEquals(tc, VERIFY_UNKNOWN, password_verify("$9$saltyfish$password", "password")); +} + +CuSuite *get_password_suite(void) { + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_passwords); + return suite; +} diff --git a/src/util/resolve.c b/src/util/resolve.c index cee4a9d19..63ef5d933 100644 --- a/src/util/resolve.c +++ b/src/util/resolve.c @@ -45,10 +45,10 @@ variant read_int(struct storage *store) } int -read_reference(void *address, storage * store, read_fun reader, -resolve_fun resolver) +read_reference(void *address, struct gamedata * data, read_fun reader, + resolve_fun resolver) { - variant var = reader(store); + variant var = reader(data); int result = resolver(var, address); if (result != 0) { ur_add(var, address, resolver); @@ -58,6 +58,7 @@ resolve_fun resolver) void ur_add(variant data, void *ptrptr, resolve_fun fun) { + assert(ptrptr); if (ur_list == NULL) { ur_list = malloc(BLOCKSIZE * sizeof(unresolved)); ur_begin = ur_current = ur_list; @@ -86,6 +87,7 @@ void resolve(void) ur_list = ur; continue; } + assert(ur->ptrptr); ur->resolve(ur->data, ur->ptrptr); ++ur; } diff --git a/src/util/resolve.h b/src/util/resolve.h index 8d24cc359..076b20508 100644 --- a/src/util/resolve.h +++ b/src/util/resolve.h @@ -21,14 +21,15 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "variant.h" struct storage; +struct gamedata; #ifdef __cplusplus extern "C" { #endif typedef int(*resolve_fun) (variant data, void *address); - typedef variant(*read_fun) (struct storage * store); - extern int read_reference(void *address, struct storage *store, + typedef variant(*read_fun) (struct gamedata * data); + extern int read_reference(void *address, struct gamedata *data, read_fun reader, resolve_fun resolver); extern void ur_add(variant data, void *address, resolve_fun fun); diff --git a/src/util/umlaut.c b/src/util/umlaut.c index 872ad8ffd..abbd59372 100644 --- a/src/util/umlaut.c +++ b/src/util/umlaut.c @@ -171,7 +171,6 @@ void addtoken(void ** root, const char *str, variant id) ref = (tref *)malloc(sizeof(tref)); ref->ucs = ucs; - ref->node = 0; ref->node = node; ref->nexthash = tk->next[index]; tk->next[index] = ref; diff --git a/src/vortex.c b/src/vortex.c index 1d558bb49..8aac12aec 100644 --- a/src/vortex.c +++ b/src/vortex.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -74,8 +75,9 @@ static int a_agedirection(attrib * a, void *owner) return (d->duration > 0) ? AT_AGE_KEEP : AT_AGE_REMOVE; } -static int a_readdirection(attrib * a, void *owner, struct storage *store) +static int a_readdirection(attrib * a, void *owner, struct gamedata *data) { + struct storage *store = data->store; spec_direction *d = (spec_direction *)(a->data.v); char lbuf[32]; @@ -149,7 +151,7 @@ attrib *create_special_direction(region * r, region * rt, int duration, spec_direction *special_direction(const region * from, const region * to) { - const attrib *a = a_findc(from->attribs, &at_direction); + const attrib *a = a_find(from->attribs, &at_direction); while (a != NULL && a->type == &at_direction) { spec_direction *sd = (spec_direction *)a->data.v; diff --git a/src/wormhole.c b/src/wormhole.c index 684cb8f4d..36e04e9e0 100644 --- a/src/wormhole.c +++ b/src/wormhole.c @@ -27,6 +27,7 @@ /* util includes */ #include +#include #include #include #include @@ -109,17 +110,18 @@ static int resolve_exit(variant id, void *address) return -1; } -static int wormhole_read(struct attrib *a, void *owner, struct storage *store) +static int wormhole_read(struct attrib *a, void *owner, struct gamedata *data) { - resolve_fun resolver = (global.data_version < UIDHASH_VERSION) + storage *store = data->store; + resolve_fun resolver = (data->version < UIDHASH_VERSION) ? resolve_exit : resolve_region_id; - read_fun reader = (global.data_version < UIDHASH_VERSION) + read_fun reader = (data->version < UIDHASH_VERSION) ? read_building_reference : read_region_reference; - if (global.data_version < ATTRIBOWNER_VERSION) { + if (data->version < ATTRIBOWNER_VERSION) { READ_INT(store, NULL); } - if (read_reference(&a->data.v, store, reader, resolver) == 0) { + if (read_reference(&a->data.v, data, reader, resolver) == 0) { if (!a->data.v) { return AT_READ_FAIL; } @@ -134,6 +136,7 @@ static attrib_type at_wormhole = { wormhole_age, wormhole_write, wormhole_read, + NULL, ATF_UNIQUE }; diff --git a/storage b/storage index 89f3c1b01..18cc3bb8f 160000 --- a/storage +++ b/storage @@ -1 +1 @@ -Subproject commit 89f3c1b01e41f2675fcbfd51fd8494894dc22d44 +Subproject commit 18cc3bb8f8906237915eb31c9899f95340318087 diff --git a/tests/data/184.dat b/tests/data/184.dat index 734e5a19f..d219f92dd 100644 Binary files a/tests/data/184.dat and b/tests/data/184.dat differ diff --git a/tests/orders.184 b/tests/orders.184 index 87825c1fd..980993481 100644 --- a/tests/orders.184 +++ b/tests/orders.184 @@ -1,4 +1,16 @@ -ERESSEA 6rLo "4jLm82" +ERESSEA 6rLo "6rLo" EINHEIT 7Lgf NACH NW NW NAECHSTER +ERESSEA w86y "w86y" +EINHEIT uc3u +STIRB "mrqa" +NAECHSTER +ERESSEA ngij "ngij" +EINHEIT iwbz +HELFE w86y ALLES +EINHEIT j536 +GRUPPE "Hodor" +HELFE w86y ALLES +HELFE w86y SILBER NICHT +NAECHSTER diff --git a/tests/run-turn.sh b/tests/run-turn.sh index 4055613e5..d47ac81a2 100755 --- a/tests/run-turn.sh +++ b/tests/run-turn.sh @@ -1,4 +1,5 @@ -NEWFILES="data/185.dat datum parteien parteien.full passwd score turn" +NEWFILES="data/185.dat datum parteien parteien.full passwd htpasswd score turn" + cleanup () { rm -rf reports $NEWFILES } @@ -19,6 +20,7 @@ expr=$2 expect=$3 count=`grep -cE $expr $file` [ $count -eq $expect ] || quit 1 "expected $expect counts of $expr in $file, got $count" +echo "PASS: $expr is $expect" } ROOT=`pwd` @@ -34,10 +36,11 @@ VALGRIND=`which valgrind` SERVER=../Debug/eressea/eressea if [ -n "$VALGRIND" ]; then SUPP=../share/ubuntu-12_04.supp -SERVER="$VALGRIND --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" +SERVER="$VALGRIND --track-origins=yes --gen-suppressions=all --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" fi echo "running $SERVER" -$SERVER -t 184 ../scripts/run-turn.lua +$SERVER -t 184 test-turn.lua +echo "integration tests" [ -d reports ] || quit 4 "no reports directory created" CRFILE=185-zvto.cr for file in $NEWFILES reports/$CRFILE ; do diff --git a/tests/test-turn.lua b/tests/test-turn.lua new file mode 100644 index 000000000..03178b74d --- /dev/null +++ b/tests/test-turn.lua @@ -0,0 +1,5 @@ +dofile('../scripts/run-turn.lua') +turn = get_turn() +eressea.free_game() +print("trying to read data from " .. turn) +eressea.read_game(turn .. ".dat") diff --git a/tests/write-reports.sh b/tests/write-reports.sh index c87059854..2183b17dd 100755 --- a/tests/write-reports.sh +++ b/tests/write-reports.sh @@ -25,7 +25,7 @@ TESTS=../Debug/eressea/test_eressea SERVER=../Debug/eressea/eressea if [ -n "$VALGRIND" ]; then SUPP=../share/ubuntu-12_04.supp -VALGRIND="$VALGRIND --suppressions=$SUPP --error-exitcode=1 --leak-check=no" +VALGRIND="$VALGRIND --track-origins=yes --gen-suppressions=all --suppressions=$SUPP --error-exitcode=1 --leak-check=no" fi echo "running $TESTS" $VALGRIND $TESTS