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
#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"
void Render(Viewport &) override;
void MergePlayerCorrection(std::uint16_t, const EntityState &);
+ void Handle(const Packet::BlockUpdate &);
void SetAudio(bool) override;
void SetVideo(bool) override;
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
#include "../app/TextureIndex.hpp"
#include "../model/CompositeModel.hpp"
#include "../io/WorldSave.hpp"
+#include "../world/ChunkIndex.hpp"
#include "../world/ChunkStore.hpp"
#include <iostream>
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) {
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);
+}
+
}
}
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;
namespace blank {
+class Block;
class Entity;
class EntityState;
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<class PayloadType>
PayloadType As() {
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)
return "ChunkBegin";
case ChunkData::TYPE:
return "ChunkData";
+ case BlockUpdate::TYPE:
+ return "BlockUpdate";
default:
return "Unknown";
}
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<const Packet *>(udp_pack.data);
case Packet::ChunkData::TYPE:
On(Packet::As<Packet::ChunkData>(udp_pack));
break;
+ case Packet::BlockUpdate::TYPE:
+ On(Packet::As<Packet::BlockUpdate>(udp_pack));
+ break;
default:
// drop unknown or unhandled packets
break;
bool HasPlayerModel() const noexcept;
const CompositeModel &GetPlayerModel() const noexcept;
+ bool ChunkInRange(const glm::ivec3 &) const noexcept;
+
private:
struct SpawnStatus {
// the entity in question
}
uint16_t ClientConnection::Send(size_t len) {
- server.GetPacket().len = len;
+ server.GetPacket().len = sizeof(Packet::Header) + len;
return Send();
}
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)
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<Packet::BlockUpdate>(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();
+ }
+ }
}
}
}
+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;
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
);
);
}
+void PacketTest::testBlockUpdate() {
+ auto pack = Packet::Make<Packet::BlockUpdate>(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,
CPPUNIT_TEST_SUITE(PacketTest);
+CPPUNIT_TEST(testSizes);
CPPUNIT_TEST(testControl);
CPPUNIT_TEST(testPing);
CPPUNIT_TEST(testLogin);
CPPUNIT_TEST(testPlayerCorrection);
CPPUNIT_TEST(testChunkBegin);
CPPUNIT_TEST(testChunkData);
+CPPUNIT_TEST(testBlockUpdate);
CPPUNIT_TEST_SUITE_END();
void setUp();
void tearDown();
+ void testSizes();
void testControl();
void testPing();
void testLogin();
void testPlayerCorrection();
void testChunkBegin();
void testChunkData();
+ void testBlockUpdate();
private:
static void AssertPacket(