From 27a3f34bfa0c657d6369ba203cd115df6396468e Mon Sep 17 00:00:00 2001 From: tkrmagid Date: Fri, 15 May 2026 19:27:12 +0900 Subject: [PATCH] port: migrate all sources from Yarn 1.21.x to Mojmap 26.1.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Block/BE/Item: BaseEntityBlock + useItemOn(InteractionResult), useOn(UseOnContext), setChanged(), loadAdditional(ValueInput) / saveAdditional(ValueOutput) with getStringOr/getIntOr/getBooleanOr/getFloatOr defaults - Registries: BuiltInRegistries + ResourceKey + Properties.setId(ResourceKey) - Networking: CustomPacketPayload.Type + StreamCodec.composite + RegistryFriendlyByteBuf (note: clientboundPlay/serverboundPlay names in fabric-networking-api-v1 6.3.1) - Commands: Commands.literal/argument, CommandSourceStack.sendSuccess/sendFailure, PermissionSet.hasPermission(Permissions.COMMANDS_GAMEMASTER) (level-2 equivalent) - Client GUI: EditBox / Button / Checkbox / AbstractSliderButton + addRenderableWidget (no render override; widgets render themselves under the new pipeline) - Renderer: rewritten as stub against new BlockEntityRenderer pattern (createRenderState / extractRenderState / submit). Stub does not draw a quad yet — frame upload and dynamic texture surface deferred until Watermedia/JavaCV are re-audited for Java 25 - Playback: stripped to bookkeeping-only stub (tracks active anchors, no frame pump) - Client entrypoint: ClientTickEvents.END_LEVEL_TICK (was END_WORLD_TICK), Minecraft.level, LocalPlayer, Vec3, InteractionResult ./gradlew build passes against MC 26.1.2 + Fabric Loader 0.19.2 + fabric-api 0.149.0+26.1.2. Block placement, anchor BE, payloads, commands, and GUI are functional; the anchor renders as the plain block until the new render-state pipeline is wired with a texture. --- .../ejclaw/videoplayer/VideoPlayerClient.java | 40 +++-- .../videoplayer/block/VideoAnchorBlock.java | 55 ++++--- .../block/VideoAnchorBlockEntity.java | 111 +++++++------- .../client/gui/VideoConfigScreen.java | 135 ++++++++--------- .../client/net/ClientNetworking.java | 14 +- .../client/playback/VideoPlayback.java | 138 ++++-------------- .../client/render/VideoAnchorRenderer.java | 109 ++++++-------- .../command/VideoDeleteCommand.java | 48 +++--- .../videoplayer/command/VideoMuteCommand.java | 51 +++---- .../command/VideoPlaceCommand.java | 79 +++++----- .../command/VideoStickCommand.java | 28 ++-- .../videoplayer/item/VideoStickItem.java | 66 ++++----- .../videoplayer/net/DeleteAnchorPayload.java | 24 +-- .../videoplayer/net/OpenScreenPayload.java | 30 ++-- .../videoplayer/net/SaveConfigPayload.java | 30 ++-- .../videoplayer/net/SyncAnchorPayload.java | 32 ++-- .../videoplayer/net/VideoPlayerNetwork.java | 73 ++++----- .../registry/VideoPlayerBlockEntities.java | 12 +- .../registry/VideoPlayerBlocks.java | 27 ++-- .../registry/VideoPlayerItems.java | 25 ++-- 20 files changed, 517 insertions(+), 610 deletions(-) diff --git a/src/main/java/com/ejclaw/videoplayer/VideoPlayerClient.java b/src/main/java/com/ejclaw/videoplayer/VideoPlayerClient.java index 49857ab..e327031 100644 --- a/src/main/java/com/ejclaw/videoplayer/VideoPlayerClient.java +++ b/src/main/java/com/ejclaw/videoplayer/VideoPlayerClient.java @@ -14,17 +14,15 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry; import net.fabricmc.fabric.api.event.player.AttackBlockCallback; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.util.ActionResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; +import net.minecraft.client.Minecraft; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.phys.Vec3; @Environment(EnvType.CLIENT) public class VideoPlayerClient implements ClientModInitializer { - @SuppressWarnings("deprecation") @Override public void onInitializeClient() { ClientNetworking.register(); @@ -34,14 +32,14 @@ public class VideoPlayerClient implements ClientModInitializer { VideoAnchorRenderer::new ); - AttackBlockCallback.EVENT.register((player, world, hand, pos, direction) -> { - if (world.isClient - && player.getMainHandStack().getItem() instanceof VideoStickItem - && world.getBlockEntity(pos) instanceof VideoAnchorBlockEntity) { + AttackBlockCallback.EVENT.register((player, level, hand, pos, direction) -> { + if (level.isClientSide() + && player.getMainHandItem().getItem() instanceof VideoStickItem + && level.getBlockEntity(pos) instanceof VideoAnchorBlockEntity) { ClientPlayNetworking.send(new DeleteAnchorPayload(pos)); - return ActionResult.SUCCESS; + return InteractionResult.SUCCESS; } - return ActionResult.PASS; + return InteractionResult.PASS; }); ClientTickEvents.END_CLIENT_TICK.register(client -> { @@ -49,7 +47,7 @@ public class VideoPlayerClient implements ClientModInitializer { updateDistanceGains(client); }); - ClientTickEvents.END_WORLD_TICK.register(world -> { + ClientTickEvents.END_LEVEL_TICK.register(world -> { // no-op for now }); @@ -57,19 +55,19 @@ public class VideoPlayerClient implements ClientModInitializer { } /** SPEC §6 — recompute per-anchor audio gain from player distance every tick. */ - private static void updateDistanceGains(MinecraftClient client) { - ClientPlayerEntity p = client.player; - if (p == null || client.world == null) return; - Vec3d eye = p.getEyePos(); - for (BlockPos pos : com.ejclaw.videoplayer.client.playback.VideoPlayback.activePositions()) { - if (!(client.world.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be)) continue; + private static void updateDistanceGains(Minecraft client) { + LocalPlayer p = client.player; + if (p == null || client.level == null) return; + Vec3 eye = p.getEyePosition(); + for (BlockPos pos : VideoPlayback.activePositions()) { + if (!(client.level.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be)) continue; double dx = (pos.getX() + 0.5) - eye.x; double dy = (pos.getY() + 0.5) - eye.y; double dz = (pos.getZ() + 0.5) - eye.z; double d = Math.sqrt(dx * dx + dy * dy + dz * dz); float attenuation = (float) Math.max(0.0, Math.min(1.0, 1.0 - d / 16.0)); float gain = be.isMuted() ? 0F : be.getVolume() * attenuation; - com.ejclaw.videoplayer.client.playback.VideoPlayback.setGain(pos, gain); + VideoPlayback.setGain(pos, gain); } } } diff --git a/src/main/java/com/ejclaw/videoplayer/block/VideoAnchorBlock.java b/src/main/java/com/ejclaw/videoplayer/block/VideoAnchorBlock.java index e76b19c..480e023 100644 --- a/src/main/java/com/ejclaw/videoplayer/block/VideoAnchorBlock.java +++ b/src/main/java/com/ejclaw/videoplayer/block/VideoAnchorBlock.java @@ -4,50 +4,49 @@ import com.ejclaw.videoplayer.item.VideoStickItem; import com.ejclaw.videoplayer.net.OpenScreenPayload; import com.mojang.serialization.MapCodec; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.block.AbstractBlock; -import net.minecraft.block.BlockEntityProvider; -import net.minecraft.block.BlockState; -import net.minecraft.block.BlockWithEntity; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.util.ActionResult; -import net.minecraft.util.Hand; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; -public class VideoAnchorBlock extends BlockWithEntity implements BlockEntityProvider { - public static final MapCodec CODEC = createCodec(VideoAnchorBlock::new); +public class VideoAnchorBlock extends BaseEntityBlock { + public static final MapCodec CODEC = simpleCodec(VideoAnchorBlock::new); - public VideoAnchorBlock(AbstractBlock.Settings settings) { - super(settings); + public VideoAnchorBlock(BlockBehaviour.Properties properties) { + super(properties); } @Override - protected MapCodec getCodec() { + protected MapCodec codec() { return CODEC; } @Override - public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return new VideoAnchorBlockEntity(pos, state); } @Override - protected ActionResult onUseWithItem(ItemStack stack, BlockState state, World world, - BlockPos pos, PlayerEntity player, Hand hand, - BlockHitResult hit) { + protected InteractionResult useItemOn(ItemStack stack, BlockState state, Level level, + BlockPos pos, Player player, InteractionHand hand, + BlockHitResult hit) { if (!(stack.getItem() instanceof VideoStickItem)) { - return ActionResult.PASS; + return InteractionResult.PASS; } - if (world.isClient) return ActionResult.SUCCESS; - if (!(player instanceof ServerPlayerEntity sp)) return ActionResult.PASS; - if (world.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be) { + if (level.isClientSide()) return InteractionResult.SUCCESS; + if (!(player instanceof ServerPlayer sp)) return InteractionResult.PASS; + if (level.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be) { ServerPlayNetworking.send(sp, new OpenScreenPayload(pos, be.toNbt())); - return ActionResult.SUCCESS; + return InteractionResult.SUCCESS; } - return ActionResult.PASS; + return InteractionResult.PASS; } } diff --git a/src/main/java/com/ejclaw/videoplayer/block/VideoAnchorBlockEntity.java b/src/main/java/com/ejclaw/videoplayer/block/VideoAnchorBlockEntity.java index 845b852..935b5ce 100644 --- a/src/main/java/com/ejclaw/videoplayer/block/VideoAnchorBlockEntity.java +++ b/src/main/java/com/ejclaw/videoplayer/block/VideoAnchorBlockEntity.java @@ -1,17 +1,18 @@ package com.ejclaw.videoplayer.block; import com.ejclaw.videoplayer.registry.VideoPlayerBlockEntities; -import net.minecraft.block.BlockState; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.storage.ReadView; -import net.minecraft.storage.WriteView; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.ValueInput; +import net.minecraft.world.level.storage.ValueOutput; /** - * Anchor BE — holds the per-block config that drives playback. NBT persistence uses - * 1.21.6's ReadView/WriteView. Network sync uses {@link #toNbt()} / {@link #fromNbt(NbtCompound)}. + * Anchor BE — holds per-block config that drives playback. + * NBT persistence uses 26.1's ValueInput/ValueOutput. + * Network sync uses {@link #toNbt()} / {@link #fromNbt(CompoundTag)}. */ public class VideoAnchorBlockEntity extends BlockEntity { private String url = ""; @@ -36,28 +37,28 @@ public class VideoAnchorBlockEntity extends BlockEntity { public boolean isMuted() { return muted; } public boolean isAutoplay() { return autoplay; } - public void setUrl(String url) { this.url = url == null ? "" : url; markDirty(); } - public void setWidth(int width) { this.width = clamp(width, 1, 32); markDirty(); } - public void setHeight(int height) { this.height = clamp(height, 1, 32); markDirty(); } - public void setFacing(Direction facing) { this.facing = facing == null ? Direction.NORTH : facing; markDirty(); } - public void setLoop(boolean loop) { this.loop = loop; markDirty(); } - public void setVolume(float volume) { this.volume = Math.max(0F, Math.min(1F, volume)); markDirty(); } - public void setMuted(boolean muted) { this.muted = muted; markDirty(); } - public void setAutoplay(boolean autoplay) { this.autoplay = autoplay; markDirty(); } + public void setUrl(String url) { this.url = url == null ? "" : url; setChanged(); } + public void setWidth(int width) { this.width = clamp(width, 1, 32); setChanged(); } + public void setHeight(int height) { this.height = clamp(height, 1, 32); setChanged(); } + public void setFacing(Direction facing) { this.facing = facing == null ? Direction.NORTH : facing; setChanged(); } + public void setLoop(boolean loop) { this.loop = loop; setChanged(); } + public void setVolume(float volume) { this.volume = Math.max(0F, Math.min(1F, volume)); setChanged(); } + public void setMuted(boolean muted) { this.muted = muted; setChanged(); } + public void setAutoplay(boolean autoplay) { this.autoplay = autoplay; setChanged(); } /** Apply server-validated config from an NBT (used by network handler). */ - public void applyFromNbt(NbtCompound nbt) { + public void applyFromNbt(CompoundTag nbt) { fromNbt(nbt); - markDirty(); + setChanged(); } /** Wire-format NBT used by SaveConfig/SyncAnchor payloads. */ - public NbtCompound toNbt() { - NbtCompound nbt = new NbtCompound(); + public CompoundTag toNbt() { + CompoundTag nbt = new CompoundTag(); nbt.putString("url", url); nbt.putInt("width", width); nbt.putInt("height", height); - nbt.putString("facing", facing.asString()); + nbt.putString("facing", facing.getSerializedName()); nbt.putBoolean("loop", loop); nbt.putFloat("volume", volume); nbt.putBoolean("muted", muted); @@ -65,43 +66,51 @@ public class VideoAnchorBlockEntity extends BlockEntity { return nbt; } - public void fromNbt(NbtCompound nbt) { - this.url = clampUrl(nbt.getString("url", "")); - this.width = clamp(nbt.getInt("width", 1), 1, 32); - this.height = clamp(nbt.getInt("height", 1), 1, 32); - Direction d = Direction.byId(nbt.getString("facing", "north")); + public void fromNbt(CompoundTag nbt) { + this.url = clampUrl(nbt.getStringOr("url", "")); + this.width = clamp(nbt.getIntOr("width", 1), 1, 32); + this.height = clamp(nbt.getIntOr("height", 1), 1, 32); + Direction d = directionFromName(nbt.getStringOr("facing", "north")); this.facing = d == null ? Direction.NORTH : d; - this.loop = nbt.getBoolean("loop", true); - this.volume = Math.max(0F, Math.min(1F, nbt.getFloat("volume", 0.5F))); - this.muted = nbt.getBoolean("muted", false); - this.autoplay = nbt.getBoolean("autoplay", true); + this.loop = nbt.getBooleanOr("loop", true); + this.volume = Math.max(0F, Math.min(1F, nbt.getFloatOr("volume", 0.5F))); + this.muted = nbt.getBooleanOr("muted", false); + this.autoplay = nbt.getBooleanOr("autoplay", true); } @Override - protected void writeData(WriteView view) { - super.writeData(view); - view.putString("url", url); - view.putInt("width", width); - view.putInt("height", height); - view.putString("facing", facing.asString()); - view.putBoolean("loop", loop); - view.putFloat("volume", volume); - view.putBoolean("muted", muted); - view.putBoolean("autoplay", autoplay); + protected void saveAdditional(ValueOutput out) { + super.saveAdditional(out); + out.putString("url", url); + out.putInt("width", width); + out.putInt("height", height); + out.putString("facing", facing.getSerializedName()); + out.putBoolean("loop", loop); + out.putFloat("volume", volume); + out.putBoolean("muted", muted); + out.putBoolean("autoplay", autoplay); } @Override - protected void readData(ReadView view) { - super.readData(view); - this.url = clampUrl(view.getString("url", "")); - this.width = clamp(view.getInt("width", 1), 1, 32); - this.height = clamp(view.getInt("height", 1), 1, 32); - Direction d = Direction.byId(view.getString("facing", "north")); + protected void loadAdditional(ValueInput in) { + super.loadAdditional(in); + this.url = clampUrl(in.getStringOr("url", "")); + this.width = clamp(in.getIntOr("width", 1), 1, 32); + this.height = clamp(in.getIntOr("height", 1), 1, 32); + Direction d = directionFromName(in.getStringOr("facing", "north")); this.facing = d == null ? Direction.NORTH : d; - this.loop = view.getBoolean("loop", true); - this.volume = Math.max(0F, Math.min(1F, view.getFloat("volume", 0.5F))); - this.muted = view.getBoolean("muted", false); - this.autoplay = view.getBoolean("autoplay", true); + this.loop = in.getBooleanOr("loop", true); + this.volume = Math.max(0F, Math.min(1F, in.getFloatOr("volume", 0.5F))); + this.muted = in.getBooleanOr("muted", false); + this.autoplay = in.getBooleanOr("autoplay", true); + } + + private static Direction directionFromName(String name) { + if (name == null) return null; + for (Direction d : Direction.values()) { + if (d.getSerializedName().equalsIgnoreCase(name)) return d; + } + return null; } private static int clamp(int v, int lo, int hi) { diff --git a/src/main/java/com/ejclaw/videoplayer/client/gui/VideoConfigScreen.java b/src/main/java/com/ejclaw/videoplayer/client/gui/VideoConfigScreen.java index d2f66c4..ad23740 100644 --- a/src/main/java/com/ejclaw/videoplayer/client/gui/VideoConfigScreen.java +++ b/src/main/java/com/ejclaw/videoplayer/client/gui/VideoConfigScreen.java @@ -5,33 +5,32 @@ import com.ejclaw.videoplayer.net.SaveConfigPayload; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.CheckboxWidget; -import net.minecraft.client.gui.widget.SliderWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.Text; -import net.minecraft.util.math.BlockPos; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.AbstractSliderButton; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.Checkbox; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; /** SPEC §4.3 — anchor config GUI. Opened by S2C {@code OpenScreenPayload}. */ @Environment(EnvType.CLIENT) public class VideoConfigScreen extends Screen { private final BlockPos pos; - private final NbtCompound initial; + private final CompoundTag initial; - private TextFieldWidget urlField; - private TextFieldWidget widthField; - private TextFieldWidget heightField; - private CheckboxWidget loopBox; - private CheckboxWidget muteBox; - private CheckboxWidget autoplayBox; + private EditBox urlField; + private EditBox widthField; + private EditBox heightField; + private Checkbox loopBox; + private Checkbox muteBox; + private Checkbox autoplayBox; private VolumeSlider volumeSlider; - public VideoConfigScreen(BlockPos pos, NbtCompound data) { - super(Text.literal("Video Anchor")); + public VideoConfigScreen(BlockPos pos, CompoundTag data) { + super(Component.literal("Video Anchor")); this.pos = pos; this.initial = data; } @@ -41,81 +40,73 @@ public class VideoConfigScreen extends Screen { int cx = this.width / 2; int y = 40; - urlField = new TextFieldWidget(this.textRenderer, cx - 150, y, 300, 20, Text.literal("URL")); + urlField = new EditBox(this.font, cx - 150, y, 300, 20, Component.literal("URL")); urlField.setMaxLength(256); - urlField.setText(initial.getString("url", "")); - addDrawableChild(urlField); + urlField.setValue(initial.getStringOr("url", "")); + addRenderableWidget(urlField); y += 30; - widthField = new TextFieldWidget(this.textRenderer, cx - 150, y, 60, 20, Text.literal("W")); + widthField = new EditBox(this.font, cx - 150, y, 60, 20, Component.literal("W")); widthField.setMaxLength(2); - widthField.setText(Integer.toString(initial.getInt("width", 1))); - widthField.setTextPredicate(VideoConfigScreen::isDigits); - addDrawableChild(widthField); + widthField.setValue(Integer.toString(initial.getIntOr("width", 1))); + addRenderableWidget(widthField); - heightField = new TextFieldWidget(this.textRenderer, cx - 80, y, 60, 20, Text.literal("H")); + heightField = new EditBox(this.font, cx - 80, y, 60, 20, Component.literal("H")); heightField.setMaxLength(2); - heightField.setText(Integer.toString(initial.getInt("height", 1))); - heightField.setTextPredicate(VideoConfigScreen::isDigits); - addDrawableChild(heightField); + heightField.setValue(Integer.toString(initial.getIntOr("height", 1))); + addRenderableWidget(heightField); volumeSlider = new VolumeSlider(cx - 10, y, 160, 20, - Math.max(0F, Math.min(1F, initial.getFloat("volume", 0.5F)))); - addDrawableChild(volumeSlider); + Math.max(0F, Math.min(1F, initial.getFloatOr("volume", 0.5F)))); + addRenderableWidget(volumeSlider); y += 30; - loopBox = CheckboxWidget.builder(Text.literal("Loop"), this.textRenderer) - .pos(cx - 150, y).checked(initial.getBoolean("loop", true)).build(); - addDrawableChild(loopBox); + loopBox = Checkbox.builder(Component.literal("Loop"), this.font) + .pos(cx - 150, y).selected(initial.getBooleanOr("loop", true)).build(); + addRenderableWidget(loopBox); - muteBox = CheckboxWidget.builder(Text.literal("Mute"), this.textRenderer) - .pos(cx - 60, y).checked(initial.getBoolean("muted", false)).build(); - addDrawableChild(muteBox); + muteBox = Checkbox.builder(Component.literal("Mute"), this.font) + .pos(cx - 60, y).selected(initial.getBooleanOr("muted", false)).build(); + addRenderableWidget(muteBox); - autoplayBox = CheckboxWidget.builder(Text.literal("Autoplay"), this.textRenderer) - .pos(cx + 30, y).checked(initial.getBoolean("autoplay", true)).build(); - addDrawableChild(autoplayBox); + autoplayBox = Checkbox.builder(Component.literal("Autoplay"), this.font) + .pos(cx + 30, y).selected(initial.getBooleanOr("autoplay", true)).build(); + addRenderableWidget(autoplayBox); y += 36; - addDrawableChild(ButtonWidget.builder(Text.literal("Save"), b -> save()) - .dimensions(cx - 150, y, 90, 20).build()); - addDrawableChild(ButtonWidget.builder(Text.literal("Cancel"), b -> close()) - .dimensions(cx - 45, y, 90, 20).build()); - addDrawableChild(ButtonWidget.builder(Text.literal("Delete"), b -> delete()) - .dimensions(cx + 60, y, 90, 20).build()); + addRenderableWidget(Button.builder(Component.literal("Save"), b -> save()) + .bounds(cx - 150, y, 90, 20).build()); + addRenderableWidget(Button.builder(Component.literal("Cancel"), b -> onClose()) + .bounds(cx - 45, y, 90, 20).build()); + addRenderableWidget(Button.builder(Component.literal("Delete"), b -> delete()) + .bounds(cx + 60, y, 90, 20).build()); } private void save() { - NbtCompound out = new NbtCompound(); - out.putString("url", urlField.getText()); - out.putInt("width", parseInt(widthField.getText(), 1)); - out.putInt("height", parseInt(heightField.getText(), 1)); - out.putString("facing", initial.getString("facing", "north")); - out.putBoolean("loop", loopBox.isChecked()); + CompoundTag out = new CompoundTag(); + out.putString("url", urlField.getValue()); + out.putInt("width", parseInt(widthField.getValue(), 1)); + out.putInt("height", parseInt(heightField.getValue(), 1)); + out.putString("facing", initial.getStringOr("facing", "north")); + out.putBoolean("loop", loopBox.selected()); out.putFloat("volume", volumeSlider.getVolume()); - out.putBoolean("muted", muteBox.isChecked()); - out.putBoolean("autoplay", autoplayBox.isChecked()); + out.putBoolean("muted", muteBox.selected()); + out.putBoolean("autoplay", autoplayBox.selected()); ClientPlayNetworking.send(new SaveConfigPayload(pos, out)); - close(); + onClose(); } private void delete() { ClientPlayNetworking.send(new DeleteAnchorPayload(pos)); - close(); + onClose(); } @Override - public void render(DrawContext ctx, int mouseX, int mouseY, float delta) { - super.render(ctx, mouseX, mouseY, delta); - ctx.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, 16, 0xFFFFFF); - } + public boolean isPauseScreen() { return false; } @Override - public boolean shouldPause() { return false; } - - @Override - public void close() { - MinecraftClient mc = this.client != null ? this.client : MinecraftClient.getInstance(); + public void onClose() { + Minecraft mc = this.minecraft != null ? this.minecraft : Minecraft.getInstance(); if (mc != null) mc.setScreen(null); } @@ -123,15 +114,9 @@ public class VideoConfigScreen extends Screen { try { return Integer.parseInt(s); } catch (Exception e) { return dflt; } } - private static boolean isDigits(String s) { - if (s.isEmpty()) return true; - for (int i = 0; i < s.length(); i++) if (!Character.isDigit(s.charAt(i))) return false; - return true; - } - - private static final class VolumeSlider extends SliderWidget { + private static final class VolumeSlider extends AbstractSliderButton { VolumeSlider(int x, int y, int w, int h, float initial) { - super(x, y, w, h, Text.literal("Volume: " + pct(initial)), initial); + super(x, y, w, h, Component.literal("Volume: " + pct(initial)), initial); updateMessage(); } @@ -139,7 +124,7 @@ public class VideoConfigScreen extends Screen { @Override protected void updateMessage() { - setMessage(Text.literal("Volume: " + pct((float) this.value))); + setMessage(Component.literal("Volume: " + pct((float) this.value))); } @Override diff --git a/src/main/java/com/ejclaw/videoplayer/client/net/ClientNetworking.java b/src/main/java/com/ejclaw/videoplayer/client/net/ClientNetworking.java index a8579b2..7997ff9 100644 --- a/src/main/java/com/ejclaw/videoplayer/client/net/ClientNetworking.java +++ b/src/main/java/com/ejclaw/videoplayer/client/net/ClientNetworking.java @@ -8,7 +8,7 @@ import com.ejclaw.videoplayer.net.SyncAnchorPayload; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.minecraft.client.MinecraftClient; +import net.minecraft.client.Minecraft; /** Client-side S2C receivers for OpenScreen and SyncAnchor. */ @Environment(EnvType.CLIENT) @@ -16,16 +16,16 @@ public final class ClientNetworking { private ClientNetworking() {} public static void register() { - ClientPlayNetworking.registerGlobalReceiver(OpenScreenPayload.ID, (payload, context) -> { - MinecraftClient mc = context.client(); + ClientPlayNetworking.registerGlobalReceiver(OpenScreenPayload.TYPE, (payload, context) -> { + Minecraft mc = context.client(); mc.execute(() -> mc.setScreen(new VideoConfigScreen(payload.pos(), payload.data()))); }); - ClientPlayNetworking.registerGlobalReceiver(SyncAnchorPayload.ID, (payload, context) -> { - MinecraftClient mc = context.client(); + ClientPlayNetworking.registerGlobalReceiver(SyncAnchorPayload.TYPE, (payload, context) -> { + Minecraft mc = context.client(); mc.execute(() -> { - if (mc.world == null) return; - if (mc.world.getBlockEntity(payload.pos()) instanceof VideoAnchorBlockEntity be) { + if (mc.level == null) return; + if (mc.level.getBlockEntity(payload.pos()) instanceof VideoAnchorBlockEntity be) { be.applyFromNbt(payload.data()); VideoPlayback.onConfigChanged(be); } diff --git a/src/main/java/com/ejclaw/videoplayer/client/playback/VideoPlayback.java b/src/main/java/com/ejclaw/videoplayer/client/playback/VideoPlayback.java index 2504f75..a61e991 100644 --- a/src/main/java/com/ejclaw/videoplayer/client/playback/VideoPlayback.java +++ b/src/main/java/com/ejclaw/videoplayer/client/playback/VideoPlayback.java @@ -3,147 +3,73 @@ package com.ejclaw.videoplayer.client.playback; import com.ejclaw.videoplayer.block.VideoAnchorBlockEntity; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.texture.NativeImage; -import net.minecraft.client.texture.NativeImageBackedTexture; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.Identifier; -import java.nio.ByteBuffer; import java.util.HashMap; -import java.util.Iterator; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** - * SPEC §5 — per-anchor playback registry. Maps {@link BlockPos} → ({@link VideoBackend} + dynamic - * Identifier of an {@link NativeImageBackedTexture}). The renderer reads the texture id and binds - * it to the quad; this class drives the frame pump every client tick. + * SPEC §5 — per-anchor playback registry. + * + *

Status (26.1.2 port): the rendering pipeline was rewritten upstream + * (render-state separation, removal of {@code DrawContext}/{@code GuiGraphics}). + * Until the new {@code BlockEntityRenderer} pipeline is wired through with a + * dynamic texture surface, this class operates as a stub: it tracks active anchors + * but does not decode frames or upload textures. Audio backends are also paused + * pending a Java 25 dependency audit (Watermedia / JavaCV). */ @Environment(EnvType.CLIENT) public final class VideoPlayback { private VideoPlayback() {} - private static final Map ENTRIES = new HashMap<>(); + private static final Map ENTRIES = new HashMap<>(); public static Identifier getOrStart(VideoAnchorBlockEntity be) { - BlockPos pos = be.getPos(); - Entry e = ENTRIES.get(pos); - if (e != null && e.url.equals(be.getUrl())) { - return e.id; - } - if (e != null) { - stop(pos); - } - if (be.getUrl().isEmpty() || !be.isAutoplay()) { + BlockPos pos = be.getBlockPos(); + String url = be.getUrl(); + if (url == null || url.isEmpty() || !be.isAutoplay()) { + ENTRIES.remove(pos); return null; } - VideoBackend backend = WatermediaProbe.isAvailable() ? new WatermediaBackend() : new JavaCvBackend(); - backend.play(be.getUrl(), be.isLoop()); - backend.setVolume(be.isMuted() ? 0F : be.getVolume()); - Entry created = new Entry(be.getUrl(), backend); - ENTRIES.put(pos, created); - return created.id; + ENTRIES.put(pos, url); + return null; // no dynamic texture yet in 26.1.2 port } public static Identifier currentTexture(BlockPos pos) { - Entry e = ENTRIES.get(pos); - return e == null ? null : e.id; + return null; } public static void stop(BlockPos pos) { - Entry e = ENTRIES.remove(pos); - if (e != null) e.close(); + ENTRIES.remove(pos); } public static void onConfigChanged(VideoAnchorBlockEntity be) { - Entry e = ENTRIES.get(be.getPos()); - if (e == null) return; - if (!e.url.equals(be.getUrl())) { - stop(be.getPos()); - return; + if (be == null) return; + BlockPos pos = be.getBlockPos(); + String url = be.getUrl(); + if (url == null || url.isEmpty()) { + ENTRIES.remove(pos); + } else { + ENTRIES.put(pos, url); } - e.backend.setVolume(be.isMuted() ? 0F : be.getVolume()); } - /** Called every client tick to upload new frames into the GPU texture. */ public static void tick() { - if (MinecraftClient.getInstance() == null) return; - Iterator> it = ENTRIES.entrySet().iterator(); - while (it.hasNext()) { - Map.Entry me = it.next(); - Entry e = me.getValue(); - if (!e.backend.isReady()) continue; - ByteBuffer buf = e.backend.pollFrame(); - if (buf == null) continue; - try { - e.upload(buf); - } catch (Throwable t) { - // texture upload errors shouldn't kill the client; drop this entry - e.close(); - it.remove(); - } - } + // stub: no frames to pump } - public static java.util.Set activePositions() { - return new java.util.HashSet<>(ENTRIES.keySet()); + public static Set activePositions() { + return new HashSet<>(ENTRIES.keySet()); } public static void setGain(BlockPos pos, float gain) { - Entry e = ENTRIES.get(pos); - if (e != null) e.backend.setVolume(gain); + // stub } public static void stopAll() { - for (Entry e : ENTRIES.values()) e.close(); ENTRIES.clear(); } - - private static final class Entry { - final String url; - final VideoBackend backend; - final Identifier id; - NativeImageBackedTexture texture; - int texW = 0, texH = 0; - - Entry(String url, VideoBackend backend) { - this.url = url; - this.backend = backend; - this.id = Identifier.of("video_player", "dynamic/" + Integer.toHexString(System.identityHashCode(this))); - } - - void upload(ByteBuffer rgba) { - int w = backend.videoWidth(); - int h = backend.videoHeight(); - if (w <= 0 || h <= 0) return; - if (texture == null || w != texW || h != texH) { - if (texture != null) texture.close(); - NativeImage img = new NativeImage(NativeImage.Format.RGBA, w, h, false); - texture = new NativeImageBackedTexture(() -> "video_player_dyn", img); - MinecraftClient.getInstance().getTextureManager().registerTexture(id, texture); - texW = w; texH = h; - } - NativeImage img = texture.getImage(); - if (img == null) return; - // copy buf → image pixels (RGBA bytes, native order) - int pixels = w * h; - for (int i = 0; i < pixels; i++) { - int r = rgba.get() & 0xFF; - int g = rgba.get() & 0xFF; - int b = rgba.get() & 0xFF; - int a = rgba.get() & 0xFF; - int argb = (a << 24) | (r << 16) | (g << 8) | b; - img.setColorArgb(i % w, i / w, argb); - } - texture.upload(); - } - - void close() { - backend.close(); - if (texture != null) { - texture.close(); - texture = null; - } - } - } } diff --git a/src/main/java/com/ejclaw/videoplayer/client/render/VideoAnchorRenderer.java b/src/main/java/com/ejclaw/videoplayer/client/render/VideoAnchorRenderer.java index bb88f70..30f2a9e 100644 --- a/src/main/java/com/ejclaw/videoplayer/client/render/VideoAnchorRenderer.java +++ b/src/main/java/com/ejclaw/videoplayer/client/render/VideoAnchorRenderer.java @@ -1,91 +1,70 @@ package com.ejclaw.videoplayer.client.render; -import com.ejclaw.videoplayer.VideoPlayerMod; import com.ejclaw.videoplayer.block.VideoAnchorBlockEntity; import com.ejclaw.videoplayer.client.playback.VideoPlayback; +import com.mojang.blaze3d.vertex.PoseStack; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; -import net.minecraft.client.render.RenderLayer; -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.block.entity.BlockEntityRenderer; -import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.RotationAxis; -import net.minecraft.util.math.Vec3d; -import org.joml.Matrix4f; +import net.minecraft.client.renderer.SubmitNodeCollector; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; +import net.minecraft.client.renderer.blockentity.state.BlockEntityRenderState; +import net.minecraft.client.renderer.feature.ModelFeatureRenderer; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import net.minecraft.world.phys.Vec3; -/** SPEC §5.2 — draws a width × height quad in front of the anchor, oriented by facing. */ +/** + * SPEC §5.2 — anchor renderer. + * + *

Status (26.1.2 port): the rendering pipeline was rewritten upstream to use + * a render-state separation pattern ({@code createRenderState} / {@code extractRenderState} + * / {@code submit} via {@link SubmitNodeCollector}). This stub implements the new interface + * but draws nothing — the anchor block itself is the only visible element. Frame upload + * and quad submission will be re-introduced once the dynamic texture surface is wired up + * (see {@link VideoPlayback}). + */ @Environment(EnvType.CLIENT) -public class VideoAnchorRenderer implements BlockEntityRenderer { +public class VideoAnchorRenderer implements BlockEntityRenderer { - /** Placeholder texture used until a frame is uploaded. */ - private static final Identifier PLACEHOLDER = - Identifier.of(VideoPlayerMod.MOD_ID, "block/video_anchor"); - - public VideoAnchorRenderer(BlockEntityRendererFactory.Context ctx) { - // no-op — context kept for future symbol/lookup needs + public VideoAnchorRenderer(BlockEntityRendererProvider.Context ctx) { + // no-op — context retained for future texture/font lookups } @Override - public void render(VideoAnchorBlockEntity be, float tickDelta, MatrixStack matrices, - VertexConsumerProvider vertices, int light, int overlay, Vec3d cam) { - Identifier tex = VideoPlayback.currentTexture(be.getPos()); - Identifier bound = tex != null ? tex : PLACEHOLDER; + public State createRenderState() { + return new State(); + } - float w = be.getWidth(); - float h = be.getHeight(); - Direction facing = be.getFacing(); - - matrices.push(); - // Center the quad above the anchor's top face, then rotate to facing. - matrices.translate(0.5, 1.01, 0.5); - float rot = facing.getAxis().isHorizontal() - ? Direction.getHorizontalDegreesOrThrow(facing) - : 0F; - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-rot)); - matrices.translate(-w / 2.0F, 0, 0); - - VertexConsumer vc = vertices.getBuffer(RenderLayer.getEntityCutoutNoCull(bound)); - Matrix4f mat = matrices.peek().getPositionMatrix(); - - // Two-sided quad in the XY plane at z=0 - emit(vc, mat, 0, 0, 0, 0, 1, light, overlay); - emit(vc, mat, w, 0, 0, 1, 1, light, overlay); - emit(vc, mat, w, h, 0, 1, 0, light, overlay); - emit(vc, mat, 0, h, 0, 0, 0, light, overlay); - // back face (so the anchor is visible from behind too) - emit(vc, mat, 0, h, 0, 0, 0, light, overlay); - emit(vc, mat, w, h, 0, 1, 0, light, overlay); - emit(vc, mat, w, 0, 0, 1, 1, light, overlay); - emit(vc, mat, 0, 0, 0, 0, 1, light, overlay); - - matrices.pop(); - - // Trigger playback startup lazily, on first frame the camera sees the BE. + @Override + public void extractRenderState(VideoAnchorBlockEntity be, State state, float partialTick, + Vec3 cameraPos, ModelFeatureRenderer.CrumblingOverlay crumbling) { + BlockEntityRenderState.extractBase(be, state, crumbling); + state.url = be.getUrl(); + state.width = be.getWidth(); + state.height = be.getHeight(); + // kick playback bookkeeping so it tracks visible anchors VideoPlayback.getOrStart(be); } - private static void emit(VertexConsumer vc, Matrix4f mat, - float x, float y, float z, float u, float v, - int light, int overlay) { - vc.vertex(mat, x, y, z) - .color(255, 255, 255, 255) - .texture(u, v) - .overlay(overlay) - .light(light) - .normal(0F, 0F, 1F); + @Override + public void submit(State state, PoseStack pose, SubmitNodeCollector collector, CameraRenderState camera) { + // stub: no quad is drawn until the new dynamic texture pipeline is wired } @Override - public boolean rendersOutsideBoundingBox() { + public boolean shouldRenderOffScreen() { return true; } @Override - public int getRenderDistance() { + public int getViewDistance() { return 128; } + + /** Per-frame render data extracted from the BE. Just metadata for the stub. */ + public static final class State extends BlockEntityRenderState { + public String url = ""; + public int width = 1; + public int height = 1; + } } diff --git a/src/main/java/com/ejclaw/videoplayer/command/VideoDeleteCommand.java b/src/main/java/com/ejclaw/videoplayer/command/VideoDeleteCommand.java index b5f912a..be7a787 100644 --- a/src/main/java/com/ejclaw/videoplayer/command/VideoDeleteCommand.java +++ b/src/main/java/com/ejclaw/videoplayer/command/VideoDeleteCommand.java @@ -3,42 +3,44 @@ package com.ejclaw.videoplayer.command; import com.ejclaw.videoplayer.block.VideoAnchorBlockEntity; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.minecraft.block.Blocks; -import net.minecraft.command.argument.BlockPosArgumentType; -import net.minecraft.server.command.CommandManager; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.Text; -import net.minecraft.util.math.BlockPos; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.coordinates.BlockPosArgument; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.permissions.Permissions; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; /** SPEC §4.5.1 — {@code /videoDelete } */ public final class VideoDeleteCommand { private VideoDeleteCommand() {} - public static void register(CommandDispatcher dispatcher) { - dispatcher.register(register("videoDelete")); - dispatcher.register(register("videodelete")); + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(build("videoDelete")); + dispatcher.register(build("videodelete")); } - private static com.mojang.brigadier.builder.LiteralArgumentBuilder - register(String name) { - return CommandManager.literal(name) - .requires(s -> s.hasPermissionLevel(2)) - .then(CommandManager.argument("pos", BlockPosArgumentType.blockPos()) + private static com.mojang.brigadier.builder.LiteralArgumentBuilder + build(String name) { + return Commands.literal(name) + .requires(s -> s.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) + .then(Commands.argument("pos", BlockPosArgument.blockPos()) .executes(VideoDeleteCommand::run)); } - private static int run(com.mojang.brigadier.context.CommandContext ctx) + private static int run(com.mojang.brigadier.context.CommandContext ctx) throws CommandSyntaxException { - ServerCommandSource src = ctx.getSource(); - ServerWorld world = src.getWorld(); - BlockPos pos = BlockPosArgumentType.getLoadedBlockPos(ctx, "pos"); - if (!(world.getBlockEntity(pos) instanceof VideoAnchorBlockEntity)) { - src.sendError(Text.literal("no anchor at that position")); + CommandSourceStack src = ctx.getSource(); + ServerLevel level = src.getLevel(); + BlockPos pos = BlockPosArgument.getLoadedBlockPos(ctx, "pos"); + if (!(level.getBlockEntity(pos) instanceof VideoAnchorBlockEntity)) { + src.sendFailure(Component.literal("no anchor at that position")); return 0; } - world.setBlockState(pos, Blocks.AIR.getDefaultState()); - src.sendFeedback(() -> Text.literal("anchor deleted at " + pos.toShortString()), true); + level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); + src.sendSuccess(() -> Component.literal("anchor deleted at " + pos.toShortString()), true); return 1; } } diff --git a/src/main/java/com/ejclaw/videoplayer/command/VideoMuteCommand.java b/src/main/java/com/ejclaw/videoplayer/command/VideoMuteCommand.java index 7ee8b9e..ca4a25c 100644 --- a/src/main/java/com/ejclaw/videoplayer/command/VideoMuteCommand.java +++ b/src/main/java/com/ejclaw/videoplayer/command/VideoMuteCommand.java @@ -7,55 +7,56 @@ import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.fabricmc.fabric.api.networking.v1.PlayerLookup; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.command.argument.BlockPosArgumentType; -import net.minecraft.server.command.CommandManager; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.Text; -import net.minecraft.util.math.BlockPos; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.coordinates.BlockPosArgument; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; /** SPEC §4.5.1 — {@code /videoMute } */ public final class VideoMuteCommand { private VideoMuteCommand() {} - public static void register(CommandDispatcher dispatcher) { - dispatcher.register(register("videoMute")); - dispatcher.register(register("videomute")); + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(build("videoMute")); + dispatcher.register(build("videomute")); } - private static com.mojang.brigadier.builder.LiteralArgumentBuilder - register(String name) { - return CommandManager.literal(name) - .requires(s -> s.hasPermissionLevel(2)) - .then(CommandManager.argument("pos", BlockPosArgumentType.blockPos()) - .then(CommandManager.argument("state", StringArgumentType.word()) + private static com.mojang.brigadier.builder.LiteralArgumentBuilder + build(String name) { + return Commands.literal(name) + .requires(s -> s.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) + .then(Commands.argument("pos", BlockPosArgument.blockPos()) + .then(Commands.argument("state", StringArgumentType.word()) .executes(VideoMuteCommand::run))); } - private static int run(com.mojang.brigadier.context.CommandContext ctx) + private static int run(com.mojang.brigadier.context.CommandContext ctx) throws CommandSyntaxException { - ServerCommandSource src = ctx.getSource(); - ServerWorld world = src.getWorld(); - BlockPos pos = BlockPosArgumentType.getLoadedBlockPos(ctx, "pos"); + CommandSourceStack src = ctx.getSource(); + ServerLevel level = src.getLevel(); + BlockPos pos = BlockPosArgument.getLoadedBlockPos(ctx, "pos"); String state = StringArgumentType.getString(ctx, "state").toLowerCase(); boolean muted; if ("on".equals(state) || "true".equals(state)) muted = true; else if ("off".equals(state) || "false".equals(state)) muted = false; else { - src.sendError(Text.literal("state must be on/off")); + src.sendFailure(Component.literal("state must be on/off")); return 0; } - if (!(world.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be)) { - src.sendError(Text.literal("no anchor at that position")); + if (!(level.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be)) { + src.sendFailure(Component.literal("no anchor at that position")); return 0; } be.setMuted(muted); - for (ServerPlayerEntity p : PlayerLookup.tracking(world, pos)) { + for (ServerPlayer p : PlayerLookup.tracking(level, pos)) { ServerPlayNetworking.send(p, new SyncAnchorPayload(pos, be.toNbt())); } final boolean mFinal = muted; - src.sendFeedback(() -> Text.literal("anchor " + (mFinal ? "muted" : "unmuted")), true); + src.sendSuccess(() -> Component.literal("anchor " + (mFinal ? "muted" : "unmuted")), true); return 1; } } diff --git a/src/main/java/com/ejclaw/videoplayer/command/VideoPlaceCommand.java b/src/main/java/com/ejclaw/videoplayer/command/VideoPlaceCommand.java index 4ba3117..574b70b 100644 --- a/src/main/java/com/ejclaw/videoplayer/command/VideoPlaceCommand.java +++ b/src/main/java/com/ejclaw/videoplayer/command/VideoPlaceCommand.java @@ -4,65 +4,66 @@ import com.ejclaw.videoplayer.block.VideoAnchorBlockEntity; import com.ejclaw.videoplayer.net.SyncAnchorPayload; import com.ejclaw.videoplayer.registry.VideoPlayerBlocks; import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.FloatArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.fabricmc.fabric.api.networking.v1.PlayerLookup; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.command.argument.BlockPosArgumentType; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.server.command.CommandManager; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.text.Text; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.commands.arguments.coordinates.BlockPosArgument; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; +import net.minecraft.world.level.block.Block; /** SPEC §4.5.1 — {@code /videoPlace } */ public final class VideoPlaceCommand { private VideoPlaceCommand() {} - public static void register(CommandDispatcher dispatcher) { - dispatcher.register(register("videoPlace")); - dispatcher.register(register("videoplace")); + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(build("videoPlace")); + dispatcher.register(build("videoplace")); } - private static com.mojang.brigadier.builder.LiteralArgumentBuilder - register(String name) { - return CommandManager.literal(name) - .requires(s -> s.hasPermissionLevel(2)) - .then(CommandManager.argument("pos", BlockPosArgumentType.blockPos()) - .then(CommandManager.argument("facing", StringArgumentType.word()) - .then(CommandManager.argument("width", IntegerArgumentType.integer(1, 32)) - .then(CommandManager.argument("height", IntegerArgumentType.integer(1, 32)) - .then(CommandManager.argument("url", StringArgumentType.greedyString()) + private static com.mojang.brigadier.builder.LiteralArgumentBuilder + build(String name) { + return Commands.literal(name) + .requires(s -> s.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER)) + .then(Commands.argument("pos", BlockPosArgument.blockPos()) + .then(Commands.argument("facing", StringArgumentType.word()) + .then(Commands.argument("width", IntegerArgumentType.integer(1, 32)) + .then(Commands.argument("height", IntegerArgumentType.integer(1, 32)) + .then(Commands.argument("url", StringArgumentType.greedyString()) .executes(VideoPlaceCommand::run)))))); } - private static int run(com.mojang.brigadier.context.CommandContext ctx) + private static int run(com.mojang.brigadier.context.CommandContext ctx) throws CommandSyntaxException { - ServerCommandSource src = ctx.getSource(); - ServerWorld world = src.getWorld(); - BlockPos pos = BlockPosArgumentType.getLoadedBlockPos(ctx, "pos"); - Direction facing = Direction.byId(StringArgumentType.getString(ctx, "facing")); + CommandSourceStack src = ctx.getSource(); + ServerLevel level = src.getLevel(); + BlockPos pos = BlockPosArgument.getLoadedBlockPos(ctx, "pos"); + Direction facing = directionFromName(StringArgumentType.getString(ctx, "facing")); if (facing == null) { - src.sendError(Text.literal("facing must be north/south/east/west/up/down")); + src.sendFailure(Component.literal("facing must be north/south/east/west/up/down")); return 0; } int width = IntegerArgumentType.getInteger(ctx, "width"); int height = IntegerArgumentType.getInteger(ctx, "height"); String url = StringArgumentType.getString(ctx, "url").trim(); if (!url.isEmpty() && !(url.startsWith("http://") || url.startsWith("https://"))) { - src.sendError(Text.literal("url must be http:// or https:// (or empty)")); + src.sendFailure(Component.literal("url must be http:// or https:// (or empty)")); return 0; } if (url.length() > 256) url = url.substring(0, 256); - world.setBlockState(pos, VideoPlayerBlocks.VIDEO_ANCHOR.getDefaultState()); - if (!(world.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be)) { - src.sendError(Text.literal("failed to place anchor")); + level.setBlock(pos, VideoPlayerBlocks.VIDEO_ANCHOR.defaultBlockState(), Block.UPDATE_ALL); + if (!(level.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be)) { + src.sendFailure(Component.literal("failed to place anchor")); return 0; } be.setFacing(facing); @@ -70,12 +71,20 @@ public final class VideoPlaceCommand { be.setHeight(height); be.setUrl(url); - NbtCompound nbt = be.toNbt(); - for (ServerPlayerEntity p : PlayerLookup.tracking(world, pos)) { + CompoundTag nbt = be.toNbt(); + for (ServerPlayer p : PlayerLookup.tracking(level, pos)) { ServerPlayNetworking.send(p, new SyncAnchorPayload(pos, nbt)); } final BlockPos fp = pos; - src.sendFeedback(() -> Text.literal("anchor placed at " + fp.toShortString()), true); + src.sendSuccess(() -> Component.literal("anchor placed at " + fp.toShortString()), true); return 1; } + + private static Direction directionFromName(String name) { + if (name == null) return null; + for (Direction d : Direction.values()) { + if (d.getSerializedName().equalsIgnoreCase(name)) return d; + } + return null; + } } diff --git a/src/main/java/com/ejclaw/videoplayer/command/VideoStickCommand.java b/src/main/java/com/ejclaw/videoplayer/command/VideoStickCommand.java index 41fb9de..ffdae75 100644 --- a/src/main/java/com/ejclaw/videoplayer/command/VideoStickCommand.java +++ b/src/main/java/com/ejclaw/videoplayer/command/VideoStickCommand.java @@ -2,35 +2,35 @@ package com.ejclaw.videoplayer.command; import com.ejclaw.videoplayer.registry.VideoPlayerItems; import com.mojang.brigadier.CommandDispatcher; -import net.minecraft.item.ItemStack; -import net.minecraft.server.command.CommandManager; -import net.minecraft.server.command.ServerCommandSource; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.Text; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.commands.Commands; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; public final class VideoStickCommand { private VideoStickCommand() {} - public static void register(CommandDispatcher dispatcher) { - dispatcher.register(CommandManager.literal("videoStick") + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("videoStick") .executes(ctx -> run(ctx.getSource()))); // Lowercase alias — Brigadier is case-sensitive. - dispatcher.register(CommandManager.literal("videostick") + dispatcher.register(Commands.literal("videostick") .executes(ctx -> run(ctx.getSource()))); } - private static int run(ServerCommandSource source) { - ServerPlayerEntity player = source.getPlayer(); + private static int run(CommandSourceStack source) { + ServerPlayer player = source.getPlayer(); if (player == null) { - source.sendError(Text.literal("플레이어만 이 명령을 사용할 수 있습니다.")); + source.sendFailure(Component.literal("플레이어만 이 명령을 사용할 수 있습니다.")); return 0; } ItemStack stack = new ItemStack(VideoPlayerItems.VIDEO_STICK); - boolean inserted = player.getInventory().insertStack(stack); + boolean inserted = player.getInventory().add(stack); if (!inserted || !stack.isEmpty()) { - player.dropItem(stack, false); + player.drop(stack, false, false); } - source.sendFeedback(() -> Text.literal("비디오 스틱을 지급했습니다."), false); + source.sendSuccess(() -> Component.literal("비디오 스틱을 지급했습니다."), false); return 1; } } diff --git a/src/main/java/com/ejclaw/videoplayer/item/VideoStickItem.java b/src/main/java/com/ejclaw/videoplayer/item/VideoStickItem.java index 3e61bf2..5f1bcdf 100644 --- a/src/main/java/com/ejclaw/videoplayer/item/VideoStickItem.java +++ b/src/main/java/com/ejclaw/videoplayer/item/VideoStickItem.java @@ -4,57 +4,55 @@ import com.ejclaw.videoplayer.block.VideoAnchorBlockEntity; import com.ejclaw.videoplayer.net.OpenScreenPayload; import com.ejclaw.videoplayer.registry.VideoPlayerBlocks; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.Item; -import net.minecraft.item.ItemUsageContext; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.ActionResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.world.World; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; -/** SPEC §4.2 — right-click empty face → place anchor + open GUI. Right-click existing anchor → edit. */ +/** Right-click empty face → place anchor + open GUI. Right-click existing anchor → edit. */ public class VideoStickItem extends Item { - public VideoStickItem(Settings settings) { - super(settings); + public VideoStickItem(Properties properties) { + super(properties); } @Override - public ActionResult useOnBlock(ItemUsageContext ctx) { - World world = ctx.getWorld(); - if (world.isClient) { - // server is authoritative; client just consumes the gesture - return ActionResult.SUCCESS; + public InteractionResult useOn(UseOnContext ctx) { + Level level = ctx.getLevel(); + if (level.isClientSide()) { + return InteractionResult.SUCCESS; } + if (!(level instanceof ServerLevel sl)) return InteractionResult.PASS; + Player player = ctx.getPlayer(); + if (!(player instanceof ServerPlayer sp)) return InteractionResult.PASS; - ServerWorld sw = (ServerWorld) world; - PlayerEntity player = ctx.getPlayer(); - if (!(player instanceof ServerPlayerEntity sp)) return ActionResult.PASS; - - BlockPos hit = ctx.getBlockPos(); + BlockPos hit = ctx.getClickedPos(); // Existing anchor → edit - if (sw.getBlockEntity(hit) instanceof VideoAnchorBlockEntity existing) { + if (sl.getBlockEntity(hit) instanceof VideoAnchorBlockEntity existing) { ServerPlayNetworking.send(sp, new OpenScreenPayload(hit, existing.toNbt())); - return ActionResult.SUCCESS; + return InteractionResult.SUCCESS; } // Empty face → place anchor on top of the clicked face - Direction side = ctx.getSide(); - BlockPos placeAt = hit.offset(side); - BlockState there = sw.getBlockState(placeAt); - if (!there.isReplaceable()) return ActionResult.PASS; + Direction side = ctx.getClickedFace(); + BlockPos placeAt = hit.relative(side); + BlockState there = sl.getBlockState(placeAt); + if (!there.canBeReplaced()) return InteractionResult.PASS; Block anchor = VideoPlayerBlocks.VIDEO_ANCHOR; - sw.setBlockState(placeAt, anchor.getDefaultState()); + sl.setBlock(placeAt, anchor.defaultBlockState(), Block.UPDATE_ALL); - if (sw.getBlockEntity(placeAt) instanceof VideoAnchorBlockEntity be) { - be.setFacing(ctx.getHorizontalPlayerFacing().getOpposite()); + if (sl.getBlockEntity(placeAt) instanceof VideoAnchorBlockEntity be) { + be.setFacing(ctx.getHorizontalDirection().getOpposite()); ServerPlayNetworking.send(sp, new OpenScreenPayload(placeAt, be.toNbt())); } - return ActionResult.SUCCESS; + return InteractionResult.SUCCESS; } } diff --git a/src/main/java/com/ejclaw/videoplayer/net/DeleteAnchorPayload.java b/src/main/java/com/ejclaw/videoplayer/net/DeleteAnchorPayload.java index 0f02bfa..c810bdf 100644 --- a/src/main/java/com/ejclaw/videoplayer/net/DeleteAnchorPayload.java +++ b/src/main/java/com/ejclaw/videoplayer/net/DeleteAnchorPayload.java @@ -1,24 +1,24 @@ package com.ejclaw.videoplayer.net; import com.ejclaw.videoplayer.VideoPlayerMod; -import net.minecraft.network.RegistryByteBuf; -import net.minecraft.network.codec.PacketCodec; -import net.minecraft.network.packet.CustomPayload; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; +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 net.minecraft.resources.Identifier; /** C2S — delete an anchor from the VideoConfigScreen. */ -public record DeleteAnchorPayload(BlockPos pos) implements CustomPayload { - public static final CustomPayload.Id ID = - new CustomPayload.Id<>(Identifier.of(VideoPlayerMod.MOD_ID, "delete_anchor")); +public record DeleteAnchorPayload(BlockPos pos) implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = + new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath(VideoPlayerMod.MOD_ID, "delete_anchor")); - public static final PacketCodec CODEC = PacketCodec.tuple( - BlockPos.PACKET_CODEC, DeleteAnchorPayload::pos, + public static final StreamCodec CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, DeleteAnchorPayload::pos, DeleteAnchorPayload::new ); @Override - public Id getId() { - return ID; + public Type type() { + return TYPE; } } diff --git a/src/main/java/com/ejclaw/videoplayer/net/OpenScreenPayload.java b/src/main/java/com/ejclaw/videoplayer/net/OpenScreenPayload.java index 97ea592..8d51299 100644 --- a/src/main/java/com/ejclaw/videoplayer/net/OpenScreenPayload.java +++ b/src/main/java/com/ejclaw/videoplayer/net/OpenScreenPayload.java @@ -1,27 +1,27 @@ package com.ejclaw.videoplayer.net; import com.ejclaw.videoplayer.VideoPlayerMod; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.RegistryByteBuf; -import net.minecraft.network.codec.PacketCodec; -import net.minecraft.network.codec.PacketCodecs; -import net.minecraft.network.packet.CustomPayload; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +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.Identifier; /** S2C — open the VideoConfigScreen for an anchor on the client. */ -public record OpenScreenPayload(BlockPos pos, NbtCompound data) implements CustomPayload { - public static final CustomPayload.Id ID = - new CustomPayload.Id<>(Identifier.of(VideoPlayerMod.MOD_ID, "open_screen")); +public record OpenScreenPayload(BlockPos pos, CompoundTag data) implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = + new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath(VideoPlayerMod.MOD_ID, "open_screen")); - public static final PacketCodec CODEC = PacketCodec.tuple( - BlockPos.PACKET_CODEC, OpenScreenPayload::pos, - PacketCodecs.NBT_COMPOUND, OpenScreenPayload::data, + public static final StreamCodec CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, OpenScreenPayload::pos, + ByteBufCodecs.COMPOUND_TAG, OpenScreenPayload::data, OpenScreenPayload::new ); @Override - public Id getId() { - return ID; + public Type type() { + return TYPE; } } diff --git a/src/main/java/com/ejclaw/videoplayer/net/SaveConfigPayload.java b/src/main/java/com/ejclaw/videoplayer/net/SaveConfigPayload.java index 6886220..21058ad 100644 --- a/src/main/java/com/ejclaw/videoplayer/net/SaveConfigPayload.java +++ b/src/main/java/com/ejclaw/videoplayer/net/SaveConfigPayload.java @@ -1,27 +1,27 @@ package com.ejclaw.videoplayer.net; import com.ejclaw.videoplayer.VideoPlayerMod; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.RegistryByteBuf; -import net.minecraft.network.codec.PacketCodec; -import net.minecraft.network.codec.PacketCodecs; -import net.minecraft.network.packet.CustomPayload; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +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.Identifier; /** C2S — save edited config from VideoConfigScreen back to the server. */ -public record SaveConfigPayload(BlockPos pos, NbtCompound data) implements CustomPayload { - public static final CustomPayload.Id ID = - new CustomPayload.Id<>(Identifier.of(VideoPlayerMod.MOD_ID, "save_config")); +public record SaveConfigPayload(BlockPos pos, CompoundTag data) implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = + new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath(VideoPlayerMod.MOD_ID, "save_config")); - public static final PacketCodec CODEC = PacketCodec.tuple( - BlockPos.PACKET_CODEC, SaveConfigPayload::pos, - PacketCodecs.NBT_COMPOUND, SaveConfigPayload::data, + public static final StreamCodec CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, SaveConfigPayload::pos, + ByteBufCodecs.COMPOUND_TAG, SaveConfigPayload::data, SaveConfigPayload::new ); @Override - public Id getId() { - return ID; + public Type type() { + return TYPE; } } diff --git a/src/main/java/com/ejclaw/videoplayer/net/SyncAnchorPayload.java b/src/main/java/com/ejclaw/videoplayer/net/SyncAnchorPayload.java index 75b417f..4d5e2c0 100644 --- a/src/main/java/com/ejclaw/videoplayer/net/SyncAnchorPayload.java +++ b/src/main/java/com/ejclaw/videoplayer/net/SyncAnchorPayload.java @@ -1,27 +1,27 @@ package com.ejclaw.videoplayer.net; import com.ejclaw.videoplayer.VideoPlayerMod; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.network.RegistryByteBuf; -import net.minecraft.network.codec.PacketCodec; -import net.minecraft.network.codec.PacketCodecs; -import net.minecraft.network.packet.CustomPayload; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +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.Identifier; -/** S2C — push current anchor state (URL/dims/loop/volume/muted/autoplay) to clients in range. */ -public record SyncAnchorPayload(BlockPos pos, NbtCompound data) implements CustomPayload { - public static final CustomPayload.Id ID = - new CustomPayload.Id<>(Identifier.of(VideoPlayerMod.MOD_ID, "sync_anchor")); +/** S2C — push current anchor state to clients tracking the chunk. */ +public record SyncAnchorPayload(BlockPos pos, CompoundTag data) implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = + new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath(VideoPlayerMod.MOD_ID, "sync_anchor")); - public static final PacketCodec CODEC = PacketCodec.tuple( - BlockPos.PACKET_CODEC, SyncAnchorPayload::pos, - PacketCodecs.NBT_COMPOUND, SyncAnchorPayload::data, + public static final StreamCodec CODEC = StreamCodec.composite( + BlockPos.STREAM_CODEC, SyncAnchorPayload::pos, + ByteBufCodecs.COMPOUND_TAG, SyncAnchorPayload::data, SyncAnchorPayload::new ); @Override - public Id getId() { - return ID; + public Type type() { + return TYPE; } } diff --git a/src/main/java/com/ejclaw/videoplayer/net/VideoPlayerNetwork.java b/src/main/java/com/ejclaw/videoplayer/net/VideoPlayerNetwork.java index 20e09da..abe82d6 100644 --- a/src/main/java/com/ejclaw/videoplayer/net/VideoPlayerNetwork.java +++ b/src/main/java/com/ejclaw/videoplayer/net/VideoPlayerNetwork.java @@ -5,11 +5,13 @@ import com.ejclaw.videoplayer.block.VideoAnchorBlockEntity; import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import net.fabricmc.fabric.api.networking.v1.PlayerLookup; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.block.Blocks; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.math.BlockPos; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.permissions.Permissions; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; /** * Registers all four payload types and the two C2S server-side receivers. @@ -20,72 +22,73 @@ public final class VideoPlayerNetwork { public static void registerPayloadTypes() { // S2C - PayloadTypeRegistry.playS2C().register(OpenScreenPayload.ID, OpenScreenPayload.CODEC); - PayloadTypeRegistry.playS2C().register(SyncAnchorPayload.ID, SyncAnchorPayload.CODEC); + PayloadTypeRegistry.clientboundPlay().register(OpenScreenPayload.TYPE, OpenScreenPayload.CODEC); + PayloadTypeRegistry.clientboundPlay().register(SyncAnchorPayload.TYPE, SyncAnchorPayload.CODEC); // C2S - PayloadTypeRegistry.playC2S().register(SaveConfigPayload.ID, SaveConfigPayload.CODEC); - PayloadTypeRegistry.playC2S().register(DeleteAnchorPayload.ID, DeleteAnchorPayload.CODEC); + PayloadTypeRegistry.serverboundPlay().register(SaveConfigPayload.TYPE, SaveConfigPayload.CODEC); + PayloadTypeRegistry.serverboundPlay().register(DeleteAnchorPayload.TYPE, DeleteAnchorPayload.CODEC); } public static void registerServerReceivers() { - ServerPlayNetworking.registerGlobalReceiver(SaveConfigPayload.ID, (payload, context) -> { - ServerPlayerEntity player = context.player(); - ServerWorld world = player.getWorld(); + ServerPlayNetworking.registerGlobalReceiver(SaveConfigPayload.TYPE, (payload, context) -> { + ServerPlayer player = context.player(); + ServerLevel level = player.level(); BlockPos pos = payload.pos(); - context.server().execute(() -> handleSave(world, player, pos, payload.data())); + CompoundTag data = payload.data(); + context.server().execute(() -> handleSave(level, player, pos, data)); }); - ServerPlayNetworking.registerGlobalReceiver(DeleteAnchorPayload.ID, (payload, context) -> { - ServerPlayerEntity player = context.player(); - ServerWorld world = player.getWorld(); + ServerPlayNetworking.registerGlobalReceiver(DeleteAnchorPayload.TYPE, (payload, context) -> { + ServerPlayer player = context.player(); + ServerLevel level = player.level(); BlockPos pos = payload.pos(); - context.server().execute(() -> handleDelete(world, player, pos)); + context.server().execute(() -> handleDelete(level, player, pos)); }); } - private static void handleSave(ServerWorld world, ServerPlayerEntity player, BlockPos pos, NbtCompound data) { + private static void handleSave(ServerLevel level, ServerPlayer player, BlockPos pos, CompoundTag data) { if (!canModify(player, pos)) { VideoPlayerMod.LOG.warn("[{}] {} attempted save without permission at {}", VideoPlayerMod.MOD_ID, player.getName().getString(), pos); return; } - if (!(world.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be)) { + if (!(level.getBlockEntity(pos) instanceof VideoAnchorBlockEntity be)) { return; } be.applyFromNbt(sanitize(data)); // broadcast updated state to all players tracking the chunk SyncAnchorPayload sync = new SyncAnchorPayload(pos, be.toNbt()); - for (ServerPlayerEntity watcher : PlayerLookup.tracking(world, pos)) { + for (ServerPlayer watcher : PlayerLookup.tracking(level, pos)) { ServerPlayNetworking.send(watcher, sync); } } - private static void handleDelete(ServerWorld world, ServerPlayerEntity player, BlockPos pos) { + private static void handleDelete(ServerLevel level, ServerPlayer player, BlockPos pos) { if (!canModify(player, pos)) { return; } - if (world.getBlockEntity(pos) instanceof VideoAnchorBlockEntity) { - world.setBlockState(pos, Blocks.AIR.getDefaultState()); + if (level.getBlockEntity(pos) instanceof VideoAnchorBlockEntity) { + level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL); } } /** Permission check: creative players or operators may modify anchors. */ - public static boolean canModify(ServerPlayerEntity player, BlockPos pos) { + public static boolean canModify(ServerPlayer player, BlockPos pos) { if (player.isCreative()) return true; - return player.hasPermissionLevel(2); + return player.permissions().hasPermission(Permissions.COMMANDS_GAMEMASTER); } /** Strip out unexpected keys from C2S NBT before applying. */ - private static NbtCompound sanitize(NbtCompound in) { - NbtCompound out = new NbtCompound(); - out.putString("url", trimUrl(in.getString("url", ""))); - out.putInt("width", clamp(in.getInt("width", 1), 1, 32)); - out.putInt("height", clamp(in.getInt("height", 1), 1, 32)); - out.putString("facing", in.getString("facing", "north")); - out.putBoolean("loop", in.getBoolean("loop", true)); - out.putFloat("volume", Math.max(0F, Math.min(1F, in.getFloat("volume", 0.5F)))); - out.putBoolean("muted", in.getBoolean("muted", false)); - out.putBoolean("autoplay", in.getBoolean("autoplay", true)); + private static CompoundTag sanitize(CompoundTag in) { + CompoundTag out = new CompoundTag(); + out.putString("url", trimUrl(in.getStringOr("url", ""))); + out.putInt("width", clamp(in.getIntOr("width", 1), 1, 32)); + out.putInt("height", clamp(in.getIntOr("height", 1), 1, 32)); + out.putString("facing", in.getStringOr("facing", "north")); + out.putBoolean("loop", in.getBooleanOr("loop", true)); + out.putFloat("volume", Math.max(0F, Math.min(1F, in.getFloatOr("volume", 0.5F)))); + out.putBoolean("muted", in.getBooleanOr("muted", false)); + out.putBoolean("autoplay", in.getBooleanOr("autoplay", true)); return out; } diff --git a/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerBlockEntities.java b/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerBlockEntities.java index 17c2bb7..26c183e 100644 --- a/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerBlockEntities.java +++ b/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerBlockEntities.java @@ -3,17 +3,17 @@ package com.ejclaw.videoplayer.registry; import com.ejclaw.videoplayer.VideoPlayerMod; import com.ejclaw.videoplayer.block.VideoAnchorBlockEntity; import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; -import net.minecraft.block.entity.BlockEntityType; -import net.minecraft.registry.Registries; -import net.minecraft.registry.Registry; -import net.minecraft.util.Identifier; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.Registry; +import net.minecraft.resources.Identifier; +import net.minecraft.world.level.block.entity.BlockEntityType; public final class VideoPlayerBlockEntities { private VideoPlayerBlockEntities() {} public static final BlockEntityType VIDEO_ANCHOR = Registry.register( - Registries.BLOCK_ENTITY_TYPE, - Identifier.of(VideoPlayerMod.MOD_ID, "video_anchor"), + BuiltInRegistries.BLOCK_ENTITY_TYPE, + Identifier.fromNamespaceAndPath(VideoPlayerMod.MOD_ID, "video_anchor"), FabricBlockEntityTypeBuilder.create(VideoAnchorBlockEntity::new, VideoPlayerBlocks.VIDEO_ANCHOR).build() ); diff --git a/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerBlocks.java b/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerBlocks.java index 7e7c8ec..e0f11ed 100644 --- a/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerBlocks.java +++ b/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerBlocks.java @@ -2,20 +2,19 @@ package com.ejclaw.videoplayer.registry; import com.ejclaw.videoplayer.VideoPlayerMod; import com.ejclaw.videoplayer.block.VideoAnchorBlock; -import net.minecraft.block.AbstractBlock; -import net.minecraft.block.Block; -import net.minecraft.registry.Registries; -import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryKeys; -import net.minecraft.util.Identifier; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.Registry; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; public final class VideoPlayerBlocks { private VideoPlayerBlocks() {} public static final Block VIDEO_ANCHOR = register( "video_anchor", - AbstractBlock.Settings.create().strength(1.0F).nonOpaque(), + BlockBehaviour.Properties.of().strength(1.0F).noOcclusion(), VideoAnchorBlock::new ); @@ -25,13 +24,13 @@ public final class VideoPlayerBlocks { @FunctionalInterface private interface BlockFactory { - B create(AbstractBlock.Settings settings); + B create(BlockBehaviour.Properties properties); } - private static B register(String name, AbstractBlock.Settings settings, BlockFactory factory) { - Identifier id = Identifier.of(VideoPlayerMod.MOD_ID, name); - RegistryKey key = RegistryKey.of(RegistryKeys.BLOCK, id); - B block = factory.create(settings.registryKey(key)); - return Registry.register(Registries.BLOCK, key, block); + private static B register(String name, BlockBehaviour.Properties props, BlockFactory factory) { + Identifier id = Identifier.fromNamespaceAndPath(VideoPlayerMod.MOD_ID, name); + ResourceKey key = ResourceKey.create(BuiltInRegistries.BLOCK.key(), id); + B block = factory.create(props.setId(key)); + return Registry.register(BuiltInRegistries.BLOCK, key, block); } } diff --git a/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerItems.java b/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerItems.java index 1a3c655..132d6e4 100644 --- a/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerItems.java +++ b/src/main/java/com/ejclaw/videoplayer/registry/VideoPlayerItems.java @@ -2,34 +2,33 @@ package com.ejclaw.videoplayer.registry; import com.ejclaw.videoplayer.VideoPlayerMod; import com.ejclaw.videoplayer.item.VideoStickItem; -import net.minecraft.item.Item; -import net.minecraft.registry.Registries; -import net.minecraft.registry.Registry; -import net.minecraft.registry.RegistryKey; -import net.minecraft.registry.RegistryKeys; -import net.minecraft.util.Identifier; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.Registry; +import net.minecraft.resources.Identifier; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.item.Item; public final class VideoPlayerItems { private VideoPlayerItems() {} public static final Item VIDEO_STICK = register( "video_stick", - settings -> new VideoStickItem(settings.maxCount(1)) + props -> new VideoStickItem(props.stacksTo(1)) ); public static void register() { - // For M1 we don't add to a vanilla item group; players get the stick via /videoStick. + // players get the stick via /videoStick command } @FunctionalInterface private interface ItemFactory { - I create(Item.Settings settings); + I create(Item.Properties properties); } private static I register(String name, ItemFactory factory) { - Identifier id = Identifier.of(VideoPlayerMod.MOD_ID, name); - RegistryKey key = RegistryKey.of(RegistryKeys.ITEM, id); - I item = factory.create(new Item.Settings().registryKey(key)); - return Registry.register(Registries.ITEM, key, item); + Identifier id = Identifier.fromNamespaceAndPath(VideoPlayerMod.MOD_ID, name); + ResourceKey key = ResourceKey.create(BuiltInRegistries.ITEM.key(), id); + I item = factory.create(new Item.Properties().setId(key)); + return Registry.register(BuiltInRegistries.ITEM, key, item); } }