server/src/common/kernel/render.c
Enno Rehling cfdbc32470 * möglichen Exploit beseitigt:
GIB xyz EINHEIT
  GIB 0 ALLES SILBER
  --> ALLE Befehle der übergebenen Einheit werden gelöscht.

* neue Funktion (convenience): ucansee(f, u1, u2)
  liefert u1, wenn cansee(f,u1), sonst u2

* neue mistakes eingefügt uns übersetzt

* message bugfix:
  u->htisorder kann gelöscht werden (z.b. NACH). Daher muss ein pointer auf
  einen befehl in einer message immer auf den u->order Eintrag zeigen, damit er
  zeit der auswertung noch existiert.
  findorder(u, u->thisorder) tut das. Ist an mehreren Stellen nicht benutzt
  worden. assert eingebaut, das das prüft.

* RESERVE_DONATIONS
  Gegenstände, die von einer anderen Partei übergeben wurden, werden nicht
  reserviert.

* TWOPASS_GIVE:
  GIB Befehle werden zuerst an fremde Einheiten, danach in einem zweiten
  Durchlauf an eigene Einheiten, ausgeführt.

* msg_message
  An einigen messages ausprobiert, ob man die gleiche Message mehreren
  Parteien einhängen kann - klappt, spart Speicher.
  Allerdings fehlt dazu ein ordentliches memory-management (refcounter)
2001-05-10 05:50:52 +00:00

559 lines
14 KiB
C

/* vi: set ts=2:
*
*
* Eressea PB(E)M host Copyright (C) 1998-2000
* Christian Schlittchen (corwin@amber.kn-bremen.de)
* Katja Zedel (katze@felidae.kn-bremen.de)
* Henning Peters (faroul@beyond.kn-bremen.de)
* Enno Rehling (enno@eressea-pbem.de)
* Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
*
* based on:
*
* Atlantis v1.0 13 September 1993 Copyright 1993 by Russell Wallace
* Atlantis v1.7 Copyright 1996 by Alex Schröder
*
* This program may not be used, modified or distributed without
* prior permission by the authors of Eressea.
* This program may not be sold or used commercially without prior written
* permission from the authors.
*/
#include <config.h>
#include "eressea.h"
#include "render.h"
#include "message.h"
#ifdef OLD_MESSAGES
#include "item.h"
#include "faction.h"
#include "language.h"
#include "goodies.h"
#include "region.h"
#include "unit.h"
#include "building.h"
#include "ship.h"
#include "reports.h"
#include "karma.h"
#include "unit.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#define RMAXHASH 64
int lastint = 0;
typedef struct localizer {
struct localizer * nexthash;
unsigned int hashkey;
const locale * lang;
struct renderer * renderers[RMAXHASH];
struct eval * evaluators[RMAXHASH];
} localizer;
typedef const char* (*eval_fun)(const locale * lang, void *);
typedef char * (*render_fun)(const message * m, const locale * lang);
typedef struct renderer {
unsigned int hashkey;
render_fun fun;
char * name;
struct renderer * nexthash;
} renderer;
typedef struct eval {
unsigned int hashkey;
eval_fun fun;
const char * name;
struct eval * nexthash;
} eval;
#define LMAXHASH 32
localizer * localizers[LMAXHASH];
static localizer *
get_localizer(const locale * lang)
{
unsigned int hkey = locale_hashkey(lang);
int id = hkey % LMAXHASH;
localizer * find = localizers[id];
while (find && find->lang!=lang) find = find->nexthash;
if (!find) {
find = calloc(sizeof(localizer), 1);
find->nexthash = localizers[id];
localizers[id] = find;
find->hashkey = hkey;
find->lang = lang;
}
return find;
}
void
add_renderfun(const char * name, localizer * l, render_fun fun)
{
unsigned int hkey = hashstring(name);
unsigned int id = hkey % RMAXHASH;
renderer * find = l->renderers[id];
while (find && find->hashkey!=hkey) find=find->nexthash;
if (!find) {
find = calloc(1, sizeof(renderer));
find->nexthash = l->renderers[id];
l->renderers[id] = find;
find->hashkey = hkey;
find->fun = fun;
find->name = strdup(name);
}
else assert(!strcmp(find->name, name));
}
void
add_evalfun(const char * name, localizer * l, eval_fun fun)
{
unsigned int hkey = hashstring(name);
unsigned int id = hkey % RMAXHASH;
eval * find = l->evaluators[id];
while (find && find->hashkey!=hkey) find=find->nexthash;
if (!find) {
find = calloc(1, sizeof(eval));
find->nexthash = l->evaluators[id];
l->evaluators[id] = find;
find->hashkey = hkey;
find->fun = fun;
find->name = strdup(name);
}
else assert(!strcmp(find->name, name));
}
void
render_cleanup(void)
{
int i;
for (i=0;i!=LMAXHASH;++i) {
while (localizers[i]) {
int s;
localizer * l = localizers[i]->nexthash;
#if 0
for (s=0;s!=SMAXHASH;++s) {
while (localizers[i]->strings[s]) {
struct locale_string * ls = localizers[i]->strings[s]->nexthash;
free(localizers[i]->strings[s]);
localizers[i]->strings[s]=ls;
}
}
#endif
for (s=0;s!=RMAXHASH;++s) {
while (localizers[i]->evaluators[s]) {
eval * e = localizers[i]->evaluators[s]->nexthash;
free(localizers[i]->evaluators[s]);
localizers[i]->evaluators[s] = e;
}
}
for (s=0;s!=RMAXHASH;++s) {
while (localizers[i]->renderers[s]) {
renderer * e = localizers[i]->renderers[s]->nexthash;
free(localizers[i]->renderers[s]);
localizers[i]->renderers[s] = e;
}
}
free(localizers[i]);
localizers[i] = l;
}
}
}
static const char *
v_unit(const locale * l, void * data) {
unit * u = (void*)data;
if (u) return unitname(u);
return locale_string(l, "unknownunit");
}
static const char *
v_of_de(const locale * l, void * data) {
int i = (int)data;
if (!i || i==INT_MAX || i==lastint) return "";
sprintf(buf, "von %d ", i);
return buf;
}
static const char *
v_sink_de(const locale * l, void * data) {
int i = (int)data;
unused(l);
return i?" und sank":"";
}
static const char *
v_mage_de(const locale * l, void * data) {
int i = (int)data;
unused(l);
/* TODO: i==1 getarnt, wilder Hack */
return i>1?unitname((unit*)i):"Jemand";
}
static const char *
v_unknown(const locale * l, void * data) {
unused(l);
unused(data);
/* TODO: i==1 getarnt, wilder Hack */
return "[an error occured]";
}
const char *
v_dir(const locale * l, void * data) {
int i = (int)data;
static const char* dres[6] = { "northwest", "northeast", "east",
"southeast", "southwest", "west" };
/* static char buffer[10]; */
return locale_string(l, dres[i]);
}
static char rbuf[8192];
static char *
render_immediate(const message * m, const char * find, localizer * l)
{
char * b = rbuf;
const char * p = find;
while (*p) {
while (*p && *p!='{') *b++ = *p++;
if (*p=='{') {
static char token[128];
static char function[128];
char * t = token;
char * f = function;
eval_fun fun = NULL;
const char * var = NULL;
int i = 0;
struct entry * e = m->type->entries;
++p;
if (*p=='$') {
unsigned int key;
eval * e;
++p;
while (*p!=' ') *f++ = *p++;
*f = 0;
key = hashstring(function);
for (e = l->evaluators[key % RMAXHASH];e;e=e->nexthash) {
if (!strcmp(e->name, function)) break;
}
if (e==NULL) {
/* in default gucken */
localizer * l = get_localizer(find_locale("de"));
for (e = l->evaluators[key % RMAXHASH];e;e=e->nexthash) {
if (!strcmp(e->name, function)) break;
}
if (!e) {
fun = v_unknown;
fprintf(stderr, "WARNING: unknown function for rendering %s\n", function);
}
else fun = e->fun;
}
else fun = e->fun;
++p;
}
while (*p!='}') {
*t++ = *p++;
}
*t = 0;
++p;
while (e && strcmp(e->name, token)) {
e = e->next;
++i;
}
if (fun) var = fun(l->lang, m->data[i]);
else if (e) switch (e->type) {
case IT_FACTION:
var = factionname((faction*)m->data[i]);
break;
case IT_UNIT:
/* TODO: Grammatik falsch. Groß/Kleinschreibung */
if (m->data[i] == (void *)&u_peasants) {
if (b == rbuf) {
var = "Die Bevölkerung";
} else {
var = "die Bevölkerung";
}
} else if(m->data[i] == NULL || m->data[i] == &u_unknown) {
if (b == rbuf) {
var = "Eine unerkannte Einheit";
} else {
var = "eine unerkannte Einheit";
}
} else {
var = unitname((unit*)m->data[i]);
}
break;
case IT_REGION:
if (m->data[i]) var = tregionid((region*)m->data[i], m->receiver);
else var="eine unbekannte Region";
break;
case IT_SHIP:
var = shipname((ship*)m->data[i]);
break;
case IT_BUILDING:
var = buildingname((building*)m->data[i]);
break;
case IT_RESOURCETYPE:
var = locale_string(l->lang, resourcename((const resource_type *)m->data[i], (lastint==1)?0:GR_PLURAL));
break;
case IT_RESOURCE:
var = resname((resource_t)m->data[i], lastint!=1);
break;
case IT_SKILL:
var = skillnames[(int)m->data[i]];
break;
case IT_INT:
sprintf(token, "%d", (int)m->data[i]);
var = token;
lastint = (int)m->data[i];
break;
case IT_STRING:
var = (char*)m->data[i];
break;
case IT_DIRECTION:
var = directions[(int)m->data[i]];
break;
case IT_FSPECIAL:
var = fspecials[(int)m->data[i]].name;
break;
} else {
assert(!"unknown token");
}
while (*var) *b++ = *var++;
}
}
*b = 0;
return rbuf;
}
void
rendercr(FILE * f, messagetype * mt, const locale * lang)
{
fprintf(f, "\"%s\";%u\n", locale_string(lang, mt->name), mt->hashkey);
}
static char *
render_default(const message * m, const locale * lang)
{
const char * str = locale_string(lang, m->type->name);
if (!str) {
fprintf(stderr, "WARNUNG: fehlende message %s\n", m->type->name);
return m->type->name;
}
else return render_immediate(m, str, get_localizer(lang));
}
static render_fun
get_renderfun(const char * name, const localizer * l)
{
unsigned int hkey = hashstring(name);
unsigned int id = hkey % RMAXHASH;
renderer * find = l->renderers[id];
while (find && find->hashkey!=hkey) find=find->nexthash;
if (find && !strcmp(find->name, name)) return find->fun;
return &render_default;
}
const char *
render(const message * m, const locale * lang)
{
localizer * loc = get_localizer(lang);
char * s;
render_fun f;
f = get_renderfun(m->type->name, loc);
s = f(m, lang);
return s;
}
static char *
render_income(const message * m, const locale * lang)
{
static const char * special[] = {"income_work","income_entertainment","income_tax","income_trade","income_tradetax", "income_steal", "income_magic"};
static const char * special2[] = {"income_work_reduced","income_entertainment_reduced","income_tax_reduced","income_trade","income_tradetax", "income_steal_reduced", "income_magic_reduced"};
int want=0, have=0, type = -1;
const char * key;
localizer * l = get_localizer(lang);
struct entry * e = m->type->entries;
const char * ls;
int i = 0;
if (!l) return render_default(m, lang);
while (e) {
switch(e->type) {
case IT_INT:
if (!strcmp(e->name, "amount")) have = (int)m->data[i];
else if (!strcmp(e->name, "wanted")) want = (int)m->data[i];
else if (!strcmp(e->name, "mode")) type = (int)m->data[i];
else assert(0);
break;
case IT_REGION:
case IT_UNIT:
/* part of the message, but safely ignored here */
break;
default:
assert(0);
}
++i;
e = e->next;
}
if (type<0) return render_default(m, lang);
if (want!=have) key = special2[type];
else key = special[type];
ls = locale_string(lang, key);
return render_immediate(m, ls, l);
}
static char *
de_render_casualties(const message * m, const locale * lang)
{
/*
* casualties;de;{unit} verlor {fallen} Personen, {alive} überlebten und {run} flohen nach {runto}.
*/
int i, fallen=0, alive=0, run=0;
region * runto = NULL;
unit * u = NULL;
char * sbuf;
struct entry * e = m->type->entries;
for(i=0;e;e=e->next, ++i) {
switch(e->type) {
case IT_INT:
if (!strcmp(e->name, "fallen")) fallen = (int)m->data[i];
else if (!strcmp(e->name, "run")) run = (int)m->data[i];
else if (!strcmp(e->name, "alive")) alive = (int)m->data[i];
else assert(0);
break;
case IT_REGION:
if (!strcmp(e->name, "runto")) runto = (region*)m->data[i];
else assert(0);
break;
case IT_UNIT:
if (!strcmp(e->name, "unit")) u = (unit*)m->data[i];
else assert(0);
break;
default:
assert(!"message format of 'casualties' is broken");
}
}
sbuf = rbuf;
if (fallen) {
sbuf = strcpy(sbuf, unitname(u)) + strlen(sbuf);
sbuf += sprintf(sbuf, " verlor %d Person%s", fallen, fallen==1?"":"en");
if (alive) sbuf += sprintf(sbuf, ", %d überlebt%s", alive, alive==1?"e":"en");
if (run) sbuf += sprintf(sbuf, " und %d floh%s nach %s", run, run==1?"":"en", tregionid(runto, m->receiver));
} else {
if (alive && run) {
sprintf(sbuf, "%d Person%s aus %s überlebt%s und %d floh%s nach %s",
alive, alive==1?"":"en",
unitname(u), alive==1?"e":"en",
run, run==1?"":"en",
tregionid(runto, m->receiver));
} else if (run) {
sprintf(sbuf, "%d Person%s aus %s floh%s nach %s",
run, run==1?"":"en", unitname(u), run==1?"":"en",
tregionid(runto, m->receiver));
}
}
strcat(sbuf, ".");
return rbuf;
}
static const char *
v_travel(const locale * l, void * data)
{
int i = (int)data;
unused(l);
switch(i) {
case 0: return "flieht";
case 1: return "reitet";
default: return "wandert";
}
}
static const char *
v_travelthru_de(const locale * l, void * data) {
char * c = (char*)data;
unused(l);
if (c && strlen(c)) sprintf(buf," Dabei wurde %s durchquert.", c);
else return "";
return buf;
}
void
render_init(void)
{
localizer * loc;
locale * lang = find_locale("de");
if (lang==NULL) lang = make_locale("de");
loc = get_localizer(lang);
add_renderfun("income", loc, render_income);
add_renderfun("casualties", loc, de_render_casualties);
add_evalfun("dir", loc, v_dir);
add_evalfun("dir", loc, v_dir);
add_evalfun("travel", loc, v_travel);
add_evalfun("unit", loc, v_unit);
add_evalfun("of", loc, v_of_de);
add_evalfun("sink", loc, v_sink_de);
add_evalfun("mage", loc, v_mage_de);
add_evalfun("travelthru", loc, v_travelthru_de);
lang = find_locale("en");
if (lang==NULL) lang = make_locale("en");
loc = get_localizer(lang);
add_renderfun("income", loc, render_income);
}
void
read_messages(FILE * F, const struct locale * deflocale)
{
unused(deflocale);
while (fgets(buf, 8192, F)) {
char * b = buf;
char * name = b;
char * language;
struct locale * lang;
char * section = NULL;
int level = 0;
messagetype * mtype;
buf[strlen(buf)-1] = 0; /* \n weg */
if (buf[0]=='#' || buf[0]==0) continue;
while (*b && *b!=';') ++b;
if (!*b) continue;
*b++ = 0;
section = b;
while (*b && *b!=';' && *b!=':') ++b;
if (!strcmp(section, "none")) section=NULL;
if (*b==':') {
char * x;
*b++ = 0;
x = b;
while (*b && *b!=';') ++b;
*b++ = 0;
level=atoi(x);
} else {
level = 0;
*b++ = 0;
}
language = b;
while (*b && *b!=';') ++b;
*b++ = 0;
lang = find_locale(language);
if (!lang) lang = make_locale(language);
mtype = find_messagetype(name);
if (!mtype) mtype = new_messagetype(name, level, section);
else if (section && !mtype->section) {
mtype->section = mc_add(section);
mtype->level = level;
}
locale_setstring(lang, name, b);
}
}
#endif