X-Git-Url: https://git.localhorst.tv/?a=blobdiff_plain;f=src%2Fnet%2Fnet.cpp;h=81de3e476e8286245e574905081b047d28b17308;hb=8ae45b6555d55f301f83daf8c1337d332d8305ab;hp=f59b80962530e5a4f4ca8b883b454247eb466a86;hpb=7fd76e64de47f564117b9e6f73f1482d93842108;p=blank.git diff --git a/src/net/net.cpp b/src/net/net.cpp index f59b809..81de3e4 100644 --- a/src/net/net.cpp +++ b/src/net/net.cpp @@ -1,21 +1,14 @@ -#include "Client.hpp" -#include "ClientConnection.hpp" #include "Connection.hpp" #include "ConnectionHandler.hpp" #include "io.hpp" #include "Packet.hpp" -#include "Server.hpp" #include "../app/init.hpp" #include "../model/CompositeModel.hpp" -#include "../world/ChunkIndex.hpp" #include "../world/Entity.hpp" #include "../world/EntityState.hpp" -#include "../world/World.hpp" #include -#include -#include using namespace std; @@ -34,413 +27,6 @@ constexpr size_t Packet::PlayerCorrection::MAX_LEN; constexpr size_t Packet::ChunkBegin::MAX_LEN; constexpr size_t Packet::ChunkData::MAX_LEN; -namespace { - -UDPsocket client_bind(Uint16 port) { - UDPsocket sock = SDLNet_UDP_Open(port); - if (!sock) { - throw NetError("SDLNet_UDP_Open"); - } - return sock; -} - -IPaddress client_resolve(const char *host, Uint16 port) { - IPaddress addr; - if (SDLNet_ResolveHost(&addr, host, port) != 0) { - throw NetError("SDLNet_ResolveHost"); - } - return addr; -} - -} - -Client::Client(const Config &conf) -: conn(client_resolve(conf.host.c_str(), conf.port)) -, client_sock(client_bind(0)) -, client_pack{ -1, nullptr, 0 } { - client_pack.data = new Uint8[sizeof(Packet)]; - client_pack.maxlen = sizeof(Packet); - // establish connection - SendPing(); -} - -Client::~Client() { - delete[] client_pack.data; - SDLNet_UDP_Close(client_sock); -} - - -void Client::Handle() { - int result = SDLNet_UDP_Recv(client_sock, &client_pack); - while (result > 0) { - HandlePacket(client_pack); - result = SDLNet_UDP_Recv(client_sock, &client_pack); - } - if (result == -1) { - // a boo boo happened - throw NetError("SDLNet_UDP_Recv"); - } -} - -void Client::HandlePacket(const UDPpacket &udp_pack) { - if (!conn.Matches(udp_pack.address)) { - // packet came from somewhere else, drop - return; - } - const Packet &pack = *reinterpret_cast(udp_pack.data); - if (pack.header.tag != Packet::TAG) { - // mistagged packet, drop - return; - } - - conn.Received(udp_pack); -} - -void Client::Update(int dt) { - conn.Update(dt); - if (conn.ShouldPing()) { - SendPing(); - } -} - -uint16_t Client::SendPing() { - return conn.SendPing(client_pack, client_sock); -} - -uint16_t Client::SendLogin(const string &name) { - auto pack = Packet::Make(client_pack); - pack.WritePlayerName(name); - return conn.Send(client_pack, client_sock); -} - -uint16_t Client::SendPlayerUpdate(const Entity &player) { - auto pack = Packet::Make(client_pack); - pack.WritePlayer(player); - return conn.Send(client_pack, client_sock); -} - -uint16_t Client::SendPart() { - Packet::Make(client_pack); - return conn.Send(client_pack, client_sock); -} - - -ClientConnection::ClientConnection(Server &server, const IPaddress &addr) -: server(server) -, conn(addr) -, player(nullptr, nullptr) -, spawns() -, confirm_wait(0) -, player_update_state() -, player_update_pack(0) -, player_update_timer(1500) -, transmitter(*this) -, chunk_queue() -, old_base() { - conn.SetHandler(this); -} - -ClientConnection::~ClientConnection() { - DetachPlayer(); -} - -void ClientConnection::Update(int dt) { - conn.Update(dt); - if (Disconnected()) { - return; - } - if (HasPlayer()) { - // sync entities - auto global_iter = server.GetWorld().Entities().begin(); - auto global_end = server.GetWorld().Entities().end(); - auto local_iter = spawns.begin(); - auto local_end = spawns.end(); - - while (global_iter != global_end && local_iter != local_end) { - if (global_iter->ID() == local_iter->entity->ID()) { - // they're the same - if (CanDespawn(*global_iter)) { - SendDespawn(*local_iter); - } else { - // update - SendUpdate(*local_iter); - } - ++global_iter; - ++local_iter; - } else if (global_iter->ID() < local_iter->entity->ID()) { - // global entity was inserted - if (CanSpawn(*global_iter)) { - auto spawned = spawns.emplace(local_iter, *global_iter); - SendSpawn(*spawned); - } - ++global_iter; - } else { - // global entity was removed - SendDespawn(*local_iter); - ++local_iter; - } - } - - // leftover spawns - while (global_iter != global_end) { - if (CanSpawn(*global_iter)) { - spawns.emplace_back(*global_iter); - SendSpawn(spawns.back()); - } - ++global_iter; - } - - // leftover despawns - while (local_iter != local_end) { - SendDespawn(*local_iter); - ++local_iter; - } - - CheckPlayerFix(); - CheckChunkQueue(); - } - if (conn.ShouldPing()) { - conn.SendPing(server.GetPacket(), server.GetSocket()); - } -} - -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.entity && - !e.Dead() && - manhattan_radius(e.ChunkCoords() - PlayerEntity().ChunkCoords()) < 7; -} - -bool ClientConnection::CanDespawn(const Entity &e) const noexcept { - return - e.Dead() || - manhattan_radius(e.ChunkCoords() - PlayerEntity().ChunkCoords()) > 7; -} - -uint16_t ClientConnection::Send() { - return conn.Send(server.GetPacket(), server.GetSocket()); -} - -uint16_t ClientConnection::Send(size_t len) { - server.GetPacket().len = len; - return Send(); -} - -void ClientConnection::SendSpawn(SpawnStatus &status) { - // don't double spawn - if (status.spawn_pack != -1) return; - - auto pack = Prepare(); - pack.WriteEntity(*status.entity); - status.spawn_pack = Send(); - ++confirm_wait; -} - -void ClientConnection::SendDespawn(SpawnStatus &status) { - // don't double despawn - if (status.despawn_pack != -1) return; - - auto pack = Prepare(); - pack.WriteEntityID(status.entity->ID()); - status.despawn_pack = Send(); - ++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 = Prepare(); - pack.WriteEntityCount(1); - pack.WriteEntity(*status.entity, 0); - Send(Packet::EntityUpdate::GetSize(1)); -} - -void ClientConnection::CheckPlayerFix() { - // player_update_state's position holds the client's most recent prediction - glm::vec3 diff = player_update_state.Diff(PlayerEntity().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 = Prepare(); - pack.WritePacketSeq(player_update_pack); - pack.WritePlayer(PlayerEntity()); - Send(); - } -} - -void ClientConnection::CheckChunkQueue() { - if (PlayerChunks().Base() != old_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) { - if (manhattan_radius(pos - old_base) > PlayerChunks().Extent()) { - chunk_queue.push_back(pos); - } - } - } - } - old_base = PlayerChunks().Base(); - } - if (transmitter.Transmitting()) { - transmitter.Transmit(); - return; - } - if (transmitter.Idle()) { - int count = 0; - constexpr int max = 64; - while (count < max && !chunk_queue.empty()) { - Chunk::Pos pos = chunk_queue.front(); - chunk_queue.pop_front(); - if (PlayerChunks().InRange(pos)) { - Chunk *chunk = PlayerChunks().Get(pos); - if (chunk) { - transmitter.Send(*chunk); - return; - } else { - chunk_queue.push_back(pos); - } - ++count; - } - } - } -} - -void ClientConnection::AttachPlayer(const Player &new_player) { - DetachPlayer(); - player = new_player; - player.entity->Ref(); - - old_base = player.chunks->Base(); - Chunk::Pos begin = player.chunks->CoordsBegin(); - Chunk::Pos end = player.chunks->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) { - chunk_queue.push_back(pos); - } - } - } - - cout << "player \"" << player.entity->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; - transmitter.Abort(); - chunk_queue.clear(); -} - -void ClientConnection::OnPacketReceived(uint16_t seq) { - if (transmitter.Waiting()) { - transmitter.Ack(seq); - } - if (!confirm_wait) return; - for (auto iter = spawns.begin(), end = spawns.end(); iter != end; ++iter) { - if (seq == iter->spawn_pack) { - iter->spawn_pack = -1; - --confirm_wait; - return; - } - if (seq == iter->despawn_pack) { - spawns.erase(iter); - --confirm_wait; - return; - } - } -} - -void ClientConnection::OnPacketLost(uint16_t seq) { - if (transmitter.Waiting()) { - transmitter.Nack(seq); - } - if (!confirm_wait) return; - for (SpawnStatus &status : spawns) { - if (seq == status.spawn_pack) { - status.spawn_pack = -1; - --confirm_wait; - SendSpawn(status); - return; - } - if (seq == status.despawn_pack) { - status.despawn_pack = -1; - --confirm_wait; - SendDespawn(status); - return; - } - } -} - -void ClientConnection::On(const Packet::Login &pack) { - string name; - pack.ReadPlayerName(name); - - Player new_player = server.GetWorld().AddPlayer(name); - - if (new_player.entity) { - // success! - AttachPlayer(new_player); - cout << "accepted login from player \"" << name << '"' << endl; - auto response = Prepare(); - response.WritePlayer(*new_player.entity); - response.WriteWorldName(server.GetWorld().Name()); - Send(); - // set up update tracking - player_update_state = new_player.entity->GetState(); - player_update_pack = pack.Seq(); - player_update_timer.Reset(); - player_update_timer.Start(); - } else { - // aw no :( - cout << "rejected login from player \"" << name << '"' << endl; - Prepare(); - Send(); - conn.Close(); - } -} - -void ClientConnection::On(const Packet::Part &) { - conn.Close(); -} - -void ClientConnection::On(const Packet::PlayerUpdate &pack) { - if (!HasPlayer()) return; - 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); - } -} - - Connection::Connection(const IPaddress &addr) : handler(nullptr) , addr(addr) @@ -876,73 +462,4 @@ void ConnectionHandler::Handle(const UDPpacket &udp_pack) { } } - -Server::Server(const Config &conf, World &world) -: serv_sock(nullptr) -, serv_pack{ -1, nullptr, 0 } -, clients() -, world(world) { - serv_sock = SDLNet_UDP_Open(conf.port); - if (!serv_sock) { - throw NetError("SDLNet_UDP_Open"); - } - - serv_pack.data = new Uint8[sizeof(Packet)]; - serv_pack.maxlen = sizeof(Packet); -} - -Server::~Server() { - delete[] serv_pack.data; - SDLNet_UDP_Close(serv_sock); -} - - -void Server::Handle() { - int result = SDLNet_UDP_Recv(serv_sock, &serv_pack); - while (result > 0) { - HandlePacket(serv_pack); - result = SDLNet_UDP_Recv(serv_sock, &serv_pack); - } - if (result == -1) { - // a boo boo happened - throw NetError("SDLNet_UDP_Recv"); - } -} - -void Server::HandlePacket(const UDPpacket &udp_pack) { - if (udp_pack.len < int(sizeof(Packet::Header))) { - // packet too small, drop - return; - } - const Packet &pack = *reinterpret_cast(udp_pack.data); - if (pack.header.tag != Packet::TAG) { - // mistagged packet, drop - return; - } - - ClientConnection &client = GetClient(udp_pack.address); - client.GetConnection().Received(udp_pack); -} - -ClientConnection &Server::GetClient(const IPaddress &addr) { - for (ClientConnection &client : clients) { - if (client.Matches(addr)) { - return client; - } - } - clients.emplace_back(*this, addr); - return clients.back(); -} - -void Server::Update(int dt) { - for (list::iterator client(clients.begin()), end(clients.end()); client != end;) { - client->Update(dt); - if (client->Disconnected()) { - client = clients.erase(client); - } else { - ++client; - } - } -} - }