diff --git a/common/include/common/config/GameConfig.h b/common/include/common/config/GameConfig.h index 8301f0cf..e0a03de5 100644 --- a/common/include/common/config/GameConfig.h +++ b/common/include/common/config/GameConfig.h @@ -82,6 +82,8 @@ struct GameConfig CfgVar glares = true; CfgVar show_enemy_bullets = true; CfgVar linear_pitch = false; + CfgVar multi_hitsounds = true; + CfgVar multi_hitsounds_legacy_mode = true; bool load(); void save(); diff --git a/common/src/config/GameConfig.cpp b/common/src/config/GameConfig.cpp index 3a31627e..e7e17acb 100644 --- a/common/src/config/GameConfig.cpp +++ b/common/src/config/GameConfig.cpp @@ -170,6 +170,8 @@ bool GameConfig::visit_vars(T&& visitor, bool is_save) result &= visitor(dash_faction_key, "Big HUD", big_hud); result &= visitor(dash_faction_key, "Reticle Scale", reticle_scale); result &= visitor(dash_faction_key, "Mesh Static Lighting", mesh_static_lighting); + result &= visitor(dash_faction_key, "multi_hitsounds", multi_hitsounds); + result &= visitor(dash_faction_key, "multi_hitsounds_legacy_mode", multi_hitsounds_legacy_mode); return result; } diff --git a/game_patch/CMakeLists.txt b/game_patch/CMakeLists.txt index ce719f9f..12afbe27 100644 --- a/game_patch/CMakeLists.txt +++ b/game_patch/CMakeLists.txt @@ -100,6 +100,8 @@ set(SRCS multi/multi.cpp multi/kill.cpp multi/network.cpp + multi/custom_packets.cpp + multi/custom_packets.h multi/level_download.cpp multi/server.h multi/server.cpp diff --git a/game_patch/misc/player.h b/game_patch/misc/player.h index 829f1f12..3c7f08ff 100644 --- a/game_patch/misc/player.h +++ b/game_patch/misc/player.h @@ -22,6 +22,7 @@ struct PlayerAdditionalData { bool is_browser = false; bool is_muted = false; + uint8_t multi_hitsounds = 0; int last_hitsound_sent_ms = 0; std::map saves; rf::Vector3 last_teleport_pos; diff --git a/game_patch/multi/custom_packets.cpp b/game_patch/multi/custom_packets.cpp new file mode 100644 index 00000000..15542652 --- /dev/null +++ b/game_patch/multi/custom_packets.cpp @@ -0,0 +1,119 @@ +#include +#include +#include "../main/main.h" +#include "../rf/multi.h" +#include "../rf/sound/sound.h" +#include "../rf/player/player.h" +#include "../misc/player.h" +#include "../multi/multi.h" +#include "../purefaction/pf.h" +#include "custom_packets.h" + +namespace custom_packets { + // Do not drop hitsound packets to save bandwidth, since hitsound packets are + // significantly smaller than sound packets. + void send_hitsound_packet(rf::Player* player){ + if (!rf::is_server) { + return; + } + + rf_packet_header packet{}; + packet.type = static_cast(custom_packet_type::hitsound); + packet.size = 0; + + rf::multi_io_send(player, &packet, sizeof(packet)); + } + + void process_hitsound_packet(void* data, int len, const rf::NetAddr& addr, rf::Player* player) { + if (rf::is_server) { + return; + } + + rf_packet_header packet; + if (len < sizeof(packet)) { + xlog::trace("Cannot process a hitsound packet; invalid length"); + return; + } + + float invalid = std::numeric_limits::quiet_NaN(); + rf::Vector3 position{invalid, invalid, invalid}; + rf::snd_play_3d(29, position, 1.0f, rf::zero_vector, 0); + } + + void send_configure_hitsounds_packet(rf::Player* player) { + if (rf::is_server) { + return; + } + + configure_hitsounds_packet packet{}; + packet.hdr.type = static_cast(custom_packet_type::configure_hitsounds); + packet.hdr.size = sizeof(packet) - sizeof(packet.hdr); + + if (g_game_config.multi_hitsounds_legacy_mode) { + packet.level = 0; + } + else if (g_game_config.multi_hitsounds) { + packet.level = 2; + } + else { + packet.level = 1; + } + + rf::multi_io_send_reliable(player, &packet, sizeof(packet), 0); + } + + void process_configure_hitsounds_packet(void* data, int len, const rf::NetAddr& addr, rf::Player* player) { + if (!rf::is_server) { + return; + } + + configure_hitsounds_packet packet; + if (len < sizeof(packet)) { + xlog::trace("Cannot process a configure_hitsounds packet; invalid length"); + return; + } + + std::memcpy(&packet, data, sizeof(packet)); + + if (packet.level > 2) { + xlog::trace("Cannot process a configure_hitsounds packet; invalid level"); + return; + } + + PlayerAdditionalData& pdata = get_player_additional_data(player); + pdata.multi_hitsounds = packet.level; + + constexpr int NETPLAYER_STATE_IN_GAME = 2; + if (player->net_data->state == NETPLAYER_STATE_IN_GAME) { + static const std::array messages { + "\xA6 Hitsounds are set to legacy mode", + "\xA6 Hitsounds are disabled", + "\xA6 Hitsounds are enabled", + }; + send_chat_line_packet(messages[pdata.multi_hitsounds].c_str(), player); + } + } + + bool process_packet(void* data, int len, const rf::NetAddr& addr, rf::Player* player) { + rf_packet_header header; + if (len < static_cast(sizeof(header))) { + xlog::trace("Cannot process a custom packet; invalid length"); + return false; + } + + std::memcpy(&header, data, sizeof(header)); + + switch (static_cast(header.type)) { + case custom_packet_type::configure_hitsounds: { + process_configure_hitsounds_packet(data, len, addr, player); + return true; + } + case custom_packet_type::hitsound: { + process_hitsound_packet(data, len, addr, player); + return true; + } + } + + return false; + } +} \ No newline at end of file diff --git a/game_patch/multi/custom_packets.h b/game_patch/multi/custom_packets.h new file mode 100644 index 00000000..5ef43e5d --- /dev/null +++ b/game_patch/multi/custom_packets.h @@ -0,0 +1,26 @@ +#pragma once + +#include "../purefaction/pf_packets.h" + +namespace rf { + struct NetAddr; + struct Player; +} + +namespace custom_packets { + enum class custom_packet_type : uint8_t { + configure_hitsounds = 200, + hitsound = 201, + }; + + struct configure_hitsounds_packet { + rf_packet_header hdr; + uint8_t level; + }; + + void send_hitsound_packet(rf::Player* player); + void process_hitsound_packet(void* data, int len, const rf::NetAddr& addr, rf::Player* player); + void send_configure_hitsounds_packet(rf::Player* player); + void process_configure_hitsounds_packet(void* data, int len, const rf::NetAddr& addr, rf::Player* player); + bool process_packet(void* data, int len, const rf::NetAddr& addr, rf::Player* player); +} \ No newline at end of file diff --git a/game_patch/multi/network.cpp b/game_patch/multi/network.cpp index 5ee3d6f6..3132fee4 100644 --- a/game_patch/multi/network.cpp +++ b/game_patch/multi/network.cpp @@ -22,6 +22,7 @@ #include "multi.h" #include "server.h" #include "server_internal.h" +#include "custom_packets.h" #include "../main/main.h" #include "../rf/multi.h" #include "../rf/misc.h" @@ -740,6 +741,7 @@ CodeInjection process_join_accept_send_game_info_req_injection{ rf::NetAddr* server_addr = regs.edi; xlog::trace("Sending game_info_req to %x:%d", server_addr->ip_addr, server_addr->port); rf::send_game_info_req_packet(*server_addr); + custom_packets::send_configure_hitsounds_packet(rf::local_player); }, }; @@ -1025,13 +1027,6 @@ void __fastcall multi_io_stats_add_new(void *this_, int edx, int size, bool is_s FunHook multi_io_stats_add_hook{0x0047CAC0, multi_io_stats_add_new}; -static void process_custom_packet(void* data, int len, const rf::NetAddr& addr, rf::Player* player) -{ -#if MASK_AS_PF - pf_process_packet(data, len, addr, player); -#endif -} - CodeInjection multi_io_process_packets_injection{ 0x0047918D, [](auto& regs) { @@ -1043,7 +1038,13 @@ CodeInjection multi_io_process_packets_injection{ int len = regs.edi; auto& addr = *addr_as_ref(stack_frame + 0xC); auto player = addr_as_ref(stack_frame + 0x10); - process_custom_packet(data + offset, len, addr, player); + #ifdef HAS_PF + if (!pf_process_packet(data, len, addr, player)) { + custom_packets::process_packet(data + offset, len, addr, player); + } + #else + custom_packets::process_packet(data + offset, len, addr, player); + #endif regs.eip = 0x00479194; } }, @@ -1082,8 +1083,45 @@ CallHook game_info_num_players_hook{ }, }; +ConsoleCommand2 multi_hitsounds_cmd{ + "multi_hitsounds", + [] (std::optional arg) { + if (!rf::is_server) { + if (g_game_config.multi_hitsounds_legacy_mode) { + rf::console::printf("You need to disable multi_hitsounds_legacy_mode"); + return; + } + if (arg.value_or("") != "?") { + g_game_config.multi_hitsounds = !g_game_config.multi_hitsounds; + g_game_config.save(); + custom_packets::send_configure_hitsounds_packet(rf::local_player); + } + rf::console::printf("multi_hitsounds is %s", g_game_config.multi_hitsounds ? "enabled" : "disabled"); + } + }, + "Toggle hitsounds; applies to compatible servers", +}; + +ConsoleCommand2 multi_hitsounds_legacy_mode_cmd{ + "multi_hitsounds_legacy_mode", + [] (std::optional arg) { + if (!rf::is_server) { + if (arg.value_or("") != "?") { + g_game_config.multi_hitsounds_legacy_mode = !g_game_config.multi_hitsounds_legacy_mode; + g_game_config.save(); + custom_packets::send_configure_hitsounds_packet(rf::local_player); + } + rf::console::printf("multi_hitsounds_legacy_mode is %s", g_game_config.multi_hitsounds_legacy_mode ? "enabled" : "disabled"); + } + }, + "Toggle legacy mode for hitsounds; applies to compatible servers", +}; + void network_init() { + multi_hitsounds_cmd.register_cmd(); + multi_hitsounds_legacy_mode_cmd.register_cmd(); + // Improve simultaneous ping rf::simultaneous_ping = 32; diff --git a/game_patch/multi/server.cpp b/game_patch/multi/server.cpp index bc23970d..04fcdde0 100644 --- a/game_patch/multi/server.cpp +++ b/game_patch/multi/server.cpp @@ -13,6 +13,7 @@ #include "server.h" #include "server_internal.h" #include "multi.h" +#include "custom_packets.h" #include "../os/console.h" #include "../misc/player.h" #include "../main/main.h" @@ -383,7 +384,13 @@ FunHook entity_damage_hook{ auto* damaged_player_stats = static_cast(damaged_player->stats); damaged_player_stats->add_damage_received(real_damage); - if (g_additional_server_config.hit_sounds.enabled) { + auto& pdata = get_player_additional_data(killer_player); + if (pdata.multi_hitsounds) { + if (pdata.multi_hitsounds == 2) { + custom_packets::send_hitsound_packet(killer_player); + } + } + else if (g_additional_server_config.hit_sounds.enabled) { send_hit_sound_packet(killer_player); } }