From 7a72b5c4e70c09769d17ed450b8b87e604f84396 Mon Sep 17 00:00:00 2001 From: Louis Moureaux Date: Sat, 7 Sep 2024 21:38:02 +0200 Subject: [PATCH] Only send bombardment combat when targets are visible Bombarding cities was very powerful because it was showing every unit inside the city, something only diplomats and spies can normally do. Stop sending this around. --- server/unithand.cpp | 54 ++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/server/unithand.cpp b/server/unithand.cpp index 56cd1b6bf4..d982135a99 100644 --- a/server/unithand.cpp +++ b/server/unithand.cpp @@ -3530,10 +3530,21 @@ void handle_unit_change_activity(struct player *pplayer, int unit_id, activity_target); } +/** + * How much information users can see about a combat. + */ +enum class combat_visibility { + /// Anyone who can see the tile can see the unit fighting there + full, + /// The defensor is hidden to those who cannot normally see it + defensor_hidden, +}; + /** Make sure everyone who can see combat does. */ -static void see_combat(struct unit *pattacker, struct unit *pdefender) +static void see_combat(struct unit *pattacker, struct unit *pdefender, + combat_visibility visibility) { struct packet_unit_short_info unit_att_short_packet, unit_def_short_packet; struct packet_unit_info unit_att_packet, unit_def_packet; @@ -3556,6 +3567,8 @@ static void see_combat(struct unit *pattacker, struct unit *pdefender) package_unit(pattacker, &unit_att_packet); package_unit(pdefender, &unit_def_packet); + bool always_show_defender = (visibility == combat_visibility::full); + conn_list_iterate(game.est_connections, pconn) { struct player *pplayer = pconn->playing; @@ -3567,15 +3580,16 @@ static void see_combat(struct unit *pattacker, struct unit *pdefender) || map_is_known_and_seen(unit_tile(pdefender), pplayer, V_MAIN)) { /* Units are sent even if they were visible already. They may * have changed orientation for combat. */ - if (pplayer == unit_owner(pattacker)) { + if (players_on_same_team(pplayer, unit_owner(pattacker))) { send_packet_unit_info(pconn, &unit_att_packet); } else { send_packet_unit_short_info(pconn, &unit_att_short_packet, false); } - if (pplayer == unit_owner(pdefender)) { + if (players_on_same_team(pplayer, unit_owner(pdefender))) { send_packet_unit_info(pconn, &unit_def_packet); - } else { + } else if (always_show_defender + || can_player_see_unit(pplayer, pdefender)) { send_packet_unit_short_info(pconn, &unit_def_short_packet, false); } } @@ -3592,7 +3606,8 @@ static void see_combat(struct unit *pattacker, struct unit *pdefender) Send combat info to players. */ static void send_combat(struct unit *pattacker, struct unit *pdefender, - int att_veteran, int def_veteran, int bombard) + int att_veteran, int def_veteran, + combat_visibility visibility) { struct packet_unit_combat_info combat; @@ -3603,6 +3618,8 @@ static void send_combat(struct unit *pattacker, struct unit *pdefender, combat.make_att_veteran = att_veteran; combat.make_def_veteran = def_veteran; + bool always_show_defender = (visibility == combat_visibility::full); + players_iterate(other_player) { /* NOTE: this means the player can see combat between submarines even @@ -3610,16 +3627,21 @@ static void send_combat(struct unit *pattacker, struct unit *pdefender, if (map_is_known_and_seen(unit_tile(pattacker), other_player, V_MAIN) || map_is_known_and_seen(unit_tile(pdefender), other_player, V_MAIN)) { - lsend_packet_unit_combat_info(other_player->connections, &combat); + // Only send the combat if the attacker can see the defender, so we + // don't leak info about the number of defenders. + if (always_show_defender + || can_player_see_unit(other_player, pdefender)) { + lsend_packet_unit_combat_info(other_player->connections, &combat); + } - /* - * Remove the client knowledge of the units. This corresponds to the - * send_packet_unit_short_info calls up above. - */ + // Remove the client knowledge of the units. This corresponds to the + // send_packet_unit_short_info calls in see_combat. if (!can_player_see_unit(other_player, pattacker)) { unit_goes_out_of_sight(other_player, pattacker); } - if (!can_player_see_unit(other_player, pdefender)) { + // Only send a remove packet if we sent the defender earlier. + if (always_show_defender + && !can_player_see_unit(other_player, pdefender)) { unit_goes_out_of_sight(other_player, pdefender); } } @@ -3724,12 +3746,13 @@ static bool unit_bombard(struct unit *punit, struct tile *ptile, nation_adjective_for_player(pplayer), unit_name_translation(punit)); - see_combat(punit, pdefender); + see_combat(punit, pdefender, combat_visibility::defensor_hidden); punit->hp = att_hp; pdefender->hp = def_hp; - send_combat(punit, pdefender, 0, 0, 1); + send_combat(punit, pdefender, 0, 0, + combat_visibility::defensor_hidden); send_unit_info(nullptr, pdefender); @@ -3989,7 +4012,7 @@ static bool do_attack(struct unit *punit, struct tile *def_tile, unit_transport_unload_send(punit); } - see_combat(punit, pdefender); + see_combat(punit, pdefender, combat_visibility::full); punit->hp = att_hp; pdefender->hp = def_hp; @@ -4021,7 +4044,8 @@ static bool do_attack(struct unit *punit, struct tile *def_tile, def_tile, unit_link(pdefender)); send_combat(punit, pdefender, punit->veteran - old_unit_vet, - pdefender->veteran - old_defender_vet, 0); + pdefender->veteran - old_defender_vet, + combat_visibility::full); // Neither died if (punit->hp > 0 && pdefender->hp > 0) {