Added the Shared Ender Chest

This commit is contained in:
2025-03-26 00:43:43 +01:00
parent 78a56b6f28
commit 7ad7ce6ed4
7 changed files with 304 additions and 12 deletions

View File

@ -13,15 +13,21 @@ As a challenge I'm trying to make it as user-friendly as possible. (It ain't the
### Commands ### 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. - /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 ### Features
- Config - [x] Config
- [x] Configurable messages - [x] Configurable messages
- [x] Feature toggle - [x] Feature toggle
- [ ] Shared Ender Chest - [x] Version "control"
- [ ] Access 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 ### Misc
- [x] Updated icon - [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 ### Features
- Vein miner
- Telekinesis
### Commands
- /slimechunk (/sc) | See if you're currently in a slimechunk

View File

@ -9,7 +9,7 @@ yarn_mappings=1.21.4+build.8
loader_version=0.16.10 loader_version=0.16.10
# Mod Properties # Mod Properties
mod_version=1.2.1 mod_version=1.3.0
maven_group=wtf.hak.survivalfabric maven_group=wtf.hak.survivalfabric
archives_base_name=survivalfabric archives_base_name=survivalfabric

View File

@ -3,11 +3,13 @@ package wtf.hak.survivalfabric;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; 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.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import wtf.hak.survivalfabric.commands.ReloadConfigCommand;
import wtf.hak.survivalfabric.commands.SpectatorCommand; 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 { public class SurvivalFabric implements ModInitializer {
@ -18,6 +20,9 @@ public class SurvivalFabric implements ModInitializer {
@Override @Override
public void onInitialize() { 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) -> 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();
} }
} }

View File

@ -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<ServerCommandSource> 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<ServerCommandSource> context) {
ConfigManager.load();
return 1;
}
}

View File

@ -1,5 +1,11 @@
package wtf.hak.survivalfabric.config; 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 class Config {
public String configVersion = "1.0"; public String configVersion = "1.0";
@ -20,4 +26,21 @@ public class Config {
public String spectatorPrefix = "§8[§eSpectator§8] "; public String spectatorPrefix = "§8[§eSpectator§8] ";
public String unknownPrefix = "§8[§7Unknown§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<String> sharedEnderChestNames = Lists.newArrayList("AlwaysHAK", "LunaticFox");
public ScreenHandlerType<GenericContainerScreenHandler> 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;
};
}
} }

View File

@ -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<ItemStack> 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<ItemStack> 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();
}
}

View File

@ -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<ItemStack> stacks;
public SharedInventory(int inventoryRows) {
this.stacks = DefaultedList.ofSize(inventoryRows * 9, ItemStack.EMPTY);
}
public SharedInventory(DefaultedList<ItemStack> 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<ItemStack> getList(DefaultedList<ItemStack> dl) {
dl = stacks;
return dl;
}
@Override
public int size() {
return stacks.size();
}
@Override
public boolean isEmpty() {
Iterator<ItemStack> 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();
}
}