NAME
galaxyng -- Server for the play-by-email game GalaxyNG
SYNOPSIS
galaxyng [command [options]]
FUNCTION
Checks incoming orders, runs the turn, and sends out the turn
reports.
The code is divided into a number of modules:
List -- (list.c list.h) functions for manipulating lists.
Util -- (util.c util.h) frequently used functions.
Phase -- (phase.c phase.h) code for the various phases in the game.
Process -- (process.c process.h ) code for order checking,
order processing, and running a turn.
Report -- (report.c report.h)
Code to generate the turn reports.
Battle -- (battle.c battle.h) code that performs the battles.
Loadgame -- saves a turn to disk
Savegame -- load a turn from disk
GalaxyNG -- glues it all together.
AUTHOR
Created by:
o Frans Slothouber (fslothouber@acm.org)
o Christophe Barbier
o Jacco van Weert
o Tommy Lindqvist
o Rogerio Fung
o Ken Weinert
This code contains parts of the the orginal Galaxy code which was
created by Russell Wallace (RWALLACE@vax1.tcd.ie), and updated by the
Galaxy PBeM Development Group which include
o Russell Wallace (RWALLACE@vax1.tcd.ie)
o Tim Myers (tmyers@unlinfo.unl.edu),
o Robert Stone (stone@athena.cs.uga.edu),
o Mayan Moudgill (moudgill@cs.cornell.edu),
o Graeme Griffiths (graeme@abekrd.co.uk),
o K Pankhurst (k.pankhurst@ic.ac.uk).
CREATION DATE
4-Jan-1997
COPYRIGHT
GPL see ../COPYING
NOTES
This is not the most pretty code around. It is a product of many
years and many people. The code hosts a lot of global variables
and many not very descriptively named function. The code is
pretty stable however.
BUGS
NAME
main -- the start of it all.
RESULT
Reports any errors that occur back to the environment.
SOURCE
int
main(int argc, char **argv)
{
char *value;
int result;
/* Some initializations */
resetErnie(197162622);
logLevel = LBRIEF;
sprintf(vcid, "GalaxyNG release-%d-%d, %s.",
GNG_MAJOR, GNG_MINOR, GNG_DATE);
/* This should be a function */
if ((value = getenv("GALAXYNGHOME"))) {
galaxynghome = strdup(value);
}
else if ((value = getenv("HOME"))) {
sprintf(lineBuffer, "%s/Games", value);
galaxynghome = strdup(lineBuffer);
}
else {
galaxynghome =
strdup("/please/set/your/HOME/or/GALAXYNGHOME/variable");
}
#ifdef WIN32
/* This should be a function */
if ((value = getenv("TEMP"))) {
tempdir = strdup(value);
}
else if ((value = getenv("TMP"))) {
tempdir = strdup(value);
}
else {
tempdir = strdup("c:/temp");
}
#else
tempdir = strdup("/tmp");
#endif
if ((value = getenv("GNG_LOG_LEVEL"))) {
if (strcasecmp(value, "full") == 0)
logLevel = LFULL;
else if (strcasecmp(value, "part") == 0)
logLevel = LPART;
else if (strcasecmp(value, "brief") == 0)
logLevel = LBRIEF;
else if (strcasecmp(value, "none") == 0)
logLevel = LNONE;
else
logLevel = LBRIEF;
}
if (argc <= 1) {
usage();
}
else if (strstr(argv[1], "create")) {
result = CMD_create(argc, argv);
}
else if (strstr(argv[1], "dummymail0")) {
result = CMD_mail0(argc, argv, CMD_CHECK_DUMMY);
}
else if (strstr(argv[1], "mail0")) {
result = CMD_mail0(argc, argv, CMD_CHECK_REAL);
}
#if 0
else if (strstr(argv[1], "filecheck")) {
result = CMD_checkFile(argc, argv, CMD_CHECK_DUMMY);
}
#endif
else if (strstr(argv[1], "dummycheck")) {
result = CMD_check(argc, argv, CMD_CHECK_DUMMY);
}
else if (strstr(argv[1], "check")) {
result = CMD_check(argc, argv, CMD_CHECK_REAL);
}
else if (strstr(argv[1], "dummyrun")) {
result = CMD_run(argc, argv, CMD_RUN_DUMMY);
}
else if (strstr(argv[1], "run")) {
result = CMD_run(argc, argv, CMD_RUN_REAL);
}
else if (strstr(argv[1], "selftest")) { /* experimental */
result = CMD_selftest();
}
else if (strstr(argv[1], "battletest")) { /* experimental */
result = CMD_battletest(argc, argv);
}
else if (strstr(argv[1], "test")) { /* experimental */
result = CMD_test(argc, argv);
}
else if (strstr(argv[1], "report")) {
result = CMD_report(argc, argv);
}
else if (strstr(argv[1], "relay")) {
result = CMD_relay(argc, argv);
}
else if (strstr(argv[1], "pscore")) { /* experimental */
result = CMD_dump(argc, argv, CMD_DUMP_PSCORE);
}
else if (strstr(argv[1], "score")) {
result = CMD_score(argc, argv);
}
else if (strstr(argv[1], "graph")) { /* experimental */
result = CMD_graph(argc, argv);
}
else if (strstr(argv[1], "template")) {
result = CMD_template(argc, argv);
}
else if (strstr(argv[1], "map")) {
result = CMD_dump(argc, argv, CMD_DUMP_MAP);
}
else if (strstr(argv[1], "hall")) {
result = CMD_dump(argc, argv, CMD_DUMP_HALL);
}
else if (strstr(argv[1], "lastorders")) {
result = CMD_dump(argc, argv, CMD_DUMP_LASTORDERS);
}
else if (strstr(argv[1], "players")) {
result = CMD_dump(argc, argv, CMD_DUMP_PLAYERS);
}
else if (strstr(argv[1], "toall")) {
result = CMD_dump(argc, argv, CMD_DUMP_MAILHEADER);
}
else if (strstr(argv[1], "teaminfo")) {
result = CMD_dump(argc, argv, CMD_DUMP_TEAM_INFO);
}
else if (strstr(argv[1], "teamdump")) {
result = CMD_dump(argc, argv, CMD_DUMP_TEAM_REPORT_NAMES);
}
else {
usage();
}
return result;
}
NAME
CMD_template -- Create a template .glx file that the GM can edit.
SYNOPSIS
./galaxyng <name> <number of players>
FUNCTION
Creates a template .glx file.
All parameters are given some sensible default values.
The number of players is used to determine the size of the galaxy,
using the following formule
42 * ceil (sqrt(number of players))
For games with very few players this size is sometimes too small.
SOURCE
int
CMD_template(int argc, char **argv)
{
int result = EXIT_FAILURE;
if (argc == 4) {
FILE *glxfile;
char *glxname;
int numberOfPlayers;
glxname = createString("%s.glx", argv[2]);
numberOfPlayers = atoi(argv[3]);
if ((glxfile = GOS_fopen(glxname, "w"))) {
int i;
int gsize;
fprintf(glxfile, "\n\nname %s\n\n", argv[2]);
gsize = (int) (42.0 * ceil(sqrt((double) numberOfPlayers)));
gsize /= 10;
gsize *= 10;
fprintf(glxfile,
"; The following size is only approximately right.\n"
"; You probably want to experiment with different sizes to get a galaxy\n"
"; that looks right. It should not be too crowded nor too sparse.\n\n");
fprintf(glxfile, "size %d\n", gsize);
fprintf(glxfile,
"\n"
"; The engine will make sure that distance between any of primary home\n"
"; planets is atleast 30.0 light years.\n"
"\n" "nation_spacing 30.0\n" "\n");
fprintf(glxfile,
"; The sizes of the core home planets for each nation.\n"
"; The following would give each nation 3 homeplanets of sizes\n"
"; 1000 250 350. The first one is the primary home planet.\n"
"; You have to define these before any of the player definitions.\n"
"\n" "core_sizes 1000 250 350\n" "\n");
fprintf(glxfile,
"; Within a radius [2,r] from the primary home world the engine allocates\n"
"; a number of empty planets for the nation to colonize.\n"
"; The following two parameters define how many there are per nation,\n"
"; and in within what radius. A number between 4 and 10 and\n"
"; a radius of nation_spacing/2.0 is a good guess.\n"
"\n");
fprintf(glxfile,
"empty_planets 6\n"
"empty_radius 15\n"
"\n"
"; It is possible to add a number of 'stuff' planets. These are useless\n"
"; planets, all of size 50 or less, that are use to fill up the empty\n"
"; space between the home worlds. They make it possible for a players to\n"
"; approach (attack) other players by different routes. The following\n"
"; parameter specifies how many there are per nation.\n"
"\n");
fprintf(glxfile,
"stuff_planets 8\n"
";\n"
"; The list of the players, you can add here the mail address\n"
"; of each player that enrolled in your game.\n" ";\n" "\n");
for (i = 1; i <= numberOfPlayers; i++) {
fprintf(glxfile, "player player%d@itsaddress.somewhere\n", i);
}
fprintf(glxfile,
";\n"
"; You can override the core size for a player by adding the sizes.\n"
"; For instance the following player will get one home planet\n"
"; of size 1600.0\n"
"\n"
"; player player3@itsaddress.somewhere 1600.0\n"
"\n"
"; While the following player gets 3 home planets of sizes\n"
"; 500.0, 100.0, and 1000.0\n"
"\n"
"; player player4@itsaddress.somewhere 500.0 100.0 1000.0\n\n");
fprintf(glxfile,
"\n"
"; You can specify several other options :\n"
";\n"
"; Initial tech levels\n"
"; In order : Drive Weapons Shields Cargo\n"
"; They can't be lower than 1 (don't ask why, I don't know\n"
"; myself why I did this).\n"
"; In the case below, drive will be forced to 1 by the server.\n"
";\n");
fprintf(glxfile,
"; Uncomment if you want this option.\n"
"\n"
"; InitialTechLevels 0.42 1 3.21 1.24\n"
";\n"
"; Full bombing, when bombed planets are completely bombed.\n"
"; All population, industry, capital, colonists, and materials are gone.\n"
"; Normally the population and industry is reduced to 25%% of its\n"
"; original value.\n" ";\n");
fprintf(glxfile,
"; Uncomment if you want this option.\n"
"\n"
"; FullBombing\n"
"\n"
"; If keepproduction is set, the production points spent\n"
"; on the previous product are preserved, otherwise all points are lost\n"
";\n" "; Uncomment if you want this option.\n" "\n");
fprintf(glxfile,
"; KeepProduction\n"
"\n"
";\n"
"; Don't remove idle nations from a game.\n"
"; Normally if players do not send in orders for a couple of turns\n"
"; their nation self destructs.\n"
";\n" "; Uncomment if you want this option.\n" "\n");
fprintf(glxfile,
"; DontDropDead\n"
"\n"
";\n"
"; Sometimes, if you have enough disk space, it is nice to get a copy\n"
"; of turn reports that are send out to the players.\n"
"; If you uncomment the following parameter, a copy of each turn report\n"
"; that is send out is stored in reports/<game name>/\n"
";\n" "; Uncomment if you want this option.\n" "\n");
fprintf(glxfile,
"; SaveReportCopy\n"
"; The galaxy can be (roughly) mapped on a sphere\n"
"; This way, the gap between x (or y) coordinates of two\n"
"; planets is computed with borders lines crossing, and reappearing\n"
"; on the other side.\n"
";\n"
"; Uncomment if you want this option.\n"
"\n" "; sphericalgalaxy\n" "\n");
fclose(glxfile);
}
else {
fprintf(stderr, "Can't open \"%s\".\n", glxname);
}
printf("Created the file \"%s\".\n", glxname);
free(glxname);
}
else {
usage();
}
return result;
}
NAME
CMD_run -- run turn and send turn reports
SYNOPSIS
./galaxyng -run <game name> <file with all orders>
FUNCTION
Run an turn, Compute the highscore list, and send the turn reports
to all players. The GM is send a status report. If the option
SaveReportCopy is specified in the .galaxyngrc file, a copy of each
turn report is also saved in reports/
This function is also run for:
./galaxyng -dummyrun <game name> <file with all orders>
In this case the run is a dummy run, and the turn reports are stored
in reports/. Nothing is mailed. This is used to debug the server code.
OUTPUT
Turn reports are send out to the players and the turn is saved to disk. The GM is sent a status report. A log can be found in log/<game name>.
DIAGNOSTICS
Message to stderr in case of an error.
(Game does not exist, not the right time to run the game,
game structure is corrupted).
RESULT
EXIT_FAILURE or EXIT_SUCCESS
SOURCE
int
CMD_run(int argc, char **argv, int kind)
{
int result;
result = EXIT_FAILURE;
if (argc >= 4) {
game* aGame;
int turn;
char* logName;
logName = createString("%s/log/%s", galaxynghome, argv[2]);
openLog(logName, "w");
free(logName);
plogtime(LPART);
plog(LPART, "Trying to run Game \"%s\".\n", argv[2]);
aGame = NULL;
turn = (argc == 4) ? LG_CURRENT_TURN : atoi(argv[4]) - 1;
if ((aGame = loadgame(argv[2], turn))) {
player *aPlayer;
loadConfig(aGame);
if (checkTime(aGame) || (kind == CMD_RUN_DUMMY)) {
checkIntegrity(aGame);
if (runTurn(aGame, argv[3])) {
highScoreList(aGame);
result = 0;
for (aPlayer = aGame->players; aPlayer; aPlayer = aPlayer->next) {
if (aPlayer->flags & F_TXTREPORT) {
if (kind == CMD_RUN_REAL) {
result |= mailTurnReport(aGame, aPlayer, F_TXTREPORT);
if (aGame->gameOptions.gameOptions & GAME_SAVECOPY) {
saveTurnReport(aGame, aPlayer, F_TXTREPORT);
}
}
else {
saveTurnReport(aGame, aPlayer, F_TXTREPORT);
}
}
if (aPlayer->flags & F_XMLREPORT) {
if (kind == CMD_RUN_REAL) {
result |= mailTurnReport(aGame, aPlayer, F_XMLREPORT);
if (aGame->gameOptions.gameOptions & GAME_SAVECOPY) {
saveTurnReport(aGame, aPlayer, F_XMLREPORT);
}
else {
saveTurnReport(aGame, aPlayer, F_XMLREPORT);
}
}
}
if (aPlayer->flags & F_MACHINEREPORT) {
if (kind == CMD_RUN_REAL) {
result |= mailTurnReport(aGame, aPlayer, F_MACHINEREPORT);
if (aGame->gameOptions.gameOptions & GAME_SAVECOPY) {
saveTurnReport(aGame, aPlayer, F_MACHINEREPORT);
}
else {
saveTurnReport(aGame, aPlayer, F_MACHINEREPORT);
}
}
}
}
savegame(aGame);
}
else {
fprintf(stderr,
"The server has detected an error in the game data structure. The run\n"
"of the turn has been aborted. No turn reports have been sent. Please\n"
"contact Ken Weinert at mc@quarter-flash.com for a solution to this\n"
"problem.\n");
result = 1;
}
plogtime(LPART);
plog(LPART, "Run is completed.\n");
result = (result) ? EXIT_FAILURE : EXIT_SUCCESS;
}
else {
plog(LBRIEF, "Error, attempt to run the game \"%s\" at the"
" wrong time.\n"
"You specified a start time of %s in your .galaxyngrc file.\n",
argv[2], aGame->starttime);
fprintf(stderr,
"Error, attempt to run the game \"%s\" at the wrong time.\n"
"You specified a start time of %s in your .galaxyngrc file.\n",
argv[2], aGame->starttime);
}
closeLog();
if (kind == CMD_RUN_REAL) {
mailGMReport(aGame, argv[2]);
}
freegame(aGame);
}
else {
plog(LBRIEF, "Game \"%s\" does not exist.\n", argv[2]);
fprintf(stderr, "Game \"%s\" does not exist.\n", argv[2]);
}
}
else {
usage();
}
closeLog();
return result;
}
NAME
checkTime -- check if it is really time to run a turn.
SYNOPSIS
int checkTime(game *aGame)
FUNCTION
Does a sanity check to see if a turn really has to be run. On some systems, after a reboot, crontab goes bezerk and executes a whole bunch of entries in your crontab file for no good reason. This will cause turns to be run prematurely. This function checks if the current time is equal to the time a game is supposed to be run. The GM has to put this time in a game specific .galaxyngrc file, using the starttime key.
EXAMPLE
You want to make sure a game runs at 13:00. Add the entry
starttime 13:00
to the .galaxyngrc file. The a turn will then only run, if and
only if, it is started by between 13:00 and 13:09. Add starttime
13:10, and it will only run if started between 13:10 and 13:19.
RESULT
TRUE -- the game is allowed to run. FALSE -- the game is not allowed to run.
SOURCE
int
checkTime(game *aGame)
{
int runGame;
assert(aGame != NULL);
runGame = FALSE;
if (aGame->starttime) {
char timeBuffer[255];
time_t ttp;
time(&ttp);
strftime(timeBuffer, 255, "%H:%M", localtime(&ttp));
if (strncmp(timeBuffer, aGame->starttime, 4) == 0) {
runGame = TRUE;
}
}
else {
runGame = TRUE;
}
return runGame;
}
NAME
CMD_check -- check incoming orders.
FUNCTION
Check incoming orders and create a forecast of the situation at the next turn.
INPUTS
Orders come in via stdin. The forecast is mailed directy to the player.
Orders are assumed to have a proper mailheader, that is start with:
To: <player>@theaddress
Subject: orders [turn number]
This header can be produced with formail (see .procmailrc file).
RESULTS
Orders are stored in
$GALAXYNGHOME/orders/<game name>/<nation name>.<turn number>
Forecast is mailed to the player.
A log is kept of all order processing in log/orders_processed.txt
SOURCE
int
CMD_check(int argc, char **argv, int kind)
{
int result;
char* logName;
envelope* anEnvelope;
char* forecastName;
char* returnAddress;
char* nationName;
char* password;
game* aGame;
FILE* forecast;
player* aPlayer;
int resNumber, theTurnNumber;
result = EXIT_FAILURE;
logName = createString("%s/log/orders_processed.txt", galaxynghome);
openLog(logName, "a");
free(logName);
plogtime(LBRIEF);
if (argc >= 2) {
anEnvelope = createEnvelope();
returnAddress = getReturnAddress(stdin);
theTurnNumber = getTurnNumber(stdin);
nationName = NULL;
password = NULL;
aGame = NULL;
resNumber = areValidOrders(stdin, &aGame, &nationName,
&password, theTurnNumber);
plog(LBRIEF, "game %s\n", aGame->name);
setHeader(anEnvelope, MAILHEADER_TO, "%s", returnAddress);
if (resNumber == RES_OK) {
aPlayer = findElement(player, aGame->players, nationName);
aPlayer->orders = NULL;
plog(LBRIEF, "Orders from %s\n", returnAddress);
/* produce an XML forecast */
if (aPlayer->flags && F_XMLREPORT) {
if ((theTurnNumber == LG_CURRENT_TURN) ||
(theTurnNumber == (aGame->turn) + 1)) {
forecastName = createString("%s/NG_XML_%d_forecast",
tempdir, getpid());
copyOrders(aGame, stdin, nationName, password, aGame->turn+1);
if ((forecast = GOS_fopen(forecastName, "w")) == NULL) {
plog(LBRIEF, "Could not open %s for forecasting\n", forecastName);
return EXIT_FAILURE;
}
setHeader(anEnvelope, MAILHEADER_SUBJECT,
"Galaxy HQ, %s turn %d XML forecast for %s", aGame->name,
(aGame->turn) + 1, nationName);
checkOrders(aGame, nationName, forecast, F_XMLREPORT);
fclose(forecast);
if (kind == CMD_CHECK_REAL) {
plog(LBRIEF, "mailing XML report %s to %s\n", forecastName,
anEnvelope->to);
result |= eMail(aGame, anEnvelope, forecastName);
}
else {
char *forecastFile;
forecastFile =
createString("%s/forecasts/%s/%s_XML",
galaxynghome, argv[2], returnAddress);
GOS_copy(forecastName, forecastFile);
}
result |= GOS_delete(forecastName);
free(forecastName);
}
}
/* produce a text forecast */
if (aPlayer->flags && F_TXTREPORT) {
if ((theTurnNumber == LG_CURRENT_TURN) ||
(theTurnNumber == (aGame->turn) + 1)) {
forecastName = createString("%s/NG_TXT_%d_forecast",
tempdir, getpid());
forecast = GOS_fopen(forecastName, "w");
if (aPlayer->orders == NULL)
copyOrders(aGame, stdin, nationName, password, aGame->turn+1);
setHeader(anEnvelope, MAILHEADER_SUBJECT,
"Galaxy HQ, %s turn %d TXT forecast for %s", aGame->name,
(aGame->turn) + 1, nationName);
checkOrders(aGame, nationName, forecast, F_TXTREPORT);
fclose(forecast);
if (kind == CMD_CHECK_REAL) {
plog(LBRIEF, "mailing TXT report %s to %s\n", forecastName,
anEnvelope->to);
result |= eMail(aGame, anEnvelope, forecastName);
}
else {
char *forecastFile;
forecastFile =
createString("%s/forecasts/%s/%s_TXT",
galaxynghome, argv[2], returnAddress);
GOS_copy(forecastName, forecastFile);
}
result |= GOS_delete(forecastName);
free(forecastName);
}
}
}
else {
forecastName = createString("%s/NG_TXT_%d_errors",
tempdir, getpid());
forecast = GOS_fopen(forecastName, "w");
setHeader(anEnvelope, MAILHEADER_SUBJECT,
"Galaxy HQ, major trouble");
plog(LBRIEF, "major trouble %d\n", resNumber);
generateErrorMessage(resNumber, aGame, nationName,
theTurnNumber, forecast);
fclose(forecast);
if (kind == CMD_CHECK_REAL) {
plog(LBRIEF, "mailing error report %s to %s\n", forecastName,
anEnvelope->to);
result |= eMail(aGame, anEnvelope, forecastName);
}
else {
char *forecastFile;
forecastFile =
createString("%s/forecasts/%s/%s_ERR",
galaxynghome, argv[2], returnAddress);
GOS_copy(forecastName, forecastFile);
}
result |= GOS_delete(forecastName);
free(forecastName);
}
/* code here for advanced orders, we need to see how to determine this */
if (!((theTurnNumber == LG_CURRENT_TURN) ||
(theTurnNumber == (aGame->turn) + 1))) {
if (aPlayer->orders == NULL)
copyOrders(aGame, stdin, nationName, password, theTurnNumber);
setHeader(anEnvelope, MAILHEADER_SUBJECT,
"Galaxy HQ, %s advance orders received for %s.",
aGame->name, nationName);
plog(LBRIEF, "%s advance orders received for %s.\n",
aGame->name, nationName);
if (aPlayer->flags && F_XMLREPORT) {
forecastName = createString("%s/NG_XML_forecast", tempdir);
forecast = GOS_fopen(forecastName, "w");
fprintf(forecast, "<galaxy>\n <variant>GalaxyNG</variant>\n");
fprintf(forecast, " <version>%d.%d.%d</version>\n",
GNG_MAJOR, GNG_MINOR, GNG_RELEASE);
fprintf(forecast, " <game name=\"%s\">\n", aGame->name);
fprintf(forecast, " <turn num=\"%d\">\n", theTurnNumber);
fprintf(forecast, " <race name=\"%s\">\n", nationName);
fprintf(forecast, " <message>\n");
fprintf(forecast, " <line num=\"1\">"
"O wise leader, your orders for turn %d</line>",
theTurnNumber);
fprintf(forecast, " <line num=\"2\">"
"have been received and stored.</line>");
fprintf(forecast, " </message>\n");
fprintf(forecast, " </race>\n");
fprintf(forecast, " </turn>\n");
fprintf(forecast, " </game>\n");
fprintf(forecast, "</galaxy>\n");
fclose(forecast);
if (kind == CMD_CHECK_REAL) {
result |= eMail(aGame, anEnvelope, forecastName);
}
else {
char *forecastFile;
forecastFile =
createString("%s/forecasts/%s/%s_XML",
galaxynghome, argv[2], returnAddress);
GOS_copy(forecastName, forecastFile);
}
result |= GOS_delete(forecastName);
free(forecastName);
}
if (aPlayer->flags && F_TXTREPORT) {
if (aPlayer->orders == NULL)
copyOrders(aGame, stdin, nationName, password, theTurnNumber);
forecastName = createString("%s/NG_TXT_forecast", tempdir);
forecast = GOS_fopen(forecastName, "w");
fprintf(forecast, "O wise leader your orders for turn %d "
"have been received and stored.\n", theTurnNumber);
fclose(forecast);
if (kind == CMD_CHECK_REAL) {
result |= eMail(aGame, anEnvelope, forecastName);
}
else {
char *forecastFile;
forecastFile =
createString("%s/forecasts/%s/%s_TXT",
galaxynghome, argv[2], returnAddress);
GOS_copy(forecastName, forecastFile);
}
result |= GOS_delete(forecastName);
free(forecastName);
}
}
}
if (nationName)
free(nationName);
if (password)
free(password);
destroyEnvelope(anEnvelope);
result = (result) ? EXIT_FAILURE : EXIT_SUCCESS;
return result;
}
NAME
CMD_checkFile -- NOTE This should be merged with CMD_check().
NAME
CMD_relay -- relay a message from one nation to another.
FUNCTION
NAME
relayMessage --
NAME
CMD_create -- create a new galaxy and game.
SYNOPSIS
galaxyng -create <game specification file>
FUNCTION
Creates a new game based on the specification found in the specification file.
INPUTS
specificationfile -- file with the dimensions of the galaxy
and the addresses of all the players. (.glx file).
SEE
CMD_mail0(), CMD_template()
SOURCE
int
CMD_create(int argc, char **argv)
{
gamespecification* gspec;
game* aGame;
int result;
FILE* specfile;
result = EXIT_FAILURE;
if (argc == 3) {
if ((specfile = GOS_fopen(argv[2], "r"))) {
gspec = readGameSpec(specfile);
fclose(specfile);
printGameSpecs(gspec);
if ((aGame = creategame(gspec))) {
struct fielddef fields;
fields.destination = stdout;
checkIntegrity(aGame);
savegame(aGame);
reportMap(aGame, aGame->players, &fields);
printf("Number of planets: %d\n",
numberOfElements(aGame->planets));
result = EXIT_SUCCESS;
}
else {
fprintf(stderr, "Can't create the game\n");
}
}
else {
fprintf(stderr, "Can't open specification file \"%s\"\n", argv[2]);
}
}
else {
usage();
}
return result;
}
NAME
CMD_mail0 -- mail the turn 0 reports.
SYNOPSIS
./galaxyng -mail0 <Game Name> int CMD_mail0(int argc, char **argv)
FUNCTION
Mail the turn 0 reports to the players. To be run after a new game is created.
SOURCE
int
CMD_mail0(int argc, char **argv, int kind)
{
game *aGame;
int result;
result = EXIT_FAILURE;
if (argc == 3) {
if ((aGame = loadgame(argv[2], LG_CURRENT_TURN))) {
player *aPlayer;
loadConfig(aGame);
checkIntegrity(aGame);
for (aPlayer = aGame->players; aPlayer; aPlayer = aPlayer->next) {
aPlayer->pswdstate = 1;
if (kind == CMD_CHECK_REAL) {
mailTurnReport(aGame, aPlayer, 0);
}
if ((aGame->gameOptions.gameOptions & GAME_SAVECOPY) |
(kind == CMD_CHECK_DUMMY)) {
saveTurnReport(aGame, aPlayer, 0);
}
if (aPlayer->flags & F_XMLREPORT) {
if (kind == CMD_CHECK_DUMMY) {
saveTurnReport(aGame, aPlayer, F_XMLREPORT);
}
else {
mailTurnReport(aGame, aPlayer, F_XMLREPORT);
}
}
if (aPlayer->flags & F_MACHINEREPORT) {
if (kind == CMD_CHECK_DUMMY) {
saveTurnReport(aGame, aPlayer, F_MACHINEREPORT);
}
else {
mailTurnReport(aGame, aPlayer, F_MACHINEREPORT);
}
}
}
result = EXIT_SUCCESS;
}
else {
fprintf(stderr, "Could not load game \"%s\"\n", argv[2]);
}
}
else {
usage();
}
return result;
}
NAME
CMD_report -- send a copy of a turn report.
SYNOPSIS
./galaxyng -check < file_with_email int CMD_report(int argc, char **argv)
FUNCTION
Recreate a turn report of a given turn. Send it to the player that requested it.
BUGS
Does not send XML nor machine reports.
SOURCE
int
CMD_report(int argc, char **argv)
{
int result;
char *logName;
logName = createString("%s/log/orders_processed.txt", galaxynghome);
openLog(logName, "a");
free(logName);
plogtime(LBRIEF);
result = EXIT_FAILURE;
if (argc >= 2) {
char *returnAddress, *nationName, *password;
int resNumber, theTurnNumber;
game *aGame;
FILE *report;
char *reportName;
reportName = createString("%s/temp_report_copy", tempdir);
if ((report = GOS_fopen(reportName, "w"))) {
envelope *anEnvelope;
anEnvelope = createEnvelope();
returnAddress = getReturnAddress(stdin);
setHeader(anEnvelope, MAILHEADER_TO, "%s", returnAddress);
plog(LBRIEF, "Report request from %s.\n", returnAddress);
theTurnNumber = getTurnNumber(stdin);
nationName = NULL;
password = NULL;
aGame = NULL;
resNumber =
areValidOrders(stdin, &aGame, &nationName, &password,
theTurnNumber);
if ((resNumber == RES_TURNRAN)
|| ((resNumber == RES_OK) && (theTurnNumber == LG_CURRENT_TURN))) {
game *aGame2;
if (theTurnNumber > 0) {
aGame2 = loadgame(aGame->name, theTurnNumber - 1);
}
else if (theTurnNumber == LG_CURRENT_TURN) {
theTurnNumber = aGame->turn;
aGame2 = loadgame(aGame->name, theTurnNumber - 1);
}
else {
aGame2 = loadgame(aGame->name, 0);
}
if (aGame2) {
player *aPlayer;
int index;
loadConfig(aGame2);
setHeader(anEnvelope, MAILHEADER_SUBJECT,
"Galaxy HQ, Copy of turn %d report", theTurnNumber);
if (theTurnNumber > 0) { /* Rerun the turn */
char *ordersName;
ordersName =
createString("%s/orders/%s/%d.all",
galaxynghome, aGame2->name, theTurnNumber);
runTurn(aGame2, ordersName);
free(ordersName);
}
/* Translate the current nation name into the name used during
* the * turn * * * * that is requested */
aPlayer = findElement(player, aGame->players, nationName);
index = ptonum(aGame->players, aPlayer);
aPlayer = numtop(aGame2->players, index);
if (theTurnNumber == 0)
aPlayer->pswdstate = 1;
highScoreList(aGame2);
createTurnReport(aGame2, aPlayer, report, 0);
}
else {
setHeader(anEnvelope, MAILHEADER_SUBJECT,
"Galaxy HQ, Copy of turn report request.");
fprintf(report,
"\n\nThe turn you requested is no longer available...\n");
}
}
else if (resNumber == RES_OK) {
setHeader(anEnvelope, MAILHEADER_SUBJECT,
"Galaxy HQ, Major Trouble");
fprintf(report,
"You can not request a report for the next turn\n");
fprintf(report,
"or any following turns,"
" I can not see into the future!\n");
}
else {
setHeader(anEnvelope, MAILHEADER_SUBJECT,
"Galaxy HQ, Major Trouble");
generateErrorMessage(resNumber, aGame, nationName, theTurnNumber,
report);
}
fclose(report);
result = eMail(aGame, anEnvelope, reportName);
destroyEnvelope(anEnvelope);
result |= ssystem("rm %s", reportName);
result = (result) ? EXIT_FAILURE : EXIT_SUCCESS;
if (nationName)
free(nationName);
if (password)
free(password);
}
else {
fprintf(stderr, "Can't open \"%s\"\n", reportName);
}
free(reportName);
}
else {
usage();
}
closeLog();
return result;
}
NAME
CMD_score -- Show highscore list.
FUNCTION
Write a HTML-ized version of the highscore list to stdout.
SOURCE
int
CMD_score(int argc, char **argv)
{
int result;
game *aGameThisTurn;
game *aGamePrevTurn;
result = EXIT_FAILURE;
if (argc == 3) {
if ((aGameThisTurn = loadgame(argv[2], LG_CURRENT_TURN))) {
if ((aGamePrevTurn =
loadgame(aGameThisTurn->name, aGameThisTurn->turn - 1))) {
score(aGamePrevTurn, aGameThisTurn, TRUE, stdout);
result = EXIT_SUCCESS;
}
else {
fprintf(stderr, "Could not load game \"%s\"\n", argv[2]);
}
}
else {
fprintf(stderr, "Could not load game \"%s\" turn %d\n", argv[2],
aGameThisTurn->turn - 1);
}
}
else {
usage();
}
return result;
}
NAME
CMD_graph -- create a data dump for a graph of a game.
NOTES
Experimental. See Tools/graphscore.tcl
SOURCE
int
CMD_graph(int argc, char **argv)
{
game *aGame;
int result;
result = EXIT_FAILURE;
if (argc == 4) {
if ((aGame = loadgame(argv[2], atoi(argv[3])))) {
player *aPlayer;
int number;
nationStatus(aGame);
for (number = 0, aPlayer = aGame->players;
aPlayer; aPlayer = aPlayer->next, number++) {
printf("%d %d %s %f\n", aGame->turn, number, aPlayer->name,
effectiveIndustry(aPlayer->totPop, aPlayer->totInd));
}
}
else {
fprintf(stderr, "Could not load game \"%s\" turn %s\n",
argv[2], argv[3]);
}
}
else {
usage();
}
return result;
}
NAME
CMD_dump -- dump game data.
SYNOPSIS
./galaxyng -players <game> [turn]
./galaxyng -lastorders <game> [turn]
./galaxyng -map <game> [turn]
CMD_dump(int argc, char **argv, int kind)
FUNCTION
Dump game information.
SWITCHES
kind:
CMD_DUMP_LASTORDERS
show turn when players last send in orders.
CMD_DUMP_MAP
show a map of the galaxy
CMD_DUMP_PLAYERS
show password and address of players
CMD_DUMP_HALL
show information for use in the hall of fame.
CMD_DUMP_TEAM_INFO
show info on all members of a team
(used in Tiger games).
SOURCE
int
CMD_dump(int argc, char **argv, int kind)
{
game *aGame;
int result;
int turn;
int team;
result = EXIT_FAILURE;
if (argc >= 3) {
if (argc == 3) {
turn = LG_CURRENT_TURN;
}
else {
turn = atoi(argv[3]);
}
if ((aGame = loadgame(argv[2], turn))) {
player *aDummyPlayer;
struct fielddef fields;
fields.destination = stdout;
loadConfig(aGame);
switch (kind) {
case CMD_DUMP_MAP:{
aDummyPlayer = allocStruct(player);
setName(aDummyPlayer, "DummyDummy");
aDummyPlayer->msize = aGame->galaxysize;
reportMap(aGame, aDummyPlayer, &fields);
break;
}
case CMD_DUMP_MAP_GNUPLOT:{
aDummyPlayer = allocStruct(player);
setName(aDummyPlayer, "DummyDummy");
aDummyPlayer->msize = aGame->galaxysize;
reportMap_gnuplot(aGame, aDummyPlayer, &fields);
break;
}
case CMD_DUMP_LASTORDERS:{
reportLastOrders(aGame->players, &fields);
break;
}
case CMD_DUMP_PLAYERS:{
reportPlayers(aGame->players, &fields);
break;
}
case CMD_DUMP_PSCORE:{
scorePercent(aGame, &fields);
break;
}
case CMD_DUMP_HALL:{
reportHall(aGame, &fields);
break;
}
case CMD_DUMP_MAILHEADER:{
createMailToAllHeader(aGame);
break;
}
case CMD_DUMP_TEAM_INFO:{
if (argc == 5) {
team = atoi(argv[4]);
reportTeam(aGame, &fields, team);
}
else {
fprintf(stderr, "You have to specify a team number.\n");
}
break;
}
case CMD_DUMP_TEAM_REPORT_NAMES:{
if (argc == 5) {
player *aPlayer;
team = atoi(argv[4]);
for (aPlayer = aGame->players;
aPlayer; aPlayer = aPlayer->next) {
if (aPlayer->team == team) {
printf("reports/%s/%s_%d.txt\n", aGame->name,
aPlayer->name, aGame->turn);
}
}
}
else {
fprintf(stderr, "You have to specify a team number.\n");
}
break;
}
}
result = EXIT_SUCCESS;
freegame(aGame);
}
else {
fprintf(stderr, "Could not load game \"%s\".\n", argv[2]);
}
}
else {
usage();
}
return result;
}
NAME
CMD_test -- check the integrity of a game.
FUNCTION
Check if a GAME is OK by running checkIntegrity() on it.
NAME
CMD_selftest -- run a series of selftests
FUNCTION
Run a series of selftest. Used for debug purposes.
NAME
usage -- print usage info.