Merge branch 'master' into 1.20-1.20.1

# Conflicts:
#	gradle.properties
#	src/main/java/one/oth3r/sit/Events.java
This commit is contained in:
Oth3r 2023-11-26 09:25:44 -06:00
commit 6ea2982876
15 changed files with 404 additions and 190 deletions

View file

@ -1,9 +1,12 @@
# 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) [![Crowdin](https://badges.crowdin.net/oth3r-sit/localized.svg)](https://crowdin.com/project/oth3r-sit)
[![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!
@ -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
* Forge Port (probably NeoForge 1.21)
* Custom dismounting logic

View file

@ -9,7 +9,7 @@ yarn_mappings=1.20.1+build.10
loader_version=0.14.22
# Mod Properties
mod_version=1.0.3+1.20-1.20.1
mod_version=1.1.0+1.20-1.20.1
maven_group=one.oth3r
archives_base_name=sit!

View file

@ -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<UseAction> notUsable = new ArrayList<>(food);
notUsable.add(UseAction.NONE);
Item mainItem = player.getMainHandStack().getItem();
Item offItem = player.getOffHandStack().getItem();
HashMap<HandType, ItemStack> 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<String> list, Item item) {
String itemID = Registries.ITEM.getId(item).toString();
public static boolean checkList(List<String> 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<String,HashMap<String,Object>> getCustomBlocks() {
// get a hashmap of custom blocks
HashMap<String,HashMap<String,Object>> 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,26 +123,31 @@ 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);
if (block instanceof StairsBlock) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+.27, pos.getZ() + 0.5, 0, 0);
entity.setBoundingBox(Box.of(Vec3d.of(pos),1.5,2,1.5));
hitBoxY = 2;
}
if (block instanceof SlabBlock) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+.27, pos.getZ() + 0.5, 0, 0);
entity.setBoundingBox(Box.of(Vec3d.of(pos),1.5,1,1.5));
hitBoxY = 1;
}
if (block instanceof CarpetBlock) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()-.17, pos.getZ() + 0.5, 0, 0);
entity.setBoundingBox(Box.of(Vec3d.of(pos),1.5,0.125,1.5));
hitBoxY = 0.125;
}
if (world.getBlockState(pos).isFullCube(world,pos.add(0,1,0))) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+.78, pos.getZ() + 0.5, 0, 0);
entity.setBoundingBox(Box.of(Vec3d.of(pos),1.5,2,1.5));
hitBoxY = 2;
}
if (config.customOn && config.customBlocks.size() != 0) {
for (HashMap<String,Object> map:getCustomBlocks().values()) {
@ -140,19 +155,24 @@ public class Events {
if (map.get("block").equals(blockID)) {
double input = Math.max(Math.min(Double.parseDouble((String) map.get("height")),1),0);
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+input-.22, pos.getZ() + 0.5, 0, 0);
entity.setBoundingBox(Box.of(Vec3d.of(pos),1.5,Double.parseDouble((String) map.get("hitbox")),1.5));
hitBoxY = Double.parseDouble((String) map.get("hitbox"));
}
}
}
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) 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;
@ -164,6 +184,7 @@ public class Events {
entities.remove(player);
}
checkPlayers.remove(player);
Sit.playerSettings.remove(player);
});
ServerLifecycleEvents.SERVER_STARTED.register(s -> {
Sit.server = s;
@ -174,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;
@ -206,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);

View file

@ -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<Map<String, String>>(){}.getType();
Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
languageMap.putAll(new Gson().fromJson(reader, type));
} catch (Exception e) {
e.printStackTrace();
}

View file

@ -90,14 +90,14 @@ public class ModMenu implements ModMenuApi {
.category(ConfigCategory.createBuilder()
.name(lang("category.main_hand"))
.tooltip(lang("category.main_hand.tooltip"))
.option(Option.<config.HandRequirements>createBuilder()
.option(Option.<config.HandRequirement>createBuilder()
.name(lang("hand.requirements"))
.description(OptionDescription.of(lang("hand.requirements.description")
.append("\n\n").append(lang("hand.requirements.description_2").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
.append("\n").append(lang("hand.requirements.description_3").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
.append("\n").append(lang("hand.requirements.description_4").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.RED))))))
.binding(config.defaults.mainReq, () -> config.mainReq, n -> config.mainReq = n)
.controller(opt -> EnumControllerBuilder.create(opt).enumClass(config.HandRequirements.class)
.controller(opt -> EnumControllerBuilder.create(opt).enumClass(config.HandRequirement.class)
.formatValue(v -> Text.translatable("config.sit."+v.name().toLowerCase())))
.build())
.group(OptionGroup.createBuilder()
@ -142,14 +142,14 @@ public class ModMenu implements ModMenuApi {
.category(ConfigCategory.createBuilder()
.name(lang("category.off_hand"))
.tooltip(lang("category.off_hand.tooltip"))
.option(Option.<config.HandRequirements>createBuilder()
.option(Option.<config.HandRequirement>createBuilder()
.name(lang("hand.requirements"))
.description(OptionDescription.of(lang("hand.requirements.description")
.append("\n\n").append(lang("hand.requirements.description_2").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
.append("\n").append(lang("hand.requirements.description_3").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
.append("\n").append(lang("hand.requirements.description_4").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.RED))))))
.binding(config.defaults.offReq, () -> config.offReq, n -> config.offReq = n)
.controller(opt -> EnumControllerBuilder.create(opt).enumClass(config.HandRequirements.class)
.controller(opt -> EnumControllerBuilder.create(opt).enumClass(config.HandRequirement.class)
.formatValue(v -> Text.translatable("config.sit."+v.name().toLowerCase())))
.build())
.group(OptionGroup.createBuilder()

View file

@ -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 = StandardCharsets.UTF_8.decode(ByteBuffer.wrap(buf.array())).toString().trim();
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;
}
}

View file

@ -1,20 +1,30 @@
package one.oth3r.sit;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
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.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<ServerPlayerEntity, HashMap<String,String>> 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 +33,14 @@ 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) -> server.execute(() -> {
PacketBuilder packet = new PacketBuilder(buf);
Type hashMapToken = new TypeToken<HashMap<String, Object>>() {}.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);

View file

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

View file

@ -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);

View file

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

View file

@ -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<String,String> getHandSettings() {
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
HashMap<String,String> 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<String> getList(ServerPlayerEntity player, HandType type, String setting) {
Type listType = new TypeToken<ArrayList<String>>() {}.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));
}
}

View file

@ -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<String> 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<String> mainWhitelist = defaults.mainWhitelist;
public static List<String> 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<String> offWhitelist = defaults.offWhitelist;
public static List<String> 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<ArrayList<String>>() {}.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<ArrayList<String>>() {}.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<String> 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<String> mainWhitelist = new ArrayList<>();
public static List<String> 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;

View file

@ -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",

View file

@ -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": "Очищены все сущности-сидения!"
}

View file

@ -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,8 +18,8 @@
"main": [
"one.oth3r.sit.Sit"
],
"server": [
"one.oth3r.sit.SitServer"
"client": [
"one.oth3r.sit.SitClient"
],
"modmenu": [
"one.oth3r.sit.ModMenu"