diff --git a/src/main/java/dev/latvian/mods/kubejs/BuiltinKubeJSPlugin.java b/src/main/java/dev/latvian/mods/kubejs/BuiltinKubeJSPlugin.java index 9274249db..dd1cef947 100644 --- a/src/main/java/dev/latvian/mods/kubejs/BuiltinKubeJSPlugin.java +++ b/src/main/java/dev/latvian/mods/kubejs/BuiltinKubeJSPlugin.java @@ -59,7 +59,6 @@ import dev.latvian.mods.kubejs.fluid.ThickFluidBuilder; import dev.latvian.mods.kubejs.fluid.ThinFluidBuilder; import dev.latvian.mods.kubejs.item.ArmorMaterialBuilder; -import dev.latvian.mods.kubejs.item.ChancedItem; import dev.latvian.mods.kubejs.item.ItemEnchantmentsWrapper; import dev.latvian.mods.kubejs.item.ItemPredicate; import dev.latvian.mods.kubejs.item.ItemStackJS; @@ -148,6 +147,7 @@ import dev.latvian.mods.kubejs.util.ScheduledEvents; import dev.latvian.mods.kubejs.util.SlotFilter; import dev.latvian.mods.kubejs.util.TimeJS; +import dev.latvian.mods.kubejs.util.Tristate; import dev.latvian.mods.kubejs.util.UtilsJS; import dev.latvian.mods.kubejs.util.registrypredicate.RegistryPredicate; import dev.latvian.mods.rhino.type.RecordTypeInfo; @@ -484,7 +484,6 @@ public void registerBindings(BindingRegistry bindings) { bindings.add("FluidAmounts", FluidAmounts.class); bindings.add("Notification", NotificationToastData.class); bindings.add("SizedIngredient", SizedIngredientWrapper.class); - bindings.add("ChancedItem", ChancedItem.class); bindings.add("ParticleOptions", ParticleOptionsWrapper.class); bindings.add("Registry", RegistryWrapper.class); @@ -592,7 +591,7 @@ public void registerTypeWrappers(TypeWrapperRegistry registry) { registry.register(ParticleOptions.class, ParticleOptionsWrapper::wrap); registry.register(ItemTintFunction.class, ItemTintFunction::of); registry.register(BlockTintFunction.class, BlockTintFunction::of); - registry.register(ChancedItem.class, ChancedItem::wrap); + registry.register(Tristate.class, Tristate::wrap); // components // registry.register(Component.class, TextWrapper::of); @@ -648,8 +647,6 @@ public void registerRecipeComponents(RecipeComponentFactoryRegistry registry) { registry.register(SizedFluidIngredientComponent.FLAT); registry.register(SizedFluidIngredientComponent.NESTED); - registry.register(ChancedItem.RECIPE_COMPONENT); - registry.register(BlockComponent.BLOCK); registry.register(BlockStateComponent.BLOCK); diff --git a/src/main/java/dev/latvian/mods/kubejs/KubeJSCommon.java b/src/main/java/dev/latvian/mods/kubejs/KubeJSCommon.java index ebff78c4c..7ed7286dc 100644 --- a/src/main/java/dev/latvian/mods/kubejs/KubeJSCommon.java +++ b/src/main/java/dev/latvian/mods/kubejs/KubeJSCommon.java @@ -1,5 +1,6 @@ package dev.latvian.mods.kubejs; +import dev.latvian.mods.kubejs.net.KubeServerData; import dev.latvian.mods.kubejs.script.ConsoleLine; import dev.latvian.mods.kubejs.script.ScriptType; import dev.latvian.mods.kubejs.script.data.ExportablePackResources; @@ -58,4 +59,7 @@ public void runInMainThread(Runnable runnable) { runnable.run(); } } + + public void updateServerData(KubeServerData data) { + } } \ No newline at end of file diff --git a/src/main/java/dev/latvian/mods/kubejs/bindings/event/ItemEvents.java b/src/main/java/dev/latvian/mods/kubejs/bindings/event/ItemEvents.java index b4c603d79..4a2652f33 100644 --- a/src/main/java/dev/latvian/mods/kubejs/bindings/event/ItemEvents.java +++ b/src/main/java/dev/latvian/mods/kubejs/bindings/event/ItemEvents.java @@ -4,6 +4,7 @@ import dev.latvian.mods.kubejs.event.EventHandler; import dev.latvian.mods.kubejs.event.EventTargetType; import dev.latvian.mods.kubejs.event.TargetedEventHandler; +import dev.latvian.mods.kubejs.item.DynamicItemTooltipsKubeEvent; import dev.latvian.mods.kubejs.item.FoodEatenKubeEvent; import dev.latvian.mods.kubejs.item.ItemClickedKubeEvent; import dev.latvian.mods.kubejs.item.ItemCraftedKubeEvent; @@ -15,7 +16,7 @@ import dev.latvian.mods.kubejs.item.ItemPickedUpKubeEvent; import dev.latvian.mods.kubejs.item.ItemSmeltedKubeEvent; import dev.latvian.mods.kubejs.item.ItemStackJS; -import dev.latvian.mods.kubejs.item.ItemTooltipKubeEvent; +import dev.latvian.mods.kubejs.item.ModifyItemTooltipsKubeEvent; import dev.latvian.mods.kubejs.item.custom.ItemToolTierRegistryKubeEvent; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; @@ -35,7 +36,8 @@ public interface ItemEvents { TargetedEventHandler> CRAFTED = GROUP.common("crafted", () -> ItemCraftedKubeEvent.class).supportsTarget(TARGET); TargetedEventHandler> SMELTED = GROUP.common("smelted", () -> ItemSmeltedKubeEvent.class).supportsTarget(TARGET); TargetedEventHandler> FOOD_EATEN = GROUP.common("foodEaten", () -> FoodEatenKubeEvent.class).hasResult().supportsTarget(TARGET); - EventHandler TOOLTIP = GROUP.client("tooltip", () -> ItemTooltipKubeEvent.class); + EventHandler MODIFY_TOOLTIPS = GROUP.common("modifyTooltips", () -> ModifyItemTooltipsKubeEvent.class); + TargetedEventHandler DYNAMIC_TOOLTIPS = GROUP.client("dynamicTooltips", () -> DynamicItemTooltipsKubeEvent.class).requiredTarget(EventTargetType.STRING); EventHandler MODEL_PROPERTIES = GROUP.startup("modelProperties", () -> ItemModelPropertiesKubeEvent.class); TargetedEventHandler> FIRST_RIGHT_CLICKED = GROUP.common("firstRightClicked", () -> ItemClickedKubeEvent.class).supportsTarget(TARGET); TargetedEventHandler> FIRST_LEFT_CLICKED = GROUP.common("firstLeftClicked", () -> ItemClickedKubeEvent.class).supportsTarget(TARGET); diff --git a/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClient.java b/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClient.java index 8ebc93d96..fb8606a47 100644 --- a/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClient.java +++ b/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClient.java @@ -3,8 +3,11 @@ import dev.latvian.mods.kubejs.KubeJS; import dev.latvian.mods.kubejs.KubeJSCommon; import dev.latvian.mods.kubejs.KubeJSPaths; +import dev.latvian.mods.kubejs.bindings.event.ItemEvents; import dev.latvian.mods.kubejs.bindings.event.NetworkEvents; +import dev.latvian.mods.kubejs.item.ModifyItemTooltipsKubeEvent; import dev.latvian.mods.kubejs.kubedex.KubedexHighlight; +import dev.latvian.mods.kubejs.net.KubeServerData; import dev.latvian.mods.kubejs.net.NetworkKubeEvent; import dev.latvian.mods.kubejs.script.ConsoleLine; import dev.latvian.mods.kubejs.script.ScriptType; @@ -12,6 +15,7 @@ import dev.latvian.mods.kubejs.script.data.GeneratedData; import dev.latvian.mods.kubejs.script.data.GeneratedDataStage; import dev.latvian.mods.kubejs.script.data.VirtualAssetPack; +import dev.latvian.mods.kubejs.tooltip.ItemTooltipData; import net.minecraft.SharedConstants; import net.minecraft.Util; import net.minecraft.client.Minecraft; @@ -36,6 +40,7 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.util.ArrayList; import java.util.EnumMap; import java.util.List; import java.util.Map; @@ -46,6 +51,7 @@ public class KubeJSClient extends KubeJSCommon { public static final ResourceLocation RECIPE_BUTTON_TEXTURE = ResourceLocation.parse("textures/gui/recipe_button.png"); public static final Map CLIENT_PACKS = new EnumMap<>(GeneratedDataStage.class); + public static List clientItemTooltips = List.of(); static { for (var stage : GeneratedDataStage.values()) { @@ -59,8 +65,10 @@ public void reloadClientInternal() { } public static void reloadClientScripts() { - KubeJSClientEventHandler.staticItemTooltips = null; KubeJS.getClientScriptManager().reload(); + var list = new ArrayList(); + ItemEvents.MODIFY_TOOLTIPS.post(ScriptType.CLIENT, new ModifyItemTooltipsKubeEvent(list::add)); + clientItemTooltips = List.copyOf(list); } public static void copyDefaultOptionsFile(File optionsFile) { @@ -177,6 +185,15 @@ public void runInMainThread(Runnable runnable) { } } + @Override + public void updateServerData(KubeServerData data) { + var sessionData = KubeSessionData.of(Minecraft.getInstance()); + + if (sessionData != null) { + sessionData.sync(data); + } + } + public static void loadPostChains(Minecraft mc) { KubedexHighlight.INSTANCE.loadPostChains(mc); } diff --git a/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClientEventHandler.java b/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClientEventHandler.java index 63a68a8eb..6f3fc0c94 100644 --- a/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClientEventHandler.java +++ b/src/main/java/dev/latvian/mods/kubejs/client/KubeJSClientEventHandler.java @@ -6,10 +6,14 @@ import dev.latvian.mods.kubejs.bindings.TextIcons; import dev.latvian.mods.kubejs.bindings.event.ClientEvents; import dev.latvian.mods.kubejs.bindings.event.ItemEvents; -import dev.latvian.mods.kubejs.item.ItemTooltipKubeEvent; +import dev.latvian.mods.kubejs.item.DynamicItemTooltipsKubeEvent; import dev.latvian.mods.kubejs.kubedex.KubedexHighlight; import dev.latvian.mods.kubejs.script.ConsoleJS; import dev.latvian.mods.kubejs.script.ScriptType; +import dev.latvian.mods.kubejs.tooltip.ItemTooltipData; +import dev.latvian.mods.kubejs.tooltip.TooltipRequirements; +import dev.latvian.mods.kubejs.tooltip.action.DynamicTooltipAction; +import dev.latvian.mods.kubejs.util.Tristate; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.ImageButton; import net.minecraft.client.gui.screens.Screen; @@ -26,8 +30,6 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.BucketItem; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; import net.minecraft.world.item.SpawnEggItem; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; @@ -45,15 +47,10 @@ import net.neoforged.neoforge.event.entity.player.ItemTooltipEvent; import org.jetbrains.annotations.Nullable; -import java.util.IdentityHashMap; import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; @EventBusSubscriber(modid = KubeJS.MOD_ID, value = Dist.CLIENT) public class KubeJSClientEventHandler { - public static Map> staticItemTooltips = null; - @SubscribeEvent public static void debugInfo(CustomizeGuiOverlayEvent.DebugText event) { var mc = Minecraft.getInstance(); @@ -84,18 +81,82 @@ private static void appendComponentValue(DynamicOps ops, MutableCompone } } - @SubscribeEvent + public static boolean testRequirements(Minecraft mc, DynamicItemTooltipsKubeEvent event, TooltipRequirements r) { + if (!r.advanced().test(event.advanced)) { + return false; + } + + if (!r.creative().test(event.creative)) { + return false; + } + + if (!r.shift().test(event.shift)) { + return false; + } + + if (!r.ctrl().test(event.ctrl)) { + return false; + } + + if (!r.alt().test(event.alt)) { + return false; + } + + if (!r.stages().isEmpty()) { + var stages = mc.player.kjs$getStages(); + + for (var entry : r.stages().entrySet()) { + if (entry.getValue() != Tristate.DEFAULT && !entry.getValue().test(stages.has(entry.getKey()))) { + return false; + } + } + } + + return true; + } + + private static void handleItemTooltips(Minecraft mc, ItemTooltipData tooltip, DynamicItemTooltipsKubeEvent event) { + if ((tooltip.filter().isEmpty() || tooltip.filter().get().test(event.item)) && (tooltip.requirements().isEmpty() || testRequirements(mc, event, tooltip.requirements().get()))) { + for (var action : tooltip.actions()) { + if (action instanceof DynamicTooltipAction dynamic) { + try { + ItemEvents.DYNAMIC_TOOLTIPS.post(ScriptType.CLIENT, dynamic.id(), event); + } catch (Exception ex) { + ConsoleJS.CLIENT.error("Item " + event.item.kjs$getId() + " dynamic tooltip error", ex); + } + } else { + action.apply(event.lines); + } + } + } + } + + @SubscribeEvent(priority = EventPriority.LOW) public static void onItemTooltip(ItemTooltipEvent event) { - var mc = Minecraft.getInstance(); var stack = event.getItemStack(); - var lines = event.getToolTip(); - var flag = event.getFlags(); if (stack.isEmpty()) { return; } - var advanced = flag.isAdvanced(); + var mc = Minecraft.getInstance(); + var lines = event.getToolTip(); + var flags = event.getFlags(); + var sessionData = KubeSessionData.of(mc); + + var dynamicEvent = new DynamicItemTooltipsKubeEvent(stack, flags, lines, sessionData == null); + + for (var tooltip : KubeJSClient.clientItemTooltips) { + handleItemTooltips(mc, tooltip, dynamicEvent); + } + + if (sessionData != null) { + for (var tooltip : sessionData.itemTooltips) { + handleItemTooltips(mc, tooltip, dynamicEvent); + } + } + + var advanced = flags.isAdvanced(); if (mc.level != null && advanced && ClientProperties.get().showComponents && Screen.hasAltDown()) { var components = BuiltInRegistries.DATA_COMPONENT_TYPE; @@ -165,36 +226,6 @@ public static void onItemTooltip(ItemTooltipEvent event) { lines.add(instance.toText()); } } - - if (staticItemTooltips == null) { - var staticItemTooltips0 = new IdentityHashMap>(); - ItemEvents.TOOLTIP.post(ScriptType.CLIENT, new ItemTooltipKubeEvent(staticItemTooltips0)); - staticItemTooltips = staticItemTooltips0; - } - - try { - var handlers = staticItemTooltips.get(Items.AIR); - - if (handlers != null && !handlers.isEmpty()) { - for (var handler : handlers) { - handler.tooltip(stack, advanced, lines); - } - } - } catch (Exception ex) { - ConsoleJS.CLIENT.error("Error while gathering tooltip for " + stack, ex); - } - - try { - var handlers = staticItemTooltips.get(stack.getItem()); - - if (handlers != null && !handlers.isEmpty()) { - for (var handler : handlers) { - handler.tooltip(stack, advanced, lines); - } - } - } catch (Exception ex) { - ConsoleJS.CLIENT.error("Error while gathering tooltip for " + stack, ex); - } } @SubscribeEvent diff --git a/src/main/java/dev/latvian/mods/kubejs/client/KubeSessionData.java b/src/main/java/dev/latvian/mods/kubejs/client/KubeSessionData.java new file mode 100644 index 000000000..e0bba5846 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/client/KubeSessionData.java @@ -0,0 +1,37 @@ +package dev.latvian.mods.kubejs.client; + +import dev.latvian.mods.kubejs.core.ClientPacketListenerKJS; +import dev.latvian.mods.kubejs.net.KubeServerData; +import dev.latvian.mods.kubejs.recipe.viewer.server.RecipeViewerData; +import dev.latvian.mods.kubejs.recipe.viewer.server.RemoteRecipeViewerDataUpdatedEvent; +import dev.latvian.mods.kubejs.tooltip.ItemTooltipData; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.resources.ResourceLocation; +import net.neoforged.neoforge.common.NeoForge; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public class KubeSessionData { + @Nullable + public static KubeSessionData of(ClientPacketListener listener) { + return listener == null ? null : ((ClientPacketListenerKJS) listener).kjs$sessionData(); + } + + @Nullable + public static KubeSessionData of(Minecraft mc) { + return mc == null ? null : of(mc.getConnection()); + } + + public ResourceLocation activePostShader = null; + public RecipeViewerData recipeViewerData = null; + public List itemTooltips = List.of(); + + public void sync(KubeServerData data) { + recipeViewerData = data.recipeViewerData().orElse(null); + itemTooltips = List.copyOf(data.itemTooltipData()); + + NeoForge.EVENT_BUS.post(new RemoteRecipeViewerDataUpdatedEvent(recipeViewerData)); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/core/ClientPacketListenerKJS.java b/src/main/java/dev/latvian/mods/kubejs/core/ClientPacketListenerKJS.java index 75a787ed9..124fea2be 100644 --- a/src/main/java/dev/latvian/mods/kubejs/core/ClientPacketListenerKJS.java +++ b/src/main/java/dev/latvian/mods/kubejs/core/ClientPacketListenerKJS.java @@ -1,10 +1,9 @@ package dev.latvian.mods.kubejs.core; -import net.minecraft.resources.ResourceLocation; -import org.apache.commons.lang3.mutable.Mutable; +import dev.latvian.mods.kubejs.client.KubeSessionData; public interface ClientPacketListenerKJS { - default Mutable kjs$activePostShader() { + default KubeSessionData kjs$sessionData() { throw new NoMixinException(); } } diff --git a/src/main/java/dev/latvian/mods/kubejs/core/ItemStackKJS.java b/src/main/java/dev/latvian/mods/kubejs/core/ItemStackKJS.java index c6a353590..a1bf66ca8 100644 --- a/src/main/java/dev/latvian/mods/kubejs/core/ItemStackKJS.java +++ b/src/main/java/dev/latvian/mods/kubejs/core/ItemStackKJS.java @@ -3,7 +3,6 @@ import com.mojang.serialization.Codec; import com.mojang.serialization.DynamicOps; import dev.latvian.mods.kubejs.bindings.DataComponentWrapper; -import dev.latvian.mods.kubejs.item.ChancedItem; import dev.latvian.mods.kubejs.item.ItemStackJS; import dev.latvian.mods.kubejs.level.BlockContainerJS; import dev.latvian.mods.kubejs.recipe.match.ItemMatch; @@ -25,7 +24,6 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.valueproviders.FloatProvider; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -248,10 +246,6 @@ default Codec getCodec(Context cx) { return ItemStack.CODEC; } - default ChancedItem kjs$withChance(FloatProvider chance) { - return new ChancedItem(kjs$self(), chance); - } - @ReturnsSelf(copy = true) default ItemStack kjs$withLore(Component[] lines) { var is = kjs$self().copy(); diff --git a/src/main/java/dev/latvian/mods/kubejs/core/mixin/ClientPacketListenerMixin.java b/src/main/java/dev/latvian/mods/kubejs/core/mixin/ClientPacketListenerMixin.java index 7ec43cf5b..25c5efc89 100644 --- a/src/main/java/dev/latvian/mods/kubejs/core/mixin/ClientPacketListenerMixin.java +++ b/src/main/java/dev/latvian/mods/kubejs/core/mixin/ClientPacketListenerMixin.java @@ -1,20 +1,18 @@ package dev.latvian.mods.kubejs.core.mixin; +import dev.latvian.mods.kubejs.client.KubeSessionData; import dev.latvian.mods.kubejs.core.ClientPacketListenerKJS; import net.minecraft.client.multiplayer.ClientPacketListener; -import net.minecraft.resources.ResourceLocation; -import org.apache.commons.lang3.mutable.Mutable; -import org.apache.commons.lang3.mutable.MutableObject; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @Mixin(ClientPacketListener.class) public class ClientPacketListenerMixin implements ClientPacketListenerKJS { @Unique - private final Mutable kjs$activePostShader = new MutableObject<>(null); + private final KubeSessionData kjs$sessionData = new KubeSessionData(); @Override - public Mutable kjs$activePostShader() { - return kjs$activePostShader; + public KubeSessionData kjs$sessionData() { + return kjs$sessionData; } } diff --git a/src/main/java/dev/latvian/mods/kubejs/core/mixin/GameRendererMixin.java b/src/main/java/dev/latvian/mods/kubejs/core/mixin/GameRendererMixin.java index b8395e8af..0e4e7c8e1 100644 --- a/src/main/java/dev/latvian/mods/kubejs/core/mixin/GameRendererMixin.java +++ b/src/main/java/dev/latvian/mods/kubejs/core/mixin/GameRendererMixin.java @@ -1,6 +1,6 @@ package dev.latvian.mods.kubejs.core.mixin; -import dev.latvian.mods.kubejs.core.ClientPacketListenerKJS; +import dev.latvian.mods.kubejs.client.KubeSessionData; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GameRenderer; import net.minecraft.client.renderer.PostChain; @@ -29,12 +29,14 @@ public abstract class GameRendererMixin { @Inject(method = "checkEntityPostEffect", at = @At("HEAD"), cancellable = true) private void kjs$checkEntityPostEffect(CallbackInfo ci) { - if (minecraft.getConnection() instanceof ClientPacketListenerKJS connection && connection.kjs$activePostShader().getValue() != null) { + var data = KubeSessionData.of(minecraft); + + if (data != null && data.activePostShader != null) { if (postEffect != null) { postEffect.close(); } - loadEffect(connection.kjs$activePostShader().getValue()); + loadEffect(data.activePostShader); ci.cancel(); } } diff --git a/src/main/java/dev/latvian/mods/kubejs/core/mixin/LocalPlayerMixin.java b/src/main/java/dev/latvian/mods/kubejs/core/mixin/LocalPlayerMixin.java index c10443757..d8b9d7e35 100644 --- a/src/main/java/dev/latvian/mods/kubejs/core/mixin/LocalPlayerMixin.java +++ b/src/main/java/dev/latvian/mods/kubejs/core/mixin/LocalPlayerMixin.java @@ -1,7 +1,7 @@ package dev.latvian.mods.kubejs.core.mixin; import com.mojang.authlib.GameProfile; -import dev.latvian.mods.kubejs.core.ClientPacketListenerKJS; +import dev.latvian.mods.kubejs.client.KubeSessionData; import dev.latvian.mods.rhino.util.RemapForJS; import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientPacketListener; @@ -45,7 +45,11 @@ public LocalPlayerMixin(Level level, BlockPos blockPos, float f, GameProfile gam @Override public void kjs$setActivePostShader(@Nullable ResourceLocation id) { - ((ClientPacketListenerKJS) connection).kjs$activePostShader().setValue(id); - minecraft.gameRenderer.checkEntityPostEffect(minecraft.options.getCameraType().isFirstPerson() ? minecraft.getCameraEntity() : null); + var sessionData = KubeSessionData.of(connection); + + if (sessionData != null) { + sessionData.activePostShader = id; + minecraft.gameRenderer.checkEntityPostEffect(minecraft.options.getCameraType().isFirstPerson() ? minecraft.getCameraEntity() : null); + } } } diff --git a/src/main/java/dev/latvian/mods/kubejs/ingredient/KubeJSIngredient.java b/src/main/java/dev/latvian/mods/kubejs/ingredient/KubeJSIngredient.java index 8728a9cee..c4bb1fbca 100644 --- a/src/main/java/dev/latvian/mods/kubejs/ingredient/KubeJSIngredient.java +++ b/src/main/java/dev/latvian/mods/kubejs/ingredient/KubeJSIngredient.java @@ -1,5 +1,6 @@ package dev.latvian.mods.kubejs.ingredient; +import dev.latvian.mods.kubejs.CommonProperties; import dev.latvian.mods.kubejs.item.ItemStackJS; import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.common.crafting.ICustomIngredient; @@ -18,6 +19,6 @@ default Stream getItems() { @Override default boolean isSimple() { - return true; + return CommonProperties.get().serverOnly; } } diff --git a/src/main/java/dev/latvian/mods/kubejs/ingredient/WildcardIngredient.java b/src/main/java/dev/latvian/mods/kubejs/ingredient/WildcardIngredient.java index 5093fb54c..271669a7b 100644 --- a/src/main/java/dev/latvian/mods/kubejs/ingredient/WildcardIngredient.java +++ b/src/main/java/dev/latvian/mods/kubejs/ingredient/WildcardIngredient.java @@ -1,15 +1,12 @@ package dev.latvian.mods.kubejs.ingredient; import com.mojang.serialization.MapCodec; -import dev.latvian.mods.kubejs.item.ItemStackJS; import io.netty.buffer.ByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.common.crafting.IngredientType; import org.jetbrains.annotations.Nullable; -import java.util.stream.Stream; - public class WildcardIngredient implements KubeJSIngredient { public static WildcardIngredient INSTANCE = new WildcardIngredient(); @@ -26,11 +23,6 @@ public IngredientType getType() { @Override public boolean test(@Nullable ItemStack stack) { - return stack != null; - } - - @Override - public Stream getItems() { - return ItemStackJS.getList().stream(); + return stack != null && !stack.isEmpty(); } } diff --git a/src/main/java/dev/latvian/mods/kubejs/integration/emi/KubeJSEMIPlugin.java b/src/main/java/dev/latvian/mods/kubejs/integration/emi/KubeJSEMIPlugin.java index 1ed676eaf..4a79b16f2 100644 --- a/src/main/java/dev/latvian/mods/kubejs/integration/emi/KubeJSEMIPlugin.java +++ b/src/main/java/dev/latvian/mods/kubejs/integration/emi/KubeJSEMIPlugin.java @@ -6,10 +6,11 @@ import dev.emi.emi.api.recipe.EmiInfoRecipe; import dev.emi.emi.api.stack.EmiIngredient; import dev.emi.emi.api.stack.EmiStack; +import dev.latvian.mods.kubejs.client.KubeSessionData; import dev.latvian.mods.kubejs.recipe.viewer.RecipeViewerEntryType; import dev.latvian.mods.kubejs.recipe.viewer.RecipeViewerEvents; -import dev.latvian.mods.kubejs.recipe.viewer.server.RecipeViewerData; import dev.latvian.mods.kubejs.script.ScriptType; +import net.minecraft.client.Minecraft; import net.minecraft.resources.ResourceLocation; import java.util.HashMap; @@ -20,7 +21,8 @@ public class KubeJSEMIPlugin implements EmiPlugin { @Override public void register(EmiRegistry registry) { - var remote = RecipeViewerData.remote; + var sessionData = KubeSessionData.of(Minecraft.getInstance()); + var remote = sessionData == null ? null : sessionData.recipeViewerData; if (remote != null) { var removedCategories = Set.copyOf(remote.removedCategories()); diff --git a/src/main/java/dev/latvian/mods/kubejs/item/ChancedItem.java b/src/main/java/dev/latvian/mods/kubejs/item/ChancedItem.java deleted file mode 100644 index 9f3b4642a..000000000 --- a/src/main/java/dev/latvian/mods/kubejs/item/ChancedItem.java +++ /dev/null @@ -1,57 +0,0 @@ -package dev.latvian.mods.kubejs.item; - -import com.mojang.serialization.MapCodec; -import com.mojang.serialization.codecs.RecordCodecBuilder; -import dev.latvian.mods.kubejs.recipe.component.RecipeComponent; -import dev.latvian.mods.kubejs.recipe.component.SimpleRecipeComponent; -import dev.latvian.mods.kubejs.util.RegistryAccessContainer; -import dev.latvian.mods.rhino.Context; -import dev.latvian.mods.rhino.type.RecordTypeInfo; -import dev.latvian.mods.rhino.type.TypeInfo; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.util.RandomSource; -import net.minecraft.util.valueproviders.ConstantFloat; -import net.minecraft.util.valueproviders.FloatProvider; -import net.minecraft.world.item.ItemStack; - -public record ChancedItem(ItemStack item, FloatProvider chance) { - public static final RecordTypeInfo TYPE_INFO = (RecordTypeInfo) TypeInfo.of(ChancedItem.class); - public static final FloatProvider DEFAULT_CHANCE = ConstantFloat.of(1F); - - public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( - ItemStack.CODEC.fieldOf("item").forGetter(ChancedItem::item), - FloatProvider.CODEC.optionalFieldOf("chance", DEFAULT_CHANCE).forGetter(ChancedItem::chance) - ).apply(instance, ChancedItem::new)); - - public static final StreamCodec STREAM_CODEC = StreamCodec.composite( - ItemStack.STREAM_CODEC, ChancedItem::item, - ByteBufCodecs.fromCodec(FloatProvider.CODEC), ChancedItem::chance, - ChancedItem::new - ); - - public static final RecipeComponent RECIPE_COMPONENT = new SimpleRecipeComponent<>("chanced_item", CODEC.codec(), TypeInfo.of(ChancedItem.class)); - - public static ChancedItem wrap(Context cx, Object from) { - if (from instanceof ItemStack is) { - return new ChancedItem(is, DEFAULT_CHANCE); - } else if (from instanceof CharSequence) { - return new ChancedItem(ItemStackJS.wrap(RegistryAccessContainer.of(cx), from), DEFAULT_CHANCE); - } else { - return (ChancedItem) TYPE_INFO.wrap(cx, from, TYPE_INFO); - } - } - - public boolean test(RandomSource random) { - return random.nextFloat() < chance.sample(random); - } - - public ItemStack getItemOrEmpty(RandomSource random) { - return test(random) ? item : ItemStack.EMPTY; - } - - public ChancedItem withChance(FloatProvider chance) { - return new ChancedItem(item, chance); - } -} \ No newline at end of file diff --git a/src/main/java/dev/latvian/mods/kubejs/item/DynamicItemTooltipsKubeEvent.java b/src/main/java/dev/latvian/mods/kubejs/item/DynamicItemTooltipsKubeEvent.java new file mode 100644 index 000000000..84454bb99 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/item/DynamicItemTooltipsKubeEvent.java @@ -0,0 +1,35 @@ +package dev.latvian.mods.kubejs.item; + +import dev.latvian.mods.kubejs.event.KubeEvent; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; + +import java.util.List; + +public class DynamicItemTooltipsKubeEvent implements KubeEvent { + public final ItemStack item; + public final List lines; + public final boolean startup; + public final boolean advanced; + public final boolean creative; + public final boolean shift; + public final boolean ctrl; + public final boolean alt; + + public DynamicItemTooltipsKubeEvent(ItemStack item, TooltipFlag flags, List lines, boolean startup) { + this.item = item; + this.lines = lines; + this.startup = startup; + this.advanced = flags.isAdvanced(); + this.creative = flags.isCreative(); + this.shift = !startup && Screen.hasShiftDown(); + this.ctrl = !startup && Screen.hasControlDown(); + this.alt = !startup && Screen.hasAltDown(); + } + + public void add(List text) { + lines.addAll(text); + } +} \ No newline at end of file diff --git a/src/main/java/dev/latvian/mods/kubejs/item/ItemTooltipKubeEvent.java b/src/main/java/dev/latvian/mods/kubejs/item/ItemTooltipKubeEvent.java deleted file mode 100644 index 15899f9d0..000000000 --- a/src/main/java/dev/latvian/mods/kubejs/item/ItemTooltipKubeEvent.java +++ /dev/null @@ -1,141 +0,0 @@ -package dev.latvian.mods.kubejs.item; - -import dev.latvian.mods.kubejs.event.KubeEvent; -import dev.latvian.mods.kubejs.script.ConsoleJS; -import dev.latvian.mods.kubejs.typings.Info; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.network.chat.Component; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.Items; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -@Info(""" - Invoked when registering handlers for item tooltips. - - `text` can be a component or a list of components. - """) -public class ItemTooltipKubeEvent implements KubeEvent { - @FunctionalInterface - public interface StaticTooltipHandler { - void tooltip(ItemStack stack, boolean advanced, List components); - } - - @FunctionalInterface - public interface StaticTooltipHandlerFromJS { - void accept(ItemStack stack, boolean advanced, List text); - } - - public static class StaticTooltipHandlerFromLines implements StaticTooltipHandler { - public final List lines; - - public StaticTooltipHandlerFromLines(List l) { - lines = l; - } - - @Override - public void tooltip(ItemStack stack, boolean advanced, List components) { - if (!stack.isEmpty()) { - components.addAll(lines); - } - } - } - - public static class StaticTooltipHandlerFromJSWrapper implements StaticTooltipHandler { - private final StaticTooltipHandlerFromJS handler; - - public StaticTooltipHandlerFromJSWrapper(StaticTooltipHandlerFromJS h) { - handler = h; - } - - @Override - public void tooltip(ItemStack stack, boolean advanced, List components) { - if (stack.isEmpty()) { - return; - } - - List text = new ArrayList<>(components); - - try { - handler.accept(stack, advanced, text); - } catch (Exception ex) { - ConsoleJS.CLIENT.error("Error while gathering tooltip for " + stack, ex); - } - - components.clear(); - components.addAll(text); - } - } - - private final Map> map; - - public ItemTooltipKubeEvent(Map> m) { - map = m; - } - - @Info("Adds text to all items matching the ingredient.") - public void add(ItemPredicate item, List text) { - if (item.kjs$isWildcard()) { - addToAll(text); - return; - } - - var l = new StaticTooltipHandlerFromLines(text); - - if (!l.lines.isEmpty()) { - for (var i : item.kjs$getItemTypes()) { - if (i != Items.AIR) { - map.computeIfAbsent(i, k -> new ArrayList<>()).add(l); - } - } - } - } - - @Info("Adds text to all items.") - public void addToAll(List text) { - var l = new StaticTooltipHandlerFromLines(text); - - if (!l.lines.isEmpty()) { - map.computeIfAbsent(Items.AIR, k -> new ArrayList<>()).add(l); - } - } - - @Info("Adds a dynamic tooltip handler to all items matching the ingredient.") - public void addAdvanced(ItemPredicate item, StaticTooltipHandlerFromJS handler) { - if (item.kjs$isWildcard()) { - addAdvancedToAll(handler); - return; - } - - var l = new StaticTooltipHandlerFromJSWrapper(handler); - - for (var i : item.kjs$getItemTypes()) { - if (i != Items.AIR) { - map.computeIfAbsent(i, k -> new ArrayList<>()).add(l); - } - } - } - - @Info("Adds a dynamic tooltip handler to all items.") - public void addAdvancedToAll(StaticTooltipHandlerFromJS handler) { - map.computeIfAbsent(Items.AIR, k -> new ArrayList<>()).add(new StaticTooltipHandlerFromJSWrapper(handler)); - } - - @Info("Is shift key pressed.") - public boolean isShift() { - return Screen.hasShiftDown(); - } - - @Info("Is control key pressed.") - public boolean isCtrl() { - return Screen.hasControlDown(); - } - - @Info("Is alt key pressed.") - public boolean isAlt() { - return Screen.hasAltDown(); - } -} \ No newline at end of file diff --git a/src/main/java/dev/latvian/mods/kubejs/item/ModifyItemTooltipsKubeEvent.java b/src/main/java/dev/latvian/mods/kubejs/item/ModifyItemTooltipsKubeEvent.java new file mode 100644 index 000000000..8b8ac2977 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/item/ModifyItemTooltipsKubeEvent.java @@ -0,0 +1,47 @@ +package dev.latvian.mods.kubejs.item; + +import dev.latvian.mods.kubejs.event.KubeEvent; +import dev.latvian.mods.kubejs.tooltip.ItemTooltipData; +import dev.latvian.mods.kubejs.tooltip.TooltipActionBuilder; +import dev.latvian.mods.kubejs.tooltip.TooltipRequirements; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.crafting.Ingredient; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; + +public class ModifyItemTooltipsKubeEvent implements KubeEvent { + private final Consumer callback; + + public ModifyItemTooltipsKubeEvent(Consumer callback) { + this.callback = callback; + } + + private void modify(@Nullable Ingredient filter, Optional requirements, Consumer consumer) { + var builder = new TooltipActionBuilder(); + consumer.accept(builder); + callback.accept(new ItemTooltipData(filter == null || filter.isEmpty() || filter.kjs$isWildcard() ? Optional.empty() : Optional.of(filter), requirements, List.copyOf(builder.actions))); + } + + public void modify(Ingredient filter, TooltipRequirements requirements, Consumer consumer) { + modify(filter, Optional.ofNullable(requirements), consumer); + } + + public void modify(Ingredient filter, Consumer consumer) { + modify(filter, Optional.empty(), consumer); + } + + public void modifyAll(TooltipRequirements requirements, Consumer consumer) { + modify(null, Optional.ofNullable(requirements), consumer); + } + + public void modifyAll(Consumer consumer) { + modify(null, Optional.empty(), consumer); + } + + public void add(Ingredient filter, List text) { + modify(filter, builder -> builder.add(text)); + } +} \ No newline at end of file diff --git a/src/main/java/dev/latvian/mods/kubejs/neoforge/KubeJSNeoForgeClient.java b/src/main/java/dev/latvian/mods/kubejs/neoforge/KubeJSNeoForgeClient.java index d24155663..43454e4b6 100644 --- a/src/main/java/dev/latvian/mods/kubejs/neoforge/KubeJSNeoForgeClient.java +++ b/src/main/java/dev/latvian/mods/kubejs/neoforge/KubeJSNeoForgeClient.java @@ -20,9 +20,11 @@ import dev.latvian.mods.kubejs.gui.KubeJSScreen; import dev.latvian.mods.kubejs.item.ItemBuilder; import dev.latvian.mods.kubejs.item.ItemModelPropertiesKubeEvent; +import dev.latvian.mods.kubejs.item.ModifyItemTooltipsKubeEvent; import dev.latvian.mods.kubejs.kubedex.KubedexHighlight; import dev.latvian.mods.kubejs.registry.RegistryObjectStorage; import dev.latvian.mods.kubejs.script.ScriptType; +import dev.latvian.mods.kubejs.tooltip.ItemTooltipData; import dev.latvian.mods.kubejs.util.ID; import net.minecraft.client.KeyMapping; import net.minecraft.client.renderer.ItemBlockRenderTypes; @@ -45,6 +47,8 @@ import org.lwjgl.glfw.GLFW; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; @EventBusSubscriber(modid = KubeJS.MOD_ID, value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD) public class KubeJSNeoForgeClient { @@ -93,6 +97,10 @@ private static void setupClient0() { } } } + + var list = new ArrayList(); + ItemEvents.MODIFY_TOOLTIPS.post(ScriptType.CLIENT, new ModifyItemTooltipsKubeEvent(list::add)); + KubeJSClient.clientItemTooltips = List.copyOf(list); } @SubscribeEvent diff --git a/src/main/java/dev/latvian/mods/kubejs/net/KubeJSNet.java b/src/main/java/dev/latvian/mods/kubejs/net/KubeJSNet.java index ec6f1f45e..ebc25f738 100644 --- a/src/main/java/dev/latvian/mods/kubejs/net/KubeJSNet.java +++ b/src/main/java/dev/latvian/mods/kubejs/net/KubeJSNet.java @@ -25,7 +25,7 @@ private static CustomPacketPayload.Type type( CustomPacketPayload.Type REQUEST_INVENTORY_KUBEDEX = type("request_inventory_kubedex"); CustomPacketPayload.Type REQUEST_BLOCK_KUBEDEX = type("request_block_kubedex"); CustomPacketPayload.Type REQUEST_ENTITY_KUBEDEX = type("request_entity_kubedex"); - CustomPacketPayload.Type SYNC_RECIPE_VIEWER = type("sync_recipe_viewer"); + CustomPacketPayload.Type SYNC_SERVER_DATA = type("sync_server_data"); CustomPacketPayload.Type SET_ACTIVE_POST_SHADER = type("set_active_post_shader"); @SubscribeEvent @@ -47,7 +47,7 @@ static void register(RegisterPayloadHandlersEvent event) { reg.playToServer(REQUEST_INVENTORY_KUBEDEX, RequestInventoryKubedexPayload.STREAM_CODEC, RequestInventoryKubedexPayload::handle); reg.playToServer(REQUEST_BLOCK_KUBEDEX, RequestBlockKubedexPayload.STREAM_CODEC, RequestBlockKubedexPayload::handle); reg.playToServer(REQUEST_ENTITY_KUBEDEX, RequestEntityKubedexPayload.STREAM_CODEC, RequestEntityKubedexPayload::handle); - reg.playToClient(SYNC_RECIPE_VIEWER, SyncRecipeViewerDataPayload.STREAM_CODEC, SyncRecipeViewerDataPayload::handle); + reg.playToClient(SYNC_SERVER_DATA, SyncServerDataPayload.STREAM_CODEC, SyncServerDataPayload::handle); reg.playToClient(SET_ACTIVE_POST_SHADER, SetActivePostShaderPayload.STREAM_CODEC, SetActivePostShaderPayload::handle); } } \ No newline at end of file diff --git a/src/main/java/dev/latvian/mods/kubejs/net/KubeServerData.java b/src/main/java/dev/latvian/mods/kubejs/net/KubeServerData.java new file mode 100644 index 000000000..97a2359e5 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/net/KubeServerData.java @@ -0,0 +1,35 @@ +package dev.latvian.mods.kubejs.net; + +import dev.latvian.mods.kubejs.bindings.event.ItemEvents; +import dev.latvian.mods.kubejs.item.ModifyItemTooltipsKubeEvent; +import dev.latvian.mods.kubejs.recipe.viewer.server.RecipeViewerData; +import dev.latvian.mods.kubejs.script.ScriptType; +import dev.latvian.mods.kubejs.tooltip.ItemTooltipData; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public record KubeServerData( + Optional recipeViewerData, + List itemTooltipData +) { + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.optional(RecipeViewerData.STREAM_CODEC), KubeServerData::recipeViewerData, + ItemTooltipData.STREAM_CODEC.apply(ByteBufCodecs.list()), KubeServerData::itemTooltipData, + KubeServerData::new + ); + + public static KubeServerData collect() { + var itemTooltipData = new ArrayList(); + ItemEvents.MODIFY_TOOLTIPS.post(ScriptType.SERVER, new ModifyItemTooltipsKubeEvent(itemTooltipData::add)); + + return new KubeServerData( + Optional.ofNullable(RecipeViewerData.collect()), + List.copyOf(itemTooltipData) + ); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/net/SyncRecipeViewerDataPayload.java b/src/main/java/dev/latvian/mods/kubejs/net/SyncRecipeViewerDataPayload.java deleted file mode 100644 index 810dbe020..000000000 --- a/src/main/java/dev/latvian/mods/kubejs/net/SyncRecipeViewerDataPayload.java +++ /dev/null @@ -1,28 +0,0 @@ -package dev.latvian.mods.kubejs.net; - -import dev.latvian.mods.kubejs.recipe.viewer.server.RecipeViewerData; -import dev.latvian.mods.kubejs.recipe.viewer.server.RemoteRecipeViewerDataUpdatedEvent; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.network.handling.IPayloadContext; - -import java.util.Optional; - -public record SyncRecipeViewerDataPayload(Optional data) implements CustomPacketPayload { - public static final StreamCodec STREAM_CODEC = ByteBufCodecs.optional(RecipeViewerData.STREAM_CODEC).map(SyncRecipeViewerDataPayload::new, SyncRecipeViewerDataPayload::data); - - @Override - public Type type() { - return KubeJSNet.SYNC_RECIPE_VIEWER; - } - - public void handle(IPayloadContext ctx) { - ctx.enqueueWork(() -> { - RecipeViewerData.remote = data.orElse(null); - NeoForge.EVENT_BUS.post(new RemoteRecipeViewerDataUpdatedEvent(RecipeViewerData.remote)); - }); - } -} \ No newline at end of file diff --git a/src/main/java/dev/latvian/mods/kubejs/net/SyncServerDataPayload.java b/src/main/java/dev/latvian/mods/kubejs/net/SyncServerDataPayload.java new file mode 100644 index 000000000..45c72c26c --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/net/SyncServerDataPayload.java @@ -0,0 +1,20 @@ +package dev.latvian.mods.kubejs.net; + +import dev.latvian.mods.kubejs.KubeJS; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +public record SyncServerDataPayload(KubeServerData data) implements CustomPacketPayload { + public static final StreamCodec STREAM_CODEC = KubeServerData.STREAM_CODEC.map(SyncServerDataPayload::new, SyncServerDataPayload::data); + + @Override + public Type type() { + return KubeJSNet.SYNC_SERVER_DATA; + } + + public void handle(IPayloadContext ctx) { + ctx.enqueueWork(() -> KubeJS.PROXY.updateServerData(data)); + } +} \ No newline at end of file diff --git a/src/main/java/dev/latvian/mods/kubejs/player/KubeJSPlayerEventHandler.java b/src/main/java/dev/latvian/mods/kubejs/player/KubeJSPlayerEventHandler.java index ed0d5d2d3..3f96b1d33 100644 --- a/src/main/java/dev/latvian/mods/kubejs/player/KubeJSPlayerEventHandler.java +++ b/src/main/java/dev/latvian/mods/kubejs/player/KubeJSPlayerEventHandler.java @@ -3,7 +3,6 @@ import dev.latvian.mods.kubejs.CommonProperties; import dev.latvian.mods.kubejs.KubeJS; import dev.latvian.mods.kubejs.bindings.event.PlayerEvents; -import dev.latvian.mods.kubejs.net.SyncRecipeViewerDataPayload; import dev.latvian.mods.kubejs.script.ConsoleJS; import dev.latvian.mods.kubejs.script.ScriptType; import net.minecraft.resources.ResourceKey; @@ -22,13 +21,11 @@ import net.neoforged.neoforge.event.tick.PlayerTickEvent; import net.neoforged.neoforge.network.PacketDistributor; -import java.util.Optional; - @EventBusSubscriber(modid = KubeJS.MOD_ID) public class KubeJSPlayerEventHandler { @SubscribeEvent(priority = EventPriority.HIGH) public static void datapackSync(OnDatapackSyncEvent event) { - var payload = new SyncRecipeViewerDataPayload(Optional.ofNullable(event.getPlayerList().getServer().getServerResources().managers().kjs$getServerScriptManager().recipeViewerData)); + var payload = event.getPlayerList().getServer().getServerResources().managers().kjs$getServerScriptManager().serverData; event.getRelevantPlayers().forEach(player -> PacketDistributor.sendToPlayer(player, payload)); } diff --git a/src/main/java/dev/latvian/mods/kubejs/recipe/schema/JsonRecipeSchemaLoader.java b/src/main/java/dev/latvian/mods/kubejs/recipe/schema/JsonRecipeSchemaLoader.java index a0ba3fc30..70690b87d 100644 --- a/src/main/java/dev/latvian/mods/kubejs/recipe/schema/JsonRecipeSchemaLoader.java +++ b/src/main/java/dev/latvian/mods/kubejs/recipe/schema/JsonRecipeSchemaLoader.java @@ -187,25 +187,38 @@ private RecipeSchema getSchema(DynamicOps jsonOps) { // schema.constructors(constructors.toArray(new RecipeConstructor[0])); for (var entry : functionMap.entrySet()) { + var funcName = entry.getKey(); var funcJson = entry.getValue().json; if (funcJson.has("set")) { Map, Object> map = new HashMap<>(1); for (var entry1 : funcJson.getAsJsonObject("set").entrySet()) { - var key = keyMap.get(entry1.getKey()); + var keyName = entry1.getKey(); + var key = keyMap.get(keyName); if (key != null) { map.put(key, key.codec.decode(jsonOps, entry1.getValue()).getOrThrow().getFirst()); } else { - throw new NullPointerException("Key '" + entry1.getKey() + "' not found in function '" + entry1.getKey() + "' of recipe schema '" + id + "'"); + throw new NullPointerException("Key '" + keyName + "' not found in function '" + funcName + "' of recipe schema '" + id + "'"); } } if (map.size() == 1) { - schema.function(entry.getKey(), new RecipeSchemaFunction.SetFunction(map.keySet().iterator().next(), map.values().iterator().next())); + schema.function(funcName, new RecipeSchemaFunction.SetFunction(map.keySet().iterator().next(), map.values().iterator().next())); } else if (!map.isEmpty()) { - schema.function(entry.getKey(), new RecipeSchemaFunction.SetManyFunction(map)); + schema.function(funcName, new RecipeSchemaFunction.SetManyFunction(map)); + } + } + + if (funcJson.has("add_to_list")) { + var keyName = funcJson.get("add_to_list").getAsString(); + var key = keyMap.get(keyName); + + if (key != null) { + schema.function(funcName, new RecipeSchemaFunction.AddToListFunction<>((RecipeKey) key)); + } else { + throw new NullPointerException("Key '" + keyName + "' not found in function '" + funcName + "' of recipe schema '" + id + "'"); } } } diff --git a/src/main/java/dev/latvian/mods/kubejs/recipe/schema/RecipeSchema.java b/src/main/java/dev/latvian/mods/kubejs/recipe/schema/RecipeSchema.java index eef86b867..d224e25e6 100644 --- a/src/main/java/dev/latvian/mods/kubejs/recipe/schema/RecipeSchema.java +++ b/src/main/java/dev/latvian/mods/kubejs/recipe/schema/RecipeSchema.java @@ -262,6 +262,10 @@ public RecipeSchema setOpFunction(String name, RecipeKey key, T value) { return function(name, new RecipeSchemaFunction.SetFunction<>(key, value)); } + public RecipeSchema addToListOpFunction(String name, RecipeKey> key) { + return function(name, new RecipeSchemaFunction.AddToListFunction<>(key)); + } + public RecipeKey getKey(String id) { for (var key : keys) { if (key.name.equals(id)) { diff --git a/src/main/java/dev/latvian/mods/kubejs/recipe/schema/RecipeSchemaFunction.java b/src/main/java/dev/latvian/mods/kubejs/recipe/schema/RecipeSchemaFunction.java index 1823e2a37..82a958c92 100644 --- a/src/main/java/dev/latvian/mods/kubejs/recipe/schema/RecipeSchemaFunction.java +++ b/src/main/java/dev/latvian/mods/kubejs/recipe/schema/RecipeSchemaFunction.java @@ -8,6 +8,7 @@ import dev.latvian.mods.rhino.Scriptable; import dev.latvian.mods.rhino.type.TypeInfo; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -79,4 +80,14 @@ public void execute(Context cx, KubeRecipe recipe, Object[] args) { } } } + + record AddToListFunction(RecipeKey> key) implements RecipeSchemaFunction { + @Override + public void execute(Context cx, KubeRecipe recipe, Object[] args) { + var value = recipe.getValue(key); + var list = value == null ? new ArrayList() : new ArrayList<>(value); + list.addAll(key.component.wrap(cx, recipe, args)); + recipe.setValue(key, list); + } + } } diff --git a/src/main/java/dev/latvian/mods/kubejs/recipe/viewer/server/RecipeViewerData.java b/src/main/java/dev/latvian/mods/kubejs/recipe/viewer/server/RecipeViewerData.java index f9faaf6af..0fdea88f8 100644 --- a/src/main/java/dev/latvian/mods/kubejs/recipe/viewer/server/RecipeViewerData.java +++ b/src/main/java/dev/latvian/mods/kubejs/recipe/viewer/server/RecipeViewerData.java @@ -28,8 +28,6 @@ public record RecipeViewerData( RecipeViewerData::new ); - public static RecipeViewerData remote = null; - @Nullable public static RecipeViewerData collect() { var removedCategories = new HashSet(); diff --git a/src/main/java/dev/latvian/mods/kubejs/server/ServerScriptManager.java b/src/main/java/dev/latvian/mods/kubejs/server/ServerScriptManager.java index 488dea07e..acb42cd48 100644 --- a/src/main/java/dev/latvian/mods/kubejs/server/ServerScriptManager.java +++ b/src/main/java/dev/latvian/mods/kubejs/server/ServerScriptManager.java @@ -7,13 +7,14 @@ import dev.latvian.mods.kubejs.bindings.event.ServerEvents; import dev.latvian.mods.kubejs.core.RecipeManagerKJS; import dev.latvian.mods.kubejs.error.KubeRuntimeException; +import dev.latvian.mods.kubejs.net.KubeServerData; +import dev.latvian.mods.kubejs.net.SyncServerDataPayload; import dev.latvian.mods.kubejs.plugin.KubeJSPlugin; import dev.latvian.mods.kubejs.plugin.KubeJSPlugins; import dev.latvian.mods.kubejs.recipe.CompostableRecipesKubeEvent; import dev.latvian.mods.kubejs.recipe.RecipesKubeEvent; import dev.latvian.mods.kubejs.recipe.schema.RecipeSchemaStorage; import dev.latvian.mods.kubejs.recipe.special.SpecialRecipeSerializerManager; -import dev.latvian.mods.kubejs.recipe.viewer.server.RecipeViewerData; import dev.latvian.mods.kubejs.registry.AdditionalObjectRegistry; import dev.latvian.mods.kubejs.registry.BuilderBase; import dev.latvian.mods.kubejs.registry.RegistryObjectStorage; @@ -87,7 +88,7 @@ public static ServerScriptManager release() { public final Map, PreTagKubeEvent> preTagEvents; public final RecipeSchemaStorage recipeSchemaStorage; - public RecipeViewerData recipeViewerData; + public SyncServerDataPayload serverData; public final VirtualDataPack internalDataPack; public final VirtualDataPack registriesDataPack; public final Map virtualPacks; @@ -97,7 +98,7 @@ private ServerScriptManager() { super(ScriptType.SERVER); this.preTagEvents = new ConcurrentHashMap<>(); this.recipeSchemaStorage = new RecipeSchemaStorage(); - this.recipeViewerData = null; + this.serverData = null; this.internalDataPack = new VirtualDataPack(GeneratedDataStage.INTERNAL); this.registriesDataPack = new VirtualDataPack(GeneratedDataStage.REGISTRIES); @@ -202,7 +203,7 @@ public void reload() { pack.reset(); } - recipeViewerData = null; + serverData = null; super.reload(); @@ -232,7 +233,7 @@ public boolean recipes(RecipeManagerKJS recipeManager, ResourceManager resourceM result = true; } - recipeViewerData = RecipeViewerData.collect(); + serverData = new SyncServerDataPayload(KubeServerData.collect()); RecipesKubeEvent.TEMP_ITEM_TAG_LOOKUP.setValue(null); return result; } diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/ItemStartupTooltipsKubeEvent.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/ItemStartupTooltipsKubeEvent.java new file mode 100644 index 000000000..ae7201f23 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/ItemStartupTooltipsKubeEvent.java @@ -0,0 +1,9 @@ +package dev.latvian.mods.kubejs.tooltip; + +import dev.latvian.mods.kubejs.event.KubeStartupEvent; +import dev.latvian.mods.kubejs.item.ItemPredicate; + +public class ItemStartupTooltipsKubeEvent implements KubeStartupEvent { + public void modify(ItemPredicate filter) { + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/ItemTooltipData.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/ItemTooltipData.java new file mode 100644 index 000000000..812b3ac06 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/ItemTooltipData.java @@ -0,0 +1,23 @@ +package dev.latvian.mods.kubejs.tooltip; + +import dev.latvian.mods.kubejs.tooltip.action.TooltipAction; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.crafting.Ingredient; + +import java.util.List; +import java.util.Optional; + +public record ItemTooltipData( + Optional filter, + Optional requirements, + List actions +) { + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + ByteBufCodecs.optional(Ingredient.CONTENTS_STREAM_CODEC), ItemTooltipData::filter, + ByteBufCodecs.optional(TooltipRequirements.STREAM_CODEC), ItemTooltipData::requirements, + TooltipAction.STREAM_CODEC.apply(ByteBufCodecs.list()), ItemTooltipData::actions, + ItemTooltipData::new + ); +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/TooltipActionBuilder.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/TooltipActionBuilder.java new file mode 100644 index 000000000..5a17abecd --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/TooltipActionBuilder.java @@ -0,0 +1,43 @@ +package dev.latvian.mods.kubejs.tooltip; + +import dev.latvian.mods.kubejs.tooltip.action.AddTooltipAction; +import dev.latvian.mods.kubejs.tooltip.action.DynamicTooltipAction; +import dev.latvian.mods.kubejs.tooltip.action.InsertTooltipAction; +import dev.latvian.mods.kubejs.tooltip.action.RemoveExactTextTooltipAction; +import dev.latvian.mods.kubejs.tooltip.action.RemoveLineTooltipAction; +import dev.latvian.mods.kubejs.tooltip.action.RemoveTextTooltipAction; +import dev.latvian.mods.kubejs.tooltip.action.TooltipAction; +import dev.latvian.mods.rhino.util.HideFromJS; +import net.minecraft.network.chat.Component; + +import java.util.ArrayList; +import java.util.List; + +public class TooltipActionBuilder { + @HideFromJS + public List actions = new ArrayList<>(1); + + public void dynamic(String id) { + actions.add(new DynamicTooltipAction(id)); + } + + public void add(List text) { + actions.add(new AddTooltipAction(text)); + } + + public void insert(int line, List text) { + actions.add(new InsertTooltipAction(line, text)); + } + + public void removeLine(int line) { + actions.add(new RemoveLineTooltipAction(line)); + } + + public void removeText(Component match) { + actions.add(new RemoveTextTooltipAction(match)); + } + + public void removeExactText(Component match) { + actions.add(new RemoveExactTextTooltipAction(match)); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/TooltipRequirements.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/TooltipRequirements.java new file mode 100644 index 000000000..cee2e4f6c --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/TooltipRequirements.java @@ -0,0 +1,37 @@ +package dev.latvian.mods.kubejs.tooltip; + +import dev.latvian.mods.kubejs.util.Tristate; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +import java.util.HashMap; +import java.util.Map; + +public record TooltipRequirements( + Tristate shift, + Tristate ctrl, + Tristate alt, + Tristate advanced, + Tristate creative, + Map stages +) { + public static final TooltipRequirements DEFAULT = new TooltipRequirements( + Tristate.DEFAULT, + Tristate.DEFAULT, + Tristate.DEFAULT, + Tristate.DEFAULT, + Tristate.DEFAULT, + Map.of() + ); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + Tristate.STREAM_CODEC, TooltipRequirements::shift, + Tristate.STREAM_CODEC, TooltipRequirements::ctrl, + Tristate.STREAM_CODEC, TooltipRequirements::alt, + Tristate.STREAM_CODEC, TooltipRequirements::advanced, + Tristate.STREAM_CODEC, TooltipRequirements::creative, + ByteBufCodecs.map(HashMap::new, ByteBufCodecs.STRING_UTF8, Tristate.STREAM_CODEC), TooltipRequirements::stages, + TooltipRequirements::new + ); +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/action/AddTooltipAction.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/AddTooltipAction.java new file mode 100644 index 000000000..d331ae306 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/AddTooltipAction.java @@ -0,0 +1,21 @@ +package dev.latvian.mods.kubejs.tooltip.action; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentSerialization; +import net.minecraft.network.codec.ByteBufCodecs; + +import java.util.List; + +public record AddTooltipAction(List lines) implements TooltipAction { + public static final TooltipActionType TYPE = new TooltipActionType<>(1, ComponentSerialization.STREAM_CODEC.apply(ByteBufCodecs.list()).map(AddTooltipAction::new, AddTooltipAction::lines)); + + @Override + public TooltipActionType type() { + return TYPE; + } + + @Override + public void apply(List tooltip) { + tooltip.addAll(lines); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/action/DynamicTooltipAction.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/DynamicTooltipAction.java new file mode 100644 index 000000000..5cdc5b56a --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/DynamicTooltipAction.java @@ -0,0 +1,20 @@ +package dev.latvian.mods.kubejs.tooltip.action; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.ByteBufCodecs; + +import java.util.List; + +public record DynamicTooltipAction(String id) implements TooltipAction { + public static final TooltipActionType TYPE = new TooltipActionType<>(0, ByteBufCodecs.STRING_UTF8.map(DynamicTooltipAction::new, DynamicTooltipAction::id)); + + @Override + public TooltipActionType type() { + return TYPE; + } + + @Override + public void apply(List tooltip) { + tooltip.add(Component.literal("Dynamic tooltip is not supported!").kjs$red()); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/action/InsertTooltipAction.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/InsertTooltipAction.java new file mode 100644 index 000000000..358536fcf --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/InsertTooltipAction.java @@ -0,0 +1,26 @@ +package dev.latvian.mods.kubejs.tooltip.action; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentSerialization; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; + +import java.util.List; + +public record InsertTooltipAction(int line, List lines) implements TooltipAction { + public static final TooltipActionType TYPE = new TooltipActionType<>(2, StreamCodec.composite( + ByteBufCodecs.VAR_INT, InsertTooltipAction::line, + ComponentSerialization.STREAM_CODEC.apply(ByteBufCodecs.list()), InsertTooltipAction::lines, + InsertTooltipAction::new + )); + + @Override + public TooltipActionType type() { + return TYPE; + } + + @Override + public void apply(List tooltip) { + tooltip.addAll(line, lines); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/action/RemoveExactTextTooltipAction.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/RemoveExactTextTooltipAction.java new file mode 100644 index 000000000..8750564d3 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/RemoveExactTextTooltipAction.java @@ -0,0 +1,20 @@ +package dev.latvian.mods.kubejs.tooltip.action; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentSerialization; + +import java.util.List; + +public record RemoveExactTextTooltipAction(Component match) implements TooltipAction { + public static final TooltipActionType TYPE = new TooltipActionType<>(5, ComponentSerialization.STREAM_CODEC.map(RemoveExactTextTooltipAction::new, RemoveExactTextTooltipAction::match)); + + @Override + public TooltipActionType type() { + return TYPE; + } + + @Override + public void apply(List tooltip) { + tooltip.removeIf(match::equals); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/action/RemoveLineTooltipAction.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/RemoveLineTooltipAction.java new file mode 100644 index 000000000..3849ead90 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/RemoveLineTooltipAction.java @@ -0,0 +1,20 @@ +package dev.latvian.mods.kubejs.tooltip.action; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.ByteBufCodecs; + +import java.util.List; + +public record RemoveLineTooltipAction(int line) implements TooltipAction { + public static final TooltipActionType TYPE = new TooltipActionType<>(3, ByteBufCodecs.VAR_INT.map(RemoveLineTooltipAction::new, RemoveLineTooltipAction::line)); + + @Override + public TooltipActionType type() { + return TYPE; + } + + @Override + public void apply(List tooltip) { + tooltip.remove(line); + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/action/RemoveTextTooltipAction.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/RemoveTextTooltipAction.java new file mode 100644 index 000000000..c0c049a81 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/RemoveTextTooltipAction.java @@ -0,0 +1,35 @@ +package dev.latvian.mods.kubejs.tooltip.action; + +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.ComponentContents; +import net.minecraft.network.chat.ComponentSerialization; + +import java.util.List; + +public record RemoveTextTooltipAction(Component match) implements TooltipAction { + public static final TooltipActionType TYPE = new TooltipActionType<>(4, ComponentSerialization.STREAM_CODEC.map(RemoveTextTooltipAction::new, RemoveTextTooltipAction::match)); + + @Override + public TooltipActionType type() { + return TYPE; + } + + @Override + public void apply(List tooltip) { + tooltip.removeIf(component -> RemoveTextTooltipAction.equals(component, match.getContents())); + } + + private static boolean equals(Component c, ComponentContents contents) { + if (c.getContents().equals(contents)) { + return true; + } + + for (var s : c.getSiblings()) { + if (equals(s, contents)) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/action/TooltipAction.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/TooltipAction.java new file mode 100644 index 000000000..fac791b49 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/TooltipAction.java @@ -0,0 +1,41 @@ +package dev.latvian.mods.kubejs.tooltip.action; + +import dev.latvian.mods.kubejs.util.Cast; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.codec.StreamCodec; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public interface TooltipAction { + Map> MAP = Stream.of( + DynamicTooltipAction.TYPE, + AddTooltipAction.TYPE, + InsertTooltipAction.TYPE, + RemoveLineTooltipAction.TYPE, + RemoveTextTooltipAction.TYPE, + RemoveExactTextTooltipAction.TYPE + ).collect(Collectors.toMap(TooltipActionType::type, Function.identity())); + + StreamCodec STREAM_CODEC = new StreamCodec<>() { + @Override + public TooltipAction decode(RegistryFriendlyByteBuf buf) { + int id = buf.readByte(); + return MAP.get(id).streamCodec().decode(buf); + } + + @Override + public void encode(RegistryFriendlyByteBuf buf, TooltipAction value) { + buf.writeByte(value.type().type()); + value.type().streamCodec().encode(buf, Cast.to(value)); + } + }; + + TooltipActionType type(); + + void apply(List tooltip); +} diff --git a/src/main/java/dev/latvian/mods/kubejs/tooltip/action/TooltipActionType.java b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/TooltipActionType.java new file mode 100644 index 000000000..00dbcb510 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/tooltip/action/TooltipActionType.java @@ -0,0 +1,7 @@ +package dev.latvian.mods.kubejs.tooltip.action; + +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; + +public record TooltipActionType(int type, StreamCodec streamCodec) { +} diff --git a/src/main/java/dev/latvian/mods/kubejs/util/RecordDefaults.java b/src/main/java/dev/latvian/mods/kubejs/util/RecordDefaults.java index bba4f59cc..2280c27f2 100644 --- a/src/main/java/dev/latvian/mods/kubejs/util/RecordDefaults.java +++ b/src/main/java/dev/latvian/mods/kubejs/util/RecordDefaults.java @@ -41,5 +41,6 @@ public static void init() { add(DataComponentPredicate.class, DataComponentPredicate.EMPTY); add(EntityPredicate.LocationWrapper.class, new EntityPredicate.LocationWrapper(Optional.empty(), Optional.empty(), Optional.empty())); add(GameTypePredicate.class, GameTypePredicate.ANY); + add(Tristate.class, Tristate.DEFAULT); } } diff --git a/src/main/java/dev/latvian/mods/kubejs/util/Tristate.java b/src/main/java/dev/latvian/mods/kubejs/util/Tristate.java new file mode 100644 index 000000000..10366cc67 --- /dev/null +++ b/src/main/java/dev/latvian/mods/kubejs/util/Tristate.java @@ -0,0 +1,57 @@ +package dev.latvian.mods.kubejs.util; + +import com.mojang.datafixers.util.Either; +import com.mojang.serialization.Codec; +import io.netty.buffer.ByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.util.StringRepresentable; + +import java.util.function.BooleanSupplier; + +public enum Tristate implements StringRepresentable { + FALSE("false"), + TRUE("true"), + DEFAULT("default"); + + public static final Tristate[] VALUES = values(); + + public static final Codec CODEC = Codec.either(Codec.BOOL, Codec.unit("default")).xmap( + either -> either.map(b -> b ? TRUE : FALSE, s -> s.equalsIgnoreCase("true") ? TRUE : s.equalsIgnoreCase("false") ? FALSE : DEFAULT), + t -> t == DEFAULT ? Either.right("default") : Either.left(t == TRUE) + ); + + public static final StreamCodec STREAM_CODEC = ByteBufCodecs.idMapper(i -> VALUES[i], Enum::ordinal); + + public static Tristate wrap(Object from) { + return switch (from) { + case null -> DEFAULT; + case Tristate t -> t; + case Boolean b -> b ? TRUE : FALSE; + default -> switch (from.toString().toLowerCase()) { + case "true" -> TRUE; + case "false" -> FALSE; + default -> DEFAULT; + }; + }; + } + + public final String name; + + Tristate(String name) { + this.name = name; + } + + @Override + public String getSerializedName() { + return name; + } + + public boolean test(boolean enabled) { + return this == DEFAULT || (this == TRUE) == enabled; + } + + public boolean test(BooleanSupplier enabled) { + return this == DEFAULT || (this == TRUE) == enabled.getAsBoolean(); + } +}