home · contact · privacy
952cebb566d317d46bc62df7a1a8a3233d02fd73
[plomrogue] / src / server / run.c
1 /* src/server/run.c */
2
3 #include "run.h"
4 #include <stddef.h> /* NULL */
5 #include <stdint.h> /* uint8_t, uint16_t, uint32_t */
6 #include <stdio.h> /* FILE, sprintf(), fflush() */
7 #include <stdlib.h> /* free() */
8 #include <string.h> /* strlen(), strcmp() strncmp(), atoi() */
9 #include <unistd.h> /* access() */
10 #include "../common/readwrite.h" /* try_fopen(), try_fcose(), try_fwrite(),
11                                   * try_fgets(), try_fclose_unlink_rename(),
12                                   * textfile_width(), try_fputc()
13                                   */
14 #include "../common/rexit.h" /* exit_trouble(), exit_err() */
15 #include "ai.h" /* ai() */
16 #include "cleanup.h" /* unset_cleanup_flag() */
17 #include "init.h" /* remake_world() */
18 #include "io.h" /* io_round() */
19 #include "thing_actions.h" /* get_thing_action_id_by_name() */
20 #include "things.h" /* Thing, get_player() */
21 #include "world.h" /* global world */
22
23
24
25 /* Run the game world and its inhabitants (and their actions) until the player
26  * avatar is free to receive new commands (or is dead).
27  */
28 static void turn_over();
29
30 /* If "msg"'s first part matches "command_name", set player's Thing's .command
31  * to the command's id and its .arg to a numerical value following in the latter
32  * part of "msg" (if no digits are found, use 0); then finish player's turn and
33  * turn game over to the NPCs via turn_over(); then return 1. Else, return 0.
34  */
35 static uint8_t apply_player_command(char * msg, char * command_name);
36
37 /* Compares first line of file at world.path_out to world.server_test, aborts if
38  * they don't match, but not before unsetting the flags deleting files in the
39  * server directory, for in that case those must be assumed to belong to another
40  * server process.
41  */
42 static void server_test();
43
44
45
46 static void turn_over()
47 {
48     struct Thing * player = get_player();
49     struct Thing * thing = player;
50     uint16_t start_turn = world.turn;
51     while (    0 < player->lifepoints
52            || (0 == player->lifepoints && start_turn == world.turn))
53     {
54         if (NULL == thing)
55         {
56             world.turn++;
57             thing = world.things;
58         }
59         if (0 < thing->lifepoints)
60         {
61             if (0 == thing->command)
62             {
63                 if (thing == player)
64                 {
65                     break;
66                 }
67                 ai(thing);
68             }
69             thing->progress++;
70             struct ThingAction * ta = world.thing_actions;
71             while (ta->id != thing->command)
72             {
73                 ta = ta->next;
74             }
75             if (thing->progress == ta->effort)
76             {
77                 ta->func(thing);
78                 thing->command = 0;
79                 thing->progress = 0;
80             }
81         }
82         thing = thing->next;
83     }
84 }
85
86
87
88 static uint8_t apply_player_command(char * msg, char * command_name)
89 {
90     if (!strncmp(msg, command_name, strlen(command_name)))
91     {
92         struct Thing * player = get_player();
93         player->arg = atoi(&(msg[strlen(command_name)]));
94         player->command = get_thing_action_id_by_name(command_name);
95         turn_over();
96         return 1;
97     }
98     return 0;
99 }
100
101
102
103 static void server_test()
104 {
105     char * f_name = "server_test()";
106     char test[10 + 1 + 10 + 1 + 1];
107     FILE * file = try_fopen(world.path_out, "r", f_name);
108     try_fgets(test, 10 + 10 + 1 + 1, file, f_name);
109     try_fclose(file, f_name);
110     if (strcmp(test, world.server_test))
111     {
112         unset_cleanup_flag(CLEANUP_WORLDSTATE);
113         unset_cleanup_flag(CLEANUP_OUT);
114         unset_cleanup_flag(CLEANUP_IN);
115         char * msg = "Server test string in server output file does not match. "
116                      "This indicates that the current server process has been "
117                      "superseded by another one.";
118         exit_err(1, msg);
119     }
120 }
121
122
123
124 extern void obey_msg(char * msg, uint8_t do_record)
125 {
126     char * f_name = "obey_msg()";
127     if (   apply_player_command(msg, "wait")   /* TODO: Check for non-error   */
128         || apply_player_command(msg, "move")   /* return value of a modified  */
129         || apply_player_command(msg, "pick_up")/*get_thing_action_id_by_name()*/
130         || apply_player_command(msg, "drop")   /* and if id found, execute on */
131         || apply_player_command(msg, "use"));  /* it what's in                */
132     else                                       /* apply_player_command().     */
133     {
134         char * seed_command = "seed";
135         if (!strncmp(msg, seed_command, strlen(seed_command)))
136         {
137             remake_world(atoi(&(msg[strlen(seed_command)])));
138         }
139     }
140     if (do_record)
141     {
142         char path_tmp[strlen(world.path_record) + strlen(world.tmp_suffix) + 1];
143         sprintf(path_tmp, "%s%s", world.path_record, world.tmp_suffix);
144         FILE * file_tmp  = try_fopen(path_tmp, "w", f_name);
145         if (!access(world.path_record, F_OK))
146         {
147             FILE * file_read = try_fopen(world.path_record, "r", f_name);
148             uint32_t linemax = textfile_width(file_read);
149             char line[linemax + 1];
150             while (try_fgets(line, linemax + 1, file_read, f_name))
151             {
152                 try_fwrite(line, strlen(line), 1, file_tmp, f_name);
153             }
154             try_fclose(file_read, f_name);
155         }
156         try_fwrite(msg, strlen(msg), 1, file_tmp, f_name);
157         try_fputc('\n', file_tmp, f_name);
158         try_fclose_unlink_rename(file_tmp, path_tmp, world.path_record, f_name);
159     }
160 }
161
162
163
164 extern uint8_t io_loop()
165 {
166     char * f_name = "io_loop()";
167     while (1)
168     {
169         server_test();
170         char * msg = io_round();
171         if (NULL == msg)
172         {
173             continue;
174         }
175         if (world.is_verbose)
176         {
177             exit_trouble(-1 == printf("Input: %s\n", msg), f_name, "printf()");
178         }
179         if (!strcmp("QUIT", msg))
180         {
181             free(msg);
182             return 1;
183         }
184         if (!strcmp("PING", msg))
185         {
186             free(msg);
187             char * pong = "PONG\n";
188             try_fwrite(pong, strlen(pong), 1, world.file_out, f_name);
189             fflush(world.file_out);
190             continue;
191         }
192         if (world.replay)
193         {
194             free(msg);
195             return 0;
196         }
197         obey_msg(msg, 1);
198         free(msg);
199     }
200 }