/// <summary>
        /// Logs the memory useage using GC.GetTotalMemory and returns the memory useage
        /// </summary>
        /// <param name="p">Process to use to calculate memory</param>
        public static void LogMemory(Process p)
        {
            long mem = +GC.GetTotalMemory(false);

            if (mem > maxMemoryUseage)
            {
                maxMemoryUseage = mem;
            }
            Globals_Server.logEvent("GC memory: " + mem);
        }
 /// <summary>
 /// Send an update to the client- used when the message to be sent requires additional information other than just a response code and some strings
 /// </summary>
 /// <param name="message">Message to be sent- can contain any number of details</param>
 public void Update(ProtoMessage message)
 {
     Contract.Requires(message != null);
     message.ActionType = Actions.Update;
     if (conn != null)
     {
         Globals_Server.logEvent("Update " + this.username + ": " + message.ResponseType.ToString());
         Console.WriteLine("Sending update " + message.ResponseType.ToString() + " to " + this.username);
         Server.SendViaProto(message, conn, alg);
     }
 }
        /// <summary>
        /// Updates the client
        /// </summary>
        /// <param name="message">The message code to send</param>
        /// <param name="fields">Additional information to add to the message</param>
        public void Update(DisplayMessages message, string[] fields = null)
        {
            ProtoMessage m = new ProtoMessage();

            m.ActionType    = Actions.Update;
            m.ResponseType  = message;
            m.MessageFields = fields;
            if (conn != null)
            {
                Globals_Server.logEvent("Update " + this.username + ": " + message.ToString());
                Console.WriteLine("Sending update " + message.ToString() + " to " + this.username);
                Server.SendViaProto(m, conn, alg);
            }
        }
        /// <summary>
        /// Processes a client disconnecting from the server- removes the client as an observer, removes their connection and deletes their CryptoServiceProvider
        /// </summary>
        /// <param name="conn">Connection of the client who disconnected</param>
        private void Disconnect(NetConnection conn)
        {
            Contract.Requires(conn != null);
            lock (ServerLock)
            {
                if (clientConnections.ContainsKey(conn))
                {
                    Client client = clientConnections[conn];
                    Globals_Server.logEvent("Client " + client.username + " has disconnected.");
                    Globals_Game.RemoveObserver(client);
                    client.conn = null;
                    clientConnections.Remove(conn);

                    client.alg = null;
                    conn.Disconnect("Disconnect");
                }
            }
        }
        //public static void SendViaProto(global::ProtoMessage.ProtoMessage m, NetConnection conn, bool isPCL, NetEncryption alg = null)
        //{
        //    Contract.Requires(m != null && conn != null);
        //    NetOutgoingMessage msg = server.CreateMessage();
        //    MemoryStream ms = new MemoryStream();
        //    Serializer.SerializeWithLengthPrefix<global::ProtoMessage.ProtoMessage>(ms, m, PrefixStyle.Fixed32);
        //    msg.Write(ms.GetBuffer());
        //    if (alg != null)
        //    {
        //        msg.Encrypt(alg);
        //    }
        //    server.SendMessage(msg, conn, NetDeliveryMethod.ReliableOrdered);
        //    server.FlushSendQueue();
        //}

        /// <summary>
        /// Read a message, get the relevant reply and send to client
        /// </summary>
        /// <param name="m">Deserialised message from client</param>
        /// <param name="connection">Client's connecton</param>
        public void ProcessMessage(ProtoMessage m, NetConnection connection)
        {
            Contract.Requires(connection != null && m != null);
            Client client;

            clientConnections.TryGetValue(connection, out client);
            if (client == null)
            {
                NetOutgoingMessage errorMessage =
                    server.CreateMessage("There was a problem with the connection. Please try re-connecting");
                server.SendMessage(errorMessage, connection, NetDeliveryMethod.ReliableOrdered);
                string log = "Connection from peer " + connection.Peer.UniqueIdentifier +
                             " not found in client connections. Timestamp: " +
                             DateTime.Now.ToString(DateTimeFormatInfo.CurrentInfo);
                Globals_Server.logError(log);
                return;
            }
            var pc = client.myPlayerCharacter;

            if (pc == null || !pc.isAlive)
            {
                NetOutgoingMessage msg = server.CreateMessage("You have no valid PlayerCharacter!");
                server.SendMessage(msg, connection, NetDeliveryMethod.ReliableOrdered);
                server.FlushSendQueue();
            }
            else
            {
                Globals_Server.logEvent("From: " + clientConnections[connection].username +
                                        ": request = " + m.ActionType.ToString());
                ProtoMessage reply = Game.ActionController(m, client);
                // Set action type to ensure client knows which action invoked response
                if (reply == null)
                {
                    ProtoMessage invalid = new ProtoMessage(DisplayMessages.ErrorGenericMessageInvalid);
                    invalid.ActionType = Actions.Update;
                    reply = invalid;
                }
                else
                {
                    reply.ActionType = m.ActionType;
                }
                SendViaProto(reply, connection, client.alg);
            }
        }
        public static void TestRun(bool encrypt = true)
        {
            Process currentProcess = Process.GetCurrentProcess();

            if (encrypt)
            {
                Globals_Server.logEvent("Running test with encryption");
            }
            else
            {
                Globals_Server.logEvent("Running test without encryption");
            }
            double LoginTime, RecruitTime, MoveTime, SpyTime;
            double start = DateTime.Now.TimeOfDay.TotalMilliseconds;

            byte[] encryptionKey = null;
            if (encrypt)
            {
                encryptionKey = LogInManager.GetRandomSalt(32);
            }
            client.LogInAndConnect(Username, Pass, encryptionKey);
            while (!client.IsConnectedAndLoggedIn())
            {
                Thread.Sleep(0);
            }
            LoginTime = DateTime.Now.TimeOfDay.TotalMilliseconds - start;
            client.RecruitTroops(OwnedArmy.armyID, 70, true);
            RecruitTime = ProcessNextAction(Actions.RecruitTroops, currentProcess);
            // Move to another fief
            client.Move(MyPlayerCharacter.charID, NotOwnedFief.id, null);
            MoveTime = ProcessNextAction(Actions.TravelTo, currentProcess);
            // Spy
            client.SpyOnFief(MyPlayerCharacter.charID, MyPlayerCharacter.location.id);
            SpyTime = ProcessNextAction(Actions.SpyFief, currentProcess);
            // Confirm spy
            Globals_Server.logEvent("Time taken to run test (ms): " + (DateTime.Now.TimeOfDay.TotalMilliseconds - start));
            Globals_Server.logEvent("LogIn time: " + LoginTime);
            Globals_Server.logEvent("Recruit time: " + (RecruitTime));
            Globals_Server.logEvent("Travel time: " + MoveTime);
            Globals_Server.logEvent("Spy time: " + SpyTime);
            Globals_Server.logEvent("Max memory measured: " + maxMemoryUseage);
        }
        /// <summary>
        /// Sends a message by serializing with ProtoBufs
        /// </summary>
        /// <param name="m">Message to be sent</param>
        /// <param name="conn">Connection to send across</param>
        /// <param name="alg">Optional encryption algorithm</param>
        public static void SendViaProto(ProtoMessage m, NetConnection conn, NetEncryption alg = null)
        {
            Contract.Requires(m != null && conn != null);
            NetOutgoingMessage msg = server.CreateMessage();
            MemoryStream       ms  = new MemoryStream();

            Serializer.SerializeWithLengthPrefix <ProtoMessage>(ms, m, PrefixStyle.Fixed32);
            msg.Write(ms.GetBuffer());
            if (alg != null)
            {
                msg.Encrypt(alg);
            }
            server.SendMessage(msg, conn, NetDeliveryMethod.ReliableOrdered);
            server.FlushSendQueue();

            Globals_Server.logEvent(""
                                    + " Sending to: " + clientConnections[conn].username
                                    + " | ActionType = " + m.ActionType.ToString()
                                    + " | ResponseType = " + m.ResponseType.ToString()
                                    + " | " + conn.RemoteEndPoint.ToString()
                                    );
        }
            public void read()
            {
                while (client.Status == NetPeerStatus.Running && !ctSource.Token.IsCancellationRequested)
                {
                    WaitHandle.WaitAny(new WaitHandle[] { client.MessageReceivedEvent, ctSource.Token.WaitHandle });
                    NetIncomingMessage im;
                    while ((im = client.ReadMessage()) != null && !ctSource.IsCancellationRequested)
                    {
                        switch (im.MessageType)
                        {
                        case NetIncomingMessageType.DebugMessage:
                        case NetIncomingMessageType.ErrorMessage:
                        case NetIncomingMessageType.WarningMessage:
                        case NetIncomingMessageType.VerboseDebugMessage:
                        case NetIncomingMessageType.Data:
                            try
                            {
                                if (alg != null)
                                {
                                    im.Decrypt(alg);
                                }
                                MemoryStream ms = new MemoryStream(im.Data);
                                ProtoMessage m  = null;
                                try
                                {
                                    m = Serializer.DeserializeWithLengthPrefix <ProtoMessage>(ms, PrefixStyle.Fixed32);
                                }
                                catch (Exception e)
                                {
                                    // Attempt to read string and add to message queue
                                    string s = im.ReadString();
                                    if (!string.IsNullOrWhiteSpace(s))
                                    {
                                        Console.WriteLine("CLIENT: Got message: " + s);

                                        tClient.stringMessageQueue.Enqueue(s);
                                    }
                                }
                                if (m != null)
                                {
                                    Console.WriteLine("CLIENT: Got ProtoMessage with ActionType: " + m.ActionType + " and response type: " + m.ResponseType);
                                    if (m.ResponseType == DisplayMessages.LogInSuccess)
                                    {
                                        loggedIn = true;
                                        tClient.protobufMessageQueue.Enqueue(m);
                                    }
                                    else
                                    {
                                        if (m.ActionType == Actions.Update)
                                        {
                                            // Don't do anything at the moment for updates
                                        }
                                        else
                                        {
                                            tClient.protobufMessageQueue.Enqueue(m);
                                            if (m.ActionType == Actions.LogIn && m.ResponseType == DisplayMessages.None)
                                            {
                                                byte[] key = null;
                                                if (ValidateCertificateAndCreateKey(m as ProtoLogIn, out key))
                                                {
                                                    ComputeAndSendHashAndKey(m as ProtoLogIn, key);
                                                }
                                            }
                                            else
                                            {
                                                // Attempt to read string and add to message queue
                                                string s = im.ReadString();
                                                if (!string.IsNullOrWhiteSpace(s))
                                                {
                                                    tClient.stringMessageQueue.Enqueue(s);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            catch (Exception e)
                            {
                                Globals_Server.logError("Error in reading data: " + e.GetType() + " :" + e.Message + "; Stack Trace: " + e.StackTrace);
                            }
                            break;

                        case NetIncomingMessageType.StatusChanged:

                            NetConnectionStatus status = (NetConnectionStatus)im.ReadByte();
                            Console.WriteLine("CLIENT: Status changed to " + status.ToString());
                            //MemoryStream ms2 = new MemoryStream(im.SenderConnection.RemoteHailMessage.Data);
                            if (status == NetConnectionStatus.Connected)
                            {
                                if (im.SenderConnection.RemoteHailMessage != null)
                                {
                                    try
                                    {
                                        MemoryStream ms2 = new MemoryStream(im.SenderConnection.RemoteHailMessage.Data);
                                        ProtoMessage m   = Serializer.DeserializeWithLengthPrefix <ProtoMessage>(ms2, PrefixStyle.Fixed32);
                                        if (m != null)
                                        {
                                            tClient.protobufMessageQueue.Enqueue(m);
                                            if (m.ActionType == Actions.LogIn && m.ResponseType == DisplayMessages.None)
                                            {
                                                byte[] key = null;
                                                if (ValidateCertificateAndCreateKey(m as ProtoLogIn, out key))
                                                {
                                                    if (autoLogIn)
                                                    {
                                                        ComputeAndSendHashAndKey(m as ProtoLogIn, key);
                                                    }
                                                }
                                                else
                                                {
                                                    Console.WriteLine("Certificate validation failed: Server may be untrusted");
                                                    client.Disconnect("Invalid Certificate");
                                                }
                                            }
                                        }
                                    }
                                    catch (Exception e)
                                    {
                                    }
                                }
                                break;
                            }
                            else if (status == NetConnectionStatus.Disconnected)
                            {
                                string reason = im.ReadString();
                                if (!string.IsNullOrEmpty(reason))
                                {
                                    tClient.stringMessageQueue.Enqueue(reason);
                                }
                            }
                            if (im.SenderConnection.RemoteHailMessage != null && (NetConnectionStatus)im.ReadByte() == NetConnectionStatus.Connected)
                            {
                            }
                            break;

                        case NetIncomingMessageType.ConnectionLatencyUpdated:
                            break;

                        default:
                            break;
                        }
                        client.Recycle(im);
                    }
                }
#if DEBUG
                Globals_Server.logEvent("Client listening thread ends");
#endif
            }
        /// <summary>
        /// Initialise the server, and store some test users and clients.
        /// </summary>
        private void initialise()
        {
            LogInManager.StoreNewUser("helen", "potato");
            LogInManager.StoreNewUser("test", "tomato");
            NetPeerConfiguration config = new NetPeerConfiguration(app_identifier);

            config.LocalAddress       = NetUtility.Resolve(host_name);
            config.MaximumConnections = max_connections;
            config.Port = port;
            config.SetMessageTypeEnabled(NetIncomingMessageType.ConnectionApproval, true);
            config.SetMessageTypeEnabled(NetIncomingMessageType.ConnectionLatencyUpdated, false);
            config.PingInterval      = 10f;
            config.ConnectionTimeout = 100f;

            config.EnableUPnP = true; //#########

            server   = new NetServer(config);
            ctSource = new CancellationTokenSource();
            server.Start();

            try {
                for (int i = 0; i < 3; i++)
                {
                    if (server.UPnP.ForwardPort(port, "Designated Listening Port"))
                    {
                        Globals_Server.logEvent("Port forwarded successfully.");
                        break;
                    }
                    else
                    {
                        Globals_Server.logEvent("Port forward FAILED " + i.ToString());
                    }
                }
            }
            catch (Exception e) {
                Globals_Server.logEvent(e.ToString());
            }

            Globals_Server.server = server;
            Globals_Server.logEvent("Server started- host: " + host_name + ", port: " + port + ", appID: " +
                                    app_identifier + ", max connections: " + max_connections);
            Client client = new Client("helen", "Char_158");

            Globals_Server.Clients.Add("helen", client);
            Client client2 = new Client("test", "Char_126");

            Globals_Server.Clients.Add("test", client2);
            String dir = Directory.GetCurrentDirectory();
            //dir = dir.Remove(dir.IndexOf("RepairHist_mmo"));
            String path;

            if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX)
            {
                path = Path.Combine(dir, "Certificates");
            }
            else
            {
                dir  = Directory.GetParent(dir).FullName;
                dir  = Directory.GetParent(dir).FullName;
                path = Path.Combine(dir, "Certificates");
            }
            LogInManager.InitialiseCertificateAndRSA(path);
        }
Beispiel #10
0
        public void Listen()
        {
            while (server.Status == NetPeerStatus.Running && !ctSource.Token.IsCancellationRequested)
            {
                NetIncomingMessage im;
                WaitHandle.WaitAny(new WaitHandle[] { server.MessageReceivedEvent, ctSource.Token.WaitHandle });
                while ((im = server.ReadMessage()) != null && !ctSource.Token.IsCancellationRequested)
                {
                    if (im.SenderConnection != null)
                    {
                        Globals_Server.logEvent("Recieved: " + im.MessageType.ToString() + " | " + im.SenderConnection.RemoteEndPoint.ToString());
                    }
                    else
                    {
                        Globals_Server.logEvent("Recieved: " + im.MessageType.ToString() + " | NULL");
                    }

                    switch (im.MessageType)
                    {
                    case NetIncomingMessageType.DebugMessage:
                    case NetIncomingMessageType.ErrorMessage:
                    case NetIncomingMessageType.WarningMessage:
                        Globals_Server.logError("Recieved warning message: " + im.ReadString());
                        break;

                    case NetIncomingMessageType.VerboseDebugMessage:
                    case NetIncomingMessageType.Data:
                    {
#if DEBUG
                        //Console.WriteLine("SERVER: recieved data message");
#endif
                        if (!clientConnections.ContainsKey(im.SenderConnection))
                        {
                            //error
                            im.SenderConnection.Disconnect("Not recognised");
                            return;
                        }
                        Client c = clientConnections[im.SenderConnection];
                        if (c.alg != null)
                        {
                            im.Decrypt(c.alg);
                        }
                        ProtoMessage m = null;
                        using (MemoryStream ms = new MemoryStream(im.Data))
                        {
                            try
                            {
                                m = Serializer.DeserializeWithLengthPrefix <ProtoMessage>(ms, PrefixStyle.Fixed32);
                            }
                            catch (Exception e)
                            {
                                NetOutgoingMessage errorMessage = server.CreateMessage(
                                    "Failed to deserialise message. The message may be incorrect, or the decryption may have failed.");
                                if (c.alg != null)
                                {
                                    errorMessage.Encrypt(c.alg);
                                }
                                server.SendMessage(errorMessage, im.SenderConnection,
                                                   NetDeliveryMethod.ReliableOrdered);
                                Globals_Server.logError("Failed to deserialize message for client: " + c.username);
                            }
                        }
                        if (m == null)
                        {
                            string error = "Recieved null message from " + im.SenderEndPoint.ToString();
                            if (clientConnections.ContainsKey(im.SenderConnection))
                            {
                                error += ", recognised client " + clientConnections[im.SenderConnection];
                            }
                            else
                            {
                                error += ", unrecognised client (possible ping)";
                            }
                            error += ". Data: " + im.ReadString();
                            Globals_Server.logError(error);
                            break;
                        }

                        if (m.ActionType == Actions.LogIn)
                        {
                            ProtoLogIn login = m as ProtoLogIn;
                            if (login == null)
                            {
                                im.SenderConnection.Disconnect("Received blank login message.");
                                return;
                            }
                            lock (ServerLock)
                            {
                                if (LogInManager.VerifyUser(c.username, login.userSalt))
                                {
                                    if (LogInManager.ProcessLogIn(login, c))
                                    {
                                        string log = c.username + " logs in from " + im.SenderEndPoint.ToString();
                                        Globals_Server.logEvent(log);
                                    }
                                }
                                else
                                {
                                    ProtoMessage reply = new ProtoMessage
                                    {
                                        ActionType   = Actions.LogIn,
                                        ResponseType = DisplayMessages.LogInFail
                                    };
                                    Server.SendViaProto(reply, c.conn, c.alg);
                                    //reply = new ProtoMessage {
                                    //    ActionType = Actions.Update,
                                    //    ResponseType = DisplayMessages.Error
                                    //};
                                    //Server.SendViaProto(reply, c.conn, c.alg);
                                    im.SenderConnection.Disconnect("Authentication Fail");
                                    Globals_Server.logEvent("Wrong Password, disconnecting user.");
                                }
                            }
                        }
                        // temp for testing, should validate connection first
                        else if (clientConnections.ContainsKey(im.SenderConnection))
                        {
                            if (Globals_Game.IsObserver(c))
                            {
                                ProcessMessage(m, im.SenderConnection);
                                ProtoClient clientDetails = new ProtoClient(c);
                                clientDetails.ActionType   = Actions.Update;
                                clientDetails.ResponseType = DisplayMessages.Success;
                                SendViaProto(clientDetails, im.SenderConnection, c.alg);
                            }
                            else
                            {
                                im.SenderConnection.Disconnect("Not logged in- Disconnecting");
                            }
                        }
                    }
                    break;

                    case NetIncomingMessageType.StatusChanged:
                        byte stat = im.ReadByte();
                        NetConnectionStatus status = NetConnectionStatus.None;
                        if (Enum.IsDefined(typeof(NetConnectionStatus), Convert.ToInt32(stat)))
                        {
                            status = (NetConnectionStatus)stat;
                        }
                        else
                        {
                            Globals_Server.logError("Failure to parse byte " + stat + " to NetConnectionStatus for endpoint " + im.ReadIPEndPoint());
                        }
                        Globals_Server.logEvent("\tStatus is now: " + status);
                        if (status == NetConnectionStatus.Disconnected)
                        {
                            string reason = im.ReadString();
                            if (reason == null)
                            {
                                reason = "Unknown";
                            }
                            Globals_Server.logEvent(im.SenderConnection.RemoteEndPoint.ToString() + " has disconnected. Reason: " + reason);
                            if (clientConnections.ContainsKey(im.SenderConnection))
                            {
                                Disconnect(im.SenderConnection);
                            }
                        }
                        break;

                    case NetIncomingMessageType.ConnectionApproval:
                    {
                        string senderID = im.ReadString();
                        string text     = im.ReadString();
                        Client client;
                        Globals_Server.Clients.TryGetValue(senderID, out client);
                        if (client != null)
                        {
                            ProtoLogIn logIn;
                            //ProtoMessage logIn;
                            if (!LogInManager.AcceptConnection(client, text, out logIn))
                            {
                                im.SenderConnection.Deny("User not recognised.");
                            }
                            else
                            {
                                ProtoMessage temp = logIn;

                                NetOutgoingMessage msg = server.CreateMessage();
                                MemoryStream       ms  = new MemoryStream();
                                // Include X509 certificate as bytes for client to validate
                                //Serializer.SerializeWithLengthPrefix<ProtoLogIn>(ms, logIn, PrefixStyle.Fixed32);
                                Serializer.SerializeWithLengthPrefix <ProtoMessage>(ms, temp, PrefixStyle.Fixed32);
                                msg.Write(ms.GetBuffer());

                                clientConnections.Add(im.SenderConnection, client);
                                client.conn = im.SenderConnection;
                                im.SenderConnection.Approve(msg);
                                //server.FlushSendQueue();
                                Globals_Server.logEvent("Accepted connection from " + client.username + " | " + senderID + " | " + text);
                            }
                        }
                        else
                        {
                            im.SenderConnection.Deny("Username unrecognised.");
                        }
                        server.FlushSendQueue();
                    }

                    break;

                    case NetIncomingMessageType.ConnectionLatencyUpdated:
                        Globals_Server.logEvent("LATENCY: Still getting these.");
                        break;

                    default:
                        Globals_Server.logError("Received unrecognised incoming message type: " + im.MessageType);
                        break;
                    }
                    server.Recycle(im);
                }
            }
            Globals_Server.logEvent("Server listening thread exits.");
        }
Beispiel #11
0
        /// <summary>
        /// Selects random adjoining hex (also equal chance to select current hex)
        /// </summary>
        /// <returns>Fief to move to (or null)</returns>
        /// <param name="from">Current fief</param>
        /// <param name="getOwned">bool indicating whether or not to try to return an owned fief</param>
        /// <param name="owner">owner, when looking for an owned fief</param>
        /// <param name="avoid">Fief to avoid (for retreats)</param>
        public Fief chooseRandomHex(Fief from, bool getOwned = false, PlayerCharacter fiefOwner = null, Fief avoid = null)
        {
            // list to store all edges
            List <TaggedEdge <Fief, string> > choices = new List <TaggedEdge <Fief, string> >();
            // list to store all edges to owned fiefs
            List <TaggedEdge <Fief, string> > ownedChoices = new List <TaggedEdge <Fief, string> >();
            // int to use in edge selection
            int selection = 0;
            // string to contain chosen move direction
            Fief goTo = null;

            //Fief goTo = from;

            // identify and store all target hexes from source hex
            foreach (var e in this.myMap.Edges)
            {
                bool okToAdd = true;
                if (e.Source == from)
                {
                    // no 'avoid' fief specified
                    if (avoid == null)
                    {
                        okToAdd = true;
                    }

                    // if 'avoid' fief specified
                    else
                    {
                        // if is NOT specified 'avoid' fief
                        if (e.Target != avoid)
                        {
                            okToAdd = true;
                        }

                        // if IS specified 'avoid' fief
                        else
                        {
                            okToAdd = false;
                        }
                    }

                    if (okToAdd)
                    {
                        choices.Add(e);

                        // if getOwned, also check for target ownership
                        if (getOwned)
                        {
                            if (e.Target.owner == fiefOwner)
                            {
                                ownedChoices.Add(e);
                            }
                        }
                    }
                }
            }

            // if looking for owned fief, get one if possible
            if ((getOwned) && (ownedChoices.Count > 0))
            {
                // choose fief by generating random int between 0 and no. of targets
                selection = Globals_Game.myRand.Next(0, ownedChoices.Count);

                // get Fief
                goTo = ownedChoices[selection].Target;
            }

            // if ownership not required, choose from all adjoining fiefs
            else if (choices.Count > 0)
            {
                // choose fief by generating random int between 0 and no. of targets
                selection = Globals_Game.myRand.Next(0, choices.Count);

                // get Fief
                goTo = choices[selection].Target;
            }

            if (goTo == null)
            {
                Globals_Server.logEvent("Random hex method returned a null value for fief.");
                goTo = from; // BUG: temporary fix is for character or army to stay where they are instead, problematic for retreating armies.
            }

            return(goTo);
        }