mirror of
https://github.com/FalsePattern/FalsePatternLib.git
synced 2026-11-15 20:44:45 +00:00
improve the robustness of the update checker, plus RFB compat
This commit is contained in:
21 files changed
+507
-137
No files matched your search
@@ -23,6 +23,7 @@ package com.falsepattern.lib.config.event;
|
||||
import com.falsepattern.lib.StableAPI;
|
||||
import com.falsepattern.lib.config.Config;
|
||||
import com.falsepattern.lib.internal.EventUtil;
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
import com.falsepattern.lib.text.FormattedText;
|
||||
import com.falsepattern.lib.toasts.GuiToast;
|
||||
@@ -131,7 +132,7 @@ public class ConfigValidationFailureEvent extends Event {
|
||||
}
|
||||
customText(errorString);
|
||||
for (val line : errorString.toString().split("\n")) {
|
||||
Share.LOG.error(line);
|
||||
FPLog.LOG.error(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2022-2023 FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice
|
||||
* shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.falsepattern.lib.internal;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public final class FPLog {
|
||||
public static final Logger LOG = LogManager.getLogger(Tags.MODNAME);
|
||||
|
||||
public static void deprecatedWarning(Throwable stacktrace) {
|
||||
LOG.warn("DEPRECATED API CALLED!", stacktrace);
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class FalsePatternLib {
|
||||
private static CommonProxy proxy;
|
||||
|
||||
public FalsePatternLib() {
|
||||
Share.LOG.info("Version " + Tags.VERSION + " initialized!");
|
||||
FPLog.LOG.info("Version " + Tags.VERSION + " initialized!");
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
|
||||
@@ -45,22 +45,11 @@ import java.util.function.Consumer;
|
||||
public class Internet {
|
||||
private static final Map<String, String> NO_HEADERS = Collections.emptyMap();
|
||||
|
||||
public static void connect(URL URL, Consumer<Exception> onError, Consumer<InputStream> onSuccess) {
|
||||
connect(URL, NO_HEADERS, onError, onSuccess);
|
||||
public static void connect(URL URL, Consumer<Exception> onError, Consumer<InputStream> onSuccess, Consumer<Long> contentLengthCallback) {
|
||||
connect(URL, NO_HEADERS, onError, onSuccess, contentLengthCallback);
|
||||
}
|
||||
|
||||
public static Map<String, String> constructHeaders(String... entries) {
|
||||
if (entries.length % 2 != 0) {
|
||||
throw new IllegalArgumentException("Entries must be in pairs");
|
||||
}
|
||||
val map = new java.util.HashMap<String, String>();
|
||||
for (int i = 0; i < entries.length; i += 2) {
|
||||
map.put(entries[i], entries[i + 1]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static void connect(URL URL, Map<String, String> headers, Consumer<Exception> onError, Consumer<InputStream> onSuccess) {
|
||||
public static void connect(URL URL, Map<String, String> headers, Consumer<Exception> onError, Consumer<InputStream> onSuccess, Consumer<Long> contentLengthCallback) {
|
||||
try {
|
||||
val connection = (HttpURLConnection) URL.openConnection();
|
||||
connection.setConnectTimeout(3500);
|
||||
@@ -85,6 +74,7 @@ public class Internet {
|
||||
if (connection.getResponseCode() != 200) {
|
||||
onError.accept(new Exception("HTTP response code " + connection.getResponseCode()));
|
||||
} else {
|
||||
contentLengthCallback.accept(connection.getContentLengthLong());
|
||||
onSuccess.accept(connection.getInputStream());
|
||||
}
|
||||
connection.disconnect();
|
||||
@@ -103,57 +93,14 @@ public class Internet {
|
||||
}
|
||||
}
|
||||
|
||||
public static CompletableFuture<byte[]> download(URL... urls) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
val result = new ByteArrayOutputStream();
|
||||
val caught = new AtomicReference<Exception>();
|
||||
val success = new AtomicBoolean(false);
|
||||
for (val url : urls) {
|
||||
connect(url, caught::set, (input) -> {
|
||||
try {
|
||||
transferAndClose(input, result);
|
||||
success.set(true);
|
||||
} catch (IOException e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
});
|
||||
if (success.get()) {
|
||||
return result.toByteArray();
|
||||
}
|
||||
}
|
||||
throw new CompletionException(caught.get());
|
||||
});
|
||||
}
|
||||
|
||||
public static CompletableFuture<Void> downloadFile(File file, URL... urls) {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
val caught = new AtomicReference<Exception>();
|
||||
val success = new AtomicBoolean(false);
|
||||
for (val url : urls) {
|
||||
connect(url, caught::set, (input) -> {
|
||||
try {
|
||||
@Cleanup val outputStream = Files.newOutputStream(file.toPath());
|
||||
transferAndClose(input, outputStream);
|
||||
success.set(true);
|
||||
} catch (IOException e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
});
|
||||
if (success.get()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new CompletionException(caught.get());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public static void transferAndClose(InputStream is, OutputStream target) throws IOException {
|
||||
public static void transferAndClose(InputStream is, OutputStream target, Consumer<Integer> downloadSizeCallback)
|
||||
throws IOException {
|
||||
var bytesRead = 0;
|
||||
|
||||
byte[] smallBuffer = new byte[4096];
|
||||
byte[] smallBuffer = new byte[256 * 1024];
|
||||
while ((bytesRead = is.read(smallBuffer)) >= 0) {
|
||||
target.write(smallBuffer, 0, bytesRead);
|
||||
downloadSizeCallback.accept(bytesRead);
|
||||
}
|
||||
target.close();
|
||||
is.close();
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.falsepattern.lib.internal.asm;
|
||||
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
import com.falsepattern.lib.internal.Tags;
|
||||
import com.falsepattern.lib.internal.impl.dependencies.DependencyLoaderImpl;
|
||||
@@ -48,19 +49,19 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin {
|
||||
private static boolean obfuscated;
|
||||
|
||||
static {
|
||||
Share.LOG.info("Removing skill issues...");
|
||||
FPLog.LOG.info("Removing skill issues...");
|
||||
try {
|
||||
Class.forName("thermos.Thermos");
|
||||
Share.LOG.fatal("Sorry, i prefer iced coffee.");
|
||||
FPLog.LOG.fatal("Sorry, i prefer iced coffee.");
|
||||
throw skillIssue("Thermos is not supported by FalsePatternLib, please use a normal forge server.");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
//Scan for dependencies now
|
||||
Share.LOG.info("Scanning for deps...");
|
||||
FPLog.LOG.info("Scanning for deps...");
|
||||
long start = System.nanoTime();
|
||||
DependencyLoaderImpl.scanDeps();
|
||||
DependencyLoaderImpl.executeDependencyLoading(true);
|
||||
long end = System.nanoTime();
|
||||
Share.LOG.info("Scanned in " + (end - start) / 1000000 + "ms");
|
||||
FPLog.LOG.info("Scanned in " + (end - start) / 1000000 + "ms");
|
||||
//Initializing the rest
|
||||
MappingManager.initialize();
|
||||
}
|
||||
@@ -116,7 +117,7 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin {
|
||||
}
|
||||
|
||||
public static void validateGasStation() {
|
||||
Share.LOG.info("Got any gas?");
|
||||
FPLog.LOG.info("Got any gas?");
|
||||
//Make sure everything is loaded correctly, crash if gasstation is bugged
|
||||
// @formatter:off
|
||||
if (!isClassPresentSafe("com.falsepattern.gasstation.core.GasStationCore") //Validate core class
|
||||
@@ -126,7 +127,7 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin {
|
||||
|| !isClassPresentSafe("org.spongepowered.asm.lib.Opcodes") //Validate correct mixins class
|
||||
|| isClassPresentSafe("org.spongepowered.libraries.org.objectweb.asm.Opcodes")
|
||||
) {
|
||||
Share.LOG.fatal("Somebody put diesel in my gas tank!");
|
||||
FPLog.LOG.fatal("Somebody put diesel in my gas tank!");
|
||||
throw new Error("Failed to validate your GasStation mixin plugin installation. "
|
||||
+ "Please make sure you have the latest GasStation installed from the official source: "
|
||||
+ "https://github.com/FalsePattern/GasStation");
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2022-2023 FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice
|
||||
* shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.falsepattern.lib.internal.asm;
|
||||
|
||||
import com.falsepattern.lib.internal.core.LowLevelCallMultiplexer;
|
||||
import com.falsepattern.lib.internal.impl.dependencies.DependencyLoaderImpl;
|
||||
import com.gtnewhorizons.retrofuturabootstrap.api.RfbPlugin;
|
||||
|
||||
public class RFBLoadingPlugin implements RfbPlugin {
|
||||
static {
|
||||
LowLevelCallMultiplexer.rfbDetected();
|
||||
DependencyLoaderImpl.executeDependencyLoading(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2022-2023 FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice
|
||||
* shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.falsepattern.lib.internal.config;
|
||||
|
||||
import com.falsepattern.lib.StableAPI;
|
||||
import com.falsepattern.lib.internal.core.LowLevelCallMultiplexer;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import lombok.Data;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.val;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
|
||||
//For stuff that runs before FML does
|
||||
@Data
|
||||
@Accessors(fluent = true)
|
||||
public class EarlyConfig {
|
||||
@Expose
|
||||
@StableAPI.Expose(since = "__INTERNAL__")
|
||||
private boolean enableLibraryDownloads;
|
||||
|
||||
private static EarlyConfig instance = null;
|
||||
|
||||
@SneakyThrows
|
||||
public static EarlyConfig load() {
|
||||
if (instance != null)
|
||||
return instance;
|
||||
val configFile = LowLevelCallMultiplexer.gameDir().resolve("config").resolve("falsepatternlib-early.json");
|
||||
val gson = new Gson();
|
||||
EarlyConfig config;
|
||||
if (!Files.exists(configFile)) {
|
||||
config = new EarlyConfig();
|
||||
config.enableLibraryDownloads(true);
|
||||
} else {
|
||||
config = gson.fromJson(new String(Files.readAllBytes(configFile), StandardCharsets.UTF_8), EarlyConfig.class);
|
||||
}
|
||||
Files.write(configFile, gson.toJson(config).getBytes(StandardCharsets.UTF_8));
|
||||
instance = config;
|
||||
return config;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.falsepattern.lib.internal.config;
|
||||
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
@@ -37,7 +38,7 @@ public final class InGameModOptionsFix {
|
||||
MODS_FIELD.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
MODS_FIELD = null;
|
||||
Share.LOG.error("Failed to get field: cpw.mods.fml.client.GuiModList.mods,"
|
||||
FPLog.LOG.error("Failed to get field: cpw.mods.fml.client.GuiModList.mods,"
|
||||
+ " In-Game Mod Options Fix will not work", e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -38,13 +38,6 @@ public class LibraryConfig {
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean ENABLE_UPDATE_CHECKER;
|
||||
|
||||
@Config.Comment({"Used to control whether FalsePatternLib should be allowed to use the internet.",
|
||||
"If this is disabled, library downloads will be blocked.",
|
||||
"Note that if a mod tries to download a library that is not downloaded yet, the game will crash."})
|
||||
@Config.LangKey("config.falsepatternlib.disableinternet")
|
||||
@Config.DefaultBoolean(true)
|
||||
public static boolean ENABLE_LIBRARY_DOWNLOADS;
|
||||
|
||||
@Config.Comment({"Fixes the mod options menu in-game.",
|
||||
"By default, the mod options when in already in a game will show \"Test1, Test2, DISABLED\" in bright red.",
|
||||
"This replaces that interface with the one from the main menu."})
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2022-2023 FalsePattern
|
||||
* All Rights Reserved
|
||||
*
|
||||
* The above copyright notice and this permission notice
|
||||
* shall be included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.falsepattern.lib.internal.core;
|
||||
|
||||
import com.gtnewhorizons.retrofuturabootstrap.RfbApiImpl;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
|
||||
@UtilityClass
|
||||
public class LowLevelCallMultiplexer {
|
||||
private static boolean rfbDetected = false;
|
||||
public static void rfbDetected() {
|
||||
rfbDetected = true;
|
||||
}
|
||||
|
||||
public static void addURLToClassPath(URL url) {
|
||||
if (rfbDetected) {
|
||||
RFBLowLevel.addURLToClassPath(url);
|
||||
} else {
|
||||
LaunchWrapperLowLevel.addURLToClassPath(url);
|
||||
}
|
||||
}
|
||||
|
||||
public static List<URL> getClassPathSources() {
|
||||
if (rfbDetected) {
|
||||
return RFBLowLevel.getClassPathSources();
|
||||
} else {
|
||||
return LaunchWrapperLowLevel.getClassPathSources();
|
||||
}
|
||||
}
|
||||
|
||||
public static Path gameDir() {
|
||||
if (rfbDetected) {
|
||||
return RFBLowLevel.gameDir();
|
||||
} else {
|
||||
return LaunchWrapperLowLevel.gameDir();
|
||||
}
|
||||
}
|
||||
|
||||
//Separate classes to avoid accidental classloading
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
private static class RFBLowLevel {
|
||||
static void addURLToClassPath(URL url) {
|
||||
RfbApiImpl.INSTANCE.compatClassLoader().addURL(url);
|
||||
}
|
||||
|
||||
static List<URL> getClassPathSources() {
|
||||
return RfbApiImpl.INSTANCE.compatClassLoader().getSources();
|
||||
}
|
||||
|
||||
static Path gameDir() {
|
||||
return RfbApiImpl.INSTANCE.gameDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
private static class LaunchWrapperLowLevel {
|
||||
static void addURLToClassPath(URL url) {
|
||||
Launch.classLoader.addURL(url);
|
||||
}
|
||||
|
||||
static List<URL> getClassPathSources() {
|
||||
return Launch.classLoader.getSources();
|
||||
}
|
||||
|
||||
static Path gameDir() {
|
||||
return Launch.minecraftHome == null ? Paths.get(".") : Launch.minecraftHome.toPath();
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-4
@@ -23,6 +23,7 @@ package com.falsepattern.lib.internal.impl.config;
|
||||
import com.falsepattern.lib.config.ConfigException;
|
||||
import com.falsepattern.lib.config.event.AllConfigSyncEvent;
|
||||
import com.falsepattern.lib.config.event.ConfigSyncEvent;
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.FalsePatternLib;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
import com.falsepattern.lib.internal.impl.config.net.SyncRequest;
|
||||
@@ -162,7 +163,7 @@ public final class ConfigurationManagerImpl {
|
||||
|
||||
public static void receiveReply(DataInput input) throws IOException {
|
||||
if (AllConfigSyncEvent.postStart()) {
|
||||
Share.LOG.warn("All config synchronization was cancelled by event.");
|
||||
FPLog.LOG.warn("All config synchronization was cancelled by event.");
|
||||
return;
|
||||
}
|
||||
int count = input.readInt();
|
||||
@@ -172,20 +173,20 @@ public final class ConfigurationManagerImpl {
|
||||
val opt = serializedNames.keySet().stream().filter((key) -> key.equals(serializedName)).findFirst();
|
||||
if (!opt.isPresent()) {
|
||||
input.skipBytes(dataSize);
|
||||
Share.LOG.warn("Server tried to sync config not registered on our side: " + serializedName);
|
||||
FPLog.LOG.warn("Server tried to sync config not registered on our side: " + serializedName);
|
||||
continue;
|
||||
}
|
||||
val clazz = serializedNames.get(opt.get());
|
||||
val config = parsedConfigMap.get(clazz);
|
||||
if (!config.sync) {
|
||||
input.skipBytes(dataSize);
|
||||
Share.LOG.warn("Server tried to sync config without @Synchronize annotation on our side: "
|
||||
FPLog.LOG.warn("Server tried to sync config without @Synchronize annotation on our side: "
|
||||
+ serializedName);
|
||||
continue;
|
||||
}
|
||||
if (ConfigSyncEvent.postStart(clazz)) {
|
||||
input.skipBytes(dataSize);
|
||||
Share.LOG.warn("Config synchronization was cancelled by event for: " + serializedName);
|
||||
FPLog.LOG.warn("Config synchronization was cancelled by event for: " + serializedName);
|
||||
continue;
|
||||
}
|
||||
val bytes = new byte[dataSize];
|
||||
|
||||
@@ -22,6 +22,7 @@ package com.falsepattern.lib.internal.impl.config.fields;
|
||||
|
||||
import com.falsepattern.lib.config.Config;
|
||||
import com.falsepattern.lib.config.event.ConfigValidationFailureEvent;
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
@@ -102,7 +103,7 @@ public class EnumConfigField<T extends Enum<T>> extends AConfigField<T> {
|
||||
|
||||
private T getEnumByName(String name) {
|
||||
if (!enumNameMap.containsKey(name)) {
|
||||
Share.LOG.warn("Invalid value "
|
||||
FPLog.LOG.warn("Invalid value "
|
||||
+ name
|
||||
+ " for enum configuration field "
|
||||
+ field.getName()
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.falsepattern.lib.internal.impl.config.net;
|
||||
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
|
||||
import cpw.mods.fml.common.network.simpleimpl.IMessage;
|
||||
@@ -40,7 +41,7 @@ public class SyncReplyHandler implements IMessageHandler<SyncReply, IMessage> {
|
||||
try {
|
||||
message.receive();
|
||||
} catch (IOException e) {
|
||||
Share.LOG.error("Failed to sync config", e);
|
||||
FPLog.LOG.error("Failed to sync config", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
package com.falsepattern.lib.internal.impl.config.net;
|
||||
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.FalsePatternLib;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
import lombok.val;
|
||||
@@ -56,7 +57,7 @@ public class SyncRequestHandler implements IMessageHandler<SyncRequest, IMessage
|
||||
FalsePatternLib.NETWORK.sendTo(reply, ctx.getServerHandler().playerEntity);
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
Share.LOG.error("Failed to sync config", e);
|
||||
FPLog.LOG.error("Failed to sync config", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+221
-44
@@ -26,7 +26,8 @@ import com.falsepattern.lib.dependencies.Version;
|
||||
import com.falsepattern.lib.internal.Internet;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
import com.falsepattern.lib.internal.Tags;
|
||||
import com.falsepattern.lib.internal.config.LibraryConfig;
|
||||
import com.falsepattern.lib.internal.config.EarlyConfig;
|
||||
import com.falsepattern.lib.internal.core.LowLevelCallMultiplexer;
|
||||
import com.falsepattern.lib.util.FileUtil;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParser;
|
||||
@@ -38,20 +39,32 @@ import lombok.val;
|
||||
import lombok.var;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.WindowConstants;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Toolkit;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -70,6 +83,7 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -216,8 +230,7 @@ public class DependencyLoaderImpl {
|
||||
|
||||
private static synchronized void addToClasspath(Path file) {
|
||||
try {
|
||||
val cl = (LaunchClassLoader) DependencyLoaderImpl.class.getClassLoader();
|
||||
cl.addURL(file.toUri().toURL());
|
||||
LowLevelCallMultiplexer.addURLToClassPath(file.toUri().toURL());
|
||||
LOG.debug("Injected file {} into classpath!", file);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to add library to classpath: " + file.toAbsolutePath(), e);
|
||||
@@ -225,11 +238,11 @@ public class DependencyLoaderImpl {
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static void download(InputStream is, Path target) {
|
||||
private static void download(InputStream is, Path target, Consumer<Integer> downloadSizeCallback) {
|
||||
if (Files.exists(target)) {
|
||||
return;
|
||||
}
|
||||
Internet.transferAndClose(is, new BufferedOutputStream(Files.newOutputStream(target)));
|
||||
Internet.transferAndClose(is, new BufferedOutputStream(Files.newOutputStream(target)), downloadSizeCallback);
|
||||
}
|
||||
|
||||
public static void loadLibraries(Library... libraries) {
|
||||
@@ -346,7 +359,50 @@ public class DependencyLoaderImpl {
|
||||
+ "(?:\\.(?:(?:[0-9]+[a-zA-Z-][\\w-]*)|(?:[a-zA-Z][\\w-]*)|(?:[1-9]\\d*)|0))*))?"
|
||||
+ "(?:\\+([\\w-]+(\\.[\\w-]+)*))?");
|
||||
|
||||
public static void scanDeps() {
|
||||
private enum DependencySide {
|
||||
COMMON,
|
||||
CLIENT,
|
||||
SERVER
|
||||
}
|
||||
|
||||
private enum DependencyScope {
|
||||
ALWAYS,
|
||||
DEV,
|
||||
OBF
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class ScopeSide {
|
||||
final DependencyScope scope;
|
||||
final DependencySide side;
|
||||
|
||||
boolean contains(ScopeSide dependency) {
|
||||
return (dependency.scope == scope || dependency.scope == DependencyScope.ALWAYS) &&
|
||||
(dependency.side == side || dependency.side == DependencySide.COMMON);
|
||||
}
|
||||
}
|
||||
|
||||
private static Pair<DependencyScope, DepRoot.SidedDependencies> scopedDependency(DependencyScope scope, DepRoot.SidedDependencies deps) {
|
||||
return new Pair<>(scope, deps);
|
||||
}
|
||||
|
||||
private static Stream<Pair<ScopeSide, String>> sidedDependency(DependencySide side, Stream<Pair<DependencyScope, String>> deps) {
|
||||
return deps.map(dep -> new Pair<>(new ScopeSide(dep.a, side), dep.b));
|
||||
}
|
||||
|
||||
private static boolean initialScan = false;
|
||||
private static List<Pair<ScopeSide, DependencyLoadTask>> tasks;
|
||||
|
||||
public static void executeDependencyLoading(boolean sideAware) {
|
||||
if (!initialScan) {
|
||||
initialScan = true;
|
||||
scanDeps();
|
||||
}
|
||||
|
||||
executeArtifactLoading(sideAware);
|
||||
}
|
||||
|
||||
private static void scanDeps() {
|
||||
LOG.debug("Discovering dependency source candidates...");
|
||||
val modsDir = FileUtil.getMinecraftHomePath().resolve("mods");
|
||||
val mods1710Dir = modsDir.resolve("1.7.10");
|
||||
@@ -360,7 +416,7 @@ public class DependencyLoaderImpl {
|
||||
LOG.error("Could not read dependency scanner cache", e);
|
||||
}
|
||||
}
|
||||
val candidates = Stream.of(Launch.classLoader.getSources().stream(),
|
||||
val candidates = Stream.of(LowLevelCallMultiplexer.getClassPathSources().stream(),
|
||||
grabSourceCandidatesFromFolder(modsDir),
|
||||
grabSourceCandidatesFromFolder(mods1710Dir))
|
||||
.flatMap((i) -> i)
|
||||
@@ -412,18 +468,20 @@ public class DependencyLoaderImpl {
|
||||
val artifacts = dependencySpecs.stream()
|
||||
.map((root) -> new Pair<>(root.source(), root.dependencies()))
|
||||
.flatMap(pair -> flatMap(pair,
|
||||
(dep) -> Stream.of(dep.always(),
|
||||
Share.DEV_ENV ? dep.dev()
|
||||
: dep.obf())))
|
||||
(dep) -> Stream.of(scopedDependency(DependencyScope.ALWAYS, dep.always()),
|
||||
scopedDependency(DependencyScope.DEV, dep.dev()),
|
||||
scopedDependency(DependencyScope.OBF, dep.obf())
|
||||
)))
|
||||
.flatMap(pair -> flatMap(pair,
|
||||
(dep) -> Stream.concat(dep.common().stream(),
|
||||
FMLLaunchHandler.side()
|
||||
.isClient()
|
||||
? dep.client().stream()
|
||||
: dep.server().stream())))
|
||||
(dep) -> Stream.concat(sidedDependency(DependencySide.COMMON, flatMap(dep, d -> d.common().stream())),
|
||||
Stream.concat(sidedDependency(DependencySide.CLIENT, flatMap(dep, d -> d.client().stream())),
|
||||
sidedDependency(DependencySide.SERVER, flatMap(dep, d -> d.server().stream())))
|
||||
)))
|
||||
.map((pair) -> {
|
||||
val source = pair.a;
|
||||
val dep = pair.b;
|
||||
val scopedDep = pair.b;
|
||||
val dep = scopedDep.b;
|
||||
val scope = scopedDep.a;
|
||||
val parts = dep.split(":");
|
||||
if (parts.length < 3) {
|
||||
LOG.error("Invalid dependency: {}", dep);
|
||||
@@ -468,43 +526,148 @@ public class DependencyLoaderImpl {
|
||||
version,
|
||||
source);
|
||||
}
|
||||
return new DependencyLoadTask(source,
|
||||
groupId,
|
||||
artifactId,
|
||||
version,
|
||||
null,
|
||||
version,
|
||||
classifier,
|
||||
classifier);
|
||||
return new Pair<>(scope,
|
||||
new DependencyLoadTask(source,
|
||||
groupId,
|
||||
artifactId,
|
||||
version,
|
||||
null,
|
||||
version,
|
||||
classifier,
|
||||
classifier));
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
tasks = new ArrayList<>(artifacts);
|
||||
}
|
||||
|
||||
private static class SideAwareAssistant {
|
||||
static ScopeSide current() {
|
||||
return new ScopeSide(Share.DEV_ENV ? DependencyScope.DEV : DependencyScope.OBF, FMLLaunchHandler.side() == Side.CLIENT ? DependencySide.CLIENT : DependencySide.SERVER);
|
||||
}
|
||||
}
|
||||
|
||||
private static void executeArtifactLoading(boolean sideAware) {
|
||||
val scopeSide = sideAware ? SideAwareAssistant.current() : new ScopeSide(DependencyScope.ALWAYS, DependencySide.COMMON);
|
||||
val iter = tasks.iterator();
|
||||
val artifactMap = new HashMap<String, DependencyLoadTask>();
|
||||
for (val artifact : artifacts) {
|
||||
val id = artifact.getGroupArtifact();
|
||||
while (iter.hasNext()) {
|
||||
val artifact = iter.next();
|
||||
val artifactSide = artifact.a;
|
||||
if (!scopeSide.contains(artifactSide))
|
||||
continue;
|
||||
iter.remove();
|
||||
val artifactPayload = artifact.b;
|
||||
val id = artifactPayload.getGroupArtifact();
|
||||
if (artifactMap.containsKey(id)) {
|
||||
val otherArtifact = artifactMap.get(id);
|
||||
//TODO: Check for conflicts
|
||||
if (artifact.preferredVersion.compareTo(otherArtifact.preferredVersion) > 0) {
|
||||
if (artifactPayload.preferredVersion.compareTo(otherArtifact.preferredVersion) > 0) {
|
||||
LOG.info("Replacing dependency {}:{} from {} with version {} from {}",
|
||||
otherArtifact.getGroupArtifact(),
|
||||
otherArtifact.preferredVersion,
|
||||
otherArtifact.loadingModId,
|
||||
artifact.preferredVersion,
|
||||
artifact.loadingModId);
|
||||
artifactMap.put(id, artifact);
|
||||
artifactPayload.preferredVersion,
|
||||
artifactPayload.loadingModId);
|
||||
artifactMap.put(id, artifactPayload);
|
||||
}
|
||||
} else {
|
||||
artifactMap.put(id, artifact);
|
||||
artifactMap.put(id, artifactPayload);
|
||||
}
|
||||
}
|
||||
if (artifactMap.isEmpty())
|
||||
return;
|
||||
|
||||
val futures = new ArrayList<CompletableFuture<Void>>();
|
||||
for (val task : artifactMap.values()) {
|
||||
futures.add(CompletableFuture.runAsync(task::load, executor));
|
||||
LOG.info("-----------------------------------------------------------");
|
||||
LOG.info("FalsePatternLib is downloading dependencies. Please wait...");
|
||||
LOG.info("-----------------------------------------------------------");
|
||||
JFrame theFrame;
|
||||
val progresses = new HashMap<DependencyLoadTask, JProgressBar>();
|
||||
{
|
||||
JFrame jFrame = null;
|
||||
try {
|
||||
jFrame = new JFrame("Dependency Download");
|
||||
val constraints = new GridBagConstraints();
|
||||
jFrame.getContentPane().setLayout(new GridBagLayout());
|
||||
constraints.gridy = 0;
|
||||
constraints.gridwidth = 2;
|
||||
jFrame.add(new JLabel("FalsePatternLib is downloading dependencies, please wait!"), constraints);
|
||||
constraints.gridwidth = 1;
|
||||
for (val artifact : artifactMap.entrySet()) {
|
||||
constraints.gridy++;
|
||||
jFrame.add(new JLabel(artifact.getKey()), constraints);
|
||||
val status = new JProgressBar();
|
||||
status.setIndeterminate(true);
|
||||
status.setStringPainted(true);
|
||||
status.setString("Waiting...");
|
||||
progresses.put(artifact.getValue(), status);
|
||||
jFrame.add(status, constraints);
|
||||
}
|
||||
jFrame.pack();
|
||||
jFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
jFrame.setVisible(true);
|
||||
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
jFrame.setLocation(dim.width/2-jFrame.getSize().width/2, dim.height/2-jFrame.getSize().height/2);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
theFrame = jFrame;
|
||||
}
|
||||
if (theFrame != null) {
|
||||
for (val task : artifactMap.values()) {
|
||||
futures.add(CompletableFuture.runAsync(() -> {
|
||||
val bar = progresses.get(task);
|
||||
bar.setString("Downloading...");
|
||||
task.load();
|
||||
bar.setMinimum(0);
|
||||
bar.setMaximum(1);
|
||||
bar.setValue(1);
|
||||
bar.setString("Completed!");
|
||||
}, executor));
|
||||
}
|
||||
} else {
|
||||
for (val task : artifactMap.values()) {
|
||||
futures.add(CompletableFuture.runAsync(task::load, executor));
|
||||
}
|
||||
}
|
||||
val theFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
AtomicBoolean doViz = new AtomicBoolean(true);
|
||||
if (theFrame != null) {
|
||||
final var vizThread = getVizThread(doViz, progresses, theFrame);
|
||||
vizThread.start();
|
||||
}
|
||||
theFuture.join();
|
||||
if (theFrame != null) {
|
||||
doViz.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Thread getVizThread(AtomicBoolean doViz, HashMap<DependencyLoadTask, JProgressBar> progresses, JFrame theFrame) {
|
||||
val vizThread = new Thread(() -> {
|
||||
while (doViz.get()) {
|
||||
for (val progress : progresses.entrySet()) {
|
||||
val task = progress.getKey();
|
||||
val bar = progress.getValue();
|
||||
if (task.contentLength == -1) {
|
||||
bar.setIndeterminate(true);
|
||||
} else {
|
||||
bar.setIndeterminate(false);
|
||||
bar.setMinimum(0);
|
||||
bar.setMaximum((int) task.contentLength);
|
||||
bar.setValue((int) task.downloaded);
|
||||
}
|
||||
}
|
||||
theFrame.repaint();
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
theFrame.dispose();
|
||||
});
|
||||
vizThread.setName("FalsePatternLib Download Visualizer");
|
||||
return vizThread;
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@@ -527,9 +690,12 @@ public class DependencyLoaderImpl {
|
||||
private String artifactLogName;
|
||||
private String artifact;
|
||||
private String mavenJarName;
|
||||
private String jarName;
|
||||
public volatile String jarName;
|
||||
private Path file;
|
||||
|
||||
public volatile long contentLength = -1;
|
||||
public volatile long downloaded = 0;
|
||||
|
||||
private void load() {
|
||||
try {
|
||||
setupLibraryNames();
|
||||
@@ -647,7 +813,7 @@ public class DependencyLoaderImpl {
|
||||
}
|
||||
|
||||
private void validateDownloadsAllowed() {
|
||||
if (!LibraryConfig.ENABLE_LIBRARY_DOWNLOADS) {
|
||||
if (!EarlyConfig.load().enableLibraryDownloads()) {
|
||||
val errorMessage = "Failed to load library "
|
||||
+ groupId
|
||||
+ ":"
|
||||
@@ -684,18 +850,28 @@ public class DependencyLoaderImpl {
|
||||
break;
|
||||
}
|
||||
val success = new AtomicBoolean(false);
|
||||
val tmpFile = file.getParent().resolve(file.getFileName().toString() + ".tmp");
|
||||
if (Files.exists(tmpFile)) {
|
||||
Files.delete(tmpFile);
|
||||
}
|
||||
Internet.connect(new URL(url),
|
||||
(ex) -> LOG.debug("Artifact {} could not be downloaded from repo {}: {}",
|
||||
ex -> LOG.debug("Artifact {} could not be downloaded from repo {}: {}",
|
||||
artifactLogName,
|
||||
finalRepo,
|
||||
ex.getMessage()),
|
||||
(input) -> {
|
||||
input -> {
|
||||
LOG.debug("Downloading {} from {}", artifactLogName, finalRepo);
|
||||
download(input, file);
|
||||
download(input, tmpFile, d -> downloaded += d);
|
||||
LOG.debug("Downloaded {} from {}", artifactLogName, finalRepo);
|
||||
success.set(true);
|
||||
});
|
||||
},
|
||||
contentLength -> this.contentLength = contentLength);
|
||||
if (success.get()) {
|
||||
try {
|
||||
Files.move(tmpFile, file, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (AtomicMoveNotSupportedException ignored) {
|
||||
Files.move(tmpFile, file);
|
||||
}
|
||||
LOG.debug("Validating checksum for {}", artifactLogName);
|
||||
val hadChecksum = validateChecksum(url);
|
||||
switch (hadChecksum) {
|
||||
@@ -732,10 +908,11 @@ public class DependencyLoaderImpl {
|
||||
ex.getMessage()),
|
||||
(input) -> {
|
||||
LOG.debug("Downloading {} checksum for {}", checksumType, artifactLogName);
|
||||
download(input, checksumFile);
|
||||
download(input, checksumFile, d -> {});
|
||||
LOG.debug("Downloaded {} checksum for {}", checksumType, artifactLogName);
|
||||
success.set(true);
|
||||
});
|
||||
},
|
||||
length -> {});
|
||||
if (success.get()) {
|
||||
return getChecksumStatus(file, checksumType, checksumFile);
|
||||
}
|
||||
|
||||
+4
-3
@@ -21,6 +21,7 @@
|
||||
|
||||
package com.falsepattern.lib.internal.impl.optifine;
|
||||
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
import lombok.val;
|
||||
|
||||
@@ -42,7 +43,7 @@ public class OptiFineTransformerHooksImpl {
|
||||
try {
|
||||
tField = LaunchClassLoader.class.getDeclaredField("transformers");
|
||||
} catch (NoSuchFieldException e) {
|
||||
Share.LOG.error("Could not retrieve transformers field", e);
|
||||
FPLog.LOG.error("Could not retrieve transformers field", e);
|
||||
return;
|
||||
}
|
||||
tField.setAccessible(true);
|
||||
@@ -50,14 +51,14 @@ public class OptiFineTransformerHooksImpl {
|
||||
try {
|
||||
transformers = (List<IClassTransformer>)tField.get(Launch.classLoader);
|
||||
} catch (IllegalAccessException e) {
|
||||
Share.LOG.error("Could not retrieve transformers list", e);
|
||||
FPLog.LOG.error("Could not retrieve transformers list", e);
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < transformers.size(); i++) {
|
||||
val transformer = transformers.get(i);
|
||||
System.out.println(transformer.getClass().getName());
|
||||
if (transformer.getClass().getName().equals("optifine.OptiFineClassTransformer")) {
|
||||
Share.LOG.info("Attaching OptiFine ASM transformer hooks");
|
||||
FPLog.LOG.info("Attaching OptiFine ASM transformer hooks");
|
||||
transformers.set(i, new WrappedOptiFineClassTransformer(transformer));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
package com.falsepattern.lib.mapping;
|
||||
|
||||
import com.falsepattern.lib.StableAPI;
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.FalsePatternLib;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
import com.falsepattern.lib.internal.asm.CoreLoadingPlugin;
|
||||
@@ -69,7 +70,7 @@ public class MappingManager {
|
||||
if (time - lastInit < 10_000) {
|
||||
continue;
|
||||
}
|
||||
Share.LOG.info("Terminating MappingManager");
|
||||
FPLog.LOG.info("Terminating MappingManager");
|
||||
internalLookup = null;
|
||||
regularLookup = null;
|
||||
initialized = false;
|
||||
@@ -88,7 +89,7 @@ public class MappingManager {
|
||||
}
|
||||
initialized = true;
|
||||
val cleanupThread = new CleanupThread();
|
||||
Share.LOG.info("Initializing MappingManager");
|
||||
FPLog.LOG.info("Initializing MappingManager");
|
||||
internalLookup = new Lookup<>();
|
||||
regularLookup = new Lookup<>();
|
||||
val stringPool = new HashMap<String, String>();
|
||||
|
||||
@@ -22,6 +22,7 @@ package com.falsepattern.lib.updates;
|
||||
|
||||
import com.falsepattern.lib.DeprecationDetails;
|
||||
import com.falsepattern.lib.StableAPI;
|
||||
import com.falsepattern.lib.internal.FPLog;
|
||||
import com.falsepattern.lib.internal.Share;
|
||||
|
||||
import net.minecraft.util.IChatComponent;
|
||||
@@ -44,7 +45,7 @@ public final class UpdateChecker {
|
||||
*/
|
||||
@StableAPI.Expose(since = "0.11.0")
|
||||
public static CompletableFuture<List<ModUpdateInfo>> fetchUpdatesAsyncV2(String url) {
|
||||
Share.deprecatedWarning(new Throwable());
|
||||
FPLog.deprecatedWarning(new Throwable());
|
||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||
}
|
||||
|
||||
@@ -53,7 +54,7 @@ public final class UpdateChecker {
|
||||
*/
|
||||
@StableAPI.Expose(since = "0.11.0")
|
||||
public static List<ModUpdateInfo> fetchUpdatesV2(String url) throws UpdateCheckException {
|
||||
Share.deprecatedWarning(new Throwable());
|
||||
FPLog.deprecatedWarning(new Throwable());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -62,7 +63,7 @@ public final class UpdateChecker {
|
||||
*/
|
||||
@StableAPI.Expose
|
||||
public static List<IChatComponent> updateListToChatMessages(String initiator, List<ModUpdateInfo> updates) {
|
||||
Share.deprecatedWarning(new Throwable());
|
||||
FPLog.deprecatedWarning(new Throwable());
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user