[ci skip] Port to NeoForge 24w14a

Signed-off-by: shedaniel <daniel@shedaniel.me>
This commit is contained in:
shedaniel
2024-04-09 18:16:18 +09:00
parent d19cb9a8f1
commit 4e15fa3bbb
88 changed files with 5191 additions and 569 deletions

View File

@@ -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")
}
}
}
}
}

View File

@@ -46,9 +46,9 @@ public interface LootEvent {
*
* <h2>Example: adding diamonds as a drop for dirt</h2>
* <pre>{@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<ModifyLootTable> MODIFY_LOOT_TABLE = EventFactory.createLoop();
Event<ModifyLootTable> MODIFY_LOOT_TABLE = EventFactory.createLoop();
@FunctionalInterface
interface ModifyLootTable {

View File

@@ -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 net.minecraft.world.level.material.Fluids;
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 @@ public final class FluidStack implements DataComponentHolder {
throw new AssertionError();
}
@Override
public DataComponentMap getComponents() {
return new DataComponentMap() {
@Nullable
@Override
public <T> T get(DataComponentType<? extends T> type) {
return getPatch().get(type).orElse(null);
}
@Override
public Set<DataComponentType<?>> keySet() {
return new AbstractSet<>() {
@Override
public Iterator<DataComponentType<?>> 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> T set(DataComponentType<? super T> 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, U> T update(DataComponentType<T> dataComponentType, T object, U object2, BiFunction<T, U, T> biFunction) {
return this.set(dataComponentType, biFunction.apply(this.getOrDefault(dataComponentType, object), object2));
}
@Nullable
public <T> T update(DataComponentType<T> dataComponentType, T object, UnaryOperator<T> unaryOperator) {
return this.set(dataComponentType, unaryOperator.apply(this.getOrDefault(dataComponentType, object)));
}
@Nullable
public <T> T remove(DataComponentType<? extends T> 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<DataComponentType<?>, 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> {
T create(Supplier<Fluid> fluid, long amount, @Nullable DataComponentPatch patch);
@@ -174,7 +80,19 @@ public final class FluidStack implements DataComponentHolder {
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> D set(T value, DataComponentType<? super D> type, @Nullable D component);
@Nullable <D> D remove(T value, DataComponentType<? extends D> type);
@Nullable <D> D update(T value, DataComponentType<D> type, D component, UnaryOperator<D> updater);
@Nullable <D, U> D update(T value, DataComponentType<D> type, D component, U updateContext, BiFunction<D, U, D> updater);
T copy(T value);
@@ -260,8 +178,37 @@ public final class FluidStack implements DataComponentHolder {
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> T set(DataComponentType<? super T> type, @Nullable T component) {
return ADAPTER.set(value, type, component);
}
@Nullable
public <T> T remove(DataComponentType<? extends T> type) {
return ADAPTER.remove(value, type);
}
@Nullable
public <T> T update(DataComponentType<T> type, T component, UnaryOperator<T> updater) {
return ADAPTER.update(value, type, component, updater);
}
@Nullable
public <T, U> T update(DataComponentType<T> type, T component, U updateContext, BiFunction<T, U, T> updater) {
return ADAPTER.update(value, type, component, updateContext, updater);
}
public Component getName() {

View File

@@ -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> 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<ResourceLocation, CustomPacketPayload.Type<BufCustomPacketPayload>> C2S_TYPE = new HashMap<>();
public static final Map<ResourceLocation, CustomPacketPayload.Type<BufCustomPacketPayload>> S2C_TYPE = new HashMap<>();
public static final Map<ResourceLocation, NetworkManager.NetworkReceiver<?>> C2S_RECEIVER = new HashMap<>();
public static final Map<ResourceLocation, NetworkManager.NetworkReceiver<?>> S2C_RECEIVER = new HashMap<>();
public static final Map<ResourceLocation, StreamCodec<ByteBuf, ?>> C2S_CODECS = new HashMap<>();
public static final Map<ResourceLocation, StreamCodec<ByteBuf, ?>> S2C_CODECS = new HashMap<>();
public static final Map<ResourceLocation, PacketTransformer> C2S_TRANSFORMERS = new HashMap<>();
public static final Map<ResourceLocation, PacketTransformer> S2C_TRANSFORMERS = new HashMap<>();
public static void registerReceiver(NetworkManager.Side side, ResourceLocation id, List<PacketTransformer> packetTransformers, NetworkManager.NetworkReceiver<RegistryFriendlyByteBuf> receiver) {
CustomPacketPayload.Type<BufCustomPacketPayload> 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 <T extends CustomPacketPayload> void registerReceiver(NetworkManager.Side side, CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, List<PacketTransformer> packetTransformers, NetworkManager.NetworkReceiver<T> 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 <T extends CustomPacketPayload> void registerC2SReceiver(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, List<PacketTransformer> packetTransformers, NetworkManager.NetworkReceiver<T> receiver) {
PacketTransformer transformer = PacketTransformer.concat(packetTransformers);
C2S_RECEIVER.put(type.id(), receiver);
C2S_CODECS.put(type.id(), (StreamCodec<ByteBuf, ?>) codec);
C2S_TRANSFORMERS.put(type.id(), transformer);
ADAPTOR.get().registerC2S((CustomPacketPayload.Type<BufCustomPacketPayload>) type, BufCustomPacketPayload.streamCodec((CustomPacketPayload.Type<BufCustomPacketPayload>) 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<T> networkReceiver = (NetworkManager.NetworkReceiver<T>) (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 <T extends CustomPacketPayload> void registerS2CReceiver(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, List<PacketTransformer> packetTransformers, NetworkManager.NetworkReceiver<T> receiver) {
PacketTransformer transformer = PacketTransformer.concat(packetTransformers);
S2C_RECEIVER.put(type.id(), receiver);
S2C_CODECS.put(type.id(), (StreamCodec<ByteBuf, ?>) codec);
S2C_TRANSFORMERS.put(type.id(), transformer);
ADAPTOR.get().registerS2C((CustomPacketPayload.Type<BufCustomPacketPayload>) type, BufCustomPacketPayload.streamCodec((CustomPacketPayload.Type<BufCustomPacketPayload>) 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<T> networkReceiver = (NetworkManager.NetworkReceiver<T>) (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 <T extends CustomPacketPayload> void collectPackets(PacketSink sink, NetworkManager.Side side, T payload, RegistryAccess access) {
CustomPacketPayload.Type<T> type = (CustomPacketPayload.Type<T>) payload.type();
PacketTransformer transformer = side == NetworkManager.Side.C2S ? C2S_TRANSFORMERS.get(type.id()) : S2C_TRANSFORMERS.get(type.id());
StreamCodec<ByteBuf, T> codec = (StreamCodec<ByteBuf, T>) (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<BufCustomPacketPayload> type1 = C2S_TYPE.getOrDefault(id1, (CustomPacketPayload.Type<BufCustomPacketPayload>) type);
sink.accept(toPacket(side1, new BufCustomPacketPayload(type1, ByteBufUtil.getBytes(buf1))));
} else if (side == NetworkManager.Side.S2C) {
CustomPacketPayload.Type<BufCustomPacketPayload> type1 = S2C_TYPE.getOrDefault(id1, (CustomPacketPayload.Type<BufCustomPacketPayload>) type);
sink.accept(toPacket(side1, new BufCustomPacketPayload(type1, ByteBufUtil.getBytes(buf1))));
}
});
} else {
sink.accept(toPacket(side, new BufCustomPacketPayload((CustomPacketPayload.Type<BufCustomPacketPayload>) type, ByteBufUtil.getBytes(buf))));
}
buf.release();
}
public static <T extends CustomPacketPayload> 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<PacketTransformer> packetTransformers) {
CustomPacketPayload.Type<BufCustomPacketPayload> type = new CustomPacketPayload.Type<>(id);
S2C_TYPE.put(id, type);
registerS2CType(type, BufCustomPacketPayload.streamCodec(type), packetTransformers);
}
public static <T extends CustomPacketPayload> void registerS2CType(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, List<PacketTransformer> packetTransformers) {
Objects.requireNonNull(type, "Cannot register a null type!");
packetTransformers = Objects.requireNonNullElse(packetTransformers, List.of());
S2C_CODECS.put(type.id(), (StreamCodec<ByteBuf, ?>) codec);
S2C_TRANSFORMERS.put(type.id(), PacketTransformer.concat(packetTransformers));
ADAPTOR.get().registerS2CType((CustomPacketPayload.Type<BufCustomPacketPayload>) type, BufCustomPacketPayload.streamCodec((CustomPacketPayload.Type<BufCustomPacketPayload>) type));
}
public interface Adaptor {
<T extends CustomPacketPayload> void registerC2S(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, NetworkManager.NetworkReceiver<T> receiver);
@Environment(EnvType.CLIENT)
<T extends CustomPacketPayload> void registerS2C(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, NetworkManager.NetworkReceiver<T> receiver);
<T extends CustomPacketPayload> Packet<?> toC2SPacket(T payload);
@Environment(EnvType.CLIENT)
<T extends CustomPacketPayload> Packet<?> toS2CPacket(T payload);
<T extends CustomPacketPayload> void registerS2CType(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec);
}
public record BufCustomPacketPayload(Type<BufCustomPacketPayload> _type,
byte[] payload) implements CustomPacketPayload {
@Override
public Type<? extends CustomPacketPayload> type() {
return this._type();
}
public static StreamCodec<ByteBuf, BufCustomPacketPayload> streamCodec(Type<BufCustomPacketPayload> type) {
return ByteBufCodecs.BYTE_ARRAY.map(bytes -> new BufCustomPacketPayload(type, bytes), BufCustomPacketPayload::payload);
}
}
}

View File

@@ -27,12 +27,16 @@ import io.netty.buffer.Unpooled;
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 final class NetworkChannel {
var s = UUID.nameUUIDFromBytes(type.getName().getBytes(StandardCharsets.UTF_8)).toString().replace("-", "");
var info = new MessageInfo<T>(new ResourceLocation(id + "/" + s), encoder, decoder, messageConsumer);
encoders.put(type, info);
NetworkManager.NetworkReceiver receiver = (buf, context) -> {
NetworkManager.NetworkReceiver<RegistryFriendlyByteBuf> receiver = (buf, context) -> {
info.messageConsumer.accept(info.decoder.apply(buf), () -> context);
};
NetworkManager.registerReceiver(NetworkManager.c2s(), info.packetId, receiver);
@@ -78,19 +82,21 @@ public final class NetworkChannel {
return h;
}
public <T> Packet<?> toPacket(NetworkManager.Side side, T message) {
public <T> Packet<?> toPacket(NetworkManager.Side side, T message, RegistryAccess access) {
var messageInfo = (MessageInfo<T>) 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 <T> 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 <T> void sendToPlayers(Iterable<ServerPlayer> players, T message) {
var packet = toPacket(NetworkManager.s2c(), message);
Iterator<ServerPlayer> 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 final class NetworkChannel {
@Environment(EnvType.CLIENT)
public <T> 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!");
}

View File

@@ -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 net.minecraft.world.entity.player.Player;
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 <T extends CustomPacketPayload> void registerS2CPayloadType(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> 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<PacketTransformer> 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 <T extends CustomPacketPayload> void registerS2CPayloadType(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, List<PacketTransformer> packetTransformers) {
NetworkAggregator.registerS2CType(type, codec, packetTransformers);
}
public static void registerReceiver(Side side, ResourceLocation id, NetworkReceiver<RegistryFriendlyByteBuf> receiver) {
registerReceiver(side, id, Collections.emptyList(), receiver);
}
@ExpectPlatform
@ApiStatus.Experimental
public static void registerReceiver(Side side, ResourceLocation id, List<PacketTransformer> packetTransformers, NetworkReceiver receiver) {
throw new AssertionError();
public static void registerReceiver(Side side, ResourceLocation id, List<PacketTransformer> packetTransformers, NetworkReceiver<RegistryFriendlyByteBuf> receiver) {
NetworkAggregator.registerReceiver(side, id, packetTransformers, receiver);
}
public static <T extends CustomPacketPayload> void registerReceiver(Side side, CustomPacketPayload.Type<T> id, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, NetworkReceiver<T> receiver) {
registerReceiver(side, id, codec, Collections.emptyList(), receiver);
}
@ApiStatus.Experimental
public static <T extends CustomPacketPayload> void registerReceiver(Side side, CustomPacketPayload.Type<T> id, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, List<PacketTransformer> packetTransformers, NetworkReceiver<T> 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<Packet<?>> toPackets(Side side, ResourceLocation id, FriendlyByteBuf buf) {
public static List<Packet<?>> 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 <T extends CustomPacketPayload> 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<ServerPlayer> players, ResourceLocation id, FriendlyByteBuf buf) {
public static void sendToPlayers(Iterable<ServerPlayer> 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 <T extends CustomPacketPayload> void sendToPlayer(ServerPlayer player, T payload) {
collectPackets(PacketSink.ofPlayer(player), serverToClient(), payload, player.registryAccess());
}
public static <T extends CustomPacketPayload> void sendToPlayers(Iterable<ServerPlayer> players, T payload) {
Iterator<ServerPlayer> iterator = players.iterator();
if (!iterator.hasNext()) return;
collectPackets(PacketSink.ofPlayers(players), serverToClient(), payload, iterator.next().registryAccess());
}
@Environment(EnvType.CLIENT)
public static <T extends CustomPacketPayload> 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 final class NetworkManager {
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<T> {
void receive(T value, PacketContext context);
}
public interface PacketContext {
@@ -124,6 +194,8 @@ public final class NetworkManager {
Env getEnvironment();
RegistryAccess registryAccess();
default EnvType getEnv() {
return getEnvironment().toPlatform();
}

View File

@@ -26,6 +26,7 @@ import net.fabricmc.api.Environment;
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 class SpawnEntityPacket {
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 class SpawnEntityPacket {
return (Packet<ClientGamePacketListener>) NetworkManager.toPacket(NetworkManager.s2c(), PACKET_ID, buffer);
}
public static void register() {
NetworkManager.registerS2CPayloadType(PACKET_ID);
}
@Environment(EnvType.CLIENT)
public static class Client {

View File

@@ -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!");
}

View File

@@ -44,7 +44,7 @@ public abstract class BaseS2CMessage extends Message {
* @param player the player
*/
public final void sendTo(ServerPlayer player) {
sendTo(player, toPacket());
sendTo(player, toPacket(player.registryAccess()));
}
/**
@@ -53,7 +53,8 @@ public abstract class BaseS2CMessage extends Message {
* @param players the players
*/
public final void sendTo(Iterable<ServerPlayer> 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 abstract class BaseS2CMessage extends Message {
* @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));
}
}

View File

@@ -21,7 +21,8 @@ package dev.architectury.networking.simple;
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);
}

View File

@@ -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 <T> the message type handled by this decoder
* @author LatvianModder
@@ -36,17 +36,17 @@ public interface MessageDecoder<T extends Message> {
* @param buf the byte buffer
* @return the decoded instance
*/
T decode(FriendlyByteBuf buf);
T decode(RegistryFriendlyByteBuf buf);
/**
* Creates a network receiver from this decoder.
*
* <p>The returned receiver will first {@linkplain #decode(FriendlyByteBuf) decode a message}
* <p>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<RegistryFriendlyByteBuf> createReceiver() {
return (buf, context) -> {
Message packet = decode(buf);
context.queue(() -> packet.handle(context));

View File

@@ -23,6 +23,7 @@ import dev.architectury.networking.NetworkManager;
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 class SimpleNetworkManager {
MessageType messageType = new MessageType(this, new ResourceLocation(namespace, id), NetworkManager.s2c());
if (Platform.getEnvironment() == Env.CLIENT) {
NetworkManager.NetworkReceiver receiver = decoder.createReceiver();
NetworkManager.NetworkReceiver<RegistryFriendlyByteBuf> receiver = decoder.createReceiver();
NetworkManager.registerReceiver(NetworkManager.s2c(), messageType.getId(), transformers, receiver);
}
@@ -107,7 +108,7 @@ public class SimpleNetworkManager {
@ApiStatus.Experimental
public MessageType registerC2S(String id, MessageDecoder<BaseC2SMessage> decoder, List<PacketTransformer> transformers) {
MessageType messageType = new MessageType(this, new ResourceLocation(namespace, id), NetworkManager.c2s());
NetworkManager.NetworkReceiver receiver = decoder.createReceiver();
NetworkManager.NetworkReceiver<RegistryFriendlyByteBuf> receiver = decoder.createReceiver();
NetworkManager.registerReceiver(NetworkManager.c2s(), messageType.getId(), transformers, receiver);
return messageType;
}

View File

@@ -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;

View File

@@ -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 @@ import java.util.List;
@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 @@ public interface PacketTransformer {
}
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<? extends PacketTransformer>) transformers).size() > index) {
PacketTransformer transformer = ((List<? extends PacketTransformer>) transformers).get(index);

View File

@@ -28,7 +28,7 @@ import io.netty.buffer.ByteBuf;
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 class SplitPacketTransformer implements PacketTransformer {
private static class PartData {
private final ResourceLocation id;
private final int partsExpected;
private final List<FriendlyByteBuf> parts;
private final List<RegistryFriendlyByteBuf> parts;
public PartData(ResourceLocation id, int partsExpected) {
this.id = id;
@@ -109,7 +109,7 @@ public class SplitPacketTransformer implements 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) {
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 class SplitPacketTransformer implements PacketTransformer {
} 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 class SplitPacketTransformer implements PacketTransformer {
} 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 class SplitPacketTransformer implements PacketTransformer {
}
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 class SplitPacketTransformer implements PacketTransformer {
}
@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);

View File

@@ -38,7 +38,7 @@ import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
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 class EventHandlerImpl {
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);

View File

@@ -25,24 +25,28 @@ import com.mojang.serialization.codecs.RecordCodecBuilder;
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<FluidStackImpl.Pair> {
INSTANCE;
@@ -60,13 +64,30 @@ public enum FluidStackImpl implements FluidStack.FluidStackAdapter<FluidStackImp
}
public static class Pair {
public FluidVariant variant;
public Fluid fluid;
public PatchedDataComponentMap components;
public long amount;
public Pair(FluidVariant variant, long amount) {
this.variant = variant;
public Pair(Fluid fluid, @Nullable DataComponentPatch patch, long amount) {
this(fluid,
patch == null ? new PatchedDataComponentMap(DataComponentMap.EMPTY)
: PatchedDataComponentMap.fromPatch(DataComponentMap.EMPTY, patch),
amount);
}
public Pair(Fluid fluid, PatchedDataComponentMap components, long amount) {
this.fluid = fluid;
this.components = components;
this.amount = amount;
}
public FluidVariant toVariant() {
return FluidVariant.of(fluid, getPatch());
}
public DataComponentPatch getPatch() {
return amount <= 0L || this.fluid == Fluids.EMPTY ? components.asPatch() : DataComponentPatch.EMPTY;
}
}
@Override
@@ -75,17 +96,17 @@ public enum FluidStackImpl implements FluidStack.FluidStackAdapter<FluidStackImp
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<Fluid> 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 enum FluidStackImpl implements FluidStack.FluidStackAdapter<FluidStackImp
}
public DataComponentPatch getPatch(FluidStackImpl.Pair value) {
return value.variant.getComponents();
return value.getPatch();
}
@Override
public void setPatch(FluidStackImpl.Pair value, DataComponentPatch patch) {
value.variant = FluidVariant.of(value.variant.getFluid(), patch);
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> D set(Pair value, DataComponentType<? super D> type, @Nullable D component) {
return value.components.set(type, component);
}
@Override
@Nullable
public <D> D remove(Pair value, DataComponentType<? extends D> type) {
return value.components.remove(type);
}
@Override
@Nullable
public <D> D update(Pair value, DataComponentType<D> type, D component, UnaryOperator<D> updater) {
return value.components.set(type, updater.apply(getComponents(value).getOrDefault(type, component)));
}
@Override
@Nullable
public <D, U> D update(Pair value, DataComponentType<D> type, D component, U updateContext, BiFunction<D, U, D> 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 enum FluidStackImpl implements FluidStack.FluidStackAdapter<FluidStackImp
? 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));
}

View File

@@ -24,7 +24,6 @@ import dev.architectury.fluid.fabric.FluidStackImpl;
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 final class FluidStackHooksFabric {
}
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();
}
}

View File

@@ -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.Entity;
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<ResourceLocation, CustomPacketPayload.Type<BufCustomPacketPayload>> C2S_TYPE = new HashMap<>();
private static final Map<ResourceLocation, CustomPacketPayload.Type<BufCustomPacketPayload>> S2C_TYPE = new HashMap<>();
private static final Map<ResourceLocation, NetworkReceiver> C2S_RECEIVER = new HashMap<>();
private static final Map<ResourceLocation, NetworkReceiver> S2C_RECEIVER = new HashMap<>();
private static final Map<ResourceLocation, PacketTransformer> C2S_TRANSFORMERS = new HashMap<>();
private static final Map<ResourceLocation, PacketTransformer> S2C_TRANSFORMERS = new HashMap<>();
private static final Logger LOGGER = LogUtils.getLogger();
public static void registerReceiver(NetworkManager.Side side, ResourceLocation id, List<PacketTransformer> 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<PacketTransformer> packetTransformers, NetworkReceiver receiver) {
LOGGER.info("Registering C2S receiver with id {}", id);
C2S_RECEIVER.put(id, receiver);
CustomPacketPayload.Type<BufCustomPacketPayload> 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<PacketTransformer> packetTransformers, NetworkReceiver receiver) {
LOGGER.info("Registering S2C receiver with id {}", id);
S2C_RECEIVER.put(id, receiver);
CustomPacketPayload.Type<BufCustomPacketPayload> 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 <T extends CustomPacketPayload> void registerC2S(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, NetworkReceiver<T> 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 <T extends CustomPacketPayload> void registerS2C(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, NetworkReceiver<T> 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 <T extends CustomPacketPayload> Packet<?> toC2SPacket(T payload) {
return ClientPlayNetworking.createC2SPacket(payload);
}
@Override
public <T extends CustomPacketPayload> Packet<?> toS2CPacket(T payload) {
return ServerPlayNetworking.createS2CPacket(payload);
}
@Override
public <T extends CustomPacketPayload> void registerS2CType(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec) {
PayloadTypeRegistry.playS2C().register(type, codec);
}
};
}
private static NetworkManager.PacketContext context(Player player, BlockableEventLoop<?> taskQueue, boolean client) {
@@ -135,30 +102,14 @@ public class NetworkManagerImpl {
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 class NetworkManagerImpl {
public static Packet<ClientGamePacketListener> createAddEntityPacket(Entity entity) {
return SpawnEntityPacket.create(entity);
}
@Environment(EnvType.CLIENT)
private static Packet<?> toC2SPacket(ResourceLocation id, FriendlyByteBuf buf) {
CustomPacketPayload.Type<BufCustomPacketPayload> 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<BufCustomPacketPayload> type = S2C_TYPE.get(id);
if (type == null) {
throw new IllegalArgumentException("Unknown packet id: " + id);
}
return ServerPlayNetworking.createS2CPacket(new BufCustomPacketPayload(type, ByteBufUtil.getBytes(buf)));
}
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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<? extends FlowingFluid> fluid, Properties properties) {
super(fluid, properties);
}
}

View File

@@ -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<ArchitecturyFluidAttributes, FluidType> 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<SoundEvent> 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<Fluid, FluidState> 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;
}
}
}

View File

@@ -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<IClientFluidTypeExtensions> 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());
}
}

View File

@@ -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<Codec<? extends BiomeModifier>> codecSupplier) {
EventBusesHooksImpl.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.<RegisterEvent>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<? extends Fluid> fluid, Properties properties) {
super(fluid, properties);
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.<RegisterCapabilitiesEvent>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();
}
}

View File

@@ -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<? extends EntityType<?>> entity, Supplier<? extends Fluid> fluid, Supplier<? extends SoundEvent> sound, Properties properties) {
super(entity, fluid, sound, properties);
}
}

View File

@@ -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 <T> Event<Consumer<T>> attachToForge(Event<Consumer<T>> 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 <T> Event<EventActor<T>> attachToForgeEventActor(Event<EventActor<T>> 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 <T> Event<EventActor<T>> attachToForgeEventActorCancellable(Event<EventActor<T>> 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;
}
}

View File

@@ -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);
// });
}
}

View File

@@ -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<Component> 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<Screen> 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<TooltipEventPositionContextImpl> 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<ClientCommandRegistrationEvent.ClientCommandSourceStack>)
(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);
}
}
}

View File

@@ -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<ItemStack> 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<ItemStack> 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();
}
}
}

View File

@@ -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 {
}
}

View File

@@ -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<LootPool> pools;
LootTableModificationContextImpl(LootTable table) {
this.table = table;
// This field has the type changed to List<LootPool> by Forge
// Since this is rather unsafe, we are making sure 100% we are getting it
List<LootPool> pools = null;
try {
Field field = LootTable.class.getDeclaredField("pools");
field.setAccessible(true);
try {
pools = (List<LootPool>) 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<LootPool>) 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());
}
}

View File

@@ -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<FluidStack> {
INSTANCE;
static {
dev.architectury.fluid.FluidStack.init();
}
public static Function<dev.architectury.fluid.FluidStack, Object> toValue;
public static Function<Object, dev.architectury.fluid.FluidStack> fromValue;
public static dev.architectury.fluid.FluidStack.FluidStackAdapter<Object> adapt(Function<dev.architectury.fluid.FluidStack, Object> toValue, Function<Object, dev.architectury.fluid.FluidStack> fromValue) {
FluidStackImpl.toValue = toValue;
FluidStackImpl.fromValue = fromValue;
return (dev.architectury.fluid.FluidStack.FluidStackAdapter<Object>) (dev.architectury.fluid.FluidStack.FluidStackAdapter<?>) INSTANCE;
}
@Override
public FluidStack create(Supplier<Fluid> fluid, long amount, @Nullable DataComponentPatch patch) {
@SuppressWarnings("deprecation")
Holder<Fluid> 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<Fluid> 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> D set(FluidStack value, DataComponentType<? super D> type, @Nullable D component) {
return value.set(type, component);
}
@Override
@Nullable
public <D> D remove(FluidStack value, DataComponentType<? extends D> type) {
return value.remove(type);
}
@Override
@Nullable
public <D> D update(FluidStack value, DataComponentType<D> type, D component, UnaryOperator<D> updater) {
return value.update(type, component, updater);
}
@Override
@Nullable
public <D, U> D update(FluidStack value, DataComponentType<D> type, D component, U updateContext, BiFunction<D, U, D> 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<dev.architectury.fluid.FluidStack> codec() {
return FluidStack.CODEC.xmap(FluidStackHooksForge::fromForge, FluidStackHooksForge::toForge);
}
@Override
public StreamCodec<RegistryFriendlyByteBuf, dev.architectury.fluid.FluidStack> streamCodec() {
return FluidStack.STREAM_CODEC.map(FluidStackHooksForge::fromForge, FluidStackHooksForge::toForge);
}
}

View File

@@ -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<NarratableEntry> getNarratables(Screen screen) {
return screen.narratables;
}
public static List<Renderable> getRenderables(Screen screen) {
return screen.renderables;
}
public static <T extends AbstractWidget & Renderable & NarratableEntry> T addRenderableWidget(Screen screen, T widget) {
return screen.addRenderableWidget(widget);
}
public static <T extends Renderable> T addRenderableOnly(Screen screen, T listener) {
return screen.addRenderableOnly(listener);
}
public static <T extends GuiEventListener & NarratableEntry> T addWidget(Screen screen, T listener) {
return screen.addWidget(listener);
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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<FluidStack> 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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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<String> getLeft(CustomizeGuiOverlayEvent.DebugText event) {
return event.getLeft();
}
public static List<String> getRight(CustomizeGuiOverlayEvent.DebugText event) {
return event.getRight();
}
}

View File

@@ -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<? extends MobEffectInstance> effectSupplier, float chance) {
builder.effect((Supplier<MobEffectInstance>) effectSupplier, chance);
}
}

View File

@@ -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();
}
}

View File

@@ -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<UseOnContext> predicate, Consumer<UseOnContext> action, Function<UseOnContext, BlockState> function) {
NeoForge.EVENT_BUS.<BlockEvent.BlockToolModificationEvent>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));
}
});
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
};
}
}

View File

@@ -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;
}
}

View File

@@ -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<Level> arg2, RegistryAccess arg3, Holder<DimensionType> arg4, Supplier<ProfilerFiller> 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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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<LevelAccessor> level;
@Override
public LevelAccessor architectury$getAttachedLevel() {
return level == null ? null : level.get();
}
@Override
public void architectury$attachLevel(LevelAccessor level) {
this.level = new WeakReference<>(level);
}
}

View File

@@ -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);
}
}

View File

@@ -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<Component> 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();
}
}

View File

@@ -34,5 +34,6 @@ public class ArchitecturyNeoForge {
BiomeModificationsImpl.init();
EnvExecutor.runInEnv(Env.CLIENT, () -> SpawnEntityPacket.Client::register);
EnvExecutor.runInEnv(Env.SERVER, () -> SpawnEntityPacket::register);
}
}

View File

@@ -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<BufCustomPacketPayload> type, byte[] payload) implements CustomPacketPayload {
public BufCustomPacketPayload(Type<BufCustomPacketPayload> 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<ByteBuf, BufCustomPacketPayload> streamCodec(Type<BufCustomPacketPayload> type) {
return ByteBufCodecs.BYTE_ARRAY.map(bytes -> new BufCustomPacketPayload(type, bytes), BufCustomPacketPayload::payload);
}
}

View File

@@ -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.api.distmarker.OnlyIn;
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<BufCustomPacketPayload> 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<ResourceLocation> receivables = NetworkManagerImpl.serverReceivables;
int size = buffer.readInt();
receivables.clear();
@@ -47,17 +47,23 @@ public class ClientNetworkingManager {
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();

View File

@@ -22,152 +22,133 @@ package dev.architectury.networking.forge;
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<PacketTransformer> 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<ResourceLocation, NetworkReceiver> S2C = Maps.newHashMap();
static final Map<ResourceLocation, NetworkReceiver> C2S = Maps.newHashMap();
static final Map<ResourceLocation, PacketTransformer> S2C_TRANSFORMERS = Maps.newHashMap();
static final Map<ResourceLocation, PacketTransformer> 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<ResourceLocation> serverReceivables = Sets.newHashSet();
private static final Multimap<Player, ResourceLocation> 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<ResourceLocation> receivables = (Set<ResourceLocation>) 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<BufCustomPacketPayload> createPacketHandler(NetworkManager.Side direction, Map<ResourceLocation, PacketTransformer> 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);
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);
public static NetworkAggregator.Adaptor getAdaptor() {
return new NetworkAggregator.Adaptor() {
@Override
public <T extends CustomPacketPayload> void registerC2S(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, NetworkReceiver<T> receiver) {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.<RegisterPayloadHandlerEvent>addListener(event -> {
event.registrar(type.id().getNamespace()).optional().play(type, codec, (arg, context) -> {
receiver.receive(arg, context(context.player().orElse(null), context.workHandler(), false));
});
});
} else {
LOGGER.error("Unknown message ID: " + type);
}
} finally {
buf.release();
});
}
@Override
public <T extends CustomPacketPayload> void registerS2C(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> codec, NetworkReceiver<T> receiver) {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.<RegisterPayloadHandlerEvent>addListener(event -> {
event.registrar(type.id().getNamespace()).optional().play(type, codec, (arg, context) -> {
receiver.receive(arg, context(context.player().orElse(null), context.workHandler(), true));
});
});
});
}
@Override
public <T extends CustomPacketPayload> Packet<?> toC2SPacket(T payload) {
return new ServerboundCustomPayloadPacket(payload);
}
@Override
public <T extends CustomPacketPayload> Packet<?> toS2CPacket(T payload) {
return new ClientboundCustomPayloadPacket(payload);
}
@Override
public <T extends CustomPacketPayload> void registerS2CType(CustomPacketPayload.Type<T> type, StreamCodec<? super RegistryFriendlyByteBuf, T> 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<PacketTransformer> 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<PacketTransformer> 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 class NetworkManagerImpl {
return SpawnEntityPacket.create(entity);
}
static FriendlyByteBuf sendSyncPacket(Map<ResourceLocation, NetworkReceiver> map) {
static RegistryFriendlyByteBuf sendSyncPacket(Map<ResourceLocation, ?> map, RegistryAccess access) {
List<ResourceLocation> 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 @@ public class NetworkManagerImpl {
@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 class NetworkManagerImpl {
clientReceivables.removeAll(event.getEntity());
}
/**
* Needs to be on the mod bus for some reason...
*/
public static void registerPackets(RegisterPayloadHandlerEvent event) {
//noinspection removal
@Nullable
IPlayPayloadHandler<BufCustomPacketPayload> 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<ResourceLocation> receivables = (Set<ResourceLocation>) 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;
}

View File

@@ -28,7 +28,7 @@ import net.neoforged.fml.loading.FMLEnvironment;
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 class PlatformImpl {
@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));
}
}
}

View File

@@ -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 net.neoforged.fml.ModList;
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<IEventBus> busConsumer) {
IEventBus bus = getModEventBus(modId).orElseThrow(() -> new IllegalStateException("Mod '" + modId + "' is not available!"));
busConsumer.accept(bus);

View File

@@ -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<String> myTargets, Set<String> otherTargets) {
}
@Override
public List<String> 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) {
}
}

View File

@@ -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<Entry<?>> entries = new ArrayList<>();
static {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.<RegisterClientTooltipComponentFactoriesEvent>addListener(EventPriority.HIGH, event -> {
if (entries != null) {
for (Entry<?> entry : entries) {
Entry<TooltipComponent> casted = (Entry<TooltipComponent>) entry;
event.register(casted.clazz(), casted.factory());
}
entries = null;
}
});
});
}
public static <T extends TooltipComponent> void register(Class<T> clazz, Function<? super T, ? extends ClientTooltipComponent> 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<T extends TooltipComponent>(
Class<T> clazz, Function<? super T, ? extends ClientTooltipComponent> factory
) {
}
}

View File

@@ -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<KeyMapping> 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;
}
}

View File

@@ -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<ModelLayerLocation, Supplier<LayerDefinition>> DEFINITIONS = new ConcurrentHashMap<>();
static {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.register(EntityModelLayerRegistryImpl.class);
});
}
public static void register(ModelLayerLocation location, Supplier<LayerDefinition> definition) {
DEFINITIONS.put(location, definition);
}
@SubscribeEvent
public static void event(EntityRenderersEvent.RegisterLayerDefinitions event) {
for (Map.Entry<ModelLayerLocation, Supplier<LayerDefinition>> entry : DEFINITIONS.entrySet()) {
event.registerLayerDefinition(entry.getKey(), entry.getValue());
}
}
}

View File

@@ -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<Supplier<EntityType<?>>, EntityRendererProvider<?>> RENDERERS = new ConcurrentHashMap<>();
public static <T extends Entity> void register(Supplier<? extends EntityType<? extends T>> type, EntityRendererProvider<T> factory) {
RENDERERS.put((Supplier<EntityType<?>>) (Supplier<? extends EntityType<?>>) type, factory);
}
static {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.register(EntityRendererRegistryImpl.class);
});
}
@SubscribeEvent
public static void event(EntityRenderersEvent.RegisterRenderers event) {
for (Map.Entry<Supplier<EntityType<?>>, EntityRendererProvider<?>> entry : RENDERERS.entrySet()) {
event.registerEntityRenderer(entry.getKey().get(), (EntityRendererProvider<Entity>) entry.getValue());
}
}
}

View File

@@ -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<TextureAtlasSprite> 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<Consumer<ParticleProviderRegistrar>> deferred = new ArrayList<>();
private static <T extends ParticleOptions> void doRegister(ParticleProviderRegistrar registrar, ParticleType<T> type, ParticleProvider<T> provider) {
registrar.register(type, provider);
}
private static <T extends ParticleOptions> void doRegister(ParticleProviderRegistrar registrar, ParticleType<T> type, ParticleProviderRegistry.DeferredParticleProvider<T> provider) {
registrar.register(type, sprites ->
provider.create(new ExtendedSpriteSetImpl(Minecraft.getInstance().particleEngine, sprites)));
}
public static <T extends ParticleOptions> void register(ParticleType<T> type, ParticleProvider<T> 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 <T extends ParticleOptions> void register(ParticleType<T> type, ParticleProviderRegistry.DeferredParticleProvider<T> 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<ParticleProviderRegistrar> consumer : deferred) {
consumer.accept(registrar);
}
// yeet deferred list - register immediately from now on
deferred = null;
}
}
private interface ParticleProviderRegistrar {
<T extends ParticleOptions> void register(ParticleType<T> type, ParticleProvider<T> provider);
<T extends ParticleOptions> void register(ParticleType<T> type, ParticleEngine.SpriteParticleRegistration<T> registration);
static ParticleProviderRegistrar ofForge(RegisterParticleProvidersEvent event) {
return new ParticleProviderRegistrar() {
@Override
public <T extends ParticleOptions> void register(ParticleType<T> type, ParticleProvider<T> provider) {
event.registerSpecial(type, provider);
}
@Override
public <T extends ParticleOptions> void register(ParticleType<T> type, ParticleEngine.SpriteParticleRegistration<T> registration) {
event.registerSpriteSet(type, registration);
}
};
}
static ParticleProviderRegistrar ofFallback() {
return new ParticleProviderRegistrar() {
@Override
public <T extends ParticleOptions> void register(ParticleType<T> type, ParticleProvider<T> provider) {
Minecraft.getInstance().particleEngine.register(type, provider);
}
@Override
public <T extends ParticleOptions> void register(ParticleType<T> type, ParticleEngine.SpriteParticleRegistration<T> registration) {
Minecraft.getInstance().particleEngine.register(type, registration);
}
};
}
}
}

View File

@@ -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 <T extends BlockEntity> void register(BlockEntityType<T> type, BlockEntityRendererProvider<? super T> provider) {
BlockEntityRenderers.register(type, provider);
}
}

View File

@@ -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<Pair<ItemColor, Supplier<? extends ItemLike>[]>> ITEM_COLORS = Lists.newArrayList();
private static final List<Pair<BlockColor, Supplier<? extends Block>[]>> 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<ItemColor, Supplier<? extends ItemLike>[]> pair : ITEM_COLORS) {
event.register(pair.getLeft(), unpackItems(pair.getRight()));
}
}
@SubscribeEvent
public static void onBlockColorEvent(RegisterColorHandlersEvent.Block event) {
for (Pair<BlockColor, Supplier<? extends Block>[]> pair : BLOCK_COLORS) {
event.register(pair.getLeft(), unpackBlocks(pair.getRight()));
}
}
@SafeVarargs
public static void registerItemColors(ItemColor itemColor, Supplier<? extends ItemLike>... 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<? extends Block>... 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<? extends ItemLike>[] 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<? extends Block>[] blocks) {
Block[] array = new Block[blocks.length];
for (int i = 0; i < blocks.length; i++) {
array[i] = Objects.requireNonNull(blocks[i].get());
}
return array;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<Consumer<BuildCreativeModeTabContentsEvent>> BUILD_CONTENTS_LISTENERS = new ArrayList<>();
private static final Multimap<TabKey, Supplier<ItemStack>> APPENDS = MultimapBuilder.hashKeys().arrayListValues().build();
static {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.addListener(CreativeTabRegistryImpl::event);
});
BUILD_CONTENTS_LISTENERS.add(event -> {
for (Map.Entry<TabKey, Collection<Supplier<ItemStack>>> keyEntry : APPENDS.asMap().entrySet()) {
Supplier<List<ItemStack>> 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<BuildCreativeModeTabContentsEvent> listener : BUILD_CONTENTS_LISTENERS) {
listener.accept(event);
}
}
@ApiStatus.Experimental
public static CreativeModeTab create(Consumer<CreativeModeTab.Builder> callback) {
CreativeModeTab.Builder builder = CreativeModeTab.builder();
callback.accept(builder);
return builder.build();
}
@ApiStatus.Experimental
public static DeferredSupplier<CreativeModeTab> 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<CreativeModeTab> 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<CreativeModeTab> 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<ItemStack, CreativeModeTab.TabVisibility> 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<CreativeModeTab> tab, Supplier<ItemStack> item) {
APPENDS.put(new TabKey.SupplierTabKey(tab), item);
}
private interface TabKey {
record SupplierTabKey(DeferredSupplier<CreativeModeTab> 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);
}
}
}
}

View File

@@ -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<PreparableReloadListener> serverDataReloadListeners = Lists.newArrayList();
static {
NeoForge.EVENT_BUS.addListener(ReloadListenerRegistryImpl::addReloadListeners);
}
public static void register(PackType type, PreparableReloadListener listener, @Nullable ResourceLocation listenerId, Collection<ResourceLocation> 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);
}
}
}

View File

@@ -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<ItemLike> 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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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<Pair<Predicate<BiomeContext>, BiConsumer<BiomeContext, BiomeProperties.Mutable>>> ADDITIONS = Lists.newArrayList();
private static final List<Pair<Predicate<BiomeContext>, BiConsumer<BiomeContext, BiomeProperties.Mutable>>> POST_PROCESSING = Lists.newArrayList();
private static final List<Pair<Predicate<BiomeContext>, BiConsumer<BiomeContext, BiomeProperties.Mutable>>> REMOVALS = Lists.newArrayList();
private static final List<Pair<Predicate<BiomeContext>, BiConsumer<BiomeContext, BiomeProperties.Mutable>>> REPLACEMENTS = Lists.newArrayList();
@Nullable
private static MapCodec<BiomeModifierImpl> noneBiomeModCodec = null;
public static void init() {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.<RegisterEvent>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<BiomeContext> predicate, BiConsumer<BiomeContext, BiomeProperties.Mutable> modifier) {
ADDITIONS.add(Pair.of(predicate, modifier));
}
public static void postProcessProperties(Predicate<BiomeContext> predicate, BiConsumer<BiomeContext, BiomeProperties.Mutable> modifier) {
POST_PROCESSING.add(Pair.of(predicate, modifier));
}
public static void removeProperties(Predicate<BiomeContext> predicate, BiConsumer<BiomeContext, BiomeProperties.Mutable> modifier) {
REMOVALS.add(Pair.of(predicate, modifier));
}
public static void replaceProperties(Predicate<BiomeContext> predicate, BiConsumer<BiomeContext, BiomeProperties.Mutable> 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<Biome> arg, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder) {
List<Pair<Predicate<BiomeContext>, BiConsumer<BiomeContext, BiomeProperties.Mutable>>> 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<? extends BiomeModifier> codec() {
if (noneBiomeModCodec != null) {
return noneBiomeModCodec;
} else {
return MapCodec.unit(INSTANCE);
}
}
}
private static BiomeContext wrapSelectionContext(Optional<ResourceKey<Biome>> biomeResourceKey, ModifiableBiomeInfo.BiomeInfo.Builder event) {
return new BiomeContext() {
BiomeProperties properties = new BiomeWrapped(event);
@Override
public Optional<ResourceLocation> getKey() {
return biomeResourceKey.map(ResourceKey::location);
}
@Override
public BiomeProperties getProperties() {
return properties;
}
@Override
public boolean hasTag(TagKey<Biome> tag) {
MinecraftServer server = GameInstance.getServer();
if (server != null) {
Optional<? extends Registry<Biome>> registry = server.registryAccess().registry(Registries.BIOME);
if (registry.isPresent()) {
Optional<Holder.Reference<Biome>> 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<Holder<ConfiguredWorldCarver<?>>> getCarvers(GenerationStep.Carving carving) {
return generation.getCarvers(carving);
}
@Override
public Iterable<Holder<PlacedFeature>> getFeatures(GenerationStep.Decoration decoration) {
return generation.getFeatures(decoration);
}
@Override
public List<Iterable<Holder<PlacedFeature>>> getFeatures() {
return (List<Iterable<Holder<PlacedFeature>>>) (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<MobCategory, List<MobSpawnSettings.SpawnerData>> getSpawners() {
return builder.spawners;
}
@Override
public Map<EntityType<?>, 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<AmbientParticleSettings> getAmbientParticle() {
return builder.ambientParticle;
}
@Override
public Optional<Holder<SoundEvent>> getAmbientLoopSound() {
return builder.ambientLoopSoundEvent;
}
@Override
public Optional<AmbientMoodSettings> getAmbientMoodSound() {
return builder.ambientMoodSettings;
}
@Override
public Optional<AmbientAdditionsSettings> getAmbientAdditionsSound() {
return builder.ambientAdditionsSettings;
}
@Override
public Optional<Music> 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<SoundEvent> 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<PlacedFeature> feature) {
generation.addFeature(decoration, feature);
return this;
}
@Override
public Mutable addFeature(GenerationStep.Decoration decoration, ResourceKey<PlacedFeature> feature) {
MinecraftServer server = GameInstance.getServer();
if (server != null) {
Optional<? extends Registry<PlacedFeature>> registry = server.registryAccess().registry(Registries.PLACED_FEATURE);
if (registry.isPresent()) {
Optional<Holder.Reference<PlacedFeature>> 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<ConfiguredWorldCarver<?>> feature) {
generation.addCarver(carving, feature);
return this;
}
@Override
public Mutable addCarver(GenerationStep.Carving carving, ResourceKey<ConfiguredWorldCarver<?>> feature) {
MinecraftServer server = GameInstance.getServer();
if (server != null) {
Optional<? extends Registry<ConfiguredWorldCarver<?>>> registry = server.registryAccess().registry(Registries.CONFIGURED_CARVER);
if (registry.isPresent()) {
Optional<Holder.Reference<ConfiguredWorldCarver<?>>> 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<PlacedFeature> feature) {
generation.getFeatures(decoration).removeIf(supplier -> supplier.is(feature));
return this;
}
@Override
public Mutable removeCarver(GenerationStep.Carving carving, ResourceKey<ConfiguredWorldCarver<?>> 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<MobCategory, MobSpawnSettings.SpawnerData> 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;
}
}
}

View File

@@ -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<? extends EntityType<? extends LivingEntity>>, Supplier<AttributeSupplier.Builder>> ATTRIBUTES = new ConcurrentHashMap<>();
public static void register(Supplier<? extends EntityType<? extends LivingEntity>> type, Supplier<AttributeSupplier.Builder> 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<? extends EntityType<? extends LivingEntity>>, Supplier<AttributeSupplier.Builder>> entry : ATTRIBUTES.entrySet()) {
event.put(entry.getKey().get(), entry.getValue().get().build());
}
}
}

View File

@@ -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<Entry<?>> entries = new ArrayList<>();
private record Entry<T extends Mob>(Supplier<? extends EntityType<T>> type, SpawnPlacementType spawnPlacement,
Heightmap.Types heightmapType,
SpawnPlacements.SpawnPredicate<T> spawnPredicate) {
}
static {
EventBusesHooks.whenAvailable(ArchitecturyConstants.MOD_ID, bus -> {
bus.<SpawnPlacementRegisterEvent>addListener(event -> {
for (Entry<?> entry : entries) {
Entry<Mob> casted = (Entry<Mob>) entry;
event.register(casted.type().get(), casted.spawnPlacement(), casted.heightmapType(), casted.spawnPredicate(), SpawnPlacementRegisterEvent.Operation.OR);
}
entries = null;
});
});
}
public static <T extends Mob> void register(Supplier<? extends EntityType<T>> type, SpawnPlacementType spawnPlacement, Heightmap.Types heightmapType, SpawnPlacements.SpawnPredicate<T> 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!");
}
}
}

View File

@@ -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<VillagerProfession, Int2ObjectMap<List<VillagerTrades.ItemListing>>> TRADES_TO_ADD = new HashMap<>();
private static final List<VillagerTrades.ItemListing> WANDERER_TRADER_TRADES_GENERIC = new ArrayList<>();
private static final List<VillagerTrades.ItemListing> 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<List<VillagerTrades.ItemListing>> tradesForProfession = TRADES_TO_ADD.computeIfAbsent(profession, $ -> new Int2ObjectOpenHashMap<>());
List<VillagerTrades.ItemListing> 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<List<VillagerTrades.ItemListing>> trades = TRADES_TO_ADD.get(event.getType());
if (trades != null) {
for (Int2ObjectMap.Entry<List<VillagerTrades.ItemListing>> 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);
}
}
}

View File

@@ -24,7 +24,7 @@ import com.google.common.base.Suppliers;
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 class RegistrarManagerImpl {
public RegistryProviderImpl(String modId) {
this.modId = modId;
EventBusesHooksImpl.getModEventBus(modId).get().register(new EventListener());
EventBusesHooks.getModEventBus(modId).get().register(new EventListener());
}
@Override

View File

@@ -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();
}
}

View File

@@ -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
}
}

View File

@@ -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"

View File

@@ -25,6 +25,7 @@ import dev.architectury.networking.NetworkManager;
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 @@ public class TestEntity extends Cow {
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);
}

View File

@@ -27,13 +27,13 @@ import net.minecraft.world.level.storage.loot.entries.LootItem;
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);
}
});*/
});
}
}

View File

@@ -22,7 +22,7 @@ package dev.architectury.test.networking;
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 class ButtonClickedMessage extends BaseC2SMessage {
buttonId = id;
}
public ButtonClickedMessage(FriendlyByteBuf buf) {
public ButtonClickedMessage(RegistryFriendlyByteBuf buf) {
buttonId = buf.readVarInt();
}
@@ -45,7 +45,7 @@ public class ButtonClickedMessage extends BaseC2SMessage {
}
@Override
public void write(FriendlyByteBuf buf) {
public void write(RegistryFriendlyByteBuf buf) {
buf.writeVarInt(buttonId);
}

View File

@@ -23,7 +23,7 @@ import dev.architectury.networking.NetworkManager;
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 class SyncDataMessage extends BaseS2CMessage {
serverData = tag;
}
public SyncDataMessage(FriendlyByteBuf buf) {
public SyncDataMessage(RegistryFriendlyByteBuf buf) {
serverData = buf.readNbt();
}
@@ -48,7 +48,7 @@ public class SyncDataMessage extends BaseS2CMessage {
}
@Override
public void write(FriendlyByteBuf buf) {
public void write(RegistryFriendlyByteBuf buf) {
buf.writeNbt(serverData);
}

View File

@@ -27,10 +27,16 @@ import dev.architectury.networking.transformers.SplitPacketTransformer;
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<BigDataPayload> 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 @@ public interface TestModNet {
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<? extends CustomPacketPayload> type() {
return TestModNet.BIG_DATA_PAYLOAD;
}
}
}

View File

@@ -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 }
}