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);