Skip to content

Commit

Permalink
Merge pull request #277 from CitiesSkylinesMultiplayer/add-port-checking
Browse files Browse the repository at this point in the history
Add port checking
  • Loading branch information
DominicMaas authored Aug 20, 2022
2 parents 25fe2d6 + 6979bf1 commit 9df6643
Show file tree
Hide file tree
Showing 11 changed files with 340 additions and 30 deletions.
4 changes: 2 additions & 2 deletions src/api/CSM.API.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="LiteNetLib">
<Version>0.9.2.2</Version>
<Version>0.9.4</Version>
</PackageReference>
<PackageReference Include="protobuf-net">
<Version>2.4.6</Version>
<Version>2.4.7</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Expand Down
4 changes: 2 additions & 2 deletions src/basegame/CSM.BaseGame.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="LiteNetLib">
<Version>0.9.2.2</Version>
<Version>0.9.4</Version>
</PackageReference>
<PackageReference Include="CitiesHarmony.API">
<Version>2.0.0</Version>
<Version>2.1.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/csm/CSM.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void OnEnabled()
// Registers all other mods which implement the API
ModSupport.Instance.Init();
// Setup join button
MainMenuHandler.CreateOrUpdateJoinGameButton();
MainMenuHandler.Init();

Log.Info("Construction Complete!");
});
Expand Down
7 changes: 4 additions & 3 deletions src/csm/CSM.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -142,20 +142,21 @@
<Compile Include="Panels\JoinGamePanel.cs" />
<Compile Include="CSM.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Util\CSMWebClient.cs" />
<Compile Include="Util\Serializer.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="LiteNetLib">
<Version>0.9.2.2</Version>
<Version>0.9.4</Version>
</PackageReference>
<PackageReference Include="Open.Nat">
<Version>2.1.0</Version>
</PackageReference>
<PackageReference Include="protobuf-net">
<Version>2.4.6</Version>
<Version>2.4.7</Version>
</PackageReference>
<PackageReference Include="CitiesHarmony.API">
<Version>2.0.0</Version>
<Version>2.1.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
Expand Down
52 changes: 49 additions & 3 deletions src/csm/Injections/MainMenuHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
using ColossalFramework.UI;
using System;
using System.Reflection;
using System.Threading;
using ColossalFramework.Threading;
using ColossalFramework.UI;
using CSM.API;
using CSM.Panels;
using CSM.Util;
using HarmonyLib;
using UnityEngine;

Expand All @@ -16,13 +21,54 @@ public class MainMenuAwake
/// </summary>
public static void Prefix()
{
MainMenuHandler.CreateOrUpdateJoinGameButton();
MainMenuHandler.Init();
}
}

public static class MainMenuHandler
{
public static void CreateOrUpdateJoinGameButton()
public static void Init()
{
CreateOrUpdateJoinGameButton();
new Thread(() => CheckForUpdate(false)).Start();
}

public static void CheckForUpdate(bool alwaysShowInfo)
{
try
{
string latest = new CSMWebClient().DownloadString("http://api.citiesskylinesmultiplayer.com/api/version");
latest = latest.Substring(1);
string[] versionParts = latest.Split('.');
Version latestVersion = new Version(int.Parse(versionParts[0]), int.Parse(versionParts[1]));

Version version = Assembly.GetAssembly(typeof(CSM)).GetName().Version;
if (latestVersion > version)
{
Log.Info(
$"Update available! Current version: {version.Major}.{version.Minor} Latest version: {latestVersion.Major}.{latestVersion.Minor}");
ThreadHelper.dispatcher.Dispatch(() =>
{
MessagePanel panel = PanelManager.ShowPanel<MessagePanel>();
panel.DisplayUpdateAvailable(version, latestVersion);
});
}
else if (alwaysShowInfo)
{
ThreadHelper.dispatcher.Dispatch(() =>
{
MessagePanel panel = PanelManager.ShowPanel<MessagePanel>();
panel.DisplayNoUpdateAvailable();
});
}
}
catch (Exception e)
{
Log.Warn($"Failed to check for updates: {e.Message}");
}
}

private static void CreateOrUpdateJoinGameButton()
{
Log.Info("Creating join game button...");

Expand Down
66 changes: 61 additions & 5 deletions src/csm/Networking/IpAddress.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using CSM.API;
using CSM.Util;

namespace CSM.Networking
{
public struct PortState
{
public string message;
public HttpStatusCode status;

public PortState(string message, HttpStatusCode status)
{
this.message = message;
this.status = status;
}
}

public static class IpAddress
{
private static string _localIp;
Expand All @@ -21,8 +37,8 @@ public static string GetLocalIpAddress()
//Create a new socket
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{
//Connect to 8.8.8.8 (Google IP)
socket.Connect("8.8.8.8", 65530);
//Connect to some server to non-listening port
socket.Connect("api.citiesskylinesmultiplayer.com", 65530);
//Get the IPEndPoint
IPEndPoint endPoint = socket.LocalEndPoint as IPEndPoint;
//Get the IP Address (Internal) from the IPEndPoint
Expand All @@ -46,15 +62,55 @@ public static string GetExternalIpAddress()

try
{
//Get the External IP (IPv4) Address from internet
_externalIp = new WebClient().DownloadString("http://api.ipify.org"); // HTTPS doesn't work
//Get the External IP address from internet
_externalIp = new CSMWebClient().DownloadString("http://api.citiesskylinesmultiplayer.com/api/ip");
return _externalIp;
}
catch (Exception)
catch (Exception e)
{
//On error return "Not found"
Log.Error("Failed to request IP: " + e.Message);
return "Not found";
}
}

public static PortState CheckPort(int port)
{
CSMWebClient client = new CSMWebClient();
try
{
string answer = client.DownloadString("http://api.citiesskylinesmultiplayer.com/api/check?port=" + port);
return new PortState(answer, client.StatusCode());
}
catch (WebException e)
{
if (e.Response is HttpWebResponse response)
{
Encoding encoding = response.CharacterSet != null ? Encoding.GetEncoding(response.CharacterSet) : Encoding.ASCII;
using (Stream stream = response.GetResponseStream())
{
if (stream != null)
{
using (StreamReader reader = new StreamReader(stream, encoding))
{
return new PortState(reader.ReadToEnd(), response.StatusCode);
}
}
else
{
return new PortState(e.Message, HttpStatusCode.InternalServerError);
}
}
}
else
{
return new PortState(e.Message, HttpStatusCode.InternalServerError);
}
}
catch (Exception e)
{
return new PortState(e.Message, HttpStatusCode.InternalServerError);
}
}
}
}
64 changes: 60 additions & 4 deletions src/csm/Networking/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public class Server
/// </summary>
public ServerStatus Status { get; private set; }

private bool automaticSuccess;

public Server()
{
// Set up network items
Expand Down Expand Up @@ -94,12 +96,14 @@ public bool StartServer(ServerConfig serverConfig)

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

new Thread(CheckPort).Start();

// Update the status
Status = ServerStatus.Running;
Expand All @@ -111,10 +115,62 @@ public bool StartServer(ServerConfig serverConfig)

// Update the console to let the user know the server is running
Log.Info("The server has started.");
Chat.Instance.PrintGameMessage("The server has started.");
Chat.Instance.PrintGameMessage("The server has started. Checking if it is reachable from the internet...");
return true;
}

private void CheckPort()
{
PortState state = IpAddress.CheckPort(Config.Port);
string message;
bool portOpen = false;
switch (state.status)
{
case HttpStatusCode.ServiceUnavailable: // Could not reach port
if (automaticSuccess)
{
message =
"It was tried to forward the port automatically, but the server is not reachable from the internet. Manual port forwarding is required.";
}
else
{
message =
"Port could not be forwarded automatically and server is not reachable from the internet. Manual port forwarding is required.";
}
break;
case HttpStatusCode.OK: // Success
portOpen = true;
if (automaticSuccess)
{
message =
"Port was forwarded automatically and server is reachable from the internet!";
}
else
{
message = "Server is reachable from the internet!";
}
break;
default: // Something failed
if (automaticSuccess)
{
message = "Port was forwarded automatically, but couldn't be checked due to error: " +
state.message;
}
else
{
message = "Port could not be forwarded automatically, and couldn't be checked due to error: " +
state.message;
}
break;
}

if (!portOpen)
{
Log.Warn(message);
}
Chat.Instance.PrintGameMessage(portOpen ? Chat.MessageType.Normal : Chat.MessageType.Warning, message);
}

/// <summary>
/// Stops the server
/// </summary>
Expand Down
Loading

0 comments on commit 9df6643

Please sign in to comment.