3 #define _POSIX_C_SOURCE 200809L
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(),
14 #include "../common/readwrite.h" /* try_fopen(), try_fcose(), try_fwrite(),
15 * try_fgets(), textfile_width(), try_fputc()
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()
30 #include "world.h" /* world */
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);
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);
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);
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);
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.
50 static uint8_t parse_do_fov(char * tok0, char * tok1);
52 /* Parse/apply god command in "tok0"/"tok1" manipulating a thing's state. */
53 static uint8_t parse_thing_manipulation(char * tok0, char * tok1);
55 /* Parse player command "tok0" with no argument to player action, comment on
56 * invalidity of non-zero "tok1" (but do not abort in that case).
58 static uint8_t parse_player_command_0arg(char * tok0, char * tok1);
60 /* If "string" and "comparand" match in string, set "c_to_set" to value." */
61 static uint8_t set_char_by_string_comparison(char * string, char * comparand,
62 char * c_to_set, char value);
64 /* Parse player command "tok0" with one argument "tok1" to player action. */
65 static uint8_t parse_player_command_1arg(char * tok0, char * tok1);
67 /* Parse/apply commadn "tok0" with argument "tok1" and test the line for further
68 * tokens, commenting on their invalidity (but don't abort on findingthem).
70 static uint8_t parse_command_1arg(char * tok0, char * tok1);
73 /* Compares first line of server out file to world.server_test, aborts if they
74 * don't match, but not before unsetting the flags deleting files in the server
75 * directory, for in that case those must be assumed to belong to another server
78 static void server_test();
80 /* Run the game world and its inhabitants (and their actions) until the player
81 * avatar is free to receive new commands (or is dead).
83 static void turn_over();
87 /* Do god commands to create / position things generate their fields of view? */
88 static uint8_t do_fov = 0;
92 static uint8_t parse_carry(char * tok0, char * tok1, struct Thing * t)
95 if (parse_val(tok0, tok1, s[S_CMD_CARRIES], '8', (char *) &id))
97 if (!err_line(id == t->id, "Thing cannot carry itself."))
99 struct Thing * o = get_thing(world.things, id, 0);
100 if (!err_line(!o, "Thing cannot carry thing that does not exist."))
102 own_thing(&(t->owns), &world.things, id);
113 static uint8_t parse_position(char* tok0, char * tok1, struct Thing * t)
116 if (!strcmp(tok0, s[S_CMD_POS_Y]))
120 else if (!strcmp(tok0, s[S_CMD_POS_X]))
124 if (axis && !parsetest_int(tok1, '8'))
126 uint8_t length = atoi(tok1);
127 char * err = "Position is outside of map.";
128 if (!err_line(length >= world.map.length, err))
134 else if ('x' == axis)
139 t->fov_map= do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map;
148 static uint8_t parse_thing_type(char * tok0, char * tok1, struct Thing * t)
151 if (parse_val(tok0, tok1, s[S_CMD_TYPE], '8', (char *) &type))
153 struct ThingType * tt = world.thing_types;
154 for (; NULL != tt && type != tt->id; tt = tt->next);
155 if (!err_line(!tt, "Thing type does not exist."))
166 static uint8_t parse_thing_command(char * tok0, char * tok1, struct Thing * t)
169 if (parse_val(tok0, tok1, s[S_CMD_COMMAND], '8', (char *) &command))
173 t->command = command;
176 struct ThingAction * ta = world.thing_actions;
177 for (; NULL != ta && command != ta->id; ta = ta->next);
178 if (!err_line(!ta, "Thing action does not exist."))
180 t->command = command;
189 static uint8_t parse_do_fov(char * tok0, char * tok1)
191 if (parse_val(tok0, tok1, s[S_CMD_DO_FOV], '8', (char *) &do_fov))
196 for (ti = world.things; ti; ti = ti->next)
198 ti->fov_map = ti->lifepoints ? build_fov_map(ti) : ti->fov_map;
208 static uint8_t parse_thing_manipulation(char * tok0, char * tok1)
211 static struct Thing * t = NULL;
212 if (t && ( parse_thing_type(tok0, tok1, t)
213 || parse_thing_command(tok0, tok1, t)
214 || parse_val(tok0, tok1, s[S_CMD_ARGUMENT], '8', (char *)&t->arg)
215 || parse_val(tok0,tok1,s[S_CMD_PROGRESS],'8',(char *)&t->progress)
216 || parse_val(tok0, tok1, s[S_CMD_LIFEPOINTS], '8',
217 (char *) &t->lifepoints)
218 || parse_position(tok0, tok1, t)
219 || parse_carry(tok0, tok1, t)));
220 else if (parse_val(tok0, tok1, s[S_CMD_THING], '8', (char *) &id))
222 t = get_thing(world.things, id, 1);
225 t = add_thing(id, 0, 0);
226 set_cleanup_flag(CLEANUP_THINGS);
227 t->fov_map= do_fov && t->lifepoints ? build_fov_map(t) : t->fov_map;
239 static uint8_t parse_player_command_0arg(char * tok0, char * tok1)
241 struct Thing * player = get_player();
242 if (!strcmp(tok0, s[S_CMD_WAIT]) || !strcmp(tok0, s[S_CMD_PICKUP]))
244 player->command = get_thing_action_id_by_name(tok0);
247 err_line (NULL != tok1, "No arguments expected, ignoring arguments.");
255 static uint8_t set_char_by_string_comparison(char * string, char * comparand,
256 char * c_to_set, char value)
258 if (!strcmp(string, comparand))
268 static uint8_t parse_player_command_1arg(char * tok0, char * tok1)
270 struct Thing * player = get_player();
272 parse_val(tok0, tok1, s[S_CMD_DROP], '8', (char *) &player->arg)
273 || parse_val(tok0, tok1, s[S_CMD_USE], '8', (char *) &player->arg))
275 player->command = get_thing_action_id_by_name(tok0);
278 else if (!strcmp(tok0, s[S_CMD_MOVE]))
281 if (!( set_char_by_string_comparison(tok1, "east", &dir, 'd')
282 || set_char_by_string_comparison(tok1, "south-east", &dir, 'c')
283 || set_char_by_string_comparison(tok1, "south-west", &dir, 'x')
284 || set_char_by_string_comparison(tok1, "west", &dir, 's')
285 || set_char_by_string_comparison(tok1, "north-west", &dir, 'w')
286 || set_char_by_string_comparison(tok1, "north-east", &dir, 'e')))
291 player->command = get_thing_action_id_by_name(tok0);
303 static uint8_t parse_command_1arg(char * tok0, char * tok1)
305 char * tok2 = token_from_line(NULL);
306 if ( parse_thing_manipulation(tok0, tok1)
307 || parse_player_command_1arg(tok0, tok1)
308 || parse_val(tok0, tok1, s[S_CMD_SEED_RAND], 'U', (char *) &world.seed)
309 || parse_val(tok0, tok1, s[S_CMD_TURN], 'u', (char *) &world.turn)
310 || parse_do_fov(tok0, tok1));
311 else if (parse_val(tok0,tok1,s[S_CMD_SEED_MAP],'U',(char *)&world.seed_map))
316 else if (parse_val(tok0, tok1, s[S_CMD_MAKE_WORLD],'U',(char *)&world.seed))
324 char * err = "But one argument expected, ignoring further arguments.";
325 err_line (NULL != tok2, err);
331 static void server_test()
333 char * f_name = "server_test()";
334 char test[10 + 1 + 10 + 1 + 1];
335 FILE * file = try_fopen(s[S_PATH_OUT], "r", f_name);
336 try_fgets(test, 10 + 10 + 1 + 1, file, f_name);
337 try_fclose(file, f_name);
338 if (strcmp(test, world.server_test))
340 unset_cleanup_flag(CLEANUP_WORLDSTATE);
341 unset_cleanup_flag(CLEANUP_OUT);
342 unset_cleanup_flag(CLEANUP_IN);
343 char * msg = "Server test string in server output file does not match. "
344 "This indicates that the current server process has been "
345 "superseded by another one.";
352 static void turn_over()
354 struct Thing * player = get_player();
355 struct Thing * thing = player;
356 uint16_t start_turn = world.turn;
357 while ( 0 < player->lifepoints
358 || (0 == player->lifepoints && start_turn == world.turn))
363 thing = world.things;
365 if (0 < thing->lifepoints)
367 if (0 == thing->command)
376 struct ThingAction * ta = world.thing_actions;
377 while (ta->id != thing->command)
381 if (thing->progress == ta->effort)
394 static void record_msg(char * msg)
396 char * f_name = "record_msg()";
398 FILE * file_tmp = atomic_write_start(s[S_PATH_RECORD], &path_tmp);
399 if (!access(s[S_PATH_RECORD], F_OK))
401 FILE * file_read = try_fopen(s[S_PATH_RECORD], "r", f_name);
402 uint32_t linemax = textfile_width(file_read);
403 char * line = try_malloc(linemax + 1, f_name);
404 while (try_fgets(line, linemax + 1, file_read, f_name))
406 try_fwrite(line, strlen(line), 1, file_tmp, f_name);
409 try_fclose(file_read, f_name);
411 try_fwrite(msg, strlen(msg), 1, file_tmp, f_name);
412 try_fputc('\n', file_tmp, f_name);
413 atomic_write_finish(file_tmp, s[S_PATH_RECORD], path_tmp);
418 extern void obey_msg(char * msg, uint8_t do_record)
420 set_err_line_options("Trouble with message: ", msg, 0, 0);
421 char * msg_copy = strdup(msg);
422 char * tok0 = token_from_line(msg_copy);
425 char * tok1 = token_from_line(NULL);
426 if ( parse_player_command_0arg(tok0, tok1)
427 || (tok1 && parse_command_1arg(tok0, tok1)))
439 err_line(1, "Unknown command or bad number of tokens.");
445 extern uint8_t io_loop()
447 char * f_name = "io_loop()";
451 char * msg = io_round();
456 if (world.is_verbose)
458 exit_trouble(-1 == printf("Input: %s\n", msg), f_name, "printf()");
460 if (!strcmp("QUIT", msg))
465 if (!strcmp("PING", msg))
468 char * pong = "PONG\n";
469 try_fwrite(pong, strlen(pong), 1, world.file_out, f_name);
470 fflush(world.file_out);