diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 2a0857a3..3dbc8119 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -49,8 +49,9 @@ Version 1.9.0 (not released yet) [@is-this-c](https://github.com/is-this-c) - Support `©` in TrueType fonts -- Improve frag messages -- Search in command descriptions in `.` command +- Improve frag message format +- Search command descriptions in `.` command +- Improve PF network protocol compatibility [@Mystyle-48](https://github.com/Mystyle-48) - Simplify installation detection in setup and launcher diff --git a/game_patch/misc/player.h b/game_patch/misc/player.h index 829f1f12..cc92b770 100644 --- a/game_patch/misc/player.h +++ b/game_patch/misc/player.h @@ -1,10 +1,12 @@ #include #include +#include #include #include #include "../rf/math/vector.h" #include "../rf/math/matrix.h" #include "../rf/os/timestamp.h" +#include "../purefaction/pf_packets.h" // Forward declarations namespace rf @@ -20,6 +22,7 @@ struct PlayerNetGameSaveData struct PlayerAdditionalData { + std::optional received_ac_status{}; bool is_browser = false; bool is_muted = false; int last_hitsound_sent_ms = 0; diff --git a/game_patch/multi/multi.h b/game_patch/multi/multi.h index 035790fa..e22a0a45 100644 --- a/game_patch/multi/multi.h +++ b/game_patch/multi/multi.h @@ -13,6 +13,7 @@ struct PlayerStatsNew : rf::PlayerLevelStats float num_shots_fired; float damage_received; float damage_given; + std::optional received_accuracy{}; void inc_kills() { @@ -67,6 +68,7 @@ struct PlayerStatsNew : rf::PlayerLevelStats max_streak = 0; damage_received = 0; damage_given = 0; + received_accuracy.reset(); } }; diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 73bfe00a..061bb10d 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -1029,7 +1029,7 @@ CodeInjection multi_io_process_packets_injection{ 0x0047918D, [](auto& regs) { int packet_type = regs.esi; - if (packet_type > 0x37) { + if (packet_type > 0x37 || packet_type == static_cast(pf_packet_type::player_stats)) { auto stack_frame = regs.esp + 0x1C; std::byte* data = regs.ecx; int offset = regs.ebp; diff --git a/game_patch/purefaction/pf.cpp b/game_patch/purefaction/pf.cpp index 143e2b9c..a70a0443 100644 --- a/game_patch/purefaction/pf.cpp +++ b/game_patch/purefaction/pf.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -9,6 +10,7 @@ #include "pf.h" #include "pf_packets.h" #include "pf_ac.h" +#include "../misc/player.h" void pf_send_reliable_packet(rf::Player* player, const void* data, int len) { @@ -35,7 +37,9 @@ static void send_pf_announce_player_packet(rf::Player* player, pf_pure_status pu announce_packet.hdr.size = sizeof(announce_packet) - sizeof(announce_packet.hdr); announce_packet.version = pf_announce_player_packet_version; announce_packet.player_id = player->net_data->player_id; - announce_packet.is_pure = static_cast(pure_status); + announce_packet.is_pure = static_cast( + get_player_additional_data(player).is_browser ? pf_pure_status::rfsb : pure_status + ); auto player_list = SinglyLinkedList(rf::player_list); for (auto& other_player : player_list) { @@ -50,7 +54,7 @@ static void process_pf_player_announce_packet(const void* data, size_t len, [[ m return; } - pf_player_announce_packet announce_packet; + pf_player_announce_packet announce_packet{}; if (len < sizeof(announce_packet)) { xlog::trace("Invalid length in PF player_announce packet"); return; @@ -64,23 +68,32 @@ static void process_pf_player_announce_packet(const void* data, size_t len, [[ m } xlog::trace("PF player_announce packet: player {} is_pure {}", announce_packet.player_id, announce_packet.is_pure); + + if (rf::Player* player = rf::multi_find_player_by_id(announce_packet.player_id); player + && announce_packet.is_pure <= static_cast(pf_pure_status::_last_variant)) { + get_player_additional_data(player).received_ac_status + = std::optional{static_cast(announce_packet.is_pure)}; + } if (announce_packet.player_id == rf::local_player->net_data->player_id) { - static const char* pf_verification_status_names[] = { "none", "blue", "gold", "red" }; - const auto* pf_verification_status = - announce_packet.is_pure < std::size(pf_verification_status_names) - ? pf_verification_status_names[announce_packet.is_pure] : "unknown"; + static constinit const std::array pf_verification_status_names{ + {"none", "blue", "gold", "fail", "old_blue", "rfsb"} + }; + const std::string_view pf_verification_status + = announce_packet.is_pure < std::size(pf_verification_status_names) + ? pf_verification_status_names[announce_packet.is_pure] + : "unknown"; xlog::info("PF Verification Status: {} ({})", pf_verification_status, announce_packet.is_pure); } } -static void send_pf_player_stats_packet(rf::Player* player) +void send_pf_player_stats_packet(rf::Player* player) { // Send: server -> client assert(rf::is_server); std::byte packet_buf[rf::max_packet_size]; - pf_player_stats_packet stats_packet; + pf_player_stats_packet stats_packet{}; stats_packet.hdr.type = static_cast(pf_packet_type::player_stats); stats_packet.hdr.size = sizeof(stats_packet) - sizeof(stats_packet.hdr); stats_packet.version = pf_player_stats_packet_version; @@ -90,12 +103,15 @@ static void send_pf_player_stats_packet(rf::Player* player) auto player_list = SinglyLinkedList{rf::player_list}; for (auto& current_player : player_list) { auto& player_stats = *static_cast(current_player.stats); - pf_player_stats_packet::player_stats out_stats; + pf_player_stats_packet::player_stats out_stats{}; out_stats.player_id = current_player.net_data->player_id; - out_stats.is_pure = static_cast(pf_ac_get_pure_status(¤t_player)); - out_stats.accuracy = 0; - out_stats.streak_max = 0; - out_stats.streak_current = 0; + const pf_pure_status pure_status = pf_ac_get_pure_status(¤t_player); + out_stats.is_pure = static_cast( + get_player_additional_data(¤t_player).is_browser ? pf_pure_status::rfsb : pure_status + ); + out_stats.accuracy = static_cast(player_stats.calc_accuracy() * 100.f); + out_stats.streak_max = player_stats.max_streak; + out_stats.streak_current = player_stats.current_streak; out_stats.kills = player_stats.num_kills; out_stats.deaths = player_stats.num_deaths; out_stats.team_kills = 0; @@ -129,7 +145,7 @@ static void process_pf_player_stats_packet(const void* data, size_t len, [[ mayb return; } - pf_player_stats_packet stats_packet; + pf_player_stats_packet stats_packet{}; if (len < sizeof(stats_packet)) { xlog::trace("Invalid length in PF player_stats packet"); return; @@ -151,12 +167,19 @@ static void process_pf_player_stats_packet(const void* data, size_t len, [[ mayb const std::byte* player_stats_ptr = static_cast(data) + sizeof(stats_packet); for (int i = 0; i < stats_packet.player_count; ++i) { - pf_player_stats_packet::player_stats in_stats; + pf_player_stats_packet::player_stats in_stats{}; std::memcpy(&in_stats, player_stats_ptr, sizeof(in_stats)); player_stats_ptr += sizeof(in_stats); auto* player = rf::multi_find_player_by_id(in_stats.player_id); if (player) { auto& stats = *static_cast(player->stats); + if (in_stats.is_pure <= static_cast(pf_pure_status::_last_variant)) { + get_player_additional_data(player).received_ac_status + = std::optional{static_cast(in_stats.is_pure)}; + } + stats.received_accuracy = std::optional{in_stats.accuracy}; + stats.max_streak = in_stats.streak_max; + stats.current_streak = in_stats.streak_current; stats.num_kills = in_stats.kills; stats.num_deaths = in_stats.deaths; } @@ -175,7 +198,7 @@ static void process_pf_players_request_packet([[ maybe_unused ]] const void* dat return; } - pf_players_packet players_packet; + pf_players_packet players_packet{}; players_packet.hdr.type = static_cast(pf_packet_type::players); players_packet.version = 1; players_packet.show_ip = 0; @@ -204,7 +227,7 @@ bool pf_process_packet(const void* data, int len, const rf::NetAddr& addr, rf::P return true; } - rf_packet_header header; + rf_packet_header header{}; if (len < static_cast(sizeof(header))) { return false; } @@ -231,7 +254,7 @@ bool pf_process_packet(const void* data, int len, const rf::NetAddr& addr, rf::P bool pf_process_raw_unreliable_packet(const void* data, int len, const rf::NetAddr& addr) { - rf_packet_header header; + rf_packet_header header{}; if (len < static_cast(sizeof(header))) { return false; } diff --git a/game_patch/purefaction/pf_packets.h b/game_patch/purefaction/pf_packets.h index 2da2f5f7..096c5237 100755 --- a/game_patch/purefaction/pf_packets.h +++ b/game_patch/purefaction/pf_packets.h @@ -24,6 +24,7 @@ enum class pf_pure_status : uint8_t fail = 3, old_pure = 4, rfsb = 5, + _last_variant = rfsb, }; struct rf_packet_header