3

As a personal project, I'm setting up a MMO, and writing the server in C#/.NET Core.

As of now my architecture is as follows: The world exists of different areas ( you can go from area to area trough portals ). An area is called a 'room' on my server. Each room has it's own list of clients ( players, each with their TCP stream ).

Now I was wondering if it was ok that I run each room on it's own thread. Each room will manage it's own update cycle ( game loop / room ), as it's independant of other rooms.

My worry is that as the game grows, I will get lots of areas and that a thread per area isn't that great of an idea.

So my question is: Am I doing something wrong or non-benificial in the future ? Am I better off having 1 thread for the game logic for all the rooms ? Or is it ok to have a thread per room ?

  • "as the game grows, I will get lots of areas" will eventually lead to no single-server solution being playable, assuming the density of players per area remains roughly constant or grows – Caleth Oct 1 '18 at 11:53
  • @Caleth what do you suggest ? – TanguyB Oct 1 '18 at 11:56
  • 2
    You'll eventually have to split things across multiple servers, which implies multiple processes, not just multiple threads. Worrying about "too many threads" is moot. – Caleth Oct 1 '18 at 11:58
  • 2
    Run each room as a single instance of the process, rather than a thread. That creates extra complications initially, but it'll then it'll be easier in future to split the processes across many servers. Of course this risks being a YAGNI solution (your game may never grow big enough to warrant a second server), so your best bet may be to design the app such that it has a thread per room now and can be reasonably easily modified to a room per process when needed. – David Arno Oct 1 '18 at 11:59
  • 5
    I really don't think you should be worrying about this at this time. Both from your experience and from development phase, this problem is irrelevant. You will be lucky if your "MMO" can saturate a single thread. You should worry about making the game playable. Optimizations can come later. – Euphoric Oct 1 '18 at 12:00
7

Separating the game rooms into different threads is a good start. It is advisable to go beyond that. How much data is shared between rooms? If little data is shared you might design an internal API (e.g. service calls or via a shared database) for sharing data between rooms and have each room be a separate process. This would ease hosting rooms on different machines.

Perhaps each room can listen at its own endpoint (IP address + port). When a player enters a portal to travel from room A to room B: process A hands the player object to process B via the internal API and channel and simultaneously the player's game client opens a new connection to the endpoint where process B is listening.

Threads can be used to enhance concurrency within a room: communication with the game clients can be handled by a pool of threads, separate from the main game loop. Physics can have its own thread. AI can have its own thread.

  • Their will be little to no data shared between rooms actually, a player only will be influenced by things inside the room. A process per room seems a bit overkill to me as an area isn't that big (e.g. areas in MapleStory if you are familiar with them) – TanguyB Oct 1 '18 at 12:21
  • 3
    @TanguyB If you are going properly MMO with a large map you will want multiple servers. Also if 2 servers can both control a room you can migrate players between them and pull servers down for maintenance without players ever noticing. – ratchet freak Oct 1 '18 at 14:28
  • That answer feel like "a fork would be more appropriate". : – Walfrat Oct 2 '18 at 11:49
  • 1
    @Walfrat, yes fork would be appropriate. Fork (or otherwise starting multiple processes) gives more insulation than threads which eases development and testing. There might be advantages for not restricting the room processes to clones of a single process: requirements may differ between rooms, consider a banking area with lots of players vs. a dungeon with lots of scripted events. – Kasper van den Berg Oct 2 '18 at 12:08
3

I think this might be a viable architecture. The biggest source of bugs in multithreaded applications is shared access to data. So when the rooms have little to no communication with each other and need little to no access to the data not explicitly owned by them, then they seem like the obvious candidate for units of parallelization.

If you are worried about too much parallelization, keep in mind that it is usually easier to reduce threading than to introduce more of it. When it turns out you would like to handle rooms sequentially after all, then it is usually not much of a refactoring effort to change your update-method to process multiple rooms in a loop instead of just one. On the other hand, chopping a single-threaded architecture into a multi-threaded one after the fact can quickly end up in a buggy mess.

By the way, you might want to organize your code around the ThreadPool class and pass Tasks to it where each task consists of performing a single update-tick for a single room. The ThreadPool can be configured to use a specific number of threads. You shouldn't set it higher than the number of (virtual) CPU cores. When you give it more tasks than it has threads, those tasks will be enqueued and processed as soon as a thread is finished. That means you can leave your update scheduling to the framework.

  • 1
    I originally posted this answer on gamedev stack exchange where this question was crossposted and closed by a moderator because crossposting is not allowed on the Stack Exchange Network. The reason why we don't allow crossposting is exactly this: Answers then also need to get crossposted, which results in duplicate content all over the network. – Philipp Oct 1 '18 at 14:53
1

Important facts about multithreading:

Consequences for your architecture:

  • Your room-threading will be much more pleasant for the user: better a slightly slower single room but without delays, rather than an ultrafast single thread letting users experience lags because of all the other rooms that are to be processed in a sequential manner !
  • In an MMO, most of the time your threads will wait for network I/O. This natural waiting effect (voluntary switching) ensures a better usage of the CPU and more than compensate the overhead of context switches.
  • However, your maximum number of rooms will be limited. And this limit stands even if older rooms are unused. If you're thinking of numbers far bellow the figures mentionned, then ok. But otherwise, you should consider another alternative.

Alternatives:

Your multithreading approach is a very good start. But instead of allocating a thread to each single room, you could consider to have a thread managing several rooms. How many could be either determined in the configuration or calculated dynamically at runtime.

Another question is how you manage the I/O. Do rooms manage their user connections ? If no, you'd better allocate more threads for the I/O and decouple it with a queue based architecture.

Finally, the problem with multithreaded rooms is that you can't scale beyond the server like real life MMO do. So you need to think how to scale all this, and how you could in future package group of threads into communicating processes, so that you could add servers with additional processes to manage increasing workload/connections.

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.