diff --git a/common/src/main/java/quaedam/mixin/MixinMinecraftServer.java b/common/src/main/java/quaedam/mixin/MixinMinecraftServer.java new file mode 100644 index 0000000..1ddb475 --- /dev/null +++ b/common/src/main/java/quaedam/mixin/MixinMinecraftServer.java @@ -0,0 +1,24 @@ +package quaedam.mixin; + +import net.minecraft.core.GlobalPos; +import net.minecraft.server.MinecraftServer; +import org.spongepowered.asm.mixin.Mixin; +import quaedam.mixininterface.ProjectionShellMutexAccessor; +import quaedam.shell.ProjectionShellMutex; + +import java.util.LinkedHashMap; + +@Mixin(MinecraftServer.class) +public class MixinMinecraftServer implements ProjectionShellMutexAccessor { + + private LinkedHashMap quaedam$projectionShellMutex; + + @Override + public LinkedHashMap quaedam$getProjectionShellMutex() { + if (quaedam$projectionShellMutex == null) { + quaedam$projectionShellMutex = new LinkedHashMap<>(); + } + return quaedam$projectionShellMutex; + } + +} diff --git a/common/src/main/java/quaedam/mixininterface/ProjectionShellMutexAccessor.java b/common/src/main/java/quaedam/mixininterface/ProjectionShellMutexAccessor.java new file mode 100644 index 0000000..e24cfdb --- /dev/null +++ b/common/src/main/java/quaedam/mixininterface/ProjectionShellMutexAccessor.java @@ -0,0 +1,12 @@ +package quaedam.mixininterface; + +import net.minecraft.core.GlobalPos; +import quaedam.shell.ProjectionShellMutex; + +import java.util.LinkedHashMap; + +public interface ProjectionShellMutexAccessor { + + LinkedHashMap quaedam$getProjectionShellMutex(); + +} diff --git a/common/src/main/kotlin/quaedam/Quaedam.kt b/common/src/main/kotlin/quaedam/Quaedam.kt index 392a45c..5c34e17 100644 --- a/common/src/main/kotlin/quaedam/Quaedam.kt +++ b/common/src/main/kotlin/quaedam/Quaedam.kt @@ -17,6 +17,7 @@ import quaedam.projection.misc.SkylightProjection import quaedam.projection.misc.SoundProjection import quaedam.projection.swarm.SwarmProjection import quaedam.projector.Projector +import quaedam.shell.ProjectionShell object Quaedam { @@ -50,6 +51,7 @@ object Quaedam { NoiseProjection ProjectionCommand SimpleProjectionUpdate + ProjectionShell creativeModeTabs.register() items.register() diff --git a/common/src/main/kotlin/quaedam/projection/EntityProjectionBlock.kt b/common/src/main/kotlin/quaedam/projection/EntityProjectionBlock.kt index 208f9d6..e58d73b 100644 --- a/common/src/main/kotlin/quaedam/projection/EntityProjectionBlock.kt +++ b/common/src/main/kotlin/quaedam/projection/EntityProjectionBlock.kt @@ -8,10 +8,12 @@ import net.minecraft.world.level.Level import net.minecraft.world.level.block.EntityBlock import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.state.BlockState +import quaedam.shell.ProjectionEffectShell +import quaedam.shell.ProjectionShellBlock import quaedam.utils.sendBlockUpdated abstract class EntityProjectionBlock

(properties: Properties = createProperties()) : - ProjectionBlock

(properties), EntityBlock { + ProjectionBlock

(properties), EntityBlock, ProjectionShellBlock { companion object { fun createProperties(): Properties = ProjectionBlock.createProperties() @@ -40,4 +42,11 @@ abstract class EntityProjectionBlock

(properties: Propertie } } + override fun getProjectionEffectForShell(level: Level, pos: BlockPos) = + (getBlockEntity(level, pos).cloneProjection() as ProjectionEffectShell.Provider).createShell() + + override fun applyFromShell(level: Level, pos: BlockPos, shell: ProjectionEffectShell) = applyChange(level, pos) { + fromNbt(shell.effect.toNbt()) + } + } diff --git a/common/src/main/kotlin/quaedam/projection/misc/SkylightProjection.kt b/common/src/main/kotlin/quaedam/projection/misc/SkylightProjection.kt index b18c771..f1492f4 100644 --- a/common/src/main/kotlin/quaedam/projection/misc/SkylightProjection.kt +++ b/common/src/main/kotlin/quaedam/projection/misc/SkylightProjection.kt @@ -1,20 +1,15 @@ package quaedam.projection.misc -import net.minecraft.core.BlockPos import net.minecraft.nbt.CompoundTag -import net.minecraft.world.InteractionHand -import net.minecraft.world.InteractionResult -import net.minecraft.world.entity.player.Player import net.minecraft.world.item.BlockItem import net.minecraft.world.item.Item -import net.minecraft.world.level.Level -import net.minecraft.world.level.block.state.BlockState -import net.minecraft.world.phys.BlockHitResult import quaedam.Quaedam import quaedam.projection.EntityProjectionBlock import quaedam.projection.ProjectionEffect import quaedam.projection.ProjectionEffectType import quaedam.projection.SimpleProjectionEntity +import quaedam.shell.ProjectionEffectShell +import quaedam.shell.buildProjectionEffectShell import kotlin.math.min object SkylightProjection { @@ -47,7 +42,7 @@ object SkylightProjectionBlock : EntityProjectionBlock } -data class SkylightProjectionEffect(var factor: Double = 2.0) : ProjectionEffect() { +data class SkylightProjectionEffect(var factor: Double = 2.0) : ProjectionEffect(), ProjectionEffectShell.Provider { companion object { const val TAG_FACTOR = "Factor" @@ -67,4 +62,8 @@ data class SkylightProjectionEffect(var factor: Double = 2.0) : ProjectionEffect } } + override fun createShell() = buildProjectionEffectShell(this) { + doubleSlider("quaedam.shell.skylight.factor", ::factor, 0.0..5.0, 0.1) + } + } diff --git a/common/src/main/kotlin/quaedam/shell/ProjectionEffectShell.kt b/common/src/main/kotlin/quaedam/shell/ProjectionEffectShell.kt new file mode 100644 index 0000000..57d25aa --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/ProjectionEffectShell.kt @@ -0,0 +1,66 @@ +package quaedam.shell + +import net.minecraft.client.gui.components.AbstractSliderButton +import net.minecraft.client.gui.components.CycleButton +import net.minecraft.client.gui.components.StringWidget +import net.minecraft.client.gui.layouts.LayoutElement +import net.minecraft.network.chat.Component +import quaedam.projection.ProjectionEffect +import kotlin.math.floor +import kotlin.reflect.KMutableProperty0 + +class ProjectionEffectShell(val effect: ProjectionEffect) { + + interface Provider { + + fun createShell(): ProjectionEffectShell + + } + + val rows = mutableListOf() + val width = 150 + val height = 20 + + data class Row(val text: Component, val renderer: ShellRenderer) + + fun row(key: String, renderer: ShellRenderer) { + rows += Row(Component.translatable(key), renderer) + } + + fun text(key: String, value: Component) = row(key) { StringWidget(value, it.font) } + + fun doubleSlider(key: String, property: KMutableProperty0, range: ClosedRange, step: Double) = + row(key) { + object : AbstractSliderButton( + 0, 0, width, height, + Component.literal(property.get().toString()), property.get() + ) { + override fun updateMessage() { + message = Component.literal(value.toString()) + } + + override fun applyValue() { + value = floor(value / step) * step + property.set(value) + } + } + } + + fun intCycle(key: String, property: KMutableProperty0, range: IntProgression) = + row(key) { + CycleButton.builder { Component.literal(it.toString()) } + .displayOnlyValue() + .withValues(range.toList()) + .create(0, 0, width, height, Component.translatable(key)) + } + +} + +inline fun buildProjectionEffectShell(effect: ProjectionEffect, builder: ProjectionEffectShell.() -> Unit) = + ProjectionEffectShell(effect).apply(builder) + +data class ShellRenderContext(val screen: ProjectionShellScreen) { + val font get() = screen.getFont() +} + +typealias ShellRenderer = (ctx: ShellRenderContext) -> LayoutElement diff --git a/common/src/main/kotlin/quaedam/shell/ProjectionShell.kt b/common/src/main/kotlin/quaedam/shell/ProjectionShell.kt new file mode 100644 index 0000000..91f57de --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/ProjectionShell.kt @@ -0,0 +1,26 @@ +package quaedam.shell + +import dev.architectury.networking.NetworkChannel +import net.minecraft.resources.ResourceLocation +import quaedam.Quaedam +import quaedam.shell.network.ClientboundPSHLockResultPacket +import quaedam.shell.network.ClientboundPSHLockRevokePacket +import quaedam.shell.network.ServerboundPSHLockAcquirePacket +import quaedam.shell.network.ServerboundPSHLockReleasePacket + +object ProjectionShell { + + const val ID = "projection_shell" + + val item = Quaedam.items.register(ID) { ProjectionShellItem }!! + + val channel = NetworkChannel.create(ResourceLocation("quaedam", ID)) + + init { + ServerboundPSHLockAcquirePacket + ServerboundPSHLockReleasePacket + ClientboundPSHLockRevokePacket + ClientboundPSHLockResultPacket + } + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/shell/ProjectionShellBlock.kt b/common/src/main/kotlin/quaedam/shell/ProjectionShellBlock.kt new file mode 100644 index 0000000..4dc343b --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/ProjectionShellBlock.kt @@ -0,0 +1,12 @@ +package quaedam.shell + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.Level + +interface ProjectionShellBlock { + + fun getProjectionEffectForShell(level: Level, pos: BlockPos): ProjectionEffectShell + + fun applyFromShell(level: Level, pos: BlockPos, shell: ProjectionEffectShell) + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/shell/ProjectionShellItem.kt b/common/src/main/kotlin/quaedam/shell/ProjectionShellItem.kt new file mode 100644 index 0000000..5469bc4 --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/ProjectionShellItem.kt @@ -0,0 +1,24 @@ +package quaedam.shell + +import net.minecraft.world.InteractionResult +import net.minecraft.world.item.Item +import net.minecraft.world.item.context.UseOnContext +import quaedam.Quaedam +import quaedam.shell.network.ServerboundPSHLockAcquirePacket + +object ProjectionShellItem : Item( + Properties() + .stacksTo(1) + .`arch$tab`(Quaedam.creativeModeTab) +) { + + override fun useOn(context: UseOnContext): InteractionResult { + val block = context.level.getBlockState(context.clickedPos).block + if (block is ProjectionShellBlock && context.level.isClientSide) { + ProjectionShell.channel.sendToServer(ServerboundPSHLockAcquirePacket(context.clickedPos)) + return InteractionResult.SUCCESS + } + return InteractionResult.PASS + } + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/shell/ProjectionShellMutex.kt b/common/src/main/kotlin/quaedam/shell/ProjectionShellMutex.kt new file mode 100644 index 0000000..74e21ff --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/ProjectionShellMutex.kt @@ -0,0 +1,49 @@ +package quaedam.shell + +import dev.architectury.event.events.common.TickEvent +import net.minecraft.core.BlockPos +import net.minecraft.core.GlobalPos +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer +import quaedam.mixininterface.ProjectionShellMutexAccessor +import quaedam.shell.network.ClientboundPSHLockRevokePacket + +object ProjectionShellMutex { + + init { + TickEvent.SERVER_POST.register { server -> + val mutex = (server as ProjectionShellMutexAccessor).`quaedam$getProjectionShellMutex`() + val currentTime = System.currentTimeMillis() + mutex.forEach { pos, lock -> + if (currentTime - lock.time > 60 * 1000) { + mutex.remove(pos) + ProjectionShell.channel.sendToPlayer(lock.player, ClientboundPSHLockRevokePacket) + } + } + } + } + + fun tryLock(level: ServerLevel, pos: BlockPos, player: ServerPlayer): Boolean { + val mutex = (level.server as ProjectionShellMutexAccessor).`quaedam$getProjectionShellMutex`() + val gPos = GlobalPos.of(level.dimension(), pos) + if (mutex.values.any { it.player == player }) { + return false + } + if (gPos !in mutex) { + mutex[gPos] = Lock(player, System.currentTimeMillis()) + return true + } + return false + } + + fun release(level: ServerLevel, pos: BlockPos, player: ServerPlayer) { + val mutex = (level.server as ProjectionShellMutexAccessor).`quaedam$getProjectionShellMutex`() + val gPos = GlobalPos.of(level.dimension(), pos) + if (mutex[gPos]?.player == player) { + mutex.remove(gPos) + } + } + + data class Lock(val player: ServerPlayer, val time: Long) + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/shell/ProjectionShellScreen.kt b/common/src/main/kotlin/quaedam/shell/ProjectionShellScreen.kt new file mode 100644 index 0000000..e52edb5 --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/ProjectionShellScreen.kt @@ -0,0 +1,53 @@ +package quaedam.shell + +import net.minecraft.client.gui.GuiGraphics +import net.minecraft.client.gui.components.Button +import net.minecraft.client.gui.components.StringWidget +import net.minecraft.client.gui.layouts.GridLayout +import net.minecraft.client.gui.screens.Screen +import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen +import net.minecraft.core.BlockPos +import net.minecraft.network.chat.Component +import net.minecraft.world.level.Level +import quaedam.shell.network.ServerboundPSHLockReleasePacket + +class ProjectionShellScreen(val level: Level, val pos: BlockPos, val shell: ProjectionEffectShell) : + Screen(Component.translatable("quaedam.screen.projection_shell")) { + + val layout = GridLayout() + + override fun init() { + super.init() + layout.spacing(4) + val rows = layout.createRowHelper(2) + val renderContext = ShellRenderContext(this) + shell.rows.forEach { + rows.addChild(StringWidget(150, 20, it.text, font)) + rows.addChild(it.renderer(renderContext)) + } + run { // Buttons + rows.addChild(StringWidget(Component.empty(), font)) + rows.addChild(Button.builder(Component.translatable("quaedam.screen.projection_shell.save")) { + val block = level.getBlockState(pos).block + if (block is ProjectionShellBlock) { + block.applyFromShell(level, pos, shell) + } + }.build()) + } + layout.arrangeElements() + layout.visitWidgets(::addRenderableWidget) + } + + fun getFont() = font + + override fun renderBackground(guiGraphics: GuiGraphics) { + super.renderBackground(guiGraphics) + guiGraphics.blit(AbstractContainerScreen.INVENTORY_LOCATION, width / 2, height / 2, 0, 0, 176, 166) + } + + override fun removed() { + super.removed() + ProjectionShell.channel.sendToServer(ServerboundPSHLockReleasePacket(pos)) + } + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/shell/network/ClientboundPSHLockResultPacket.kt b/common/src/main/kotlin/quaedam/shell/network/ClientboundPSHLockResultPacket.kt new file mode 100644 index 0000000..f2a6d88 --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/network/ClientboundPSHLockResultPacket.kt @@ -0,0 +1,70 @@ +package quaedam.shell.network + +import dev.architectury.networking.NetworkManager.PacketContext +import dev.architectury.utils.GameInstance +import net.minecraft.core.BlockPos +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.network.chat.Component +import quaedam.Quaedam +import quaedam.shell.ProjectionShell +import quaedam.shell.ProjectionShellBlock +import quaedam.shell.ProjectionShellScreen +import java.util.function.Supplier + +data class ClientboundPSHLockResultPacket(val pos: BlockPos, val result: Boolean) { + + companion object { + init { + ProjectionShell.channel.register( + ClientboundPSHLockResultPacket::class.java, + ClientboundPSHLockResultPacket::encode, + ::ClientboundPSHLockResultPacket, + ClientboundPSHLockResultPacket::apply + ) + } + } + + constructor(buf: FriendlyByteBuf) : this(buf.readBlockPos(), buf.readBoolean()) + + fun encode(buf: FriendlyByteBuf) { + buf.writeBlockPos(pos) + buf.writeBoolean(result) + } + + fun apply(context: Supplier) { + val ctx = context.get() + if (ctx.player.level().isClientSide) { + val client = GameInstance.getClient() + if (result) { + val level = ctx.player.level() + val block = level.getBlockState(pos).block + if (block is ProjectionShellBlock) { + ctx.queue { + try { + client.setScreen( + ProjectionShellScreen( + level, + pos, + block.getProjectionEffectForShell(level, pos) + ) + ) + } catch (e: Throwable) { + Quaedam.logger.error("Failed to open projection shell screen", e) + } + } + } else { + Quaedam.logger.warn("ClientboundPSHLockResultPacket with non-shell-provider block received") + } + } else { + ctx.queue { + client.setScreen(null) + client.gui.setOverlayMessage( + Component.translatable("quaedam.screen.projection_shell.lock_failed"), + false + ) + } + } + } + } + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/shell/network/ClientboundPSHLockRevokePacket.kt b/common/src/main/kotlin/quaedam/shell/network/ClientboundPSHLockRevokePacket.kt new file mode 100644 index 0000000..1ecc160 --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/network/ClientboundPSHLockRevokePacket.kt @@ -0,0 +1,37 @@ +package quaedam.shell.network + +import dev.architectury.networking.NetworkManager.PacketContext +import dev.architectury.utils.GameInstance +import net.minecraft.network.chat.Component +import quaedam.shell.ProjectionShell +import quaedam.shell.ProjectionShellScreen +import java.util.function.Supplier + +object ClientboundPSHLockRevokePacket { + + init { + ProjectionShell.channel.register( + ClientboundPSHLockRevokePacket::class.java, + { _, _ -> }, + { ClientboundPSHLockRevokePacket }, + { _, ctx -> apply(ctx) } + ) + } + + private fun apply(context: Supplier) { + val ctx = context.get() + if (ctx.player.level().isClientSide) { + ctx.queue { + val client = GameInstance.getClient() + if (client.screen is ProjectionShellScreen) { + client.setScreen(null) + client.gui.setOverlayMessage( + Component.translatable("quaedam.screen.projection_shell.lock_revoked"), + false + ) + } + } + } + } + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/shell/network/ServerboundPSHLockAcquirePacket.kt b/common/src/main/kotlin/quaedam/shell/network/ServerboundPSHLockAcquirePacket.kt new file mode 100644 index 0000000..39f9147 --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/network/ServerboundPSHLockAcquirePacket.kt @@ -0,0 +1,42 @@ +package quaedam.shell.network + +import dev.architectury.networking.NetworkManager.PacketContext +import net.minecraft.core.BlockPos +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer +import quaedam.shell.ProjectionShell +import quaedam.shell.ProjectionShellMutex +import java.util.function.Supplier + +data class ServerboundPSHLockAcquirePacket(val pos: BlockPos) { + + companion object { + init { + ProjectionShell.channel.register( + ServerboundPSHLockAcquirePacket::class.java, + ServerboundPSHLockAcquirePacket::encode, + ::ServerboundPSHLockAcquirePacket, + ServerboundPSHLockAcquirePacket::apply + ) + } + } + + constructor(buf: FriendlyByteBuf) : this(buf.readBlockPos()) + + fun encode(buf: FriendlyByteBuf) { + buf.writeBlockPos(pos) + } + + fun apply(context: Supplier) { + val ctx = context.get() + if (!ctx.player.level().isClientSide) { + ctx.queue { + val player = ctx.player as ServerPlayer + val result = ProjectionShellMutex.tryLock(ctx.player.level() as ServerLevel, pos, player) + ProjectionShell.channel.sendToPlayer(player, ClientboundPSHLockResultPacket(pos, result)) + } + } + } + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/shell/network/ServerboundPSHLockReleasePacket.kt b/common/src/main/kotlin/quaedam/shell/network/ServerboundPSHLockReleasePacket.kt new file mode 100644 index 0000000..386f9da --- /dev/null +++ b/common/src/main/kotlin/quaedam/shell/network/ServerboundPSHLockReleasePacket.kt @@ -0,0 +1,41 @@ +package quaedam.shell.network + +import dev.architectury.networking.NetworkManager.PacketContext +import net.minecraft.core.BlockPos +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer +import quaedam.shell.ProjectionShell +import quaedam.shell.ProjectionShellMutex +import java.util.function.Supplier + +data class ServerboundPSHLockReleasePacket(val pos: BlockPos) { + + companion object { + init { + ProjectionShell.channel.register( + ServerboundPSHLockReleasePacket::class.java, + ServerboundPSHLockReleasePacket::encode, + ::ServerboundPSHLockReleasePacket, + ServerboundPSHLockReleasePacket::apply + ) + } + } + + constructor(buf: FriendlyByteBuf) : this(buf.readBlockPos()) + + fun encode(buf: FriendlyByteBuf) { + buf.writeBlockPos(pos) + } + + fun apply(context: Supplier) { + val ctx = context.get() + if (!ctx.player.level().isClientSide) { + ctx.queue { + val player = ctx.player as ServerPlayer + ProjectionShellMutex.release(ctx.player.level() as ServerLevel, pos, player) + } + } + } + +} \ No newline at end of file diff --git a/common/src/main/resources/assets/quaedam/lang/en_us.json b/common/src/main/resources/assets/quaedam/lang/en_us.json index dc4f9ec..612916b 100644 --- a/common/src/main/resources/assets/quaedam/lang/en_us.json +++ b/common/src/main/resources/assets/quaedam/lang/en_us.json @@ -5,5 +5,11 @@ "block.quaedam.swarm_projection": "Swarm Projection", "block.quaedam.sound_projection": "Sound Projection", "block.quaedam.noise_projection": "Noise Projection", - "entity.quaedam.projected_person": "Virtual Person" + "entity.quaedam.projected_person": "Virtual Person", + "item.quaedam.projection_shell": "Projection Shell", + "quaedam.screen.projection_shell": "Projection Shell", + "quaedam.screen.projection_shell.lock_revoked": "Timeout! Connection Lost", + "quaedam.screen.projection_shell.lock_failed": "Permission denied!", + "quaedam.screen.projection_shell.save": "Save", + "quaedam.shell.skylight.factor": "Factor" } \ No newline at end of file diff --git a/common/src/main/resources/assets/quaedam/lang/zh_cn.json b/common/src/main/resources/assets/quaedam/lang/zh_cn.json index e50f560..271c246 100644 --- a/common/src/main/resources/assets/quaedam/lang/zh_cn.json +++ b/common/src/main/resources/assets/quaedam/lang/zh_cn.json @@ -5,5 +5,9 @@ "block.quaedam.swarm_projection": "人群投影", "block.quaedam.sound_projection": "声音投影", "block.quaedam.noise_projection": "噪音投影", - "entity.quaedam.projected_person": "虚拟个体" + "entity.quaedam.projected_person": "虚拟个体", + "item.quaedam.projection_shell": "投影操作面板", + "quaedam.screen.projection_shell": "投影操作", + "quaedam.screen.projection_shell.lock_revoked": "超时!连接丢失", + "quaedam.screen.projection_shell.lock_failed": "正被使用" } diff --git a/common/src/main/resources/quaedam-common.mixins.json b/common/src/main/resources/quaedam-common.mixins.json index 99e8b24..2ca5811 100644 --- a/common/src/main/resources/quaedam-common.mixins.json +++ b/common/src/main/resources/quaedam-common.mixins.json @@ -8,7 +8,8 @@ ], "mixins": [ "MixinBedBlock", - "MixinBuiltInRegistries" + "MixinBuiltInRegistries", + "MixinMinecraftServer" ], "injectors": { "defaultRequire": 1