]> git.pond.sub.org Git - empserver/blob - src/lib/player/player.c
Declare all configuration parameters in optlist.h. Remove some
[empserver] / src / lib / player / player.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2000, 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 the "LEGAL", "LICENSE", "CREDITS" and "README" files for all the
23  *  related information and legal notices. It is expected that any future
24  *  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  *     
33  */
34
35 #include "prototypes.h"
36 #include <string.h>
37 #include "gamesdef.h"
38 #include "misc.h"
39 #include "player.h"
40 #include "proto.h"
41 #include "var.h"
42 #include "com.h"
43 #include "nat.h"
44 #include "sect.h"
45 #include "deity.h"
46 #include "file.h"
47 #include "proto.h"
48 #include "empio.h"
49 #include "empthread.h"
50 #include "tel.h"
51 #include "gen.h"
52 #include "subs.h"
53 #include "common.h"
54 #include "optlist.h"
55
56 #if !defined(_WIN32)
57 #include <unistd.h>
58 #include <sys/time.h>
59 #endif
60 #include <stdio.h>
61 #include <fcntl.h>
62
63 struct player *player;
64 extern int m_m_p_d;
65
66 void
67 player_main(struct player *p)
68 {
69     struct natstr *natp;
70     int hour[2];
71     int secs;
72     s_char buf[128];
73
74     p->state = PS_PLAYING;
75     player = p;
76     time(&player->lasttime);
77     (void)time(&player->curup);
78     showvers(CLIENTPROTO);
79     show_motd();
80     if (init_nats() < 0)
81         return;
82     natp = getnatp(player->cnum);
83     if (player->god && !match_user(authfil, player)) {
84         logerror("NON-AUTHed Login attempted by %s", praddr(player));
85         pr("You're not a deity!\n");
86         return;
87     }
88     if (!gamehours(player->curup, hour)) {
89         pr("Empire hours restriction in force\n");
90         if ((natp->nat_stat & STAT_GOD) == 0)
91             return;
92     }
93     daychange(player->curup);
94     if ((player->minleft = getminleft(player->curup, hour, &m_m_p_d)) <= 0) {
95         pr("Time exceeded today\n");
96         return;
97     }
98     if ((*natp->nat_hostaddr &&
99          *player->hostaddr &&
100          strcmp(natp->nat_hostaddr, player->hostaddr)) ||
101         (*natp->nat_userid &&
102          *player->userid && strcmp(natp->nat_userid, player->userid))) {
103         if (natp->nat_stat != VIS) {
104             pr("Last connection from: %s", ctime(&natp->nat_last_login));
105             pr("                  to: %s", natp->nat_last_login <
106                natp->nat_last_logout ? ctime(&natp->
107                                              nat_last_logout) : "?");
108             pr("                  by: %s@%s\n",
109                *natp->nat_userid ? natp->nat_userid : (s_char *)"nobody",
110                *natp->nat_hostname ? natp->nat_hostname : *natp->
111                nat_hostaddr ? natp->nat_hostaddr : (s_char *)"nowhere");
112         }
113     }
114     if (*player->userid)
115         strcpy(natp->nat_userid, player->userid);
116     else
117         strcpy(natp->nat_userid, "nobody");
118
119     if (*player->hostname)
120         strcpy(natp->nat_hostname, player->hostname);
121     else
122         strcpy(natp->nat_hostname, "nowhere");
123
124     if (*player->hostaddr)
125         strcpy(natp->nat_hostaddr, player->hostaddr);
126
127     time(&natp->nat_last_login);
128     natp->nat_connected = 1;
129     putnat(natp);
130     if (natp->nat_flags & NF_INFORM && natp->nat_tgms > 0) {
131         if (natp->nat_tgms == 1)
132             pr("You have a new telegram waiting ...\n");
133         else
134             pr("You have %s new telegrams waiting ...\n",
135                numstr(buf, natp->nat_tgms));
136         natp->nat_tgms = 0;
137     }
138
139     while (status()) {
140         if (command() == 0 && !player->aborted)
141             break;
142         player->aborted = 0;
143     }
144     /* #*# I put the following line in to prevent server crash -KHS */
145     natp = getnatp(player->cnum);
146     /*
147      * randomly round up to the nearest minute,
148      * charging at least 15 seconds.
149      */
150     time(&natp->nat_last_logout);
151     secs = max(natp->nat_last_logout - player->lasttime, 15);
152     natp->nat_minused += secs / 60;
153     secs = secs % 60;
154     if (chance(secs / 60.0))
155         natp->nat_minused += 1;
156     natp->nat_connected = 0;
157     putnat(natp);
158     pr("Bye-bye\n");
159 }
160
161 int
162 command(void)
163 {
164     register unsigned int x;
165     s_char *redir;
166     s_char scanspace[1024];
167
168     if (getcommand(player->combuf) < 0)
169         return 0;
170     if (parse(player->combuf, player->argp, &player->condarg,
171               scanspace, &redir) < 0) {
172         pr("See \"info Syntax\"?\n");
173     } else {
174         /* XXX don't use alarm; use a scavenger thread */
175         /* DONT USE IT!!!! alarm and sleep may and dont work
176            together -- Sasha */
177         /* alarm((unsigned int)60*60); 1 hour */
178         if (player->condarg != (s_char *)0)
179             for (x = 0; x < strlen(player->condarg); x++)
180                 if (isupper(*(player->condarg + x)))
181                     *(player->condarg + x) =
182                         tolower(*(player->condarg + x));
183         if (dispatch(player->combuf, redir) < 0)
184             pr("Try \"list of commands\" or \"info\"\n");
185     }
186     return 1;
187 }
188
189 int
190 status(void)
191 {
192     struct natstr *natp;
193     int minute;
194     struct sctstr sect;
195     int hour[2];
196     s_char buf[128];
197
198     if (player->state == PS_SHUTDOWN)
199         return 0;
200     natp = getnatp(player->cnum);
201     if (io_error(player->iop) || io_eof(player->iop)) {
202         putnat(natp);
203         return 0;
204     }
205     player->visitor = (natp->nat_stat & (STAT_NORM | STAT_GOD)) == 0;
206     if (player->dolcost != 0.0) {
207         if (player->dolcost > 100.0)
208             pr("That just cost you $%.2f\n", player->dolcost);
209         else if (player->dolcost < -100.0)
210             pr("You just made $%.2f\n", -player->dolcost);
211         if (natp->nat_money < player->dolcost && !player->broke) {
212             player->broke = 1;
213             player->nstat &= ~MONEY;
214             pr("You are now broke; industries are on strike.\n");
215         } else if (player->broke && natp->nat_money - player->dolcost > 0) {
216             player->broke = 0;
217             player->nstat |= MONEY;
218             pr("You are no longer broke!\n");
219         }
220         natp->nat_money -= roundavg(player->dolcost);
221         player->dolcost = 0.0;
222     } else {
223         if (natp->nat_money < 0.0 && !player->broke) {
224             player->broke = 1;
225             player->nstat &= ~MONEY;
226             pr("You are now broke; industries are on strike.\n");
227         }
228         if (player->broke && natp->nat_money > 0) {
229             player->broke = 0;
230             player->nstat |= MONEY;
231             pr("You are no longer broke!\n");
232         }
233     }
234     getsect(natp->nat_xcap, natp->nat_ycap, &sect);
235     if ((sect.sct_type == SCT_CAPIT || sect.sct_type == SCT_MOUNT ||
236          sect.sct_type == SCT_SANCT) && sect.sct_own == player->cnum)
237         player->nstat |= CAP;
238     else
239         player->nstat &= ~CAP;
240     /* Ok, has the country owner reset his capital yet after it was sacked? */
241     if (natp->nat_flags & NF_SACKED)
242         player->nstat &= ~CAP;  /* No capital yet */
243     player->ncomstat = player->nstat;
244     (void)time(&player->curup);
245     minute = (player->curup - player->lasttime) / 60;
246     if (minute > 0) {
247         player->minleft -= minute;
248         if (player->minleft <= 0) {
249             /*
250              * countdown timer "player->minleft" has expired.
251              * either day change, or hours restriction
252              */
253             daychange(player->curup);
254             if (!gamehours(player->curup, hour)) {
255                 pr("Empire hours restriction in force\n");
256                 if ((natp->nat_stat & STAT_GOD) == 0) {
257                     putnat(natp);
258                     return 0;
259                 }
260             }
261             player->minleft = getminleft(player->curup, hour, &m_m_p_d);
262         }
263         player->lasttime += minute * 60;
264         natp->nat_minused += minute;
265     }
266     if ((player->nstat & NORM) && natp->nat_minused > m_m_p_d) {
267         pr("Max minutes per day limit exceeded.\n");
268         player->ncomstat = VIS;
269     }
270     if (player->btused) {
271         natp->nat_btu -= player->btused;
272         player->btused = 0;
273     }
274     if (natp->nat_tgms > 0) {
275         if (!(natp->nat_flags & NF_INFORM)) {
276             if (natp->nat_tgms == 1)
277                 pr("You have a new telegram waiting ...\n");
278             else
279                 pr("You have %s new telegrams waiting ...\n",
280                    numstr(buf, natp->nat_tgms));
281             natp->nat_tgms = 0;
282         }
283     }
284     if (natp->nat_ann > 0) {
285         if (natp->nat_ann == 1)
286             pr("You have a new announcement waiting ...\n");
287         else
288             pr("You have %s new announcements waiting ...\n",
289                numstr(buf, natp->nat_ann));
290         natp->nat_ann = 0;
291     }
292     if (!player->visitor && !player->god && (player->nstat & CAP) == 0)
293         pr("You lost your capital... better designate one\n");
294     putnat(natp);
295     if (gamedown() && !player->god) {
296         pr("gamedown\n");
297         return 0;
298     }
299     return 1;
300 }
301
302 /*
303  * actually a command; redirection and piping ignored.
304  * XXX This whole mess should be redone; execute block should
305  * start with "exec start", and should end with "exec end".
306  * We'll wait until 1.2 I guess.
307  */
308 int
309 execute(void)
310 {
311     s_char buf[512];
312     int abort;
313     s_char *p;
314     s_char *redir;
315     s_char scanspace[1024];
316
317     abort = 0;
318     redir = 0;
319
320     p = getstarg(player->argp[1], "File? ", buf);
321
322     if (p == (s_char *)0 || p == '\0')
323         return RET_SYN;
324
325     prexec(player->argp[1]);
326     while (!abort && status()) {
327         if (recvclient(buf, sizeof(buf)) < 0)
328             break;
329         if (parse(buf, player->argp, &player->condarg,
330                   scanspace, &redir) < 0) {
331             abort = 1;
332             continue;
333         }
334         if (redir == 0)
335             pr("\nExecute : %s\n", buf);
336         if (dispatch(buf, redir) < 0)
337             abort = 1;
338     }
339     if (abort) {
340         while (recvclient(buf, sizeof(buf)) >= 0) ;
341     }
342     if (redir == 0)
343         pr("Execute : %s\n", abort ? "aborted" : "terminated");
344     return RET_OK;
345 }
346
347 int
348 show_motd(void)
349 {
350     int upf;
351     struct telstr tgm;
352     s_char buf[MAXTELSIZE];
353
354 #if !defined(_WIN32)
355     if ((upf = open(upfil, O_RDONLY, 0)) < 0)
356 #else
357     if ((upf = open(upfil, O_RDONLY | O_BINARY, 0)) < 0)
358 #endif
359         return RET_FAIL;
360     if (read(upf, (s_char *)&tgm, sizeof(tgm)) != sizeof(tgm)) {
361         logerror("bad header on login message (upfil)");
362         close(upf);
363         return RET_FAIL;
364     }
365     if (read(upf, buf, tgm.tel_length) != tgm.tel_length) {
366         logerror("bad length %d on login message", tgm.tel_length);
367         close(upf);
368         return RET_FAIL;
369     }
370     if (tgm.tel_length >= (long)sizeof(buf))
371         tgm.tel_length = sizeof(buf) - 1;
372     buf[tgm.tel_length] = 0;
373     pr(buf);
374     (void)close(upf);
375     return RET_OK;
376 }
377
378 int
379 match_user(char *file, struct player *player)
380 {
381     FILE *fp;
382     int match = 0;
383     s_char host[256];
384     s_char user[256];
385
386     if ((fp = fopen(file, "r")) == NULL) {
387         /*logerror("Cannot find file %s", file); */
388         return 0;
389     }
390     match = 0;
391     while (!feof(fp) && !match) {
392         if (fgets(host, sizeof(host) - 1, fp) == NULL)
393             break;
394         if (host[0] == '#')
395             continue;
396         if (fgets(user, sizeof(user) - 1, fp) == NULL)
397             break;
398         host[strlen(host) - 1] = '\0';
399         user[strlen(user) - 1] = '\0';
400         if (strstr(player->userid, user) &&
401             (strstr(player->hostaddr, host) ||
402              strstr(player->hostname, host)))
403             ++match;
404     }
405     fclose(fp);
406     return match;
407 }
408
409 int
410 quit(void)
411 {
412     player->state = PS_SHUTDOWN;
413     return RET_OK;
414 }
415
416 s_char *
417 praddr(struct player *player)
418 {
419     return prbuf("%s@%s", player->userid,
420                  *player->hostname ? player->hostname : player->hostaddr);
421 }