diff --git a/README.md b/README.md index 9cc9eb5..ce74066 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,13 @@ # Sit! -[![github](https://img.shields.io/github/v/release/oth3r/Sit?color=blueviolet&logo=github)](https://github.com/Oth3r/Sit/releases) [![discord](https://dcbadge.vercel.app/api/server/Mec6yNQ9B7?style=flat)](https://discord.gg/Mec6yNQ9B7) [![modrinth](https://img.shields.io/modrinth/dt/sit!?label=Modrinth&logo=modrinth)](https://modrinth.com/mod/sit!) [![curseforge](https://cf.way2muchnoise.eu/892424.svg)](https://www.curseforge.com/minecraft/mc-mods/Sit1) +[![github](https://img.shields.io/github/issues/oth3r/Sit?logo=github?label=Issues)](https://github.com/Oth3r/Sit/releases) [![Crowdin](https://badges.crowdin.net/oth3r-sit/localized.svg)](https://crowdin.com/project/oth3r-sit) [![discord](https://dcbadge.vercel.app/api/server/Mec6yNQ9B7?style=flat)](https://discord.gg/Mec6yNQ9B7) + +[![modrinth](https://img.shields.io/modrinth/dt/sit!?label=Modrinth&logo=modrinth)](https://modrinth.com/mod/sit!) [![curseforge](https://cf.way2muchnoise.eu/892424.svg)](https://www.curseforge.com/minecraft/mc-mods/Sit1) **Sit!** is a minecraft mod that adds sitting in vanilla minecraft. Sit on **stairs**, **slabs**, **carpets** by default, and sit on everything else using the config! -You can also customize hand restrictions to stop accidentally sitting down! - +You can also customize hand restrictions to stop accidentally sitting down! +The mod also has full Geyser support! Enjoy sitting with everyone, weather they are on Bedrock or Java! +## Help localize Sit! on [Crowdin](https://crowdin.com/project/oth3r-sit)! ![overview](https://github.com/Oth3r/Sit/blob/master/media/overview.gif?raw=true) ## Check out my other Projects! [![DirectionHUD badge](https://github.com/Oth3r/DirectionHUD/blob/master/media/mod-badge.png?raw=true)](https://modrinth.com/mod/directionhud) @@ -26,5 +29,5 @@ Configure to your hearts desire with the in-game config with **[ModMenu](https:/ ![custom blocks config](https://github.com/Oth3r/Sit/blob/master/media/custom-blocks-config.png?raw=true) ## Future Goals - * Forge Port - * Client & Server sync to have custom hand restrictions per player \ No newline at end of file + * Forge Port (probably NeoForge 1.21) + * Custom dismounting logic diff --git a/build.gradle b/build.gradle index 81698be..eea3b51 100644 --- a/build.gradle +++ b/build.gradle @@ -21,13 +21,6 @@ dependencies { modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" - -// modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}" -// modImplementation "dev.isxander.yacl:yet-another-config-lib-fabric:${project.yacl_version}" - - // Uncomment the following line to enable the deprecated Fabric API modules. - // These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time. - // modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}" } processResources { diff --git a/gradle.properties b/gradle.properties index 14c1777..c38bb12 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,5 +15,3 @@ archives_base_name=sit! # Dependencies fabric_version=0.90.11+1.20.3 -modmenu_version=7.0.0 -yacl_version=3.0.0+1.20 diff --git a/src/main/java/one/oth3r/sit/Events.java b/src/main/java/one/oth3r/sit/Events.java index 70a8540..444e023 100644 --- a/src/main/java/one/oth3r/sit/Events.java +++ b/src/main/java/one/oth3r/sit/Events.java @@ -12,7 +12,7 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.decoration.DisplayEntity; import net.minecraft.item.BlockItem; -import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; import net.minecraft.registry.Registries; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.Text; @@ -23,6 +23,7 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import one.oth3r.sit.Utl.HandType; import java.util.*; @@ -36,34 +37,39 @@ public class Events { food.add(UseAction.DRINK); ArrayList notUsable = new ArrayList<>(food); notUsable.add(UseAction.NONE); - Item mainItem = player.getMainHandStack().getItem(); - Item offItem = player.getOffHandStack().getItem(); + HashMap itemMap = new HashMap<>(); + itemMap.put(HandType.main,player.getMainHandStack()); + itemMap.put(HandType.off,player.getOffHandStack()); + // if sneaking cant sit if (player.isSneaking()) return false; - if (config.mainReq.equals(config.HandRequirements.empty) && !player.getMainHandStack().isEmpty()) return false; - if (config.mainReq.equals(config.HandRequirements.restrictive)) { - if (checkList(config.mainBlacklist,mainItem)) return false; - if (!checkList(config.mainWhitelist,mainItem)) { - if (config.mainBlock && (mainItem instanceof BlockItem)) return false; - if (config.mainFood && food.contains(player.getMainHandStack().getUseAction())) return false; - if (config.mainUsable && !notUsable.contains(player.getMainHandStack().getUseAction())) return false; - } - } - if (config.offReq.equals(config.HandRequirements.empty) && !player.getOffHandStack().isEmpty()) return false; - if (config.offReq.equals(config.HandRequirements.restrictive)) { - if (checkList(config.offBlacklist,offItem)) return false; - if (!checkList(config.offWhitelist,offItem)) { - if (config.offBlock && (offItem instanceof BlockItem)) return false; - if (config.offFood && food.contains(player.getOffHandStack().getUseAction())) return false; - if (config.offUsable && !notUsable.contains(player.getOffHandStack().getUseAction())) return false; + // for both hands + for (HandType type:HandType.values()) { + ItemStack targetStack = itemMap.get(type); + // if req is empty and the item isn't empty, false + if (Utl.getReq(player,type).equals(config.HandRequirement.empty) && !targetStack.isEmpty()) return false; + // if req is restrictive + if (Utl.getReq(player,type).equals(config.HandRequirement.restrictive)) { + // if item is in blacklist, false + if (checkList(Utl.getList(player,type,"blacklist"),targetStack)) return false; + // if item is NOT in whitelist + if (!checkList(Utl.getList(player,type,"whitelist"),targetStack)) { + // if block is restricted and items is block, false, ect + if (Utl.getBool(player,type,"block") && (targetStack.getItem() instanceof BlockItem)) return false; + if (Utl.getBool(player,type,"food") && food.contains(targetStack.getUseAction())) return false; + if (Utl.getBool(player,type,"usable") && !notUsable.contains(targetStack.getUseAction())) return false; + } } } + // else true return true; } - public static boolean checkList(List list, Item item) { - String itemID = Registries.ITEM.getId(item).toString(); + public static boolean checkList(List list, ItemStack itemStack) { + // check if a list has an item + String itemID = Registries.ITEM.getId(itemStack.getItem()).toString(); return list.contains(itemID); } public static HashMap> getCustomBlocks() { + // get a hashmap of custom blocks HashMap> map = new HashMap<>(); int i = 1; for (String s:config.customBlocks) { @@ -78,17 +84,21 @@ public class Events { } return map; } - public static boolean checkBlocks(BlockPos pos, World world) { + public static boolean isSitSafe(Block block) { + // check if the block is sit safe (like a sign in the way) + return block instanceof WallSignBlock || block instanceof TrapdoorBlock || + block instanceof WallBannerBlock || block instanceof AirBlock; + } + public static boolean checkBlocks(BlockPos pos, World world, boolean isAbove) { BlockState blockState = world.getBlockState(pos); Block block = blockState.getBlock(); - BlockState blockStateAbove = world.getBlockState(pos.add(0,1,0)); - Block blockAbove = blockStateAbove.getBlock(); - // todo strict checker option to check 2 blocks above?? - // set amount of blocks that can be above a chair & air - if (!(blockAbove instanceof WallSignBlock || blockAbove instanceof TrapdoorBlock || - blockAbove instanceof WallBannerBlock || blockAbove instanceof AirBlock)) return false; - //if there's already an entity at the block location or above it + // make sure the block above the chair is safe + if (!isSitSafe(world.getBlockState(pos.add(0,1,0)).getBlock())) return false; + // if the player is above the block, (taller) check the next block above + if (isAbove && !isSitSafe(world.getBlockState(pos.add(0,2,0)).getBlock())) return false; + //if there's already an entity at the block location or one above it for (Entity entity:entities.values()) if (entity.getBlockPos().equals(pos) || entity.getBlockPos().add(0,1,0).equals(pos)) return false; + // return for the 4 default types if (block instanceof StairsBlock && config.stairsOn) return blockState.get(StairsBlock.HALF) == BlockHalf.BOTTOM; if (block instanceof SlabBlock && config.slabsOn) return blockState.get(SlabBlock.TYPE) == SlabType.BOTTOM; @@ -113,9 +123,13 @@ public class Events { } return false; } + public static boolean isAboveBlockheight(Entity entity) { + return entity.getPitch()<0; + } public static void setEntity(BlockPos pos, World world, Entity entity) { Block block = world.getBlockState(pos).getBlock(); entity.setCustomName(Text.of(Sit.ENTITY_NAME)); + entity.setCustomNameVisible(false); double hitBoxY = 0.5; entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+.47, pos.getZ() + 0.5, 0, 0); entity.setInvulnerable(true); @@ -145,18 +159,20 @@ public class Events { } } } - //1.20.2 mounting pos change (shifts everything down by .25) - entity.updatePositionAndAngles(entity.getX(),entity.getY()+.25,entity.getZ(),0,0); + entity.updatePositionAndAngles(entity.getX(),entity.getY(),entity.getZ(),0,0); entity.setBoundingBox(Box.of(Vec3d.of(pos),1.5,hitBoxY,1.5)); //change pitch based on if player is sitting below block height or not - if (entity.getY() <= pos.getY()+.35+.25) entity.setPitch(90); - else entity.setPitch(-90); + if (entity.getY() <= pos.getY()+.35) entity.setPitch(90); // below + else entity.setPitch(-90); // above } public static void register() { ServerTickEvents.END_SERVER_TICK.register(minecraftServer -> minecraftServer.execute(Events::cleanUp)); + // PLAYER JOIN ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { ServerPlayerEntity player = handler.player; checkPlayers.put(player,2); + // put server settings in the player settings + Sit.playerSettings.put(player,Utl.getHandSettings()); }); ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> { ServerPlayerEntity player = handler.player; @@ -168,6 +184,7 @@ public class Events { entities.remove(player); } checkPlayers.remove(player); + Sit.playerSettings.remove(player); }); ServerLifecycleEvents.SERVER_STARTED.register(s -> { Sit.server = s; @@ -178,18 +195,20 @@ public class Events { if (hand == net.minecraft.util.Hand.MAIN_HAND && hitResult.getType() == HitResult.Type.BLOCK) { BlockPos pos = hitResult.getBlockPos(); if (!checkLogic(player)) return ActionResult.PASS; - if (checkBlocks(pos,world)) { + // todo interactions entity to make the hitbox? + // make the entity first before checking to make sure the blocks around are fine + DisplayEntity.TextDisplayEntity entity = new DisplayEntity.TextDisplayEntity(EntityType.TEXT_DISPLAY,player.getServerWorld()); + setEntity(pos,world,entity); + if (checkBlocks(pos,world,isAboveBlockheight(entity))) { if (entities.containsKey(player)) { if (!config.sitWhileSeated) return ActionResult.PASS; entities.get(player).setRemoved(Entity.RemovalReason.DISCARDED); entities.remove(player); } - DisplayEntity.TextDisplayEntity entity = new DisplayEntity.TextDisplayEntity(EntityType.TEXT_DISPLAY,player.getServerWorld()); - setEntity(pos,world,entity); player.getServerWorld().spawnEntity(entity); player.startRiding(entity); entities.put(player,entity); - return ActionResult.CONSUME; + return ActionResult.FAIL; } } return ActionResult.PASS; @@ -210,9 +229,12 @@ public class Events { entity.setRemoved(Entity.RemovalReason.DISCARDED); entityLoop.remove(); } else { - BlockPos pos = new BlockPos(entity.getBlockX(),(int) Math.floor(player.getY()),entity.getBlockZ()); - if (entity.getPitch() == 90) pos = new BlockPos(entity.getBlockX(),(int) Math.ceil(player.getY()),entity.getBlockZ()); + // if below the blockheight, round up the player pos + BlockPos pos = new BlockPos(entity.getBlockX(),(int)Math.ceil(player.getY()),entity.getBlockZ()); + // if above the block, round down the player pos + if (isAboveBlockheight(entity)) pos = new BlockPos(entity.getBlockX(),(int)Math.floor(player.getY()),entity.getBlockZ()); BlockState blockState = player.getWorld().getBlockState(pos); + // check if said block is still there if (blockState.isAir()) { player.teleport(player.getX(),player.getBlockY()+1,player.getZ()); entity.setRemoved(Entity.RemovalReason.DISCARDED); diff --git a/src/main/java/one/oth3r/sit/LangReader.java b/src/main/java/one/oth3r/sit/LangReader.java index b6ae51b..0e3cdbd 100644 --- a/src/main/java/one/oth3r/sit/LangReader.java +++ b/src/main/java/one/oth3r/sit/LangReader.java @@ -1,11 +1,19 @@ package one.oth3r.sit; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import net.minecraft.text.MutableText; import net.minecraft.text.Text; -import net.minecraft.text.TextContent; import java.io.InputStream; -import java.util.*; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -75,21 +83,9 @@ public class LangReader { config.lang = config.defaults.lang; } if (inputStream == null) throw new IllegalArgumentException("CANT LOAD THE LANGUAGE FILE. DIRECTIONHUD WILL BREAK."); - Scanner scanner = new Scanner(inputStream); - String currentLine; - while (scanner.hasNextLine()) { - currentLine = scanner.nextLine().trim(); - if (currentLine.startsWith("{") || currentLine.startsWith("}")) { - continue; - } - String[] keyValue = currentLine.split(":", 2); - String key = keyValue[0].trim(); - key = key.substring(1,key.length()-1).replace("\\",""); - String value = keyValue[1].trim(); - if (value.endsWith(",")) value = value.substring(0, value.length() - 1); - value = value.substring(1,value.length()-1).replace("\\",""); - languageMap.put(key, value); - } + Type type = new TypeToken>(){}.getType(); + Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); + languageMap.putAll(new Gson().fromJson(reader, type)); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/one/oth3r/sit/PacketBuilder.java b/src/main/java/one/oth3r/sit/PacketBuilder.java new file mode 100644 index 0000000..538609d --- /dev/null +++ b/src/main/java/one/oth3r/sit/PacketBuilder.java @@ -0,0 +1,34 @@ +package one.oth3r.sit; + +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.util.Identifier; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public class PacketBuilder { + public static final String SETTINGS = "settings_v1.0"; + private final String message; + private PacketByteBuf packetByteBuf = PacketByteBufs.create(); + public PacketBuilder(PacketByteBuf buf) { + // Read any data sent in the packet + message = buf.toString(StandardCharsets.UTF_8); + packetByteBuf = buf; + } + public PacketBuilder(String message) { + this.message = message; + packetByteBuf.writeBytes(ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)).array()); + } + public static Identifier getIdentifier() { + // only 1 packet rn + return new Identifier(Sit.MOD_ID, SETTINGS); + } + public void send() { + ClientPlayNetworking.send(getIdentifier(), packetByteBuf); + } + public String getMessage() { + return this.message; + } +} diff --git a/src/main/java/one/oth3r/sit/Sit.java b/src/main/java/one/oth3r/sit/Sit.java index ee4d704..d621a73 100644 --- a/src/main/java/one/oth3r/sit/Sit.java +++ b/src/main/java/one/oth3r/sit/Sit.java @@ -1,20 +1,35 @@ package one.oth3r.sit; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import io.netty.buffer.ByteBuf; +import io.netty.util.ReferenceCountUtil; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.network.PacketByteBuf; import net.minecraft.server.MinecraftServer; import net.minecraft.server.command.CommandManager; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; + public class Sit implements ModInitializer { public static final Logger LOGGER = LoggerFactory.getLogger("sit"); + public static final String MOD_ID = "oth3r-sit"; + public static HashMap> playerSettings = new HashMap<>(); public static final String ENTITY_NAME = "-sit!-entity-"; public static MinecraftServer server; public static CommandManager commandManager; - public static boolean isClient = true; + public static boolean isClient = false; @Override public void onInitialize() { @@ -23,6 +38,16 @@ public class Sit implements ModInitializer { // inner stair offset & custom support for that ig config.load(); Events.register(); + //PACKETS + ServerPlayNetworking.registerGlobalReceiver(PacketBuilder.getIdentifier(), + (server, player, handler, buf, responseSender) -> { + PacketBuilder packet = new PacketBuilder(buf); + server.execute(() -> { + Type hashMapToken = new TypeToken>() {}.getType(); + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + playerSettings.put(player,gson.fromJson(packet.getMessage(),hashMapToken)); + }); + }); } public static MutableText lang(String key, Object... args) { if (isClient) return Text.translatable(key, args); diff --git a/src/main/java/one/oth3r/sit/SitClient.java b/src/main/java/one/oth3r/sit/SitClient.java new file mode 100644 index 0000000..2a1d2e1 --- /dev/null +++ b/src/main/java/one/oth3r/sit/SitClient.java @@ -0,0 +1,25 @@ +package one.oth3r.sit; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; + +public class SitClient implements ClientModInitializer { + public static boolean inGame = false; + @Override + public void onInitializeClient() { + Sit.isClient = true; + ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { + inGame = true; + // send a data packet whenever joining a server + client.execute(SitClient::sendPackets); + }); + // reset inGame + ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> inGame = false); + } + public static void sendPackets() { + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + new PacketBuilder(gson.toJson(Utl.getHandSettings())).send(); + } +} diff --git a/src/main/java/one/oth3r/sit/SitCommand.java b/src/main/java/one/oth3r/sit/SitCommand.java index 8d59a80..0fee18c 100644 --- a/src/main/java/one/oth3r/sit/SitCommand.java +++ b/src/main/java/one/oth3r/sit/SitCommand.java @@ -36,7 +36,6 @@ public class SitCommand { } private static int command(ServerCommandSource source, String arg) { ServerPlayerEntity player = source.getPlayer(); - if (player == null) return 1; //trim all the arguments before the command String keyword = "sit"; int index = Integer.MAX_VALUE; @@ -46,18 +45,26 @@ public class SitCommand { String[] args = arg.split(" "); if (args[0].equalsIgnoreCase("sit")) args = arg.replaceFirst("sit ", "").split(" "); + // if console + if (player == null) { + if (args[0].equalsIgnoreCase("reload")) { + config.load(); + Sit.LOGGER.info(Sit.lang("key.sit.command.reloaded").getString()); + } + return 1; + } if (args[0].equalsIgnoreCase("sit")) { BlockPos pos = player.getBlockPos(); if (!(player.getY() -((int) player.getY()) > 0.00)) { pos = pos.add(0,-1,0); } World world = player.getWorld(); - if (Events.checkBlocks(pos,world)) { - if (Events.entities.containsKey(player)) { - return 1; - } - DisplayEntity.TextDisplayEntity entity = new DisplayEntity.TextDisplayEntity(EntityType.TEXT_DISPLAY,player.getServerWorld()); - Events.setEntity(pos,world,entity); + // if already sitting, ignore + if (Events.entities.containsKey(player)) return 1; + // make entity first to check the blocks + DisplayEntity.TextDisplayEntity entity = new DisplayEntity.TextDisplayEntity(EntityType.TEXT_DISPLAY,player.getServerWorld()); + Events.setEntity(pos,world,entity); + if (Events.checkBlocks(pos,world,Events.isAboveBlockheight(entity))) { player.getServerWorld().spawnEntity(entity); player.startRiding(entity); Events.entities.put(player,entity); diff --git a/src/main/java/one/oth3r/sit/SitServer.java b/src/main/java/one/oth3r/sit/SitServer.java deleted file mode 100644 index 199f5da..0000000 --- a/src/main/java/one/oth3r/sit/SitServer.java +++ /dev/null @@ -1,12 +0,0 @@ -package one.oth3r.sit; - -import net.fabricmc.api.DedicatedServerModInitializer; - -public class SitServer implements DedicatedServerModInitializer { - @Override - public void onInitializeServer() { - Sit.isClient = false; - config.load(); - LangReader.loadLanguageFile(); - } -} diff --git a/src/main/java/one/oth3r/sit/Utl.java b/src/main/java/one/oth3r/sit/Utl.java new file mode 100644 index 0000000..3045ec1 --- /dev/null +++ b/src/main/java/one/oth3r/sit/Utl.java @@ -0,0 +1,45 @@ +package one.oth3r.sit; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import net.minecraft.server.network.ServerPlayerEntity; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class Utl { + enum HandType { + main, + off + } + public static HashMap getHandSettings() { + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + HashMap settings = new HashMap<>(); + settings.put("hand.main.requirement",String.valueOf(config.mainReq)); + settings.put("hand.main.block",String.valueOf(config.mainBlock)); + settings.put("hand.main.food",String.valueOf(config.mainFood)); + settings.put("hand.main.usable",String.valueOf(config.mainUsable)); + settings.put("hand.main.whitelist",gson.toJson(config.mainWhitelist)); + settings.put("hand.main.blacklist",gson.toJson(config.mainBlacklist)); + settings.put("hand.off.requirement",String.valueOf(config.offReq)); + settings.put("hand.off.block",String.valueOf(config.offBlock)); + settings.put("hand.off.food",String.valueOf(config.offFood)); + settings.put("hand.off.usable",String.valueOf(config.offUsable)); + settings.put("hand.off.whitelist",gson.toJson(config.offWhitelist)); + settings.put("hand.off.blacklist",gson.toJson(config.offBlacklist)); + return settings; + } + public static config.HandRequirement getReq(ServerPlayerEntity player, HandType type) { + return config.HandRequirement.get(Sit.playerSettings.get(player).get("hand."+type+".requirement")); + } + public static List getList(ServerPlayerEntity player, HandType type, String setting) { + Type listType = new TypeToken>() {}.getType(); + return new Gson().fromJson(Sit.playerSettings.get(player).get("hand."+type+"."+setting),listType); + } + public static boolean getBool(ServerPlayerEntity player, HandType type, String setting) { + return Boolean.parseBoolean(Sit.playerSettings.get(player).get("hand."+type+"."+setting)); + } +} diff --git a/src/main/java/one/oth3r/sit/config.java b/src/main/java/one/oth3r/sit/config.java index 409349b..2f67973 100644 --- a/src/main/java/one/oth3r/sit/config.java +++ b/src/main/java/one/oth3r/sit/config.java @@ -4,12 +4,12 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.reflect.TypeToken; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.text.MutableText; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -24,47 +24,31 @@ public class config { public static boolean fullBlocksOn = defaults.fullBlocksOn; public static boolean customOn = defaults.customOn; public static List customBlocks = defaults.customBlocks; - enum HandRequirements { + enum HandRequirement { empty, restrictive, - none + none; + public static HandRequirement get(String s) { + try { + return HandRequirement.valueOf(s); + + } catch (IllegalArgumentException e) { + return empty; + } + } } - public static HandRequirements mainReq = defaults.mainReq; + public static HandRequirement mainReq = defaults.mainReq; public static boolean mainBlock = defaults.mainBlock; public static boolean mainFood = defaults.mainFood; public static boolean mainUsable = defaults.mainUsable; public static List mainWhitelist = defaults.mainWhitelist; public static List mainBlacklist = defaults.mainBlacklist; - public static HandRequirements offReq = defaults.offReq; + public static HandRequirement offReq = defaults.offReq; public static boolean offBlock = defaults.offBlock; public static boolean offFood = defaults.offFood; public static boolean offUsable = defaults.offUsable; public static List offWhitelist = defaults.offWhitelist; public static List offBlacklist = defaults.offBlacklist; - public static void resetDefaults() { - lang = defaults.lang; - keepActive = defaults.keepActive; - sitWhileSeated = defaults.sitWhileSeated; - stairsOn = defaults.stairsOn; - slabsOn = defaults.slabsOn; - carpetsOn = defaults.carpetsOn; - fullBlocksOn = defaults.fullBlocksOn; - customOn = defaults.customOn; - customBlocks = defaults.customBlocks; - mainReq = defaults.mainReq; - mainBlock = defaults.mainBlock; - mainFood = defaults.mainFood; - mainUsable = defaults.mainUsable; - mainWhitelist = defaults.mainWhitelist; - mainBlacklist = defaults.mainBlacklist; - offReq = defaults.offReq; - offBlock = defaults.offBlock; - offFood = defaults.offFood; - offUsable = defaults.offUsable; - offWhitelist = defaults.offWhitelist; - offBlacklist = defaults.offBlacklist; - save(); - } public static File configFile() { return new File(FabricLoader.getInstance().getConfigDir().toFile()+"/Sit!.properties"); } @@ -77,66 +61,94 @@ public class config { try (FileInputStream fileStream = new FileInputStream(configFile())) { Properties properties = new Properties(); properties.load(fileStream); - loadVersion(properties,(String) properties.computeIfAbsent("version", a -> defaults.version)); + String ver = (String) properties.computeIfAbsent("version", a -> String.valueOf(defaults.version)); + // if the old version system (v1.0) remove "v: + if (ver.contains("v")) ver = ver.substring(1); + loadVersion(properties,Double.parseDouble(ver)); + LangReader.loadLanguageFile(); save(); } catch (Exception f) { //read fail f.printStackTrace(); - resetDefaults(); + save(); } } - public static void loadVersion(Properties properties, String version) { - Type mapType = new TypeToken>() {}.getType(); - lang = (String) properties.computeIfAbsent("lang", a -> defaults.lang); - //CONFIG - keepActive = Boolean.parseBoolean((String) properties.computeIfAbsent("keep-active", a -> String.valueOf(defaults.keepActive))); - sitWhileSeated = Boolean.parseBoolean((String) properties.computeIfAbsent("sit-while-seated", a -> String.valueOf(defaults.sitWhileSeated))); - stairsOn = Boolean.parseBoolean((String) properties.computeIfAbsent("stairs", a -> String.valueOf(defaults.stairsOn))); - slabsOn = Boolean.parseBoolean((String) properties.computeIfAbsent("slabs", a -> String.valueOf(defaults.slabsOn))); - carpetsOn = Boolean.parseBoolean((String) properties.computeIfAbsent("carpets", a -> String.valueOf(defaults.carpetsOn))); - fullBlocksOn = Boolean.parseBoolean((String) properties.computeIfAbsent("full-blocks", a -> String.valueOf(defaults.fullBlocksOn))); - customOn = Boolean.parseBoolean((String) properties.computeIfAbsent("custom", a -> String.valueOf(defaults.customOn))); - customBlocks = new Gson().fromJson((String) - properties.computeIfAbsent("custom-blocks", a -> String.valueOf(defaults.customBlocks)),mapType); - mainReq = HandRequirements.valueOf((String) properties.computeIfAbsent("main-hand-requirement", a -> String.valueOf(defaults.mainReq))); - mainBlock = Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-block", a -> String.valueOf(defaults.mainBlock))); - mainFood = Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-food", a -> String.valueOf(defaults.mainFood))); - mainUsable = Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-usable", a -> String.valueOf(defaults.mainUsable))); - mainWhitelist = new Gson().fromJson((String) - properties.computeIfAbsent("main-hand-whitelist", a -> String.valueOf(defaults.mainWhitelist)),mapType); - mainBlacklist = new Gson().fromJson((String) - properties.computeIfAbsent("main-hand-blacklist", a -> String.valueOf(defaults.mainBlacklist)),mapType); - offReq = HandRequirements.valueOf((String) properties.computeIfAbsent("off-hand-requirement", a -> String.valueOf(defaults.offReq))); - offBlock = Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-block", a -> String.valueOf(defaults.offBlock))); - offFood = Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-food", a -> String.valueOf(defaults.offFood))); - offUsable = Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-usable", a -> String.valueOf(defaults.offUsable))); - offWhitelist = new Gson().fromJson((String) - properties.computeIfAbsent("off-hand-whitelist", a -> String.valueOf(defaults.offWhitelist)),mapType); - offBlacklist = new Gson().fromJson((String) - properties.computeIfAbsent("off-hand-blacklist", a -> String.valueOf(defaults.offBlacklist)),mapType); + public static void loadVersion(Properties properties, double version) { + try { + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + Type listType = new TypeToken>() {}.getType(); + lang = (String) properties.computeIfAbsent("lang", a -> defaults.lang); + //CONFIG + keepActive = Boolean.parseBoolean((String) properties.computeIfAbsent("keep-active", a -> String.valueOf(defaults.keepActive))); + sitWhileSeated = Boolean.parseBoolean((String) properties.computeIfAbsent("sit-while-seated", a -> String.valueOf(defaults.sitWhileSeated))); + stairsOn = Boolean.parseBoolean((String) properties.computeIfAbsent("stairs", a -> String.valueOf(defaults.stairsOn))); + slabsOn = Boolean.parseBoolean((String) properties.computeIfAbsent("slabs", a -> String.valueOf(defaults.slabsOn))); + carpetsOn = Boolean.parseBoolean((String) properties.computeIfAbsent("carpets", a -> String.valueOf(defaults.carpetsOn))); + fullBlocksOn = Boolean.parseBoolean((String) properties.computeIfAbsent("full-blocks", a -> String.valueOf(defaults.fullBlocksOn))); + customOn = Boolean.parseBoolean((String) properties.computeIfAbsent("custom", a -> String.valueOf(defaults.customOn))); + customBlocks = new Gson().fromJson((String) + properties.computeIfAbsent("custom-blocks", a -> gson.toJson(defaults.customBlocks)), listType); + mainReq = HandRequirement.valueOf((String) properties.computeIfAbsent("hand.main.requirement", a -> String.valueOf(defaults.mainReq))); + mainBlock = Boolean.parseBoolean((String) properties.computeIfAbsent("hand.main.block", a -> String.valueOf(defaults.mainBlock))); + mainFood = Boolean.parseBoolean((String) properties.computeIfAbsent("hand.main.food", a -> String.valueOf(defaults.mainFood))); + mainUsable = Boolean.parseBoolean((String) properties.computeIfAbsent("hand.main.usable", a -> String.valueOf(defaults.mainUsable))); + mainWhitelist = new Gson().fromJson((String) + properties.computeIfAbsent("hand.main.whitelist", a -> gson.toJson(defaults.mainWhitelist)), listType); + mainBlacklist = new Gson().fromJson((String) + properties.computeIfAbsent("hand.main.blacklist", a -> gson.toJson(defaults.mainBlacklist)), listType); + offReq = HandRequirement.valueOf((String) properties.computeIfAbsent("hand.off.requirement", a -> String.valueOf(defaults.offReq))); + offBlock = Boolean.parseBoolean((String) properties.computeIfAbsent("hand.off.block", a -> String.valueOf(defaults.offBlock))); + offFood = Boolean.parseBoolean((String) properties.computeIfAbsent("hand.off.food", a -> String.valueOf(defaults.offFood))); + offUsable = Boolean.parseBoolean((String) properties.computeIfAbsent("hand.off.usable", a -> String.valueOf(defaults.offUsable))); + offWhitelist = new Gson().fromJson((String) + properties.computeIfAbsent("hand.off.whitelist", a -> gson.toJson(defaults.offWhitelist)), listType); + offBlacklist = new Gson().fromJson((String) + properties.computeIfAbsent("hand.off.blacklist", a -> gson.toJson(defaults.offBlacklist)), listType); + if (version == 1.0) { + mainReq = HandRequirement.valueOf((String) properties.computeIfAbsent("main-hand-requirement", a -> String.valueOf(defaults.mainReq))); + mainBlock = Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-block", a -> String.valueOf(defaults.mainBlock))); + mainFood = Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-food", a -> String.valueOf(defaults.mainFood))); + mainUsable = Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-usable", a -> String.valueOf(defaults.mainUsable))); + mainWhitelist = new Gson().fromJson((String) + properties.computeIfAbsent("main-hand-whitelist", a -> gson.toJson(defaults.mainWhitelist)), listType); + mainBlacklist = new Gson().fromJson((String) + properties.computeIfAbsent("main-hand-blacklist", a -> gson.toJson(defaults.mainBlacklist)), listType); + offReq = HandRequirement.valueOf((String) properties.computeIfAbsent("off-hand-requirement", a -> String.valueOf(defaults.offReq))); + offBlock = Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-block", a -> String.valueOf(defaults.offBlock))); + offFood = Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-food", a -> String.valueOf(defaults.offFood))); + offUsable = Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-usable", a -> String.valueOf(defaults.offUsable))); + offWhitelist = new Gson().fromJson((String) + properties.computeIfAbsent("off-hand-whitelist", a -> gson.toJson(defaults.offWhitelist)), listType); + offBlacklist = new Gson().fromJson((String) + properties.computeIfAbsent("off-hand-blacklist", a -> gson.toJson(defaults.offBlacklist)), listType); + } + } catch (Exception e) { + Sit.LOGGER.info("ERROR LOADING CONFIG - PLEASE REPORT WITH THE ERROR LOG"); + e.printStackTrace(); + } } - public static MutableText lang(String key, Object... args) { - LangReader.loadLanguageFile(); - return LangReader.of("config.sit."+key, args).getTxT(); + public static String lang(String key, Object... args) { + return LangReader.of("config.sit."+key, args).getTxT().getString(); } public static void save() { - try (var file = new FileOutputStream(configFile(), false)) { + try (var file = Files.newBufferedWriter(configFile().toPath(), StandardCharsets.UTF_8)) { Gson gson = new GsonBuilder().disableHtmlEscaping().create(); - file.write("# Sit! Config\n".getBytes()); - file.write(("version="+defaults.version).getBytes()); - file.write(("\n# all available languages: en_us").getBytes()); - file.write(("\nlang=" + lang).getBytes()); - file.write(("\n\n# "+lang("general.keep_active.description").getString()).getBytes()); - file.write(("\nkeep-active=" + keepActive).getBytes()); - file.write(("\n# "+lang("general.sit_while_seated.description").getString()).getBytes()); - file.write(("\nsit-while-seated=" + sitWhileSeated).getBytes()); - file.write(("\n# "+lang("general.sittable.description").getString()).getBytes()); - file.write(("\nstairs=" + stairsOn).getBytes()); - file.write(("\nslabs=" + slabsOn).getBytes()); - file.write(("\ncarpets=" + carpetsOn).getBytes()); - file.write(("\nfull-blocks=" + fullBlocksOn).getBytes()); - file.write(("\ncustom=" + customOn).getBytes()); - file.write(("\n# "+lang("general.sittable_blocks.description") + file.write("# Sit! Config\n"); + file.write(("\nversion="+defaults.version)); + file.write(("\n# all available languages: en_us, ru_ru")); + file.write(("\nlang=" + lang)); + file.write(("\n\n# "+lang("general.keep_active.description"))); + file.write(("\nkeep-active=" + keepActive)); + file.write(("\n# "+lang("general.sit_while_seated.description"))); + file.write(("\nsit-while-seated=" + sitWhileSeated)); + file.write(("\n# "+lang("general.sittable.description"))); + file.write(("\nstairs=" + stairsOn)); + file.write(("\nslabs=" + slabsOn)); + file.write(("\ncarpets=" + carpetsOn)); + file.write(("\nfull-blocks=" + fullBlocksOn)); + file.write(("\ncustom=" + customOn)); + file.write(("\n# "+Sit.lang("config.sit."+ + "general.sittable_blocks.description") .append("\n# ").append(lang("general.sittable_blocks.description_2")) .append(lang("general.sittable_blocks.description_3", lang("general.sittable_blocks.description_3_2"), @@ -147,30 +159,34 @@ public class config { .append("\n# ").append(lang("general.sittable_blocks.description_5")) .append("\n# ").append(lang("general.sittable_blocks.description_6")) .append("\n# ").append(lang("general.sittable_blocks.description_7")) - .append("\n# ").append(lang("general.sittable_blocks.description_8")).getString()).getBytes()); - file.write(("\ncustom-blocks="+gson.toJson(customBlocks)).getBytes()); - file.write(("\n\n# "+lang("hand.requirements.description") + .append("\n# ").append(lang("general.sittable_blocks.description_8")))); + file.write(("\ncustom-blocks="+gson.toJson(customBlocks))); + file.write(("\n\n# "+lang("hand"))); + file.write(("\n# "+Sit.lang("config.sit."+ + "hand.requirements.description") .append("\n# ").append(lang("hand.requirements.description_2")) .append("\n# ").append(lang("hand.requirements.description_3")) - .append("\n# ").append(lang("hand.requirements.description_4")).getString()).getBytes()); - file.write(("\nmain-hand-requirement=" + mainReq).getBytes()); - file.write(("\nmain-hand-block=" + mainBlock).getBytes()); - file.write(("\nmain-hand-food=" + mainFood).getBytes()); - file.write(("\nmain-hand-usable=" + mainUsable).getBytes()); - file.write(("\nmain-hand-whitelist="+gson.toJson(mainWhitelist)).getBytes()); - file.write(("\nmain-hand-blacklist="+gson.toJson(mainBlacklist)).getBytes()); - file.write(("\noff-hand-requirement=" + offReq).getBytes()); - file.write(("\noff-hand-block=" + offBlock).getBytes()); - file.write(("\noff-hand-food=" + offFood).getBytes()); - file.write(("\noff-hand-usable=" + offUsable).getBytes()); - file.write(("\noff-hand-whitelist="+gson.toJson(offWhitelist)).getBytes()); - file.write(("\noff-hand-blacklist="+gson.toJson(offBlacklist)).getBytes()); + .append("\n# ").append(lang("hand.requirements.description_4")))); + file.write(("\nhand.main.requirement=" + mainReq)); + file.write(("\nhand.main.block=" + mainBlock)); + file.write(("\nhand.main.food=" + mainFood)); + file.write(("\nhand.main.usable=" + mainUsable)); + file.write(("\nhand.main.whitelist="+gson.toJson(mainWhitelist))); + file.write(("\nhand.main.blacklist="+gson.toJson(mainBlacklist))); + file.write(("\nhand.off.requirement=" + offReq)); + file.write(("\nhand.off.block=" + offBlock)); + file.write(("\nhand.off.food=" + offFood)); + file.write(("\nhand.off.usable=" + offUsable)); + file.write(("\nhand.off.whitelist="+gson.toJson(offWhitelist))); + file.write(("\nhand.off.blacklist="+gson.toJson(offBlacklist))); + // send packets to update the settings on the server + if (SitClient.inGame) SitClient.sendPackets(); } catch (Exception e) { e.printStackTrace(); } } public static class defaults { - public static String version = "v1.0"; + public static double version = 1.1; public static String lang = "en_us"; public static boolean keepActive = true; public static boolean sitWhileSeated = true; @@ -180,13 +196,13 @@ public class config { public static boolean fullBlocksOn = false; public static boolean customOn = false; public static List customBlocks = List.of("minecraft:campfire|.46|1|lit=false","minecraft:soul_campfire|.46|1|lit=false"); - public static HandRequirements mainReq = HandRequirements.empty; + public static HandRequirement mainReq = HandRequirement.empty; public static boolean mainBlock = false; public static boolean mainFood = false; public static boolean mainUsable = false; public static List mainWhitelist = new ArrayList<>(); public static List mainBlacklist = new ArrayList<>(); - public static HandRequirements offReq = HandRequirements.restrictive; + public static HandRequirement offReq = HandRequirement.restrictive; public static boolean offBlock = true; public static boolean offFood = false; public static boolean offUsable = true; diff --git a/src/main/resources/assets/sit/lang/en_us.json b/src/main/resources/assets/sit/lang/en_us.json index cf40690..471d3be 100644 --- a/src/main/resources/assets/sit/lang/en_us.json +++ b/src/main/resources/assets/sit/lang/en_us.json @@ -33,6 +33,7 @@ "config.sit.general.sittable_blocks.description_6": "Third entry: hitbox size (where the player spawns above the entity when dismounting)", "config.sit.general.sittable_blocks.description_7": "Fourth entry (optional): required blockstate to sit (Put a \"!\" to exclude blockstates)", "config.sit.general.sittable_blocks.description_8": "Separate different entries with \"|\"!", + "config.sit.hand": "Hand Settings", "config.sit.hand.requirements": "Requirements", "config.sit.hand.requirements.description": "Hand requirements for sitting.", "config.sit.hand.requirements.description_2": "Empty = hand has to be empty", diff --git a/src/main/resources/assets/sit/lang/ru_ru.json b/src/main/resources/assets/sit/lang/ru_ru.json new file mode 100644 index 0000000..d458dc1 --- /dev/null +++ b/src/main/resources/assets/sit/lang/ru_ru.json @@ -0,0 +1,55 @@ +{ + "config.sit.empty": "Пусто", + "config.sit.restrictive": "Ограничение", + "config.sit.none": "Нет", + "config.sit.category.general": "Общий", + "config.sit.category.general.tooltip": "Основные настройки", + "config.sit.category.main_hand": "Главная рука", + "config.sit.category.main_hand.tooltip": "Настройки главной руки", + "config.sit.category.off_hand": "Левая рука", + "config.sit.category.off_hand.tooltip": "Настройки левой руки", + "config.sit.general.keep_active": "Оставлять активным", + "config.sit.general.keep_active.description": "Оставлять сущности активными даже при выходе из системы / выключении", + "config.sit.general.sittable": "Блоки, для сидения", + "config.sit.general.sittable.description": "Вкл/выкл возможность сидеть на разных типах блоков.", + "config.sit.general.sit_while_seated": "Пересаживание на другой блок", + "config.sit.general.sit_while_seated.description": "Вкл/выкл возможность пересесть на другой блок, когда уже сидишь.", + "config.sit.general.sittable.stairs": "Ступени", + "config.sit.general.sittable.slabs": "Полублоки", + "config.sit.general.sittable.carpets": "Ковры", + "config.sit.general.sittable.full_blocks": "Полные блоки", + "config.sit.general.sittable.custom": "Другие блоки", + "config.sit.general.sittable.custom.description": "Позволяет добавить собственные блоки, на которые можно сесть.", + "config.sit.general.sittable_blocks": "Собственные блоки для сидения", + "config.sit.general.sittable_blocks.description": "Добавь свой блок для сидения!", + "config.sit.general.sittable_blocks.description_2": "Пример: ", + "config.sit.general.sittable_blocks.description_3": "\"%s|%s|%s|%s\"", + "config.sit.general.sittable_blocks.description_3_2": "minecraft:campfire", + "config.sit.general.sittable_blocks.description_3_3": ".255", + "config.sit.general.sittable_blocks.description_3_4": "1", + "config.sit.general.sittable_blocks.description_3_5": "lit=false", + "config.sit.general.sittable_blocks.description_4": "Первый вход: собственный блок", + "config.sit.general.sittable_blocks.description_5": "Второй вход: высота сидения (число от 0 до 1, например 0.52)", + "config.sit.general.sittable_blocks.description_6": "Третий вход: размер хитбокса (место, где игрок появляется над объектом при спешивании)", + "config.sit.general.sittable_blocks.description_7": "Четвертый вход (опционально): требуемое состояние блока для сидения (Вставьте \"!\" для исключения состояния блоков)", + "config.sit.general.sittable_blocks.description_8": "Разделяйте разные входы знаком \"|\"!", + "config.sit.hand.requirements": "Требования", + "config.sit.hand.requirements.description": "Что нужно, чтобы сесть.", + "config.sit.hand.requirements.description_2": "Пусто = рука должна быть пуста", + "config.sit.hand.requirements.description_3": "Ограниченный = установить ограничения для состояния руки", + "config.sit.hand.requirements.description_4": "Ничего = можно сеть когда угодно", + "config.sit.hand.restrictions": "Ограничения", + "config.sit.hand.restrictions.description": "Вкл/выкл пресет ограничений руки, для возможности сидеть.", + "config.sit.hand.restrictions.blocks": "Блоки", + "config.sit.hand.restrictions.food": "Еда", + "config.sit.hand.restrictions.usable": "Доступный", + "config.sit.hand.restrictions.usable.description": "например луки, трезубцы, щиты", + "config.sit.hand.whitelist": "Белый список", + "config.sit.hand.whitelist.description": "Создай свой белый список предметов, которые игрок может использовать, чтобы сесть.", + "config.sit.hand.blacklist": "Черный список", + "config.sit.hand.blacklist.description": "Создай свой черный список предметов, которые игрок не может использовать, чтобы сесть.", + "config.sit.hand.list.description": "Пример: ", + "config.sit.hand.list.description_2": "\"minecraft:torch\"", + "key.sit.command.reloaded": "Конфигурация перезагружена!", + "key.sit.command.purged": "Очищены все сущности-сидения!" +} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 8a9783f..18c0e3f 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "id": "sit", + "id": "oth3r-sit", "version": "${version}", "name": "Sit!", "description": "Adds sitting to minecraft! Endless customizability for hand restrictions and sittable blocks.", @@ -18,14 +18,14 @@ "main": [ "one.oth3r.sit.Sit" ], - "server": [ - "one.oth3r.sit.SitServer" + "client": [ + "one.oth3r.sit.SitClient" ] }, "depends": { "fabricloader": ">=0.14.21", "minecraft": [ - "1.20.3-beta.1" + ">=1.20.3-beta.1" ], "java": ">=17", "fabric-api": "*"