#pragma once // Client side of the protocol: connection state machine and message queues. // The World consumes snapshots/events; screens consume profile/dialogue/chat. #include "Net.h" #include "Protocol.h" #include #include #include #include #include namespace bs { struct ChatEntry { uint32_t entity = 0; std::string name, text; float age = 0.0f; }; class NetClient { public: enum class State { Idle, Connecting, NeedCharacter, InGame, Failed }; bool connect(const std::string& host, uint16_t port, const std::string& token); void disconnect(); void update(); // poll the network, fill the queues void sendCreate(const std::string& name, const Appearance& look); void sendInput(const InputCmd& cmd); void sendAction(ActionKind kind, uint32_t target = 0, float x = 0.0f, float z = 0.0f); void sendChat(const std::string& text); void sendDialogueDone(const std::string& script); State state() const { return state_; } const std::string& error() const { return error_; } const WelcomeMsg& welcome() const { return welcome_; } const ProfileMsg& profile() const { return profile_; } bool hasProfile() const { return hasProfile_; } uint32_t localEntity() const { return profile_.entityId ? profile_.entityId : welcome_.entityId; } uint32_t pingMs() const; std::map players; // every player entity's name + look std::deque snapshots; // newest last; the World pops them std::vector events; std::vector dialogues; std::vector toasts; std::vector chat; std::vector playersLeft; bool profileChanged = false; private: void handle(const std::vector& data); void send(net::Channel ch, const Writer& w); std::unique_ptr host_; uint32_t peer_ = 0; State state_ = State::Idle; std::string error_, token_; WelcomeMsg welcome_; ProfileMsg profile_; bool hasProfile_ = false; std::vector recentInputs_; }; } // namespace bs