diff --git a/build.gradle b/build.gradle index 0ec26eb41..0b402b79e 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,14 @@ subprojects { repositories { maven { url "https://maven.neoforged.net/releases/" } + if (rootProject.neoforge_pr != "") { + maven { + url "https://prmaven.neoforged.net/NeoForge/pr$rootProject.neoforge_pr" + content { + includeModule("net.neoforged", "neoforge") + } + } + } } } diff --git a/common/src/main/java/dev/architectury/event/events/common/LootEvent.java b/common/src/main/java/dev/architectury/event/events/common/LootEvent.java index 6ceb78dba..65a09cef9 100644 --- a/common/src/main/java/dev/architectury/event/events/common/LootEvent.java +++ b/common/src/main/java/dev/architectury/event/events/common/LootEvent.java @@ -46,9 +46,9 @@ public interface LootEvent { * *

Example: adding diamonds as a drop for dirt

*
{@code
-     * LootEvent.MODIFY_LOOT_TABLE.register((lootTables, id, context, builtin) -> {
+     * LootEvent.MODIFY_LOOT_TABLE.register((key, context, builtin) -> {
      *     // Check that the loot table is dirt and built-in
-     *     if (builtin && Blocks.DIRT.getLootTable().equals(id)) {
+     *     if (builtin && Blocks.DIRT.getLootTable().equals(key)) {
      *         // Create a loot pool with a single item entry of Items.DIAMOND
      *         LootPool.Builder pool = LootPool.lootPool().add(LootItem.lootTableItem(Items.DIAMOND));
      *         context.addPool(pool);
@@ -58,7 +58,7 @@ public interface LootEvent {
      *
      * @see ModifyLootTable#modifyLootTable(ResourceKey, LootTableModificationContext, boolean)
      */
-    // Event MODIFY_LOOT_TABLE = EventFactory.createLoop();
+    Event MODIFY_LOOT_TABLE = EventFactory.createLoop();
     
     @FunctionalInterface
     interface ModifyLootTable {
diff --git a/common/src/main/java/dev/architectury/fluid/FluidStack.java b/common/src/main/java/dev/architectury/fluid/FluidStack.java
index f30ef2815..6d0d8d342 100644
--- a/common/src/main/java/dev/architectury/fluid/FluidStack.java
+++ b/common/src/main/java/dev/architectury/fluid/FluidStack.java
@@ -19,7 +19,6 @@
 
 package dev.architectury.fluid;
 
-import com.google.common.collect.Iterators;
 import com.mojang.serialization.Codec;
 import dev.architectury.hooks.fluid.FluidStackHooks;
 import dev.architectury.injectables.annotations.ExpectPlatform;
@@ -35,7 +34,8 @@
 import org.jetbrains.annotations.ApiStatus;
 import org.jetbrains.annotations.Nullable;
 
-import java.util.*;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.function.BiFunction;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -66,100 +66,6 @@ private static FluidStackAdapter adapt(Function toVa
         throw new AssertionError();
     }
     
-    @Override
-    public DataComponentMap getComponents() {
-        return new DataComponentMap() {
-            @Nullable
-            @Override
-            public  T get(DataComponentType type) {
-                return getPatch().get(type).orElse(null);
-            }
-            
-            @Override
-            public Set> keySet() {
-                return new AbstractSet<>() {
-                    @Override
-                    public Iterator> iterator() {
-                        return Iterators.transform(getPatch().entrySet().iterator(), Map.Entry::getKey);
-                    }
-                    
-                    @Override
-                    public int size() {
-                        return getPatch().entrySet().size();
-                    }
-                    
-                    @Override
-                    public boolean contains(Object o) {
-                        if (!(o instanceof DataComponentType type)) return false;
-                        return getPatch().get(type).isPresent();
-                    }
-                };
-            }
-        };
-    }
-    
-    public  T set(DataComponentType dataComponentType, @Nullable T object) {
-        T previous = (T) get(dataComponentType);
-        DataComponentPatch.Builder builder = DataComponentPatch.builder();
-        for (TypedDataComponent component : getComponents()) {
-            if (component.type() != dataComponentType) {
-                builder.set(component);
-            }
-        }
-        if (object != null) {
-            builder.set(dataComponentType, object);
-        }
-        setPatch(builder.build());
-        return previous;
-    }
-    
-    @Nullable
-    public  T update(DataComponentType dataComponentType, T object, U object2, BiFunction biFunction) {
-        return this.set(dataComponentType, biFunction.apply(this.getOrDefault(dataComponentType, object), object2));
-    }
-    
-    @Nullable
-    public  T update(DataComponentType dataComponentType, T object, UnaryOperator unaryOperator) {
-        return this.set(dataComponentType, unaryOperator.apply(this.getOrDefault(dataComponentType, object)));
-    }
-    
-    @Nullable
-    public  T remove(DataComponentType dataComponentType) {
-        return this.set(dataComponentType, null);
-    }
-    
-    public void applyComponents(DataComponentPatch dataComponentPatch) {
-        DataComponentPatch.Builder builder = DataComponentPatch.builder();
-        for (TypedDataComponent component : getComponents()) {
-            builder.set(component);
-        }
-        for (Map.Entry, Optional> entry : dataComponentPatch.entrySet()) {
-            if (entry.getValue().isPresent()) {
-                //noinspection rawtypes
-                builder.set((DataComponentType) entry.getKey(), entry.getValue().get());
-            } else {
-                builder.remove(entry.getKey());
-            }
-        }
-        setPatch(builder.build());
-    }
-    
-    public void applyComponents(DataComponentMap dataComponentMap) {
-        DataComponentPatch.Builder builder = DataComponentPatch.builder();
-        for (TypedDataComponent component : getComponents()) {
-            builder.set(component);
-        }
-        for (TypedDataComponent entry : dataComponentMap) {
-            if (entry.value() != null) {
-                //noinspection rawtypes
-                builder.set((DataComponentType) entry.type(), entry.value());
-            } else {
-                builder.remove(entry.type());
-            }
-        }
-        setPatch(builder.build());
-    }
-    
     @ApiStatus.Internal
     public interface FluidStackAdapter {
         T create(Supplier fluid, long amount, @Nullable DataComponentPatch patch);
@@ -174,7 +80,19 @@ public interface FluidStackAdapter {
         
         DataComponentPatch getPatch(T value);
         
-        void setPatch(T value, DataComponentPatch patch);
+        PatchedDataComponentMap getComponents(T value);
+        
+        void applyComponents(T value, DataComponentPatch patch);
+        
+        void applyComponents(T value, DataComponentMap patch);
+        
+        @Nullable  D set(T value, DataComponentType type, @Nullable D component);
+        
+        @Nullable  D remove(T value, DataComponentType type);
+        
+        @Nullable  D update(T value, DataComponentType type, D component, UnaryOperator updater);
+        
+        @Nullable  D update(T value, DataComponentType type, D component, U updateContext, BiFunction updater);
         
         T copy(T value);
         
@@ -260,8 +178,37 @@ public DataComponentPatch getPatch() {
         return ADAPTER.getPatch(value);
     }
     
-    public void setPatch(DataComponentPatch patch) {
-        ADAPTER.setPatch(value, patch);
+    @Override
+    public PatchedDataComponentMap getComponents() {
+        return ADAPTER.getComponents(value);
+    }
+    
+    public void applyComponents(DataComponentPatch patch) {
+        ADAPTER.applyComponents(value, patch);
+    }
+    
+    public void applyComponents(DataComponentMap patch) {
+        ADAPTER.applyComponents(value, patch);
+    }
+    
+    @Nullable
+    public  T set(DataComponentType type, @Nullable T component) {
+        return ADAPTER.set(value, type, component);
+    }
+    
+    @Nullable
+    public  T remove(DataComponentType type) {
+        return ADAPTER.remove(value, type);
+    }
+    
+    @Nullable
+    public  T update(DataComponentType type, T component, UnaryOperator updater) {
+        return ADAPTER.update(value, type, component, updater);
+    }
+    
+    @Nullable
+    public  T update(DataComponentType type, T component, U updateContext, BiFunction updater) {
+        return ADAPTER.update(value, type, component, updateContext, updater);
     }
     
     public Component getName() {
diff --git a/common/src/main/java/dev/architectury/impl/NetworkAggregator.java b/common/src/main/java/dev/architectury/impl/NetworkAggregator.java
new file mode 100644
index 000000000..2d3e67edb
--- /dev/null
+++ b/common/src/main/java/dev/architectury/impl/NetworkAggregator.java
@@ -0,0 +1,215 @@
+/*
+ * This file is part of architectury.
+ * Copyright (C) 2020, 2021, 2022 architectury
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package dev.architectury.impl;
+
+import com.google.common.base.Suppliers;
+import dev.architectury.networking.NetworkManager;
+import dev.architectury.networking.transformers.PacketSink;
+import dev.architectury.networking.transformers.PacketTransformer;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.ByteBufUtil;
+import io.netty.buffer.Unpooled;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.core.RegistryAccess;
+import net.minecraft.network.RegistryFriendlyByteBuf;
+import net.minecraft.network.codec.ByteBufCodecs;
+import net.minecraft.network.codec.StreamCodec;
+import net.minecraft.network.protocol.Packet;
+import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
+import net.minecraft.resources.ResourceLocation;
+import org.jetbrains.annotations.ApiStatus;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+@ApiStatus.Internal
+public class NetworkAggregator {
+    public static final Supplier ADAPTOR = Suppliers.memoize(() -> {
+        try {
+            Method adaptor = NetworkManager.class.getDeclaredMethod("getAdaptor");
+            adaptor.setAccessible(true);
+            return (Adaptor) adaptor.invoke(null);
+        } catch (Throwable throwable) {
+            throw new RuntimeException(throwable);
+        }
+    });
+    public static final Map> C2S_TYPE = new HashMap<>();
+    public static final Map> S2C_TYPE = new HashMap<>();
+    public static final Map> C2S_RECEIVER = new HashMap<>();
+    public static final Map> S2C_RECEIVER = new HashMap<>();
+    public static final Map> C2S_CODECS = new HashMap<>();
+    public static final Map> S2C_CODECS = new HashMap<>();
+    public static final Map C2S_TRANSFORMERS = new HashMap<>();
+    public static final Map S2C_TRANSFORMERS = new HashMap<>();
+    
+    public static void registerReceiver(NetworkManager.Side side, ResourceLocation id, List packetTransformers, NetworkManager.NetworkReceiver receiver) {
+        CustomPacketPayload.Type type = new CustomPacketPayload.Type<>(id);
+        if (side == NetworkManager.Side.C2S) {
+            C2S_TYPE.put(id, type);
+            registerC2SReceiver(type, BufCustomPacketPayload.streamCodec(type), packetTransformers, (value, context) -> {
+                RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(value.payload()), context.registryAccess());
+                receiver.receive(buf, context);
+                buf.release();
+            });
+        } else if (side == NetworkManager.Side.S2C) {
+            S2C_TYPE.put(id, type);
+            registerS2CReceiver(type, BufCustomPacketPayload.streamCodec(type), packetTransformers, (value, context) -> {
+                RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(value.payload()), context.registryAccess());
+                receiver.receive(buf, context);
+                buf.release();
+            });
+        }
+    }
+    
+    public static  void registerReceiver(NetworkManager.Side side, CustomPacketPayload.Type type, StreamCodec codec, List packetTransformers, NetworkManager.NetworkReceiver receiver) {
+        Objects.requireNonNull(type, "Cannot register receiver with a null type!");
+        packetTransformers = Objects.requireNonNullElse(packetTransformers, List.of());
+        Objects.requireNonNull(receiver, "Cannot register a null receiver!");
+        if (side == NetworkManager.Side.C2S) {
+            registerC2SReceiver(type, codec, packetTransformers, receiver);
+        } else if (side == NetworkManager.Side.S2C) {
+            registerS2CReceiver(type, codec, packetTransformers, receiver);
+        }
+    }
+    
+    private static  void registerC2SReceiver(CustomPacketPayload.Type type, StreamCodec codec, List packetTransformers, NetworkManager.NetworkReceiver receiver) {
+        PacketTransformer transformer = PacketTransformer.concat(packetTransformers);
+        C2S_RECEIVER.put(type.id(), receiver);
+        C2S_CODECS.put(type.id(), (StreamCodec) codec);
+        C2S_TRANSFORMERS.put(type.id(), transformer);
+        ADAPTOR.get().registerC2S((CustomPacketPayload.Type) type, BufCustomPacketPayload.streamCodec((CustomPacketPayload.Type) type), (payload, context) -> {
+            RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(payload.payload()), context.registryAccess());
+            transformer.inbound(NetworkManager.Side.C2S, type.id(), buf, context, (side, id1, buf1) -> {
+                NetworkManager.NetworkReceiver networkReceiver = (NetworkManager.NetworkReceiver) (side == NetworkManager.Side.C2S ? C2S_RECEIVER.get(id1) : S2C_RECEIVER.get(id1));
+                if (networkReceiver == null) {
+                    throw new IllegalArgumentException("Network Receiver not found! " + id1);
+                }
+                T actualPayload = codec.decode(buf1);
+                networkReceiver.receive(actualPayload, context);
+            });
+            buf.release();
+        });
+    }
+    
+    private static  void registerS2CReceiver(CustomPacketPayload.Type type, StreamCodec codec, List packetTransformers, NetworkManager.NetworkReceiver receiver) {
+        PacketTransformer transformer = PacketTransformer.concat(packetTransformers);
+        S2C_RECEIVER.put(type.id(), receiver);
+        S2C_CODECS.put(type.id(), (StreamCodec) codec);
+        S2C_TRANSFORMERS.put(type.id(), transformer);
+        ADAPTOR.get().registerS2C((CustomPacketPayload.Type) type, BufCustomPacketPayload.streamCodec((CustomPacketPayload.Type) type), (payload, context) -> {
+            RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(payload.payload()), context.registryAccess());
+            transformer.inbound(NetworkManager.Side.S2C, type.id(), buf, context, (side, id1, buf1) -> {
+                NetworkManager.NetworkReceiver networkReceiver = (NetworkManager.NetworkReceiver) (side == NetworkManager.Side.C2S ? C2S_RECEIVER.get(id1) : S2C_RECEIVER.get(id1));
+                if (networkReceiver == null) {
+                    throw new IllegalArgumentException("Network Receiver not found! " + id1);
+                }
+                T actualPayload = codec.decode(buf1);
+                networkReceiver.receive(actualPayload, context);
+            });
+            buf.release();
+        });
+    }
+    
+    public static void collectPackets(PacketSink sink, NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf) {
+        if (side == NetworkManager.Side.C2S) {
+            collectPackets(sink, side, new BufCustomPacketPayload(C2S_TYPE.get(id), ByteBufUtil.getBytes(buf)), buf.registryAccess());
+        } else {
+            collectPackets(sink, side, new BufCustomPacketPayload(S2C_TYPE.get(id), ByteBufUtil.getBytes(buf)), buf.registryAccess());
+        }
+    }
+    
+    public static  void collectPackets(PacketSink sink, NetworkManager.Side side, T payload, RegistryAccess access) {
+        CustomPacketPayload.Type type = (CustomPacketPayload.Type) payload.type();
+        PacketTransformer transformer = side == NetworkManager.Side.C2S ? C2S_TRANSFORMERS.get(type.id()) : S2C_TRANSFORMERS.get(type.id());
+        StreamCodec codec = (StreamCodec) (side == NetworkManager.Side.C2S ? C2S_CODECS.get(type.id()) : S2C_CODECS.get(type.id()));
+        RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), access);
+        codec.encode(buf, payload);
+        
+        if (transformer != null) {
+            transformer.outbound(side, type.id(), buf, (side1, id1, buf1) -> {
+                if (side == NetworkManager.Side.C2S) {
+                    CustomPacketPayload.Type type1 = C2S_TYPE.getOrDefault(id1, (CustomPacketPayload.Type) type);
+                    sink.accept(toPacket(side1, new BufCustomPacketPayload(type1, ByteBufUtil.getBytes(buf1))));
+                } else if (side == NetworkManager.Side.S2C) {
+                    CustomPacketPayload.Type type1 = S2C_TYPE.getOrDefault(id1, (CustomPacketPayload.Type) type);
+                    sink.accept(toPacket(side1, new BufCustomPacketPayload(type1, ByteBufUtil.getBytes(buf1))));
+                }
+            });
+        } else {
+            sink.accept(toPacket(side, new BufCustomPacketPayload((CustomPacketPayload.Type) type, ByteBufUtil.getBytes(buf))));
+        }
+        buf.release();
+    }
+    
+    public static  Packet toPacket(NetworkManager.Side side, T payload) {
+        if (side == NetworkManager.Side.C2S) {
+            return ADAPTOR.get().toC2SPacket(payload);
+        } else if (side == NetworkManager.Side.S2C) {
+            return ADAPTOR.get().toS2CPacket(payload);
+        }
+        
+        throw new IllegalArgumentException("Invalid side: " + side);
+    }
+    
+    public static void registerS2CType(ResourceLocation id, List packetTransformers) {
+        CustomPacketPayload.Type type = new CustomPacketPayload.Type<>(id);
+        S2C_TYPE.put(id, type);
+        registerS2CType(type, BufCustomPacketPayload.streamCodec(type), packetTransformers);
+    }
+    
+    public static  void registerS2CType(CustomPacketPayload.Type type, StreamCodec codec, List packetTransformers) {
+        Objects.requireNonNull(type, "Cannot register a null type!");
+        packetTransformers = Objects.requireNonNullElse(packetTransformers, List.of());
+        S2C_CODECS.put(type.id(), (StreamCodec) codec);
+        S2C_TRANSFORMERS.put(type.id(), PacketTransformer.concat(packetTransformers));
+        ADAPTOR.get().registerS2CType((CustomPacketPayload.Type) type, BufCustomPacketPayload.streamCodec((CustomPacketPayload.Type) type));
+    }
+    
+    public interface Adaptor {
+         void registerC2S(CustomPacketPayload.Type type, StreamCodec codec, NetworkManager.NetworkReceiver receiver);
+        
+        @Environment(EnvType.CLIENT)
+         void registerS2C(CustomPacketPayload.Type type, StreamCodec codec, NetworkManager.NetworkReceiver receiver);
+        
+         Packet toC2SPacket(T payload);
+        
+        @Environment(EnvType.CLIENT)
+         Packet toS2CPacket(T payload);
+        
+         void registerS2CType(CustomPacketPayload.Type type, StreamCodec codec);
+    }
+    
+    public record BufCustomPacketPayload(Type _type,
+                                         byte[] payload) implements CustomPacketPayload {
+        @Override
+        public Type type() {
+            return this._type();
+        }
+        
+        public static StreamCodec streamCodec(Type type) {
+            return ByteBufCodecs.BYTE_ARRAY.map(bytes -> new BufCustomPacketPayload(type, bytes), BufCustomPacketPayload::payload);
+        }
+    }
+}
diff --git a/common/src/main/java/dev/architectury/networking/NetworkChannel.java b/common/src/main/java/dev/architectury/networking/NetworkChannel.java
index acb37d992..c0c560b42 100644
--- a/common/src/main/java/dev/architectury/networking/NetworkChannel.java
+++ b/common/src/main/java/dev/architectury/networking/NetworkChannel.java
@@ -27,12 +27,16 @@
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
 import net.minecraft.client.Minecraft;
+import net.minecraft.client.multiplayer.ClientPacketListener;
+import net.minecraft.core.RegistryAccess;
 import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.RegistryFriendlyByteBuf;
 import net.minecraft.network.protocol.Packet;
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.server.level.ServerPlayer;
 
 import java.nio.charset.StandardCharsets;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.Objects;
 import java.util.UUID;
@@ -60,7 +64,7 @@ public  void register(Class type, BiConsumer encoder,
         var s = UUID.nameUUIDFromBytes(type.getName().getBytes(StandardCharsets.UTF_8)).toString().replace("-", "");
         var info = new MessageInfo(new ResourceLocation(id + "/" + s), encoder, decoder, messageConsumer);
         encoders.put(type, info);
-        NetworkManager.NetworkReceiver receiver = (buf, context) -> {
+        NetworkManager.NetworkReceiver receiver = (buf, context) -> {
             info.messageConsumer.accept(info.decoder.apply(buf), () -> context);
         };
         NetworkManager.registerReceiver(NetworkManager.c2s(), info.packetId, receiver);
@@ -78,19 +82,21 @@ public static long hashCodeString(String str) {
         return h;
     }
     
-    public  Packet toPacket(NetworkManager.Side side, T message) {
+    public  Packet toPacket(NetworkManager.Side side, T message, RegistryAccess access) {
         var messageInfo = (MessageInfo) Objects.requireNonNull(encoders.get(message.getClass()), "Unknown message type! " + message);
-        var buf = new FriendlyByteBuf(Unpooled.buffer());
+        var buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), access);
         messageInfo.encoder.accept(message, buf);
         return NetworkManager.toPacket(side, messageInfo.packetId, buf);
     }
     
     public  void sendToPlayer(ServerPlayer player, T message) {
-        Objects.requireNonNull(player, "Unable to send packet to a 'null' player!").connection.send(toPacket(NetworkManager.s2c(), message));
+        Objects.requireNonNull(player, "Unable to send packet to a 'null' player!").connection.send(toPacket(NetworkManager.s2c(), message, player.registryAccess()));
     }
     
     public  void sendToPlayers(Iterable players, T message) {
-        var packet = toPacket(NetworkManager.s2c(), message);
+        Iterator iterator = players.iterator();
+        if (!iterator.hasNext()) return;
+        var packet = toPacket(NetworkManager.s2c(), message, iterator.next().registryAccess());
         for (var player : players) {
             Objects.requireNonNull(player, "Unable to send packet to a 'null' player!").connection.send(packet);
         }
@@ -98,8 +104,9 @@ public  void sendToPlayers(Iterable players, T message) {
     
     @Environment(EnvType.CLIENT)
     public  void sendToServer(T message) {
-        if (Minecraft.getInstance().getConnection() != null) {
-            Minecraft.getInstance().getConnection().send(toPacket(NetworkManager.c2s(), message));
+        ClientPacketListener connection = Minecraft.getInstance().getConnection();
+        if (connection != null) {
+            connection.send(toPacket(NetworkManager.c2s(), message, connection.registryAccess()));
         } else {
             throw new IllegalStateException("Unable to send packet to the server while not in game!");
         }
diff --git a/common/src/main/java/dev/architectury/networking/NetworkManager.java b/common/src/main/java/dev/architectury/networking/NetworkManager.java
index 58c6c4ed7..7a6e9c4f0 100644
--- a/common/src/main/java/dev/architectury/networking/NetworkManager.java
+++ b/common/src/main/java/dev/architectury/networking/NetworkManager.java
@@ -19,16 +19,22 @@
 
 package dev.architectury.networking;
 
+import dev.architectury.impl.NetworkAggregator;
 import dev.architectury.injectables.annotations.ExpectPlatform;
 import dev.architectury.networking.transformers.PacketCollector;
 import dev.architectury.networking.transformers.PacketSink;
 import dev.architectury.networking.transformers.PacketTransformer;
 import dev.architectury.networking.transformers.SinglePacketCollector;
 import dev.architectury.utils.Env;
+import dev.architectury.utils.GameInstance;
 import net.fabricmc.api.EnvType;
 import net.fabricmc.api.Environment;
-import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.client.multiplayer.ClientPacketListener;
+import net.minecraft.core.RegistryAccess;
+import net.minecraft.network.RegistryFriendlyByteBuf;
+import net.minecraft.network.codec.StreamCodec;
 import net.minecraft.network.protocol.Packet;
+import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
 import net.minecraft.network.protocol.game.ClientGamePacketListener;
 import net.minecraft.resources.ResourceLocation;
 import net.minecraft.server.level.ServerPlayer;
@@ -37,53 +43,112 @@
 import org.jetbrains.annotations.ApiStatus;
 
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 
 public final class NetworkManager {
-    public static void registerReceiver(Side side, ResourceLocation id, NetworkReceiver receiver) {
+    /**
+     * For S2C types, {@link #registerReceiver} should be called on the client side,
+     * while {@link #registerS2CPayloadType} should be called on the server side.
+     */
+    public static void registerS2CPayloadType(ResourceLocation id) {
+        NetworkAggregator.registerS2CType(id, List.of());
+    }
+    
+    /**
+     * For S2C types, {@link #registerReceiver} should be called on the client side,
+     * while {@link #registerS2CPayloadType} should be called on the server side.
+     */
+    public static  void registerS2CPayloadType(CustomPacketPayload.Type type, StreamCodec codec) {
+        NetworkAggregator.registerS2CType(type, codec, List.of());
+    }
+    
+    /**
+     * For S2C types, {@link #registerReceiver} should be called on the client side,
+     * while {@link #registerS2CPayloadType} should be called on the server side.
+     */
+    public static void registerS2CPayloadType(ResourceLocation id, List packetTransformers) {
+        NetworkAggregator.registerS2CType(id, packetTransformers);
+    }
+    
+    /**
+     * For S2C types, {@link #registerReceiver} should be called on the client side,
+     * while {@link #registerS2CPayloadType} should be called on the server side.
+     */
+    public static  void registerS2CPayloadType(CustomPacketPayload.Type type, StreamCodec codec, List packetTransformers) {
+        NetworkAggregator.registerS2CType(type, codec, packetTransformers);
+    }
+    
+    public static void registerReceiver(Side side, ResourceLocation id, NetworkReceiver receiver) {
         registerReceiver(side, id, Collections.emptyList(), receiver);
     }
     
-    @ExpectPlatform
     @ApiStatus.Experimental
-    public static void registerReceiver(Side side, ResourceLocation id, List packetTransformers, NetworkReceiver receiver) {
-        throw new AssertionError();
+    public static void registerReceiver(Side side, ResourceLocation id, List packetTransformers, NetworkReceiver receiver) {
+        NetworkAggregator.registerReceiver(side, id, packetTransformers, receiver);
+    }
+    
+    public static  void registerReceiver(Side side, CustomPacketPayload.Type id, StreamCodec codec, NetworkReceiver receiver) {
+        registerReceiver(side, id, codec, Collections.emptyList(), receiver);
+    }
+    
+    @ApiStatus.Experimental
+    public static  void registerReceiver(Side side, CustomPacketPayload.Type id, StreamCodec codec, List packetTransformers, NetworkReceiver receiver) {
+        NetworkAggregator.registerReceiver(side, id, codec, packetTransformers, receiver);
     }
     
     @Deprecated
-    @ApiStatus.ScheduledForRemoval
-    public static Packet toPacket(Side side, ResourceLocation id, FriendlyByteBuf buf) {
+    public static Packet toPacket(Side side, ResourceLocation id, RegistryFriendlyByteBuf buf) {
         SinglePacketCollector sink = new SinglePacketCollector(null);
         collectPackets(sink, side, id, buf);
         return sink.getPacket();
     }
     
     @Deprecated
-    @ApiStatus.ScheduledForRemoval
-    public static List> toPackets(Side side, ResourceLocation id, FriendlyByteBuf buf) {
+    public static List> toPackets(Side side, ResourceLocation id, RegistryFriendlyByteBuf buf) {
         PacketCollector sink = new PacketCollector(null);
         collectPackets(sink, side, id, buf);
         return sink.collect();
     }
     
-    @ExpectPlatform
-    public static void collectPackets(PacketSink sink, Side side, ResourceLocation id, FriendlyByteBuf buf) {
-        throw new AssertionError();
+    public static void collectPackets(PacketSink sink, Side side, ResourceLocation id, RegistryFriendlyByteBuf buf) {
+        NetworkAggregator.collectPackets(sink, side, id, buf);
     }
     
-    public static void sendToPlayer(ServerPlayer player, ResourceLocation id, FriendlyByteBuf buf) {
+    public static  void collectPackets(PacketSink sink, Side side, T payload, RegistryAccess access) {
+        NetworkAggregator.collectPackets(sink, side, payload, access);
+    }
+    
+    public static void sendToPlayer(ServerPlayer player, ResourceLocation id, RegistryFriendlyByteBuf buf) {
         collectPackets(PacketSink.ofPlayer(player), serverToClient(), id, buf);
     }
     
-    public static void sendToPlayers(Iterable players, ResourceLocation id, FriendlyByteBuf buf) {
+    public static void sendToPlayers(Iterable players, ResourceLocation id, RegistryFriendlyByteBuf buf) {
         collectPackets(PacketSink.ofPlayers(players), serverToClient(), id, buf);
     }
     
     @Environment(EnvType.CLIENT)
-    public static void sendToServer(ResourceLocation id, FriendlyByteBuf buf) {
+    public static void sendToServer(ResourceLocation id, RegistryFriendlyByteBuf buf) {
         collectPackets(PacketSink.client(), clientToServer(), id, buf);
     }
     
+    public static  void sendToPlayer(ServerPlayer player, T payload) {
+        collectPackets(PacketSink.ofPlayer(player), serverToClient(), payload, player.registryAccess());
+    }
+    
+    public static  void sendToPlayers(Iterable players, T payload) {
+        Iterator iterator = players.iterator();
+        if (!iterator.hasNext()) return;
+        collectPackets(PacketSink.ofPlayers(players), serverToClient(), payload, iterator.next().registryAccess());
+    }
+    
+    @Environment(EnvType.CLIENT)
+    public static  void sendToServer(T payload) {
+        ClientPacketListener connection = GameInstance.getClient().getConnection();
+        if (connection == null) return;
+        collectPackets(PacketSink.client(), clientToServer(), payload, connection.registryAccess());
+    }
+    
     @Environment(EnvType.CLIENT)
     @ExpectPlatform
     public static boolean canServerReceive(ResourceLocation id) {
@@ -112,9 +177,14 @@ public static Packet createAddEntityPacket(Entity enti
         throw new AssertionError();
     }
     
+    @ExpectPlatform
+    private static NetworkAggregator.Adaptor getAdaptor() {
+        throw new AssertionError();
+    }
+    
     @FunctionalInterface
-    public interface NetworkReceiver {
-        void receive(FriendlyByteBuf buf, PacketContext context);
+    public interface NetworkReceiver {
+        void receive(T value, PacketContext context);
     }
     
     public interface PacketContext {
@@ -124,6 +194,8 @@ public interface PacketContext {
         
         Env getEnvironment();
         
+        RegistryAccess registryAccess();
+        
         default EnvType getEnv() {
             return getEnvironment().toPlatform();
         }
diff --git a/common/src/main/java/dev/architectury/networking/SpawnEntityPacket.java b/common/src/main/java/dev/architectury/networking/SpawnEntityPacket.java
index c5b6e73d3..d5e993f59 100644
--- a/common/src/main/java/dev/architectury/networking/SpawnEntityPacket.java
+++ b/common/src/main/java/dev/architectury/networking/SpawnEntityPacket.java
@@ -26,6 +26,7 @@
 import net.minecraft.client.Minecraft;
 import net.minecraft.core.registries.BuiltInRegistries;
 import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.RegistryFriendlyByteBuf;
 import net.minecraft.network.protocol.Packet;
 import net.minecraft.network.protocol.game.ClientGamePacketListener;
 import net.minecraft.resources.ResourceLocation;
@@ -41,7 +42,7 @@ public static Packet create(Entity entity) {
         if (entity.level().isClientSide()) {
             throw new IllegalStateException("SpawnPacketUtil.create called on the logical client!");
         }
-        var buffer = new FriendlyByteBuf(Unpooled.buffer());
+        var buffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), entity.registryAccess());
         buffer.writeVarInt(BuiltInRegistries.ENTITY_TYPE.getId(entity.getType()));
         buffer.writeUUID(entity.getUUID());
         buffer.writeVarInt(entity.getId());
@@ -62,6 +63,10 @@ public static Packet create(Entity entity) {
         return (Packet) NetworkManager.toPacket(NetworkManager.s2c(), PACKET_ID, buffer);
     }
     
+    public static void register() {
+        NetworkManager.registerS2CPayloadType(PACKET_ID);
+    }
+    
     
     @Environment(EnvType.CLIENT)
     public static class Client {
diff --git a/common/src/main/java/dev/architectury/networking/simple/BaseC2SMessage.java b/common/src/main/java/dev/architectury/networking/simple/BaseC2SMessage.java
index 79fa63315..7628ab326 100644
--- a/common/src/main/java/dev/architectury/networking/simple/BaseC2SMessage.java
+++ b/common/src/main/java/dev/architectury/networking/simple/BaseC2SMessage.java
@@ -33,7 +33,7 @@ public abstract class BaseC2SMessage extends Message {
     @Environment(EnvType.CLIENT)
     public final void sendToServer() {
         if (Minecraft.getInstance().getConnection() != null) {
-            Minecraft.getInstance().getConnection().send(toPacket());
+            Minecraft.getInstance().getConnection().send(toPacket(Minecraft.getInstance().level.registryAccess()));
         } else {
             throw new IllegalStateException("Unable to send packet to the server while not in game!");
         }
diff --git a/common/src/main/java/dev/architectury/networking/simple/BaseS2CMessage.java b/common/src/main/java/dev/architectury/networking/simple/BaseS2CMessage.java
index a632f06fb..d44c3927e 100644
--- a/common/src/main/java/dev/architectury/networking/simple/BaseS2CMessage.java
+++ b/common/src/main/java/dev/architectury/networking/simple/BaseS2CMessage.java
@@ -44,7 +44,7 @@ private void sendTo(ServerPlayer player, Packet packet) {
      * @param player the player
      */
     public final void sendTo(ServerPlayer player) {
-        sendTo(player, toPacket());
+        sendTo(player, toPacket(player.registryAccess()));
     }
 
     /**
@@ -53,7 +53,8 @@ public final void sendTo(ServerPlayer player) {
      * @param players the players
      */
     public final void sendTo(Iterable players) {
-        Packet packet = toPacket();
+        if (!players.iterator().hasNext()) return;
+        Packet packet = toPacket(players.iterator().next().registryAccess());
 
         for (ServerPlayer player : players) {
             sendTo(player, packet);
@@ -84,7 +85,7 @@ public final void sendToLevel(ServerLevel level) {
      * @param chunk the listened chunk
      */
     public final void sendToChunkListeners(LevelChunk chunk) {
-        Packet packet = toPacket();
+        Packet packet = toPacket(chunk.getLevel().registryAccess());
         ((ServerChunkCache) chunk.getLevel().getChunkSource()).chunkMap.getPlayers(chunk.getPos(), false).forEach(e -> sendTo(e, packet));
     }
 }
\ No newline at end of file
diff --git a/common/src/main/java/dev/architectury/networking/simple/Message.java b/common/src/main/java/dev/architectury/networking/simple/Message.java
index 44fa2f802..49a400ad7 100644
--- a/common/src/main/java/dev/architectury/networking/simple/Message.java
+++ b/common/src/main/java/dev/architectury/networking/simple/Message.java
@@ -21,7 +21,8 @@
 
 import dev.architectury.networking.NetworkManager;
 import io.netty.buffer.Unpooled;
-import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.core.RegistryAccess;
+import net.minecraft.network.RegistryFriendlyByteBuf;
 import net.minecraft.network.protocol.Packet;
 
 /**
@@ -48,7 +49,7 @@ public abstract class Message {
      *
      * @param buf the byte buffer
      */
-    public abstract void write(FriendlyByteBuf buf);
+    public abstract void write(RegistryFriendlyByteBuf buf);
     
     /**
      * Handles this message when it is received.
@@ -62,8 +63,8 @@ public abstract class Message {
      *
      * @return the converted {@link Packet}
      */
-    public final Packet toPacket() {
-        FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer());
+    public final Packet toPacket(RegistryAccess access) {
+        RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), access);
         write(buf);
         return NetworkManager.toPacket(getType().getSide(), getType().getId(), buf);
     }
diff --git a/common/src/main/java/dev/architectury/networking/simple/MessageDecoder.java b/common/src/main/java/dev/architectury/networking/simple/MessageDecoder.java
index 36d399a44..b95fd20a7 100644
--- a/common/src/main/java/dev/architectury/networking/simple/MessageDecoder.java
+++ b/common/src/main/java/dev/architectury/networking/simple/MessageDecoder.java
@@ -20,10 +20,10 @@
 package dev.architectury.networking.simple;
 
 import dev.architectury.networking.NetworkManager;
-import net.minecraft.network.FriendlyByteBuf;
+import net.minecraft.network.RegistryFriendlyByteBuf;
 
 /**
- * Decodes a {@link Message} from a {@link FriendlyByteBuf}.
+ * Decodes a {@link Message} from a {@link RegistryFriendlyByteBuf}.
  *
  * @param  the message type handled by this decoder
  * @author LatvianModder
@@ -36,17 +36,17 @@ public interface MessageDecoder {
      * @param buf the byte buffer
      * @return the decoded instance
      */
-    T decode(FriendlyByteBuf buf);
+    T decode(RegistryFriendlyByteBuf buf);
     
     /**
      * Creates a network receiver from this decoder.
      *
-     * 

The returned receiver will first {@linkplain #decode(FriendlyByteBuf) decode a message} + *

The returned receiver will first {@linkplain #decode(RegistryFriendlyByteBuf) decode a message} * and then call {@link Message#handle(NetworkManager.PacketContext)} on the decoded message. * * @return the created receiver */ - default NetworkManager.NetworkReceiver createReceiver() { + default NetworkManager.NetworkReceiver createReceiver() { return (buf, context) -> { Message packet = decode(buf); context.queue(() -> packet.handle(context)); diff --git a/common/src/main/java/dev/architectury/networking/simple/SimpleNetworkManager.java b/common/src/main/java/dev/architectury/networking/simple/SimpleNetworkManager.java index 7fdc45370..de2d9cff1 100644 --- a/common/src/main/java/dev/architectury/networking/simple/SimpleNetworkManager.java +++ b/common/src/main/java/dev/architectury/networking/simple/SimpleNetworkManager.java @@ -23,6 +23,7 @@ import dev.architectury.networking.transformers.PacketTransformer; import dev.architectury.platform.Platform; import dev.architectury.utils.Env; +import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.ApiStatus; @@ -77,7 +78,7 @@ public MessageType registerS2C(String id, MessageDecoder decoder MessageType messageType = new MessageType(this, new ResourceLocation(namespace, id), NetworkManager.s2c()); if (Platform.getEnvironment() == Env.CLIENT) { - NetworkManager.NetworkReceiver receiver = decoder.createReceiver(); + NetworkManager.NetworkReceiver receiver = decoder.createReceiver(); NetworkManager.registerReceiver(NetworkManager.s2c(), messageType.getId(), transformers, receiver); } @@ -107,7 +108,7 @@ public MessageType registerC2S(String id, MessageDecoder decoder @ApiStatus.Experimental public MessageType registerC2S(String id, MessageDecoder decoder, List transformers) { MessageType messageType = new MessageType(this, new ResourceLocation(namespace, id), NetworkManager.c2s()); - NetworkManager.NetworkReceiver receiver = decoder.createReceiver(); + NetworkManager.NetworkReceiver receiver = decoder.createReceiver(); NetworkManager.registerReceiver(NetworkManager.c2s(), messageType.getId(), transformers, receiver); return messageType; } diff --git a/common/src/main/java/dev/architectury/networking/simple/package-info.java b/common/src/main/java/dev/architectury/networking/simple/package-info.java new file mode 100644 index 000000000..e1b313ad1 --- /dev/null +++ b/common/src/main/java/dev/architectury/networking/simple/package-info.java @@ -0,0 +1,21 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +@Deprecated(forRemoval = true) +package dev.architectury.networking.simple; \ No newline at end of file diff --git a/common/src/main/java/dev/architectury/networking/transformers/PacketTransformer.java b/common/src/main/java/dev/architectury/networking/transformers/PacketTransformer.java index 2b977970b..64a34e2bd 100644 --- a/common/src/main/java/dev/architectury/networking/transformers/PacketTransformer.java +++ b/common/src/main/java/dev/architectury/networking/transformers/PacketTransformer.java @@ -20,7 +20,7 @@ package dev.architectury.networking.transformers; import dev.architectury.networking.NetworkManager; -import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; @@ -31,24 +31,24 @@ @ApiStatus.Experimental public interface PacketTransformer { - void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink); + void inbound(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink); - void outbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, TransformationSink sink); + void outbound(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf, TransformationSink sink); @FunctionalInterface interface TransformationSink { - void accept(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf); + void accept(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf); } static PacketTransformer none() { return new PacketTransformer() { @Override - public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink) { + public void inbound(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink) { sink.accept(side, id, buf); } @Override - public void outbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, TransformationSink sink) { + public void outbound(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf, TransformationSink sink) { sink.accept(side, id, buf); } }; @@ -62,16 +62,16 @@ static PacketTransformer concat(Iterable transforme } return new PacketTransformer() { @Override - public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink) { + public void inbound(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink) { traverse(side, id, buf, context, sink, true, 0); } @Override - public void outbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, TransformationSink sink) { + public void outbound(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf, TransformationSink sink) { traverse(side, id, buf, null, sink, false, 0); } - private void traverse(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, @Nullable NetworkManager.PacketContext context, TransformationSink outerSink, boolean inbound, int index) { + private void traverse(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf, @Nullable NetworkManager.PacketContext context, TransformationSink outerSink, boolean inbound, int index) { if (transformers instanceof List) { if (((List) transformers).size() > index) { PacketTransformer transformer = ((List) transformers).get(index); diff --git a/common/src/main/java/dev/architectury/networking/transformers/SplitPacketTransformer.java b/common/src/main/java/dev/architectury/networking/transformers/SplitPacketTransformer.java index 56e4fa095..56b628a3f 100644 --- a/common/src/main/java/dev/architectury/networking/transformers/SplitPacketTransformer.java +++ b/common/src/main/java/dev/architectury/networking/transformers/SplitPacketTransformer.java @@ -28,7 +28,7 @@ import io.netty.buffer.Unpooled; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.resources.ResourceLocation; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -81,7 +81,7 @@ public String toString() { private static class PartData { private final ResourceLocation id; private final int partsExpected; - private final List parts; + private final List parts; public PartData(ResourceLocation id, int partsExpected) { this.id = id; @@ -109,7 +109,7 @@ private void init() { } @Override - public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink) { + public void inbound(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf, NetworkManager.PacketContext context, TransformationSink sink) { PartKey key = side == NetworkManager.Side.S2C ? new PartKey(side, null) : new PartKey(side, context.getPlayer().getUUID()); PartData data; switch (buf.readByte()) { @@ -128,7 +128,7 @@ public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteB } else if (!data.id.equals(id)) { LOGGER.warn("Received invalid PART packet for SplitPacketTransformer with packet id " + id + " for side " + side + ", id in cache is " + data.id); buf.release(); - for (FriendlyByteBuf part : data.parts) { + for (RegistryFriendlyByteBuf part : data.parts) { if (part != buf) { part.release(); } @@ -146,7 +146,7 @@ public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteB } else if (!data.id.equals(id)) { LOGGER.warn("Received invalid END packet for SplitPacketTransformer with packet id " + id + " for side " + side + ", id in cache is " + data.id); buf.release(); - for (FriendlyByteBuf part : data.parts) { + for (RegistryFriendlyByteBuf part : data.parts) { if (part != buf) { part.release(); } @@ -158,13 +158,13 @@ public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteB } if (data.parts.size() != data.partsExpected) { LOGGER.warn("Received invalid END packet for SplitPacketTransformer with packet id " + id + " for side " + side + " with size " + data.parts + ", parts expected is " + data.partsExpected); - for (FriendlyByteBuf part : data.parts) { + for (RegistryFriendlyByteBuf part : data.parts) { if (part != buf) { part.release(); } } } else { - FriendlyByteBuf byteBuf = new FriendlyByteBuf(Unpooled.wrappedBuffer(data.parts.toArray(new ByteBuf[0]))); + RegistryFriendlyByteBuf byteBuf = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(data.parts.toArray(new ByteBuf[0])), buf.registryAccess()); sink.accept(side, data.id, byteBuf); byteBuf.release(); } @@ -179,18 +179,18 @@ public void inbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteB } @Override - public void outbound(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf, TransformationSink sink) { + public void outbound(NetworkManager.Side side, ResourceLocation id, RegistryFriendlyByteBuf buf, TransformationSink sink) { int maxSize = (side == NetworkManager.Side.C2S ? 32767 : 1048576) - 1 - 20 - id.toString().getBytes(StandardCharsets.UTF_8).length; if (buf.readableBytes() <= maxSize) { ByteBuf stateBuf = Unpooled.buffer(1); stateBuf.writeByte(ONLY); - FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.wrappedBuffer(stateBuf, buf)); + RegistryFriendlyByteBuf packetBuffer = new RegistryFriendlyByteBuf(Unpooled.wrappedBuffer(stateBuf, buf), buf.registryAccess()); sink.accept(side, id, packetBuffer); } else { int partSize = maxSize - 4; int parts = (int) Math.ceil(buf.readableBytes() / (float) partSize); for (int i = 0; i < parts; i++) { - FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.buffer()); + RegistryFriendlyByteBuf packetBuffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), buf.registryAccess()); if (i == 0) { packetBuffer.writeByte(START); packetBuffer.writeInt(parts); diff --git a/fabric/src/main/java/dev/architectury/event/fabric/EventHandlerImpl.java b/fabric/src/main/java/dev/architectury/event/fabric/EventHandlerImpl.java index fe15a0f6d..e7f035f17 100644 --- a/fabric/src/main/java/dev/architectury/event/fabric/EventHandlerImpl.java +++ b/fabric/src/main/java/dev/architectury/event/fabric/EventHandlerImpl.java @@ -38,7 +38,7 @@ import net.fabricmc.fabric.api.event.player.AttackEntityCallback; import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.event.player.UseItemCallback; -// import net.fabricmc.fabric.api.loot.v2.LootTableEvents; +import net.fabricmc.fabric.api.loot.v2.LootTableEvents; import net.fabricmc.fabric.api.message.v1.ServerMessageDecoratorEvent; import net.fabricmc.fabric.api.message.v1.ServerMessageEvents; @@ -83,7 +83,7 @@ public static void registerCommon() { AttackBlockCallback.EVENT.register((player, world, hand, pos, face) -> InteractionEvent.LEFT_CLICK_BLOCK.invoker().click(player, hand, pos, face).asMinecraft()); AttackEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> PlayerEvent.ATTACK_ENTITY.invoker().attack(player, world, entity, hand, hitResult).asMinecraft()); - // LootTableEvents.MODIFY.register((key, tableBuilder, source) -> LootEvent.MODIFY_LOOT_TABLE.invoker().modifyLootTable(lootManager, id, new LootTableModificationContextImpl(tableBuilder), source.isBuiltin())); + LootTableEvents.MODIFY.register((key, tableBuilder, source) -> LootEvent.MODIFY_LOOT_TABLE.invoker().modifyLootTable(key, new LootTableModificationContextImpl(tableBuilder), source.isBuiltin())); ServerMessageDecoratorEvent.EVENT.register(ServerMessageDecoratorEvent.CONTENT_PHASE, (player, component) -> { ChatEvent.ChatComponent chatComponent = new ChatComponentImpl(component); diff --git a/fabric/src/main/java/dev/architectury/fluid/fabric/FluidStackImpl.java b/fabric/src/main/java/dev/architectury/fluid/fabric/FluidStackImpl.java index b0d6e7ad8..46763a15f 100644 --- a/fabric/src/main/java/dev/architectury/fluid/fabric/FluidStackImpl.java +++ b/fabric/src/main/java/dev/architectury/fluid/fabric/FluidStackImpl.java @@ -25,24 +25,28 @@ import dev.architectury.fluid.FluidStack; import io.netty.buffer.ByteBuf; import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; +import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.core.component.PatchedDataComponentMap; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; -import net.minecraft.util.ExtraCodecs; import net.minecraft.world.level.material.FlowingFluid; import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import java.util.Objects; +import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Supplier; +import java.util.function.UnaryOperator; @ApiStatus.Internal -@SuppressWarnings("UnstableApiUsage") public enum FluidStackImpl implements FluidStack.FluidStackAdapter { INSTANCE; @@ -60,13 +64,30 @@ public static FluidStack.FluidStackAdapter adapt(Function fluid, long amount, @Nullable if (fluidType instanceof FlowingFluid flowingFluid) { fluidType = flowingFluid.getSource(); } - return new Pair(FluidVariant.of(fluidType, patch == null ? DataComponentPatch.EMPTY : patch), amount); + return new Pair(fluidType, patch, amount); } @Override public Supplier getRawFluidSupplier(FluidStackImpl.Pair object) { - return () -> object.variant.getFluid(); + return () -> object.fluid; } @Override public Fluid getFluid(FluidStackImpl.Pair object) { - return object.variant.getFluid(); + return object.fluid; } @Override @@ -99,28 +120,60 @@ public void setAmount(FluidStackImpl.Pair object, long amount) { } public DataComponentPatch getPatch(FluidStackImpl.Pair value) { - return value.variant.getComponents(); + return value.getPatch(); + } + + @Override + public PatchedDataComponentMap getComponents(Pair value) { + return value.components; + } + + @Override + public void applyComponents(Pair value, DataComponentPatch patch) { + value.components.applyPatch(patch); + } + + @Override + public void applyComponents(Pair value, DataComponentMap patch) { + value.components.setAll(patch); + } + + @Override + @Nullable + public D set(Pair value, DataComponentType type, @Nullable D component) { + return value.components.set(type, component); + } + + @Override + @Nullable + public D remove(Pair value, DataComponentType type) { + return value.components.remove(type); + } + + @Override + @Nullable + public D update(Pair value, DataComponentType type, D component, UnaryOperator updater) { + return value.components.set(type, updater.apply(getComponents(value).getOrDefault(type, component))); } @Override - public void setPatch(FluidStackImpl.Pair value, DataComponentPatch patch) { - value.variant = FluidVariant.of(value.variant.getFluid(), patch); + @Nullable + public D update(Pair value, DataComponentType type, D component, U updateContext, BiFunction updater) { + return value.components.set(type, updater.apply(getComponents(value).getOrDefault(type, component), updateContext)); } @Override public FluidStackImpl.Pair copy(FluidStackImpl.Pair value) { - return new Pair(value.variant, value.amount); + return new Pair(value.fluid, value.components.copy(), value.amount); } @Override public int hashCode(FluidStackImpl.Pair value) { var pair = (Pair) value; var code = 1; - code = 31 * code + pair.variant.hashCode(); + code = 31 * code + pair.fluid.hashCode(); code = 31 * code + Long.hashCode(pair.amount); - var patch = pair.variant.getComponents(); - if (patch != null) - code = 31 * code + patch.hashCode(); + code = 31 * code + pair.components.hashCode(); return code; } @@ -133,7 +186,7 @@ public Codec codec() { ? DataResult.success(value) : DataResult.error(() -> "Value must be non-negative: " + value); }).fieldOf("amount").forGetter(FluidStack::getAmount), - DataComponentPatch.CODEC.fieldOf("components").forGetter(FluidStack::getPatch) + DataComponentPatch.CODEC.optionalFieldOf("components", DataComponentPatch.EMPTY).forGetter(FluidStack::getPatch) ).apply(instance, FluidStack::create)); } diff --git a/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java index 8f395c369..a509d502b 100644 --- a/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java +++ b/fabric/src/main/java/dev/architectury/hooks/fluid/fabric/FluidStackHooksFabric.java @@ -24,7 +24,6 @@ import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant; import net.fabricmc.fabric.api.transfer.v1.storage.StorageView; -@SuppressWarnings("UnstableApiUsage") public final class FluidStackHooksFabric { private FluidStackHooksFabric() { } @@ -34,10 +33,10 @@ public static FluidStack fromFabric(StorageView storageView) { } public static FluidStack fromFabric(FluidVariant variant, long amount) { - return FluidStackImpl.fromValue.apply(new FluidStackImpl.Pair(variant, amount)); + return FluidStackImpl.fromValue.apply(new FluidStackImpl.Pair(variant.getFluid(), variant.getComponents(), amount)); } public static FluidVariant toFabric(FluidStack stack) { - return ((FluidStackImpl.Pair) FluidStackImpl.toValue.apply(stack)).variant; + return ((FluidStackImpl.Pair) FluidStackImpl.toValue.apply(stack)).toVariant(); } } \ No newline at end of file diff --git a/fabric/src/main/java/dev/architectury/networking/fabric/NetworkManagerImpl.java b/fabric/src/main/java/dev/architectury/networking/fabric/NetworkManagerImpl.java index 023b2a950..f872095a6 100644 --- a/fabric/src/main/java/dev/architectury/networking/fabric/NetworkManagerImpl.java +++ b/fabric/src/main/java/dev/architectury/networking/fabric/NetworkManagerImpl.java @@ -20,20 +20,19 @@ package dev.architectury.networking.fabric; import com.mojang.logging.LogUtils; +import dev.architectury.impl.NetworkAggregator; import dev.architectury.networking.NetworkManager; import dev.architectury.networking.NetworkManager.NetworkReceiver; import dev.architectury.networking.SpawnEntityPacket; -import dev.architectury.networking.transformers.PacketSink; -import dev.architectury.networking.transformers.PacketTransformer; import dev.architectury.utils.Env; -import io.netty.buffer.ByteBufUtil; -import io.netty.buffer.Unpooled; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.core.RegistryAccess; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.protocol.game.ClientGamePacketListener; @@ -44,79 +43,47 @@ import net.minecraft.world.entity.player.Player; import org.slf4j.Logger; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; - public class NetworkManagerImpl { - private static final Map> C2S_TYPE = new HashMap<>(); - private static final Map> S2C_TYPE = new HashMap<>(); - private static final Map C2S_RECEIVER = new HashMap<>(); - private static final Map S2C_RECEIVER = new HashMap<>(); - private static final Map C2S_TRANSFORMERS = new HashMap<>(); - private static final Map S2C_TRANSFORMERS = new HashMap<>(); - private static final Logger LOGGER = LogUtils.getLogger(); - public static void registerReceiver(NetworkManager.Side side, ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { - Objects.requireNonNull(id, "Cannot register receiver with a null ID!"); - packetTransformers = Objects.requireNonNullElse(packetTransformers, List.of()); - Objects.requireNonNull(receiver, "Cannot register a null receiver!"); - if (side == NetworkManager.Side.C2S) { - registerC2SReceiver(id, packetTransformers, receiver); - } else if (side == NetworkManager.Side.S2C) { - registerS2CReceiver(id, packetTransformers, receiver); - } - } - - private static void registerC2SReceiver(ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { - LOGGER.info("Registering C2S receiver with id {}", id); - C2S_RECEIVER.put(id, receiver); - CustomPacketPayload.Type type = new CustomPacketPayload.Type<>(id); - C2S_TYPE.put(id, type); - PayloadTypeRegistry.playC2S().register(type, BufCustomPacketPayload.streamCodec(type)); - PacketTransformer transformer = PacketTransformer.concat(packetTransformers); - ServerPlayNetworking.registerGlobalReceiver(type, (payload, fabricContext) -> { - var context = context(fabricContext.player(), fabricContext.player().server, false); - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(payload.payload())); - transformer.inbound(NetworkManager.Side.C2S, id, buf, context, (side, id1, buf1) -> { - NetworkReceiver networkReceiver = side == NetworkManager.Side.C2S ? C2S_RECEIVER.get(id1) : S2C_RECEIVER.get(id1); - if (networkReceiver == null) { - throw new IllegalArgumentException("Network Receiver not found! " + id1); - } - networkReceiver.receive(buf1, context); - }); - buf.release(); - }); - C2S_TRANSFORMERS.put(id, transformer); - } - - @SuppressWarnings("Convert2Lambda") - @Environment(EnvType.CLIENT) - private static void registerS2CReceiver(ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { - LOGGER.info("Registering S2C receiver with id {}", id); - S2C_RECEIVER.put(id, receiver); - CustomPacketPayload.Type type = new CustomPacketPayload.Type<>(id); - S2C_TYPE.put(id, type); - PayloadTypeRegistry.playS2C().register(type, BufCustomPacketPayload.streamCodec(type)); - PacketTransformer transformer = PacketTransformer.concat(packetTransformers); - ClientPlayNetworking.registerGlobalReceiver(type, new ClientPlayNetworking.PlayPayloadHandler<>() { + public static NetworkAggregator.Adaptor getAdaptor() { + return new NetworkAggregator.Adaptor() { @Override - public void receive(BufCustomPacketPayload payload, ClientPlayNetworking.Context fabricContext) { - var context = context(fabricContext.player(), fabricContext.client(), true); - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(payload.payload())); - transformer.inbound(NetworkManager.Side.S2C, id, buf, context, (side, id1, buf1) -> { - NetworkReceiver networkReceiver = side == NetworkManager.Side.C2S ? C2S_RECEIVER.get(id1) : S2C_RECEIVER.get(id1); - if (networkReceiver == null) { - throw new IllegalArgumentException("Network Receiver not found! " + id1); - } - networkReceiver.receive(buf1, context); + public void registerC2S(CustomPacketPayload.Type type, StreamCodec codec, NetworkReceiver receiver) { + LOGGER.info("Registering C2S receiver with id {}", type.id()); + PayloadTypeRegistry.playC2S().register(type, codec); + ServerPlayNetworking.registerGlobalReceiver(type, (payload, fabricContext) -> { + var context = context(fabricContext.player(), fabricContext.player().server, false); + receiver.receive(payload, context); }); - buf.release(); } - }); - S2C_TRANSFORMERS.put(id, transformer); + + @Override + @Environment(EnvType.CLIENT) + public void registerS2C(CustomPacketPayload.Type type, StreamCodec codec, NetworkReceiver receiver) { + LOGGER.info("Registering S2C receiver with id {}", type.id()); + PayloadTypeRegistry.playS2C().register(type, codec); + ClientPlayNetworking.registerGlobalReceiver(type, (payload, fabricContext) -> { + var context = context(fabricContext.player(), fabricContext.client(), true); + receiver.receive(payload, context); + }); + } + + @Override + public Packet toC2SPacket(T payload) { + return ClientPlayNetworking.createC2SPacket(payload); + } + + @Override + public Packet toS2CPacket(T payload) { + return ServerPlayNetworking.createS2CPacket(payload); + } + + @Override + public void registerS2CType(CustomPacketPayload.Type type, StreamCodec codec) { + PayloadTypeRegistry.playS2C().register(type, codec); + } + }; } private static NetworkManager.PacketContext context(Player player, BlockableEventLoop taskQueue, boolean client) { @@ -135,30 +102,14 @@ public void queue(Runnable runnable) { public Env getEnvironment() { return client ? Env.CLIENT : Env.SERVER; } + + @Override + public RegistryAccess registryAccess() { + return player.registryAccess(); + } }; } - public static void collectPackets(PacketSink sink, NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf) { - PacketTransformer transformer = side == NetworkManager.Side.C2S ? C2S_TRANSFORMERS.get(id) : S2C_TRANSFORMERS.get(id); - if (transformer != null) { - transformer.outbound(side, id, buf, (side1, id1, buf1) -> { - sink.accept(toPacket(side1, id1, buf1)); - }); - } else { - sink.accept(toPacket(side, id, buf)); - } - } - - public static Packet toPacket(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf) { - if (side == NetworkManager.Side.C2S) { - return toC2SPacket(id, buf); - } else if (side == NetworkManager.Side.S2C) { - return toS2CPacket(id, buf); - } - - throw new IllegalArgumentException("Invalid side: " + side); - } - @Environment(EnvType.CLIENT) public static boolean canServerReceive(ResourceLocation id) { return ClientPlayNetworking.canSend(id); @@ -171,21 +122,4 @@ public static boolean canPlayerReceive(ServerPlayer player, ResourceLocation id) public static Packet createAddEntityPacket(Entity entity) { return SpawnEntityPacket.create(entity); } - - @Environment(EnvType.CLIENT) - private static Packet toC2SPacket(ResourceLocation id, FriendlyByteBuf buf) { - CustomPacketPayload.Type type = C2S_TYPE.get(id); - if (type == null) { - throw new IllegalArgumentException("Unknown packet id: " + id); - } - return ClientPlayNetworking.createC2SPacket(new BufCustomPacketPayload(type, ByteBufUtil.getBytes(buf))); - } - - private static Packet toS2CPacket(ResourceLocation id, FriendlyByteBuf buf) { - CustomPacketPayload.Type type = S2C_TYPE.get(id); - if (type == null) { - throw new IllegalArgumentException("Unknown packet id: " + id); - } - return ServerPlayNetworking.createS2CPacket(new BufCustomPacketPayload(type, ByteBufUtil.getBytes(buf))); - } } diff --git a/gradle.properties b/gradle.properties index 511a225d4..d68294d55 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ org.gradle.jvmargs=-Xmx6G org.gradle.daemon=false -platforms=fabric +platforms=fabric,neoforge minecraft_version=24w14a supported_version=1.20.5 (24w14a) @@ -18,7 +18,10 @@ fabric_api_version=0.96.14+1.20.5 mod_menu_version=9.0.0 forge_version=49.0.14 -neoforge_version=20.4.77-beta +neoforge_version=20.5.0-alpha.24w14a.20240407.201521 + +# Set to empty if not snapshots +neoforge_pr=787 curseforge_id=419699 modrinth_id=lhGA9TYQ diff --git a/neoforge/build.gradle b/neoforge/build.gradle index e2cd2398a..046d67038 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -11,38 +11,29 @@ architectury { platformSetupLoomIde() neoForge { platformPackage = "forge" - remapForgeLike "net/minecraftforge/common/extensions/IForgeItem", "net/neoforged/neoforge/common/extensions/IItemExtension" - remapForgeLike "net/minecraftforge/client/event/TextureStitchEvent\$Post", "net/neoforged/neoforge/client/event/TextureAtlasStitchedEvent" - remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid", "net/neoforged/neoforge/fluids/BaseFlowingFluid" - remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid\$Properties", "net/neoforged/neoforge/fluids/BaseFlowingFluid\$Properties" - remapForgeLike "net/minecraftforge/common/ForgeHooks", "net/neoforged/neoforge/common/CommonHooks" } } configurations { common - forgeLike shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this. - compileClasspath.extendsFrom common, forgeLike - runtimeClasspath.extendsFrom common, forgeLike + compileClasspath.extendsFrom common + runtimeClasspath.extendsFrom common developmentNeoForge.extendsFrom common - developmentForgeLike.extendsFrom forgeLike } dependencies { neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" common(project(path: ":common", configuration: "namedElements")) { transitive false } - forgeLike(project(path: ":forge", configuration: "namedElements")) { transitive false } shadowCommon(project(path: ":common", configuration: "transformProductionNeoForge")) { transitive false } - shadowCommon(project(path: ":forge", configuration: "transformProductionNeoForge")) { transitive false } } processResources { - filesMatching("META-INF/mods.toml") { + filesMatching("META-INF/neoforge.mods.toml") { expand "version": project.version } - inputs.property "META-INF/mods.toml", project.version + inputs.property "META-INF/neoforge.mods.toml", project.version } shadowJar { @@ -113,7 +104,7 @@ publishing { } repositories { - if (System.getenv("MAVEN_PASS") != null) { + if (System.getenv("MAVEN_PASS") != null && rootProject.neoforge_pr == "") { maven { url = "https://deploy.shedaniel.me/" credentials { @@ -126,6 +117,7 @@ publishing { } unifiedPublishing { + if (rootProject.neoforge_pr != "") return // Don't publish PRs project { displayName = "[NeoForge $rootProject.supported_version] v$project.version" releaseType = "$rootProject.artifact_type" diff --git a/neoforge/src/main/java/dev/architectury/core/block/forge/imitator/ArchitecturyLiquidBlock.java b/neoforge/src/main/java/dev/architectury/core/block/forge/imitator/ArchitecturyLiquidBlock.java new file mode 100644 index 000000000..45daccce7 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/core/block/forge/imitator/ArchitecturyLiquidBlock.java @@ -0,0 +1,31 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.block.forge.imitator; + +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.material.FlowingFluid; + +import java.util.function.Supplier; + +public class ArchitecturyLiquidBlock extends LiquidBlock { + public ArchitecturyLiquidBlock(Supplier fluid, Properties properties) { + super(fluid, properties); + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFlowingFluid.java b/neoforge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFlowingFluid.java new file mode 100644 index 000000000..f387c2fac --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFlowingFluid.java @@ -0,0 +1,184 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid.forge.imitator; + +import com.google.common.base.MoreObjects; +import com.google.common.base.Suppliers; +import dev.architectury.core.fluid.ArchitecturyFluidAttributes; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.neoforged.neoforge.fluids.BaseFlowingFluid; +import net.neoforged.neoforge.fluids.FluidType; +import org.jetbrains.annotations.NotNull; + +import java.util.IdentityHashMap; +import java.util.Map; +import java.util.Optional; + +public abstract class ArchitecturyFlowingFluid extends BaseFlowingFluid { + private static final Map FLUID_TYPE_MAP = new IdentityHashMap<>(); + private final ArchitecturyFluidAttributes attributes; + + ArchitecturyFlowingFluid(ArchitecturyFluidAttributes attributes) { + super(toForgeProperties(attributes)); + this.attributes = attributes; + } + + private static Properties toForgeProperties(ArchitecturyFluidAttributes attributes) { + Properties forge = new Properties(Suppliers.memoize(() -> { + return FLUID_TYPE_MAP.computeIfAbsent(attributes, attr -> { + return new ArchitecturyFluidAttributesForge(FluidType.Properties.create(), attr.getSourceFluid(), attr); + }); + }), attributes::getSourceFluid, attributes::getFlowingFluid); + forge.slopeFindDistance(attributes.getSlopeFindDistance()); + forge.levelDecreasePerBlock(attributes.getDropOff()); + forge.bucket(() -> MoreObjects.firstNonNull(attributes.getBucketItem(), Items.AIR)); + forge.tickRate(attributes.getTickDelay()); + forge.explosionResistance(attributes.getExplosionResistance()); + forge.block(() -> MoreObjects.firstNonNull(attributes.getBlock(), (LiquidBlock) Blocks.WATER)); + return forge; + } + + @Override + public Fluid getFlowing() { + return attributes.getFlowingFluid(); + } + + @Override + public Fluid getSource() { + return attributes.getSourceFluid(); + } + + @Override + protected boolean canConvertToSource(Level level) { + return attributes.canConvertToSource(); + } + + @Override + protected void beforeDestroyingBlock(LevelAccessor level, BlockPos pos, BlockState state) { + // Same implementation as in WaterFluid. + BlockEntity blockEntity = state.hasBlockEntity() ? level.getBlockEntity(pos) : null; + Block.dropResources(state, level, pos, blockEntity); + } + + @Override + protected int getSlopeFindDistance(LevelReader level) { + return attributes.getSlopeFindDistance(level); + } + + @Override + protected int getDropOff(LevelReader level) { + return attributes.getDropOff(level); + } + + @Override + public Item getBucket() { + Item item = attributes.getBucketItem(); + return item == null ? Items.AIR : item; + } + + @Override + protected boolean canBeReplacedWith(FluidState state, BlockGetter level, BlockPos pos, Fluid fluid, Direction direction) { + // Same implementation as in WaterFluid. + return direction == Direction.DOWN && !this.isSame(fluid); + } + + @Override + public int getTickDelay(LevelReader level) { + return attributes.getTickDelay(level); + } + + @Override + protected float getExplosionResistance() { + return attributes.getExplosionResistance(); + } + + @Override + protected BlockState createLegacyBlock(FluidState state) { + LiquidBlock block = attributes.getBlock(); + if (block == null) return Blocks.AIR.defaultBlockState(); + return block.defaultBlockState().setValue(LiquidBlock.LEVEL, getLegacyLevel(state)); + } + + @NotNull + @Override + public Optional getPickupSound() { + return Optional.ofNullable(attributes.getFillSound()); + } + + @Override + public boolean isSame(Fluid fluid) { + return fluid == getSource() || fluid == getFlowing(); + } + + public static class Source extends ArchitecturyFlowingFluid { + public Source(ArchitecturyFluidAttributes attributes) { + super(attributes); + } + + @Override + public int getAmount(FluidState state) { + return 8; + } + + @Override + public boolean isSource(FluidState state) { + return true; + } + } + + public static class Flowing extends ArchitecturyFlowingFluid { + public Flowing(ArchitecturyFluidAttributes attributes) { + super(attributes); + this.registerDefaultState(this.getStateDefinition().any().setValue(LEVEL, 7)); + } + + @Override + protected void createFluidStateDefinition(StateDefinition.Builder builder) { + super.createFluidStateDefinition(builder); + builder.add(LEVEL); + } + + @Override + public int getAmount(FluidState state) { + return state.getValue(LEVEL); + } + + @Override + public boolean isSource(FluidState state) { + return false; + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFluidAttributesForge.java b/neoforge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFluidAttributesForge.java new file mode 100644 index 000000000..ca24d3b11 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/core/fluid/forge/imitator/ArchitecturyFluidAttributesForge.java @@ -0,0 +1,265 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.fluid.forge.imitator; + +import com.google.common.base.MoreObjects; +import dev.architectury.core.fluid.ArchitecturyFluidAttributes; +import dev.architectury.hooks.fluid.forge.FluidStackHooksForge; +import net.minecraft.Util; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Rarity; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions; +import net.neoforged.neoforge.common.SoundAction; +import net.neoforged.neoforge.fluids.FluidStack; +import net.neoforged.neoforge.fluids.FluidType; +import org.jetbrains.annotations.Nullable; + +import java.util.function.Consumer; + +import static net.minecraft.sounds.SoundEvents.BUCKET_EMPTY; +import static net.minecraft.sounds.SoundEvents.BUCKET_FILL; + +class ArchitecturyFluidAttributesForge extends FluidType { + private final ArchitecturyFluidAttributes attributes; + private final String defaultTranslationKey; + + public ArchitecturyFluidAttributesForge(Properties builder, Fluid fluid, ArchitecturyFluidAttributes attributes) { + super(addArchIntoBuilder(builder, attributes)); + this.attributes = attributes; + this.defaultTranslationKey = Util.makeDescriptionId("fluid", BuiltInRegistries.FLUID.getKey(fluid)); + } + + private static Properties addArchIntoBuilder(Properties builder, ArchitecturyFluidAttributes attributes) { + builder.lightLevel(attributes.getLuminosity()) + .density(attributes.getDensity()) + .temperature(attributes.getTemperature()) + .rarity(attributes.getRarity()) + .canConvertToSource(attributes.canConvertToSource()) + .viscosity(attributes.getViscosity()); + return builder; + } + + @Override + public ItemStack getBucket(FluidStack stack) { + Item item = attributes.getBucketItem(); + return item == null ? super.getBucket(stack) : new ItemStack(item); + } + + @Override + public void initializeClient(Consumer consumer) { + consumer.accept(new IClientFluidTypeExtensions() { + @Override + public int getTintColor() { + return attributes.getColor(); + } + + @Override + public ResourceLocation getStillTexture() { + return attributes.getSourceTexture(); + } + + @Override + public ResourceLocation getFlowingTexture() { + return attributes.getFlowingTexture(); + } + + @Override + @Nullable + public ResourceLocation getOverlayTexture() { + return attributes.getOverlayTexture(); + } + + @Override + public ResourceLocation getStillTexture(FluidState state, BlockAndTintGetter getter, BlockPos pos) { + return attributes.getSourceTexture(state, getter, pos); + } + + @Override + public ResourceLocation getFlowingTexture(FluidState state, BlockAndTintGetter getter, BlockPos pos) { + return attributes.getFlowingTexture(state, getter, pos); + } + + @Override + @Nullable + public ResourceLocation getOverlayTexture(FluidState state, BlockAndTintGetter getter, BlockPos pos) { + return attributes.getOverlayTexture(state, getter, pos); + } + + @Override + public int getTintColor(FluidState state, BlockAndTintGetter getter, BlockPos pos) { + return attributes.getColor(state, getter, pos); + } + + @Override + public int getTintColor(FluidStack stack) { + return attributes.getColor(convertSafe(stack)); + } + + @Override + public ResourceLocation getStillTexture(FluidStack stack) { + return attributes.getSourceTexture(convertSafe(stack)); + } + + @Override + public ResourceLocation getFlowingTexture(FluidStack stack) { + return attributes.getFlowingTexture(convertSafe(stack)); + } + + @Override + @Nullable + public ResourceLocation getOverlayTexture(FluidStack stack) { + return attributes.getOverlayTexture(convertSafe(stack)); + } + }); + } + + @Override + public int getLightLevel(FluidStack stack) { + return attributes.getLuminosity(convertSafe(stack)); + } + + @Override + public int getLightLevel(FluidState state, BlockAndTintGetter level, BlockPos pos) { + return attributes.getLuminosity(convertSafe(state), level, pos); + } + + @Override + public int getDensity(FluidStack stack) { + return attributes.getDensity(convertSafe(stack)); + } + + @Override + public int getDensity(FluidState state, BlockAndTintGetter level, BlockPos pos) { + return attributes.getDensity(convertSafe(state), level, pos); + } + + @Override + public int getTemperature(FluidStack stack) { + return attributes.getTemperature(convertSafe(stack)); + } + + @Override + public int getTemperature(FluidState state, BlockAndTintGetter level, BlockPos pos) { + return attributes.getTemperature(convertSafe(state), level, pos); + } + + @Override + public int getViscosity(FluidStack stack) { + return attributes.getViscosity(convertSafe(stack)); + } + + @Override + public int getViscosity(FluidState state, BlockAndTintGetter level, BlockPos pos) { + return attributes.getViscosity(convertSafe(state), level, pos); + } + + @Override + public Rarity getRarity() { + return attributes.getRarity(); + } + + @Override + public Rarity getRarity(FluidStack stack) { + return attributes.getRarity(convertSafe(stack)); + } + + @Override + public Component getDescription() { + return attributes.getName(); + } + + @Override + public Component getDescription(FluidStack stack) { + return attributes.getName(convertSafe(stack)); + } + + @Override + public String getDescriptionId() { + return MoreObjects.firstNonNull(attributes.getTranslationKey(), defaultTranslationKey); + } + + @Override + public String getDescriptionId(FluidStack stack) { + return MoreObjects.firstNonNull(attributes.getTranslationKey(convertSafe(stack)), defaultTranslationKey); + } + + @Override + @Nullable + public SoundEvent getSound(SoundAction action) { + return getSound((FluidStack) null, action); + } + + @Override + @Nullable + public SoundEvent getSound(@Nullable FluidStack stack, SoundAction action) { + var archStack = convertSafe(stack); + if (BUCKET_FILL.equals(action)) { + return attributes.getFillSound(archStack); + } else if (BUCKET_EMPTY.equals(action)) { + return attributes.getEmptySound(archStack); + } + return null; + } + + @Override + @Nullable + public SoundEvent getSound(@Nullable Player player, BlockGetter getter, BlockPos pos, SoundAction action) { + if (getter instanceof BlockAndTintGetter level) { + if (BUCKET_FILL.equals(action)) { + return attributes.getFillSound(null, level, pos); + } else if (BUCKET_EMPTY.equals(action)) { + return attributes.getEmptySound(null, level, pos); + } + } + return getSound((FluidStack) null, action); + } + + @Override + public boolean canConvertToSource(FluidStack stack) { + return attributes.canConvertToSource(); + } + + @Override + public boolean canConvertToSource(FluidState state, LevelReader reader, BlockPos pos) { + return attributes.canConvertToSource(); + } + + @Nullable + public dev.architectury.fluid.FluidStack convertSafe(@Nullable FluidStack stack) { + return stack == null ? null : FluidStackHooksForge.fromForge(stack); + } + + @Nullable + public dev.architectury.fluid.FluidStack convertSafe(@Nullable FluidState state) { + return state == null ? null : dev.architectury.fluid.FluidStack.create(state.getType(), dev.architectury.fluid.FluidStack.bucketAmount()); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeHooksImpl.java b/neoforge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyBucketItem.java similarity index 53% rename from neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeHooksImpl.java rename to neoforge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyBucketItem.java index 6d07b53a1..353204870 100644 --- a/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeHooksImpl.java +++ b/neoforge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyBucketItem.java @@ -17,47 +17,38 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package dev.architectury.hooks.forgelike.forge; +package dev.architectury.core.item.forge.imitator; -import com.mojang.serialization.Codec; -import dev.architectury.platform.hooks.forge.EventBusesHooksImpl; +import dev.architectury.platform.hooks.EventBusesHooks; import dev.architectury.utils.ArchitecturyConstants; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; +import net.minecraft.world.item.BucketItem; +import net.minecraft.world.level.material.Fluid; import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; -import net.neoforged.neoforge.common.world.BiomeModifier; import net.neoforged.neoforge.fluids.capability.wrappers.FluidBucketWrapper; -import net.neoforged.neoforge.registries.NeoForgeRegistries; -import net.neoforged.neoforge.registries.RegisterEvent; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.function.Supplier; -public class ForgeLikeHooksImpl { - private static final Logger LOGGER = LogManager.getLogger(ForgeLikeHooksImpl.class); +public class ArchitecturyBucketItem extends BucketItem { + private static final Logger LOGGER = LogManager.getLogger(ArchitecturyBucketItem.class); - public static void registerBiomeModifier(ResourceLocation id, Supplier> codecSupplier) { - EventBusesHooksImpl.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { - bus.addListener(event -> { - event.register(NeoForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, registry -> { - registry.register(id, codecSupplier.get()); - }); - }); - }); - } - - public static void registerBucketItemCapability(Item item) { - EventBusesHooksImpl.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + public ArchitecturyBucketItem(Supplier fluid, Properties properties) { + super(fluid, properties); + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { bus.addListener(event -> { - if (BuiltInRegistries.ITEM.containsValue(item)) { - event.registerItem(Capabilities.FluidHandler.ITEM, (stack, ctx) -> new FluidBucketWrapper(stack), item); + if (BuiltInRegistries.ITEM.containsValue(this)) { + event.registerItem(Capabilities.FluidHandler.ITEM, (stack, ctx) -> new FluidBucketWrapper(stack), this); } else { - LOGGER.warn("Tried to register a bucket item capability for an item that is not registered: {}", item); + LOGGER.warn("Tried to register a bucket item capability for an item that is not registered: {}", this); } }); }); } + + public final Fluid getContainedFluid() { + return getFluid(); + } } diff --git a/neoforge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyMobBucketItem.java b/neoforge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyMobBucketItem.java new file mode 100644 index 000000000..3a484a2ab --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/core/item/forge/imitator/ArchitecturyMobBucketItem.java @@ -0,0 +1,33 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.core.item.forge.imitator; + +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.item.MobBucketItem; +import net.minecraft.world.level.material.Fluid; + +import java.util.function.Supplier; + +public class ArchitecturyMobBucketItem extends MobBucketItem { + public ArchitecturyMobBucketItem(Supplier> entity, Supplier fluid, Supplier sound, Properties properties) { + super(entity, fluid, sound, properties); + } +} diff --git a/neoforge/src/main/java/dev/architectury/event/forge/EventFactoryImpl.java b/neoforge/src/main/java/dev/architectury/event/forge/EventFactoryImpl.java new file mode 100644 index 000000000..2b7feaefc --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/event/forge/EventFactoryImpl.java @@ -0,0 +1,73 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.event.forge; + +import dev.architectury.event.Event; +import dev.architectury.event.EventActor; +import dev.architectury.event.EventResult; +import net.neoforged.bus.api.ICancellableEvent; +import net.neoforged.neoforge.common.NeoForge; +import org.jetbrains.annotations.ApiStatus; + +import java.util.function.Consumer; + +public class EventFactoryImpl { + public static Event> attachToForge(Event> event) { + event.register(eventObj -> { + if (!(eventObj instanceof net.neoforged.bus.api.Event)) { + throw new ClassCastException(eventObj.getClass() + " is not an instance of forge Event!"); + } + NeoForge.EVENT_BUS.post((net.neoforged.bus.api.Event) eventObj); + }); + return event; + } + + @ApiStatus.Internal + public static Event> attachToForgeEventActor(Event> event) { + event.register(eventObj -> { + if (!(eventObj instanceof net.neoforged.bus.api.Event)) { + throw new ClassCastException(eventObj.getClass() + " is not an instance of forge Event!"); + } + if (!(eventObj instanceof ICancellableEvent)) { + throw new ClassCastException(eventObj.getClass() + " is not cancellable Event!"); + } + NeoForge.EVENT_BUS.post((net.neoforged.bus.api.Event) eventObj); + return EventResult.pass(); + }); + return event; + } + + @ApiStatus.Internal + public static Event> attachToForgeEventActorCancellable(Event> event) { + event.register(eventObj -> { + if (!(eventObj instanceof net.neoforged.bus.api.Event)) { + throw new ClassCastException(eventObj.getClass() + " is not an instance of forge Event!"); + } + if (!(eventObj instanceof ICancellableEvent)) { + throw new ClassCastException(eventObj.getClass() + " is not cancellable Event!"); + } + if (((ICancellableEvent) NeoForge.EVENT_BUS.post((net.neoforged.bus.api.Event) eventObj)).isCanceled()) { + return EventResult.interrupt(false); + } + return EventResult.pass(); + }); + return event; + } +} diff --git a/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImpl.java b/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImpl.java new file mode 100644 index 000000000..0a2f6b18d --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImpl.java @@ -0,0 +1,51 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.event.forge; + +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.neoforge.common.NeoForge; + +public class EventHandlerImpl { + @OnlyIn(Dist.CLIENT) + public static void registerClient() { + NeoForge.EVENT_BUS.register(EventHandlerImplClient.class); + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.register(EventHandlerImplClient.ModBasedEventHandler.class); + }); + } + + public static void registerCommon() { + NeoForge.EVENT_BUS.register(EventHandlerImplCommon.class); + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.register(EventHandlerImplCommon.ModBasedEventHandler.class); + }); + } + + @OnlyIn(Dist.DEDICATED_SERVER) + public static void registerServer() { + // MinecraftForge.EVENT_BUS.register(EventHandlerImplServer.class); + // EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + // bus.register(EventHandlerImplServer.ModBasedEventHandler.class); + // }); + } +} diff --git a/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java b/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java new file mode 100644 index 000000000..a2ac26a18 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImplClient.java @@ -0,0 +1,351 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.event.forge; + +import com.mojang.brigadier.CommandDispatcher; +import dev.architectury.event.CompoundEventResult; +import dev.architectury.event.EventResult; +import dev.architectury.event.events.client.ClientChatEvent; +import dev.architectury.event.events.client.*; +import dev.architectury.event.events.common.InteractionEvent; +import dev.architectury.impl.ScreenAccessImpl; +import dev.architectury.impl.TooltipEventColorContextImpl; +import dev.architectury.impl.TooltipEventPositionContextImpl; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.network.chat.Component; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.bus.api.EventPriority; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.neoforge.client.event.*; +import net.neoforged.neoforge.event.TickEvent; +import net.neoforged.neoforge.event.entity.player.ItemTooltipEvent; +import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent; +import net.neoforged.neoforge.event.level.LevelEvent; + +@OnlyIn(Dist.CLIENT) +public class EventHandlerImplClient { + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ItemTooltipEvent event) { + ClientTooltipEvent.ITEM.invoker().append(event.getItemStack(), event.getToolTip(), event.getFlags()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(TickEvent.ClientTickEvent event) { + if (event.phase == TickEvent.Phase.START) + ClientTickEvent.CLIENT_PRE.invoker().tick(Minecraft.getInstance()); + else if (event.phase == TickEvent.Phase.END) + ClientTickEvent.CLIENT_POST.invoker().tick(Minecraft.getInstance()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventRenderGameOverlayEvent(RenderGuiEvent.Post event) { + ClientGuiEvent.RENDER_HUD.invoker().renderHud(event.getGuiGraphics(), event.getPartialTick()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ClientPlayerNetworkEvent.LoggingIn event) { + ClientPlayerEvent.CLIENT_PLAYER_JOIN.invoker().join(event.getPlayer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ClientPlayerNetworkEvent.LoggingOut event) { + ClientPlayerEvent.CLIENT_PLAYER_QUIT.invoker().quit(event.getPlayer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ClientPlayerNetworkEvent.Clone event) { + ClientPlayerEvent.CLIENT_PLAYER_RESPAWN.invoker().respawn(event.getOldPlayer(), event.getNewPlayer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventInitScreenEvent(ScreenEvent.Init.Pre event) { + if (ClientGuiEvent.INIT_PRE.invoker().init(event.getScreen(), new ScreenAccessImpl(event.getScreen())).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventInitScreenEvent(ScreenEvent.Init.Post event) { + ClientGuiEvent.INIT_POST.invoker().init(event.getScreen(), new ScreenAccessImpl(event.getScreen())); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventRenderGameOverlayEvent(CustomizeGuiOverlayEvent.DebugText event) { + if (Minecraft.getInstance().gui.getDebugOverlay().showDebugScreen()) { + ClientGuiEvent.DEBUG_TEXT_LEFT.invoker().gatherText(event.getLeft()); + ClientGuiEvent.DEBUG_TEXT_RIGHT.invoker().gatherText(event.getRight()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(net.neoforged.neoforge.client.event.ClientChatEvent event) { + EventResult process = ClientChatEvent.SEND.invoker().send(event.getMessage(), null); + if (process.isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ClientChatReceivedEvent event) { + CompoundEventResult process = ClientChatEvent.RECEIVED.invoker().process(event.getBoundChatType(), event.getMessage()); + if (process.isPresent()) { + if (process.isFalse()) + event.setCanceled(true); + else if (process.object() != null) + event.setMessage(process.object()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventWorldEvent(LevelEvent.Load event) { + if (event.getLevel().isClientSide()) { + ClientLevel world = (ClientLevel) event.getLevel(); + ClientLifecycleEvent.CLIENT_LEVEL_LOAD.invoker().act(world); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ScreenEvent.Opening event) { + CompoundEventResult result = ClientGuiEvent.SET_SCREEN.invoker().modifyScreen(event.getScreen()); + if (result.isPresent()) { + if (result.isFalse()) + event.setCanceled(true); + else if (result.object() != null) + event.setNewScreen(result.object()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventDrawScreenEvent(ScreenEvent.Render.Pre event) { + if (ClientGuiEvent.RENDER_PRE.invoker().render(event.getScreen(), event.getGuiGraphics(), event.getMouseX(), event.getMouseY(), event.getPartialTick()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventDrawScreenEvent(ScreenEvent.Render.Post event) { + ClientGuiEvent.RENDER_POST.invoker().render(event.getScreen(), event.getGuiGraphics(), event.getMouseX(), event.getMouseY(), event.getPartialTick()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventContainerScreenEvent(ContainerScreenEvent.Render.Background event) { + ClientGuiEvent.RENDER_CONTAINER_BACKGROUND.invoker().render(event.getContainerScreen(), event.getGuiGraphics(), event.getMouseX(), event.getMouseY(), Minecraft.getInstance().getDeltaFrameTime()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventContainerScreenEvent(ContainerScreenEvent.Render.Foreground event) { + ClientGuiEvent.RENDER_CONTAINER_FOREGROUND.invoker().render(event.getContainerScreen(), event.getGuiGraphics(), event.getMouseX(), event.getMouseY(), Minecraft.getInstance().getDeltaFrameTime()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventPlayerInteractEvent(PlayerInteractEvent.RightClickEmpty event) { + InteractionEvent.CLIENT_RIGHT_CLICK_AIR.invoker().click(event.getEntity(), event.getHand()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventPlayerInteractEvent(PlayerInteractEvent.LeftClickEmpty event) { + InteractionEvent.CLIENT_LEFT_CLICK_AIR.invoker().click(event.getEntity(), event.getHand()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(RecipesUpdatedEvent event) { + ClientRecipeUpdateEvent.EVENT.invoker().update(event.getRecipeManager()); + } + + private static final ThreadLocal tooltipPositionContext = ThreadLocal.withInitial(TooltipEventPositionContextImpl::new); + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventRenderTooltipEvent(RenderTooltipEvent.Pre event) { + GuiGraphics graphics = event.getGraphics(); + ClientTooltipEvent.additionalContexts().setItem(event.getItemStack()); + + try { + if (ClientTooltipEvent.RENDER_PRE.invoker().renderTooltip(graphics, event.getComponents(), event.getX(), event.getY()).isFalse()) { + event.setCanceled(true); + return; + } + + TooltipEventPositionContextImpl positionContext = tooltipPositionContext.get(); + positionContext.reset(event.getX(), event.getY()); + ClientTooltipEvent.RENDER_MODIFY_POSITION.invoker().renderTooltip(graphics, positionContext); + event.setX(positionContext.getTooltipX()); + event.setY(positionContext.getTooltipY()); + } finally { + ClientTooltipEvent.additionalContexts().setItem(null); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventRenderTooltipEvent(RenderTooltipEvent.Color event) { + GuiGraphics graphics = event.getGraphics(); + ClientTooltipEvent.additionalContexts().setItem(event.getItemStack()); + + try { + TooltipEventColorContextImpl colorContext = TooltipEventColorContextImpl.CONTEXT.get(); + colorContext.reset(); + colorContext.setBackgroundColor(event.getBackgroundStart()); + colorContext.setOutlineGradientTopColor(event.getBorderStart()); + colorContext.setOutlineGradientBottomColor(event.getBorderEnd()); + ClientTooltipEvent.RENDER_MODIFY_COLOR.invoker().renderTooltip(graphics, event.getX(), event.getY(), colorContext); + event.setBackground(colorContext.getBackgroundColor()); + event.setBorderEnd(colorContext.getOutlineGradientBottomColor()); + event.setBorderStart(colorContext.getOutlineGradientTopColor()); + } finally { + ClientTooltipEvent.additionalContexts().setItem(null); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventMouseScrollEvent(ScreenEvent.MouseScrolled.Pre event) { + if (ClientScreenInputEvent.MOUSE_SCROLLED_PRE.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getScrollDeltaX(), event.getScrollDeltaY()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventMouseScrollEvent(ScreenEvent.MouseScrolled.Post event) { + ClientScreenInputEvent.MOUSE_SCROLLED_POST.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getScrollDeltaX(), event.getScrollDeltaY()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventMouseClickedEvent(ScreenEvent.MouseButtonPressed.Pre event) { + if (ClientScreenInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getButton()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventMouseClickedEvent(ScreenEvent.MouseButtonPressed.Post event) { + ClientScreenInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getButton()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventMouseDragEvent(ScreenEvent.MouseDragged.Pre event) { + if (ClientScreenInputEvent.MOUSE_DRAGGED_PRE.invoker().mouseDragged(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getMouseButton(), event.getDragX(), event.getDragY()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventMouseDragEvent(ScreenEvent.MouseDragged.Post event) { + ClientScreenInputEvent.MOUSE_DRAGGED_POST.invoker().mouseDragged(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getMouseButton(), event.getDragX(), event.getDragY()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventMouseReleasedEvent(ScreenEvent.MouseButtonReleased.Pre event) { + if (ClientScreenInputEvent.MOUSE_RELEASED_PRE.invoker().mouseReleased(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getButton()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventMouseReleasedEvent(ScreenEvent.MouseButtonReleased.Post event) { + ClientScreenInputEvent.MOUSE_RELEASED_PRE.invoker().mouseReleased(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getButton()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventKeyboardCharTypedEvent(ScreenEvent.CharacterTyped.Pre event) { + if (ClientScreenInputEvent.CHAR_TYPED_PRE.invoker().charTyped(Minecraft.getInstance(), event.getScreen(), event.getCodePoint(), event.getModifiers()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventKeyboardCharTypedEvent(ScreenEvent.CharacterTyped.Post event) { + ClientScreenInputEvent.CHAR_TYPED_POST.invoker().charTyped(Minecraft.getInstance(), event.getScreen(), event.getCodePoint(), event.getModifiers()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventKeyboardKeyPressedEvent(ScreenEvent.KeyPressed.Pre event) { + if (ClientScreenInputEvent.KEY_PRESSED_PRE.invoker().keyPressed(Minecraft.getInstance(), event.getScreen(), event.getKeyCode(), event.getScanCode(), event.getModifiers()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventKeyboardKeyPressedEvent(ScreenEvent.KeyPressed.Post event) { + ClientScreenInputEvent.KEY_PRESSED_POST.invoker().keyPressed(Minecraft.getInstance(), event.getScreen(), event.getKeyCode(), event.getScanCode(), event.getModifiers()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventKeyboardKeyReleasedEvent(ScreenEvent.KeyReleased.Pre event) { + if (ClientScreenInputEvent.KEY_RELEASED_PRE.invoker().keyReleased(Minecraft.getInstance(), event.getScreen(), event.getKeyCode(), event.getScanCode(), event.getModifiers()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventKeyboardKeyReleasedEvent(ScreenEvent.KeyReleased.Post event) { + ClientScreenInputEvent.KEY_RELEASED_POST.invoker().keyReleased(Minecraft.getInstance(), event.getScreen(), event.getKeyCode(), event.getScanCode(), event.getModifiers()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventInputEvent(InputEvent.MouseScrollingEvent event) { + if (ClientRawInputEvent.MOUSE_SCROLLED.invoker().mouseScrolled(Minecraft.getInstance(), event.getScrollDeltaX(), event.getScrollDeltaY()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventInputEvent(InputEvent.MouseButton.Pre event) { + if (ClientRawInputEvent.MOUSE_CLICKED_PRE.invoker().mouseClicked(Minecraft.getInstance(), event.getButton(), event.getAction(), event.getModifiers()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventInputEvent(InputEvent.MouseButton.Post event) { + ClientRawInputEvent.MOUSE_CLICKED_POST.invoker().mouseClicked(Minecraft.getInstance(), event.getButton(), event.getAction(), event.getModifiers()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventInputEvent(InputEvent.Key event) { + ClientRawInputEvent.KEY_PRESSED.invoker().keyPressed(Minecraft.getInstance(), event.getKey(), event.getScanCode(), event.getAction(), event.getModifiers()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(RegisterClientCommandsEvent event) { + ClientCommandRegistrationEvent.EVENT.invoker().register((CommandDispatcher) + (CommandDispatcher) event.getDispatcher(), event.getBuildContext()); + } + + @OnlyIn(Dist.CLIENT) + public static class ModBasedEventHandler { + // @SubscribeEvent(priority = EventPriority.HIGH) + // public static void eventTextureStitchEvent(TextureStitchEvent.Post event) { + // ClientTextureStitchEvent.POST.invoker().stitch(event.getAtlas()); + // } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(FMLClientSetupEvent event) { + ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(Minecraft.getInstance()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(RegisterShadersEvent event) { + ClientReloadShadersEvent.EVENT.invoker().reload(event.getResourceProvider(), event::registerShader); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImplCommon.java b/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImplCommon.java new file mode 100644 index 000000000..f7f29b536 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImplCommon.java @@ -0,0 +1,446 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.event.forge; + +import dev.architectury.event.CompoundEventResult; +import dev.architectury.event.EventResult; +import dev.architectury.event.events.common.PlayerEvent; +import dev.architectury.event.events.common.*; +import dev.architectury.utils.value.IntValue; +import net.minecraft.core.registries.Registries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.neoforged.bus.api.Event; +import net.neoforged.bus.api.EventPriority; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.LogicalSide; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; +import net.neoforged.neoforge.event.CommandEvent; +import net.neoforged.neoforge.event.LootTableLoadEvent; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.ServerChatEvent; +import net.neoforged.neoforge.event.TickEvent.LevelTickEvent; +import net.neoforged.neoforge.event.TickEvent.Phase; +import net.neoforged.neoforge.event.TickEvent.PlayerTickEvent; +import net.neoforged.neoforge.event.TickEvent.ServerTickEvent; +import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; +import net.neoforged.neoforge.event.entity.item.ItemTossEvent; +import net.neoforged.neoforge.event.entity.living.AnimalTameEvent; +import net.neoforged.neoforge.event.entity.living.LivingAttackEvent; +import net.neoforged.neoforge.event.entity.living.LivingDeathEvent; +import net.neoforged.neoforge.event.entity.living.MobSpawnEvent; +import net.neoforged.neoforge.event.entity.player.*; +import net.neoforged.neoforge.event.entity.player.PlayerEvent.*; +import net.neoforged.neoforge.event.level.BlockEvent.BreakEvent; +import net.neoforged.neoforge.event.level.BlockEvent.EntityPlaceEvent; +import net.neoforged.neoforge.event.level.BlockEvent.FarmlandTrampleEvent; +import net.neoforged.neoforge.event.level.ChunkDataEvent; +import net.neoforged.neoforge.event.level.ExplosionEvent.Detonate; +import net.neoforged.neoforge.event.level.ExplosionEvent.Start; +import net.neoforged.neoforge.event.level.LevelEvent; +import net.neoforged.neoforge.event.server.*; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + +public class EventHandlerImplCommon { + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ServerTickEvent event) { + if (event.phase == Phase.START) + TickEvent.SERVER_PRE.invoker().tick(ServerLifecycleHooks.getCurrentServer()); + else if (event.phase == Phase.END) + TickEvent.SERVER_POST.invoker().tick(ServerLifecycleHooks.getCurrentServer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(LevelTickEvent event) { + if (event.side == LogicalSide.SERVER) { + if (event.phase == Phase.START) + TickEvent.SERVER_LEVEL_PRE.invoker().tick((ServerLevel) event.level); + else if (event.phase == Phase.END) + TickEvent.SERVER_LEVEL_POST.invoker().tick((ServerLevel) event.level); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ServerStartingEvent event) { + LifecycleEvent.SERVER_STARTING.invoker().stateChanged(event.getServer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ServerStartedEvent event) { + LifecycleEvent.SERVER_STARTED.invoker().stateChanged(event.getServer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ServerStoppingEvent event) { + LifecycleEvent.SERVER_STOPPING.invoker().stateChanged(event.getServer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ServerStoppedEvent event) { + LifecycleEvent.SERVER_STOPPED.invoker().stateChanged(event.getServer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(RegisterCommandsEvent event) { + CommandRegistrationEvent.EVENT.invoker().register(event.getDispatcher(), event.getBuildContext(), event.getCommandSelection()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(PlayerLoggedInEvent event) { + PlayerEvent.PLAYER_JOIN.invoker().join((ServerPlayer) event.getEntity()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(PlayerLoggedOutEvent event) { + PlayerEvent.PLAYER_QUIT.invoker().quit((ServerPlayer) event.getEntity()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(PlayerRespawnEvent event) { + PlayerEvent.PLAYER_RESPAWN.invoker().respawn((ServerPlayer) event.getEntity(), event.isEndConquered()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(CommandEvent event) { + CommandPerformEvent performEvent = new CommandPerformEvent(event.getParseResults(), event.getException()); + if (CommandPerformEvent.EVENT.invoker().act(performEvent).isFalse()) { + event.setCanceled(true); + } + event.setParseResults(performEvent.getResults()); + event.setException(performEvent.getThrowable()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(PlayerTickEvent event) { + if (event.phase == Phase.START) { + TickEvent.PLAYER_PRE.invoker().tick(event.player); + } else if (event.phase == Phase.END) { + TickEvent.PLAYER_POST.invoker().tick(event.player); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ServerChatEvent event) { + class ChatComponentImpl implements ChatEvent.ChatComponent { + @Override + public Component get() { + return event.getMessage(); + } + + @Override + public void set(Component component) { + event.setMessage(component); + } + } + ChatEvent.DECORATE.invoker().decorate(event.getPlayer(), new ChatComponentImpl()); + } + + @SubscribeEvent(priority = EventPriority.LOWEST) + public static void eventAfter(ServerChatEvent event) { + EventResult process = ChatEvent.RECEIVED.invoker().received(event.getPlayer(), event.getMessage()); + if (process.isFalse()) + event.setCanceled(true); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventWorldEvent(LevelEvent.Load event) { + if (event.getLevel() instanceof ServerLevel) { + ServerLevel world = (ServerLevel) event.getLevel(); + LifecycleEvent.SERVER_LEVEL_LOAD.invoker().act(world); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventWorldEvent(LevelEvent.Unload event) { + if (event.getLevel() instanceof ServerLevel) { + ServerLevel world = (ServerLevel) event.getLevel(); + LifecycleEvent.SERVER_LEVEL_UNLOAD.invoker().act(world); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventWorldEvent(LevelEvent.Save event) { + if (event.getLevel() instanceof ServerLevel) { + ServerLevel world = (ServerLevel) event.getLevel(); + LifecycleEvent.SERVER_LEVEL_SAVE.invoker().act(world); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(LivingDeathEvent event) { + if (EntityEvent.LIVING_DEATH.invoker().die(event.getEntity(), event.getSource()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(AdvancementEvent.AdvancementEarnEvent event) { + if (event.getEntity() instanceof ServerPlayer) { + PlayerEvent.PLAYER_ADVANCEMENT.invoker().award((ServerPlayer) event.getEntity(), event.getAdvancement()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventPlayerEvent(Clone event) { + if (event.getOriginal() instanceof ServerPlayer && event.getEntity() instanceof ServerPlayer) { + PlayerEvent.PLAYER_CLONE.invoker().clone((ServerPlayer) event.getOriginal(), (ServerPlayer) event.getEntity(), !event.isWasDeath()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventExplosionEvent(Start event) { + if (ExplosionEvent.PRE.invoker().explode(event.getLevel(), event.getExplosion()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventExplosionEvent(Detonate event) { + ExplosionEvent.DETONATE.invoker().explode(event.getLevel(), event.getExplosion(), event.getAffectedEntities()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(LivingAttackEvent event) { + if (EntityEvent.LIVING_HURT.invoker().hurt(event.getEntity(), event.getSource(), event.getAmount()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(EntityJoinLevelEvent event) { + if (EntityEvent.ADD.invoker().add(event.getEntity(), event.getLevel()).isFalse()) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(FarmlandTrampleEvent event) { + if (event.getLevel() instanceof Level && InteractionEvent.FARMLAND_TRAMPLE.invoker().trample((Level) event.getLevel(), event.getPos(), event.getState(), event.getFallDistance(), event.getEntity()).value() != null) { + event.setCanceled(true); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(FillBucketEvent event) { + ItemStack oldItem = event.getEmptyBucket(); + CompoundEventResult result = PlayerEvent.FILL_BUCKET.invoker().fill(event.getEntity(), event.getLevel(), oldItem, event.getTarget()); + if (result.interruptsFurtherEvaluation()) { + event.setCanceled(true); + event.setFilledBucket(result.object()); + + if (result.value() != null) { + event.setResult(result.value() ? Event.Result.ALLOW : Event.Result.DENY); + } + } + } + + // TODO: Hook ourselves when mixin is available + // @SubscribeEvent(priority = EventPriority.HIGH) + // public static void event(EnteringChunk event) { + // EntityEvent.ENTER_SECTION.invoker().enterChunk(event.getEntity(), event.getNewChunkX(), event.getNewChunkZ(), event.getOldChunkX(), event.getOldChunkZ()); + // } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventLivingSpawnEvent(MobSpawnEvent.FinalizeSpawn event) { + EventResult result = EntityEvent.LIVING_CHECK_SPAWN.invoker().canSpawn(event.getEntity(), event.getLevel(), event.getX(), event.getY(), event.getZ(), event.getSpawnType(), event.getSpawner()); + if (result.interruptsFurtherEvaluation()) { + if (!result.isEmpty()) { + event.setSpawnCancelled(result.value()); + } + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(AnimalTameEvent event) { + EventResult result = EntityEvent.ANIMAL_TAME.invoker().tame(event.getAnimal(), event.getTamer()); + event.setCanceled(result.isFalse()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ItemCraftedEvent event) { + PlayerEvent.CRAFT_ITEM.invoker().craft(event.getEntity(), event.getCrafting(), event.getInventory()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ItemSmeltedEvent event) { + PlayerEvent.SMELT_ITEM.invoker().smelt(event.getEntity(), event.getSmelting()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(EntityItemPickupEvent event) { + // note: this event is weird, cancel is equivalent to denying the pickup, + // and setting the result to ALLOW will pick it up without adding it to the player's inventory + var result = PlayerEvent.PICKUP_ITEM_PRE.invoker().canPickup(event.getEntity(), event.getItem(), event.getItem().getItem()); + if (result.isFalse()) { + event.setCanceled(true); + } else if (result.isTrue()) { + event.setResult(Event.Result.ALLOW); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ItemPickupEvent event) { + PlayerEvent.PICKUP_ITEM_POST.invoker().pickup(event.getEntity(), event.getOriginalEntity(), event.getStack()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ItemTossEvent event) { + PlayerEvent.DROP_ITEM.invoker().drop(event.getPlayer(), event.getEntity()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventPlayerContainerEvent(PlayerContainerEvent.Open event) { + PlayerEvent.OPEN_MENU.invoker().open(event.getEntity(), event.getContainer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventPlayerContainerEvent(PlayerContainerEvent.Close event) { + PlayerEvent.CLOSE_MENU.invoker().close(event.getEntity(), event.getContainer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventPlayerInteractEvent(PlayerInteractEvent.RightClickItem event) { + CompoundEventResult result = InteractionEvent.RIGHT_CLICK_ITEM.invoker().click(event.getEntity(), event.getHand()); + if (result.isPresent()) { + event.setCanceled(true); + event.setCancellationResult(result.result().asMinecraft()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventPlayerInteractEvent(PlayerInteractEvent.RightClickBlock event) { + EventResult result = InteractionEvent.RIGHT_CLICK_BLOCK.invoker().click(event.getEntity(), event.getHand(), event.getPos(), event.getFace()); + if (result.isPresent()) { + event.setCanceled(true); + event.setCancellationResult(result.asMinecraft()); + event.setUseBlock(Event.Result.DENY); + event.setUseItem(Event.Result.DENY); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventPlayerInteractEvent(PlayerInteractEvent.EntityInteract event) { + EventResult result = InteractionEvent.INTERACT_ENTITY.invoker().interact(event.getEntity(), event.getTarget(), event.getHand()); + if (result.isPresent()) { + event.setCanceled(true); + event.setCancellationResult(result.asMinecraft()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventPlayerInteractEvent(PlayerInteractEvent.LeftClickBlock event) { + if (event.getAction() != PlayerInteractEvent.LeftClickBlock.Action.START) return; + EventResult result = InteractionEvent.LEFT_CLICK_BLOCK.invoker().click(event.getEntity(), event.getHand(), event.getPos(), event.getFace()); + if (result.isPresent()) { + event.setCanceled(true); + event.setUseBlock(result.value() ? Event.Result.ALLOW : Event.Result.DENY); + event.setUseItem(result.value() ? Event.Result.ALLOW : Event.Result.DENY); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(BreakEvent event) { + if (event.getPlayer() instanceof ServerPlayer && event.getLevel() instanceof Level) { + EventResult result = BlockEvent.BREAK.invoker().breakBlock((Level) event.getLevel(), event.getPos(), event.getState(), (ServerPlayer) event.getPlayer(), new IntValue() { + @Override + public int getAsInt() { + return event.getExpToDrop(); + } + + @Override + public void accept(int value) { + event.setExpToDrop(value); + } + }); + if (result.isFalse()) { + event.setCanceled(true); + } + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(EntityPlaceEvent event) { + if (event.getLevel() instanceof Level) { + EventResult result = BlockEvent.PLACE.invoker().placeBlock((Level) event.getLevel(), event.getPos(), event.getState(), event.getEntity()); + if (result.isFalse()) { + event.setCanceled(true); + } + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(ServerAboutToStartEvent event) { + LifecycleEvent.SERVER_BEFORE_START.invoker().stateChanged(event.getServer()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(PlayerChangedDimensionEvent event) { + if (event.getEntity() instanceof ServerPlayer) { + PlayerEvent.CHANGE_DIMENSION.invoker().change((ServerPlayer) event.getEntity(), event.getFrom(), event.getTo()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventChunkDataEvent(ChunkDataEvent.Save event) { + if (event.getLevel() instanceof ServerLevel) { + ChunkEvent.SAVE_DATA.invoker().save(event.getChunk(), (ServerLevel) event.getLevel(), event.getData()); + } + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void eventChunkDataEvent(ChunkDataEvent.Load event) { + LevelAccessor level = event.getChunk().getWorldForge(); + if (!(level instanceof ServerLevel) && event instanceof LevelEventAttachment) { + level = ((LevelEventAttachment) event).architectury$getAttachedLevel(); + } + ChunkEvent.LOAD_DATA.invoker().load(event.getChunk(), level instanceof ServerLevel ? (ServerLevel) level : null, event.getData()); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(LootTableLoadEvent event) { + LootEvent.MODIFY_LOOT_TABLE.invoker().modifyLootTable(ResourceKey.create(Registries.LOOT_TABLE, event.getName()), new LootTableModificationContextImpl(event.getTable()), true); + } + + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(AttackEntityEvent event) { + EventResult result = PlayerEvent.ATTACK_ENTITY.invoker().attack(event.getEntity(), event.getEntity().level(), event.getTarget(), event.getEntity().getUsedItemHand(), null); + if (result.isFalse()) { + event.setCanceled(true); + } + } + + public interface LevelEventAttachment { + LevelAccessor architectury$getAttachedLevel(); + + void architectury$attachLevel(LevelAccessor level); + } + + public static class ModBasedEventHandler { + @SubscribeEvent(priority = EventPriority.HIGH) + public static void event(FMLCommonSetupEvent event) { + LifecycleEvent.SETUP.invoker().run(); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImplServer.java b/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImplServer.java new file mode 100644 index 000000000..ac846a198 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/event/forge/EventHandlerImplServer.java @@ -0,0 +1,31 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.event.forge; + +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; + +@OnlyIn(Dist.DEDICATED_SERVER) +public class EventHandlerImplServer { + @OnlyIn(Dist.DEDICATED_SERVER) + public static class ModBasedEventHandler { + + } +} diff --git a/neoforge/src/main/java/dev/architectury/event/forge/LootTableModificationContextImpl.java b/neoforge/src/main/java/dev/architectury/event/forge/LootTableModificationContextImpl.java new file mode 100644 index 000000000..540b10b5d --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/event/forge/LootTableModificationContextImpl.java @@ -0,0 +1,72 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.event.forge; + +import dev.architectury.event.events.common.LootEvent; +import net.minecraft.world.level.storage.loot.LootPool; +import net.minecraft.world.level.storage.loot.LootTable; + +import java.lang.reflect.Field; +import java.util.List; + +final class LootTableModificationContextImpl implements LootEvent.LootTableModificationContext { + private final LootTable table; + private final List pools; + + LootTableModificationContextImpl(LootTable table) { + this.table = table; + + // This field has the type changed to List by Forge + // Since this is rather unsafe, we are making sure 100% we are getting it + List pools = null; + try { + Field field = LootTable.class.getDeclaredField("pools"); + field.setAccessible(true); + try { + pools = (List) field.get(table); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } catch (NoSuchFieldException ignored3) { + for (Field field : LootTable.class.getDeclaredFields()) { + if (field.getType().equals(List.class)) { + // This is probably the field + field.setAccessible(true); + try { + pools = (List) field.get(table); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + + if (pools == null) { + throw new RuntimeException("Unable to find pools field in LootTable!"); + } + } + + this.pools = pools; + } + + @Override + public void addPool(LootPool.Builder pool) { + this.pools.add(pool.build()); + } +} diff --git a/neoforge/src/main/java/dev/architectury/fluid/forge/FluidStackImpl.java b/neoforge/src/main/java/dev/architectury/fluid/forge/FluidStackImpl.java new file mode 100644 index 000000000..bb3a10699 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/fluid/forge/FluidStackImpl.java @@ -0,0 +1,160 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.fluid.forge; + +import com.mojang.serialization.Codec; +import dev.architectury.hooks.fluid.forge.FluidStackHooksForge; +import net.minecraft.core.Holder; +import net.minecraft.core.component.DataComponentMap; +import net.minecraft.core.component.DataComponentPatch; +import net.minecraft.core.component.DataComponentType; +import net.minecraft.core.component.PatchedDataComponentMap; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.level.material.Fluid; +import net.neoforged.neoforge.fluids.FluidStack; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +import static dev.architectury.utils.Amount.toInt; + +@ApiStatus.Internal +public enum FluidStackImpl implements dev.architectury.fluid.FluidStack.FluidStackAdapter { + INSTANCE; + + static { + dev.architectury.fluid.FluidStack.init(); + } + + public static Function toValue; + public static Function fromValue; + + public static dev.architectury.fluid.FluidStack.FluidStackAdapter adapt(Function toValue, Function fromValue) { + FluidStackImpl.toValue = toValue; + FluidStackImpl.fromValue = fromValue; + return (dev.architectury.fluid.FluidStack.FluidStackAdapter) (dev.architectury.fluid.FluidStack.FluidStackAdapter) INSTANCE; + } + + @Override + public FluidStack create(Supplier fluid, long amount, @Nullable DataComponentPatch patch) { + @SuppressWarnings("deprecation") + Holder holder = Objects.requireNonNull(fluid).get().builtInRegistryHolder(); + if (patch == null) { + return new FluidStack(holder, toInt(amount)); + } else { + return new FluidStack(holder, toInt(amount), patch); + } + } + + @Override + public Supplier getRawFluidSupplier(FluidStack object) { + return () -> object.getFluidHolder().value(); + } + + @Override + public Fluid getFluid(FluidStack object) { + return object.getFluid(); + } + + @Override + public long getAmount(FluidStack object) { + return object.getAmount(); + } + + @Override + public void setAmount(FluidStack object, long amount) { + object.setAmount(toInt(amount)); + } + + @Override + public DataComponentPatch getPatch(FluidStack value) { + return value.getComponentsPatch(); + } + + @Override + public PatchedDataComponentMap getComponents(FluidStack value) { + return value.getComponents(); + } + + @Override + public void applyComponents(FluidStack value, DataComponentPatch patch) { + value.applyComponents(patch); + } + + @Override + public void applyComponents(FluidStack value, DataComponentMap patch) { + value.applyComponents(patch); + } + + @Override + @Nullable + public D set(FluidStack value, DataComponentType type, @Nullable D component) { + return value.set(type, component); + } + + @Override + @Nullable + public D remove(FluidStack value, DataComponentType type) { + return value.remove(type); + } + + @Override + @Nullable + public D update(FluidStack value, DataComponentType type, D component, UnaryOperator updater) { + return value.update(type, component, updater); + } + + @Override + @Nullable + public D update(FluidStack value, DataComponentType type, D component, U updateContext, BiFunction updater) { + return value.update(type, component, updateContext, updater); + } + + + @Override + public FluidStack copy(FluidStack value) { + return value.copy(); + } + + @Override + public int hashCode(FluidStack value) { + var code = 1; + code = 31 * code + value.getFluid().hashCode(); + code = 31 * code + value.getAmount(); + code = 31 * code + value.getComponents().hashCode(); + return code; + } + + @Override + public Codec codec() { + return FluidStack.CODEC.xmap(FluidStackHooksForge::fromForge, FluidStackHooksForge::toForge); + } + + @Override + public StreamCodec streamCodec() { + return FluidStack.STREAM_CODEC.map(FluidStackHooksForge::fromForge, FluidStackHooksForge::toForge); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/client/screen/forge/ScreenHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/client/screen/forge/ScreenHooksImpl.java new file mode 100644 index 000000000..f7acc49c2 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/client/screen/forge/ScreenHooksImpl.java @@ -0,0 +1,50 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.client.screen.forge; + +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Renderable; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.narration.NarratableEntry; +import net.minecraft.client.gui.screens.Screen; + +import java.util.List; + +public class ScreenHooksImpl { + public static List getNarratables(Screen screen) { + return screen.narratables; + } + + public static List getRenderables(Screen screen) { + return screen.renderables; + } + + public static T addRenderableWidget(Screen screen, T widget) { + return screen.addRenderableWidget(widget); + } + + public static T addRenderableOnly(Screen screen, T listener) { + return screen.addRenderableOnly(listener); + } + + public static T addWidget(Screen screen, T listener) { + return screen.addWidget(listener); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/FluidBucketHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/FluidBucketHooksImpl.java new file mode 100644 index 000000000..b4eb4d3fc --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/FluidBucketHooksImpl.java @@ -0,0 +1,29 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.fluid.forge; + +import net.minecraft.world.item.BucketItem; +import net.minecraft.world.level.material.Fluid; + +public class FluidBucketHooksImpl { + public static Fluid getFluid(BucketItem item) { + return item.getFluid(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksForge.java b/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksForge.java new file mode 100644 index 000000000..bcdc3c43c --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksForge.java @@ -0,0 +1,36 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.fluid.forge; + +import dev.architectury.fluid.FluidStack; +import dev.architectury.fluid.forge.FluidStackImpl; + +public final class FluidStackHooksForge { + private FluidStackHooksForge() { + } + + public static FluidStack fromForge(net.neoforged.neoforge.fluids.FluidStack stack) { + return FluidStackImpl.fromValue.apply(stack); + } + + public static net.neoforged.neoforge.fluids.FluidStack toForge(FluidStack stack) { + return (net.neoforged.neoforge.fluids.FluidStack) FluidStackImpl.toValue.apply(stack); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksImpl.java new file mode 100644 index 000000000..cf6743670 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/FluidStackHooksImpl.java @@ -0,0 +1,190 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.fluid.forge; + +import com.mojang.logging.LogUtils; +import dev.architectury.fluid.FluidStack; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.nbt.Tag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.neoforge.client.extensions.common.IClientFluidTypeExtensions; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; + +import java.util.Optional; + +public class FluidStackHooksImpl { + private static final Logger LOGGER = LogUtils.getLogger(); + + public static Component getName(FluidStack stack) { + return stack.getFluid().getFluidType().getDescription(FluidStackHooksForge.toForge(stack)); + } + + public static String getTranslationKey(FluidStack stack) { + return stack.getFluid().getFluidType().getDescriptionId(FluidStackHooksForge.toForge(stack)); + } + + public static FluidStack read(RegistryFriendlyByteBuf buf) { + return FluidStack.STREAM_CODEC.decode(buf); + } + + public static void write(FluidStack stack, RegistryFriendlyByteBuf buf) { + FluidStack.STREAM_CODEC.encode(buf, stack); + } + + public static Optional read(HolderLookup.Provider provider, Tag tag) { + return FluidStack.CODEC.parse(provider.createSerializationContext(NbtOps.INSTANCE), tag) + .resultOrPartial(string -> LOGGER.error("Tried to load invalid fluid stack: '{}'", string)); + } + + public static FluidStack readOptional(HolderLookup.Provider provider, CompoundTag tag) { + return tag.isEmpty() ? FluidStack.empty() : read(provider, tag).orElse(FluidStack.empty()); + } + + public static Tag write(HolderLookup.Provider provider, FluidStack stack, Tag tag) { + return FluidStack.CODEC.encode(stack, provider.createSerializationContext(NbtOps.INSTANCE), tag).getOrThrow(IllegalStateException::new); + } + + public static long bucketAmount() { + return 1000; + } + + @OnlyIn(Dist.CLIENT) + @Nullable + public static TextureAtlasSprite getStillTexture(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, FluidState state) { + if (state.getType() == Fluids.EMPTY) return null; + ResourceLocation texture = IClientFluidTypeExtensions.of(state).getStillTexture(state, level, pos); + return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture); + } + + @OnlyIn(Dist.CLIENT) + @Nullable + public static TextureAtlasSprite getStillTexture(FluidStack stack) { + if (stack.getFluid() == Fluids.EMPTY) return null; + ResourceLocation texture = IClientFluidTypeExtensions.of(stack.getFluid()).getStillTexture(FluidStackHooksForge.toForge(stack)); + return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture); + } + + @OnlyIn(Dist.CLIENT) + @Nullable + public static TextureAtlasSprite getStillTexture(Fluid fluid) { + if (fluid == Fluids.EMPTY) return null; + ResourceLocation texture = IClientFluidTypeExtensions.of(fluid).getStillTexture(); + return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture); + } + + @OnlyIn(Dist.CLIENT) + @Nullable + public static TextureAtlasSprite getFlowingTexture(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, FluidState state) { + if (state.getType() == Fluids.EMPTY) return null; + ResourceLocation texture = IClientFluidTypeExtensions.of(state).getFlowingTexture(state, level, pos); + return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture); + } + + @OnlyIn(Dist.CLIENT) + @Nullable + public static TextureAtlasSprite getFlowingTexture(FluidStack stack) { + if (stack.getFluid() == Fluids.EMPTY) return null; + ResourceLocation texture = IClientFluidTypeExtensions.of(stack.getFluid()).getFlowingTexture(FluidStackHooksForge.toForge(stack)); + return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture); + } + + @OnlyIn(Dist.CLIENT) + @Nullable + public static TextureAtlasSprite getFlowingTexture(Fluid fluid) { + if (fluid == Fluids.EMPTY) return null; + ResourceLocation texture = IClientFluidTypeExtensions.of(fluid).getFlowingTexture(); + return Minecraft.getInstance().getTextureAtlas(TextureAtlas.LOCATION_BLOCKS).apply(texture); + } + + @OnlyIn(Dist.CLIENT) + public static int getColor(@Nullable BlockAndTintGetter level, @Nullable BlockPos pos, FluidState state) { + if (state.getType() == Fluids.EMPTY) return -1; + return IClientFluidTypeExtensions.of(state).getTintColor(state, level, pos); + } + + @OnlyIn(Dist.CLIENT) + public static int getColor(FluidStack stack) { + if (stack.getFluid() == Fluids.EMPTY) return -1; + return IClientFluidTypeExtensions.of(stack.getFluid()).getTintColor(FluidStackHooksForge.toForge(stack)); + } + + @OnlyIn(Dist.CLIENT) + public static int getColor(Fluid fluid) { + if (fluid == Fluids.EMPTY) return -1; + return IClientFluidTypeExtensions.of(fluid).getTintColor(); + } + + public static int getLuminosity(FluidStack fluid, @Nullable Level level, @Nullable BlockPos pos) { + return fluid.getFluid().getFluidType().getLightLevel(FluidStackHooksForge.toForge(fluid)); + } + + @Deprecated(forRemoval = true) + public static int getLuminosity(Fluid fluid, @Nullable Level level, @Nullable BlockPos pos) { + if (level != null && pos != null) { + var state = level.getFluidState(pos); + return fluid.getFluidType().getLightLevel(state, level, pos); + } + + return fluid.getFluidType().getLightLevel(); + } + + public static int getTemperature(FluidStack fluid, @Nullable Level level, @Nullable BlockPos pos) { + return fluid.getFluid().getFluidType().getTemperature(FluidStackHooksForge.toForge(fluid)); + } + + public static int getTemperature(Fluid fluid, @Nullable Level level, @Nullable BlockPos pos) { + if (level != null && pos != null) { + var state = level.getFluidState(pos); + return fluid.getFluidType().getTemperature(state, level, pos); + } + + return fluid.getFluidType().getTemperature(); + } + + public static int getViscosity(FluidStack fluid, @Nullable Level level, @Nullable BlockPos pos) { + return fluid.getFluid().getFluidType().getViscosity(FluidStackHooksForge.toForge(fluid)); + } + + public static int getViscosity(Fluid fluid, @Nullable Level level, @Nullable BlockPos pos) { + if (level != null && pos != null) { + var state = level.getFluidState(pos); + return fluid.getFluidType().getViscosity(state, level, pos); + } + + return fluid.getFluidType().getViscosity(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/LiquidBlockHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/LiquidBlockHooksImpl.java new file mode 100644 index 000000000..f5f7bb639 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/fluid/forge/LiquidBlockHooksImpl.java @@ -0,0 +1,29 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.fluid.forge; + +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.material.FlowingFluid; + +public class LiquidBlockHooksImpl { + public static FlowingFluid getFluid(LiquidBlock block) { + return block.getFluid(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/forge/DyeColorHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/forge/DyeColorHooksImpl.java new file mode 100644 index 000000000..c94a13be9 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/forge/DyeColorHooksImpl.java @@ -0,0 +1,29 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.forge; + +import net.minecraft.world.item.DyeColor; + +public class DyeColorHooksImpl { + public static int getColorValue(DyeColor dyeColor) { + var colors = dyeColor.getTextureDiffuseColors(); + return ((int) (colors[0] * 255.0F + 0.5D) & 255) << 16 | ((int) (colors[1] * 255.0F + 0.5D) & 255) << 8 | (int) (colors[2] * 255.0F + 0.5D); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/forge/PackRepositoryHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/forge/PackRepositoryHooksImpl.java new file mode 100644 index 000000000..4e87e14cd --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/forge/PackRepositoryHooksImpl.java @@ -0,0 +1,29 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.forge; + +import net.minecraft.server.packs.repository.PackRepository; +import net.minecraft.server.packs.repository.RepositorySource; + +public class PackRepositoryHooksImpl { + public static void addSource(PackRepository repository, RepositorySource source) { + repository.addPackFinder(source); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeClientHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeClientHooksImpl.java deleted file mode 100644 index 208d0a822..000000000 --- a/neoforge/src/main/java/dev/architectury/hooks/forgelike/forge/ForgeLikeClientHooksImpl.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of architectury. - * Copyright (C) 2020, 2021, 2022 architectury - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -package dev.architectury.hooks.forgelike.forge; - -import dev.architectury.event.events.client.ClientRawInputEvent; -import dev.architectury.event.events.client.ClientScreenInputEvent; -import net.minecraft.client.Minecraft; -import net.neoforged.neoforge.client.event.CustomizeGuiOverlayEvent; -import net.neoforged.neoforge.client.event.InputEvent; -import net.neoforged.neoforge.client.event.ScreenEvent; - -import java.util.List; - -public class ForgeLikeClientHooksImpl { - public static void preMouseScroll(ScreenEvent.MouseScrolled.Pre event) { - if (ClientScreenInputEvent.MOUSE_SCROLLED_PRE.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getScrollDeltaX(), event.getScrollDeltaY()).isFalse()) { - event.setCanceled(true); - } - } - - public static void postMouseScroll(ScreenEvent.MouseScrolled.Post event) { - ClientScreenInputEvent.MOUSE_SCROLLED_POST.invoker().mouseScrolled(Minecraft.getInstance(), event.getScreen(), event.getMouseX(), event.getMouseY(), event.getScrollDeltaX(), event.getScrollDeltaY()); - } - - public static void inputMouseScroll(InputEvent.MouseScrollingEvent event) { - if (ClientRawInputEvent.MOUSE_SCROLLED.invoker().mouseScrolled(Minecraft.getInstance(), event.getScrollDeltaX(), event.getScrollDeltaY()).isFalse()) { - event.setCanceled(true); - } - } - - public static List getLeft(CustomizeGuiOverlayEvent.DebugText event) { - return event.getLeft(); - } - - public static List getRight(CustomizeGuiOverlayEvent.DebugText event) { - return event.getRight(); - } -} diff --git a/neoforge/src/main/java/dev/architectury/hooks/item/food/forge/FoodPropertiesHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/item/food/forge/FoodPropertiesHooksImpl.java new file mode 100644 index 000000000..d10d5add5 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/item/food/forge/FoodPropertiesHooksImpl.java @@ -0,0 +1,33 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.item.food.forge; + +import net.minecraft.world.effect.MobEffectInstance; +import net.minecraft.world.food.FoodProperties; + +import java.util.function.Supplier; + +public class FoodPropertiesHooksImpl { + @SuppressWarnings("unchecked") + public static void effect(FoodProperties.Builder builder, + Supplier effectSupplier, float chance) { + builder.effect((Supplier) effectSupplier, chance); + } +} \ No newline at end of file diff --git a/neoforge/src/main/java/dev/architectury/hooks/item/forge/ItemStackHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/item/forge/ItemStackHooksImpl.java new file mode 100644 index 000000000..e6542bffd --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/item/forge/ItemStackHooksImpl.java @@ -0,0 +1,32 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.item.forge; + +import net.minecraft.world.item.ItemStack; + +public class ItemStackHooksImpl { + public static boolean hasCraftingRemainingItem(ItemStack stack) { + return stack.hasCraftingRemainingItem(); + } + + public static ItemStack getCraftingRemainingItem(ItemStack stack) { + return stack.getCraftingRemainingItem(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/item/tool/forge/HoeItemHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/item/tool/forge/HoeItemHooksImpl.java new file mode 100644 index 000000000..274c5ba62 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/item/tool/forge/HoeItemHooksImpl.java @@ -0,0 +1,47 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.item.tool.forge; + +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.common.ToolActions; +import net.neoforged.neoforge.event.level.BlockEvent; + +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; + +public class HoeItemHooksImpl { + public static void addTillable(Block input, Predicate predicate, Consumer action, Function function) { + NeoForge.EVENT_BUS.addListener(event -> { + UseOnContext context = event.getContext(); + if (ToolActions.HOE_TILL == event.getToolAction() && context.getItemInHand().canPerformAction(ToolActions.HOE_TILL) + && event.getState().is(input) && predicate.test(context)) { + if (!event.isSimulated()) { + action.accept(context); + } + + event.setFinalState(function.apply(context)); + } + }); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/level/biome/forge/BiomeHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/level/biome/forge/BiomeHooksImpl.java new file mode 100644 index 000000000..4ad90ad85 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/level/biome/forge/BiomeHooksImpl.java @@ -0,0 +1,28 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.level.biome.forge; + +import net.minecraft.world.level.biome.Biome; + +public class BiomeHooksImpl { + public static Biome.ClimateSettings extractClimateSettings(Biome biome) { + return biome.getModifiedClimateSettings(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/level/entity/forge/ItemEntityHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/level/entity/forge/ItemEntityHooksImpl.java new file mode 100644 index 000000000..945261624 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/level/entity/forge/ItemEntityHooksImpl.java @@ -0,0 +1,39 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.level.entity.forge; + +import dev.architectury.utils.value.IntValue; +import net.minecraft.world.entity.item.ItemEntity; + +public class ItemEntityHooksImpl { + public static IntValue lifespan(ItemEntity entity) { + return new IntValue() { + @Override + public void accept(int value) { + entity.lifespan = value; + } + + @Override + public int getAsInt() { + return entity.lifespan; + } + }; + } +} diff --git a/neoforge/src/main/java/dev/architectury/hooks/level/entity/forge/PlayerHooksImpl.java b/neoforge/src/main/java/dev/architectury/hooks/level/entity/forge/PlayerHooksImpl.java new file mode 100644 index 000000000..720e778fd --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/hooks/level/entity/forge/PlayerHooksImpl.java @@ -0,0 +1,29 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.hooks.level.entity.forge; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.player.Player; + +public class PlayerHooksImpl { + public static boolean isFake(Player playerEntity) { + return playerEntity instanceof ServerPlayer && playerEntity.getClass() != ServerPlayer.class; + } +} diff --git a/neoforge/src/main/java/dev/architectury/mixin/forge/MixinClientLevel.java b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinClientLevel.java new file mode 100644 index 000000000..d764ab9e8 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinClientLevel.java @@ -0,0 +1,59 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.forge; + +import dev.architectury.event.events.client.ClientTickEvent; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.core.Holder; +import net.minecraft.core.RegistryAccess; +import net.minecraft.resources.ResourceKey; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.WritableLevelData; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.function.Supplier; + +@Mixin(ClientLevel.class) +public abstract class MixinClientLevel extends Level { + protected MixinClientLevel(WritableLevelData arg, ResourceKey arg2, RegistryAccess arg3, Holder arg4, Supplier supplier, boolean bl, boolean bl2, long l, int i) { + super(arg, arg2, arg3, arg4, supplier, bl, bl2, l, i); + } + + @Inject(method = "tickEntities", at = @At("HEAD")) + private void tickEntities(CallbackInfo ci) { + ProfilerFiller profiler = getProfiler(); + profiler.push("architecturyClientLevelPreTick"); + ClientTickEvent.CLIENT_LEVEL_PRE.invoker().tick((ClientLevel) (Object) this); + profiler.pop(); + } + + @Inject(method = "tickEntities", at = @At("RETURN")) + private void tickEntitiesPost(CallbackInfo ci) { + ProfilerFiller profiler = getProfiler(); + profiler.push("architecturyClientLevelPostTick"); + ClientTickEvent.CLIENT_LEVEL_POST.invoker().tick((ClientLevel) (Object) this); + profiler.pop(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/mixin/forge/MixinFallingBlockEntity.java b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinFallingBlockEntity.java new file mode 100644 index 000000000..99610e6f8 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinFallingBlockEntity.java @@ -0,0 +1,52 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.forge; + +import dev.architectury.event.events.common.BlockEvent; +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.item.FallingBlockEntity; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(FallingBlockEntity.class) +public abstract class MixinFallingBlockEntity extends Entity { + public MixinFallingBlockEntity(EntityType entityType, Level level) { + super(entityType, level); + } + + @Shadow + private BlockState blockState; + + @Inject(method = "tick", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/level/block/Fallable;onLand(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/entity/item/FallingBlockEntity;)V"), + locals = LocalCapture.CAPTURE_FAILHARD) + public void handleLand(CallbackInfo ci, Block block, BlockPos blockPos2, boolean bl, boolean bl2, double d, BlockState blockState) { + BlockEvent.FALLING_LAND.invoker().onLand(this.level(), blockPos2, this.blockState, blockState, (FallingBlockEntity) (Object) this); + } +} diff --git a/neoforge/src/main/java/dev/architectury/mixin/forge/MixinItemExtension.java b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinItemExtension.java new file mode 100644 index 000000000..687858791 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinItemExtension.java @@ -0,0 +1,43 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.forge; + +import dev.architectury.extensions.ItemExtension; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.neoforged.neoforge.common.extensions.IItemExtension; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ItemExtension.class) +public interface MixinItemExtension extends IItemExtension { + @Override + default void onArmorTick(ItemStack stack, Level world, Player player) { + ((ItemExtension) this).tickArmor(stack, player); + } + + @Nullable + @Override + default EquipmentSlot getEquipmentSlot(ItemStack stack) { + return ((ItemExtension) this).getCustomEquipmentSlot(stack); + } +} diff --git a/neoforge/src/main/java/dev/architectury/mixin/forge/MixinLevelEvent.java b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinLevelEvent.java new file mode 100644 index 000000000..919f2b933 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinLevelEvent.java @@ -0,0 +1,44 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.forge; + +import dev.architectury.event.forge.EventHandlerImplCommon; +import net.minecraft.world.level.LevelAccessor; +import net.neoforged.neoforge.event.level.LevelEvent; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +import java.lang.ref.WeakReference; + +@Mixin(LevelEvent.class) +public class MixinLevelEvent implements EventHandlerImplCommon.LevelEventAttachment { + @Unique + private WeakReference level; + + @Override + public LevelAccessor architectury$getAttachedLevel() { + return level == null ? null : level.get(); + } + + @Override + public void architectury$attachLevel(LevelAccessor level) { + this.level = new WeakReference<>(level); + } +} diff --git a/neoforge/src/main/java/dev/architectury/mixin/forge/MixinMinecraft.java b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinMinecraft.java new file mode 100644 index 000000000..841046a2b --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/mixin/forge/MixinMinecraft.java @@ -0,0 +1,42 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.forge; + +import dev.architectury.event.events.client.ClientLifecycleEvent; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +// adopted from fabric +@Mixin(Minecraft.class) +public abstract class MixinMinecraft { + @Inject(at = @At(value = "INVOKE", target = "Ljava/lang/Runtime;getRuntime()Ljava/lang/Runtime;", ordinal = 0), method = "run") + private void onStart(CallbackInfo ci) { + ClientLifecycleEvent.CLIENT_STARTED.invoker().stateChanged((Minecraft) (Object) this); + } + + // using {1} rather than Log4j directly for 1.18.2+ support + @Inject(at = @At(value = "INVOKE", target = "{1}(Ljava/lang/String;)V" /* Logger.info */, shift = At.Shift.AFTER, remap = false), method = "destroy") + private void onStopping(CallbackInfo ci) { + ClientLifecycleEvent.CLIENT_STOPPING.invoker().stateChanged((Minecraft) (Object) this); + } +} diff --git a/neoforge/src/main/java/dev/architectury/mixin/forge/client/MixinCommandSourceStack.java b/neoforge/src/main/java/dev/architectury/mixin/forge/client/MixinCommandSourceStack.java new file mode 100644 index 000000000..364cf5b60 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/mixin/forge/client/MixinCommandSourceStack.java @@ -0,0 +1,69 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.mixin.forge.client; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import dev.architectury.event.events.client.ClientCommandRegistrationEvent; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.world.phys.Vec2; +import net.minecraft.world.phys.Vec3; +import org.spongepowered.asm.mixin.Mixin; + +import java.util.function.Supplier; + +@Mixin(CommandSourceStack.class) +public abstract class MixinCommandSourceStack implements ClientCommandRegistrationEvent.ClientCommandSourceStack { + @Override + public void arch$sendSuccess(Supplier message, boolean broadcastToAdmins) { + ((CommandSourceStack) (Object) this).sendSuccess(message, broadcastToAdmins); + } + + @Override + public void arch$sendFailure(Component message) { + ((CommandSourceStack) (Object) this).sendFailure(message); + } + + @Override + public LocalPlayer arch$getPlayer() { + try { + return (LocalPlayer) ((CommandSourceStack) (Object) this).getEntityOrException(); + } catch (CommandSyntaxException e) { + throw new RuntimeException(e); + } + } + + @Override + public Vec3 arch$getPosition() { + return ((CommandSourceStack) (Object) this).getPosition(); + } + + @Override + public Vec2 arch$getRotation() { + return ((CommandSourceStack) (Object) this).getRotation(); + } + + @Override + public ClientLevel arch$getLevel() { + return (ClientLevel) ((CommandSourceStack) (Object) this).getUnsidedLevel(); + } +} diff --git a/neoforge/src/main/java/dev/architectury/neoforge/ArchitecturyNeoForge.java b/neoforge/src/main/java/dev/architectury/neoforge/ArchitecturyNeoForge.java index 71cbd4b2f..ff7fa7bd1 100644 --- a/neoforge/src/main/java/dev/architectury/neoforge/ArchitecturyNeoForge.java +++ b/neoforge/src/main/java/dev/architectury/neoforge/ArchitecturyNeoForge.java @@ -34,5 +34,6 @@ public ArchitecturyNeoForge() { BiomeModificationsImpl.init(); EnvExecutor.runInEnv(Env.CLIENT, () -> SpawnEntityPacket.Client::register); + EnvExecutor.runInEnv(Env.SERVER, () -> SpawnEntityPacket::register); } } diff --git a/neoforge/src/main/java/dev/architectury/networking/forge/BufCustomPacketPayload.java b/neoforge/src/main/java/dev/architectury/networking/forge/BufCustomPacketPayload.java index 7ec020115..f2d43cac6 100644 --- a/neoforge/src/main/java/dev/architectury/networking/forge/BufCustomPacketPayload.java +++ b/neoforge/src/main/java/dev/architectury/networking/forge/BufCustomPacketPayload.java @@ -19,27 +19,26 @@ package dev.architectury.networking.forge; +import io.netty.buffer.ByteBuf; import net.minecraft.network.FriendlyByteBuf; +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.minecraft.resources.ResourceLocation; /** * Wraps a {@link FriendlyByteBuf} because NeoForge doesn't easily let us use the buf directly. */ -public record BufCustomPacketPayload(ResourceLocation type, byte[] payload) implements CustomPacketPayload { - public BufCustomPacketPayload(FriendlyByteBuf buf) { - this(buf.readResourceLocation(), buf.readByteArray()); +public record BufCustomPacketPayload(Type type, byte[] payload) implements CustomPacketPayload { + public BufCustomPacketPayload(Type type, RegistryFriendlyByteBuf buf) { + this(type, buf.readByteArray()); } - @Override - public void write(FriendlyByteBuf buf) { - buf.writeResourceLocation(type); + public void write(RegistryFriendlyByteBuf buf) { buf.writeByteArray(payload); } - @SuppressWarnings("NullableProblems") - @Override - public ResourceLocation id() { - return NetworkManagerImpl.CHANNEL_ID; + public static StreamCodec streamCodec(Type type) { + return ByteBufCodecs.BYTE_ARRAY.map(bytes -> new BufCustomPacketPayload(type, bytes), BufCustomPacketPayload::payload); } } diff --git a/neoforge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java b/neoforge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java index e5532f4f1..339742137 100644 --- a/neoforge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java +++ b/neoforge/src/main/java/dev/architectury/networking/forge/ClientNetworkingManager.java @@ -19,8 +19,10 @@ package dev.architectury.networking.forge; +import dev.architectury.impl.NetworkAggregator; import dev.architectury.networking.NetworkManager; import net.minecraft.client.Minecraft; +import net.minecraft.core.RegistryAccess; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Player; import net.neoforged.api.distmarker.Dist; @@ -28,18 +30,16 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.neoforge.client.event.ClientPlayerNetworkEvent; import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.network.handling.IPlayPayloadHandler; import java.util.Collections; import java.util.Set; @OnlyIn(Dist.CLIENT) public class ClientNetworkingManager { - public static IPlayPayloadHandler initClient() { - var handler = NetworkManagerImpl.createPacketHandler(NetworkManager.Side.S2C, NetworkManagerImpl.S2C_TRANSFORMERS); + public static void initClient() { NeoForge.EVENT_BUS.register(ClientNetworkingManager.class); - NetworkManagerImpl.registerS2CReceiver(NetworkManagerImpl.SYNC_IDS, Collections.emptyList(), (buffer, context) -> { + NetworkManager.registerReceiver(NetworkManager.Side.S2C, NetworkManagerImpl.SYNC_IDS_S2C, Collections.emptyList(), (buffer, context) -> { Set receivables = NetworkManagerImpl.serverReceivables; int size = buffer.readInt(); receivables.clear(); @@ -47,17 +47,23 @@ public static IPlayPayloadHandler initClient() { receivables.add(buffer.readResourceLocation()); } context.queue(() -> { - NetworkManager.sendToServer(NetworkManagerImpl.SYNC_IDS, NetworkManagerImpl.sendSyncPacket(NetworkManagerImpl.C2S)); + NetworkManager.sendToServer(NetworkManagerImpl.SYNC_IDS_C2S, NetworkManagerImpl.sendSyncPacket(NetworkAggregator.C2S_RECEIVER, context.registryAccess())); }); }); - - return handler; } public static Player getClientPlayer() { return Minecraft.getInstance().player; } + public static RegistryAccess getClientRegistryAccess() { + if (Minecraft.getInstance().level != null) { + return Minecraft.getInstance().level.registryAccess(); + } + + return Minecraft.getInstance().getConnection().registryAccess(); + } + @SubscribeEvent public static void loggedOut(ClientPlayerNetworkEvent.LoggingOut event) { NetworkManagerImpl.serverReceivables.clear(); diff --git a/neoforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java b/neoforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java index 8231ce3e9..90016350a 100644 --- a/neoforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java +++ b/neoforge/src/main/java/dev/architectury/networking/forge/NetworkManagerImpl.java @@ -22,152 +22,133 @@ import com.google.common.collect.*; import com.mojang.logging.LogUtils; +import dev.architectury.impl.NetworkAggregator; import dev.architectury.networking.NetworkManager; import dev.architectury.networking.NetworkManager.NetworkReceiver; import dev.architectury.networking.SpawnEntityPacket; -import dev.architectury.networking.transformers.PacketSink; -import dev.architectury.networking.transformers.PacketTransformer; -import dev.architectury.platform.hooks.forge.EventBusesHooksImpl; +import dev.architectury.platform.hooks.EventBusesHooks; import dev.architectury.utils.ArchitecturyConstants; import dev.architectury.utils.Env; -import io.netty.buffer.ByteBufUtil; +import dev.architectury.utils.EnvExecutor; import io.netty.buffer.Unpooled; -import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.core.RegistryAccess; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.PacketFlow; import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket; import net.minecraft.network.protocol.common.ServerboundCustomPayloadPacket; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.neoforged.api.distmarker.Dist; -import net.neoforged.api.distmarker.OnlyIn; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.DistExecutor; -import net.neoforged.fml.LogicalSide; import net.neoforged.fml.common.Mod; import net.neoforged.neoforge.event.entity.player.PlayerEvent; import net.neoforged.neoforge.network.event.RegisterPayloadHandlerEvent; -import net.neoforged.neoforge.network.handling.IPlayPayloadHandler; -import net.neoforged.neoforge.network.registration.IPayloadRegistrar; -import org.jetbrains.annotations.Nullable; +import net.neoforged.neoforge.network.handling.ISynchronizedWorkHandler; import org.slf4j.Logger; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import static dev.architectury.networking.forge.ClientNetworkingManager.getClientPlayer; +import static dev.architectury.networking.forge.ClientNetworkingManager.getClientRegistryAccess; @Mod.EventBusSubscriber(modid = ArchitecturyConstants.MOD_ID) public class NetworkManagerImpl { - public static void registerReceiver(NetworkManager.Side side, ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { - Objects.requireNonNull(id, "Cannot register receiver with a null ID!"); - packetTransformers = Objects.requireNonNullElse(packetTransformers, List.of()); - Objects.requireNonNull(receiver, "Cannot register a null receiver!"); - if (side == NetworkManager.Side.C2S) { - registerC2SReceiver(id, packetTransformers, receiver); - } else if (side == NetworkManager.Side.S2C) { - registerS2CReceiver(id, packetTransformers, receiver); - } - } - - public static Packet toPacket(NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buffer) { - try { - return side == NetworkManager.Side.C2S ? new ServerboundCustomPayloadPacket(new BufCustomPacketPayload(id, ByteBufUtil.getBytes(buffer))) : new ClientboundCustomPayloadPacket(new BufCustomPacketPayload(id, ByteBufUtil.getBytes(buffer))); - } finally { - buffer.release(); - } - } - - public static void collectPackets(PacketSink sink, NetworkManager.Side side, ResourceLocation id, FriendlyByteBuf buf) { - PacketTransformer transformer = side == NetworkManager.Side.C2S ? C2S_TRANSFORMERS.get(id) : S2C_TRANSFORMERS.get(id); - if (transformer != null) { - transformer.outbound(side, id, buf, (side1, id1, buf1) -> { - sink.accept(toPacket(side1, id1, buf1)); - }); - } else { - sink.accept(toPacket(side, id, buf)); - } - } - private static final Logger LOGGER = LogUtils.getLogger(); - static final ResourceLocation CHANNEL_ID = new ResourceLocation("architectury:network"); - static final ResourceLocation SYNC_IDS = new ResourceLocation("architectury:sync_ids"); - static final Map S2C = Maps.newHashMap(); - static final Map C2S = Maps.newHashMap(); - static final Map S2C_TRANSFORMERS = Maps.newHashMap(); - static final Map C2S_TRANSFORMERS = Maps.newHashMap(); + static final ResourceLocation SYNC_IDS_S2C = new ResourceLocation("architectury:sync_ids_s2c"); + static final ResourceLocation SYNC_IDS_C2S = new ResourceLocation("architectury:sync_ids_c2s"); static final Set serverReceivables = Sets.newHashSet(); private static final Multimap clientReceivables = Multimaps.newMultimap(Maps.newHashMap(), Sets::newHashSet); static { - EventBusesHooksImpl.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { - bus.addListener(NetworkManagerImpl::registerPackets); + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ClientNetworkingManager::initClient); + NetworkManager.registerReceiver(NetworkManager.Side.C2S, SYNC_IDS_C2S, Collections.emptyList(), (buffer, context) -> { + Set receivables = (Set) clientReceivables.get(context.getPlayer()); + int size = buffer.readInt(); + receivables.clear(); + for (int i = 0; i < size; i++) { + receivables.add(buffer.readResourceLocation()); + } }); + EnvExecutor.runInEnv(Env.SERVER, () -> () -> NetworkManager.registerS2CPayloadType(SYNC_IDS_S2C)); } - static IPlayPayloadHandler createPacketHandler(NetworkManager.Side direction, Map map) { - return (arg, context) -> { - NetworkManager.Side side = side(context.flow()); - if (side != direction) return; - ResourceLocation type = arg.type(); - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(arg.payload())); - PacketTransformer transformer = map.get(type); + public static NetworkAggregator.Adaptor getAdaptor() { + return new NetworkAggregator.Adaptor() { + @Override + public void registerC2S(CustomPacketPayload.Type type, StreamCodec codec, NetworkReceiver receiver) { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(event -> { + event.registrar(type.id().getNamespace()).optional().play(type, codec, (arg, context) -> { + receiver.receive(arg, context(context.player().orElse(null), context.workHandler(), false)); + }); + }); + }); + } - try { - if (transformer != null) { - NetworkManager.PacketContext packetContext = new NetworkManager.PacketContext() { - @Override - public Player getPlayer() { - return getEnvironment() == Env.CLIENT ? getClientPlayer() : context.player().orElse(null); - } - - @Override - public void queue(Runnable runnable) { - context.workHandler().submitAsync(runnable); - } - - @Override - public Env getEnvironment() { - return context.flow().getReceptionSide() == LogicalSide.CLIENT ? Env.CLIENT : Env.SERVER; - } - - @SuppressWarnings("removal") - private Player getClientPlayer() { - return DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> ClientNetworkingManager::getClientPlayer); - } - }; - - transformer.inbound(side, type, buf, packetContext, (side1, id1, buf1) -> { - NetworkReceiver networkReceiver = side == NetworkManager.Side.C2S ? C2S.get(id1) : S2C.get(id1); - if (networkReceiver == null) { - throw new IllegalArgumentException("Network Receiver not found! " + id1); - } - networkReceiver.receive(buf1, packetContext); + @Override + public void registerS2C(CustomPacketPayload.Type type, StreamCodec codec, NetworkReceiver receiver) { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(event -> { + event.registrar(type.id().getNamespace()).optional().play(type, codec, (arg, context) -> { + receiver.receive(arg, context(context.player().orElse(null), context.workHandler(), true)); + }); }); - } else { - LOGGER.error("Unknown message ID: " + type); - } - } finally { - buf.release(); + }); + } + + @Override + public Packet toC2SPacket(T payload) { + return new ServerboundCustomPayloadPacket(payload); + } + + @Override + public Packet toS2CPacket(T payload) { + return new ClientboundCustomPayloadPacket(payload); + } + + @Override + public void registerS2CType(CustomPacketPayload.Type type, StreamCodec codec) { + registerS2C(type, codec, (payload, context) -> { + }); + } + + public NetworkManager.PacketContext context(Player player, ISynchronizedWorkHandler taskQueue, boolean client) { + return new NetworkManager.PacketContext() { + @Override + public Player getPlayer() { + return getEnvironment() == Env.CLIENT ? getClientPlayer() : player; + } + + @Override + public void queue(Runnable runnable) { + taskQueue.submitAsync(runnable); + } + + @Override + public Env getEnvironment() { + return client ? Env.CLIENT : Env.SERVER; + } + + @Override + public RegistryAccess registryAccess() { + return getEnvironment() == Env.CLIENT ? getClientRegistryAccess() : player.registryAccess(); + } + }; } }; } - @OnlyIn(Dist.CLIENT) - public static void registerS2CReceiver(ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { - LOGGER.info("Registering S2C receiver with id {}", id); - S2C.put(id, receiver); - PacketTransformer transformer = PacketTransformer.concat(packetTransformers); - S2C_TRANSFORMERS.put(id, transformer); - } - - public static void registerC2SReceiver(ResourceLocation id, List packetTransformers, NetworkReceiver receiver) { - LOGGER.info("Registering C2S receiver with id {}", id); - C2S.put(id, receiver); - PacketTransformer transformer = PacketTransformer.concat(packetTransformers); - C2S_TRANSFORMERS.put(id, transformer); - } - public static boolean canServerReceive(ResourceLocation id) { return serverReceivables.contains(id); } @@ -180,9 +161,9 @@ public static Packet createAddEntityPacket(Entity enti return SpawnEntityPacket.create(entity); } - static FriendlyByteBuf sendSyncPacket(Map map) { + static RegistryFriendlyByteBuf sendSyncPacket(Map map, RegistryAccess access) { List availableIds = Lists.newArrayList(map.keySet()); - FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.buffer()); + RegistryFriendlyByteBuf packetBuffer = new RegistryFriendlyByteBuf(Unpooled.buffer(), access); packetBuffer.writeInt(availableIds.size()); for (ResourceLocation availableId : availableIds) { packetBuffer.writeResourceLocation(availableId); @@ -192,7 +173,7 @@ static FriendlyByteBuf sendSyncPacket(Map map @SubscribeEvent public static void loggedIn(PlayerEvent.PlayerLoggedInEvent event) { - NetworkManager.sendToPlayer((ServerPlayer) event.getEntity(), SYNC_IDS, sendSyncPacket(C2S)); + NetworkManager.sendToPlayer((ServerPlayer) event.getEntity(), SYNC_IDS_S2C, sendSyncPacket(NetworkAggregator.C2S_RECEIVER, event.getEntity().registryAccess())); } @SubscribeEvent @@ -200,29 +181,6 @@ public static void loggedOut(PlayerEvent.PlayerLoggedOutEvent event) { clientReceivables.removeAll(event.getEntity()); } - /** - * Needs to be on the mod bus for some reason... - */ - public static void registerPackets(RegisterPayloadHandlerEvent event) { - //noinspection removal - @Nullable - IPlayPayloadHandler s2c = DistExecutor.unsafeCallWhenOn(Dist.CLIENT, () -> ClientNetworkingManager::initClient); - - IPayloadRegistrar registrar = event.registrar(ArchitecturyConstants.MOD_ID).optional(); - registrar.play(CHANNEL_ID, BufCustomPacketPayload::new, builder -> { - builder.server(createPacketHandler(NetworkManager.Side.C2S, C2S_TRANSFORMERS)).client(Objects.requireNonNullElseGet(s2c, IPlayPayloadHandler::noop)); - }); - - registerC2SReceiver(SYNC_IDS, Collections.emptyList(), (buffer, context) -> { - Set receivables = (Set) clientReceivables.get(context.getPlayer()); - int size = buffer.readInt(); - receivables.clear(); - for (int i = 0; i < size; i++) { - receivables.add(buffer.readResourceLocation()); - } - }); - } - static NetworkManager.Side side(PacketFlow flow) { return flow.isClientbound() ? NetworkManager.Side.S2C : flow.isServerbound() ? NetworkManager.Side.C2S : null; } diff --git a/neoforge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java b/neoforge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java index 214b7ab31..61c89ad3b 100644 --- a/neoforge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java +++ b/neoforge/src/main/java/dev/architectury/platform/forge/PlatformImpl.java @@ -28,7 +28,7 @@ import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.FMLPaths; import net.neoforged.fml.loading.moddiscovery.ModFileInfo; -import net.neoforged.neoforge.client.ConfigScreenHandler; +import net.neoforged.neoforge.client.gui.IConfigScreenFactory; import net.neoforged.neoforgespi.language.IModFileInfo; import net.neoforged.neoforgespi.language.IModInfo; import org.jetbrains.annotations.NotNull; @@ -178,8 +178,7 @@ public Optional getIssueTracker() { @Override public void registerConfigurationScreen(ConfigurationScreenProvider configurationScreenProvider) { - container.registerExtensionPoint(ConfigScreenHandler.ConfigScreenFactory.class, () -> - new ConfigScreenHandler.ConfigScreenFactory((minecraft, screen) -> configurationScreenProvider.provide(screen))); + container.registerExtensionPoint(IConfigScreenFactory.class, (minecraft, screen) -> configurationScreenProvider.provide(screen)); } } } diff --git a/neoforge/src/main/java/dev/architectury/platform/hooks/forge/EventBusesHooksImpl.java b/neoforge/src/main/java/dev/architectury/platform/hooks/EventBusesHooks.java similarity index 92% rename from neoforge/src/main/java/dev/architectury/platform/hooks/forge/EventBusesHooksImpl.java rename to neoforge/src/main/java/dev/architectury/platform/hooks/EventBusesHooks.java index 00b6b1290..2bdae12de 100644 --- a/neoforge/src/main/java/dev/architectury/platform/hooks/forge/EventBusesHooksImpl.java +++ b/neoforge/src/main/java/dev/architectury/platform/hooks/EventBusesHooks.java @@ -17,7 +17,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package dev.architectury.platform.hooks.forge; +package dev.architectury.platform.hooks; import net.neoforged.bus.api.IEventBus; import net.neoforged.fml.ModContainer; @@ -26,7 +26,10 @@ import java.util.Optional; import java.util.function.Consumer; -public class EventBusesHooksImpl { +public final class EventBusesHooks { + private EventBusesHooks() { + } + public static void whenAvailable(String modId, Consumer busConsumer) { IEventBus bus = getModEventBus(modId).orElseThrow(() -> new IllegalStateException("Mod '" + modId + "' is not available!")); busConsumer.accept(bus); diff --git a/neoforge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java b/neoforge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java new file mode 100644 index 000000000..d26e3a2fd --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/plugin/forge/ArchitecturyMixinPlugin.java @@ -0,0 +1,64 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.plugin.forge; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class ArchitecturyMixinPlugin implements IMixinConfigPlugin { + @Override + public void onLoad(String mixinPackage) { + + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + + } + + @Override + public List getMixins() { + return List.of("neoforge.MixinChunkSerializer"); + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/client/gui/forge/ClientTooltipComponentRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/client/gui/forge/ClientTooltipComponentRegistryImpl.java new file mode 100644 index 000000000..6b9896189 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/client/gui/forge/ClientTooltipComponentRegistryImpl.java @@ -0,0 +1,69 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.gui.forge; + +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; +import net.minecraft.world.inventory.tooltip.TooltipComponent; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.bus.api.EventPriority; +import net.neoforged.neoforge.client.event.RegisterClientTooltipComponentFactoriesEvent; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; + +@OnlyIn(Dist.CLIENT) +@ApiStatus.Internal +public class ClientTooltipComponentRegistryImpl { + @Nullable + private static List> entries = new ArrayList<>(); + + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(EventPriority.HIGH, event -> { + if (entries != null) { + for (Entry entry : entries) { + Entry casted = (Entry) entry; + event.register(casted.clazz(), casted.factory()); + } + + entries = null; + } + }); + }); + } + + public static void register(Class clazz, Function factory) { + if (entries == null) { + throw new IllegalStateException("Cannot register ClientTooltipComponent factory when factories are already aggregated!"); + } + entries.add(new Entry<>(clazz, factory)); + } + + public record Entry( + Class clazz, Function factory + ) { + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/client/keymappings/forge/KeyMappingRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/client/keymappings/forge/KeyMappingRegistryImpl.java new file mode 100644 index 000000000..c29010be6 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/client/keymappings/forge/KeyMappingRegistryImpl.java @@ -0,0 +1,60 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.keymappings.forge; + +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.client.KeyMapping; +import net.minecraft.client.Minecraft; +import net.minecraft.client.Options; +import net.neoforged.neoforge.client.event.RegisterKeyMappingsEvent; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +public class KeyMappingRegistryImpl { + private static final Logger LOGGER = LogManager.getLogger(KeyMappingRegistryImpl.class); + private static final List MAPPINGS = new ArrayList<>(); + private static boolean eventCalled = false; + + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(KeyMappingRegistryImpl::event); + }); + } + + public static void register(KeyMapping mapping) { + if (eventCalled) { + Options options = Minecraft.getInstance().options; + options.keyMappings = ArrayUtils.add(options.keyMappings, mapping); + LOGGER.warn("Key mapping %s registered after event".formatted(mapping.getName()), new RuntimeException()); + } else { + MAPPINGS.add(mapping); + } + } + + public static void event(RegisterKeyMappingsEvent event) { + MAPPINGS.forEach(event::register); + eventCalled = true; + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityModelLayerRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityModelLayerRegistryImpl.java new file mode 100644 index 000000000..c75c112c8 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityModelLayerRegistryImpl.java @@ -0,0 +1,52 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.level.entity.forge; + +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.client.model.geom.ModelLayerLocation; +import net.minecraft.client.model.geom.builders.LayerDefinition; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.client.event.EntityRenderersEvent; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class EntityModelLayerRegistryImpl { + private static final Map> DEFINITIONS = new ConcurrentHashMap<>(); + + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.register(EntityModelLayerRegistryImpl.class); + }); + } + + public static void register(ModelLayerLocation location, Supplier definition) { + DEFINITIONS.put(location, definition); + } + + @SubscribeEvent + public static void event(EntityRenderersEvent.RegisterLayerDefinitions event) { + for (Map.Entry> entry : DEFINITIONS.entrySet()) { + event.registerLayerDefinition(entry.getKey(), entry.getValue()); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityRendererRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityRendererRegistryImpl.java new file mode 100644 index 000000000..1cead8a6b --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/client/level/entity/forge/EntityRendererRegistryImpl.java @@ -0,0 +1,53 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.level.entity.forge; + +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.client.renderer.entity.EntityRendererProvider; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.client.event.EntityRenderersEvent; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class EntityRendererRegistryImpl { + private static final Map>, EntityRendererProvider> RENDERERS = new ConcurrentHashMap<>(); + + public static void register(Supplier> type, EntityRendererProvider factory) { + RENDERERS.put((Supplier>) (Supplier>) type, factory); + } + + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.register(EntityRendererRegistryImpl.class); + }); + } + + @SubscribeEvent + public static void event(EntityRenderersEvent.RegisterRenderers event) { + for (Map.Entry>, EntityRendererProvider> entry : RENDERERS.entrySet()) { + event.registerEntityRenderer(entry.getKey().get(), (EntityRendererProvider) entry.getValue()); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/client/particle/forge/ParticleProviderRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/client/particle/forge/ParticleProviderRegistryImpl.java new file mode 100644 index 000000000..d3bfd13f3 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/client/particle/forge/ParticleProviderRegistryImpl.java @@ -0,0 +1,155 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.particle.forge; + +import com.mojang.logging.LogUtils; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.registry.client.particle.ParticleProviderRegistry; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.ParticleEngine; +import net.minecraft.client.particle.ParticleProvider; +import net.minecraft.client.particle.SpriteSet; +import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.core.particles.ParticleType; +import net.minecraft.util.RandomSource; +import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +public class ParticleProviderRegistryImpl { + public static final Logger LOGGER = LogUtils.getLogger(); + + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(ParticleProviderRegistryImpl::onParticleFactoryRegister); + }); + } + + private static final class ExtendedSpriteSetImpl implements ParticleProviderRegistry.ExtendedSpriteSet { + private final ParticleEngine engine; + private final SpriteSet delegate; + + private ExtendedSpriteSetImpl(ParticleEngine engine, SpriteSet delegate) { + this.engine = engine; + this.delegate = delegate; + } + + @Override + public TextureAtlas getAtlas() { + return engine.textureAtlas; + } + + @Override + public List getSprites() { + return ((ParticleEngine.MutableSpriteSet) delegate).sprites; + } + + @Override + public TextureAtlasSprite get(int i, int j) { + return delegate.get(i, j); + } + + @Override + public TextureAtlasSprite get(RandomSource random) { + return delegate.get(random); + } + } + + private static List> deferred = new ArrayList<>(); + + private static void doRegister(ParticleProviderRegistrar registrar, ParticleType type, ParticleProvider provider) { + registrar.register(type, provider); + } + + private static void doRegister(ParticleProviderRegistrar registrar, ParticleType type, ParticleProviderRegistry.DeferredParticleProvider provider) { + registrar.register(type, sprites -> + provider.create(new ExtendedSpriteSetImpl(Minecraft.getInstance().particleEngine, sprites))); + } + + public static void register(ParticleType type, ParticleProvider provider) { + if (deferred == null) { + LOGGER.warn("Something is attempting to register particle providers at a later point than intended! This might cause issues!", new Throwable()); + doRegister(ParticleProviderRegistrar.ofFallback(), type, provider); + } else { + deferred.add(registrar -> doRegister(registrar, type, provider)); + } + } + + public static void register(ParticleType type, ParticleProviderRegistry.DeferredParticleProvider provider) { + if (deferred == null) { + LOGGER.warn("Something is attempting to register particle providers at a later point than intended! This might cause issues!", new Throwable()); + doRegister(ParticleProviderRegistrar.ofFallback(), type, provider); + } else { + deferred.add(registrar -> doRegister(registrar, type, provider)); + } + } + + public static void onParticleFactoryRegister(RegisterParticleProvidersEvent event) { + if (deferred != null) { + ParticleProviderRegistrar registrar = ParticleProviderRegistrar.ofForge(event); + // run all deferred registrations + for (Consumer consumer : deferred) { + consumer.accept(registrar); + } + // yeet deferred list - register immediately from now on + deferred = null; + } + } + + private interface ParticleProviderRegistrar { + void register(ParticleType type, ParticleProvider provider); + + void register(ParticleType type, ParticleEngine.SpriteParticleRegistration registration); + + static ParticleProviderRegistrar ofForge(RegisterParticleProvidersEvent event) { + return new ParticleProviderRegistrar() { + @Override + public void register(ParticleType type, ParticleProvider provider) { + event.registerSpecial(type, provider); + } + + @Override + public void register(ParticleType type, ParticleEngine.SpriteParticleRegistration registration) { + event.registerSpriteSet(type, registration); + } + }; + } + + static ParticleProviderRegistrar ofFallback() { + return new ParticleProviderRegistrar() { + @Override + public void register(ParticleType type, ParticleProvider provider) { + Minecraft.getInstance().particleEngine.register(type, provider); + } + + @Override + public void register(ParticleType type, ParticleEngine.SpriteParticleRegistration registration) { + Minecraft.getInstance().particleEngine.register(type, registration); + } + }; + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/client/rendering/forge/BlockEntityRendererRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/client/rendering/forge/BlockEntityRendererRegistryImpl.java new file mode 100644 index 000000000..1c5480c49 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/client/rendering/forge/BlockEntityRendererRegistryImpl.java @@ -0,0 +1,31 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.rendering.forge; + +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; + +public class BlockEntityRendererRegistryImpl { + public static void register(BlockEntityType type, BlockEntityRendererProvider provider) { + BlockEntityRenderers.register(type, provider); + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/client/rendering/forge/ColorHandlerRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/client/rendering/forge/ColorHandlerRegistryImpl.java new file mode 100644 index 000000000..4b0f87e5e --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/client/rendering/forge/ColorHandlerRegistryImpl.java @@ -0,0 +1,97 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.rendering.forge; + +import com.google.common.collect.Lists; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.client.Minecraft; +import net.minecraft.client.color.block.BlockColor; +import net.minecraft.client.color.item.ItemColor; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Block; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.client.event.RegisterColorHandlersEvent; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.List; +import java.util.Objects; +import java.util.function.Supplier; + +public class ColorHandlerRegistryImpl { + private static final List[]>> ITEM_COLORS = Lists.newArrayList(); + private static final List[]>> BLOCK_COLORS = Lists.newArrayList(); + + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.register(ColorHandlerRegistryImpl.class); + }); + } + + @SubscribeEvent + public static void onItemColorEvent(RegisterColorHandlersEvent.Item event) { + for (Pair[]> pair : ITEM_COLORS) { + event.register(pair.getLeft(), unpackItems(pair.getRight())); + } + } + + @SubscribeEvent + public static void onBlockColorEvent(RegisterColorHandlersEvent.Block event) { + for (Pair[]> pair : BLOCK_COLORS) { + event.register(pair.getLeft(), unpackBlocks(pair.getRight())); + } + } + + @SafeVarargs + public static void registerItemColors(ItemColor itemColor, Supplier... items) { + Objects.requireNonNull(itemColor, "color is null!"); + if (Minecraft.getInstance().getItemColors() == null) { + ITEM_COLORS.add(Pair.of(itemColor, items)); + } else { + Minecraft.getInstance().getItemColors().register(itemColor, unpackItems(items)); + } + } + + @SafeVarargs + public static void registerBlockColors(BlockColor blockColor, Supplier... blocks) { + Objects.requireNonNull(blockColor, "color is null!"); + if (Minecraft.getInstance().getBlockColors() == null) { + BLOCK_COLORS.add(Pair.of(blockColor, blocks)); + } else { + Minecraft.getInstance().getBlockColors().register(blockColor, unpackBlocks(blocks)); + } + } + + private static ItemLike[] unpackItems(Supplier[] items) { + ItemLike[] array = new ItemLike[items.length]; + for (int i = 0; i < items.length; i++) { + array[i] = Objects.requireNonNull(items[i].get()); + } + return array; + } + + private static Block[] unpackBlocks(Supplier[] blocks) { + Block[] array = new Block[blocks.length]; + for (int i = 0; i < blocks.length; i++) { + array[i] = Objects.requireNonNull(blocks[i].get()); + } + return array; + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/client/rendering/forge/RenderTypeRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/client/rendering/forge/RenderTypeRegistryImpl.java new file mode 100644 index 000000000..6d52d18d8 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/client/rendering/forge/RenderTypeRegistryImpl.java @@ -0,0 +1,39 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.client.rendering.forge; + +import net.minecraft.client.renderer.ItemBlockRenderTypes; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.material.Fluid; + +public class RenderTypeRegistryImpl { + public static void register(RenderType type, Block... blocks) { + for (Block block : blocks) { + ItemBlockRenderTypes.setRenderLayer(block, type); + } + } + + public static void register(RenderType type, Fluid... fluids) { + for (Fluid fluid : fluids) { + ItemBlockRenderTypes.setRenderLayer(fluid, type); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/forge/CreativeTabRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/forge/CreativeTabRegistryImpl.java new file mode 100644 index 000000000..87c8cc1d2 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/forge/CreativeTabRegistryImpl.java @@ -0,0 +1,228 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.forge; + +import com.google.common.base.Suppliers; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.registry.CreativeTabOutput; +import dev.architectury.registry.CreativeTabRegistry; +import dev.architectury.registry.registries.DeferredSupplier; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.ItemStack; +import net.neoforged.neoforge.common.CreativeModeTabRegistry; +import net.neoforged.neoforge.common.util.MutableHashedLinkedMap; +import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class CreativeTabRegistryImpl { + private static final Logger LOGGER = LogManager.getLogger(CreativeTabRegistryImpl.class); + + private static final List> BUILD_CONTENTS_LISTENERS = new ArrayList<>(); + private static final Multimap> APPENDS = MultimapBuilder.hashKeys().arrayListValues().build(); + + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(CreativeTabRegistryImpl::event); + }); + + BUILD_CONTENTS_LISTENERS.add(event -> { + for (Map.Entry>> keyEntry : APPENDS.asMap().entrySet()) { + Supplier> stacks = Suppliers.memoize(() -> keyEntry.getValue().stream() + .map(Supplier::get) + .toList()); + if (keyEntry.getKey() instanceof TabKey.SupplierTabKey supplierTabKey) { + if (Objects.equals(CreativeModeTabRegistry.getName(event.getTab()), supplierTabKey.supplier().getId())) { + for (ItemStack stack : stacks.get()) { + event.getEntries().put(stack, CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); + } + } + } else if (keyEntry.getKey() instanceof TabKey.DirectTabKey directTabKey) { + if (event.getTab().equals(directTabKey.tab())) { + for (ItemStack stack : stacks.get()) { + event.getEntries().put(stack, CreativeModeTab.TabVisibility.PARENT_AND_SEARCH_TABS); + } + } + } + } + }); + } + + public static void event(BuildCreativeModeTabContentsEvent event) { + for (Consumer listener : BUILD_CONTENTS_LISTENERS) { + listener.accept(event); + } + } + + @ApiStatus.Experimental + public static CreativeModeTab create(Consumer callback) { + CreativeModeTab.Builder builder = CreativeModeTab.builder(); + callback.accept(builder); + return builder.build(); + } + + @ApiStatus.Experimental + public static DeferredSupplier ofBuiltin(CreativeModeTab tab) { + ResourceLocation key = BuiltInRegistries.CREATIVE_MODE_TAB.getKey(tab); + if (key == null) { + throw new IllegalArgumentException("Builtin tab %s is not registered!".formatted(tab)); + } + return new DeferredSupplier<>() { + @Override + public ResourceLocation getRegistryId() { + return Registries.CREATIVE_MODE_TAB.location(); + } + + @Override + public ResourceLocation getId() { + return BuiltInRegistries.CREATIVE_MODE_TAB.getKey(tab); + } + + @Override + public boolean isPresent() { + return true; + } + + @Override + public CreativeModeTab get() { + return tab; + } + }; + } + + @ApiStatus.Experimental + public static DeferredSupplier defer(ResourceLocation name) { + return new DeferredSupplier<>() { + @Nullable + private CreativeModeTab tab; + + @Override + public ResourceLocation getRegistryId() { + return Registries.CREATIVE_MODE_TAB.location(); + } + + @Override + public ResourceLocation getId() { + return name; + } + + @Override + public CreativeModeTab get() { + resolve(); + if (tab == null) + throw new IllegalStateException("Creative tab %s was not registered yet!".formatted(name)); + return tab; + } + + @Override + public boolean isPresent() { + resolve(); + return tab != null; + } + + private void resolve() { + if (this.tab == null) { + this.tab = BuiltInRegistries.CREATIVE_MODE_TAB.get(name); + } + } + }; + } + + public static void modify(DeferredSupplier tab, CreativeTabRegistry.ModifyTabCallback filler) { + BUILD_CONTENTS_LISTENERS.add(event -> { + if (tab.isPresent()) { + if (event.getTab().equals(tab.get())) { + filler.accept(event.getFlags(), wrapTabOutput(event.getEntries()), event.hasPermissions()); + } + } else if (Objects.equals(CreativeModeTabRegistry.getName(event.getTab()), tab.getId())) { + filler.accept(event.getFlags(), wrapTabOutput(event.getEntries()), event.hasPermissions()); + } + }); + } + + private static CreativeTabOutput wrapTabOutput(MutableHashedLinkedMap entries) { + return new CreativeTabOutput() { + @Override + public void acceptAfter(ItemStack after, ItemStack stack, CreativeModeTab.TabVisibility visibility) { + if (after.isEmpty()) { + entries.put(stack, visibility); + } else { + entries.putAfter(after, stack, visibility); + } + } + + @Override + public void acceptBefore(ItemStack before, ItemStack stack, CreativeModeTab.TabVisibility visibility) { + if (before.isEmpty()) { + entries.put(stack, visibility); + } else { + entries.putBefore(before, stack, visibility); + } + } + }; + } + + @ApiStatus.Experimental + public static void appendStack(DeferredSupplier tab, Supplier item) { + APPENDS.put(new TabKey.SupplierTabKey(tab), item); + } + + private interface TabKey { + record SupplierTabKey(DeferredSupplier supplier) implements TabKey { + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SupplierTabKey that)) return false; + return Objects.equals(supplier.getId(), that.supplier.getId()); + } + + @Override + public int hashCode() { + return Objects.hash(supplier.getId()); + } + } + + record DirectTabKey(CreativeModeTab tab) implements TabKey { + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof DirectTabKey that)) return false; + return tab == that.tab; + } + + @Override + public int hashCode() { + return System.identityHashCode(tab); + } + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/forge/ReloadListenerRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/forge/ReloadListenerRegistryImpl.java new file mode 100644 index 000000000..d8c8f6655 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/forge/ReloadListenerRegistryImpl.java @@ -0,0 +1,62 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.forge; + +import com.google.common.collect.Lists; +import net.minecraft.client.Minecraft; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.PackType; +import net.minecraft.server.packs.resources.PreparableReloadListener; +import net.minecraft.server.packs.resources.ReloadableResourceManager; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.AddReloadListenerEvent; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; + +public class ReloadListenerRegistryImpl { + private static List serverDataReloadListeners = Lists.newArrayList(); + + static { + NeoForge.EVENT_BUS.addListener(ReloadListenerRegistryImpl::addReloadListeners); + } + + public static void register(PackType type, PreparableReloadListener listener, @Nullable ResourceLocation listenerId, Collection dependencies) { + if (type == PackType.SERVER_DATA) { + serverDataReloadListeners.add(listener); + } else if (type == PackType.CLIENT_RESOURCES) { + registerClient(listener); + } + } + + @OnlyIn(Dist.CLIENT) + private static void registerClient(PreparableReloadListener listener) { + ((ReloadableResourceManager) Minecraft.getInstance().getResourceManager()).registerReloadListener(listener); + } + + public static void addReloadListeners(AddReloadListenerEvent event) { + for (PreparableReloadListener listener : serverDataReloadListeners) { + event.addListener(listener); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/fuel/forge/FuelRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/fuel/forge/FuelRegistryImpl.java new file mode 100644 index 000000000..da905c72d --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/fuel/forge/FuelRegistryImpl.java @@ -0,0 +1,55 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.fuel.forge; + +import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.furnace.FurnaceFuelBurnTimeEvent; + +public class FuelRegistryImpl { + private static final Object2IntMap ITEMS = new Object2IntLinkedOpenHashMap<>(); + + static { + NeoForge.EVENT_BUS.register(FuelRegistryImpl.class); + } + + public static void register(int time, ItemLike... items) { + for (ItemLike item : items) { + ITEMS.put(item, time); + } + } + + public static int get(ItemStack stack) { + return stack.getBurnTime(null); + } + + @SubscribeEvent + public static void event(FurnaceFuelBurnTimeEvent event) { + if (event.getItemStack().isEmpty()) return; + int time = ITEMS.getOrDefault(event.getItemStack().getItem(), Integer.MIN_VALUE); + if (time != Integer.MIN_VALUE) { + event.setBurnTime(time); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/item/forge/ItemPropertiesRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/item/forge/ItemPropertiesRegistryImpl.java new file mode 100644 index 000000000..1f656c486 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/item/forge/ItemPropertiesRegistryImpl.java @@ -0,0 +1,37 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.item.forge; + +import net.minecraft.client.renderer.item.ClampedItemPropertyFunction; +import net.minecraft.client.renderer.item.ItemProperties; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.ItemLike; + +public class ItemPropertiesRegistryImpl { + public static ClampedItemPropertyFunction registerGeneric(ResourceLocation propertyId, ClampedItemPropertyFunction function) { + ItemProperties.registerGeneric(propertyId, function); + return function; + } + + public static ClampedItemPropertyFunction register(ItemLike item, ResourceLocation propertyId, ClampedItemPropertyFunction function) { + ItemProperties.register(item.asItem(), propertyId, function); + return function; + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/level/biome/forge/BiomeModificationsImpl.java b/neoforge/src/main/java/dev/architectury/registry/level/biome/forge/BiomeModificationsImpl.java new file mode 100644 index 000000000..f0444d6f1 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/level/biome/forge/BiomeModificationsImpl.java @@ -0,0 +1,587 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.level.biome.forge; + +import com.google.common.collect.Lists; +import com.mojang.serialization.MapCodec; +import dev.architectury.hooks.level.biome.*; +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.registry.level.biome.BiomeModifications.BiomeContext; +import dev.architectury.utils.ArchitecturyConstants; +import dev.architectury.utils.GameInstance; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.sounds.Music; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.tags.TagKey; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.biome.*; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver; +import net.minecraft.world.level.levelgen.placement.PlacedFeature; +import net.neoforged.neoforge.common.world.*; +import net.neoforged.neoforge.registries.NeoForgeRegistries; +import net.neoforged.neoforge.registries.RegisterEvent; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +public class BiomeModificationsImpl { + private static final List, BiConsumer>> ADDITIONS = Lists.newArrayList(); + private static final List, BiConsumer>> POST_PROCESSING = Lists.newArrayList(); + private static final List, BiConsumer>> REMOVALS = Lists.newArrayList(); + private static final List, BiConsumer>> REPLACEMENTS = Lists.newArrayList(); + @Nullable + private static MapCodec noneBiomeModCodec = null; + + public static void init() { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(event -> { + event.register(NeoForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, registry -> { + registry.register(new ResourceLocation(ArchitecturyConstants.MOD_ID, "none_biome_mod_codec"), + noneBiomeModCodec = MapCodec.unit(BiomeModifierImpl.INSTANCE)); + }); + }); + }); + } + + public static void addProperties(Predicate predicate, BiConsumer modifier) { + ADDITIONS.add(Pair.of(predicate, modifier)); + } + + public static void postProcessProperties(Predicate predicate, BiConsumer modifier) { + POST_PROCESSING.add(Pair.of(predicate, modifier)); + } + + public static void removeProperties(Predicate predicate, BiConsumer modifier) { + REMOVALS.add(Pair.of(predicate, modifier)); + } + + public static void replaceProperties(Predicate predicate, BiConsumer modifier) { + REPLACEMENTS.add(Pair.of(predicate, modifier)); + } + + private static class BiomeModifierImpl implements BiomeModifier { + // cry about it + private static final BiomeModifierImpl INSTANCE = new BiomeModifierImpl(); + + @Override + public void modify(Holder arg, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) { + List, BiConsumer>> list = switch (phase) { + case ADD -> ADDITIONS; + case REMOVE -> REMOVALS; + case MODIFY -> REPLACEMENTS; + case AFTER_EVERYTHING -> POST_PROCESSING; + default -> null; + }; + + if (list == null) return; + BiomeContext biomeContext = wrapSelectionContext(arg.unwrapKey(), builder); + BiomeProperties.Mutable mutableBiome = new MutableBiomeWrapped(builder); + for (var pair : list) { + if (pair.getLeft().test(biomeContext)) { + pair.getRight().accept(biomeContext, mutableBiome); + } + } + } + + @Override + public MapCodec codec() { + if (noneBiomeModCodec != null) { + return noneBiomeModCodec; + } else { + return MapCodec.unit(INSTANCE); + } + } + } + + private static BiomeContext wrapSelectionContext(Optional> biomeResourceKey, ModifiableBiomeInfo.BiomeInfo.Builder event) { + return new BiomeContext() { + BiomeProperties properties = new BiomeWrapped(event); + + @Override + public Optional getKey() { + return biomeResourceKey.map(ResourceKey::location); + } + + @Override + public BiomeProperties getProperties() { + return properties; + } + + @Override + public boolean hasTag(TagKey tag) { + MinecraftServer server = GameInstance.getServer(); + if (server != null) { + Optional> registry = server.registryAccess().registry(Registries.BIOME); + if (registry.isPresent()) { + Optional> holder = registry.get().getHolder(biomeResourceKey.get()); + if (holder.isPresent()) { + return holder.get().is(tag); + } + } + } + return false; + } + }; + } + + public static class BiomeWrapped implements BiomeProperties { + protected final ModifiableBiomeInfo.BiomeInfo.Builder event; + protected final ClimateProperties climateProperties; + protected final EffectsProperties effectsProperties; + protected final GenerationProperties generationProperties; + protected final SpawnProperties spawnProperties; + + public BiomeWrapped(ModifiableBiomeInfo.BiomeInfo.Builder event) { + this(event, + new MutableClimatePropertiesWrapped(event.getClimateSettings()), + new MutableEffectsPropertiesWrapped(event.getSpecialEffects()), + new GenerationSettingsBuilderWrapped(event.getGenerationSettings()), + new SpawnSettingsBuilderWrapped(event.getMobSpawnSettings()) + ); + } + + public BiomeWrapped(ModifiableBiomeInfo.BiomeInfo.Builder event, ClimateProperties climateProperties, EffectsProperties effectsProperties, GenerationProperties generationProperties, SpawnProperties spawnProperties) { + this.event = event; + this.climateProperties = climateProperties; + this.effectsProperties = effectsProperties; + this.generationProperties = generationProperties; + this.spawnProperties = spawnProperties; + } + + @Override + public ClimateProperties getClimateProperties() { + return climateProperties; + } + + @Override + public EffectsProperties getEffectsProperties() { + return effectsProperties; + } + + @Override + public GenerationProperties getGenerationProperties() { + return generationProperties; + } + + @Override + public SpawnProperties getSpawnProperties() { + return spawnProperties; + } + } + + private static class GenerationSettingsBuilderWrapped implements GenerationProperties { + protected final BiomeGenerationSettingsBuilder generation; + + public GenerationSettingsBuilderWrapped(BiomeGenerationSettingsBuilder generation) { + this.generation = generation; + } + + @Override + public Iterable>> getCarvers(GenerationStep.Carving carving) { + return generation.getCarvers(carving); + } + + @Override + public Iterable> getFeatures(GenerationStep.Decoration decoration) { + return generation.getFeatures(decoration); + } + + @Override + public List>> getFeatures() { + return (List>>) (List) generation.features; + } + } + + private static class SpawnSettingsBuilderWrapped implements SpawnProperties { + protected final MobSpawnSettingsBuilder builder; + + public SpawnSettingsBuilderWrapped(MobSpawnSettingsBuilder builder) { + this.builder = builder; + } + + @Override + public float getCreatureProbability() { + return builder.getProbability(); + } + + @Override + public Map> getSpawners() { + return builder.spawners; + } + + @Override + public Map, MobSpawnSettings.MobSpawnCost> getMobSpawnCosts() { + return builder.mobSpawnCosts; + } + } + + public static class MutableBiomeWrapped extends BiomeWrapped implements BiomeProperties.Mutable { + public MutableBiomeWrapped(ModifiableBiomeInfo.BiomeInfo.Builder event) { + super(event, + new MutableClimatePropertiesWrapped(event.getClimateSettings()), + new MutableEffectsPropertiesWrapped(event.getSpecialEffects()), + new MutableGenerationSettingsBuilderWrapped(event.getGenerationSettings()), + new MutableSpawnSettingsBuilderWrapped(event.getMobSpawnSettings()) + ); + } + + @Override + public ClimateProperties.Mutable getClimateProperties() { + return (ClimateProperties.Mutable) super.getClimateProperties(); + } + + @Override + public EffectsProperties.Mutable getEffectsProperties() { + return (EffectsProperties.Mutable) super.getEffectsProperties(); + } + + @Override + public GenerationProperties.Mutable getGenerationProperties() { + return (GenerationProperties.Mutable) super.getGenerationProperties(); + } + + @Override + public SpawnProperties.Mutable getSpawnProperties() { + return (SpawnProperties.Mutable) super.getSpawnProperties(); + } + } + + public static class MutableClimatePropertiesWrapped implements ClimateProperties.Mutable { + public ClimateSettingsBuilder builder; + + public MutableClimatePropertiesWrapped(ClimateSettingsBuilder builder) { + this.builder = builder; + } + + @Override + public boolean hasPrecipitation() { + return builder.hasPrecipitation(); + } + + @Override + public float getTemperature() { + return builder.getTemperature(); + } + + @Override + public Biome.TemperatureModifier getTemperatureModifier() { + return builder.getTemperatureModifier(); + } + + @Override + public float getDownfall() { + return builder.getDownfall(); + } + + @Override + public Mutable setHasPrecipitation(boolean hasPrecipitation) { + this.builder.setHasPrecipitation(hasPrecipitation); + return this; + } + + @Override + public Mutable setTemperature(float temperature) { + this.builder.setTemperature(temperature); + return this; + } + + @Override + public Mutable setTemperatureModifier(Biome.TemperatureModifier temperatureModifier) { + this.builder.setTemperatureModifier(temperatureModifier); + return this; + } + + @Override + public Mutable setDownfall(float downfall) { + this.builder.setDownfall(downfall); + return this; + } + + } + + public static class MutableEffectsPropertiesWrapped implements EffectsProperties.Mutable { + public BiomeSpecialEffects.Builder builder; + + public MutableEffectsPropertiesWrapped(BiomeSpecialEffects.Builder builder) { + this.builder = builder; + } + + @Override + public int getFogColor() { + if (builder instanceof BiomeSpecialEffectsBuilder b) return b.getFogColor(); + return builder.fogColor.orElse(-1); + } + + @Override + public int getWaterColor() { + if (builder instanceof BiomeSpecialEffectsBuilder b) return b.getWaterFogColor(); + return builder.waterColor.orElse(-1); + } + + @Override + public int getWaterFogColor() { + if (builder instanceof BiomeSpecialEffectsBuilder b) return b.getWaterFogColor(); + return builder.waterFogColor.orElse(-1); + } + + @Override + public int getSkyColor() { + if (builder instanceof BiomeSpecialEffectsBuilder b) return b.getSkyColor(); + return builder.skyColor.orElse(-1); + } + + @Override + public OptionalInt getFoliageColorOverride() { + return builder.foliageColorOverride.map(OptionalInt::of).orElseGet(OptionalInt::empty); + } + + @Override + public OptionalInt getGrassColorOverride() { + return builder.grassColorOverride.map(OptionalInt::of).orElseGet(OptionalInt::empty); + } + + @Override + public BiomeSpecialEffects.GrassColorModifier getGrassColorModifier() { + return builder.grassColorModifier; + } + + @Override + public Optional getAmbientParticle() { + return builder.ambientParticle; + } + + @Override + public Optional> getAmbientLoopSound() { + return builder.ambientLoopSoundEvent; + } + + @Override + public Optional getAmbientMoodSound() { + return builder.ambientMoodSettings; + } + + @Override + public Optional getAmbientAdditionsSound() { + return builder.ambientAdditionsSettings; + } + + @Override + public Optional getBackgroundMusic() { + return builder.backgroundMusic; + } + + @Override + public Mutable setFogColor(int color) { + builder.fogColor(color); + return this; + } + + @Override + public Mutable setWaterColor(int color) { + builder.waterColor(color); + return this; + } + + @Override + public Mutable setWaterFogColor(int color) { + builder.waterFogColor(color); + return this; + } + + @Override + public Mutable setSkyColor(int color) { + builder.skyColor(color); + return this; + } + + @Override + public Mutable setFoliageColorOverride(@Nullable Integer colorOverride) { + builder.foliageColorOverride = Optional.ofNullable(colorOverride); + return this; + } + + @Override + public Mutable setGrassColorOverride(@Nullable Integer colorOverride) { + builder.foliageColorOverride = Optional.ofNullable(colorOverride); + return this; + } + + @Override + public Mutable setGrassColorModifier(BiomeSpecialEffects.GrassColorModifier modifier) { + builder.grassColorModifier(modifier); + return this; + } + + @Override + public Mutable setAmbientParticle(@Nullable AmbientParticleSettings settings) { + builder.ambientParticle = Optional.ofNullable(settings); + return this; + } + + @Override + public Mutable setAmbientLoopSound(@Nullable Holder sound) { + builder.ambientLoopSoundEvent = Optional.ofNullable(sound); + return this; + } + + @Override + public Mutable setAmbientMoodSound(@Nullable AmbientMoodSettings settings) { + builder.ambientMoodSettings = Optional.ofNullable(settings); + return this; + } + + @Override + public Mutable setAmbientAdditionsSound(@Nullable AmbientAdditionsSettings settings) { + builder.ambientAdditionsSettings = Optional.ofNullable(settings); + return this; + } + + @Override + public Mutable setBackgroundMusic(@Nullable Music music) { + builder.backgroundMusic = Optional.ofNullable(music); + return this; + } + } + + private static class MutableGenerationSettingsBuilderWrapped extends GenerationSettingsBuilderWrapped implements GenerationProperties.Mutable { + public MutableGenerationSettingsBuilderWrapped(BiomeGenerationSettingsBuilder generation) { + super(generation); + } + + @Override + public Mutable addFeature(GenerationStep.Decoration decoration, Holder feature) { + generation.addFeature(decoration, feature); + return this; + } + + @Override + public Mutable addFeature(GenerationStep.Decoration decoration, ResourceKey feature) { + MinecraftServer server = GameInstance.getServer(); + if (server != null) { + Optional> registry = server.registryAccess().registry(Registries.PLACED_FEATURE); + if (registry.isPresent()) { + Optional> holder = registry.get().getHolder(feature); + if (holder.isPresent()) { + return addFeature(decoration, holder.get()); + } else { + throw new IllegalArgumentException("Unknown feature: " + feature); + } + } + } + return this; + } + + @Override + public Mutable addCarver(GenerationStep.Carving carving, Holder> feature) { + generation.addCarver(carving, feature); + return this; + } + + @Override + public Mutable addCarver(GenerationStep.Carving carving, ResourceKey> feature) { + MinecraftServer server = GameInstance.getServer(); + if (server != null) { + Optional>> registry = server.registryAccess().registry(Registries.CONFIGURED_CARVER); + if (registry.isPresent()) { + Optional>> holder = registry.get().getHolder(feature); + if (holder.isPresent()) { + return addCarver(carving, holder.get()); + } else { + throw new IllegalArgumentException("Unknown carver: " + feature); + } + } + } + return this; + } + + @Override + public Mutable removeFeature(GenerationStep.Decoration decoration, ResourceKey feature) { + generation.getFeatures(decoration).removeIf(supplier -> supplier.is(feature)); + return this; + } + + @Override + public Mutable removeCarver(GenerationStep.Carving carving, ResourceKey> feature) { + generation.getCarvers(carving).removeIf(supplier -> supplier.is(feature)); + return this; + } + } + + private static class MutableSpawnSettingsBuilderWrapped extends SpawnSettingsBuilderWrapped implements SpawnProperties.Mutable { + public MutableSpawnSettingsBuilderWrapped(MobSpawnSettingsBuilder builder) { + super(builder); + } + + @Override + public Mutable setCreatureProbability(float probability) { + builder.creatureGenerationProbability(probability); + return this; + } + + @Override + public Mutable addSpawn(MobCategory category, MobSpawnSettings.SpawnerData data) { + builder.addSpawn(category, data); + return this; + } + + @Override + public boolean removeSpawns(BiPredicate predicate) { + boolean removed = false; + for (MobCategory type : builder.getSpawnerTypes()) { + if (builder.getSpawner(type).removeIf(data -> predicate.test(type, data))) { + removed = true; + } + } + return removed; + } + + @Override + public Mutable setSpawnCost(EntityType entityType, MobSpawnSettings.MobSpawnCost cost) { + builder.addMobCharge(entityType, cost.charge(), cost.energyBudget()); + return this; + } + + @Override + public Mutable setSpawnCost(EntityType entityType, double charge, double energyBudget) { + builder.addMobCharge(entityType, charge, energyBudget); + return this; + } + + @Override + public Mutable clearSpawnCost(EntityType entityType) { + getMobSpawnCosts().remove(entityType); + return this; + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/level/entity/forge/EntityAttributeRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/level/entity/forge/EntityAttributeRegistryImpl.java new file mode 100644 index 000000000..1ea66afcc --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/level/entity/forge/EntityAttributeRegistryImpl.java @@ -0,0 +1,53 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.level.entity.forge; + +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.ai.attributes.AttributeSupplier; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.event.entity.EntityAttributeCreationEvent; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +public class EntityAttributeRegistryImpl { + private static final Map>, Supplier> ATTRIBUTES = new ConcurrentHashMap<>(); + + public static void register(Supplier> type, Supplier attribute) { + ATTRIBUTES.put(type, attribute); + } + + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.register(EntityAttributeRegistryImpl.class); + }); + } + + @SubscribeEvent + public static void event(EntityAttributeCreationEvent event) { + for (Map.Entry>, Supplier> entry : ATTRIBUTES.entrySet()) { + event.put(entry.getKey().get(), entry.getValue().get().build()); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/level/entity/forge/SpawnPlacementsRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/level/entity/forge/SpawnPlacementsRegistryImpl.java new file mode 100644 index 000000000..ee54d751d --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/level/entity/forge/SpawnPlacementsRegistryImpl.java @@ -0,0 +1,62 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.level.entity.forge; + +import dev.architectury.platform.hooks.EventBusesHooks; +import dev.architectury.utils.ArchitecturyConstants; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.entity.SpawnPlacementType; +import net.minecraft.world.entity.SpawnPlacements; +import net.minecraft.world.level.levelgen.Heightmap; +import net.neoforged.neoforge.event.entity.SpawnPlacementRegisterEvent; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class SpawnPlacementsRegistryImpl { + private static List> entries = new ArrayList<>(); + + private record Entry(Supplier> type, SpawnPlacementType spawnPlacement, + Heightmap.Types heightmapType, + SpawnPlacements.SpawnPredicate spawnPredicate) { + } + + static { + EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> { + bus.addListener(event -> { + for (Entry entry : entries) { + Entry casted = (Entry) entry; + event.register(casted.type().get(), casted.spawnPlacement(), casted.heightmapType(), casted.spawnPredicate(), SpawnPlacementRegisterEvent.Operation.OR); + } + entries = null; + }); + }); + } + + public static void register(Supplier> type, SpawnPlacementType spawnPlacement, Heightmap.Types heightmapType, SpawnPlacements.SpawnPredicate spawnPredicate) { + if (entries != null) { + entries.add(new Entry<>(type, spawnPlacement, heightmapType, spawnPredicate)); + } else { + throw new IllegalStateException("SpawnPlacementsRegistry.register must not be called after the registry has been collected!"); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/level/entity/trade/forge/TradeRegistryImpl.java b/neoforge/src/main/java/dev/architectury/registry/level/entity/trade/forge/TradeRegistryImpl.java new file mode 100644 index 000000000..8c182f647 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/registry/level/entity/trade/forge/TradeRegistryImpl.java @@ -0,0 +1,75 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.registry.level.entity.trade.forge; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.minecraft.core.NonNullList; +import net.minecraft.world.entity.npc.VillagerProfession; +import net.minecraft.world.entity.npc.VillagerTrades; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.village.VillagerTradesEvent; +import net.neoforged.neoforge.event.village.WandererTradesEvent; + +import java.util.*; + +public class TradeRegistryImpl { + private static final Map>> TRADES_TO_ADD = new HashMap<>(); + private static final List WANDERER_TRADER_TRADES_GENERIC = new ArrayList<>(); + private static final List WANDERER_TRADER_TRADES_RARE = new ArrayList<>(); + + static { + NeoForge.EVENT_BUS.addListener(TradeRegistryImpl::onTradeRegistering); + NeoForge.EVENT_BUS.addListener(TradeRegistryImpl::onWanderingTradeRegistering); + } + + public static void registerVillagerTrade0(VillagerProfession profession, int level, VillagerTrades.ItemListing... trades) { + Int2ObjectMap> tradesForProfession = TRADES_TO_ADD.computeIfAbsent(profession, $ -> new Int2ObjectOpenHashMap<>()); + List tradesForLevel = tradesForProfession.computeIfAbsent(level, $ -> new ArrayList<>()); + Collections.addAll(tradesForLevel, trades); + } + + public static void registerTradeForWanderingTrader(boolean rare, VillagerTrades.ItemListing... trades) { + if (rare) { + Collections.addAll(WANDERER_TRADER_TRADES_RARE, trades); + } else { + Collections.addAll(WANDERER_TRADER_TRADES_GENERIC, trades); + } + } + + public static void onTradeRegistering(VillagerTradesEvent event) { + Int2ObjectMap> trades = TRADES_TO_ADD.get(event.getType()); + + if (trades != null) { + for (Int2ObjectMap.Entry> entry : trades.int2ObjectEntrySet()) { + event.getTrades().computeIfAbsent(entry.getIntKey(), $ -> NonNullList.create()).addAll(entry.getValue()); + } + } + } + + public static void onWanderingTradeRegistering(WandererTradesEvent event) { + if (!WANDERER_TRADER_TRADES_GENERIC.isEmpty()) { + event.getGenericTrades().addAll(WANDERER_TRADER_TRADES_GENERIC); + } + if (!WANDERER_TRADER_TRADES_RARE.isEmpty()) { + event.getRareTrades().addAll(WANDERER_TRADER_TRADES_RARE); + } + } +} diff --git a/neoforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java b/neoforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java index e0bd5d683..9aec8712b 100644 --- a/neoforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java +++ b/neoforge/src/main/java/dev/architectury/registry/registries/forge/RegistrarManagerImpl.java @@ -24,7 +24,7 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import dev.architectury.impl.RegistrySupplierImpl; -import dev.architectury.platform.hooks.forge.EventBusesHooksImpl; +import dev.architectury.platform.hooks.EventBusesHooks; import dev.architectury.registry.registries.Registrar; import dev.architectury.registry.registries.RegistrarBuilder; import dev.architectury.registry.registries.RegistrarManager; @@ -109,7 +109,7 @@ public static class RegistryProviderImpl implements RegistrarManager.RegistryPro public RegistryProviderImpl(String modId) { this.modId = modId; - EventBusesHooksImpl.getModEventBus(modId).get().register(new EventListener()); + EventBusesHooks.getModEventBus(modId).get().register(new EventListener()); } @Override diff --git a/neoforge/src/main/java/dev/architectury/utils/forge/GameInstanceImpl.java b/neoforge/src/main/java/dev/architectury/utils/forge/GameInstanceImpl.java new file mode 100644 index 000000000..be7978316 --- /dev/null +++ b/neoforge/src/main/java/dev/architectury/utils/forge/GameInstanceImpl.java @@ -0,0 +1,29 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021, 2022 architectury + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package dev.architectury.utils.forge; + +import net.minecraft.server.MinecraftServer; +import net.neoforged.neoforge.server.ServerLifecycleHooks; + +public class GameInstanceImpl { + public static MinecraftServer getServer() { + return ServerLifecycleHooks.getCurrentServer(); + } +} diff --git a/neoforge/src/main/resources/META-INF/mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml similarity index 100% rename from neoforge/src/main/resources/META-INF/mods.toml rename to neoforge/src/main/resources/META-INF/neoforge.mods.toml diff --git a/neoforge/src/main/resources/architectury.mixins.json b/neoforge/src/main/resources/architectury.mixins.json new file mode 100644 index 000000000..c9577f146 --- /dev/null +++ b/neoforge/src/main/resources/architectury.mixins.json @@ -0,0 +1,21 @@ +{ + "required": true, + "package": "dev.architectury.mixin.forge", + "plugin": "dev.architectury.plugin.forge.ArchitecturyMixinPlugin", + "compatibilityLevel": "JAVA_16", + "minVersion": "0.8", + "client": [ + "client.MixinCommandSourceStack", + "MixinClientLevel", + "MixinMinecraft" + ], + "mixins": [ + "neoforge.MixinChunkSerializer", + "MixinFallingBlockEntity", + "MixinItemExtension", + "MixinLevelEvent" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/settings.gradle b/settings.gradle index 7f514a9ea..aab12c8cc 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,10 +15,10 @@ include("common") include("fabric") //include("forge") //include("minecraftforge") -//include("neoforge") +include("neoforge") include("testmod-common") include("testmod-fabric") //include("testmod-forge") -//include("testmod-neoforge") +include("testmod-neoforge") rootProject.name = "architectury" diff --git a/testmod-common/src/main/java/dev/architectury/test/entity/TestEntity.java b/testmod-common/src/main/java/dev/architectury/test/entity/TestEntity.java index 60673d5a1..02347b626 100644 --- a/testmod-common/src/main/java/dev/architectury/test/entity/TestEntity.java +++ b/testmod-common/src/main/java/dev/architectury/test/entity/TestEntity.java @@ -25,6 +25,7 @@ import io.netty.buffer.Unpooled; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.game.ClientGamePacketListener; import net.minecraft.resources.ResourceLocation; @@ -56,7 +57,7 @@ protected void tickDeath() { if (this.getLastAttacker() instanceof ServerPlayer player) { CompoundTag compoundTag = new CompoundTag(); compoundTag.putString("DeathCauser", player.getStringUUID()); - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), this.registryAccess()); buf.writeNbt(compoundTag); NetworkManager.sendToPlayer(player, new ResourceLocation("architectury_test", "sync_data"), buf); } diff --git a/testmod-common/src/main/java/dev/architectury/test/loot/TestLoot.java b/testmod-common/src/main/java/dev/architectury/test/loot/TestLoot.java index 6056a9a6d..0259dd72e 100644 --- a/testmod-common/src/main/java/dev/architectury/test/loot/TestLoot.java +++ b/testmod-common/src/main/java/dev/architectury/test/loot/TestLoot.java @@ -27,13 +27,13 @@ public class TestLoot { public static void init() { - /*LootEvent.MODIFY_LOOT_TABLE.register((lootTables, id, context, builtin) -> { + LootEvent.MODIFY_LOOT_TABLE.register((key, context, builtin) -> { // Check that the loot table is dirt and built-in - if (builtin && Blocks.DIRT.getLootTable().equals(id)) { + if (builtin && Blocks.DIRT.getLootTable().equals(key)) { // Create a loot pool with a single item entry of Items.DIAMOND LootPool.Builder pool = LootPool.lootPool().add(LootItem.lootTableItem(Items.DIAMOND)); context.addPool(pool); } - });*/ + }); } } diff --git a/testmod-common/src/main/java/dev/architectury/test/networking/ButtonClickedMessage.java b/testmod-common/src/main/java/dev/architectury/test/networking/ButtonClickedMessage.java index a1555b18b..c08289446 100644 --- a/testmod-common/src/main/java/dev/architectury/test/networking/ButtonClickedMessage.java +++ b/testmod-common/src/main/java/dev/architectury/test/networking/ButtonClickedMessage.java @@ -22,7 +22,7 @@ import dev.architectury.networking.NetworkManager; import dev.architectury.networking.simple.BaseC2SMessage; import dev.architectury.networking.simple.MessageType; -import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; public class ButtonClickedMessage extends BaseC2SMessage { @@ -35,7 +35,7 @@ public ButtonClickedMessage(int id) { buttonId = id; } - public ButtonClickedMessage(FriendlyByteBuf buf) { + public ButtonClickedMessage(RegistryFriendlyByteBuf buf) { buttonId = buf.readVarInt(); } @@ -45,7 +45,7 @@ public MessageType getType() { } @Override - public void write(FriendlyByteBuf buf) { + public void write(RegistryFriendlyByteBuf buf) { buf.writeVarInt(buttonId); } diff --git a/testmod-common/src/main/java/dev/architectury/test/networking/SyncDataMessage.java b/testmod-common/src/main/java/dev/architectury/test/networking/SyncDataMessage.java index d89ef41fc..ceb10afe7 100644 --- a/testmod-common/src/main/java/dev/architectury/test/networking/SyncDataMessage.java +++ b/testmod-common/src/main/java/dev/architectury/test/networking/SyncDataMessage.java @@ -23,7 +23,7 @@ import dev.architectury.networking.simple.BaseS2CMessage; import dev.architectury.networking.simple.MessageType; import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; public class SyncDataMessage extends BaseS2CMessage { @@ -38,7 +38,7 @@ public SyncDataMessage(CompoundTag tag) { serverData = tag; } - public SyncDataMessage(FriendlyByteBuf buf) { + public SyncDataMessage(RegistryFriendlyByteBuf buf) { serverData = buf.readNbt(); } @@ -48,7 +48,7 @@ public MessageType getType() { } @Override - public void write(FriendlyByteBuf buf) { + public void write(RegistryFriendlyByteBuf buf) { buf.writeNbt(serverData); } diff --git a/testmod-common/src/main/java/dev/architectury/test/networking/TestModNet.java b/testmod-common/src/main/java/dev/architectury/test/networking/TestModNet.java index c65e551cd..d71a7dab9 100644 --- a/testmod-common/src/main/java/dev/architectury/test/networking/TestModNet.java +++ b/testmod-common/src/main/java/dev/architectury/test/networking/TestModNet.java @@ -27,10 +27,16 @@ import dev.architectury.test.TestMod; import io.netty.buffer.Unpooled; import net.minecraft.network.FriendlyByteBuf; +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.minecraft.resources.ResourceLocation; +import net.minecraft.util.ExtraCodecs; import org.apache.commons.lang3.StringUtils; import java.util.Collections; +import java.util.List; public interface TestModNet { SimpleNetworkManager NET = SimpleNetworkManager.create(TestMod.MOD_ID); @@ -41,6 +47,7 @@ public interface TestModNet { // An example Server to Client message MessageType SYNC_DATA = NET.registerS2C("sync_data", SyncDataMessage::new); ResourceLocation BIG_DATA = new ResourceLocation(TestMod.MOD_ID, "big_data"); + CustomPacketPayload.Type BIG_DATA_PAYLOAD = new CustomPacketPayload.Type<>(new ResourceLocation(TestMod.MOD_ID, "big_data_payload")); String BIG_STRING = StringUtils.repeat('a', 100000); static void initialize() { @@ -58,15 +65,40 @@ static void initialize() { throw new AssertionError(utf); } }); + NetworkManager.registerReceiver(NetworkManager.Side.C2S, BIG_DATA_PAYLOAD, new StreamCodec<>() { + @Override + public BigDataPayload decode(RegistryFriendlyByteBuf object) { + return new BigDataPayload(object.readUtf(Integer.MAX_VALUE / 4)); + } + + @Override + public void encode(RegistryFriendlyByteBuf object, BigDataPayload payload) { + object.writeUtf(payload.data, Integer.MAX_VALUE / 4); + } + }, List.of(new SplitPacketTransformer()), (value, context) -> { + if (value.data().equals(BIG_STRING)) { + TestMod.SINK.accept("Network Split Packets worked"); + } else { + throw new AssertionError(value.data()); + } + }); } static void initializeClient() { ClientPlayerEvent.CLIENT_PLAYER_JOIN.register(player -> { - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + RegistryFriendlyByteBuf buf = new RegistryFriendlyByteBuf(Unpooled.buffer(), player.registryAccess()); buf.writeUtf(BIG_STRING, Integer.MAX_VALUE / 4); // write twice buf.writeUtf(BIG_STRING, Integer.MAX_VALUE / 4); NetworkManager.sendToServer(BIG_DATA, buf); + NetworkManager.sendToServer(new BigDataPayload(BIG_STRING)); }); } + + record BigDataPayload(String data) implements CustomPacketPayload { + @Override + public Type type() { + return TestModNet.BIG_DATA_PAYLOAD; + } + } } \ No newline at end of file diff --git a/testmod-neoforge/build.gradle b/testmod-neoforge/build.gradle index 962f05b79..b775ac48b 100644 --- a/testmod-neoforge/build.gradle +++ b/testmod-neoforge/build.gradle @@ -17,21 +17,15 @@ architectury { platformSetupLoomIde() neoForge { platformPackage = "forge" - remapForgeLike "net/minecraftforge/common/extensions/IForgeItem", "net/neoforged/neoforge/common/extensions/IItemExtension" - remapForgeLike "net/minecraftforge/client/event/TextureStitchEvent\$Post", "net/neoforged/neoforge/client/event/TextureAtlasStitchedEvent" - remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid", "net/neoforged/neoforge/fluids/BaseFlowingFluid" - remapForgeLike "net/minecraftforge/fluids/ForgeFlowingFluid\$Properties", "net/neoforged/neoforge/fluids/BaseFlowingFluid\$Properties" - remapForgeLike "net/minecraftforge/common/ForgeHooks", "net/neoforged/neoforge/common/CommonHooks" } } configurations { common forgeLike - compileClasspath.extendsFrom common, forgeLike - runtimeClasspath.extendsFrom common, forgeLike + compileClasspath.extendsFrom common + runtimeClasspath.extendsFrom common developmentNeoForge.extendsFrom common - developmentForgeLike.extendsFrom forgeLike } dependencies { @@ -40,5 +34,4 @@ dependencies { implementation(project(path: ":neoforge", configuration: "namedElements")) { transitive false } common(project(path: ":common", configuration: "namedElements")) { transitive false } common(project(path: ":testmod-common", configuration: "namedElements")) { transitive false } - forgeLike(project(path: ":forge", configuration: "namedElements")) { transitive false } } diff --git a/testmod-neoforge/src/main/resources/META-INF/mods.toml b/testmod-neoforge/src/main/resources/META-INF/neoforge.mods.toml similarity index 100% rename from testmod-neoforge/src/main/resources/META-INF/mods.toml rename to testmod-neoforge/src/main/resources/META-INF/neoforge.mods.toml