home · contact · privacy
Server: Only write record and save file if 15 seconds have passed.
[plomrogue] / src / server / init.c
1 /* src/server/init.c */
2
3 #define _POSIX_C_SOURCE 2 /* getopt(), optarg */
4 #include "init.h"
5 #include <errno.h> /* global errno, EEXIST */
6 #include <stddef.h> /* NULL */
7 #include <stdint.h> /* uint32_t */
8 #include <stdio.h> /* FILE, sprintf(), fflush() */
9 #include <stdlib.h> /* exit(), free(), atoi() */
10 #include <string.h> /* strlen() */
11 #include <sys/stat.h> /* mkdir() */
12 #include <sys/types.h> /* defines pid_t, time_t */
13 #include <time.h> /* time() */
14 #include <unistd.h> /* optarg, getopt(), access(), getpid() */
15 #include "../common/parse_file.h" /* err_line_zero(), err_line_inc() */
16 #include "../common/readwrite.h" /* try_fopen(), try_fclose(), textfile_width(),
17                                   * try_fgets(), try_fwrite(),
18                                   * detect_atomic_leftover()
19                                   */
20 #include "../common/rexit.h" /* exit_err(), exit_trouble() */
21 #include "../common/try_malloc.h" /* try_malloc() */
22 #include "cleanup.h" /* set_cleanup_flag() */
23 #include "field_of_view.h" /* build_fov_map() */
24 #include "hardcoded_strings.h" /* s */
25 #include "map.h" /* remake_map() */
26 #include "things.h" /* Thing, ThingType, free_things(), add_things(),
27                      * get_thing_id_action_id_by_name()
28                      */
29 #include "run.h" /* obey_msg(), io_loop(), record() */
30 #include "world.h" /* global world */
31
32
33
34
35 /* Pass to obey_msg() lines from file at "path", on "record" write to same. Do
36  * not pass lines that consist only of a newline character. Transform newline
37  * in the line passed to \0.
38  */
39 static void obey_lines_from_file(char * path, uint8_t record);
40
41 /* Replay game from record file up to the turn named in world.replay, then turn
42  * over to manual replay via io_loop().
43  */
44 static void replay_game();
45
46 /* Return 1 if the type defined by world.player_type has a .start_n of 0.
47  * Return 2 if no thing action with .name of s[S_CMD_WAIT] is defined.
48  * Else, return 0.
49  */
50 static uint8_t world_cannot_be_made();
51
52
53 static void obey_lines_from_file(char * path, uint8_t record)
54 {
55     FILE * file = try_fopen(path, "r", __func__);
56     uint32_t linemax = textfile_width(file);
57     char * line = try_malloc(linemax + 1, __func__);
58     while (NULL != try_fgets(line, linemax + 1, file, __func__))
59     {
60         if (strlen(line))
61         {
62             if (strcmp("\n", line))
63             {
64                 char * nl = strchr(line, '\n');
65                 if (nl)
66                 {
67                     *nl = '\0';
68                 }
69                 obey_msg(line, record, 1);
70             }
71             err_line_inc();
72         }
73     }
74     free(line);
75     try_fclose(file, __func__);
76 }
77
78
79
80 static void replay_game()
81 {
82     exit_err(access(s[S_PATH_RECORD], F_OK), "No record found to replay.");
83     FILE * file = try_fopen(s[S_PATH_RECORD], "r", __func__);
84     uint32_t linemax = textfile_width(file);
85     char * line = try_malloc(linemax + 1, __func__);
86     while (   world.turn < world.replay
87            && NULL != try_fgets(line, linemax + 1, file, __func__))
88     {
89         obey_msg(line, 0, 1);
90         err_line_inc();
91     }
92     uint8_t end = 0;
93     while (!io_loop())
94     {
95         if (!end)
96         {
97             end = (NULL == try_fgets(line, linemax + 1, file, __func__));
98             if (!end)
99             {
100                 obey_msg(line, 0, 1);
101                 err_line_inc();
102             }
103         }
104     }
105     free(line);
106     try_fclose(file, __func__);
107 }
108
109
110
111 static uint8_t world_cannot_be_made()
112 {
113     uint8_t player_will_be_generated = 0;
114     struct ThingType * tt;
115     for (tt = world.thing_types; NULL != tt; tt = tt->next)
116     {
117         if (world.player_type == tt->id)
118         {
119             player_will_be_generated = 0 < tt->start_n;
120             break;
121         }
122     }
123     if (!player_will_be_generated)
124     {
125         return 1;
126     }
127     if (!get_thing_action_id_by_name(s[S_CMD_WAIT]))
128     {
129         return 2;
130     }
131     return 0;
132 }
133
134
135
136 extern void obey_argv(int argc, char * argv[])
137 {
138     int opt;
139     while (-1 != (opt = getopt(argc, argv, "vs::")))
140     {
141         if      ('v' == opt)
142         {
143             world.is_verbose = 1;
144         }
145         else if ('s' == opt)
146         {
147             world.replay = 1;
148             if (optarg)
149             {
150                 world.replay = atoi(optarg);
151             }
152         }
153         else
154         {
155             exit(EXIT_FAILURE);
156         }
157     }
158 }
159
160
161
162 extern void setup_server_io()
163 {
164     int test = mkdir("server", 0700);
165     exit_trouble(test && EEXIST != errno, __func__, "mkdir");
166     world.file_out = try_fopen(s[S_PATH_OUT], "w", __func__);
167     world.server_test = try_malloc(10 + 1 + 10 + 1 + 1, __func__);
168     test = sprintf(world.server_test, "%d %d\n", getpid(), (int) time(0));
169     exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
170     try_fwrite(world.server_test, strlen(world.server_test), 1,
171                world.file_out, __func__);
172     fflush(world.file_out);
173     set_cleanup_flag(CLEANUP_OUT);
174     char * path_in = s[S_PATH_IN];
175     if (!access(path_in, F_OK))        /* This keeps out input from old input */
176     {                                  /* file streams of clients             */
177         unlink(path_in)   ;            /* communicating with server processes */
178     }                                  /* superseded by this current one.     */
179     world.file_in = try_fopen(path_in, "w", __func__);
180     try_fclose(world.file_in, __func__);
181     world.file_in = try_fopen(path_in, "r", __func__);
182     set_cleanup_flag(CLEANUP_IN);
183 }
184
185
186
187 extern uint8_t remake_world()
188 {
189     uint8_t test = world_cannot_be_made();
190     if (test)
191     {
192         return test;
193     }
194     free(world.log);
195     world.log = NULL;      /* thing_actions.c's update_log() checks for this. */
196     world.seed_map = world.seed;
197     free_things(world.things);
198     remake_map();
199     struct ThingType * tt;
200     for (tt = world.thing_types; NULL != tt; tt = tt->next)
201     {
202         if (world.player_type == tt->id)
203         {
204             add_things(tt->id, tt->start_n);
205             break;
206         }
207     }
208     for (tt = world.thing_types; NULL != tt; tt = tt->next)
209     {
210         if (world.player_type != tt->id)
211         {
212             add_things(tt->id, tt->start_n);
213         }
214     }
215     struct Thing * t;
216     for (t = world.things; NULL != t; t = t->next)
217     {
218         if (t->lifepoints)
219         {
220             build_fov_map(t);
221         }
222     }
223     world.turn = 1;
224     world.do_update = 1;
225     world.exists = 1;
226     return 0;
227 }
228
229
230
231 extern void run_game()
232 {
233     detect_atomic_leftover(s[S_PATH_SAVE]);
234     detect_atomic_leftover(s[S_PATH_RECORD]);
235     err_line_zero();
236     if (world.replay)
237     {
238         replay_game();
239         return;
240     }
241     if (!access(s[S_PATH_SAVE], F_OK))
242     {
243         obey_lines_from_file(s[S_PATH_SAVE], 0);
244     }
245     else
246     {
247         char * err = "No world config file from which to start a new world.";
248         exit_err(access(s[S_PATH_CONFIG], F_OK), err);
249         obey_lines_from_file(s[S_PATH_CONFIG], 1);
250         err_line_zero();
251         char * command = s[S_CMD_MAKE_WORLD];
252         char * msg = try_malloc(strlen(command) + 1 + 11 + 1, __func__);
253         int test = sprintf(msg, "%s %d", command, (int) time(NULL));
254         exit_trouble(test < 0, __func__, s[S_FCN_SPRINTF]);
255         obey_msg(msg, 1, 1);
256         free(msg);
257     }
258     err_line_zero();
259     io_loop();
260     record(NULL, 1);
261 }