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