diff --git a/README.md b/README.md index 0eacfd7..a94314f 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,21 @@ As a challenge I'm trying to make it as user-friendly as possible. (It ain't the ### Commands - /spectator | Essentially server-side free-cam, you get put in spectator and are able to fly around, once you use the command again you get put back to where you were. -## Currently working on +## Currently working on v1.3.0 ### Features -- Config +- [x] Config - [x] Configurable messages - [x] Feature toggle - - [ ] Shared Ender Chest - - [ ] Access control + - [x] Version "control" +- [x] Shared Ender Chest + - [x] Shared EC Access control (via config) +- [ ] Vein miner +- [ ] Telekinesis +### Commands + +- [ ] /slimechunk (/sc) | See if you're currently in a slimechunk ### Misc - [x] Updated icon @@ -30,9 +36,5 @@ As a challenge I'm trying to make it as user-friendly as possible. (It ain't the ### Features -- Vein miner -- Telekinesis -### Commands -- /slimechunk (/sc) | See if you're currently in a slimechunk \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 06eadf3..e337af0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,7 +9,7 @@ yarn_mappings=1.21.4+build.8 loader_version=0.16.10 # Mod Properties -mod_version=1.2.1 +mod_version=1.3.0 maven_group=wtf.hak.survivalfabric archives_base_name=survivalfabric diff --git a/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java b/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java index 46541d8..ddd2f46 100644 --- a/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java +++ b/src/main/java/wtf/hak/survivalfabric/SurvivalFabric.java @@ -3,11 +3,13 @@ package wtf.hak.survivalfabric; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; -import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents; -import net.minecraft.scoreboard.Team; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import wtf.hak.survivalfabric.commands.ReloadConfigCommand; import wtf.hak.survivalfabric.commands.SpectatorCommand; +import wtf.hak.survivalfabric.sharedenderchest.SharedEnderChest; + +import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; public class SurvivalFabric implements ModInitializer { @@ -18,6 +20,9 @@ public class SurvivalFabric implements ModInitializer { @Override public void onInitialize() { CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SpectatorCommand.register(dispatcher, new String[] { "spectator", "s", "S", "camera", "c", "C", })); - + CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> ReloadConfigCommand.register(dispatcher, new String[] { "reloadsurvivalconfig" })); + + if(getConfig().sharedEnderChestEnabled) + new SharedEnderChest().onInitialize(); } } \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/commands/ReloadConfigCommand.java b/src/main/java/wtf/hak/survivalfabric/commands/ReloadConfigCommand.java new file mode 100644 index 0000000..85a3f35 --- /dev/null +++ b/src/main/java/wtf/hak/survivalfabric/commands/ReloadConfigCommand.java @@ -0,0 +1,22 @@ +package wtf.hak.survivalfabric.commands; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; +import net.minecraft.server.command.CommandManager; +import net.minecraft.server.command.ServerCommandSource; +import wtf.hak.survivalfabric.config.ConfigManager; + +public class ReloadConfigCommand { + + public static void register(CommandDispatcher dispatcher, String... aliases) { + for (String str : aliases) + dispatcher.register(CommandManager.literal(str) + .requires(source -> source.hasPermissionLevel(2)) + .executes(ReloadConfigCommand::execute)); + } + + private static int execute(CommandContext context) { + ConfigManager.load(); + return 1; + } +} diff --git a/src/main/java/wtf/hak/survivalfabric/config/Config.java b/src/main/java/wtf/hak/survivalfabric/config/Config.java index 3cd7e15..fecfb19 100644 --- a/src/main/java/wtf/hak/survivalfabric/config/Config.java +++ b/src/main/java/wtf/hak/survivalfabric/config/Config.java @@ -1,5 +1,11 @@ package wtf.hak.survivalfabric.config; +import com.google.common.collect.Lists; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.ScreenHandlerType; + +import java.util.List; + public class Config { public String configVersion = "1.0"; @@ -20,4 +26,21 @@ public class Config { public String spectatorPrefix = "§8[§eSpectator§8] "; public String unknownPrefix = "§8[§7Unknown§8] "; + public boolean sharedEnderChestEnabled = true; + public String sharedEnderChestName = "Ender Chest"; + public int sharedEnderChestRows = 6; + public boolean sharedEnderChestLimitedAccess = true; + public List sharedEnderChestNames = Lists.newArrayList("AlwaysHAK", "LunaticFox"); + + public ScreenHandlerType screenHandlerType() { + return switch (sharedEnderChestRows) { + case 1 -> ScreenHandlerType.GENERIC_9X1; + case 2 -> ScreenHandlerType.GENERIC_9X2; + case 3 -> ScreenHandlerType.GENERIC_9X3; + case 4 -> ScreenHandlerType.GENERIC_9X4; + case 5 -> ScreenHandlerType.GENERIC_9X5; + case 6 -> ScreenHandlerType.GENERIC_9X6; + default -> ScreenHandlerType.GENERIC_9X3; + }; + } } \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/sharedenderchest/SharedEnderChest.java b/src/main/java/wtf/hak/survivalfabric/sharedenderchest/SharedEnderChest.java new file mode 100644 index 0000000..32fccda --- /dev/null +++ b/src/main/java/wtf/hak/survivalfabric/sharedenderchest/SharedEnderChest.java @@ -0,0 +1,129 @@ +package wtf.hak.survivalfabric.sharedenderchest; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; + +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; + +import net.minecraft.block.EnderChestBlock; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventories; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.NbtSizeTracker; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.SimpleNamedScreenHandlerFactory; +import net.minecraft.server.MinecraftServer; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvents; +import net.minecraft.text.Text; +import net.minecraft.util.*; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import static wtf.hak.survivalfabric.config.ConfigManager.getConfig; + +public class SharedEnderChest implements ServerLifecycleEvents.ServerStopping, ServerLifecycleEvents.ServerStarted, ServerTickEvents.EndTick { + + private static SharedInventory sharedInventory; + + private long ticksUntilSave = -20; + + public void onServerStarted(MinecraftServer server) { + File inventoryFile = getFile(server); + if (inventoryFile.exists()) { + try (FileInputStream inventoryFileInputStream = new FileInputStream(inventoryFile); + DataInputStream inventoryFileDataInput = new DataInputStream(inventoryFileInputStream)) { + NbtCompound nbt = NbtIo.readCompressed(inventoryFileDataInput, NbtSizeTracker.ofUnlimitedBytes()); + DefaultedList inventoryItemStacks = DefaultedList.ofSize(getConfig().sharedEnderChestRows * 9, ItemStack.EMPTY); + Inventories.readNbt(nbt, inventoryItemStacks, server.getRegistryManager()); + sharedInventory = new SharedInventory(inventoryItemStacks); + } catch (Exception e) { + System.out.println("[ShareEnderChest] Error while loading inventory: " + e); + sharedInventory = new SharedInventory(getConfig().sharedEnderChestRows); + } + } else { + sharedInventory = new SharedInventory(getConfig().sharedEnderChestRows); + } + } + + public static void saveInventory(MinecraftServer server) { + File inventoryFile = getFile(server); + NbtCompound nbt = new NbtCompound(); + DefaultedList inventoryItemStacks = DefaultedList.ofSize(getConfig().sharedEnderChestRows * 9, ItemStack.EMPTY); + Inventories.writeNbt(nbt, sharedInventory.getList(inventoryItemStacks), server.getRegistryManager()); + try (FileOutputStream inventoryFileOutputStream = new FileOutputStream(inventoryFile); + DataOutputStream inventoryFileDataOutput = new DataOutputStream(inventoryFileOutputStream)) { + inventoryFile.createNewFile(); + NbtIo.writeCompressed(nbt, inventoryFileDataOutput); + } catch (Exception e) { + System.out.println("[ShareEnderChest] Error while saving inventory: " + e); + } + } + + public void onServerStopping(MinecraftServer server) { + saveInventory(server); + } + + public void onEndTick(MinecraftServer server) { + if (ticksUntilSave != -20 && --ticksUntilSave <= 0L) { + saveInventory(server); + ticksUntilSave = 20L; + } + } + + public void onInitialize() { + ticksUntilSave = 20L; + + UseBlockCallback listenerUseBlock = (player, world, hand, hitResult) -> { + + if (world.getBlockState(hitResult.getBlockPos()).getBlock() instanceof EnderChestBlock) { + if (!player.isSpectator()) { + if(!getConfig().sharedEnderChestLimitedAccess) { + if (world.isClient()) return ActionResult.SUCCESS; + playEnderChestOpenSound(world, hitResult.getBlockPos()); + openSharedEnderChest(player); + return ActionResult.SUCCESS; + } else { + for(String name : getConfig().sharedEnderChestNames) { + if(name.toLowerCase().strip().equalsIgnoreCase(player.getNameForScoreboard().toLowerCase())) { + if (world.isClient()) return ActionResult.SUCCESS; + playEnderChestOpenSound(world, hitResult.getBlockPos()); + openSharedEnderChest(player); + return ActionResult.SUCCESS; + } + } + } + } + } + return ActionResult.PASS; + }; + + UseBlockCallback.EVENT.register(listenerUseBlock); + ServerLifecycleEvents.SERVER_STARTED.register(this); + ServerLifecycleEvents.SERVER_STOPPING.register(this); + ServerTickEvents.END_SERVER_TICK.register(this); + + } + + public static void openSharedEnderChest(PlayerEntity player) { + player.openHandledScreen(new SimpleNamedScreenHandlerFactory((int_1, playerInventory, playerEntity) -> + new GenericContainerScreenHandler(getConfig().screenHandlerType(), int_1, playerInventory, sharedInventory, getConfig().sharedEnderChestRows), Text.of(getConfig().sharedEnderChestName))); + } + + public static void playEnderChestOpenSound(World world, BlockPos pos) { + world.playSound(null, pos, SoundEvents.BLOCK_ENDER_CHEST_OPEN, SoundCategory.BLOCKS, 0.5F, world.random.nextFloat() * 0.1F + 0.9F); + } + + private static File getFile(MinecraftServer server) { + return server.getSavePath(WorldSavePath.ROOT).resolve("sharedenderchest.sav").toFile(); + } + +} \ No newline at end of file diff --git a/src/main/java/wtf/hak/survivalfabric/sharedenderchest/SharedInventory.java b/src/main/java/wtf/hak/survivalfabric/sharedenderchest/SharedInventory.java new file mode 100644 index 0000000..d441f03 --- /dev/null +++ b/src/main/java/wtf/hak/survivalfabric/sharedenderchest/SharedInventory.java @@ -0,0 +1,111 @@ +package wtf.hak.survivalfabric.sharedenderchest; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventories; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; + +import java.util.Iterator; + +public class SharedInventory implements Inventory { + + private final DefaultedList stacks; + + public SharedInventory(int inventoryRows) { + this.stacks = DefaultedList.ofSize(inventoryRows * 9, ItemStack.EMPTY); + } + + public SharedInventory(DefaultedList dl) { + this.stacks = dl; + } + + /* + @Override + public void onClose(PlayerEntity player) { + Inventory.super.onClose(player); + EnderChestBlockEntity blockEntity = enderChests.remove(player); + if (blockEntity != null) + blockEntity.onClose(player); + } + + @Override + public void onOpen(PlayerEntity player) { + Inventory.super.onOpen(player); + EnderChestBlockEntity blockEntity = enderChests.get(player); + if (blockEntity != null) + blockEntity.onOpen(player); + } + + public void setBlockEntity(PlayerEntity player, EnderChestBlockEntity be) { + enderChests.put(player, be); + } + */ + + + public DefaultedList getList(DefaultedList dl) { + dl = stacks; + return dl; + } + + @Override + public int size() { + return stacks.size(); + } + + @Override + public boolean isEmpty() { + Iterator var1 = this.stacks.iterator(); + + ItemStack itemStack_1; + do { + if (!var1.hasNext()) { + return true; + } + + itemStack_1 = (ItemStack)var1.next(); + } while(itemStack_1.isEmpty()); + + return false; + } + + @Override + public ItemStack getStack(int i) { + return i >= stacks.size() ? ItemStack.EMPTY : stacks.get(i); + } + + @Override + public ItemStack removeStack(int int_1, int int_2) { + ItemStack itemStack_1 = Inventories.splitStack(this.stacks, int_1, int_2); + if (!itemStack_1.isEmpty()) { + //this.container.onContentChanged(this); + } + + return itemStack_1; + } + + @Override + public ItemStack removeStack(int i) { + return Inventories.removeStack(this.stacks, i); + } + + @Override + public void setStack(int i, ItemStack itemStack) { + this.stacks.set(i, itemStack); + //this.container.onContentChanged(this); + } + + @Override + public void markDirty() { + + } + + @Override + public boolean canPlayerUse(PlayerEntity playerEntity) { + return true; + } + + @Override + public void clear() { + stacks.clear(); + } +} \ No newline at end of file