From fa318825ef0404816728f733484caa1bc23afa53 Mon Sep 17 00:00:00 2001 From: hovertank3d Date: Fri, 14 Nov 2025 20:05:59 +0100 Subject: george droid --- include/file/metadata.hpp | 54 +++++++++++++ include/file/world.hpp | 65 +++++++++++++++ include/net/client.hpp | 2 +- include/net/conn.hpp | 90 +++++++-------------- include/net/packet.hpp | 27 +++---- include/net/server.hpp | 2 +- include/net/types.hpp | 189 +------------------------------------------ include/util/bitset.hpp | 45 +++++++++++ include/util/io.hpp | 202 ++++++++++++++++++++++++++++++++++++++++++++++ include/version.hpp | 3 +- include/world/world.hpp | 12 --- 11 files changed, 410 insertions(+), 281 deletions(-) create mode 100644 include/file/metadata.hpp create mode 100644 include/file/world.hpp create mode 100644 include/util/io.hpp delete mode 100644 include/world/world.hpp (limited to 'include') diff --git a/include/file/metadata.hpp b/include/file/metadata.hpp new file mode 100644 index 0000000..8b9ec4f --- /dev/null +++ b/include/file/metadata.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "util/bitset.hpp" +#include "util/io.hpp" +#include +#include +#include + +namespace file { + +enum class filetype : uint8_t { + Map = 1, + World, + Player, + TOTAL, +}; + +struct metadata { + static constexpr auto magic = 27981915666277746uL; + static constexpr auto magic_mask = 0xFFFFFFFFFFFFFFuL; + static constexpr auto size = 20; + + enum class flags { + Favorite, + }; + + filetype ftype; + uint32_t revision; + bitset<64> flags; + + template + ssize_t read(io::serialized_io& io) + { + uint64_t filemagic; + auto bytes = io.read(filemagic); + if ((filemagic & magic_mask) != magic) { + throw std::runtime_error("not a relogic file"); + } + + uint8_t type = ((filemagic >> 56) & 0xFF); + if (type == 0 || type >= int(filetype::TOTAL)) { + throw std::runtime_error("unknown file type"); + } + + ftype = filetype(type); + + bytes += io.read(revision); + bytes += io.read(flags); + + return bytes; + } +}; + +} \ No newline at end of file diff --git a/include/file/world.hpp b/include/file/world.hpp new file mode 100644 index 0000000..300a3e0 --- /dev/null +++ b/include/file/world.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "file/metadata.hpp" +#include "util/bitset.hpp" +#include "util/io.hpp" +#include "version.hpp" + +namespace file { + +class world { + int32_t version; + // metadata meta; + + std::vector positions; + bitset<0> importance; + + int32_t seed; + std::array guid; + int32_t worldID; + int32_t world_left; + int32_t world_right; + int32_t world_top; + int32_t world_botton; + int32_t height; + int32_t width; + +public: + void load_header(std::ifstream& f) + { + } + + world(std::string filename) + { + if (!std::filesystem::exists(filename)) { + throw std::runtime_error("file not found"); + } + auto rw = io::serialized_io(io::file_io { filename }); + + rw.read(version); + if (version <= 88 || version > terraria_version) { + throw std::runtime_error("invalid world file version"); + } + std::cout << version << std::endl; + metadata meta; + + rw.read(meta); + + if (meta.ftype != filetype::World) { + throw std::runtime_error("expected world file"); + } + } +}; + +} \ No newline at end of file diff --git a/include/net/client.hpp b/include/net/client.hpp index 23d250b..87b0ea1 100644 --- a/include/net/client.hpp +++ b/include/net/client.hpp @@ -23,7 +23,7 @@ namespace net { class client { - struct sockaddr_in server_address { }; + struct sockaddr_in server_address {}; public: client(std::string hostname, int port) diff --git a/include/net/conn.hpp b/include/net/conn.hpp index 95992ed..6f57293 100644 --- a/include/net/conn.hpp +++ b/include/net/conn.hpp @@ -9,15 +9,17 @@ #include #include "net/packet.hpp" +#include "util/io.hpp" namespace net { class conn { - int sock_fd; + io::serialized_io rw; sockaddr_in conn_addr; socklen_t conn_addr_size; - using handler_list = std::vector)>>; + using handler = std::function)>; + using handler_list = std::vector; std::array handlers; std::map netmodule_handlers; @@ -27,72 +29,20 @@ private: uint16_t packet_size; uint8_t id; - recv(packet_size); - recv(id); + rw.read(packet_size); + rw.read(id); return { id, packet_size - 3 }; } public: conn(int sock_fd, sockaddr_in conn_addr, socklen_t conn_addr_size) - : sock_fd(sock_fd) + : rw(io::file_io(sock_fd)) , conn_addr(conn_addr) , conn_addr_size(conn_addr_size) { } - ~conn() - { - close(sock_fd); - } - - ssize_t send(std::span data) - { - auto bytes = ::send(sock_fd, data.data(), data.size_bytes(), 0); - if (bytes < 0) { - throw std::runtime_error(strerror(errno)); - } - return bytes; - } - - template - ssize_t send(T t) - { - t = to_little(t); - auto bytes = ::send(sock_fd, &t, sizeof(T), 0); - if (bytes < 0) { - throw std::runtime_error(strerror(errno)); - } - return bytes; - } - - ssize_t recv(std::span data) - { - if (data.size_bytes() == 0) { - return 0; - } - - auto bytes = ::recv(sock_fd, data.data(), data.size_bytes(), 0); - if (bytes <= 0) { - throw std::runtime_error(strerror(errno)); - } - return bytes; - } - - template - ssize_t recv(T& t) - { - auto bytes = ::recv(sock_fd, &t, sizeof(T), 0); - if (bytes <= 0) { - throw std::runtime_error(strerror(errno)); - } - t = to_little(t); - if (bytes != sizeof(T)) { - throw std::runtime_error(strerror(errno)); - } - return bytes; - } - template void expect_packet(T& t) { @@ -101,11 +51,17 @@ public: if (len > 0) { std::vector payload(len); - recv(payload); + rw.read(payload); decode_packet(payload, t); } } + template + void reg_handler(int id, H h) + { + handlers[id].push_back(h); + } + template void reg_handler(H h) { @@ -133,13 +89,21 @@ public: template void send_packet(T p) { - auto payload = encode_packet(p); + send_packet(T::packet_id, encode_packet(p)); + } - send(int16_t(payload.size() + 3)); - send(T::packet_id); + void send_packet(uint8_t id, std::span payload) + { + std::vector packet_data; + auto buffer = io::serialized_io(io::buffered_io(packet_data)); + + buffer.write(int16_t(payload.size() + 3)); + buffer.write(id); if (payload.size() > 0) { - send(payload); + buffer.write(payload); } + + rw.write(packet_data); } bool handle_netmodule(std::span payload) @@ -170,7 +134,7 @@ public: } std::vector payload(len); - recv(payload); + rw.read(payload); // got netmodule if (id == 82) { diff --git a/include/net/packet.hpp b/include/net/packet.hpp index 117fc15..950601e 100644 --- a/include/net/packet.hpp +++ b/include/net/packet.hpp @@ -17,6 +17,7 @@ #include #include "types.hpp" +#include "util/io.hpp" namespace net { @@ -27,28 +28,22 @@ using packet_header = std::tuple; template void decode_packet(std::span payload, T& t) { - std::size_t offset = 0; + std::size_t len = 0; - boost::pfr::for_each_field(t, [&payload, &offset](auto& field, int i) { - if (offset >= payload.size()) { - throw std::runtime_error("Received invalid packet"); - } - offset += decode_field(payload.subspan(offset), field); - }); + auto rd = io::serialized_io(io::buffered_io { payload }); + len = rd.read(t); - if (offset < payload.size()) { - throw std::runtime_error(std::format("{}: {}", offset, payload.size())); + if (len != payload.size()) { + throw std::runtime_error(std::format("{}: {}", len, payload.size())); } } template -std::vector encode_raw_packet(T& t) +std::vector encode_packet_impl(T& t) { std::vector payload; - - boost::pfr::for_each_field(t, [&payload](auto& field) { - encode_field(payload, field); - }); + auto wt = io::serialized_io(io::buffered_io { payload }); + wt.write(t); return payload; } @@ -56,7 +51,7 @@ std::vector encode_raw_packet(T& t) template std::vector encode_packet(T& t) { - return encode_raw_packet(t); + return encode_packet_impl(t); } template @@ -69,7 +64,7 @@ std::vector encode_packet(T& t) int ret, flush; z_stream strm; - auto raw_payload = encode_raw_packet(t); + auto raw_payload = encode_packet_impl(t); strm.zalloc = Z_NULL; strm.zfree = Z_NULL; diff --git a/include/net/server.hpp b/include/net/server.hpp index 30791fc..9b5e7c5 100644 --- a/include/net/server.hpp +++ b/include/net/server.hpp @@ -23,7 +23,7 @@ namespace net { class server { - struct sockaddr_in server_address { }; + struct sockaddr_in server_address {}; int sock_fd; public: diff --git a/include/net/types.hpp b/include/net/types.hpp index a4a8c3b..13c96e0 100644 --- a/include/net/types.hpp +++ b/include/net/types.hpp @@ -1,13 +1,11 @@ #pragma once -#include -#include #include -#include #include +#include +#include #include "util/bitset.hpp" -#include "util/endian.hpp" #include "version.hpp" namespace net { @@ -76,183 +74,6 @@ struct death_reason { std::string custom; }; -void encode_field(std::vector& data, const std::string& f) -{ - auto bytes = reinterpret_cast(f.data()); - data.insert(data.end(), uint8_t(f.size())); - data.insert(data.end(), bytes, bytes + f.size()); -} - -template -void encode_field(std::vector& data, T f) -{ - auto fixed = to_little(f); - auto* p = reinterpret_cast(&fixed); - data.insert(data.end(), p, p + sizeof(T)); -} - -template -void encode_field(std::vector& data, T f) -{ - auto x = reinterpret_cast(&f); - data.insert(data.end(), x, x + sizeof(T)); -} - -void encode_field(std::vector& data, nstring& s) -{ - data.insert(data.end(), uint8_t(s.mode)); - encode_field(data, s.s); -} - -void encode_field(std::vector& data, const rgb& f) -{ - encode_field(data, f.r); - encode_field(data, f.g); - encode_field(data, f.b); -} - -template -void encode_field(std::vector& data, std::array& f) -{ - for (auto& e : f) { - encode_field(data, e); - } -} - -template -void encode_field(std::vector& data, std::vector& f) -{ - for (auto& e : f) { - encode_field(data, e); - } -} - -template -void encode_field(std::vector& data, std::span& f) -{ - for (auto& e : f) { - encode_field(data, e); - } -} - -template -void encode_field(std::vector& data, bitset& f) -{ - for (auto& e : f) { - encode_field(data, e); - } -} - -void encode_field(std::vector& data, death_reason& f) -{ - encode_field(data, f.reasons); - if (f.reasons[death_reason::reason::Player]) { - encode_field(data, f.player_index); - } - if (f.reasons[death_reason::reason::NPC]) { - encode_field(data, f.npc_index); - } - if (f.reasons[death_reason::reason::Projectile]) { - encode_field(data, f.projectile_index); - } - if (f.reasons[death_reason::reason::Other]) { - encode_field(data, f.other_index); - } - if (f.reasons[death_reason::reason::ProjectileType]) { - encode_field(data, f.projectile_type); - } - if (f.reasons[death_reason::reason::Item]) { - encode_field(data, f.item_type); - } - if (f.reasons[death_reason::reason::ItemPrefix]) { - encode_field(data, f.item_prefix); - } - if (f.reasons[death_reason::reason::Custom]) { - encode_field(data, f.custom); - } -} - -ssize_t decode_field(std::span data, std::string& f) -{ - assert(data.size() > 0); - uint8_t size = uint8_t(data[0]); // FIXME: varint - - assert(data.size() >= std::size_t(size + 1)); - - f.resize(size); - f.assign(reinterpret_cast(data.data() + 1 /* FIXME: varint*/), size); - return size + 1; -} - -template -ssize_t decode_field(std::span data, T& f) -{ - assert(data.size() >= sizeof(T)); - std::memcpy(&f, data.data(), sizeof(T)); - f = to_little(f); - return sizeof(T); -} - -ssize_t decode_field(std::span data, std::vector& f) -{ - f.insert(f.begin(), data.begin(), data.end()); - return f.size(); -} - -template -ssize_t decode_field(std::span data, std::array& f) -{ - assert(data.size() >= sizeof(T) * sz); - - ssize_t offset = 0; - for (auto& e : f) { - offset += decode_field(data.subspan(offset), e); - } - - return offset; -} - -ssize_t decode_field(std::span data, death_reason& f) -{ - int offset = 0; - offset += decode_field(data, f.reasons); - if (f.reasons[0]) { - offset += decode_field(data.subspan(offset), f.player_index); - } - if (f.reasons[1]) { - offset += decode_field(data.subspan(offset), f.npc_index); - } - if (f.reasons[2]) { - offset += decode_field(data.subspan(offset), f.projectile_index); - } - if (f.reasons[3]) { - offset += decode_field(data.subspan(offset), f.other_index); - } - if (f.reasons[4]) { - offset += decode_field(data.subspan(offset), f.projectile_type); - } - if (f.reasons[5]) { - offset += decode_field(data.subspan(offset), f.item_type); - } - if (f.reasons[6]) { - offset += decode_field(data.subspan(offset), f.item_prefix); - } - if (f.reasons[7]) { - offset += decode_field(data.subspan(offset), f.custom); - } - - return offset; -} -ssize_t decode_field(std::span data, rgb& f) -{ - assert(data.size() >= 3); - - f.r = uint8_t(data[0]); - f.g = uint8_t(data[1]); - f.b = uint8_t(data[2]); - return 3; -} - struct conn_request { static constexpr uint8_t packet_id = 1; @@ -480,12 +301,6 @@ struct damage_player { int8_t cooldown_counter; }; -template -struct raw { - static constexpr uint8_t packet_id = id; - std::vector data; -}; - inline nstring operator""_ns(const char* str, std::size_t) { return nstring(str); diff --git a/include/util/bitset.hpp b/include/util/bitset.hpp index de5bd49..cd1dbd0 100644 --- a/include/util/bitset.hpp +++ b/include/util/bitset.hpp @@ -3,6 +3,7 @@ #include #include #include +#include constexpr int bits_ceil(int bits) { @@ -50,4 +51,48 @@ public: assert(i < flags); return bit_ref(*this, i / 8, (1 << (i % 8))); } +}; + +// FIXME: ?? +template <> +struct bitset<0> : std::vector { + using container_type = std::vector; + +private: + struct bit_ref { + bitset<0>& a; + int referenced_byte; + uint8_t referenced_bit_mask; + + bool operator=(bool b) + { + if (b) + a.at(referenced_byte) |= referenced_bit_mask; + else + a.at(referenced_byte) &= ~referenced_bit_mask; + return b; + } + + constexpr operator bool() const + { + return (a.at(referenced_byte) & referenced_bit_mask) > 0; + } + }; + +public: + template + constexpr bool operator[](T idx) const + { + unsigned int i = (unsigned int)(idx); + assert(i < container_type::size()); + return (container_type::at(i / 8) & (1 << (i % 8))) > 0; + } + + template + bit_ref operator[](T idx) + { + unsigned int i = (unsigned int)(idx); + assert(i < container_type::size()); + return bit_ref(*this, i / 8, (1 << (i % 8))); + } }; \ No newline at end of file diff --git a/include/util/io.hpp b/include/util/io.hpp new file mode 100644 index 0000000..8cfaf4c --- /dev/null +++ b/include/util/io.hpp @@ -0,0 +1,202 @@ +#pragma once + +#include "util/endian.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace io { + +template +concept custom_write = requires(T t) { + { t.write(K()) } -> std::same_as; +}; +template +concept custom_read = requires(T& t, K& k) { + { t.read(k) } -> std::same_as; +}; + +template +concept container = requires(T& t) { + { t.data() }; + { t.size() }; + { t.begin() }; + { t.end() }; +}; + +class file_io { + int fd; + +public: + file_io(int fd) + : fd(fd) + { + } + + file_io(std::string filename) + : fd(open(filename.c_str(), O_RDWR)) + { + if (fd < 0) + throw std::runtime_error(strerror(errno)); + } + + //~file_io() + //{ + // ::close(fd); + //} + + ssize_t read_data(char* buf, size_t nbytes) + { + auto bytes = ::read(fd, buf, nbytes); + assert(bytes >= 0); + return bytes; + } + + ssize_t write_data(const char* buf, size_t nbytes) + { + auto bytes = ::write(fd, buf, nbytes); + assert(bytes >= 0); + return bytes; + } +}; + +template +class buffered_io { + size_t cursor = 0; + T& buffer; + +public: + buffered_io(T& buffer) + : buffer(buffer) + { + } + + ssize_t read_data(char* buf, size_t nbytes) + { + auto bytes = nbytes > (buffer.size() - cursor) ? (buffer.size() - cursor) : nbytes; + + memcpy(buf, buffer.data() + cursor, bytes); + cursor += bytes; + return bytes; + } + + ssize_t write_data(const char* buf, size_t nbytes) + { + buffer.insert(buffer.end(), buf, buf + nbytes); + return nbytes; + } +}; + +template +class serialized_io { + I io; + +public: + serialized_io(I io) + : io(io) + { + } + + template + ssize_t read(T& f) + { + auto bytes = io.read_data(reinterpret_cast(&f), sizeof(T)); + f = to_little(f); + return bytes; + } + + template + ssize_t read(T& f) + { + auto bytes = io.read_data(reinterpret_cast(&f), sizeof(T)); + f = to_little(f); + return bytes; + } + + ssize_t read(std::string& f) + { + // FIXME: varint + uint8_t size; + auto bytes = read(size); + + f.resize(size); + bytes += io.read_data(f.data(), size); + return bytes; + } + + template > T> + ssize_t read(T& f) + { + return f.read(*this); + } + + template + ssize_t read(T& f) + { + return io.read_data(reinterpret_cast(f.data()), f.size() * sizeof(typename T::value_type)); + } + + template + ssize_t read(T& f) + { + ssize_t bytes = 0; + boost::pfr::for_each_field(f, [this, &bytes](auto& field) { + bytes += read(field); + }); + return bytes; + } + + template + ssize_t write(const T f) + { + auto x = to_little(f); + return io.write_data(reinterpret_cast(&x), sizeof(T)); + } + + template + ssize_t write(const T f) + { + // FIXME: endianness + return io.write_data(reinterpret_cast(&f), sizeof(T)); + } + + ssize_t write(const std::string f) + { + // FIXME: varint + auto bytes = write(f.size()); + bytes += io.write_data(f.data(), f.size()); + return bytes; + } + + template + ssize_t write(const T f) + { + return io.write_data(reinterpret_cast(f.data()), f.size() * sizeof(typename T::value_type)); + } + + template > T> + ssize_t write(T& f) + { + return f.write(*this); + } + + template + ssize_t write(const T f) + { + ssize_t bytes = 0; + boost::pfr::for_each_field(f, [this, &bytes](auto field) { + bytes += write(field); + }); + return bytes; + } +}; + +} \ No newline at end of file diff --git a/include/version.hpp b/include/version.hpp index b5f4856..b5ec59f 100644 --- a/include/version.hpp +++ b/include/version.hpp @@ -1,3 +1,4 @@ #pragma once -constexpr auto max_buffs = 44; \ No newline at end of file +constexpr auto max_buffs = 44; +constexpr auto terraria_version = 279; \ No newline at end of file diff --git a/include/world/world.hpp b/include/world/world.hpp deleted file mode 100644 index 1b2481b..0000000 --- a/include/world/world.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include - -namespace world { - -class world { - int32_t version; - -}; - -} \ No newline at end of file -- cgit v1.2.3