--- server/srv_main.c Wed Apr 25 00:48:27 2001 +++ ../srv_main.c Wed Apr 25 00:44:10 2001 @@ -185,904 +185,574 @@ /************************************************************************** ... **************************************************************************/ -void srv_main(void) +static int is_game_over(void) { + int barbs = 0; + int alive = 0; int i; - int save_counter = 0; - struct timer *eot_timer; /* time server processing at end-of-turn */ - /* make sure it's initialized */ - if (!has_been_srv_init) { - srv_init(); - } + if (game.year > game.end_year) + return 1; - my_init_network(); + for (i=0;ix, pcity->y, 0); + city_list_iterate_end; + } + } else { + map_know_all(pplayer); + send_all_known_tiles(&pplayer->connections); + send_all_known_cities(&pplayer->connections); + } + } +} - if (srvarg.script_filename) - read_init_script(srvarg.script_filename); +/************************************************************************** +... +**************************************************************************/ +static void marco_polo_make_contact(void) +{ + int cityid = game.global_wonders[B_MARCO]; + int o; + struct city *pcity; + if (cityid && (pcity = find_city_by_id(cityid))) + for (o = 0; o < game.nplayers; o++) + make_contact(pcity->owner, o, pcity->x, pcity->y); +} - /* load a saved game */ - - if(srvarg.load_filename) { - struct timer *loadtimer, *uloadtimer; - struct section_file file; - - freelog(LOG_NORMAL, _("Loading saved game: %s"), srvarg.load_filename); - loadtimer = new_timer_start(TIMER_CPU, TIMER_ACTIVE); - uloadtimer = new_timer_start(TIMER_USER, TIMER_ACTIVE); - if(!section_file_load_nodup(&file, srvarg.load_filename)) { - freelog(LOG_FATAL, _("Couldn't load savefile: %s"), srvarg.load_filename); - exit(1); - } - game_load(&file); - section_file_check_unused(&file, srvarg.load_filename); - section_file_free(&file); +/************************************************************************** +... +**************************************************************************/ +static void update_environmental_upset(enum tile_special_type cause, + int *current, int *accum, int *level, + void (*upset_action_fn)(int)) +{ + int x, y, count; - if (!game.is_new_game) { - /* Is this right/necessary/the_right_place_for_this/etc? --dwp */ - while(is_id_allocated(global_id_counter++)); + count = 0; + for (x = 0; x < map.xsize; x++) { + for (y = 0; y < map.ysize; y++) { + if (map_get_special(x,y) & cause) { + count++; + } } - freelog(LOG_VERBOSE, "Load time: %g seconds (%g apparent)", - read_timer_seconds_free(loadtimer), - read_timer_seconds_free(uloadtimer)); } - /* init network */ - init_connections(); - server_open_socket(); - if(!(srvarg.metaserver_no_send)) { - freelog(LOG_NORMAL, _("Sending info to metaserver [%s]"), - meta_addr_port()); - server_open_udp(); /* open socket for meta server */ + *current = count; + *accum += count; + if (*accum < *level) { + *accum = 0; + } else { + *accum -= *level; + if (myrand(200) <= *accum) { + upset_action_fn((map.xsize / 10) + (map.ysize / 10) + ((*accum) * 5)); + *accum = 0; + *level+=4; + send_all_known_tiles(&game.game_connections); + } } - send_server_info_to_metaserver(1,0); - - /* accept new players, wait for serverop to start..*/ - freelog(LOG_NORMAL, _("Now accepting new client connections.")); - server_state=PRE_GAME_STATE; + freelog(LOG_DEBUG, + "environmental_upset: cause=%-4d current=%-2d level=%-2d accum=%-2d", + cause, *current, *level, *accum); +} - while(server_state==PRE_GAME_STATE) - sniff_packets(); +/************************************************************************** + check for cease-fires running out; update reputation; update cancelling + reasons +**************************************************************************/ +static void update_diplomatics(void) +{ + int p, p2; - send_server_info_to_metaserver(1,0); + for(p = 0; p < game.nplayers; p++) { + for(p2 = 0; p2 < game.nplayers; p2++) { + game.players[p].diplstates[p2].has_reason_to_cancel = + MAX(game.players[p].diplstates[p2].has_reason_to_cancel - 1, 0); - if(game.is_new_game) { - load_rulesets(); - /* otherwise rulesets were loaded when savegame was loaded */ + if(game.players[p].diplstates[p2].type == DS_CEASEFIRE) { + switch(--game.players[p].diplstates[p2].turns_left) { + case 1: + notify_player(&game.players[p], + _("Game: Concerned citizens point " + "out that the cease-fire with %s will run out soon."), + game.players[p2].name); + break; + case -1: + notify_player(&game.players[p], + _("Game: The cease-fire with %s has " + "run out. You are now neutral towards the %s."), + game.players[p2].name, + get_nation_name_plural(game.players[p2].nation)); + game.players[p].diplstates[p2].type = DS_NEUTRAL; + break; + } + } + game.players[p].reputation = + MIN(game.players[p].reputation + GAME_REPUTATION_INCR, + GAME_MAX_REPUTATION); + } } +} - nations_avail = fc_calloc(game.playable_nation_count, sizeof(int)); - nations_used = fc_calloc(game.playable_nation_count, sizeof(int)); - -main_start_players: +/************************************************************************** + Send packet which tells clients that the server is starting its + "end year" calculations (and will be sending end-turn updates etc). + (This is referred to as "before new year" in packet and client code.) +**************************************************************************/ +static void before_end_year(void) +{ + lsend_packet_generic_empty(&game.est_connections, PACKET_BEFORE_NEW_YEAR); +} - send_rulesets(&game.est_connections); +/************************************************************************** +... +**************************************************************************/ +static void ai_start_turn(void) +{ + int i; - num_nations_avail = game.playable_nation_count; - for(i=0; iai.control) { + ai_do_first_activities(pplayer); + flush_packets(); /* AIs can be such spammers... */ + } } +} - if (game.auto_ai_toggle) { - for (i=0;iis_connected && !pplayer->ai.control) { - toggle_ai_player_direct(NULL, pplayer); - } +/************************************************************************** +Handle the beginning of each turn. +Note: This does not give "time" to any player; + it is solely for updating turn-dependent data. +**************************************************************************/ +static void begin_turn(void) +{ + int i; + + /* See if the value of fog of war has changed */ + if (game.fogofwar != game.fogofwar_old) { + if (game.fogofwar == 1) { + enable_fog_of_war(); + game.fogofwar_old = 1; + } else { + disable_fog_of_war(); + game.fogofwar_old = 0; } } - /* Allow players to select a nation (case new game). - * AI players may not yet have a nation; these will be selected - * in generate_ai_players() later - */ - server_state = RUN_GAME_STATE; + conn_list_do_buffer(&game.game_connections); + for(i=0; iname); + begin_player_turn(pplayer); } - - while(server_state==SELECT_RACES_STATE) { - sniff_packets(); - for(i=0; i0) { - server_state=RUN_GAME_STATE; - } else { - con_write(C_COMMENT, - _("Last player has disconnected: will need to restart.")); - server_state=PRE_GAME_STATE; - while(server_state==PRE_GAME_STATE) { - sniff_packets(); - } - goto main_start_players; - } - } + for (i=0; i> 1); - } - - if(!myrand_is_init()) - mysrand(game.randseed); + conn_list_do_unbuffer(&game.game_connections); +} -#ifdef TEST_RANDOM /* not defined anywhere, set it if you want it */ - test_random1(200); - test_random1(2000); - test_random1(20000); - test_random1(200000); -#endif - - if(game.is_new_game) - generate_ai_players(); - - /* if we have a tile map, and map.generator==0, call map_fractal_generate - anyway, to make the specials and huts */ - if(map_is_empty() || (map.generator == 0 && game.is_new_game)) - map_fractal_generate(); +/************************************************************************** +... +**************************************************************************/ +static int end_turn(void) +{ + int i; - if (map.num_continents==0) - assign_continent_numbers(); + nocity_send = 1; + for(i=0; iname); + update_player_activities(pplayer); + /* ai unit activity has been moved UP -- Syela */ - gamelog_map(); - /* start the game */ + flush_packets(); + /* update_player_activities calls update_unit_activities which + causes *major* network traffic */ + pplayer->turn_done=0; + } + nocity_send = 0; + for (i=0; ieconomic.gold=game.gold; - } - game.max_players=game.nplayers; +Note that if !HAVE_LIBZ, then game.save_compress_level should never +become non-zero, so no need to check HAVE_LIBZ explicitly here as well. +**************************************************************************/ +void save_game(char *orig_filename) +{ + char filename[600]; + struct section_file file; + struct timer *timer_cpu, *timer_user; - /* we don't want random start positions in a scenario which already - provides them. -- Gudy */ - if(map.num_start_positions==0) { - create_start_positions(); - } + if (orig_filename != NULL && orig_filename[0] != '\0'){ + sz_strlcpy(filename, orig_filename); + } else { /* If orig_filename is NULL or empty, use "civgamem.sav" */ + my_snprintf(filename, sizeof(filename), + "%s%dm.sav", game.save_name, game.year); } + + timer_cpu = new_timer_start(TIMER_CPU, TIMER_ACTIVE); + timer_user = new_timer_start(TIMER_USER, TIMER_ACTIVE); + + section_file_init(&file); + game_save(&file); - initialize_move_costs(); /* this may be the wrong place to do this */ - generate_minimap(); /* for city_desire; saves a lot of calculations */ - - if (!game.is_new_game) { - for (i=0;iai.control) { - set_ai_level_direct(pplayer, pplayer->ai.skill_level); - assess_danger_player(pplayer); /* a slowdown, but a necessary one */ - } + if (game.save_compress_level > 0) { + /* Append ".gz" to filename if not there: */ + size_t len = strlen(filename); + if (len < 3 || strcmp(filename+len-3, ".gz") != 0) { + sz_strlcat(filename, ".gz"); } } - - send_all_info(&game.game_connections); - - if(game.is_new_game) - init_new_game(); - - game.is_new_game = 0; - - send_game_state(&game.est_connections, CLIENT_GAME_RUNNING_STATE); - while(server_state==RUN_GAME_STATE) { - /* absolute beginning of a turn */ - freelog(LOG_DEBUG, "Begin turn"); - begin_turn(); + if(!section_file_save(&file, filename, game.save_compress_level)) + con_write(C_FAIL, _("Failed saving game as %s"), filename); + else + con_write(C_OK, _("Game saved as %s"), filename); -#if (IS_DEVEL_VERSION || IS_BETA_VERSION) - sanity_check(); -#endif + section_file_free(&file); - force_end_of_sniff=0; + freelog(LOG_VERBOSE, "Save time: %g seconds (%g apparent)", + read_timer_seconds_free(timer_cpu), + read_timer_seconds_free(timer_user)); +} - freelog(LOG_DEBUG, "Shuffleplayers"); - shuffle_players(); - freelog(LOG_DEBUG, "Aistartturn"); - ai_start_turn(); +/************************************************************************** +Save game with autosave filename, and call gamelog_save(). +**************************************************************************/ +static void save_game_auto(void) +{ + char filename[512]; - /* Before sniff (human player activites), report time to now: */ - freelog(LOG_VERBOSE, "End/start-turn server/ai activities: %g seconds", - read_timer_seconds(eot_timer)); + assert(strlen(game.save_name)<256); + + my_snprintf(filename, sizeof(filename), + "%s%d.sav", game.save_name, game.year); + save_game(filename); + gamelog_save(); /* should this be in save_game()? --dwp */ +} - /* Do auto-saves just before starting sniff_packets(), so that - * autosave happens effectively "at the same time" as manual - * saves, from the point of view of restarting and AI players. - * Post-increment so we don't count the first loop. - */ - if(save_counter >= game.save_nturns && game.save_nturns>0) { - save_counter=0; - save_game_auto(); - } - save_counter++; - - freelog(LOG_DEBUG, "sniffingpackets"); - while(sniff_packets()==1); +/************************************************************************** +... +**************************************************************************/ +void start_game(void) +{ + if(server_state!=PRE_GAME_STATE) { + con_puts(C_SYNTAX, _("The game is already running.")); + return; + } - /* After sniff, re-zero the timer: (read-out above on next loop) */ - clear_timer_start(eot_timer); - - conn_list_do_buffer(&game.game_connections); + con_puts(C_OK, _("Starting game.")); -#if (IS_DEVEL_VERSION || IS_BETA_VERSION) - sanity_check(); -#endif - - before_end_year(); - /* This empties the client Messages window; put this before - everything else below, since otherwise any messages from - the following parts get wiped out before the user gets a - chance to see them. --dwp - */ - freelog(LOG_DEBUG, "Season of native unrests"); - summon_barbarians(); /* wild guess really, no idea where to put it, but - I want to give them chance to move their units */ - freelog(LOG_DEBUG, "Autosettlers"); - auto_settlers(); /* moved this after ai_start_turn for efficiency -- Syela */ - /* moved after sniff_packets for even more efficiency. - What a guy I am. -- Syela */ - /* and now, we must manage our remaining units BEFORE the cities that are - empty get to refresh and defend themselves. How totally stupid. */ - ai_start_turn(); /* Misleading name for manage_units -- Syela */ - freelog(LOG_DEBUG, "Auto-Attack phase"); - auto_attack(); - freelog(LOG_DEBUG, "Endturn"); - end_turn(); - freelog(LOG_DEBUG, "Gamenextyear"); - game_advance_year(); - check_spaceship_arrivals(); - freelog(LOG_DEBUG, "Sendplayerinfo"); - send_player_info(0, 0); - freelog(LOG_DEBUG, "Sendgameinfo"); - send_game_info(0); - freelog(LOG_DEBUG, "Sendyeartoclients"); - send_year_to_clients(game.year); - freelog(LOG_DEBUG, "Sendinfotometaserver"); - send_server_info_to_metaserver(0,0); - - conn_list_do_unbuffer(&game.game_connections); - - if (is_game_over()) - server_state=GAME_OVER_STATE; - } - - report_scores(1); - show_map_to_all(); - notify_player(0, _("Game: The game is over...")); - - while(server_state==GAME_OVER_STATE) { - force_end_of_sniff=0; - sniff_packets(); - } - - server_close_udp(); - - my_shutdown_network(); - - return; -} + server_state=SELECT_RACES_STATE; /* loaded ??? */ + force_end_of_sniff=1; + game.turn_start = time(NULL); +} /************************************************************************** ... **************************************************************************/ -static int is_game_over(void) +static void handle_report_request(struct connection *pconn, + enum report_type type) { - int barbs = 0; - int alive = 0; - int i; - - if (game.year > game.end_year) - return 1; - - for (i=0;iself; + + switch(type) { + case REPORT_WONDERS_OF_THE_WORLD: + report_wonders_of_the_world(dest); + break; + case REPORT_TOP_5_CITIES: + report_top_five_cities(dest); + break; + case REPORT_DEMOGRAPHIC: + report_demographics(pconn); + break; + case REPORT_SERVER_OPTIONS1: + report_server_options(dest, 1); + break; + case REPORT_SERVER_OPTIONS2: + report_server_options(dest, 2); + break; + case REPORT_SERVER_OPTIONS: /* obsolete */ + default: + notify_conn(dest, "Game: request for unknown report (type %d)", type); } - return (alive <= 1); } /************************************************************************** - Send all information for when game starts or client reconnects. - Ruleset information should have been sent before this. +... **************************************************************************/ -static void send_all_info(struct conn_list *dest) +void dealloc_id(int id) { - send_game_info(dest); - send_map_info(dest); - send_player_info_c(0, dest); - send_conn_info(&game.est_connections, dest); - send_spaceship_info(0, dest); - send_all_known_tiles(dest); - send_all_known_cities(dest); - send_all_known_units(dest); - send_player_turn_notifications(dest); + used_ids[id/8]&= 0xff ^ (1<<(id%8)); } /************************************************************************** ... **************************************************************************/ -static void do_apollo_program(void) +int is_id_allocated(int id) { - int cityid; - int i; - struct player *pplayer; - struct city *pcity; - if ((cityid=game.global_wonders[B_APOLLO]) && - (pcity=find_city_by_id(cityid))) { - pplayer=city_owner(pcity); - if (game.civstyle == 1) { - for(i=0; ix, pcity->y, 0); - city_list_iterate_end; - } - } else { - map_know_all(pplayer); - send_all_known_tiles(&pplayer->connections); - send_all_known_cities(&pplayer->connections); - } - } + return (used_ids[id/8])&(1<<(id%8)); } /************************************************************************** ... **************************************************************************/ -static void marco_polo_make_contact(void) +void alloc_id(int id) { - int cityid = game.global_wonders[B_MARCO]; - int o; - struct city *pcity; - if (cityid && (pcity = find_city_by_id(cityid))) - for (o = 0; o < game.nplayers; o++) - make_contact(pcity->owner, o, pcity->x, pcity->y); + used_ids[id/8]|= (1<<(id%8)); } /************************************************************************** ... **************************************************************************/ -static void update_environmental_upset(enum tile_special_type cause, - int *current, int *accum, int *level, - void (*upset_action_fn)(int)) -{ - int x, y, count; - - count = 0; - for (x = 0; x < map.xsize; x++) { - for (y = 0; y < map.ysize; y++) { - if (map_get_special(x,y) & cause) { - count++; - } - } - } - - *current = count; - *accum += count; - if (*accum < *level) { - *accum = 0; - } else { - *accum -= *level; - if (myrand(200) <= *accum) { - upset_action_fn((map.xsize / 10) + (map.ysize / 10) + ((*accum) * 5)); - *accum = 0; - *level+=4; - send_all_known_tiles(&game.game_connections); - } - } - freelog(LOG_DEBUG, - "environmental_upset: cause=%-4d current=%-2d level=%-2d accum=%-2d", - cause, *current, *level, *accum); +int get_next_id_number(void) +{ + while(is_id_allocated(++global_id_counter) || !global_id_counter) ; + return global_id_counter; } /************************************************************************** - check for cease-fires running out; update reputation; update cancelling - reasons +... **************************************************************************/ -static void update_diplomatics(void) +void handle_packet_input(struct connection *pconn, char *packet, int type) { - int p, p2; + struct player *pplayer; - for(p = 0; p < game.nplayers; p++) { - for(p2 = 0; p2 < game.nplayers; p2++) { - game.players[p].diplstates[p2].has_reason_to_cancel = - MAX(game.players[p].diplstates[p2].has_reason_to_cancel - 1, 0); + /* a NULL packet can be returned from receive_packet_goto_route() */ + if (packet == NULL) + return; - if(game.players[p].diplstates[p2].type == DS_CEASEFIRE) { - switch(--game.players[p].diplstates[p2].turns_left) { - case 1: - notify_player(&game.players[p], - _("Game: Concerned citizens point " - "out that the cease-fire with %s will run out soon."), - game.players[p2].name); - break; - case -1: - notify_player(&game.players[p], - _("Game: The cease-fire with %s has " - "run out. You are now neutral towards the %s."), - game.players[p2].name, - get_nation_name_plural(game.players[p2].nation)); - game.players[p].diplstates[p2].type = DS_NEUTRAL; - break; - } - } - game.players[p].reputation = - MIN(game.players[p].reputation + GAME_REPUTATION_INCR, - GAME_MAX_REPUTATION); - } + switch(type) { + case PACKET_REQUEST_JOIN_GAME: + handle_request_join_game(pconn, (struct packet_req_join_game *)packet); + free(packet); + return; + break; } -} -/************************************************************************** - Send packet which tells clients that the server is starting its - "end year" calculations (and will be sending end-turn updates etc). - (This is referred to as "before new year" in packet and client code.) -**************************************************************************/ -static void before_end_year(void) -{ - lsend_packet_generic_empty(&game.est_connections, PACKET_BEFORE_NEW_YEAR); -} + if (!pconn->established) { + freelog(LOG_ERROR, "Received game packet from unaccepted connection %s", + conn_description(pconn)); + free(packet); + return; + } + + pplayer = pconn->player; -/************************************************************************** -... -**************************************************************************/ -static void ai_start_turn(void) -{ - int i; + if(pplayer == NULL) { + /* don't support these yet */ + freelog(LOG_ERROR, "Received packet from non-player connection %s", + conn_description(pconn)); + free(packet); + return; + } - for (i = 0; i < game.nplayers; i++) { - struct player *pplayer = shuffled_player(i); - if (pplayer->ai.control) { - ai_do_first_activities(pplayer); - flush_packets(); /* AIs can be such spammers... */ - } + if (server_state != RUN_GAME_STATE + && type != PACKET_ALLOC_NATION + && type != PACKET_CHAT_MSG + && type != PACKET_CONN_PONG) { + freelog(LOG_ERROR, _("got a packet of type %d " + "outside RUN_GAME_STATE"), type); + free(packet); + return; } -} -/************************************************************************** -Handle the beginning of each turn. -Note: This does not give "time" to any player; - it is solely for updating turn-dependent data. -**************************************************************************/ -static void begin_turn(void) -{ - int i; + pplayer->nturns_idle=0; - /* See if the value of fog of war has changed */ - if (game.fogofwar != game.fogofwar_old) { - if (game.fogofwar == 1) { - enable_fog_of_war(); - game.fogofwar_old = 1; - } else { - disable_fog_of_war(); - game.fogofwar_old = 0; - } + if((!pplayer->is_alive || pconn->observer) + && !(type == PACKET_CHAT_MSG || type == PACKET_REPORT_REQUEST + || type == PACKET_CONN_PONG)) { + freelog(LOG_ERROR, _("Got a packet of type %d from a " + "dead or observer player"), type); + free(packet); + return; } + + /* Make sure to set this back to NULL before leaving this function: */ + pplayer->current_conn = pconn; + + switch(type) { + + case PACKET_TURN_DONE: + handle_turn_done(pplayer); + break; - conn_list_do_buffer(&game.game_connections); + case PACKET_ALLOC_NATION: + handle_alloc_nation(pplayer, (struct packet_alloc_nation *)packet); + break; - for(i=0; iname); - begin_player_turn(pplayer); - } - for (i=0; iname); - update_player_activities(pplayer); - /* ai unit activity has been moved UP -- Syela */ + case PACKET_CITY_BUY: + handle_city_buy(pplayer, (struct packet_city_request *)packet); + break; + + case PACKET_CITY_CHANGE: + handle_city_change(pplayer, (struct packet_city_request *)packet); + break; - flush_packets(); - /* update_player_activities calls update_unit_activities which - causes *major* network traffic */ - pplayer->turn_done=0; - } - nocity_send = 0; - for (i=0; im.sav" */ - my_snprintf(filename, sizeof(filename), - "%s%dm.sav", game.save_name, game.year); - } - - timer_cpu = new_timer_start(TIMER_CPU, TIMER_ACTIVE); - timer_user = new_timer_start(TIMER_USER, TIMER_ACTIVE); - - section_file_init(&file); - game_save(&file); + case PACKET_CITY_RENAME: + handle_city_rename(pplayer, (struct packet_city_request *)packet); + break; - if (game.save_compress_level > 0) { - /* Append ".gz" to filename if not there: */ - size_t len = strlen(filename); - if (len < 3 || strcmp(filename+len-3, ".gz") != 0) { - sz_strlcat(filename, ".gz"); - } - } + case PACKET_PLAYER_RATES: + handle_player_rates(pplayer, (struct packet_player_request *)packet); + break; - if(!section_file_save(&file, filename, game.save_compress_level)) - con_write(C_FAIL, _("Failed saving game as %s"), filename); - else - con_write(C_OK, _("Game saved as %s"), filename); + case PACKET_PLAYER_REVOLUTION: + handle_player_revolution(pplayer); + break; - section_file_free(&file); + case PACKET_PLAYER_GOVERNMENT: + handle_player_government(pplayer, (struct packet_player_request *)packet); + break; - freelog(LOG_VERBOSE, "Save time: %g seconds (%g apparent)", - read_timer_seconds_free(timer_cpu), - read_timer_seconds_free(timer_user)); -} + case PACKET_PLAYER_RESEARCH: + handle_player_research(pplayer, (struct packet_player_request *)packet); + break; -/************************************************************************** -Save game with autosave filename, and call gamelog_save(). -**************************************************************************/ -static void save_game_auto(void) -{ - char filename[512]; + case PACKET_PLAYER_TECH_GOAL: + handle_player_tech_goal(pplayer, (struct packet_player_request *)packet); + break; - assert(strlen(game.save_name)<256); - - my_snprintf(filename, sizeof(filename), - "%s%d.sav", game.save_name, game.year); - save_game(filename); - gamelog_save(); /* should this be in save_game()? --dwp */ -} + case PACKET_PLAYER_WORKLIST: + handle_player_worklist(pplayer, (struct packet_player_request *)packet); + break; -/************************************************************************** -... -**************************************************************************/ -void start_game(void) -{ - if(server_state!=PRE_GAME_STATE) { - con_puts(C_SYNTAX, _("The game is already running.")); - return; - } + case PACKET_UNIT_BUILD_CITY: + handle_unit_build_city(pplayer, (struct packet_unit_request *)packet); + break; - con_puts(C_OK, _("Starting game.")); + case PACKET_UNIT_DISBAND: + handle_unit_disband(pplayer, (struct packet_unit_request *)packet); + break; - server_state=SELECT_RACES_STATE; /* loaded ??? */ - force_end_of_sniff=1; - game.turn_start = time(NULL); -} + case PACKET_UNIT_CHANGE_HOMECITY: + handle_unit_change_homecity(pplayer, (struct packet_unit_request *)packet); + break; -/************************************************************************** -... -**************************************************************************/ -static void handle_report_request(struct connection *pconn, - enum report_type type) -{ - struct conn_list *dest = &pconn->self; - - switch(type) { - case REPORT_WONDERS_OF_THE_WORLD: - report_wonders_of_the_world(dest); + case PACKET_UNIT_AUTO: + handle_unit_auto_request(pplayer, (struct packet_unit_request *)packet); break; - case REPORT_TOP_5_CITIES: - report_top_five_cities(dest); + + case PACKET_UNIT_UNLOAD: + handle_unit_unload_request(pplayer, (struct packet_unit_request *)packet); break; - case REPORT_DEMOGRAPHIC: - report_demographics(pconn); + + case PACKET_UNITTYPE_UPGRADE: + handle_upgrade_unittype_request(pplayer, (struct packet_unittype_info *)packet); break; - case REPORT_SERVER_OPTIONS1: - report_server_options(dest, 1); + + case PACKET_UNIT_ESTABLISH_TRADE: + handle_unit_establish_trade(pplayer, (struct packet_unit_request *)packet); break; - case REPORT_SERVER_OPTIONS2: - report_server_options(dest, 2); - break; - case REPORT_SERVER_OPTIONS: /* obsolete */ - default: - notify_conn(dest, "Game: request for unknown report (type %d)", type); - } -} - -/************************************************************************** -... -**************************************************************************/ -void dealloc_id(int id) -{ - used_ids[id/8]&= 0xff ^ (1<<(id%8)); -} - -/************************************************************************** -... -**************************************************************************/ -int is_id_allocated(int id) -{ - return (used_ids[id/8])&(1<<(id%8)); -} - -/************************************************************************** -... -**************************************************************************/ -void alloc_id(int id) -{ - used_ids[id/8]|= (1<<(id%8)); -} - -/************************************************************************** -... -**************************************************************************/ - -int get_next_id_number(void) -{ - while(is_id_allocated(++global_id_counter) || !global_id_counter) ; - return global_id_counter; -} - -/************************************************************************** -... -**************************************************************************/ -void handle_packet_input(struct connection *pconn, char *packet, int type) -{ - struct player *pplayer; - - /* a NULL packet can be returned from receive_packet_goto_route() */ - if (packet == NULL) - return; - - switch(type) { - case PACKET_REQUEST_JOIN_GAME: - handle_request_join_game(pconn, (struct packet_req_join_game *)packet); - free(packet); - return; - break; - } - - if (!pconn->established) { - freelog(LOG_ERROR, "Received game packet from unaccepted connection %s", - conn_description(pconn)); - free(packet); - return; - } - - pplayer = pconn->player; - - if(pplayer == NULL) { - /* don't support these yet */ - freelog(LOG_ERROR, "Received packet from non-player connection %s", - conn_description(pconn)); - free(packet); - return; - } - - if (server_state != RUN_GAME_STATE - && type != PACKET_ALLOC_NATION - && type != PACKET_CHAT_MSG - && type != PACKET_CONN_PONG) { - freelog(LOG_ERROR, _("got a packet of type %d " - "outside RUN_GAME_STATE"), type); - free(packet); - return; - } - - pplayer->nturns_idle=0; - - if((!pplayer->is_alive || pconn->observer) - && !(type == PACKET_CHAT_MSG || type == PACKET_REPORT_REQUEST - || type == PACKET_CONN_PONG)) { - freelog(LOG_ERROR, _("Got a packet of type %d from a " - "dead or observer player"), type); - free(packet); - return; - } - - /* Make sure to set this back to NULL before leaving this function: */ - pplayer->current_conn = pconn; - - switch(type) { - - case PACKET_TURN_DONE: - handle_turn_done(pplayer); - break; - - case PACKET_ALLOC_NATION: - handle_alloc_nation(pplayer, (struct packet_alloc_nation *)packet); - break; - - case PACKET_UNIT_INFO: - handle_unit_info(pplayer, (struct packet_unit_info *)packet); - break; - - case PACKET_MOVE_UNIT: - handle_move_unit(pplayer, (struct packet_move_unit *)packet); - break; - - case PACKET_CHAT_MSG: - handle_chat_msg(pconn, (struct packet_generic_message *)packet); - break; - - case PACKET_CITY_SELL: - handle_city_sell(pplayer, (struct packet_city_request *)packet); - break; - - case PACKET_CITY_BUY: - handle_city_buy(pplayer, (struct packet_city_request *)packet); - break; - - case PACKET_CITY_CHANGE: - handle_city_change(pplayer, (struct packet_city_request *)packet); - break; - - case PACKET_CITY_WORKLIST: - handle_city_worklist(pplayer, (struct packet_city_request *)packet); - break; - - case PACKET_CITY_MAKE_SPECIALIST: - handle_city_make_specialist(pplayer, (struct packet_city_request *)packet); - break; - - case PACKET_CITY_MAKE_WORKER: - handle_city_make_worker(pplayer, (struct packet_city_request *)packet); - break; - - case PACKET_CITY_CHANGE_SPECIALIST: - handle_city_change_specialist(pplayer, (struct packet_city_request *)packet); - break; - - case PACKET_CITY_RENAME: - handle_city_rename(pplayer, (struct packet_city_request *)packet); - break; - - case PACKET_PLAYER_RATES: - handle_player_rates(pplayer, (struct packet_player_request *)packet); - break; - - case PACKET_PLAYER_REVOLUTION: - handle_player_revolution(pplayer); - break; - - case PACKET_PLAYER_GOVERNMENT: - handle_player_government(pplayer, (struct packet_player_request *)packet); - break; - - case PACKET_PLAYER_RESEARCH: - handle_player_research(pplayer, (struct packet_player_request *)packet); - break; - - case PACKET_PLAYER_TECH_GOAL: - handle_player_tech_goal(pplayer, (struct packet_player_request *)packet); - break; - - case PACKET_PLAYER_WORKLIST: - handle_player_worklist(pplayer, (struct packet_player_request *)packet); - break; - - case PACKET_UNIT_BUILD_CITY: - handle_unit_build_city(pplayer, (struct packet_unit_request *)packet); - break; - - case PACKET_UNIT_DISBAND: - handle_unit_disband(pplayer, (struct packet_unit_request *)packet); - break; - - case PACKET_UNIT_CHANGE_HOMECITY: - handle_unit_change_homecity(pplayer, (struct packet_unit_request *)packet); - break; - - case PACKET_UNIT_AUTO: - handle_unit_auto_request(pplayer, (struct packet_unit_request *)packet); - break; - - case PACKET_UNIT_UNLOAD: - handle_unit_unload_request(pplayer, (struct packet_unit_request *)packet); - break; - - case PACKET_UNITTYPE_UPGRADE: - handle_upgrade_unittype_request(pplayer, (struct packet_unittype_info *)packet); - break; - - case PACKET_UNIT_ESTABLISH_TRADE: - handle_unit_establish_trade(pplayer, (struct packet_unit_request *)packet); - break; - - case PACKET_UNIT_HELP_BUILD_WONDER: - handle_unit_help_build_wonder(pplayer, (struct packet_unit_request *)packet); + + case PACKET_UNIT_HELP_BUILD_WONDER: + handle_unit_help_build_wonder(pplayer, (struct packet_unit_request *)packet); break; case PACKET_UNIT_GOTO_TILE: @@ -1809,153 +1479,485 @@ continue; } - for (nation = 0; nation < game.playable_nation_count; nation++) { - if (check_nation_leader_name(nation, pplayer->name)) { - if (nations_used[nation] != -1) { - pplayer->nation = mark_nation_as_used(nation); - pplayer->city_style = get_nation_city_style(nation); - pplayer->is_male = get_nation_leader_sex(nation, pplayer->name); - break; - } - } - } + for (nation = 0; nation < game.playable_nation_count; nation++) { + if (check_nation_leader_name(nation, pplayer->name)) { + if (nations_used[nation] != -1) { + pplayer->nation = mark_nation_as_used(nation); + pplayer->city_style = get_nation_city_style(nation); + pplayer->is_male = get_nation_leader_sex(nation, pplayer->name); + break; + } + } + } + + if (nation == game.playable_nation_count) { + pplayer->nation = + mark_nation_as_used(nations_avail[myrand(num_nations_avail)]); + pplayer->city_style = get_nation_city_style(pplayer->nation); + pplayer->is_male = myrand(2); + } + + announce_ai_player(pplayer); + } + + /* Create and pick nation and name for AI players needed to bring the + * total number of players to equal game.aifill + */ + + if (game.playable_nation_count < game.aifill) { + game.aifill = game.playable_nation_count; + freelog(LOG_NORMAL, + _("Nation count smaller than aifill; aifill reduced to %d."), + game.playable_nation_count); + } + + if (game.max_players < game.aifill) { + game.aifill = game.max_players; + freelog(LOG_NORMAL, + _("Maxplayers smaller than aifill; aifill reduced to %d."), + game.max_players); + } + + for(;game.nplayers < game.aifill;) { + nation = mark_nation_as_used(nations_avail[myrand(num_nations_avail)]); + pick_ai_player_name(nation, player_name); + + old_nplayers = game.nplayers; + accept_new_player(player_name, NULL); + pplayer = get_player(old_nplayers); + + if (!((game.nplayers == old_nplayers+1) + && strcmp(player_name, pplayer->name)==0)) { + con_write(C_FAIL, _("Error creating new ai player: %s\n"), + player_name); + break; /* don't loop forever */ + } + + pplayer->nation = nation; + pplayer->city_style = get_nation_city_style(nation); + pplayer->ai.control = 1; + pplayer->ai.skill_level = game.skill_level; + if (check_nation_leader_name(nation, player_name)) { + pplayer->is_male = get_nation_leader_sex(nation, player_name); + } else { + pplayer->is_male = myrand(2); + } + announce_ai_player(pplayer); + set_ai_level_direct(pplayer, pplayer->ai.skill_level); + } + send_server_info_to_metaserver(1, 0); +} + +/************************************************************************* + Used in pick_ai_player_name() below; buf has size at least MAX_LEN_NAME; +*************************************************************************/ +static int good_name(char *ptry, char *buf) { + if (!find_player_by_name(ptry)) { + mystrlcpy(buf, ptry, MAX_LEN_NAME); + return 1; + } + return 0; +} + +/************************************************************************* + pick_ai_player_name() - Returns a random ruler name picked from given nation + ruler names, given that nation's number. If that player name is already + taken, iterates through all leader names to find unused one. If it fails + it iterates through "Player 1", "Player 2", ... until an unused name + is found. + newname should point to a buffer of size at least MAX_LEN_NAME. +*************************************************************************/ +void pick_ai_player_name(Nation_Type_id nation, char *newname) +{ + int i, names_count; + char **names; + + names = get_nation_leader_names(nation, &names_count); + + /* Try random names (scattershot), then all available, + * then "Player 1" etc: + */ + for(i=0; ination), + pplayer->name); + + for(i=0; iname, + get_nation_name_plural(pplayer->nation)); + +} + +/************************************************************************** +Play the game! Returns when server_state == GAME_OVER_STATE. +**************************************************************************/ +static void main_loop(void) +{ + struct timer *eot_timer; /* time server processing at end-of-turn */ + int save_counter = 0; + + eot_timer = new_timer_start(TIMER_CPU, TIMER_ACTIVE); + + while(server_state==RUN_GAME_STATE) { + /* absolute beginning of a turn */ + freelog(LOG_DEBUG, "Begin turn"); + begin_turn(); + +#if (IS_DEVEL_VERSION || IS_BETA_VERSION) + sanity_check(); +#endif + + force_end_of_sniff=0; + + freelog(LOG_DEBUG, "Shuffleplayers"); + shuffle_players(); + freelog(LOG_DEBUG, "Aistartturn"); + ai_start_turn(); + + /* Before sniff (human player activites), report time to now: */ + freelog(LOG_VERBOSE, "End/start-turn server/ai activities: %g seconds", + read_timer_seconds(eot_timer)); + + /* Do auto-saves just before starting sniff_packets(), so that + * autosave happens effectively "at the same time" as manual + * saves, from the point of view of restarting and AI players. + * Post-increment so we don't count the first loop. + */ + if(save_counter >= game.save_nturns && game.save_nturns>0) { + save_counter=0; + save_game_auto(); + } + save_counter++; + + freelog(LOG_DEBUG, "sniffingpackets"); + while(sniff_packets()==1); + + /* After sniff, re-zero the timer: (read-out above on next loop) */ + clear_timer_start(eot_timer); + + conn_list_do_buffer(&game.game_connections); + +#if (IS_DEVEL_VERSION || IS_BETA_VERSION) + sanity_check(); +#endif + + before_end_year(); + /* This empties the client Messages window; put this before + everything else below, since otherwise any messages from + the following parts get wiped out before the user gets a + chance to see them. --dwp + */ + freelog(LOG_DEBUG, "Season of native unrests"); + summon_barbarians(); /* wild guess really, no idea where to put it, but + I want to give them chance to move their units */ + freelog(LOG_DEBUG, "Autosettlers"); + auto_settlers(); /* moved this after ai_start_turn for efficiency -- Syela */ + /* moved after sniff_packets for even more efficiency. + What a guy I am. -- Syela */ + /* and now, we must manage our remaining units BEFORE the cities that are + empty get to refresh and defend themselves. How totally stupid. */ + ai_start_turn(); /* Misleading name for manage_units -- Syela */ + freelog(LOG_DEBUG, "Auto-Attack phase"); + auto_attack(); + freelog(LOG_DEBUG, "Endturn"); + end_turn(); + freelog(LOG_DEBUG, "Gamenextyear"); + game_advance_year(); + check_spaceship_arrivals(); + freelog(LOG_DEBUG, "Sendplayerinfo"); + send_player_info(0, 0); + freelog(LOG_DEBUG, "Sendgameinfo"); + send_game_info(0); + freelog(LOG_DEBUG, "Sendyeartoclients"); + send_year_to_clients(game.year); + freelog(LOG_DEBUG, "Sendinfotometaserver"); + send_server_info_to_metaserver(0,0); + + conn_list_do_unbuffer(&game.game_connections); + + if (is_game_over()) + server_state=GAME_OVER_STATE; + } +} + +/************************************************************************** +... +**************************************************************************/ +void srv_main(void) +{ + int i; + + /* make sure it's initialized */ + if (!has_been_srv_init) { + srv_init(); + } + + my_init_network(); + + con_log_init(srvarg.log_filename, srvarg.loglevel); + gamelog_init(srvarg.gamelog_filename); + gamelog_set_level(GAMELOG_FULL); + gamelog(GAMELOG_NORMAL,"Starting new log"); + +#ifdef GENERATING_MAC /* mac beta notice */ + con_puts(C_COMMENT, ""); + con_puts(C_COMMENT, "This is an alpha/beta version of MacFreeciv."); + con_puts(C_COMMENT, "Visit http://www.geocities.com/SiliconValley/Orchard/8738/MFC/index.html"); + con_puts(C_COMMENT, "for new versions of this project and information about it."); + con_puts(C_COMMENT, ""); +#endif +#if IS_BETA_VERSION + con_puts(C_COMMENT, ""); + con_puts(C_COMMENT, beta_message()); + con_puts(C_COMMENT, ""); +#endif + + con_flush(); + + init_our_capability(); + game_init(); + + /* load a script file */ + + if (srvarg.script_filename) + read_init_script(srvarg.script_filename); + + /* load a saved game */ + + if(srvarg.load_filename) { + struct timer *loadtimer, *uloadtimer; + struct section_file file; + + freelog(LOG_NORMAL, _("Loading saved game: %s"), srvarg.load_filename); + loadtimer = new_timer_start(TIMER_CPU, TIMER_ACTIVE); + uloadtimer = new_timer_start(TIMER_USER, TIMER_ACTIVE); + if(!section_file_load_nodup(&file, srvarg.load_filename)) { + freelog(LOG_FATAL, _("Couldn't load savefile: %s"), srvarg.load_filename); + exit(1); + } + game_load(&file); + section_file_check_unused(&file, srvarg.load_filename); + section_file_free(&file); + + freelog(LOG_VERBOSE, "Load time: %g seconds (%g apparent)", + read_timer_seconds_free(loadtimer), + read_timer_seconds_free(uloadtimer)); + } + + /* init network */ + init_connections(); + server_open_socket(); + if(!(srvarg.metaserver_no_send)) { + freelog(LOG_NORMAL, _("Sending info to metaserver [%s]"), + meta_addr_port()); + server_open_udp(); /* open socket for meta server */ + } + + send_server_info_to_metaserver(1,0); + + /* accept new players, wait for serverop to start..*/ + freelog(LOG_NORMAL, _("Now accepting new client connections.")); + server_state=PRE_GAME_STATE; + + while(server_state==PRE_GAME_STATE) + sniff_packets(); /* Accepting commands. */ - if (nation == game.playable_nation_count) { - pplayer->nation = - mark_nation_as_used(nations_avail[myrand(num_nations_avail)]); - pplayer->city_style = get_nation_city_style(pplayer->nation); - pplayer->is_male = myrand(2); - } + send_server_info_to_metaserver(1,0); - announce_ai_player(pplayer); + if(game.is_new_game) { + load_rulesets(); + /* otherwise rulesets were loaded when savegame was loaded */ } - /* Create and pick nation and name for AI players needed to bring the - * total number of players to equal game.aifill - */ + nations_avail = fc_calloc(game.playable_nation_count, sizeof(int)); + nations_used = fc_calloc(game.playable_nation_count, sizeof(int)); - if (game.playable_nation_count < game.aifill) { - game.aifill = game.playable_nation_count; - freelog(LOG_NORMAL, - _("Nation count smaller than aifill; aifill reduced to %d."), - game.playable_nation_count); +main_start_players: + + send_rulesets(&game.est_connections); + + num_nations_avail = game.playable_nation_count; + for(i=0; iis_connected && !pplayer->ai.control) { + toggle_ai_player_direct(NULL, pplayer); + } + } } - for(;game.nplayers < game.aifill;) { - nation = mark_nation_as_used(nations_avail[myrand(num_nations_avail)]); - pick_ai_player_name(nation, player_name); + /* Allow players to select a nation (case new game). + * AI players may not yet have a nation; these will be selected + * in generate_ai_players() later + */ + server_state = RUN_GAME_STATE; + for(i=0; iname)==0)) { - con_write(C_FAIL, _("Error creating new ai player: %s\n"), - player_name); - break; /* don't loop forever */ + while(server_state==SELECT_RACES_STATE) { + sniff_packets(); + for(i=0; ination = nation; - pplayer->city_style = get_nation_city_style(nation); - pplayer->ai.control = 1; - pplayer->ai.skill_level = game.skill_level; - if (check_nation_leader_name(nation, player_name)) { - pplayer->is_male = get_nation_leader_sex(nation, player_name); - } else { - pplayer->is_male = myrand(2); + if(i==game.nplayers) { + if(i>0) { + server_state=RUN_GAME_STATE; + } else { + con_write(C_COMMENT, + _("Last player has disconnected: will need to restart.")); + server_state=PRE_GAME_STATE; + while(server_state==PRE_GAME_STATE) { + sniff_packets(); + } + goto main_start_players; + } } - announce_ai_player(pplayer); - set_ai_level_direct(pplayer, pplayer->ai.skill_level); } - send_server_info_to_metaserver(1, 0); -} -/************************************************************************* - Used in pick_ai_player_name() below; buf has size at least MAX_LEN_NAME; -*************************************************************************/ -static int good_name(char *ptry, char *buf) { - if (!find_player_by_name(ptry)) { - mystrlcpy(buf, ptry, MAX_LEN_NAME); - return 1; + if(!game.randseed) { + /* We strip the high bit for now because neither game file nor + server options can handle unsigned ints yet. - Cedric */ + game.randseed = time(NULL) & (MAX_UINT32 >> 1); } - return 0; -} + + if(!myrand_is_init()) + mysrand(game.randseed); -/************************************************************************* - pick_ai_player_name() - Returns a random ruler name picked from given nation - ruler names, given that nation's number. If that player name is already - taken, iterates through all leader names to find unused one. If it fails - it iterates through "Player 1", "Player 2", ... until an unused name - is found. - newname should point to a buffer of size at least MAX_LEN_NAME. -*************************************************************************/ -void pick_ai_player_name(Nation_Type_id nation, char *newname) -{ - int i, names_count; - char **names; +#ifdef TEST_RANDOM /* not defined anywhere, set it if you want it */ + test_random1(200); + test_random1(2000); + test_random1(20000); + test_random1(200000); +#endif + + if(game.is_new_game) + generate_ai_players(); + + /* if we have a tile map, and map.generator==0, call map_fractal_generate + anyway, to make the specials and huts */ + if(map_is_empty() || (map.generator == 0 && game.is_new_game)) + map_fractal_generate(); - names = get_nation_leader_names(nation, &names_count); + if (map.num_continents==0) + assign_continent_numbers(); - /* Try random names (scattershot), then all available, - * then "Player 1" etc: - */ - for(i=0; ieconomic.gold=game.gold; + } + game.max_players=game.nplayers; + + /* we don't want random start positions in a scenario which already + provides them. -- Gudy */ + if(map.num_start_positions==0) { + create_start_positions(); + } } - nations_used[nations_avail[num_nations_avail-1]]=nations_used[nation]; - nations_avail[nations_used[nation]]=nations_avail[--num_nations_avail]; - nations_used[nation]=-1; + initialize_move_costs(); /* this may be the wrong place to do this */ + generate_minimap(); /* for city_desire; saves a lot of calculations */ - return nation; -} + if (!game.is_new_game) { + for (i=0;iai.control) { + set_ai_level_direct(pplayer, pplayer->ai.skill_level); + assess_danger_player(pplayer); /* a slowdown, but a necessary one */ + } + } + } + + send_all_info(&game.game_connections); + + if(game.is_new_game) + init_new_game(); -/************************************************************************* -... -*************************************************************************/ -static void announce_ai_player (struct player *pplayer) { - int i; + game.is_new_game = 0; - freelog(LOG_NORMAL, _("AI is controlling the %s ruled by %s."), - get_nation_name_plural(pplayer->nation), - pplayer->name); + send_game_state(&game.est_connections, CLIENT_GAME_RUNNING_STATE); - for(i=0; iname, - get_nation_name_plural(pplayer->nation)); + /*** Where the action is. ***/ + main_loop(); + + report_scores(1); + show_map_to_all(); + notify_player(0, _("Game: The game is over...")); + + while(server_state==GAME_OVER_STATE) { + force_end_of_sniff=0; + sniff_packets(); + } + + server_close_udp(); + + my_shutdown_network(); + return; }