"same than" -> "same as".
[freeciv.git] / server / gamehand.c
blobb25345cee151025980b5e848be79610c30f067d9
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdio.h> /* for remove() */
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"
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"
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 "unittools.h"
49 /* server/advisors */
50 #include "advdata.h"
52 #include "gamehand.h"
54 #define CHALLENGE_ROOT "challenge"
56 #define SPECLIST_TAG startpos
57 #define SPECLIST_TYPE struct startpos
58 #include "speclist.h"
59 #define startpos_list_iterate(list, plink, psp) \
60 TYPED_LIST_BOTH_ITERATE(struct startpos_list_link, struct startpos, \
61 list, plink, psp)
62 #define startpos_list_iterate_end LIST_BOTH_ITERATE_END
64 struct team_placement_config {
65 struct tile **startpos;
66 int flexible_startpos_num;
67 int usable_startpos_num;
68 int total_startpos_num;
71 struct team_placement_state {
72 int *startpos;
73 long score;
76 #define SPECPQ_TAG team_placement
77 #define SPECPQ_DATA_TYPE struct team_placement_state *
78 #define SPECPQ_PRIORITY_TYPE long
79 #include "specpq.h"
81 static struct strvec *ruleset_choices = NULL;
83 /****************************************************************************
84 Get role_id for given role character
85 ****************************************************************************/
86 enum unit_role_id crole_to_role_id(char crole)
88 switch (crole) {
89 case 'c':
90 return L_START_CITIES;
91 case 'w':
92 return L_START_WORKER;
93 case 'x':
94 return L_START_EXPLORER;
95 case 'k':
96 return L_START_KING;
97 case 's':
98 return L_START_DIPLOMAT;
99 case 'f':
100 return L_START_FERRY;
101 case 'd':
102 return L_START_DEFEND_OK;
103 case 'D':
104 return L_START_DEFEND_GOOD;
105 case 'a':
106 return L_START_ATTACK_FAST;
107 case 'A':
108 return L_START_ATTACK_STRONG;
109 default:
110 return 0;
114 /****************************************************************************
115 Get unit_type for given role character
116 ****************************************************************************/
117 struct unit_type *crole_to_unit_type(char crole, struct player *pplayer)
119 struct unit_type *utype = NULL;
120 enum unit_role_id role = crole_to_role_id(crole);
122 if (role == 0) {
123 fc_assert_ret_val(FALSE, NULL);
124 return NULL;
127 /* Create the unit of an appropriate type, if it exists */
128 if (num_role_units(role) > 0) {
129 if (pplayer != NULL) {
130 utype = first_role_unit_for_player(pplayer, role);
132 if (utype == NULL) {
133 utype = get_role_unit(role, 0);
137 return utype;
140 /****************************************************************************
141 Place a starting unit for the player. Returns tile where unit was really
142 placed.
143 ****************************************************************************/
144 static struct tile *place_starting_unit(struct tile *starttile,
145 struct player *pplayer,
146 char crole)
148 struct tile *ptile = NULL;
149 struct unit_type *utype = crole_to_unit_type(crole, pplayer);
150 bool hut_present = FALSE;
152 if (utype != NULL) {
153 iterate_outward(starttile, wld.map.xsize + wld.map.ysize, itertile) {
154 if (!is_non_allied_unit_tile(itertile, pplayer)
155 && is_native_tile(utype, itertile)) {
156 ptile = itertile;
157 break;
159 } iterate_outward_end;
162 if (ptile == NULL) {
163 /* No place where unit may exist. */
164 return NULL;
167 fc_assert_ret_val(!is_non_allied_unit_tile(ptile, pplayer), NULL);
169 /* For scenarios or dispersion, huts may coincide with player starts (in
170 * other cases, huts are avoided as start positions). Remove any such hut,
171 * and make sure to tell the client, since we may have already sent this
172 * tile (with the hut) earlier: */
173 extra_type_by_cause_iterate(EC_HUT, pextra) {
174 if (tile_has_extra(ptile, pextra)) {
175 tile_extra_rm_apply(ptile, pextra);
176 hut_present = TRUE;
178 } extra_type_by_cause_iterate_end;
180 if (hut_present) {
181 update_tile_knowledge(ptile);
182 log_verbose("Removed hut on start position for %s",
183 player_name(pplayer));
186 /* Expose visible area. */
187 map_show_circle(pplayer, ptile, game.server.init_vis_radius_sq);
189 if (utype != NULL) {
190 (void) create_unit(pplayer, ptile, utype, FALSE, 0, 0);
191 return ptile;
194 return NULL;
197 /****************************************************************************
198 Find a valid position not far from our starting position.
199 ****************************************************************************/
200 static struct tile *find_dispersed_position(struct player *pplayer,
201 struct tile *pcenter)
203 struct tile *ptile;
204 int x, y;
206 do {
207 index_to_map_pos(&x, &y, tile_index(pcenter));
208 x += fc_rand(2 * game.server.dispersion + 1) - game.server.dispersion;
209 y += fc_rand(2 * game.server.dispersion + 1) - game.server.dispersion;
210 } while (!((ptile = map_pos_to_tile(x, y))
211 && tile_continent(pcenter) == tile_continent(ptile)
212 && !is_ocean_tile(ptile)
213 && !is_non_allied_unit_tile(ptile, pplayer)));
215 return ptile;
218 /* Calculate the distance between tiles, according to the 'teamplacement'
219 * setting set to 'CLOSEST'. */
220 #define team_placement_closest sq_map_distance
222 /****************************************************************************
223 Calculate the distance between tiles, according to the 'teamplacement'
224 setting set to 'CONTINENT'.
225 ****************************************************************************/
226 static int team_placement_continent(const struct tile *ptile1,
227 const struct tile *ptile2)
229 return (ptile1->continent == ptile2->continent
230 ? sq_map_distance(ptile1, ptile2)
231 : sq_map_distance(ptile1, ptile2) + MAP_INDEX_SIZE);
234 /****************************************************************************
235 Calculate the distance between tiles, according to the 'teamplacement'
236 setting set to 'HORIZONTAL'.
237 ****************************************************************************/
238 static int team_placement_horizontal(const struct tile *ptile1,
239 const struct tile *ptile2)
241 int dx, dy;
243 map_distance_vector(&dx, &dy, ptile1, ptile2);
244 /* Map vector to natural vector (Y axis). */
245 return abs(MAP_IS_ISOMETRIC ? dx + dy : dy);
248 /****************************************************************************
249 Calculate the distance between tiles, according to the 'teamplacement'
250 setting set to 'VERTICAL'.
251 ****************************************************************************/
252 static int team_placement_vertical(const struct tile *ptile1,
253 const struct tile *ptile2)
255 int dx, dy;
257 map_distance_vector(&dx, &dy, ptile1, ptile2);
258 /* Map vector to natural vector (X axis). */
259 return abs(MAP_IS_ISOMETRIC ? dx - dy : dy);
262 /****************************************************************************
263 Destroys a team_placement_state structure.
264 ****************************************************************************/
265 static void team_placement_state_destroy(struct team_placement_state *pstate)
267 free(pstate->startpos);
268 free(pstate);
271 /****************************************************************************
272 Find the best team placement, according to the 'team_placement' setting.
273 ****************************************************************************/
274 static void do_team_placement(const struct team_placement_config *pconfig,
275 struct team_placement_state *pbest_state,
276 int iter_max)
278 const size_t state_array_size = (sizeof(*pbest_state->startpos)
279 * pconfig->total_startpos_num);
280 struct team_placement_pq *pqueue =
281 team_placement_pq_new(pconfig->total_startpos_num * 4);
282 int (*distance)(const struct tile *, const struct tile *) = NULL;
283 struct team_placement_state *pstate, *pnew;
284 const struct tile *ptile1, *ptile2;
285 long base_delta, delta;
286 bool base_delta_calculated;
287 int iter = 0;
288 bool repeat;
289 int i, j, k, t1, t2;
291 switch (wld.map.server.team_placement) {
292 case TEAM_PLACEMENT_CLOSEST:
293 distance = team_placement_closest;
294 break;
295 case TEAM_PLACEMENT_CONTINENT:
296 distance = team_placement_continent;
297 break;
298 case TEAM_PLACEMENT_HORIZONTAL:
299 distance = team_placement_horizontal;
300 break;
301 case TEAM_PLACEMENT_VERTICAL:
302 distance = team_placement_vertical;
303 break;
304 case TEAM_PLACEMENT_DISABLED:
305 break;
307 fc_assert_ret_msg(distance != NULL, "Wrong team_placement variant (%d)",
308 wld.map.server.team_placement);
310 /* Initialize starting state. */
311 pstate = fc_malloc(sizeof(*pstate));
312 pstate->startpos = fc_malloc(state_array_size);
313 memcpy(pstate->startpos, pbest_state->startpos, state_array_size);
314 pstate->score = pbest_state->score;
316 do {
317 repeat = FALSE;
318 for (i = 0; i < pconfig->usable_startpos_num; i++) {
319 t1 = pstate->startpos[i];
320 if (t1 == -1) {
321 continue; /* Not used. */
323 ptile1 = pconfig->startpos[i];
324 base_delta_calculated = FALSE;
325 for (j = i + 1; j < (i >= pconfig->flexible_startpos_num
326 ? pconfig->usable_startpos_num
327 : pconfig->flexible_startpos_num); j++) {
328 t2 = pstate->startpos[j];
329 if (t2 == -1) {
330 /* Not assigned yet. */
331 ptile2 = pconfig->startpos[j];
332 if (base_delta_calculated) {
333 delta = base_delta;
334 for (k = 0; k < pconfig->total_startpos_num; k++) {
335 if (k != i && t1 == pstate->startpos[k]) {
336 delta += distance(ptile2, pconfig->startpos[k]);
339 } else {
340 delta = 0;
341 base_delta = 0;
342 for (k = 0; k < pconfig->total_startpos_num; k++) {
343 if (k != i && t1 == pstate->startpos[k]) {
344 base_delta -= distance(ptile1, pconfig->startpos[k]);
345 delta += distance(ptile2, pconfig->startpos[k]);
348 delta += base_delta;
349 base_delta_calculated = TRUE;
351 } else if (t1 < t2) {
352 ptile2 = pconfig->startpos[j];
353 if (base_delta_calculated) {
354 delta = base_delta;
355 for (k = 0; k < pconfig->total_startpos_num; k++) {
356 if (k != i && t1 == pstate->startpos[k]) {
357 delta += distance(ptile2, pconfig->startpos[k]);
358 } else if (k != j && t2 == pstate->startpos[k]) {
359 delta -= distance(ptile2, pconfig->startpos[k]);
360 delta += distance(ptile1, pconfig->startpos[k]);
363 } else {
364 delta = 0;
365 base_delta = 0;
366 for (k = 0; k < pconfig->total_startpos_num; k++) {
367 if (k != i && t1 == pstate->startpos[k]) {
368 base_delta -= distance(ptile1, pconfig->startpos[k]);
369 delta += distance(ptile2, pconfig->startpos[k]);
370 } else if (k != j && t2 == pstate->startpos[k]) {
371 delta -= distance(ptile2, pconfig->startpos[k]);
372 delta += distance(ptile1, pconfig->startpos[k]);
375 delta += base_delta;
376 base_delta_calculated = TRUE;
378 } else {
379 continue;
382 if (delta <= 0) {
383 repeat = TRUE;
384 pnew = fc_malloc(sizeof(*pnew));
385 pnew->startpos = fc_malloc(state_array_size);
386 memcpy(pnew->startpos, pstate->startpos, state_array_size);
387 pnew->startpos[i] = t2;
388 pnew->startpos[j] = t1;
389 pnew->score = pstate->score + delta;
390 team_placement_pq_insert(pqueue, pnew, -pnew->score);
392 if (pnew->score < pbest_state->score) {
393 memcpy(pbest_state->startpos, pnew->startpos, state_array_size);
394 pbest_state->score = pnew->score;
400 team_placement_state_destroy(pstate);
401 if (iter++ >= iter_max) {
402 log_normal(_("Didn't find optimal solution for team placement "
403 "in %d iterations."), iter);
404 break;
407 } while (repeat && team_placement_pq_remove(pqueue, &pstate));
409 team_placement_pq_destroy_full(pqueue, team_placement_state_destroy);
412 /****************************************************************************
413 Initialize a new game: place the players' units onto the map, etc.
414 ****************************************************************************/
415 void init_new_game(void)
417 struct startpos_list *impossible_list, *targeted_list, *flexible_list;
418 struct tile *player_startpos[player_slot_count()];
419 int placed_units[player_slot_count()];
420 int players_to_place = player_count();
421 int sulen;
423 randomize_base64url_string(server.game_identifier,
424 sizeof(server.game_identifier));
426 /* Assign players to starting positions on the map.
427 * (In scenarios with restrictions on which nations can use which predefined
428 * start positions, this process tries to satisfy those restrictions, but
429 * does not guarantee to. Even if there is a solution to the matching
430 * problem, this algorithm may not find it.) */
432 fc_assert(player_count() <= map_startpos_count());
434 /* Convert the startposition hash table in a linked lists, as we mostly
435 * need now to iterate it now. And then, we will be able to remove the
436 * assigned start postions one by one. */
437 impossible_list = startpos_list_new();
438 targeted_list = startpos_list_new();
439 flexible_list = startpos_list_new();
441 map_startpos_iterate(psp) {
442 if (startpos_allows_all(psp)) {
443 startpos_list_append(flexible_list, psp);
444 } else {
445 startpos_list_append(targeted_list, psp);
447 } map_startpos_iterate_end;
449 fc_assert(startpos_list_size(targeted_list)
450 + startpos_list_size(flexible_list) == map_startpos_count());
452 memset(player_startpos, 0, sizeof(player_startpos));
453 log_verbose("Placing players at start positions.");
455 /* First assign start positions which have restrictions on which nations
456 * can use them. */
457 if (0 < startpos_list_size(targeted_list)) {
458 log_verbose("Assigning matching nations.");
460 startpos_list_shuffle(targeted_list); /* Randomize. */
461 do {
462 struct nation_type *pnation;
463 struct startpos_list_link *choice;
464 bool removed = FALSE;
466 /* Assign first players which can pick only one start position. */
467 players_iterate(pplayer) {
468 if (NULL != player_startpos[player_index(pplayer)]) {
469 /* Already assigned. */
470 continue;
473 pnation = nation_of_player(pplayer);
474 choice = NULL;
475 startpos_list_iterate(targeted_list, plink, psp) {
476 if (startpos_nation_allowed(psp, pnation)) {
477 if (NULL != choice) {
478 choice = NULL;
479 break; /* Many choices. */
480 } else {
481 choice = plink;
484 } startpos_list_iterate_end;
486 if (NULL != choice) {
487 /* Assign this start position to this player and remove
488 * both from consideration. */
489 struct tile *ptile =
490 startpos_tile(startpos_list_link_data(choice));
492 player_startpos[player_index(pplayer)] = ptile;
493 startpos_list_erase(targeted_list, choice);
494 players_to_place--;
495 removed = TRUE;
496 log_verbose("Start position (%d, %d) exactly matches player %s (%s).",
497 TILE_XY(ptile), player_name(pplayer),
498 nation_rule_name(pnation));
500 } players_iterate_end;
502 if (!removed) {
503 /* Didn't find any 1:1 matches. For the next restricted start
504 * position, assign a random matching player. (This may create
505 * restrictions such that more 1:1 matches are possible.) */
506 struct startpos *psp = startpos_list_back(targeted_list);
507 struct tile *ptile = startpos_tile(psp);
508 struct player *rand_plr = NULL;
509 int i = 0;
511 startpos_list_pop_back(targeted_list); /* Detach 'psp'. */
512 players_iterate(pplayer) {
513 if (NULL != player_startpos[player_index(pplayer)]) {
514 /* Already assigned. */
515 continue;
518 pnation = nation_of_player(pplayer);
519 if (startpos_nation_allowed(psp, pnation) && 0 == fc_rand(++i)) {
520 rand_plr = pplayer;
522 } players_iterate_end;
524 if (NULL != rand_plr) {
525 player_startpos[player_index(rand_plr)] = ptile;
526 players_to_place--;
527 log_verbose("Start position (%d, %d) matches player %s (%s).",
528 TILE_XY(ptile), player_name(rand_plr),
529 nation_rule_name(nation_of_player(rand_plr)));
530 } else {
531 /* This start position cannot be assigned, given the assignments
532 * made so far. We may have to fall back to mismatched
533 * assignments. */
534 log_verbose("Start position (%d, %d) cannot be assigned for "
535 "any player, keeping for the moment...",
536 TILE_XY(ptile));
537 /* Keep it for later, we may need it. */
538 startpos_list_append(impossible_list, psp);
541 } while (0 < players_to_place && 0 < startpos_list_size(targeted_list));
544 /* Now try to assign with regard to the 'teamplacement' setting. */
545 if (players_to_place > 0
546 && wld.map.server.team_placement != TEAM_PLACEMENT_DISABLED
547 && player_count() > team_count()) {
548 const struct player_list *members;
549 int team_placement_players_to_place = 0;
550 int real_team_count = 0;
552 teams_iterate(pteam) {
553 members = team_members(pteam);
554 fc_assert(0 < player_list_size(members));
555 real_team_count++;
556 if (player_list_size(members) == 1) {
557 /* Single player teams, doesn't count for team placement. */
558 continue;
560 player_list_iterate(members, pplayer) {
561 if (player_startpos[player_index(pplayer)] == NULL) {
562 team_placement_players_to_place++;
564 } player_list_iterate_end;
565 } teams_iterate_end;
567 if (real_team_count > 1 && team_placement_players_to_place > 0) {
568 /* We really can do something to improve team placement. */
569 struct team_placement_config config;
570 struct team_placement_state state;
571 int i, j, t;
573 log_verbose("Do team placement for %d players, using %s variant.",
574 team_placement_players_to_place,
575 team_placement_name(wld.map.server.team_placement));
577 /* Initialize configuration. */
578 config.flexible_startpos_num = startpos_list_size(flexible_list);
579 config.usable_startpos_num = config.flexible_startpos_num;
580 if (config.flexible_startpos_num < team_placement_players_to_place) {
581 config.usable_startpos_num += startpos_list_size(impossible_list);
583 config.total_startpos_num = (config.usable_startpos_num
584 + player_count() - players_to_place);
585 config.startpos = fc_malloc(sizeof(*config.startpos)
586 * config.total_startpos_num);
587 i = 0;
588 startpos_list_iterate(flexible_list, plink, psp) {
589 config.startpos[i++] = startpos_tile(psp);
590 } startpos_list_iterate_end;
591 fc_assert(i == config.flexible_startpos_num);
592 if (i < config.usable_startpos_num) {
593 startpos_list_iterate(impossible_list, plink, psp) {
594 config.startpos[i++] = startpos_tile(psp);
595 } startpos_list_iterate_end;
597 fc_assert(i == config.usable_startpos_num);
598 while (i < config.total_startpos_num) {
599 config.startpos[i++] = NULL;
601 fc_assert(i == config.total_startpos_num);
603 /* Initialize state. */
604 state.startpos = fc_malloc(sizeof(*state.startpos)
605 * config.total_startpos_num);
606 state.score = 0;
607 i = 0;
608 j = config.usable_startpos_num;
609 teams_iterate(pteam) {
610 members = team_members(pteam);
611 if (player_list_size(members) <= 1) {
612 /* Single player teams, doesn't count for team placement. */
613 continue;
615 t = team_number(pteam);
616 player_list_iterate(members, pplayer) {
617 struct tile *ptile = player_startpos[player_index(pplayer)];
619 if (ptile == NULL) {
620 state.startpos[i++] = t;
621 } else {
622 state.startpos[j] = t;
623 config.startpos[j] = ptile;
624 j++;
626 } player_list_iterate_end;
627 } teams_iterate_end;
628 while (i < config.usable_startpos_num) {
629 state.startpos[i++] = -1;
631 fc_assert(i == config.usable_startpos_num);
632 while (j < config.total_startpos_num) {
633 state.startpos[j++] = -1;
635 fc_assert(j == config.total_startpos_num);
637 /* Look for best team placement. */
638 do_team_placement(&config, &state, team_placement_players_to_place);
640 /* Apply result. */
641 for (i = 0; i < config.usable_startpos_num; i++) {
642 t = state.startpos[i];
643 if (t != -1) {
644 const struct team *pteam = team_by_number(t);
645 int candidate_index = -1;
646 int candidate_num = 0;
648 log_verbose("Start position (%d, %d) assigned to team %d (%s)",
649 TILE_XY(config.startpos[i]),
650 t, team_rule_name(pteam));
652 player_list_iterate(team_members(pteam), member) {
653 if (player_startpos[player_index(member)] == NULL
654 && fc_rand(++candidate_num) == 0) {
655 candidate_index = player_index(member);
657 } player_list_iterate_end;
658 fc_assert(candidate_index >= 0);
659 player_startpos[candidate_index] = config.startpos[i];
660 team_placement_players_to_place--;
661 players_to_place--;
664 fc_assert(team_placement_players_to_place == 0);
666 /* Free data. */
667 if (players_to_place > 0) {
668 /* We need to remove used startpos from the lists. */
669 i = 0;
670 startpos_list_iterate(flexible_list, plink, psp) {
671 fc_assert(config.startpos[i] == startpos_tile(psp));
672 if (state.startpos[i] != -1) {
673 startpos_list_erase(flexible_list, plink);
675 i++;
676 } startpos_list_iterate_end;
677 fc_assert(i == config.flexible_startpos_num);
678 if (i < config.usable_startpos_num) {
679 startpos_list_iterate(impossible_list, plink, psp) {
680 fc_assert(config.startpos[i] == startpos_tile(psp));
681 if (state.startpos[i] != -1) {
682 startpos_list_erase(impossible_list, plink);
684 i++;
685 } startpos_list_iterate_end;
687 fc_assert(i == config.usable_startpos_num);
690 free(config.startpos);
694 /* Now assign unrestricted start positions to any remaining players. */
695 if (0 < players_to_place && 0 < startpos_list_size(flexible_list)) {
696 struct tile *ptile;
698 log_verbose("Assigning unrestricted start positions.");
700 startpos_list_shuffle(flexible_list); /* Randomize. */
701 players_iterate(pplayer) {
702 if (NULL != player_startpos[player_index(pplayer)]) {
703 /* Already assigned. */
704 continue;
707 ptile = startpos_tile(startpos_list_front(flexible_list));
708 player_startpos[player_index(pplayer)] = ptile;
709 players_to_place--;
710 startpos_list_pop_front(flexible_list);
711 log_verbose("Start position (%d, %d) assigned randomly "
712 "to player %s (%s).", TILE_XY(ptile), player_name(pplayer),
713 nation_rule_name(nation_of_player(pplayer)));
714 if (0 == startpos_list_size(flexible_list)) {
715 break;
717 } players_iterate_end;
720 if (0 < players_to_place && 0 < startpos_list_size(impossible_list)) {
721 /* We still have players to place, and we have some restricted start
722 * positions whose nation requirements can't be satisfied given existing
723 * assignments. Fall back to making assignments ignoring the positions'
724 * nation requirements. */
726 struct tile *ptile;
728 log_verbose("Ignoring nation restrictions on remaining start positions.");
730 startpos_list_shuffle(impossible_list); /* Randomize. */
731 players_iterate(pplayer) {
732 if (NULL != player_startpos[player_index(pplayer)]) {
733 /* Already assigned. */
734 continue;
737 ptile = startpos_tile(startpos_list_front(impossible_list));
738 player_startpos[player_index(pplayer)] = ptile;
739 players_to_place--;
740 startpos_list_pop_front(impossible_list);
741 log_verbose("Start position (%d, %d) assigned to mismatched "
742 "player %s (%s).", TILE_XY(ptile), player_name(pplayer),
743 nation_rule_name(nation_of_player(pplayer)));
744 if (0 == startpos_list_size(impossible_list)) {
745 break;
747 } players_iterate_end;
750 fc_assert(0 == players_to_place);
752 startpos_list_destroy(impossible_list);
753 startpos_list_destroy(targeted_list);
754 startpos_list_destroy(flexible_list);
756 sulen = strlen(game.server.start_units);
758 /* Loop over all players, creating their initial units... */
759 players_iterate(pplayer) {
760 struct tile *ptile;
762 /* We have to initialise the advisor and ai here as we could make contact
763 * to other nations at this point. */
764 adv_data_phase_init(pplayer, FALSE);
765 CALL_PLR_AI_FUNC(phase_begin, pplayer, pplayer, FALSE);
767 ptile = player_startpos[player_index(pplayer)];
769 fc_assert_action(NULL != ptile, continue);
771 /* Place first city */
772 if (game.server.start_city) {
773 create_city(pplayer, ptile, city_name_suggestion(pplayer, ptile),
774 NULL);
777 if (sulen > 0) {
778 /* Place the first unit. */
779 if (place_starting_unit(ptile, pplayer,
780 game.server.start_units[0]) != NULL) {
781 placed_units[player_index(pplayer)] = 1;
782 } else {
783 placed_units[player_index(pplayer)] = 0;
785 } else {
786 placed_units[player_index(pplayer)] = 0;
788 } players_iterate_end;
790 /* Place all other units. */
791 players_iterate(pplayer) {
792 int i;
793 struct tile *const ptile = player_startpos[player_index(pplayer)];
794 struct nation_type *nation = nation_of_player(pplayer);
796 fc_assert_action(NULL != ptile, continue);
798 /* Place global start units */
799 for (i = 1; i < sulen; i++) {
800 struct tile *rand_tile = find_dispersed_position(pplayer, ptile);
802 /* Create the unit of an appropriate type. */
803 if (place_starting_unit(rand_tile, pplayer,
804 game.server.start_units[i]) != NULL) {
805 placed_units[player_index(pplayer)]++;
809 /* Place nation specific start units (not role based!) */
810 i = 0;
811 while (NULL != nation->init_units[i] && MAX_NUM_UNIT_LIST > i) {
812 struct tile *rand_tile = find_dispersed_position(pplayer, ptile);
814 create_unit(pplayer, rand_tile, nation->init_units[i], FALSE, 0, 0);
815 placed_units[player_index(pplayer)]++;
816 i++;
818 } players_iterate_end;
820 players_iterate(pplayer) {
821 /* Close the active phase for advisor and ai for all players; it was
822 * opened in the first loop above. */
823 adv_data_phase_done(pplayer);
824 CALL_PLR_AI_FUNC(phase_finished, pplayer, pplayer);
826 fc_assert_msg(game.server.start_city || 0 < placed_units[player_index(pplayer)],
827 _("No units placed for %s!"), player_name(pplayer));
828 } players_iterate_end;
830 shuffle_players();
833 /**************************************************************************
834 Tell clients the year, and also update turn_done and nturns_idle fields
835 for all players.
836 **************************************************************************/
837 void send_year_to_clients(void)
839 struct packet_new_year apacket;
841 players_iterate(pplayer) {
842 pplayer->nturns_idle++;
843 } players_iterate_end;
845 apacket.year = game.info.year;
846 apacket.fragments = game.info.fragment_count;
847 apacket.turn = game.info.turn;
848 lsend_packet_new_year(game.est_connections, &apacket);
850 /* Hmm, clients could add this themselves based on above packet? */
851 notify_conn(game.est_connections, NULL, E_NEXT_YEAR, ftc_any,
852 _("Year: %s"), calendar_text());
855 /**************************************************************************
856 Send game_info packet; some server options and various stuff...
857 dest == NULL means game.est_connections
859 It may be sent at any time. It MUST be sent before any player info,
860 as it contains the number of players. To avoid inconsistency, it
861 SHOULD be sent after rulesets and any other server settings.
862 **************************************************************************/
863 void send_game_info(struct conn_list *dest)
865 struct packet_timeout_info tinfo;
867 if (!dest) {
868 dest = game.est_connections;
871 tinfo = game.tinfo;
873 /* the following values are computed every
874 time a packet_game_info packet is created */
876 /* Sometimes this function is called before the phase_timer is
877 * initialized. In that case we want to send the dummy value. */
878 if (current_turn_timeout() > 0 && game.server.phase_timer) {
879 /* Whenever the client sees this packet, it starts a new timer at 0;
880 * but the server's timer is only ever reset at the start of a phase
881 * (and game.tinfo.seconds_to_phasedone is relative to this).
882 * Account for the difference. */
883 tinfo.seconds_to_phasedone = game.tinfo.seconds_to_phasedone
884 - timer_read_seconds(game.server.phase_timer);
885 } else {
886 /* unused but at least initialized */
887 tinfo.seconds_to_phasedone = -1.0;
890 conn_list_iterate(dest, pconn) {
891 /* Timeout info is separate from other packets since it has to
892 * be sent always (it's not 'is-info') while the others are 'is-info'
893 * Calendar info has been split from Game info packet to make packet
894 * size more tolerable when json protocol is in use. */
895 send_packet_game_info(pconn, &(game.info));
896 send_packet_calendar_info(pconn, &(game.calendar));
897 send_packet_timeout_info(pconn, &tinfo);
899 conn_list_iterate_end;
902 /**************************************************************************
903 Send current scenario info. dest NULL causes send to everyone
904 **************************************************************************/
905 void send_scenario_info(struct conn_list *dest)
907 if (!dest) {
908 dest = game.est_connections;
911 conn_list_iterate(dest, pconn) {
912 send_packet_scenario_info(pconn, &(game.scenario));
913 } conn_list_iterate_end;
916 /**************************************************************************
917 Send description of the current scenario. dest NULL causes send to everyone
918 **************************************************************************/
919 void send_scenario_description(struct conn_list *dest)
921 if (!dest) {
922 dest = game.est_connections;
925 conn_list_iterate(dest, pconn) {
926 send_packet_scenario_description(pconn, &(game.scenario_desc));
927 } conn_list_iterate_end;
930 /**************************************************************************
931 adjusts game.info.timeout based on various server options
933 timeoutint: adjust game.info.timeout every timeoutint turns
934 timeoutinc: adjust game.info.timeout by adding timeoutinc to it.
935 timeoutintinc: every time we adjust game.info.timeout, we add timeoutintinc
936 to timeoutint.
937 timeoutincmult: every time we adjust game.info.timeout, we multiply timeoutinc
938 by timeoutincmult
939 **************************************************************************/
940 int update_timeout(void)
942 /* if there's no timer or we're doing autogame, do nothing */
943 if (game.info.timeout < 1 || game.server.timeoutint == 0) {
944 return game.info.timeout;
947 if (game.server.timeoutcounter >= game.server.timeoutint) {
948 game.info.timeout += game.server.timeoutinc;
949 game.server.timeoutinc *= game.server.timeoutincmult;
951 game.server.timeoutcounter = 1;
952 game.server.timeoutint += game.server.timeoutintinc;
954 if (game.info.timeout > GAME_MAX_TIMEOUT) {
955 notify_conn(game.est_connections, NULL, E_SETTING, ftc_server,
956 _("The turn timeout has exceeded its maximum value, "
957 "fixing at its maximum."));
958 log_debug("game.info.timeout exceeded maximum value");
959 game.info.timeout = GAME_MAX_TIMEOUT;
960 game.server.timeoutint = 0;
961 game.server.timeoutinc = 0;
962 } else if (game.info.timeout < 0) {
963 notify_conn(game.est_connections, NULL, E_SETTING, ftc_server,
964 _("The turn timeout is smaller than zero, "
965 "fixing at zero."));
966 log_debug("game.info.timeout less than zero");
967 game.info.timeout = 0;
969 } else {
970 game.server.timeoutcounter++;
973 log_debug("timeout=%d, inc=%d incmult=%d\n "
974 "int=%d, intinc=%d, turns till next=%d",
975 game.info.timeout, game.server.timeoutinc,
976 game.server.timeoutincmult, game.server.timeoutint,
977 game.server.timeoutintinc,
978 game.server.timeoutint - game.server.timeoutcounter);
980 return game.info.timeout;
983 /**************************************************************************
984 adjusts game.seconds_to_turn_done when enemy moves a unit, we see it and
985 the remaining timeout is smaller than the timeoutaddenemymove option.
987 It's possible to use a similar function to do that per-player. In
988 theory there should be a separate timeout for each player and the
989 added time should only go onto the victim's timer.
990 **************************************************************************/
991 void increase_timeout_because_unit_moved(void)
993 if (current_turn_timeout() > 0 && game.server.timeoutaddenemymove > 0) {
994 double maxsec = (timer_read_seconds(game.server.phase_timer)
995 + (double) game.server.timeoutaddenemymove);
997 if (maxsec > game.tinfo.seconds_to_phasedone) {
998 game.tinfo.seconds_to_phasedone = maxsec;
999 send_game_info(NULL);
1004 /**************************************************************************
1005 generate challenge filename for this connection, cannot fail.
1006 **************************************************************************/
1007 static void gen_challenge_filename(struct connection *pc)
1011 /**************************************************************************
1012 get challenge filename for this connection.
1013 **************************************************************************/
1014 static const char *get_challenge_filename(struct connection *pc)
1016 static char filename[MAX_LEN_PATH];
1018 fc_snprintf(filename, sizeof(filename), "%s_%d_%d",
1019 CHALLENGE_ROOT, srvarg.port, pc->id);
1021 return filename;
1024 /**************************************************************************
1025 get challenge full filename for this connection.
1026 **************************************************************************/
1027 static const char *get_challenge_fullname(struct connection *pc)
1029 static char fullname[MAX_LEN_PATH];
1030 const char *sdir = freeciv_storage_dir();
1031 const char *cname;
1033 if (sdir == NULL) {
1034 return NULL;
1037 cname = get_challenge_filename(pc);
1039 if (cname == NULL) {
1040 return NULL;
1043 fc_snprintf(fullname, sizeof(fullname), "%s" DIR_SEPARATOR "%s", sdir, cname);
1045 return fullname;
1048 /**************************************************************************
1049 find a file that we can write too, and return it's name.
1050 **************************************************************************/
1051 const char *new_challenge_filename(struct connection *pc)
1053 gen_challenge_filename(pc);
1054 return get_challenge_filename(pc);
1058 /**************************************************************************
1059 Call this on a connection with HACK access to send it a set of ruleset
1060 choices. Probably this should be called immediately when granting
1061 HACK access to a connection.
1062 **************************************************************************/
1063 static void send_ruleset_choices(struct connection *pc)
1065 struct packet_ruleset_choices packet;
1066 size_t i;
1068 if (ruleset_choices == NULL) {
1069 /* This is only read once per server invocation. Add a new ruleset
1070 * and you have to restart the server. */
1071 ruleset_choices = fileinfolist(get_data_dirs(), RULESET_SUFFIX);
1074 packet.ruleset_count = MIN(MAX_NUM_RULESETS, strvec_size(ruleset_choices));
1075 for (i = 0; i < packet.ruleset_count; i++) {
1076 sz_strlcpy(packet.rulesets[i], strvec_get(ruleset_choices, i));
1079 send_packet_ruleset_choices(pc, &packet);
1082 /**************************************************************************
1083 Free list of ruleset choices.
1084 **************************************************************************/
1085 void ruleset_choices_free(void)
1087 if (ruleset_choices != NULL) {
1088 strvec_destroy(ruleset_choices);
1089 ruleset_choices = NULL;
1093 /****************************************************************************
1094 Opens a file specified by the packet and compares the packet values with
1095 the file values. Sends an answer to the client once it's done.
1096 ****************************************************************************/
1097 void handle_single_want_hack_req(struct connection *pc,
1098 const struct packet_single_want_hack_req *
1099 packet)
1101 struct section_file *secfile;
1102 const char *token = NULL;
1103 bool you_have_hack = FALSE;
1105 if ((secfile = secfile_load(get_challenge_fullname(pc), FALSE))) {
1106 token = secfile_lookup_str(secfile, "challenge.token");
1107 you_have_hack = (token && strcmp(token, packet->token) == 0);
1108 secfile_destroy(secfile);
1109 } else {
1110 log_debug("Error reading '%s':\n%s", get_challenge_fullname(pc),
1111 secfile_error());
1114 if (!token) {
1115 log_debug("Failed to read authentication token");
1118 if (you_have_hack) {
1119 conn_set_access(pc, ALLOW_HACK, TRUE);
1122 dsend_packet_single_want_hack_reply(pc, you_have_hack);
1124 send_ruleset_choices(pc);
1125 send_conn_info(pc->self, NULL);