165 lines
		
	
	
	
		
			5.8 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			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;
 | |
| 	}
 | |
| }
 |