Update to 1.19.3
ci/woodpecker/push/docs Pipeline was successful Details
ci/woodpecker/push/jfmod Pipeline failed Details

This commit is contained in:
Johannes Frohnmeyer 2022-12-07 19:54:35 +01:00
parent 6ece62df7f
commit f9e31f36ef
Signed by: Johannes
GPG Key ID: E76429612C2929F4
18 changed files with 284 additions and 249 deletions

View File

@ -1,7 +1,7 @@
# https://fabricmc.net/develop/
minecraft_version=1.19.2
yarn_mappings=build.28
loader_version=0.14.10
minecraft_version=1.19.3
yarn_mappings=build.1
loader_version=0.14.11
maven_group=io.gitlab.jfronny.libjf
archive_base_name=libjf
@ -14,9 +14,9 @@ modrinth_optional_dependencies=fabric-api
curseforge_id=482600
curseforge_optional_dependencies=fabric-api
fabric_version=0.67.1+1.19.2
fabric_version=0.68.1+1.19.3
commons_version=1.0-SNAPSHOT
modmenu_version=4.1.1
modmenu_version=5.0.0-alpha.4
asm_version=9.4
ant_version=1.10.12

View File

@ -57,22 +57,29 @@ public class TinyConfigScreen extends Screen {
}
if (!config.getPresets().isEmpty()) {
this.addDrawableChild(new ButtonWidget(4, 6, 80, 20, Text.translatable("libjf-config-v1.presets"), button -> {
Objects.requireNonNull(client).setScreen(new PresetsScreen(this, config));
}));
this.addDrawableChild(ButtonWidget.builder(Text.translatable("libjf-config-v1.presets"),
button -> Objects.requireNonNull(client).setScreen(new PresetsScreen(this, config)))
.position(4, 6)
.size(80, 20)
.build());
}
this.addDrawableChild(new ButtonWidget(this.width / 2 - 154, this.height - 28, 150, 20, ScreenTexts.CANCEL, button -> {
Objects.requireNonNull(client).setScreen(parent);
}));
this.addDrawableChild(ButtonWidget.builder(ScreenTexts.CANCEL,
button -> Objects.requireNonNull(client).setScreen(parent))
.position(this.width / 2 - 154, this.height - 28)
.size(150, 20)
.build());
ButtonWidget done = this.addDrawableChild(new ButtonWidget(this.width / 2 + 4, this.height - 28, 150, 20, ScreenTexts.DONE, (button) -> {
for (WidgetState<?> state : widgets) {
Try.orElse(state::writeToEntry, e -> LibJf.LOGGER.error("Could not write config data to class", e));
}
config.getRoot().write();
Objects.requireNonNull(client).setScreen(parent);
}));
ButtonWidget done = this.addDrawableChild(ButtonWidget.builder(ScreenTexts.DONE, (button) -> {
for (WidgetState<?> state : widgets) {
Try.orElse(state::writeToEntry, e -> LibJf.LOGGER.error("Could not write config data to class", e));
}
config.getRoot().write();
Objects.requireNonNull(client).setScreen(parent);
})
.position(this.width / 2 + 4, this.height - 28)
.size(150, 20)
.build());
this.list = new EntryListWidget(this.client, this.width, this.height, 32, this.height - 32, 25);
this.addSelectableChild(this.list);
@ -84,10 +91,13 @@ public class TinyConfigScreen extends Screen {
for (WidgetState<?> info : widgets) {
MutableText name = Text.translatable(translationPrefix + info.entry.getName());
WidgetFactory.Widget control = info.factory.build(width, textRenderer, done);
ButtonWidget resetButton = new ButtonWidget(width - 155, 0, 40, 20, Text.translatable("libjf-config-v1.reset"), (button -> {
info.reset();
control.updateControls().run();
}));
ButtonWidget resetButton = ButtonWidget.builder(Text.translatable("libjf-config-v1.reset"), (button -> {
info.reset();
control.updateControls().run();
}))
.position(width - 155, 0)
.size(40, 20)
.build();
this.list.addButton(control.control(), resetButton, () -> {
boolean visible = !Objects.equals(info.entry.getDefault(), info.cachedValue);
resetButton.active = visible;

View File

@ -1,5 +1,6 @@
package io.gitlab.jfronny.libjf.config.impl.ui.tiny.entry;
import io.gitlab.jfronny.commons.ref.R;
import io.gitlab.jfronny.commons.throwable.Try;
import io.gitlab.jfronny.commons.tuple.Tuple;
import io.gitlab.jfronny.libjf.LibJf;
@ -17,6 +18,7 @@ import net.minecraft.util.Formatting;
import java.util.*;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
@Environment(EnvType.CLIENT)
@ -56,9 +58,9 @@ public class EntryInfoWidgetBuilder {
} else if (type.isString()) {
factory = textField(info, state, null, String::length, true, Math.min(info.getMinValue(), 0), Math.max(info.getMaxValue(), 1));
} else if (type.isBool()) {
factory = toggle(info, state,
factory = toggle((EntryInfo<Boolean>) info, (WidgetState<Boolean>) state,
value -> !(Boolean) value,
value -> Text.literal((Boolean) value ? "True" : "False").formatted((Boolean) value ? Formatting.GREEN : Formatting.RED));
value -> Text.literal(value ? "True" : "False").formatted(value ? Formatting.GREEN : Formatting.RED));
} else if (type.isEnum()) {
T[] values = type.<T>asEnum().options();
factory = toggle(info, state, value -> {
@ -73,7 +75,10 @@ public class EntryInfoWidgetBuilder {
});
} else {
LibJf.LOGGER.error("Unsupported entry type in " + info.getName() + ": " + type.getName() + " - not displaying config control");
factory = ((screenWidth, textRenderer, done) -> new WidgetFactory.Widget(() -> {}, new ButtonWidget(-10, 0, 0, 0, Text.of(""), null)));
factory = ((screenWidth, textRenderer, done) -> new WidgetFactory.Widget(R::nop, ButtonWidget.builder(Text.of(""), R::nop)
.position(-10, 0)
.size(0, 0)
.build()));
}
Try.orThrow(() -> state.initialize(info, knownStates, factory));
@ -87,13 +92,13 @@ public class EntryInfoWidgetBuilder {
return -1;
}
private static <T> WidgetFactory toggle(EntryInfo<T> info, WidgetState<T> state, Function<Object, Object> increment, Function<T, Text> valueTextifier) {
private static <T> WidgetFactory toggle(EntryInfo<T> info, WidgetState<T> state, UnaryOperator<T> increment, Function<T, Text> valueToText) {
return (screenWidth, textRenderer, done) -> {
ButtonWidget button = new ButtonWidget(screenWidth - 110, 0, info.getWidth(), 20, valueTextifier.apply(state.cachedValue), btn -> {
final ButtonWidget button = ButtonWidget.builder(valueToText.apply(state.cachedValue), btn -> {
state.updateCache((T) increment.apply(state.cachedValue));
btn.setMessage(valueTextifier.apply(state.cachedValue));
});
return new WidgetFactory.Widget(() -> button.setMessage(valueTextifier.apply(state.cachedValue)), button);
btn.setMessage(valueToText.apply(state.cachedValue));
}).position(screenWidth - 110, 0).size(info.getWidth(), 20).build();
return new WidgetFactory.Widget(() -> button.setMessage(valueToText.apply(state.cachedValue)), button);
};
}

View File

@ -50,7 +50,7 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
for (ConfigEntry abstractEntry : this.children()) {
if (abstractEntry instanceof ConfigScreenEntry entry
&& entry.button.visible
&& mouseY >= entry.button.y && mouseY < entry.button.y + itemHeight) {
&& mouseY >= entry.button.getY() && mouseY < entry.button.getY() + itemHeight) {
return Optional.of(entry.getText());
}
}
@ -66,7 +66,10 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
private final ClickableWidget button;
public ConfigReferenceEntry(int centerX, Text text, Supplier<Screen> targetScreen) {
this.button = new ButtonWidget(centerX - 154, 0, 308, 20, text, btn -> MinecraftClient.getInstance().setScreen(targetScreen.get()));
this.button = ButtonWidget.builder(text, btn -> MinecraftClient.getInstance().setScreen(targetScreen.get()))
.position(centerX - 154, 0)
.size(308, 20)
.build();
}
@Override
@ -81,7 +84,7 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
@Override
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
button.y = y;
button.setY(y);
button.render(matrices, mouseX, mouseY, tickDelta);
}
}
@ -103,11 +106,11 @@ public class EntryListWidget extends ElementListWidget<EntryListWidget.ConfigEnt
@Override
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
button.y = y;
button.setY(y);
button.render(matrices, mouseX, mouseY, tickDelta);
drawTextWithShadow(matrices,textRenderer, text,12,y+5,0xFFFFFF);
if (resetVisible.getAsBoolean()) {
resetButton.y = y;
resetButton.setY(y);
resetButton.render(matrices, mouseX, mouseY, tickDelta);
}
}

View File

@ -46,7 +46,7 @@ public class PresetListWidget extends ElementListWidget<PresetListWidget.PresetE
@Override
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
button.y = y;
button.setY(y);
button.render(matrices, mouseX, mouseY, tickDelta);
}
}

View File

@ -29,14 +29,16 @@ public class PresetsScreen extends Screen {
super.init();
this.list = new PresetListWidget(this.client, this.width, this.height, 32, this.height - 32, 25);
for (Map.Entry<String, Runnable> entry : config.getPresets().entrySet()) {
this.list.addButton(new ButtonWidget(width / 2 - 100, 0, 200, 20,
Text.translatable(entry.getKey()),
this.list.addButton(ButtonWidget.builder(Text.translatable(entry.getKey()),
button -> {
LibJf.LOGGER.info("Preset selected: " + entry.getKey());
entry.getValue().run();
config.fix();
Objects.requireNonNull(client).setScreen(parent);
}));
})
.position(width / 2 - 100, 0)
.size(200, 20)
.build());
}
this.addSelectableChild(this.list);
}

View File

@ -1,19 +1,18 @@
package io.gitlab.jfronny.libjf.data.manipulation.api;
import io.gitlab.jfronny.commons.LazySupplier;
import io.gitlab.jfronny.commons.throwable.*;
import io.gitlab.jfronny.commons.throwable.ThrowingRunnable;
import io.gitlab.jfronny.commons.throwable.ThrowingSupplier;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.data.manipulation.impl.ResourcePackHook;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourceType;
import net.minecraft.resource.*;
import net.minecraft.resource.metadata.ResourceMetadataReader;
import net.minecraft.util.Identifier;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.function.Predicate;
import java.util.function.Supplier;
@SuppressWarnings("unused")
@ -37,69 +36,73 @@ public class UserResourceEvents {
}
}
public static final Event<Contains> CONTAINS = EventFactory.createArrayBacked(Contains.class,
(listeners) -> (type, id, previous, pack) -> {
LazySupplier<Boolean> lazy = new LazySupplier<>(previous);
for (Contains listener : listeners) {
lazy = lazy.andThen(supplier -> listener.contains(type, id, supplier, pack));
}
return lazy.get();
});
public static final Event<FindResource> FIND_RESOURCE = EventFactory.createArrayBacked(FindResource.class,
(listeners) -> ((type, namespace, prefix, allowedPathPredicate, previous, pack) -> {
LazySupplier<Collection<Identifier>> lazy = new LazySupplier<>(previous);
for (FindResource listener : listeners) {
lazy = lazy.andThen(supplier -> listener.findResources(type, namespace, prefix, allowedPathPredicate, supplier, pack));
}
return lazy.get();
}));
public static final Event<OpenRoot> OPEN_ROOT = EventFactory.createArrayBacked(OpenRoot.class,
listeners -> (fileName, previous, pack) -> {
InputSupplier<InputStream> lazy = previous;
for (OpenRoot listener : listeners) {
lazy = listener.openRoot(fileName, lazy, pack);
}
return lazy;
});
public static final Event<Open> OPEN = EventFactory.createArrayBacked(Open.class,
(listeners) -> ((type, id, previous, pack) -> {
LazySupplier<InputStream> lazy = new LazySupplier<>(previous);
for (Open listener : listeners) {
lazy = lazy.andThen(supplier -> {
try {
return listener.open(type, id, supplier, pack);
} catch (IOException e) {
LibJf.LOGGER.error("Could not call ResourcePack.OPEN listener", e);
return null;
}
});
}
return lazy.get();
}));
listeners -> (type, id, previous, pack) -> {
InputSupplier<InputStream> lazy = previous;
for (Open listener : listeners) {
lazy = listener.open(type, id, lazy, pack);
}
return lazy;
});
public static final Event<OpenRoot> OPEN_ROOT = EventFactory.createArrayBacked(OpenRoot.class,
(listeners) -> ((fileName, previous, pack) -> {
LazySupplier<InputStream> lazy = new LazySupplier<>(previous);
for (OpenRoot listener : listeners) {
lazy = lazy.andThen(supplier -> {
try {
return listener.openRoot(fileName, supplier, pack);
} catch (IOException e) {
LibJf.LOGGER.error("Could not call ResourcePack.OPEN_ROOT listener", e);
return null;
}
});
}
return lazy.get();
}));
public static final Event<FindResource> FIND_RESOURCE = EventFactory.createArrayBacked(FindResource.class,
listeners -> (type, namespace, prefix, previous, pack) -> {
ResourcePack.ResultConsumer lazy = previous;
for (FindResource listener : listeners) {
lazy = listener.findResources(type, namespace, prefix, lazy, pack);
}
return lazy;
});
public interface Contains {
boolean contains(ResourceType type, Identifier id, Supplier<Boolean> previous, ResourcePack pack);
}
public static final Event<ParseMetadata> PARSE_METADATA = EventFactory.createArrayBacked(ParseMetadata.class, ParseMetadataHandler::new);
public interface FindResource {
Collection<Identifier> findResources(ResourceType type, String namespace, String prefix, Predicate<Identifier> allowedPathPredicate, Supplier<Collection<Identifier>> previous, ResourcePack pack);
}
// Here because generic targets don't work
private static class ParseMetadataHandler implements ParseMetadata {
private final ParseMetadata[] listeners;
public interface Open {
InputStream open(ResourceType type, Identifier id, Supplier<InputStream> previous, ResourcePack pack) throws IOException;
public ParseMetadataHandler(ParseMetadata[] listeners) {
this.listeners = listeners;
}
@Override
public <T> T parseMetadata(ResourceMetadataReader<T> reader, Supplier<T> previous, ResourcePack pack) throws IOException {
LazySupplier<T> lazy = new LazySupplier<>(previous);
for (ParseMetadata listener: listeners) {
lazy = lazy.andThen(supplier -> {
try {
return listener.parseMetadata(reader, supplier, pack);
} catch (IOException e) {
LibJf.LOGGER.error("Could not call ResourcePack.OPEN_ROOT listener", e);
return null;
}
});
}
return lazy.get();
}
}
public interface OpenRoot {
InputStream openRoot(String fileName, Supplier<InputStream> previous, ResourcePack pack) throws IOException;
InputSupplier<InputStream> openRoot(String[] fileName, InputSupplier<InputStream> previous, ResourcePack pack);
}
public interface Open {
InputSupplier<InputStream> open(ResourceType type, Identifier id, InputSupplier<InputStream> previous, ResourcePack pack);
}
public interface FindResource {
ResourcePack.ResultConsumer findResources(ResourceType type, String namespace, String prefix, ResourcePack.ResultConsumer previous, ResourcePack pack);
}
public interface ParseMetadata {
<T> T parseMetadata(ResourceMetadataReader<T> reader, Supplier<T> previous, ResourcePack pack) throws IOException;
}
}

View File

@ -1,20 +1,16 @@
package io.gitlab.jfronny.libjf.data.manipulation.impl;
import io.gitlab.jfronny.commons.LazySupplier;
import io.gitlab.jfronny.libjf.ResourcePath;
import io.gitlab.jfronny.libjf.data.manipulation.api.UserResourceEvents;
import net.minecraft.resource.ResourcePack;
import net.minecraft.resource.ResourceType;
import net.minecraft.resource.*;
import net.minecraft.resource.metadata.ResourceMetadataReader;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.ApiStatus;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
@SuppressWarnings("unused")
@ApiStatus.Internal
@ -31,42 +27,32 @@ public class ResourcePackHook {
return disabled.getOrDefault(Thread.currentThread().getId(), false);
}
public static boolean hookContains(boolean value, ResourcePack pack, ResourceType type, Identifier id) {
return isDisabled() ? value : UserResourceEvents.CONTAINS.invoker().contains(type, id, new LazySupplier<>(value), pack);
}
public static InputStream hookOpen(InputStream value, ResourcePack pack, ResourceType type, Identifier id) throws IOException {
public static InputSupplier<InputStream> hookOpenRoot(InputSupplier<InputStream> value, ResourcePack pack, String[] fileName) {
if (isDisabled()) return value;
InputStream is = UserResourceEvents.OPEN.invoker().open(type, id, new LazySupplier<>(value), pack);
if (is == null)
throw new FileNotFoundException(new ResourcePath(type, id).getName() + "CN");
return is;
return UserResourceEvents.OPEN_ROOT.invoker().openRoot(fileName, value, pack);
}
public static InputStream hookOpenEx(IOException ex, ResourcePack pack, ResourceType type, Identifier id) throws IOException {
public static InputSupplier<InputStream> hookOpen(InputSupplier<InputStream> value, ResourcePack pack, ResourceType type, Identifier id) {
if (isDisabled()) return value;
return UserResourceEvents.OPEN.invoker().open(type, id, value, pack);
}
public static ResourcePack.ResultConsumer hookFindResources(ResourcePack pack, ResourceType type, String namespace, String prefix, ResourcePack.ResultConsumer target) {
if (isDisabled()) return target;
return UserResourceEvents.FIND_RESOURCE.invoker().findResources(type, namespace, prefix, target, pack);
}
public static <T> T hookParseMetadata(T value, ResourcePack pack, ResourceMetadataReader<T> reader) throws IOException {
if (isDisabled()) return value;
return UserResourceEvents.PARSE_METADATA.invoker().parseMetadata(reader, new LazySupplier<>(value), pack);
}
public static <T> T hookParseMetadataEx(IOException ex, ResourcePack pack, ResourceMetadataReader<T> reader) throws IOException {
if (isDisabled()) throw ex;
try {
return hookOpen(null, pack, type, id);
}
catch (Throwable t) {
throw ex;
}
}
public static Collection<Identifier> hookFindResources(Collection<Identifier> value, ResourcePack pack, ResourceType type, String namespace, String prefix, Predicate<Identifier> allowedPathPredicate) {
return isDisabled() ? value : UserResourceEvents.FIND_RESOURCE.invoker().findResources(type, namespace, prefix, allowedPathPredicate, new LazySupplier<>(value), pack);
}
public static InputStream hookOpenRoot(InputStream value, ResourcePack pack, String fileName) throws IOException {
if (isDisabled()) return value;
InputStream is = value;
is = UserResourceEvents.OPEN_ROOT.invoker().openRoot(fileName, new LazySupplier<>(is), pack);
if (is == null)
throw new FileNotFoundException(fileName);
return is;
}
public static InputStream hookOpenRootEx(IOException ex, ResourcePack pack, String fileName) throws IOException {
if (isDisabled()) throw ex;
try {
return hookOpenRoot(null, pack, fileName);
}
catch (Throwable t) {
return hookParseMetadata(null, pack, reader);
} catch (Throwable t) {
ex.addSuppressed(t);
throw ex;
}
}

View File

@ -1,25 +1,23 @@
package io.gitlab.jfronny.libjf.data.manipulation.impl;
import io.gitlab.jfronny.libjf.unsafe.asm.AsmConfig;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.PatchUtil;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.*;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.modification.MethodModificationPatch;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.targeting.InterfaceImplTargetPatch;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;
public class ResourcePackHookPatch implements AsmConfig {
private static final String TARGET_CLASS_INTERMEDIARY = "net.minecraft.class_3262";
private static final String HOOK_IMPLEMENTATION = Type.getInternalName(ResourcePackHook.class);
@Override
public Set<String> skipClasses() {
return Set.of(PatchUtil.getRemapped(TARGET_CLASS_INTERMEDIARY),
"io.gitlab.jfronny.libjf.data.wrappedPackImpl.WrappedResourcePack",
"io.gitlab.jfronny.libjf.data.wrappedPackImpl.SafeWrappedResourcePack");
return Set.of(PatchUtil.mapClassName(TARGET_CLASS_INTERMEDIARY));
}
@Override
@ -27,31 +25,41 @@ public class ResourcePackHookPatch implements AsmConfig {
// https://maven.fabricmc.net/docs/yarn-21w38a+build.9/net/minecraft/resource/ResourcePack.html
return Set.of(new InterfaceImplTargetPatch(TARGET_CLASS_INTERMEDIARY, new MethodModificationPatch(TARGET_CLASS_INTERMEDIARY, Set.of(
// open root
new MethodModificationPatch.MethodDescriptorPatch("method_14410", "(Ljava/lang/String;)Ljava/io/InputStream;", (method, klazz) -> {
catchEx(method, "hookOpenRootEx", new String[]{"java/lang/String"});
hookReturn(method, "hookOpenRoot", "Ljava/io/InputStream;", new String[]{"java/lang/String"});
new MethodModificationPatch.MethodDescriptorPatch("method_14410", "([Ljava/lang/String;)Lnet/minecraft/class_7367;", (method, klazz) -> {
hookReturn(method, "hookOpenRoot", "Lnet/minecraft/class_7367;", "[java/lang/String");
}),
// open
new MethodModificationPatch.MethodDescriptorPatch("method_14405", "(Lnet/minecraft/class_3264;Lnet/minecraft/class_2960;)Ljava/io/InputStream;", (method, klazz) -> {
catchEx(method, "hookOpenEx", new String[]{"net/minecraft/class_3264", "net/minecraft/class_2960"});
hookReturn(method, "hookOpen", "Ljava/io/InputStream;", new String[]{"net/minecraft/class_3264", "net/minecraft/class_2960"});
new MethodModificationPatch.MethodDescriptorPatch("method_14405", "(Lnet/minecraft/class_3264;Lnet/minecraft/class_2960;)Lnet/minecraft/class_7367;", (method, klazz) -> {
hookReturn(method, "hookOpen", "Lnet/minecraft/class_7367;", "net/minecraft/class_3264", "net/minecraft/class_2960");
}),
// find resource
new MethodModificationPatch.MethodDescriptorPatch("method_14408", "(Lnet/minecraft/class_3264;Ljava/lang/String;Ljava/lang/String;Ljava/util/function/Predicate;)Ljava/util/Collection;", (method, klazz) -> {
hookReturn(method, "hookFindResources", "Ljava/util/Collection;", new String[]{"net/minecraft/class_3264", "java/lang/String", "java/lang/String", "java/util/function/Predicate"});
new MethodModificationPatch.MethodDescriptorPatch("method_14408", "(Lnet/minecraft/class_3264;Ljava/lang/String;Ljava/lang/String;Lnet/minecraft/class_3262$class_7664;)V", (method, klazz) -> {
InsnList head = PatchUtil.buildParamPassingInvoker(
TARGET_CLASS_INTERMEDIARY,
HOOK_IMPLEMENTATION,
"hookFindResources",
null,
"Lnet/minecraft/class_3262$class_7664;",
"net/minecraft/class_3264", "java/lang/String", "java/lang/String", "net/minecraft/class_3262$class_7664");
head.add(new VarInsnNode(Opcodes.ASTORE, 4));
method.instructions.insert(head);
}),
// contains
new MethodModificationPatch.MethodDescriptorPatch("method_14411", "(Lnet/minecraft/class_3264;Lnet/minecraft/class_2960;)Z", (method, klazz) -> {
hookReturn(method, "hookContains", "Z", new String[]{"net/minecraft/class_3264", "net/minecraft/class_2960"});
// parse metadata
new MethodModificationPatch.MethodDescriptorPatch("method_14407", "(Lnet/minecraft/class_3270;)Ljava/lang/Object;", (method, klazz) -> {
hookReturn(method, "hookParseMetadata", "Ljava/lang/Object;", "net/minecraft/class_3270");
PatchUtil.redirectExceptions(
method,
TARGET_CLASS_INTERMEDIARY,
HOOK_IMPLEMENTATION,
Type.getInternalName(IOException.class),
Type.getInternalName(Object.class),
"hookParseMetadataEx",
"net/minecraft/class_3270");
})
))));
}
private void hookReturn(MethodNode method, String targetMethod, String targetType, String[] extraParamTypes) {
private void hookReturn(MethodNode method, String targetMethod, String targetType, String... extraParamTypes) {
PatchUtil.redirectReturn(method, TARGET_CLASS_INTERMEDIARY, HOOK_IMPLEMENTATION, targetMethod, targetType, extraParamTypes);
}
private void catchEx(MethodNode method, String targetMethod, String[] extraParamTypes) {
PatchUtil.redirectExceptions(method, TARGET_CLASS_INTERMEDIARY, HOOK_IMPLEMENTATION, Type.getInternalName(IOException.class), Type.getInternalName(InputStream.class), targetMethod, extraParamTypes);
}
}

View File

@ -14,14 +14,9 @@ public class TestEntrypoint implements ModInitializer {
UserResourceEvents.OPEN.register((type, id, previous, pack) -> {
if (pack instanceof DirectoryResourcePack) {
LibJf.LOGGER.info(pack.getName() + " opened " + type.name() + "/" + id.toString());
return null;
}
return previous.get();
});
UserResourceEvents.CONTAINS.register((type, id, previous, pack) -> {
if (pack instanceof DirectoryResourcePack) {
return false;
}
return previous.get();
return previous;
});
RecipeUtil.removeRecipeFor(Items.DIAMOND_SWORD);
}

View File

@ -2,11 +2,11 @@ package io.gitlab.jfronny.libjf.data;
import io.gitlab.jfronny.libjf.LibJf;
import net.minecraft.item.Item;
import net.minecraft.tag.TagKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
public class Tags {
public static final TagKey<Item> SHULKER_ILLEGAL = TagKey.of(Registry.ITEM_KEY, new Identifier(LibJf.MOD_ID, "shulker_boxes_illegal"));
public static final TagKey<Item> OVERPOWERED = TagKey.of(Registry.ITEM_KEY, new Identifier(LibJf.MOD_ID, "overpowered"));
public static final TagKey<Item> SHULKER_ILLEGAL = TagKey.of(RegistryKeys.ITEM, new Identifier(LibJf.MOD_ID, "shulker_boxes_illegal"));
public static final TagKey<Item> OVERPOWERED = TagKey.of(RegistryKeys.ITEM, new Identifier(LibJf.MOD_ID, "overpowered"));
}

View File

@ -1,31 +1,21 @@
package io.gitlab.jfronny.libjf.devutil.mixin;
import com.mojang.authlib.minecraft.UserApiService;
import net.minecraft.client.util.ProfileKeys;
import net.minecraft.network.encryption.PlayerKeyPair;
import net.minecraft.network.encryption.PlayerPublicKey;
import net.minecraft.client.util.Session;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.nio.file.Path;
@Mixin(ProfileKeys.class)
public class ProfileKeysMixin {
public interface ProfileKeysMixin {
/**
* @author JFronny
* @reason Do not load keys!
*/
@Overwrite
public CompletableFuture<Optional<PlayerPublicKey.PublicKeyData>> refresh() {
return CompletableFuture.completedFuture(Optional.empty());
}
/**
* @author JFronny
* @reason Do not load keys!
*/
@Overwrite
private CompletableFuture<Optional<Object>> getKeyPair(Optional<PlayerKeyPair> currentKey) {
return CompletableFuture.completedFuture(Optional.empty());
static ProfileKeys create(UserApiService service, Session session, Path root) {
return ProfileKeys.MISSING;
}
}

View File

@ -36,36 +36,6 @@ public class NoOpUserApi implements UserApiService {
return false;
}
@Override
public TelemetryPropertyContainer globalProperties() {
return new TelemetryPropertyContainer() {
@Override
public void addProperty(String id, String value) {
// ignored
}
@Override
public void addProperty(String id, int value) {
// ignored
}
@Override
public void addProperty(String id, boolean value) {
// ignored
}
@Override
public void addNullProperty(String id) {
// ignored
}
};
}
@Override
public void eventSetupFunction(Consumer<TelemetryPropertyContainer> eventSetupFunction) {
// ignored
}
@Override
public TelemetryEvent createNewEvent(String type) {
return new TelemetryEvent() {

View File

@ -3,7 +3,7 @@ package io.gitlab.jfronny.libjf.devutil.mixin;
import com.mojang.brigadier.arguments.*;
import net.minecraft.command.argument.*;
import net.minecraft.command.argument.serialize.*;
import net.minecraft.util.registry.*;
import net.minecraft.registry.Registry;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.*;
@ -14,7 +14,7 @@ public abstract class ArgumentTypesMixin {
throw new RuntimeException("Mixin not applied properly");
}
@Redirect(method = "register(Lnet/minecraft/util/registry/Registry;)Lnet/minecraft/command/argument/serialize/ArgumentSerializer;", at = @At(value = "INVOKE", target = "Lnet/minecraft/command/argument/ArgumentTypes;register(Lnet/minecraft/util/registry/Registry;Ljava/lang/String;Ljava/lang/Class;Lnet/minecraft/command/argument/serialize/ArgumentSerializer;)Lnet/minecraft/command/argument/serialize/ArgumentSerializer;"))
@Redirect(method = "register(Lnet/minecraft/registry/Registry;)Lnet/minecraft/command/argument/serialize/ArgumentSerializer;", at = @At(value = "INVOKE", target = "Lnet/minecraft/command/argument/ArgumentTypes;register(Lnet/minecraft/registry/Registry;Ljava/lang/String;Ljava/lang/Class;Lnet/minecraft/command/argument/serialize/ArgumentSerializer;)Lnet/minecraft/command/argument/serialize/ArgumentSerializer;"))
private static <A extends ArgumentType<?>, T extends ArgumentSerializer.ArgumentTypeProperties<A>> ArgumentSerializer<A, T> libjf$redirectRegister(Registry<ArgumentSerializer<?, ?>> registry, String id, Class<? extends A> clazz, ArgumentSerializer<A, T> serializer) {
if (clazz != TestFunctionArgumentType.class && clazz != TestClassArgumentType.class)
return register(registry, id, clazz, serializer);

View File

@ -0,0 +1,17 @@
package io.gitlab.jfronny.libjf.unsafe.asm.patch;
import io.gitlab.jfronny.libjf.LibJf;
import org.objectweb.asm.tree.*;
public class PatchDebug {
public static void print(InsnList instructions) {
StringBuilder sb = new StringBuilder("InsnList:");
for (AbstractInsnNode node : instructions) {
sb.append('\n').append(node.getClass().getSimpleName()).append(' ').append(Integer.toHexString(node.getOpcode()));
if (node instanceof VarInsnNode v) sb.append(' ').append(v.var);
else if (node instanceof LabelNode v) sb.append(' ').append(v.getLabel().toString());
else if (node instanceof MethodInsnNode v) sb.append(" L").append(v.owner).append(';').append(v.name).append(v.desc).append(" (I=").append(v.itf).append(')');
}
LibJf.LOGGER.debug(sb.toString());
}
}

View File

@ -5,62 +5,104 @@ import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.*;
public class PatchUtil {
public static String getRemappedInternal(String className) {
return getRemapped(className.replace('/', '.')).replace('.', '/');
public static String mapClassNameInternal(String className) {
return mapClassName(className.replace('/', '.')).replace('.', '/');
}
public static String getRemapped(String className) {
public static String mapClassName(String className) {
return AsmTransformer.MAPPING_RESOLVER.mapClassName(AsmTransformer.INTERMEDIARY, className);
}
public static void redirectReturn(MethodNode method, String targetClass, String hookClass, String targetMethod, String targetType, String[] extraParamTypes) {
public static String mapMethodName(String owner, String name, String descriptor) {
return AsmTransformer.MAPPING_RESOLVER.mapMethodName(AsmTransformer.INTERMEDIARY, owner, name, descriptor);
}
public static String mapDescriptor(String descriptor) {
return mapDescriptor(descriptor, 0, descriptor.length());
}
// Adapted from mapping-io
public static String mapDescriptor(String desc, int start, int end) {
StringBuilder ret = null;
int searchStart = start;
int clsStart;
while ((clsStart = desc.indexOf('L', searchStart)) >= 0) {
int clsEnd = desc.indexOf(';', clsStart + 1);
if (clsEnd < 0) throw new IllegalArgumentException();
String cls = desc.substring(clsStart + 1, clsEnd);
String mappedCls = PatchUtil.mapClassNameInternal(cls);
if (ret == null) ret = new StringBuilder(end - start);
ret.append(desc, start, clsStart + 1);
ret.append(mappedCls);
start = clsEnd;
searchStart = clsEnd + 1;
}
if (ret == null) return desc.substring(start, end);
ret.append(desc, start, end);
System.out.println("Transformed " + desc + " to " + ret);
return ret.toString();
}
public static void redirectReturn(MethodNode method, String targetClass, String hookClass, String targetMethod, String targetType, String... extraParamTypes) {
for (AbstractInsnNode node : method.instructions) {
if (node.getOpcode() == Opcodes.ARETURN
|| node.getOpcode() == Opcodes.IRETURN
|| node.getOpcode() == Opcodes.DRETURN
|| node.getOpcode() == Opcodes.FRETURN
|| node.getOpcode() == Opcodes.LRETURN) {
method.instructions.insertBefore(node, PatchUtil.buildParamPassingInvoker(targetClass, hookClass, targetMethod, targetType, targetType, extraParamTypes));
method.instructions.insertBefore(node, buildParamPassingInvoker(targetClass, hookClass, targetMethod, targetType, targetType, extraParamTypes));
}
}
}
public static void redirectExceptions(MethodNode method, String targetClass, String hookClass, String exceptionType, String returnTypeNormal, String targetMethod, String[] extraParamTypes) {
public static void redirectExceptions(MethodNode method, String targetClass, String hookClass, String exceptionType, String returnTypeNormal, String targetMethod, String... extraParamTypes) {
LabelNode start = new LabelNode();
LabelNode end = new LabelNode();
LabelNode handler = new LabelNode();
method.instructions.insertBefore(method.instructions.getFirst(), start);
method.instructions.insertBefore(method.instructions.getLast(), end);
method.instructions.add(handler);
method.instructions.add(PatchUtil.buildParamPassingInvoker(targetClass,
method.instructions.add(buildParamPassingInvoker(targetClass,
hookClass,
targetMethod,
"L" + getRemappedInternal(exceptionType) + ";",
"L" + getRemappedInternal(returnTypeNormal) + ";",
"L" + exceptionType + ";",
"L" + returnTypeNormal + ";",
extraParamTypes));
method.instructions.add(new InsnNode(Opcodes.ARETURN));
method.tryCatchBlocks.add(new TryCatchBlockNode(start, end, handler, exceptionType));
}
public static InsnList buildParamPassingInvoker(String targetClass, String hookClass, String targetMethod, String inType, String outType, String[] extraParamTypes) {
public static InsnList buildParamPassingInvoker(String targetClass, String hookClass, String targetMethod, String inType, String outType, String... extraParamTypes) {
InsnList instructions = new InsnList();
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
StringBuilder descriptor = new StringBuilder("(").append(inType);
descriptor.append('L').append(getRemappedInternal(targetClass)).append(';');
StringBuilder descriptor = new StringBuilder("(");
if (inType != null) descriptor.append(mapDescriptor(inType));
descriptor.append('L').append(mapClassNameInternal(targetClass)).append(';');
for (int i = 0; i < extraParamTypes.length; i++) {
instructions.add(new VarInsnNode(switch (extraParamTypes[i]) {
String param = extraParamTypes[i];
instructions.add(new VarInsnNode(switch (param) {
case "I" -> Opcodes.ILOAD;
case "D" -> Opcodes.DLOAD;
case "F" -> Opcodes.FLOAD;
case "L" -> Opcodes.LLOAD;
default -> Opcodes.ALOAD;
}, i + 1));
if (extraParamTypes[i].length() == 1)
descriptor.append(extraParamTypes[i]);
else
descriptor.append('L').append(PatchUtil.getRemappedInternal(extraParamTypes[i])).append(';');
while (param.charAt(0) == '[') {
descriptor.append('[');
param = param.substring(1);
}
if (param.length() == 1) descriptor.append(param);
else descriptor.append('L').append(mapClassNameInternal(param)).append(';');
}
descriptor.append(")").append(outType);
descriptor.append(")").append(mapDescriptor(outType));
instructions.add(new MethodInsnNode(Opcodes.INVOKESTATIC, hookClass, targetMethod, descriptor.toString()));
return instructions;
}

View File

@ -2,26 +2,24 @@ package io.gitlab.jfronny.libjf.unsafe.asm.patch.modification;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.unsafe.asm.AsmTransformer;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.MethodPatch;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.*;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.*;
public class MethodModificationPatch implements Patch {
private final Map<String, MethodPatch> patches = new LinkedHashMap<>();
private final Map<Key, MethodPatch> patches = new LinkedHashMap<>();
public MethodModificationPatch(String targetMethodOwner, Set<MethodDescriptorPatch> patches) {
for (MethodDescriptorPatch patch : patches) {
this.patches.put(
AsmTransformer.MAPPING_RESOLVER.mapMethodName(AsmTransformer.INTERMEDIARY, targetMethodOwner, patch.methodName, patch.methodDescriptor),
new Key(PatchUtil.mapMethodName(targetMethodOwner, patch.methodName, patch.methodDescriptor),
PatchUtil.mapDescriptor(patch.methodDescriptor)),
patch.patch
);
}
for (String s : this.patches.keySet()) {
for (Key s : this.patches.keySet()) {
if (AsmTransformer.INSTANCE.debugLogsEnabled()) LibJf.LOGGER.info("Registered patch for " + s);
}
}
@ -29,14 +27,21 @@ public class MethodModificationPatch implements Patch {
@Override
public void apply(ClassNode klazz) {
for (MethodNode method : klazz.methods) {
if (patches.containsKey(method.name)) {
MethodPatch mp = patches.get(new Key(method.name, method.desc));
if (mp != null) {
if (AsmTransformer.INSTANCE.debugLogsEnabled()) LibJf.LOGGER.info("Patching " + method.name);
patches.get(method.name).apply(method, klazz);
mp.apply(method, klazz);
}
}
}
public record MethodDescriptorPatch(String methodName, String methodDescriptor, MethodPatch patch) {
}
private record Key(String name, String descriptor) {
@Override
public String toString() {
return name + descriptor;
}
}
}

View File

@ -14,8 +14,7 @@
"license": "MIT",
"icon": "assets/libjf/icon.png",
"environment": "*",
"entrypoints": {
},
"entrypoints": {},
"depends": {
"fabricloader": ">=0.12.0",
"minecraft": "*",