1- using System ;
2- using System . Collections . Generic ;
1+ using PCL . Neo . Core . Models . Minecraft . Game . Data ;
2+ using PCL . Neo . Core . Utils ;
33using System . Diagnostics ;
4- using System . IO ;
5- using System . Runtime . InteropServices ;
64using 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
116namespace 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