]> git.pond.sub.org Git - empserver/blob - src/lib/player/player.c
Don't call time() for nat_last_login, use player->curup
[empserver] / src / lib / player / player.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2008, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                           Ken Stevens, Steve McClure
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  *  ---
21  *
22  *  See files README, COPYING and CREDITS in the root of the source
23  *  tree for related information and legal notices.  It is expected
24  *  that future projects/authors will amend these files as needed.
25  *
26  *  ---
27  *
28  *  player.c: Main command loop for a player
29  *
30  *  Known contributors to this file:
31  *     Steve McClure, 2000
32  *     Markus Armbruster, 2004-2008
33  *     Ron Koenderink, 2004-2007
34  */
35
36 #include <config.h>
37
38 #include <errno.h>
39 #include <stdio.h>
40 #include "com.h"
41 #include "empio.h"
42 #include "empthread.h"
43 #include "file.h"
44 #include "journal.h"
45 #include "misc.h"
46 #include "nat.h"
47 #include "optlist.h"
48 #include "player.h"
49 #include "proto.h"
50 #include "prototypes.h"
51 #include "tel.h"
52
53
54 static int command(void);
55 static int status(void);
56
57 struct player *player;
58
59 void
60 player_main(struct player *p)
61 {
62     struct natstr *natp;
63     char buf[128];
64
65     p->state = PS_PLAYING;
66     player = p;
67     time(&player->curup);
68     update_timeused_login(player->curup);
69     show_motd();
70     if (init_nats() < 0) {
71         pr("Server confused, try again later\n");
72         return;
73     }
74     natp = getnatp(player->cnum);
75     if (!gamehours(player->curup)) {
76         pr("Empire hours restriction in force\n");
77         if (natp->nat_stat != STAT_GOD)
78             return;
79     }
80     if (natp->nat_stat == STAT_ACTIVE &&
81         natp->nat_timeused > m_m_p_d * 60) {
82         pr("Time exceeded today\n");
83         return;
84     }
85     if (natp->nat_stat != STAT_VIS
86         && natp->nat_last_login
87         && (strcmp(natp->nat_hostaddr, player->hostaddr)
88             || strcmp(natp->nat_userid, player->userid))) {
89         pr("Last connection from: %s", ctime(&natp->nat_last_login));
90         pr("                  to: %s",
91            natp->nat_last_login <= natp->nat_last_logout
92            ? ctime(&natp->nat_last_logout) : "?");
93         pr("                  by: %s@%s\n",
94            natp->nat_userid,
95            *natp->nat_hostname ? natp->nat_hostname : natp->nat_hostaddr);
96     }
97     strcpy(natp->nat_userid, player->userid);
98     strcpy(natp->nat_hostname, player->hostname);
99     strcpy(natp->nat_hostaddr, player->hostaddr);
100     natp->nat_last_login = player->curup;
101     putnat(natp);
102     journal_login();
103     if (natp->nat_flags & NF_INFORM && natp->nat_tgms > 0) {
104         if (natp->nat_tgms == 1)
105             pr("You have a new telegram waiting ...\n");
106         else
107             pr("You have %s new telegrams waiting ...\n",
108                numstr(buf, natp->nat_tgms));
109         natp->nat_tgms = 0;
110     }
111
112     while (status()) {
113         command();
114         player->aborted = player->eof;
115         empth_yield();
116     }
117     /* #*# I put the following line in to prevent server crash -KHS */
118     natp = getnatp(player->cnum);
119     time(&natp->nat_last_logout);
120     putnat(natp);
121     update_timeused(natp->nat_last_logout);
122     enforce_minimum_session_time();
123     pr("Bye-bye\n");
124     journal_logout();
125 }
126
127 static int
128 command(void)
129 {
130     char *redir;                /* UTF-8 */
131     char scanspace[1024];
132
133     if (getcommand(player->combuf) < 0)
134         return 0;
135     if (parse(player->combuf, scanspace, player->argp, player->comtail,
136               &player->condarg, &redir) < 0) {
137         pr("See \"info Syntax\"?\n");
138     } else {
139         if (dispatch(player->combuf, redir) < 0)
140             pr("Try \"list of commands\" or \"info\"\n");
141     }
142     return 1;
143 }
144
145 static int
146 status(void)
147 {
148     struct natstr *natp;
149     int old_nstat;
150     char buf[128];
151
152     if (player->eof || player->state == PS_SHUTDOWN)
153         return 0;
154     natp = getnatp(player->cnum);
155     if (player->dolcost > 100.0)
156         pr("That just cost you $%.2f\n", player->dolcost);
157     else if (player->dolcost < -100.0)
158         pr("You just made $%.2f\n", -player->dolcost);
159     if (player->dolcost != 0.0) {
160         /*
161          * Hackish work around for a race condition in the nightly
162          * build's regression tests: sometimes the update starts right
163          * after the force command yields, sometimes a bit later.  If
164          * it is late, we use one random number here, for the bye,
165          * and throwing off the random sequence.
166          */
167         natp->nat_money -= roundavg(player->dolcost);
168         player->dolcost = 0.0;
169     }
170
171     old_nstat = player->nstat;
172     player_set_nstat(player, natp);
173     if ((old_nstat & MONEY) && !(player->nstat & MONEY))
174         pr("You are now broke; industries are on strike.\n");
175     if (!(old_nstat & MONEY) && (player->nstat & MONEY))
176         pr("You are no longer broke!\n");
177
178     time(&player->curup);
179     update_timeused(player->curup);
180     if (natp->nat_stat == STAT_ACTIVE &&
181         natp->nat_timeused > m_m_p_d * 60) {
182         pr("Max minutes per day limit exceeded.\n");
183         player->nstat = (player->nstat & ~NORM) | VIS;
184     }
185     if (player->btused) {
186         natp->nat_btu -= player->btused;
187         player->btused = 0;
188     }
189     if (natp->nat_tgms > 0) {
190         if (!(natp->nat_flags & NF_INFORM)) {
191             if (natp->nat_tgms == 1)
192                 pr("You have a new telegram waiting ...\n");
193             else
194                 pr("You have %s new telegrams waiting ...\n",
195                    numstr(buf, natp->nat_tgms));
196             natp->nat_tgms = 0;
197         }
198     }
199     if (natp->nat_ann > 0) {
200         if (natp->nat_ann == 1)
201             pr("You have a new announcement waiting ...\n");
202         else
203             pr("You have %s new announcements waiting ...\n",
204                numstr(buf, natp->nat_ann));
205         natp->nat_ann = 0;
206     }
207     if (natp->nat_stat == STAT_ACTIVE && (player->nstat & CAP) == 0)
208         pr("You lost your capital... better designate one\n");
209     putnat(natp);
210     if (gamedown() && !player->god) {
211         pr("gamedown\n");
212         return 0;
213     }
214     return 1;
215 }
216
217 /*
218  * XXX This whole mess should be redone; execute block should
219  * start with "exec start", and should end with "exec end".
220  * We'll wait until 1.2 I guess.
221  */
222 int
223 execute(void)
224 {
225     char buf[1024];
226     int failed;
227     char *p;
228     char *redir;                /* UTF-8 */
229     char scanspace[1024];
230
231     failed = 0;
232
233     if (player->comtail[1])
234         p = player->comtail[1];
235     else
236         p = getstring("File? ", buf);
237     if (p == NULL || *p == '\0')
238         return RET_SYN;
239     prexec(p);
240
241     while (!failed && status()) {
242         player->nstat &= ~EXEC;
243         if (recvclient(buf, sizeof(buf)) < 0)
244             break;
245         if (parse(buf, scanspace, player->argp, player->comtail,
246                   &player->condarg, &redir) < 0) {
247             failed = 1;
248             continue;
249         }
250         pr("\nExecute : %s\n", buf);
251         if (redir) {
252             pr("Execute : redirection not supported\n");
253             failed = 1;
254         } else if (dispatch(buf, NULL) < 0)
255             failed = 1;
256     }
257     if (failed) {
258         while (recvclient(buf, sizeof(buf)) >= 0) ;
259     }
260
261     pr("Execute : %s\n", failed ? "aborted" : "terminated");
262     player->eof = 0;
263     return RET_OK;
264 }
265
266 int
267 show_motd(void)
268 {
269     FILE *motd_fp;
270     struct telstr tgm;
271     char buf[MAXTELSIZE + 1];   /* UTF-8 */
272
273     if ((motd_fp = fopen(motdfil, "rb")) == NULL) {
274         if (errno == ENOENT)
275             return RET_OK;
276         else {
277             pr ("Could not open motd.\n");
278             logerror("Could not open motd (%s).\n", motdfil);
279             return RET_FAIL;
280         }
281     }
282     if (fread(&tgm, sizeof(tgm), 1, motd_fp) != 1) {
283         logerror("bad header on login message (motdfil)");
284         fclose(motd_fp);
285         return RET_FAIL;
286     }
287     if (tgm.tel_length >= (long)sizeof(buf)) {
288         logerror("text length (%ld) is too long for login message (motdfil)", tgm.tel_length);
289         fclose(motd_fp);
290         return RET_FAIL;
291     }
292     if (fread(buf, tgm.tel_length, 1, motd_fp) != 1) {
293         logerror("bad length %ld on login message", tgm.tel_length);
294         fclose(motd_fp);
295         return RET_FAIL;
296     }
297     buf[tgm.tel_length] = 0;
298     uprnf(buf);
299     fclose(motd_fp);
300     return RET_OK;
301 }
302
303 int
304 quit(void)
305 {
306     player->state = PS_SHUTDOWN;
307     return RET_OK;
308 }
309
310 char *
311 praddr(struct player *p)
312 {
313     return prbuf("%s@%s", p->userid,
314                  *p->hostname ? p->hostname : p->hostaddr);
315 }