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.
7
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
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17
18 #include <stdio.h> /* for remove() */
19
20 /* utility */
21 #include "capability.h"
22 #include "fcintl.h"
23 #include "log.h"
24 #include "mem.h"
25 #include "rand.h"
26 #include "registry.h"
27 #include "shared.h"
28 #include "string_vector.h"
29 #include "support.h"
30
31 /* common */
32 #include "ai.h"
33 #include "calendar.h"
34 #include "events.h"
35 #include "game.h"
36 #include "improvement.h"
37 #include "movement.h"
38 #include "packets.h"
39
40 /* server */
41 #include "citytools.h"
42 #include "connecthand.h"
43 #include "maphand.h"
44 #include "notify.h"
45 #include "plrhand.h"
46 #include "srv_main.h"
47 #include "stdinhand.h"
48 #include "unittools.h"
49
50 /* server/advisors */
51 #include "advdata.h"
52
53 #include "gamehand.h"
54
55 #define CHALLENGE_ROOT "challenge"
56
57 #define SPECLIST_TAG startpos
58 #define SPECLIST_TYPE struct startpos
59 #include "speclist.h"
60 #define startpos_list_iterate(list, plink, psp) \
61 TYPED_LIST_BOTH_ITERATE(struct startpos_list_link, struct startpos, \
62 list, plink, psp)
63 #define startpos_list_iterate_end LIST_BOTH_ITERATE_END
64
65 struct team_placement_config {
66 struct tile **startpos;
67 int flexible_startpos_num;
68 int usable_startpos_num;
69 int total_startpos_num;
70 };
71
72 struct team_placement_state {
73 int *startpos;
74 long score;
75 };
76
77 #define SPECPQ_TAG team_placement
78 #define SPECPQ_DATA_TYPE struct team_placement_state *
79 #define SPECPQ_PRIORITY_TYPE long
80 #include "specpq.h"
81
82 /****************************************************************************
83 Get role_id for given role character
84 ****************************************************************************/
crole_to_role_id(char crole)85 enum unit_role_id crole_to_role_id(char crole)
86 {
87 switch (crole) {
88 case 'c':
89 return L_START_CITIES;
90 case 'w':
91 return L_START_WORKER;
92 case 'x':
93 return L_START_EXPLORER;
94 case 'k':
95 return L_START_KING;
96 case 's':
97 return L_START_DIPLOMAT;
98 case 'f':
99 return L_START_FERRY;
100 case 'd':
101 return L_START_DEFEND_OK;
102 case 'D':
103 return L_START_DEFEND_GOOD;
104 case 'a':
105 return L_START_ATTACK_FAST;
106 case 'A':
107 return L_START_ATTACK_STRONG;
108 default:
109 return 0;
110 }
111 }
112
113 /****************************************************************************
114 Get unit_type for given role character
115 ****************************************************************************/
crole_to_unit_type(char crole,struct player * pplayer)116 struct unit_type *crole_to_unit_type(char crole, struct player *pplayer)
117 {
118 struct unit_type *utype = NULL;
119 enum unit_role_id role = crole_to_role_id(crole);
120
121 if (role == 0) {
122 fc_assert_ret_val(FALSE, NULL);
123 return NULL;
124 }
125
126 /* Create the unit of an appropriate type, if it exists */
127 if (num_role_units(role) > 0) {
128 if (pplayer != NULL) {
129 utype = first_role_unit_for_player(pplayer, role);
130 }
131 if (utype == NULL) {
132 utype = get_role_unit(role, 0);
133 }
134 }
135
136 return utype;
137 }
138
139 /****************************************************************************
140 Place a starting unit for the player. Returns tile where unit was really
141 placed. By default the ptype is used and crole does not matter, but if
142 former is NULL, crole will be used instead.
143 ****************************************************************************/
place_starting_unit(struct tile * starttile,struct player * pplayer,struct unit_type * ptype,char crole)144 static struct tile *place_starting_unit(struct tile *starttile,
145 struct player *pplayer,
146 struct unit_type *ptype, char crole)
147 {
148 struct tile *ptile = NULL;
149 struct unit_type *utype;
150 bool hut_present = FALSE;
151
152 if (ptype != NULL) {
153 utype = ptype;
154 } else {
155 utype = crole_to_unit_type(crole, pplayer);
156 }
157
158 if (utype != NULL) {
159 iterate_outward(starttile, game.map.xsize + game.map.ysize, itertile) {
160 if (!is_non_allied_unit_tile(itertile, pplayer)
161 && is_native_tile(utype, itertile)) {
162 ptile = itertile;
163 break;
164 }
165 } iterate_outward_end;
166 }
167
168 if (ptile == NULL) {
169 /* No place where unit may exist. */
170 return NULL;
171 }
172
173 fc_assert_ret_val(!is_non_allied_unit_tile(ptile, pplayer), NULL);
174
175 /* For scenarios or dispersion, huts may coincide with player starts (in
176 * other cases, huts are avoided as start positions). Remove any such hut,
177 * and make sure to tell the client, since we may have already sent this
178 * tile (with the hut) earlier: */
179 extra_type_by_cause_iterate(EC_HUT, pextra) {
180 if (tile_has_extra(ptile, pextra)) {
181 tile_extra_rm_apply(ptile, pextra);
182 hut_present = TRUE;
183 }
184 } extra_type_by_cause_iterate_end;
185
186 if (hut_present) {
187 update_tile_knowledge(ptile);
188 log_verbose("Removed hut on start position for %s",
189 player_name(pplayer));
190 }
191
192 /* Expose visible area. */
193 map_show_circle(pplayer, ptile, game.server.init_vis_radius_sq);
194
195 if (utype != NULL) {
196 (void) create_unit(pplayer, ptile, utype, FALSE, 0, 0);
197 return ptile;
198 }
199
200 return NULL;
201 }
202
203 /****************************************************************************
204 Find a valid position not far from our starting position.
205 ****************************************************************************/
find_dispersed_position(struct player * pplayer,struct tile * pcenter)206 static struct tile *find_dispersed_position(struct player *pplayer,
207 struct tile *pcenter)
208 {
209 struct tile *ptile;
210 int x, y;
211 int bailout;
212
213 if (game.server.dispersion == 0) {
214 bailout = 1; /* One attempt is guaranteed to cover single tile */
215 } else {
216 bailout = game.server.dispersion * 2 + 1; /* Side of the area */
217 bailout *= bailout; /* Area */
218 bailout *= 5; /* Likely to hit each tile at least once */
219 }
220
221 do {
222 if (!bailout--) {
223 return NULL;
224 }
225 index_to_map_pos(&x, &y, tile_index(pcenter));
226 x += fc_rand(2 * game.server.dispersion + 1) - game.server.dispersion;
227 y += fc_rand(2 * game.server.dispersion + 1) - game.server.dispersion;
228 } while (!((ptile = map_pos_to_tile(x, y))
229 && tile_continent(pcenter) == tile_continent(ptile)
230 && !is_ocean_tile(ptile)
231 && real_map_distance(pcenter, ptile) < game.server.dispersion
232 + 1
233 && !is_non_allied_unit_tile(ptile, pplayer)));
234
235 return ptile;
236 }
237
238 /* Calculate the distance between tiles, according to the 'teamplacement'
239 * setting set to 'CLOSEST'. */
240 #define team_placement_closest sq_map_distance
241
242 /****************************************************************************
243 Calculate the distance between tiles, according to the 'teamplacement'
244 setting set to 'CONTINENT'.
245 ****************************************************************************/
team_placement_continent(const struct tile * ptile1,const struct tile * ptile2)246 static int team_placement_continent(const struct tile *ptile1,
247 const struct tile *ptile2)
248 {
249 return (ptile1->continent == ptile2->continent
250 ? sq_map_distance(ptile1, ptile2)
251 : sq_map_distance(ptile1, ptile2) + MAP_INDEX_SIZE);
252 }
253
254 /****************************************************************************
255 Calculate the distance between tiles, according to the 'teamplacement'
256 setting set to 'HORIZONTAL'.
257 ****************************************************************************/
team_placement_horizontal(const struct tile * ptile1,const struct tile * ptile2)258 static int team_placement_horizontal(const struct tile *ptile1,
259 const struct tile *ptile2)
260 {
261 int dx, dy;
262
263 map_distance_vector(&dx, &dy, ptile1, ptile2);
264 /* Map vector to natural vector (Y axis). */
265 return abs(MAP_IS_ISOMETRIC ? dx + dy : dy);
266 }
267
268 /****************************************************************************
269 Calculate the distance between tiles, according to the 'teamplacement'
270 setting set to 'VERTICAL'.
271 ****************************************************************************/
team_placement_vertical(const struct tile * ptile1,const struct tile * ptile2)272 static int team_placement_vertical(const struct tile *ptile1,
273 const struct tile *ptile2)
274 {
275 int dx, dy;
276
277 map_distance_vector(&dx, &dy, ptile1, ptile2);
278 /* Map vector to natural vector (X axis). */
279 return abs(MAP_IS_ISOMETRIC ? dx - dy : dy);
280 }
281
282 /****************************************************************************
283 Destroys a team_placement_state structure.
284 ****************************************************************************/
team_placement_state_destroy(struct team_placement_state * pstate)285 static void team_placement_state_destroy(struct team_placement_state *pstate)
286 {
287 free(pstate->startpos);
288 free(pstate);
289 }
290
291 /****************************************************************************
292 Find the best team placement, according to the 'team_placement' setting.
293 ****************************************************************************/
do_team_placement(const struct team_placement_config * pconfig,struct team_placement_state * pbest_state,int iter_max)294 static void do_team_placement(const struct team_placement_config *pconfig,
295 struct team_placement_state *pbest_state,
296 int iter_max)
297 {
298 const size_t state_array_size = (sizeof(*pbest_state->startpos)
299 * pconfig->total_startpos_num);
300 struct team_placement_pq *pqueue =
301 team_placement_pq_new(pconfig->total_startpos_num * 4);
302 int (*distance)(const struct tile *, const struct tile *) = NULL;
303 struct team_placement_state *pstate, *pnew;
304 const struct tile *ptile1, *ptile2;
305 long base_delta, delta;
306 bool base_delta_calculated;
307 int iter = 0;
308 bool repeat;
309 int i, j, k, t1, t2;
310
311 switch (game.map.server.team_placement) {
312 case TEAM_PLACEMENT_CLOSEST:
313 distance = team_placement_closest;
314 break;
315 case TEAM_PLACEMENT_CONTINENT:
316 distance = team_placement_continent;
317 break;
318 case TEAM_PLACEMENT_HORIZONTAL:
319 distance = team_placement_horizontal;
320 break;
321 case TEAM_PLACEMENT_VERTICAL:
322 distance = team_placement_vertical;
323 break;
324 case TEAM_PLACEMENT_DISABLED:
325 break;
326 }
327 fc_assert_ret_msg(distance != NULL, "Wrong team_placement variant (%d)",
328 game.map.server.team_placement);
329
330 /* Initialize starting state. */
331 pstate = fc_malloc(sizeof(*pstate));
332 pstate->startpos = fc_malloc(state_array_size);
333 memcpy(pstate->startpos, pbest_state->startpos, state_array_size);
334 pstate->score = pbest_state->score;
335
336 do {
337 repeat = FALSE;
338 for (i = 0; i < pconfig->usable_startpos_num; i++) {
339 t1 = pstate->startpos[i];
340 if (t1 == -1) {
341 continue; /* Not used. */
342 }
343 ptile1 = pconfig->startpos[i];
344 base_delta_calculated = FALSE;
345 for (j = i + 1; j < (i >= pconfig->flexible_startpos_num
346 ? pconfig->usable_startpos_num
347 : pconfig->flexible_startpos_num); j++) {
348 t2 = pstate->startpos[j];
349 if (t2 == -1) {
350 /* Not assigned yet. */
351 ptile2 = pconfig->startpos[j];
352 if (base_delta_calculated) {
353 delta = base_delta;
354 for (k = 0; k < pconfig->total_startpos_num; k++) {
355 if (k != i && t1 == pstate->startpos[k]) {
356 delta += distance(ptile2, pconfig->startpos[k]);
357 }
358 }
359 } else {
360 delta = 0;
361 base_delta = 0;
362 for (k = 0; k < pconfig->total_startpos_num; k++) {
363 if (k != i && t1 == pstate->startpos[k]) {
364 base_delta -= distance(ptile1, pconfig->startpos[k]);
365 delta += distance(ptile2, pconfig->startpos[k]);
366 }
367 }
368 delta += base_delta;
369 base_delta_calculated = TRUE;
370 }
371 } else if (t1 < t2) {
372 ptile2 = pconfig->startpos[j];
373 if (base_delta_calculated) {
374 delta = base_delta;
375 for (k = 0; k < pconfig->total_startpos_num; k++) {
376 if (k != i && t1 == pstate->startpos[k]) {
377 delta += distance(ptile2, pconfig->startpos[k]);
378 } else if (k != j && t2 == pstate->startpos[k]) {
379 delta -= distance(ptile2, pconfig->startpos[k]);
380 delta += distance(ptile1, pconfig->startpos[k]);
381 }
382 }
383 } else {
384 delta = 0;
385 base_delta = 0;
386 for (k = 0; k < pconfig->total_startpos_num; k++) {
387 if (k != i && t1 == pstate->startpos[k]) {
388 base_delta -= distance(ptile1, pconfig->startpos[k]);
389 delta += distance(ptile2, pconfig->startpos[k]);
390 } else if (k != j && t2 == pstate->startpos[k]) {
391 delta -= distance(ptile2, pconfig->startpos[k]);
392 delta += distance(ptile1, pconfig->startpos[k]);
393 }
394 }
395 delta += base_delta;
396 base_delta_calculated = TRUE;
397 }
398 } else {
399 continue;
400 }
401
402 if (delta <= 0) {
403 repeat = TRUE;
404 pnew = fc_malloc(sizeof(*pnew));
405 pnew->startpos = fc_malloc(state_array_size);
406 memcpy(pnew->startpos, pstate->startpos, state_array_size);
407 pnew->startpos[i] = t2;
408 pnew->startpos[j] = t1;
409 pnew->score = pstate->score + delta;
410 team_placement_pq_insert(pqueue, pnew, -pnew->score);
411
412 if (pnew->score < pbest_state->score) {
413 memcpy(pbest_state->startpos, pnew->startpos, state_array_size);
414 pbest_state->score = pnew->score;
415 }
416 }
417 }
418 }
419
420 team_placement_state_destroy(pstate);
421 if (iter++ >= iter_max) {
422 log_normal(_("Didn't find optimal solution for team placement "
423 "in %d iterations."), iter);
424 break;
425 }
426
427 } while (repeat && team_placement_pq_remove(pqueue, &pstate));
428
429 team_placement_pq_destroy_full(pqueue, team_placement_state_destroy);
430 }
431
432 /****************************************************************************
433 Initialize a new game: place the players' units onto the map, etc.
434 ****************************************************************************/
init_new_game(void)435 void init_new_game(void)
436 {
437 struct startpos_list *impossible_list, *targeted_list, *flexible_list;
438 struct tile *player_startpos[player_slot_count()];
439 int placed_units[player_slot_count()];
440 int players_to_place = player_count();
441 int sulen;
442
443 randomize_base64url_string(server.game_identifier,
444 sizeof(server.game_identifier));
445
446 /* Assign players to starting positions on the map.
447 * (In scenarios with restrictions on which nations can use which predefined
448 * start positions, this process tries to satisfy those restrictions, but
449 * does not guarantee to. Even if there is a solution to the matching
450 * problem, this algorithm may not find it.) */
451
452 fc_assert(player_count() <= map_startpos_count());
453
454 /* Convert the startposition hash table in a linked lists, as we mostly
455 * need now to iterate it now. And then, we will be able to remove the
456 * assigned start postions one by one. */
457 impossible_list = startpos_list_new();
458 targeted_list = startpos_list_new();
459 flexible_list = startpos_list_new();
460
461 map_startpos_iterate(psp) {
462 if (startpos_allows_all(psp)) {
463 startpos_list_append(flexible_list, psp);
464 } else {
465 startpos_list_append(targeted_list, psp);
466 }
467 } map_startpos_iterate_end;
468
469 fc_assert(startpos_list_size(targeted_list)
470 + startpos_list_size(flexible_list) == map_startpos_count());
471
472 memset(player_startpos, 0, sizeof(player_startpos));
473 log_verbose("Placing players at start positions.");
474
475 /* First assign start positions which have restrictions on which nations
476 * can use them. */
477 if (0 < startpos_list_size(targeted_list)) {
478 log_verbose("Assigning matching nations.");
479
480 startpos_list_shuffle(targeted_list); /* Randomize. */
481 do {
482 struct nation_type *pnation;
483 struct startpos_list_link *choice;
484 bool removed = FALSE;
485
486 /* Assign first players which can pick only one start position. */
487 players_iterate(pplayer) {
488 if (NULL != player_startpos[player_index(pplayer)]) {
489 /* Already assigned. */
490 continue;
491 }
492
493 pnation = nation_of_player(pplayer);
494 choice = NULL;
495 startpos_list_iterate(targeted_list, plink, psp) {
496 if (startpos_nation_allowed(psp, pnation)) {
497 if (NULL != choice) {
498 choice = NULL;
499 break; /* Many choices. */
500 } else {
501 choice = plink;
502 }
503 }
504 } startpos_list_iterate_end;
505
506 if (NULL != choice) {
507 /* Assign this start position to this player and remove
508 * both from consideration. */
509 struct tile *ptile =
510 startpos_tile(startpos_list_link_data(choice));
511
512 player_startpos[player_index(pplayer)] = ptile;
513 startpos_list_erase(targeted_list, choice);
514 players_to_place--;
515 removed = TRUE;
516 log_verbose("Start position (%d, %d) exactly matches player %s (%s).",
517 TILE_XY(ptile), player_name(pplayer),
518 nation_rule_name(pnation));
519 }
520 } players_iterate_end;
521
522 if (!removed) {
523 /* Didn't find any 1:1 matches. For the next restricted start
524 * position, assign a random matching player. (This may create
525 * restrictions such that more 1:1 matches are possible.) */
526 struct startpos *psp = startpos_list_back(targeted_list);
527 struct tile *ptile = startpos_tile(psp);
528 struct player *rand_plr = NULL;
529 int i = 0;
530
531 startpos_list_pop_back(targeted_list); /* Detach 'psp'. */
532 players_iterate(pplayer) {
533 if (NULL != player_startpos[player_index(pplayer)]) {
534 /* Already assigned. */
535 continue;
536 }
537
538 pnation = nation_of_player(pplayer);
539 if (startpos_nation_allowed(psp, pnation) && 0 == fc_rand(++i)) {
540 rand_plr = pplayer;
541 }
542 } players_iterate_end;
543
544 if (NULL != rand_plr) {
545 player_startpos[player_index(rand_plr)] = ptile;
546 players_to_place--;
547 log_verbose("Start position (%d, %d) matches player %s (%s).",
548 TILE_XY(ptile), player_name(rand_plr),
549 nation_rule_name(nation_of_player(rand_plr)));
550 } else {
551 /* This start position cannot be assigned, given the assignments
552 * made so far. We may have to fall back to mismatched
553 * assignments. */
554 log_verbose("Start position (%d, %d) cannot be assigned for "
555 "any player, keeping for the moment...",
556 TILE_XY(ptile));
557 /* Keep it for later, we may need it. */
558 startpos_list_append(impossible_list, psp);
559 }
560 }
561 } while (0 < players_to_place && 0 < startpos_list_size(targeted_list));
562 }
563
564 /* Now try to assign with regard to the 'teamplacement' setting. */
565 if (players_to_place > 0
566 && game.map.server.team_placement != TEAM_PLACEMENT_DISABLED
567 && player_count() > team_count()) {
568 const struct player_list *members;
569 int team_placement_players_to_place = 0;
570 int real_team_count = 0;
571
572 teams_iterate(pteam) {
573 members = team_members(pteam);
574 fc_assert(0 < player_list_size(members));
575 real_team_count++;
576 if (player_list_size(members) == 1) {
577 /* Single player teams, doesn't count for team placement. */
578 continue;
579 }
580 player_list_iterate(members, pplayer) {
581 if (player_startpos[player_index(pplayer)] == NULL) {
582 team_placement_players_to_place++;
583 }
584 } player_list_iterate_end;
585 } teams_iterate_end;
586
587 if (real_team_count > 1 && team_placement_players_to_place > 0) {
588 /* We really can do something to improve team placement. */
589 struct team_placement_config config;
590 struct team_placement_state state;
591 int i, j, t;
592
593 log_verbose("Do team placement for %d players, using %s variant.",
594 team_placement_players_to_place,
595 team_placement_name(game.map.server.team_placement));
596
597 /* Initialize configuration. */
598 config.flexible_startpos_num = startpos_list_size(flexible_list);
599 config.usable_startpos_num = config.flexible_startpos_num;
600 if (config.flexible_startpos_num < team_placement_players_to_place) {
601 config.usable_startpos_num += startpos_list_size(impossible_list);
602 }
603 config.total_startpos_num = (config.usable_startpos_num
604 + player_count() - players_to_place);
605 config.startpos = fc_malloc(sizeof(*config.startpos)
606 * config.total_startpos_num);
607 i = 0;
608 startpos_list_iterate(flexible_list, plink, psp) {
609 config.startpos[i++] = startpos_tile(psp);
610 } startpos_list_iterate_end;
611 fc_assert(i == config.flexible_startpos_num);
612 if (i < config.usable_startpos_num) {
613 startpos_list_iterate(impossible_list, plink, psp) {
614 config.startpos[i++] = startpos_tile(psp);
615 } startpos_list_iterate_end;
616 }
617 fc_assert(i == config.usable_startpos_num);
618 while (i < config.total_startpos_num) {
619 config.startpos[i++] = NULL;
620 }
621 fc_assert(i == config.total_startpos_num);
622
623 /* Initialize state. */
624 state.startpos = fc_malloc(sizeof(*state.startpos)
625 * config.total_startpos_num);
626 state.score = 0;
627 i = 0;
628 j = config.usable_startpos_num;
629 teams_iterate(pteam) {
630 members = team_members(pteam);
631 if (player_list_size(members) <= 1) {
632 /* Single player teams, doesn't count for team placement. */
633 continue;
634 }
635 t = team_number(pteam);
636 player_list_iterate(members, pplayer) {
637 struct tile *ptile = player_startpos[player_index(pplayer)];
638
639 if (ptile == NULL) {
640 state.startpos[i++] = t;
641 } else {
642 state.startpos[j] = t;
643 config.startpos[j] = ptile;
644 j++;
645 }
646 } player_list_iterate_end;
647 } teams_iterate_end;
648 while (i < config.usable_startpos_num) {
649 state.startpos[i++] = -1;
650 }
651 fc_assert(i == config.usable_startpos_num);
652 while (j < config.total_startpos_num) {
653 state.startpos[j++] = -1;
654 }
655 fc_assert(j == config.total_startpos_num);
656
657 /* Look for best team placement. */
658 do_team_placement(&config, &state, team_placement_players_to_place);
659
660 /* Apply result. */
661 for (i = 0; i < config.usable_startpos_num; i++) {
662 t = state.startpos[i];
663 if (t != -1) {
664 const struct team *pteam = team_by_number(t);
665 int candidate_index = -1;
666 int candidate_num = 0;
667
668 log_verbose("Start position (%d, %d) assigned to team %d (%s)",
669 TILE_XY(config.startpos[i]),
670 t, team_rule_name(pteam));
671
672 player_list_iterate(team_members(pteam), member) {
673 if (player_startpos[player_index(member)] == NULL
674 && fc_rand(++candidate_num) == 0) {
675 candidate_index = player_index(member);
676 }
677 } player_list_iterate_end;
678 fc_assert(candidate_index >= 0);
679 player_startpos[candidate_index] = config.startpos[i];
680 team_placement_players_to_place--;
681 players_to_place--;
682 }
683 }
684 fc_assert(team_placement_players_to_place == 0);
685
686 /* Free data. */
687 if (players_to_place > 0) {
688 /* We need to remove used startpos from the lists. */
689 i = 0;
690 startpos_list_iterate(flexible_list, plink, psp) {
691 fc_assert(config.startpos[i] == startpos_tile(psp));
692 if (state.startpos[i] != -1) {
693 startpos_list_erase(flexible_list, plink);
694 }
695 i++;
696 } startpos_list_iterate_end;
697 fc_assert(i == config.flexible_startpos_num);
698 if (i < config.usable_startpos_num) {
699 startpos_list_iterate(impossible_list, plink, psp) {
700 fc_assert(config.startpos[i] == startpos_tile(psp));
701 if (state.startpos[i] != -1) {
702 startpos_list_erase(impossible_list, plink);
703 }
704 i++;
705 } startpos_list_iterate_end;
706 }
707 fc_assert(i == config.usable_startpos_num);
708 }
709
710 free(config.startpos);
711 }
712 }
713
714 /* Now assign unrestricted start positions to any remaining players. */
715 if (0 < players_to_place && 0 < startpos_list_size(flexible_list)) {
716 struct tile *ptile;
717
718 log_verbose("Assigning unrestricted start positions.");
719
720 startpos_list_shuffle(flexible_list); /* Randomize. */
721 players_iterate(pplayer) {
722 if (NULL != player_startpos[player_index(pplayer)]) {
723 /* Already assigned. */
724 continue;
725 }
726
727 ptile = startpos_tile(startpos_list_front(flexible_list));
728 player_startpos[player_index(pplayer)] = ptile;
729 players_to_place--;
730 startpos_list_pop_front(flexible_list);
731 log_verbose("Start position (%d, %d) assigned randomly "
732 "to player %s (%s).", TILE_XY(ptile), player_name(pplayer),
733 nation_rule_name(nation_of_player(pplayer)));
734 if (0 == startpos_list_size(flexible_list)) {
735 break;
736 }
737 } players_iterate_end;
738 }
739
740 if (0 < players_to_place && 0 < startpos_list_size(impossible_list)) {
741 /* We still have players to place, and we have some restricted start
742 * positions whose nation requirements can't be satisfied given existing
743 * assignments. Fall back to making assignments ignoring the positions'
744 * nation requirements. */
745
746 struct tile *ptile;
747
748 log_verbose("Ignoring nation restrictions on remaining start positions.");
749
750 startpos_list_shuffle(impossible_list); /* Randomize. */
751 players_iterate(pplayer) {
752 if (NULL != player_startpos[player_index(pplayer)]) {
753 /* Already assigned. */
754 continue;
755 }
756
757 ptile = startpos_tile(startpos_list_front(impossible_list));
758 player_startpos[player_index(pplayer)] = ptile;
759 players_to_place--;
760 startpos_list_pop_front(impossible_list);
761 log_verbose("Start position (%d, %d) assigned to mismatched "
762 "player %s (%s).", TILE_XY(ptile), player_name(pplayer),
763 nation_rule_name(nation_of_player(pplayer)));
764 if (0 == startpos_list_size(impossible_list)) {
765 break;
766 }
767 } players_iterate_end;
768 }
769
770 fc_assert(0 == players_to_place);
771
772 startpos_list_destroy(impossible_list);
773 startpos_list_destroy(targeted_list);
774 startpos_list_destroy(flexible_list);
775
776 sulen = strlen(game.server.start_units);
777
778 /* Loop over all players, creating their initial units... */
779 shuffled_players_iterate(pplayer) {
780 struct tile *ptile;
781
782 /* We have to initialise the advisor and ai here as we could make contact
783 * to other nations at this point. */
784 adv_data_phase_init(pplayer, FALSE);
785 CALL_PLR_AI_FUNC(phase_begin, pplayer, pplayer, FALSE);
786
787 ptile = player_startpos[player_index(pplayer)];
788
789 fc_assert_action(NULL != ptile, continue);
790
791 /* Place first city */
792 if (game.server.start_city) {
793 create_city(pplayer, ptile, city_name_suggestion(pplayer, ptile),
794 NULL);
795 }
796
797 if (sulen > 0) {
798 /* Place the first unit. */
799 if (place_starting_unit(ptile, pplayer, NULL,
800 game.server.start_units[0]) != NULL) {
801 placed_units[player_index(pplayer)] = 1;
802 } else {
803 placed_units[player_index(pplayer)] = 0;
804 }
805 } else {
806 placed_units[player_index(pplayer)] = 0;
807 }
808 } shuffled_players_iterate_end;
809
810 /* Place all other units. */
811 shuffled_players_iterate(pplayer) {
812 int i;
813 struct tile *const ptile = player_startpos[player_index(pplayer)];
814 struct nation_type *nation = nation_of_player(pplayer);
815
816 fc_assert_action(NULL != ptile, continue);
817
818 /* Place global start units */
819 for (i = 1; i < sulen; i++) {
820 struct tile *rand_tile = find_dispersed_position(pplayer, ptile);
821
822 /* Create the unit of an appropriate type. */
823 if (rand_tile != NULL
824 && place_starting_unit(rand_tile, pplayer, NULL,
825 game.server.start_units[i]) != NULL) {
826 placed_units[player_index(pplayer)]++;
827 }
828 }
829
830 /* Place nation specific start units (not role based!) */
831 i = 0;
832 while (NULL != nation->init_units[i] && MAX_NUM_UNIT_LIST > i) {
833 struct tile *rand_tile = find_dispersed_position(pplayer, ptile);
834
835 if (rand_tile != NULL
836 && place_starting_unit(rand_tile, pplayer,
837 nation->init_units[i], '\0') != NULL) {
838 placed_units[player_index(pplayer)]++;
839 }
840 i++;
841 }
842 } shuffled_players_iterate_end;
843
844 players_iterate(pplayer) {
845 /* Close the active phase for advisor and ai for all players; it was
846 * opened in the first loop above. */
847 adv_data_phase_done(pplayer);
848 CALL_PLR_AI_FUNC(phase_finished, pplayer, pplayer);
849
850 fc_assert_msg(game.server.start_city || 0 < placed_units[player_index(pplayer)],
851 _("No units placed for %s!"), player_name(pplayer));
852 } players_iterate_end;
853 }
854
855 /**************************************************************************
856 Tell clients the year, and also update turn_done and nturns_idle fields
857 for all players.
858 **************************************************************************/
send_year_to_clients(void)859 void send_year_to_clients(void)
860 {
861 struct packet_new_year apacket;
862
863 players_iterate(pplayer) {
864 pplayer->nturns_idle++;
865 } players_iterate_end;
866
867 apacket.year32 = game.info.year32;
868 apacket.year16 = game.info.year32;
869 apacket.fragments = game.info.fragment_count;
870 apacket.turn = game.info.turn;
871 lsend_packet_new_year(game.est_connections, &apacket);
872
873 /* Hmm, clients could add this themselves based on above packet? */
874 notify_conn(game.est_connections, NULL, E_NEXT_YEAR, ftc_any,
875 _("Year: %s"), calendar_text());
876 }
877
878 /**************************************************************************
879 Send game_info packet; some server options and various stuff...
880 dest==NULL means game.est_connections
881
882 It may be sent at any time. It MUST be sent before any player info,
883 as it contains the number of players. To avoid inconsistency, it
884 SHOULD be sent after rulesets and any other server settings.
885 **************************************************************************/
send_game_info(struct conn_list * dest)886 void send_game_info(struct conn_list *dest)
887 {
888 struct packet_timeout_info tinfo;
889
890 if (!dest) {
891 dest = game.est_connections;
892 }
893
894 tinfo = game.tinfo;
895
896 /* the following values are computed every
897 time a packet_game_info packet is created */
898
899 /* Sometimes this function is called before the phase_timer is
900 * initialized. In that case we want to send the dummy value. */
901 if (current_turn_timeout() > 0 && game.server.phase_timer) {
902 /* Whenever the client sees this packet, it starts a new timer at 0;
903 * but the server's timer is only ever reset at the start of a phase
904 * (and game.tinfo.seconds_to_phasedone is relative to this).
905 * Account for the difference. */
906 tinfo.seconds_to_phasedone = game.tinfo.seconds_to_phasedone
907 - timer_read_seconds(game.server.phase_timer);
908 } else {
909 /* unused but at least initialized */
910 tinfo.seconds_to_phasedone = -1.0;
911 }
912
913 conn_list_iterate(dest, pconn) {
914 /* These are separate packets as first one may not get sent at all
915 * if there's no changes in it */
916 send_packet_game_info(pconn, &(game.info));
917 send_packet_timeout_info(pconn, &tinfo);
918 }
919 conn_list_iterate_end;
920 }
921
922 /**************************************************************************
923 Send current scenario info. dest NULL causes send to everyone
924 **************************************************************************/
send_scenario_info(struct conn_list * dest)925 void send_scenario_info(struct conn_list *dest)
926 {
927 if (!dest) {
928 dest = game.est_connections;
929 }
930
931 conn_list_iterate(dest, pconn) {
932 send_packet_scenario_info(pconn, &(game.scenario));
933 } conn_list_iterate_end;
934 }
935
936 /**************************************************************************
937 Send description of the current scenario. dest NULL causes send to everyone
938 **************************************************************************/
send_scenario_description(struct conn_list * dest)939 void send_scenario_description(struct conn_list *dest)
940 {
941 if (!dest) {
942 dest = game.est_connections;
943 }
944
945 conn_list_iterate(dest, pconn) {
946 send_packet_scenario_description(pconn, &(game.scenario_desc));
947 } conn_list_iterate_end;
948 }
949
950 /**************************************************************************
951 adjusts game.info.timeout based on various server options
952
953 timeoutint: adjust game.info.timeout every timeoutint turns
954 timeoutinc: adjust game.info.timeout by adding timeoutinc to it.
955 timeoutintinc: every time we adjust game.info.timeout, we add timeoutintinc
956 to timeoutint.
957 timeoutincmult: every time we adjust game.info.timeout, we multiply timeoutinc
958 by timeoutincmult
959 **************************************************************************/
update_timeout(void)960 int update_timeout(void)
961 {
962 /* if there's no timer or we're doing autogame, do nothing */
963 if (game.info.timeout < 1 || game.server.timeoutint == 0) {
964 return game.info.timeout;
965 }
966
967 if (game.server.timeoutcounter >= game.server.timeoutint) {
968 game.info.timeout += game.server.timeoutinc;
969 game.server.timeoutinc *= game.server.timeoutincmult;
970
971 game.server.timeoutcounter = 1;
972 game.server.timeoutint += game.server.timeoutintinc;
973
974 if (game.info.timeout > GAME_MAX_TIMEOUT) {
975 notify_conn(game.est_connections, NULL, E_SETTING, ftc_server,
976 _("The turn timeout has exceeded its maximum value, "
977 "fixing at its maximum."));
978 log_debug("game.info.timeout exceeded maximum value");
979 game.info.timeout = GAME_MAX_TIMEOUT;
980 game.server.timeoutint = 0;
981 game.server.timeoutinc = 0;
982 } else if (game.info.timeout < 0) {
983 notify_conn(game.est_connections, NULL, E_SETTING, ftc_server,
984 _("The turn timeout is smaller than zero, "
985 "fixing at zero."));
986 log_debug("game.info.timeout less than zero");
987 game.info.timeout = 0;
988 }
989 } else {
990 game.server.timeoutcounter++;
991 }
992
993 log_debug("timeout=%d, inc=%d incmult=%d\n "
994 "int=%d, intinc=%d, turns till next=%d",
995 game.info.timeout, game.server.timeoutinc,
996 game.server.timeoutincmult, game.server.timeoutint,
997 game.server.timeoutintinc,
998 game.server.timeoutint - game.server.timeoutcounter);
999
1000 return game.info.timeout;
1001 }
1002
1003 /**************************************************************************
1004 adjusts game.seconds_to_turn_done when enemy moves a unit, we see it and
1005 the remaining timeout is smaller than the timeoutaddenemymove option.
1006
1007 It's possible to use a similar function to do that per-player. In
1008 theory there should be a separate timeout for each player and the
1009 added time should only go onto the victim's timer.
1010 **************************************************************************/
increase_timeout_because_unit_moved(void)1011 void increase_timeout_because_unit_moved(void)
1012 {
1013 if (current_turn_timeout() > 0 && game.server.timeoutaddenemymove > 0) {
1014 double maxsec = (timer_read_seconds(game.server.phase_timer)
1015 + (double) game.server.timeoutaddenemymove);
1016
1017 if (maxsec > game.tinfo.seconds_to_phasedone) {
1018 game.tinfo.seconds_to_phasedone = maxsec;
1019 send_game_info(NULL);
1020 }
1021 }
1022 }
1023
1024 /**************************************************************************
1025 generate challenge filename for this connection, cannot fail.
1026 **************************************************************************/
gen_challenge_filename(struct connection * pc)1027 static void gen_challenge_filename(struct connection *pc)
1028 {
1029 }
1030
1031 /**************************************************************************
1032 get challenge filename for this connection.
1033 **************************************************************************/
get_challenge_filename(struct connection * pc)1034 static const char *get_challenge_filename(struct connection *pc)
1035 {
1036 static char filename[MAX_LEN_PATH];
1037
1038 fc_snprintf(filename, sizeof(filename), "%s_%d_%d",
1039 CHALLENGE_ROOT, srvarg.port, pc->id);
1040
1041 return filename;
1042 }
1043
1044 /**************************************************************************
1045 get challenge full filename for this connection.
1046 **************************************************************************/
get_challenge_fullname(struct connection * pc)1047 static const char *get_challenge_fullname(struct connection *pc)
1048 {
1049 static char fullname[MAX_LEN_PATH];
1050
1051 #ifdef HAIKU
1052 interpret_tilde(fullname, sizeof(fullname), "~" DIR_SEPARATOR "config" DIR_SEPARATOR "settings"
1053 DIR_SEPARATOR "freeciv" DIR_SEPARATOR);
1054 #else /* HAIKU */
1055 interpret_tilde(fullname, sizeof(fullname), "~" DIR_SEPARATOR ".freeciv" DIR_SEPARATOR);
1056 #endif /* HAIKU */
1057 sz_strlcat(fullname, get_challenge_filename(pc));
1058
1059 return fullname;
1060 }
1061
1062 /**************************************************************************
1063 find a file that we can write too, and return it's name.
1064 **************************************************************************/
new_challenge_filename(struct connection * pc)1065 const char *new_challenge_filename(struct connection *pc)
1066 {
1067 gen_challenge_filename(pc);
1068 return get_challenge_filename(pc);
1069 }
1070
1071
1072 /**************************************************************************
1073 Call this on a connection with HACK access to send it a set of ruleset
1074 choices. Probably this should be called immediately when granting
1075 HACK access to a connection.
1076 **************************************************************************/
send_ruleset_choices(struct connection * pc)1077 static void send_ruleset_choices(struct connection *pc)
1078 {
1079 struct strvec *ruleset_choices;
1080 struct packet_ruleset_choices packet;
1081 size_t i = 0;
1082
1083 ruleset_choices = get_init_script_choices();
1084
1085 strvec_iterate(ruleset_choices, s) {
1086 const int maxlen = sizeof packet.rulesets[i];
1087 if (i >= MAX_NUM_RULESETS) {
1088 log_verbose("Can't send more than %d ruleset names to client, "
1089 "skipping some", MAX_NUM_RULESETS);
1090 break;
1091 }
1092 if (fc_strlcpy(packet.rulesets[i], s, maxlen) < maxlen) {
1093 i++;
1094 } else {
1095 log_verbose("Ruleset name '%s' too long to send to client, skipped", s);
1096 }
1097 } strvec_iterate_end;
1098 packet.ruleset_count = i;
1099
1100 send_packet_ruleset_choices(pc, &packet);
1101
1102 strvec_destroy(ruleset_choices);
1103 }
1104
1105 /****************************************************************************
1106 Opens a file specified by the packet and compares the packet values with
1107 the file values. Sends an answer to the client once it's done.
1108 ****************************************************************************/
handle_single_want_hack_req(struct connection * pc,const struct packet_single_want_hack_req * packet)1109 void handle_single_want_hack_req(struct connection *pc,
1110 const struct packet_single_want_hack_req *
1111 packet)
1112 {
1113 struct section_file *secfile;
1114 const char *token = NULL;
1115 bool you_have_hack = FALSE;
1116
1117 if ((secfile = secfile_load(get_challenge_fullname(pc), FALSE))) {
1118 token = secfile_lookup_str(secfile, "challenge.token");
1119 you_have_hack = (token && strcmp(token, packet->token) == 0);
1120 secfile_destroy(secfile);
1121 } else {
1122 log_debug("Error reading '%s':\n%s", get_challenge_fullname(pc),
1123 secfile_error());
1124 }
1125
1126 if (!token) {
1127 log_debug("Failed to read authentication token");
1128 }
1129
1130 if (you_have_hack) {
1131 conn_set_access(pc, ALLOW_HACK, TRUE);
1132 }
1133
1134 dsend_packet_single_want_hack_reply(pc, you_have_hack);
1135
1136 send_ruleset_choices(pc);
1137 send_conn_info(pc->self, NULL);
1138 }
1139