Skip to content

Commit

Permalink
Merge pull request #103 from Corona-Studio/sample
Browse files Browse the repository at this point in the history
Add sample
  • Loading branch information
BiDuang authored Dec 20, 2023
2 parents 40157b3 + dabc03e commit 1e96533
Show file tree
Hide file tree
Showing 14 changed files with 669 additions and 3 deletions.
109 changes: 109 additions & 0 deletions ProjBobcat/ProjBobcat.Sample/Commands/Game/GameLaunchCommands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using System.ComponentModel.DataAnnotations;
using Cocona;
using Microsoft.Extensions.Logging;
using ProjBobcat.Class.Helper;
using ProjBobcat.Class.Model;
using ProjBobcat.DefaultComponent.Authenticator;

namespace ProjBobcat.Sample.Commands.Game;

public class GameLaunchCommands(ILogger<GameLaunchCommands> logger)
{
[Command("launch", Aliases = ["run", "start", "play"], Description = "Launch the game.")]
public async Task LaunchGameAsync(
[Argument(Description = "Game Id")] string gameId,
[Argument(Description = "Offline account name")] string displayName,
[Argument(Description = "Java executable path")] string javaPath,
[Range(0, 5)] [Option("gc", Description = "Garbage collection type")] int gcType = 0,
[Range(1024, 1024 * 1024 * 10)] [Option("max-memory", Description = "Max memory size in MB")] uint maxMemory = 1024,
[Option("title", Description = "Custom game window title (Windows only)")] string windowTitle = "Minecraft - By ProjBobcat")
{
var version = GameHelper.GameCore.VersionLocator.GetGame(gameId);

if (version == null)
{
logger.LogCritical(
"Game {GameId} not found. Please check if the game id is correct.",
gameId);

return;
}

var auth = new OfflineAuthenticator
{
LauncherAccountParser = GameHelper.GameCore.VersionLocator.LauncherAccountParser!,
Username = displayName
};
var authResult = await auth.AuthTaskAsync(false);

var args = new GameArguments
{
GcType = (GcType)gcType,
JavaExecutable = javaPath,
MaxMemory = maxMemory
};
var ls = new LaunchSettings
{
Authenticator = auth,
Version = gameId,
GameArguments = args,
GameName = version.Name,
GamePath = GamePathHelper.GetVersionPath(GameHelper.GameCore.RootPath),
GameResourcePath = GameHelper.GameCore.RootPath,
LauncherName = "LauncherX (By ProjBobcat)",
VersionInsulation = true,
VersionLocator = GameHelper.GameCore.VersionLocator,
WindowTitle = windowTitle,
SelectedProfile = authResult.SelectedProfile
};

GameHelper.GameCore.LaunchLogEventDelegate += (_, eventArgs) =>
{
logger.LogInformation("[{Time}] {Message}", eventArgs.ItemRunTime, eventArgs.Item);
};

GameHelper.GameCore.GameExitEventDelegate += (_, eventArgs) =>
{
logger.LogInformation("Game exit with code {ExitCode}.", eventArgs.ExitCode);
};

GameHelper.GameCore.GameLogEventDelegate += (_, eventArgs) =>
{
logger.LogInformation(eventArgs.RawContent);
};

var result = await GameHelper.GameCore.LaunchTaskAsync(ls);
var isSucceed = result.Error == null;
var errorReason = result.ErrorType switch
{
LaunchErrorType.AuthFailed => "AuthFailed",
LaunchErrorType.DecompressFailed => "DecompressFailed",
LaunchErrorType.IncompleteArguments => "IncompleteArgument",
LaunchErrorType.NoJava => "NoJava",
LaunchErrorType.None => "None",
LaunchErrorType.OperationFailed => "OperationFailed",
LaunchErrorType.Unknown => "Unknown",
var x => throw new ArgumentOutOfRangeException(x.ToString())
};
var detail = isSucceed
? $"Duration: {result.RunTime:g}"
: $"{result.Error?.ErrorMessage ?? "Unknown"}, Reason: {errorReason}";

logger.Log(isSucceed ? LogLevel.Information : LogLevel.Critical, detail);

if (!isSucceed && result.Error?.Exception != null)
{
logger.LogError(result.Error?.Exception, "Exception occurred when launching game.");

return;
}

if (result.GameProcess == null) return;

logger.LogInformation("Game process started, wait for game to exit.");

await result.GameProcess.WaitForExitAsync();

logger.LogInformation("Game process exited.");
}
}
185 changes: 185 additions & 0 deletions ProjBobcat/ProjBobcat.Sample/Commands/Game/GameManageCommands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
using System.Collections.Immutable;
using System.ComponentModel.DataAnnotations;
using Cocona;
using Microsoft.Extensions.Logging;
using ProjBobcat.Class.Helper;
using ProjBobcat.Class.Model;
using ProjBobcat.Interface;
using ProjBobcat.Sample.DownloadMirrors;

namespace ProjBobcat.Sample.Commands.Game;

public class GameManageCommands(ILogger<GameManageCommands> logger)
{
[Command("search", Aliases = ["list", "find"], Description = "Search games on the local machine")]
public void GetLocalGames()
{
logger.LogInformation("Start searching games...");

var games = GameHelper.GameCore.VersionLocator.GetAllGames().ToImmutableList();

if (games.Count == 0)
{
logger.LogWarning("No games found.");
return;
}

foreach (var game in games)
logger.LogInformation("Found game: {GameId}[{GameName}]", game.Id, game.Name);
}

[Command("mirrors", Description = "Get download mirrors")]
public void GetDownloadMirrors()
{
string[] mirrors =
[
nameof(OfficialDownloadMirror),
nameof(BangBangDownloadMirror),
nameof(McBbsDownloadMirror)
];

for (var i = 0; i < mirrors.Length; i++)
{
var mirror = mirrors[i];
var mirrorName = mirror[..^"DownloadMirror".Length];

logger.LogInformation("[{Index}] {MirrorName}", i, mirrorName);
}
}

[Command("gc", Description = "Get garbage collection types")]
public void GetGcTypes()
{
var arr = Enum.GetNames<GcType>();

for (var i = 0; i < arr.Length; i++)
{
var name = arr[i];

logger.LogInformation("[{Index}] {Name}", i, name);
}
}

[Command("repair", Description = "Check game files and repair them if needed")]
public async Task RepairGameAsync(
[Argument(Description = "Game id")] string gameId,
[Range(0, 2)]
[Argument("mirror", Description = "Download mirror index, use [game mirrors] to reveal mirror indexes")]
int mirrorIndex = 0,
[Range(1, 32)] [Argument("parallel", Description = "Parallel tasks")]
int parallel = 8,
[Range(1, 128)] [Argument("download-threads", Description = "Download threads")]
int downloadThreads = 32,
[Range(1, 16)] [Argument("download-parts", Description = "Download parts")]
int downloadParts = 8,
[Option("verbose", Description = "Enable verbose mode")]
bool verbose = false,
[Option("disable-verify", Description = "Disable file verification")]
bool disableVerify = false,
[Option("uncheck-version", Description = "Disable Version Info checker")]
bool uncheckVersion = false,
[Option("uncheck-asset", Description = "Disable Asset Info checker")]
bool uncheckAsset = false,
[Option("uncheck-lib", Description = "Disable Library checker")]
bool uncheckLib = false)
{
logger.LogInformation("Start repairing game {GameId}...", gameId);

var versionInfo = GameHelper.GameCore.VersionLocator.GetGame(gameId);
IDownloadMirror mirror = mirrorIndex switch
{
0 => new OfficialDownloadMirror(),
1 => new BangBangDownloadMirror(),
2 => new McBbsDownloadMirror(),
_ => throw new ArgumentOutOfRangeException(nameof(mirrorIndex))
};

if (versionInfo == null)
{
logger.LogCritical(
"Game {GameId} not found. Please check if the game id is correct.",
gameId);

return;
}

var resolvers = new List<IResourceInfoResolver>();

if (!uncheckVersion)
resolvers.Add(GameHelper.GetVersionInfoResolver(versionInfo));
if (!uncheckAsset)
{
logger.LogInformation("Start to fetch Version Manifest from remote...");

var vm = await GameHelper.GetVersionManifestAsync(mirror);

if (vm == null)
{
logger.LogCritical(
"Failed to fetch Version Manifest from remote. Please check your network connection.");
return;
}

resolvers.Add(GameHelper.GetAssetInfoResolver(versionInfo, vm, mirror));
}

if (!uncheckLib)
resolvers.Add(GameHelper.GetLibraryInfoResolver(versionInfo, mirror));

if (resolvers.Count == 0)
{
logger.LogWarning("No resolver enabled. Please check your command.");
return;
}

foreach (var resolver in resolvers)
logger.LogInformation("Resolver enabled: {Resolver}", resolver.GetType().Name);

var completer = GameHelper.GetResourceCompleter(
resolvers,
parallel,
downloadThreads,
downloadParts,
!disableVerify);

if (verbose)
{
completer.GameResourceInfoResolveStatus += (_, args) =>
{
logger.LogInformation("[{Progress:P2}] {Message}", args.Progress, args.Status);
};
}

var progressVal = 0d;

completer.DownloadFileChangedEvent += (_, args) => { progressVal = args.ProgressPercentage; };

completer.DownloadFileCompletedEvent += (sender, args) =>
{
if (sender is not DownloadFile file) return;
if (args.Error != null)
logger.LogError(args.Error, "Failed to download file {FileName}", file.FileName);
var isSuccess = args.Success == true ? "Success" : "Failed";
var retry = file.RetryCount == 0
? null
: $"<Retry - {file.RetryCount}>";
var fileName = file.FileName;
var speed = DownloadHelper.AutoFormatSpeedString(args.AverageSpeed);
var pD = $"<{file.FileType}>{retry}{isSuccess} {fileName} [{speed}]";
logger.LogInformation("[{Progress:P}] {ProgressDetail}", progressVal, pD);
};

logger.LogInformation("Start to check and download game {GameId}...", gameId);

var rCResult = await completer.CheckAndDownloadTaskAsync();

if (rCResult.TaskStatus == TaskResultStatus.Error || (rCResult.Value?.IsLibDownloadFailed ?? false))
logger.LogCritical("Failed to repair game {GameId}.", gameId);
else
logger.LogInformation("Game {GameId} repaired.", gameId);
}
}
44 changes: 44 additions & 0 deletions ProjBobcat/ProjBobcat.Sample/Commands/SystemInfoCommands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Cocona;
using Microsoft.Extensions.Logging;
using ProjBobcat.Class.Helper;

namespace ProjBobcat.Sample.Commands;

public class SystemInfoCommands(ILogger<SystemInfoCommands> logger)
{
[SupportedOSPlatform("windows10.0.10586")]
[SupportedOSPlatform(nameof(OSPlatform.OSX))]
[Command("running-native", Description = "Check if the current process is running on native platform.")]
public void CheckCpuTranslation()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
logger.LogError("This command is not supported on Linux.");
return;
}

logger.LogInformation("Is running under translation: {IsRunningUnderTranslation}",
SystemInfoHelper.IsRunningUnderTranslation());
}

[SupportedOSPlatform(nameof(OSPlatform.Windows))]
[SupportedOSPlatform(nameof(OSPlatform.OSX))]
[SupportedOSPlatform(nameof(OSPlatform.Linux))]
[Command("find-java", Description = "Search Java on the local machine")]
public async Task SearchJavaAsync(
[Option("d", Description = "Enable deep search")] bool deepSearch)
{
logger.LogInformation("Searching Java...");

await foreach(var java in SystemInfoHelper.FindJava(deepSearch))
logger.LogInformation("Found Java: {JavaPath}", java);
}

[Command("arch", Description = "Get system arch")]
public void CheckSystemArch()
{
logger.LogInformation("System arch: {SystemArch}", SystemInfoHelper.GetSystemArch());
}
}
34 changes: 34 additions & 0 deletions ProjBobcat/ProjBobcat.Sample/Commands/SystemMetricsCommands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using Cocona;
using Microsoft.Extensions.Logging;
using ProjBobcat.Class.Helper;

namespace ProjBobcat.Sample.Commands;

public class SystemMetricsCommands(ILogger<SystemMetricsCommands> logger)
{
[Command("cpu", Description = "Get CPU usage")]
public async Task GetCpuUsageAsync(
[Argument(Description = "Sample count")] int sampleCount = 6)
{
for (var i = 0; i < sampleCount; i++)
{
var cpuUsage = SystemInfoHelper.GetProcessorUsage();
logger.LogInformation("CPU usage: {CpuUsage}", cpuUsage);

await Task.Delay(TimeSpan.FromSeconds(1));
}
}

[Command("mem", Description = "Get memory usage")]
public async Task GetMemoryUsageAsync(
[Argument(Description = "Sample count")] int sampleCount = 6)
{
for (var i = 0; i < sampleCount; i++)
{
var memUsage = SystemInfoHelper.GetMemoryUsage();
logger.LogInformation("Memory usage: {MemoryUsage}", memUsage);

await Task.Delay(TimeSpan.FromSeconds(1));
}
}
}
15 changes: 15 additions & 0 deletions ProjBobcat/ProjBobcat.Sample/Commands/TestCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Cocona;
using Microsoft.Extensions.Logging;

namespace ProjBobcat.Sample.Commands;

public class TestCommand(ILogger<TestCommand> logger)
{
readonly ILogger _logger = logger;

[Command("hello")]
public void Hello()
{
_logger.LogInformation("Hello from Cocona.");
}
}
Loading

0 comments on commit 1e96533

Please sign in to comment.