Compare commits

...
38 Commits
Author SHA1 Message Date
huanghongxun 295499c17b fix: missing translations. 2021-12-18 16:55:33 +08:00
Glavo 043d1c88c8 Fix #1096: Feedback item cannot be clicked 2021-12-18 16:49:42 +08:00
bling-yshs 55e9b93b1c fix curse category 2021-12-18 16:49:23 +08:00
Glavo aa5e3e9697 Fix #1148: Specify console encoding explicitly 2021-12-18 15:59:24 +08:00
Glavo fbf7ff6ebd Lifting GB2312 to GB18030 2021-12-18 15:59:24 +08:00
Glavo 3a5e8a827c update README 2021-12-18 15:59:24 +08:00
Glavo 6176dfaaf7 Lifting encoding to be compatible with more Unicode characters 2021-12-18 15:59:24 +08:00
Glavo 76c361e9bc Fix #1201: Download version jar if the jar is empty 2021-12-18 15:58:33 +08:00
Glavo ccb8981634 Remove invalid Appender 2021-12-18 15:47:41 +08:00
Glavo 99b55d03c1 Allows user to enable message lookup 2021-12-18 15:47:41 +08:00
Glavo 651fa6e2d1 fix #663: Incorrect judgment of WebView loading status 2021-12-18 15:46:24 +08:00
Glavo 4b258da0a0 Fix code format 2021-12-18 15:45:35 +08:00
Glavo 9a66605616 Disable JndiLookup using log4j-patch 2021-12-18 15:45:35 +08:00
Glavo da13895b72 fix: InputStream not closed 2021-12-18 15:45:35 +08:00
Glavo c657a1d2cc Introducing log4j-patch 2021-12-18 15:45:35 +08:00
Glavo eac8896f3e fix NPE in MultiplayerManager 2021-12-18 15:39:57 +08:00
Gamom 113f4f948b Update I18N_zh_CN.properties
翻译问题
解决 https://github.com/huanghongxun/HMCL/issues/1203#issuecomment-997155654
2021-12-18 15:39:26 +08:00
huanghongxun 1b8dc1f75e fix(mcbbs): NPE when fileApi is not empty. Closes #1197. 2021-12-18 01:40:25 +08:00
huanghongxun 001236e8e6 feat(download): refresh button 2021-12-18 01:40:25 +08:00
Glavo 8025b8e24b Support offline installation of OptiFine 1.17.1 H1 and later 2021-12-18 01:26:03 +08:00
Glavo e48bf012a6 Safely save config 2021-12-18 01:21:45 +08:00
Glavo 94e32694b2 fix: Game should use global settings by default 2021-12-12 16:51:01 +08:00
Glavo cbcf033b15 Delete unnecessary properties 2021-12-11 23:52:03 +08:00
Glavo 7da2671ec5 Use custom log4j2.xml 2021-12-11 23:52:03 +08:00
Glavo b6ea4c7003 Fix the problem that you cannot continue after canceling the prompt 2021-12-11 23:52:03 +08:00
Glavo 49378061bf Always issue security warning 2021-12-11 23:52:03 +08:00
Glavo 38013a9298 Issue security warning for CVE-2021-44228 2021-12-11 23:52:03 +08:00
Glavo 5ffbd16800 Prohibit JNDI remote invoke 2021-12-11 23:52:03 +08:00
Glavo bcb29ef792 Update GitHub workflow configuration 2021-12-11 23:50:37 +08:00
Glavo d20adb20d3 fix: Logs should be encoded in UTF-8 2021-12-11 23:49:29 +08:00
Kylepoops 4f28cdd58f Fix CVE-2021-44228 again
https://github.com/apache/logging-log4j2/pull/614
2021-12-11 23:49:09 +08:00
huanghongxun 4b6b5c2f0e fix(game): fix remote code execution from log4j. 2021-12-10 14:22:16 +08:00
Glavo c31269a358 Disable message pattern lookup by default 2021-12-10 14:19:32 +08:00
Glavo 55092326a4 Use StringWriter instead of ByteArrayOutputStream 2021-12-10 14:18:08 +08:00
Glavo 10dbcf63b7 Adjust the default capability to reduce memory allocation 2021-12-10 14:18:08 +08:00
Glavo 32fad77104 Optimize logging performance 2021-12-10 14:18:08 +08:00
SettingDust 05d2fbbbb9 fix(core): zipper lose the file attributes 2021-12-10 14:17:03 +08:00
ttsdzb 916733a1e8 根据最近的变化对README下载部分稍作修改 2021-12-10 14:16:50 +08:00
43 changed files with 590 additions and 147 deletions

No files matched your search

+9 -8
View File
@@ -1,16 +1,14 @@
name: Check Update
on:
push:
branches:
- javafx
workflow_dispatch:
schedule:
- cron: '30 * * * *'
jobs:
check-update:
if: ${{ github.repository_owner == 'huanghongxun' }}
runs-on: ubuntu-latest
env:
HMCL_BASE_VERSION: '3.4'
steps:
- uses: actions/checkout@v2
- name: Fetch tags
@@ -20,7 +18,11 @@ jobs:
- name: Fetch last version
run: |
wget -O ci.json https://ci.huangyuhui.net/job/HMCL/lastSuccessfulBuild/api/json
export HMCL_EXE_FILE_NAME=`cat ci.json | jq -M -r '.artifacts[] | select(.fileName | endswith(".exe")) | .fileName'`
if [ -z `echo $HMCL_EXE_FILE_NAME | grep "^HMCL-[0-9]\.[0-9]\.[0-9]\{3\}\.exe\$"` ]; then exit 1; fi
export HMCL_BASE_VERSION=${HMCL_EXE_FILE_NAME:5:3}
export HMCL_BUILD_NUMBER=`cat ci.json | jq -M -r '.id'`
export HMCL_VERSION="$HMCL_BASE_VERSION.$HMCL_BUILD_NUMBER"
export HMCL_COMMIT_SHA=`cat ci.json | jq -M -r '.actions[] | select(._class == "hudson.plugins.git.util.BuildData") | .lastBuiltRevision.SHA1'`
@@ -28,6 +30,7 @@ jobs:
if [ "${#HMCL_BUILD_NUMBER}" != 3 ]; then exit 1; fi
if [ "${#HMCL_COMMIT_SHA}" != 40 ]; then exit 1; fi
echo "HMCL_BASE_VERSION=$HMCL_BASE_VERSION" >> $GITHUB_ENV
echo "HMCL_BUILD_NUMBER=$HMCL_BUILD_NUMBER" >> $GITHUB_ENV
echo "HMCL_VERSION=$HMCL_VERSION" >> $GITHUB_ENV
echo "HMCL_COMMIT_SHA=$HMCL_COMMIT_SHA" >> $GITHUB_ENV
@@ -63,5 +66,3 @@ jobs:
target_commitish: ${{ env.HMCL_COMMIT_SHA }}
name: ${{ env.HMCL_TAG_NAME }}
tag_name: ${{ env.HMCL_TAG_NAME }}
-44
View File
@@ -1,44 +0,0 @@
name: Create Release
on:
push:
tags:
- "v*.*.*"
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Check and set HMCL version
run: |
export HMCL_VERSION=`echo ${{ github.ref }} | awk '{print substr($0,12)}'`
echo "HMCL_VERSION=$HMCL_VERSION" >> $GITHUB_ENV
if [ -z `echo $HMCL_VERSION | grep "^[0-9]\.[0-9]\.[0-9]\{3\}\$"` ]; then
exit 1
fi
echo "HMCL_BUILD_NUMBER=`echo $HMCL_VERSION | awk '{print substr($0,5)}'`" >> $GITHUB_ENV
- name: Download artifacts
run: |
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.exe"
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.exe.sha1"
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.jar"
wget "$DOWNLOAD_BASE_URL/HMCL-$HMCL_VERSION.jar.sha1"
env:
DOWNLOAD_BASE_URL: https://ci.huangyuhui.net/job/HMCL/${{ env.HMCL_BUILD_NUMBER }}/artifact/HMCL/build/libs
- name: Generate release note
run: |
echo "HMCL v$HMCL_VERSION" >> RELEASE_NOTE
echo "" >> RELEASE_NOTE
echo "The full changelogs can be found on the website: https://hmcl.huangyuhui.net/changelog/dev.html" >> RELEASE_NOTE
echo "Notice: changelogs are written in Chinese." >> RELEASE_NOTE
- name: Create release
uses: softprops/action-gh-release@v1
with:
body_path: RELEASE_NOTE
files: |
HMCL-${{ env.HMCL_VERSION }}.exe
HMCL-${{ env.HMCL_VERSION }}.exe.sha1
HMCL-${{ env.HMCL_VERSION }}.jar
HMCL-${{ env.HMCL_VERSION }}.jar.sha1
+6 -3
View File
@@ -17,20 +17,23 @@ NVIDIA
/build/
/HMCL/build/
/HMCLCore/build/
/HMCLTransformerDiscoveryService/build/
/minecraft/libraries/HMCLTransformerDiscoveryService/build/
/minecraft/libraries/log4j-patch/build/
# idea
.idea
/out/
/HMCL/out/
/HMCLCore/out/
/HMCLTransformerDiscoveryService/out/
/minecraft/libraries/HMCLTransformerDiscoveryService/out/
/minecraft/libraries/log4j-patch/out/
# eclipse
/bin/
/HMCL/bin/
/HMCLCore/bin/
/HMCLTransformerDiscoveryService/bin/
/minecraft/libraries/HMCLTransformerDiscoveryService/bin/
/minecraft/libraries/log4j-patch/bin/
.classpath
.project
.settings
-1
View File
@@ -129,7 +129,6 @@ shadowJar {
exclude(dependency('com.google.code.gson:.*:.*'))
exclude(dependency('com.github.steveice10:.*:.*'))
exclude(dependency('libs:JFoenix:.*'))
exclude(project(":HMCLTransformerDiscoveryService"))
}
manifest {
@@ -494,7 +494,7 @@ public final class LauncherHelper {
// Cannot allocate too much memory exceeding free space.
if (!suggested && OperatingSystem.TOTAL_MEMORY > 0 && OperatingSystem.TOTAL_MEMORY < setting.getMaxMemory()) {
Controllers.confirm(i18n("launch.advice.not_enough_space", OperatingSystem.TOTAL_MEMORY), i18n("message.error"), continueAction, null);
Controllers.confirm(i18n("launch.advice.not_enough_space", OperatingSystem.TOTAL_MEMORY), i18n("message.error"), continueAction, breakAction);
suggested = true;
}
@@ -506,7 +506,7 @@ public final class LauncherHelper {
VersionNumber.VERSION_COMPARATOR.compare(it.getVersion(), "1.12.2-14.23.5.2773") < 0);
boolean hasLiteLoader = version.getLibraries().stream().anyMatch(it -> it.is("com.mumfrey", "liteloader"));
if (hasForge2760 && hasLiteLoader && gameVersion.compareTo(VersionNumber.asVersion("1.12.2")) == 0) {
Controllers.confirm(i18n("launch.advice.forge2760_liteloader"), i18n("message.error"), continueAction, null);
Controllers.confirm(i18n("launch.advice.forge2760_liteloader"), i18n("message.error"), continueAction, breakAction);
suggested = true;
}
}
@@ -518,12 +518,12 @@ public final class LauncherHelper {
VersionNumber.VERSION_COMPARATOR.compare("1.14.4-28.2.2", it.getVersion()) <= 0);
boolean hasOptiFine = version.getLibraries().stream().anyMatch(it -> it.is("optifine", "OptiFine"));
if (hasForge28_2_2 && hasOptiFine && gameVersion.compareTo(VersionNumber.asVersion("1.14.4")) == 0) {
Controllers.confirm(i18n("launch.advice.forge28_2_2_optifine"), i18n("message.error"), continueAction, null);
Controllers.confirm(i18n("launch.advice.forge28_2_2_optifine"), i18n("message.error"), continueAction, breakAction);
suggested = true;
}
}
if (!suggested) {
if (!future.isDone()) {
future.complete(javaVersion);
}
@@ -30,7 +30,6 @@ import java.nio.file.*;
import java.util.Map;
import java.util.logging.Level;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.jackhuang.hmcl.util.Logging.LOG;
public final class ConfigHolder {
@@ -175,7 +174,7 @@ public final class ConfigHolder {
private static void writeToConfig(String content) throws IOException {
LOG.info("Saving config");
synchronized (configLocation) {
Files.write(configLocation, content.getBytes(UTF_8));
FileUtils.saveSafely(configLocation, content);
}
}
@@ -219,7 +218,7 @@ public final class ConfigHolder {
private static void writeToGlobalConfig(String content) throws IOException {
LOG.info("Saving global config");
synchronized (GLOBAL_CONFIG_PATH) {
Files.write(GLOBAL_CONFIG_PATH, content.getBytes(UTF_8));
FileUtils.saveSafely(GLOBAL_CONFIG_PATH, content);
}
}
@@ -60,7 +60,7 @@ public final class VersionSetting implements Cloneable {
this.global = global;
}
private final BooleanProperty usesGlobalProperty = new SimpleBooleanProperty(this, "usesGlobal", false);
private final BooleanProperty usesGlobalProperty = new SimpleBooleanProperty(this, "usesGlobal", true);
public BooleanProperty usesGlobalProperty() {
return usesGlobalProperty;
@@ -19,6 +19,7 @@ package org.jackhuang.hmcl.ui;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXDialogLayout;
import javafx.concurrent.Worker;
import javafx.scene.control.Label;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
@@ -41,23 +42,23 @@ public class UpgradeDialog extends JFXDialogLayout {
{
String url = CHANGELOG_URL + remoteVersion.getChannel().channelName + ".html";
WebView webView = new WebView();
webView.getEngine().setUserDataDirectory(Metadata.HMCL_DIRECTORY.toFile());
try {
WebView webView = new WebView();
webView.getEngine().setUserDataDirectory(Metadata.HMCL_DIRECTORY.toFile());
WebEngine engine = webView.getEngine();
engine.load(CHANGELOG_URL + remoteVersion.getChannel().channelName);
engine.load(url);
engine.getLoadWorker().stateProperty().addListener((observable, oldValue, newValue) -> {
String viewURL = engine.getLoadWorker().getMessage().trim();
if (!viewURL.startsWith(CHANGELOG_URL)) {
engine.getLoadWorker().cancel();
FXUtils.openLink(viewURL);
if (newValue == Worker.State.FAILED) {
LOG.warning("Failed to load update log, trying to open it in browser");
FXUtils.openLink(url);
setBody();
}
});
setBody(webView);
} catch (NoClassDefFoundError | UnsatisfiedLinkError e) {
LOG.log(Level.WARNING, "WebView is missing or initialization failed", e);
FXUtils.openLink(url);
}
setBody(webView);
}
{
@@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui.download;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.JFXSpinner;
@@ -39,7 +40,9 @@ import org.jackhuang.hmcl.download.forge.ForgeRemoteVersion;
import org.jackhuang.hmcl.download.game.GameRemoteVersion;
import org.jackhuang.hmcl.download.liteloader.LiteLoaderRemoteVersion;
import org.jackhuang.hmcl.download.optifine.OptiFineRemoteVersion;
import org.jackhuang.hmcl.setting.Theme;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.TransitionPane;
import org.jackhuang.hmcl.ui.construct.ComponentList;
@@ -58,6 +61,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.wrap;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@@ -84,6 +88,8 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
@FXML
private JFXCheckBox chkOld;
@FXML
private JFXButton btnRefresh;
@FXML
private HBox checkPane;
@FXML
private ComponentList centrePane;
@@ -114,6 +120,8 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
chkSnapshot.selectedProperty().addListener(listener);
chkOld.selectedProperty().addListener(listener);
btnRefresh.setGraphic(wrap(SVG.refresh(Theme.blackFillBinding(), -1, -1)));
list.setCellFactory(listView -> new ListCell<RemoteVersion>() {
IconedTwoLineListItem content = new IconedTwoLineListItem();
RipplerContainer ripplerContainer = new RipplerContainer(content);
@@ -128,7 +128,7 @@ public class FeedbackPage extends VBox implements PageAware {
container.setPickOnBounds(false);
container.setAlignment(Pos.CENTER_LEFT);
HBox.setHgrow(content, Priority.ALWAYS);
content.setMouseTransparent(true);
content.setMouseTransparent(false);
setSelectable();
likeButton.getStyleClass().add("toggle-icon4");
@@ -152,7 +152,7 @@ public class FeedbackPage extends VBox implements PageAware {
"#" + feedback.getId(),
i18n("feedback.state." + feedback.getState().name().toLowerCase(Locale.US)),
i18n("feedback.type." + feedback.getType().name().toLowerCase(Locale.US)));
container.setOnMouseClicked(e -> {
content.setOnMouseClicked(e -> {
getFeedback(feedback.getId())
.thenAcceptAsync(Schedulers.javafx(), f -> {
Controllers.dialog(new ViewFeedbackDialog(f));
@@ -400,7 +400,8 @@ public final class MultiplayerManager {
onExit.fireEvent(new CatoExitEvent(this, CatoExitEvent.EXIT_CODE_INTERRUPTED));
} finally {
try {
writer.close();
if (writer != null)
writer.close();
} catch (IOException e) {
LOG.log(Level.WARNING, "Failed to close cato stdin writer", e);
}
@@ -112,7 +112,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private final StringProperty selectedVersion = new SimpleStringProperty();
private final BooleanProperty navigateToSpecificSettings = new SimpleBooleanProperty(false);
private final BooleanProperty enableSpecificSettings = new SimpleBooleanProperty(true);
private final BooleanProperty enableSpecificSettings = new SimpleBooleanProperty(false);
private final IntegerProperty maxMemory = new SimpleIntegerProperty();
private final ObjectProperty<OperatingSystem.PhysicalMemoryStatus> memoryStatus = new SimpleObjectProperty<>(OperatingSystem.PhysicalMemoryStatus.INVALID);
private final BooleanProperty modpack = new SimpleBooleanProperty();
@@ -23,10 +23,26 @@
<JFXSpinner fx:id="spinner" styleClass="first-spinner" />
<StackPane fx:id="center" style="-fx-padding: 10;">
<ComponentList fx:id="centrePane" styleClass="no-padding">
<HBox fx:id="checkPane" spacing="10" style="-fx-padding: 10;">
<JFXCheckBox fx:id="chkRelease" text="%version.game.releases" selected="true" />
<JFXCheckBox fx:id="chkSnapshot" text="%version.game.snapshots" />
<JFXCheckBox fx:id="chkOld" text="%version.game.old" />
<HBox fx:id="checkPane" spacing="10">
<JFXCheckBox fx:id="chkRelease" text="%version.game.releases" selected="true">
<HBox.margin>
<Insets top="10" bottom="10" />
</HBox.margin>
</JFXCheckBox>
<JFXCheckBox fx:id="chkSnapshot" text="%version.game.snapshots">
<HBox.margin>
<Insets top="10" bottom="10" />
</HBox.margin>
</JFXCheckBox>
<JFXCheckBox fx:id="chkOld" text="%version.game.old">
<HBox.margin>
<Insets top="10" bottom="10" />
</HBox.margin>
</JFXCheckBox>
<HBox HBox.hgrow="ALWAYS" />
<JFXButton fx:id="btnRefresh" styleClass="jfx-tool-bar-button" onMouseClicked="#onRefresh" text="%button.refresh" />
</HBox>
<JFXListView fx:id="list" styleClass="jfx-list-view-float" VBox.vgrow="ALWAYS">
</JFXListView>
@@ -182,6 +182,11 @@ curse.category.4472=Tech
curse.category.4473=Magic
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5129=Vanilla+
curse.category.5189=Utility & QOL
curse.category.5190=QoL
curse.category.5191=Utility & QoL
curse.category.5192=FancyMenu
curse.category.423=Map and Information
curse.category.426=Addons
curse.category.434=Armor, Tools, and Weapons
@@ -223,7 +228,8 @@ curse.category.4558=Redstone
curse.category.4843=Automation
curse.category.4906=MCreator
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
# https://addons-ecs.forgesvc.net/api/v2/category/section/12
curse.category.5193=Data Packs
curse.category.399=Steampunk
curse.category.396=128x
curse.category.398=512x and Higher
@@ -182,6 +182,11 @@ curse.category.4472=科技
curse.category.4473=魔法
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5129=原版增強
curse.category.5189=實用與QOL
curse.category.5190=QoL
curse.category.5191=實用與QoL
curse.category.5192=夢幻菜單
curse.category.423=訊息展示
curse.category.426=模組擴展
curse.category.434=裝備武器
@@ -224,6 +229,7 @@ curse.category.4843=自動化
curse.category.4906=MCreator
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5193=封包
curse.category.399=蒸汽朋克
curse.category.396=128x
curse.category.398=512x 及更高
@@ -182,6 +182,11 @@ curse.category.4472=科技
curse.category.4473=魔法
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
curse.category.5129=原版增强
curse.category.5189=实用与QOL
curse.category.5190=QoL
curse.category.5191=实用与QoL
curse.category.5192=梦幻菜单
curse.category.423=信息展示
curse.category.426=模组扩展
curse.category.434=装备武器
@@ -223,7 +228,8 @@ curse.category.4558=红石
curse.category.4843=自动化
curse.category.4906=MCreator
# https://addons-ecs.forgesvc.net/api/v2/category/section/6
# https://addons-ecs.forgesvc.net/api/v2/category/section/12
curse.category.5193=数据包
curse.category.399=蒸汽朋克
curse.category.396=128x
curse.category.398=512x 及更高
+8
View File
@@ -17,3 +17,11 @@ dependencies {
api group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
compileOnlyApi group: 'org.jetbrains', name: 'annotations', version: '16.0.3'
}
tasks.processResources {
dependsOn ':log4j-patch:jar'
into('assets/game') {
from project(':log4j-patch').file("build/libs")
}
}
@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.game.Library;
import org.jackhuang.hmcl.game.Version;
import org.jackhuang.hmcl.task.Task;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -78,7 +79,8 @@ public class DefaultDependencyManager extends AbstractDependencyManager {
Version version = original.resolve(repository);
return Task.allOf(
Task.composeAsync(() -> {
if (!repository.getVersionJar(version).exists())
File versionJar = repository.getVersionJar(version);
if (!versionJar.exists() || versionJar.length() == 0)
return new GameDownloadTask(this, null, version);
else
return null;
@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -62,18 +63,57 @@ public class MaintainTask extends Task<Version> {
String mainClass = version.resolve(null).getMainClass();
if (mainClass != null && mainClass.equals(LibraryAnalyzer.LAUNCH_WRAPPER_MAIN)) {
return maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version), true), false);
version = maintainOptiFineLibrary(repository, maintainGameWithLaunchWrapper(unique(version), true), false);
} else if (mainClass != null && mainClass.equals(LibraryAnalyzer.MOD_LAUNCHER_MAIN)) {
// Forge 1.13 and OptiFine
return maintainOptiFineLibrary(repository, maintainGameWithCpwModLauncher(repository, unique(version)), true);
version = maintainOptiFineLibrary(repository, maintainGameWithCpwModLauncher(repository, unique(version)), true);
} else if (mainClass != null && mainClass.equals(LibraryAnalyzer.BOOTSTRAP_LAUNCHER_MAIN)) {
// Forge 1.17
return maintainGameWithCpwBoostrapLauncher(repository, unique(version));
version = maintainGameWithCpwBoostrapLauncher(repository, unique(version));
} else {
// Vanilla Minecraft does not need maintain
// Fabric does not need maintain, nothing compatible with fabric now.
return maintainOptiFineLibrary(repository, unique(version), false);
version = maintainOptiFineLibrary(repository, unique(version), false);
}
Library log4jPatch = null;
List<Library> libraries = version.getLibraries();
for (Library library : libraries) {
if (library.is("org.apache.logging.log4j", "log4j-core")) {
if (library.getVersion().startsWith("2.0-beta")) {
if ("2.0-beta9".equals(library.getVersion())) {
log4jPatch = new Library(new Artifact("org.glavo", "log4j-patch-beta9", "1.0"));
} else {
Logging.LOG.warning("Log4j " + library.getVersion() + " cannot be patched");
}
} else if (VersionNumber.VERSION_COMPARATOR.compare(library.getVersion(), "2.16") < 0) {
log4jPatch = new Library(new Artifact("org.glavo", "log4j-patch", "1.0"));
}
break;
}
}
if (log4jPatch != null) {
ArrayList<Library> patchedLibraries = new ArrayList<>(libraries.size() + 1);
patchedLibraries.add(log4jPatch);
patchedLibraries.addAll(libraries);
version = version.setLibraries(patchedLibraries);
Path log4jPatchPath = repository.getLibraryFile(version, log4jPatch).toPath();
String patchName = log4jPatch.getArtifactId() + "-" + log4jPatch.getVersion();
if (Files.notExists(log4jPatchPath)) {
try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/" + patchName + ".jar")) {
Files.createDirectories(log4jPatchPath.getParent());
Files.copy(input, log4jPatchPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Unable to unpack " + patchName, e);
}
}
Logging.LOG.info("Apply patch " + patchName + " to log4j");
}
return version;
}
public static Version maintainPreservingPatches(GameRepository repository, Version version) {
@@ -136,10 +176,9 @@ public class MaintainTask extends Task<Version> {
builder.addJvmArgument("-Dhmcl.transformer.candidates=${library_directory}/" + library.getPath());
if (!libraryExisting) builder.addLibrary(hmclTransformerDiscoveryService);
Path libraryPath = repository.getLibraryFile(version, hmclTransformerDiscoveryService).toPath();
try {
try (InputStream input = MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar")) {
Files.createDirectories(libraryPath.getParent());
Files.copy(MaintainTask.class.getResourceAsStream("/assets/game/HMCLTransformerDiscoveryService-1.0.jar"),
libraryPath, StandardCopyOption.REPLACE_EXISTING);
Files.copy(input, libraryPath, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Unable to unpack HMCLTransformerDiscoveryService", e);
}
@@ -229,6 +229,7 @@ public final class OptiFineInstallTask extends Task<Version> {
try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installer)) {
Path configClass = fs.getPath("Config.class");
if (!Files.exists(configClass)) configClass = fs.getPath("net/optifine/Config.class");
if (!Files.exists(configClass)) configClass = fs.getPath("notch/net/optifine/Config.class");
if (!Files.exists(configClass)) throw new IOException("Unrecognized installer");
ConstantPool pool = ConstantPoolScanner.parse(Files.readAllBytes(configClass), ConstantType.UTF8);
List<String> constants = new ArrayList<>();
@@ -31,12 +31,14 @@ import org.jackhuang.hmcl.util.Log4jLevel;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
import org.jackhuang.hmcl.util.io.Unzipper;
import org.jackhuang.hmcl.util.platform.CommandBuilder;
import org.jackhuang.hmcl.util.platform.JavaVersion;
import org.jackhuang.hmcl.util.platform.ManagedProcess;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.util.platform.Bits;
import org.jackhuang.hmcl.util.versioning.VersionNumber;
import java.io.*;
import java.net.InetSocketAddress;
@@ -48,8 +50,10 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Supplier;
import java.util.logging.Level;
import static org.jackhuang.hmcl.util.Lang.mapOf;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.Pair.pair;
/**
@@ -114,6 +118,19 @@ public class DefaultLauncher extends Launcher {
res.addAllWithoutParsing(options.getJavaArguments());
Charset encoding = OperatingSystem.NATIVE_CHARSET;
// After Java 17, file.encoding does not affect console encoding
if (options.getJava().getParsedVersion() <= JavaVersion.JAVA_17) {
try {
String fileEncoding = res.addDefault("-Dfile.encoding=", encoding.name());
if (fileEncoding != null)
encoding = Charset.forName(fileEncoding.substring("-Dfile.encoding=".length()));
} catch (Throwable ex) {
LOG.log(Level.WARNING, "Bad file encoding", ex);
}
}
// JVM Args
if (!options.isNoGeneratedJVMArgs()) {
appendJvmArgs(res);
@@ -180,6 +197,16 @@ public class DefaultLauncher extends Launcher {
res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true");
}
// Fix RCE vulnerability of log4j2
res.addDefault("-Djava.rmi.server.useCodebaseOnly=", "true");
res.addDefault("-Dcom.sun.jndi.rmi.object.trustURLCodebase=", "false");
res.addDefault("-Dcom.sun.jndi.cosnaming.object.trustURLCodebase=", "false");
String formatMsgNoLookups = res.addDefault("-Dlog4j2.formatMsgNoLookups=", "true");
if (!"-Dlog4j2.formatMsgNoLookups=false".equals(formatMsgNoLookups) && isUsingLog4j()) {
res.addDefault("-Dlog4j.configurationFile=", getLog4jConfigurationFile().getAbsolutePath());
}
Proxy proxy = options.getProxy();
if (proxy != null && StringUtils.isBlank(options.getProxyUser()) && StringUtils.isBlank(options.getProxyPass())) {
InetSocketAddress address = (InetSocketAddress) options.getProxy().address();
@@ -269,7 +296,7 @@ public class DefaultLauncher extends Launcher {
res.addAllWithoutParsing(Arguments.parseStringArguments(options.getGameArguments(), configuration));
res.removeIf(it -> getForbiddens().containsKey(it) && getForbiddens().get(it).get());
return new Command(res, tempNativeFolder);
return new Command(res, tempNativeFolder, encoding);
}
public Map<String, Boolean> getFeatures() {
@@ -333,6 +360,29 @@ public class DefaultLauncher extends Launcher {
}
}
private boolean isUsingLog4j() {
return VersionNumber.VERSION_COMPARATOR.compare(repository.getGameVersion(version).orElse("Unknown"), "1.7") >= 0;
}
public File getLog4jConfigurationFile() {
return new File(repository.getVersionRoot(version.getId()), "log4j2.xml");
}
public void extractLog4jConfigurationFile() throws IOException {
File targetFile = getLog4jConfigurationFile();
InputStream source;
if (VersionNumber.VERSION_COMPARATOR.compare(repository.getGameVersion(version).orElse("Unknown"), "1.12") < 0) {
source = DefaultLauncher.class.getResourceAsStream("/assets/game/log4j2-1.7.xml");
} else {
source = DefaultLauncher.class.getResourceAsStream("/assets/game/log4j2-1.12.xml");
}
try (InputStream input = source;
OutputStream output = new FileOutputStream(targetFile)) {
IOUtils.copyTo(input, output);
}
}
protected Map<String, String> getConfigurations() {
return mapOf(
// defined by Minecraft official launcher
@@ -400,6 +450,10 @@ public class DefaultLauncher extends Launcher {
decompressNatives(nativeFolder);
}
if (isUsingLog4j()) {
extractLog4jConfigurationFile();
}
File runDirectory = repository.getRunDirectory(version.getId());
if (StringUtils.isNotBlank(options.getPreLaunchCommand())) {
@@ -430,7 +484,7 @@ public class DefaultLauncher extends Launcher {
ManagedProcess p = new ManagedProcess(process, rawCommandLine, classpath);
if (listener != null)
startMonitors(p, listener, daemon);
startMonitors(p, listener, command.encoding, daemon);
return p;
}
@@ -474,6 +528,10 @@ public class DefaultLauncher extends Launcher {
decompressNatives(nativeFolder);
}
if (isUsingLog4j()) {
extractLog4jConfigurationFile();
}
String scriptExtension = FileUtils.getExtension(scriptFile);
boolean usePowerShell = "ps1".equals(scriptExtension);
@@ -591,17 +649,17 @@ public class DefaultLauncher extends Launcher {
throw new ExecutionPolicyLimitException();
}
private void startMonitors(ManagedProcess managedProcess, ProcessListener processListener, boolean isDaemon) {
private void startMonitors(ManagedProcess managedProcess, ProcessListener processListener, Charset encoding, boolean isDaemon) {
processListener.setProcess(managedProcess);
Thread stdout = Lang.thread(new StreamPump(managedProcess.getProcess().getInputStream(), it -> {
processListener.onLog(it, Optional.ofNullable(Log4jLevel.guessLevel(it)).orElse(Log4jLevel.INFO));
managedProcess.addLine(it);
}, OperatingSystem.NATIVE_CHARSET), "stdout-pump", isDaemon);
}, encoding), "stdout-pump", isDaemon);
managedProcess.addRelatedThread(stdout);
Thread stderr = Lang.thread(new StreamPump(managedProcess.getProcess().getErrorStream(), it -> {
processListener.onLog(it, Log4jLevel.ERROR);
managedProcess.addLine(it);
}, OperatingSystem.NATIVE_CHARSET), "stderr-pump", isDaemon);
}, encoding), "stderr-pump", isDaemon);
managedProcess.addRelatedThread(stderr);
managedProcess.addRelatedThread(Lang.thread(new ExitWaiter(managedProcess, Arrays.asList(stdout, stderr), processListener::onExit), "exit-waiter", isDaemon));
}
@@ -609,10 +667,12 @@ public class DefaultLauncher extends Launcher {
private static final class Command {
final CommandBuilder commandLine;
final Path tempNativeFolder;
final Charset encoding;
Command(CommandBuilder commandBuilder, Path tempNativeFolder) {
Command(CommandBuilder commandBuilder, Path tempNativeFolder, Charset encoding) {
this.commandLine = commandBuilder;
this.tempNativeFolder = tempNativeFolder;
this.encoding = encoding;
}
}
}
@@ -62,7 +62,6 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
private final String version;
private final File configurationFile;
private ModpackConfiguration<McbbsModpackManifest> configuration;
private GetTask dependent;
private McbbsModpackManifest manifest;
private final List<Task<?>> dependencies = new LinkedList<>();
@@ -108,11 +107,11 @@ public class McbbsModpackCompletionTask extends CompletableFutureTask<Void> {
}
})).thenComposeAsync(wrap(unused1 -> {
return executor.one(new GetTask(new URL(manifest.getFileApi() + "/manifest.json")));
})).thenComposeAsync(wrap(unused1 -> {
})).thenComposeAsync(wrap(remoteManifestJson -> {
McbbsModpackManifest remoteManifest;
// We needs to update modpack from online server.
try {
remoteManifest = JsonUtils.fromNonNullJson(dependent.getResult(), McbbsModpackManifest.class);
remoteManifest = JsonUtils.fromNonNullJson(remoteManifestJson, McbbsModpackManifest.class);
} catch (JsonParseException e) {
throw new IOException("Unable to parse server manifest.json from " + manifest.getFileApi(), e);
}
@@ -17,13 +17,14 @@
*/
package org.jackhuang.hmcl.util;
import org.jackhuang.hmcl.util.io.IOUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.text.MessageFormat;
import java.util.Date;
import java.util.logging.*;
@@ -35,7 +36,7 @@ public final class Logging {
}
public static final Logger LOG = Logger.getLogger("HMCL");
private static ByteArrayOutputStream storedLogs = new ByteArrayOutputStream();
private static final ByteArrayOutputStream storedLogs = new ByteArrayOutputStream(IOUtils.DEFAULT_BUFFER_SIZE);
public static void start(Path logFolder) {
LOG.setLevel(Level.ALL);
@@ -46,6 +47,7 @@ public final class Logging {
FileHandler fileHandler = new FileHandler(logFolder.resolve("hmcl.log").toAbsolutePath().toString());
fileHandler.setLevel(Level.FINEST);
fileHandler.setFormatter(DefaultFormatter.INSTANCE);
fileHandler.setEncoding("UTF-8");
LOG.addHandler(fileHandler);
} catch (IOException e) {
System.err.println("Unable to create hmcl.log, " + e.getMessage());
@@ -87,27 +89,29 @@ public final class Logging {
}
public static String getLogs() {
return storedLogs.toString();
try {
return storedLogs.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new InternalError(e);
}
}
private static final class DefaultFormatter extends Formatter {
static final DefaultFormatter INSTANCE = new DefaultFormatter();
private final SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
private static final MessageFormat format = new MessageFormat("[{0,date,HH:mm:ss}] [{1}.{2}/{3}] {4}\n");
@Override
public String format(LogRecord record) {
String date = format.format(new Date(record.getMillis()));
String log = String.format("[%s] [%s.%s/%s] %s%n",
date, record.getSourceClassName(), record.getSourceMethodName(),
record.getLevel().getName(), record.getMessage()
);
ByteArrayOutputStream builder = new ByteArrayOutputStream();
String log = format.format(new Object[]{
new Date(record.getMillis()),
record.getSourceClassName(), record.getSourceMethodName(), record.getLevel().getName(),
record.getMessage()
}, new StringBuffer(128), null).toString();
if (record.getThrown() != null)
try (PrintWriter writer = new PrintWriter(builder)) {
record.getThrown().printStackTrace(writer);
}
return log + builder.toString();
log += StringUtils.getStackTrace(record.getThrown());
return log;
}
}
@@ -19,9 +19,7 @@ package org.jackhuang.hmcl.util;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.io.*;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
@@ -37,13 +35,11 @@ public final class StringUtils {
}
public static String getStackTrace(Throwable throwable) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try {
throwable.printStackTrace(new PrintStream(stream, false, "UTF-8"));
return stream.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new InternalError(e);
StringWriter stringWriter = new StringWriter(512);
try (PrintWriter printWriter = new PrintWriter(stringWriter)) {
throwable.printStackTrace(printWriter);
}
return stringWriter.toString();
}
public static String getStackTrace(StackTraceElement[] elements) {
@@ -20,6 +20,7 @@ package org.jackhuang.hmcl.util.io;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.StringUtils;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -36,7 +37,6 @@ import java.util.function.Predicate;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
*
* @author huang
*/
public final class FileUtils {
@@ -425,4 +425,24 @@ public final class FileUtils {
return Optional.empty();
}
}
public static Path tmpSaveFile(Path file) {
return file.toAbsolutePath().resolveSibling("." + file.getFileName().toString() + ".tmp");
}
public static void saveSafely(Path file, String content) throws IOException {
Path tmpFile = tmpSaveFile(file);
try (BufferedWriter writer = Files.newBufferedWriter(tmpFile, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE)) {
writer.write(content);
}
try {
if (Files.exists(file) && Files.getAttribute(file, "dos:hidden") == Boolean.TRUE) {
Files.setAttribute(tmpFile, "dos:hidden", true);
}
} catch (Throwable ignored) {
}
Files.move(tmpFile, file, StandardCopyOption.REPLACE_EXISTING);
}
}
@@ -80,7 +80,7 @@ public final class Zipper implements Closeable {
if (filter != null && !filter.test(relativePath.replace('\\', '/'))) {
return FileVisitResult.SKIP_SUBTREE;
}
Files.copy(file, root.resolve(relativePath));
Files.copy(file, root.resolve(relativePath), StandardCopyOption.COPY_ATTRIBUTES);
return FileVisitResult.CONTINUE;
}
@@ -104,11 +104,11 @@ public final class Zipper implements Closeable {
}
public void putFile(Path file, String path) throws IOException {
Files.copy(file, fs.getPath(path));
Files.copy(file, fs.getPath(path), StandardCopyOption.COPY_ATTRIBUTES);
}
public void putStream(InputStream in, String path) throws IOException {
Files.copy(in, fs.getPath(path));
Files.copy(in, fs.getPath(path), StandardCopyOption.COPY_ATTRIBUTES);
}
public void putTextFile(String text, String path) throws IOException {
@@ -83,33 +83,33 @@ public final class CommandBuilder {
return this;
}
public CommandBuilder addDefault(String opt) {
public String addDefault(String opt) {
for (Item item : raw) {
if (item.arg.equals(opt)) {
return this;
return item.arg;
}
}
raw.add(new Item(opt, true));
return this;
return null;
}
public CommandBuilder addDefault(String opt, String value) {
public String addDefault(String opt, String value) {
for (Item item : raw) {
if (item.arg.startsWith(opt)) {
LOG.info("Default option '" + opt + value + "' is suppressed by '" + item.arg + "'");
return this;
return item.arg;
}
}
raw.add(new Item(opt + value, true));
return this;
return null;
}
public CommandBuilder addUnstableDefault(String opt, boolean value) {
public String addUnstableDefault(String opt, boolean value) {
for (Item item : raw) {
final Matcher matcher = UNSTABLE_BOOLEAN_OPTION_PATTERN.matcher(item.arg);
if (matcher.matches()) {
if (matcher.group("key").equals(opt)) {
return this;
return item.arg;
}
}
}
@@ -119,21 +119,21 @@ public final class CommandBuilder {
} else {
raw.add(new Item("-XX:-" + opt, true));
}
return this;
return null;
}
public CommandBuilder addUnstableDefault(String opt, String value) {
public String addUnstableDefault(String opt, String value) {
for (Item item : raw) {
final Matcher matcher = UNSTABLE_OPTION_PATTERN.matcher(item.arg);
if (matcher.matches()) {
if (matcher.group("key").equals(opt)) {
return this;
return item.arg;
}
}
}
raw.add(new Item("-XX:" + opt + "=" + value, true));
return this;
return null;
}
public boolean removeIf(Predicate<String> pred) {
@@ -112,6 +112,7 @@ public final class JavaVersion {
public static final int JAVA_8 = 8;
public static final int JAVA_9 = 9;
public static final int JAVA_16 = 16;
public static final int JAVA_17 = 17;
private static int parseVersion(String version) {
Matcher matcher = VERSION.matcher(version);
@@ -22,6 +22,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -112,15 +113,26 @@ public enum OperatingSystem {
private static final Pattern MEMINFO_PATTERN = Pattern.compile("^(?<key>.*?):\\s+(?<value>\\d+) kB?$");
static {
String nativeEncoding = System.getProperty("native.encoding", System.getProperty("sun.jnu.encoding"));
String nativeEncoding = System.getProperty("native.encoding");
String hmclNativeEncoding = System.getProperty("hmcl.native.encoding");
Charset nativeCharset = Charset.defaultCharset();
if (nativeEncoding != null) {
try {
nativeCharset = Charset.forName(nativeEncoding);
} catch (UnsupportedCharsetException e) {
e.printStackTrace();
try {
if (hmclNativeEncoding != null) {
nativeCharset = Charset.forName(hmclNativeEncoding);
} else {
if (nativeEncoding != null && !nativeEncoding.equalsIgnoreCase(nativeCharset.name())) {
nativeCharset = Charset.forName(nativeEncoding);
}
if (nativeCharset == StandardCharsets.UTF_8 || nativeCharset == StandardCharsets.US_ASCII) {
nativeCharset = StandardCharsets.UTF_8;
} else if ("GBK".equalsIgnoreCase(nativeCharset.name()) || "GB2312".equalsIgnoreCase(nativeCharset.name())) {
nativeCharset = Charset.forName("GB18030");
}
}
} catch (UnsupportedCharsetException e) {
e.printStackTrace();
}
NATIVE_CHARSET = nativeCharset;
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="SysOut" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg{nolookups}%n" />
</Console>
<Queue name="ServerGuiConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg{nolookups}%n" />
</Queue>
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg{nolookups}%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<OnStartupTriggeringPolicy />
</Policies>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="info">
<filters>
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
</filters>
<AppenderRef ref="SysOut"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="SysOut" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
</Console>
<Queue name="ServerGuiConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n" />
</Queue>
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n" />
<Policies>
<TimeBasedTriggeringPolicy />
<OnStartupTriggeringPolicy />
</Policies>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="info">
<filters>
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
<RegexFilter regex=".*\$\{[^}]*\}.*" onMatch="DENY" onMismatch="NEUTRAL"/>
</filters>
<AppenderRef ref="SysOut"/>
<AppenderRef ref="File"/>
</Root>
</Loggers>
</Configuration>
@@ -1 +0,0 @@
org.jackhuang.hmcl.HMCLTransformerDiscoveryService
+4 -1
View File
@@ -11,7 +11,9 @@ No plugin API is provided.
## Download
Download the latest version [from the official website](https://hmcl.huangyuhui.net/download)
Note: Github releases are outdated.
Note: The recent versions in Github release is the beta version, which contains extra testing functions compared to the release versions on the official website. However, they may be unstable and you're more likely to encounter bugs or unexpected problems.
If not necessary, it is recommended to download the ones from the official website.
## License
The software is distributed under [GPL v3](https://www.gnu.org/licenses/gpl-3.0.html) with additional terms.
@@ -56,5 +58,6 @@ Make sure you have Java installed with JavaFX 8 at least. Liberica full JDK 8 or
|`-Dhmcl.update_source.override=<url>`|Override the update source.|
|`-Dhmcl.authlibinjector.location=<path>`|Use specified authlib-injector (instead of downloading one).|
|`-Dhmcl.openjfx.repo=<maven repository url>`|Add custom maven repository for download OpenJFX.|
|`-Dhmcl.native.encoding=<encoding>`|Override the native encoding.|
|`-Dhmcl.microsoft.auth.id=<App ID>`|Override Microsoft OAuth App ID.|
|`-Dhmcl.microsoft.auth.secret=<App Secret>`|Override Microsoft OAuth App secret.|
@@ -4,6 +4,11 @@ dependencies {
compileOnly project.files("lib/modlauncher-4.1.0.jar")
}
compileJava {
sourceCompatibility = 8
targetCompatibility = 8
}
jar {
manifest {
attributes 'Created-By': 'Copyright(c) 2013-2020 huangyuhui.',
+13
View File
@@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <[email protected]>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.
@@ -0,0 +1,6 @@
# Log4j Patch
Copy from [Glavo/log4j-patch](https://github.com/Glavo/log4j-patch/).
`org.apache.logging.log4j.core.lookup.JndiLookup` is licensed under the WTFPL 2.0 license,
and `org.apache.logging.log4j.core.lookup.Interpolator` is licensed under the Apache 2.0 license.
@@ -0,0 +1,46 @@
version '1.0'
dependencies {
compileOnly group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.0-beta9'
}
tasks.compileJava {
sourceCompatibility = 8
targetCompatibility = 8
doLast {
FileTree tree = fileTree('build/classes/java/main')
tree.include '**/*.class'
tree.each {
RandomAccessFile rf = new RandomAccessFile(it, "rw")
rf.seek(7) // major version
rf.write(50) // java 6
rf.close()
}
}
}
task patchJar(type: Jar) {
dependsOn(tasks.compileJava)
baseName = 'log4j-patch'
from(sourceSets.main.output) {
include('**/JndiLookup.class')
}
}
task patchBeta9Jar(type: Jar) {
dependsOn(tasks.compileJava)
baseName = 'log4j-patch-beta9'
from(sourceSets.main.output) {
include '**/Interpolator.class'
}
}
tasks.jar {
enabled = false
dependsOn patchJar, patchBeta9Jar
}
@@ -0,0 +1,155 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache license, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the license for the specific language governing permissions and
* limitations under the license.
*/
package org.apache.logging.log4j.core.lookup;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.config.plugins.PluginManager;
import org.apache.logging.log4j.core.config.plugins.PluginType;
import org.apache.logging.log4j.status.StatusLogger;
import java.util.HashMap;
import java.util.Map;
/**
* The Interpolator is a StrLookup that acts as a proxy for all the other StrLookups.
*/
public class Interpolator implements StrLookup {
private static final Logger LOGGER = StatusLogger.getLogger();
/** Constant for the prefix separator. */
private static final char PREFIX_SEPARATOR = ':';
private final Map<String, StrLookup> lookups = new HashMap<String, StrLookup>();
private final StrLookup defaultLookup;
@SuppressWarnings("deprecation")
public Interpolator(final StrLookup defaultLookup) {
this.defaultLookup = defaultLookup == null ? new MapLookup(new HashMap<String, String>()) : defaultLookup;
final PluginManager manager = new PluginManager("Lookup");
manager.collectPlugins();
final Map<String, PluginType<?>> plugins = manager.getPlugins();
for (final Map.Entry<String, PluginType<?>> entry : plugins.entrySet()) {
@SuppressWarnings("unchecked")
final Class<? extends StrLookup> clazz = (Class<? extends StrLookup>) entry.getValue().getPluginClass();
try {
lookups.put(entry.getKey(), clazz.newInstance());
} catch (final Exception ex) {
LOGGER.error("Unable to create Lookup for " + entry.getKey(), ex);
}
}
}
/**
* Create the default Interpolator using only Lookups that work without an event.
*/
public Interpolator() {
this.defaultLookup = new MapLookup(new HashMap<String, String>());
lookups.put("sys", new SystemPropertiesLookup());
lookups.put("env", new EnvironmentLookup());
// lookups.put("jndi", new JndiLookup());
LOGGER.warn("log4j-patch works, JNDI lookup is disabled");
try {
if (Class.forName("javax.servlet.ServletContext") != null) {
lookups.put("web", new WebLookup());
}
} catch (ClassNotFoundException ex) {
LOGGER.debug("ServletContext not present - WebLookup not added");
} catch (Exception ex) {
LOGGER.error("Unable to locate ServletContext", ex);
}
}
/**
* Resolves the specified variable. This implementation will try to extract
* a variable prefix from the given variable name (the first colon (':') is
* used as prefix separator). It then passes the name of the variable with
* the prefix stripped to the lookup object registered for this prefix. If
* no prefix can be found or if the associated lookup object cannot resolve
* this variable, the default lookup object will be used.
*
* @param var the name of the variable whose value is to be looked up
* @return the value of this variable or <b>null</b> if it cannot be
* resolved
*/
@Override
public String lookup(final String var) {
return lookup(null, var);
}
/**
* Resolves the specified variable. This implementation will try to extract
* a variable prefix from the given variable name (the first colon (':') is
* used as prefix separator). It then passes the name of the variable with
* the prefix stripped to the lookup object registered for this prefix. If
* no prefix can be found or if the associated lookup object cannot resolve
* this variable, the default lookup object will be used.
*
* @param event The current LogEvent or null.
* @param var the name of the variable whose value is to be looked up
* @return the value of this variable or <b>null</b> if it cannot be
* resolved
*/
@Override
public String lookup(final LogEvent event, String var) {
if (var == null) {
return null;
}
final int prefixPos = var.indexOf(PREFIX_SEPARATOR);
if (prefixPos >= 0) {
final String prefix = var.substring(0, prefixPos);
final String name = var.substring(prefixPos + 1);
final StrLookup lookup = lookups.get(prefix);
String value = null;
if (lookup != null) {
value = event == null ? lookup.lookup(name) : lookup.lookup(event, name);
}
if (value != null) {
return value;
}
var = var.substring(prefixPos + 1);
}
if (defaultLookup != null) {
return event == null ? defaultLookup.lookup(var) : defaultLookup.lookup(event, var);
}
return null;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
for (final String name : lookups.keySet()) {
if (sb.length() == 0) {
sb.append("{");
} else {
sb.append(", ");
}
sb.append(name);
}
if (sb.length() > 0) {
sb.append("}");
}
return sb.toString();
}
}
@@ -0,0 +1,13 @@
/*
* Copyright © 2021 Glavo <zjx001202@gmail.com>
* This work is free. You can redistribute it and/or modify it under the
* terms of the Do What The Fuck You Want To Public License, Version 2,
* as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
*/
package org.apache.logging.log4j.core.lookup;
public class JndiLookup {
public JndiLookup() {
throw new NoClassDefFoundError("JNDI lookup is disabled");
}
}
+4
View File
@@ -2,3 +2,7 @@ rootProject.name = 'HMCL3'
include ':HMCL'
include ':HMCLCore'
include ':HMCLTransformerDiscoveryService'
include ':log4j-patch'
project(':HMCLTransformerDiscoveryService').projectDir = file('minecraft/libraries/HMCLTransformerDiscoveryService')
project(':log4j-patch').projectDir = file('minecraft/libraries/log4j-patch')