2
\$\begingroup\$

EDIT: I forgot to mention that in this implementation E stands for Entity which is just an ID, C => Component as data-only types and S for Systems which should implement logic

Currently i'm working on a small hobby project which is a multiplayer card game with a client/server architecture. I'm using a framework for the network part which triggers message events to services objects like this:

class AuthRequestMessage : public fqpnet::IMessage
{
public:
  //methods for serialization

  inline const std::string& getLoginId() const 
  { return loginId_; }

  inline const std::string& getPassword() const 
  { return loginId_; }

private:
  std::string loginId_;
  std::string password_;
};

//Handles authentication 
class GatewayService : public fqpnet::Service
{
public:
  //registers handlers
  GatewayService() 

private:
  void onAuthRequestMessage(const AuthRequestMessage* msg, ClientSession* session);
};

and now i'm at the point where i have to create the player entity. My first thought was to create the player entity directly in the message handler. But it feels like this code should be in a system so i created a system and added a method:

class PlayerSystem : public entityx::System<PlayerSystem>
{
public:
//Entity createPlayer(/*info needed...*/);

//update etc
};

But this feels rather bad too as the method is only used once in the handler. My third attempt was to use the builtin messaging system of the ECS:

struct PlayerAuthRequest
{
//id etc
};

class PlayerSystem : public entityx::System<PlayerSystem>
{
public:
void receive(const PlayerAuthRequest& request);

};

//and now just emit the event in the message handler
void GatewayService::onAuthRequestMessage(const AuthRequestMessage* msg, 
ClientSession* session) 
{
  //verify, load from db etc.
  eventMgr->emit<PlayerAuthRequest>(/*data*/);
}

Which of these approaches would be the best? Or are there alternative ones? Basicly i just want to do it in the right way to learn. Performance does not really matter to me.

\$\endgroup\$
1
\$\begingroup\$

and now i'm at the point where i have to create the player entity. My first thought was to create the player entity directly in the message handler. But it feels like this code should be in a system ...

There is nothing inherently wrong with placing the code for creating a player entity somewhere else. The only difference is I would probably not refer to this as a system but more as a factory because the underlying goal is this class constructs some complicated object.

class PlayerFactory {
  static Player* CreatePlayer( /* whatever inputs */ );
}

Under the hood, the PlayerFactory may interact with various subsystems in your architecture in order to accomplish the task of creating a Player object. This may likely include interacting with the entity system to create an entity identifeir for the player and constructing any default components that a Player entity should contain.

But this feels rather bad too as the method is only used once in the handler.

When this is the case, its often because we simply question whether the code belongs in some separate class unit or whether it should just be baked right into the business method or class that which is invoking the call.

What is important here is that code separation is a good thing. Long term, if you need to make changes to the construction of a Player object, where does it make more sense to locate that? The PlayerFactory or trying to find in all the handler spaghetti code where the actual logic happens?

Over the years, i have often preferred handlers that behave more like thin wrappers around other objects that more granularly describe and encapsulate specific behavior. Its the same mindset we approach component-based systems with, to decompose a larger problem into smaller reusable parts, allowing for flexibility and expansion downstream as needed.

Which of these approaches would be the best? Or are there alternative ones? Basicly i just want to do it in the right way to learn.

As you can tell, your event model approach similarly aligns with what I've already discussed, you just use different terminology basically.

The real benefit with the event-model however is that you're allowing for other hooks to be able to respond to that authentication request, not just the player system. That can be a good thing too.

But at the end of the day, there is not one that is "best".

Personal projects in particular often are better suited to fit your own development style and preference style than try to be dictated by a style that you don't connect with well. You'll be able to make far better progress on the code and it'll make more sense to you.

At the end of the day, no design is perfect. We'll always find ways to improve code through refactor iterations and so write code that resonates with you. You can always change it later should you be compelled.

\$\endgroup\$
  • \$\begingroup\$ Awesome! Your post teaches to increase readability, maintainability and most important to not limit myself just on one paradigm. I decided to encapsulate the player creation and related db query in a factory, use direct calls to the systems for request handling and events for inter system communication. Thank you very much Naros! \$\endgroup\$ – Salco Aug 8 '17 at 6:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.