diff --git a/.editorconfig b/.editorconfig index bf31f95c..9ee4d2af 100644 --- a/.editorconfig +++ b/.editorconfig @@ -280,6 +280,8 @@ dotnet_diagnostic.CA1812.severity = none dotnet_diagnostic.CA1848.severity = none # CA1308 tries to make user facing string upper case dotnet_diagnostic.CA1308.severity = none +# CA1303 wants localization of text - I'm unlikely to do this +dotnet_diagnostic.CA1303.severity = none # Workaround for https://github.com/dotnet/roslyn/issues/41640 dotnet_diagnostic.IDE0005.severity = suggestion diff --git a/Worms.sln b/Worms.sln index 545d8c54..9ef91023 100644 --- a/Worms.sln +++ b/Worms.sln @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worms.Cli", "src\Worms.Cli\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worms.Armageddon.Files.Tests", "src\Worms.Armageddon.Files.Tests\Worms.Armageddon.Files.Tests.csproj", "{6233FC52-586A-40A0-B369-FA77ADE35E94}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Worms.Hub.ReplayProcessor", "src\Worms.Hub.ReplayProcessor\Worms.Hub.ReplayProcessor.csproj", "{F41B5B20-FDE9-4835-8282-7DB750F06992}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -48,6 +50,10 @@ Global {6233FC52-586A-40A0-B369-FA77ADE35E94}.Debug|Any CPU.Build.0 = Debug|Any CPU {6233FC52-586A-40A0-B369-FA77ADE35E94}.Release|Any CPU.ActiveCfg = Release|Any CPU {6233FC52-586A-40A0-B369-FA77ADE35E94}.Release|Any CPU.Build.0 = Release|Any CPU + {F41B5B20-FDE9-4835-8282-7DB750F06992}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F41B5B20-FDE9-4835-8282-7DB750F06992}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F41B5B20-FDE9-4835-8282-7DB750F06992}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F41B5B20-FDE9-4835-8282-7DB750F06992}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution EndGlobalSection diff --git a/build/docker/makefile b/build/docker/makefile index 14c7e855..8d937d38 100644 --- a/build/docker/makefile +++ b/build/docker/makefile @@ -51,7 +51,9 @@ $1.package: $1.version --build-arg VERSION=$$($1_VERSION) \ --cache-from=type=gha,scope=package \ --cache-from=type=gha,scope=build \ - --cache-to=type=gha,mode=max,scope=package + --cache-to=type=gha,mode=max,scope=package \ + --progress=plain \ + --load @echo "" ## Release diff --git a/build/docker/replay-processor/Dockerfile b/build/docker/replay-processor/Dockerfile new file mode 100644 index 00000000..0ec4efa3 --- /dev/null +++ b/build/docker/replay-processor/Dockerfile @@ -0,0 +1,79 @@ +#### Build #### +FROM mcr.microsoft.com/dotnet/sdk:8.0.303@sha256:7d0ba26469267b563120456557e38eccef9972cb6b9cfbbd47a50d1218fa7b30 AS build +WORKDIR /app + +COPY src/Directory.Build.props . +COPY .editorconfig . +COPY src/Worms.Armageddon.Game/Worms.Armageddon.Game.csproj ./src/Worms.Armageddon.Game/Worms.Armageddon.Game.csproj +COPY src/Worms.Hub.ReplayProcessor/Worms.Hub.ReplayProcessor.csproj ./src/Worms.Hub.ReplayProcessor/Worms.Hub.ReplayProcessor.csproj +RUN dotnet restore src/Worms.Hub.ReplayProcessor/Worms.Hub.ReplayProcessor.csproj + +COPY src/Worms.Armageddon.Game ./src/Worms.Armageddon.Game +COPY src/Worms.Hub.ReplayProcessor ./src/Worms.Hub.ReplayProcessor +ARG VERSION=0.0.1 +RUN dotnet publish \ + src/Worms.Hub.ReplayProcessor/Worms.Hub.ReplayProcessor.csproj \ + -c Release \ + -o out \ + --no-restore \ + -p:AssemblyVersion=${VERSION} \ + -p:Version=${VERSION} + +#### Test #### +FROM build AS test +RUN dotnet test --no-restore --no-build --verbosity normal + +#### Runtime #### +FROM ubuntu:22.04 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + wget \ + p7zip-full \ + unshield \ + dotnet-runtime-8.0 + +# Get key to Wine repo +RUN wget -nc https://dl.winehq.org/wine-builds/winehq.key -O /tmp/winehq.key + +# Add Wine repo +RUN apt-get update \ + && apt-get install -y software-properties-common gnupg \ + && apt-key add /tmp/winehq.key \ + && add-apt-repository 'deb https://dl.winehq.org/wine-builds/ubuntu/ jammy main' \ + && rm /tmp/winehq.key + +# AMD 32-bit deps for Wine +RUN dpkg --add-architecture i386 \ + && apt-get update \ + && apt-get install -y --install-recommends winehq-stable wine32 xvfb \ + && rm -rf /var/lib/apt/lists/* + +# Create WINEPREFIX +RUN WINEDLLOVERRIDES="mscoree,mshtml=" xvfb-run wineboot -i \ + && wineserver -k + +# Copy game installation directory + +# Some settings for WA +RUN wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v WineCompatibilitySuggested /d 0x7FFFFFFF /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v WindowedMode /d 0x00000001 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v ChatPinned /d 0x00000001 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v DetailLevel /d 0x00000005 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v WindowedMode /d 0x00000001 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v PinnedChatLines /d 0x00000007 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v InfoTransparency /d 0x00000001 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v InfoSpy /d 0x00000001 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v DisableSmoothBackgroundGradient /d 0x00000000 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v HardwareRendering /d 0x00000001 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v LargerFonts /d 0x00000000 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v AssistedVsync /d 0x00000000 /f \ + && wine reg add 'HKEY_CURRENT_USER\Software\Team17SoftwareLTD\WormsArmageddon\Options' /t REG_DWORD /v Vsync /d 0x00000000 /f \ + && wineserver -k + +RUN Xvfb :1 & export DISPLAY=:1 + +WORKDIR /app +COPY --from=build /app/out . +ENTRYPOINT ["./Worms.Hub.ReplayProcessor"] diff --git a/build/docker/replay-processor/Dockerfile.dockerignore b/build/docker/replay-processor/Dockerfile.dockerignore new file mode 100644 index 00000000..bdc7aac4 --- /dev/null +++ b/build/docker/replay-processor/Dockerfile.dockerignore @@ -0,0 +1,10 @@ +# Ignore everything +** + +# Except for these files +!/src +!/scripts +!.editorconfig + +**/bin +**/obj diff --git a/build/docker/replay-processor/config.mk b/build/docker/replay-processor/config.mk new file mode 100644 index 00000000..2d2bcc0d --- /dev/null +++ b/build/docker/replay-processor/config.mk @@ -0,0 +1,3 @@ +replay-processor_IMAGE_NAME := theeadie/worms-hub-replay-processor +replay-processor_NEXT_VERSION := 0.1 +replay-processor_TAG_PREFIX := replay-processor/v diff --git a/src/Worms.Armageddon.Game/Linux/WormsLocator.cs b/src/Worms.Armageddon.Game/Linux/WormsLocator.cs index e8f31949..6709d847 100644 --- a/src/Worms.Armageddon.Game/Linux/WormsLocator.cs +++ b/src/Worms.Armageddon.Game/Linux/WormsLocator.cs @@ -14,7 +14,7 @@ public GameInfo Find() return GameInfo.NotInstalled; } - var rootLocation = Path.Combine(userHomeDirectory, "games", "worms"); + var rootLocation = Path.Combine(userHomeDirectory, ".wine", "drive_c", "WA"); var exeLocation = Path.Combine(rootLocation, processName + ".exe"); var schemesFolder = Path.Combine(rootLocation, "User", "Schemes"); var gamesFolder = Path.Combine(rootLocation, "User", "Games"); diff --git a/src/Worms.Armageddon.Game/Linux/WormsRunner.cs b/src/Worms.Armageddon.Game/Linux/WormsRunner.cs index 1e6d620f..0ca69801 100644 --- a/src/Worms.Armageddon.Game/Linux/WormsRunner.cs +++ b/src/Worms.Armageddon.Game/Linux/WormsRunner.cs @@ -10,24 +10,55 @@ public Task RunWorms(params string[] wormsArgs) async () => { var gameInfo = wormsLocator.Find(); - var args = string.Join(" ", wormsArgs); + + Console.WriteLine("ARGS: " + args); var processStartInfo = new ProcessStartInfo { - FileName = "wine", - Arguments = gameInfo.ExeLocation + " " + args, + //FileName = "wine", + //Arguments = gameInfo.ExeLocation + " " + args, + FileName = "/bin/bash", + Arguments = $""" + -c "xvfb-run wine "{gameInfo.ExeLocation}" {args}" + """, RedirectStandardOutput = true, - RedirectStandardError = true + RedirectStandardError = true, + WorkingDirectory = $"{gameInfo.ReplayFolder}", }; + processStartInfo.EnvironmentVariables["DISPLAY"] = Environment.GetEnvironmentVariable("DISPLAY"); + processStartInfo.EnvironmentVariables["WINEDLLOVERRIDES"] = "mscoree,mshtml="; using var process = Process.Start(processStartInfo); if (process is not null) { - await process.WaitForExitAsync().ConfigureAwait(false); + var processTask = Task.Run(() => process.WaitForExitAsync()); + var output = Task.Run(() => PrintStdOut(process)); + var errors = Task.Run(() => PrintStdErr(process)); + + await Task.WhenAll(processTask, errors, output).ConfigureAwait(false); + await Console.Error.WriteLineAsync("Exit code:" + process.ExitCode).ConfigureAwait(false); } return Task.CompletedTask; }); } + + private static async Task PrintStdOut(Process process) + { + while (!process.StandardOutput.EndOfStream) + { + var line = await process.StandardOutput.ReadLineAsync().ConfigureAwait(false); + await Console.Error.WriteLineAsync("StdOut: " + line).ConfigureAwait(false); + } + } + + private static async Task PrintStdErr(Process process) + { + while (!process.StandardError.EndOfStream) + { + var line = await process.StandardError.ReadLineAsync().ConfigureAwait(false); + await Console.Error.WriteLineAsync("StdErr: " + line).ConfigureAwait(false); + } + } } diff --git a/src/Worms.Armageddon.Game/Replays/ReplayFrameExtractor.cs b/src/Worms.Armageddon.Game/Replays/ReplayFrameExtractor.cs index 1da98425..b74099e1 100644 --- a/src/Worms.Armageddon.Game/Replays/ReplayFrameExtractor.cs +++ b/src/Worms.Armageddon.Game/Replays/ReplayFrameExtractor.cs @@ -19,7 +19,7 @@ public Task ExtractReplayFrames( return wormsRunner.RunWorms( "/getvideo", - $"\"{replayPath}\"", + $"'{replayPath}'", fps.ToString(CultureInfo.CurrentCulture), start, end, diff --git a/src/Worms.Hub.ReplayProcessor/Program.cs b/src/Worms.Hub.ReplayProcessor/Program.cs new file mode 100644 index 00000000..3ff7cdb8 --- /dev/null +++ b/src/Worms.Hub.ReplayProcessor/Program.cs @@ -0,0 +1,39 @@ +using Microsoft.Extensions.DependencyInjection; +using Worms.Armageddon.Game; +using Worms.Armageddon.Game.Replays; + +Console.WriteLine("Starting replay processor..."); + +var serviceCollection = new ServiceCollection().AddWormsArmageddonGameServices(); +var serviceProvider = serviceCollection.BuildServiceProvider(); + +var gameLocator = serviceProvider.GetService(); +var logGenerator = serviceProvider.GetService(); + +// Get message from queue +// Grab the replay path from the message +var replayPath = args[0]; +var userHomeDirectory = Environment.GetEnvironmentVariable("HOME"); +// Generate the replay log and save it +var gameInfo = gameLocator!.Find(); + +if (gameInfo.IsInstalled) +{ + Console.WriteLine("Game found at: {0}", gameInfo.ExeLocation); +} +else +{ + Console.WriteLine("Looking in {0}", userHomeDirectory); + Console.WriteLine("Game not found. Please install the game and try again."); + return; +} + +// Set the DISPLAY environment variable +Environment.SetEnvironmentVariable("DISPLAY", ":99"); + +// Print the DISPLAY environment variable +Console.WriteLine("DISPLAY: {0}", Environment.GetEnvironmentVariable("DISPLAY")); + +await logGenerator!.ExtractReplayFrames(replayPath, 5, TimeSpan.Zero, TimeSpan.FromSeconds(5)).ConfigureAwait(false); + +Console.WriteLine("Replay processor finished."); diff --git a/src/Worms.Hub.ReplayProcessor/Worms.Hub.ReplayProcessor.csproj b/src/Worms.Hub.ReplayProcessor/Worms.Hub.ReplayProcessor.csproj new file mode 100644 index 00000000..b22bf0e7 --- /dev/null +++ b/src/Worms.Hub.ReplayProcessor/Worms.Hub.ReplayProcessor.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + +