diff --git a/build.gradle b/build.gradle index 1a12e54..6588f7a 100644 --- a/build.gradle +++ b/build.gradle @@ -167,7 +167,7 @@ tasks.withType(JavaCompile).configureEach { // IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior. idea { module { - downloadSources = false + downloadSources = true downloadJavadoc = true } } diff --git a/src/main/java/dev/exhq/ajarc/Ajar.java b/src/main/java/dev/exhq/ajarc/Ajar.java index e56767a..20e2e13 100644 --- a/src/main/java/dev/exhq/ajarc/Ajar.java +++ b/src/main/java/dev/exhq/ajarc/Ajar.java @@ -1,80 +1,23 @@ package dev.exhq.ajarc; import dev.exhq.ajarc.config.Config; -import dev.exhq.ajarc.items.Register; +import dev.exhq.ajarc.register.Register; import org.slf4j.Logger; import com.mojang.logging.LogUtils; -import net.minecraft.client.Minecraft; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.chat.Component; -import net.minecraft.world.food.FoodProperties; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.CreativeModeTab; -import net.minecraft.world.item.CreativeModeTabs; -import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.material.MapColor; -import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.IEventBus; -import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModContainer; -import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; import net.neoforged.fml.config.ModConfig; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; -import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; -import net.neoforged.neoforge.event.server.ServerStartingEvent; -import net.neoforged.neoforge.registries.DeferredBlock; -import net.neoforged.neoforge.registries.DeferredHolder; -import net.neoforged.neoforge.registries.DeferredItem; -import net.neoforged.neoforge.registries.DeferredRegister; -// The value here should match an entry in the META-INF/neoforge.mods.toml file @Mod(Ajar.MODID) -public class Ajar -{ - public static final String MODID = "ajarc"; - private static final Logger LOGGER = LogUtils.getLogger(); - - public Ajar(IEventBus modEventBus, ModContainer modContainer) - { - - modEventBus.addListener(this::commonSetup); +public class Ajar { + public static final String MODID = "ajarc"; + public static final Logger LOGGER = LogUtils.getLogger(); + public Ajar(IEventBus modEventBus, ModContainer modContainer) { Register.registerAll(modEventBus); - modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC); - } - - private void commonSetup(final FMLCommonSetupEvent event) - { - // Some common setup code - LOGGER.info("HELLO FROM COMMON SETUP"); - - if (Config.logDirtBlock) - LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT)); - - LOGGER.info(Config.magicNumberIntroduction + Config.magicNumber); - - Config.items.forEach((item) -> LOGGER.info("ITEM >> {}", item.toString())); - } - - - @EventBusSubscriber(modid = MODID, bus = EventBusSubscriber.Bus.MOD, value = Dist.CLIENT) - public static class ClientModEvents - { - @SubscribeEvent - public static void onClientSetup(FMLClientSetupEvent event) - { - // Some client setup code - LOGGER.info("HELLO FROM CLIENT SETUP"); - LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName()); - } - } + modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC); + } } diff --git a/src/main/java/dev/exhq/ajarc/computer/AjarDirectory.java b/src/main/java/dev/exhq/ajarc/computer/AjarDirectory.java new file mode 100644 index 0000000..3d71486 --- /dev/null +++ b/src/main/java/dev/exhq/ajarc/computer/AjarDirectory.java @@ -0,0 +1,27 @@ +package dev.exhq.ajarc.computer; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import java.util.HashMap; +import java.util.Map; + +public record AjarDirectory( + Map listing +) implements AjarFile { + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + builder -> + builder.group(Codec.unboundedMap(Codec.STRING, Codec.lazyInitialized(() -> AjarFile.CODEC)) + .fieldOf("listing").forGetter(AjarDirectory::listing)) + .apply(builder, AjarDirectory::new)); + + public static AjarDirectory ofEmpty() { + return new AjarDirectory(new HashMap<>()); + } + + @Override + public Type getType() { + return Type.DIRECTORY; + } +} diff --git a/src/main/java/dev/exhq/ajarc/computer/AjarFile.java b/src/main/java/dev/exhq/ajarc/computer/AjarFile.java new file mode 100644 index 0000000..823d438 --- /dev/null +++ b/src/main/java/dev/exhq/ajarc/computer/AjarFile.java @@ -0,0 +1,30 @@ +package dev.exhq.ajarc.computer; + +import com.mojang.serialization.Codec; +import net.minecraft.util.StringRepresentable; +import org.jetbrains.annotations.NotNull; + +public sealed interface AjarFile permits AjarDirectory, AjarRegularFile, AjarSymlinkFile { + Codec CODEC = Type.CODEC.dispatch( + "type", + AjarFile::getType, + type -> switch (type) { + case SYMLINK -> AjarSymlinkFile.CODEC; + case DIRECTORY -> AjarDirectory.CODEC; + case REGULAR_FILE -> AjarRegularFile.CODEC; + }); + + Type getType(); + + enum Type implements StringRepresentable { + SYMLINK, + DIRECTORY, + REGULAR_FILE; + public static final Codec CODEC = StringRepresentable.fromEnum(Type::values); + + @Override + public @NotNull String getSerializedName() { + return name(); + } + } +} diff --git a/src/main/java/dev/exhq/ajarc/computer/AjarFileSystem.java b/src/main/java/dev/exhq/ajarc/computer/AjarFileSystem.java new file mode 100644 index 0000000..d2f901d --- /dev/null +++ b/src/main/java/dev/exhq/ajarc/computer/AjarFileSystem.java @@ -0,0 +1,18 @@ +package dev.exhq.ajarc.computer; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +public record AjarFileSystem( + AjarDirectory root +) { + public static final MapCodec CODEC = + RecordCodecBuilder.mapCodec( + builder -> + builder.group(AjarDirectory.CODEC.fieldOf("root").forGetter(AjarFileSystem::root)) + .apply(builder, AjarFileSystem::new)); + + public static AjarFileSystem ofBlank() { + return new AjarFileSystem(AjarDirectory.ofEmpty()); + } +} diff --git a/src/main/java/dev/exhq/ajarc/computer/AjarRegularFile.java b/src/main/java/dev/exhq/ajarc/computer/AjarRegularFile.java new file mode 100644 index 0000000..5c3dc5c --- /dev/null +++ b/src/main/java/dev/exhq/ajarc/computer/AjarRegularFile.java @@ -0,0 +1,35 @@ +package dev.exhq.ajarc.computer; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public record AjarRegularFile( + byte[] content +) implements AjarFile { + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( + builder -> builder.group(Codec.BYTE_BUFFER.xmap(AjarRegularFile::bufferToArray, ByteBuffer::wrap) + .fieldOf("content").forGetter(AjarRegularFile::content)) + .apply(builder, AjarRegularFile::new) + ); + + private static byte[] bufferToArray(ByteBuffer buffer) { + var remaining = buffer.remaining(); + var array = new byte[remaining]; + buffer.get(array); + return array; + } + + public String getTextContent() { + return new String(content, StandardCharsets.UTF_8); + } + + @Override + public Type getType() { + return Type.REGULAR_FILE; + } +} diff --git a/src/main/java/dev/exhq/ajarc/computer/AjarSymlinkFile.java b/src/main/java/dev/exhq/ajarc/computer/AjarSymlinkFile.java new file mode 100644 index 0000000..778d85d --- /dev/null +++ b/src/main/java/dev/exhq/ajarc/computer/AjarSymlinkFile.java @@ -0,0 +1,19 @@ +package dev.exhq.ajarc.computer; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +public record AjarSymlinkFile( + String target +) implements AjarFile { + public static final MapCodec CODEC = + RecordCodecBuilder.mapCodec(builder -> builder + .group(Codec.STRING.fieldOf("target").forGetter(AjarSymlinkFile::target)) + .apply(builder, AjarSymlinkFile::new)); + + @Override + public Type getType() { + return Type.SYMLINK; + } +} diff --git a/src/main/java/dev/exhq/ajarc/computer/ComputerBlock.java b/src/main/java/dev/exhq/ajarc/computer/ComputerBlock.java new file mode 100644 index 0000000..1e74985 --- /dev/null +++ b/src/main/java/dev/exhq/ajarc/computer/ComputerBlock.java @@ -0,0 +1,35 @@ +package dev.exhq.ajarc.computer; + +import dev.exhq.ajarc.register.NeaBlock; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ComputerBlock extends NeaBlock implements EntityBlock { + public ComputerBlock(Properties properties) { + super(properties); + } + + + @Nullable + @Override + public BlockEntity newBlockEntity(@NotNull BlockPos pPos, @NotNull BlockState pState) { + return new ComputerBlockEntity(pPos, pState); + } + + @Override + protected @NotNull InteractionResult useWithoutItem( + @NotNull BlockState pState, @NotNull Level pLevel, + @NotNull BlockPos pPos, @NotNull Player pPlayer, + @NotNull BlockHitResult pHitResult) { + if (pLevel.isClientSide()) return InteractionResult.SUCCESS; + return InteractionResult.CONSUME; + } +} diff --git a/src/main/java/dev/exhq/ajarc/computer/ComputerBlockEntity.java b/src/main/java/dev/exhq/ajarc/computer/ComputerBlockEntity.java new file mode 100644 index 0000000..1736766 --- /dev/null +++ b/src/main/java/dev/exhq/ajarc/computer/ComputerBlockEntity.java @@ -0,0 +1,38 @@ +package dev.exhq.ajarc.computer; + +import dev.exhq.ajarc.Ajar; +import dev.exhq.ajarc.register.Register; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtOps; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import org.jetbrains.annotations.NotNull; + +public class ComputerBlockEntity extends BlockEntity { + public ComputerBlockEntity(BlockPos pPos, BlockState pBlockState) { + super(Register.COMPUTER_BLOCK_ENTITY.get(), pPos, pBlockState); + } + + private AjarFileSystem fileSystem = AjarFileSystem.ofBlank(); + + @Override + protected void loadAdditional(@NotNull CompoundTag pTag, @NotNull HolderLookup.Provider pRegistries) { + super.loadAdditional(pTag, pRegistries); + var compound = pTag.getCompound("fileSystem"); + fileSystem = AjarFileSystem.CODEC.codec() + .parse(NbtOps.INSTANCE, compound) + .resultOrPartial(Ajar.LOGGER::error) + .orElseGet(AjarFileSystem::ofBlank); + } + + @Override + protected void saveAdditional(@NotNull CompoundTag pTag, HolderLookup.@NotNull Provider pRegistries) { + super.saveAdditional(pTag, pRegistries); + pTag.put("fileSystem", + AjarFileSystem.CODEC + .codec().encodeStart(NbtOps.INSTANCE, fileSystem) + .getOrThrow()); + } +} diff --git a/src/main/java/dev/exhq/ajarc/config/Config.java b/src/main/java/dev/exhq/ajarc/config/Config.java index c83eab6..770fea2 100644 --- a/src/main/java/dev/exhq/ajarc/config/Config.java +++ b/src/main/java/dev/exhq/ajarc/config/Config.java @@ -16,49 +16,46 @@ import net.neoforged.neoforge.common.ModConfigSpec; // An example config class. This is not required, but it's a good idea to have one to keep your config organized. // Demonstrates how to use Neo's config APIs @EventBusSubscriber(modid = Ajar.MODID, bus = EventBusSubscriber.Bus.MOD) -public class Config -{ - private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); +public class Config { + private static final ModConfigSpec.Builder BUILDER = new ModConfigSpec.Builder(); - private static final ModConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER - .comment("Whether to log the dirt block on common setup") - .define("logDirtBlock", true); + private static final ModConfigSpec.BooleanValue LOG_DIRT_BLOCK = BUILDER + .comment("Whether to log the dirt block on common setup") + .define("logDirtBlock", true); - private static final ModConfigSpec.IntValue MAGIC_NUMBER = BUILDER - .comment("A magic number") - .defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE); + private static final ModConfigSpec.IntValue MAGIC_NUMBER = BUILDER + .comment("A magic number") + .defineInRange("magicNumber", 42, 0, Integer.MAX_VALUE); - public static final ModConfigSpec.ConfigValue MAGIC_NUMBER_INTRODUCTION = BUILDER - .comment("What you want the introduction message to be for the magic number") - .define("magicNumberIntroduction", "The magic number is... "); + public static final ModConfigSpec.ConfigValue MAGIC_NUMBER_INTRODUCTION = BUILDER + .comment("What you want the introduction message to be for the magic number") + .define("magicNumberIntroduction", "The magic number is... "); - // a list of strings that are treated as resource locations for items - private static final ModConfigSpec.ConfigValue> ITEM_STRINGS = BUILDER - .comment("A list of items to log on common setup.") - .defineListAllowEmpty("items", List.of("minecraft:iron_ingot"), Config::validateItemName); + // a list of strings that are treated as resource locations for items + private static final ModConfigSpec.ConfigValue> ITEM_STRINGS = BUILDER + .comment("A list of items to log on common setup.") + .defineListAllowEmpty("items", List.of("minecraft:iron_ingot"), Config::validateItemName); - public static final ModConfigSpec SPEC = BUILDER.build(); + public static final ModConfigSpec SPEC = BUILDER.build(); - public static boolean logDirtBlock; - public static int magicNumber; - public static String magicNumberIntroduction; - public static Set items; + public static boolean logDirtBlock; + public static int magicNumber; + public static String magicNumberIntroduction; + public static Set items; - private static boolean validateItemName(final Object obj) - { - return obj instanceof String itemName && BuiltInRegistries.ITEM.containsKey(ResourceLocation.parse(itemName)); - } + private static boolean validateItemName(final Object obj) { + return obj instanceof String itemName && BuiltInRegistries.ITEM.containsKey(ResourceLocation.parse(itemName)); + } - @SubscribeEvent - static void onLoad(final ModConfigEvent event) - { - logDirtBlock = LOG_DIRT_BLOCK.get(); - magicNumber = MAGIC_NUMBER.get(); - magicNumberIntroduction = MAGIC_NUMBER_INTRODUCTION.get(); + @SubscribeEvent + static void onLoad(final ModConfigEvent event) { + logDirtBlock = LOG_DIRT_BLOCK.get(); + magicNumber = MAGIC_NUMBER.get(); + magicNumberIntroduction = MAGIC_NUMBER_INTRODUCTION.get(); - // convert the list of strings into a set of items - items = ITEM_STRINGS.get().stream() - .map(itemName -> BuiltInRegistries.ITEM.get(ResourceLocation.parse(itemName))) - .collect(Collectors.toSet()); - } + // convert the list of strings into a set of items + items = ITEM_STRINGS.get().stream() + .map(itemName -> BuiltInRegistries.ITEM.get(ResourceLocation.parse(itemName))) + .collect(Collectors.toSet()); + } } diff --git a/src/main/java/dev/exhq/ajarc/items/blocks/NeaBlock.java b/src/main/java/dev/exhq/ajarc/register/NeaBlock.java similarity index 81% rename from src/main/java/dev/exhq/ajarc/items/blocks/NeaBlock.java rename to src/main/java/dev/exhq/ajarc/register/NeaBlock.java index 8f8c31f..79d38cc 100644 --- a/src/main/java/dev/exhq/ajarc/items/blocks/NeaBlock.java +++ b/src/main/java/dev/exhq/ajarc/register/NeaBlock.java @@ -1,4 +1,4 @@ -package dev.exhq.ajarc.items.blocks; +package dev.exhq.ajarc.register; import net.minecraft.world.level.block.Block; diff --git a/src/main/java/dev/exhq/ajarc/items/Register.java b/src/main/java/dev/exhq/ajarc/register/Register.java similarity index 79% rename from src/main/java/dev/exhq/ajarc/items/Register.java rename to src/main/java/dev/exhq/ajarc/register/Register.java index 907a585..9d6fb97 100644 --- a/src/main/java/dev/exhq/ajarc/items/Register.java +++ b/src/main/java/dev/exhq/ajarc/register/Register.java @@ -1,14 +1,15 @@ -package dev.exhq.ajarc.items; +package dev.exhq.ajarc.register; -import dev.exhq.ajarc.items.blocks.NeaBlock; +import dev.exhq.ajarc.computer.ComputerBlock; +import dev.exhq.ajarc.computer.ComputerBlockEntity; +import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; import net.minecraft.network.chat.Component; import net.minecraft.world.food.FoodProperties; -import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.CreativeModeTabs; import net.minecraft.world.item.Item; -import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.material.MapColor; import net.neoforged.bus.api.IEventBus; @@ -20,6 +21,7 @@ import net.neoforged.neoforge.registries.DeferredRegister; import java.util.ArrayList; import java.util.List; import java.util.function.Function; +import java.util.function.Supplier; import static dev.exhq.ajarc.Ajar.MODID; @@ -40,7 +42,8 @@ public class Register { public static final DeferredRegister.Items ITEMS = registry(DeferredRegister.createItems(MODID)); public static final DeferredRegister CREATIVE_MODE_TABS = registry(DeferredRegister.create(Registries.CREATIVE_MODE_TAB, MODID)); - + public static final DeferredRegister> BLOCK_ENTITIES = + registry(DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, MODID)); /// /// @@ -58,7 +61,12 @@ public class Register { "example_block", NeaBlock::new, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_GREEN)); public static final DeferredBlock OTHER_COMPUTER_BLOCK = block( "other_block", NeaBlock::new, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_RED)); - + public static final DeferredBlock COMPUTER_BLOCK = block("computer", ComputerBlock::new, BlockBehaviour.Properties.of()); + public static final Supplier> COMPUTER_BLOCK_ENTITY = + BLOCK_ENTITIES + .register("computer", + () -> BlockEntityType.Builder.of(ComputerBlockEntity::new, COMPUTER_BLOCK.get()) + .build(null)); /// ///