Skip to content

Commit 5e22978

Browse files
authored
Merge pull request PCL-Community#133 from YiZhiMCQiu/main
feat: 为启动命令添加类路径参数
2 parents 2655b90 + d91e493 commit 5e22978

File tree

2 files changed

+78
-67
lines changed

2 files changed

+78
-67
lines changed

PCL.Neo.Core/Models/Minecraft/Game/Data/Arguments.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using System.Text.RegularExpressions;
2-
using System.Text.Json.Serialization;
31
using System.Runtime.InteropServices;
2+
using System.Text.Json.Serialization;
3+
using System.Text.RegularExpressions;
44

55
namespace PCL.Neo.Core.Models.Minecraft.Game.Data;
66

PCL.Neo.Core/Models/Minecraft/Game/GameLauncher.cs

Lines changed: 76 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using PCL.Neo.Core.Models.Minecraft.Game.Data;
2+
using PCL.Neo.Core.Utils;
33
using System.Diagnostics;
4-
using System.IO;
5-
using System.Runtime.InteropServices;
64
using System.Text;
7-
using System.Threading.Tasks;
8-
using PCL.Neo.Core.Models.Minecraft.Game.Data;
9-
using PCL.Neo.Core.Models.Minecraft.Java;
105

116
namespace PCL.Neo.Core.Models.Minecraft.Game
127
{
@@ -101,7 +96,7 @@ public class LaunchOptions
10196
public class GameLauncher
10297
{
10398
private readonly GameService _gameService;
104-
99+
105100
public GameLauncher(GameService gameService)
106101
{
107102
_gameService = gameService;
@@ -115,42 +110,42 @@ public async Task<Process> LaunchAsync(LaunchOptions options)
115110
// 验证必要参数
116111
if (string.IsNullOrEmpty(options.VersionId))
117112
throw new ArgumentException("版本ID不能为空");
118-
113+
119114
if (string.IsNullOrEmpty(options.JavaPath))
120115
throw new ArgumentException("Java路径不能为空");
121-
116+
122117
// 确保目录存在
123118
string mcDir = options.MinecraftDirectory;
124119
if (string.IsNullOrEmpty(mcDir))
125120
mcDir = GameService.DefaultGameDirectory;
126-
121+
127122
string gameDir = options.GameDirectory;
128123
if (string.IsNullOrEmpty(gameDir))
129124
gameDir = mcDir;
130-
125+
131126
// 确保目录存在
132127
Directory.CreateDirectory(mcDir);
133128
Directory.CreateDirectory(gameDir);
134-
129+
135130
// 获取版本信息
136131
var versionInfo = await Versions.GetVersionByIdAsync(mcDir, options.VersionId);
137132
if (versionInfo == null)
138133
throw new Exception($"找不到版本 {options.VersionId}");
139-
134+
140135
// 解析继承关系(如果有)
141136
if (!string.IsNullOrEmpty(versionInfo.InheritsFrom))
142137
{
143138
var parentInfo = await Versions.GetVersionByIdAsync(mcDir, versionInfo.InheritsFrom);
144139
if (parentInfo == null)
145140
throw new Exception($"找不到父版本 {versionInfo.InheritsFrom}");
146-
141+
147142
// 合并版本信息
148143
versionInfo = MergeVersionInfo(versionInfo, parentInfo);
149144
}
150-
145+
151146
// 构建启动命令
152-
var commandArgs = BuildLaunchCommand(options, versionInfo, mcDir, gameDir);
153-
147+
var commandArgs = BuildLaunchCommand(options, versionInfo);
148+
154149
// 创建进程
155150
var process = new Process
156151
{
@@ -165,22 +160,22 @@ public async Task<Process> LaunchAsync(LaunchOptions options)
165160
WorkingDirectory = gameDir
166161
}
167162
};
168-
163+
169164
// 设置环境变量
170165
foreach (var env in options.EnvironmentVariables)
171166
{
172167
process.StartInfo.EnvironmentVariables[env.Key] = env.Value;
173168
}
174-
169+
175170
// 启动进程
176171
process.Start();
177-
172+
178173
// 记录日志(异步)
179174
_ = LogProcessOutputAsync(process);
180-
175+
181176
return process;
182177
}
183-
178+
184179
/// <summary>
185180
/// 合并版本信息(处理继承关系)
186181
/// </summary>
@@ -196,24 +191,24 @@ private VersionInfo MergeVersionInfo(VersionInfo child, VersionInfo parent)
196191
Time = child.Time,
197192
JsonData = child.JsonData
198193
};
199-
194+
200195
// 从父版本继承属性
201196
merged.MinecraftArguments = child.MinecraftArguments ?? parent.MinecraftArguments;
202197
merged.Arguments = child.Arguments ?? parent.Arguments;
203198
merged.MainClass = child.MainClass ?? parent.MainClass;
204199
merged.AssetIndex = child.AssetIndex ?? parent.AssetIndex;
205200
merged.Assets = child.Assets ?? parent.Assets;
206201
merged.JavaVersion = child.JavaVersion ?? parent.JavaVersion;
207-
202+
208203
// 合并下载信息
209204
merged.Downloads = child.Downloads ?? parent.Downloads;
210-
205+
211206
// 合并库文件(子版本优先)
212207
var libraries = new List<Library>();
213-
208+
214209
if (parent.Libraries != null)
215210
libraries.AddRange(parent.Libraries);
216-
211+
217212
if (child.Libraries != null)
218213
{
219214
foreach (var lib in child.Libraries)
@@ -228,28 +223,28 @@ private VersionInfo MergeVersionInfo(VersionInfo child, VersionInfo parent)
228223
break;
229224
}
230225
}
231-
226+
232227
// 不存在则添加
233228
if (!exists)
234229
libraries.Add(lib);
235230
}
236231
}
237-
232+
238233
merged.Libraries = libraries;
239234
return merged;
240235
}
241-
236+
242237
/// <summary>
243238
/// 构建游戏启动命令
244239
/// </summary>
245-
private string BuildLaunchCommand(LaunchOptions options, VersionInfo versionInfo, string mcDir, string gameDir)
240+
private string BuildLaunchCommand(LaunchOptions options, VersionInfo versionInfo)
246241
{
247242
var args = new List<string>();
248-
243+
249244
// JVM参数
250245
args.Add($"-Xmx{options.MaxMemoryMB}M");
251246
args.Add($"-Xms{options.MinMemoryMB}M");
252-
247+
253248
// 标准JVM参数
254249
args.Add("-XX:+UseG1GC");
255250
args.Add("-XX:+ParallelRefProcEnabled");
@@ -269,42 +264,58 @@ private string BuildLaunchCommand(LaunchOptions options, VersionInfo versionInfo
269264
args.Add("-XX:SurvivorRatio=32");
270265
args.Add("-XX:+PerfDisableSharedMem");
271266
args.Add("-XX:MaxTenuringThreshold=1");
272-
267+
273268
// 设置natives路径
274-
string nativesDir = Path.Combine(mcDir, "versions", options.VersionId, "natives");
269+
string nativesDir = Path.Combine(options.MinecraftDirectory, "versions", options.VersionId, "natives");
275270
EnsureDirectoryExists(nativesDir);
276-
271+
277272
args.Add($"-Djava.library.path={QuotePath(nativesDir)}");
278273
args.Add($"-Dminecraft.launcher.brand=PCL.Neo");
279274
args.Add($"-Dminecraft.launcher.version=1.0.0");
280-
275+
276+
// 类路径
277+
args.Add("-cp");
278+
List<String> classpaths = new();
279+
if (versionInfo.Libraries != null)
280+
{
281+
foreach (Library library in versionInfo.Libraries)
282+
{
283+
if (library.Downloads?.Artifact?.Path != null)
284+
{
285+
classpaths.Add(Path.Combine(options.MinecraftDirectory, "libraries", library.Downloads!.Artifact!.Path!)); // 不用担心空格问题
286+
}
287+
}
288+
}
289+
classpaths.Add(Path.Combine(options.GameDirectory, options.VersionId));
290+
args.Add(string.Join(SystemUtils.Os == SystemUtils.RunningOs.Windows ? ';' : ':', classpaths));
291+
281292
// 客户端类型
282293
string clientType = options.IsOfflineMode ? "legacy" : "mojang";
283-
294+
284295
// 添加额外的JVM参数
285296
if (options.ExtraJvmArgs != null && options.ExtraJvmArgs.Count > 0)
286297
{
287298
args.AddRange(options.ExtraJvmArgs);
288299
}
289-
300+
290301
// 主类
291302
args.Add(versionInfo.MainClass);
292-
303+
293304
// 游戏参数
294305
if (!string.IsNullOrEmpty(versionInfo.MinecraftArguments))
295306
{
296307
// 旧版格式
297308
string gameArgs = versionInfo.MinecraftArguments
298309
.Replace("${auth_player_name}", options.Username)
299310
.Replace("${version_name}", options.VersionId)
300-
.Replace("${game_directory}", QuotePath(gameDir))
301-
.Replace("${assets_root}", QuotePath(Path.Combine(mcDir, "assets")))
311+
.Replace("${game_directory}", QuotePath(options.GameDirectory))
312+
.Replace("${assets_root}", QuotePath(Path.Combine(options.MinecraftDirectory, "assets")))
302313
.Replace("${assets_index_name}", versionInfo.AssetIndex?.Id ?? "legacy")
303314
.Replace("${auth_uuid}", options.UUID)
304315
.Replace("${auth_access_token}", options.AccessToken)
305316
.Replace("${user_type}", clientType)
306317
.Replace("${version_type}", versionInfo.Type);
307-
318+
308319
args.AddRange(gameArgs.Split(' '));
309320
}
310321
else if (versionInfo.Arguments != null)
@@ -320,14 +331,14 @@ private string BuildLaunchCommand(LaunchOptions options, VersionInfo versionInfo
320331
string processedArg = strArg
321332
.Replace("${auth_player_name}", options.Username)
322333
.Replace("${version_name}", options.VersionId)
323-
.Replace("${game_directory}", QuotePath(gameDir))
324-
.Replace("${assets_root}", QuotePath(Path.Combine(mcDir, "assets")))
334+
.Replace("${game_directory}", QuotePath(options.GameDirectory))
335+
.Replace("${assets_root}", QuotePath(Path.Combine(options.MinecraftDirectory, "assets")))
325336
.Replace("${assets_index_name}", versionInfo.AssetIndex?.Id ?? "legacy")
326337
.Replace("${auth_uuid}", options.UUID)
327338
.Replace("${auth_access_token}", options.AccessToken)
328339
.Replace("${user_type}", clientType)
329340
.Replace("${version_type}", versionInfo.Type);
330-
341+
331342
args.Add(processedArg);
332343
}
333344
}
@@ -341,9 +352,9 @@ private string BuildLaunchCommand(LaunchOptions options, VersionInfo versionInfo
341352
args.Add("--version");
342353
args.Add(options.VersionId);
343354
args.Add("--gameDir");
344-
args.Add(QuotePath(gameDir));
355+
args.Add(QuotePath(options.GameDirectory));
345356
args.Add("--assetsDir");
346-
args.Add(QuotePath(Path.Combine(mcDir, "assets")));
357+
args.Add(QuotePath(Path.Combine(options.MinecraftDirectory, "assets")));
347358
args.Add("--assetIndex");
348359
args.Add(versionInfo.AssetIndex?.Id ?? "legacy");
349360
args.Add("--uuid");
@@ -355,7 +366,7 @@ private string BuildLaunchCommand(LaunchOptions options, VersionInfo versionInfo
355366
args.Add("--versionType");
356367
args.Add(versionInfo.Type);
357368
}
358-
369+
359370
// 窗口大小
360371
if (!options.FullScreen)
361372
{
@@ -368,17 +379,17 @@ private string BuildLaunchCommand(LaunchOptions options, VersionInfo versionInfo
368379
{
369380
args.Add("--fullscreen");
370381
}
371-
382+
372383
// 添加额外的游戏参数
373384
if (options.ExtraGameArgs != null && options.ExtraGameArgs.Count > 0)
374385
{
375386
args.AddRange(options.ExtraGameArgs);
376387
}
377-
388+
378389
// 拼接所有参数
379390
return string.Join(" ", args);
380391
}
381-
392+
382393
/// <summary>
383394
/// 确保目录存在
384395
/// </summary>
@@ -389,38 +400,38 @@ private void EnsureDirectoryExists(string path)
389400
Directory.CreateDirectory(path);
390401
}
391402
}
392-
403+
393404
/// <summary>
394405
/// 为路径加上引号(如果包含空格)
395406
/// </summary>
396407
private string QuotePath(string path)
397408
{
398409
// 统一路径分隔符为当前系统的分隔符
399410
path = path.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar);
400-
411+
401412
// 如果路径包含空格,则加上引号
402413
if (path.Contains(" "))
403414
{
404415
return $"\"{path}\"";
405416
}
406417
return path;
407418
}
408-
419+
409420
/// <summary>
410421
/// 记录进程输出
411422
/// </summary>
412423
private async Task LogProcessOutputAsync(Process process)
413424
{
414425
var logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "PCL.Neo", "logs");
415426
EnsureDirectoryExists(logDir);
416-
427+
417428
string logFile = Path.Combine(logDir, $"game_{DateTime.Now:yyyyMMdd_HHmmss}.log");
418-
429+
419430
using (var writer = new StreamWriter(logFile, false, Encoding.UTF8))
420431
{
421432
writer.WriteLine($"PCL.Neo Game Log - {DateTime.Now}");
422433
writer.WriteLine("---------------------------------------------");
423-
434+
424435
try
425436
{
426437
string? line;
@@ -434,7 +445,7 @@ private async Task LogProcessOutputAsync(Process process)
434445
{
435446
await writer.WriteLineAsync($"[ERROR] Error reading standard output: {ex.Message}");
436447
}
437-
448+
438449
try
439450
{
440451
string? line;
@@ -450,7 +461,7 @@ private async Task LogProcessOutputAsync(Process process)
450461
}
451462
}
452463
}
453-
464+
454465
/// <summary>
455466
/// 导出游戏日志
456467
/// </summary>
@@ -463,20 +474,20 @@ public async Task ExportGameLogsAsync(string filePath)
463474
await File.WriteAllTextAsync(filePath, "没有找到游戏日志");
464475
return;
465476
}
466-
477+
467478
// 获取最新的日志文件
468479
var logFile = Directory.GetFiles(logDir, "game_*.log")
469480
.OrderByDescending(f => File.GetCreationTime(f))
470481
.FirstOrDefault();
471-
482+
472483
if (string.IsNullOrEmpty(logFile))
473484
{
474485
await File.WriteAllTextAsync(filePath, "没有找到游戏日志");
475486
return;
476487
}
477-
488+
478489
// 复制日志
479490
File.Copy(logFile, filePath, true);
480491
}
481492
}
482-
}
493+
}

0 commit comments

Comments
 (0)