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