+ClientConnection::SpawnStatus::SpawnStatus(Entity &e)
+: entity(&e)
+, spawn_pack(-1)
+, despawn_pack(-1) {
+ entity->Ref();
+}
+
+ClientConnection::SpawnStatus::~SpawnStatus() {
+ entity->UnRef();
+}
+
+bool ClientConnection::CanSpawn(const Entity &e) const noexcept {
+ return
+ &e != player &&
+ !e.Dead() &&
+ manhattan_radius(e.ChunkCoords() - Player().ChunkCoords()) < 7;
+}
+
+bool ClientConnection::CanDespawn(const Entity &e) const noexcept {
+ return
+ e.Dead() ||
+ manhattan_radius(e.ChunkCoords() - Player().ChunkCoords()) > 7;
+}
+
+void ClientConnection::SendSpawn(SpawnStatus &status) {
+ // don't double spawn
+ if (status.spawn_pack != -1) return;
+
+ auto pack = Packet::Make<Packet::SpawnEntity>(server.GetPacket());
+ pack.WriteEntity(*status.entity);
+ status.spawn_pack = conn.Send(server.GetPacket(), server.GetSocket());
+ ++confirm_wait;
+}
+
+void ClientConnection::SendDespawn(SpawnStatus &status) {
+ // don't double despawn
+ if (status.despawn_pack != -1) return;
+
+ auto pack = Packet::Make<Packet::DespawnEntity>(server.GetPacket());
+ pack.WriteEntityID(status.entity->ID());
+ status.despawn_pack = conn.Send(server.GetPacket(), server.GetSocket());
+ ++confirm_wait;
+}
+
+void ClientConnection::SendUpdate(SpawnStatus &status) {
+ // don't send updates while spawn not ack'd or despawn sent
+ if (status.spawn_pack != -1 || status.despawn_pack != -1) return;
+
+ // TODO: pack entity updates
+ auto pack = Packet::Make<Packet::EntityUpdate>(server.GetPacket());
+ pack.WriteEntityCount(1);
+ pack.WriteEntity(*status.entity, 0);
+ server.GetPacket().len = Packet::EntityUpdate::GetSize(1);
+ conn.Send(server.GetPacket(), server.GetSocket());
+}
+
+void ClientConnection::CheckPlayerFix() {
+ // player_update_state's position holds the client's most recent prediction
+ glm::vec3 diff = player_update_state.Diff(Player().GetState());
+ float dist_squared = dot(diff, diff);
+
+ // if client's prediction is off by more than 1cm, send
+ // our (authoritative) state back so it can fix it
+ constexpr float fix_thresh = 0.0001f;
+
+ if (dist_squared > fix_thresh) {
+ auto pack = Packet::Make<Packet::PlayerCorrection>(server.GetPacket());
+ pack.WritePacketSeq(player_update_pack);
+ pack.WritePlayer(Player());
+ conn.Send(server.GetPacket(), server.GetSocket());
+ }
+}
+