posted by Jeremy Friesner on Mon 12th Aug 2002 20:52 UTC

"How to Set up the Custom Server Logic"
Now that we have some idea of what our custom server's sessions will be, let's look at how the server gets set up. Here is the interesting bit of code, found at the end of main(), in frcd.cpp:
[...]
   FRCServer server;
   FRCPlayerSessionFactory playerFactory;

   AbstractReflectSessionRef gameStateSessionRef(new FRCGameStateSession(), NULL);
   ReflectSessionFactoryRef playerFactoryRef(&playerFactory, NULL, false);

   if ((server.AddNewSession(gameStateSessionRef, -1) == B_NO_ERROR)&&
       (server.PutAcceptFactory(4444, playerFactoryRef) == B_NO_ERROR))
   {
      server.ServerProcessLoop();
   }
   else LogTime(MUSCLE_LOG_ERROR, "Error setting up frcd (port 4444 already in use?)\n");

   server.Cleanup();

   return 0;
}

In the first two lines we declare an FRCPlayerSessionFactory object, and the FRCServer object. These objects must both be present for the duration of the server's execution, but because we are in main(), we can nevertheless safely declare them on the stack.

The FRCServer object (named "server") is our subclass of the MUSCLE ReflectServer class. As mentioned on the previous page, it is the object that will be handling all of the networking chores that the server needs to do. The first thing we do with our FRCServer is add a single FRCGameStateSession to it, by calling server.AddNewSession(). The second argument to AddNewSession(), -1, indicates that this session has no associated TCP connection, since "real" TCP sockets are non-negative integers.

The FRCPlayerSessionFactory object (named "playerFactory") is a subclass of MUSCLE's ReflectSessionFactory class. The FRCPlayerSessionFactory object's job is to return a new FRCPlayerSession whenever its CreateSession() method is called. The call to server.PutAcceptFactory() tells the FRCServer object to start listening on port 4444: whenever a TCP connection is accepted on that port, the factory's CreateSession() method will be called, and the new FRCPlayerSession object that FRCPlayerSessionFactory::CreateSession() returns will be responsible for handling all future interactions with that TCP connection.

Once the the FRCGameStateSession has been added and the FRCPlayerSessionFactory has been installed, we are ready to enter the server's main event loop, by calling server.ServerProcessLoop(). Execution will not return from the ServerProcessLoop() method until the server is ready to quit -- and most servers are designed to never quit! Nevertheless, in case the server does quit, we also include a call to server.Cleanup(), which allows the server to tidy things up nicely before the process terminates.

And that's pretty much all there is to setting up the server!

One additional bit of explanation is in order, however: you've probably noticed that both the FRCPlayerSessionFactory object and the FRCGameStateSession object are handed to the FRCServer object wrapped inside Ref objects... an AbstractReflectSessionRef and ReflectSessionFactoryRef, to be exact. Ref objects are a common idiom in MUSCLE code -- many objects are passed around using the (very lightweight) Ref objects, which allows MUSCLE to use reference-counting as an efficient and easy form of garbage collection. With reference-counting, there is no need to worry about when to delete an object that was previously allocated with the new operator -- instead, the object is automagically deleted by the destructor of the very last Ref object that points to it. This avoids most memory-leak and dangling-pointer type bugs with almost no extra effort required from the programmer.

The above code also illustrates another way in which references are handy -- note that the FRCGameStateSession object was allocated on the heap (using the new operator), while the FRCPlayerSessionFactory object was allocated on the stack. But the FRCServer object doesn't know that, and it doesn't have to know that. It merely holds copies of the Ref objects, and accesses the C++ objects that the Ref objects point to. Note, however, that the Ref objects themselves need to know whether or not the object needs to be eventually deleted -- that is what the third argument to the ReflectSessionFactoryRef() constructor does -- by specifying (false), we tell the ReflectSessionFactoryRef that playerFactory was allocated on the stack, and thus should not ever be deleted.

Table of contents
  1. "Foxes, Rabbits, and Carrots"
  2. "The Game and How to Play it"
  3. "A Brief Review of the MUSCLE Networking Layer"
  4. "muscled, The basic MUSCLE server"
  5. "Customizing the MUSCLE server"
  6. "How to Set up the Custom Server Logic"
  7. "The FRCPlayerSession Class"
  8. "The FRCGameStateSession class"
  9. "Overview of the FRC server"
  10. "The FoxRabbitCarrot Client Program"
  11. "How the Client Handles Database Update Messages"
  12. "MUSCLE gaming Performance Issues"
e p (0)    8 Comment(s)

Technology White Papers

See More