home · contact · privacy
Server: Expect (but don't force) some player commands as sans argument.
[plomrogue] / src / server / run.c
1 /* src/server/run.c */
2
3 #define _POSIX_C_SOURCE 200809L
4 #include "run.h"
5 #include <stddef.h> /* NULL */
6 #include <stdint.h> /* uint8_t, uint16_t, uint32_t */
7 #include <stdio.h> /* FILE, printf(), fflush() */
8 #include <stdlib.h> /* free(), atoi() */
9 #include <string.h> /* strlen(), strcmp() strncmp(), strdup() */
10 #include <unistd.h> /* access() */
11 #include "../common/parse_file.h" /* set_err_line_options(), token_from_line(),
12                                    * err_line()
13                                    */
14 #include "../common/readwrite.h" /* try_fopen(), try_fcose(), try_fwrite(),
15                                   * try_fgets(), textfile_width(), try_fputc()
16                                   */
17 #include "../common/rexit.h" /* exit_trouble(), exit_err() */
18 #include "../common/try_malloc.h" /* try_malloc() */
19 #include "ai.h" /* ai() */
20 #include "cleanup.h" /* set_cleanup_flag(), unset_cleanup_flag() */
21 #include "field_of_view.h" /* build_fov_map() */
22 #include "hardcoded_strings.h" /* s */
23 #include "init.h" /* remake_world() */
24 #include "io.h" /* io_round(), save_world() */
25 #include "map.h" /* remake_map() */
26 #include "thing_actions.h" /* ThingAction */
27 #include "things.h" /* Thing, get_thing(), own_thing(), add_thing(),
28                      * get_thing_action_id_by_name(), get_player()
29                      */
30 #include "world.h" /* world */
31
32
33
34 /* Parse/apply god command in "tok0"/"tok1" on "t" owning another thing. */
35 static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t);
36
37 /* Parse/apply god commansd in "tok0"/"tok1" on positioning a thing "t". */
38 static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t);
39
40 /* Parse/apply god command in "tok0"/"tok1" oo setting "t"'s thing type. */
41 static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t);
42
43 /* Parse/apply god command in "tok0"/"tok1" on setting up thing "t". */
44 static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t);
45
46 /* Parse/apply god command on enabling/disabling generation of fields of view on
47  * god commands that may affect them, via static global "do_fov". On enabling,
48  * (re-)generate all animate things' fields of view.
49  */
50 static uint8_t parse_do_fov(char * tok0, char * tok1);
51
52 /* Parse/apply god command in "tok0"/"tok1" manipulating a thing's state. */
53 static uint8_t parse_thing_manipulation(char * tok0, char * tok1);
54
55 /* Parse player command "tok0" with one arguments "tok1" to player action. */
56 static uint8_t parse_player_command_0arg(char * tok0, char * tok1);
57
58 /* Parse player command "tok0" with no argument to player action, comment on
59  * invalidity of non-zero "tok1" (but do not abort in that case).
60  */
61 static uint8_t parse_player_command_1arg(char * tok0, char * tok1);
62
63 /* Parse/apply commadn "tok0" with argument "tok1" and test the line for further
64  * tokens, commenting on their invalidity (but don't abort on findingthem).
65  */
66 static uint8_t parse_command_1arg(char * tok0, char * tok1);
67
68
69 /* Compares first line of server out file to world.server_test, aborts if they
70  * don't match, but not before unsetting the flags deleting files in the server
71  * directory, for in that case those must be assumed to belong to another server
72  * process.
73  */
74 static void server_test();
75
76 /* Run the game world and its inhabitants (and their actions) until the player
77  * avatar is free to receive new commands (or is dead).
78  */
79 static void turn_over();
80
81
82
83 /* Do god commands to create / position things generate their fields of view? */
84 static uint8_t do_fov = 0;
85
86
87
88 static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t)
89 {
90     uint8_t id;
91     if (parse_val(tok0, tok1, s[S_CMD_CARRIES], '8', (char *) &id))
92     {
93         if (!err_line(id == t->id, "Thing cannot carry itself."))
94         {
95             struct Thing * o = get_thing(world.things, id, 0);
96             if (!err_line(!o, "Thing cannot carry thing that does not exist."))
97             {
98                 own_thing(&(t->owns), &world.things, id);
99                 o->pos = t->pos;
100             }
101         }
102         return 1;
103     }
104     return 0;
105 }
106
107
108
109 static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t)
110 {
111     char axis = 0;
112     if      (!strcmp(tok0, s[S_CMD_POS_Y]))
113     {
114         axis = 'y';
115     }
116     else if (!strcmp(tok0, s[S_CMD_POS_X]))
117     {
118         axis = 'x';
119     }
120     if (axis && !parsetest_int(tok1, '8'))
121     {
122         uint8_t length = atoi(tok1);
123         char * err = "Position is outside of map.";
124         if (!err_line(length >= world.map.length, err))
125         {
126             if      ('y' == axis)
127             {
128                 t->pos.y = length;
129             }
130             else if ('x' == axis)
131             {
132                 t->pos.x = length;
133             }
134             free(t->fov_map);
135             t->fov_map= do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map;
136         }
137         return 1;
138     }
139     return 0;
140 }
141
142
143
144 static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t)
145 {
146     uint8_t type;
147     if (parse_val(tok0, tok1, s[S_CMD_TYPE], '8', (char *) &type))
148     {
149         struct ThingType * tt = world.thing_types;
150         for (; NULL != tt && type != tt->id; tt = tt->next);
151         if (!err_line(!tt, "Thing type does not exist."))
152         {
153             t->type = type;
154         }
155         return 1;
156     }
157     return 0;
158 }
159
160
161
162 static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t)
163 {
164     uint8_t command;
165     if (parse_val(tok0, tok1, s[S_CMD_COMMAND], '8', (char *) &command))
166     {
167         if (!command)
168         {
169             t->command = command;
170             return 1;
171         }
172         struct ThingAction * ta = world.thing_actions;
173         for (; NULL != ta && command != ta->id; ta = ta->next);
174         if (!err_line(!ta, "Thing action does not exist."))
175         {
176             t->command = command;
177         }
178         return 1;
179     }
180     return 0;
181 }
182
183
184
185 static uint8_t parse_do_fov(char * tok0, char * tok1)
186 {
187     if (parse_val(tok0, tok1, s[S_CMD_DO_FOV], '8', (char *) &do_fov))
188     {
189         if (do_fov)
190         {
191             struct Thing * ti;
192             for (ti = world.things; ti; ti = ti->next)
193             {
194                 ti->fov_map = ti->lifepoints ? build_fov_map(ti) : ti->fov_map;
195             }
196         }
197         return 1;
198     }
199     return 0;
200 }
201
202
203
204 static uint8_t parse_thing_manipulation(char * tok0, char * tok1)
205 {
206     uint8_t id;
207     static struct Thing * t = NULL;
208     if (t && (   parse_thing_type(tok0, tok1, t)
209               || parse_thing_command(tok0, tok1, t)
210               || parse_val(tok0, tok1, s[S_CMD_ARGUMENT], '8', (char *)&t->arg)
211               || parse_val(tok0,tok1,s[S_CMD_PROGRESS],'8',(char *)&t->progress)
212               || parse_val(tok0, tok1, s[S_CMD_LIFEPOINTS], '8',
213                                                         (char *) &t->lifepoints)
214               || parse_position(tok0, tok1, t)
215               || parse_carry(tok0, tok1, t)));
216     else if (parse_val(tok0, tok1, s[S_CMD_THING], '8', (char *) &id))
217     {
218         t = get_thing(world.things, id, 1);
219         if (!t)
220         {
221             t = add_thing(id, 0, 0);
222             set_cleanup_flag(CLEANUP_THINGS);
223             t->fov_map= do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map;
224         }
225     }
226     else
227     {
228         return 0;
229     }
230     return 1;
231 }
232
233
234
235 static uint8_t parse_player_command_0arg(char * tok0, char * tok1)
236 {
237     struct Thing * player = get_player();
238     if (!strcmp(tok0, s[S_CMD_WAIT]) || !strcmp(tok0, s[S_CMD_PICKUP]))
239     {
240         player->command = get_thing_action_id_by_name(tok0);
241         player->arg = 0;
242         turn_over();
243         err_line (NULL != tok1, "No arguments expected, ignoring arguments.");
244         return 1;
245     }
246     return 0;
247 }
248
249
250
251 static uint8_t parse_player_command_1arg(char * tok0, char * tok1)
252 {
253     struct Thing * player = get_player();
254     if (   parse_val(tok0, tok1, s[S_CMD_MOVE], '8', (char *) &player->arg)
255         || parse_val(tok0, tok1, s[S_CMD_DROP], '8', (char *) &player->arg)
256         || parse_val(tok0, tok1, s[S_CMD_USE], '8', (char *) &player->arg))
257     {
258         player->command = get_thing_action_id_by_name(tok0);
259         turn_over();
260     }
261     else
262     {
263         return 0;
264     }
265     return 1;
266 }
267
268
269
270 static uint8_t parse_command_1arg(char * tok0, char * tok1)
271 {
272     char * tok2 = token_from_line(NULL);
273     if (   parse_thing_manipulation(tok0, tok1)
274         || parse_player_command_1arg(tok0, tok1)
275         || parse_val(tok0, tok1, s[S_CMD_SEED_RAND], 'U', (char *) &world.seed)
276         || parse_val(tok0, tok1, s[S_CMD_TURN], 'u', (char *) &world.turn)
277         || parse_do_fov(tok0, tok1));
278     else if (parse_val(tok0,tok1,s[S_CMD_SEED_MAP],'U',(char *)&world.seed_map))
279
280     {
281         remake_map();
282     }
283     else if (parse_val(tok0, tok1, s[S_CMD_MAKE_WORLD],'U',(char *)&world.seed))
284     {
285         remake_world();
286     }
287     else
288     {
289         return 0;
290     }
291     char * err = "But one argument expected, ignoring further arguments.";
292     err_line (NULL != tok2, err);
293     return 1;
294 }
295
296
297
298 static void server_test()
299 {
300     char * f_name = "server_test()";
301     char test[10 + 1 + 10 + 1 + 1];
302     FILE * file = try_fopen(s[S_PATH_OUT], "r", f_name);
303     try_fgets(test, 10 + 10 + 1 + 1, file, f_name);
304     try_fclose(file, f_name);
305     if (strcmp(test, world.server_test))
306     {
307         unset_cleanup_flag(CLEANUP_WORLDSTATE);
308         unset_cleanup_flag(CLEANUP_OUT);
309         unset_cleanup_flag(CLEANUP_IN);
310         char * msg = "Server test string in server output file does not match. "
311                      "This indicates that the current server process has been "
312                      "superseded by another one.";
313         exit_err(1, msg);
314     }
315 }
316
317
318
319 static void turn_over()
320 {
321     struct Thing * player = get_player();
322     struct Thing * thing = player;
323     uint16_t start_turn = world.turn;
324     while (    0 < player->lifepoints
325            || (0 == player->lifepoints && start_turn == world.turn))
326     {
327         if (NULL == thing)
328         {
329             world.turn++;
330             thing = world.things;
331         }
332         if (0 < thing->lifepoints)
333         {
334             if (0 == thing->command)
335             {
336                 if (thing == player)
337                 {
338                     break;
339                 }
340                 ai(thing);
341             }
342             thing->progress++;
343             struct ThingAction * ta = world.thing_actions;
344             while (ta->id != thing->command)
345             {
346                 ta = ta->next;
347             }
348             if (thing->progress == ta->effort)
349             {
350                 ta->func(thing);
351                 thing->command = 0;
352                 thing->progress = 0;
353             }
354         }
355         thing = thing->next;
356     }
357 }
358
359
360
361 static void record_msg(char * msg)
362 {
363     char * f_name = "record_msg()";
364     char * path_tmp;
365     FILE * file_tmp = atomic_write_start(s[S_PATH_RECORD], &path_tmp);
366     if (!access(s[S_PATH_RECORD], F_OK))
367     {
368         FILE * file_read = try_fopen(s[S_PATH_RECORD], "r", f_name);
369         uint32_t linemax = textfile_width(file_read);
370         char * line = try_malloc(linemax + 1, f_name);
371         while (try_fgets(line, linemax + 1, file_read, f_name))
372         {
373             try_fwrite(line, strlen(line), 1, file_tmp, f_name);
374         }
375         free(line);
376         try_fclose(file_read, f_name);
377     }
378     try_fwrite(msg, strlen(msg), 1, file_tmp, f_name);
379     try_fputc('\n', file_tmp, f_name);
380     atomic_write_finish(file_tmp, s[S_PATH_RECORD], path_tmp);
381 }
382
383
384
385 extern void obey_msg(char * msg, uint8_t do_record)
386 {
387     set_err_line_options("Trouble with message: ", msg, 0, 0);
388     char * msg_copy = strdup(msg);
389     char * tok0 = token_from_line(msg_copy);
390     if (NULL != tok0)
391     {
392         char * tok1 = token_from_line(NULL);
393         if (    parse_player_command_0arg(tok0, tok1)
394             || (tok1 && parse_command_1arg(tok0, tok1)))
395         {
396             world.do_update = 1;
397             if (do_record)
398             {
399                 save_world();
400                 record_msg(msg);
401             }
402             free(msg_copy);
403             return;
404         }
405     }
406     err_line(1, "Unknown command or bad number of tokens.");
407     free(msg_copy);
408 }
409
410
411
412 extern uint8_t io_loop()
413 {
414     char * f_name = "io_loop()";
415     while (1)
416     {
417         server_test();
418         char * msg = io_round();
419         if (NULL == msg)
420         {
421             continue;
422         }
423         if (world.is_verbose)
424         {
425             exit_trouble(-1 == printf("Input: %s\n", msg), f_name, "printf()");
426         }
427         if (!strcmp("QUIT", msg))
428         {
429             free(msg);
430             return 1;
431         }
432         if (!strcmp("PING", msg))
433         {
434             free(msg);
435             char * pong = "PONG\n";
436             try_fwrite(pong, strlen(pong), 1, world.file_out, f_name);
437             fflush(world.file_out);
438             continue;
439         }
440         if (world.replay)
441         {
442             free(msg);
443             return 0;
444         }
445         obey_msg(msg, 1);
446         free(msg);
447     }
448 }