We are starting to get the groundwork of our server in place, there are just a couple of things left to do. The first thing is to create a loop that will keep the server running. The second thing is to handle signals such as a user pressing CTRL-C
in the console where the program is running.
For the previous article see part 3, and you can find the code we will be building on on github under the part-3
tag.
The main loop
As a server, our MUD has to wait for timed events and user input, and react to them in different ways. The simplest way is to use an infinite loop. We will add this loop to the main.cpp
source file in an anonymous namespace:
[codesyntax lang=”cpp”]
namespace { bool keep_running = true; void mainloop() { while (keep_running) { } } }
[/codesyntax]
Now call this function from the main function, after initializing the logging system:
[codesyntax lang=”cpp”]
mainloop();
[/codesyntax]
If you run this program you have to use CTRL-C
to terminate it, and you will also notice that it will load the CPU on the computer to the max. Doing a clean shutdown will be done later in this part, but first we want to keep the server from bogging down the computer. For this we will use the new C++ thread functionality, namely the sleep_for
function. For this we need to modify our main loop:
[codesyntax lang=”cpp”]
#include <thread> #include <chrono> namespace { bool keep_running = true; void mainloop() { LOG(debug, "Entering main loop..."); while (keep_running) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } LOG(debug, "Left main loop"); } }
[/codesyntax]
Every time our main loop loops, it will sleep for 100 milliseconds (i.e. one tenth of a second). This gives the computer enough time to also do other tings besides running our server.
Now how to stop the main loop for keep going for ever? Just set the keep_running
variable to false
. For that we add a new function, in the tm
namespace, shutdown
. First wee need to add it to the tm.h
header file:
[codesyntax lang=”cpp”]
void tm_shutdown();
[/codesyntax]
And add the implementation to main.cpp
:
[codesyntax lang=”cpp”]
void tm_shutdown() { LOG(info, "Shutdown down"); keep_running = false; }
[/codesyntax]
Signals
At the moment the only way to shut off our server is to use CTRL-C
, but this kills the server without any chance for us to clean up. For example, if the server is shutting down all players should be properly disconnected and saved, this can not happen if the program is just killed.
One way of handling this, is to catch the signal the operating system send to our program when a user presses CTRL-C
. This can be done using the aptly names signal
system call. In the anonymous namespace in the main.cpp
source file add these functions:
[codesyntax lang=”cpp”]
#include <signal.h> namespace { void handle_sigint(int) { tm::shutdown(); } void init_signals() { signal(SIGINT, handle_sigint); signal(SIGHUP, handle_sigint); signal(SIGTERM, handle_sigint); } // ... mainloop }
[/codesyntax]
When we receive a signal to terminate, call our handle_sigint
function. What the handle_sigint
function does is to call our shutdown
function that tells the main loop to stop looping. These three signals handles the cases of the user pressing CTRL-C
, the terminal the server is running in is closed, or the computer shutting down.
All the code from this part of the series can, as usual, be found in github with the part-4
tag on the master branch.