Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop

This commit is contained in:
EvilFactory
2024-03-28 14:26:18 -03:00
271 changed files with 13174 additions and 3021 deletions

View File

@@ -10,10 +10,10 @@ public static class Deployables
{
public const string ResultPath = "Deploy/bin/content";
private const string clientProjFmt = "Barotrauma/BarotraumaClient/{0}Client.csproj";
private const string serverProjFmt = "Barotrauma/BarotraumaServer/{0}Server.csproj";
private const string ClientProjFmt = "Barotrauma/BarotraumaClient/{0}Client.csproj";
private const string ServerProjFmt = "Barotrauma/BarotraumaServer/{0}Server.csproj";
private static readonly ImmutableArray<(string Project, string Runtime)> platforms = new[]
private static readonly ImmutableArray<(string Project, string Runtime)> Platforms = new[]
{
("Windows", "win-x64"),
("Mac", "osx-x64"),
@@ -28,11 +28,11 @@ public static class Deployables
Path.Combine(ResultPath, "readme.txt"),
$"This is Barotrauma {configuration} v{version} ({gitBranch}, {gitRevision}) built on {DateTime.Now}");
foreach (var (project, runtime) in platforms)
foreach (var (project, runtime) in Platforms)
{
string serverPath = Path.Combine(ResultPath, project, "Server");
void checkVersion(string projPath)
void CheckVersion(string projPath)
{
Version projVersion = Version.Parse(
XDocument.Load(projPath).Root?
@@ -45,11 +45,11 @@ public static class Deployables
}
}
string serverProj = string.Format(serverProjFmt, project);
string clientProj = string.Format(clientProjFmt, project);
string serverProj = string.Format(ServerProjFmt, project);
string clientProj = string.Format(ClientProjFmt, project);
checkVersion(serverProj);
checkVersion(clientProj);
CheckVersion(serverProj);
CheckVersion(clientProj);
Console.WriteLine(
$"*** Building Barotrauma {configuration}{project} v{version} ({gitBranch}, {gitRevision}) to \"{Path.Combine(ResultPath, project)}\" ***");

View File

@@ -16,7 +16,7 @@ public static class DotnetCmd
{
private const string DotnetAppName = "dotnet";
private const string desiredRuntimeVersion = "6.0.8";
private const string DesiredRuntimeVersion = "6.0.8";
public static void Publish(string projPath, string configuration, string runtime, string resultPath)
{
@@ -36,7 +36,7 @@ public static class DotnetCmd
"/p:Platform=x64",
"/p:ErrorOnDuplicatePublishOutputFiles=false", //TODO: fix our duplicate files
"/p:RollForward=Disable",
$"/p:RuntimeFrameworkVersion={desiredRuntimeVersion}",
$"/p:RuntimeFrameworkVersion={DesiredRuntimeVersion}",
"-o",
resultPath
},

View File

@@ -0,0 +1,143 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Xml;
using System.Xml.Linq;
namespace DeployAll;
public static class EgsAssistant
{
private static string BuildToolFilePath
=> Path.Combine(BuildToolExtractRootPath, true switch
{
_ when RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
=> "Engine/Binaries/Win64/BuildPatchTool.exe",
_ when RuntimeInformation.IsOSPlatform(OSPlatform.Linux)
=> "Engine/Binaries/Linux/BuildPatchTool",
_ when RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
=> "Engine/Binaries/Mac/BuildPatchTool",
_ => throw new Exception($"Unsupported host platform: {RuntimeInformation.OSDescription}")
});
private const string BuildToolExtractRootPath = "Deploy/bin/EpicBuildTool";
private const string BuildToolConfig = $"{BuildToolExtractRootPath}/epic_build_tool_config.xml";
private const string CloudDir = $"{BuildToolExtractRootPath}/CloudDir";
private const string FileIgnoreListPath = $"{BuildToolExtractRootPath}/ignore.txt";
private const string FileAttributeListPath = $"{BuildToolExtractRootPath}/fileattributes.txt";
public static void Upload(Version version, string configuration, string revision)
{
while (!File.Exists(BuildToolFilePath))
{
Directory.CreateDirectory(BuildToolExtractRootPath);
if (Util.AskQuestion(
$"Epic BuildPatchTool not found. Extract it to {BuildToolExtractRootPath}, then enter Y to continue. Enter nothing to cancel.")
.AnsweredNo())
{
return;
}
}
XDocument? cfg = null;
while (!Util.TryLoadXml(BuildToolConfig, out cfg)
|| cfg.Root!.Attributes().Any(attr => !attr.Value.IsValidEpicCfg()))
{
if (!File.Exists(BuildToolConfig))
{
var doc = new XDocument(
new XElement("config"));
doc.Root!.Add(new XAttribute("OrganizationId", " ORGANIZATION ID "));
doc.Root!.Add(new XAttribute("ProductId", " PRODUCT ID "));
doc.Root!.Add(new XAttribute("ArtifactId", " ARTIFACT ID "));
doc.Root!.Add(new XAttribute("ClientId", " BUILDPATCHTOOL CLIENT ID "));
doc.Root!.Add(new XAttribute("ClientSecret", " BUILDPATCHTOOL CLIENT SECRET "));
var xmlWriterSettings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = true,
NewLineOnAttributes = true
};
using var writer = XmlWriter.Create(BuildToolConfig, xmlWriterSettings);
doc.WriteTo(writer);
writer.Flush();
}
if (Util.AskQuestion(
$"Parameters for BuildPatchTool are missing or invalid. Fill in {BuildToolConfig} with the appropriate values, then enter Y to continue. Enter nothing to cancel.")
.AnsweredNo())
{
return;
}
}
Directory.CreateDirectory(CloudDir);
XElement configElement = cfg.Root;
string organizationId = configElement.GetAttributeOrThrow("OrganizationId");
string productId = configElement.GetAttributeOrThrow("ProductId");
string artifactId = configElement.GetAttributeOrThrow("ArtifactId");
string clientId = configElement.GetAttributeOrThrow("ClientId");
string clientSecret = configElement.GetAttributeOrThrow("ClientSecret");
var supportedPlatforms = new (string Platform, string ExecutablePath)[]
{
("Windows", "Barotrauma.exe"),
// TODO: reevaluate macOS support for the Epic Games Store version of Barotrauma
// This was dropped because of QA difficulty and missing features on the platform
// but it may be possible to get it working well enough to be shipped.
//("Mac", "Barotrauma.app/Contents/MacOS/Barotrauma")
};
foreach ((string platform, string executablePath) in supportedPlatforms)
{
string RelativeToAbsolute(string relativePath)
=> Path.Combine(Path.GetDirectoryName(executablePath) ?? "", relativePath).NormalizePathSeparators();
var filesToIgnore = new[] { "steam_api64.dll", "libsteam_api64.dylib", "libsteam_api64.so" }
.Select(RelativeToAbsolute)
.ToArray();
File.WriteAllLines(FileIgnoreListPath, filesToIgnore);
var fileAttributes = platform == "Mac"
? new[] { "DedicatedServer" }
: Array.Empty<string>();
fileAttributes = fileAttributes
.Select(RelativeToAbsolute)
.Select(f => $"\"{f}\" executable")
.ToArray();
File.WriteAllLines(FileAttributeListPath, fileAttributes);
var psi = new ProcessStartInfo
{
FileName = BuildToolFilePath,
ArgumentList =
{
$"-OrganizationId=\"{organizationId}\"",
$"-ProductId=\"{productId}\"",
$"-ArtifactId=\"{artifactId}\"",
$"-ClientId=\"{clientId}\"",
$"-ClientSecret=\"{clientSecret}\"",
"-mode=UploadBinary",
$"-BuildRoot=\"{Path.GetFullPath(Path.Combine(Deployables.ResultPath, platform, "Client")).NormalizePathSeparators()}\"",
$"-CloudDir=\"{Path.GetFullPath(CloudDir).NormalizePathSeparators()}\"",
$"-BuildVersion=\"{version}-{platform}{configuration}-{revision}\"",
$"-AppLaunch=\"{executablePath}\"",
"-AppArgs=\"\"",
$"-FileIgnoreList={Path.GetFullPath(FileIgnoreListPath).NormalizePathSeparators()}",
$"-FileAttributeList={Path.GetFullPath(FileAttributeListPath).NormalizePathSeparators()}"
},
RedirectStandardOutput = false,
RedirectStandardError = false
};
var process = Util.StartProcess(psi);
process.WaitForExit();
}
}
}

View File

@@ -5,13 +5,13 @@ namespace DeployAll;
public static class GitCmd
{
private const string gitCmdName = "git";
private const string GitCmdName = "git";
private static ProcessStartInfo MakePsi(params string[] args)
{
var psi = new ProcessStartInfo
{
FileName = gitCmdName,
FileName = GitCmdName,
RedirectStandardError = true,
RedirectStandardOutput = true
};

View File

@@ -4,6 +4,8 @@ using System.Linq;
using System.Xml.Linq;
using DeployAll;
AppDomain.CurrentDomain.ProcessExit += (_, _) => Console.WriteLine("Bye!");
while (!Directory.GetFiles(".").Any(f => f.EndsWith(".sln")))
{
Directory.SetCurrentDirectory("..");
@@ -46,6 +48,13 @@ if (string.IsNullOrWhiteSpace(configuration)) { return; }
Deployables.Generate(configuration, gameVersion, gitBranch, gitRevision);
if (Util.AskQuestion("Would you like to upload the generated builds to EGS? [y/n]")
.AnsweredYes())
{
EgsAssistant.Upload(gameVersion, configuration, gitRevision);
}
if (Util.AskQuestion("Would you like to upload the generated builds to Steam? [y/n]")
.AnsweredNo()) { return; }

View File

@@ -34,7 +34,7 @@ public static class SteamPipeAssistant
}
}
private static string steamCmdUrl
private static string SteamCmdUrl
=> true switch
{
_ when RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
@@ -46,7 +46,7 @@ public static class SteamPipeAssistant
_ => throw new Exception($"Unsupported host platform: {RuntimeInformation.OSDescription}")
};
private static string[] steamCmdFilenames
private static string[] SteamCmdFilenames
=> true switch
{
_ when RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
@@ -71,9 +71,9 @@ public static class SteamPipeAssistant
Util.RecreateDirectory(SteamCmdPath);
var steamCmdPkg = Util.DownloadFile(steamCmdUrl).ToArray();
var steamCmdPkg = Util.DownloadFile(SteamCmdUrl, out _).ToArray();
if (Path.GetExtension(steamCmdUrl) == ".zip")
if (Path.GetExtension(SteamCmdUrl) == ".zip")
{
using var memStream = new MemoryStream(steamCmdPkg);
using ZipArchive archive = new ZipArchive(memStream, ZipArchiveMode.Read);
@@ -81,7 +81,7 @@ public static class SteamPipeAssistant
}
else
{
string downloadResultPath = Path.Combine(SteamCmdPath, Path.GetFileName(steamCmdUrl));
string downloadResultPath = Path.Combine(SteamCmdPath, Path.GetFileName(SteamCmdUrl));
File.WriteAllBytes(downloadResultPath, steamCmdPkg);
var psi = new ProcessStartInfo
@@ -102,7 +102,7 @@ public static class SteamPipeAssistant
File.Delete(downloadResultPath);
foreach (var filename in steamCmdFilenames)
foreach (var filename in SteamCmdFilenames)
{
psi = new ProcessStartInfo
{
@@ -126,7 +126,7 @@ public static class SteamPipeAssistant
private const string ScriptPath = "Deploy/bin/scripts";
private const string BuildOutput = "Deploy/bin/output";
private const string appIdScriptFileFmt = "app_{0}.vdf";
private const string AppIdScriptFileFmt = "app_{0}.vdf";
private const ulong ClientAppId = 602960;
private const ulong ClientWindowsDepotId = 602961;
@@ -194,14 +194,14 @@ public static class SteamPipeAssistant
new SingleItem("contentroot", Path.GetFullPath(Deployables.ResultPath)),
new SingleItem("setlive", appId switch
{
ClientAppId => "experimental",
ServerAppId => "development",
ClientAppId => "refactor_our_souls",
ServerAppId => "refactor_our_souls",
_ => throw new InvalidOperationException()
}),
new SingleItem("preview", "0"),
depotScripts);
var scriptFileName = Path.Combine(ScriptPath, string.Format(appIdScriptFileFmt, appId));
var scriptFileName = Path.Combine(ScriptPath, string.Format(AppIdScriptFileFmt, appId));
File.WriteAllText(scriptFileName, script.ToString());
}
@@ -223,7 +223,7 @@ public static class SteamPipeAssistant
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = Path.Combine(SteamCmdPath, steamCmdFilenames.First()),
FileName = Path.Combine(SteamCmdPath, SteamCmdFilenames.First()),
ArgumentList =
{
"+login",
@@ -233,13 +233,13 @@ public static class SteamPipeAssistant
RedirectStandardError = false
};
void addScriptCmd(ulong appId)
void AddScriptCmd(ulong appId)
{
psi.ArgumentList.Add("+run_app_build");
psi.ArgumentList.Add(Path.GetFullPath(Path.Combine(ScriptPath, string.Format(appIdScriptFileFmt, appId))));
psi.ArgumentList.Add(Path.GetFullPath(Path.Combine(ScriptPath, string.Format(AppIdScriptFileFmt, appId))));
}
addScriptCmd(ClientAppId);
if (configuration == "Release") { addScriptCmd(ServerAppId); }
AddScriptCmd(ClientAppId);
if (configuration == "Release") { AddScriptCmd(ServerAppId); }
psi.ArgumentList.Add("+quit");
var process = Util.StartProcess(psi);

View File

@@ -1,9 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Xml.Linq;
namespace DeployAll;
@@ -64,13 +66,16 @@ public static class Util
DeleteDirectory(path);
Directory.CreateDirectory(path);
}
public static IReadOnlyList<byte> DownloadFile(string url)
public static IReadOnlyList<byte> DownloadFile(string url, out string finalUrl)
{
var httpClient = new HttpClient();
finalUrl = url;
using var httpClient = new HttpClient();
var response = httpClient.Send(new HttpRequestMessage(
HttpMethod.Get,
new Uri(url)));
finalUrl = response.RequestMessage?.RequestUri?.AbsoluteUri ?? url;
using var stream = response.Content.ReadAsStream();
using var reader = new BinaryReader(stream);
@@ -100,6 +105,50 @@ public static class Util
public static bool AnsweredNo(this string answer)
=> !answer.AnsweredYes();
public static bool IsValidEpicCfg(this char c)
=> char.IsDigit(c)
|| c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '-' or '_' or '+' or '/';
public static bool IsValidEpicCfg(this string s)
=> !string.IsNullOrEmpty(s) && s.All(IsValidEpicCfg);
public static bool TryLoadXml(string path, [NotNullWhen(returnValue: true)]out XDocument? doc)
{
try
{
doc = XDocument.Load(path);
return true;
}
catch
{
doc = null;
return false;
}
}
public static string GetAttributeOrThrow(this XElement element, string attributeName)
{
var attribute = element
.Attributes()
.FirstOrDefault(e => e.Name.LocalName.Equals(attributeName, StringComparison.OrdinalIgnoreCase));
if (attribute != null
&& !string.IsNullOrEmpty(attribute.Value))
{
return attribute.Value;
}
throw new Exception($"{attributeName} is not set");
}
public static string ThrowIfNullOrEmpty(this string? s, string msg)
{
if (string.IsNullOrEmpty(s)) { throw new Exception(msg); }
return s;
}
public static string NormalizePathSeparators(this string s)
=> s.Replace("\\", "/");
public static Process StartProcess(ProcessStartInfo info)
=> Process.Start(info)
?? throw new Exception($"Failed to start process \"{info.FileName}\"");