Beispiel #1
0
        /// <summary>
        ///     When we get a message from a client, we handle the message here
        ///     and perform any necessary tasks.
        /// </summary>
        private void ListenerOnNetworkReceiveEvent(NetPeer peer, NetPacketReader reader, DeliveryMethod deliveryMethod)
        {
            try
            {
                // Parse this message
                bool relayOnServer = CommandReceiver.Parse(reader, peer);

                if (relayOnServer)
                {
                    // Copy relevant message part (exclude protocol headers)
                    byte[] data = new byte[reader.UserDataSize];
                    Array.Copy(reader.RawData, reader.UserDataOffset, data, 0, reader.UserDataSize);

                    // Send this message to all other clients
                    var peers = _netServer.ConnectedPeerList;
                    foreach (var client in peers)
                    {
                        // Don't send the message back to the client that sent it.
                        if (client.Id == peer.Id)
                        {
                            continue;
                        }

                        // Send the message so the other client can stay in sync
                        client.Send(data, DeliveryMethod.ReliableOrdered);
                    }
                }
            }
            catch (Exception ex)
            {
                ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, "Error while parsing command. See log.");
                _logger.Error(ex, $"Encountered an error while reading command from {peer.EndPoint.Address}:{peer.EndPoint.Port}:");
            }
        }
Beispiel #2
0
 public void HandlePlayerConnect(Player player)
 {
     _logger.Info($"Player {player.Username} has connected!");
     ChatLogPanel.PrintGameMessage($"Player {player.Username} has connected!");
     MultiplayerManager.Instance.PlayerList.Add(player.Username);
     Command.HandleClientConnect(player);
 }
Beispiel #3
0
        private static void RemoveUnused <N>(object arr, N id, string type) where N : IConvertible
        {
            N[]       unusedItems      = (N[])arr.GetType().GetField("m_unusedItems", ReflectionHelper.AllAccessFlags).GetValue(arr);
            FieldInfo unusedCountField = arr.GetType().GetField("m_unusedCount", ReflectionHelper.AllAccessFlags);
            uint      unusedCount      = (uint)unusedCountField.GetValue(arr);

            uint expectedPos = Convert.ToUInt32(id) - 1;

            // Special case, when array was modified (search for the id)
            if (expectedPos >= unusedCount || !EqualityComparer <N> .Default.Equals(unusedItems[expectedPos], id))
            {
                bool found = false;
                for (uint num = 0; num < unusedCount; num++)
                {
                    if (EqualityComparer <N> .Default.Equals(unusedItems[num], id))
                    {
                        expectedPos = num;
                        found       = true;
                        break;
                    }
                }

                if (!found)
                {
                    // The arrays are no longer in sync
                    Log.Error($"{type}: Received id {id} already in use. Please restart the multiplayer session!");
                    ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, "ID collision. Please restart the multiplayer session.");
                    return;
                }
            }

            unusedItems[expectedPos] = unusedItems[--unusedCount];

            unusedCountField.SetValue(arr, unusedCount);
        }
Beispiel #4
0
            public static void RemoveUnused(A arr, N id, string type)
            {
                N[]       unusedItems      = (N[])arr.GetType().GetField("m_unusedItems", AccessTools.all).GetValue(arr);
                FieldInfo unusedCountField = arr.GetType().GetField("m_unusedCount", AccessTools.all);
                uint      unusedCount      = (uint)unusedCountField.GetValue(arr);

                uint expectedPos = Convert.ToUInt32(id) - 1;

                // Special case, when array was modified (search for the id)
                if (!EqualityComparer <N> .Default.Equals(unusedItems[expectedPos], id))
                {
                    bool found = false;
                    for (uint num = 0; num <= unusedCount; num++)
                    {
                        if (EqualityComparer <N> .Default.Equals(unusedItems[num], id))
                        {
                            expectedPos = num;
                            found       = true;
                            break;
                        }
                    }

                    if (!found)
                    {
                        // The arrays are no longer in sync
                        LogManager.GetCurrentClassLogger().Error($"{type}: Received id already in use. Please restart the multiplayer session!");
                        ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, "ID collision. Please restart the multiplayer session.");
                        return;
                    }
                }

                unusedItems[expectedPos] = unusedItems[--unusedCount];

                unusedCountField.SetValue(arr, unusedCount);
            }
        protected override void Handle(ClientConnectCommand command)
        {
            LogManager.GetCurrentClassLogger().Info($"Player {command.Username} has connected!");
            ChatLogPanel.PrintGameMessage($"Player {command.Username} has connected!");

            MultiplayerManager.Instance.PlayerList.Add(command.Username);
        }
Beispiel #6
0
        private void ListenerOnPeerDisconnectedEvent(NetPeer peer, DisconnectInfo disconnectInfo)
        {
            if (!ConnectedPlayers.TryGetValue(peer.Id, out Player player))
            {
                return;
            }

            _logger.Info($"Player {player.Username} lost connection! Reason: {disconnectInfo.Reason}");

            switch (disconnectInfo.Reason)
            {
            case DisconnectReason.RemoteConnectionClose:
                ChatLogPanel.PrintGameMessage($"Player {player.Username} disconnected!");
                break;

            case DisconnectReason.Timeout:
                ChatLogPanel.PrintGameMessage($"Player {player.Username} timed out!");
                break;

            default:
                ChatLogPanel.PrintGameMessage($"Player {player.Username} lost connection!");
                break;
            }

            HandlePlayerDisconnect(player);
        }
Beispiel #7
0
        public override void Handle(ClientDisconnectCommand command)
        {
            LogManager.GetCurrentClassLogger().Info($"Player {command.Username} has disconnected!");
            ChatLogPanel.PrintGameMessage($"Player {command.Username} has disconnected!");

            MultiplayerManager.Instance.PlayerList.Remove(command.Username);

            TransactionHandler.ClearTransactions(command.ClientId);
        }
Beispiel #8
0
        protected override void Handle(ClientDisconnectCommand command)
        {
            Log.Info($"Player {command.Username} has disconnected!");
            ChatLogPanel.PrintGameMessage($"Player {command.Username} has disconnected!");

            MultiplayerManager.Instance.PlayerList.Remove(command.Username);

            TransactionHandler.ClearTransactions(command.ClientId);
            ToolSimulator.RemoveSender(command.ClientId);
        }
Beispiel #9
0
        /// <summary>
        ///     Starts the server with the specified config options
        /// </summary>
        /// <param name="serverConfig">Server config information</param>
        /// <returns>If the server has started.</returns>
        public bool StartServer(ServerConfig serverConfig)
        {
            // If the server is already running, we will stop and start it again
            if (Status == ServerStatus.Running)
            {
                StopServer();
            }

            // Set the config
            Config = serverConfig;

            // Let the user know that we are trying to start the server
            _logger.Info($"Attempting to start server on port {Config.Port}...");


            // Attempt to start the server
            _netServer.DiscoveryEnabled = true;
            var result = _netServer.Start(Config.Port);

            // If the server has not started, tell the user and return false.
            if (!result)
            {
                _logger.Error("The server failed to start.");
                StopServer(); // Make sure the server is fully stopped
                return(false);
            }

            try
            {
                // This async stuff is nasty, but we have to target .net 3.5 (unless cities skylines upgrades to something higher).
                var nat = new NatDiscoverer();
                var cts = new CancellationTokenSource();
                cts.CancelAfter(5000);

                nat.DiscoverDeviceAsync(PortMapper.Upnp, cts).ContinueWith(task => task.Result.CreatePortMapAsync(new Mapping(Protocol.Udp, Config.Port,
                                                                                                                              Config.Port, "Cities Skylines Multiplayer (UDP)"))).Wait();
            }
            catch (Exception e)
            {
                _logger.Error($"Failed to automatically open port. Manual Port Forwarding is required: {e.Message}");
                ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, "Failed to automatically open port. Manual port forwarding is required.");
            }

            // Update the status
            Status = ServerStatus.Running;

            // Initialize host player
            _hostPlayer = new Player(Config.Username);
            MultiplayerManager.Instance.PlayerList.Add(_hostPlayer.Username);

            // Update the console to let the user know the server is running
            _logger.Info("The server has started.");
            ChatLogPanel.PrintGameMessage("The server has started.");
            return(true);
        }
Beispiel #10
0
        public override void Handle(ParkCreateCommand command)
        {
            DistrictHandler.IgnoreAll = true;
            DistrictManager.instance.CreatePark(out byte park, command.ParkType, command.ParkLevel);

            if (park != command.ParkId)
            {
                LogManager.GetCurrentClassLogger().Log(LogLevel.Error, $"Park array no longer in sync! Generated {park} instead of {command.ParkId}");
                ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, "Park array no longer in sync! Please restart the multiplayer session!");
            }

            DistrictManager.instance.m_parks.m_buffer[park].m_randomSeed = command.Seed;
            DistrictHandler.IgnoreAll = false;
        }
Beispiel #11
0
        protected override void Handle(ParkCreateCommand command)
        {
            IgnoreHelper.StartIgnore();
            DistrictManager.instance.CreatePark(out byte park, command.ParkType, command.ParkLevel);

            if (park != command.ParkId)
            {
                Log.Error($"Park array no longer in sync! Generated {park} instead of {command.ParkId}");
                ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, "Park array no longer in sync! Please restart the multiplayer session!");
            }

            DistrictManager.instance.m_parks.m_buffer[park].m_randomSeed = command.Seed;
            IgnoreHelper.EndIgnore();
        }
        protected override void Handle(DistrictCreateCommand command)
        {
            IgnoreHelper.StartIgnore();
            DistrictManager.instance.CreateDistrict(out byte district);

            if (district != command.DistrictId)
            {
                LogManager.GetCurrentClassLogger().Log(LogLevel.Error, $"District array no longer in sync! Generated {district} instead of {command.DistrictId}");
                ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, "District array no longer in sync! Please restart the multiplayer session!");
            }

            DistrictManager.instance.m_districts.m_buffer[district].m_randomSeed = command.Seed;
            IgnoreHelper.EndIgnore();
        }
Beispiel #13
0
        /// <summary>
        /// Stops the client or server, depending on the current role
        /// </summary>
        public void StopEverything()
        {
            switch (CurrentRole)
            {
            case MultiplayerRole.Client:
                CurrentClient.Disconnect();
                ChatLogPanel.PrintGameMessage("Disconnected from server.");
                break;

            case MultiplayerRole.Server:
                CurrentServer.StopServer();
                ChatLogPanel.PrintGameMessage("Server stopped.");
                break;
            }
            CurrentRole = MultiplayerRole.None;
        }
Beispiel #14
0
        private void ListenerOnPeerDisconnectedEvent(NetPeer peer, DisconnectInfo disconnectInfo)
        {
            if (Status == ClientStatus.Connecting)
            {
                ConnectionMessage = $"Failed to connect!";
            }

            // Log the error message
            _logger.Info($"Disconnected from server. Message: {disconnectInfo.Reason}, Code: {disconnectInfo.SocketErrorCode}");

            // Log the reason to the console if we are not in 'connecting' state
            if (Status != ClientStatus.Connecting)
            {
                switch (disconnectInfo.Reason)
                {
                case DisconnectReason.Timeout:
                    ChatLogPanel.PrintGameMessage("Disconnected: Timed out!");
                    break;

                case DisconnectReason.DisconnectPeerCalled:
                    ChatLogPanel.PrintGameMessage("Disconnected!");
                    break;

                case DisconnectReason.RemoteConnectionClose:
                    ChatLogPanel.PrintGameMessage("Disconnected: Server closed!");
                    break;

                default:
                    ChatLogPanel.PrintGameMessage($"Disconnected: Connection lost ({disconnectInfo.Reason})!");
                    break;
                }
            }

            // If we are connected, disconnect
            if (Status == ClientStatus.Connected)
            {
                MultiplayerManager.Instance.StopEverything();
            }

            // In the case of ClientStatus.Connecting, this also ends the wait loop
            Status = ClientStatus.Disconnected;
        }
Beispiel #15
0
 /// <summary>
 ///     Called when the speed or pause state have changed.
 ///     This normally happens when the player clicks on one of the two buttons in the bottom left.
 ///     This will only trigger any action if the current state is either Playing or Paused.
 /// </summary>
 /// <param name="pause">If the game should be paused.</param>
 /// <param name="speed">The newly selected speed.</param>
 private static void PlayPauseSpeedChanged(bool pause, int speed)
 {
     if (_state == SpeedPauseState.Paused && !pause)
     {
         RequestPlay(speed);
         Log.Debug($"[SpeedPauseHelper] State {SpeedPauseState.Playing} requested locally.");
     }
     else if (_state == SpeedPauseState.Playing && pause)
     {
         RequestPause();
         Log.Debug($"[SpeedPauseHelper] State {SpeedPauseState.Paused} requested locally.");
     }
     else if (_state == SpeedPauseState.Playing && speed != _speed)
     {
         RequestSpeedChange(speed);
         Log.Debug("[SpeedPauseHelper] Speed change requested locally.");
     }
     else
     {
         ChatLogPanel.PrintGameMessage("Please wait until all players have reached the same play/pause state and speed!");
         Log.Info($"[SpeedPauseHelper] (Pause: {pause}, Speed: {speed}) requested, but state was {_state}");
     }
 }
Beispiel #16
0
        public override void HandleOnClient(ConnectionResultCommand command)
        {
            // We only want this message while connecting
            if (MultiplayerManager.Instance.CurrentClient.Status != ClientStatus.Connecting)
            {
                return;
            }

            // If we are allowed to connect
            if (command.Success)
            {
                // Log and set that we are connected.
                _logger.Info("Successfully connected to server.");
                ChatLogPanel.PrintGameMessage("Successfully connected to server.");
                MultiplayerManager.Instance.CurrentClient.Status = ClientStatus.Connected;
            }
            else
            {
                _logger.Info($"Could not connect: {command.Reason}");
                MultiplayerManager.Instance.CurrentClient.ConnectionMessage = command.Reason;
                MultiplayerManager.Instance.CurrentClient.Disconnect();
            }
        }
        public override void Handle(ConnectionResultCommand command)
        {
            // We only want this message while connecting
            if (MultiplayerManager.Instance.CurrentClient.Status != ClientStatus.Connecting)
            {
                return;
            }

            // If we are allowed to connect
            if (command.Success)
            {
                // Log and set that we are connected.
                _logger.Info("Successfully connected to server.");
                ChatLogPanel.PrintGameMessage("Successfully connected to server.");
                MultiplayerManager.Instance.CurrentClient.Status   = ClientStatus.Connected;
                MultiplayerManager.Instance.CurrentClient.ClientId = command.ClientId;
            }
            else
            {
                _logger.Info($"Could not connect: {command.Reason}");
                MultiplayerManager.Instance.CurrentClient.ConnectionMessage = command.Reason;
                MultiplayerManager.Instance.CurrentClient.Disconnect();
                if (command.DLCBitMask != SteamHelper.DLC_BitMask.None)
                {
                    DLCHelper.DLCComparison compare = DLCHelper.Compare(command.DLCBitMask, DLCHelper.GetOwnedDLCs());
                    if (compare.ClientMissing != SteamHelper.DLC_BitMask.None)
                    {
                        ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, $"You are missing the following DLCs: {compare.ClientMissing}");
                    }
                    if (compare.ServerMissing != SteamHelper.DLC_BitMask.None)
                    {
                        ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, $"The server doesn't have the following DLCs: {compare.ServerMissing}");
                    }
                    ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Normal, "DLCs can be disabled via checkbox in Steam");
                }
            }
        }
 protected override void Handle(ChatMessageCommand command)
 {
     ChatLogPanel.PrintChatMessage(command.Username, command.Message);
 }
Beispiel #19
0
 public override void HandleOnClient(ChatMessageCommand command)
 {
     ChatLogPanel.PrintChatMessage(command.Username, command.Message);
 }
Beispiel #20
0
 public override void HandleOnServer(ChatMessageCommand command, Player player)
 {
     ChatLogPanel.PrintChatMessage(command.Username, command.Message);
 }
Beispiel #21
0
 /// <summary>
 ///     Called whenever an error happens, we
 ///     log this to the console for now.
 /// </summary>
 private void ListenerOnNetworkErrorEvent(NetEndPoint endpoint, int socketerrorcode)
 {
     ChatLogPanel.GetDefault().AddGameMessage(ChatLogPanel.MessageType.Error, $"[{endpoint.Host}:{endpoint.Port}] is causing errors. See log.");
     _logger.Error($"Received an error from {endpoint.Host}:{endpoint.Port}. Code: {socketerrorcode}");
 }
Beispiel #22
0
        /// <summary>
        ///     Attempt to connect to a server
        /// </summary>
        /// <param name="clientConfig">Client config params</param>
        /// <returns>True if the client is connected to the server, false if not</returns>
        public bool Connect(ClientConfig clientConfig)
        {
            // Let the user know that we are trying to connect to a server
            _logger.Info($"Attempting to connect to server at {clientConfig.HostAddress}:{clientConfig.Port}...");

            // if we are currently trying to connect, cancel
            // and try again.
            if (Status == ClientStatus.Connecting)
            {
                _logger.Info("Current status is 'connecting', attempting to disconnect first.");
                Disconnect();
            }

            // The client is already connected so we need to
            // disconnect.
            if (Status == ClientStatus.Connected)
            {
                _logger.Info("Current status is 'connected', attempting to disconnect first.");
                Disconnect();
            }

            // Set the configuration
            Config = clientConfig;

            // Start the client, if client setup fails, return out and
            // tell the user
            var result = _netClient.Start();

            if (!result)
            {
                _logger.Error("The client failed to start.");
                ConnectionMessage = "The client failed to start.";
                Disconnect(); // make sure we are fully disconnected
                return(false);
            }

            _logger.Info("Set status to 'connecting'...");

            // Try connect to server, update the status to say that
            // we are trying to connect.
            try
            {
                _netClient.Connect(Config.HostAddress, Config.Port, "CSM");
            }
            catch (Exception ex)
            {
                ConnectionMessage = "Failed to connect.";
                _logger.Error(ex, $"Failed to connect to {Config.HostAddress}:{Config.Port}");
                ChatLogPanel.PrintGameMessage(ChatLogPanel.MessageType.Error, $"Failed to connect: {ex.Message}");
                Disconnect();
                return(false);
            }

            // Start processing networking
            Status   = ClientStatus.Connecting;
            ClientId = 0;

            // We need to wait in a loop for 30 seconds (waiting 500ms each time)
            // while we wait for a successful connection (Status = Connected) or a
            // failed connection (Status = Disconnected).
            var waitWatch = new Stopwatch();

            waitWatch.Start();

            // Try connect for 30 seconds
            while (waitWatch.Elapsed < TimeSpan.FromSeconds(30))
            {
                // If we connect, exit the loop and return true
                if (Status == ClientStatus.Connected)
                {
                    _logger.Info("Client has connected.");
                    return(true);
                }

                // The client cannot connect for some reason, the ConnectionMessage
                // variable will contain why.
                if (Status == ClientStatus.Disconnected)
                {
                    _logger.Warn("Client disconnected while in connecting loop.");
                    Disconnect(); // make sure we are fully disconnected
                    return(false);
                }

                // Wait 500ms
                Thread.Sleep(500);
            }

            // We have timed out
            ConnectionMessage = "Could not connect to server, timed out.";
            _logger.Warn("Connection timeout!");

            // Did not connect
            Disconnect(); // make sure we are fully disconnected
            return(false);
        }
Beispiel #23
0
 public static void Prefix()
 {
     ChatLogPanel.HideChirpText();
 }
Beispiel #24
0
 public override void HandleOnServer(ChatMessageCommand command, Player player)
 {
     ChatLogPanel.GetDefault().AddChatMessage(command.Username, command.Message);
 }
Beispiel #25
0
 public override void HandleOnClient(ChatMessageCommand command)
 {
     ChatLogPanel.GetDefault().AddChatMessage(command.Username, command.Message);
 }