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