static void Main(string[] args)
        {
            if (File.Exists("database.db"))
            {
                m_Database = new SQLDatabase("database.db", true);

                m_LoginsTable = m_Database.getLoginTable("logins");
                m_UserTable   = m_Database.getUsersTable("users");
                m_IDTable     = m_Database.getIdTable("ID");
            }
            else
            {
                m_Database = new SQLDatabase("database.db");

                m_LoginsTable = m_Database.addLoginTable("logins",
                                                         "name varchar(20) NOT NULL, " +
                                                         "passwordHash varchar(512) NOT NULL, " +
                                                         "passwordSalt varchar(512) NOT NULL, " +
                                                         "passwordRenewalDate varchar(128) NOT NULL, " +
                                                         "isLoggedIn varchar(8), " +
                                                         "id int NOT NULL");

                m_UserTable = m_Database.addUsersTable("users",
                                                       "name varchar(24) NOT NULL, " +
                                                       "id int NOT NULL, " +
                                                       "securityLevel int NOT NULL");

                m_IDTable = m_Database.addIDTable("ID",
                                                  "name varchar(24) NOT NULL, " +
                                                  "nextID int");

                m_LoginsTable.AddEntry(new string[] { "admin", "J1NF8m6ZRuDcx/5038/xP/zVdHPwg2YEdpOZvEVRFCw=", "IxBicNFzHtBa5GBFOuZTatjPTmVvgQ0JQ5NHwp+BOTI=", DateTime.MaxValue.Date.ToShortDateString(), "false", "0" });
                m_UserTable.AddEntry(new string[] { "admin", "0", "999" });

                m_IDTable.AddIDEntry("next", 1);
            }

            controls = new Controls(m_UserTable, m_LoginsTable);

            Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            // Local address
            serverSocket.Bind(new IPEndPoint(IPAddress.Parse(serverIPAddress), 8500));

            serverSocket.Listen(32);

            bool bQuit = false;

            Console.WriteLine("This is the server!");

            while (!bQuit)
            {
                // When there is a new connection, create a new socket reference
                Socket serverClient = serverSocket.Accept();

                // Start a new thread assigned to this socket
                Thread myThread = new Thread(ReceiveClientProcess);
                myThread.Start(serverClient);

                Thread.Sleep(500);

                // Perform any new connection actions. Log timestamp etc
            }
        }
        static void ReceiveClientProcess(Object _socket)
        {
            bool bQuit = false;

            Socket clientSocket = (Socket)_socket;

            Console.WriteLine("client receive new user thread");

            String sendMsg = "";

            while (bQuit == false)
            {
                try
                {
                    byte[] buffer = new byte[4096];
                    int    result;

                    result = clientSocket.Receive(buffer);

                    if (result > 0)
                    {
                        MemoryStream stream = new MemoryStream(buffer);
                        BinaryReader read   = new BinaryReader(stream);

                        Msg message = Msg.DecodeStream(read);

                        if (message != null)
                        {
                            Console.Write("Client action: " + clientSocket + ": " + message.ToString() + "\r\n");
                            switch (message.mID)
                            {
                            case ActionMsg.ID:
                            {
                                ActionMsg actionMessage = (ActionMsg)message;

                                String formattedMsg = actionMessage.msg;

                                String clientName = GetNameFromSocket(clientSocket);

                                lock (m_UserSocketDictionary)
                                {
                                    String thisUserName = GetNameFromSocket(clientSocket);

                                    sendMsg = controls.Update(clientName, formattedMsg);

                                    Console.WriteLine(sendMsg);

                                    try
                                    {
                                        if (sendMsg.Substring(0, 23) == "<AuthenticateRequested>")
                                        {
                                            SendLoginMessage(clientSocket, "AuthenticateRequested");
                                            break;
                                        }
                                    }
                                    catch { }

                                    // If sendMsg conditions have not returned true for any of the above specific cases, then send the message back to the player socket
                                    SendActionMessage(clientSocket, "", sendMsg);
                                }
                            }
                            break;

                            case CreateNewUserMsg.ID:
                            {
                                CreateNewUserMsg createNewUserMessage = (CreateNewUserMsg)message;
                                String           newLoginInfo         = createNewUserMessage.msg;

                                // Safe to split with this as no spaces allowed in username or password
                                String[] processedLoginInfo = newLoginInfo.Split(' ');

                                // If length == 1 then the client is performing a username availability check on the database
                                if (processedLoginInfo.Length == 1)
                                {
                                    if (m_LoginsTable.queryExists(processedLoginInfo[0], "name"))
                                    {
                                        SendNewUserMessage(clientSocket, "NameTaken");
                                    }
                                    else
                                    {
                                        SendNewUserMessage(clientSocket, "NameAvailable");
                                    }
                                }
                                else
                                {
                                    // The position of the user name in the message
                                    String userName = processedLoginInfo[0];

                                    // Someone else may have taken the userName since it was checked last so do one more quick check
                                    if (m_LoginsTable.queryExists(userName, "name"))
                                    {
                                        SendNewUserMessage(clientSocket, "NameTaken");
                                    }
                                    else
                                    {
                                        // Tells the client to close it's 'registerNewUser' window
                                        SendNewUserMessage(clientSocket, "Success");

                                        // Get the unique id first so as to use the same ID in the user table and the login table to link them together
                                        String uniqueID = GetNextUniqueID().ToString();

                                        // Add the new user details to the logins table
                                        m_LoginsTable.AddEntry(new string[]
                                            {
                                                // Starting values
                                                userName,                                                                    // name
                                                processedLoginInfo[1],                                                       // password
                                                processedLoginInfo[2],                                                       // salt
                                                DateTime.Today.AddMonths(passwordRenewalNumberOfMonths).ToShortDateString(), // password renewal date
                                                "false",                                                                     // is logged in
                                                uniqueID                                                                     // Id (not currently used as each name is unique)
                                            });

                                        // Add the new user details to the users table
                                        m_UserTable.AddEntry(new string[]
                                            {
                                                userName,                           // name
                                                uniqueID,                           // Id (the same one as used in the login table)
                                                "0"                                 // Security level
                                            });
                                    }
                                }
                            }
                            break;

                            case LoginMsg.ID:

                                LoginMsg loginAttempt          = (LoginMsg)message;
                                String   loginAttemptMessage   = loginAttempt.msg;
                                String[] processedLoginMessage = loginAttemptMessage.Split(' ');

                                // The position of the user name in the message
                                String submittedUserName = processedLoginMessage[0];

                                // First check is whether the logins table contains a record of this username
                                if (!m_LoginsTable.queryExists(submittedUserName, "name"))
                                {
                                    SendLoginMessage(clientSocket, "LoginFailed");
                                }
                                // Else if client is requesting salt
                                else if (processedLoginMessage[1] == "RequestSalt")
                                {
                                    if (true)    //m_LoginsTable.getStringFieldFromName(submittedUserName, "isLoggedIn") == "false")
                                    {
                                        String salt = m_LoginsTable.getStringFieldFromName(submittedUserName, "passwordSalt");
                                        SendLoginMessage(clientSocket, "Salt " + salt);
                                    }
                                    else
                                    {
                                        SendLoginMessage(clientSocket, "UserAlreadyLoggedIn");
                                    }
                                }
                                else
                                {
                                    DateTime.TryParse(m_LoginsTable.getStringFieldFromName(submittedUserName, "passwordRenewalDate"), out DateTime dtRenewDate);

                                    if (DateTime.Today.CompareTo(dtRenewDate) >= 0)
                                    {
                                        SendUpdatePasswordMessage(clientSocket, "UpdatePasswordRequired");
                                        break;
                                    }

                                    // Get the database copy of the hashed salted password
                                    String databaseHash = m_LoginsTable.getStringFieldFromName(submittedUserName, "passwordHash");

                                    // Compare against the hashed salted password sent in the login message
                                    if (processedLoginMessage[1] == databaseHash)
                                    {
                                        SendLoginMessage(clientSocket, "LoginAccepted");

                                        m_UserSocketDictionary[submittedUserName] = clientSocket;

                                        m_LoginsTable.setFieldFromName(submittedUserName, "true", "isLoggedIn");

                                        // Initial message to client
                                        SendActionMessage(clientSocket, "", "Welcome user");
                                    }
                                    else
                                    {
                                        SendLoginMessage(clientSocket, "LoginFailed");
                                    }
                                }
                                break;

                            case UpdatePasswordMsg.ID:
                            {
                                UpdatePasswordMsg UpdatePasswordMsg  = (UpdatePasswordMsg)message;
                                String            updatePasswordInfo = UpdatePasswordMsg.msg;

                                String[] processedPasswordInfo = updatePasswordInfo.Split(' ');

                                // Add the new user details to the logins table
                                m_LoginsTable.UpdatePassword(new string[]
                                    {
                                        // Starting values
                                        processedPasswordInfo[0],                                                    // name
                                        processedPasswordInfo[1],                                                    // password
                                        processedPasswordInfo[2],                                                    // salt
                                        DateTime.Today.AddMonths(passwordRenewalNumberOfMonths).ToShortDateString(), // password renewal date
                                    });

                                // Tells the client to close it's window
                                SendUpdatePasswordMessage(clientSocket, "SuccessUpdatedPassword");
                            }
                            break;

                            case LogoutMsg.ID:
                                throw new Exception();
                            }
                        }
                    }
                }

                // Remove user from process
                catch (Exception)
                {
                    bQuit = true;

                    String output = "Lost client: " + GetNameFromSocket(clientSocket);

                    Console.WriteLine(output);

                    // Log user disconnected?

                    // Try to adjust the isLoggedIn field as the client may disconnect before they have logged in successfully
                    try
                    {
                        // Allow the client to log back in if disconnected
                        m_LoginsTable.setFieldFromName(GetNameFromSocket(clientSocket), "false", "isLoggedIn");
                    }
                    catch { }

                    lock (m_UserSocketDictionary)
                    {
                        try
                        {
                            // Sanity check, then remove player information from playerDictionary
                            if (m_UserSocketDictionary.ContainsKey(GetNameFromSocket(clientSocket)))
                            {
                                m_UserSocketDictionary.Remove(GetNameFromSocket(clientSocket));
                            }
                        }
                        catch (Exception)
                        { }
                    }

                    // Log action?
                }
            }
        }