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