minecraft-src/net/minecraft/util/datafix/fixes/TextComponentHoverAndClickEventFix.java
2025-07-04 03:45:38 +03:00

165 lines
5.8 KiB
Java

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.datafixers.util.Unit;
import com.mojang.serialization.Dynamic;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import net.minecraft.Util;
import net.minecraft.util.datafix.ExtraDataFixUtils;
import org.jetbrains.annotations.Nullable;
public class TextComponentHoverAndClickEventFix extends DataFix {
public TextComponentHoverAndClickEventFix(Schema outputSchema) {
super(outputSchema, true);
}
@Override
protected TypeRewriteRule makeRule() {
Type<? extends Pair<String, ?>> type = (Type<? extends Pair<String, ?>>)this.getInputSchema().getType(References.TEXT_COMPONENT).findFieldType("hoverEvent");
return this.createFixer(this.getInputSchema().getTypeRaw(References.TEXT_COMPONENT), this.getOutputSchema().getType(References.TEXT_COMPONENT), type);
}
private <C1, C2, H extends Pair<String, ?>> TypeRewriteRule createFixer(Type<C1> inputComponentType, Type<C2> outputComponentType, Type<H> hoverEventType) {
Type<Pair<String, Either<Either<String, List<C1>>, Pair<Either<List<C1>, Unit>, Pair<Either<C1, Unit>, Pair<Either<H, Unit>, Dynamic<?>>>>>>> type = DSL.named(
References.TEXT_COMPONENT.typeName(),
DSL.or(
DSL.or(DSL.string(), DSL.list(inputComponentType)),
DSL.and(
DSL.optional(DSL.field("extra", DSL.list(inputComponentType))),
DSL.optional(DSL.field("separator", inputComponentType)),
DSL.optional(DSL.field("hoverEvent", hoverEventType)),
DSL.remainderType()
)
)
);
if (!type.equals(this.getInputSchema().getType(References.TEXT_COMPONENT))) {
throw new IllegalStateException(
"Text component type did not match, expected " + type + " but got " + this.getInputSchema().getType(References.TEXT_COMPONENT)
);
} else {
Type<?> type2 = ExtraDataFixUtils.patchSubType(type, type, outputComponentType);
return this.fixTypeEverywhere(
"TextComponentHoverAndClickEventFix",
type,
outputComponentType,
dynamicOps -> pair -> {
boolean bl = ((Either)pair.getSecond()).<Boolean>map(either -> false, pairx -> {
Pair<Either<H, Unit>, Dynamic<?>> pair2 = (Pair<Either<H, Unit>, Dynamic<?>>)((Pair)pairx.getSecond()).getSecond();
boolean blx = pair2.getFirst().left().isPresent();
boolean bl2 = pair2.getSecond().get("clickEvent").result().isPresent();
return blx || bl2;
});
return !bl
? pair
: Util.writeAndReadTypedOrThrow(
ExtraDataFixUtils.cast(type2, pair, dynamicOps), outputComponentType, TextComponentHoverAndClickEventFix::fixTextComponent
)
.getValue();
}
);
}
}
private static Dynamic<?> fixTextComponent(Dynamic<?> data) {
return data.renameAndFixField("hoverEvent", "hover_event", TextComponentHoverAndClickEventFix::fixHoverEvent)
.renameAndFixField("clickEvent", "click_event", TextComponentHoverAndClickEventFix::fixClickEvent);
}
private static Dynamic<?> copyFields(Dynamic<?> newData, Dynamic<?> oldData, String... fields) {
for (String string : fields) {
newData = Dynamic.copyField(oldData, string, newData, string);
}
return newData;
}
private static Dynamic<?> fixHoverEvent(Dynamic<?> data) {
String string = data.get("action").asString("");
return switch (string) {
case "show_text" -> data.renameField("contents", "value");
case "show_item" -> {
Dynamic<?> dynamic = data.get("contents").orElseEmptyMap();
Optional<String> optional = dynamic.asString().result();
yield optional.isPresent() ? data.renameField("contents", "id") : copyFields(data.remove("contents"), dynamic, "id", "count", "components");
}
case "show_entity" -> {
Dynamic<?> dynamic = data.get("contents").orElseEmptyMap();
yield copyFields(data.remove("contents"), dynamic, "id", "type", "name").renameField("id", "uuid").renameField("type", "id");
}
default -> data;
};
}
@Nullable
private static <T> Dynamic<T> fixClickEvent(Dynamic<T> data) {
String string = data.get("action").asString("");
String string2 = data.get("value").asString("");
return switch (string) {
case "open_url" -> !validateUri(string2) ? null : data.renameField("value", "url");
case "open_file" -> data.renameField("value", "path");
case "run_command", "suggest_command" -> !validateChat(string2) ? null : data.renameField("value", "command");
case "change_page" -> {
Integer integer = (Integer)data.get("value").result().map(TextComponentHoverAndClickEventFix::parseOldPage).orElse(null);
if (integer == null) {
yield null;
} else {
int i = Math.max(integer, 1);
yield data.remove("value").set("page", data.createInt(i));
}
}
default -> data;
};
}
@Nullable
private static Integer parseOldPage(Dynamic<?> data) {
Optional<Number> optional = data.asNumber().result();
if (optional.isPresent()) {
return ((Number)optional.get()).intValue();
} else {
try {
return Integer.parseInt(data.asString(""));
} catch (Exception var3) {
return null;
}
}
}
private static boolean validateUri(String uri) {
try {
URI uRI = new URI(uri);
String string = uRI.getScheme();
if (string == null) {
return false;
} else {
String string2 = string.toLowerCase(Locale.ROOT);
return "http".equals(string2) || "https".equals(string2);
}
} catch (URISyntaxException var4) {
return false;
}
}
private static boolean validateChat(String chat) {
for (int i = 0; i < chat.length(); i++) {
char c = chat.charAt(i);
if (c == 167 || c < ' ' || c == 127) {
return false;
}
}
return true;
}
}