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)
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 ***********************************************************************/
14 #include <fc_config.h>
29 #include "requirements.h"
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. */
43 /* Means this worklist is plainly usable at running time. */
48 /* The global worklist structure. */
49 struct global_worklist
{
51 char name
[MAX_LEN_NAME
];
52 enum global_worklist_status status
;
59 } entries
[MAX_LEN_WORKLIST
];
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
;
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. */
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. */
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
;
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
;
161 fc_strdup(universal_type_rule_name(worklist
.entries
+ i
));
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)
177 global_worklists_iterate(pgwl
) {
179 } global_worklists_iterate_end
;
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
;
196 /* Specific initializer. */
197 switch (pgwl
->status
) {
199 /* All members set to 0 by fc_calloc. */
201 case STATUS_WORKLIST
:
202 worklist_init(&pgwl
->worklist
);
206 global_worklist_list_append(client
.worklists
, 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
) {
224 struct uni_name
*puni_name
;
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
);
234 case STATUS_WORKLIST
:
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
);
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
);
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
;
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);
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
) {
304 } global_worklists_iterate_all_end
;
309 /***********************************************************************
310 Sets the name of this global worklist.
311 ***********************************************************************/
312 void global_worklist_set_name(struct global_worklist
*pgwl
,
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
);
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
;
343 /* The first part of the registry path is taken from the varargs to the
346 fc_vsnprintf(path_str
, sizeof(path_str
), path
, ap
);
349 length
= secfile_lookup_int_default(file
, -1, "%s.wl_length", path_str
);
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)"),
361 for (i
= 0; i
< length
; i
++) {
362 kind
= secfile_lookup_str_default(file
, NULL
, "%s.wl_kind%d",
366 /* before 2.2.0 unit production was indicated by flag. */
367 bool is_unit
= secfile_lookup_bool_default(file
, FALSE
,
370 kind
= universals_n_name(is_unit
? VUT_UTYPE
: VUT_IMPROVEMENT
);
373 name
= secfile_lookup_str_default(file
, NULL
, "%s.wl_value%d",
375 if (NULL
== kind
|| '\0' == kind
[0] || NULL
== name
|| '\0' == name
[0]) {
378 pgwl
->unbuilt
.entries
[i
].kind
= fc_strdup(kind
);
379 pgwl
->unbuilt
.entries
[i
].name
= fc_strdup(name
);
380 pgwl
->unbuilt
.length
++;
387 /***********************************************************************
388 Load all global worklist from a section file.
389 ***********************************************************************/
390 void global_worklists_load(struct section_file
*file
)
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
, ...)
420 /* The first part of the registry path is taken from the varargs to the
423 fc_vsnprintf(path_str
, sizeof(path_str
), path
, ap
);
426 secfile_insert_str(file
, pgwl
->name
, "%s.wl_name", path_str
);
428 switch (pgwl
->status
) {
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
);
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
);
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
);
462 /***********************************************************************
463 Save all global worklist into a section file.
464 ***********************************************************************/
465 void global_worklists_save(struct section_file
*file
)
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
) {
475 max_length
= MAX(max_length
, pgwl
->unbuilt
.length
);
477 case STATUS_WORKLIST
:
478 max_length
= MAX(max_length
, worklist_length(&pgwl
->worklist
));
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
;