Skip to content

Commit

Permalink
Merge pull request NebulaModTeam#677 from PhantomGamers/ngrok-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
PhantomGamers authored Jun 5, 2024
2 parents 3bb1ae4 + c30b811 commit f8c240e
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 50 deletions.
1 change: 1 addition & 0 deletions NebulaModel/Networking/IServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public interface IServer : INetworkProvider
bool NgrokActive { get; }
bool NgrokEnabled { get; }
string NgrokLastErrorCode { get; }
string NgrokLastErrorCodeDesc { get; }
public event EventHandler<INebulaConnection> Connected;
public event EventHandler<INebulaConnection> Disconnected;

Expand Down
122 changes: 79 additions & 43 deletions NebulaNetwork/Ngrok/NgrokManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ namespace NebulaNetwork.Ngrok;

public class NgrokManager
{
private readonly string _authtoken;
private readonly string _authToken;
private readonly TaskCompletionSource<bool> _ngrokAddressObtainedSource = new();
private readonly string _ngrokConfigPath;

private readonly string _ngrokPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
private readonly string _ngrokPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? throw new InvalidOperationException("_ngrokPath null"),
"ngrok-v3-stable-windows-amd64", "ngrok.exe");

private readonly int _port;
Expand All @@ -37,21 +37,22 @@ public class NgrokManager

public string NgrokAddress;
public string NgrokLastErrorCode;
public string NgrokLastErrorCodeDesc;
private static readonly string[] contents = { "version: 2" };

public NgrokManager(int port, string authtoken = null, string region = null)
public NgrokManager(int port, string authToken = null, string region = null)
{
_ngrokConfigPath = Path.Combine(Path.GetDirectoryName(_ngrokPath), "ngrok.yml");
_ngrokConfigPath = Path.Combine(Path.GetDirectoryName(_ngrokPath) ?? throw new InvalidOperationException("_ngrokConfigPath null"), "ngrok.yml");
_port = port;
_authtoken = authtoken ?? Config.Options.NgrokAuthtoken;
_authToken = authToken ?? Config.Options.NgrokAuthtoken;
_region = region ?? Config.Options.NgrokRegion;

if (!NgrokEnabled)
{
return;
}

if (string.IsNullOrEmpty(_authtoken))
if (string.IsNullOrEmpty(_authToken))
{
Log.WarnInform("Ngrok support was enabled, however no Authtoken was provided".Translate());
return;
Expand Down Expand Up @@ -97,6 +98,10 @@ public NgrokManager(int port, string authtoken = null, string region = null)
try
{
await DownloadAndInstallNgrok();
if (!IsNgrokInstalled())
{
throw new FileNotFoundException();
}
}
catch
{
Expand All @@ -108,14 +113,14 @@ public NgrokManager(int port, string authtoken = null, string region = null)
if (!StartNgrok())
{
Log.WarnInform(
string.Format("Failed to start Ngrok tunnel! LastErrorCode: {0}".Translate(), NgrokLastErrorCode));
string.Format("Failed to start Ngrok tunnel! LastErrorCode: {0} {1}".Translate(), NgrokLastErrorCode, NgrokLastErrorCodeDesc));
return;
}

if (!IsNgrokActive())
{
Log.WarnInform(string.Format("Ngrok tunnel has exited prematurely! LastErrorCode: {0}".Translate(),
NgrokLastErrorCode));
Log.WarnInform(string.Format("Ngrok tunnel has exited prematurely! LastErrorCode: {0} {1}".Translate(),
NgrokLastErrorCode, NgrokLastErrorCodeDesc));
}
});
}
Expand All @@ -124,22 +129,25 @@ private async Task DownloadAndInstallNgrok()
{
using (var client = new HttpClient())
{
using (var s = client.GetStreamAsync("https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-windows-amd64.zip"))
using var s = client.GetStreamAsync("https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-windows-amd64.zip");
using var zip = new ZipArchive(await s, ZipArchiveMode.Read);
if (File.Exists(_ngrokPath))
{
using (var zip = new ZipArchive(await s, ZipArchiveMode.Read))
{
if (File.Exists(_ngrokPath))
{
File.Delete(_ngrokPath);
}
zip.ExtractToDirectory(Path.GetDirectoryName(_ngrokPath));
}
File.Delete(_ngrokPath);
}
zip.ExtractToDirectory(Path.GetDirectoryName(_ngrokPath));
}

File.WriteAllLines(_ngrokConfigPath, contents);

Log.WarnInform("Ngrok install completed in the plugin folder".Translate());
if (File.Exists(_ngrokPath))
{
Log.WarnInform("Ngrok install completed in the plugin folder".Translate());
}
else
{
Log.Error("Ngrok installation failed".Translate());
}
}

private bool IsNgrokInstalled()
Expand All @@ -151,36 +159,46 @@ private bool StartNgrok()
{
StopNgrok();

_ngrokProcess = new Process();
_ngrokProcess.StartInfo = new ProcessStartInfo
_ngrokProcess = new Process
{
WindowStyle = ProcessWindowStyle.Hidden,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
FileName = _ngrokPath,
Arguments =
$"tcp {_port} --authtoken {_authtoken} --log stdout --log-format json --config \"{_ngrokConfigPath}\"" +
StartInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
FileName = _ngrokPath,
Arguments =
$"tcp {_port} --authtoken {_authToken} --log stdout --log-format json --config \"{_ngrokConfigPath}\"" +
(!string.IsNullOrEmpty(_region) ? $" --region {_region}" : "")
}
};

_ngrokProcess.OutputDataReceived += OutputDataReceivedEventHandler;
_ngrokProcess.ErrorDataReceived += ErrorDataReceivedEventHandler;
_ngrokProcess.Exited += (_, _) =>
{
StopNgrok();
};

var started = _ngrokProcess.Start();
if (IsNgrokActive())
{
// This links the process as a child process by attaching a null debugger thus ensuring that the process is killed when its parent dies.
new ChildProcessLinker(_ngrokProcess, exception =>
_ = new ChildProcessLinker(_ngrokProcess, _ =>
{
Log.Warn(
"Failed to link Ngrok process to DSP process as a child! (This might result in a left over ngrok process if the DSP process uncleanly killed)");
});
}
else
{
Log.Error("Failed to start Ngrok process!");
}

_ngrokProcess.BeginOutputReadLine();
_ngrokProcess.BeginErrorReadLine();
_ngrokProcess?.BeginOutputReadLine();
_ngrokProcess?.BeginErrorReadLine();

return started;
}
Expand Down Expand Up @@ -240,7 +258,15 @@ private void ErrorDataReceivedEventHandler(object sender, DataReceivedEventArgs
return;
}
NgrokLastErrorCode = errorCodeMatches[errorCodeMatches.Count - 1].Value;
Log.WarnInform(string.Format("Ngrok Error! Code: {0}".Translate(), NgrokLastErrorCode));
NgrokLastErrorCodeDesc = NgrokLastErrorCode switch
{
"ERR_NGROK_105" => "Authtoken is empty or expired".Translate(),
"ERR_NGROK_108" => "Session limit reached".Translate(),
"ERR_NGROK_123" => "Account email not verified".Translate(),
_ => string.Empty
};
NgrokLastErrorCodeDesc = !string.IsNullOrWhiteSpace(NgrokLastErrorCodeDesc) ? $"({NgrokLastErrorCodeDesc})" : string.Empty;
Log.WarnInform(string.Format("Ngrok Error! Code: {0} {1}".Translate(), NgrokLastErrorCode, NgrokLastErrorCodeDesc));
}

public void StopNgrok()
Expand All @@ -250,23 +276,33 @@ public void StopNgrok()
return;
}
_ngrokProcess.Refresh();
if (!_ngrokProcess.HasExited)
try
{
_ngrokProcess.Kill();
_ngrokProcess.Close();
if (!_ngrokProcess.HasExited)
{
_ngrokProcess.Kill();
}
}
catch (Exception e)
{
Log.Error(e);
}
_ngrokProcess.Close();
_ngrokProcess = null;
}

public bool IsNgrokActive()
{
if (_ngrokProcess == null)
try
{
_ngrokProcess?.Refresh();
return !_ngrokProcess?.HasExited ?? false;
}
catch (Exception e)
{
Log.Error(e);
return false;
}

_ngrokProcess.Refresh();
return !_ngrokProcess.HasExited;
}

public Task<string> GetNgrokAddressAsync()
Expand All @@ -276,13 +312,13 @@ public Task<string> GetNgrokAddressAsync()
if (!IsNgrokActive())
{
throw new Exception(
$"Not able to get Ngrok tunnel address because Ngrok is not started (or exited prematurely)! LastErrorCode: {NgrokLastErrorCode}");
$"Not able to get Ngrok tunnel address because Ngrok is not started (or exited prematurely)! LastErrorCode: {NgrokLastErrorCode} {NgrokLastErrorCodeDesc}");
}

if (!_ngrokAddressObtainedSource.Task.Wait(TimeSpan.FromSeconds(15)))
{
throw new TimeoutException(
$"Not able to get Ngrok tunnel address because 15s timeout was exceeded! LastErrorCode: {NgrokLastErrorCode}");
$"Not able to get Ngrok tunnel address because 15s timeout was exceeded! LastErrorCode: {NgrokLastErrorCode} {NgrokLastErrorCodeDesc}");
}

return NgrokAddress;
Expand All @@ -294,7 +330,7 @@ public async Task<string> GetTunnelAddressFromAPI()
if (!IsNgrokActive())
{
throw new Exception(
$"Not able to get Ngrok tunnel address from API because Ngrok is not started (or exited prematurely)! LastErrorCode: {NgrokLastErrorCode}");
$"Not able to get Ngrok tunnel address from API because Ngrok is not started (or exited prematurely)! LastErrorCode: {NgrokLastErrorCode} {NgrokLastErrorCodeDesc}");
}

if (_ngrokAPIAddress == null)
Expand Down
1 change: 1 addition & 0 deletions NebulaNetwork/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public Server(ushort port, bool loadSaveFile = false)
public bool NgrokActive => ngrokManager.IsNgrokActive();
public bool NgrokEnabled => ngrokManager.NgrokEnabled;
public string NgrokLastErrorCode => ngrokManager.NgrokLastErrorCode;
public string NgrokLastErrorCodeDesc => ngrokManager.NgrokLastErrorCodeDesc;
public event EventHandler<INebulaConnection> Connected;
public event EventHandler<INebulaConnection> Disconnected;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public static IEnumerable<CodeInstruction> AddBuildTargetToModules_Transpiler(IE
new CodeMatch(OpCodes.Ldloc_0),
new CodeMatch(OpCodes.Bgt_Un)
);
Log.Info(codeMatcher.IsValid);
var sqrDist = codeMatcher.InstructionAt(-2).operand;
var skipLabel = codeMatcher.Operand;
codeMatcher.Advance(1)
Expand Down
12 changes: 6 additions & 6 deletions NebulaWorld/Chat/Commands/InfoCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace NebulaWorld.Chat.Commands;

public class InfoCommandHandler : IChatCommandHandler
{
private static readonly string[] s_separator = { "]:" };
private static readonly string[] s_separator = ["]:"];

public void Execute(ChatWindow window, string[] parameters)
{
Expand Down Expand Up @@ -73,7 +73,7 @@ public string GetDescription()

public string[] GetUsage()
{
return new[] { "[full]" };
return ["[full]"];
}

public static string GetServerInfoText(IServer server, IPUtils.IpInfo ipInfo, bool full)
Expand Down Expand Up @@ -105,17 +105,17 @@ public static string GetServerInfoText(IServer server, IPUtils.IpInfo ipInfo, bo
{
if (server.NgrokActive)
{
sb.Append("\n ").Append("Ngrok address: ".Translate())
sb.Append("\n ").Append("Ngrok address: ".Translate())
.Append(FormatCopyString(server.NgrokAddress, true, NgrokAddressFilter));
}
else
{
sb.Append("\n ").Append("Ngrok address: Tunnel Inactive!".Translate());
sb.Append("\n ").Append("Ngrok address: Tunnel Inactive!".Translate());
}

if (server.NgrokLastErrorCode != null)
if (!string.IsNullOrWhiteSpace(server.NgrokLastErrorCode))
{
sb.Append($" ({FormatCopyString(server.NgrokLastErrorCode)})");
sb.Append($" ({FormatCopyString(server.NgrokLastErrorCode)}){FormatCopyString(server.NgrokLastErrorCodeDesc)}");
}
}

Expand Down

0 comments on commit f8c240e

Please sign in to comment.