X-Git-Url: http://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fserver%2Fnet.cpp;h=6ceb186644d3a9f7d5d24526d3cfb2376492db8c;hb=dbd214ac278019c20c883cbb76456b0b0fddc063;hp=0cf0cdd60cc12ad6b9ab36d1a50745cf85c0999f;hpb=8ae45b6555d55f301f83daf8c1337d332d8305ab;p=blank.git diff --git a/src/server/net.cpp b/src/server/net.cpp index 0cf0cdd..6ceb186 100644 --- a/src/server/net.cpp +++ b/src/server/net.cpp @@ -3,12 +3,16 @@ #include "Server.hpp" #include "../app/init.hpp" +#include "../io/WorldSave.hpp" +#include "../model/Model.hpp" #include "../world/ChunkIndex.hpp" #include "../world/Entity.hpp" #include "../world/World.hpp" +#include #include #include +#include using namespace std; @@ -171,12 +175,15 @@ void ChunkTransmitter::Release() { ClientConnection::ClientConnection(Server &server, const IPaddress &addr) : server(server) , conn(addr) -, player(nullptr, nullptr) +, input() +, player_model(nullptr) , spawns() , confirm_wait(0) +, entity_updates() , player_update_state() , player_update_pack(0) , player_update_timer(1500) +, old_actions(0) , transmitter(*this) , chunk_queue() , old_base() { @@ -206,7 +213,7 @@ void ClientConnection::Update(int dt) { SendDespawn(*local_iter); } else { // update - SendUpdate(*local_iter); + QueueUpdate(*local_iter); } ++global_iter; ++local_iter; @@ -238,7 +245,9 @@ void ClientConnection::Update(int dt) { SendDespawn(*local_iter); ++local_iter; } + SendUpdates(); + input->Update(dt); CheckPlayerFix(); CheckChunkQueue(); } @@ -260,7 +269,7 @@ ClientConnection::SpawnStatus::~SpawnStatus() { bool ClientConnection::CanSpawn(const Entity &e) const noexcept { return - &e != player.entity && + &e != &PlayerEntity() && !e.Dead() && manhattan_radius(e.ChunkCoords() - PlayerEntity().ChunkCoords()) < 7; } @@ -276,7 +285,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(); } @@ -300,15 +309,31 @@ void ClientConnection::SendDespawn(SpawnStatus &status) { ++confirm_wait; } -void ClientConnection::SendUpdate(SpawnStatus &status) { +void ClientConnection::QueueUpdate(SpawnStatus &status) { // don't send updates while spawn not ack'd or despawn sent - if (status.spawn_pack != -1 || status.despawn_pack != -1) return; + if (status.spawn_pack == -1 && status.despawn_pack == -1) { + entity_updates.push_back(&status); + } +} - // TODO: pack entity updates +void ClientConnection::SendUpdates() { auto pack = Prepare(); - pack.WriteEntityCount(1); - pack.WriteEntity(*status.entity, 0); - Send(Packet::EntityUpdate::GetSize(1)); + int entity_pos = 0; + for (SpawnStatus *status : entity_updates) { + pack.WriteEntity(*status->entity, entity_pos); + ++entity_pos; + if (entity_pos == Packet::EntityUpdate::MAX_ENTITIES) { + pack.WriteEntityCount(entity_pos); + Send(Packet::EntityUpdate::GetSize(entity_pos)); + pack = Prepare(); + entity_pos = 0; + } + } + if (entity_pos > 0) { + pack.WriteEntityCount(entity_pos); + Send(Packet::EntityUpdate::GetSize(entity_pos)); + } + entity_updates.clear(); } void ClientConnection::CheckPlayerFix() { @@ -328,6 +353,23 @@ void ClientConnection::CheckPlayerFix() { } } +namespace { + +struct QueueCompare { + explicit QueueCompare(const glm::ivec3 &base) + : base(base) { } + bool operator ()(const glm::ivec3 &left, const glm::ivec3 &right) const noexcept { + const glm::ivec3 ld(left - base); + const glm::ivec3 rd(right - base); + return + ld.x * ld.x + ld.y * ld.y + ld.z * ld.z < + rd.x * rd.x + rd.y * rd.y + rd.z * rd.z; + } + const glm::ivec3 &base; +}; + +} + void ClientConnection::CheckChunkQueue() { if (PlayerChunks().Base() != old_base) { Chunk::Pos begin = PlayerChunks().CoordsBegin(); @@ -342,6 +384,7 @@ void ClientConnection::CheckChunkQueue() { } } old_base = PlayerChunks().Base(); + sort(chunk_queue.begin(), chunk_queue.end(), QueueCompare(old_base)); } if (transmitter.Transmitting()) { transmitter.Transmit(); @@ -367,14 +410,14 @@ void ClientConnection::CheckChunkQueue() { } } -void ClientConnection::AttachPlayer(const Player &new_player) { +void ClientConnection::AttachPlayer(Player &player) { DetachPlayer(); - player = new_player; - player.entity->Ref(); + input.reset(new DirectInput(server.GetWorld(), player, server)); + PlayerEntity().Ref(); - old_base = player.chunks->Base(); - Chunk::Pos begin = player.chunks->CoordsBegin(); - Chunk::Pos end = player.chunks->CoordsEnd(); + old_base = PlayerChunks().Base(); + Chunk::Pos begin = PlayerChunks().CoordsBegin(); + Chunk::Pos end = PlayerChunks().CoordsEnd(); for (Chunk::Pos pos = begin; pos.z < end.z; ++pos.z) { for (pos.y = begin.y; pos.y < end.y; ++pos.y) { for (pos.x = begin.x; pos.x < end.x; ++pos.x) { @@ -382,19 +425,40 @@ void ClientConnection::AttachPlayer(const Player &new_player) { } } } + sort(chunk_queue.begin(), chunk_queue.end(), QueueCompare(old_base)); + // TODO: should the server do this? + if (HasPlayerModel()) { + GetPlayerModel().Instantiate(PlayerEntity().GetModel()); + } - cout << "player \"" << player.entity->Name() << "\" joined" << endl; + cout << "player \"" << player.Name() << "\" joined" << endl; } void ClientConnection::DetachPlayer() { if (!HasPlayer()) return; - cout << "player \"" << player.entity->Name() << "\" left" << endl; - player.entity->Kill(); - player.entity->UnRef(); - player.entity = nullptr; - player.chunks = nullptr; + cout << "player \"" << input->GetPlayer().Name() << "\" left" << endl; + server.GetWorldSave().Write(input->GetPlayer()); + PlayerEntity().Kill(); + PlayerEntity().UnRef(); + input.reset(); transmitter.Abort(); chunk_queue.clear(); + old_actions = 0; +} + +void ClientConnection::SetPlayerModel(const Model &m) noexcept { + player_model = &m; + if (HasPlayer()) { + m.Instantiate(PlayerEntity().GetModel()); + } +} + +bool ClientConnection::HasPlayerModel() const noexcept { + return player_model; +} + +const Model &ClientConnection::GetPlayerModel() const noexcept { + return *player_model; } void ClientConnection::OnPacketReceived(uint16_t seq) { @@ -441,18 +505,18 @@ void ClientConnection::On(const Packet::Login &pack) { string name; pack.ReadPlayerName(name); - Player new_player = server.GetWorld().AddPlayer(name); + Player *new_player = server.JoinPlayer(name); - if (new_player.entity) { + if (new_player) { // success! - AttachPlayer(new_player); + AttachPlayer(*new_player); cout << "accepted login from player \"" << name << '"' << endl; auto response = Prepare(); - response.WritePlayer(*new_player.entity); + response.WritePlayer(new_player->GetEntity()); response.WriteWorldName(server.GetWorld().Name()); Send(); // set up update tracking - player_update_state = new_player.entity->GetState(); + player_update_state = new_player->GetEntity().GetState(); player_update_pack = pack.Seq(); player_update_timer.Reset(); player_update_timer.Start(); @@ -474,21 +538,76 @@ void ClientConnection::On(const Packet::PlayerUpdate &pack) { int pack_diff = int16_t(pack.Seq()) - int16_t(player_update_pack); bool overdue = player_update_timer.HitOnce(); player_update_timer.Reset(); - if (pack_diff > 0 || overdue) { - player_update_pack = pack.Seq(); - pack.ReadPlayerState(player_update_state); - // accept velocity and orientation as "user input" - PlayerEntity().Velocity(player_update_state.velocity); - PlayerEntity().Orientation(player_update_state.orient); + if (pack_diff <= 0 && !overdue) { + // drop old packets if we have a fairly recent state + 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->SelectInventory(slot); + + if ((new_actions & 0x01) && !(old_actions & 0x01)) { + input->StartPrimaryAction(); + } else if (!(new_actions & 0x01) && (old_actions & 0x01)) { + input->StopPrimaryAction(); + } + if ((new_actions & 0x02) && !(old_actions & 0x02)) { + input->StartSecondaryAction(); + } else if (!(new_actions & 0x02) && (old_actions & 0x02)) { + input->StopSecondaryAction(); + } + if ((new_actions & 0x04) && !(old_actions & 0x04)) { + input->StartTertiaryAction(); + } else if (!(new_actions & 0x04) && (old_actions & 0x04)) { + input->StopTertiaryAction(); + } + old_actions = new_actions; } +bool ClientConnection::ChunkInRange(const glm::ivec3 &pos) const noexcept { + return HasPlayer() && PlayerChunks().InRange(pos); +} + +void ClientConnection::On(const Packet::Message &pack) { + uint8_t type; + uint32_t ref; + string msg; + pack.ReadType(type); + pack.ReadReferral(ref); + pack.ReadMessage(msg); -Server::Server(const Config &conf, World &world) + if (type == 1 && HasPlayer()) { + server.DistributeMessage(1, PlayerEntity().ID(), msg); + } +} + + +Server::Server( + const Config::Network &conf, + World &world, + const World::Config &wc, + const WorldSave &save) : serv_sock(nullptr) , serv_pack{ -1, nullptr, 0 } , clients() -, world(world) { +, world(world) +, spawn_index(world.Chunks().MakeIndex(wc.spawn, 3)) +, save(save) +, player_model(nullptr) { serv_sock = SDLNet_UDP_Open(conf.port); if (!serv_sock) { throw NetError("SDLNet_UDP_Open"); @@ -499,6 +618,7 @@ Server::Server(const Config &conf, World &world) } Server::~Server() { + world.Chunks().UnregisterIndex(spawn_index); delete[] serv_pack.data; SDLNet_UDP_Close(serv_sock); } @@ -538,6 +658,9 @@ ClientConnection &Server::GetClient(const IPaddress &addr) { } } clients.emplace_back(*this, addr); + if (HasPlayerModel()) { + clients.back().SetPlayerModel(GetPlayerModel()); + } return clients.back(); } @@ -552,5 +675,67 @@ void Server::Update(int dt) { } } +void Server::SetPlayerModel(const Model &m) noexcept { + player_model = &m; + for (ClientConnection &client : clients) { + client.SetPlayerModel(m); + } +} + +bool Server::HasPlayerModel() const noexcept { + return player_model; +} + +const Model &Server::GetPlayerModel() const noexcept { + return *player_model; +} + +Player *Server::JoinPlayer(const string &name) { + if (spawn_index.MissingChunks() > 0) { + return nullptr; + } + Player *player = world.AddPlayer(name); + if (!player) { + return nullptr; + } + if (save.Exists(*player)) { + save.Read(*player); + } else { + // TODO: spawn + } + return player; +} + +void Server::SetBlock(Chunk &chunk, int index, const Block &block) { + chunk.SetBlock(index, block); + // 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(); + } + } +} + +void Server::DistributeMessage(uint8_t type, uint32_t ref, const string &msg) { + auto pack = Packet::Make(serv_pack); + pack.WriteType(type); + pack.WriteReferral(ref); + pack.WriteMessage(msg); + serv_pack.len = sizeof(Packet::Header) + Packet::Message::GetSize(msg); + SendAll(); +} + +void Server::SendAll() { + for (ClientConnection &client : clients) { + client.GetConnection().Send(serv_pack, serv_sock); + } +} + } }