Search Unity

Need help deploying Master Server

Discussion in 'Multiplayer' started by Aubrey-Falconer, Sep 12, 2008.

  1. Aubrey-Falconer

    Aubrey-Falconer

    Joined:
    Feb 13, 2008
    Posts:
    438
    Since the main Unity master server appears to be crumpling under the load of all the great new games coming out, it looks like deploying my own master server is my only option to keep my multiplayer game functioning.

    I have a vps running Ubuntu, but am not too familiar administrating Linux systems from the command line.

    After SSHing into my vps, downloading the latest master server source, uncompressing it into /usr/bin/unity_master_server, and running make, I got what looks to me like a big mess :)
    http://tinypaste.com/35862

    I would really appreciate it if someone could post step by step instructions for deploying a master server.

    (And once it is compiled, how do I start and configure it, and is there anything else I need to do to make sure it always restarts when my server does?)

    Thanks!

    -Aubrey
     
  2. shaun

    shaun

    Joined:
    Mar 23, 2007
    Posts:
    728
    You're missing curses.h, which probably means you need to download the development tools for your linux install. I ran into the same problem on CentOS, I think I was missing automake, GCC and a few other bits.

    You need to build both the connection tester and master server.
    When you start the connection tester it will attempt to connect to the master server, so better to start it second.
    The IP address is hardcoded in the Connection tester, so you'll need to modify the source code. Below is another version of ConnTester.cpp which defaults to 127.0.0.1, but you can specify your own IP and port options;
    Code (csharp):
    1. ConnTester %ConnTesterPort% %FacilitatorIP% %FacilitatorPort%
    The updated conntester.cpp;
    Code (csharp):
    1. #include "BitStream.h"
    2. #include "StringCompressor.h"
    3.  
    4. #include "RakPeerInterface.h"
    5. #include "RakNetworkFactory.h"
    6. #include "RakSleep.h"
    7. #include "MessageIdentifiers.h"
    8. #include "NatPunchthrough.h"
    9.  
    10.  
    11. #include <string.h>
    12.  
    13. #include <signal.h>
    14. #include <curses.h>
    15. #include <stdio.h>
    16. #include <time.h>
    17. #include <stdlib.h>
    18. #include <string>
    19. #include <iostream>
    20. #include <map>
    21.  
    22. void print_log(const char* format, ...)
    23. {
    24.     time_t rawtime;
    25.     struct tm * timeinfo;
    26.     time ( &rawtime );
    27.     timeinfo = localtime ( &rawtime );
    28.     printf("%02d-%02d-%d %02d:%02d:%02d\t",timeinfo->tm_mday, 1+timeinfo->tm_mon, 1900+timeinfo->tm_year, timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
    29.  
    30.     va_list va;
    31.     va_start( va, format );
    32.     vprintf(format, va);
    33. }
    34.  
    35. NatPunchthrough natPunchthrough;
    36.  
    37. int main(int argc, char *argv[])
    38. {  
    39.     // Default settings
    40.     SystemAddress facilitatorAddress;
    41.     facilitatorAddress.SetBinaryAddress("127.0.0.1");
    42.     facilitatorAddress.port = 10736;
    43.    
    44.     int listenPort = 10735;
    45.    
    46.     // Setup phase
    47.     //setlinebuf(stdout);
    48.     std::map<SystemAddress, SystemAddress> addresses;
    49.     RakPeerInterface *peer = RakNetworkFactory::GetRakPeerInterface();
    50.        
    51.     if (argc > 1)
    52.     {
    53.         listenPort = atoi(argv[1]);
    54.         print_log("Connection Tester Port set to %d\n",listenPort);
    55.  
    56.         facilitatorAddress.SetBinaryAddress(argv[2]);
    57.         print_log("Facilitator IP set to %s\n",argv[2]);
    58.  
    59.         facilitatorAddress.port = atoi(argv[3]);
    60.         print_log("Facilitator Port set to %d\n",atoi(argv[3]));   
    61.    
    62.     }
    63.     else
    64.     {
    65.     print_log("Facilitator IP set to 127.0.0.1\n");
    66.     print_log("Facilitator Port set to %d\n",facilitatorAddress.port); 
    67.     print_log("Connection Tester Port set to %d\n",listenPort);
    68.     }
    69.        
    70.    
    71.        
    72.     natPunchthrough.FacilitateConnections(true);
    73.  
    74.     peer->SetMaximumIncomingConnections(1000);
    75.     SocketDescriptor sd(listenPort,0);
    76.     peer->Startup(1000, 0, &sd, 1);
    77.     peer->AttachPlugin(&natPunchthrough);
    78.    
    79.     // Connect to facilitator
    80.     if (!peer->Connect(facilitatorAddress.ToString(false), facilitatorAddress.port,0,0))
    81.     {
    82.         print_log("Error: Failed to connect to NAT facilitator at %s\n", facilitatorAddress.ToString());
    83.         return 1;
    84.     }
    85.    
    86.     Packet *packet;
    87.     while (1)
    88.     {
    89.         packet=peer->Receive();
    90.         while (packet)
    91.         {
    92.             switch (packet->data[0])
    93.             {
    94.                 case ID_DISCONNECTION_NOTIFICATION:
    95.                     print_log("%s has diconnected\n", packet->systemAddress.ToString());
    96.                     break;
    97.                 case ID_CONNECTION_LOST:
    98.                     print_log("Connection to %s lost\n", packet->systemAddress.ToString());
    99.                     if (packet->systemAddress == facilitatorAddress)
    100.                     {
    101.                         print_log("Reconnecting in 10 seconds\n");
    102.                         //sleep(10);
    103.                         RakSleep(10);
    104.                         if (!peer->Connect(facilitatorAddress.ToString(false), facilitatorAddress.port,0,0))
    105.                         {
    106.                             print_log("Error: Failed to connect to NAT facilitator at %s\n", facilitatorAddress.ToString());
    107.                             return 1;
    108.                         }
    109.                     }
    110.                     break;
    111.                 case ID_NEW_INCOMING_CONNECTION:
    112.                     print_log("New connection established to %s\n", packet->systemAddress.ToString());
    113.                     break;
    114.                 case ID_CONNECTION_REQUEST_ACCEPTED:
    115.                     print_log("Connected to %s\n", packet->systemAddress.ToString());
    116.                     // Immediately disconnect again if this is a connection test
    117.                     if (packet->systemAddress != facilitatorAddress)
    118.                         peer->CloseConnection(packet->systemAddress, true, 0);
    119.                     break;
    120.                 case ID_CONNECTION_ATTEMPT_FAILED:
    121.                 {
    122.                     if (addresses.count(packet->systemAddress) > 0)
    123.                     {
    124.                         RakNet::BitStream bitStream;
    125.                         bitStream.Write((unsigned char)254);
    126.                         peer->Send(&bitStream, HIGH_PRIORITY, RELIABLE, 0, addresses[packet->systemAddress], false);
    127.                         addresses.erase(packet->systemAddress);
    128.                     }
    129.                     else
    130.                     {
    131.                         print_log("Failed to connect to %s", packet->systemAddress.ToString());
    132.                     }
    133.                     break;
    134.                 }
    135.                 case ID_NAT_TARGET_NOT_CONNECTED:
    136.                 {
    137.                     SystemAddress systemAddress;
    138.                     RakNet::BitStream bitStream(packet->data, packet->length, false);
    139.                     bitStream.IgnoreBits(8); // Ignore the ID_...
    140.                     bitStream.Read(systemAddress);
    141.                     print_log("NAT target %s is not connected to the facilitator\n", systemAddress.ToString());
    142.                     break;
    143.                 }
    144.                 case ID_NAT_TARGET_CONNECTION_LOST:
    145.                 {
    146.                     SystemAddress systemAddress;
    147.                     RakNet::BitStream bitStream(packet->data, packet->length, false);
    148.                     bitStream.IgnoreBits(8); // Ignore the ID_...
    149.                     bitStream.Read(systemAddress);
    150.                     print_log("NAT connection to %s lost\n", systemAddress.ToString());
    151.                     break;
    152.                 }
    153.                 case 253:
    154.                 {
    155.                     int remotePort;
    156.                     RakNet::BitStream bitStream(packet->data, packet->length, false);
    157.                     bitStream.IgnoreBits(8); // Ignore the ID_...
    158.                     bitStream.Read(remotePort);
    159.  
    160.                     // Add system to address list so it can be told if the test failed
    161.                     SystemAddress remoteTarget;
    162.                     remoteTarget.binaryAddress = packet->systemAddress.binaryAddress;
    163.                     remoteTarget.port = remotePort;
    164.                     addresses[remoteTarget] = packet->systemAddress;
    165.                     char remote[32];
    166.                     strcpy(remote, remoteTarget.ToString());
    167.                     print_log("Adding %s to %s in map\n", packet->systemAddress.ToString(), remote);
    168.  
    169.                     print_log("Attempting connection test to %s:%d\n", packet->systemAddress.ToString(false), remotePort);
    170.                     if (!peer->Connect(packet->systemAddress.ToString(false), remotePort, 0, 0))
    171.                         print_log("Failed to connect to %s:%d\n", packet->systemAddress.ToString(false), remotePort);                  
    172.                     break;
    173.                 }
    174.                 default:
    175.                     print_log("Unknown ID %d from %s\n", packet->data[0], packet->systemAddress.ToString());
    176.             }
    177.             peer->DeallocatePacket(packet);
    178.             packet=peer->Receive();
    179.         }
    180.         RakSleep(30);
    181.     }
    182.  
    183.     peer->Shutdown(100,0);
    184.     RakNetworkFactory::DestroyRakPeerInterface(peer);
    185.     print_log("Quitting\n");
    186.            
    187.     return 0;
    188. }
    189.  
     
  3. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280
    To get the curses.h just type
    Code (csharp):
    1.  
    2. sudo apt-get install libncurses5-dev
    3.  
    To make it start with the server I think you can just add it to /etc/rc.local.

    You don't need your own connection tester if you keep using the official facilitator. A facilitator is integrated in the master server but you don't need to use it. However, setting up the connection tester is pretty straightforward.
     
  4. Aubrey-Falconer

    Aubrey-Falconer

    Joined:
    Feb 13, 2008
    Posts:
    438
    Thanks!

    After installing ncurses, the master server compiled perfectly and appears to be working.
    I wasn't expecting it to be that easy :)

    -(a gleeful) Aubrey
     
  5. Aubrey-Falconer

    Aubrey-Falconer

    Joined:
    Feb 13, 2008
    Posts:
    438
    Hmm...

    Adding the line:

    /usr/bin/unity_master_server/masterserver

    to my rc.local file seemed to work, but a couple hours later all my sites started randomly dropping offline... I finally traced the problem to the fact that my DNS server wasn't running :)

    If I run the /usr/bin/unity_master_server/masterserver line from a terminal window after my server has started everything works great, but if I put it in the rc.local file it seems to prevent my DNS server from starting. Is this because the Unity master server doesn't return control to the rc.local script after it has started? I examined the source code of the master server, but don't see any parameter that would cause it to act like a normal "start" script that doesn't sieze control of the shell.

    What should I put in my rc.local file to start the master server automatically?

    -Aubrey
     
  6. larus

    larus

    Unity Technologies

    Joined:
    Oct 12, 2007
    Posts:
    280
    Yes, it doesn't return. You should put it in the background like so
    Code (csharp):
    1.  
    2. /usr/bin/unity_master_server/masterserver
    3.  
    or wrap it in nohup. Actually, its quite useful to redirect the output to a log file.
    Code (csharp):
    1.  
    2. /usr/bin/unity_master_server/masterserver > /usr/bin/unity_master_server/output.log
    3.  
    or use some fancy log parser.