feat(M1): Fabric scaffold for MC 1.21.6 with /videoStick
- video_player mod id, 영상재생모드 display name - VideoAnchorBlock + VideoAnchorBlockEntity (placeholder) - VideoStickItem - /videoStick (+ /videostick alias) command gives the stick - gradle 9.5.1 wrapper, fabric-loom 1.16.2, Java 21 toolchain - works in both singleplayer and dedicated server (environment: *)
This commit is contained in:
15
src/main/java/com/ejclaw/videoplayer/VideoPlayerClient.java
Normal file
15
src/main/java/com/ejclaw/videoplayer/VideoPlayerClient.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.ejclaw.videoplayer;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class VideoPlayerClient implements ClientModInitializer {
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
// M2+: BlockEntityRendererFactories.register(...)
|
||||
// M5+: AttackBlockCallback for left-click delete
|
||||
VideoPlayerMod.LOG.info("[{}] client initialized (M1 scaffold)", VideoPlayerMod.MOD_ID);
|
||||
}
|
||||
}
|
||||
28
src/main/java/com/ejclaw/videoplayer/VideoPlayerMod.java
Normal file
28
src/main/java/com/ejclaw/videoplayer/VideoPlayerMod.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package com.ejclaw.videoplayer;
|
||||
|
||||
import com.ejclaw.videoplayer.command.VideoStickCommand;
|
||||
import com.ejclaw.videoplayer.registry.VideoPlayerBlockEntities;
|
||||
import com.ejclaw.videoplayer.registry.VideoPlayerBlocks;
|
||||
import com.ejclaw.videoplayer.registry.VideoPlayerItems;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class VideoPlayerMod implements ModInitializer {
|
||||
public static final String MOD_ID = "video_player";
|
||||
public static final Logger LOG = LoggerFactory.getLogger(MOD_ID);
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
VideoPlayerBlocks.register();
|
||||
VideoPlayerBlockEntities.register();
|
||||
VideoPlayerItems.register();
|
||||
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, env) -> {
|
||||
VideoStickCommand.register(dispatcher);
|
||||
});
|
||||
|
||||
LOG.info("[{}] initialized (M1 scaffold)", MOD_ID);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ejclaw.videoplayer.block;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.BlockWithEntity;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class VideoAnchorBlock extends BlockWithEntity implements BlockEntityProvider {
|
||||
public static final MapCodec<VideoAnchorBlock> CODEC = createCodec(VideoAnchorBlock::new);
|
||||
|
||||
public VideoAnchorBlock(AbstractBlock.Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends BlockWithEntity> getCodec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return new VideoAnchorBlockEntity(pos, state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
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.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
/**
|
||||
* M1 placeholder. Holds the runtime fields described in SPEC §3.1; NBT persistence
|
||||
* (ReadView/WriteView in 1.21.6) lands in M3/M4 alongside the playback and GUI work.
|
||||
*/
|
||||
public class VideoAnchorBlockEntity extends BlockEntity {
|
||||
private String url = "";
|
||||
private int width = 1;
|
||||
private int height = 1;
|
||||
private Direction facing = Direction.NORTH;
|
||||
private boolean loop = true;
|
||||
private float volume = 0.5F;
|
||||
private boolean muted = false;
|
||||
private boolean autoplay = true;
|
||||
|
||||
public VideoAnchorBlockEntity(BlockPos pos, BlockState state) {
|
||||
super(VideoPlayerBlockEntities.VIDEO_ANCHOR, pos, state);
|
||||
}
|
||||
|
||||
public String getUrl() { return url; }
|
||||
public int getWidth() { return width; }
|
||||
public int getHeight() { return height; }
|
||||
public Direction getFacing() { return facing; }
|
||||
public boolean isLoop() { return loop; }
|
||||
public float getVolume() { return volume; }
|
||||
public boolean isMuted() { return muted; }
|
||||
public boolean isAutoplay() { return autoplay; }
|
||||
|
||||
public void setMuted(boolean muted) {
|
||||
this.muted = muted;
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
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;
|
||||
|
||||
public final class VideoStickCommand {
|
||||
private VideoStickCommand() {}
|
||||
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||
dispatcher.register(CommandManager.literal("videoStick")
|
||||
.executes(ctx -> run(ctx.getSource())));
|
||||
// Lowercase alias — Brigadier is case-sensitive.
|
||||
dispatcher.register(CommandManager.literal("videostick")
|
||||
.executes(ctx -> run(ctx.getSource())));
|
||||
}
|
||||
|
||||
private static int run(ServerCommandSource source) {
|
||||
ServerPlayerEntity player = source.getPlayer();
|
||||
if (player == null) {
|
||||
source.sendError(Text.literal("플레이어만 이 명령을 사용할 수 있습니다."));
|
||||
return 0;
|
||||
}
|
||||
ItemStack stack = new ItemStack(VideoPlayerItems.VIDEO_STICK);
|
||||
boolean inserted = player.getInventory().insertStack(stack);
|
||||
if (!inserted || !stack.isEmpty()) {
|
||||
player.dropItem(stack, false);
|
||||
}
|
||||
source.sendFeedback(() -> Text.literal("비디오 스틱을 지급했습니다."), false);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.ejclaw.videoplayer.item;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
|
||||
/**
|
||||
* M1: registered placeholder. Right/left-click handlers land in M4–M5.
|
||||
*/
|
||||
public class VideoStickItem extends Item {
|
||||
public VideoStickItem(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
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;
|
||||
|
||||
public final class VideoPlayerBlockEntities {
|
||||
private VideoPlayerBlockEntities() {}
|
||||
|
||||
public static final BlockEntityType<VideoAnchorBlockEntity> VIDEO_ANCHOR = Registry.register(
|
||||
Registries.BLOCK_ENTITY_TYPE,
|
||||
Identifier.of(VideoPlayerMod.MOD_ID, "video_anchor"),
|
||||
FabricBlockEntityTypeBuilder.create(VideoAnchorBlockEntity::new, VideoPlayerBlocks.VIDEO_ANCHOR).build()
|
||||
);
|
||||
|
||||
public static void register() {
|
||||
// class-load side effect triggers registration
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
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;
|
||||
|
||||
public final class VideoPlayerBlocks {
|
||||
private VideoPlayerBlocks() {}
|
||||
|
||||
public static final Block VIDEO_ANCHOR = register(
|
||||
"video_anchor",
|
||||
AbstractBlock.Settings.create().strength(1.0F).nonOpaque(),
|
||||
VideoAnchorBlock::new
|
||||
);
|
||||
|
||||
public static void register() {
|
||||
// class-load side effect triggers registration
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface BlockFactory<B extends Block> {
|
||||
B create(AbstractBlock.Settings settings);
|
||||
}
|
||||
|
||||
private static <B extends Block> B register(String name, AbstractBlock.Settings settings, BlockFactory<B> factory) {
|
||||
Identifier id = Identifier.of(VideoPlayerMod.MOD_ID, name);
|
||||
RegistryKey<Block> key = RegistryKey.of(RegistryKeys.BLOCK, id);
|
||||
B block = factory.create(settings.registryKey(key));
|
||||
return Registry.register(Registries.BLOCK, key, block);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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;
|
||||
|
||||
public final class VideoPlayerItems {
|
||||
private VideoPlayerItems() {}
|
||||
|
||||
public static final Item VIDEO_STICK = register(
|
||||
"video_stick",
|
||||
settings -> new VideoStickItem(settings.maxCount(1))
|
||||
);
|
||||
|
||||
public static void register() {
|
||||
// For M1 we don't add to a vanilla item group; players get the stick via /videoStick.
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ItemFactory<I extends Item> {
|
||||
I create(Item.Settings settings);
|
||||
}
|
||||
|
||||
private static <I extends Item> I register(String name, ItemFactory<I> factory) {
|
||||
Identifier id = Identifier.of(VideoPlayerMod.MOD_ID, name);
|
||||
RegistryKey<Item> key = RegistryKey.of(RegistryKeys.ITEM, id);
|
||||
I item = factory.create(new Item.Settings().registryKey(key));
|
||||
return Registry.register(Registries.ITEM, key, item);
|
||||
}
|
||||
}
|
||||
4
src/main/resources/assets/video_player/lang/en_us.json
Normal file
4
src/main/resources/assets/video_player/lang/en_us.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"block.video_player.video_anchor": "Video Anchor",
|
||||
"item.video_player.video_stick": "Video Stick"
|
||||
}
|
||||
4
src/main/resources/assets/video_player/lang/ko_kr.json
Normal file
4
src/main/resources/assets/video_player/lang/ko_kr.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"block.video_player.video_anchor": "영상 앵커",
|
||||
"item.video_player.video_stick": "비디오 스틱"
|
||||
}
|
||||
24
src/main/resources/fabric.mod.json
Normal file
24
src/main/resources/fabric.mod.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "${mod_id}",
|
||||
"version": "${version}",
|
||||
"name": "영상재생모드",
|
||||
"description": "Play arbitrary mp4 URLs on block surfaces in Minecraft (Fabric).",
|
||||
"authors": [ "tkrmagid" ],
|
||||
"contact": {
|
||||
"homepage": "https://git.tkrmagid.kr/tkrmagid/mc_video_player_mod",
|
||||
"sources": "https://git.tkrmagid.kr/tkrmagid/mc_video_player_mod"
|
||||
},
|
||||
"license": "MIT",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [ "com.ejclaw.videoplayer.VideoPlayerMod" ],
|
||||
"client": [ "com.ejclaw.videoplayer.VideoPlayerClient" ]
|
||||
},
|
||||
"depends": {
|
||||
"fabricloader": ">=0.16.0",
|
||||
"fabric-api": "*",
|
||||
"minecraft": ">=1.21.6",
|
||||
"java": ">=21"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user