diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 007286ff..42cc5f16 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -20,6 +20,7 @@ Version 1.4.0 (Lupin): Not yet released - Make clock clutter objects correctly display the current local real world time [@is-this-c](https://github.com/is-this-c) +- Add `IoCursor` and `PacketBuilder` - Disable `Refresh Selected` in the server browser, only if `Refresh Selected` was pressed - Never disable `Add Server` in the server browser - Add `NOT IN ROUND`, `IDLE`, and `SPECTATOR` to the spectate UI diff --git a/game_patch/CMakeLists.txt b/game_patch/CMakeLists.txt index d57df8e3..eb549db1 100644 --- a/game_patch/CMakeLists.txt +++ b/game_patch/CMakeLists.txt @@ -217,15 +217,16 @@ set(SRCS multi/bots/bot_waypoint_route.cpp multi/bots/bot_waypoint_route.h multi/bots/bot_weapon_profiles.h + os/autocomplete.cpp os/console.cpp os/console.h os/commands.cpp - os/autocomplete.cpp os/frametime.cpp - os/timer.cpp - os/win32_console.cpp + os/io_cursor.h os/os.cpp os/os.h + os/timer.cpp + os/win32_console.cpp object/object.h object/mover.h object/object_private.h diff --git a/game_patch/multi/alpine_packets.cpp b/game_patch/multi/alpine_packets.cpp index c5ab678b..4ade7d5c 100644 --- a/game_patch/multi/alpine_packets.cpp +++ b/game_patch/multi/alpine_packets.cpp @@ -49,8 +49,8 @@ void af_send_packet(rf::Player* player, const void* data, int len, bool is_relia } return; } - if (len <= 0 || len > rf::max_packet_size) { - xlog::error("af_send_packet: Packet size {} exceeds max {}", len, rf::max_packet_size); + if (len <= 0 || len > rf::MAX_PACKET_SIZE) { + xlog::error("af_send_packet: Packet size {} exceeds max {}", len, rf::MAX_PACKET_SIZE); return; } @@ -152,7 +152,7 @@ void af_send_ping_location_req_packet(rf::Vector3* pos) return; } - std::byte packet_buf[rf::max_packet_size]; + std::byte packet_buf[rf::MAX_PACKET_SIZE]; af_ping_location_req_packet ping_location_req_packet{}; ping_location_req_packet.header.type = static_cast(af_packet_type::af_ping_location_req); ping_location_req_packet.header.size = sizeof(ping_location_req_packet) - sizeof(ping_location_req_packet.header); @@ -233,7 +233,7 @@ void af_send_ping_location_packet(rf::Vector3* pos, uint8_t player_id, rf::Playe return; } - std::byte packet_buf[rf::max_packet_size]; + std::byte packet_buf[rf::MAX_PACKET_SIZE]; af_ping_location_packet ping_location_packet{}; ping_location_packet.header.type = static_cast(af_packet_type::af_ping_location); ping_location_packet.header.size = sizeof(ping_location_packet) - sizeof(ping_location_packet.header); @@ -313,7 +313,7 @@ void af_send_damage_notify_packet(uint8_t player_id, float damage, bool died, rf return; } - std::byte packet_buf[rf::max_packet_size]; + std::byte packet_buf[rf::MAX_PACKET_SIZE]; af_damage_notify_packet damage_notify_packet{}; damage_notify_packet.header.type = static_cast(af_packet_type::af_damage_notify); damage_notify_packet.header.size = sizeof(damage_notify_packet) - sizeof(damage_notify_packet.header); @@ -433,7 +433,7 @@ void af_send_obj_update_packet(rf::Player* player) size_t object_data_size = obj_updates.size() * sizeof(af_obj_update); size_t total_packet_size = sizeof(RF_GamePacketHeader) + object_data_size; - if (total_packet_size > rf::max_packet_size) { + if (total_packet_size > rf::MAX_PACKET_SIZE) { xlog::error("af_send_obj_update_packet: Packet too large! Size: {}", total_packet_size); return; } @@ -591,7 +591,7 @@ void af_send_client_req_packet(const af_client_req_packet& packet) return; } - std::byte buf[rf::max_packet_size]; + std::byte buf[rf::MAX_PACKET_SIZE]; size_t offset = 0; // Write header @@ -690,7 +690,7 @@ void af_send_server_req_packet(const af_server_req_packet& packet, rf::Player* p return; } - std::byte buf[rf::max_packet_size]; + std::byte buf[rf::MAX_PACKET_SIZE]; size_t offset = 0; std::memcpy(buf + offset, &packet.header, sizeof(packet.header)); @@ -838,7 +838,7 @@ void af_send_just_spawned_loadout(rf::Player* to_player, std::vectorsize = static_cast(1 + payload_written); const size_t total_len = sizeof(RF_GamePacketHeader) + hdr->size; - if (total_len > rf::max_packet_size) + if (total_len > rf::MAX_PACKET_SIZE) return; af_send_packet(to_player, buf, static_cast(total_len), true); @@ -962,10 +962,10 @@ void af_send_koth_hill_state_packet(rf::Player* player, const HillInfo& h, const pkt.red_score = static_cast(std::clamp(g_koth_info.red_team_score, 0, 0xFFFF)); pkt.blue_score = static_cast(std::clamp(g_koth_info.blue_team_score, 0, 0xFFFF)); - std::byte buf[rf::max_packet_size]; + std::byte buf[rf::MAX_PACKET_SIZE]; const size_t wire_sz = sizeof(pkt); - if (wire_sz > rf::max_packet_size) { - xlog::error("af_koth_state: packet too large ({}>{})", wire_sz, rf::max_packet_size); + if (wire_sz > rf::MAX_PACKET_SIZE) { + xlog::error("af_koth_state: packet too large ({}>{})", wire_sz, rf::MAX_PACKET_SIZE); return; } std::memcpy(buf, &pkt, wire_sz); @@ -1056,12 +1056,12 @@ void af_send_koth_hill_captured_packet(rf::Player* player, uint8_t hill_uid, Hil const size_t payload_size = (sizeof(af_koth_hill_captured_packet) - sizeof(RF_GamePacketHeader)) + id_count; const size_t wire_size = sizeof(RF_GamePacketHeader) + payload_size; - if (wire_size > rf::max_packet_size) { - xlog::error("af_koth_hill_captured: packet too large ({} > {})", wire_size, rf::max_packet_size); + if (wire_size > rf::MAX_PACKET_SIZE) { + xlog::error("af_koth_hill_captured: packet too large ({} > {})", wire_size, rf::MAX_PACKET_SIZE); return; } - std::byte packet_buf[rf::max_packet_size]; + std::byte packet_buf[rf::MAX_PACKET_SIZE]; af_koth_hill_captured_packet af_koth_hill_captured_packet{}; af_koth_hill_captured_packet.header.type = static_cast(af_packet_type::af_koth_hill_captured); @@ -1653,7 +1653,7 @@ void af_send_server_cfg(rf::Player* player) { } const auto send_msg = [player] (const std::string_view msg) { - constexpr size_t max_chunk_len = rf::max_packet_size - sizeof(af_server_msg_packet); + constexpr size_t max_chunk_len = rf::MAX_PACKET_SIZE - sizeof(af_server_msg_packet); const size_t len = std::clamp(msg.size(), 0uz, max_chunk_len); af_server_msg_packet server_msg_packet; @@ -1682,7 +1682,7 @@ void af_send_server_cfg(rf::Player* player) { // We cannot send multiple server configs at once. send_queues_rel_clear_packets(player->net_data->reliable_socket); - constexpr int chunk_size = rf::max_packet_size - sizeof(af_server_msg_packet); + constexpr int chunk_size = rf::MAX_PACKET_SIZE - sizeof(af_server_msg_packet); for (const auto chunk : g_alpine_server_config.printed_cfg | std::views::chunk(chunk_size)) { send_msg(std::string_view{chunk.begin(), chunk.end()}); @@ -1705,13 +1705,13 @@ void af_send_server_cfg(rf::Player* player) { union af_server_msg_packet_buf { af_server_msg_packet packet; - std::array buf; + std::array buf; }; af_server_msg_packet_buf build_automated_chat_msg_packet( const std::string_view msg ) { - constexpr size_t max_len = rf::max_packet_size - sizeof(af_server_msg_packet); + constexpr size_t max_len = rf::MAX_PACKET_SIZE - sizeof(af_server_msg_packet); const size_t len = std::clamp(msg.size(), 0uz, max_len); af_server_msg_packet_buf buf{}; @@ -1730,7 +1730,7 @@ af_server_msg_packet_buf build_automated_chat_msg_packet( af_server_msg_packet_buf build_server_console_msg_packet( const std::string_view msg ) { - constexpr size_t max_len = rf::max_packet_size - sizeof(af_server_msg_packet); + constexpr size_t max_len = rf::MAX_PACKET_SIZE - sizeof(af_server_msg_packet); const size_t len = std::clamp(msg.size(), 0uz, max_len); af_server_msg_packet_buf buf{}; @@ -1874,7 +1874,7 @@ void af_send_bot_control_simple(rf::Player* player, af_bot_control_type subtype) return; } - std::byte buf[rf::max_packet_size]; + std::byte buf[rf::MAX_PACKET_SIZE]; size_t offset = 0; RF_GamePacketHeader header{}; @@ -1947,7 +1947,7 @@ void af_send_bot_control_update_personality(rf::Player* player, const ServerBotC return; } - std::byte buf[rf::max_packet_size]; + std::byte buf[rf::MAX_PACKET_SIZE]; const size_t len = write_profile_update_payload( buf, sizeof(buf), af_bot_control_type::update_personality, @@ -1966,7 +1966,7 @@ void af_send_bot_control_update_skill(rf::Player* player, const ServerBotConfig& return; } - std::byte buf[rf::max_packet_size]; + std::byte buf[rf::MAX_PACKET_SIZE]; const size_t len = write_profile_update_payload( buf, sizeof(buf), af_bot_control_type::update_skill, @@ -1988,7 +1988,7 @@ void af_send_bot_control_update_identity(rf::Player* player, const std::string& const uint8_t name_len = static_cast( std::min(name.size(), kMaxPresetNameLen)); - std::byte buf[rf::max_packet_size]; + std::byte buf[rf::MAX_PACKET_SIZE]; size_t offset = 0; RF_GamePacketHeader header{}; @@ -2400,7 +2400,7 @@ void af_send_player_info_response(const rf::NetAddr& addr) // Compute segment boundaries (each segment fits within max_packet_size) constexpr int header_size = static_cast(sizeof(af_player_info_packet)); - constexpr int max_payload = static_cast(rf::max_packet_size) - header_size; + constexpr int max_payload = static_cast(rf::MAX_PACKET_SIZE) - header_size; int seg_boundaries[max_players + 2]; // preamble + entries + sentinel int total_segments = 0; @@ -2436,7 +2436,7 @@ void af_send_player_info_response(const rf::NetAddr& addr) } // Build and send each segment - std::byte packet_buf[rf::max_packet_size]; + std::byte packet_buf[rf::MAX_PACKET_SIZE]; for (int seg = 0; seg < total_segments; ++seg) { int first_entry = seg_boundaries[seg]; int end_entry = seg_boundaries[seg + 1]; diff --git a/game_patch/multi/network.h b/game_patch/multi/network.h index 84a3c281..673e1981 100644 --- a/game_patch/multi/network.h +++ b/game_patch/multi/network.h @@ -2,9 +2,11 @@ #include #include +#include #include #include "server_internal.h" #include "../rf/multi.h" +#include "../os/io_cursor.h" constexpr uint32_t ALPINE_FACTION_SIGNATURE = 0x4E4C5246; constexpr uint32_t DASH_FACTION_SIGNATURE = 0xDA58FAC7; @@ -277,3 +279,117 @@ void send_queues_rel_clear_packets(int socket_id); void send_queues_rel_add_packet(int socket_id, const uint8_t* data, size_t len); void clear_rcon_profile_sessions(); void multi_disconnect_from_server(); + +template +class PacketBuilder { +public: + explicit PacketBuilder(Storage storage) + noexcept(std::is_nothrow_move_constructible_v) + : storage{std::move(storage)} + , cursor{this->storage} { + if constexpr (std::derived_from) { + this->storage.reserve(rf::MAX_PACKET_SIZE); + } + this->cursor.write(DUMMY_HEADER); + } + + template + requires std::constructible_from + explicit PacketBuilder(Args&&... args) + noexcept(std::is_nothrow_constructible_v) + : storage{std::forward(args)...} + , cursor{this->storage} { + if constexpr (std::derived_from) { + this->storage.reserve(rf::MAX_PACKET_SIZE); + } + this->cursor.write(DUMMY_HEADER); + } + + template + bool write(this PacketBuilder& self, const T& value) noexcept { + const bool res = self.cursor.write(value); + if (!res || self.size() > MAX_LEN) { + self.cursor.poison(); + return false; + } else { + return true; + } + } + + bool write_zstring( + this PacketBuilder& self, + const std::string_view text + ) noexcept { + const bool res = self.cursor.write_zstring(text); + if (!res || self.size() > MAX_LEN) { + self.cursor.poison(); + return false; + } else { + return true; + } + } + + template + requires std::is_enum_v + && (sizeof(std::underlying_type_t) == 1) + bool finalize(this PacketBuilder& self, const E type) noexcept { + if (self.cursor.is_poisoned() + || self.payload_size() > std::numeric_limits::max()) { + return false; + } + const RF_GamePacketHeader header{ + .type = std::to_underlying(type), + .size = static_cast(self.payload_size()) + }; + try { + const size_t num_bytes = + self.storage.write(0, std::as_bytes(std::span{&header, 1})); + if (num_bytes != sizeof(RF_GamePacketHeader) || self.size() > MAX_LEN) { + goto POISON; + } + } catch (...) { + goto POISON; + } + return true; + POISON: + self.cursor.poison(); + return false; + } + + [[nodiscard]] + size_t capacity(this const PacketBuilder& self) noexcept { + return self.cursor.size(); + } + + [[nodiscard]] + size_t size(this const PacketBuilder& self) noexcept { + return self.cursor.position(); + } + + [[nodiscard]] + size_t payload_size(this const PacketBuilder& self) noexcept { + return self.cursor.position() - sizeof(RF_GamePacketHeader); + } + + [[nodiscard]] + const char* data(this const PacketBuilder& self) noexcept { + return self.storage.data(); + } + + [[nodiscard]] + bool is_poisoned(this const PacketBuilder& self) noexcept { + return self.cursor.is_poisoned(); + } + +private: + static constexpr RF_GamePacketHeader DUMMY_HEADER{}; + + Storage storage; + IoCursor cursor; +}; + +template +PacketBuilder(char (&)[N]) -> PacketBuilder; +PacketBuilder(std::span) -> PacketBuilder; +PacketBuilder(void*, size_t) -> PacketBuilder; +PacketBuilder(std::vector&) -> PacketBuilder; diff --git a/game_patch/os/io_cursor.h b/game_patch/os/io_cursor.h new file mode 100644 index 00000000..bfaac6e7 --- /dev/null +++ b/game_patch/os/io_cursor.h @@ -0,0 +1,399 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace io_cursor_detail { + +template +concept PodLike = std::is_trivially_copyable_v + && std::is_standard_layout_v; + +template +concept ReadSeek = requires( + const Storage& storage, + size_t position, + std::span out +) { + { storage.size() } noexcept -> std::same_as; + { storage.data() } noexcept -> std::same_as; + { storage.read(position, out) } noexcept -> std::same_as; +}; + +template +concept WriteSeek = requires( + Storage& storage, + size_t position, + std::span in +) { + { storage.size() } noexcept -> std::same_as; + { storage.data() } noexcept -> std::same_as; + { storage.write(position, in) } -> std::same_as; +}; + +template +concept ReadWriteSeek = ReadSeek && WriteSeek; + +class ReadOnlySpanStorage { +public: + ReadOnlySpanStorage() = default; + + explicit ReadOnlySpanStorage(const std::span buf) noexcept + : view{buf} {} + + explicit ReadOnlySpanStorage(const std::vector& buf) noexcept + : view{buf} {} + + template + explicit ReadOnlySpanStorage(const char (&buf)[N]) noexcept + : view{buf, N} {} + + explicit ReadOnlySpanStorage(const void* const buf, const size_t len) noexcept + : view{static_cast(buf), len} {} + + [[nodiscard]] + const char* data(this const ReadOnlySpanStorage& self) noexcept { + return self.view.data(); + } + + [[nodiscard]] + size_t size(this const ReadOnlySpanStorage& self) noexcept { + return self.view.size(); + } + + size_t read( + this const ReadOnlySpanStorage& self, + const size_t position, + const std::span out + ) noexcept { + if (position > self.view.size()) { + return 0; + } + const size_t num_bytes = std::min(out.size(), self.view.size() - position); + if (num_bytes > 0) { + std::memcpy(out.data(), self.view.data() + position, num_bytes); + } + return num_bytes; + } + +private: + std::span view; +}; + +class FixedSpanStorage { +public: + FixedSpanStorage() = default; + + explicit FixedSpanStorage(const std::span buf) noexcept + : view{buf} {} + + template + explicit FixedSpanStorage(char (&buf)[N]) noexcept + : view{buf, N} {} + + explicit FixedSpanStorage(void* const buf, const size_t len) noexcept + : view{static_cast(buf), len} {} + + [[nodiscard]] + const char* data(this const FixedSpanStorage& self) noexcept { + return self.view.data(); + } + + [[nodiscard]] + size_t size(this const FixedSpanStorage& self) noexcept { + return self.view.size(); + } + + size_t read( + this const FixedSpanStorage& self, + const size_t position, + const std::span out + ) noexcept { + if (position > self.view.size()) { + return 0; + } + const size_t num_bytes = std::min(out.size(), self.view.size() - position); + if (num_bytes > 0) { + std::memcpy(out.data(), self.view.data() + position, num_bytes); + } + return num_bytes; + } + + size_t write( + this FixedSpanStorage& self, + const size_t position, + const std::span in + ) noexcept { + if (position > self.view.size()) { + return 0; + } + const size_t num_bytes = std::min(in.size(), self.view.size() - position); + if (num_bytes > 0) { + std::memcpy(self.view.data() + position, in.data(), num_bytes); + } + return num_bytes; + } + +private: + std::span view; +}; + +class VectorGrowStorage { +public: + VectorGrowStorage() = delete; + + explicit VectorGrowStorage(std::vector& buf) noexcept + : buf{buf} {} + + [[nodiscard]] + const char* data(this const VectorGrowStorage& self) noexcept { + return self.buf.data(); + } + + [[nodiscard]] + size_t size(this const VectorGrowStorage& self) noexcept { + return self.buf.size(); + } + + void reserve(this VectorGrowStorage& self, const size_t size) noexcept { + self.buf.reserve(size); + } + + size_t read( + this const VectorGrowStorage& self, + const size_t position, + const std::span out + ) noexcept { + if (position > self.buf.size()) { + return 0; + } + const size_t num_bytes = std::min(out.size(), self.buf.size() - position); + if (num_bytes > 0) { + std::memcpy(out.data(), self.buf.data() + position, num_bytes); + } + return num_bytes; + } + + size_t write( + this VectorGrowStorage& self, + const size_t position, + const std::span in + ) { + if (in.empty()) { + return 0; + } + const size_t end = position + in.size(); + if (self.buf.size() < end) { + self.buf.resize(end); + } + std::memcpy(self.buf.data() + position, in.data(), in.size()); + return in.size(); + } + +private: + std::vector& buf; +}; + +} + +template +class IoCursor { +public: + explicit IoCursor(Storage storage) + noexcept(std::is_nothrow_move_constructible_v) + : storage{std::move(storage)} + , pos{0} + , poisoned{false} {} + + template + requires std::constructible_from + explicit IoCursor(Args&&... args) + noexcept(std::is_nothrow_constructible_v) + : storage{std::forward(args)...} + , pos{0} + , poisoned{false} {} + + template + requires io_cursor_detail::ReadSeek || io_cursor_detail::WriteSeek + [[nodiscard]] + bool available(this const IoCursor& self) noexcept { + return !self.poisoned + && self.pos <= self.storage.size() + && sizeof(T) <= self.storage.size() - self.pos; + } + + template + requires io_cursor_detail::ReadSeek + bool peek(this IoCursor& self, T& out) noexcept { + if (!self.available()) { + self.poison(); + return false; + } + const std::span bytes = std::as_bytes(std::span{std::addressof(out), 1}); + if (self.storage.read(self.pos, bytes) != sizeof(T)) { + self.poison(); + return false; + } + return true; + } + + template + requires io_cursor_detail::ReadSeek || io_cursor_detail::WriteSeek + bool skip(this IoCursor& self) noexcept { + if (!self.available()) { + self.poison(); + return false; + } + self.pos += sizeof(T); + return true; + } + + + template + requires io_cursor_detail::ReadSeek + bool read(this IoCursor& self, T& out) noexcept { + if (!self.peek(out)) { + return false; + } + self.pos += sizeof(T); + return true; + } + + template + requires io_cursor_detail::ReadSeek + bool read_zstring(this IoCursor& self, std::string_view& out) noexcept { + if (self.poisoned) { + return false; + } else if (self.pos >= self.storage.size()) { + self.poison(); + return false; + } + const char* const data = self.storage.data(); + const char* const begin = data + self.pos; + const char* const end = data + self.storage.size(); + const char* const nul = std::find(begin, end, '\0'); + if (nul == end) { + self.poison(); + return false; + } + const size_t len = std::distance(begin, nul); + out = std::string_view{begin, len}; + self.pos += len + 1; + return true; + } + + template + requires io_cursor_detail::ReadSeek + bool read_zstring(this IoCursor& self, std::string& out) noexcept { + std::string_view view{}; + if (!self.read_zstring(view)) { + return false; + } + try { + out.assign(view); + } catch (...) { + self.poison(); + return false; + } + return true; + } + + template + requires io_cursor_detail::WriteSeek + bool write(this IoCursor& self, const T& value) noexcept { + if (self.poisoned) { + return false; + } + const std::span bytes = std::as_bytes(std::span{std::addressof(value), 1}); + size_t num_bytes = 0; + try { + num_bytes = self.storage.write(self.pos, bytes); + } catch (...) { + goto POISON; + } + if (num_bytes != sizeof(T)) { + POISON: + self.poison(); + return false; + } + self.pos += num_bytes; + return true; + } + + template + requires io_cursor_detail::WriteSeek + bool write_zstring(this IoCursor& self, const std::string_view text) noexcept { + if (self.poisoned) { + return false; + } + size_t num_bytes = 0; + try { + num_bytes = self.storage.write(self.pos, std::span{text}); + } catch (...) { + goto POISON; + } + if (num_bytes != text.size()) { + POISON: + self.poison(); + return false; + } + self.pos += num_bytes; + constexpr char NUL = '\0'; + try { + if (self.storage.write(self.pos, std::span{&NUL, 1}) != sizeof(char)) { + goto POISON; + } + } catch (...) { + goto POISON; + } + self.pos += 1; + return true; + } + + [[nodiscard]] + bool is_poisoned(this const IoCursor& self) noexcept { + return self.poisoned; + } + + [[nodiscard]] + size_t position(this const IoCursor& self) noexcept { + return self.pos; + } + + template + requires io_cursor_detail::ReadSeek || io_cursor_detail::WriteSeek + [[nodiscard]] + size_t size(this const IoCursor& self) noexcept { + return self.storage.size(); + } + +private: + void poison(this IoCursor& self) noexcept { + self.poisoned = true; + } + + Storage storage; + size_t pos; + bool poisoned; +}; + +template +IoCursor(const char (&)[N]) -> IoCursor; +IoCursor(std::span) -> IoCursor; +IoCursor(const std::vector&) -> IoCursor; +IoCursor(const void*, size_t) -> IoCursor; + +template +IoCursor(char (&)[N]) -> IoCursor; +IoCursor(std::span) -> IoCursor; +IoCursor(void*, size_t) -> IoCursor; + +IoCursor(std::vector&) -> IoCursor; diff --git a/game_patch/purefaction/pf.cpp b/game_patch/purefaction/pf.cpp index a651c29e..bfc3dce5 100644 --- a/game_patch/purefaction/pf.cpp +++ b/game_patch/purefaction/pf.cpp @@ -36,7 +36,7 @@ void send_pf_player_stats_packet(rf::Player* player) return; } - std::byte packet_buf[rf::max_packet_size]; + std::byte packet_buf[rf::MAX_PACKET_SIZE]; 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); diff --git a/game_patch/rf/multi.h b/game_patch/rf/multi.h index a7a9877f..5908949f 100644 --- a/game_patch/rf/multi.h +++ b/game_patch/rf/multi.h @@ -242,7 +242,7 @@ namespace rf #pragma pack(pop) static_assert(sizeof(NetReliableSocket) == 0x6BF); - constexpr size_t max_packet_size = 512; + constexpr size_t MAX_PACKET_SIZE = 512; static auto& multi_get_game_type = addr_as_ref(0x00470770); static auto& multi_io_send = addr_as_ref(0x00479370);