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) {