Skip to content

Commit d095eab

Browse files
Glavo3gf8jv4dv
andauthored
[release/3.12] 通过 Java Agent 优化 LWJGL 3.4.1 性能 (#5886)
#5870 #5873 #5874 #5882 --------- Co-authored-by: 3gf8jv4dv <3gf8jv4dv@gmail.com>
1 parent 447edff commit d095eab

11 files changed

Lines changed: 139 additions & 0 deletions

File tree

HMCL/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ dependencies {
6666
}
6767

6868
embedResources(libs.authlib.injector)
69+
embedResources(libs.lwjgl.unsafe.agent)
6970
}
7071

7172
fun digest(algorithm: String, bytes: ByteArray): ByteArray = MessageDigest.getInstance(algorithm).digest(bytes)
@@ -155,6 +156,7 @@ val hmclProperties = buildList {
155156
add("hmcl.microsoft.auth.id" to microsoftAuthId)
156157
add("hmcl.curseforge.apikey" to curseForgeApiKey)
157158
add("hmcl.authlib-injector.version" to libs.authlib.injector.get().version!!)
159+
add("hmcl.lwjgl-unsafe-agent.version" to libs.lwjgl.unsafe.agent.get().version!!)
158160
}
159161

160162
val hmclPropertiesFile = layout.buildDirectory.file("hmcl.properties")

HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameLauncher.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121
import org.jackhuang.hmcl.auth.AuthInfo;
2222
import org.jackhuang.hmcl.launch.DefaultLauncher;
2323
import org.jackhuang.hmcl.launch.ProcessListener;
24+
import org.jackhuang.hmcl.util.NativePatcher;
2425
import org.jackhuang.hmcl.util.i18n.LocaleUtils;
2526
import org.jackhuang.hmcl.util.io.FileUtils;
27+
import org.jackhuang.hmcl.util.io.JarUtils;
28+
import org.jackhuang.hmcl.util.platform.CommandBuilder;
2629
import org.jackhuang.hmcl.util.platform.ManagedProcess;
2730
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
2831

2932
import java.io.IOException;
33+
import java.io.InputStream;
3034
import java.nio.file.*;
3135
import java.util.*;
3236
import java.util.stream.Stream;
@@ -150,4 +154,60 @@ public void makeLaunchScript(Path scriptFile) throws IOException {
150154
generateOptionsTxt();
151155
super.makeLaunchScript(scriptFile);
152156
}
157+
158+
@Override
159+
protected void appendJvmArgs(CommandBuilder result) {
160+
super.appendJvmArgs(result);
161+
162+
if (config().getAllowAutoAgent()
163+
&& !options.isNoGeneratedJVMArgs()
164+
&& !options.isNoGeneratedOptimizingJVMArgs()
165+
&& NativePatcher.needPatchMemoryUtil(version, options.getJava().getParsedVersion())) {
166+
LOG.info("Attempting to patch game with lwjgl-unsafe-agent");
167+
try {
168+
result.add("-javaagent:" + extractLwjglUnsafeAgent());
169+
} catch (Exception e) {
170+
LOG.warning("Failed to extract lwjgl-unsafe-agent", e);
171+
}
172+
}
173+
}
174+
175+
private Path extractLwjglUnsafeAgent() throws IOException {
176+
String agentVersion = JarUtils.getAttribute("hmcl.lwjgl-unsafe-agent.version", null);
177+
if (agentVersion == null) {
178+
throw new IOException("Missing hmcl.lwjgl-unsafe-agent.version attribute");
179+
}
180+
181+
Library library = new Library(new Artifact("org.glavo", "lwjgl-unsafe-agent", agentVersion));
182+
String fileName = library.getArtifact().getFileName();
183+
184+
Path agentPath = repository.getLibraryFile(version, library).toAbsolutePath().normalize();
185+
if (agentPath.toString().contains("=")) {
186+
throw new IOException("Invalid library path: " + agentPath);
187+
}
188+
189+
byte[] bytes;
190+
try (InputStream input = DefaultLauncher.class.getResourceAsStream("/assets/" + fileName)) {
191+
if (input == null) {
192+
throw new IOException("/assets/" + fileName + " not found");
193+
}
194+
195+
bytes = input.readAllBytes();
196+
}
197+
198+
if (Files.isRegularFile(agentPath)) {
199+
try {
200+
if (Files.size(agentPath) == bytes.length) {
201+
return agentPath;
202+
}
203+
} catch (IOException e) {
204+
LOG.warning("Failed to check size of " + agentPath, e);
205+
}
206+
}
207+
208+
Files.createDirectories(agentPath.getParent());
209+
FileUtils.saveSafely(agentPath, output -> output.write(bytes));
210+
return agentPath;
211+
}
212+
153213
}

HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565

6666
import static javafx.application.Platform.runLater;
6767
import static javafx.application.Platform.setImplicitExit;
68+
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
6869
import static org.jackhuang.hmcl.ui.FXUtils.runInFX;
6970
import static org.jackhuang.hmcl.util.DataSizeUnit.MEGABYTES;
7071
import static org.jackhuang.hmcl.util.Lang.resolveException;
@@ -75,6 +76,8 @@
7576

7677
public final class LauncherHelper {
7778

79+
private static final String LWJGL_3_4_1_TIP = "lwjgl3.4.1-ffm";
80+
7881
private final Profile profile;
7982
private Account account;
8083
private final String selectedVersion;
@@ -197,6 +200,28 @@ private void launch0() {
197200
);
198201
}).withStage("launch.state.dependencies")
199202
.thenComposeAsync(() -> gameVersion.map(s -> new GameVerificationFixTask(dependencyManager, s, version.get())).orElse(null))
203+
.thenComposeAsync(() -> {
204+
if (config().getAllowAutoAgent()
205+
|| setting.isNoJVMArgs()
206+
|| setting.isNoOptimizingJVMArgs()
207+
|| Boolean.TRUE.equals(config().getShownTips().get(LWJGL_3_4_1_TIP))
208+
|| !NativePatcher.needPatchMemoryUtil(version.get(), javaVersionRef.get().getParsedVersion())) {
209+
return Task.completed(null);
210+
} else {
211+
CompletableFuture<Void> future = new CompletableFuture<>();
212+
runInFX(() -> {
213+
Controllers.confirm(i18n("launch.advice.lwjgl_3_4_1"), i18n("launch.advice.lwjgl_3_4_1.title"), MessageType.QUESTION, () -> {
214+
config().getShownTips().put(LWJGL_3_4_1_TIP, true);
215+
config().setAllowAutoAgent(true);
216+
future.complete(null);
217+
}, () -> {
218+
config().getShownTips().put(LWJGL_3_4_1_TIP, true);
219+
future.complete(null);
220+
});
221+
});
222+
return Task.fromCompletableFuture(future);
223+
}
224+
})
200225
.thenComposeAsync(() -> logIn(account).withStage("launch.state.logging_in"))
201226
.thenComposeAsync(authInfo -> Task.supplyAsync(() -> {
202227
LaunchOptions.Builder launchOptionsBuilder = repository.getLaunchOptions(

HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,21 @@ public void setDisableAutoGameOptions(boolean disableAutoGameOptions) {
689689
this.disableAutoGameOptions.set(disableAutoGameOptions);
690690
}
691691

692+
@SerializedName("allowAutoAgent")
693+
private final BooleanProperty allowAutoAgent = new SimpleBooleanProperty(false);
694+
695+
public BooleanProperty allowAutoAgentProperty() {
696+
return allowAutoAgent;
697+
}
698+
699+
public boolean getAllowAutoAgent() {
700+
return allowAutoAgent.get();
701+
}
702+
703+
public void setAllowAutoAgent(boolean allowAutoAgent) {
704+
this.allowAutoAgent.set(allowAutoAgent);
705+
}
706+
692707
// Accounts
693708

694709
@SerializedName("authlibInjectorServers")

HMCL/src/main/java/org/jackhuang/hmcl/ui/main/SettingsPage.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,15 @@ else if (locale.isSameLanguage(currentLocale))
288288
settingsPane.getContent().add(disableAutoGameOptionsPane);
289289
}
290290

291+
{
292+
LineToggleButton allowAutoAgentPane = new LineToggleButton();
293+
allowAutoAgentPane.setTitle(i18n("settings.launcher.allow_auto_agent"));
294+
allowAutoAgentPane.setSubtitle(i18n("settings.launcher.allow_auto_agent.subtitle"));
295+
allowAutoAgentPane.selectedProperty().bindBidirectional(config().allowAutoAgentProperty());
296+
297+
settingsPane.getContent().add(allowAutoAgentPane);
298+
}
299+
291300
{
292301
BorderPane debugPane = new BorderPane();
293302

HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ private static Map<String, Library> getNatives(Platform platform) {
6363
});
6464
}
6565

66+
// https://github.com/LWJGL/lwjgl3/issues/1111
67+
public static boolean needPatchMemoryUtil(Version version, int javaVersion) {
68+
return javaVersion >= 25 && javaVersion <= 26 && version.getLibraries().stream().anyMatch(library ->
69+
"org.lwjgl".equals(library.getGroupId())
70+
&& "lwjgl".equals(library.getArtifactId())
71+
&& "3.4.1".equals(library.getVersion())
72+
&& library.getClassifier() == null
73+
);
74+
}
75+
6676
public static Version patchNative(DefaultGameRepository repository,
6777
Version version, String gameVersion,
6878
JavaRuntime javaVersion,

HMCL/src/main/resources/assets/lang/I18N.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,8 @@ launch.advice.forge37_0_60=Forge versions prior to 37.0.60 are not compatible wi
808808
launch.advice.java8_1_13=Minecraft 1.13 and later can only be run on Java 8 or later. Please use Java 8 or later versions.
809809
launch.advice.java8_51_1_13=Minecraft 1.13 may crash on Java 8 versions prior to 1.8.0_51. Please install the latest Java 8 version.
810810
launch.advice.java9=You cannot launch Minecraft 1.12 or earlier with Java 9 or later. Please use Java 8 instead.
811+
launch.advice.lwjgl_3_4_1=The current game version has a known issue that may cause stuttering in the game.\nHMCL can automatically optimize the game performance (using a Java Agent).\nDo you allow HMCL to automatically optimize the game?\nIf you encounter issues, you can disable the "Allow HMCL to modify the game" option in "Settings → General".
812+
launch.advice.lwjgl_3_4_1.title=Allow HMCL to automatically optimize the game?
811813
launch.advice.modded_java=Some mods may not be compatible with newer Java versions. It is recommended to use Java %1$s to launch Minecraft %2$s.
812814
launch.advice.modlauncher8=The Forge version you are using is not compatible with the current Java version. Please try updating Forge.
813815
launch.advice.newer_java=You are using an older Java version to launch the game. It is recommended to update to Java 8, otherwise some mods may cause the game to crash.
@@ -1416,6 +1418,8 @@ settings.game.working_directory.hint=Enable the "Isolated" option in "Working Di
14161418
settings.icon=Icon
14171419

14181420
settings.launcher=Launcher Settings
1421+
settings.launcher.allow_auto_agent=Allow HMCL to modify the game
1422+
settings.launcher.allow_auto_agent.subtitle=Allow HMCL to modify the game by attaching a Java Agent to improve the gaming experience.
14191423
settings.launcher.appearance=Appearance
14201424
settings.launcher.brightness=Theme Mode
14211425
settings.launcher.brightness.auto=Follow System Settings

HMCL/src/main/resources/assets/lang/I18N_lzh.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,8 @@ launch.advice.forge37_0_60=鍛三七點〇點五九與舊者弗兼爪哇十七
579579
launch.advice.java8_1_13=礦藝一點一三以晉但行於爪哇八與晉者。宜行以爪哇八與晉者。
580580
launch.advice.java8_51_1_13=爪哇之前一點八點〇之五一者或崩礦藝一點一三。宜新爪哇於一點八點〇之五一以晉,然後復啟。
581581
launch.advice.java9=礦藝之前一點一三而有改囊者弗見兼於爪哇九與晉者。宜啟以爪哇八。
582+
launch.advice.lwjgl_3_4_1=戲之是版有已知之謬,行戲之時或致頓滯。\nHMCL 可假爪哇 Agent 自優其戲,以弭是患。君其許 HMCL 自優其戲乎?\n誠有謬,可至於「置設 → 貫用」,廢「許改戲」之項。
583+
launch.advice.lwjgl_3_4_1.title=君其許 HMCL 自優其戲乎?
582584
launch.advice.modded_java=改囊或弗見兼於爪哇之新者。宜以爪哇 %s 啟礦藝 %s。
583585
launch.advice.modlauncher8=鍛版之所行弗兼於爪哇之所行。宜新鍛。
584586
launch.advice.newer_java=余識君之啟戲以舊爪哇也,蓋改囊或是以崩戲也。宜新爪哇於八,然後復啟。
@@ -1126,6 +1128,8 @@ settings.game.working_directory.hint=夫于「運徑」擇「例獨」者,可
11261128
settings.icon=戲徵
11271129

11281130
settings.launcher=啟者置設
1131+
settings.launcher.allow_auto_agent=許改戲
1132+
settings.launcher.allow_auto_agent.subtitle=許 HMCL 假附加爪哇 Agent 以改戲,而優其戲感。
11291133
settings.launcher.appearance=外觀
11301134
settings.launcher.common_path.tooltip=啟者置戲資與府庫於是。倘戲有成案,則弗取公庫之案。
11311135
settings.launcher.debug=勘誤

HMCL/src/main/resources/assets/lang/I18N_zh.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,8 @@ launch.advice.forge37_0_60=Forge 37.0.59 及更低版本與 Java 17 不相容。
612612
launch.advice.java8_1_13=Minecraft 1.13 及更高版本僅支援 Java 8 或更高版本。請使用 Java 8 或最新版本。
613613
launch.advice.java8_51_1_13=低於 1.8.0_51 的 Java 版本可能會導致 Minecraft 1.13 崩潰。建議你到 https://java.com 安裝最新版的 Java 8。
614614
launch.advice.java9=低於 (包含) 1.13 的有安裝模組的 Minecraft 版本不支援 Java 9 或更高版本。請使用 Java 8。
615+
launch.advice.lwjgl_3_4_1=當前遊戲版本存在已知問題,可能在遊戲中發生卡頓。\nHMCL 可以 (透過 Java Agent) 自動最佳化遊戲解決此問題。是否允許 HMCL 自動最佳化遊戲?\n如遇到問題,你可以在「設定 → 一般」中關閉「允許 HMCL 修改遊戲」選項。
616+
launch.advice.lwjgl_3_4_1.title=是否允許 HMCL 自動最佳化遊戲?
615617
launch.advice.modded_java=部分模組可能與高版本 Java 不相容。建議使用 Java %s 啟動 Minecraft %s。
616618
launch.advice.modlauncher8=你所使用的 Forge 版本與目前使用的 Java 不相容。請更新 Forge。
617619
launch.advice.newer_java=偵測到你正在使用舊版本 Java 啟動遊戲,這可能導致部分模組引發遊戲崩潰。建議更新至 Java 8 後再次啟動。
@@ -1201,6 +1203,8 @@ settings.game.working_directory.hint=在「執行路徑」選項中選取「各
12011203
settings.icon=遊戲圖示
12021204

12031205
settings.launcher=啟動器設定
1206+
settings.launcher.allow_auto_agent=允許 HMCL 修改遊戲
1207+
settings.launcher.allow_auto_agent.subtitle=允許 HMCL 透過附加 Java Agent 修改遊戲以改善遊戲體驗。
12041208
settings.launcher.appearance=外觀
12051209
settings.launcher.brightness=主題模式
12061210
settings.launcher.brightness.auto=跟隨系統設定

HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,8 @@ launch.advice.forge37_0_60=Forge 37.0.59 及更低版本与 Java 17 不兼容。
617617
launch.advice.java8_1_13=Minecraft 1.13 及更高版本只能在 Java 8 或更高版本上运行。请使用 Java 8 或最新版本。
618618
launch.advice.java8_51_1_13=低于 1.8.0_51 的 Java 版本可能会导致 Minecraft 1.13 崩溃。建议更新 Java 至 1.8.0_51 或更高版本后再次启动。
619619
launch.advice.java9=低于 1.13 的有安装模组的 Minecraft 版本与 Java 9 或更高版本不兼容。请使用 Java 8。
620+
launch.advice.lwjgl_3_4_1=当前游戏版本存在已知问题,可能在游戏中发生卡顿。\nHMCL 可以 (通过 Java Agent) 自动优化游戏解决此问题。是否允许 HMCL 自动优化游戏?\n如遇到问题,你可以在“设置 → 通用”中关闭“允许 HMCL 修改游戏”选项。
621+
launch.advice.lwjgl_3_4_1.title=是否允许 HMCL 自动优化游戏?
620622
launch.advice.modded_java=部分模组可能与高版本 Java 不兼容。建议使用 Java %s 启动 Minecraft %s。
621623
launch.advice.modlauncher8=你所使用的 Forge 版本与当前使用的 Java 不兼容。请更新 Forge。
622624
launch.advice.newer_java=检测到你正在使用旧版本 Java 启动游戏,这可能导致部分模组引发游戏崩溃。建议更新至 Java 8 后再次启动。
@@ -1206,6 +1208,8 @@ settings.game.working_directory.hint=在“版本隔离”中选择“各实例
12061208
settings.icon=游戏图标
12071209

12081210
settings.launcher=启动器设置
1211+
settings.launcher.allow_auto_agent=允许 HMCL 修改游戏
1212+
settings.launcher.allow_auto_agent.subtitle=允许 HMCL 通过附加 Java Agent 修改游戏以改善游戏体验。
12091213
settings.launcher.appearance=外观
12101214
settings.launcher.brightness=主题模式
12111215
settings.launcher.brightness.auto=跟随系统设置

0 commit comments

Comments
 (0)