Compare commits

...
Sign in to create a new pull request.

20 commits

Author SHA1 Message Date
c313aafd8a WIP empdump, %a 2008-02-26 08:18:59 +01:00
add345f77f WIP empdump, fill in stuff not in xdump 2008-02-26 08:18:58 +01:00
4ef3574427 WIP empdump, can't shrink tables 2008-02-26 08:18:58 +01:00
b9c725224e Make xdump code available for future use outside the server
Move the bits useful there from src/lib/commands/xdump.c to new
include/xdump.h and src/lib/common/xdump.c.
2008-02-26 08:09:52 +01:00
d83dd2f5e4 Prepare some xdump code for future use outside the server
We don't have player->cnum, player->god and pr() there.  Pass
parameters instead.
2008-02-26 08:09:52 +01:00
3f15ea4bea Document tables EF_COMM, EF_TRADE and EF_COUNTRY have an owner
xdvisible() relies on that.

Set EFF_OWNER in the table definitions.  EFF_OWNER is not actually
used right now, so this doesn't fix anything broken.
2008-02-26 07:53:21 +01:00
1aa3cdb0ca Simplify unit_put() 2008-02-25 21:50:25 +01:00
e7e61687f4 Use get_empobj() instead of get_empobjp() in unit_map()
The get_FOOp() macros are generally avoided outside the update,
because direct access to the sector cache needs synchronization to be
safe.  unit_map() didn't access the cache directly until it was
converted from get_ship() & friends to get_empobjp() in commit
fec9878c.  Switching to get_empobj() reverts the change to direct
access while keeping the simplification.
2008-02-25 21:50:25 +01:00
9a1fef87fe Define common get/put macros for empobj
This turns existing functions get_empobjp() and put_empobj() into
equivalent macros.
2008-02-25 21:50:25 +01:00
5b7a6af94e Fix get_empobjp() not to reject EF_SECTOR
There's no technical reason for rejecting sector access by id.  It's
unusual, but not wrong.

Also remove the superfluous test for EF_BAD; ef_ptr() covers that.
2008-02-25 21:50:25 +01:00
e63f38833b Fix put_empobj() for types other than units
put_empobj() used struct empobj member uid, which is valid only for
units.  Existing users pass only units, fortunately.  Fix by making it
take type and uid parameters.
2008-02-25 21:50:25 +01:00
3117ef9c7a Give unit_map() internal linkage 2008-02-25 21:50:25 +01:00
2dea1b914d New realm selector timestamp
The timestamp always existed, but the selector was missing.  It's
needed for incremental xdumps.
2008-02-25 21:50:25 +01:00
38ead5e199 Clean up ugly line break 2008-02-25 21:50:24 +01:00
5a1cfeba95 Fix test for water in explore
This led to a bogus message when an interactive explore moved onto a
bridge and got prompted, the bridge was destroyed, and the player
stopped the explore "on the water".
2008-02-25 21:50:24 +01:00
fc807a4c0a Make sector types signed
get_empobj_chr() and emp_obj_chr_name() access struct sctstr member
sct_type through struct empobj member type.  This is technically
non-portable, because the two differ in signedness.  It was also
undocumented.  Fix by making sct_type signed.  sct_newtype as well,
for consistency.

map_char() uses unsigned char for a sector type argument.  Change that
to int.  Matches how this is done elsewhere.
2008-02-25 21:50:21 +01:00
f8a35dda1e Make xundump capable of extending tables
Use ef_ensure_space() in getobj().  This also makes sure objects are
properly initialized before undumping writes to them.

Clean up how sentinels are appended: instead of keeping its slot
reserved while undumping, keep it in the table, and strip it off when
done.
2008-02-24 08:40:08 +01:00
d4ac7d94b2 Generalize ef_extend() to non-file-backed tables
ef_extend() extended the file bypassing the cache, which screws up the
cache if it's EFF_MEM.  It fixed that by closing and reopening the
table.  Cheesy, and worked only for file-backed tables.

Rewrite ef_extend() to remap the cache properly for EFF_MEM.  While
there, simplify the !EFF_MEM case: steal a cache slot instead of
allocating a buffer.

Factor cache mapping out of ef_open() and ef_extend() into new
ef_remap_cache().
2008-02-24 08:40:05 +01:00
642c11eb64 Replace EFF_RDONLY by EFF_PRIVATE
Read-only was a bit of a misnomer: you could write to the table by
obtaining a pointer into it from ef_ptr(), you just couldn't write to
the backing file.

Semantic changes:

* ef_flush() is now allowed when the table is file-backed or privately
  mapped.  Before, it had to be file-backed.  Flushing a privately
  mapped table does nothing, just like flushing a read-only table did.

* ef_write() is now allowed when the table is file-backed or fully
  cached.  Before, it had to be file-backed and not read-only.
  Writing to a privately mapped file-backed table doesn't write to the
  file.

* ef_extend() is not implemented for privately mapped tables, just
  like it wasn't implemented for read-only tables.
2008-02-21 21:05:16 +01:00
53bcf04664 Remove obsolete comment
Obsolete since ef_open() locks the file (commit de124108).
2008-02-19 20:27:01 +01:00
24 changed files with 1142 additions and 362 deletions

View file

@ -104,7 +104,7 @@ deps := $(obj:.o=.d)
# Library archives:
libs := $(addprefix lib/, libcommon.a libas.a libgen.a libglobal.a)
# Programs:
util := $(addprefix src/util/, $(addsuffix $(EXEEXT), empsched fairland files pconfig))
util := $(addprefix src/util/, $(addsuffix $(EXEEXT), empdump empsched fairland files pconfig))
client := src/client/empire$(EXEEXT)
server := src/server/emp_server$(EXEEXT)
# Info subjects:

View file

@ -174,9 +174,10 @@ precisely specified. We use EBNF (ISO 14977) for syntax, except we
use '-' in meta-identifiers and omit the concatenation symbol ','.
table = header { record } footer ;
header = "XDUMP" space [ "meta" space ] name space timestamp newline ;
name = name-chr { name-chr } ;
name-chr = ? ASCII characters 33..126 ? ;
header = "XDUMP" space [ "meta" space ]
identifier space timestamp newline ;
identifier = id-chr { id-chr } ;
id-char = ? ASCII characters 33..126 except '"#()<>=' ? ;
timestamp = intnum ;
footer = "/" number newline ;
record = [ fields ] newline ;
@ -211,7 +212,7 @@ Notes:
Semantics:
* The table name in the header is one of the names in xdump table.
* The table identifier in the header is one of the names in xdump table.
* The timestamp increases monotonically. It has a noticeable
granularity: game state may change between an xdump and the next
@ -524,7 +525,7 @@ EBNF changes:
* Header and footer:
header = "config" name newline colhdr newline ;
header = "config" identifier newline colhdr newline ;
colhdr = { identifier [ "(" ( intnum | identifier ) ")" ] } [ "..." ] ;
footer = "/config" newline ;
@ -542,7 +543,7 @@ EBNF changes:
update.
- The column header is due to the self-containedness requirement.
It contains just the essential bit of meta-data: the column name.
It contains just the essential bit of meta-data: the column names.
* Symbolic fields:
@ -662,7 +663,7 @@ and section `Objectives':
enemy ships, nations).
(4) Bandwidth will be minimized (i.e. the format will be as
concise as possible) while remaining human readable (i.e. no
concise as possible) while remaining human-readable (i.e. no
binary messages). [Note that data compression may be added at a later
date, but if it is added, it will be added on a separate port to
maintain backwards compatability.]
@ -735,14 +736,14 @@ f. The data to sync is not readily available the server.
limited view on certain parts of server game state on certain
events.
To be complete, a machine readable protocol must disclose as much
information as the human readable output. Tracking server game
To be complete, a machine-readable protocol must disclose as much
information as the human-readable output. Tracking server game
state changes cannot do that alone. For instance, lookout tells
you ship#, owner and location. That event does not trigger any
state change on the server!
To be correct, a machine readable protocol must disclose no more
information than the human readable output. When you observe a
To be correct, a machine-readable protocol must disclose no more
information than the human-readable output. When you observe a
server game state change, you can only guess what event triggered
it, and what it disclosed to which player. You're stuck with
conservative assumptions. That's the death knell for completeness.
@ -761,7 +762,7 @@ a shortcut, and it didn't work. That doesn't mean there's no way at
all. I believe the only way to get this done right is by tracking
*events*. Whenever something is printed to a player, be it live
connection or telegram, we need to transmit precisely the same
information in machine readable form. Much more work.
information in machine-readable form. Much more work.
xdump shares valuable ideas with C_SYNC, e.g. using selector
meta-data. It is, however, much more modest in scope. We're pretty

View file

@ -41,9 +41,11 @@
#include "item.h"
struct comstr {
/* initial part must match struct empobj */
short ef_type;
natid com_owner;
short com_uid;
/* end of part matching struct empobj */
i_type com_type;
int com_amount;
float com_price;

View file

@ -56,7 +56,8 @@ struct empobj {
short uid;
coord x; /* is valid if EFF_XY is set in table def. */
coord y; /* is valid if EFF_XY is set in table def. */
signed char type;
signed char type; /* is valid for sectors and units */
/* remaining are valid for units */
signed char effic;
signed char mobil;
unsigned char off;
@ -88,9 +89,11 @@ union empobj_storage {
struct empobj_chr;
#define get_empobj(type, n, p) ef_read((type), (n), (p))
#define put_empobj(type, n, p) ef_write((type), (n), (p))
#define get_empobjp(type, n) ((struct empobj *)ef_ptr((type), (n)))
extern char *obj_nameof(struct empobj *gp);
extern struct empobj *get_empobjp(int type, int id);
extern int put_empobj(struct empobj *gp);
extern struct empobj_chr *get_empobj_chr(struct empobj *gp);
extern char *emp_obj_chr_name(struct empobj *gp);
extern int get_empobj_mob_max(int type);

View file

@ -57,7 +57,7 @@ struct empfile {
int cids; /* # entries in cache */
int fids; /* # entries in table */
int fd; /* file descriptor, -1 if not open */
/* flags bits EFF_RDONLY, EFF_CUSTOM also vary */
/* flags bits EFF_PRIVATE, EFF_CUSTOM also vary */
/* User callbacks */
void (*init)(int, void *); /* called after entry creation, unless null */
@ -86,8 +86,8 @@ struct empfile {
/* Flags set when table contents is mapped */
/* Table is entirely in memory */
#define EFF_MEM bit(8)
/* Table is read-only */
#define EFF_RDONLY bit(9)
/* Table is privately mapped: changes don't affect the underlying file */
#define EFF_PRIVATE bit(9)
/* Table is customized (configuration tables only) */
#define EFF_CUSTOM bit(10)
/* Transient flags, only occur in argument of ef_open() */
@ -97,7 +97,8 @@ struct empfile {
/*
* Empire `file types'
* These are really table IDs. Some tables are backed by files, some
* are compiled into the server.
* are compiled into the server, some initialized from configuration
* files.
*/
enum {
/* Error value */

View file

@ -315,8 +315,8 @@ extern int sct_typematch(char *);
extern int demand_update_want(int *, int *, int);
extern int demand_check(void);
extern int demandupdatecheck(void);
/* xundump.c */
extern int xundump(FILE *, char *, int *, int);
/* xdump.c xundump.c */
/* in xdump.h */
/*
* src/lib/gen/ *.c
@ -480,7 +480,6 @@ extern int islist(char *);
/* maps.c */
extern int do_map(int bmap, int unit_type, char *arg1, char *arg2);
extern int draw_map(int, char, int, struct nstr_sect *);
extern int unit_map(int, int, struct nstr_sect *, char *);
extern int display_region_map(int bmap, int unit_type, coord curx,
coord cury, char *arg);
extern int bmaps_intersect(natid, natid);

View file

@ -48,8 +48,8 @@ struct sctstr {
short sct_elev; /* elevation/depth */
coord sct_x; /* x coord of sector */
coord sct_y; /* y coord of sector */
signed char sct_type; /* sector type */
/* end of part matching struct empobj */
unsigned char sct_type; /* sector type */
unsigned char sct_effic; /* 0% to 100% */
short sct_mobil; /* mobility units */
unsigned char sct_loyal; /* updates until civilans "converted" */
@ -65,7 +65,7 @@ struct sctstr {
short sct_fill; /* gunk */
unsigned char sct_work; /* pct of civ actually working */
unsigned char sct_coastal; /* is this a coastal sector? */
unsigned char sct_newtype; /* for changing designations */
signed char sct_newtype; /* for changing designations */
unsigned char sct_min; /* ease of mining ore */
unsigned char sct_gmin; /* amount of gold ore */
unsigned char sct_fertil; /* fertility of soil */

View file

@ -41,9 +41,11 @@
#include "types.h"
struct trdstr {
/* initial part must match struct empobj */
short ef_type;
natid trd_owner;
short trd_uid;
/* end of part matching struct empobj */
short trd_type;
short trd_unitid;
long trd_price;

59
include/xdump.h Normal file
View file

@ -0,0 +1,59 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See files README, COPYING and CREDITS in the root of the source
* tree for related information and legal notices. It is expected
* that future projects/authors will amend these files as needed.
*
* ---
*
* xdump.h: Extended dumps
*
* Known contributors to this file:
* Markus Armbruster, 2008
*/
#ifndef XDUMP_H
#define XDUMP_H
#include <stdio.h>
#include "nsc.h"
#include "types.h"
/* xdump descriptor */
struct xdstr {
natid cnum; /* dump for this country */
int divine; /* is this a deity dump? */
int human; /* dump human-readable format */
void (*pr)(char *fmt, ...); /* callback for printing dump */
};
struct xdstr *xdinit(struct xdstr *, natid, int, void (*)(char *, ...));
extern void xdhdr(struct xdstr *, char *, int);
extern void xdcolhdr(struct xdstr *, struct castr[]);
extern void xdflds(struct xdstr *, struct castr[], void *);
extern struct valstr *xdeval(struct valstr *, struct xdstr *, nsc_type, void *, ptrdiff_t, int, int);
extern char *xdprval(struct xdstr *, struct valstr *, char *);
extern void xdftr(struct xdstr *, int);
extern int xdmeta(struct xdstr *, int);
extern int xundump(FILE *, char *, int *, int);
#endif

57
man/empdump.6 Normal file
View file

@ -0,0 +1,57 @@
.TH EMPDUMP 6
.SH NAME
empdump \- Export/import Empire game state
.SH SYNOPSIS
.B empdump
[
.B \-mtxhv
]
[
.BI \-e " configfile"
]
[
.I dump-file
]
.br
.SH DESCRIPTION
.B empdump
imports and exports game state as plain text.
.SH OPTIONS
.TP
.BI \-e " configfile"
Use game configuration in \fIconfigfile\fR.
.TP
.B \-h
Help. Print brief usage information and exit.
.TP
.TP
.B \-m
Use machine-readable format for export. Import always recognizes both
machine-readable and human-readable format.
.TP
.B \-t
Test import, don't update game state.
.TP
.B \-v
Print version information and exit.
.TP
.B \-x
Export game state to standard output.
.SH OPERANDS
.TP
.I dump-file
The file to import.
.SH "LIMITATIONS"
.B empdump
can't export player bmaps, power report, telegrams, announcements,
message of the day, no-login message and log files. Exported
floating-point values may be inexact. Importing an exported game
state may not result in identical data files; besides the loss of
floating-point precision just mentioned, coordinates are normalized,
and characters beyond a string's terminating zero in a character array
may be lost.
FIXME can't change file size, fix or document here
.SH "SEE ALSO"
\fIemp_server\fR(6).
.SH AUTHOR
Markus Armbruster <armbru@pond.sub.org>

View file

@ -196,7 +196,7 @@ explore(void)
putsect(&sect);
return RET_FAIL;
}
if (chksect.sct_type == '.') {
if (chksect.sct_type == SCT_WATER) {
pr("Bridge disappeared!\n");
getsect(start.sct_x, start.sct_y, &start);
start.sct_flags &= ~MOVE_IN_PROGRESS;

View file

@ -321,7 +321,7 @@ mission(void)
gp->mission = mission;
gp->opx = x;
gp->opy = y;
put_empobj(gp);
put_empobj(type, gp->uid, gp);
}
if (num == 0) {
pr("No %s%s\n", ef_nameof(type), splur(num));

View file

@ -74,8 +74,7 @@ nati(void)
mil = sect.sct_item[I_MILIT];
pr("%d%% eff %s at %s has %d civilian%s & %d military\n",
sect.sct_effic,
(sect.sct_type ==
SCT_CAPIT ? "capital" : "mountain capital"),
sect.sct_type == SCT_CAPIT ? "capital" : "mountain capital",
xyas(sect.sct_x, sect.sct_y, cnum), civ, splur(civ),
mil);
}

View file

@ -28,168 +28,17 @@
* xdump.c: Extended dump
*
* Known contributors to this file:
* Markus Armbruster, 2004-2007
* Markus Armbruster, 2004-2008
*/
/*
* Dump everything under the sun
*
* Static game data (configuration):
* - Item characteristics: ichr[]
* - Land unit characteristics: lchr[]
* - Nuke characteristics: nchr[]
* - Plane characteristics: plchr[]
* - Product characteristics: pchr[]
* - Sector designation characteristics: dchr[]
* - Sector infrastructure characteristics: intrchr[]
* - Ship characteristics: mchr[]
* - News item characteristics: rpt[]
* - News page headings: page_headings[]
* - Commands: player_coms[] (TODO)
* - Update schedule: update_time[] (not really static)
* - Configuration: configkeys[]
*
* Dynamic game data:
* - Sectors: EF_SECTOR (superseding dump)
* - Land units: EF_LAND (superseding ldump)
* - Lost: EF_LOST (superseding lost)
* - Nukes: EF_NUKE (superseding ndump)
* - Planes: EF_PLANE (superseding pdump)
* - Ships: EF_SHIP (superseding sdump)
* - News: EF_NEWS
* - Treaties: EF_TREATY
* - Power: EF_POWER (TODO)
* - Nations: EF_NATION
* - Loans: EF_LOAN
* - Map: EF_MAP (TODO)
* - Bmap: EF_BMAP (TODO)
* - Market: EF_COMM
*/
/*
* See doc/xdump for motivation, syntax, semantics, and rationale.
* Make sure to keep it up-to-date!
*/
/* TODO don't dump stuff that's useless due to options */
#include <config.h>
#include <ctype.h>
#include <stddef.h>
#include "commands.h"
#include "empobj.h"
#include "match.h"
#include "news.h"
#include "optlist.h"
#include "treaty.h"
#include "version.h"
/*
* Evaluate a attribute of an object into VAL, return VAL.
* TYPE is the attribute's type.
* PTR points to the context object.
* The attribute is stored there at offset OFF + IDX * S, where S is
* its size.
* LEN is the #array elements if it is an array, else zero.
*/
static struct valstr *
xdeval(struct valstr *val,
nsc_type type, void *ptr, ptrdiff_t off, int idx, int len)
{
val->val_type = type;
val->val_cat = NSC_OFF;
val->val_as.sym.off = off;
val->val_as.sym.len = len;
val->val_as.sym.idx = idx;
nstr_exec_val(val, player->cnum, ptr, NSC_NOTYPE);
return val; /* FIXME nstr_exec_val() should return VAL */
}
/* Dump VAL prefixed with SEP, return " ". */
static char *
xdprval(struct valstr *val, char *sep)
{
unsigned char *s, *e, *l;
switch (val->val_type) {
case NSC_LONG:
pr("%s%ld", sep, val->val_as.lng);
break;
case NSC_DOUBLE:
pr("%s%#g", sep, val->val_as.dbl);
break;
case NSC_STRING:
s = (unsigned char *)val->val_as.str.base;
if (s) {
pr("%s\"", sep);
l = s + val->val_as.str.maxsz;
/* FIXME maxsz == INT_MAX ! */
for (;;) {
for (e = s;
e < l && *e != '"' && *e != '\\' && isgraph(*e);
++e)
;
pr("%.*s", (int)(e-s), s);
if (e < l && *e)
pr("\\%03o", *e++);
else
break;
s = e;
}
pr("\"");
} else
pr("%snil", sep);
break;
default:
CANT_REACH();
pr("0");
}
return " ";
}
/*
* Dump field values of a context object.
* CA[] describes fields.
* PTR points to context object.
*/
static void
xdflds(struct castr ca[], void *ptr)
{
int i, j, n;
struct valstr val;
char *sep = "";
for (i = 0; ca[i].ca_name; ++i) {
if (ca[i].ca_flags & NSC_DEITY && !player->god)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
j = 0;
do {
xdeval(&val, ca[i].ca_type, ptr, ca[i].ca_off, j, ca[i].ca_len);
sep = xdprval(&val, sep);
} while (++j < n);
}
}
/*
* Dump header for dump NAME.
* If META, it's for the meta-data dump rather than the data dump.
*/
static void
xdhdr(char *name, int meta)
{
pr("XDUMP %s%s %ld\n", meta ? "meta " : "", name, (long)time(NULL));
}
/* Dump footer for a dump that dumped N objects. */
static void
xdftr(int n)
{
pr("/%d\n", n);
}
#include "xdump.h"
/*
* Is object P of type TYPE visible to the player?
@ -265,11 +114,11 @@ xdvisible(int type, void *p)
}
/*
* Dump items of type TYPE selected by ARG.
* Dump items of type TYPE selected by ARG to XD.
* Return RET_OK on success, RET_SYN on error.
*/
static int
xditem(int type, char *arg)
xditem(struct xdstr *xd, int type, char *arg)
{
struct castr *ca;
struct nstr_item ni;
@ -283,59 +132,29 @@ xditem(int type, char *arg)
if (!snxtitem(&ni, type, arg))
return RET_SYN;
xdhdr(ef_nameof(type), 0);
xdhdr(xd, ef_nameof(type), 0);
n = 0;
while (nxtitem(&ni, buf)) {
if (!xdvisible(type, buf))
continue;
++n;
xdflds(ca, buf);
pr("\n");
xdflds(xd, ca, buf);
xd->pr("\n");
}
xdftr(n);
xdftr(xd, n);
return RET_OK;
}
/*
* Dump meta-data for items of type TYPE.
* Dump configkeys[] to XD.
* If META, dump meta-data rather than data.
* Return RET_OK.
*/
static int
xdmeta(int type)
{
struct castr *ca = ef_cadef(type);
int i;
int n = 0;
if (!ca)
return RET_SYN;
xdhdr(ef_nameof(type), 1);
for (i = 0; ca[i].ca_name; i++) {
if (ca[i].ca_flags & NSC_DEITY && !player->god)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
xdflds(mdchr_ca, &ca[i]);
pr("\n");
n++;
}
xdftr(n);
return RET_OK;
}
/*
* Dump configkeys[], return RET_OK.
* If META, dump meta-data rather than data.
*/
static int
xdver(int meta)
xdver(struct xdstr *xd, int meta)
{
static struct castr vers_ca = {
NSC_STRINGY, 0, sizeof(PACKAGE_STRING), 0, "version", EF_BAD
@ -346,11 +165,11 @@ xdver(int meta)
struct castr ca;
struct valstr val;
xdhdr("version", meta);
xdhdr(xd, "version", meta);
if (meta) {
n = 0;
xdflds(mdchr_ca, &vers_ca);
xdflds(xd, mdchr_ca, &vers_ca);
pr("\n");
n++;
for (kp = configkeys; kp->km_key; ++kp) {
@ -361,39 +180,40 @@ xdver(int meta)
ca.ca_off = 0;
ca.ca_name = kp->km_key;
ca.ca_table = EF_BAD;
xdflds(mdchr_ca, &ca);
xdflds(xd, mdchr_ca, &ca);
pr("\n");
n++;
}
}
xdftr(n);
xdftr(xd, n);
return RET_OK;
}
xdeval(&val, vers_ca.ca_type, version, vers_ca.ca_off, 0, vers_ca.ca_len);
sep = xdprval(&val, "");
xdeval(&val, xd,
vers_ca.ca_type, version, vers_ca.ca_off, 0, vers_ca.ca_len);
sep = xdprval(xd, &val, "");
for (kp = configkeys; kp->km_key; ++kp) {
if (kp->km_type != NSC_NOTYPE && !(kp->km_flags & KM_INTERNAL)) {
xdeval(&val, kp->km_type, kp->km_data, 0, 0, 0);
sep = xdprval(&val, sep);
xdeval(&val, xd, kp->km_type, kp->km_data, 0, 0, 0);
sep = xdprval(xd, &val, sep);
}
}
pr("\n");
xdftr(1);
xdftr(xd, 1);
return RET_OK;
}
/* Experimental extended dump command */
int
xdump(void)
{
char *p;
char buf[1024];
struct xdstr xd;
struct natstr *natp;
int type;
int meta = 0;
struct natstr *natp;
p = getstarg(player->argp[1], "Table name, or meta? ", buf);
if (p && strcmp(p, "meta") == 0) {
@ -403,19 +223,20 @@ xdump(void)
if (!p || !*p)
return RET_SYN;
xdinit(&xd, player->cnum, 0, pr);
natp = getnatp(player->cnum);
type = isdigit(p[0]) ? atoi(p) : ef_byname(p);
if (type >= 0 && type < EF_MAX) {
if (meta)
return xdmeta(type);
return xdmeta(&xd, type);
else if ((EF_IS_GAME_STATE(type) || EF_IS_VIEW(type))
&& !(natp->nat_stat == STAT_ACTIVE || player->god)) {
pr("Access to table %s denied\n", ef_nameof(type));
return RET_FAIL;
} else
return xditem(type, player->argp[2]);
return xditem(&xd, type, player->argp[2]);
} else if (!strncmp(p, "ver", strlen(p))) {
return xdver(meta);
return xdver(&xd, meta);
}
return RET_SYN;

View file

@ -39,6 +39,7 @@
#include "optlist.h"
#include "product.h"
#include "prototypes.h"
#include "xdump.h"
static int read_custom_table_file(char *);

View file

@ -46,8 +46,10 @@
#include "nsc.h"
#include "prototypes.h"
static int ef_remap_cache(struct empfile *, int);
static int fillcache(struct empfile *, int);
static int do_write(struct empfile *, void *, int, int);
static void ef_blank(struct empfile *, void *, int, int);
/*
* Open the file-backed table TYPE (EF_SECTOR, ...).
@ -61,7 +63,7 @@ ef_open(int type, int how)
{
struct empfile *ep;
struct flock lock;
int oflags, fd, fsiz, size;
int oflags, fd, fsiz, nslots;
if (ef_check(type) < 0)
return 0;
@ -73,7 +75,7 @@ ef_open(int type, int how)
if (CANT_HAPPEN(ep->fd >= 0))
return 0;
oflags = O_RDWR;
if (how & EFF_RDONLY)
if (how & EFF_PRIVATE)
oflags = O_RDONLY;
if (how & EFF_CREATE)
oflags |= O_CREAT | O_TRUNC;
@ -85,7 +87,7 @@ ef_open(int type, int how)
return 0;
}
lock.l_type = how & EFF_RDONLY ? F_RDLCK : F_WRLCK;
lock.l_type = how & EFF_PRIVATE ? F_RDLCK : F_WRLCK;
lock.l_whence = SEEK_SET;
lock.l_start = lock.l_len = 0;
if (fcntl(fd, F_SETLK, &lock) == -1) {
@ -116,18 +118,13 @@ ef_open(int type, int how)
}
}
} else {
if (how & EFF_MEM)
ep->csize = ep->fids;
else
ep->csize = blksize(fd) / ep->size;
/* 0 could lead to null cache, which confuses assertions */
if (!ep->csize)
ep->csize++;
size = ep->csize * ep->size;
if (CANT_HAPPEN(ep->cache))
free(ep->cache);
ep->cache = malloc(size);
if (ep->cache == NULL) {
if (how & EFF_MEM)
nslots = ep->fids;
else
nslots = blksize(fd) / ep->size;
if (!ef_remap_cache(ep, nslots)) {
logerror("Can't open %s: out of memory", ep->file);
close(fd);
return 0;
@ -148,11 +145,31 @@ ef_open(int type, int how)
}
}
/*
* Could close fd if both EFF_RDONLY and EFF_MEM, but that doesn't
* happen, so don't bother.
*/
return 1;
}
static int
ef_remap_cache(struct empfile *ep, int nslots)
{
void *cache;
if (CANT_HAPPEN(ep->flags & EFF_STATIC))
return 0;
if (CANT_HAPPEN(nslots < 0))
nslots = 0;
/*
* Avoid zero slots, because that can lead to null cache, which
* would be interpreted as unmapped cache.
*/
if (nslots == 0)
nslots++;
cache = realloc(ep->cache, nslots * ep->size);
if (!cache)
return 0;
ep->cache = cache;
ep->csize = nslots;
return 1;
}
@ -182,7 +199,8 @@ ef_close(int type)
}
/*
* Flush file-backed table TYPE (EF_SECTOR, ...) to disk.
* Flush table TYPE (EF_SECTOR, ...) to disk.
* Does nothing if the table is privately mapped.
* Return non-zero on success, zero on failure.
*/
int
@ -193,6 +211,8 @@ ef_flush(int type)
if (ef_check(type) < 0)
return 0;
ep = &empfile[type];
if (ep->flags & EFF_PRIVATE)
return 1; /* nothing to do */
if (CANT_HAPPEN(ep->fd < 0))
return 0;
/*
@ -201,7 +221,7 @@ ef_flush(int type)
* allowed only with EFF_MEM. Assume the whole cash is dirty
* then.
*/
if (!(ep->flags & EFF_RDONLY) && (ep->flags & EFF_MEM))
if (ep->flags & EFF_MEM)
return do_write(ep, ep->cache, ep->baseid, ep->cids) >= 0;
return 1;
@ -316,7 +336,8 @@ do_write(struct empfile *ep, void *buf, int id, int count)
int n, ret;
char *p;
if (CANT_HAPPEN(ep->fd < 0 || id < 0 || count < 0))
if (CANT_HAPPEN(ep->fd < 0 || (ep->flags & EFF_PRIVATE)
|| id < 0 || count < 0))
return -1;
if (lseek(ep->fd, id * ep->size, SEEK_SET) == (off_t)-1) {
@ -344,9 +365,10 @@ do_write(struct empfile *ep, void *buf, int id, int count)
}
/*
* Write element ID into file-backed table TYPE from buffer FROM.
* Write element ID into table TYPE from buffer FROM.
* FIXME pass buffer size!
* Write through cache straight to disk.
* If table is file-backed and not privately mapped, write through
* cache straight to disk.
* Cannot write beyond the end of fully cached table (flags & EFF_MEM).
* Can write at the end of partially cached table.
* Return non-zero on success, zero on failure.
@ -360,12 +382,16 @@ ef_write(int type, int id, void *from)
if (ef_check(type) < 0)
return 0;
ep = &empfile[type];
if (CANT_HAPPEN((ep->flags & (EFF_MEM | EFF_PRIVATE)) == EFF_PRIVATE))
return 0;
if (ep->prewrite)
ep->prewrite(id, from);
if (CANT_HAPPEN((ep->flags & EFF_MEM) ? id >= ep->fids : id > ep->fids))
return 0; /* not implemented */
if (!(ep->flags & EFF_PRIVATE)) {
if (do_write(ep, from, id, 1) < 0)
return 0;
}
if (id >= ep->baseid && id < ep->baseid + ep->cids) {
/* update the cache if necessary */
to = ep->cache + (id - ep->baseid) * ep->size;
@ -380,44 +406,66 @@ ef_write(int type, int id, void *from)
}
/*
* Extend the file-backed table TYPE by COUNT elements.
* Extend table TYPE by COUNT elements.
* Any pointers obtained from ef_ptr() become invalid.
* Return non-zero on success, zero on failure.
*/
int
ef_extend(int type, int count)
{
struct empfile *ep;
void *tmpobj;
int id, i, how;
char *p;
int i, id;
if (ef_check(type) < 0)
return 0;
ep = &empfile[type];
if (CANT_HAPPEN(ep->fd < 0 || count < 0))
if (CANT_HAPPEN(count < 0))
return 0;
tmpobj = calloc(1, ep->size);
id = ep->fids;
for (i = 0; i < count; i++) {
if (ep->init)
ep->init(id + i, tmpobj);
if (do_write(ep, tmpobj, id + i, 1) < 0)
break;
}
free(tmpobj);
if (ep->flags & EFF_MEM) {
/* FIXME lazy bastards... do this right */
/* XXX this will cause problems if there are ef_ptrs (to the
* old allocated structure) active when we do the re-open */
how = ep->flags & ~EFF_IMMUTABLE;
ef_close(type);
ef_open(type, how);
} else {
ep->fids += i;
if (id + count > ep->csize) {
if (ep->flags & EFF_STATIC)
return 0;
if (!ef_remap_cache(ep, id + count))
return 0;
}
p = ep->cache + id * ep->size;
ef_blank(ep, p, id, count);
if (!(ep->flags & EFF_PRIVATE)) {
if (do_write(ep, p, id, count) < 0)
return 0;
}
ep->cids += count;
} else {
/* need a buffer, steal last cache slot */
if (ep->cids == ep->csize)
ep->cids--;
p = ep->cache + ep->cids * ep->size;
for (i = 0; i < count; i++) {
ef_blank(ep, p, id + i, 1);
if (do_write(ep, p, id + i, 1) < 0)
return 0;
}
}
ep->fids += count;
return 1;
}
return i == count;
/*
* Initialize COUNT elements of EP in BUF, starting with element ID.
*/
static void
ef_blank(struct empfile *ep, void *buf, int id, int count)
{
int i;
memset(buf, 0, count * ep->size);
if (ep->init) {
for (i = 0; i < count; i++)
ep->init(id + i, (char *)buf + i * ep->size);
}
}
struct castr *

351
src/lib/common/xdump.c Normal file
View file

@ -0,0 +1,351 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See files README, COPYING and CREDITS in the root of the source
* tree for related information and legal notices. It is expected
* that future projects/authors will amend these files as needed.
*
* ---
*
* xdump.c: Extended dumps
*
* Known contributors to this file:
* Markus Armbruster, 2008
*/
/*
* Dump everything under the sun
*
* Static game data (configuration):
* - Item characteristics: ichr[]
* - Land unit characteristics: lchr[]
* - Nuke characteristics: nchr[]
* - Plane characteristics: plchr[]
* - Product characteristics: pchr[]
* - Sector designation characteristics: dchr[]
* - Sector infrastructure characteristics: intrchr[]
* - Ship characteristics: mchr[]
* - News item characteristics: rpt[]
* - News page headings: page_headings[]
* - Commands: player_coms[] (TODO)
* - Update schedule: update_time[] (not really static)
* - Configuration: configkeys[]
*
* Dynamic game data:
* - Sectors: EF_SECTOR (superseding dump)
* - Land units: EF_LAND (superseding ldump)
* - Lost: EF_LOST (superseding lost)
* - Nukes: EF_NUKE (superseding ndump)
* - Planes: EF_PLANE (superseding pdump)
* - Ships: EF_SHIP (superseding sdump)
* - News: EF_NEWS
* - Treaties: EF_TREATY
* - Power: EF_POWER (TODO)
* - Nations: EF_NATION
* - Loans: EF_LOAN
* - Map: EF_MAP (TODO)
* - Bmap: EF_BMAP (TODO)
* - Market: EF_COMM
*/
/*
* See doc/xdump for motivation, syntax, semantics, and rationale.
* Make sure to keep it up-to-date!
*/
/* TODO don't dump stuff that's useless due to options */
#include <config.h>
#include <ctype.h>
#include <limits.h>
#include "file.h"
#include "nat.h"
#include "xdump.h"
/*
* Initialize XD to dump for country CNUM.
* If HUMAN, dump in human-readable format.
* Dump is to be delivered through callback PR.
* Return XD.
*/
struct xdstr *
xdinit(struct xdstr *xd, natid cnum, int human, void (*pr)(char *fmt, ...))
{
xd->cnum = cnum;
xd->divine = getnatp(cnum)->nat_stat == STAT_GOD;
xd->human = human;
xd->pr = pr;
return xd;
}
/*
* Evaluate a attribute of an object into VAL, return VAL.
* XD is the xdump descriptor.
* TYPE is the attribute's type.
* PTR points to the context object.
* The attribute is stored there at offset OFF + IDX * S, where S is
* its size.
* LEN is the #array elements if it is an array, else zero.
*/
struct valstr *
xdeval(struct valstr *val, struct xdstr *xd,
nsc_type type, void *ptr, ptrdiff_t off, int idx, int len)
{
val->val_type = type;
val->val_cat = NSC_OFF;
val->val_as.sym.off = off;
val->val_as.sym.len = len;
val->val_as.sym.idx = idx;
nstr_exec_val(val, xd->cnum, ptr, NSC_NOTYPE);
return val; /* FIXME nstr_exec_val() should return VAL */
}
/*
* Dump string STR to XD with funny characters escaped.
* Dump at most N characters.
*/
static void
xdpresc(struct xdstr *xd, char *str, size_t n)
{
unsigned char *s, *e, *l;
s = (unsigned char *)str;
l = s + n;
for (;;) {
for (e = s;
e < l && *e != '"' && *e != '\\' && isgraph(*e);
++e)
;
xd->pr("%.*s", (int)(e-s), s);
if (e < l && *e)
xd->pr("\\%03o", *e++);
else
break;
s = e;
}
}
/* Dump VAL in machine readable format, prefixed with SEP, return " ". */
static char *
xdprval_nosym(struct xdstr *xd, struct valstr *val, char *sep)
{
switch (val->val_type) {
case NSC_LONG:
xd->pr("%s%ld", sep, val->val_as.lng);
break;
case NSC_DOUBLE:
#if 1 /* TODO have %a */
xd->pr("%s%a", sep, val->val_as.dbl);
#else
xd->pr("%s%#g", sep, val->val_as.dbl);
#endif
break;
case NSC_STRING:
if (val->val_as.str.base) {
xd->pr("%s\"", sep);
xdpresc(xd, val->val_as.str.base, val->val_as.str.maxsz);
xd->pr("\"");
} else
xd->pr("%snil", sep);
break;
default:
CANT_REACH();
xd->pr("0");
}
return " ";
}
/*
* Dump symbol with value KEY from symbol table TYPE to XD.
* Prefix with SEP, return " ".
*/
static char *
xdprsym(struct xdstr *xd, int key, int type, char *sep)
{
char *sym = symbol_by_value(key, ef_ptr(type, 0));
if (CANT_HAPPEN(!sym))
xd->pr("%s%ld", sep, key);
else {
xd->pr("%s", sep);
xdpresc(xd, sym, INT_MAX);
}
return " ";
}
/*
* Dump VAL prefixed with SEP, return " ".
* CA describes the field from which the value was fetched.
*/
static char *
xdprval_sym(struct xdstr *xd, struct valstr *val, struct castr *ca, char *sep)
{
unsigned long bit;
struct castr *ca_sym;
char *sym;
if (xd->human && val->val_type == NSC_LONG && ca->ca_table != EF_BAD) {
ca_sym = ef_cadef(ca->ca_table);
if (ca_sym != symbol_ca)
;
else if (ca->ca_flags & NSC_BITS) {
xd->pr("%s(", sep);
sep = "";
for (bit = 1; bit; bit <<= 1) {
if (bit & val->val_as.lng)
sep = xdprsym(xd, bit, ca->ca_table, sep);
}
xd->pr(")");
return " ";
} else
return xdprsym(xd, val->val_as.lng, ca->ca_table, sep);
}
return xdprval_nosym(xd, val, sep);
}
/*
* Dump VAL prefixed with SEP, return " ".
* XD must not be human-readable.
*/
char *
xdprval(struct xdstr *xd, struct valstr *val, char *sep)
{
CANT_HAPPEN(xd->human);
return xdprval_nosym(xd, val, sep);
}
/*
* Dump field values of a context object to XD.
* CA[] describes fields.
* PTR points to context object.
*/
void
xdflds(struct xdstr *xd, struct castr ca[], void *ptr)
{
int i, j, n;
struct valstr val;
char *sep = "";
for (i = 0; ca[i].ca_name; ++i) {
if (ca[i].ca_flags & NSC_DEITY && !xd->divine)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
j = 0;
do {
xdeval(&val, xd,
ca[i].ca_type, ptr, ca[i].ca_off, j, ca[i].ca_len);
sep = xdprval_sym(xd, &val, &ca[i], sep);
} while (++j < n);
}
}
/*
* Dump header for dump NAME to XD.
* If META, it's for the meta-data dump rather than the data dump.
*/
void
xdhdr(struct xdstr *xd, char *name, int meta)
{
if (xd->human) {
xd->pr("config %s\n", name);
} else
xd->pr("XDUMP %s%s %ld\n",
meta ? "meta " : "", name, (long)time(NULL));
}
/*
* Dump column header to XD.
* CA[] describes fields.
* Does nothing unless XD is human-readable.
*/
void
xdcolhdr(struct xdstr *xd, struct castr ca[])
{
int i, j, n;
char *sep = "";
if (!xd->human)
return;
for (i = 0; ca[i].ca_name; ++i) {
if (ca[i].ca_flags & NSC_DEITY && !xd->divine)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
n = ca[i].ca_type != NSC_STRINGY ? ca[i].ca_len : 0;
if (n) {
for (j = 0; j < n; j++) {
xd->pr("%s%s(%d)",sep, ca[i].ca_name, j);
sep = " ";
}
} else {
xd->pr("%s%s", sep, ca[i].ca_name);
sep = " ";
}
}
xd->pr("\n");
}
/* Dump footer for a dump that dumped N objects to XD. */
void
xdftr(struct xdstr *xd, int n)
{
if (xd->human)
xd->pr("/config\n");
else
xd->pr("/%d\n", n);
}
/*
* Dump meta-data for items of type TYPE to XD.
* Return RET_OK.
*/
int
xdmeta(struct xdstr *xd, int type)
{
struct castr *ca = ef_cadef(type);
int i;
int n = 0;
if (!ca)
return RET_SYN;
xdhdr(xd, ef_nameof(type), 1);
xdcolhdr(xd, ca);
for (i = 0; ca[i].ca_name; i++) {
if (ca[i].ca_flags & NSC_DEITY && !xd->divine)
continue;
if (ca[i].ca_flags & NSC_EXTRA)
continue;
xdflds(xd, mdchr_ca, &ca[i]);
xd->pr("\n");
n++;
}
xdftr(xd, n);
return RET_OK;
}

View file

@ -56,12 +56,14 @@
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "file.h"
#include "match.h"
#include "nsc.h"
#include "optlist.h"
#include "prototypes.h"
#include "xdump.h"
static char *fname; /* Name of file being read */
static int lineno; /* Current line number */
@ -130,21 +132,6 @@ skipfs(FILE *fp)
return ch;
}
/*
* Read an identifier from FP into BUF.
* BUF must have space for 1024 characters.
* Return number of characters read on success, -1 on failure.
*/
static int
getid(FILE *fp, char *buf)
{
int n;
if (fscanf(fp, "%1023[^#()<>=#\" \t\n]%n", buf, &n) != 1
|| !isalpha(buf[0]))
return -1;
return n;
}
/*
* Decode escape sequences in BUF.
* Return BUF on success, null pointer on failure.
@ -170,6 +157,22 @@ xuesc(char *buf)
return buf;
}
/*
* Read an identifier from FP into BUF.
* BUF must have space for 1024 characters.
* Return number of characters read on success, -1 on failure.
*/
static int
getid(FILE *fp, char *buf)
{
int n;
if (fscanf(fp, "%1023[^\"#()<>= \t\n]%n", buf, &n) != 1
|| !isalpha(buf[0]))
return -1;
xuesc(buf);
return n;
}
/*
* Try to read a field name from FP.
* I is the field number, counting from zero.
@ -479,17 +482,20 @@ getobj(void)
int need_sentinel = !EF_IS_GAME_STATE(cur_type);
if (!cur_obj) {
cur_obj_is_blank = cur_id >= ep->fids;
cur_obj_is_blank = cur_id >= ep->fids - !!need_sentinel;
if (cur_obj_is_blank) {
/* TODO grow cache (and possibly file) unless EFF_STATIC */
if (cur_id < ep->csize - !!need_sentinel)
ep->cids = ep->fids = cur_id + 1;
/* else: ef_ptr() will fail */
}
if (ef_ensure_space(cur_type, cur_id + !!need_sentinel, 1))
cur_obj = ef_ptr(cur_type, cur_id);
/* FIXME diagnose out of dynamic memory vs. static table full */
if (!cur_obj)
gripe("Can't put ID %d into table %s, it holds only 0..%d.",
cur_id, ep->name, ep->fids - 1);
cur_id, ep->name, ep->fids - !!need_sentinel - 1);
} else
cur_obj = ef_ptr(cur_type, cur_id);
/* TODO find a less ugly solution */
if (EF_IS_GAME_STATE(cur_type) && cur_type != EF_POWER &&
cur_type != EF_MAP && cur_type != EF_BMAP)
*((short *)cur_obj) = cur_type;
}
return cur_obj;
@ -958,8 +964,12 @@ xubody(FILE *fp)
{
struct empfile *ep = &empfile[cur_type];
int need_sentinel = !EF_IS_GAME_STATE(cur_type);
int old_maxid = ep->fids;
int i, maxid, ch;
if (old_maxid == 0 && need_sentinel)
ef_ensure_space(cur_type, 0, 1);
maxid = 0;
for (i = 0;; ++i) {
while ((ch = skipfs(fp)) == '\n')
@ -974,6 +984,12 @@ xubody(FILE *fp)
maxid = MAX(maxid, cur_id + 1);
}
if (maxid >= old_maxid && need_sentinel) {
/* appended a sentinel, strip it off */
ep->fids--;
ep->cids--;
}
if (CANT_HAPPEN(maxid > ep->fids))
maxid = ep->fids;
if (maxid < ep->fids) {
@ -988,11 +1004,5 @@ xubody(FILE *fp)
ef_nameof(cur_type), ep->fids, maxid);
}
if (need_sentinel) {
if (CANT_HAPPEN(maxid >= ep->csize))
return gripe("No space for sentinel");
memset(ep->cache + ep->size * maxid, 0, ep->size);
}
return i;
}

View file

@ -85,7 +85,7 @@
SZ((array)), 0, SZ((array)) - 1, SZ((array)) - 1, -1, NULL, NULL, NULL
/* Common configuration table flags */
#define EFF_CFG (EFF_RDONLY | EFF_MEM | EFF_STATIC)
#define EFF_CFG (EFF_PRIVATE | EFF_MEM | EFF_STATIC)
struct empfile empfile[] = {
/*
@ -95,7 +95,7 @@ struct empfile empfile[] = {
* bits of flags get their final value.
* If flags & EFF_STATIC, the cache is mapped here, and members
* cache, csize get their final value.
* Members baseid, cids, fids and the EFF_MEM|EFF_RDONLY bits of
* Members baseid, cids, fids and the EFF_MEM|EFF_PRIVATE bits of
* flags are initialized according the initial cache contents.
* Member fd is initialized to -1.
* Members init, postread, prewrite get initialized to NULL, but
@ -131,7 +131,7 @@ struct empfile empfile[] = {
{EF_TREATY, "treaty", "treaty", treaty_ca,
UNMAPPED_CACHE(struct trtstr, 0)},
{EF_TRADE, "trade", "trade", trade_ca,
UNMAPPED_CACHE(struct trdstr, 0)},
UNMAPPED_CACHE(struct trdstr, EFF_OWNER)},
{EF_POWER, "pow", "power", NULL,
UNMAPPED_CACHE(struct powstr, 0)},
{EF_NATION, "nat", "nation", nat_ca,
@ -143,7 +143,7 @@ struct empfile empfile[] = {
{EF_BMAP, "bmap", "bmap", NULL,
0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL},
{EF_COMM, "commodity", "commodity", commodity_ca,
UNMAPPED_CACHE(struct comstr, 0)},
UNMAPPED_CACHE(struct comstr, EFF_OWNER)},
{EF_LOST, "lost", "lostitems", lost_ca,
UNMAPPED_CACHE(struct loststr, EFF_OWNER)},
{EF_REALM, "realm", "realms", realm_ca,
@ -214,7 +214,8 @@ struct empfile empfile[] = {
SYMTAB(EF_TREATY_FLAGS, "treaty-flags", treaty_flags),
/* Views */
{EF_COUNTRY, "country", NULL, cou_ca, UNMAPPED_CACHE(struct natstr, 0)},
{EF_COUNTRY, "country", NULL, cou_ca,
UNMAPPED_CACHE(struct natstr, EFF_OWNER)},
/* Sentinel */
{EF_BAD, NULL, NULL, NULL, 0, 0, NULL, 0, 0, 0, 0, -1, NULL, NULL, NULL},

View file

@ -108,7 +108,7 @@ struct castr sect_ca[] = {
{NSC_SHORT, NSC_DEITY, 0, fldoff(sctstr, sct_elev), "elev", EF_BAD},
{NSC_XCOORD, NSC_CONST, 0, fldoff(sctstr, sct_x), "xloc", EF_BAD},
{NSC_YCOORD, NSC_CONST, 0, fldoff(sctstr, sct_y), "yloc", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_type), "des", EF_SECTOR_CHR},
{NSC_CHAR, 0, 0, fldoff(sctstr, sct_type), "des", EF_SECTOR_CHR},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_effic), "effic", EF_BAD},
{NSC_SHORT, 0, 0, fldoff(sctstr, sct_mobil), "mobil", EF_BAD},
{NSC_UCHAR, NSC_DEITY, 0, fldoff(sctstr, sct_loyal), "loyal", EF_BAD},
@ -123,7 +123,7 @@ struct castr sect_ca[] = {
{NSC_SHORT, 0, 0, fldoff(sctstr, sct_avail), "avail", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_work), "work", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_coastal), "coastal", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_newtype), "newdes", EF_SECTOR_CHR},
{NSC_CHAR, 0, 0, fldoff(sctstr, sct_newtype), "newdes", EF_SECTOR_CHR},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_min), "min", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_gmin), "gold", EF_BAD},
{NSC_UCHAR, 0, 0, fldoff(sctstr, sct_fertil), "fert", EF_BAD},
@ -542,6 +542,8 @@ struct castr realm_ca[] = {
{NSC_SHORT, 0, 0, fldoff(realmstr, r_xh), "xh", EF_BAD},
{NSC_SHORT, 0, 0, fldoff(realmstr, r_yl), "yl", EF_BAD},
{NSC_SHORT, 0, 0, fldoff(realmstr, r_yh), "yh", EF_BAD},
{NSC_TIME, NSC_EXTRA, 0, fldoff(realmstr, r_timestamp), "timestamp",
EF_BAD},
{NSC_NOTYPE, 0, 0, 0, NULL, EF_BAD}
};

View file

@ -57,30 +57,6 @@ obj_nameof(struct empobj *gp)
return "The Beast #666";
}
struct empobj *
get_empobjp(int type, int id)
{
if (CANT_HAPPEN(type == EF_SECTOR || type == EF_BAD))
return NULL;
return ef_ptr(type, id);
}
int
put_empobj(struct empobj *gp)
{
switch (gp->ef_type)
{
case EF_SECTOR:
return ef_write(gp->ef_type, sctoff(gp->x, gp->y), gp);
case EF_NATION:
case EF_BMAP:
case EF_MAP:
return ef_write(gp->ef_type, gp->own, gp);
default:
return ef_write(gp->ef_type, gp->uid, gp);
}
}
struct empobj_chr *
get_empobj_chr(struct empobj *gp)
{

View file

@ -54,7 +54,8 @@
#include "xy.h"
static int bmnxtsct(struct nstr_sect *);
static char map_char(unsigned char type, natid own, int owner_or_god);
static char map_char(int, natid, int);
static int unit_map(int, int, struct nstr_sect *, char *);
int
do_map(int bmap, int unit_type, char *arg, char *map_flags_arg)
@ -330,7 +331,7 @@ bmnxtsct(struct nstr_sect *np)
* If OWNER_OR_GOD, the map is for the sector's owner or a deity.
*/
static char
map_char(unsigned char type, natid own, int owner_or_god)
map_char(int type, natid own, int owner_or_god)
{
if (CANT_HAPPEN(type > SCT_TYPE_MAX || !dchr[type].d_mnem))
return '?';
@ -341,29 +342,30 @@ map_char(unsigned char type, natid own, int owner_or_god)
return '?';
}
int
static int
unit_map(int unit_type, int uid, struct nstr_sect *nsp, char *originp)
{
struct empobj *gp;
union empobj_storage unit;
struct range range;
char *name;
gp = get_empobjp(unit_type, uid);
if (!gp || (gp->own != player->cnum && !player->god) || gp->own == 0)
if (!get_empobj(unit_type, uid, &unit))
return RET_FAIL;
if (!player->owner || unit.gen.own == 0)
return RET_FAIL;
if (unit_type == EF_NUKE)
*originp = 'n';
else {
if ((name = emp_obj_chr_name(gp)) == NULL)
if ((name = emp_obj_chr_name(&unit.gen)) == NULL)
return RET_FAIL;
*originp = *name;
}
range.lx = xnorm(gp->x - 10);
range.hx = xnorm(gp->x + 11);
range.ly = ynorm(gp->y - 5);
range.hy = ynorm(gp->y + 6);
range.lx = xnorm(unit.gen.x - 10);
range.hx = xnorm(unit.gen.x + 11);
range.ly = ynorm(unit.gen.y - 5);
range.hy = ynorm(unit.gen.y + 6);
xysize_range(&range);
snxtsct_area(nsp, &range);
return RET_OK;

View file

@ -102,23 +102,24 @@ unit_put(struct emp_qelem *list, natid actor)
struct emp_qelem *qp;
struct emp_qelem *newqp;
struct ulist *ulp;
struct empobj *unit;
qp = list->q_back;
while (qp != list) {
ulp = (struct ulist *)qp;
unit = &ulp->unit.gen;
if (actor) {
mpr(actor, "%s stopped at %s\n", obj_nameof(&ulp->unit.gen),
xyas(ulp->unit.gen.x, ulp->unit.gen.y,
ulp->unit.gen.own));
if (ulp->unit.ef_type == EF_LAND) {
mpr(actor, "%s stopped at %s\n", obj_nameof(unit),
xyas(unit->x, unit->y, unit->own));
if (unit->ef_type == EF_LAND) {
if (ulp->mobil < -127)
ulp->mobil = -127;
ulp->unit.land.lnd_mobil = ulp->mobil;
unit->mobil = ulp->mobil;
}
}
if (ulp->unit.ef_type == EF_SHIP)
ulp->unit.ship.shp_mobil = (int)ulp->mobil;
put_empobj(&ulp->unit.gen);
if (unit->ef_type == EF_SHIP)
unit->mobil = (int)ulp->mobil;
put_empobj(unit->ef_type, unit->uid, unit);
newqp = qp->q_back;
emp_remque(qp);
free(qp);

444
src/util/empdump.c Normal file
View file

@ -0,0 +1,444 @@
/*
* Empire - A multi-player, client/server Internet based war game.
* Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
* Ken Stevens, Steve McClure
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ---
*
* See files README, COPYING and CREDITS in the root of the source
* tree for related information and legal notices. It is expected
* that future projects/authors will amend these files as needed.
*
* ---
*
* empdump.c: Export/import Empire game state
*
* Known contributors to this file:
* Markus Armbruster, 2008
*/
#include <config.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "file.h"
#include "optlist.h"
#include "prototypes.h"
#include "version.h"
#include "xdump.h"
static void dump_table(int, int);
static void shp_fixup(void);
static void pln_fixup(void);
static void lnd_fixup(void);
static void nuk_fixup(void);
int
main(int argc, char *argv[])
{
char *config_file = NULL;
char *import = NULL;
int export = 0;
int private = 0;
int human = 1;
int opt, i, lineno, type;
FILE *impf;
int dirty[EF_MAX];
while ((opt = getopt(argc, argv, "e:mtxhv")) != EOF) {
switch (opt) {
case 'e':
config_file = optarg;
break;
case 'h':
printf("Usage: %s [OPTION]... [DUMP-FILE]\n"
" -e CONFIG-FILE configuration file\n"
" (default %s)\n"
" -m use machine-readable format\n"
" -t test import, don't update game state\n"
" -x export to standard output\n"
" -h display this help and exit\n"
" -v display version information and exit\n",
argv[0], dflt_econfig);
exit(0);
case 'm':
human = 0;
break;
case 't':
private = EFF_PRIVATE;
break;
case 'x':
export = 1;
break;
case 'v':
printf("%s\n\n%s", version, legal);
exit(0);
default:
fprintf(stderr, "Try -h for help.\n");
exit(1);
}
}
if (argv[optind])
import = argv[optind++];
if (import) {
impf = fopen(import, "r");
if (!impf) {
fprintf(stderr, "Cant open %s for reading (%s)\n",
import, strerror(errno));
exit(1);
}
} else
private = EFF_PRIVATE;
/* read configuration & initialize */
empfile_init();
if (emp_config(config_file) < 0)
exit(1);
empfile_fixup();
nsc_init();
if (read_builtin_tables() < 0)
exit(1);
if (read_custom_tables() < 0)
exit(1);
if (chdir(gamedir)) {
fprintf(stderr, "Can't chdir to %s (%s)\n",
gamedir, strerror(errno));
exit(1);
}
global_init();
for (i = 0; i < EF_MAX; i++) {
if (!EF_IS_GAME_STATE(i))
continue;
if (!ef_open(i, EFF_MEM | private))
exit(1);
}
/* import from IMPORT */
memset(dirty, 0, sizeof(dirty));
if (import) {
lineno = 1;
while ((type = xundump(impf, import, &lineno, EF_BAD)) >= 0)
dirty[type] = 1;
if (type == EF_BAD)
exit(1);
shp_fixup();
pln_fixup();
lnd_fixup();
nuk_fixup();
}
if (ef_verify() < 0)
exit(1);
/* export to stdout */
if (export) {
for (i = 0; i < EF_MAX; i++) {
if (!EF_IS_GAME_STATE(i))
continue;
dump_table(i, human);
}
}
/* write out imported data */
for (i = 0; i < EF_MAX; i++) {
if (!EF_IS_GAME_STATE(i))
continue;
if (!private && dirty[i]) {
if (!ef_close(i))
exit(1);
}
}
return 0;
}
static void
printf_wrapper(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
}
static void
dump_table(int type, int human)
{
struct xdstr xd;
struct castr *ca;
int i;
void *p;
ca = ef_cadef(type);
if (!ca)
return;
xdinit(&xd, 0, human, printf_wrapper);
xdhdr(&xd, ef_nameof(type), 0);
xdcolhdr(&xd, ca);
for (i = 0; (p = ef_ptr(type, i)); i++) {
xdflds(&xd, ca, p);
printf("\n");
}
xdftr(&xd, i);
}
/* FIXME remove need for this */
#include <math.h>
#include "ship.h"
#include "plane.h"
#include "land.h"
#include "nuke.h"
static int fit_plane_on_ship(struct plnstr *, struct shpstr *);
static int fit_plane_on_land(struct plnstr *, struct lndstr *);
static void
shp_fixup(void)
{
int i;
struct shpstr *sp;
for (i = 0; (sp = ef_ptr(EF_SHIP, i)); i++) {
if (!sp->shp_own)
continue;
shp_set_tech(sp, sp->shp_tech);
}
}
static void
pln_fixup(void)
{
int i;
struct plnstr *pp;
struct shpstr *csp;
struct lndstr *clp;
for (i = 0; (pp = ef_ptr(EF_PLANE, i)); i++) {
if (!pp->pln_own)
continue;
pln_set_tech(pp, pp->pln_tech);
csp = ef_ptr(EF_SHIP, pp->pln_ship);
clp = ef_ptr(EF_LAND, pp->pln_land);
if (csp)
fit_plane_on_ship(pp, csp);
else if (clp)
fit_plane_on_land(pp, clp);
}
}
static void
lnd_fixup(void)
{
int i;
struct lndstr *lp;
struct shpstr *csp;
struct lndstr *clp;
for (i = 0; (lp = ef_ptr(EF_LAND, i)); i++) {
if (!lp->lnd_own)
continue;
lnd_set_tech(lp, lp->lnd_tech);
csp = ef_ptr(EF_SHIP, lp->lnd_ship);
clp = ef_ptr(EF_LAND, lp->lnd_land);
if (csp)
csp->shp_nland++;
else if (clp)
clp->lnd_nland++;
}
}
static void
nuk_fixup(void)
{
int i;
struct nukstr *np;
struct plnstr *cpp;
for (i = 0; (np = ef_ptr(EF_NUKE, i)); i++) {
if (!np->nuk_own)
continue;
cpp = ef_ptr(EF_PLANE, np->nuk_plane);
if (cpp)
cpp->pln_nuketype = np->nuk_type;
}
}
/* FIXME don't copy this from src/lib/subs/???sub.c */
/*
* Set SP's tech to TLEV along with everything else that depends on it.
*/
void
shp_set_tech(struct shpstr *sp, int tlev)
{
struct mchrstr *mcp = mchr + sp->shp_type;
int tech_diff = tlev - mcp->m_tech;
if (CANT_HAPPEN(tech_diff < 0)) {
tlev -= tech_diff;
tech_diff = 0;
}
sp->shp_tech = tlev;
sp->shp_armor = (short)SHP_DEF(mcp->m_armor, tech_diff);
sp->shp_speed = (short)SHP_SPD(mcp->m_speed, tech_diff);
sp->shp_visib = (short)SHP_VIS(mcp->m_visib, tech_diff);
sp->shp_frnge = (short)SHP_RNG(mcp->m_frnge, tech_diff);
sp->shp_glim = (short)SHP_FIR(mcp->m_glim, tech_diff);
}
/*
* Fit a plane of PP's type on ship SP.
* Adjust SP's plane counters.
* Updating the plane accordingly is the caller's job.
* Return whether it fits.
*/
static int
fit_plane_on_ship(struct plnstr *pp, struct shpstr *sp)
{
struct plchrstr *pcp = plchr + pp->pln_type;
struct mchrstr *mcp = mchr + sp->shp_type;
int wanted;
if (pcp->pl_flags & P_K) {
/* chopper, try chopper slot first */
if (sp->shp_nchoppers < mcp->m_nchoppers)
return ++sp->shp_nchoppers;
/* else try plane slot */
wanted = M_FLY;
} else if (pcp->pl_flags & P_E) {
/* x-light, try x-light slot first */
if (sp->shp_nxlight < mcp->m_nxlight)
return ++sp->shp_nxlight;
/* else try plane slot */
wanted = M_MSL | M_FLY;
} else if (!(pcp->pl_flags & P_L)) {
/* not light, no go */
wanted = 0;
} else if (pcp->pl_flags & P_M) {
/* missile, use plane slot */
wanted = M_MSL | M_FLY;
} else {
/* fixed-wing plane, use plane slot */
wanted = M_FLY;
}
if ((mcp->m_flags & wanted) == 0)
return 0; /* ship not capable */
if (sp->shp_nplane < mcp->m_nplanes)
return ++sp->shp_nplane;
return 0;
}
/*
* Fit a plane of PP's type on land unit LP.
* Adjust LP's plane counters.
* Updating the plane accordingly is the caller's job.
* Return whether it fits.
*/
static int
fit_plane_on_land(struct plnstr *pp, struct lndstr *lp)
{
struct plchrstr *pcp = plchr + pp->pln_type;
struct lchrstr *lcp = lchr + lp->lnd_type;
if ((pcp->pl_flags & P_E) && lp->lnd_nxlight < lcp->l_nxlight)
return ++lp->lnd_nxlight;
return 0;
}
/*
* Set PP's tech to TLEV along with everything else that depends on it.
*/
void
pln_set_tech(struct plnstr *pp, int tlev)
{
struct plchrstr *pcp = plchr + pp->pln_type;
int tech_diff = tlev - pcp->pl_tech;
int limited_range = pp->pln_range < pp->pln_range_max;
if (CANT_HAPPEN(tech_diff < 0)) {
tlev -= tech_diff;
tech_diff = 0;
}
pp->pln_tech = tlev;
pp->pln_att = PLN_ATTDEF(pcp->pl_att, tech_diff);
pp->pln_def = PLN_ATTDEF(pcp->pl_def, tech_diff);
pp->pln_acc = PLN_ACC(pcp->pl_acc, tech_diff);
pp->pln_range_max = PLN_RAN(pcp->pl_range, tech_diff);
pp->pln_load = PLN_LOAD(pcp->pl_load, tech_diff);
if (!limited_range || pp->pln_range > pp->pln_range_max)
pp->pln_range = pp->pln_range_max;
}
/*
* Set LP's tech to TLEV along with everything else that depends on it.
*/
void
lnd_set_tech(struct lndstr *lp, int tlev)
{
struct lchrstr *lcp = lchr + lp->lnd_type;
int tech_diff = tlev - lcp->l_tech;
if (CANT_HAPPEN(tech_diff < 0)) {
tlev -= tech_diff;
tech_diff = 0;
}
lp->lnd_tech = tlev;
lp->lnd_att = (float)LND_ATTDEF(lcp->l_att, tech_diff);
lp->lnd_def = (float)LND_ATTDEF(lcp->l_def, tech_diff);
lp->lnd_vul = (int)LND_VUL(lcp->l_vul, tech_diff);
lp->lnd_spd = (int)LND_SPD(lcp->l_spd, tech_diff);
lp->lnd_vis = (int)LND_VIS(lcp->l_vis, tech_diff);
lp->lnd_spy = (int)LND_SPY(lcp->l_spy, tech_diff);
lp->lnd_rad = (int)LND_RAD(lcp->l_rad, tech_diff);
lp->lnd_frg = (int)LND_FRG(lcp->l_frg, tech_diff);
lp->lnd_acc = (int)LND_ACC(lcp->l_acc, tech_diff);
lp->lnd_dam = (int)LND_DAM(lcp->l_dam, tech_diff);
lp->lnd_ammo = (int)LND_AMM(lcp->l_ammo, tech_diff);
lp->lnd_aaf = (int)LND_AAF(lcp->l_aaf, tech_diff);
lp->lnd_fuelc = (int)LND_FC(lcp->l_fuelc, tech_diff);
lp->lnd_fuelu = (int)LND_FU(lcp->l_fuelu, tech_diff);
lp->lnd_maxlight = (int)LND_XPL(lcp->l_nxlight, tech_diff);
lp->lnd_maxland = (int)LND_MXL(lcp->l_nland, tech_diff);
}
/* FIXME don't copy this from src/lib/update/nat.c */
double
logx(double d, double base)
{
if (base == 1.0)
return d;
return log10(d) / log10(base);
}