]> git.pond.sub.org Git - empserver/blob - src/lib/player/player.c
Indented with src/scripts/indent-emp.
[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
55 #if !defined(_WIN32)
56 #include <unistd.h>
57 #include <sys/time.h>
58 #endif
59 #include <stdio.h>
60 #include <fcntl.h>
61
62 struct player *player;
63 extern int m_m_p_d;
64
65 void
66 player_main(struct player *p)
67 {
68     extern s_char *authfil;
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     int kill_player();
167     s_char scanspace[1024];
168
169     if (getcommand(player->combuf) < 0)
170         return 0;
171     if (parse(player->combuf, player->argp, &player->condarg,
172               scanspace, &redir) < 0) {
173         pr("See \"info Syntax\"?\n");
174     } else {
175         /* XXX don't use alarm; use a scavenger thread */
176         /* DONT USE IT!!!! alarm and sleep may and dont work
177            together -- Sasha */
178         /* alarm((unsigned int)60*60); 1 hour */
179         if (player->condarg != (s_char *)0)
180             for (x = 0; x < strlen(player->condarg); x++)
181                 if (isupper(*(player->condarg + x)))
182                     *(player->condarg + x) =
183                         tolower(*(player->condarg + x));
184         if (dispatch(player->combuf, redir) < 0)
185             pr("Try \"list of commands\" or \"info\"\n");
186     }
187     return 1;
188 }
189
190 int
191 status(void)
192 {
193     struct natstr *natp;
194     int minute;
195     struct sctstr sect;
196     int hour[2];
197     s_char buf[128];
198
199     if (player->state == PS_SHUTDOWN)
200         return 0;
201     natp = getnatp(player->cnum);
202     if (io_error(player->iop) || io_eof(player->iop)) {
203         putnat(natp);
204         return 0;
205     }
206     player->visitor = (natp->nat_stat & (STAT_NORM | STAT_GOD)) == 0;
207     if (player->dolcost != 0.0) {
208         if (player->dolcost > 100.0)
209             pr("That just cost you $%.2f\n", player->dolcost);
210         else if (player->dolcost < -100.0)
211             pr("You just made $%.2f\n", -player->dolcost);
212         if (natp->nat_money < player->dolcost && !player->broke) {
213             player->broke = 1;
214             player->nstat &= ~MONEY;
215             pr("You are now broke; industries are on strike.\n");
216         } else if (player->broke && natp->nat_money - player->dolcost > 0) {
217             player->broke = 0;
218             player->nstat |= MONEY;
219             pr("You are no longer broke!\n");
220         }
221         natp->nat_money -= roundavg(player->dolcost);
222         player->dolcost = 0.0;
223     } else {
224         if (natp->nat_money < 0.0 && !player->broke) {
225             player->broke = 1;
226             player->nstat &= ~MONEY;
227             pr("You are now broke; industries are on strike.\n");
228         }
229         if (player->broke && natp->nat_money > 0) {
230             player->broke = 0;
231             player->nstat |= MONEY;
232             pr("You are no longer broke!\n");
233         }
234     }
235     getsect(natp->nat_xcap, natp->nat_ycap, &sect);
236     if ((sect.sct_type == SCT_CAPIT || sect.sct_type == SCT_MOUNT ||
237          sect.sct_type == SCT_SANCT) && sect.sct_own == player->cnum)
238         player->nstat |= CAP;
239     else
240         player->nstat &= ~CAP;
241     /* Ok, has the country owner reset his capital yet after it was sacked? */
242     if (natp->nat_flags & NF_SACKED)
243         player->nstat &= ~CAP;  /* No capital yet */
244     player->ncomstat = player->nstat;
245     (void)time(&player->curup);
246     minute = (player->curup - player->lasttime) / 60;
247     if (minute > 0) {
248         player->minleft -= minute;
249         if (player->minleft <= 0) {
250             /*
251              * countdown timer "player->minleft" has expired.
252              * either day change, or hours restriction
253              */
254             daychange(player->curup);
255             if (!gamehours(player->curup, hour)) {
256                 pr("Empire hours restriction in force\n");
257                 if ((natp->nat_stat & STAT_GOD) == 0) {
258                     putnat(natp);
259                     return 0;
260                 }
261             }
262             player->minleft = getminleft(player->curup, hour, &m_m_p_d);
263         }
264         player->lasttime += minute * 60;
265         natp->nat_minused += minute;
266     }
267     if ((player->nstat & NORM) && natp->nat_minused > m_m_p_d) {
268         pr("Max minutes per day limit exceeded.\n");
269         player->ncomstat = VIS;
270     }
271     if (player->btused) {
272         natp->nat_btu -= player->btused;
273         player->btused = 0;
274     }
275     if (natp->nat_tgms > 0) {
276         if (!(natp->nat_flags & NF_INFORM)) {
277             if (natp->nat_tgms == 1)
278                 pr("You have a new telegram waiting ...\n");
279             else
280                 pr("You have %s new telegrams waiting ...\n",
281                    numstr(buf, natp->nat_tgms));
282             natp->nat_tgms = 0;
283         }
284     }
285     if (natp->nat_ann > 0) {
286         if (natp->nat_ann == 1)
287             pr("You have a new announcement waiting ...\n");
288         else
289             pr("You have %s new announcements waiting ...\n",
290                numstr(buf, natp->nat_ann));
291         natp->nat_ann = 0;
292     }
293     if (!player->visitor && !player->god && (player->nstat & CAP) == 0)
294         pr("You lost your capital... better designate one\n");
295     putnat(natp);
296     if (gamedown() && !player->god) {
297         pr("gamedown\n");
298         return 0;
299     }
300     return 1;
301 }
302
303 /*
304  * actually a command; redirection and piping ignored.
305  * XXX This whole mess should be redone; execute block should
306  * start with "exec start", and should end with "exec end".
307  * We'll wait until 1.2 I guess.
308  */
309 int
310 execute(void)
311 {
312     s_char buf[512];
313     int abort;
314     s_char *p;
315     s_char *redir;
316     s_char scanspace[1024];
317
318     abort = 0;
319     redir = 0;
320
321     p = getstarg(player->argp[1], "File? ", buf);
322
323     if (p == (s_char *)0 || p == '\0')
324         return RET_SYN;
325
326     prexec(player->argp[1]);
327     while (!abort && status()) {
328         if (recvclient(buf, sizeof(buf)) < 0)
329             break;
330         if (parse(buf, player->argp, &player->condarg,
331                   scanspace, &redir) < 0) {
332             abort = 1;
333             continue;
334         }
335         if (redir == 0)
336             pr("\nExecute : %s\n", buf);
337         if (dispatch(buf, redir) < 0)
338             abort = 1;
339     }
340     if (abort) {
341         while (recvclient(buf, sizeof(buf)) >= 0) ;
342     }
343     if (redir == 0)
344         pr("Execute : %s\n", abort ? "aborted" : "terminated");
345     return RET_OK;
346 }
347
348 int
349 show_motd(void)
350 {
351     extern s_char *upfil;
352     int upf;
353     struct telstr tgm;
354     s_char buf[MAXTELSIZE];
355
356 #if !defined(_WIN32)
357     if ((upf = open(upfil, O_RDONLY, 0)) < 0)
358 #else
359     if ((upf = open(upfil, O_RDONLY | O_BINARY, 0)) < 0)
360 #endif
361         return RET_FAIL;
362     if (read(upf, (s_char *)&tgm, sizeof(tgm)) != sizeof(tgm)) {
363         logerror("bad header on login message (upfil)");
364         close(upf);
365         return RET_FAIL;
366     }
367     if (read(upf, buf, tgm.tel_length) != tgm.tel_length) {
368         logerror("bad length %d on login message", tgm.tel_length);
369         close(upf);
370         return RET_FAIL;
371     }
372     if (tgm.tel_length >= (long)sizeof(buf))
373         tgm.tel_length = sizeof(buf) - 1;
374     buf[tgm.tel_length] = 0;
375     pr(buf);
376     (void)close(upf);
377     return RET_OK;
378 }
379
380 int
381 match_user(char *file, struct player *player)
382 {
383     FILE *fp;
384     int match = 0;
385     s_char host[256];
386     s_char user[256];
387
388     if ((fp = fopen(file, "r")) == NULL) {
389         /*logerror("Cannot find file %s", file); */
390         return 0;
391     }
392     match = 0;
393     while (!feof(fp) && !match) {
394         if (fgets(host, sizeof(host) - 1, fp) == NULL)
395             break;
396         if (host[0] == '#')
397             continue;
398         if (fgets(user, sizeof(user) - 1, fp) == NULL)
399             break;
400         host[strlen(host) - 1] = '\0';
401         user[strlen(user) - 1] = '\0';
402         if (strstr(player->userid, user) &&
403             (strstr(player->hostaddr, host) ||
404              strstr(player->hostname, host)))
405             ++match;
406     }
407     fclose(fp);
408     return match;
409 }
410
411 int
412 quit(void)
413 {
414     player->state = PS_SHUTDOWN;
415     return RET_OK;
416 }
417
418 s_char *
419 praddr(struct player *player)
420 {
421     return prbuf("%s@%s", player->userid,
422                  *player->hostname ? player->hostname : player->hostaddr);
423 }