]> git.pond.sub.org Git - empserver/blob - src/lib/player/player.c
bf6d682a3d1e850785231e164fa2928b7f7a75cd
[empserver] / src / lib / player / player.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2011, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire 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 3 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, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  player.c: Main command loop for a player
28  *
29  *  Known contributors to this file:
30  *     Steve McClure, 2000
31  *     Markus Armbruster, 2004-2011
32  *     Ron Koenderink, 2004-2009
33  */
34
35 #include <config.h>
36
37 #include "com.h"
38 #include "empio.h"
39 #include "empthread.h"
40 #include "file.h"
41 #include "journal.h"
42 #include "misc.h"
43 #include "nat.h"
44 #include "optlist.h"
45 #include "player.h"
46 #include "proto.h"
47 #include "prototypes.h"
48 #include "tel.h"
49
50
51 static int command(void);
52 static int status(void);
53
54 struct player *player;
55
56 void
57 player_main(struct player *p)
58 {
59     struct natstr *natp;
60     char buf[128];
61
62     p->state = PS_PLAYING;
63     player = p;
64     time(&player->curup);
65     update_timeused_login(player->curup);
66     show_motd();
67     if (init_nats() < 0) {
68         pr("Server confused, try again later\n");
69         return;
70     }
71     natp = getnatp(player->cnum);
72     if (!may_play_now(natp, player->curup))
73         return;
74     if (natp->nat_stat != STAT_VIS
75         && natp->nat_last_login
76         && (strcmp(natp->nat_hostaddr, player->hostaddr)
77             || strcmp(natp->nat_userid, player->userid))) {
78         pr("Last connection from: %s", ctime(&natp->nat_last_login));
79         pr("                  to: %s",
80            natp->nat_last_login <= natp->nat_last_logout
81            ? ctime(&natp->nat_last_logout) : "?");
82         pr("                  by: %s@%s\n",
83            natp->nat_userid,
84            *natp->nat_hostname ? natp->nat_hostname : natp->nat_hostaddr);
85     }
86     strcpy(natp->nat_userid, player->userid);
87     strcpy(natp->nat_hostname, player->hostname);
88     strcpy(natp->nat_hostaddr, player->hostaddr);
89     natp->nat_last_login = player->curup;
90     putnat(natp);
91     journal_login();
92     if (natp->nat_flags & NF_INFORM && natp->nat_tgms > 0) {
93         if (natp->nat_tgms == 1)
94             pr("You have a new telegram waiting ...\n");
95         else
96             pr("You have %s new telegrams waiting ...\n",
97                numstr(buf, natp->nat_tgms));
98         natp->nat_tgms = 0;
99     }
100
101     while (status() && command()) {
102         player->aborted = player->eof;
103         empth_yield();
104     }
105     /* #*# I put the following line in to prevent server crash -KHS */
106     natp = getnatp(player->cnum);
107     time(&natp->nat_last_logout);
108     putnat(natp);
109     update_timeused(natp->nat_last_logout);
110     enforce_minimum_session_time();
111     pr("Bye-bye\n");
112     journal_logout();
113 }
114
115 static int
116 command(void)
117 {
118     struct natstr *natp = getnatp(player->cnum);
119     char *redir;                /* UTF-8 */
120     char scanspace[1024];
121     time_t now;
122
123     prprompt(natp->nat_timeused / 60, natp->nat_btu);
124     if (getcommand(player->combuf) < 0)
125         return player->aborted;
126
127     now = time(NULL);
128     update_timeused(now);
129     if (!player->god && !may_play_now(natp, now))
130         return 0;
131
132     if (parse(player->combuf, scanspace, player->argp, player->comtail,
133               &player->condarg, &redir) < 0) {
134         pr("See \"info Syntax\"?\n");
135     } else {
136         if (dispatch(player->combuf, redir) < 0)
137             pr("Try \"list of commands\" or \"info\"\n");
138     }
139     return 1;
140 }
141
142 static int
143 status(void)
144 {
145     struct natstr *natp;
146     int old_nstat;
147     char buf[128];
148
149     if (player->eof || player->state == PS_SHUTDOWN)
150         return 0;
151     natp = getnatp(player->cnum);
152     if (player->dolcost > 100.0)
153         pr("That just cost you $%.2f\n", player->dolcost);
154     else if (player->dolcost < -100.0)
155         pr("You just made $%.2f\n", -player->dolcost);
156     if (player->dolcost != 0.0) {
157         /*
158          * Hackish work around for a race condition in the nightly
159          * build's regression tests: sometimes the update starts right
160          * after the force command yields, sometimes a bit later.  If
161          * it is late, we use one random number here, for the bye,
162          * and throwing off the random sequence.
163          */
164         natp->nat_money -= roundavg(player->dolcost);
165         player->dolcost = 0.0;
166     }
167
168     old_nstat = player->nstat;
169     player_set_nstat(player, natp);
170     if ((old_nstat & MONEY) && !(player->nstat & MONEY))
171         pr("You are now broke; industries are on strike.\n");
172     if (!(old_nstat & MONEY) && (player->nstat & MONEY))
173         pr("You are no longer broke!\n");
174
175     time(&player->curup);
176     update_timeused(player->curup);
177     if (!may_play_now(natp, player->curup))
178         return 0;
179     if (player->btused) {
180         natp->nat_btu -= player->btused;
181         player->btused = 0;
182     }
183     if (natp->nat_tgms > 0) {
184         if (!(natp->nat_flags & NF_INFORM)) {
185             if (natp->nat_tgms == 1)
186                 pr("You have a new telegram waiting ...\n");
187             else
188                 pr("You have %s new telegrams waiting ...\n",
189                    numstr(buf, natp->nat_tgms));
190             natp->nat_tgms = 0;
191         }
192     }
193     if (natp->nat_ann > 0) {
194         if (natp->nat_ann == 1)
195             pr("You have a new announcement waiting ...\n");
196         else
197             pr("You have %s new announcements waiting ...\n",
198                numstr(buf, natp->nat_ann));
199         natp->nat_ann = 0;
200     }
201     if (natp->nat_stat == STAT_ACTIVE && (player->nstat & CAP) == 0)
202         pr("You lost your capital... better designate one (see info capital)\n");
203     putnat(natp);
204     return 1;
205 }
206
207 /* Is ARG one of the player's last command's arguments?  */
208 static int
209 is_command_arg(char *arg)
210 {
211     int i;
212
213     for (i = 1; i < 128 && player->argp[i]; i++) {
214         if (arg == player->argp[i])
215             return 1;
216     }
217     return 0;
218 }
219
220 /*
221  * Make all objects stale if ARG is one of the player's command arguments.
222  * See ef_make_stale() for what "making stale" means.
223  * Useful for functions that prompt for missing arguments.
224  * These can yield the processor, so we'd like to call ef_make_stale()
225  * there.  Except that leads to false positives when the caller passes
226  * an argument that is never null, and relies on the fact that the
227  * function doesn't yield then.  We can't know that in general.  But
228  * we do know in the common special case of command arguments.
229  */
230 void
231 make_stale_if_command_arg(char *arg)
232 {
233     if (is_command_arg(arg))
234         ef_make_stale();
235 }
236
237 /*
238  * XXX This whole mess should be redone; execute block should
239  * start with "exec start", and should end with "exec end".
240  * We'll wait until 1.2 I guess.
241  */
242 int
243 execute(void)
244 {
245     char buf[1024];
246     int failed;
247     char *p;
248     char *redir;                /* UTF-8 */
249     char scanspace[1024];
250
251     failed = 0;
252
253     p = getstarg(player->comtail[1], "File? ", buf);
254     if (p == NULL || *p == '\0')
255         return RET_SYN;
256     prexec(p);
257
258     while (!failed && status()) {
259         player->nstat &= ~EXEC;
260         if (getcommand(buf) < 0)
261             break;
262         if (parse(buf, scanspace, player->argp, player->comtail,
263                   &player->condarg, &redir) < 0) {
264             failed = 1;
265             continue;
266         }
267         pr("\nExecute : ");
268         uprnf(buf);
269         pr("\n");
270         if (redir) {
271             pr("Execute : redirection not supported\n");
272             failed = 1;
273         } else if (dispatch(buf, NULL) < 0)
274             failed = 1;
275     }
276     if (failed) {
277         while (recvclient(buf, sizeof(buf)) >= 0) ;
278     }
279
280     pr("Execute : %s\n", failed ? "aborted" : "terminated");
281     player->eof = 0;
282     return RET_OK;
283 }
284
285 int
286 show_motd(void)
287 {
288     show_first_tel(motdfil);
289     return RET_OK;
290 }
291
292 int
293 quit(void)
294 {
295     player->state = PS_SHUTDOWN;
296     return RET_OK;
297 }
298
299 char *
300 praddr(struct player *p)
301 {
302     return prbuf("%s@%s", p->userid,
303                  *p->hostname ? p->hostname : p->hostaddr);
304 }