]> git.tdb.fi Git - libs/game.git/commitdiff
Add string interning to the network protocol
authorMikko Rasa <tdb@tdb.fi>
Sun, 11 Jun 2023 12:42:25 +0000 (15:42 +0300)
committerMikko Rasa <tdb@tdb.fi>
Sun, 11 Jun 2023 14:13:56 +0000 (17:13 +0300)
This allows transmitting commonly used strings using short IDs.

source/game/messages.h
source/game/networking.cpp
source/game/networking.h
source/game/protocol.cpp

index 3b18c3379b45d08868a109fd2a5f00204b40ce12..8efc1bda3e7f0ae24d6374951c387b52917c0000 100644 (file)
@@ -27,6 +27,12 @@ struct PlayerRemoved
        std::uint16_t id;
 };
 
+struct InternString
+{
+       std::uint16_t id;
+       std::string value;
+};
+
 } // namespace Messages
 } // namespace Msp::Game
 
index 49b7d39f56c1c4c48dc37e40523331c8ea2f51e8..30f6b484cb0be6d510c839bc9efe6d03ed12dea2 100644 (file)
@@ -10,7 +10,9 @@ namespace Msp::Game {
 Networking::Networking(Director &d):
        event_source(d.get_event_bus()),
        io_disp(d.get_io_dispatcher())
-{ }
+{
+       strings.emplace_back();
+}
 
 void Networking::start_server(unsigned port)
 {
@@ -44,6 +46,8 @@ void Networking::disable()
 {
        server_socket.reset();
        connection.reset();
+       strings.clear();
+       strings.emplace_back();
        set_state(DISABLED);
 }
 
@@ -74,6 +78,34 @@ void Networking::reap_connections()
        }
 }
 
+uint16_t Networking::intern_string(const string &value)
+{
+       auto i = find(strings, value);
+       if(i==strings.end())
+       {
+               if(!is_server())
+                       throw invalid_state("Networking::intern_string");
+               if(strings.size()>=numeric_limits<uint16_t>::max())
+                       throw invalid_state("Networking::intern_string");
+               i = strings.insert(i, value);
+
+               Messages::InternString msg;
+               msg.id = strings.size()-1;
+               msg.value = value;
+               for(const auto &c: clients)
+                       c->get_communicator().send(msg);
+       }
+
+       return i-strings.begin();
+}
+
+const string &Networking::get_string(uint16_t id) const
+{
+       if(id>=strings.size())
+               throw key_error(id);
+       return strings[id];
+}
+
 void Networking::add_player(const string &name, function<void(unsigned)> callback)
 {
        if(state!=CONNECTED && state!=SERVER)
@@ -202,13 +234,33 @@ void Networking::ServerConnection::receive(const Messages::PlayerRemoved &remove
        }
 }
 
+void Networking::ServerConnection::receive(const Messages::InternString &intern)
+{
+       if(networking.strings.size()<=intern.id)
+               networking.strings.resize(intern.id+1);
+       networking.strings[intern.id] = intern.value;
+}
+
 
 Networking::ClientConnection::ClientConnection(Networking &n, unique_ptr<Net::StreamSocket> s):
        Connection(n, move(s))
 {
+       communicator.signal_protocol_ready.connect(sigc::mem_fun(this, &ClientConnection::protocol_ready));
        communicator.add_protocol(networking.protocol, *this);
 }
 
+void Networking::ClientConnection::protocol_ready(const Net::Protocol &p)
+{
+       if(&p==&networking.protocol)
+       {
+               size_t n_strings = networking.strings.size();
+               for(size_t i=1; i<n_strings; ++i)
+                       communicator.send(Messages::InternString{ static_cast<uint16_t>(i), networking.strings[i] });
+               if(Stage *stage = networking.director.get_active_stage())
+                       communicator.send(Messages::StageActivated{ networking.intern_string(stage->get_name()) });
+       }
+}
+
 void Networking::ClientConnection::receive(const Messages::AddPlayerRequest &add_req)
 {
        Player &player = networking.create_player(add_req.name);
index eacba9302b87d1210a502daf998e5af54566bcdd..b0ee36696a702cc3db64d4641ebf51878819a2e1 100644 (file)
@@ -51,7 +51,8 @@ private:
        class ServerConnection: public Connection,
                public Net::PacketReceiver<Messages::AddPlayerResponse>,
                public Net::PacketReceiver<Messages::PlayerAdded>,
-               public Net::PacketReceiver<Messages::PlayerRemoved>
+               public Net::PacketReceiver<Messages::PlayerRemoved>,
+               public Net::PacketReceiver<Messages::InternString>
        {
        public:
                ServerConnection(Networking &, const Net::SockAddr &);
@@ -63,6 +64,7 @@ private:
                void receive(const Messages::AddPlayerResponse &) override;
                void receive(const Messages::PlayerAdded &) override;
                void receive(const Messages::PlayerRemoved &) override;
+               void receive(const Messages::InternString &) override;
        };
 
        class ClientConnection: public Connection,
@@ -71,6 +73,7 @@ private:
        public:
                ClientConnection(Networking &, std::unique_ptr<Net::StreamSocket>);
 
+               void protocol_ready(const Net::Protocol &);
                void receive(const Messages::AddPlayerRequest &) override;
        };
 
@@ -93,15 +96,20 @@ private:
        std::vector<std::unique_ptr<ClientConnection>> clients;
        std::vector<Player> players;
        unsigned next_id = 1;
+       std::vector<std::string> strings;
 
 public:
        Networking(Director &);
 
        void start_server(unsigned);
        void connect_to_server(const std::string &);
+       bool is_server() const { return state==DISABLED || state==SERVER; }
        void disable();
        void reap_connections();
 
+       std::uint16_t intern_string(const std::string &);
+       const std::string &get_string(std::uint16_t) const;
+
        void add_player(const std::string &, std::function<void(unsigned)>);
 private:
        Player &create_player(const std::string &, unsigned = 0);
index 12265218ff20fdd91f4c838b129b23c1079e3552..ec345314ba7b6c3621067860160d63dfc2fa97f8 100644 (file)
@@ -10,6 +10,7 @@ Protocol::Protocol()
        add<AddPlayerRequest>(&AddPlayerRequest::token, &AddPlayerRequest::name);
        add<AddPlayerResponse>(&AddPlayerResponse::token, &AddPlayerResponse::id);
        add<PlayerAdded>(&PlayerAdded::id, &PlayerAdded::name);
+       add<InternString>(&InternString::id, &InternString::value);
 }
 
 } // namespace Msp::Game