diff --git a/.editorconfig b/.editorconfig
index 9cebe6d6d..cd3315bd8 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -9,7 +9,7 @@ end_of_line = lf
insert_final_newline = true
# 4 space indentation
-[*.{c,h,lua}]
+[*.{c,h,lua,py}]
indent_style = space
indent_size = 4
diff --git a/process/CMakeLists.txt b/process/CMakeLists.txt
index 174fae694..0ffb7b4a2 100644
--- a/process/CMakeLists.txt
+++ b/process/CMakeLists.txt
@@ -1,5 +1,6 @@
install(PROGRAMS create-orders backup-eressea run-turn send-zip-report
send-bz2-report compress.py compress.sh epasswd.py orders-process
+process-orders.py accept-orders.py
checkpasswd.py sendreport.sh sendreports.sh orders-accept DESTINATION bin)
install(DIRECTORY cron/ DESTINATION bin USE_SOURCE_PERMISSIONS
diff --git a/process/accept-orders.py b/process/accept-orders.py
new file mode 100755
index 000000000..3bf9ea965
--- /dev/null
+++ b/process/accept-orders.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from email.Utils import parseaddr
+from email.Parser import Parser
+import os
+import os.path
+import ConfigParser
+from re import compile, IGNORECASE
+from stat import ST_MTIME
+from string import upper, split, replace
+import logging
+import sys
+import subprocess
+from sys import stdin
+from time import ctime, sleep, time
+from socket import gethostname
+from rfc822 import parsedate_tz, mktime_tz
+
+if 'ERESSEA' in os.environ:
+ dir = os.environ['ERESSEA']
+elif 'HOME' in os.environ:
+ dir = os.path.join(os.environ['HOME'], 'eressea')
+else: # WTF? No HOME?
+ dir = "/home/eressea/eressea"
+if not os.path.isdir(dir):
+ print "please set the ERESSEA environment variable to the install path"
+ sys.exit(1)
+rootdir = dir
+
+game = int(sys.argv[1])
+gamedir = os.path.join(rootdir, "game-%d" % (game, ))
+frommail = 'eressea-server@kn-bremen.de'
+gamename = 'Eressea'
+sender = '%s Server <%s>' % (gamename, frommail)
+
+inifile = os.path.join(gamedir, 'eressea.ini')
+if not os.path.exists(inifile):
+ print "no such file: " . inifile
+else:
+ config = ConfigParser.ConfigParser()
+ config.read(inifile)
+ if config.has_option('game', 'email'):
+ frommail = config.get('game', 'email')
+ if config.has_option('game', 'name'):
+ gamename = config.get('game', 'name')
+ if config.has_option('game', 'sender'):
+ sender = config.get('game', 'sender')
+ else:
+ sender = "%s Server <%s>" % (gamename, frommail)
+ config = None
+prefix = 'turn-'
+hostname = gethostname()
+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
+
+def unlock_file(filename):
+ try:
+ os.unlink(filename+".lock")
+ except:
+ print "could not unlock %s.lock, file not found" % filename
+
+def lock_file(filename):
+ i = 0
+ wait = 1
+ if not os.path.exists(filename):
+ file=open(filename, "w")
+ file.close()
+ while True:
+ try:
+ os.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 os.path.exists(filename):
+ maxdate = max(os.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 os.path.exists(dirname): os.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, gamedir, gamename, sender
+ if extend is not None:
+ orderbase = orderbase + ".pre-" + extend
+ savedir = os.path.join(gamedir, orderbase)
+ # check if it's one of the pre-sent orders.
+ # create the save-directories if they don't exist
+ if not os.path.exists(gamedir): os.mkdir(gamedir)
+ if not os.path.exists(savedir): os.mkdir(savedir)
+ # parse message
+ message = Parser().parse(stream)
+ email = get_sender(message)
+ logger = logging.getLogger(email)
+ # write syslog
+ if email is None or valid_email(email)==0:
+ logger.warning("invalid email address: " + str(email))
+ return -1
+ logger.info("received orders from " + email)
+ # get an available filename
+ lock_file(gamedir + "/orders.queue")
+ maxdate, filename = available_file(savedir, prefix + email)
+ if filename is None:
+ logger.warning("more than " + str(maxfiles) + " orders from " + email)
+ return -1
+ # copy the orders to the file
+ text_ok = copy_orders(message, filename, email)
+ unlock_file(gamedir + "/orders.queue")
+
+ warning, msg, fail = None, "", False
+ maildate = message.get("Date")
+ if maildate != None:
+ turndate = mktime_tz(parsedate_tz(maildate))
+ os.utime(filename, (turndate, turndate))
+ logger.debug("mail date is '%s' (%d)" % (maildate, turndate))
+ if False and turndate < maxdate:
+ logger.warning("inconsistent message date " + email)
+ warning = " (" + messages["warning-" + locale] + ")"
+ msg = msg + formatpar(messages["maildate-" + locale] % (ctime(maxdate),ctime(turndate)), 76, 2) + "\n"
+ else:
+ logger.warning("missing message date " + email)
+ 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 " + email)
+ os.unlink(filename)
+ savedir = savedir + "/rejected"
+ if not os.path.exists(savedir): os.mkdir(savedir)
+ lock_file(gamedir + "/orders.queue")
+ maxdate, filename = available_file(savedir, prefix + email)
+ store_message(message, filename)
+ unlock_file(gamedir + "/orders.queue")
+ fail = True
+
+ if sendmail and warning is not None:
+ subject = gamename + " " + messages["subject-"+locale] + warning
+ print("mail " + subject)
+ ps = subprocess.Popen(['mutt', '-s', subject, email], stdin=subprocess.PIPE)
+ ps.communicate(msg)
+
+ if not sendmail:
+ print text_ok, fail, email
+ 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" % (email, filename, locale, game))
+ queue.close()
+ unlock_file(gamedir + "/orders.queue")
+
+ logger.info("done - accepted orders from " + email)
+
+ return 0
+
+# the main body of the script:
+try:
+ os.mkdir(os.path.join(rootdir, 'log'))
+except:
+ pass # already exists?
+LOG_FILENAME=os.path.join(rootdir, 'log/orders.log')
+logging.basicConfig(level=logging.DEBUG, filename=LOG_FILENAME)
+logger = logging
+delay = None # TODO: parse the turn delay
+locale = sys.argv[2]
+infile = stdin
+if len(sys.argv)>3:
+ infile = open(sys.argv[3], "r")
+retval = accept(game, locale, infile, delay)
+if infile!=stdin:
+ infile.close()
+sys.exit(retval)
diff --git a/process/orders-accept b/process/orders-accept
index 2f8f0bd29..266424a41 100755
--- a/process/orders-accept
+++ b/process/orders-accept
@@ -1,378 +1,5 @@
-#!/usr/bin/env python
-# -*- coding: iso-8859-1 -*-
+#!/bin/sh
+SCRIPT=$(readlink -f $0)
+cd $(dirname $SCRIPT)
+python accept-orders.py "$@"
-from email.Utils import parseaddr
-from email.Parser import Parser
-import os
-import os.path
-import ConfigParser
-from re import compile, IGNORECASE
-from stat import ST_MTIME
-from string import upper, split, replace
-import logging
-import sys
-from sys import stdin
-from time import ctime, sleep, time
-from socket import gethostname
-from rfc822 import parsedate_tz, mktime_tz
-
-if 'ERESSEA' in os.environ:
- dir = os.environ['ERESSEA']
-elif 'HOME' in os.environ:
- dir = os.path.join(os.environ['HOME'], '/eressea')
-else: # WTF? No HOME?
- dir = "/home/eressea/eressea"
-if not os.path.isdir(dir):
- print "please set the ERESSEA environment variable to the install path"
- sys.exit(1)
-rootdir = dir
-
-game = int(sys.argv[1])
-gamedir = os.path.join(rootdir, "game-%d" % (game, ))
-frommail = 'eressea-server@kn-bremen.de'
-gamename = 'Eressea'
-sender = '%s Server <%s>' % (gamename, frommail)
-
-inifile = os.path.join(gamedir, 'eressea.ini')
-if not os.path.exists(inifile):
- print "no such file: " . inifile
-else:
- config = ConfigParser.ConfigParser()
- config.read(inifile)
- if config.has_option('game', 'email'):
- frommail = config.get('game', 'email')
- if config.has_option('game', 'name'):
- gamename = config.get('game', 'name')
- if config.has_option('game', 'sender'):
- sender = config.get('game', 'sender')
- else:
- sender = "%s Server <%s>" % (gamename, frommail)
- config = None
-prefix = 'turn-'
-hostname = gethostname()
-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
-
-def unlock_file(filename):
- try:
- os.unlink(filename+".lock")
- except:
- print "could not unlock %s.lock, file not found" % filename
-
-def lock_file(filename):
- i = 0
- wait = 1
- if not os.path.exists(filename):
- file=open(filename, "w")
- file.close()
- while True:
- try:
- os.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 os.path.exists(filename):
- maxdate = max(os.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 os.path.exists(dirname): os.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, gamedir, gamename, sender
- if extend is not None:
- orderbase = orderbase + ".pre-" + extend
- savedir = os.path.join(gamedir, orderbase)
- # check if it's one of the pre-sent orders.
- # create the save-directories if they don't exist
- if not os.path.exists(gamedir): os.mkdir(gamedir)
- if not os.path.exists(savedir): os.mkdir(savedir)
- # parse message
- message = Parser().parse(stream)
- email = get_sender(message)
- logger = logging.getLogger(email)
- # write syslog
- if email is None or valid_email(email)==0:
- logger.warning("invalid email address: " + str(email))
- return -1
- logger.info("received orders from " + email)
- # get an available filename
- lock_file(gamedir + "/orders.queue")
- maxdate, filename = available_file(savedir, prefix + email)
- if filename is None:
- logger.warning("more than " + str(maxfiles) + " orders from " + email)
- return -1
- # copy the orders to the file
- text_ok = copy_orders(message, filename, email)
- unlock_file(gamedir + "/orders.queue")
-
- warning, msg, fail = None, "", False
- maildate = message.get("Date")
- if maildate != None:
- turndate = mktime_tz(parsedate_tz(maildate))
- os.utime(filename, (turndate, turndate))
- logger.debug("mail date is '%s' (%d)" % (maildate, turndate))
- if turndate < maxdate:
- logger.warning("inconsistent message date " + email)
- warning = " (" + messages["warning-" + locale] + ")"
- msg = msg + formatpar(messages["maildate-" + locale] % (ctime(maxdate),ctime(turndate)), 76, 2) + "\n"
- else:
- logger.warning("missing message date " + email)
- 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 " + email)
- os.unlink(filename)
- savedir = savedir + "/rejected"
- if not os.path.exists(savedir): os.mkdir(savedir)
- lock_file(gamedir + "/orders.queue")
- maxdate, filename = available_file(savedir, prefix + email)
- store_message(message, filename)
- unlock_file(gamedir + "/orders.queue")
- fail = True
-
- if sendmail and warning is not None:
- subject = gamename + " " + messages["subject-"+locale] + warning
- mail = "Subject: %s\nFrom: %s\nTo: %s\n\n" % (subject, sender, email) + msg
- from smtplib import SMTP
- server = SMTP("localhost")
- server.sendmail(sender, email, mail)
- server.close()
-
- if not sendmail:
- print text_ok, fail, email
- 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" % (email, filename, locale, game))
- queue.close()
- unlock_file(gamedir + "/orders.queue")
-
- logger.info("done - accepted orders from " + email)
-
- return 0
-
-# the main body of the script:
-try:
- os.mkdir(os.path.join(rootdir, 'log'))
-except:
- pass # already exists?
-LOG_FILENAME=os.path.join(rootdir, 'log/orders.log')
-logging.basicConfig(level=logging.DEBUG, filename=LOG_FILENAME)
-logger = logging
-delay=None # TODO: parse the turn delay
-locale = sys.argv[2]
-infile = stdin
-if len(sys.argv)>3:
- infile = open(sys.argv[3], "r")
-retval = accept(game, locale, infile, delay)
-if infile!=stdin:
- infile.close()
-sys.exit(retval)
diff --git a/process/orders-process b/process/orders-process
index 79c7b3378..38b2d1115 100755
--- a/process/orders-process
+++ b/process/orders-process
@@ -1,242 +1,5 @@
-#!/usr/bin/env python
-# -*- coding: iso-8859-1 -*-
+#!/bin/sh
+SCRIPT=$(readlink -f $0)
+cd $(dirname $SCRIPT)
+python process-orders.py "$@"
-from os import unlink, symlink, rename, popen, tmpfile
-import sys
-import os
-import os.path
-import ConfigParser
-from re import compile, IGNORECASE
-from string import split, join, upper, strip
-from sys import argv, exit
-from time import sleep, time, ctime
-from syslog import openlog, closelog, syslog
-
-from epasswd import EPasswd
-
-def pwd_get_email(faction, pwd, pwdfile=None):
- return None
-
-def split_filename(filename):
- return os.path.split(filename)
-
-def unlock_file(filename):
- try:
- unlink(filename+".lock")
- except:
- print "could not unlock %s.lock, file not found" % filename
- raise
-
-def lock_file(filename):
- i = 0
- wait = 1
- if not os.path.exists(filename):
- file=open(filename, "w")
- file.close()
- while True:
- try:
- symlink(filename, filename+".lock")
- return
- except:
- i = i+1
- if i == 5:
- raise
- sleep(wait)
- wait = wait*2
-
-messages = {
-"subject-de": "Befehle angekommen",
-"subject-en": "orders received",
-
-"validate-en": "Validating",
-"validate-de": "Verarbeite",
-
-"faction-en": "Faction",
-"faction-de": "Partei",
-
-"unknown-de": "WARNUNG: Die Partei ist nicht bekannt, oder das Passwort falsch!",
-"unknown-en": "WARNING: This faction is unknown, or the password is incorrect!",
-
-"warning-de": "Warnung",
-"warning-en": "Warning",
-
-"error-de": "Fehler",
-"error-en": "Error",
-}
-
-game = int(sys.argv[1])
-echeck_cmd = "/home/eressea/echeck/echeck.sh"
-maxlines = 25
-
-# base directory for all your games:
-install_dir = "/home/eressea/eressea"
-if 'ERESSEA' in os.environ:
- install_dir = os.environ['ERESSEA']
-elif 'HOME' in os.environ:
- install_dir = os.path.join(os.environ['HOME'], '/eressea')
-if not os.path.isdir(install_dir):
- print "please set the ERESSEA environment variable to the install path"
- sys.exit(1)
-
-game_dir = os.path.join(install_dir, "game-%d" % (game, ))
-frommail = 'eressea-server@kn-bremen.de'
-gamename = 'Eressea'
-sender = '%s Server <%s>' % (gamename, frommail)
-
-inifile = os.path.join(game_dir, 'eressea.ini')
-if not os.path.exists(inifile):
- print "no such file: " . inifile
-else:
- config = ConfigParser.ConfigParser()
- config.read(inifile)
- if config.has_option('game', 'email'):
- frommail = config.get('game', 'email')
- if config.has_option('game', 'name'):
- gamename = config.get('game', 'name')
- if config.has_option('game', 'sender'):
- sender = config.get('game', 'sender')
- else:
- sender = "%s Server <%s>" % (gamename, frommail)
- config = None
-
-queue_file = os.path.join(game_dir, "orders.queue")
-if not os.path.exists(queue_file):
- exit(0)
-
-# regular expression that finds the start of a faction
-fact_re = compile("^\s*(eressea|partei|faction)\s+([a-zA-Z0-9]+)\s+\"?([^\"]*)\"?", IGNORECASE)
-
-def check_pwd(filename, email, pw_data):
- results = []
- try:
- file = open(filename, "r")
- except:
- print "could not open file", filename
- return results
- for line in file.readlines():
- mo = fact_re.search(strip(line))
- if mo != None:
- fact_nr = str(mo.group(2))
- fact_pw = str(mo.group(3))
- if pw_data.fac_exists(fact_nr):
- if 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:
- game_email = pw_data.get_email(fact_nr)
- results = results + [ (fact_nr, game_email, True, fact_pw) ]
- else:
- results = results + [ (fact_nr, None, False, fact_pw) ]
- return results
-
-def echeck(filename, locale, rules):
- dirname, filename = split_filename(filename)
- stream = popen("%s %s %s %s %s" % (echeck_cmd, locale, filename, dirname, rules), 'r')
- lines = stream.readlines()
- if len(lines)==0:
- stream.close()
- return None
- if len(lines)>maxlines:
- mail = join(lines[:maxlines-3] + ["...", "\n"] + lines[-3:], '')
- else:
- mail = join(lines[:maxlines], '')
- stream.close()
- return mail
-
-# parse the queue file -
-#print "connecting to SMTP..."
-from smtplib import SMTP
-try:
- server = SMTP("localhost")
-except:
- print "could not connect to SMTP server"
- exit(0)
-
-#print "reading password file..."
-pw_data = EPasswd(os.path.join(game_dir,"passwd"))
-
-#print "reading orders.queue..."
-# move the queue file to a save space while locking it:
-try:
- lock_file(queue_file)
-except:
- exit(0)
-queuefile = open(queue_file, "r")
-lines = queuefile.readlines()
-queuefile.close()
-
-# copy to a temp file
-
-tname="/tmp/orders.queue.%s" % str(time())
-try:
- lock_file(tname)
-except:
- exit(0)
-tmpfile=open(tname, "w")
-for line in lines:
- tmpfile.write(line)
-tmpfile.close()
-
-openlog("orders")
-
-unlink(queue_file)
-try:
- unlock_file(queue_file)
-except:
- pass
-
-for line in lines:
- tokens = split(line[:-1], ' ')
- dict = {}
- for token in tokens:
- name, value = split(token, '=')
- dict[name] = value
-
- email = dict["email"]
- locale = dict["locale"]
- game = int(dict["game"])
- infile = dict["file"]
- gamename='[E%d]' % game
- rules='e%d' % game
- warning = ""
- failed = True
- results = check_pwd(infile, email, pw_data)
- logfile = open(os.path.join(game_dir, "zug.log"), "a")
- dirname, filename = split_filename(infile)
- msg = messages["validate-"+locale] + " " + infile + "\n\n"
- for faction, game_email, success, pwd in results:
- msg = msg + messages["faction-"+locale] + " " + faction + "\n"
- if success: failed = False
- else: msg = msg + messages["unknown-"+locale] + "\n"
- msg = msg + "\n"
- logfile.write("%s:%s:%s:%s:%s:%s\n" % (ctime(time()), email, game_email, faction, pwd, success))
- logfile.close()
-
- if failed:
- warning = " (" + messages["warning-" + locale] + ")"
- syslog("failed - no valid password in " + infile)
- else:
- result = None
- if os.path.exists(echeck_cmd):
- result = echeck(infile, locale, rules)
- if result is None:
- # echeck did not finish
- msg = msg + "Echeck is broken. Your turn was accepted, but could not be verified.\n"
- warning = " (" + messages["warning-" + locale] + ")"
- syslog("process - echeck broken, " + infile)
- else:
- msg = msg + result
- syslog("process - checked orders in " + infile)
-
- subject = gamename + " " + messages["subject-" + locale] + warning
- msg = "Subject: %s\nFrom: %s\nTo: %s\nContent-Type: text/plain; charset=utf-8\n\n" % (subject, sender, email) + msg
- try:
- server.sendmail(sender, email, msg)
- except:
- syslog("failed - cannot send to " + email)
-
-server.close()
-
-closelog()
-unlink(tname)
-unlock_file(tname)
diff --git a/process/process-orders.py b/process/process-orders.py
new file mode 100755
index 000000000..8ed5e8625
--- /dev/null
+++ b/process/process-orders.py
@@ -0,0 +1,215 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from os import unlink, symlink, rename, popen, tmpfile
+import sys
+import os
+import os.path
+import ConfigParser
+import subprocess
+from re import compile, IGNORECASE
+from string import split, join, upper, strip
+from sys import argv, exit
+from time import sleep, time, ctime
+from syslog import openlog, closelog, syslog
+
+from epasswd import EPasswd
+
+def pwd_get_email(faction, pwd, pwdfile=None):
+ return None
+
+def split_filename(filename):
+ return os.path.split(filename)
+
+def unlock_file(filename):
+ try:
+ unlink(filename+".lock")
+ except:
+ print "could not unlock %s.lock, file not found" % filename
+ raise
+
+def lock_file(filename):
+ i = 0
+ wait = 1
+ if not os.path.exists(filename):
+ file=open(filename, "w")
+ file.close()
+ while True:
+ try:
+ symlink(filename, filename+".lock")
+ return
+ except:
+ i = i+1
+ if i == 5:
+ raise
+ sleep(wait)
+ wait = wait*2
+
+messages = {
+"subject-de": "Befehle angekommen",
+"subject-en": "orders received",
+
+"validate-en": "Validating",
+"validate-de": "Verarbeite",
+
+"faction-en": "Faction",
+"faction-de": "Partei",
+
+"unknown-de": "WARNUNG: Die Partei ist nicht bekannt, oder das Passwort falsch!",
+"unknown-en": "WARNING: This faction is unknown, or the password is incorrect!",
+
+"warning-de": "Warnung",
+"warning-en": "Warning",
+
+"error-de": "Fehler",
+"error-en": "Error",
+}
+
+game = int(sys.argv[1])
+echeck_cmd = "/home/eressea/echeck/echeck.sh"
+maxlines = 25
+
+# base directory for all your games:
+install_dir = "/home/eressea/eressea"
+if 'ERESSEA' in os.environ:
+ install_dir = os.environ['ERESSEA']
+elif 'HOME' in os.environ:
+ install_dir = os.path.join(os.environ['HOME'], 'eressea')
+if not os.path.isdir(install_dir):
+ print "please set the ERESSEA environment variable to the install path"
+ sys.exit(1)
+
+game_dir = os.path.join(install_dir, "game-%d" % (game, ))
+gamename = 'Eressea'
+
+inifile = os.path.join(game_dir, 'eressea.ini')
+queue_file = os.path.join(game_dir, "orders.queue")
+if not os.path.exists(queue_file):
+ exit(0)
+
+# regular expression that finds the start of a faction
+fact_re = compile("^\s*(eressea|partei|faction)\s+([a-zA-Z0-9]+)\s+\"?([^\"]*)\"?", IGNORECASE)
+
+def check_pwd(filename, email, pw_data):
+ results = []
+ try:
+ file = open(filename, "r")
+ except:
+ print "could not open file", filename
+ return results
+ for line in file.readlines():
+ mo = fact_re.search(strip(line))
+ if mo != None:
+ fact_nr = str(mo.group(2))
+ fact_pw = str(mo.group(3))
+ if pw_data.fac_exists(fact_nr):
+ if 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:
+ game_email = pw_data.get_email(fact_nr)
+ results = results + [ (fact_nr, game_email, True, fact_pw) ]
+ else:
+ results = results + [ (fact_nr, None, False, fact_pw) ]
+ return results
+
+def echeck(filename, locale, rules):
+ dirname, filename = split_filename(filename)
+ stream = popen("%s %s %s %s %s" % (echeck_cmd, locale, filename, dirname, rules), 'r')
+ lines = stream.readlines()
+ if len(lines)==0:
+ stream.close()
+ return None
+ if len(lines)>maxlines:
+ mail = join(lines[:maxlines-3] + ["...", "\n"] + lines[-3:], '')
+ else:
+ mail = join(lines[:maxlines], '')
+ stream.close()
+ return mail
+
+#print "reading password file..."
+pw_data = EPasswd(os.path.join(game_dir,"passwd"))
+
+#print "reading orders.queue..."
+# move the queue file to a save space while locking it:
+try:
+ lock_file(queue_file)
+except:
+ exit(0)
+queuefile = open(queue_file, "r")
+lines = queuefile.readlines()
+queuefile.close()
+
+# copy to a temp file
+
+tname="/tmp/orders.queue.%s" % str(time())
+try:
+ lock_file(tname)
+except:
+ exit(0)
+tmpfile=open(tname, "w")
+for line in lines:
+ tmpfile.write(line)
+tmpfile.close()
+
+openlog("orders")
+
+unlink(queue_file)
+try:
+ unlock_file(queue_file)
+except:
+ pass
+
+for line in lines:
+ tokens = split(line[:-1], ' ')
+ dict = {}
+ for token in tokens:
+ name, value = split(token, '=')
+ dict[name] = value
+
+ email = dict["email"]
+ locale = dict["locale"]
+ game = int(dict["game"])
+ infile = dict["file"]
+ gamename='[E%d]' % game
+ rules='e%d' % game
+ warning = ""
+ failed = True
+ results = check_pwd(infile, email, pw_data)
+ logfile = open(os.path.join(game_dir, "zug.log"), "a")
+ dirname, filename = split_filename(infile)
+ msg = messages["validate-"+locale] + " " + infile + "\n\n"
+ for faction, game_email, success, pwd in results:
+ msg = msg + messages["faction-"+locale] + " " + faction + "\n"
+ if success: failed = False
+ else: msg = msg + messages["unknown-"+locale] + "\n"
+ msg = msg + "\n"
+ logfile.write("%s:%s:%s:%s:%s:%s\n" % (ctime(time()), email, game_email, faction, pwd, success))
+ logfile.close()
+
+ if failed:
+ warning = " (" + messages["warning-" + locale] + ")"
+ syslog("failed - no valid password in " + infile)
+ else:
+ result = None
+ if os.path.exists(echeck_cmd):
+ result = echeck(infile, locale, rules)
+ if result is None:
+ # echeck did not finish
+ msg = msg + "Echeck is broken. Your turn was accepted, but could not be verified.\n"
+ warning = " (" + messages["warning-" + locale] + ")"
+ syslog("process - echeck broken, " + infile)
+ else:
+ msg = msg + result
+ syslog("process - checked orders in " + infile)
+
+ subject = gamename + " " + messages["subject-" + locale] + warning
+ try:
+ sp = subprocess.Popen(['mutt', '-s', subject, email], stdin=subprocess.PIPE)
+ sp.communicate(msg)
+ except:
+ syslog("failed - cannot send to " + email)
+
+closelog()
+unlink(tname)
+unlock_file(tname)
diff --git a/res/core/messages.xml b/res/core/messages.xml
index 0f62ccc0c..d566c82e6 100644
--- a/res/core/messages.xml
+++ b/res/core/messages.xml
@@ -698,10 +698,16 @@
-
+
-
+
+
+
+
+
+
+
diff --git a/res/translations/messages.de.po b/res/translations/messages.de.po
index 13fd10c96..3413ffc2c 100644
--- a/res/translations/messages.de.po
+++ b/res/translations/messages.de.po
@@ -1355,8 +1355,11 @@ msgstr "\"Es herrscht eine fröhliche und ausgelassene Stimmung. ($int36($id))\"
msgid "buildroad"
msgstr "\"$unit($unit) erweitert in $region($region) das Straßennetz um $int($size).\""
-msgid "nr_borderlist_postfix"
-msgstr "\"$if($transparent,\" befindet sich\",\" versperrt\") ${object}$if($transparent,\"\",\" die Sicht\").\""
+msgid "nr_border_opaque"
+msgstr "Im $direction($dir) verperrt ${object} die Sicht."
+
+msgid "nr_border_transparent"
+msgstr "Im $direction($dir) befindet sich ${object}."
msgid "effectstrength"
msgstr "\"$unit($mage) erhöht die Körperkraft von $unit.dative($target) beträchtlich.\""
diff --git a/res/translations/messages.en.po b/res/translations/messages.en.po
index fa795d0f6..6eaf6f258 100644
--- a/res/translations/messages.en.po
+++ b/res/translations/messages.en.po
@@ -1355,8 +1355,11 @@ msgstr "\"Everyone in this region seems to be having a very good time. ($int36($
msgid "buildroad"
msgstr "\"$unit($unit) extends the road network in $region($region) by $int($size).\""
-msgid "nr_borderlist_postfix"
-msgstr "\"$if($transparent,\" there is\",\" sight is blocked by \") ${object}.\""
+msgid "nr_border_opaque"
+msgstr "To the $direction($dir), ${object} is blocking the view."
+
+msgid "nr_border_transparent"
+msgstr "To the $direction($dir) is ${object}."
msgid "effectstrength"
msgstr "\"$unit($mage) increases the strength of $unit($target) dramatically.\""
diff --git a/res/translations/strings.de.po b/res/translations/strings.de.po
index e56e36f55..afcceb94b 100644
--- a/res/translations/strings.de.po
+++ b/res/translations/strings.de.po
@@ -1906,9 +1906,6 @@ msgstr "Mallorn"
msgid "castle"
msgstr "Burg"
-msgid "nr_borderlist_infix"
-msgstr ", im"
-
msgctxt "race"
msgid "shadowbat_p"
msgstr "Todesflattern"
@@ -3706,9 +3703,6 @@ msgctxt "spell"
msgid "analyse_object"
msgstr "Lied des Ortes analysieren"
-msgid "nr_borderlist_lastfix"
-msgstr "und im"
-
msgctxt "race"
msgid "shadowknight_d"
msgstr "Schattenrittern"
@@ -5573,7 +5567,7 @@ msgid "swamp_trail"
msgstr "der Sumpf von %s"
msgid "nr_nb_final"
-msgstr "und im"
+msgstr "und im "
msgid "aoc"
msgstr "Katzenamulett"
diff --git a/res/translations/strings.en.po b/res/translations/strings.en.po
index 8629d5f5e..c8bd637db 100644
--- a/res/translations/strings.en.po
+++ b/res/translations/strings.en.po
@@ -1642,9 +1642,6 @@ msgstr "mallorn"
msgid "castle"
msgstr "castle"
-msgid "nr_borderlist_infix"
-msgstr ", to the "
-
msgctxt "race"
msgid "shadowbat_p"
msgstr "darkbats"
@@ -3261,9 +3258,6 @@ msgctxt "spell"
msgid "analyse_object"
msgstr "Analysis"
-msgid "nr_borderlist_lastfix"
-msgstr ", and to the "
-
msgctxt "race"
msgid "shadowknight_d"
msgstr "shadow knights"
diff --git a/scripts/eressea/e2/init.lua b/scripts/eressea/e2/init.lua
index 52636d207..9df7a1062 100644
--- a/scripts/eressea/e2/init.lua
+++ b/scripts/eressea/e2/init.lua
@@ -4,6 +4,7 @@ eressea.log.debug('rules for game E2')
math.randomseed(rng.random())
local equipment = require('eressea.equipment')
+
local sets = {
['seed_faction'] = {
['items'] = {
diff --git a/scripts/eressea/init.lua b/scripts/eressea/init.lua
index 1af37d2d5..d840fe899 100644
--- a/scripts/eressea/init.lua
+++ b/scripts/eressea/init.lua
@@ -1,5 +1,6 @@
require 'eressea.path'
require 'eressea.resources'
+require 'eressea.equipment'
require 'eressea.spells'
local self = {}
diff --git a/scripts/map.lua b/scripts/map.lua
index cad3ae1fd..98579924f 100644
--- a/scripts/map.lua
+++ b/scripts/map.lua
@@ -6,5 +6,6 @@ package.path = package.path .. ';' .. path .. '/?.lua;' .. path .. '/?/init.lua'
require 'eressea.path'
require 'eressea'
require 'eressea.xmlconf'
+
eressea.read_game(get_turn() .. ".dat")
gmtool.editor()
diff --git a/scripts/tests/e2/e2features.lua b/scripts/tests/e2/e2features.lua
index 4758fce39..be09ec448 100644
--- a/scripts/tests/e2/e2features.lua
+++ b/scripts/tests/e2/e2features.lua
@@ -491,8 +491,29 @@ function test_dwarf_mining()
local f = faction.create('dwarf')
local r = region.create(0, 0, 'plain')
local u = unit.create(f, r)
- u.name = 'Xolgrim'
u:set_skill('mining', 2)
assert_equal(2, u:get_skill('mining'))
assert_equal(4, u:eff_skill('mining'))
end
+
+function test_buy_sell()
+ local f = faction.create('human')
+ local r = region.create(0, 0, 'plain')
+ local u = unit.create(f, r)
+ local lux = r.luxury
+ local b = building.create(r, 'castle')
+ b.size = 10
+ u:set_skill('trade', 1)
+ item = 'silk'
+ name = 'Seide'
+ if lux == 'silk' then
+ item = 'balm'
+ name = 'Balsam'
+ end
+ u:add_item(item, 5)
+ u:add_order('VERKAUFE 1 ' .. name)
+ assert_equal(0, u:get_item('money'))
+ process_orders()
+ assert_equal(4, u:get_item(item))
+ assert_not_equal(0, u:get_item('money'))
+end
diff --git a/src/exparse.c b/src/exparse.c
index e6975fc17..07acb090e 100644
--- a/src/exparse.c
+++ b/src/exparse.c
@@ -818,7 +818,9 @@ static void start_resources(parseinfo *pi, const XML_Char *el, const XML_Char **
handle_requirement(pi, el, attr);
}
else if (xml_strcmp(el, "luxury") == 0) {
- rtype->ltype = new_luxurytype(itype, 0);
+ int price = atoi(attr_get(attr, "price"));
+ assert(price > 0);
+ rtype->ltype = new_luxurytype(itype, price);
}
else if (xml_strcmp(el, "potion") == 0) {
int i, level = 0;
@@ -1090,9 +1092,13 @@ static void start_races(parseinfo *pi, const XML_Char *el, const XML_Char **attr
name = attr_get(attr, "name");
if (name) {
+ int i;
+
assert(!rc);
pi->object = rc = rc_get_or_create(name);
- int i;
+ while (AT_NONE != rc->attack[nattacks].type) {
+ ++nattacks;
+ }
for (i = 0; attr[i]; i += 2) {
const XML_Char *key = attr[i], *val = attr[i + 1];
diff --git a/src/kernel/config.c b/src/kernel/config.c
index ff2457971..ec45ddd0d 100644
--- a/src/kernel/config.c
+++ b/src/kernel/config.c
@@ -631,28 +631,24 @@ void kernel_init(void)
translation_init();
}
-static order * defaults[MAXLOCALES];
-
order *default_order(const struct locale *lang)
{
int i = locale_index(lang);
+ keyword_t kwd;
+ const char * str;
order *result = 0;
- assert(i < MAXLOCALES);
- result = defaults[i];
- if (!result) {
- const char * str;
- keyword_t kwd = NOKEYWORD;
- str = config_get("orders.default");
- if (str) {
- kwd = findkeyword(str);
- }
- if (kwd != NOKEYWORD) {
- result = create_order(kwd, lang, NULL);
- defaults[i] = result;
- }
+ assert(i < MAXLOCALES);
+ kwd = keyword_disabled(K_WORK) ? NOKEYWORD : K_WORK;
+ str = config_get("orders.default");
+ if (str) {
+ kwd = findkeyword(str);
}
- return result ? copy_order(result) : 0;
+ if (kwd != NOKEYWORD) {
+ result = create_order(kwd, lang, NULL);
+ return copy_order(result);
+ }
+ return NULL;
}
int rule_give(void)
@@ -760,14 +756,6 @@ void free_config(void) {
*/
void free_gamedata(void)
{
- int i;
-
- for (i = 0; i != MAXLOCALES; ++i) {
- if (defaults[i]) {
- free_order(defaults[i]);
- defaults[i] = 0;
- }
- }
free(forbidden_ids);
forbidden_ids = NULL;
diff --git a/src/kernel/config.test.c b/src/kernel/config.test.c
index 7a2097ce4..e99ba9d08 100644
--- a/src/kernel/config.test.c
+++ b/src/kernel/config.test.c
@@ -180,15 +180,22 @@ static void test_default_order(CuTest *tc) {
test_setup();
loc = test_create_locale();
- ord = default_order(loc);
- CuAssertPtrEquals(tc, 0, ord);
- free_order(ord);
- config_set("orders.default", "work");
ord = default_order(loc);
CuAssertPtrNotNull(tc, ord);
CuAssertIntEquals(tc, K_WORK, getkeyword(ord));
free_order(ord);
+
+ enable_keyword(K_WORK, false);
+ ord = default_order(loc);
+ CuAssertPtrEquals(tc, NULL, ord);
+ free_order(ord);
+
+ config_set("orders.default", "entertain");
+ ord = default_order(loc);
+ CuAssertPtrNotNull(tc, ord);
+ CuAssertIntEquals(tc, K_ENTERTAIN, getkeyword(ord));
+ free_order(ord);
test_teardown();
}
diff --git a/src/kernel/connection.c b/src/kernel/connection.c
index 014fce9d8..213700fe1 100644
--- a/src/kernel/connection.c
+++ b/src/kernel/connection.c
@@ -188,7 +188,7 @@ border_type *find_bordertype(const char *name)
{
border_type *bt = bordertypes;
- while (bt && strcmp(bt->__name, name)!=0)
+ while (bt && strcmp(bt->_name, name)!=0)
bt = bt->next;
return bt;
}
@@ -563,7 +563,7 @@ void write_borders(struct storage *store)
for (b = bhash; b != NULL; b = b->next) {
if (b->type->valid && !b->type->valid(b))
continue;
- WRITE_TOK(store, b->type->__name);
+ WRITE_TOK(store, b->type->_name);
WRITE_INT(store, b->id);
WRITE_INT(store, b->from->uid);
WRITE_INT(store, b->to->uid);
@@ -614,7 +614,7 @@ int read_borders(gamedata *data)
if (to == from && from) {
direction_t dir = (direction_t)(rng_int() % MAXDIRECTIONS);
region *r = rconnect(from, dir);
- log_error("[read_borders] invalid %s in %s\n", type->__name, regionname(from, NULL));
+ log_error("[read_borders] invalid %s in %s\n", type->_name, regionname(from, NULL));
if (r != NULL)
to = r;
}
@@ -633,5 +633,5 @@ int read_borders(gamedata *data)
}
const char * border_name(const connection *co, const struct region * r, const struct faction * f, int flags) {
- return (co->type->name) ? co->type->name(co, r, f, flags) : co->type->__name;
+ return (co->type->name) ? co->type->name(co, r, f, flags) : co->type->_name;
}
diff --git a/src/kernel/connection.h b/src/kernel/connection.h
index c847f690f..af412b036 100644
--- a/src/kernel/connection.h
+++ b/src/kernel/connection.h
@@ -45,7 +45,7 @@ extern "C" {
} connection;
typedef struct border_type {
- const char *__name; /* internal use only */
+ const char *_name; /* internal use only */
variant_type datatype;
bool(*transparent) (const connection *, const struct faction *);
/* is it possible to see through this? */
diff --git a/src/kernel/faction.c b/src/kernel/faction.c
index bc08ba010..16c0c789c 100755
--- a/src/kernel/faction.c
+++ b/src/kernel/faction.c
@@ -29,6 +29,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include "group.h"
#include "item.h"
#include "messages.h"
+#include "order.h"
#include "plane.h"
#include "race.h"
#include "region.h"
@@ -294,8 +295,13 @@ unit *addplayer(region * r, faction * f)
assert(f->units == NULL);
faction_setorigin(f, 0, r->x, r->y);
u = create_unit(r, f, 1, f->race, 0, NULL, NULL);
+ u->thisorder = default_order(f->locale);
+ unit_addorder(u, copy_order(u->thisorder));
name = config_get("rules.equip_first");
- equip_unit(u, name ? name : "first_unit");
+ if (!equip_unit(u, name ? name : "first_unit")) {
+ /* give every unit enough money to survive the first turn */
+ i_change(&u->items, get_resourcetype(R_SILVER)->itype, maintenance_cost(u));
+ }
u->hp = unit_max_hp(u) * u->number;
fset(u, UFL_ISNEW);
if (f->race == get_race(RC_DAEMON)) {
diff --git a/src/kernel/faction.test.c b/src/kernel/faction.test.c
index 7748f693d..37ec70e50 100644
--- a/src/kernel/faction.test.c
+++ b/src/kernel/faction.test.c
@@ -3,8 +3,10 @@
#include
#include
#include
+#include
#include
#include
+#include
#include
#include
#include
@@ -298,9 +300,30 @@ static void test_save_special_items(CuTest *tc) {
test_teardown();
}
+static void test_addplayer(CuTest *tc) {
+ unit *u;
+ region *r;
+ faction *f;
+ item_type *itype;
+ test_setup();
+ callbacks.equip_unit = NULL;
+ itype = test_create_silver();
+ r = test_create_plain(0, 0);
+ f = test_create_faction(NULL);
+ u = addplayer(r, f);
+ CuAssertPtrNotNull(tc, u);
+ CuAssertPtrEquals(tc, r, u->region);
+ CuAssertPtrEquals(tc, f, u->faction);
+ CuAssertIntEquals(tc, i_get(u->items, itype), 10);
+ CuAssertPtrNotNull(tc, u->orders);
+ CuAssertIntEquals(tc, K_WORK, getkeyword(u->orders));
+ test_teardown();
+}
+
CuSuite *get_faction_suite(void)
{
CuSuite *suite = CuSuiteNew();
+ SUITE_ADD_TEST(suite, test_addplayer);
SUITE_ADD_TEST(suite, test_max_migrants);
SUITE_ADD_TEST(suite, test_addfaction);
SUITE_ADD_TEST(suite, test_remove_empty_factions);
diff --git a/src/report.c b/src/report.c
index c171eae5c..31e1bcd26 100644
--- a/src/report.c
+++ b/src/report.c
@@ -761,11 +761,11 @@ static void rp_battles(struct stream *out, faction * f)
while (bm) {
char buf[256];
RENDER(f, buf, sizeof(buf), ("header_battle", "region", bm->r));
- newline(out);
centre(out, buf, true);
newline(out);
rp_messages(out, bm->msgs, f, 0, false);
bm = bm->next;
+ newline(out);
}
}
}
@@ -1229,39 +1229,17 @@ void report_region(struct stream *out, const region * r, faction * f)
if (edges)
newline(out);
for (e = edges; e; e = e->next) {
- bool first = true;
message *msg;
- bufp = buf;
- size = sizeof(buf) - 1;
for (d = 0; d != MAXDIRECTIONS; ++d) {
- if (!e->exist[d])
- continue;
- /* this localization might not work for every language but is fine for de and en */
- if (first)
- bytes = (int)str_strlcpy(bufp, LOC(f->locale, "nr_borderlist_prefix"), size);
- else if (e->lastd == d)
- bytes = (int)str_strlcpy(bufp, LOC(f->locale, "nr_borderlist_lastfix"), size);
- else
- bytes = (int)str_strlcpy(bufp, LOC(f->locale, "nr_borderlist_infix"), size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- bytes = (int)str_strlcpy(bufp, LOC(f->locale, directions[d]), size);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
- first = false;
+ if (e->exist[d]) {
+ msg = msg_message(e->transparent ? "nr_border_transparent" : "nr_border_opaque",
+ "object dir", e->name, d);
+ nr_render(msg, f->locale, buf, sizeof(buf), f);
+ msg_release(msg);
+ paragraph(out, buf, 0, 0, 0);
+ }
}
- /* TODO name is localized? Works for roads anyway... */
- /* TODO: creating messages during reporting makes them not show up in CR? */
- msg = msg_message("nr_borderlist_postfix", "transparent object",
- e->transparent, e->name);
- bytes = (int)nr_render(msg, f->locale, bufp, size, f);
- msg_release(msg);
- if (wrptr(&bufp, &size, bytes) != 0)
- WARN_STATIC_BUFFER();
-
- *bufp = 0;
- paragraph(out, buf, 0, 0, 0);
}
if (edges) {
while (edges) {
@@ -1291,7 +1269,6 @@ static void statistics(struct stream *out, const region * r, const faction * f)
}
}
/* print */
- newline(out);
m = msg_message("nr_stat_header", "region", r);
nr_render(m, f->locale, buf, sizeof(buf), f);
msg_release(m);
@@ -2284,8 +2261,10 @@ report_plaintext(const char *filename, report_context * ctx,
report_travelthru(out, r, f);
}
- if (wants_stats && r->seen.mode >= seen_unit)
+ if (wants_stats && r->seen.mode >= seen_unit) {
statistics(out, r, f);
+ newline(out);
+ }
/* Nachrichten an REGION in der Region */
if (r->seen.mode >= seen_travel) {