diff --git a/src/main/java/dev/galacticraft/machinelib/api/block/entity/ConfiguredBlockEntity.java b/src/main/java/dev/galacticraft/machinelib/api/block/entity/ConfiguredBlockEntity.java index d80fbb9b..a9c35407 100644 --- a/src/main/java/dev/galacticraft/machinelib/api/block/entity/ConfiguredBlockEntity.java +++ b/src/main/java/dev/galacticraft/machinelib/api/block/entity/ConfiguredBlockEntity.java @@ -32,6 +32,7 @@ import dev.galacticraft.machinelib.api.util.BlockFace; import dev.galacticraft.machinelib.impl.Constant; import dev.galacticraft.machinelib.impl.network.s2c.BaseMachineUpdatePayload; +import dev.galacticraft.machinelib.impl.network.s2c.MachineStatusUpdatePayload; import dev.galacticraft.machinelib.impl.network.s2c.SideConfigurationUpdatePayload; import net.fabricmc.fabric.api.blockview.v2.RenderDataBlockEntity; import net.minecraft.core.BlockPos; @@ -329,8 +330,12 @@ private class InternalMachineState extends MachineState { @Override public void setStatus(@Nullable MachineStatus status) { if (this.status != status) { + MachineStatus oldStatus = this.status; this.status = status; ConfiguredBlockEntity.this.setChanged(); + if (ConfiguredBlockEntity.this.level != null && !ConfiguredBlockEntity.this.level.isClientSide) { + ConfiguredBlockEntity.this.broadcastToPlayers(new MachineStatusUpdatePayload(ConfiguredBlockEntity.this.worldPosition, status, oldStatus)); + } } } diff --git a/src/main/java/dev/galacticraft/machinelib/api/machine/MachineStatus.java b/src/main/java/dev/galacticraft/machinelib/api/machine/MachineStatus.java index 4906b8ae..cf616c35 100644 --- a/src/main/java/dev/galacticraft/machinelib/api/machine/MachineStatus.java +++ b/src/main/java/dev/galacticraft/machinelib/api/machine/MachineStatus.java @@ -28,6 +28,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.chat.Style; +import net.minecraft.network.codec.StreamCodec; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -38,6 +39,8 @@ * @see MachineStatuses */ public interface MachineStatus { + public static final StreamCodec STREAM_CODEC = StreamCodec.ofMember(MachineStatus::writePacket, MachineStatus::readPacket); + /** * Creates a new machine status. * diff --git a/src/main/java/dev/galacticraft/machinelib/client/api/event/MachineStatusEvents.java b/src/main/java/dev/galacticraft/machinelib/client/api/event/MachineStatusEvents.java new file mode 100644 index 00000000..bb3110c8 --- /dev/null +++ b/src/main/java/dev/galacticraft/machinelib/client/api/event/MachineStatusEvents.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.galacticraft.machinelib.client.api.event; + +import dev.galacticraft.machinelib.api.machine.MachineStatus; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.core.BlockPos; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import org.jetbrains.annotations.Nullable; + +/** + * Events related to the statuses of machines. + */ +public interface MachineStatusEvents { + /** + * An event that is called on the client-side when the {@link MachineStatus} of a {@link dev.galacticraft.machinelib.api.block.entity.ConfiguredBlockEntity} changes. + */ + Event MACHINE_STATUS_CHANGED = EventFactory.createArrayBacked(MachineStatusChanged.class, callbacks -> (minecraft, player, pos, status, oldStatus) -> { + for (MachineStatusChanged callback : callbacks) + callback.onMachineStatusChanged(minecraft, player, pos, status, oldStatus); + }); + + @FunctionalInterface + interface MachineStatusChanged { + /** + * Called on the client-side after the {@link MachineStatus} of a {@link dev.galacticraft.machinelib.api.block.entity.ConfiguredBlockEntity} has changed. + */ + void onMachineStatusChanged(Minecraft minecraft, LocalPlayer player, BlockPos pos, @Nullable MachineStatus status, @Nullable MachineStatus oldStatus); + } +} diff --git a/src/main/java/dev/galacticraft/machinelib/impl/network/MachineLibPackets.java b/src/main/java/dev/galacticraft/machinelib/impl/network/MachineLibPackets.java index 6e5fe93e..d33ab589 100644 --- a/src/main/java/dev/galacticraft/machinelib/impl/network/MachineLibPackets.java +++ b/src/main/java/dev/galacticraft/machinelib/impl/network/MachineLibPackets.java @@ -27,6 +27,7 @@ import dev.galacticraft.machinelib.impl.network.c2s.SideConfigurationClickPayload; import dev.galacticraft.machinelib.impl.network.c2s.TankInteractionPayload; import dev.galacticraft.machinelib.impl.network.s2c.BaseMachineUpdatePayload; +import dev.galacticraft.machinelib.impl.network.s2c.MachineStatusUpdatePayload; import dev.galacticraft.machinelib.impl.network.s2c.MenuSyncPayload; import dev.galacticraft.machinelib.impl.network.s2c.SideConfigurationUpdatePayload; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; @@ -43,6 +44,7 @@ public static void registerServer() { public static void registerClient() { ClientPlayNetworking.registerGlobalReceiver(BaseMachineUpdatePayload.TYPE, BaseMachineUpdatePayload::apply); + ClientPlayNetworking.registerGlobalReceiver(MachineStatusUpdatePayload.TYPE, MachineStatusUpdatePayload::apply); ClientPlayNetworking.registerGlobalReceiver(SideConfigurationUpdatePayload.TYPE, SideConfigurationUpdatePayload::apply); ClientPlayNetworking.registerGlobalReceiver(MenuSyncPayload.TYPE, MenuSyncPayload::apply); } @@ -57,6 +59,7 @@ public static void registerChannels() { // s2c PayloadTypeRegistry.playS2C().register(BaseMachineUpdatePayload.TYPE, BaseMachineUpdatePayload.CODEC); PayloadTypeRegistry.playS2C().register(SideConfigurationUpdatePayload.TYPE, SideConfigurationUpdatePayload.CODEC); + PayloadTypeRegistry.playS2C().register(MachineStatusUpdatePayload.TYPE, MachineStatusUpdatePayload.CODEC); PayloadTypeRegistry.playS2C().register(MenuSyncPayload.TYPE, MenuSyncPayload.CODEC); } } diff --git a/src/main/java/dev/galacticraft/machinelib/impl/network/s2c/MachineStatusUpdatePayload.java b/src/main/java/dev/galacticraft/machinelib/impl/network/s2c/MachineStatusUpdatePayload.java new file mode 100644 index 00000000..48f2091f --- /dev/null +++ b/src/main/java/dev/galacticraft/machinelib/impl/network/s2c/MachineStatusUpdatePayload.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2021-2025 Team Galacticraft + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.galacticraft.machinelib.impl.network.s2c; + +import dev.galacticraft.machinelib.api.machine.MachineStatus; +import dev.galacticraft.machinelib.client.api.event.MachineStatusEvents; +import dev.galacticraft.machinelib.impl.Constant; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.minecraft.core.BlockPos; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import org.jetbrains.annotations.Nullable; + +public record MachineStatusUpdatePayload(BlockPos pos, @Nullable MachineStatus status, @Nullable MachineStatus oldStatus) implements CustomPacketPayload { + public static final Type TYPE = new Type<>(Constant.id("status_update")); + public static final StreamCodec CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, p -> p.pos, + MachineStatus.STREAM_CODEC, p -> p.status, + MachineStatus.STREAM_CODEC, p -> p.oldStatus, + MachineStatusUpdatePayload::new + ); + + @Override + public Type type() { + return TYPE; + } + + public void apply(ClientPlayNetworking.Context context) { + MachineStatusEvents.MACHINE_STATUS_CHANGED.invoker().onMachineStatusChanged(context.client(), context.player(), this.pos, this.status, this.oldStatus); + } +}