diff --git a/src/csm/Networking/Config/ServerConfig.cs b/src/csm/Networking/Config/ServerConfig.cs index 072c9ace..242fb97b 100644 --- a/src/csm/Networking/Config/ServerConfig.cs +++ b/src/csm/Networking/Config/ServerConfig.cs @@ -15,12 +15,14 @@ public class ServerConfig /// The user name for the hosting player. /// The optional password for this server. /// The maximum amount of players that can connect to the server. - public ServerConfig(int port, string username, string password, int maxPlayers) + /// Whether automatic port forwarding (UPnP) should be enabled. + public ServerConfig(int port, string username, string password, int maxPlayers, bool enablePortForwarding) { Port = port; Username = username; Password = password; MaxPlayers = maxPlayers; + EnablePortForwarding = enablePortForwarding; } public ServerConfig() @@ -29,6 +31,7 @@ public ServerConfig() Username = ""; Password = ""; MaxPlayers = 0; + EnablePortForwarding = true; } /// @@ -50,5 +53,11 @@ public ServerConfig() /// Gets the optional server password. /// public string Password; + + /// + /// Gets whether automatic port forwarding (UPnP) should be enabled. + /// Default is false for security reasons. + /// + public bool EnablePortForwarding; } } diff --git a/src/csm/Networking/Server.cs b/src/csm/Networking/Server.cs index d7450b36..f7130ceb 100644 --- a/src/csm/Networking/Server.cs +++ b/src/csm/Networking/Server.cs @@ -117,15 +117,25 @@ public bool StartServer(ServerConfig serverConfig) // First strategy for NAT traversal: Hole punching SetupHolePunching(); - // Second strategy for NAT traversal: Upnp - try + // Second strategy for NAT traversal: Upnp (only if enabled in config) + if (Config.EnablePortForwarding) { - NatDiscoverer nat = new NatDiscoverer(); - nat.DiscoverDeviceAsync().ContinueWith(task => task.Result.CreatePortMapAsync(new Mapping(Protocol.Udp, Config.Port, - Config.Port, "Cities Skylines Multiplayer (UDP)"))).Wait(); - AutomaticSuccess = true; + try + { + Log.Info("Attempting automatic port forwarding via UPnP..."); + NatDiscoverer nat = new NatDiscoverer(); + nat.DiscoverDeviceAsync().ContinueWith(task => task.Result.CreatePortMapAsync(new Mapping(Protocol.Udp, Config.Port, + Config.Port, "Cities Skylines Multiplayer (UDP)"))).Wait(); + AutomaticSuccess = true; + Log.Info("Automatic port forwarding successful."); + } + catch (Exception) + { + AutomaticSuccess = false; + Log.Warn("Automatic port forwarding failed. You may need to manually forward port " + Config.Port); + } } - catch (Exception) + else { AutomaticSuccess = false; } diff --git a/src/csm/Panels/HostGamePanel.cs b/src/csm/Panels/HostGamePanel.cs index 27336d96..7b7dabc9 100644 --- a/src/csm/Panels/HostGamePanel.cs +++ b/src/csm/Panels/HostGamePanel.cs @@ -29,6 +29,7 @@ public class HostGamePanel : UIPanel private UICheckBox _passwordBox; private UICheckBox _rememberBox; + private UICheckBox _portForwardingBox; private ServerConfig _serverConfig; private bool _hasRemembered; @@ -44,7 +45,7 @@ public override void Start() color = new Color32(110, 110, 110, 250); width = 360; - height = 600; + height = 625; relativePosition = PanelManager.GetCenterPosition(this); // Title Label @@ -78,20 +79,25 @@ public override void Start() _rememberBox = this.CreateCheckBox("Remember Me", new Vector2(10, -325)); _rememberBox.isChecked = _hasRemembered; - _connectionStatus = this.CreateLabel("", new Vector2(10, -350)); + // Port Forwarding box (UPnP) + _portForwardingBox = this.CreateCheckBox("Enable Port Forwarding (UPnP)", new Vector2(10, -350)); + _portForwardingBox.isChecked = _serverConfig.EnablePortForwarding; + _portForwardingBox.tooltip = "Automatically forward ports via UPnP."; + + _connectionStatus = this.CreateLabel("", new Vector2(10, -375)); _connectionStatus.textAlignment = UIHorizontalAlignment.Center; _connectionStatus.textColor = new Color32(255, 0, 0, 255); // Create Local IP Label - _localIp = this.CreateLabel("", new Vector2(10, -380)); + _localIp = this.CreateLabel("", new Vector2(10, -405)); _localIp.textAlignment = UIHorizontalAlignment.Center; // Create External IP Label - _externalIp = this.CreateLabel("", new Vector2(10, -400)); + _externalIp = this.CreateLabel("", new Vector2(10, -425)); _externalIp.textAlignment = UIHorizontalAlignment.Center; // Create VPN IP Label - _vpnIp = this.CreateLabel("", new Vector2(10, -420)); + _vpnIp = this.CreateLabel("", new Vector2(10, -445)); _vpnIp.textAlignment = UIHorizontalAlignment.Center; // Request IP addresses async @@ -105,11 +111,11 @@ public override void Start() }; // Create Server Button - _createButton = this.CreateButton("Create Server", new Vector2(10, -465)); + _createButton = this.CreateButton("Create Server", new Vector2(10, -490)); _createButton.eventClick += OnCreateServerClick; // Close this dialog - _closeButton = this.CreateButton("Cancel", new Vector2(10, -535)); + _closeButton = this.CreateButton("Cancel", new Vector2(10, -560)); _closeButton.eventClick += (component, param) => { isVisible = false; @@ -165,7 +171,7 @@ private void RequestIPs() /// private void OnCreateServerClick(UIComponent uiComponent, UIMouseEventParameter eventParam) { - _serverConfig = new ServerConfig(Int32.Parse(_portField.text), _usernameField.text, _passwordField.text, 0); + _serverConfig = new ServerConfig(Int32.Parse(_portField.text), _usernameField.text, _passwordField.text, 0, _portForwardingBox.isChecked); ConfigData.Save(ref _serverConfig, ConfigData.ServerFile, _rememberBox.isChecked); _connectionStatus.textColor = new Color32(255, 255, 0, 255);