minecraft_25w45a_unobfuscated/net/minecraft/util/datafix/fixes/TextComponentHoverAndClickEventFix.java
2025-11-24 22:52:51 +03:00

163 lines
7.4 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.mojang.datafixers.DSL
* com.mojang.datafixers.DataFix
* com.mojang.datafixers.TypeRewriteRule
* com.mojang.datafixers.schemas.Schema
* com.mojang.datafixers.types.Type
* com.mojang.datafixers.util.Either
* com.mojang.datafixers.util.Pair
* com.mojang.serialization.Dynamic
* org.jspecify.annotations.Nullable
*/
package net.minecraft.util.datafix.fixes;
import com.mojang.datafixers.DSL;
import com.mojang.datafixers.DataFix;
import com.mojang.datafixers.TypeRewriteRule;
import com.mojang.datafixers.schemas.Schema;
import com.mojang.datafixers.types.Type;
import com.mojang.datafixers.util.Either;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.Dynamic;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Locale;
import java.util.Optional;
import net.minecraft.util.Util;
import net.minecraft.util.datafix.ExtraDataFixUtils;
import net.minecraft.util.datafix.fixes.References;
import org.jspecify.annotations.Nullable;
public class TextComponentHoverAndClickEventFix
extends DataFix {
public TextComponentHoverAndClickEventFix(Schema outputSchema) {
super(outputSchema, true);
}
protected TypeRewriteRule makeRule() {
Type hoverEventType = this.getInputSchema().getType(References.TEXT_COMPONENT).findFieldType("hoverEvent");
return this.createFixer(this.getInputSchema().getTypeRaw(References.TEXT_COMPONENT), this.getOutputSchema().getType(References.TEXT_COMPONENT), hoverEventType);
}
private <C1, C2, H extends Pair<String, ?>> TypeRewriteRule createFixer(Type<C1> oldRawTextComponentType, Type<C2> newTextComponentType, Type<H> hoverEventType) {
Type oldTextComponentType = DSL.named((String)References.TEXT_COMPONENT.typeName(), (Type)DSL.or((Type)DSL.or((Type)DSL.string(), (Type)DSL.list(oldRawTextComponentType)), (Type)DSL.and((Type)DSL.optional((Type)DSL.field((String)"extra", (Type)DSL.list(oldRawTextComponentType))), (Type)DSL.optional((Type)DSL.field((String)"separator", oldRawTextComponentType)), (Type)DSL.optional((Type)DSL.field((String)"hoverEvent", hoverEventType)), (Type)DSL.remainderType())));
if (!oldTextComponentType.equals((Object)this.getInputSchema().getType(References.TEXT_COMPONENT))) {
throw new IllegalStateException("Text component type did not match, expected " + String.valueOf(oldTextComponentType) + " but got " + String.valueOf(this.getInputSchema().getType(References.TEXT_COMPONENT)));
}
Type<?> patchedInputType = ExtraDataFixUtils.patchSubType(oldTextComponentType, oldTextComponentType, newTextComponentType);
return this.fixTypeEverywhere("TextComponentHoverAndClickEventFix", oldTextComponentType, newTextComponentType, ops -> textComponent -> {
boolean hasHoverOrClick = (Boolean)((Either)textComponent.getSecond()).map(simple -> false, full -> {
Pair hoverAndRemainder = (Pair)((Pair)full.getSecond()).getSecond();
boolean hasHover = ((Either)hoverAndRemainder.getFirst()).left().isPresent();
boolean hasClick = ((Dynamic)hoverAndRemainder.getSecond()).get("clickEvent").result().isPresent();
return hasHover || hasClick;
});
if (!hasHoverOrClick) {
return textComponent;
}
return Util.writeAndReadTypedOrThrow(ExtraDataFixUtils.cast(patchedInputType, textComponent, ops), newTextComponentType, TextComponentHoverAndClickEventFix::fixTextComponent).getValue();
});
}
private static Dynamic<?> fixTextComponent(Dynamic<?> dynamic) {
return dynamic.renameAndFixField("hoverEvent", "hover_event", TextComponentHoverAndClickEventFix::fixHoverEvent).renameAndFixField("clickEvent", "click_event", TextComponentHoverAndClickEventFix::fixClickEvent);
}
private static Dynamic<?> copyFields(Dynamic<?> target, Dynamic<?> source, String ... fields) {
for (String field : fields) {
target = Dynamic.copyField(source, (String)field, target, (String)field);
}
return target;
}
private static Dynamic<?> fixHoverEvent(Dynamic<?> dynamic) {
String action;
return switch (action = dynamic.get("action").asString("")) {
case "show_text" -> dynamic.renameField("contents", "value");
case "show_item" -> {
Dynamic contents = dynamic.get("contents").orElseEmptyMap();
Optional simpleId = contents.asString().result();
if (simpleId.isPresent()) {
yield dynamic.renameField("contents", "id");
}
yield TextComponentHoverAndClickEventFix.copyFields(dynamic.remove("contents"), contents, "id", "count", "components");
}
case "show_entity" -> {
Dynamic contents = dynamic.get("contents").orElseEmptyMap();
yield TextComponentHoverAndClickEventFix.copyFields(dynamic.remove("contents"), contents, "id", "type", "name").renameField("id", "uuid").renameField("type", "id");
}
default -> dynamic;
};
}
private static <T> @Nullable Dynamic<T> fixClickEvent(Dynamic<T> dynamic) {
String action = dynamic.get("action").asString("");
String value = dynamic.get("value").asString("");
return switch (action) {
case "open_url" -> {
if (!TextComponentHoverAndClickEventFix.validateUri(value)) {
yield null;
}
yield dynamic.renameField("value", "url");
}
case "open_file" -> dynamic.renameField("value", "path");
case "run_command", "suggest_command" -> {
if (!TextComponentHoverAndClickEventFix.validateChat(value)) {
yield null;
}
yield dynamic.renameField("value", "command");
}
case "change_page" -> {
Integer oldPage = dynamic.get("value").result().map(TextComponentHoverAndClickEventFix::parseOldPage).orElse(null);
if (oldPage == null) {
yield null;
}
int page = Math.max(oldPage, 1);
yield dynamic.remove("value").set("page", dynamic.createInt(page));
}
default -> dynamic;
};
}
private static @Nullable Integer parseOldPage(Dynamic<?> value) {
Optional numberValue = value.asNumber().result();
if (numberValue.isPresent()) {
return ((Number)numberValue.get()).intValue();
}
try {
return Integer.parseInt(value.asString(""));
}
catch (Exception ignored) {
return null;
}
}
private static boolean validateUri(String uri) {
try {
URI parsedUri = new URI(uri);
String scheme = parsedUri.getScheme();
if (scheme == null) {
return false;
}
String protocol = scheme.toLowerCase(Locale.ROOT);
return "http".equals(protocol) || "https".equals(protocol);
}
catch (URISyntaxException e) {
return false;
}
}
private static boolean validateChat(String string) {
for (int i = 0; i < string.length(); ++i) {
char c = string.charAt(i);
if (c != '\u00a7' && c >= ' ' && c != '\u007f') continue;
return false;
}
return true;
}
}