empserver/src/lib/player/player.c
Markus Armbruster b9b0710128 Fix treatment of EOF from player
Commit 79407e68 (v4.3.11) changed recvclient() to keep failing after
receiving EOF from player.  This was bad, because some places getting
input check player->aborted instead of recvclient() failure, and
player->aborted wasn't set on EOF.  Bugs caused by this:

* comm_bomb(), ship_bomb(), plane_bomb(), land_bomb() went into an
  infinite loop that eventually ate all memory.

* deli(), desi(), dist(), fly(), morale(), zdon(), att_prompt(),
  ask_move_in() interpreted EOF as empty input instead of no more
  input.

* cmd_sail_ship() dereferenced a null pointer.

Fix by setting player->aborted on EOF, too.
(cherry picked from commit b3a7a8ee11)
2008-07-15 06:37:05 -04:00

332 lines
8.5 KiB
C

/*
* 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.
*
* ---
*
* player.c: Main command loop for a player
*
* Known contributors to this file:
* Steve McClure, 2000
*/
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include "com.h"
#include "empio.h"
#include "empthread.h"
#include "file.h"
#include "journal.h"
#include "misc.h"
#include "nat.h"
#include "optlist.h"
#include "player.h"
#include "proto.h"
#include "prototypes.h"
#include "tel.h"
static int command(void);
static int status(void);
struct player *player;
void
player_main(struct player *p)
{
struct natstr *natp;
int secs;
char buf[128];
p->state = PS_PLAYING;
player = p;
time(&player->lasttime);
time(&player->curup);
show_motd();
if (init_nats() < 0) {
pr("Server confused, try again later\n");
return;
}
natp = getnatp(player->cnum);
if (!gamehours(player->curup)) {
pr("Empire hours restriction in force\n");
if (natp->nat_stat != STAT_GOD)
return;
}
daychange(player->curup);
if ((player->minleft = getminleft(player->curup, m_m_p_d)) <= 0) {
pr("Time exceeded today\n");
return;
}
if (natp->nat_stat != STAT_VIS
&& natp->nat_last_login
&& (strcmp(natp->nat_hostaddr, player->hostaddr)
|| strcmp(natp->nat_userid, player->userid))) {
pr("Last connection from: %s", ctime(&natp->nat_last_login));
pr(" to: %s",
natp->nat_last_login <= natp->nat_last_logout
? ctime(&natp->nat_last_logout) : "?");
pr(" by: %s@%s\n",
natp->nat_userid,
*natp->nat_hostname ? natp->nat_hostname : natp->nat_hostaddr);
}
strcpy(natp->nat_userid, player->userid);
strcpy(natp->nat_hostname, player->hostname);
strcpy(natp->nat_hostaddr, player->hostaddr);
time(&natp->nat_last_login);
putnat(natp);
journal_login();
if (natp->nat_flags & NF_INFORM && natp->nat_tgms > 0) {
if (natp->nat_tgms == 1)
pr("You have a new telegram waiting ...\n");
else
pr("You have %s new telegrams waiting ...\n",
numstr(buf, natp->nat_tgms));
natp->nat_tgms = 0;
}
while (status()) {
command();
player->aborted = player->eof;
empth_yield();
}
/* #*# I put the following line in to prevent server crash -KHS */
natp = getnatp(player->cnum);
/*
* randomly round up to the nearest minute,
* charging at least 15 seconds.
*/
time(&natp->nat_last_logout);
secs = MAX(natp->nat_last_logout - player->lasttime, 15);
natp->nat_minused += secs / 60;
secs = secs % 60;
if (chance(secs / 60.0))
natp->nat_minused += 1;
putnat(natp);
pr("Bye-bye\n");
journal_logout();
}
static int
command(void)
{
char *redir; /* UTF-8 */
char scanspace[1024];
if (getcommand(player->combuf) < 0)
return 0;
if (parse(player->combuf, scanspace, player->argp, player->comtail,
&player->condarg, &redir) < 0) {
pr("See \"info Syntax\"?\n");
} else {
if (dispatch(player->combuf, redir) < 0)
pr("Try \"list of commands\" or \"info\"\n");
}
return 1;
}
static int
status(void)
{
struct natstr *natp;
int old_nstat, minute;
char buf[128];
if (player->eof || player->state == PS_SHUTDOWN)
return 0;
natp = getnatp(player->cnum);
if (player->dolcost > 100.0)
pr("That just cost you $%.2f\n", player->dolcost);
else if (player->dolcost < -100.0)
pr("You just made $%.2f\n", -player->dolcost);
natp->nat_money -= roundavg(player->dolcost);
player->dolcost = 0.0;
old_nstat = player->nstat;
player_set_nstat(player, natp);
if ((old_nstat & MONEY) && !(player->nstat & MONEY))
pr("You are now broke; industries are on strike.\n");
if (!(old_nstat & MONEY) && (player->nstat & MONEY))
pr("You are no longer broke!\n");
time(&player->curup);
minute = (player->curup - player->lasttime) / 60;
if (minute > 0) {
player->minleft -= minute;
if (player->minleft <= 0) {
/*
* countdown timer "player->minleft" has expired.
* either day change, or hours restriction
*/
daychange(player->curup);
if (!gamehours(player->curup)) {
pr("Empire hours restriction in force\n");
if (natp->nat_stat != STAT_GOD) {
putnat(natp);
return 0;
}
}
player->minleft = getminleft(player->curup, m_m_p_d);
}
player->lasttime += minute * 60;
natp->nat_minused += minute;
}
if (natp->nat_stat == STAT_ACTIVE && natp->nat_minused > m_m_p_d) {
pr("Max minutes per day limit exceeded.\n");
player->nstat = (player->nstat & ~NORM) | VIS;
}
if (player->btused) {
natp->nat_btu -= player->btused;
player->btused = 0;
}
if (natp->nat_tgms > 0) {
if (!(natp->nat_flags & NF_INFORM)) {
if (natp->nat_tgms == 1)
pr("You have a new telegram waiting ...\n");
else
pr("You have %s new telegrams waiting ...\n",
numstr(buf, natp->nat_tgms));
natp->nat_tgms = 0;
}
}
if (natp->nat_ann > 0) {
if (natp->nat_ann == 1)
pr("You have a new announcement waiting ...\n");
else
pr("You have %s new announcements waiting ...\n",
numstr(buf, natp->nat_ann));
natp->nat_ann = 0;
}
if (natp->nat_stat == STAT_ACTIVE && (player->nstat & CAP) == 0)
pr("You lost your capital... better designate one\n");
putnat(natp);
if (gamedown() && !player->god) {
pr("gamedown\n");
return 0;
}
return 1;
}
/*
* XXX This whole mess should be redone; execute block should
* start with "exec start", and should end with "exec end".
* We'll wait until 1.2 I guess.
*/
int
execute(void)
{
char buf[1024];
int failed;
char *p;
char *redir; /* UTF-8 */
char scanspace[1024];
failed = 0;
if (player->comtail[1])
p = player->comtail[1];
else
p = getstring("File? ", buf);
if (p == NULL || *p == '\0')
return RET_SYN;
prexec(p);
while (!failed && status()) {
player->nstat &= ~EXEC;
if (recvclient(buf, sizeof(buf)) < 0)
break;
if (parse(buf, scanspace, player->argp, player->comtail,
&player->condarg, &redir) < 0) {
failed = 1;
continue;
}
pr("\nExecute : %s\n", buf);
if (redir) {
pr("Execute : redirection not supported\n");
failed = 1;
} else if (dispatch(buf, NULL) < 0)
failed = 1;
}
if (failed) {
while (recvclient(buf, sizeof(buf)) >= 0) ;
}
pr("Execute : %s\n", failed ? "aborted" : "terminated");
player->eof = 0;
return RET_OK;
}
int
show_motd(void)
{
FILE *motd_fp;
struct telstr tgm;
char buf[MAXTELSIZE + 1]; /* UTF-8 */
if ((motd_fp = fopen(motdfil, "rb")) == NULL) {
if (errno == ENOENT)
return RET_OK;
else {
pr ("Could not open motd.\n");
logerror("Could not open motd (%s).\n", motdfil);
return RET_SYS;
}
}
if (fread(&tgm, sizeof(tgm), 1, motd_fp) != 1) {
logerror("bad header on login message (motdfil)");
fclose(motd_fp);
return RET_FAIL;
}
if (tgm.tel_length >= (long)sizeof(buf)) {
logerror("text length (%ld) is too long for login message (motdfil)", tgm.tel_length);
fclose(motd_fp);
return RET_FAIL;
}
if (fread(buf, tgm.tel_length, 1, motd_fp) != 1) {
logerror("bad length %ld on login message", tgm.tel_length);
fclose(motd_fp);
return RET_FAIL;
}
buf[tgm.tel_length] = 0;
uprnf(buf);
fclose(motd_fp);
return RET_OK;
}
int
quit(void)
{
player->state = PS_SHUTDOWN;
return RET_OK;
}
char *
praddr(struct player *p)
{
return prbuf("%s@%s", p->userid,
*p->hostname ? p->hostname : p->hostaddr);
}