<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: RYU JAEMIN</title>
    <description>The latest articles on DEV Community by RYU JAEMIN (@ryujm1828).</description>
    <link>https://dev.to/ryujm1828</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3721782%2F386ca676-dbfd-40d6-bb6c-bbd2a037f7e6.png</url>
      <title>DEV Community: RYU JAEMIN</title>
      <link>https://dev.to/ryujm1828</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ryujm1828"/>
    <language>en</language>
    <item>
      <title>[BlindSpot] Log 04. Let's follow the SOLID principles : DIP</title>
      <dc:creator>RYU JAEMIN</dc:creator>
      <pubDate>Thu, 29 Jan 2026 00:41:03 +0000</pubDate>
      <link>https://dev.to/ryujm1828/blindspot-log-04-lets-follow-the-solid-principles-dip-1b3i</link>
      <guid>https://dev.to/ryujm1828/blindspot-log-04-lets-follow-the-solid-principles-dip-1b3i</guid>
      <description>&lt;h2&gt;
  
  
  SOLID Principles
&lt;/h2&gt;

&lt;p&gt;Last time, we learned about SRP, one of the SOLID principles, and refactored our code based on it. This time, we'll refactor our code based on the DIP principle.&lt;/p&gt;

&lt;h3&gt;
  
  
  DIP(Dependency Inversion Principle)
&lt;/h3&gt;

&lt;p&gt;DIP is the principle that higher-level modules should depend on abstractions (interfaces) rather than on the concrete implementations of lower-level modules.&lt;br&gt;
Failure to adhere to this principle can lead to increased dependencies between classes, such that modifying one class can lead to subsequent modifications to other classes affected by the change. Furthermore, if a test class lacks an interface, a new class must be created to perform the same functionality.&lt;br&gt;
In my code, there are currently a large number of Manager/Service objects that use the Singleton pattern. If I were applying DIP, it might be easier to use something like TestAuthManager to load dummy data for testing before applying the DB later. I'll split Manager classes out to adhere to the Dependency Independent Programming (DIP) principle.&lt;/p&gt;
&lt;h3&gt;
  
  
  Why only Managers?
&lt;/h3&gt;

&lt;p&gt;Currently, both the Manager and Service classes are singletons, using no interfaces. I'll create an interface for the Manager class here. This is because, if we later implement sign-up/login functionality, Managers will likely need to interact with the database, requiring a complete overhaul of the entire logic. If we were to use the Manager object directly instead of an interface, other code would also need to be completely overhauled. Since the Service object is responsible for the actual running logic, I decided that creating an interface was a low priority.&lt;br&gt;
However, dependency injection will also be applied to service and handler classes without interface.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's follow the DIP principle
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Making interfaces for managers
&lt;/h3&gt;

&lt;p&gt;First, I made interfaces for managers(Auth,Player,Room,Session).&lt;br&gt;
It contains functions from existing classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//IAuthManager.h&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IAuthManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;IAuthManager&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;GetPlayerIdByToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;GenerateToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;RemoveToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;RegisterToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;//... Make other interfaces the same&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inheriting an interface
&lt;/h3&gt;

&lt;p&gt;I inherited the interface from the manager.&lt;br&gt;
At this point, copy constructors and copy assignment operators are prevented.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AuthManager(const AuthManager&amp;amp;) = delete;
AuthManager&amp;amp; operator=(const AuthManager&amp;amp;) = delete;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because the mutex used by AuthManager cannot be copied, and even if there is no mutex, the manager object must remain intact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//AuthManager.h
#include "../Core/Interfaces/IAuthManager.h"

class AuthManager : public IAuthManager{

public:
    AuthManager() = default;
    virtual ~AuthManager() = default;


    int32_t GetPlayerIdByToken(const std::string&amp;amp; token) override;
    std::string GenerateToken() override;
    void RemoveToken(const std::string&amp;amp; token) override;
    void RegisterToken(const std::string&amp;amp; token, int32_t playerId) override;
private:
    AuthManager(const AuthManager&amp;amp;) = delete;
    AuthManager&amp;amp; operator=(const AuthManager&amp;amp;) = delete;

    std::mutex token_mutex_;
    std::map&amp;lt;std::string, int32_t&amp;gt; tokenToPlayerId_;
    std::mutex name_mutex_;
    std::map&amp;lt;int32_t, std::string&amp;gt; playerIdToName_;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Injecting dependencies
&lt;/h3&gt;

&lt;p&gt;Now I should inject dependencies to Service.&lt;br&gt;
This should apply to all services, but I'll just use AuthService as an example.&lt;br&gt;
First, I created a manager interface pointer required for private.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class AuthService {
public:

private:
    std::shared_ptr&amp;lt;IAuthManager&amp;gt; authMgr_;
    std::shared_ptr&amp;lt;ISessionManager&amp;gt; sessionMgr_;
    std::shared_ptr&amp;lt;IPlayerManager&amp;gt; playerMgr_;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, And I created a constructor that includes those managers. As you can see from the code, the Service does not use static variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//AuthService.h
class AuthService {
public:

    AuthService(std::shared_ptr&amp;lt;IAuthManager&amp;gt; authMgr,
        std::shared_ptr&amp;lt;IPlayerManager&amp;gt; playerMgr,
        std::shared_ptr&amp;lt;ISessionManager&amp;gt; sessionMgr)
        : authMgr_(authMgr), playerMgr_(playerMgr), sessionMgr_(sessionMgr) {
    }
    void Login(std::shared_ptr&amp;lt;Session&amp;gt; session, blindspot::LoginRequest&amp;amp; pkt);

private:
    std::shared_ptr&amp;lt;IAuthManager&amp;gt; authMgr_;
    std::shared_ptr&amp;lt;ISessionManager&amp;gt; sessionMgr_;
    std::shared_ptr&amp;lt;IPlayerManager&amp;gt; playerMgr_;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The singleton pattern using the Instance() function is no longer used. Therefore, it can be used by calling the manager from within the internal function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//Before
newPlayer-&amp;gt;name = PlayerManager.Instance().GetPlayerNameById(playerId);
//After
newPlayer-&amp;gt;name = playerMgr_-&amp;gt;GetPlayerNameById(playerId);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service has been injected with a manager, and that service will now be injected with a handler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using PacketFunc = std::function&amp;lt;void(std::shared_ptr&amp;lt;Session&amp;gt;, uint8_t*, uint16_t)&amp;gt;;

class ServerPacketHandler {
public:
    ServerPacketHandler(std::shared_ptr&amp;lt;AuthService&amp;gt; authService, std::shared_ptr&amp;lt;RoomService&amp;gt; roomService);

    void Init();
    void HandlePacket(std::shared_ptr&amp;lt;Session&amp;gt; session, uint16_t id, uint8_t* payload, uint16_t size);

private:
    void Handle_LOGIN_REQUEST(std::shared_ptr&amp;lt;Session&amp;gt; session, blindspot::LoginRequest&amp;amp; pkt);
    void Handle_JOIN_ROOM_REQUEST(std::shared_ptr&amp;lt;Session&amp;gt; session, blindspot::JoinRoomRequest&amp;amp; pkt);
    void Handle_MAKE_ROOM_REQUEST(std::shared_ptr&amp;lt;Session&amp;gt; session, blindspot::MakeRoomRequest&amp;amp; pkt);

private:
    std::shared_ptr&amp;lt;AuthService&amp;gt; authService_;
    std::shared_ptr&amp;lt;RoomService&amp;gt; roomService_;

    PacketFunc packet_handlers_[UINT16_MAX];
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Making Composition Root
&lt;/h3&gt;

&lt;p&gt;Finally, we'll create a Composition Root, which is where we'll create and inject the managers, services, and handlers we'll actually use. In this project, the Server class will fulfill this role.&lt;br&gt;
When the Server class's constructor is executed, it creates managers, and then creates services with those managers as constructor arguments. Finally, it creates handlers that inject those services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//main.cpp
int main() {

    try {
        boost::asio::io_context io_context;

        std::cout &amp;lt;&amp;lt; "Server starting on port "&amp;lt;&amp;lt; PORT &amp;lt;&amp;lt; "..." &amp;lt;&amp;lt; std::endl;
        Server s(io_context, PORT);   //Managers, services, and handlers are created and injected here.

        io_context.run(); // Start the server event loop
    }
    catch (std::exception&amp;amp; e) {
        std::cerr &amp;lt;&amp;lt; "Exception: " &amp;lt;&amp;lt; e.what() &amp;lt;&amp;lt; std::endl;
    }

    return 0;
}

//Server.cpp
Server::Server(boost::asio::io_context&amp;amp; io_context, short port)
    : acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
{
    sessionMgr_ = std::make_shared&amp;lt;SessionManager&amp;gt;();
    roomMgr_ = std::make_shared&amp;lt;RoomManager&amp;gt;();
    authMgr_ = std::make_shared&amp;lt;AuthManager&amp;gt;();
    playerMgr_ = std::make_shared&amp;lt;PlayerManager&amp;gt;();
    authService_ = std::make_shared&amp;lt;AuthService&amp;gt;(authMgr_, playerMgr_, sessionMgr_);
    roomService_ = std::make_shared&amp;lt;RoomService&amp;gt;(roomMgr_);
    packetHandler_ = std::make_shared&amp;lt;ServerPacketHandler&amp;gt;(authService_, roomService_);

    packetHandler_-&amp;gt;Init();

    std::cout &amp;lt;&amp;lt; "Server initialized on port " &amp;lt;&amp;lt; port &amp;lt;&amp;lt; std::endl;

    DoAccept();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the main function is executed, the server constructor takes action, and the server constructor creates all managers, services, and handlers.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Conclusion
&lt;/h2&gt;

&lt;p&gt;This time, I tried refactoring the code to follow the DIP. It doesn't seem to fully adhere to the SOLID pattern yet, but I've prioritized addressing the SRP and DIP issues, which would require significant time and effort to fix later.&lt;br&gt;
Next, I'll tackle the actual in-game implementation.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>design</category>
      <category>devjournal</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>[BlindSpot] Log 03. Let's follow the SOLID principles : SRP</title>
      <dc:creator>RYU JAEMIN</dc:creator>
      <pubDate>Tue, 27 Jan 2026 23:23:54 +0000</pubDate>
      <link>https://dev.to/ryujm1828/blindspot-log-03-lets-follow-the-solid-principles-srp-2bh2</link>
      <guid>https://dev.to/ryujm1828/blindspot-log-03-lets-follow-the-solid-principles-srp-2bh2</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/ryujm1828/BlindSpot" rel="noopener noreferrer"&gt;BlindSpot Github&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  SOLID Principles
&lt;/h2&gt;

&lt;p&gt;SOLID principles are the five design principles of object-oriented programming.&lt;br&gt;
Today I will be solving SRP and DIP issues in my code.&lt;/p&gt;
&lt;h3&gt;
  
  
  SRP(Single Responsibility principle)
&lt;/h3&gt;

&lt;p&gt;SRP is the principle that a class(object) should have only one responsibility(function).&lt;br&gt;
When this principle is not followed, maintenance problems can easily arise, such as when one function is modified, other functions that seemed unrelated stop working.&lt;br&gt;
In my code, I have several classes that handle various functions.&lt;br&gt;
ServerPacketHandler performs both the role of routing packets and the actual business logic(login processing, room creation logic, etc.) at the same time.&lt;br&gt;
Session has a mixed role of being responsible for socket communication (Receive/Send) and a data container that holds user status information (playerId, name, room).&lt;br&gt;
PlayerManager handles all the 'player' related functions, including session management, ID generation, name mapping, token management, etc.&lt;br&gt;
So, I will separate all of these functions.&lt;/p&gt;
&lt;h2&gt;
  
  
  Let's follow the SRP principle
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ServerPacketHandler.cpp
&lt;/h3&gt;

&lt;p&gt;ServerPacketHandler is both receiving and processing packets.&lt;br&gt;
I will change its role to only receive packets and forward them to other new service files.&lt;br&gt;
I made AuthService(Login), RoomSerivce(JoinRoom, MakeRoom), GameService(In game operations to add later).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Before&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;ServerPacketHandler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Handle_MAKE_ROOM_REQUEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blindspot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MakeRoomRequest&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Already in a room&lt;/span&gt;
        &lt;span class="n"&gt;blindspot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MakeRoomResponse&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blindspot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MAKE_ALREADY_IN_ROOM&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;blindspot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PacketID&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ID_MAKE_ROOM_RESPONSE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;room_name&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;maxPlayers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_players&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="c1"&gt;//...omission&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//After&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;ServerPacketHandler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Handle_JOIN_ROOM_REQUEST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;blindspot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JoinRoomRequest&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;RoomService&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;JoinRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Session.h
&lt;/h3&gt;

&lt;p&gt;Session.h currently contains both socket communication and player information (ID, name, room). We'll separate this into a communication object (Session) and a game object (Player).&lt;br&gt;
I made Player object, and insert id,name,room informations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//Models/Player.h&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt; &lt;span class="n"&gt;_nameLock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;weak_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GameRoom&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;room&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;SetName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;playerName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;lock_guard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_nameLock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;playerName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;lock_guard&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_nameLock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;//Network/Session.h&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;enable_shared_from_this&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tcp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;socket_&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Player&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;player_&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;_sessionKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  PlayerManager.h
&lt;/h3&gt;

&lt;p&gt;Currently, PlayerManager manages everything from sessions, authentication, to player data.&lt;br&gt;
So I'm going to break this down into PlayerManager, AuthManager, and SessionManager.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//PlayerManager.h&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PlayerManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;atomic&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int32_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;playerIdGenerator_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt; &lt;span class="n"&gt;name_mutex_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int32_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;playerIdToName_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;PlayerManager&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;GeneratePlayerId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;RegisterPlayerName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;EditPlayerName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;newName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;GetPlayerNameById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;//AuthManager.h&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt; &lt;span class="n"&gt;token_mutex_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sessionKeyToPlayerId_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt; &lt;span class="n"&gt;name_mutex_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int32_t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;playerIdToName_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;AuthManager&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;GetPlayerIdBySessionKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;GenerateSessionKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;RemoveSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;RegisterSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int32_t&lt;/span&gt; &lt;span class="n"&gt;playerId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;//SessionManager.h&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SessionManager&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;mutex&lt;/span&gt; &lt;span class="n"&gt;sessions_mutex_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sessions_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;SessionManager&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;uint16_t&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;protobuf&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  In Conclusion
&lt;/h2&gt;

&lt;p&gt;Before the project grew any larger, I refactored the code to align with the SRP principle. Next time, I'll try refactoring to align with the DIP principle mentioned above.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>codequality</category>
      <category>programming</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>[BlindSpot] Log 02. C# Socket Optimization : Zero-Allocation &amp; GC Free</title>
      <dc:creator>RYU JAEMIN</dc:creator>
      <pubDate>Wed, 21 Jan 2026 14:21:48 +0000</pubDate>
      <link>https://dev.to/ryujm1828/blindspot-log-02-c-socket-optimization-2mgb</link>
      <guid>https://dev.to/ryujm1828/blindspot-log-02-c-socket-optimization-2mgb</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/ryujm1828/BlindSpot" rel="noopener noreferrer"&gt;BlindSpot Github&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Refactoring?
&lt;/h2&gt;

&lt;p&gt;Since BlindSpot is an Online FPS Game, Zero-latency and Stutter-free frames are essential.&lt;br&gt;
Therefore, I performed a major refactoring to minimize the garbage collection (GC) load and secure structural scalability.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Using a Sliding Window instead of a List
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Before
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//BlindSpotClient/Assets/Scripts/Network
/NetworkManager.cs

//Add received data to assemble buffer
byte[] temp = new byte[bytesRead];   // GC Overhead
Array.Copy(recvBuffer, 0, temp, 0, bytesRead);
assembleBuffer.AddRange(temp);
// Process complete packets
while (assembleBuffer.Count &amp;gt;= 4) 
{
      ushort packetSize = BitConverter.ToUInt16(assembleBuffer.ToArray(), 0); // GC Overhead

      if (assembleBuffer.Count &amp;lt; packetSize) break;

      ushort packetID = BitConverter.ToUInt16(assembleBuffer.ToArray(), 2); // GC Overhead

      byte[] payload = new byte[packetSize - 4];
      Array.Copy(assembleBuffer.ToArray(), 4, payload, 0, payload.Length);

      HandlePacket((PacketID)packetID, payload);

      assembleBuffer.RemoveRange(0, packetSize);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  After
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_currentLength += bytesRead;

int processOffset = 0;
while(_currentLength - processOffset &amp;gt;= 4) // Check for minimum header size
{
    //Read Size without ToArray()
    ushort packetSize = BitConverter.ToUInt16(_recvBuffer, processOffset);

    //Check packetSize validation
    if (packetSize &amp;lt; 4 || packetSize &amp;gt; Buffersize)
    {
        Debug.LogError($"[Client] Invalid Packet Size: {packetSize}");
        // Modify to reconnect logic
        CloseConnection();
        return;
    }

    if(_currentLength - processOffset &amp;gt;= packetSize) // Check if full packet is received
    {
        ushort packetId = BitConverter.ToUInt16(_recvBuffer, processOffset + 2);
        byte[] payload = new byte[packetSize - 4]; //payload array
        Array.Copy(_recvBuffer, processOffset + 4, payload, 0, payload.Length);
        HandlePacket((PacketID)packetId, payload);
        processOffset += packetSize; // Go to next packet
    }
    else
    {
        break; // Wait for more data
    }
}
if(processOffset &amp;gt; 0)
{
    int remaining = _currentLength - processOffset;
    if (remaining &amp;gt; 0)
    {
        Buffer.BlockCopy(_recvBuffer, processOffset, _recvBuffer, 0, remaining);
    }
    _currentLength = remaining;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This code reuses only one byte[] array of fixed size (8KB) ​​and moves only the index (Offset).&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Producer-Consumer Pattern with ConcurrentQueue
&lt;/h2&gt;

&lt;p&gt;The callback function (OnReceiveData) connected to Socket.BeginRead in C# was executed on a thread other than Unity's main thread.&lt;br&gt;
Accessing Unity's Transform or UI within this callback will cause a crash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//BlindSpotClient/Assets/Scripts/Network
/NetworkManager.cs

// Network Thread
private void HandlePacket(PacketID id, byte[] payload) {
    // Insert safely without lock
    _packetQueue.Enqueue(new PacketMessage(id, payload));
}

// Main Thread (Unity Update)
void Update() {
    // Take it out of the queue and process it
    while (_packetQueue.TryDequeue(out PacketMessage packet))
{
    try
    {
        ProcessPacket(packet);
    }
    catch (Exception e)
    {
        Debug.LogError($"[Client] Packet Processing Error: {e.Message}");
    }
}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use a general Queue, you need to lock it, which can cause performance degradation in case of thread contention.&lt;br&gt;
ConcurrentQueue uses a lock-free algorithm internally to safely transfer data without bottlenecks even in a multi-threaded environment.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Remove switch-case
&lt;/h2&gt;

&lt;p&gt;Switch-case is fast when there are only a few packet types, but as the game grows to 100 or 200 packets, the code becomes longer and less readable. Furthermore, branch prediction errors can cause a slight performance degradation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//BlindSpotClient/Assets/Scripts/Network
/NetworkManager.cs
private Action&amp;lt;byte[]&amp;gt;[] _packetHandlers = new Action&amp;lt;byte[]&amp;gt;[MaxPacketID];

private void RegisterHandlers()
{
    _packetHandlers[(int)PacketID.IdLoginResponse] = HandleLoginResponse;
    _packetHandlers[(int)PacketID.IdJoinRoomResponse] = HandleJoinRoomResponse;
    _packetHandlers[(int)PacketID.IdMakeRoomResponse] = HandleMakeRoomResponse;
}

private void ProcessPacket(PacketMessage packet)
{
    int id = (int)packet.Id;
    if (id &amp;gt;= 0 &amp;amp;&amp;amp; id &amp;lt; _packetHandlers.Length &amp;amp;&amp;amp; _packetHandlers[id] != null)
    {
        _packetHandlers[id](packet.Payload, packet.Size); 
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The time complexity for packet processing has been reduced from O(N) (worst case) or O(log N) to constant time O(1).&lt;br&gt;
Furthermore, the structure adheres to the Open-Closed Principle (OCP) because adding a new packet does not require modifying the existing logic (ProcessPacket).&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Use ArrayPool in Sliding Window
&lt;/h2&gt;

&lt;p&gt;In the code of sliding window, it uses new byte[] for payload.&lt;br&gt;
Creating an array every time a packet is received  triggers the Garbage Collector.&lt;br&gt;
So I decided to use ArrayPool&lt;br&gt;
Previous: byte[] payload = new byte[size];&lt;br&gt;
Changed: byte[] payload = ArrayPool.Shared.Rent(size);&lt;/p&gt;

&lt;p&gt;But ArrayPool lends arrays in power-of-two sizes for performance.&lt;br&gt;
Therefore, if we trust payload.Length and try to parse Protobuf as is, we will end up reading the previous data (garbage data) that was left behind, which will result in an error.&lt;br&gt;
So, I added a size variable to the PacketMessage structure, and made the handler recognize and process only up to size as payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ushort packetId = BitConverter.ToUInt16(_recvBuffer, processOffset + 2);
int payloadSize = packetSize - 4;
byte[] payload = ArrayPool&amp;lt;byte&amp;gt;.Shared.Rent(packetSize - 4);
Array.Copy(_recvBuffer, processOffset + 4, payload, 0, payloadSize);
HandlePacket((PacketID)packetId, payloadSize, payload);
processOffset += packetSize; // Go to next packet

//...

void HandleLoginResponse(byte[] payload,int size)
{
    CodedInputStream stream = new CodedInputStream(payload, 0, size);
    LoginResponse pkt = LoginResponse.Parser.ParseFrom(stream);
    Debug.Log($"[Server] Login Result: {pkt.Success}, Msg: {pkt.Message}");
    Debug.Log($"Session ID: {pkt.SessionKey}, PlayerId: {pkt.PlayerId}");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ArrayPools must be returned to the pool after use to avoid memory leaks. A finally block is used to ensure they are returned unconditionally, even if an exception occurs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void Update()
{
    while (_packetQueue.TryDequeue(out PacketMessage packet))
    {
        try
        {
            ProcessPacket(packet);
        }
        catch (Exception e)
        {
            Debug.LogError($"[Client] Packet Processing Error: {e.Message}");
        }
        finally
        {
            if(packet.Payload != null) 
                ArrayPool&amp;lt;byte&amp;gt;.Shared.Return(packet.Payload);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  In conclusion..
&lt;/h2&gt;

&lt;p&gt;Of course, this code isn't completely Zero-Allocated.&lt;br&gt;
However, by using ArrayPool to eliminate byte[] allocation/deallocation on every packet received, it addresses memory fragmentation and bulk allocation, which are major causes of GC spikes&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>programming</category>
      <category>csharp</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>[BlindSpot] Log 01. Designing a Game Server Architecture with C++ and Unity</title>
      <dc:creator>RYU JAEMIN</dc:creator>
      <pubDate>Tue, 20 Jan 2026 14:26:14 +0000</pubDate>
      <link>https://dev.to/ryujm1828/blindspot-log-01-architecture-55mf</link>
      <guid>https://dev.to/ryujm1828/blindspot-log-01-architecture-55mf</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/ryujm1828/BlindSpot" rel="noopener noreferrer"&gt;BlindSpot Github&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the 'BlindSpot'?
&lt;/h2&gt;

&lt;p&gt;BlindSpot is the 3:3 top view shooting online game I'm developing.&lt;br&gt;
github : &lt;a href="https://github.com/ryujm1828/BlindSpot" rel="noopener noreferrer"&gt;https://github.com/ryujm1828/BlindSpot&lt;/a&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Whole system architecture
&lt;/h2&gt;

&lt;p&gt;BlindSpot's client and server operate independently and communicate via an efficient binary protocol.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server: C++ (asynchronous I/O based Boost.Asio)&lt;/li&gt;
&lt;li&gt;Client: Unity (C#)&lt;/li&gt;
&lt;li&gt;Protocol: Google Protocol Buffers (Protobuf)&lt;/li&gt;
&lt;li&gt;IDE : Visual Studio 2026&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Why C++ and Unity?
&lt;/h2&gt;

&lt;p&gt;In real-time multiplayer games, the performance of server is directly connected to user experience.&lt;br&gt;
I chose C++ for server to handle a lot of packets without delay, and provide stable concurrency.&lt;br&gt;
I choce Unity for client, because I've used it before, and this project won't be high quality graphics.&lt;/p&gt;
&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1.Efficient Packet Processing
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Using protobuf
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;syntax = "proto3";
package blindspot;

enum PacketID {
    ID_NONE = 0;
    ID_LOGIN_REQUEST = 1;
    ID_LOGIN_RESPONSE = 2;
    ID_JOIN_ROOM_REQUEST = 3;
    ID_JOIN_ROOM_RESPONSE = 4;
    ID_MAKE_ROOM_REQUEST = 5;
    ID_MAKE_ROOM_RESPONSE = 6;
}
message LoginRequest {
  string name = 1;
  string session_key = 2;  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With this code, I prevent human error by automatically generating communication specifications between C++ servers and C# clients with a single .proto file.&lt;/p&gt;
&lt;h4&gt;
  
  
  How it works
&lt;/h4&gt;
&lt;h5&gt;
  
  
  Server -&amp;gt; Client
&lt;/h5&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//BlindSpotServer/Network/Session.h
struct PacketHeader {
    uint16_t length;
    uint16_t id;
};
//...
void Send(uint16_t id, google::protobuf::Message&amp;amp; msg) {
        std::string payload;
        msg.SerializeToString(&amp;amp;payload);

        uint16_t header_size = sizeof(PacketHeader);
        uint16_t payload_size = static_cast&amp;lt;uint16_t&amp;gt;(payload.size());
        uint16_t total_size = header_size + payload_size;

        auto send_buffer = std::make_shared&amp;lt;std::vector&amp;lt;uint8_t&amp;gt;&amp;gt;(total_size);

        PacketHeader* header = reinterpret_cast&amp;lt;PacketHeader*&amp;gt;(send_buffer-&amp;gt;data());
        header-&amp;gt;length = total_size;
        header-&amp;gt;id = id;

        std::memcpy(send_buffer-&amp;gt;data() + header_size, payload.data(), payload_size);

        auto self(shared_from_this());
        boost::asio::async_write(socket_, boost::asio::buffer(*send_buffer),
            [this, self, send_buffer](boost::system::error_code ec, std::size_t /*length*/) {
                if (ec) {
                    std::cout &amp;lt;&amp;lt; "Send failed: " &amp;lt;&amp;lt; ec.message() &amp;lt;&amp;lt; std::endl;
                    PlayerManager::Instance().Remove(shared_from_this());
                }
            });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When server sends response to client, it first serialize the payload.&lt;br&gt;
And adds a header containing the packet type identifier(Id) and packet length.&lt;br&gt;
Lastly, send a packet to the client via async_write.&lt;br&gt;
At this time, since the Session object including the Send function must not disappear during async_write, this is prevented through auto self(shared_from_this());&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//BlindSpotClient/Assets/Scripts/Network&lt;/span&gt;
&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;NetworkManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnReceiveData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IAsyncResult&lt;/span&gt; &lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;bytesRead&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytesRead&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[Client] Disconnected from server."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nf"&gt;CloseConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;//Add received data to assemble buffer&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bytesRead&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recvBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytesRead&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;assembleBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;temp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Process complete packets&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assembleBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;packetSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitConverter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToUInt16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assembleBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assembleBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;packetSize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;packetID&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BitConverter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToUInt16&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assembleBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;packetSize&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;assembleBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToArray&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="nf"&gt;HandlePacket&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;PacketID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;packetID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="n"&gt;assembleBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;packetSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recvBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recvBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AsyncCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OnReceiveData&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"[Client] Receive Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;CloseConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TCP sends data as a stream.&lt;br&gt;
So I had it reassemble the packets as it received the data flow in real time.&lt;br&gt;
The size of the packet is found out through the header, and if the number of packets corresponding to the size is not enough, more data is received, and if the number of packets corresponding to the size is enough, the packets are assembled and then processed.&lt;br&gt;
(For simplicity in this example, I used ToArray(), but it can be optimized)&lt;/p&gt;
&lt;h4&gt;
  
  
  Client -&amp;gt; Server
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;//BlindSpotClient/Assets/Scripts/Network&lt;/span&gt;
&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;NetworkManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PacketID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IMessage&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;packet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToByteArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;payloadSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ushort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;headerSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="kt"&gt;ushort&lt;/span&gt; &lt;span class="n"&gt;totalSize&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ushort&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="n"&gt;headerSize&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;payloadSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;sendBuffer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;totalSize&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

            &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BitConverter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;totalSize&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sendBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BitConverter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;ushort&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sendBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sendBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payloadSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sendBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sendBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"[Client] Sent Packet ID: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LogError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"[Client] Send Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Make header and assemble whole packet.&lt;br&gt;
And send the packet to Server with stream.Write().&lt;br&gt;
It is safe now, however, depending on the server architecture, endianness must be taken into account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//BlindSpotServer/Network/Session.h
void DoRead() {
        auto self(shared_from_this());
        // Wait for data asynchronously
        socket_.async_read_some(boost::asio::buffer(data_, max_length),
            [this, self](boost::system::error_code ec, std::size_t length) {
                if (!ec) {
                    std::cout &amp;lt;&amp;lt; "[Debug] Raw Data Length: " &amp;lt;&amp;lt; length &amp;lt;&amp;lt; std::endl;
                    recv_buffer_.insert(recv_buffer_.end(), data_, data_ + length);

                    while (recv_buffer_.size() &amp;gt;= sizeof(PacketHeader)) {
                        PacketHeader* header = reinterpret_cast&amp;lt;PacketHeader*&amp;gt;(recv_buffer_.data());

                        std::cout &amp;lt;&amp;lt; "[Debug] Expected Packet Length: " &amp;lt;&amp;lt; header-&amp;gt;length &amp;lt;&amp;lt; std::endl;
                        std::cout &amp;lt;&amp;lt; "[Debug] Current Buffer Size: " &amp;lt;&amp;lt; recv_buffer_.size() &amp;lt;&amp;lt; std::endl;
                        if (recv_buffer_.size() &amp;lt; header-&amp;gt;length) {
                            break; // Not enough data for a full packet
                        }

                        std::cout &amp;lt;&amp;lt; "[Debug] Packet ID: " &amp;lt;&amp;lt; header-&amp;gt;id &amp;lt;&amp;lt; ", Length: " &amp;lt;&amp;lt; header-&amp;gt;length &amp;lt;&amp;lt; std::endl;
                        // Process complete packet
                        uint16_t packet_id = header-&amp;gt;id;
                        uint8_t* payload = recv_buffer_.data() + sizeof(PacketHeader);
                        uint16_t payload_size = header-&amp;gt;length - sizeof(PacketHeader);

                        HandlePacket(packet_id, payload, payload_size);

                        recv_buffer_.erase(recv_buffer_.begin(), recv_buffer_.begin() + header-&amp;gt;length);
                    }

                    DoRead(); // Wait for more data
                }
                else {
                    std::cout &amp;lt;&amp;lt; "Client Disconnected." &amp;lt;&amp;lt; ec.message() &amp;lt;&amp;lt; std::endl;
                    PlayerManager::Instance().Remove(shared_from_this());
                }
            });

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like the client, it is implemented in a way that receives data in real time and reassembles packets.&lt;/p&gt;

&lt;h3&gt;
  
  
  Asynchronous I/O
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//BlindSpotServer/main.cpp
try {
        boost::asio::io_context io_context;

        std::cout &amp;lt;&amp;lt; "Server starting on port "&amp;lt;&amp;lt; PORT &amp;lt;&amp;lt; "..." &amp;lt;&amp;lt; std::endl;
        Server s(io_context, PORT);

        io_context.run(); // Start the server event loop
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than having a single thread handle a single connection, we detect OS-level asynchronous events through io_context.&lt;br&gt;
This structure allows us to efficiently manage tens of thousands of connections with a small number of threads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Management
&lt;/h3&gt;

&lt;p&gt;To prevent memory leaks and dangling pointers, which are the most dangerous elements in C++ servers, we actively utilized Smart Pointer(shared_ptr, unique_ptr).&lt;br&gt;
The Session object inherits enable_shared_from_this to safely ensure the object's lifecycle within asynchronous handlers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scalability
&lt;/h3&gt;

&lt;p&gt;Modifying networking code every time a new game feature is added is risky.&lt;br&gt;
BlindSpot uses the Packet Handler pattern to automatically branch the logic based on the received packet ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;The server does not trust the client, and all logic is designed to operate through the session context, not the playerId sent by the client.&lt;br&gt;
(If you find any vulnerabilities, please let me know.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges ahead
&lt;/h2&gt;

&lt;p&gt;Now that I've implemented the ability to create and join a room, let's implement the actual player movement and attacking in the game.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>unity3d</category>
      <category>cpp</category>
      <category>boostasio</category>
    </item>
  </channel>
</rss>
