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