MUDs are usually client-server applications. That means there are two separate programs, a server and a client. The server program runs on a server in a datacenter (although for testing you can just run it on your local machine). The client program runs on the devices of the players and connects to that server.
The server handles all the game mechanics, while the clients serve as an interface between the player and the game. In the case of classic text-based MUDs, those clients are often just terminal emulators which do nothing but send the text inputs from the player to the server and receive text from the server. They do not contain any logic at all, not even basic syntax checking. All the game logic happens on the server.
If I would build an application like that, I would structure it something like this:
│Game Logic Implementation │
│ │ │
Server │ │Event Router │
│ │ │
│ │ │ │
│Parser │ │Event Translator │
│ │ │ │
Clients │ │
│ Client │ │Client │
│ Input │ │Output │
│ │ │ │
│ │ │ │
- The client sends a text string "Attack DEMON with SWORD" to the server
- The Parser finds out what that actually means (something like
entity.attack([entity, entity.getItem(6)) and translates it to a command to the game logic implementation.
- The Game Logic Implementation then processes that command and communicates the results of the action in form of a series of event objects which it then sends to the event router. Note that these events are not yet human-readable strings describing what happens. They are logical message objects in an internal format.
- The Event Router decides which clients need to know about which event. Some events might go to only the one player it concerns. Some events go to every player in the room. Some events go to every player currently connected to the server.
- The Event Translator then translates these events into strings. Note that depending on who receives the message, the translation could look different. For example, the message stating that Bob did 2 damage to the demon would be translated as "You hit the demon for 2 damage" for Bob and "Bob hits the demon for 2 damage" for everyone else in the room. This is also where you could do literal translation into the preferred language of the end-user if you wanted to.
- The resulting string gets send to the client, which then gets displayed on their screen.
OK, but how would you implement sending messages from client to server and from server to client? In order to learn that, I would recommend you to look for a basic tutorial on "Socket programming in C++". Note that there are no network sockets in the C++ standard library. So you will have to use a library for networking. One popular choice are POSIX sockets - the original BSD socket library that was ported to pretty much everything by now and became the de-facto standard for low-level C/C++ network programming. But there are other networking libraries which might be worth looking at.
Bonus answer: How would this change for a graphical game?
In a graphical game, the line of separation between client and server would move up by a level.
The client is usually not nearly as "dumb" as in a MUD. It usually maintains an own copy of the game state (or at least those parts of the game state which are currently relevant for the player) and takes care of presenting that game-state to the player in form of visual output.
The client usually doesn't just send raw inputs to the server. It translates them into machine-readable messages of what the player intends to do (not does but intends to do - the server still has the authority to deny any player actions).
And the client doesn't receive pre-rendered output. It receives events in a binary format which it then translates into updates to its local copy of the game-state.
The format specification for the binary messages sent between client and server are usually referred to as the "network protocol" of the game. The program code responsible for creating and interpreting those messages is usually called the "netcode".