- make server and client communicate by specific world state info requests
in server/out, replacing server/worldstate
+- in message reading/queueing, handle case of unfinished nl-delimited messages
+
CLIENT:
- re-work unnecessary complex command / keybinding / server message mapping
free(world.map.cells);
free(world.mem_map);
free(world.log);
+ free(world.queue);
free(world.player_inventory);
if (cleanup_flags & CLEANUP_INTERFACE)
{
#include "../common/try_malloc.h" /* try_malloc() */
#include "../common/rexit.h" /* exit_trouble(), exit_err() */
#include "../common/readwrite.h" /* try_fopen(), try_fclose(), try_fgets(),
- * try_fgetc(), textfile_width(), try_fputc()
+ * try_fgetc(), textfile_width(), try_fputc(),
+ * read_file_into_queue(),
+ * get_message_from_queue(),
*/
#include "control.h" /* try_key() */
#include "map.h" /* map_center() */
*/
static void read_map_cells(FILE * file, char ** map);
-/* Repeatedly use try_fgets() with given arguments to read the remaining lines
- * of "file" into the world.log string.
- */
-static void read_log(char * read_buf, uint32_t linemax, FILE * file);
-
/* Return value seen by atoi() in next line of "file" when passed to try_fgets()
* with the given arguments.
*/
*/
static void ping_pong_test(time_t last_server_answer_time);
-/* Update "last_server_answer_time" if new stuff has been written to the
- * server's out file.
+/* Read server's out file into queue, update "last_server_answer_time" if new
+ * stuff is found there.
*/
-static void server_activity_test(time_t * last_server_answer_time);
+static void try_growing_queue(time_t * last_server_answer_time);
+
+/* Read server out file for messages, act on them (i.e. derive log messages). */
+static uint8_t read_outfile();
-static void read_log(char * read_buf, uint32_t linemax, FILE * file)
-{
- free(world.log);
- world.log = NULL;
- while (try_fgets(read_buf, linemax + 1, file, __func__))
- {
- int old_size = 0;
- if (world.log)
- {
- old_size = strlen(world.log);
- }
- int new_size = strlen(read_buf);
- char * new_log = try_malloc(old_size + new_size + 1, __func__);
- memcpy(new_log, world.log, old_size);
- int test = sprintf(new_log + old_size, "%s", read_buf);
- exit_trouble(test < 0, __func__, "sprintf");
- free(world.log);
- world.log = new_log;
- }
-}
-
-
-
static uint16_t read_value_from_line(char * read_buf, uint32_t linemax,
FILE * file)
{
world.map.length = read_value_from_line(read_buf, linemax, file);
read_map_cells(file, &world.map.cells);
read_map_cells(file, &world.mem_map);
- read_log(read_buf, linemax, file);
free(read_buf);
try_fclose(file, __func__);
return 1;
-static void server_activity_test(time_t * last_server_answer_time)
+static void try_growing_queue(time_t * last_server_answer_time)
{
- int test = try_fgetc(world.file_server_out, __func__);
- if (EOF == test)
+ if (read_file_into_queue(world.file_server_out, &world.queue,
+ &world.queue_size))
{
- return;
+ * last_server_answer_time = time(0);
}
- do
+}
+
+
+
+static uint8_t read_outfile()
+{
+ uint8_t ret = 0;
+ char * msg;
+ while (NULL != (msg=get_message_from_queue(&world.queue,&world.queue_size)))
{
- ;
+ char * log_prefix = "LOG ";
+ if (!strncmp(msg, log_prefix, strlen(log_prefix)))
+ {
+ ret = 1;
+ char * log_msg = msg + strlen(log_prefix);
+ int old_size = 0;
+ if (world.log)
+ {
+ old_size = strlen(world.log);
+ }
+ int new_size = strlen(log_msg);
+ char * new_log = try_malloc(old_size + 1 + new_size + 1, __func__);
+ memcpy(new_log, world.log, old_size);
+ int test = sprintf(new_log + old_size, "\n%s", log_msg);
+ exit_trouble(test < 0, __func__, "sprintf");
+ free(world.log);
+ world.log = new_log;
+ }
+ free(msg);
}
- while (EOF != (test = try_fgetc(world.file_server_out, __func__)));
- * last_server_answer_time = time(0);
+ return ret;
}
time_t last_server_answer_time = time(0);
while (1)
{
- server_activity_test(&last_server_answer_time);
+ try_growing_queue(&last_server_answer_time);
ping_pong_test(last_server_answer_time);
if (world.winch)
{
world.winch = 0;
change_in_client++;
}
- if (change_in_client || read_worldstate())
+ if (change_in_client || read_worldstate() || read_outfile())
{
if (world.turn != last_focused_turn && world.focus_each_turn)
{
char * path_commands; /* path of commands config file */
char * player_inventory; /* one-item-per-line string list of owned items */
char * mem_map; /* map cells of player's map memory */
+ char * queue; /* Stores un-processed messages read from the input file. */
+ uint32_t queue_size;/* Length of .queue sequence of \0-terminated strings.*/
struct yx_uint8 player_pos; /* coordinates of player on map */
uint16_t turn; /* world/game turn */
uint8_t halfdelay; /* how long to wait for getch() input in io_loop() */
*/
#include "readwrite.h"
-#include <stddef.h> /* size_t */
+#include <stddef.h> /* NULL, size_t */
#include <stdint.h> /* uint8_t, uint16_t, uint32_t, UINT32_MAX */
#include <stdio.h> /* FILE, fseek(), sprintf(), fgets(), fgetc(), ferror(),
* fputc(), fwrite(), fclose(), fopen(), clearerr()
*/
#include <stdlib.h> /* free() */
-#include <string.h> /* strlen() */
+#include <string.h> /* strlen(), memcpy() */
#include <unistd.h> /* access(), unlink() */
#include "rexit.h" /* exit_err(), exit_trouble() */
#include "try_malloc.h" /* try_malloc() */
exit_trouble(-1 == fseek(file, 0, SEEK_SET), __func__, "fseek");
return linemax;
}
+
+
+
+extern uint8_t read_file_into_queue(FILE * file, char ** queue,
+ uint32_t * queue_size)
+{
+ int test = try_fgetc(file, __func__);
+ if (EOF != test)
+ {
+ do
+ {
+ char c = (char) test;
+ if ('\n' == c)
+ {
+ c = '\0';
+ }
+ char * new_queue = try_malloc(*queue_size + 1, __func__);
+ memcpy(new_queue, *queue, *queue_size);
+ char * new_pos = new_queue + *queue_size;
+ * new_pos = c;
+ *queue_size = *queue_size + 1;
+ free(*queue);
+ *queue = new_queue;
+ }
+ while (EOF != (test = try_fgetc(file, __func__)));
+ if (*queue_size && '\0' != (*queue)[*queue_size - 1])
+ {
+ char * new_queue = try_malloc(*queue_size + 1, __func__);
+ memcpy(new_queue, *queue, *queue_size);
+ new_queue[*queue_size] = '\0';
+ *queue_size = *queue_size + 1;
+ free(*queue);
+ *queue = new_queue;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+
+
+extern char * get_message_from_queue(char ** queue, uint32_t * queue_size)
+{
+ char * message = NULL;
+ if (*queue_size)
+ {
+ size_t cutout_len = strlen(*queue);
+ uint8_t is_nullbyte_chunk = !cutout_len;
+ if (0 < cutout_len)
+ {
+ cutout_len++;
+ message = try_malloc(cutout_len, __func__);
+ memcpy(message, *queue, cutout_len);
+ }
+ for (;
+ cutout_len != *queue_size && '\0' == (*queue)[cutout_len];
+ cutout_len++);
+ *queue_size = *queue_size - cutout_len;
+ if (0 == *queue_size)
+ {
+ free(*queue); /* NULL so read_file_into_queue() and */
+ *queue = NULL; /* cleanup() may free() this every time, */
+ } /* even when it's un-allocated. */
+ else
+ {
+ char * new_queue = try_malloc(*queue_size, __func__);
+ memcpy(new_queue, &((*queue)[cutout_len]), *queue_size);
+ free(*queue);
+ *queue = new_queue;
+ if (is_nullbyte_chunk)
+ {
+ return get_message_from_queue(queue, queue_size);
+ }
+ }
+ }
+ return message;
+}
/* Return largest line length from "file" (including newline chars). */
extern uint32_t textfile_width(FILE * file);
+/* Read "file" for load of bytes to put onto "queue" of "queue_size" (growing
+ * that size). May put many \0-terminated strings on the queue at once. \n chars
+ * are replaced with \0 chars. If the queue doesn't end in \0, a \o byte is
+ * appended to it. Returns 1 if reading succeeds, 0 if nothing is read.
+ */
+extern uint8_t read_file_into_queue(FILE * file, char ** queue,
+ uint32_t * queue_size);
+
+/* Cut out and return first \0-terminated string from "queue" and appropriately
+ * reduce "queue_size". Return NULL if queue is empty. Superfluous \0 bytes
+ * after the string are also cut out. Should the queue start with \0 bytes,
+ * those are cut out before returning anything after them.
+ */
+extern char * get_message_from_queue(char ** queue, uint32_t * queue_size);
+
#endif
extern void cleanup()
{
free(world.queue);
- free(world.log);
free(world.map.cells);
if (cleanup_flags & CLEANUP_WORLDSTATE)
{
{
return test;
}
- free(world.log);
- world.log = NULL; /* thing_actions.c's update_log() checks for this. */
world.seed_map = world.seed;
free_things(world.things);
remake_map();
#include "io.h"
#include <errno.h> /* global errno */
#include <limits.h> /* PIPE_BUF */
-#include <stddef.h> /* size_t, NULL */
+#include <stddef.h> /* NULL */
#include <stdint.h> /* uint8_t, uint16_t, uint32_t, UINT8_MAX */
-#include <stdio.h> /* defines EOF, FILE, sprintf(), fprintf() */
+#include <stdio.h> /* defines FILE, sprintf(), fprintf() */
#include <stdlib.h> /* free() */
#include <string.h> /* strlen(), snprintf(), memcpy(), memset(), strchr() */
#include <sys/types.h> /* time_t */
#include <time.h> /* time(), nanosleep() */
#include "../common/readwrite.h" /* atomic_write_start(), atomic_write_finish(),
- * try_fwrite(), try_fputc(), try_fgetc()
+ * get_message_from_queue(), try_fwrite(),
+ * read_file_into_queue(), try_fputc()
*/
#include "../common/rexit.h" /* exit_trouble() */
#include "../common/try_malloc.h" /* try_malloc() */
/* Write to "file" \n-delimited line of "key" + space + "value" as string. */
static void write_thing(FILE * file, struct Thing * t);
-/* Cut out and return first \0-terminated string from world.queue and
- * appropriately reduce world.queue_size. Return NULL if queue is empty.
- * Superfluous \0 bytes after the string are also cut out. Should the queue
- * start with \0 bytes, those are cut out before returning anything after them.
- */
-static char * get_message_from_queue();
-
/* Poll input file for world.queue input. Wait a few seconds until giving up;
* poll only every 0.03 seconds. Translate '\n' chars in input file into '\0'.
*/
-static void read_file_into_queue();
+static void try_growing_queue();
/* Write world state as visible to clients to its file. Write single dot line to
* server output file to satisfy client ping mechanisms.
-static char * get_message_from_queue()
-{
- char * message = NULL;
- if (world.queue_size)
- {
- size_t cutout_len = strlen(world.queue);
- uint8_t is_nullbyte_chunk = !cutout_len;
- if (0 < cutout_len)
- {
- cutout_len++;
- message = try_malloc(cutout_len, __func__);
- memcpy(message, world.queue, cutout_len);
- }
- for (;
- cutout_len != world.queue_size && '\0' == world.queue[cutout_len];
- cutout_len++);
- world.queue_size = world.queue_size - cutout_len;
- if (0 == world.queue_size)
- {
- free(world.queue); /* NULL so read_file_into_queue() may free() */
- world.queue = NULL; /* this every time, even when it's */
- } /* un-allocated first. */
- else
- {
- char * new_queue = try_malloc(world.queue_size, __func__);
- memcpy(new_queue, &(world.queue[cutout_len]), world.queue_size);
- free(world.queue);
- world.queue = new_queue;
- if (is_nullbyte_chunk)
- {
- return get_message_from_queue();
- }
- }
- }
- return message;
-}
-
-
-
-static void read_file_into_queue()
+static void try_growing_queue()
{
uint8_t wait_seconds = 5;
time_t now = time(0);
struct timespec dur;
dur.tv_sec = 0;
dur.tv_nsec = 33333333;
- int test;
- while (EOF == (test = try_fgetc(world.file_in, __func__)))
+ while (1)
{
- nanosleep(&dur, NULL);
- if (time(0) > now + wait_seconds)
+ if (read_file_into_queue(world.file_in, &world.queue,&world.queue_size))
{
return;
}
- }
- do
- {
- char c = (char) test;
- if ('\n' == c)
+ nanosleep(&dur, NULL);
+ if (time(0) > now + wait_seconds)
{
- c = '\0';
+ return;
}
- char * new_queue = try_malloc(world.queue_size + 1, __func__);
- memcpy(new_queue, world.queue, world.queue_size);
- char * new_pos = new_queue + world.queue_size;
- * new_pos = c;
- world.queue_size++;
- free(world.queue);
- world.queue = new_queue;
}
- while (EOF != (test = try_fgetc(world.file_in, __func__)));
}
write_value_as_line(player->pos.x, file);
write_value_as_line(world.map.length, file);
write_map(player, file);
- if (world.log)
- {
- try_fwrite(world.log, strlen(world.log), 1, file, __func__);
- }
atomic_write_finish(file, s[S_PATH_WORLDSTATE], path_tmp);
set_cleanup_flag(CLEANUP_WORLDSTATE);
char * dot = ".\n";
{
if (0 < world.queue_size)
{
- return get_message_from_queue();
+ return get_message_from_queue(&world.queue, &world.queue_size);
}
if (world.do_update)
{
update_worldstate_file();
world.do_update = 0;
}
- read_file_into_queue();
- if (world.queue_size && '\0' != world.queue[world.queue_size - 1])
- {
- char * new_queue = try_malloc(world.queue_size + 1, __func__);
- memcpy(new_queue, world.queue, world.queue_size);
- new_queue[world.queue_size] = '\0';
- world.queue_size++;
- free(world.queue);
- world.queue = new_queue;
- }
- return get_message_from_queue();
+ try_growing_queue();
+ return get_message_from_queue(&world.queue, &world.queue_size);
}
* if queue is empty and world.do_update is set, update world state file (and
* unset world.do_update) and write a single dot line to server out file, then
* read server in file for the next load of bytes to put onto the input queue.
- *
- * Reading the server in file may put many \0-terminated strings on the queue at
- * once. Successive calls of io_round() will make these available one by one.
- * Each such call cuts off bytes from the beginning of world.queue, up to and
- * including the last \0 byte that is followed by a non-\0 byte or ends the
- * queue. If the queue starts with a \0 byte, it and its \0 followers are cut
- * before returning anything after them. Reading from the input file stops only
- * at its end or when one or more byte were read and the next read returns 0
- * bytes. If the re-filled queue doesn't end in \0, a \0 byte is appended to it.
*/
extern char * io_round();
*/
static void turn_over();
-/* Append "answer" to server output file, with instant fflush(). */
-static void answer_query(char * answer);
-
/* Try to read "msg" as meta command, act accordingly; on success, free it. */
static uint8_t meta_commands(char * msg);
-static void answer_query(char * answer)
-{
- try_fwrite(answer, strlen(answer), 1, world.file_out, __func__);
- fflush(world.file_out);
-}
-
-
-
static uint8_t meta_commands(char * msg)
{
if (!strcmp("QUIT", msg))
if (!strcmp("PING", msg))
{
free(msg);
- answer_query("PONG\n");
+ send_to_outfile("PONG\n");
return 1;
}
if (!strcmp("STACK", msg))
{
free(msg);
- answer_query("THINGS_BELOW_PLAYER START\n");
+ send_to_outfile("THINGS_BELOW_PLAYER START\n");
struct Thing * player = get_player();
struct Thing * t;
for (t = world.things; t; t = t->next)
&& t != player)
{
struct ThingType * tt = get_thing_type(t->type);
- answer_query(tt->name);
- answer_query("\n");
+ send_to_outfile(tt->name);
+ send_to_outfile("\n");
}
}
- answer_query("THINGS_BELOW_PLAYER END\n");
+ send_to_outfile("THINGS_BELOW_PLAYER END\n");
return 1;
}
return 0;
+extern void send_to_outfile(char * answer)
+{
+ try_fwrite(answer, strlen(answer), 1, world.file_out, __func__);
+ fflush(world.file_out);
+}
+
+
+
extern void record(char * msg, uint8_t force)
{
static FILE * file_tmp = NULL;
+/* Append "answer" to server output file, with instant fflush(). */
+extern void send_to_outfile(char * answer);
+
/* Record save and record file data. Both are only written if "force" is set, or
* on the first run with unset "force", or if 15 seconds have passed since the
* last file writing. "msg" is appended to the record file if it is set.
#include "thing_actions.h"
#include <stddef.h> /* NULL */
-#include <stdint.h> /* uint8_t, uint16_t */
+#include <stdint.h> /* uint8_t */
#include <stdio.h> /* sprintf() */
#include <stdlib.h> /* free() */
-#include <string.h> /* strlen(), memcpy(), strncmp() */
+#include <string.h> /* strlen() */
#include "../common/rexit.h" /* exit_trouble() */
#include "../common/try_malloc.h" /* try_malloc() */
#include "../common/yx_uint8.h" /* yx_uint8 */
#include "field_of_view.h" /* build_fov_map() */
#include "hardcoded_strings.h" /* s */
-#include "things.h" /* Thing, ThingType, get_player(), own_thing(),
- * set_thing_position(), get_thing_type(),
- * free_things_in_memory()
+#include "things.h" /* Thing, ThingType, get_player(), free_things_in_memory(),
+ * own_thing(), set_thing_position(), get_thing_type(),
*/
#include "map.h" /* mv_yx_in_dir_legal() */
+#include "run.h" /* send_to_outfile() */
#include "world.h" /* global world */
-/* How many previous characters of the game log to keep on adding new text */
-#define MAX_BACKLOG_CHARS 3000
-
-
-
-/* If "text" is equal "log"'s last line, return 1, else 0. */
-static uint8_t text_equals_log_end(char * log, char * text);
-
-/* Append "text" to game log shortened to MAX_BACKLOG_CHARS characters, or
- * continuation period if "text" is the same as the (shortened) log's last line
- * minus continuation periods.
- */
+/* Send "text" as log message to server out file. */
static void update_log(char * text);
/* One actor "wounds" another actor, decrementing his lifepoints and, if they
-static uint8_t text_equals_log_end(char * log, char * text)
-{
- uint16_t len_old = strlen(log);
- uint16_t last_nl = len_old - 1;
- while (last_nl != 0)
- {
- if ('\n' == log[last_nl])
- {
- break;
- }
- last_nl--;
- }
- uint16_t last_stop = len_old - 1;
- while (last_stop != 0)
- {
- if ('.' == log[last_stop] && '.' != log[last_stop - 1])
- {
- break;
- }
- last_stop--;
- }
- if ( (last_stop + 1) - last_nl == (uint16_t) strlen(text)
- && 0 == strncmp(log + last_nl, text, strlen(text)))
- {
- return 1;
- }
- return 0;
-}
-
-
-
static void update_log(char * text)
{
- uint16_t len_new = strlen(text);
- uint16_t len_old = 0;
- uint16_t offset = 0;
- if (world.log)
- {
- len_old = strlen(world.log);
- if (len_old > MAX_BACKLOG_CHARS)
- {
- offset = len_old - MAX_BACKLOG_CHARS;
- len_old = MAX_BACKLOG_CHARS;
- }
- if (text_equals_log_end(world.log + offset, text))
- {
- text = ".";
- }
- }
- uint16_t len_whole = len_old + len_new + 1;
- char * new_text = try_malloc(len_whole, __func__);
- memcpy(new_text, world.log + offset, len_old);
- int test = sprintf(new_text + len_old, "%s", text);
- exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
- free(world.log);
- world.log = new_text;
+ send_to_outfile("LOG ");
+ send_to_outfile(text);
+ send_to_outfile("\n");
}
{
msg3 = tt_hitted->name;
}
- uint8_t len = 1 + strlen(msg1) + 1 + strlen(msg2) + 1 + strlen(msg3) + 2;
+ uint8_t len = strlen(msg1) + 1 + strlen(msg2) + 1 + strlen(msg3) + 2;
char * msg = try_malloc(len, __func__);
- int test = sprintf(msg, "\n%s %s %s.", msg1, msg2, msg3);
+ int test = sprintf(msg, "%s %s %s.", msg1, msg2, msg3);
exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
update_log(msg);
free(msg);
hitted->type = tt_hitted->corpse_id;
if (player == hitted)
{
- update_log(" You die.");
+ update_log("You die.");
memset(hitted->fov_map, ' ', world.map.length * world.map.length);
return;
}
free_things_in_memory(hitted->t_mem);
hitted->t_mem = NULL;
}
- update_log(" It dies.");
+ update_log("It dies.");
}
}
static void playerbonus_wait()
{
- update_log("\nYou wait.");
+ update_log("You wait.");
}
{
dsc_move = "You fail to move ";
}
- char * msg = try_malloc(strlen(dsc_move) + strlen (dsc_dir) + 3, __func__);
- int test = sprintf(msg, "\n%s%s.", dsc_move, dsc_dir);
+ char * msg = try_malloc(strlen(dsc_move) + strlen (dsc_dir) + 2, __func__);
+ int test = sprintf(msg, "%s%s.", dsc_move, dsc_dir);
exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
update_log(msg);
free(msg);
{
if (0 != owns_none)
{
- update_log("\nYou try to drop an object, but you own none.");
+ update_log("You try to drop an object, but you own none.");
return;
}
- update_log("\nYou drop an object.");
+ update_log("You drop an object.");
}
{
if (picked)
{
- update_log("\nYou pick up an object.");
+ update_log("You pick up an object.");
return;
}
- update_log("\nYou try to pick up an object, but there is none.");
+ update_log("You try to pick up an object, but there is none.");
}
{
if (no_thing)
{
- update_log("\nYou try to use an object, but you own none.");
+ update_log("You try to use an object, but you own none.");
return;
}
else if (wrong_thing)
{
- update_log("\nYou try to use this object, but fail.");
+ update_log("You try to use this object, but fail.");
return;
}
- update_log("\nYou consume MAGIC MEAT.");
+ update_log("You consume MAGIC MEAT.");
}
struct ThingType * thing_types; /* Thing type definitions. */
struct ThingAction * thing_actions; /* Thing action definitions. */
struct Thing * things; /* All physical things of the game world. */
- char * log; /* Logs the game events from the player's view. */
char * server_test; /* String uniquely identifying server process. */
char * queue; /* Stores un-processed messages read from the input file. */
uint32_t queue_size;/* Length of .queue sequence of \0-terminated strings.*/