From 933ca0fe6c660e482edd45742d981f2de59a32df Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Thu, 1 Oct 2015 13:20:54 +0200 Subject: [PATCH] exchange block updates with clients --- doc/protocol | 15 ++++++ src/client/InteractiveState.hpp | 2 + src/client/MasterState.hpp | 1 + src/client/client.cpp | 30 ++++++++++++ src/net/ConnectionHandler.hpp | 1 + src/net/Packet.hpp | 21 +++++++++ src/net/net.cpp | 42 +++++++++++++++++ src/server/ClientConnection.hpp | 2 + src/server/net.cpp | 20 ++++++-- tst/net/PacketTest.cpp | 81 ++++++++++++++++++++++++++++++++- tst/net/PacketTest.hpp | 4 ++ 11 files changed, 215 insertions(+), 4 deletions(-) diff --git a/doc/protocol b/doc/protocol index a707d25..3347158 100644 --- a/doc/protocol +++ b/doc/protocol @@ -181,3 +181,18 @@ Payload: 8 block size, size of the data block, 32bit unsigned int 12 data, raw data Length: 12-484 + + +Block Update +------------ + +Sent by the server whenever one or more block in a chunk have changed. + +Code: 11 +Payload: + 0 chunk coordinates, vec3i + 12 number of blocks, 32bit unsigned int, 1-78 + 16 first block index, 16bit unsigned int + 18 first block data, 32bit + 22 second block index... +Length: 16 + multiple of 6, max 484 diff --git a/src/client/InteractiveState.hpp b/src/client/InteractiveState.hpp index 1129e99..2db2270 100644 --- a/src/client/InteractiveState.hpp +++ b/src/client/InteractiveState.hpp @@ -11,6 +11,7 @@ #include "../graphics/SkyBox.hpp" #include "../io/WorldSave.hpp" #include "../model/Skeletons.hpp" +#include "../net/Packet.hpp" #include "../ui/HUD.hpp" #include "../ui/InteractiveManipulator.hpp" #include "../ui/Interface.hpp" @@ -48,6 +49,7 @@ public: void Render(Viewport &) override; void MergePlayerCorrection(std::uint16_t, const EntityState &); + void Handle(const Packet::BlockUpdate &); void SetAudio(bool) override; void SetVideo(bool) override; diff --git a/src/client/MasterState.hpp b/src/client/MasterState.hpp index 3c9b23c..1d69998 100644 --- a/src/client/MasterState.hpp +++ b/src/client/MasterState.hpp @@ -60,6 +60,7 @@ public: void On(const Packet::PlayerCorrection &) override; void On(const Packet::ChunkBegin &) override; void On(const Packet::ChunkData &) override; + void On(const Packet::BlockUpdate &) override; private: /// flag entity as updated by given packet diff --git a/src/client/client.cpp b/src/client/client.cpp index 524454b..91d6aed 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -8,6 +8,7 @@ #include "../app/TextureIndex.hpp" #include "../model/CompositeModel.hpp" #include "../io/WorldSave.hpp" +#include "../world/ChunkIndex.hpp" #include "../world/ChunkStore.hpp" #include @@ -214,6 +215,27 @@ void InteractiveState::MergePlayerCorrection(std::uint16_t pack, const EntitySta input.MergePlayerCorrection(pack, state); } +void InteractiveState::Handle(const Packet::BlockUpdate &pack) { + glm::ivec3 pos; + pack.ReadChunkCoords(pos); + Chunk *chunk = player.GetChunks().Get(pos); + if (!chunk) { + // this change doesn't concern us + return; + } + uint32_t count = 0; + pack.ReadBlockCount(count); + for (uint32_t i = 0; i < count; ++i) { + uint16_t index; + Block block; + pack.ReadIndex(index, i); + pack.ReadBlock(block, i); + if (index < Chunk::size && block.type < block_types.Size()) { + manip.SetBlock(*chunk, index, block); + } + } +} + void InteractiveState::SetAudio(bool b) { master.GetConfig().audio.enabled = b; if (b) { @@ -476,5 +498,13 @@ void MasterState::On(const Packet::ChunkData &pack) { state->GetChunkReceiver().Handle(pack); } +void MasterState::On(const Packet::BlockUpdate &pack) { + if (!state) { + cout << "received block update, but the world has not been created yet" << endl; + return; + } + state->Handle(pack); +} + } } diff --git a/src/net/ConnectionHandler.hpp b/src/net/ConnectionHandler.hpp index 942826b..0f0afda 100644 --- a/src/net/ConnectionHandler.hpp +++ b/src/net/ConnectionHandler.hpp @@ -41,6 +41,7 @@ private: virtual void On(const Packet::PlayerCorrection &) { } virtual void On(const Packet::ChunkBegin &) { } virtual void On(const Packet::ChunkData &) { } + virtual void On(const Packet::BlockUpdate &) { } private: unsigned int packets_lost; diff --git a/src/net/Packet.hpp b/src/net/Packet.hpp index b32b42b..7b9b69e 100644 --- a/src/net/Packet.hpp +++ b/src/net/Packet.hpp @@ -10,6 +10,7 @@ namespace blank { +class Block; class Entity; class EntityState; @@ -197,6 +198,26 @@ struct Packet { void ReadData(std::uint8_t *, std::size_t maxlen) const noexcept; }; + struct BlockUpdate : public Payload { + static constexpr std::uint8_t TYPE = 11; + static constexpr std::size_t MAX_LEN = 484; + + static constexpr std::uint32_t MAX_BLOCKS = 78; + static constexpr std::size_t GetSize(std::uint32_t num) noexcept { + return 16 + (num * 6); + } + + void WriteChunkCoords(const glm::ivec3 &) noexcept; + void ReadChunkCoords(glm::ivec3 &) const noexcept; + void WriteBlockCount(std::uint32_t) noexcept; + void ReadBlockCount(std::uint32_t &) const noexcept; + + void WriteIndex(std::uint16_t, std::uint32_t) noexcept; + void ReadIndex(std::uint16_t &, std::uint32_t) const noexcept; + void WriteBlock(const Block &, std::uint32_t) noexcept; + void ReadBlock(Block &, std::uint32_t) const noexcept; + }; + template PayloadType As() { diff --git a/src/net/net.cpp b/src/net/net.cpp index e766c76..58875e7 100644 --- a/src/net/net.cpp +++ b/src/net/net.cpp @@ -26,6 +26,7 @@ constexpr size_t Packet::EntityUpdate::MAX_LEN; constexpr size_t Packet::PlayerCorrection::MAX_LEN; constexpr size_t Packet::ChunkBegin::MAX_LEN; constexpr size_t Packet::ChunkData::MAX_LEN; +constexpr size_t Packet::BlockUpdate::MAX_LEN; Connection::Connection(const IPaddress &addr) : handler(nullptr) @@ -211,6 +212,8 @@ const char *Packet::Type2String(uint8_t t) noexcept { return "ChunkBegin"; case ChunkData::TYPE: return "ChunkData"; + case BlockUpdate::TYPE: + return "BlockUpdate"; default: return "Unknown"; } @@ -502,6 +505,42 @@ void Packet::ChunkData::ReadData(uint8_t *d, size_t l) const noexcept { memcpy(d, &data[12], len); } +void Packet::BlockUpdate::WriteChunkCoords(const glm::ivec3 &coords) noexcept { + Write(coords, 0); +} + +void Packet::BlockUpdate::ReadChunkCoords(glm::ivec3 &coords) const noexcept { + Read(coords, 0); +} + +void Packet::BlockUpdate::WriteBlockCount(uint32_t count) noexcept { + Write(count, 12); +} + +void Packet::BlockUpdate::ReadBlockCount(uint32_t &count) const noexcept { + Read(count, 12); +} + +void Packet::BlockUpdate::WriteIndex(uint16_t index, uint32_t num) noexcept { + uint32_t off = GetSize(num); + Write(index, off); +} + +void Packet::BlockUpdate::ReadIndex(uint16_t &index, uint32_t num) const noexcept { + uint32_t off = GetSize(num); + Read(index, off); +} + +void Packet::BlockUpdate::WriteBlock(const Block &block, uint32_t num) noexcept { + uint32_t off = GetSize(num) + 2; + Write(block, off); +} + +void Packet::BlockUpdate::ReadBlock(Block &block, uint32_t num) const noexcept { + uint32_t off = GetSize(num) + 2; + Read(block, off); +} + void ConnectionHandler::Handle(const UDPpacket &udp_pack) { const Packet &pack = *reinterpret_cast(udp_pack.data); @@ -539,6 +578,9 @@ void ConnectionHandler::Handle(const UDPpacket &udp_pack) { case Packet::ChunkData::TYPE: On(Packet::As(udp_pack)); break; + case Packet::BlockUpdate::TYPE: + On(Packet::As(udp_pack)); + break; default: // drop unknown or unhandled packets break; diff --git a/src/server/ClientConnection.hpp b/src/server/ClientConnection.hpp index 4927d1c..9aaae12 100644 --- a/src/server/ClientConnection.hpp +++ b/src/server/ClientConnection.hpp @@ -61,6 +61,8 @@ public: bool HasPlayerModel() const noexcept; const CompositeModel &GetPlayerModel() const noexcept; + bool ChunkInRange(const glm::ivec3 &) const noexcept; + private: struct SpawnStatus { // the entity in question diff --git a/src/server/net.cpp b/src/server/net.cpp index ea5b4d9..8b4448e 100644 --- a/src/server/net.cpp +++ b/src/server/net.cpp @@ -283,7 +283,7 @@ uint16_t ClientConnection::Send() { } uint16_t ClientConnection::Send(size_t len) { - server.GetPacket().len = len; + server.GetPacket().len = sizeof(Packet::Header) + len; return Send(); } @@ -555,6 +555,10 @@ void ClientConnection::On(const Packet::PlayerUpdate &pack) { old_actions = new_actions; } +bool ClientConnection::ChunkInRange(const glm::ivec3 &pos) const noexcept { + return HasPlayer() && PlayerChunks().InRange(pos); +} + Server::Server(const Config::Network &conf, World &world) : serv_sock(nullptr) @@ -645,8 +649,18 @@ const CompositeModel &Server::GetPlayerModel() const noexcept { void Server::SetBlock(Chunk &chunk, int index, const Block &block) { chunk.SetBlock(index, block); - // TODO: send to clients - // also TODO: batch chunk changes + // TODO: batch chunk changes + auto pack = Packet::Make(GetPacket()); + pack.WriteChunkCoords(chunk.Position()); + pack.WriteBlockCount(uint32_t(1)); + pack.WriteIndex(index, 0); + pack.WriteBlock(chunk.BlockAt(index), 0); + GetPacket().len = sizeof(Packet::Header) + Packet::BlockUpdate::GetSize(1); + for (ClientConnection &client : clients) { + if (client.ChunkInRange(chunk.Position())) { + client.Send(); + } + } } } diff --git a/tst/net/PacketTest.cpp b/tst/net/PacketTest.cpp index 42d8348..751421b 100644 --- a/tst/net/PacketTest.cpp +++ b/tst/net/PacketTest.cpp @@ -25,6 +25,25 @@ static constexpr uint32_t TEST_TAG = 0xFB1AB1AF; } +void PacketTest::testSizes() { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected size of vec3", + size_t(12), sizeof(glm::vec3) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected size of vec3i", + size_t(12), sizeof(glm::ivec3) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected size of quat", + size_t(16), sizeof(glm::quat) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected size of entity state", + size_t(64), sizeof(EntityState) + ); +} + void PacketTest::testControl() { Packet::TControl ctrl; ctrl.ack = 10; @@ -278,7 +297,7 @@ void PacketTest::testEntityUpdate() { pack.length = Packet::EntityUpdate::GetSize(3); CPPUNIT_ASSERT_EQUAL_MESSAGE( - "length not correctly set in DespawnEntity packet", + "length not correctly set in EntityUpdate packet", size_t(4 + 3 * 68), pack.length ); @@ -432,6 +451,66 @@ void PacketTest::testChunkData() { ); } +void PacketTest::testBlockUpdate() { + auto pack = Packet::Make(udp_pack); + AssertPacket("BlockUpdate", 11, 16, 484, pack); + + pack.length = Packet::BlockUpdate::GetSize(3); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "length not correctly set in BlockUpdate packet", + size_t(16 + 3 * 6), pack.length + ); + + glm::ivec3 write_coords(432, -325, 99998); + uint32_t write_count = 3; + uint16_t write_index = 432; + Block write_block(324, Block::FACE_DOWN, Block::TURN_AROUND); + + pack.WriteChunkCoords(write_coords); + pack.WriteBlockCount(write_count); + pack.WriteIndex(write_index, 1); + pack.WriteBlock(write_block, 1); + pack.WriteIndex(write_index, 0); + pack.WriteBlock(write_block, 0); + pack.WriteIndex(write_index, 2); + pack.WriteBlock(write_block, 2); + + glm::ivec3 read_coords; + uint32_t read_count; + uint16_t read_index; + Block read_block; + + pack.ReadChunkCoords(read_coords); + pack.ReadBlockCount(read_count); + pack.ReadIndex(read_index, 1); + pack.ReadBlock(read_block, 1); + + AssertEqual( + "chunk coordinates not correctly transported in BlockUpdate packet", + write_coords, read_coords + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "block count not correctly transported in BlockUpdate packet", + write_count, read_count + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "block index not correctly transported in BlockUpdate packet", + write_index, read_index + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "block type not correctly transported in BlockUpdate packet", + write_block.type, read_block.type + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "block face not correctly transported in BlockUpdate packet", + write_block.GetFace(), read_block.GetFace() + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "block turn not correctly transported in BlockUpdate packet", + write_block.GetTurn(), read_block.GetTurn() + ); +} + void PacketTest::AssertPacket( const string &name, diff --git a/tst/net/PacketTest.hpp b/tst/net/PacketTest.hpp index 89e15dd..32976c8 100644 --- a/tst/net/PacketTest.hpp +++ b/tst/net/PacketTest.hpp @@ -21,6 +21,7 @@ class PacketTest CPPUNIT_TEST_SUITE(PacketTest); +CPPUNIT_TEST(testSizes); CPPUNIT_TEST(testControl); CPPUNIT_TEST(testPing); CPPUNIT_TEST(testLogin); @@ -33,6 +34,7 @@ CPPUNIT_TEST(testEntityUpdate); CPPUNIT_TEST(testPlayerCorrection); CPPUNIT_TEST(testChunkBegin); CPPUNIT_TEST(testChunkData); +CPPUNIT_TEST(testBlockUpdate); CPPUNIT_TEST_SUITE_END(); @@ -40,6 +42,7 @@ public: void setUp(); void tearDown(); + void testSizes(); void testControl(); void testPing(); void testLogin(); @@ -52,6 +55,7 @@ public: void testPlayerCorrection(); void testChunkBegin(); void testChunkData(); + void testBlockUpdate(); private: static void AssertPacket( -- 2.39.2