Rework civ2civ3 ruleset help and documentation.
[freeciv.git] / client / global_worklist.c
blobab85cb957ff528e8ec73c00ad0d0a3afa068fa84
1 /**********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
13 #ifdef HAVE_CONFIG_H
14 #include <fc_config.h>
15 #endif
17 #include <stdarg.h>
19 /* utility */
20 #include "fcintl.h"
21 #include "log.h"
22 #include "mem.h"
23 #include "registry.h"
24 #include "shared.h"
25 #include "support.h"
27 /* common */
28 #include "fc_types.h"
29 #include "requirements.h"
30 #include "worklist.h"
32 /* client */
33 #include "client_main.h"
35 #include "global_worklist.h"
37 enum global_worklist_status {
38 /* Means we have a list of kind of universal and their name, but we
39 * don't know the id they could have on the server's ruleset. In this
40 * case, we use the unbuilt part and not the worklist one. */
41 STATUS_UNBUILT,
43 /* Means this worklist is plainly usable at running time. */
44 STATUS_WORKLIST
48 /* The global worklist structure. */
49 struct global_worklist {
50 int id;
51 char name[MAX_LEN_NAME];
52 enum global_worklist_status status;
53 union {
54 struct {
55 int length;
56 struct uni_name {
57 char *kind;
58 char *name;
59 } entries[MAX_LEN_WORKLIST];
60 } unbuilt;
61 struct worklist worklist;
65 static bool global_worklist_load(struct section_file *file,
66 const char *path, ...)
67 fc__attribute((__format__ (__printf__, 2, 3)));
68 static void global_worklist_save(const struct global_worklist *pgwl,
69 struct section_file *file, int fill_until,
70 const char *path, ...)
71 fc__attribute((__format__ (__printf__, 4, 5)));
73 /***********************************************************************
74 Initialize the client global worklists.
75 ***********************************************************************/
76 void global_worklists_init(void)
78 if (!client.worklists) {
79 client.worklists = global_worklist_list_new();
83 /***********************************************************************
84 Free the client global worklists.
85 ***********************************************************************/
86 void global_worklists_free(void)
88 if (client.worklists) {
89 global_worklists_iterate_all(pgwl) {
90 global_worklist_destroy(pgwl);
91 } global_worklists_iterate_all_end;
92 global_worklist_list_destroy(client.worklists);
93 client.worklists = NULL;
97 /***********************************************************************
98 Check if the global worklists are valid or not for the ruleset.
99 ***********************************************************************/
100 void global_worklists_build(void)
102 global_worklists_iterate_all(pgwl) {
103 if (pgwl->status == STATUS_UNBUILT) {
104 struct worklist worklist;
105 struct uni_name *puni_name;
106 int i;
108 /* Build a worklist. */
109 worklist_init(&worklist);
110 for (i = 0; i < pgwl->unbuilt.length; i++) {
111 struct universal source;
113 puni_name = pgwl->unbuilt.entries + i;
114 source = universal_by_rule_name(puni_name->kind, puni_name->name);
115 if (source.kind == universals_n_invalid()) {
116 /* This worklist is not valid on this ruleset.
117 * N.B.: Don't remove it to resave it in client rc file. */
118 break;
119 } else {
120 worklist_append(&worklist, source);
124 if (worklist_length(&worklist) != pgwl->unbuilt.length) {
125 /* Somewhat in this worklist is not supported by the current
126 * ruleset. Don't try to build it, but keep it for later save. */
127 continue;
130 /* Now the worklist is built, change status. */
131 for (i = 0; i < pgwl->unbuilt.length; i++) {
132 puni_name = pgwl->unbuilt.entries + i;
133 free(puni_name->kind);
134 free(puni_name->name);
136 pgwl->status = STATUS_WORKLIST;
137 worklist_copy(&pgwl->worklist, &worklist);
139 } global_worklists_iterate_all_end;
142 /***********************************************************************
143 Convert the universal pointers to strings to work out-ruleset.
144 ***********************************************************************/
145 void global_worklists_unbuild(void)
147 global_worklists_iterate_all(pgwl) {
148 if (pgwl->status == STATUS_WORKLIST) {
149 struct worklist worklist;
150 struct uni_name *puni_name;
151 int i;
153 /* Copy before over-write. */
154 worklist_copy(&worklist, &pgwl->worklist);
155 pgwl->status = STATUS_UNBUILT;
157 pgwl->unbuilt.length = worklist_length(&worklist);
158 for (i = 0; i < worklist_length(&worklist); i++) {
159 puni_name = pgwl->unbuilt.entries + i;
160 puni_name->kind =
161 fc_strdup(universal_type_rule_name(worklist.entries + i));
162 puni_name->name =
163 fc_strdup(universal_rule_name(worklist.entries + i));
166 } global_worklists_iterate_all_end;
169 /***********************************************************************
170 Returns the number of valid global worklists.
171 N.B.: This counts only the valid global worklists.
172 ***********************************************************************/
173 int global_worklists_number(void)
175 int count = 0;
177 global_worklists_iterate(pgwl) {
178 count++;
179 } global_worklists_iterate_end;
181 return count;
184 /***********************************************************************
185 Returns a new created global worklist structure.
186 ***********************************************************************/
187 static struct global_worklist *
188 global_worklist_alloc(enum global_worklist_status type)
190 static int last_id = 0;
191 struct global_worklist *pgwl = fc_calloc(1, sizeof(struct global_worklist));
193 pgwl->id = ++last_id;
194 pgwl->status = type;
196 /* Specific initializer. */
197 switch (pgwl->status) {
198 case STATUS_UNBUILT:
199 /* All members set to 0 by fc_calloc. */
200 break;
201 case STATUS_WORKLIST:
202 worklist_init(&pgwl->worklist);
203 break;
206 global_worklist_list_append(client.worklists, pgwl);
208 return pgwl;
211 /***********************************************************************
212 Destroys a glocal worklist.
213 ***********************************************************************/
214 void global_worklist_destroy(struct global_worklist *pgwl)
216 fc_assert_ret(NULL != pgwl);
218 global_worklist_list_remove(client.worklists, pgwl);
220 /* Specific descturctor. */
221 switch (pgwl->status) {
222 case STATUS_UNBUILT:
224 struct uni_name *puni_name;
225 int i;
227 for (i = 0; i < pgwl->unbuilt.length; i++) {
228 puni_name = pgwl->unbuilt.entries + i;
229 free(puni_name->kind);
230 free(puni_name->name);
233 break;
234 case STATUS_WORKLIST:
235 break;
238 free(pgwl);
241 /***********************************************************************
242 Creates a new global worklist form a normal worklist.
243 ***********************************************************************/
244 struct global_worklist *global_worklist_new(const char *name)
246 struct global_worklist *pgwl = global_worklist_alloc(STATUS_WORKLIST);
248 global_worklist_set_name(pgwl, name);
249 return pgwl;
252 /***********************************************************************
253 Returns TRUE if this global worklist is valid.
254 ***********************************************************************/
255 bool global_worklist_is_valid(const struct global_worklist *pgwl)
257 return pgwl && pgwl->status == STATUS_WORKLIST;
260 /***********************************************************************
261 Sets the worklist. Return TRUE on success.
262 ***********************************************************************/
263 bool global_worklist_set(struct global_worklist *pgwl,
264 const struct worklist *pwl)
266 if (pgwl && pgwl->status == STATUS_WORKLIST && pwl) {
267 worklist_copy(&pgwl->worklist, pwl);
268 return TRUE;
270 return FALSE;
273 /***********************************************************************
274 Returns the worklist of this global worklist or NULL if it's not valid.
275 ***********************************************************************/
276 const struct worklist *global_worklist_get(const struct global_worklist *pgwl)
278 if (pgwl && pgwl->status == STATUS_WORKLIST) {
279 return &pgwl->worklist;
280 } else {
281 return NULL;
285 /***********************************************************************
286 Returns the id of the global worklist.
287 ***********************************************************************/
288 int global_worklist_id(const struct global_worklist *pgwl)
290 fc_assert_ret_val(NULL != pgwl, -1);
291 return pgwl->id;
294 /***********************************************************************
295 Returns the global worklist corresponding to this id.
296 N.B.: It can returns an invalid glocbal worklist.
297 ***********************************************************************/
298 struct global_worklist *global_worklist_by_id(int id)
300 global_worklists_iterate_all(pgwl) {
301 if (pgwl->id == id) {
302 return pgwl;
304 } global_worklists_iterate_all_end;
306 return NULL;
309 /***********************************************************************
310 Sets the name of this global worklist.
311 ***********************************************************************/
312 void global_worklist_set_name(struct global_worklist *pgwl,
313 const char *name)
315 if (name) {
316 sz_strlcpy(pgwl->name, name);
320 /***********************************************************************
321 Return the name of the global worklist.
322 ***********************************************************************/
323 const char *global_worklist_name(const struct global_worklist *pgwl)
325 fc_assert_ret_val(NULL != pgwl, NULL);
326 return pgwl->name;
329 /***********************************************************************
330 Load a global worklist form a section file. Returns FALSE if we
331 reached the end of the array.
332 ***********************************************************************/
333 static bool global_worklist_load(struct section_file *file,
334 const char *path, ...)
336 struct global_worklist *pgwl;
337 const char *kind;
338 const char *name;
339 char path_str[1024];
340 va_list ap;
341 int i, length;
343 /* The first part of the registry path is taken from the varargs to the
344 * function. */
345 va_start(ap, path);
346 fc_vsnprintf(path_str, sizeof(path_str), path, ap);
347 va_end(ap);
349 length = secfile_lookup_int_default(file, -1, "%s.wl_length", path_str);
350 if (length == -1) {
351 /* Not set. */
352 return FALSE;
354 length = MIN(length, MAX_LEN_WORKLIST);
355 pgwl = global_worklist_alloc(STATUS_UNBUILT);
356 global_worklist_set_name(pgwl,
357 secfile_lookup_str_default(file, _("(noname)"),
358 "%s.wl_name",
359 path_str));
361 for (i = 0; i < length; i++) {
362 kind = secfile_lookup_str_default(file, NULL, "%s.wl_kind%d",
363 path_str, i);
365 if (!kind) {
366 /* before 2.2.0 unit production was indicated by flag. */
367 bool is_unit = secfile_lookup_bool_default(file, FALSE,
368 "%s.wl_is_unit%d",
369 path_str, i);
370 kind = universals_n_name(is_unit ? VUT_UTYPE : VUT_IMPROVEMENT);
373 name = secfile_lookup_str_default(file, NULL, "%s.wl_value%d",
374 path_str, i);
375 if (NULL == kind || '\0' == kind[0] || NULL == name || '\0' == name[0]) {
376 break;
377 } else {
378 pgwl->unbuilt.entries[i].kind = fc_strdup(kind);
379 pgwl->unbuilt.entries[i].name = fc_strdup(name);
380 pgwl->unbuilt.length++;
384 return TRUE;
387 /***********************************************************************
388 Load all global worklist from a section file.
389 ***********************************************************************/
390 void global_worklists_load(struct section_file *file)
392 int i;
394 /* Clear the current global worklists. */
395 global_worklists_iterate_all(pgwl) {
396 global_worklist_destroy(pgwl);
397 } global_worklists_iterate_all_end;
399 for (i = 0; global_worklist_load(file, "worklists.worklist%d", i); i++) {
400 /* Nothing to do more. */
403 if (C_S_RUNNING <= client_state()) {
404 /* We need to build the worklists immediately. */
405 global_worklists_build();
409 /***********************************************************************
410 Save one global worklist into a section file.
411 ***********************************************************************/
412 static void global_worklist_save(const struct global_worklist *pgwl,
413 struct section_file *file, int fill_until,
414 const char *path, ...)
416 char path_str[1024];
417 int i = 0;
418 va_list ap;
420 /* The first part of the registry path is taken from the varargs to the
421 * function. */
422 va_start(ap, path);
423 fc_vsnprintf(path_str, sizeof(path_str), path, ap);
424 va_end(ap);
426 secfile_insert_str(file, pgwl->name, "%s.wl_name", path_str);
428 switch (pgwl->status) {
429 case STATUS_UNBUILT:
430 secfile_insert_int(file, pgwl->unbuilt.length,
431 "%s.wl_length", path_str);
432 for (i = 0; i < pgwl->unbuilt.length; i++) {
433 secfile_insert_str(file, pgwl->unbuilt.entries[i].kind,
434 "%s.wl_kind%d", path_str, i);
435 secfile_insert_str(file, pgwl->unbuilt.entries[i].name,
436 "%s.wl_value%d", path_str, i);
438 break;
439 case STATUS_WORKLIST:
440 secfile_insert_int(file, worklist_length(&pgwl->worklist),
441 "%s.wl_length", path_str);
442 for (i = 0; i < pgwl->unbuilt.length; i++) {
443 secfile_insert_str(file,
444 universal_type_rule_name(pgwl->worklist.entries + i),
445 "%s.wl_kind%d", path_str, i);
446 secfile_insert_str(file,
447 universal_rule_name(pgwl->worklist.entries + i),
448 "%s.wl_value%d", path_str, i);
450 break;
453 /* We want to keep savegame in tabular format, so each line has to be
454 * of equal length. Fill table up to maximum worklist size. */
455 while (i < fill_until) {
456 secfile_insert_str(file, "", "%s.wl_kind%d", path_str, i);
457 secfile_insert_str(file, "", "%s.wl_value%d", path_str, i);
458 i++;
462 /***********************************************************************
463 Save all global worklist into a section file.
464 ***********************************************************************/
465 void global_worklists_save(struct section_file *file)
467 int max_length = 0;
468 int i = 0;
470 /* We want to keep savegame in tabular format, so each line has to be
471 * of equal length. So we need to know about the biggest worklist. */
472 global_worklists_iterate_all(pgwl) {
473 switch (pgwl->status) {
474 case STATUS_UNBUILT:
475 max_length = MAX(max_length, pgwl->unbuilt.length);
476 break;
477 case STATUS_WORKLIST:
478 max_length = MAX(max_length, worklist_length(&pgwl->worklist));
479 break;
481 } global_worklists_iterate_all_end;
483 global_worklists_iterate_all(pgwl) {
484 global_worklist_save(pgwl, file, max_length,
485 "worklists.worklist%d", i++);
486 } global_worklists_iterate_all_end;