vec3n 6 3x packn
vec3u 6 3x packu
quat 8 4x packn float
-entity state 50 [ 0] vec3i chunk pos (there's a variation where this is a vec3b)
+entity state 42 [ 0] vec3i chunk pos (there's a variation where this is a vec3b)
[12] vec3u block pos by 16,
[18] vec3 velocity,
[30] quat orientation,
- [38] 12 reserved bytes (used to be angular velocity)
+ [38] packn pitch by PI/2
+ [40] packn yaw by PI
Packets
Payload:
0 entity ID of the player, 32bit unsigned int
4 entity state of the player
- 54 name of the world the server's currently running
+ 46 name of the world the server's currently running
max 32 byte UTF-8 string
-Length: 54-86
+Length: 47-78
Part
Code: 4
Payload:
0 player's entity state as predicted by the client
- 50 movement input, vec3n
- 56 pitch input by PI/2, packn
- 58 yaw input by PI, packn
- 60 active actions, 8bit bit field, first three bits are primary, secondary, and tertiary
- 61 selected inventory slot, 8bit unsigned int
-Length: 62
+ 42 movement input, vec3n
+ 48 active actions, 8bit bit field, first three bits are primary, secondary, and tertiary
+ 49 selected inventory slot, 8bit unsigned int
+Length: 50
Spawn Entity
0 entity ID, 32bit unsigned int
4 entity's model ID, 32bit unsigned int
8 entity state
- 58 bounding box of the entity, 6x 32bit float
- 82 flags, 32bit bitfield with boolean values
+ 50 bounding box of the entity, 6x 32bit float
+ 74 flags, 32bit bitfield with boolean values
1: world collision
- 86 entity name, max 32 byte UTF-8 string
-Length: 87 - 118
+ 78 entity name, max 32 byte UTF-8 string
+Length: 79 - 110
Despawn Entity
Code: 7
Payload:
- 0 number of entities, 32bit int, 1-10
+ 0 number of entities, 32bit int, 1-12
4 base for chunk coordinates, vec3i
16 entity ID, 32bit unsigned int
20 entity state with vec3b as chunk position (rather than vec3i)
- 62 next entity...
-Length: 16 + multiple of 45, max 466
+ 53 next entity...
+Length: 16 + multiple of 37, max 460
Player Correction
Payload:
0 sequence number of the offending packet, 16bit unsigned int
2 entity state of the player's entity on the server
-Length: 52
+Length: 44
Chunk Begin
in.Skip(Token::EQUALS);
if (prop_name == "root") {
model.RootPart().Read(in, tex_index, shapes);
+ } else if (prop_name == "body") {
+ model.SetBody(in.GetULong());
} else if (prop_name == "eyes") {
model.SetEyes(in.GetULong());
} else {
auto pack = Packet::Make<Packet::PlayerUpdate>(client_pack);
pack.WritePredictedState(prediction);
pack.WriteMovement(movement);
- pack.WritePitch(pitch);
- pack.WriteYaw(yaw);
pack.WriteActions(actions);
pack.WriteSlot(slot);
return conn.Send(client_pack, client_sock);
in.ReadVec(state.block_pos);
} else if (name == "orientation") {
in.ReadQuat(state.orient);
+ } else if (name == "pitch") {
+ state.pitch = in.GetFloat();
+ } else if (name == "yaw") {
+ state.yaw = in.GetFloat();
} else if (name == "slot") {
int slot;
in.ReadNumber(slot);
out << "chunk = " << state.chunk_pos << ';' << endl;
out << "position = " << state.block_pos << ';' << endl;
out << "orientation = " << state.orient << ';' << endl;
+ out << "pitch = " << state.pitch << ';' << endl;
+ out << "yaw = " << state.yaw << ';' << endl;
out << "slot = " << player.GetInventorySlot() << ';' << endl;
}
operator bool() const noexcept { return model; }
const Model &GetModel() const noexcept { return *model; }
+ glm::mat4 BodyTransform() const noexcept;
+ Part::State &BodyState() noexcept;
+
glm::mat4 EyesTransform() const noexcept;
Part::State &EyesState() noexcept;
Part &GetPart(std::size_t i) noexcept { return *part[i]; }
const Part &GetPart(std::size_t i) const noexcept { return *part[i]; }
+ void SetBody(std::uint16_t id) { body_id = id; }
+ const Part &GetBodyPart() const noexcept { return GetPart(body_id); }
+
void SetEyes(std::uint16_t id) { eyes_id = id; }
const Part &GetEyesPart() const noexcept { return GetPart(eyes_id); }
std::uint32_t id;
Part root;
std::vector<Part *> part;
+ std::uint16_t body_id;
std::uint16_t eyes_id;
};
}
+Part::State &Instance::BodyState() noexcept {
+ return state[model->GetBodyPart().ID()];
+}
+
+glm::mat4 Instance::BodyTransform() const noexcept {
+ return model->GetBodyPart().GlobalTransform(*this);
+}
+
Part::State &Instance::EyesState() noexcept {
return state[model->GetEyesPart().ID()];
}
: id(0)
, root()
, part()
+, body_id(0)
, eyes_id(0) {
}
struct Join : public Payload {
static constexpr std::uint8_t TYPE = 2;
- static constexpr std::size_t MAX_LEN = 86;
+ static constexpr std::size_t MAX_LEN = 78;
void WritePlayer(const Entity &) noexcept;
void ReadPlayerID(std::uint32_t &) const noexcept;
struct PlayerUpdate : public Payload {
static constexpr std::uint8_t TYPE = 4;
- static constexpr std::size_t MAX_LEN = 62;
+ static constexpr std::size_t MAX_LEN = 50;
void WritePredictedState(const EntityState &) noexcept;
void ReadPredictedState(EntityState &) const noexcept;
void WriteMovement(const glm::vec3 &) noexcept;
void ReadMovement(glm::vec3 &) const noexcept;
- void WritePitch(float) noexcept;
- void ReadPitch(float &) const noexcept;
- void WriteYaw(float) noexcept;
- void ReadYaw(float &) const noexcept;
void WriteActions(std::uint8_t) noexcept;
void ReadActions(std::uint8_t &) const noexcept;
void WriteSlot(std::uint8_t) noexcept;
struct SpawnEntity : public Payload {
static constexpr std::uint8_t TYPE = 5;
- static constexpr std::size_t MAX_LEN = 118;
+ static constexpr std::size_t MAX_LEN = 110;
void WriteEntity(const Entity &) noexcept;
void ReadEntityID(std::uint32_t &) const noexcept;
struct EntityUpdate : public Payload {
static constexpr std::uint8_t TYPE = 7;
- static constexpr std::size_t MAX_LEN = 466;
+ static constexpr std::size_t MAX_LEN = 460;
- static constexpr std::uint32_t MAX_ENTITIES = 10;
+ static constexpr std::uint32_t MAX_ENTITIES = 12;
static constexpr std::size_t GetSize(std::uint32_t num) noexcept {
- return 16 + (num * 45);
+ return 16 + (num * 37);
}
void WriteEntityCount(std::uint32_t) noexcept;
struct PlayerCorrection : public Payload {
static constexpr std::uint8_t TYPE = 8;
- static constexpr std::size_t MAX_LEN = 52;
+ static constexpr std::size_t MAX_LEN = 44;
void WritePacketSeq(std::uint16_t) noexcept;
void ReadPacketSeq(std::uint16_t &) const noexcept;
WritePackU(state.block_pos * (1.0f / 16.0f), off + 12);
Write(state.velocity, off + 18);
Write(state.orient, off + 30);
+ WritePackN(state.pitch * PI_0p5_inv, off + 38);
+ WritePackN(state.yaw * PI_inv, off + 40);
}
void Packet::Payload::Read(EntityState &state, size_t off) const noexcept {
ReadPackU(state.block_pos, off + 12);
Read(state.velocity, off + 18);
Read(state.orient, off + 30);
+ ReadPackN(state.pitch, off + 38);
+ ReadPackN(state.yaw, off + 40);
state.block_pos *= 16.0f;
+ state.pitch *= PI_0p5;
+ state.yaw *= PI;
}
void Packet::Payload::Write(const EntityState &state, const glm::ivec3 &base, size_t off) noexcept {
WritePackU(state.block_pos * (1.0f / 16.0f), off + 3);
Write(state.velocity, off + 9);
Write(state.orient, off + 21);
+ WritePackN(state.pitch * PI_0p5_inv, off + 29);
+ WritePackN(state.yaw * PI_inv, off + 31);
}
void Packet::Payload::Read(EntityState &state, const glm::ivec3 &base, size_t off) const noexcept {
ReadPackU(state.block_pos, off + 3);
Read(state.velocity, off + 9);
Read(state.orient, off + 21);
+ ReadPackN(state.pitch, off + 29);
+ ReadPackN(state.yaw, off + 31);
state.chunk_pos += base;
state.block_pos *= 16.0f;
-}
-
-void Packet::Payload::WritePackN(float val, size_t off) noexcept {
- int16_t raw = glm::clamp(glm::round(val * 32767.0f), -32767.0f, 32767.0f);
- Write(raw, off);
+ state.pitch *= PI_0p5;
+ state.yaw *= PI;
}
void Packet::Payload::WritePackB(const glm::ivec3 &val, size_t off) noexcept {
val.z = conv;
}
+void Packet::Payload::WritePackN(float val, size_t off) noexcept {
+ int16_t raw = glm::clamp(glm::round(val * 32767.0f), -32767.0f, 32767.0f);
+ Write(raw, off);
+}
+
void Packet::Payload::ReadPackN(float &val, size_t off) const noexcept {
int16_t raw = 0;
Read(raw, off);
}
void Packet::Join::WriteWorldName(const string &name) noexcept {
- WriteString(name, 54, 32);
+ WriteString(name, 46, 32);
}
void Packet::Join::ReadWorldName(string &name) const noexcept {
- ReadString(name, 54, 32);
+ ReadString(name, 46, 32);
}
void Packet::PlayerUpdate::WritePredictedState(const EntityState &state) noexcept {
}
void Packet::PlayerUpdate::WriteMovement(const glm::vec3 &mov) noexcept {
- WritePackN(mov, 50);
+ WritePackN(mov, 42);
}
void Packet::PlayerUpdate::ReadMovement(glm::vec3 &mov) const noexcept {
- ReadPackN(mov, 50);
-}
-
-void Packet::PlayerUpdate::WritePitch(float pitch) noexcept {
- float conv = pitch * PI_0p5_inv;
- WritePackN(conv, 56);
-}
-
-void Packet::PlayerUpdate::ReadPitch(float &pitch) const noexcept {
- ReadPackN(pitch, 56);
- pitch *= PI_0p5;
-}
-
-void Packet::PlayerUpdate::WriteYaw(float yaw) noexcept {
- float conv = yaw * PI_inv;
- WritePackN(conv, 58);
-}
-
-void Packet::PlayerUpdate::ReadYaw(float &yaw) const noexcept {
- ReadPackN(yaw, 58);
- yaw *= PI;
+ ReadPackN(mov, 42);
}
void Packet::PlayerUpdate::WriteActions(uint8_t actions) noexcept {
- Write(actions, 60);
+ Write(actions, 48);
}
void Packet::PlayerUpdate::ReadActions(uint8_t &actions) const noexcept {
- Read(actions, 60);
+ Read(actions, 48);
}
void Packet::PlayerUpdate::WriteSlot(uint8_t slot) noexcept {
- Write(slot, 61);
+ Write(slot, 49);
}
void Packet::PlayerUpdate::ReadSlot(uint8_t &slot) const noexcept {
- Read(slot, 61);
+ Read(slot, 49);
}
void Packet::SpawnEntity::WriteEntity(const Entity &e) noexcept {
Write(uint32_t(0), 4);
}
Write(e.GetState(), 8);
- Write(e.Bounds(), 58);
+ Write(e.Bounds(), 50);
uint32_t flags = 0;
if (e.WorldCollidable()) {
flags |= 1;
}
- Write(flags, 82);
- WriteString(e.Name(), 86, 32);
+ Write(flags, 74);
+ WriteString(e.Name(), 78, 32);
}
void Packet::SpawnEntity::ReadEntityID(uint32_t &id) const noexcept {
string name;
Read(state, 8);
- Read(bounds, 58);
- Read(flags, 82);
- ReadString(name, 86, 32);
+ Read(bounds, 50);
+ Read(flags, 74);
+ ReadString(name, 78, 32);
e.SetState(state);
e.Bounds(bounds);
return;
}
glm::vec3 movement(0.0f);
- float pitch = 0.0f;
- float yaw = 0.0f;
uint8_t new_actions;
uint8_t slot;
player_update_pack = pack.Seq();
pack.ReadPredictedState(player_update_state);
pack.ReadMovement(movement);
- pack.ReadPitch(pitch);
- pack.ReadYaw(yaw);
pack.ReadActions(new_actions);
pack.ReadSlot(slot);
input->SetMovement(movement);
- input->TurnHead(pitch - input->GetPitch(), yaw - input->GetYaw());
+ input->TurnHead(player_update_state.pitch - input->GetPitch(), player_update_state.yaw - input->GetYaw());
input->SelectInventory(slot);
if ((new_actions & 0x01) && !(old_actions & 0x01)) {
void TurnHead(float pitch, float yaw) noexcept;
/// get player pitch in radians, normalized to [-PI/2,PI/2]
- float GetPitch() const noexcept { return pitch; }
+ float GetPitch() const noexcept;
/// get player yaw in radians, normalized to [-PI,PI]
- float GetYaw() const noexcept { return yaw; }
+ float GetYaw() const noexcept;
/// start doing primary action
/// what exactly this means depends on the active item
World &world;
Player &player;
glm::vec3 move_dir;
- float pitch;
- float yaw;
bool dirty;
WorldCollision aim_world;
: world(world)
, player(player)
, move_dir(0.0f)
-, pitch(0.0f)
-, yaw(0.0f)
, dirty(true)
, aim_world()
, aim_entity() {
}
void PlayerController::TurnHead(float dp, float dy) noexcept {
- pitch += dp;
- if (pitch > PI / 2) {
- pitch = PI / 2;
- } else if (pitch < -PI / 2) {
- pitch = -PI / 2;
- }
- yaw += dy;
- if (yaw > PI) {
- yaw -= PI * 2;
- } else if (yaw < -PI) {
- yaw += PI * 2;
- }
- Invalidate();
+ player.GetEntity().TurnHead(dp, dy);
+}
+
+float PlayerController::GetPitch() const noexcept {
+ return player.GetEntity().Pitch();
+}
+
+float PlayerController::GetYaw() const noexcept {
+ return player.GetEntity().Yaw();
}
void PlayerController::SelectInventory(int i) noexcept {
void PlayerController::UpdatePlayer() noexcept {
constexpr float max_vel = 5.0f; // in m/s
if (dirty) {
- player.GetEntity().Orientation(glm::quat(glm::vec3(0.0f, yaw, 0.0f)));
- player.GetEntity().GetModel().EyesState().orientation = glm::quat(glm::vec3(pitch, 0.0f, 0.0f));
- player.GetEntity().TargetVelocity(glm::rotateY(move_dir * max_vel, yaw));
+ player.GetEntity().TargetVelocity(glm::rotateY(move_dir * max_vel, player.GetEntity().Yaw()));
Ray aim = player.Aim();
if (!world.Intersection(aim, glm::mat4(1.0f), player.GetEntity().ChunkCoords(), aim_world)) {
bool WorldCollidable() const noexcept { return world_collision; }
void WorldCollidable(bool b) noexcept { world_collision = b; }
+ /// desired velocity in local coordinate system
const glm::vec3 &TargetVelocity() const noexcept { return tgt_vel; }
void TargetVelocity(const glm::vec3 &v) noexcept { tgt_vel = v; }
return state.Diff(other.state);
}
+ /// orientation of local coordinate system
const glm::quat &Orientation() const noexcept { return state.orient; }
void Orientation(const glm::quat &o) noexcept { state.orient = o; }
+ /// orientation of head within local coordinate system, in radians
+ float Pitch() const noexcept { return state.pitch; }
+ float Yaw() const noexcept { return state.yaw; }
+ void TurnHead(float delta_pitch, float delta_yaw) noexcept;
+ void SetHead(float pitch, float yaw) noexcept;
+
/// get a transform for this entity's coordinate space
glm::mat4 Transform(const glm::ivec3 &reference) const noexcept;
/// get a transform for this entity's view space
if (model) model.Render(M, prog);
}
+private:
+ void UpdateModel() noexcept;
+
private:
Instance model;
glm::vec3 velocity;
glm::quat orient;
+ float pitch;
+ float yaw;
EntityState();
/// make sure block_pos is within chunk bounds
void AdjustPosition() noexcept;
+ /// make sure pitch and yaw are normalized
+ void AdjustHeading() noexcept;
/// get a position vector relative to the (0,0,0) chunk
glm::vec3 AbsolutePosition() const noexcept {
state.AdjustPosition();
}
+void Entity::TurnHead(float dp, float dy) noexcept {
+ SetHead(state.pitch + dp, state.yaw + dy);
+}
+
+void Entity::SetHead(float p, float y) noexcept {
+ state.pitch = p;
+ state.yaw = y;
+ // TODO: I feel like this could be delayed
+ UpdateModel();
+}
+
glm::mat4 Entity::Transform(const glm::ivec3 &reference) const noexcept {
return state.Transform(reference);
}
return Ray{ glm::vec3(from), glm::normalize(glm::vec3(to - from)) };
}
+void Entity::UpdateModel() noexcept {
+ state.AdjustHeading();
+ if (model) {
+ Part::State &body_state = model.BodyState();
+ Part::State &eyes_state = model.EyesState();
+ if (&body_state != &eyes_state) {
+ body_state.orientation = glm::quat(glm::vec3(0.0f, state.yaw, 0.0f));
+ eyes_state.orientation = glm::quat(glm::vec3(state.pitch, 0.0f, 0.0f));
+ } else {
+ eyes_state.orientation = glm::quat(glm::vec3(state.pitch, state.yaw, 0.0f));
+ }
+ }
+}
+
EntityState::EntityState()
: chunk_pos(0)
, block_pos(0.0f)
, velocity(0.0f)
-, orient(1.0f, 0.0f, 0.0f, 0.0f) {
+, orient(1.0f, 0.0f, 0.0f, 0.0f)
+, pitch(0.0f)
+, yaw(0.0f) {
}
}
}
+void EntityState::AdjustHeading() noexcept {
+ while (pitch > PI / 2) {
+ pitch = PI / 2;
+ }
+ while (pitch < -PI / 2) {
+ pitch = -PI / 2;
+ }
+ while (yaw > PI) {
+ yaw -= PI * 2;
+ }
+ while (yaw < -PI) {
+ yaw += PI * 2;
+ }
+}
+
glm::mat4 EntityState::Transform(const glm::ivec3 &reference) const noexcept {
const glm::vec3 translation = RelativePosition(reference);
glm::mat4 transform(toMat4(orient));
}
Entity &entity = AddEntity();
entity.Name(name);
- entity.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
+ entity.Bounds({ { -0.4f, -0.9f, -0.4f }, { 0.4f, 0.9f, 0.4f } });
entity.WorldCollidable(true);
ChunkIndex &index = chunks.MakeIndex(entity.ChunkCoords(), 6);
players.emplace_back(entity, index);
return nullptr;
}
entity->Name(name);
- entity->Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
+ entity->Bounds({ { -0.4f, -0.9f, -0.4f }, { 0.4f, 0.9f, 0.4f } });
entity->WorldCollidable(true);
ChunkIndex &index = chunks.MakeIndex(entity->ChunkCoords(), 6);
players.emplace_back(*entity, index);
void PacketTest::testJoin() {
auto pack = Packet::Make<Packet::Join>(udp_pack);
- AssertPacket("Join", 2, 54, 86, pack);
+ AssertPacket("Join", 2, 47, 78, pack);
Entity write_entity;
write_entity.ID(534574);
write_entity.GetState().block_pos = { 1.5f, 0.9f, 12.0f };
write_entity.GetState().velocity = { 0.025f, 0.001f, 0.0f };
write_entity.GetState().orient = { 1.0f, 0.0f, 0.0f, 0.0f };
+ write_entity.GetState().pitch = 0.3f;
+ write_entity.GetState().yaw = -2.3f;
uint32_t read_id = 0;
EntityState read_state;
pack.WritePlayer(write_entity);
void PacketTest::testPlayerUpdate() {
auto pack = Packet::Make<Packet::PlayerUpdate>(udp_pack);
- AssertPacket("PlayerUpdate", 4, 62, pack);
+ AssertPacket("PlayerUpdate", 4, 50, pack);
EntityState write_state;
write_state.chunk_pos = { 7, 2, -3 };
write_state.velocity = { 0.025f, 0.001f, 0.0f };
write_state.orient = { 1.0f, 0.0f, 0.0f, 0.0f };
glm::vec3 write_movement(0.5f, -1.0f, 1.0f);
- float write_pitch = 1.25f;
- float write_yaw = -2.5f;
uint8_t write_actions = 0x05;
uint8_t write_slot = 3;
pack.WritePredictedState(write_state);
pack.WriteMovement(write_movement);
- pack.WritePitch(write_pitch);
- pack.WriteYaw(write_yaw);
pack.WriteActions(write_actions);
pack.WriteSlot(write_slot);
EntityState read_state;
glm::vec3 read_movement;
- float read_pitch;
- float read_yaw;
uint8_t read_actions;
uint8_t read_slot;
pack.ReadPredictedState(read_state);
pack.ReadMovement(read_movement);
- pack.ReadPitch(read_pitch);
- pack.ReadYaw(read_yaw);
pack.ReadActions(read_actions);
pack.ReadSlot(read_slot);
AssertEqual(
"player movement input not correctly transported in PlayerUpdate packet",
write_movement, read_movement, 0.0001f
);
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
- "player pitch input not correctly transported in PlayerUpdate packet",
- write_pitch, read_pitch, 0.0001f
- );
- CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
- "player yaw input not correctly transported in PlayerUpdate packet",
- write_yaw, read_yaw, 0.0001f
- );
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"player actions not correctly transported in PlayerUpdate packet",
int(write_actions), int(read_actions)
void PacketTest::testSpawnEntity() {
auto pack = Packet::Make<Packet::SpawnEntity>(udp_pack);
- AssertPacket("SpawnEntity", 5, 87, 118, pack);
+ AssertPacket("SpawnEntity", 5, 79, 110, pack);
Entity write_entity;
write_entity.ID(534574);
write_entity.GetState().block_pos = { 1.5f, 0.9f, 12.0f };
write_entity.GetState().velocity = { 0.025f, 0.001f, 0.0f };
write_entity.GetState().orient = { 1.0f, 0.0f, 0.0f, 0.0f };
+ write_entity.GetState().pitch = 0.3f;
+ write_entity.GetState().yaw = -2.3f;
write_entity.Bounds({{ -1, -1, -1 }, { 1, 1, 1 }});
write_entity.WorldCollidable(true);
write_entity.Name("blah");
void PacketTest::testEntityUpdate() {
auto pack = Packet::Make<Packet::EntityUpdate>(udp_pack);
- AssertPacket("EntityUpdate", 7, 16, 466, pack);
+ AssertPacket("EntityUpdate", 7, 16, 460, pack);
pack.length = Packet::EntityUpdate::GetSize(3);
CPPUNIT_ASSERT_EQUAL_MESSAGE(
"length not correctly set in EntityUpdate packet",
- size_t(16 + 3 * 45), pack.length
+ size_t(16 + 3 * 37), pack.length
);
uint32_t write_count = 3;
write_entity.GetState().block_pos = { 1.5f, 0.9f, 12.0f };
write_entity.GetState().velocity = { 0.025f, 0.001f, 0.0f };
write_entity.GetState().orient = { 1.0f, 0.0f, 0.0f, 0.0f };
+ write_entity.GetState().pitch = 0.3f;
+ write_entity.GetState().yaw = -2.3f;
pack.WriteEntity(write_entity, write_base, 1);
pack.WriteEntity(write_entity, write_base, 0);
pack.WriteEntity(write_entity, write_base, 2);
void PacketTest::testPlayerCorrection() {
auto pack = Packet::Make<Packet::PlayerCorrection>(udp_pack);
- AssertPacket("PlayerCorrection", 8, 52, pack);
+ AssertPacket("PlayerCorrection", 8, 44, pack);
uint16_t write_seq = 50050;
uint16_t read_seq;
write_entity.GetState().block_pos = { 1.5f, 0.9f, 12.0f };
write_entity.GetState().velocity = { 0.025f, 0.001f, 0.0f };
write_entity.GetState().orient = { 1.0f, 0.0f, 0.0f, 0.0f };
+ write_entity.GetState().pitch = 0.3f;
+ write_entity.GetState().yaw = -2.3f;
pack.WritePlayer(write_entity);
EntityState read_state;
message + ": bad orientation",
expected.orient, actual.orient
);
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+ message + ": bad pitch",
+ expected.pitch, actual.pitch, PI/65534.0f
+ );
+ CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE(
+ message + ": bad yaw",
+ expected.yaw, actual.yaw, PI/32767.0f
+ );
}
void PacketTest::AssertEqual(