diff --git a/Fabric/src/main/java/vazkii/botania/fabric/network/FabricPacketHandler.java b/Fabric/src/main/java/vazkii/botania/fabric/network/FabricPacketHandler.java index d6674767bd..7dadb8e397 100644 --- a/Fabric/src/main/java/vazkii/botania/fabric/network/FabricPacketHandler.java +++ b/Fabric/src/main/java/vazkii/botania/fabric/network/FabricPacketHandler.java @@ -13,7 +13,6 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; - import vazkii.botania.network.TriConsumer; import vazkii.botania.network.clientbound.*; import vazkii.botania.network.serverbound.*; @@ -25,7 +24,7 @@ public final class FabricPacketHandler { public static void init() { ServerPlayNetworking.registerGlobalReceiver(DodgePacket.ID, makeServerBoundHandler(DodgePacket::decode, DodgePacket::handle)); ServerPlayNetworking.registerGlobalReceiver(IndexKeybindRequestPacket.ID, makeServerBoundHandler(IndexKeybindRequestPacket::decode, IndexKeybindRequestPacket::handle)); - ServerPlayNetworking.registerGlobalReceiver(IndexStringRequestPacket.ID, makeServerBoundHandler(IndexStringRequestPacket::decode, IndexStringRequestPacket::handle)); + ServerPlayNetworking.registerGlobalReceiver(IndexRequestPacket.ID, makeServerBoundHandler(IndexRequestPacket::decode, IndexRequestPacket::handle)); ServerPlayNetworking.registerGlobalReceiver(JumpPacket.ID, makeServerBoundHandler(JumpPacket::decode, JumpPacket::handle)); ServerPlayNetworking.registerGlobalReceiver(LeftClickPacket.ID, makeServerBoundHandler(LeftClickPacket::decode, LeftClickPacket::handle)); } @@ -47,6 +46,7 @@ private static ClientPlayNetworking.PlayChannelHandler makeClientBoundHandle return (_client, _handler, buf, _responseSender) -> handler.accept(decoder.apply(buf)); } - private FabricPacketHandler() {} + private FabricPacketHandler() { + } } diff --git a/Forge/src/main/java/vazkii/botania/forge/network/ForgePacketHandler.java b/Forge/src/main/java/vazkii/botania/forge/network/ForgePacketHandler.java index 599290776e..36a47782e3 100644 --- a/Forge/src/main/java/vazkii/botania/forge/network/ForgePacketHandler.java +++ b/Forge/src/main/java/vazkii/botania/forge/network/ForgePacketHandler.java @@ -5,7 +5,6 @@ import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; - import vazkii.botania.network.TriConsumer; import vazkii.botania.network.clientbound.*; import vazkii.botania.network.serverbound.*; @@ -27,30 +26,19 @@ public class ForgePacketHandler { public static void init() { int i = 0; // Serverbound - CHANNEL.registerMessage(i++, DodgePacket.class, DodgePacket::encode, DodgePacket::decode, - makeServerBoundHandler(DodgePacket::handle)); - CHANNEL.registerMessage(i++, IndexKeybindRequestPacket.class, IndexKeybindRequestPacket::encode, IndexKeybindRequestPacket::decode, - makeServerBoundHandler(IndexKeybindRequestPacket::handle)); - CHANNEL.registerMessage(i++, IndexStringRequestPacket.class, IndexStringRequestPacket::encode, IndexStringRequestPacket::decode, - makeServerBoundHandler(IndexStringRequestPacket::handle)); - CHANNEL.registerMessage(i++, JumpPacket.class, JumpPacket::encode, JumpPacket::decode, - makeServerBoundHandler(JumpPacket::handle)); - CHANNEL.registerMessage(i++, LeftClickPacket.class, LeftClickPacket::encode, LeftClickPacket::decode, - makeServerBoundHandler(LeftClickPacket::handle)); + CHANNEL.registerMessage(i++, DodgePacket.class, DodgePacket::encode, DodgePacket::decode, makeServerBoundHandler(DodgePacket::handle)); + CHANNEL.registerMessage(i++, IndexKeybindRequestPacket.class, IndexKeybindRequestPacket::encode, IndexKeybindRequestPacket::decode, makeServerBoundHandler(IndexKeybindRequestPacket::handle)); + CHANNEL.registerMessage(i++, IndexRequestPacket.class, IndexRequestPacket::encode, IndexRequestPacket::decode, makeServerBoundHandler(IndexRequestPacket::handle)); + CHANNEL.registerMessage(i++, JumpPacket.class, JumpPacket::encode, JumpPacket::decode, makeServerBoundHandler(JumpPacket::handle)); + CHANNEL.registerMessage(i++, LeftClickPacket.class, LeftClickPacket::encode, LeftClickPacket::decode, makeServerBoundHandler(LeftClickPacket::handle)); // Clientbound - CHANNEL.registerMessage(i++, AvatarSkiesRodPacket.class, AvatarSkiesRodPacket::encode, AvatarSkiesRodPacket::decode, - makeClientBoundHandler(AvatarSkiesRodPacket.Handler::handle)); - CHANNEL.registerMessage(i++, BotaniaEffectPacket.class, BotaniaEffectPacket::encode, BotaniaEffectPacket::decode, - makeClientBoundHandler(BotaniaEffectPacket.Handler::handle)); - CHANNEL.registerMessage(i++, GogWorldPacket.class, GogWorldPacket::encode, GogWorldPacket::decode, - makeClientBoundHandler(GogWorldPacket.Handler::handle)); - CHANNEL.registerMessage(i++, ItemAgePacket.class, ItemAgePacket::encode, ItemAgePacket::decode, - makeClientBoundHandler(ItemAgePacket.Handler::handle)); - CHANNEL.registerMessage(i++, SpawnGaiaGuardianPacket.class, SpawnGaiaGuardianPacket::encode, SpawnGaiaGuardianPacket::decode, - makeClientBoundHandler(SpawnGaiaGuardianPacket.Handler::handle)); - CHANNEL.registerMessage(i++, UpdateItemsRemainingPacket.class, UpdateItemsRemainingPacket::encode, UpdateItemsRemainingPacket::decode, - makeClientBoundHandler(UpdateItemsRemainingPacket.Handler::handle)); + CHANNEL.registerMessage(i++, AvatarSkiesRodPacket.class, AvatarSkiesRodPacket::encode, AvatarSkiesRodPacket::decode, makeClientBoundHandler(AvatarSkiesRodPacket.Handler::handle)); + CHANNEL.registerMessage(i++, BotaniaEffectPacket.class, BotaniaEffectPacket::encode, BotaniaEffectPacket::decode, makeClientBoundHandler(BotaniaEffectPacket.Handler::handle)); + CHANNEL.registerMessage(i++, GogWorldPacket.class, GogWorldPacket::encode, GogWorldPacket::decode, makeClientBoundHandler(GogWorldPacket.Handler::handle)); + CHANNEL.registerMessage(i++, ItemAgePacket.class, ItemAgePacket::encode, ItemAgePacket::decode, makeClientBoundHandler(ItemAgePacket.Handler::handle)); + CHANNEL.registerMessage(i++, SpawnGaiaGuardianPacket.class, SpawnGaiaGuardianPacket::encode, SpawnGaiaGuardianPacket::decode, makeClientBoundHandler(SpawnGaiaGuardianPacket.Handler::handle)); + CHANNEL.registerMessage(i++, UpdateItemsRemainingPacket.class, UpdateItemsRemainingPacket::encode, UpdateItemsRemainingPacket::decode, makeClientBoundHandler(UpdateItemsRemainingPacket.Handler::handle)); } private static BiConsumer> makeServerBoundHandler(TriConsumer handler) { diff --git a/Xplat/src/main/java/vazkii/botania/api/corporea/CorporeaHelper.java b/Xplat/src/main/java/vazkii/botania/api/corporea/CorporeaHelper.java index 0a4649ae56..62cedb7b1e 100644 --- a/Xplat/src/main/java/vazkii/botania/api/corporea/CorporeaHelper.java +++ b/Xplat/src/main/java/vazkii/botania/api/corporea/CorporeaHelper.java @@ -10,13 +10,12 @@ import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; - import org.jetbrains.annotations.Nullable; - import vazkii.botania.api.ServiceUtil; import java.util.Collections; @@ -54,7 +53,7 @@ default CorporeaRequestMatcher createMatcher(String name) { /** * Requests items from the network associated with {@code spark}. - * + * * @param matcher Specifies what you want to request. See {@link #createMatcher} to create one. * @param itemCount Specifies the maximum amount you want to request. If -1, the amount is unlimited. * @param requestor The entity that initiated this request, if there is one. @@ -89,5 +88,13 @@ default int signalStrengthForRequestSize(int requestSize) { return 0; } - default void registerRequestMatcher(ResourceLocation id, Class clazz, Function deserializer) {} + /** + * @deprecated Use the overload that can ser/de from a buffer also + */ + @Deprecated + default void registerRequestMatcher(ResourceLocation id, Class clazz, Function nbtDeserializer) { + } + + default void registerRequestMatcher(ResourceLocation id, Class clazz, Function nbtDeserializer, Function bufDeserializer) { + } } diff --git a/Xplat/src/main/java/vazkii/botania/api/corporea/CorporeaRequestMatcher.java b/Xplat/src/main/java/vazkii/botania/api/corporea/CorporeaRequestMatcher.java index 152f23472a..074d12591d 100644 --- a/Xplat/src/main/java/vazkii/botania/api/corporea/CorporeaRequestMatcher.java +++ b/Xplat/src/main/java/vazkii/botania/api/corporea/CorporeaRequestMatcher.java @@ -9,6 +9,7 @@ package vazkii.botania.api.corporea; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; @@ -18,7 +19,6 @@ * An interface for a Corporea Request matcher. Accepts an ItemStack and returns whether it fulfills the request. */ public interface CorporeaRequestMatcher extends Predicate { - /** * Returns whether the given stack matches the request's criteria. */ @@ -32,6 +32,11 @@ default boolean test(ItemStack stack) { */ default void writeToNBT(CompoundTag tag) {} + /** + * Serialize over the wire, for requesting an item from an index. + */ + default void writeToBuf(FriendlyByteBuf buf) {} + /** * Returns the pretty name of the requested item, for printing request feedback on Corporea Indexes. */ diff --git a/Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaIndexBlockEntity.java b/Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaIndexBlockEntity.java index 809bf178da..a6e53b7198 100644 --- a/Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaIndexBlockEntity.java +++ b/Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaIndexBlockEntity.java @@ -8,7 +8,10 @@ */ package vazkii.botania.common.block.block_entity.corporea; +import it.unimi.dsi.fastutil.ints.IntObjectPair; + import net.minecraft.ChatFormatting; +import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; @@ -27,7 +30,10 @@ import vazkii.botania.common.advancements.CorporeaRequestTrigger; import vazkii.botania.common.block.block_entity.BotaniaBlockEntities; import vazkii.botania.common.helper.MathHelper; -import vazkii.botania.network.serverbound.IndexStringRequestPacket; +import vazkii.botania.common.impl.corporea.matcher.CorporeaConstantMatcher; +import vazkii.botania.common.impl.corporea.matcher.CorporeaItemStackMatcher; +import vazkii.botania.common.impl.corporea.matcher.CorporeaStringMatcher; +import vazkii.botania.network.serverbound.IndexRequestPacket; import vazkii.botania.xplat.ClientXplatAbstractions; import vazkii.botania.xplat.XplatAbstractions; @@ -42,7 +48,7 @@ public class CorporeaIndexBlockEntity extends BaseCorporeaBlockEntity implements private static final Set serverIndexes = Collections.newSetFromMap(new WeakHashMap<>()); private static final Set clientIndexes = Collections.newSetFromMap(new WeakHashMap<>()); - private static final Map patterns = new LinkedHashMap<>(); + private static final Map PATTERNS = new LinkedHashMap<>(); /** * (name) = Item name, or "this" for the name of the item in your hand @@ -220,7 +226,7 @@ public String getName(Matcher m) { } }); - // [a ]nice [of ](name) = 69 + // [a ]nice [of ](name) = 69 addPattern("(?:a )?nice (?:of )?(.+)", new IRegexStacker() { @Override public int getCount(Matcher m) { @@ -268,12 +274,14 @@ public CorporeaIndexBlockEntity(BlockPos pos, BlockState state) { super(BotaniaBlockEntities.CORPOREA_INDEX, pos, state); } - public static void commonTick(Level level, BlockPos worldPosition, BlockState state, CorporeaIndexBlockEntity self) { + public static void commonTick(Level level, BlockPos worldPosition, BlockState state, + CorporeaIndexBlockEntity self) { double x = worldPosition.getX() + 0.5; double y = worldPosition.getY() + 0.5; double z = worldPosition.getZ() + 0.5; - List players = level.getEntitiesOfClass(Player.class, new AABB(x - RADIUS, y - RADIUS, z - RADIUS, x + RADIUS, y + RADIUS, z + RADIUS)); + List players = level.getEntitiesOfClass(Player.class, new AABB(x - RADIUS, y - RADIUS, z - RADIUS, + x + RADIUS, y + RADIUS, z + RADIUS)); self.hasCloseby = false; if (self.getSpark() != null) { for (Player player : players) { @@ -326,7 +334,8 @@ private CorporeaResult doRequest(CorporeaRequestMatcher matcher, int count, Corp spark.onItemsRequested(stacks); for (ItemStack stack : stacks) { if (!stack.isEmpty()) { - ItemEntity item = new ItemEntity(level, worldPosition.getX() + 0.5, worldPosition.getY() + 1.5, worldPosition.getZ() + 0.5, stack); + ItemEntity item = new ItemEntity(level, worldPosition.getX() + 0.5, worldPosition.getY() + 1.5, + worldPosition.getZ() + 0.5, stack); level.addFreshEntity(item); } } @@ -340,7 +349,7 @@ private boolean isInRange(Player player) { } public static void addPattern(String pattern, IRegexStacker stacker) { - patterns.put(Pattern.compile(pattern), stacker); + PATTERNS.put(Pattern.compile(pattern), stacker); } public static int i(Matcher m, int g) { @@ -378,44 +387,66 @@ public void performPlayerRequest(ServerPlayer player, CorporeaRequestMatcher req } public static class ClientHandler { - public static boolean onChat(Player player, String message) { + public static boolean onChat(LocalPlayer player, String message) { if (!getNearbyValidIndexes(player).isEmpty()) { - ClientXplatAbstractions.INSTANCE.sendToServer(new IndexStringRequestPacket(message)); + // TEMP TEST + var count2Matcher = getMatcherFromMsg(message, player); + + ClientXplatAbstractions.INSTANCE.sendToServer( + new IndexRequestPacket(count2Matcher.second(), count2Matcher.firstInt())); return true; } return false; } + + public static IntObjectPair getMatcherFromMsg(String message, LocalPlayer reqer) { + message = message.toLowerCase(Locale.ROOT).trim(); + + String name = ""; + int count = 0; + boolean foundAny = false; + for (var pattern : PATTERNS.keySet()) { + // They're in the set from least -> most specific + // TODO could it be faster to iterate reversed and break once something is found? + Matcher matcher = pattern.matcher(message); + if (matcher.matches()) { + IRegexStacker stacker = PATTERNS.get(pattern); + count = Math.min(MAX_REQUEST, stacker.getCount(matcher)); + name = stacker.getName(matcher).toLowerCase(Locale.ROOT).trim(); + foundAny = true; + } + } + + // The nonsense values above will always be filled by this point + // TODO would it make more sense for the "no special RE" matches to be special-cased? + CorporeaRequestMatcher corpyMatcher; + if (!foundAny) { + // this should never be passed? + corpyMatcher = new CorporeaConstantMatcher(false); + } else if (name.equals("this")) { + ItemStack stack = reqer.getMainHandItem(); + if (!stack.isEmpty()) { + corpyMatcher = new CorporeaItemStackMatcher(stack, true); + } else { + // Well, you're gonna have a tough time checking for air + corpyMatcher = new CorporeaConstantMatcher(false); + } + } else { + corpyMatcher = new CorporeaStringMatcher(name); + } + + return IntObjectPair.of(count, corpyMatcher); + } } - public static void onChatMessage(ServerPlayer player, String message) { + public static void receiveRequestFromPlayer(ServerPlayer player, CorporeaRequestMatcher matcher, int count) { if (player.isSpectator()) { return; } List nearbyIndexes = getNearbyValidIndexes(player); - if (!nearbyIndexes.isEmpty()) { - String msg = message.toLowerCase(Locale.ROOT).trim(); - for (CorporeaIndexBlockEntity index : nearbyIndexes) { - String name = ""; - int count = 0; - for (Pattern pattern : patterns.keySet()) { - Matcher matcher = pattern.matcher(msg); - if (matcher.matches()) { - IRegexStacker stacker = patterns.get(pattern); - count = Math.min(MAX_REQUEST, stacker.getCount(matcher)); - name = stacker.getName(matcher).toLowerCase(Locale.ROOT).trim(); - } - } - - if (name.equals("this")) { - ItemStack stack = player.getMainHandItem(); - if (!stack.isEmpty()) { - name = stack.getHoverName().getString().toLowerCase(Locale.ROOT).trim(); - } - } - - index.performPlayerRequest(player, CorporeaHelper.instance().createMatcher(name), count); - } + for (CorporeaIndexBlockEntity index : nearbyIndexes) { + index.performPlayerRequest(player, matcher, count); } } diff --git a/Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaRetainerBlockEntity.java b/Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaRetainerBlockEntity.java index 7456a86a48..befd34b3a3 100644 --- a/Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaRetainerBlockEntity.java +++ b/Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaRetainerBlockEntity.java @@ -9,7 +9,6 @@ package vazkii.botania.common.block.block_entity.corporea; import com.mojang.blaze3d.vertex.PoseStack; - import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; import net.minecraft.client.resources.language.I18n; @@ -21,19 +20,17 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; - import org.jetbrains.annotations.Nullable; - import vazkii.botania.api.block.WandHUD; import vazkii.botania.api.block.Wandable; -import vazkii.botania.api.corporea.*; +import vazkii.botania.api.corporea.CorporeaHelper; +import vazkii.botania.api.corporea.CorporeaRequestMatcher; +import vazkii.botania.api.corporea.CorporeaRequestor; +import vazkii.botania.api.corporea.CorporeaSpark; import vazkii.botania.api.internal.VanillaPacketDispatcher; import vazkii.botania.common.block.block_entity.BotaniaBlockEntities; import vazkii.botania.common.block.block_entity.BotaniaBlockEntity; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Function; +import vazkii.botania.common.impl.corporea.CorporeaHelperImpl; public class CorporeaRetainerBlockEntity extends BotaniaBlockEntity implements Wandable { private static final String TAG_REQUEST_X = "requestX"; @@ -43,9 +40,6 @@ public class CorporeaRetainerBlockEntity extends BotaniaBlockEntity implements W private static final String TAG_REQUEST_COUNT = "requestCount"; private static final String TAG_RETAIN_MISSING = "retainMissing"; - private static final Map> corporeaMatcherDeserializers = new ConcurrentHashMap<>(); - private static final Map, ResourceLocation> corporeaMatcherSerializers = new ConcurrentHashMap<>(); - private BlockPos requestPos = BlockPos.ZERO; @Nullable @@ -103,7 +97,8 @@ public void writePacketNBT(CompoundTag cmp) { cmp.putInt(TAG_REQUEST_Y, requestPos.getY()); cmp.putInt(TAG_REQUEST_Z, requestPos.getZ()); - ResourceLocation reqType = request != null ? corporeaMatcherSerializers.get(request.getClass()) : null; + ResourceLocation reqType = request != null ? + CorporeaHelperImpl.corporeaMatcherSerializers.get(request.getClass()) : null; if (reqType != null) { cmp.putString(TAG_REQUEST_TYPE, reqType.toString()); @@ -123,8 +118,9 @@ public void readPacketNBT(CompoundTag cmp) { requestPos = new BlockPos(x, y, z); ResourceLocation reqType = ResourceLocation.tryParse(cmp.getString(TAG_REQUEST_TYPE)); - if (reqType != null && corporeaMatcherDeserializers.containsKey(reqType)) { - request = corporeaMatcherDeserializers.get(reqType).apply(cmp); + if (reqType != null && CorporeaHelperImpl.corporeaMatcherDeserializers.containsKey(reqType)) { + var deser = CorporeaHelperImpl.corporeaMatcherDeserializers.get(reqType); + request = deser.nbtDeser().apply(cmp); } else { request = null; } @@ -132,11 +128,6 @@ public void readPacketNBT(CompoundTag cmp) { retainMissing = cmp.getBoolean(TAG_RETAIN_MISSING); } - public static void addCorporeaRequestMatcher(ResourceLocation id, Class clazz, Function deserializer) { - corporeaMatcherSerializers.put(clazz, id); - corporeaMatcherDeserializers.put(id, deserializer); - } - public static class WandHud implements WandHUD { private final CorporeaRetainerBlockEntity retainer; diff --git a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaHelperImpl.java b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaHelperImpl.java index 20ff2e9e7d..8060b262f3 100644 --- a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaHelperImpl.java +++ b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaHelperImpl.java @@ -9,13 +9,12 @@ package vazkii.botania.common.impl.corporea; import com.google.common.base.Predicates; - import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMaps; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; - import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; @@ -24,17 +23,22 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.AABB; - import org.jetbrains.annotations.Nullable; - import vazkii.botania.api.corporea.*; -import vazkii.botania.common.block.block_entity.corporea.CorporeaRetainerBlockEntity; +import vazkii.botania.common.impl.corporea.matcher.CorporeaItemStackMatcher; +import vazkii.botania.common.impl.corporea.matcher.CorporeaStringMatcher; import vazkii.botania.xplat.XplatAbstractions; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; public class CorporeaHelperImpl implements CorporeaHelper { + // TODO: these should probably be a registry + public static final Map corporeaMatcherDeserializers = + new ConcurrentHashMap<>(); + public static final Map, ResourceLocation> corporeaMatcherSerializers = + new ConcurrentHashMap<>(); private final WeakHashMap> cachedNetworks = new WeakHashMap<>(); @Override @@ -129,10 +133,20 @@ public int signalStrengthForRequestSize(int requestSize) { @Override public void registerRequestMatcher(ResourceLocation id, Class clazz, Function deserializer) { - CorporeaRetainerBlockEntity.addCorporeaRequestMatcher(id, clazz, deserializer); + this.registerRequestMatcher(id, clazz, deserializer, null); + } + + @Override + public void registerRequestMatcher(ResourceLocation id, Class clazz, Function nbtDeserializer, Function bufDeserializer) { + corporeaMatcherSerializers.put(clazz, id); + corporeaMatcherDeserializers.put(id, new RequestDeserializer(nbtDeserializer, bufDeserializer)); } public void clearCache() { cachedNetworks.clear(); } + + public record RequestDeserializer(Function nbtDeser, + @Nullable Function bufDeser) { + } } diff --git a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaStringMatcher.java b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaStringMatcher.java deleted file mode 100644 index d528abcb99..0000000000 --- a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaStringMatcher.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This class is distributed as part of the Botania Mod. - * Get the Source Code in github: - * https://github.com/Vazkii/Botania - * - * Botania is Open Source and distributed under the - * Botania License: http://botaniamod.net/license.php - */ -package vazkii.botania.common.impl.corporea; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.chat.Component; -import net.minecraft.world.item.ItemStack; - -import org.apache.commons.lang3.text.WordUtils; - -import vazkii.botania.api.corporea.CorporeaRequestMatcher; - -import java.util.Locale; -import java.util.StringJoiner; -import java.util.regex.Pattern; - -public class CorporeaStringMatcher implements CorporeaRequestMatcher { - - private static final Pattern patternControlCode = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]"); - public static final String[] WILDCARD_STRINGS = { "...", "~", "+", "?" }; - private static final String TAG_REQUEST_CONTENTS = "requestContents"; - - private final String[] expression; - - public CorporeaStringMatcher(String expression) { - boolean contains = false; - for (String wc : WILDCARD_STRINGS) { - if (expression.endsWith(wc)) { - contains = true; - expression = expression.substring(0, expression.length() - wc.length()); - } else if (expression.startsWith(wc)) { - contains = true; - expression = expression.substring(wc.length()); - } - - if (contains) { - break; - } - } - this.expression = (contains ? "*" + expression + "*" : expression).split("\\*+", -1); - } - - @Override - public boolean test(ItemStack stack) { - if (stack.isEmpty()) { - return false; - } - - String name = stripControlCodes(stack.getHoverName().getString().toLowerCase(Locale.ROOT).trim()); - return matchGlob(name) - || matchGlob(name + "s") - || matchGlob(name + "es") - || name.endsWith("y") && matchGlob(name.substring(0, name.length() - 1) + "ies"); - } - - public static CorporeaStringMatcher createFromNBT(CompoundTag tag) { - String expression = tag.getString(TAG_REQUEST_CONTENTS); - return new CorporeaStringMatcher(expression); - } - - @Override - public void writeToNBT(CompoundTag tag) { - tag.putString(TAG_REQUEST_CONTENTS, toString()); - } - - @Override - @SuppressWarnings("deprecation") - public Component getRequestName() { - String value = WordUtils.capitalizeFully(toString()); - // cope with initial globs - if (value.charAt(0) == '*' && value.length() >= 2) { - value = "*" + Character.toUpperCase(value.charAt(1)) + value.substring(2); - } - return Component.literal(value); - } - - @Override - public String toString() { - StringJoiner sj = new StringJoiner("*"); - for (String s : expression) { - sj.add(s); - } - return sj.toString(); - } - - private boolean matchGlob(String str) { - if (expression.length == 1) { - return expression[0].equals(str); - } - - if (!str.startsWith(expression[0])) { - return false; - } - - int offset = expression[0].length(); - for (int i = 1; i < expression.length - 1; i++) { - String section = expression[i]; - int found = str.indexOf(section, offset); - if (found == -1) { - return false; - } - offset = found + section.length(); - } - return str.substring(offset).endsWith(expression[expression.length - 1]); - } - - // Copy from StringUtils - private static String stripControlCodes(String str) { - return patternControlCode.matcher(str).replaceAll(""); - } -} diff --git a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/DefaultCorporeaMatchers.java b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/DefaultCorporeaMatchers.java index 4fd717f4e2..841e81d0de 100644 --- a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/DefaultCorporeaMatchers.java +++ b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/DefaultCorporeaMatchers.java @@ -1,14 +1,22 @@ package vazkii.botania.common.impl.corporea; import vazkii.botania.api.corporea.CorporeaHelper; +import vazkii.botania.common.impl.corporea.matcher.CorporeaConstantMatcher; +import vazkii.botania.common.impl.corporea.matcher.CorporeaItemStackMatcher; +import vazkii.botania.common.impl.corporea.matcher.CorporeaStringMatcher; import static vazkii.botania.common.lib.ResourceLocationHelper.prefix; public final class DefaultCorporeaMatchers { public static void init() { - CorporeaHelper.instance().registerRequestMatcher(prefix("string"), CorporeaStringMatcher.class, CorporeaStringMatcher::createFromNBT); - CorporeaHelper.instance().registerRequestMatcher(prefix("item_stack"), CorporeaItemStackMatcher.class, CorporeaItemStackMatcher::createFromNBT); + CorporeaHelper.instance().registerRequestMatcher(prefix("string"), CorporeaStringMatcher.class, + CorporeaStringMatcher::createFromNBT, CorporeaStringMatcher::createFromBuf); + CorporeaHelper.instance().registerRequestMatcher(prefix("item_stack"), CorporeaItemStackMatcher.class, + CorporeaItemStackMatcher::createFromNBT, CorporeaItemStackMatcher::createFromBuf); + CorporeaHelper.instance().registerRequestMatcher(prefix("constant"), CorporeaConstantMatcher.class, + CorporeaConstantMatcher::createFromNBT, CorporeaConstantMatcher::createFromBuf); } - private DefaultCorporeaMatchers() {} + private DefaultCorporeaMatchers() { + } } diff --git a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/matcher/CorporeaConstantMatcher.java b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/matcher/CorporeaConstantMatcher.java new file mode 100644 index 0000000000..86b54ecad3 --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/matcher/CorporeaConstantMatcher.java @@ -0,0 +1,56 @@ +package vazkii.botania.common.impl.corporea.matcher; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import vazkii.botania.api.corporea.CorporeaRequestMatcher; + +/** + * Either always or never matches + */ +public class CorporeaConstantMatcher implements CorporeaRequestMatcher { + private static final String TAG_MATCH = "match"; + + private final boolean match; + + public CorporeaConstantMatcher(boolean match) { + this.match = match; + } + + @Override + public boolean test(ItemStack stack) { + return this.match; + } + + public static CorporeaConstantMatcher createFromNBT(CompoundTag tag) { + var match = tag.getBoolean(TAG_MATCH); + + return new CorporeaConstantMatcher(match); + } + + @Override + public void writeToNBT(CompoundTag tag) { + tag.putBoolean(TAG_MATCH, this.match); + } + + public static CorporeaConstantMatcher createFromBuf(FriendlyByteBuf buf) { + var match = buf.readBoolean(); + + return new CorporeaConstantMatcher(match); + } + + @Override + public void writeToBuf(FriendlyByteBuf buf) { + buf.writeBoolean(this.match); + } + + @Override + public Component getRequestName() { + if (this.match) { + return Component.literal("*"); + } else { + return Component.translatable("key.botania_corporea_request.never"); + } + } +} diff --git a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaItemStackMatcher.java b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/matcher/CorporeaItemStackMatcher.java similarity index 78% rename from Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaItemStackMatcher.java rename to Xplat/src/main/java/vazkii/botania/common/impl/corporea/matcher/CorporeaItemStackMatcher.java index 42a00deeb8..8ed641126f 100644 --- a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/CorporeaItemStackMatcher.java +++ b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/matcher/CorporeaItemStackMatcher.java @@ -6,12 +6,12 @@ * Botania is Open Source and distributed under the * Botania License: http://botaniamod.net/license.php */ -package vazkii.botania.common.impl.corporea; +package vazkii.botania.common.impl.corporea.matcher; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; - import vazkii.botania.api.corporea.CorporeaRequestMatcher; import vazkii.botania.common.helper.ItemNBTHelper; @@ -43,6 +43,18 @@ public void writeToNBT(CompoundTag tag) { tag.putBoolean(TAG_REQUEST_CHECK_NBT, checkNBT); } + public static CorporeaItemStackMatcher createFromBuf(FriendlyByteBuf buf) { + var stack = buf.readItem(); + var checkNBT = buf.readBoolean(); + return new CorporeaItemStackMatcher(stack, checkNBT); + } + + @Override + public void writeToBuf(FriendlyByteBuf buf) { + buf.writeItem(this.match); + buf.writeBoolean(this.checkNBT); + } + @Override public Component getRequestName() { return match.getDisplayName(); diff --git a/Xplat/src/main/java/vazkii/botania/common/impl/corporea/matcher/CorporeaStringMatcher.java b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/matcher/CorporeaStringMatcher.java new file mode 100644 index 0000000000..5f8625a09a --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/common/impl/corporea/matcher/CorporeaStringMatcher.java @@ -0,0 +1,356 @@ +/* + * This class is distributed as part of the Botania Mod. + * Get the Source Code in github: + * https://github.com/Vazkii/Botania + * + * Botania is Open Source and distributed under the + * Botania License: http://botaniamod.net/license.php + */ +package vazkii.botania.common.impl.corporea.matcher; + +import it.unimi.dsi.fastutil.ints.*; + +import net.minecraft.Util; +import net.minecraft.client.resources.language.I18n; +import net.minecraft.core.Registry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; + +import org.apache.commons.lang3.text.WordUtils; + +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.corporea.CorporeaRequestMatcher; + +import java.util.Arrays; +import java.util.Locale; +import java.util.StringJoiner; +import java.util.regex.Pattern; + +public class CorporeaStringMatcher implements CorporeaRequestMatcher { + + private static final Pattern patternControlCode = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]"); + public static final String[] WILDCARD_STRINGS = { "...", "~", "+", "?" }; + + // TODO: remove this when 1.20 comes around + private static final String TAG_REQUEST_CONTENTS_LEGACY = "requestContents"; + + private static final String TAG_GLOB_CONTENTS = "glob"; + private static final String TAG_RANGES_RANGES = "ranges"; + private static final String TAG_RANGES_SINGLETONS = "singletons"; + private static final String TAG_RANGES_HASH = "hash"; + + // Stored as a list of segments that must match. + // *foo*bar is stored as "", "foo", "bar" + // TODO: stop being public + private final String[] expression; + private final RegistryRanges ranges; + + public CorporeaStringMatcher(String expression) { + boolean contains = false; + for (String wc : WILDCARD_STRINGS) { + if (expression.endsWith(wc)) { + contains = true; + expression = expression.substring(0, expression.length() - wc.length()); + } else if (expression.startsWith(wc)) { + contains = true; + expression = expression.substring(wc.length()); + } + + if (contains) { + break; + } + } + this.expression = (contains ? "*" + expression + "*" : expression).split("\\*+", -1); + + this.ranges = getRegistryRanges(this.expression); + } + + private CorporeaStringMatcher(String[] expression, RegistryRanges ranges) { + this.expression = expression; + this.ranges = ranges; + } + + @Override + public boolean test(ItemStack stack) { + if (stack.isEmpty()) { + return false; + } + + if (this.ranges.registryHash != RegistryRanges.ITEM_REGISTRY_HASH) { + // True if the current registry hash is different than one loaded from NBT. + // + // This will only be true when: + // - A request is intercepted + // - The server is closed + // - Mods are added/removed + // - The server is reopened + // and in that edge case we logged an error at deser, and fallback to english string matching on the server + return matchGlob(this.expression, stack.getHoverName().getString()); + } + + if (stack.hasCustomHoverName()) { + // In alwinfy's words, "matching by name is matching by name." + // If the item is renamed, we don't want to get it if its "natural" name matches + return matchGlob(this.expression, stack.getHoverName().getString()); + } + return this.ranges.containsItem(stack.getItem()); + } + + public static CorporeaStringMatcher createFromNBT(CompoundTag tag) { + if (tag.contains(TAG_REQUEST_CONTENTS_LEGACY, Tag.TAG_LIST)) { + return new CorporeaStringMatcher(tag.getString(TAG_REQUEST_CONTENTS_LEGACY)); + } + + ListTag contents = tag.getList(TAG_GLOB_CONTENTS, Tag.TAG_STRING); + String[] expr = new String[contents.size()]; + for (int i = 0; i < contents.size(); i++) { + expr[i] = contents.getString(i); + } + + int[] ranges = tag.getIntArray(TAG_RANGES_RANGES); + int[] singletons = tag.getIntArray(TAG_RANGES_SINGLETONS); + int hash = tag.getInt(TAG_RANGES_HASH); + + if (hash != RegistryRanges.ITEM_REGISTRY_HASH) { + BotaniaAPI.LOGGER.warn("Registry hash when a CorporeaStringMatcher was written to NBT ({}) is different " + + "than the current hash ({}). " + + "This request will fall back to a server-side I18n check.", + hash, RegistryRanges.ITEM_REGISTRY_HASH); + } + return new CorporeaStringMatcher(expr, new RegistryRanges(ranges, IntSet.of(singletons), hash)); + } + + @Override + public void writeToNBT(CompoundTag tag) { + ListTag contents = new ListTag(); + for (var part : this.expression) { + contents.add(StringTag.valueOf(part)); + } + tag.put(TAG_GLOB_CONTENTS, contents); + tag.putIntArray(TAG_RANGES_RANGES, this.ranges.ranges); + tag.putIntArray(TAG_RANGES_SINGLETONS, this.ranges.singletons.toIntArray()); + tag.putInt(TAG_RANGES_HASH, this.ranges.registryHash); + } + + public static CorporeaStringMatcher createFromBuf(FriendlyByteBuf buf) { + var globLen = buf.readVarInt(); + var expr = new String[globLen]; + for (int i = 0; i < globLen; i++) { + expr[i] = buf.readUtf(); + } + + var ranges = buf.readVarIntArray(); + var singletons = buf.readVarIntArray(); + var hash = buf.readVarInt(); + + return new CorporeaStringMatcher(expr, new RegistryRanges(ranges, IntSet.of(singletons), hash)); + } + + @Override + public void writeToBuf(FriendlyByteBuf buf) { + buf.writeVarInt(this.expression.length); + for (var part : this.expression) { + buf.writeUtf(part); + } + + // TODO: we could probably write this more efficiently + // for example, on games with very large registries, it would probably be faster to send + // sorted deltas of the singletons array... + buf.writeVarIntArray(this.ranges.ranges); + buf.writeVarIntArray(this.ranges.singletons.toIntArray()); + // If the hash is not the same on the client and the server there's + // already going to be waaaaaaaaaaaay bigger problems + // but we do need to put *something* there on the server and it's like 4 extra bytes we'll live + buf.writeVarInt(this.ranges.registryHash); + } + + @Override + @SuppressWarnings("deprecation") + public Component getRequestName() { + String value = WordUtils.capitalizeFully(toString()); + // cope with initial globs + if (value.charAt(0) == '*' && value.length() >= 2) { + value = "*" + Character.toUpperCase(value.charAt(1)) + value.substring(2); + } + return Component.literal(value); + } + + @Override + public String toString() { + StringJoiner sj = new StringJoiner("*"); + for (String s : expression) { + sj.add(s); + } + return sj.toString(); + } + + private static boolean matchGlob(String[] expression, String str) { + str = stripControlCodes(str.toLowerCase(Locale.ROOT).trim()); + + if (expression.length == 1) { + return expression[0].equals(str); + } + + if (!str.startsWith(expression[0])) { + return false; + } + + int offset = expression[0].length(); + for (int i = 1; i < expression.length - 1; i++) { + String section = expression[i]; + int found = str.indexOf(section, offset); + if (found == -1) { + return false; + } + offset = found + section.length(); + } + return str.substring(offset).endsWith(expression[expression.length - 1]); + } + + // Copy from StringUtils + private static String stripControlCodes(String str) { + return patternControlCode.matcher(str).replaceAll(""); + } + + /** + * Client side only, get the ranges of registry IDs the glob matches from the langfile + */ + public static RegistryRanges getRegistryRanges(String[] glob) { + // TODO what kind of set might be fastest, RB, AVL? + // I'm pretty sure inserting sorted is faster than inserting all and then sorting; + // they're both O(n log n) but the n in the log n is smaller when inserting sorted because + // there's fewer to cmp against ... + IntSortedSet regiMatches = new IntRBTreeSet(); + for (Item item : Registry.ITEM) { + var name = I18n.get(item.getDescriptionId()); + if (matchGlob(glob, name)) { + var id = Registry.ITEM.getId(item); + regiMatches.add(id); + } + } + + // Compose these ints into a set of ranges (for compactness over the wire) + if (regiMatches.isEmpty()) { + return new RegistryRanges(new int[0], IntSet.of(), RegistryRanges.ITEM_REGISTRY_HASH); + } + + // guess on the out size of the list, idk if this is a good one or not + var ranges = new IntArrayList(regiMatches.size() / 4); + var singletons = new IntOpenHashSet(regiMatches.size() / 4); + var regiIter = regiMatches.intIterator(); + var anchor = regiIter.nextInt(); + var prev = anchor; + + while (regiIter.hasNext()) { + var here = regiIter.nextInt(); + assert here > prev; + if (here != prev + 1) { + // Then we've come to a gap + if (anchor == prev) { + singletons.add(anchor); + } else { + // Start a new range + ranges.push(anchor); + ranges.push(prev); + } + anchor = here; + } + + prev = here; + } + // and do it again to get the last element + if (anchor == prev) { + singletons.add(anchor); + } else { + // Start a new range + ranges.push(anchor); + ranges.push(prev); + } + assert ranges.size() % 2 == 0; + return new RegistryRanges(ranges.toIntArray(), singletons, RegistryRanges.ITEM_REGISTRY_HASH); + } + + /** + * @param ranges List of int range pairs; {@code [min0, max0, min1, max1, ...] }. Inclusive. + * @param singletons List of singleton ranges + * @param registryHash Hash of the registry at creation time. We use this to try and detect if it's changed. + * If so, the IDs won't be valid anymore, so fallback to the dumb English string checking, + * which won't work on Quilt, but my god that's a like 1% of 1% chance you'll live + */ + private record RegistryRanges(int[] ranges, IntSet singletons, int registryHash) { + + private static final int HASH_SEED = 0xBAD_5EED; + + /** + * Recalculated once each time the JVM is launched. + */ + public static final int ITEM_REGISTRY_HASH = Util.make(() -> { + int hash = HASH_SEED; + + // It looks like the for loop is not the same order every time, so use plain addition, + // which is associative, so it doesn't matter the order + for (Item item : Registry.ITEM) { + hash += Registry.ITEM.getKey(item).hashCode(); + } + + return hash; + }); + + public boolean containsItem(Item item) { + var id = Registry.ITEM.getId(item); + // TODO: is it faster on average to check set then ranges, or vice versa? + + if (this.singletons.contains(id)) { + return true; + } + + int searchIdx = Arrays.binarySearch(this.ranges, id); + if (searchIdx >= 0) { + // then we hit one of the edges of a range, so it's in + return true; + } else { + int insertionPoint = -searchIdx + 1; + // If we have a list [1, 2, 10, 20], the "good" places to insert the number are: + // [1 HERE 2 10 HERE 20] + // that is, when the insertion index is odd + return insertionPoint % 2 == 1; + } + } + + @Override + public String toString() { + var bob = new StringBuilder("RegistryRanges["); + for (int i = 0; i < this.ranges.length; i += 2) { + bob.append(this.ranges[i]); + bob.append('-'); + bob.append(this.ranges[i + 1]); + if (i < this.ranges.length - 2) { + bob.append(','); + } else { + bob.append(';'); + } + bob.append(' '); + } + + var first = true; + for (var item : this.singletons) { + if (!first) { + bob.append(", "); + } + bob.append(item); + first = false; + } + + bob.append("; #"); + bob.append(this.registryHash); + bob.append(']'); + return bob.toString(); + } + } +} diff --git a/Xplat/src/main/java/vazkii/botania/network/serverbound/IndexRequestPacket.java b/Xplat/src/main/java/vazkii/botania/network/serverbound/IndexRequestPacket.java new file mode 100644 index 0000000000..956131cc85 --- /dev/null +++ b/Xplat/src/main/java/vazkii/botania/network/serverbound/IndexRequestPacket.java @@ -0,0 +1,53 @@ +package vazkii.botania.network.serverbound; + +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import vazkii.botania.api.BotaniaAPI; +import vazkii.botania.api.corporea.CorporeaRequestMatcher; +import vazkii.botania.common.block.block_entity.corporea.CorporeaIndexBlockEntity; +import vazkii.botania.common.impl.corporea.CorporeaHelperImpl; +import vazkii.botania.common.impl.corporea.matcher.CorporeaConstantMatcher; +import vazkii.botania.network.BotaniaPacket; + +import static vazkii.botania.common.lib.ResourceLocationHelper.prefix; + +public record IndexRequestPacket(CorporeaRequestMatcher request, int count) implements BotaniaPacket { + public static final ResourceLocation ID = prefix("idxr"); + + public static IndexRequestPacket decode(FriendlyByteBuf buf) { + var ty = buf.readResourceLocation(); + var deser = CorporeaHelperImpl.corporeaMatcherDeserializers.get(ty); + if (deser == null || deser.bufDeser() == null) { + // oh dear, oh god. someone is doing something very wrong. just do our best to bail out. + return new IndexRequestPacket(CorporeaRequestMatcher.Dummy.INSTANCE, 0); + } + var req = deser.bufDeser().apply(buf); + var count = buf.readVarInt(); + return new IndexRequestPacket(req, count); + } + + @Override + public void encode(FriendlyByteBuf buf) { + var resloc = CorporeaHelperImpl.corporeaMatcherSerializers.get(this.request.getClass()); + if (resloc == null) { + // oh god ummm uhhhhh + new IndexRequestPacket(new CorporeaConstantMatcher(false), 0).encode(buf); + return; + } else { + buf.writeResourceLocation(resloc); + } + this.request.writeToBuf(buf); + buf.writeVarInt(this.count); + } + + @Override + public ResourceLocation getFabricId() { + return ID; + } + + public void handle(MinecraftServer server, ServerPlayer player) { + server.execute(() -> CorporeaIndexBlockEntity.receiveRequestFromPlayer(player, this.request(), count)); + } +} diff --git a/Xplat/src/main/java/vazkii/botania/network/serverbound/IndexStringRequestPacket.java b/Xplat/src/main/java/vazkii/botania/network/serverbound/IndexStringRequestPacket.java deleted file mode 100644 index 4f99d71614..0000000000 --- a/Xplat/src/main/java/vazkii/botania/network/serverbound/IndexStringRequestPacket.java +++ /dev/null @@ -1,33 +0,0 @@ -package vazkii.botania.network.serverbound; - -import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerPlayer; - -import vazkii.botania.common.block.block_entity.corporea.CorporeaIndexBlockEntity; -import vazkii.botania.network.BotaniaPacket; - -import static vazkii.botania.common.lib.ResourceLocationHelper.prefix; - -public record IndexStringRequestPacket(String message) implements BotaniaPacket { - public static final ResourceLocation ID = prefix("idxs"); - - public static IndexStringRequestPacket decode(FriendlyByteBuf buf) { - return new IndexStringRequestPacket(buf.readUtf()); - } - - @Override - public void encode(FriendlyByteBuf buf) { - buf.writeUtf(message); - } - - @Override - public ResourceLocation getFabricId() { - return ID; - } - - public void handle(MinecraftServer server, ServerPlayer player) { - server.execute(() -> CorporeaIndexBlockEntity.onChatMessage(player, message())); - } -} diff --git a/Xplat/src/main/resources/assets/botania/lang/en_us.json b/Xplat/src/main/resources/assets/botania/lang/en_us.json index d8be60e1bb..55fd3a5294 100644 --- a/Xplat/src/main/resources/assets/botania/lang/en_us.json +++ b/Xplat/src/main/resources/assets/botania/lang/en_us.json @@ -206,6 +206,7 @@ "tag.botania.seed_apothecary_reagent": "Seed Reagents", "key.botania_corporea_request": "Corporea Request", + "key.botania_corporea_request.never": "", "botania.triggers.manaDetector": "Mana Burst", "botania.triggers.manaEmpty": "Mana Empty", @@ -3464,7 +3465,7 @@ "botania.entry.preventingDecay": "Numerical Mana", "botania.tagline.preventingDecay": "Seeing the numbers in mana", "botania.page.preventingDecay0": "no", - + "botania.subtitle.agricarnation": "Agricarnation glimmers", "botania.subtitle.airRod": "Rod of the Skies whooshes", "botania.subtitle.altarCraft": "Petal Apothecary twinkles", @@ -3549,6 +3550,6 @@ "botania.subtitle.vineBallThrow": "Vine Ball flies", "botania.subtitle.virusInfect": "Virus infects", "botania.subtitle.worldSeedTeleport": "World Seed teleports", - + "botania.subtitle.way": "O-oooooooooo AAAAE-A-A-I-A-U-JO-oooooooooooo AAE-O-A-A-U-U-A-E-eee-ee-eee AAAAE-A-E-I-E-A-JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA O-oooooooooo AAAAE-A-A-I-A-U-JO-oooooooooooo AAE-O-A-A-U-U-A-E-eee-ee-eee AAAAE-A-E-I-E-A-JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA O-oooooooooo AAAAE-A-A-I-A-U-JO-oooooooooooo AAE-O-A-A-U-U-A-E-eee-ee-eee AAAAE-A-E-I-E-A-JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA" }