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