Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions PCL.Core/App/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -626,5 +626,6 @@ partial class HideConfigGroup
[ConfigItem<string>("VersionModpackVersion", "", ConfigSource.GameInstance)] public partial ArgConfig<string> ModpackVersion { get; }
[ConfigItem<string>("VersionModpackSource", "", ConfigSource.GameInstance)] public partial ArgConfig<string> ModpackSource { get; }
[ConfigItem<string>("VersionModpackId", "", ConfigSource.GameInstance)] public partial ArgConfig<string> ModpackId { get; }
[ConfigItem<string>("VersionAdvanceEnv", "", ConfigSource.GameInstance)] public partial ArgConfig<string> EnvVariables { get; }
}
}
100 changes: 100 additions & 0 deletions Plain Craft Launcher 2/Modules/Minecraft/ModLaunch.vb
Original file line number Diff line number Diff line change
Expand Up @@ -2313,6 +2313,105 @@ NextInstance:
End If

End Sub
Private Sub _ApplyInstanceEnvVariables(startInfo As ProcessStartInfo, instance As McInstance)
' 自定义环境变量列表处理(增强版:支持跨行值,值需用双引号包裹)
Try
Dim envRaw As String = Setup.Get("VersionAdvanceEnv", instance:=instance)
If String.IsNullOrWhiteSpace(envRaw) Then Return

Dim i As Integer = 0
While i < envRaw.Length
' 跳过空白字符(空格、制表符等,但不包括换行,因为换行作为分隔符由后续逻辑处理)
While i < envRaw.Length AndAlso Char.IsWhiteSpace(envRaw(i)) AndAlso envRaw(i) <> ControlChars.Cr AndAlso envRaw(i) <> ControlChars.Lf
i += 1
End While
If i >= envRaw.Length Then Exit While

' 跳过注释行(以 # 开头)
If envRaw(i) = "#"c Then
While i < envRaw.Length AndAlso envRaw(i) <> ControlChars.Cr AndAlso envRaw(i) <> ControlChars.Lf
i += 1
End While
' 跳过换行符
If i < envRaw.Length AndAlso envRaw(i) = ControlChars.Cr Then i += 1
If i < envRaw.Length AndAlso envRaw(i) = ControlChars.Lf Then i += 1
Continue While
End If

' 解析键名(直到遇到 = 或行尾)
Dim keyStart As Integer = i
While i < envRaw.Length AndAlso envRaw(i) <> "="c
i += 1
End While
If i >= envRaw.Length Then Exit While ' 没有等号,终止
Dim key As String = envRaw.Substring(keyStart, i - keyStart).Trim()
i += 1 ' 跳过 '='

' 解析值(可能跨行,引号内保留所有字符)
Dim valueStart As Integer = i
Dim inQuotes As Boolean = False
While i < envRaw.Length
If envRaw(i) = """"c Then
inQuotes = Not inQuotes
ElseIf Not inQuotes AndAlso (envRaw(i) = ControlChars.Cr OrElse envRaw(i) = ControlChars.Lf) Then
' 不在引号内遇到换行,值结束
Exit While
End If
i += 1
End While
Dim value As String = envRaw.Substring(valueStart, i - valueStart)

' 去除值首尾的引号(如果存在),否则仅去除首尾空白
If value.Length >= 2 AndAlso value(0) = """"c AndAlso value(value.Length - 1) = """"c Then
value = value.Substring(1, value.Length - 2)
Else
value = value.Trim()
End If

' 键名合法性检查
If String.IsNullOrEmpty(key) Then
Log($"环境变量键名为空,跳过", LogLevel.Debug)
McLaunchLog("环境变量键名为空,跳过")
ElseIf key.Contains(" "c) OrElse key.Contains("="c) Then
Log($"环境变量键名包含非法字符(空格或等号),跳过: {key}", LogLevel.Debug)
McLaunchLog($"环境变量键名包含非法字符(空格或等号),跳过: {key}")
Else
' 追加 PATH
If key.Equals("PATH", StringComparison.OrdinalIgnoreCase) Then
Dim existingPath = startInfo.EnvironmentVariables("PATH")
If String.IsNullOrEmpty(existingPath) Then
startInfo.EnvironmentVariables("PATH") = value
Else
' 确保分号分隔
If existingPath.EndsWith(";") Then
startInfo.EnvironmentVariables("PATH") = existingPath & value
Else
startInfo.EnvironmentVariables("PATH") = existingPath & ";" & value
End If
End If
Log($"追加或覆盖环境变量 PATH: {value}", LogLevel.Debug)
McLaunchLog($"追加或覆盖环境变量 PATH: {value}")
Else
' 覆盖或新增其他变量
startInfo.EnvironmentVariables(key) = value
' 值脱敏记录(防止敏感信息泄露)
Dim safeValue = If(value.Length > 20, value.Substring(0, 20) & "...", value)
Log($"设置环境变量 {key} = {safeValue}", LogLevel.Debug)
McLaunchLog($"已设置环境变量: {key} = {safeValue}")
End If
End If

' 跳过值后空白字符
While i < envRaw.Length AndAlso (Char.IsWhiteSpace(envRaw(i)) OrElse envRaw(i) = ControlChars.Cr OrElse envRaw(i) = ControlChars.Lf)
i += 1
End While
End While

Catch ex As Exception
Log(ex, "解析/注入自定义环境变量失败", LogLevel.Feedback)
McLaunchLog("解析/注入自定义环境变量失败")
End Try
End Sub
Private Sub McLaunchRun(Loader As LoaderTask(Of Integer, Process))
Dim noJavaw As Boolean = Setup.Get("LaunchAdvanceNoJavaw") AndAlso McLaunchJavaSelected.Installation.JavawExePath IsNot Nothing

Expand All @@ -2325,6 +2424,7 @@ NextInstance:
Paths.Add(ShortenPath(McLaunchJavaSelected.Installation.JavaFolder))
StartInfo.EnvironmentVariables("Path") = Join(Paths.Distinct.ToList, ";")
StartInfo.EnvironmentVariables("appdata") = ShortenPath(McFolderSelected)
_ApplyInstanceEnvVariables(StartInfo, McInstanceSelected)

'设置其他参数
StartInfo.WorkingDirectory = ShortenPath(McInstanceSelected.PathIndie)
Expand Down
19 changes: 12 additions & 7 deletions Plain Craft Launcher 2/Pages/PageInstance/PageInstanceSetup.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,10 @@
<RowDefinition Height="9" />
<RowDefinition Height="28" />
<RowDefinition Height="9" />
<RowDefinition Height="28" />
<RowDefinition Height="Auto" />
<RowDefinition Height="9" />
<RowDefinition Height="31" />
<RowDefinition Height="28" />
<RowDefinition Height="10" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
Expand All @@ -227,14 +228,18 @@
<TextBlock Grid.Row="4" VerticalAlignment="Center" HorizontalAlignment="Left" Text="游戏参数尾部" Margin="0,0,25,0" />
<local:MyTextBox x:Name="TextAdvanceGame" Grid.Row="4" Grid.ColumnSpan="2" Tag="VersionAdvanceGame" Grid.Column="1" ToolTip="文本框中的内容将会被直接拼合在启动参数的末尾。&#xa;例如,输入 --demo 则会以试玩模式启动游戏。&#xa;支持 Minecraft 实例 JSON 中的字符串替换标记,例如 ${library_directory}。&#xa;&#xa;若留空,则跟随全局设置的值。" HintText="跟随全局设置" />

<TextBlock Grid.Row="6" VerticalAlignment="Center" HorizontalAlignment="Left" Text="Classpath 头部附加" Margin="0,0,25,0" />
<local:MyTextBox x:Name="TextAdvanceClasspathHead" Grid.Row="6" Grid.ColumnSpan="2" Tag="VersionAdvanceClasspathHead" Grid.Column="1" ToolTip="文本框中的内容将被作为 Classpath 添加到该实例的启动参数中的 Classpath 头部。&#xa;需要使用绝对路径。若有多个,请以半角 (英文) 分号分隔。" />
<TextBlock Grid.Row="6" VerticalAlignment="Center" HorizontalAlignment="Left" Text="自定义环境变量" Margin="0,0,25,0" />
<local:MyTextBox x:Name="TextAdvanceEnv" Grid.Row="6" Grid.ColumnSpan="2" Tag="VersionAdvanceEnv" Grid.Column="1" Height="100" AcceptsReturn="True" TextWrapping="Wrap" VerticalContentAlignment="Top" Padding="0,5" VerticalScrollBarVisibility="Auto"
ToolTip="每行一个环境变量,格式为 KEY=VALUE,值包含换行时请使用引号将值括起。以 # 开头的行将被忽略。&#xa;示例:&#xa;JAVA_HOME=C:\Program Files\Java\jdk-21&#xa;MAVEN_OPTS=-Xmx2G" />

<TextBlock Grid.Row="8" VerticalAlignment="Center" HorizontalAlignment="Left" Text="Classpath 头部附加" Margin="0,0,25,0" />
<local:MyTextBox x:Name="TextAdvanceClasspathHead" Grid.Row="8" Grid.ColumnSpan="2" Tag="VersionAdvanceClasspathHead" Grid.Column="1" ToolTip="文本框中的内容将被作为 Classpath 添加到该实例的启动参数中的 Classpath 头部。&#xa;需要使用绝对路径。若有多个,请以半角 (英文) 分号分隔。" />

<TextBlock Grid.Row="8" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" />
<local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="8" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3"
<TextBlock Grid.Row="10" VerticalAlignment="Center" HorizontalAlignment="Left" Text="启动前执行命令" Margin="0,0,25,3" />
<local:MyTextBox x:Name="TextAdvanceRun" Grid.Row="10" Grid.ColumnSpan="2" Tag="VersionAdvanceRun" Grid.Column="1" HintText="" Margin="0,0,0,3"
ToolTip="该项不会覆盖全局设置:启动时会先执行全局设置的命令,再执行实例设置的命令。&#xa;&#xa;在 MC 启动前执行特定命令或程序,语法与 Windows 的命令提示符一致。&#xa;&#xa;可以使用以下替换标记实现相对路径(路径均以 \ 结尾):&#xa; · {path}:PCL 的 exe 文件所在的文件夹&#xa; · {minecraft}:.minecraft 文件夹&#xa; · {verpath}:实例文件夹(.minecraft\versions\实例名\)&#xa; · {verindie}:开启版本隔离时等同版本文件夹,未开启时等同 .minecraft 文件夹&#xa; · {java}:游戏运行时的 Java 文件夹&#xa;&#xa;除此之外,也支持以下替换标记:&#xa; · {user}:玩家名字&#xa; · {login}:玩家的登录方式&#xa; · {uuid}:玩家的 UUID&#xa; · {name}:游戏实例名&#xa; · {date}、{time}:当前的系统时间&#xa; · {version}:游戏对应的原版版本号&#xa;&#xa;例如:&#xa; · &quot;{verpath}test.exe&quot; :运行实例文件夹下的 test.exe 程序&#xa; · &quot;{java}java.exe&quot; -jar &quot;{verpath}test.jar&quot; :用 Java 运行实例文件夹下的 test.jar&#xa; · notepad &quot;{verindie}options.txt&quot; :使用记事本打开该实例的设置文件&#xa;&#xa;涉及路径的操作最好都打上双引号,以避免路径中的空格导致运行失败。&#xa;执行命令时,命令行所在的目录是当前的 .minecraft 文件夹。" />

<StackPanel Margin="0,8,0,4" Grid.Row="9" Grid.ColumnSpan="2" HorizontalAlignment="Left">
<StackPanel Margin="0,8,0,4" Grid.Row="11" Grid.ColumnSpan="2" HorizontalAlignment="Left">
<local:MyCheckBox Height="28" x:Name="CheckAdvanceRunWait" Tag="VersionAdvanceRunWait" Visibility="Collapsed"
Text="等待命令执行完成后再继续启动" />
<local:MyCheckBox Height="28" Text="忽略 Java 兼容性警告" x:Name="CheckAdvanceJava" Tag="VersionAdvanceJava"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ Public Class PageInstanceSetup
TextAdvanceJvm.Text = Setup.Get("VersionAdvanceJvm", instance:=PageInstanceLeft.Instance)
TextAdvanceGame.Text = Setup.Get("VersionAdvanceGame", instance:=PageInstanceLeft.Instance)
TextAdvanceRun.Text = Setup.Get("VersionAdvanceRun", instance:=PageInstanceLeft.Instance)
TextAdvanceEnv.Text = Setup.Get("VersionAdvanceEnv", instance:=PageInstanceLeft.Instance)
CheckAdvanceRunWait.Checked = Setup.Get("VersionAdvanceRunWait", instance:=PageInstanceLeft.Instance)
If Setup.Get("VersionAdvanceAssets", instance:=PageInstanceLeft.Instance) = 2 Then
Log("[Setup] 已迁移老版本的关闭文件校验设置")
Expand Down Expand Up @@ -115,6 +116,7 @@ Public Class PageInstanceSetup
Setup.Reset("VersionAdvanceRenderer", instance:=PageInstanceLeft.Instance)

Setup.Reset("VersionArgumentJavaSelect", instance:=PageInstanceLeft.Instance)
Setup.Reset("VersionAdvanceEnv", instance:=PageInstanceLeft.Instance)

Log("[Setup] 已初始化实例独立设置")
Hint("已初始化实例独立设置!", HintType.Finish, False)
Expand All @@ -130,7 +132,7 @@ Public Class PageInstanceSetup
Dim gotCfg = sender.Tag.ToString.Split("/")
If AniControlEnabled = 0 Then Setup.Set(gotCfg(0), Integer.Parse(gotCfg(1)), instance:=PageInstanceLeft.Instance)
End Sub
Private Shared Sub TextBoxChange(sender As MyTextBox, e As Object) Handles TextServerEnter.ValidatedTextChanged, TextArgumentInfo.ValidatedTextChanged, TextAdvanceGame.ValidatedTextChanged, TextAdvanceJvm.ValidatedTextChanged, TextServerAuthName.ValidatedTextChanged, TextServerAuthRegister.ValidatedTextChanged, TextServerAuthServer.ValidatedTextChanged, TextArgumentTitle.TextChanged, TextAdvanceRun.ValidatedTextChanged
Private Shared Sub TextBoxChange(sender As MyTextBox, e As Object) Handles TextServerEnter.ValidatedTextChanged, TextArgumentInfo.ValidatedTextChanged, TextAdvanceGame.ValidatedTextChanged, TextAdvanceJvm.ValidatedTextChanged, TextServerAuthName.ValidatedTextChanged, TextServerAuthRegister.ValidatedTextChanged, TextServerAuthServer.ValidatedTextChanged, TextArgumentTitle.TextChanged, TextAdvanceRun.ValidatedTextChanged, TextAdvanceEnv.ValidatedTextChanged
If AniControlEnabled = 0 Then
'#3194,不能删减 /
'Dim HandledText As String = sender.Text
Expand Down