From 45a378b0f0dd91e366aefb44ef9099900aa119e5 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 17 Nov 2022 00:27:14 +1000 Subject: [PATCH 1/7] Add TShock.Installer to download dotnet runtime This will include a new ./TShock.Installer executable that will be for users without the dotnet runtime. This program will download the dotnet runtime, extract it, and then run ./TShock.Server for them using the downloaded runtime. Note: only tested on osx, likely a no-go for linux/windows until more testing occurs. --- .github/workflows/ci-otapi3.yml | 9 +++ TShock.sln | 18 +++++ TShockInstaller/Program.cs | 93 ++++++++++++++++++++++++++ TShockInstaller/TShockInstaller.csproj | 16 +++++ 4 files changed, 136 insertions(+) create mode 100644 TShockInstaller/Program.cs create mode 100644 TShockInstaller/TShockInstaller.csproj diff --git a/.github/workflows/ci-otapi3.yml b/.github/workflows/ci-otapi3.yml index 4f0365aa..f5599ffc 100644 --- a/.github/workflows/ci-otapi3.yml +++ b/.github/workflows/ci-otapi3.yml @@ -36,6 +36,11 @@ jobs: - name: Install msgfmt run: sudo apt-get install -y gettext + - name: Produce installer + run: | + cd TShockInstaller + dotnet publish -r ${{ matrix.arch }} -f net6.0 -c Release -p:PublishSingleFile=true --self-contained true + - name: Produce build run: | cd TShockLauncher @@ -46,6 +51,10 @@ jobs: run: | chmod +x TShockLauncher/bin/Release/net6.0/${{ matrix.arch }}/publish/TShock.Server + - name: Copy installer + run: | + cp TShockInstaller/bin/Release/net6.0/${{ matrix.arch }}/publish/* TShockLauncher/bin/Release/net6.0/${{ matrix.arch }}/publish/ + # preserve file perms: https://github.com/actions/upload-artifact#maintaining-file-permissions-and-case-sensitive-files - name: Tarball artifact (non-Windows) if: ${{ matrix.arch != 'win-x64' }} diff --git a/TShock.sln b/TShock.sln index 7e9acded..27b8665b 100644 --- a/TShock.sln +++ b/TShock.sln @@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TShockLauncher", "TShockLau EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TShockLauncher.Tests", "TShockLauncher.Tests\TShockLauncher.Tests.csproj", "{90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TShockInstaller", "TShockInstaller\TShockInstaller.csproj", "{17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -102,6 +104,22 @@ Global {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|x64.Build.0 = Release|Any CPU {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|x86.ActiveCfg = Release|Any CPU {90AB47F3-8220-48FC-BDAB-D6E97BFDA51B}.Release|x86.Build.0 = Release|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|x64.ActiveCfg = Debug|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|x64.Build.0 = Debug|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|x86.ActiveCfg = Debug|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Debug|x86.Build.0 = Debug|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Release|Any CPU.Build.0 = Release|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Release|x64.ActiveCfg = Release|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Release|x64.Build.0 = Release|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Release|x86.ActiveCfg = Release|Any CPU + {17AC4DD0-8334-4B5C-ABED-77EAF52D75FA}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TShockInstaller/Program.cs b/TShockInstaller/Program.cs new file mode 100644 index 00000000..0d033906 --- /dev/null +++ b/TShockInstaller/Program.cs @@ -0,0 +1,93 @@ +using System.Diagnostics; +using System.IO.Compression; +using System.Runtime.InteropServices; +using ICSharpCode.SharpZipLib.GZip; +using ICSharpCode.SharpZipLib.Tar; + +Console.WriteLine($"TShock Installer {typeof(Program).GetType().Assembly.GetName().Version}."); + +// reference: https://github.com/dotnet/install-scripts/blob/main/src/dotnet-install.sh +// ./dotnet-install.sh -verbose -version 6.0.11 --runtime dotnet + +Console.WriteLine("Determining dotnet runtime url..."); + +string? url = null; +if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + url = "https://dotnetcli.azureedge.net/dotnet/Runtime/6.0.11/dotnet-runtime-6.0.11-osx-x64.tar.gz"; +else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + url = "https://dotnetcli.azureedge.net/dotnet/Runtime/6.0.11/dotnet-runtime-6.0.11-win-x64.zip"; +else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + url = "https://dotnetcli.azureedge.net/dotnet/Runtime/6.0.11/dotnet-runtime-6.0.11-linux-x64.tar.gz"; + +if(url is null) +{ + Console.WriteLine("Unable to determine .net runtime to install. " + + "Refer to https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script " + + "and install using --install-dir dotnet, so that the dotnet folder is beside TShock.Server.dll"); + return; +} + +Console.WriteLine("Using url: " + url); + +var filename = url.Split('/').Last(); +var is_targz = filename.EndsWith(".tar.gz"); + +var download_info = new FileInfo(filename); +if (!download_info.Exists) // todo hash check +{ + Console.WriteLine($"Downloading: {filename}..."); + + using var client = new HttpClient(); + using var resp = await client.GetStreamAsync(url); + using var fs = new FileStream(filename, FileMode.Create); + await resp.CopyToAsync(fs); +} +else +{ + Console.WriteLine("Using existing download on disk: " + filename); +} + +var dotnet_path = Path.Combine("dotnet", "dotnet" + (is_targz ? "" : ".exe")); +var tshock_path = "TShock.Server" + (is_targz ? "" : ".exe"); + +if (!File.Exists(dotnet_path)) +{ + Console.WriteLine("Extracting to ./dotnet/"); + if (is_targz) + { + using var srm_dotnet_file = File.OpenRead(filename); + using var srm_gzip = new GZipInputStream(srm_dotnet_file); + + using var tar_archive = TarArchive.CreateInputTarArchive(srm_gzip, System.Text.Encoding.UTF8); + tar_archive.ExtractContents("dotnet"); + + [DllImport("libc", SetLastError = true)] + static extern int chmod(string pathname, int mode); + + chmod(dotnet_path, 755); + } + else + { + ZipFile.ExtractToDirectory(filename, "dotnet"); + } +} +else +{ + Console.WriteLine($"Extract skipped, existing found at: {dotnet_path}"); +} + +var dotnet_root = System.IO.Path.GetFullPath("dotnet"); +Console.WriteLine($"To be able to run {tshock_path} yourself set the environment variable DOTNET_ROOT={dotnet_root}"); + +Environment.SetEnvironmentVariable("DOTNET_ROOT", dotnet_root); + +Console.WriteLine($"Extracted, launching: {tshock_path}"); +var proc = new Process(); +proc.StartInfo = new() +{ + FileName = tshock_path, +}; +foreach (var arg in args) + proc.StartInfo.ArgumentList.Add(arg); +proc.Start(); +await proc.WaitForExitAsync(); diff --git a/TShockInstaller/TShockInstaller.csproj b/TShockInstaller/TShockInstaller.csproj new file mode 100644 index 00000000..8dedd065 --- /dev/null +++ b/TShockInstaller/TShockInstaller.csproj @@ -0,0 +1,16 @@ + + + + Exe + net6.0 + enable + enable + 5.0.0 + true + TShock.Installer + + + + + + From 4277fbaa98437c24d0c753ae747bb70767e6a9a3 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Nov 2022 08:43:54 +1000 Subject: [PATCH 2/7] Add architecture variants to tshock installer --- TShockInstaller/Program.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/TShockInstaller/Program.cs b/TShockInstaller/Program.cs index 0d033906..01f8cc84 100644 --- a/TShockInstaller/Program.cs +++ b/TShockInstaller/Program.cs @@ -11,13 +11,26 @@ Console.WriteLine($"TShock Installer {typeof(Program).GetType().Assembly.GetName Console.WriteLine("Determining dotnet runtime url..."); +var arch = RuntimeInformation.ProcessArchitecture switch +{ + Architecture.X64 => "x64", + Architecture.Arm64 => "arm64", + _ => null +}; + +if (arch is null) +{ + Console.WriteLine($"{RuntimeInformation.ProcessArchitecture} is not yet supported via this installer."); + return; +} + string? url = null; if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - url = "https://dotnetcli.azureedge.net/dotnet/Runtime/6.0.11/dotnet-runtime-6.0.11-osx-x64.tar.gz"; + url = $"https://dotnetcli.azureedge.net/dotnet/Runtime/6.0.11/dotnet-runtime-6.0.11-osx-{arch}.tar.gz"; else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - url = "https://dotnetcli.azureedge.net/dotnet/Runtime/6.0.11/dotnet-runtime-6.0.11-win-x64.zip"; + url = $"https://dotnetcli.azureedge.net/dotnet/Runtime/6.0.11/dotnet-runtime-6.0.11-win-{arch}.zip"; else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - url = "https://dotnetcli.azureedge.net/dotnet/Runtime/6.0.11/dotnet-runtime-6.0.11-linux-x64.tar.gz"; + url = $"https://dotnetcli.azureedge.net/dotnet/Runtime/6.0.11/dotnet-runtime-6.0.11-linux-{arch}.tar.gz"; if(url is null) { From d84c14fc58051900c00341d90f75c593c854af47 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Nov 2022 08:44:37 +1000 Subject: [PATCH 3/7] Remove TShock.Server.dll reference --- TShockInstaller/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockInstaller/Program.cs b/TShockInstaller/Program.cs index 01f8cc84..72fc63bd 100644 --- a/TShockInstaller/Program.cs +++ b/TShockInstaller/Program.cs @@ -36,7 +36,7 @@ if(url is null) { Console.WriteLine("Unable to determine .net runtime to install. " + "Refer to https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script " + - "and install using --install-dir dotnet, so that the dotnet folder is beside TShock.Server.dll"); + "and install using --install-dir dotnet, so that the dotnet folder is beside TShock.Server[.exe]"); return; } From 95506310648e598eee7cd568c507885b8ecec1c0 Mon Sep 17 00:00:00 2001 From: Luke Date: Mon, 21 Nov 2022 12:23:22 +1000 Subject: [PATCH 4/7] Fix TShock Installer version being written to console v5 not v6... --- TShockInstaller/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TShockInstaller/Program.cs b/TShockInstaller/Program.cs index 72fc63bd..700aadd1 100644 --- a/TShockInstaller/Program.cs +++ b/TShockInstaller/Program.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; -Console.WriteLine($"TShock Installer {typeof(Program).GetType().Assembly.GetName().Version}."); +Console.WriteLine($"TShock Installer {typeof(Program).Assembly.GetName().Version}."); // reference: https://github.com/dotnet/install-scripts/blob/main/src/dotnet-install.sh // ./dotnet-install.sh -verbose -version 6.0.11 --runtime dotnet From fbd82bbce8802b0b5a10f836410aeaa7de0c0ef1 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 22 Nov 2022 16:40:39 +1000 Subject: [PATCH 5/7] Prevent installer from closing via ctrl+c while server remains open This allows tshock to handle CancelKeyPress as per normal, instead of closing the parent process, leaving the second trigger to never be reached. see https://github.com/Pryaxis/TShock/blob/general-devel/TShockAPI/TShock.cs#L710 --- TShockInstaller/Program.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TShockInstaller/Program.cs b/TShockInstaller/Program.cs index 700aadd1..50b9a541 100644 --- a/TShockInstaller/Program.cs +++ b/TShockInstaller/Program.cs @@ -95,7 +95,14 @@ Console.WriteLine($"To be able to run {tshock_path} yourself set the environment Environment.SetEnvironmentVariable("DOTNET_ROOT", dotnet_root); Console.WriteLine($"Extracted, launching: {tshock_path}"); + var proc = new Process(); + +Console.CancelKeyPress += (sender, e) => +{ + e.Cancel = !proc.HasExited; +}; + proc.StartInfo = new() { FileName = tshock_path, From 403677fad2c11d0a29230e19a3edf04e7d325b64 Mon Sep 17 00:00:00 2001 From: Luke Date: Tue, 22 Nov 2022 16:44:24 +1000 Subject: [PATCH 6/7] Embed symbols in the installer to remove .pdb --- TShockInstaller/TShockInstaller.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/TShockInstaller/TShockInstaller.csproj b/TShockInstaller/TShockInstaller.csproj index 8dedd065..9b73d66a 100644 --- a/TShockInstaller/TShockInstaller.csproj +++ b/TShockInstaller/TShockInstaller.csproj @@ -8,6 +8,7 @@ 5.0.0 true TShock.Installer + embedded From e561158699286042154e4b0f8feeac2dabba4853 Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 24 Nov 2022 16:19:39 +1000 Subject: [PATCH 7/7] Remove installers sdk archive when archive fails This will allow the next run to redownload the file. --- TShockInstaller/Program.cs | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/TShockInstaller/Program.cs b/TShockInstaller/Program.cs index 50b9a541..03606b51 100644 --- a/TShockInstaller/Program.cs +++ b/TShockInstaller/Program.cs @@ -4,6 +4,7 @@ using System.Runtime.InteropServices; using ICSharpCode.SharpZipLib.GZip; using ICSharpCode.SharpZipLib.Tar; +Console.ForegroundColor = ConsoleColor.White; Console.WriteLine($"TShock Installer {typeof(Program).Assembly.GetName().Version}."); // reference: https://github.com/dotnet/install-scripts/blob/main/src/dotnet-install.sh @@ -65,23 +66,37 @@ var tshock_path = "TShock.Server" + (is_targz ? "" : ".exe"); if (!File.Exists(dotnet_path)) { - Console.WriteLine("Extracting to ./dotnet/"); - if (is_targz) + try { - using var srm_dotnet_file = File.OpenRead(filename); - using var srm_gzip = new GZipInputStream(srm_dotnet_file); + Console.WriteLine("Extracting to ./dotnet/"); + if (is_targz) + { + using var srm_dotnet_file = File.OpenRead(filename); + using var srm_gzip = new GZipInputStream(srm_dotnet_file); - using var tar_archive = TarArchive.CreateInputTarArchive(srm_gzip, System.Text.Encoding.UTF8); - tar_archive.ExtractContents("dotnet"); + using var tar_archive = TarArchive.CreateInputTarArchive(srm_gzip, System.Text.Encoding.UTF8); + tar_archive.ExtractContents("dotnet"); - [DllImport("libc", SetLastError = true)] - static extern int chmod(string pathname, int mode); + [DllImport("libc", SetLastError = true)] + static extern int chmod(string pathname, int mode); - chmod(dotnet_path, 755); + chmod(dotnet_path, 755); + } + else + { + ZipFile.ExtractToDirectory(filename, "dotnet"); + } } - else + catch (Exception ex) { - ZipFile.ExtractToDirectory(filename, "dotnet"); + Console.ForegroundColor = ConsoleColor.Red; + Console.Error.WriteLine($"Failed to extract {filename}. The archive will be removed. Restart the installer to begin the download again."); + Console.Error.WriteLine(ex); + + if (File.Exists(filename)) + File.Delete(filename); + + return; } } else