-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Chapter 1: Evolution of NF
Many years ago, I worked as a front-end developer. It was hard to find an architecture with a good object/data management feature, so I decided to write a framework to use at my workplace.
During my career, I learned Ogre, Mygui, Bigworld, and Unity3D, which gave me some ideas I could use in my own engine, especially BigWorld and Ogre. Eventually, I created NoahGameFrame (NF), my own engine which today can be used as a high performance multiplayer gaming solution.
When it started out, NF was first designed as a client game engine, but later was changed into a game server engine. Because it started as a client engine, it absorbed many advantages of the engines it was inspired by, including Ogre's plug-in and module management system, and Mygui's hierarchical design system. After years of evolution and refinement NF has evolved into a set of game development solutions. Today, NF includes an open-source server architecture, a network library (built on top of libevent), and the source code of a unity3d client demo. NF has been used in multiple projects by multiple companies, including the popular Chinese action MMO, "All People Without Warriors," (Chinese: 《全民无双》).
In the bad old days, many people had to write their own networking libraries and back-end architecture. It was hard to maintain, and hard to add new modules because all code had to work together and interface with the many internal parts. Today I’ll talk about a new architecture, which can be a great help when developing your project. I hope this new architecture can open a new door for anyone who is still working with a bad, hard to maintain architecture.
All right, let's see the first solution used in the bad old days...
Well, it's always best to consider the easiest solution first...
Technically speaking, it's so simple and easy to understand, new team members will find it easy to pick up and join in quickly.
It's bad for scaling, and when a game crash occurs, it will kick all players offline simultaneously.
All right, we can add some new features to solve this: load balancing and server to interface with the Database for us. Our new design is pictured below:
Having gateways/multiple processes allows it to scale a little better, while also avoiding a single point of failure.
The GateServer can shield the GameServer from illegal external data, preventing hostile attacks and ensuring safety.
Having the DBServer gateway means we don't have to directly connect to the database, which will increase processing speed.
More complex than the first solution, and we'll have to write some logic for asynchronous database accessing. Still not ideal for scaling.
...Let's looks the third and final solution (the one NoahGameFrame uses):
Great for scaling. Uses a multi-thread/process Actor model which can be dynamically expanded while maintaining a proportional response to the traffic load. Theoretically, by continuously expanding the hardware, the upper limit of the load can also be continuously increased.
Supports load balancing. Clients can be directed to connect to the server with the current lowest traffic load to distribute processing among a cluster of machines.
Supports a plugin architecture. Instead of maintaining a monolithic system, a large number of basic library plugins can used to increase development efficiency and simplicity.
Avoids the problem of having a single point of failure. Various functions are split into separate servers, effectively lowering the risk of having any single point of failure. A problem in one server won't bring down the entire cluster.
More complex than both previously shown solutions.
Now let's take a look at some basic elements of NF's server architecture.
NF is designed to be split up into Areas for each region your game is deployed into. This is set in the Server.xml file using the "Area" attribute. To simply deploy to all regions, the Area attribute should be the same for all servers in the Server.xml file. To deploy to one region specifically, you can change the value of "Area" to 1, 2, 3 to represent for example America, EU, and Asia.
masterserver manages all loginservers and worldservers. Maintains list of all servers and their statuses (all loginservers and worldservers must register with the masterserver when they are started). It is the boss and center node of the entire architecture. Can be configured to use multiple masterservers without additional code to avoid a single point of failure.
worldserver manages all GameServers and ProxyServers under this service, and saves their state (all gameservers and proxyservers must register with worldserver when they are started). Can be configured to use multiple worldservers without additional code to avoid a single point of failure.
The loginserver is mainly used to display the service list to the user and handle login logic. Can be configured to use multiple loginservers without additional code to avoid a single point of failure.
When started, the gameserver registers itself with the worldserver and accepts management. If the worldserver broadcasts a list of other worldservers, it also needs to connect to the other worldservers (almost all servers will follow this logic to maintain communication between each other). The gameserver must also accept connection from the proxyserver. gameserver also connects to the database, and because the gameserver is an Actor (Actor model), a Database gateway like before is not needed here. When accessing the database, a coroutine is used to make the asynchronous access look like a synchronous one, allowing for better maintainability of the code.
The proxyserver is mainly used to act as an intermediary for client data requests and to check the type of messages. Once started, it needs to connect to the worldserver and accept management, and obtain the gameserver list from worldserver, and then actively connect to all gameservers. The proxyserver reports its own status and open ports to the worldserver. Can be configured to use multiple proxyservers without additional code to avoid a single point of failure.
When any NF server is started, it loads the plug-ins specified in the Plugin.xml
file. In each plug-in, there are a large number of modules which organize the different functions of the plug-in. The plug-in will initialize all the modules, and then start the main loop. The plug-in acts as the container/loader of modules. Modules can call each other and provide services for each other.
The Actor model is used to solve the bottleneck of synchronous blocking accesses, such as database access, http requests, and other long-running operations. In NF, a large number of processes can be split up each running an Actor to increase the game server's maximum traffic load.
From the architecture diagram, it can be seen that the gameserver is directly connected to the database, and does not use a gateway node like previous architectures shown. This is because NoSQL is getting better and better, and it is no longer a bottleneck to connect directly to the database. The actor model solves the problem of synchronous saving/loading of data.
Now you are done learning about the general design and inner workings of NoahGameFrame. If you are interested in it, please read on to the next chapter. We will continue to explain the entire design of NF and the process of using it to create your own game server.