Merge branch '1.20.6' into 1.20.4

# Conflicts:
#	gradle.properties
#	src/main/java/one/oth3r/sit/Sit.java
#	src/main/java/one/oth3r/sit/SitClient.java
This commit is contained in:
Oth3r 2024-11-29 12:27:01 -06:00
commit 0810fd64e8
55 changed files with 3101 additions and 1111 deletions

1
.gitignore vendored
View file

@ -38,3 +38,4 @@ hs_err_*.log
replay_*.log replay_*.log
*.hprof *.hprof
*.jfr *.jfr
/.env

View file

@ -1,13 +1,16 @@
plugins { plugins {
id 'fabric-loom' version '1.6-SNAPSHOT' id 'fabric-loom' version '1.8-SNAPSHOT'
id 'maven-publish' id 'maven-publish'
id 'com.modrinth.minotaur' version '2.+'
id 'net.darkhax.curseforgegradle' version '1.1.+'
id 'co.uzzu.dotenv.gradle' version '4.0.0'
} }
version = project.mod_version version = project.mod_version
group = project.maven_group group = project.maven_group
base { base {
archivesName = project.archives_base_name archivesName = project.file_name
} }
repositories { repositories {
@ -15,6 +18,10 @@ repositories {
maven { url "https://maven.isxander.dev/releases" } maven { url "https://maven.isxander.dev/releases" }
} }
loom {
accessWidenerPath = file("src/main/resources/sit!.accesswidener")
}
dependencies { dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
@ -23,16 +30,21 @@ dependencies {
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}" modImplementation "com.terraformersmc:modmenu:${project.modmenu_version}"
modImplementation ("dev.isxander:yet-another-config-lib:${project.yacl_version}")
} }
processResources { processResources {
inputs.property "version", project.version filteringCharset "UTF-8"
inputs.property "minecraft_version", project.minecraft_version
var replaceProperties = [
version : project.version,
minecraft_version : minecraft_version,
min_minecraft_version : min_minecraft_version,
loader_version : loader_version
]
inputs.properties replaceProperties
filesMatching("fabric.mod.json") { filesMatching("fabric.mod.json") {
expand "version": project.version, expand replaceProperties
"minecraft_version": project.minecraft_version
} }
} }
@ -72,3 +84,34 @@ publishing {
// retrieving dependencies. // retrieving dependencies.
} }
} }
import com.modrinth.minotaur.dependencies.ModDependency
modrinth {
token = env.fetchOrNull('MODRINTH')
projectId = 'EsYqsGV4'
versionNumber = project.mod_version
versionName = "v${project.mod_version} [Fabric]"
versionType = "release"
uploadFile = remapJar
gameVersions = [project.minecraft_version]
loaders = ['fabric', 'quilt']
dependencies = [
new ModDependency('P7dR8mSH', 'required'),
new ModDependency('mOgUt4GM', 'optional')
]
changelog = file('changelog.md').text
}
import net.darkhax.curseforgegradle.TaskPublishCurseForge
tasks.register('publishCurseForge', TaskPublishCurseForge) {
apiToken = env.fetchOrNull('CURSEFORGE')
def mainFile = upload(892424, remapJar)
mainFile.changelog = file('changelog.md')
mainFile.displayName = "v${project.mod_version} [Fabric]"
mainFile.addModLoader("fabric", 'quilt')
mainFile.releaseType = "release"
mainFile.addEnvironment("client", "server")
}

32
changelog.md Normal file
View file

@ -0,0 +1,32 @@
# v1.2.0 - The Rewrite!
Hey yall, 1.2.0 is here!
Not too much to show in the changelog, as most of the changes were under the hood.
There is still some work that can be done, but I am happy where the mod is at currently.
As always, leave suggestions / bugs in the discord or github, and help localize the mod on [Crowdin](https://crowdin.com/project/oth3r-sit)!
Thank you for playing, and have a good day! - Oth3r 🦦
### Config
brand new config system for a better editing experience
* switch to json for all mod configuration
* split the config into 2, `server-config` & `sitting-config`
* block & item tag selection support
* select multiple block ids / tags per sitting height
* `sit-while-seated` config entry defaulted to false
* added a sitting block blacklist
* removed the YACL ModMenu config, as it is incompatible with the new system
* added a new custom config UI (full config control still WIP)
### Dismounting
*fixed dismounting sit entities!*
* added a new custom dismounting system
* dismounts the player in the direction that they are looking in, instead of on top of the block every time
### QOL
*im sure ive missed some things*
* interaction blocks now cannot be sat with the hand
* mod id is now `sit-oth3r` from `oth3r-sit`
* added keybinds
* sit toggle command
* `/sit` priorities target block over the block below the player

View file

@ -9,9 +9,9 @@ yarn_mappings=1.20.4+build.3
loader_version=0.15.11 loader_version=0.15.11
# Mod Properties # Mod Properties
mod_version=1.1.10+1.20.4 mod_version=1.2.0+1.20.4
maven_group=one.oth3r maven_group=one.oth3r
archives_base_name=sit! file_name=sit!
# Dependencies # Dependencies
fabric_version=0.97.1+1.20.4 fabric_version=0.97.1+1.20.4

View file

@ -1,281 +0,0 @@
package one.oth3r.sit;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.block.*;
import net.minecraft.block.enums.BlockHalf;
import net.minecraft.block.enums.SlabType;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.UseAction;
import net.minecraft.util.hit.HitResult;
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.HandSettings.HandType;
import one.oth3r.sit.file.Config;
import java.util.*;
public class Events {
private static int tick;
public static HashMap<ServerPlayerEntity, Entity> entities = new HashMap<>();
public static HashMap<ServerPlayerEntity, Integer> checkPlayers = new HashMap<>();
public static boolean checkLogic(ServerPlayerEntity player) {
ArrayList<UseAction> food = new ArrayList<>();
food.add(UseAction.EAT);
food.add(UseAction.DRINK);
ArrayList<UseAction> notUsable = new ArrayList<>(food);
notUsable.add(UseAction.NONE);
HashMap<HandType, ItemStack> itemMap = new HashMap<>();
itemMap.put(Utl.HandSettings.HandType.main,player.getMainHandStack());
itemMap.put(Utl.HandSettings.HandType.off,player.getOffHandStack());
// if sneaking cant sit
if (player.isSneaking()) return false;
// for both hands
for (HandType type: Utl.HandSettings.HandType.values()) {
ItemStack targetStack = itemMap.get(type);
// if req is empty and the item isn't empty, false
if (Utl.HandSettings.getReq(player,type).equals(Config.HandRequirement.empty) && !targetStack.isEmpty()) return false;
// if req is restrictive
if (Utl.HandSettings.getReq(player,type).equals(Config.HandRequirement.restrictive)) {
// if item is in blacklist, false
if (checkList(Utl.HandSettings.getList(player,type,"blacklist"),targetStack)) return false;
// if item is NOT in whitelist
if (!checkList(Utl.HandSettings.getList(player,type,"whitelist"),targetStack)) {
// if block is restricted and items is block, false, ect
if (Utl.HandSettings.getBool(player,type,"block") && (targetStack.getItem() instanceof BlockItem)) return false;
if (Utl.HandSettings.getBool(player,type,"food") && food.contains(targetStack.getUseAction())) return false;
if (Utl.HandSettings.getBool(player,type,"usable") && !notUsable.contains(targetStack.getUseAction())) return false;
}
}
}
// else true
return true;
}
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) {
String[] split = s.split("\\|");
HashMap<String,Object> data = new HashMap<>();
data.put("block",split[0]);
data.put("height",split[1]);
data.put("hitbox",split[2]);
if (split.length==4) data.put("state",split[3]);
map.put(String.valueOf(i),data);
i++;
}
return map;
}
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();
// 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;
if (block instanceof CarpetBlock && Config.carpetsOn) return true;
if (blockState.isFullCube(world,pos.add(0,1,0)) && Config.fullBlocksOn) return true;
// custom checker
if (Config.customOn && Config.customBlocks.size() != 0) {
for (HashMap<String,Object> map:getCustomBlocks().values()) {
String blockID = Registries.BLOCK.getId(block).toString();
if (map.get("block").equals(blockID)) {
if (!map.containsKey("state")) return true;
String[] states = ((String) map.get("state")).split(",\\s*");
boolean matching = true;
for (String state:states) {
if (state.charAt(0) == '!') {
if (blockState.toString().contains(state.substring(1))) matching = false;
} else if (!blockState.toString().contains(state)) matching = false;
}
return matching;
}
}
}
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);
hitBoxY = 2;
}
if (block instanceof SlabBlock) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+.27, pos.getZ() + 0.5, 0, 0);
hitBoxY = 1;
}
if (block instanceof CarpetBlock) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()-.17, pos.getZ() + 0.5, 0, 0);
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);
hitBoxY = 2;
}
if (Config.customOn && !Config.customBlocks.isEmpty()) {
for (HashMap<String,Object> map:getCustomBlocks().values()) {
String blockID = Registries.BLOCK.getId(block).toString();
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);
hitBoxY = Double.parseDouble((String) map.get("hitbox"));
}
}
}
//1.20.2 mounting pos change (shifts everything down by .25)
double oneTwentyTwo = .25;
entity.updatePositionAndAngles(entity.getX(),entity.getY()+oneTwentyTwo,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+oneTwentyTwo) entity.setPitch(90); // below
else entity.setPitch(-90); // above
}
public static boolean sit(ServerPlayerEntity player, BlockPos pos) {
// todo interactions entity to make the sitting hitbox?
World world = player.getWorld();
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 false;
entities.get(player).setRemoved(Entity.RemovalReason.DISCARDED);
entities.remove(player);
}
player.getServerWorld().spawnEntity(entity);
player.startRiding(entity);
entities.put(player,entity);
return true;
}
return false;
}
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.HandSettings.getHandSettings());
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
ServerPlayerEntity player = handler.player;
if (entities.containsKey(player)) {
if (!Config.keepActive) {
player.dismountVehicle();
entities.get(player).setRemoved(Entity.RemovalReason.DISCARDED);
}
entities.remove(player);
}
checkPlayers.remove(player);
Sit.playerSettings.remove(player);
});
ServerLifecycleEvents.SERVER_STARTED.register(s -> {
Sit.server = s;
Sit.commandManager = s.getCommandManager();
UseBlockCallback.EVENT.register((pl, world, hand, hitResult) -> {
if (!Config.handSitting) return ActionResult.PASS;
// make sure its a server player
if (pl.getClass() != ServerPlayerEntity.class) return ActionResult.PASS;
ServerPlayerEntity player = Sit.server.getPlayerManager().getPlayer(pl.getUuid());
if (player == null) return ActionResult.PASS;
if (hand == net.minecraft.util.Hand.MAIN_HAND && hitResult.getType() == HitResult.Type.BLOCK) {
BlockPos pos = hitResult.getBlockPos();
// check the players hands
if (!checkLogic(player)) return ActionResult.PASS;
// make the player sit
boolean status = sit(player,pos);
// if sat, cancel / FAIL the use block event
if (status) return ActionResult.FAIL;
}
return ActionResult.PASS;
});
});
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SitCommand.register(dispatcher));
}
public static void cleanUp() {
tick++;
if (tick >= 5) {
tick = 0;
Iterator<Map.Entry<ServerPlayerEntity, Entity>> entityLoop = entities.entrySet().iterator();
while (entityLoop.hasNext()) {
Map.Entry<ServerPlayerEntity, Entity> entry = entityLoop.next();
ServerPlayerEntity player = entry.getKey();
Entity entity = entry.getValue();
if (player.getVehicle() == null || !player.getVehicle().equals(entity)) {
entity.setRemoved(Entity.RemovalReason.DISCARDED);
entityLoop.remove();
} else {
// 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);
entityLoop.remove();
}
}
}
Iterator<Map.Entry<ServerPlayerEntity, Integer>> playerCheckLoop = checkPlayers.entrySet().iterator();
while (playerCheckLoop.hasNext()) {
Map.Entry<ServerPlayerEntity, Integer> entry = playerCheckLoop.next();
ServerPlayerEntity player = entry.getKey();
int i = entry.getValue();
checkPlayers.put(player,i-1);
if (i<0) {
playerCheckLoop.remove();
continue;
}
if (player.getVehicle() != null) {
Entity entity = player.getVehicle();
if (entity.getName().getString().equals(Sit.ENTITY_NAME)) {
setEntity(player.getBlockPos().add(0,1,0),player.getServerWorld(),entity);
entities.put(player,entity);
playerCheckLoop.remove();
}
}
}
}
}
}

View file

@ -1,229 +0,0 @@
package one.oth3r.sit;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
import dev.isxander.yacl3.api.*;
import dev.isxander.yacl3.api.controller.BooleanControllerBuilder;
import dev.isxander.yacl3.api.controller.EnumControllerBuilder;
import dev.isxander.yacl3.api.controller.StringControllerBuilder;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.text.TextColor;
import net.minecraft.util.Formatting;
import one.oth3r.sit.file.Config;
public class ModMenu implements ModMenuApi {
private static MutableText lang(String key) {
return Text.translatable("config.sit."+key);
}
private static MutableText lang(String key, Object... args) {
return Text.translatable("config.sit."+key,args);
}
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
// return null if YACL isn't installed to not throw an error
if (!yaclCheck()) return screen -> null;
return parent -> YetAnotherConfigLib.createBuilder().save(() -> {
// save and load to get rid of bad data
Config.save();
Config.load();
})
.title(Text.of("Sit!"))
.category(ConfigCategory.createBuilder()
.name(lang("category.general"))
.tooltip(lang("category.general.tooltip"))
.option(Option.<Boolean>createBuilder()
.name(lang("general.keep_active"))
.description(OptionDescription.of(lang("general.keep_active.description")))
.binding(Config.defaults.keepActive, () -> Config.keepActive, n -> Config.keepActive = n)
.controller(opt -> BooleanControllerBuilder.create(opt).trueFalseFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(lang("general.sit_while_seated"))
.description(OptionDescription.of(lang("general.sit_while_seated.description")))
.binding(Config.defaults.sitWhileSeated, () -> Config.sitWhileSeated, n -> Config.sitWhileSeated = n)
.controller(opt -> BooleanControllerBuilder.create(opt).trueFalseFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(Text.of("Sitting with Hand"))
.description(OptionDescription.of(Text.of("Toggles the player's ability to sit with their hand.")))
.binding(Config.defaults.handSitting,()-> Config.handSitting, n -> Config.handSitting = n)
.controller(opt -> BooleanControllerBuilder.create(opt).trueFalseFormatter())
.build())
.group(OptionGroup.createBuilder()
.name(lang("general.sittable"))
.description(OptionDescription.of(lang("general.sittable.description")))
.option(Option.<Boolean>createBuilder()
.name(lang("general.sittable.stairs"))
.binding(Config.defaults.stairsOn, () -> Config.stairsOn, n -> Config.stairsOn = n)
.controller(opt -> BooleanControllerBuilder.create(opt).onOffFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(lang("general.sittable.slabs"))
.binding(Config.defaults.slabsOn, () -> Config.slabsOn, n -> Config.slabsOn = n)
.controller(opt -> BooleanControllerBuilder.create(opt).onOffFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(lang("general.sittable.carpets"))
.binding(Config.defaults.carpetsOn, () -> Config.carpetsOn, n -> Config.carpetsOn = n)
.controller(opt -> BooleanControllerBuilder.create(opt).onOffFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(lang("general.sittable.full_blocks"))
.binding(Config.defaults.fullBlocksOn, () -> Config.fullBlocksOn, n -> Config.fullBlocksOn = n)
.controller(opt -> BooleanControllerBuilder.create(opt).onOffFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(lang("general.sittable.custom"))
.description(OptionDescription.of(lang("general.sittable.custom.description")))
.binding(Config.defaults.customOn, () -> Config.customOn, n -> Config.customOn = n)
.controller(opt -> BooleanControllerBuilder.create(opt).onOffFormatter())
.build())
.build())
.group(ListOption.<String>createBuilder()
.name(lang("general.sittable_blocks"))
.description(OptionDescription.of(
lang("general.sittable_blocks.description")
.append("\n\n").append(lang("general.sittable_blocks.description_2",
Text.literal("\"")
.append(Text.literal("minecraft:campfire").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
.append("|")
.append(Text.literal("0.255").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.RED))))
.append("|")
.append(Text.literal("1").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
.append("|")
.append(Text.literal("lit=false").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GOLD))))
.append("\"").styled(style -> style.withItalic(true).withColor(TextColor.fromFormatting(Formatting.GRAY)))))
.append("\n\n").append(lang("general.sittable_blocks.description_4").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
.append("\n").append(lang("general.sittable_blocks.description_5").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.RED))))
.append("\n").append(lang("general.sittable_blocks.description_6").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
.append("\n").append(lang("general.sittable_blocks.description_7").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GOLD))))
.append("\n\n").append(lang("general.sittable_blocks.description_8").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.YELLOW))))))
.binding(Config.defaults.customBlocks, () -> Config.customBlocks, n -> Config.customBlocks = n)
.controller(StringControllerBuilder::create)
.initial("")
.build())
.build())
.category(ConfigCategory.createBuilder()
.name(lang("category.main_hand"))
.tooltip(lang("category.main_hand.tooltip"))
.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.HandRequirement.class)
.formatValue(v -> Text.translatable("config.sit."+v.name().toLowerCase())))
.build())
.group(OptionGroup.createBuilder()
.name(lang("hand.restrictions"))
.description(OptionDescription.of(lang("hand.restrictions.description")))
.option(Option.<Boolean>createBuilder()
.name(lang("hand.restrictions.blocks"))
.binding(Config.defaults.mainBlock,()-> Config.mainBlock, n -> Config.mainBlock = n)
.controller(opt -> BooleanControllerBuilder.create(opt).trueFalseFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(lang("hand.restrictions.food"))
.binding(Config.defaults.mainFood,()-> Config.mainFood, n -> Config.mainFood = n)
.controller(opt -> BooleanControllerBuilder.create(opt).trueFalseFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(lang("hand.restrictions.usable"))
.description(OptionDescription.of(lang("hand.restrictions.usable.description")))
.binding(Config.defaults.mainUsable,()-> Config.mainUsable, n -> Config.mainUsable = n)
.controller(opt -> BooleanControllerBuilder.create(opt).trueFalseFormatter())
.build())
.build())
.group(ListOption.<String>createBuilder()
.name(lang("hand.whitelist"))
.description(OptionDescription.of(lang("hand.whitelist.description")
.append("\n\n").append(lang("hand.list.description"))
.append(lang("hand.list.description_2").styled(style -> style.withItalic(true).withColor(TextColor.fromFormatting(Formatting.GRAY))))))
.binding(Config.defaults.mainWhitelist, () -> Config.mainWhitelist, n -> Config.mainWhitelist = n)
.controller(StringControllerBuilder::create)
.initial("")
.build())
.group(ListOption.<String>createBuilder()
.name(lang("hand.blacklist"))
.description(OptionDescription.of(lang("hand.blacklist.description")
.append("\n\n").append(lang("hand.list.description"))
.append(lang("hand.list.description_2").styled(style -> style.withItalic(true).withColor(TextColor.fromFormatting(Formatting.GRAY))))))
.binding(Config.defaults.mainBlacklist, () -> Config.mainBlacklist, n -> Config.mainBlacklist = n)
.controller(StringControllerBuilder::create)
.initial("")
.build())
.build())
.category(ConfigCategory.createBuilder()
.name(lang("category.off_hand"))
.tooltip(lang("category.off_hand.tooltip"))
.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.HandRequirement.class)
.formatValue(v -> Text.translatable("config.sit."+v.name().toLowerCase())))
.build())
.group(OptionGroup.createBuilder()
.name(lang("hand.restrictions"))
.description(OptionDescription.of(lang("hand.restrictions.description")))
.option(Option.<Boolean>createBuilder()
.name(lang("hand.restrictions.blocks"))
.binding(Config.defaults.offBlock,()-> Config.offBlock, n -> Config.offBlock = n)
.controller(opt -> BooleanControllerBuilder.create(opt).trueFalseFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(lang("hand.restrictions.food"))
.binding(Config.defaults.offFood,()-> Config.offFood, n -> Config.offFood = n)
.controller(opt -> BooleanControllerBuilder.create(opt).trueFalseFormatter())
.build())
.option(Option.<Boolean>createBuilder()
.name(lang("hand.restrictions.usable"))
.description(OptionDescription.of(lang("hand.restrictions.usable.description")))
.binding(Config.defaults.offUsable,()-> Config.offUsable, n -> Config.offUsable = n)
.controller(opt -> BooleanControllerBuilder.create(opt).trueFalseFormatter())
.build())
.build())
.group(ListOption.<String>createBuilder()
.name(lang("hand.whitelist"))
.description(OptionDescription.of(lang("hand.whitelist.description")
.append("\n\n").append(lang("hand.list.description"))
.append(lang("hand.list.description_2").styled(style -> style.withItalic(true).withColor(TextColor.fromFormatting(Formatting.GRAY))))))
.binding(Config.defaults.offWhitelist, () -> Config.offWhitelist, n -> Config.offWhitelist = n)
.controller(StringControllerBuilder::create)
.initial("")
.build())
.group(ListOption.<String>createBuilder()
.name(lang("hand.blacklist"))
.description(OptionDescription.of(lang("hand.blacklist.description")
.append("\n\n").append(lang("hand.list.description"))
.append(lang("hand.list.description_2").styled(style -> style.withItalic(true).withColor(TextColor.fromFormatting(Formatting.GRAY))))))
.binding(Config.defaults.offBlacklist, () -> Config.offBlacklist, n -> Config.offBlacklist = n)
.controller(StringControllerBuilder::create)
.initial("")
.build())
.build())
.build().generateScreen(parent);
}
/**
* check if YACL is installed by getting a class and seeing if it throws
* @return if YACL is installed
*/
public static boolean yaclCheck() {
try {
Class.forName("dev.isxander.yacl3.platform.Env");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}

View file

@ -1,54 +1,15 @@
package one.oth3r.sit; 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.api.ModInitializer;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import one.oth3r.sit.file.FileData;
import net.minecraft.server.MinecraftServer; import one.oth3r.sit.utl.Events;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import one.oth3r.sit.file.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Type;
import java.util.HashMap;
public class Sit implements ModInitializer { 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 = false;
@Override @Override
public void onInitialize() { public void onInitialize() {
//todo future: FileData.loadFiles();
// make it so it updates the sitting height and pos based on the block so if it changed while offline it still works (or if stair changes shape) Events.registerCommon();
// inner stair offset & custom support for that ig
Config.load();
Events.register();
//PACKETS
ServerPlayNetworking.registerGlobalReceiver(PacketBuilder.getIdentifier(),
(server, player, handler, buf, responseSender) -> {
// copy to not throw errors
PacketBuilder packet = new PacketBuilder(buf.copy());
server.execute(() -> {
LOGGER.info(String.format("Received custom sitting settings from %s.",player.getName().getString()));
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);
else return LangReader.of(key, args).getTxT();
} }
} }

View file

@ -1,31 +1,16 @@
package one.oth3r.sit; package one.oth3r.sit;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Events;
public class SitClient implements ClientModInitializer { public class SitClient implements ClientModInitializer {
public static boolean inGame = false;
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
Sit.isClient = true; Data.setClient(true);
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { Events.registerClient();
inGame = true;
// send a data packet whenever joining a server
sendSettingsPackets();
});
// reset inGame
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> inGame = false);
} }
/**
* sends the settings packets to the server is the client is in game
*/
public static void sendSettingsPackets() {
if (inGame) {
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
new PacketBuilder(gson.toJson(Utl.HandSettings.getHandSettings())).send();
}
}
} }

View file

@ -1,70 +0,0 @@
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 one.oth3r.sit.file.Config;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Utl {
public static class HandSettings {
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));
}
public enum HandType {
main,
off
}
}
public static class Num {
public static boolean isInt(String string) {
try {
Integer.parseInt(string);
} catch (NumberFormatException nfe) {
return false;
}
return true;
}
public static boolean isFloat(String string) {
try {
Float.parseFloat(string);
} catch (NumberFormatException nfe) {
return false;
}
return true;
}
}
}

View file

@ -1,19 +1,18 @@
package one.oth3r.sit; package one.oth3r.sit.command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.ParseResults;
import com.mojang.brigadier.arguments.StringArgumentType; import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder; import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.TextColor;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import one.oth3r.sit.file.Config; import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Logic;
import one.oth3r.sit.utl.Utl;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -27,57 +26,59 @@ public class SitCommand {
.suggests(SitCommand::getSuggestions) .suggests(SitCommand::getSuggestions)
.executes((context2) -> command(context2.getSource(), context2.getInput())))); .executes((context2) -> command(context2.getSource(), context2.getInput()))));
} }
public static CompletableFuture<Suggestions> getSuggestions(CommandContext<ServerCommandSource> context, SuggestionsBuilder builder) { public static CompletableFuture<Suggestions> getSuggestions(CommandContext<ServerCommandSource> context, SuggestionsBuilder builder) {
builder.suggest("reload"); builder.suggest("reload");
builder.suggest("purgeChairEntities"); builder.suggest("purgeChairEntities");
return builder.buildFuture(); return builder.buildFuture();
} }
private static int command(ServerCommandSource source, String arg) { private static int command(ServerCommandSource source, String arg) {
ServerPlayerEntity player = source.getPlayer(); ServerPlayerEntity player = source.getPlayer();
//trim all the arguments before the command // trim all the arguments before the command (for commands like /execute)
String keyword = "sit"; int index = arg.indexOf("sit");
int index = Integer.MAX_VALUE;
if (arg.contains(keyword)) index = arg.indexOf(keyword);
// trims the words before the text // trims the words before the text
if (index != Integer.MAX_VALUE) arg = arg.substring(index).trim(); if (index != -1) arg = arg.substring(index).trim();
String[] args = arg.split(" "); String[] args = arg.split(" ");
// if command string starts with sit, remove it
if (args[0].equalsIgnoreCase("sit")) if (args[0].equalsIgnoreCase("sit"))
args = arg.replaceFirst("sit ", "").split(" "); args = arg.replaceFirst("sit ", "").split(" ");
// if console // if console
if (player == null) { if (player == null) {
if (args[0].equalsIgnoreCase("reload")) { if (args[0].equalsIgnoreCase("reload")) {
Config.load(); Logic.reload();
Sit.LOGGER.info(Sit.lang("key.sit.command.reloaded").getString()); Data.LOGGER.info(Utl.lang("sit!.chat.reloaded").getString());
} }
return 1; return 1;
} }
// player
if (args[0].equalsIgnoreCase("sit")) { if (args[0].equalsIgnoreCase("sit")) {
// if the player can't sit where they're looking, try to sit below
if (!Logic.sitLooking(player)) {
BlockPos pos = player.getBlockPos(); BlockPos pos = player.getBlockPos();
// get the block under the player if player on top of a solid
if (!(player.getY() - ((int) player.getY()) > 0.00)) { if (!(player.getY() - ((int) player.getY()) > 0.00)) {
pos = pos.add(0, -1, 0); pos = pos.add(0, -1, 0);
} }
// if already sitting, ignore // if already sitting, ignore
if (Events.entities.containsKey(player)) return 1; if (Data.getSitEntity(player) != null) return 1;
// sit
Events.sit(player,pos); // try to make the player sit
Logic.sit(player, pos, null);
} }
}
if (args[0].equalsIgnoreCase("reload")) { if (args[0].equalsIgnoreCase("reload")) {
Config.load(); Logic.reload();
player.sendMessage(Sit.lang("key.sit.command.reloaded").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN)))); player.sendMessage(Utl.messageTag().append(Utl.lang("sit!.chat.reloaded").formatted(Formatting.GREEN)));
}
if (args[0].equalsIgnoreCase("purgeChairEntities")) {
String cmd = "kill @e[type=minecraft:text_display,name=\""+Sit.ENTITY_NAME+"\"]";
try {
ParseResults<ServerCommandSource> parse =
Sit.commandManager.getDispatcher().parse(cmd, player.getCommandSource());
Sit.commandManager.getDispatcher().execute(parse);
player.sendMessage(Sit.lang("key.sit.command.purged"));
} catch (CommandSyntaxException e) {
player.sendMessage(Sit.lang("key.sit.command.purged"));
e.printStackTrace();
}
} }
if (args[0].equalsIgnoreCase("purgeChairEntities")) Utl.Entity.purge(player,true);
return 1; return 1;
} }
} }

View file

@ -1,252 +0,0 @@
package one.oth3r.sit.file;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import net.fabricmc.loader.api.FabricLoader;
import one.oth3r.sit.LangReader;
import one.oth3r.sit.Sit;
import one.oth3r.sit.SitClient;
import one.oth3r.sit.Utl.*;
import java.io.File;
import java.io.FileInputStream;
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;
public class Config {
public static String lang = defaults.lang;
public static boolean keepActive = defaults.keepActive;
public static boolean sitWhileSeated = defaults.sitWhileSeated;
public static boolean stairsOn = defaults.stairsOn;
public static boolean slabsOn = defaults.slabsOn;
public static boolean carpetsOn = defaults.carpetsOn;
public static boolean fullBlocksOn = defaults.fullBlocksOn;
public static boolean customOn = defaults.customOn;
public static List<String> customBlocks = defaults.customBlocks;
public enum HandRequirement {
empty,
restrictive,
none;
public static HandRequirement get(String s) {
try {
return HandRequirement.valueOf(s);
} catch (IllegalArgumentException e) {
return empty;
}
}
}
public static boolean handSitting = defaults.handSitting;
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 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 File configFile() {
return new File(FabricLoader.getInstance().getConfigDir().toFile()+"/Sit!.properties");
}
public static void load() {
if (!configFile().exists() || !configFile().canRead()) {
save();
load();
return;
}
try (FileInputStream fileStream = new FileInputStream(configFile())) {
Properties properties = new Properties();
properties.load(fileStream);
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 e) {
//read fail
e.printStackTrace();
save();
}
}
public static ArrayList<String> validateCustomBlocks(ArrayList<String> fix) {
ArrayList<String> out = new ArrayList<>();
for (String entry : fix) {
String[] split = entry.split("\\|");
// skip if not the right size
if (split.length < 3 || split.length > 4) continue;
// keep going if that block exists
// if (Registries.BLOCK.stream().anyMatch(match -> Registries.BLOCK.getId(match).toString().equals(split[0]))) {}
// if the other entries aren't correct, skip
if (!Num.isFloat(split[1]) || !Num.isInt(split[2])) continue;
// add if everything is a okay
out.add(entry);
}
return out;
}
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)));
try {
customBlocks = validateCustomBlocks(new Gson().fromJson((String)
properties.computeIfAbsent("custom-blocks", a -> gson.toJson(defaults.customBlocks)), listType));
} catch (JsonSyntaxException ignore) {}
handSitting = Boolean.parseBoolean((String) properties.computeIfAbsent("hand.sitting", a -> String.valueOf(defaults.handSitting)));
mainReq = HandRequirement.get((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)));
try {
mainWhitelist = new Gson().fromJson((String)
properties.computeIfAbsent("hand.main.whitelist", a -> gson.toJson(defaults.mainWhitelist)), listType);
} catch (JsonSyntaxException ignore) {}
try {
mainBlacklist = new Gson().fromJson((String)
properties.computeIfAbsent("hand.main.blacklist", a -> gson.toJson(defaults.mainBlacklist)), listType);
} catch (JsonSyntaxException ignore) {}
offReq = HandRequirement.get((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.get((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)));
try {
mainWhitelist = new Gson().fromJson((String)
properties.computeIfAbsent("main-hand-whitelist", a -> gson.toJson(defaults.mainWhitelist)), listType);
} catch (JsonSyntaxException ignore) {}
try {
mainBlacklist = new Gson().fromJson((String)
properties.computeIfAbsent("main-hand-blacklist", a -> gson.toJson(defaults.mainBlacklist)), listType);
} catch (JsonSyntaxException ignore) {}
offReq = HandRequirement.get((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)));
try {
offWhitelist = new Gson().fromJson((String)
properties.computeIfAbsent("off-hand-whitelist", a -> gson.toJson(defaults.offWhitelist)), listType);
} catch (JsonSyntaxException ignore) {}
try {
offBlacklist = new Gson().fromJson((String)
properties.computeIfAbsent("off-hand-blacklist", a -> gson.toJson(defaults.offBlacklist)), listType);
} catch (JsonSyntaxException ignore) {}
}
} catch (Exception e) {
Sit.LOGGER.info("ERROR LOADING CONFIG - PLEASE REPORT WITH THE ERROR LOG");
e.printStackTrace();
}
}
public static String lang(String key, Object... args) {
return LangReader.of("config.sit."+key, args).getTxT().getString();
}
public static void save() {
try (var file = Files.newBufferedWriter(configFile().toPath(), StandardCharsets.UTF_8)) {
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
file.write("# Sit! Config\n");
file.write("\nversion="+defaults.version);
file.write("\n# all available languages: en_us, ru_ru, es_es, zh_tw");
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",
"\"minecraft:campfire|0.255|1|lit=false\""))
.append("\n# ").append(lang("general.sittable_blocks.description_4"))
.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());
file.write("\ncustom-blocks="+gson.toJson(customBlocks));
file.write("\n\n# "+lang("hand"));
file.write("\nhand.sitting=" + handSitting);
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());
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
SitClient.sendSettingsPackets();
} catch (Exception e) {
e.printStackTrace();
}
}
public static class defaults {
public static double version = 1.1;
public static String lang = "en_us";
public static boolean keepActive = true;
public static boolean sitWhileSeated = true;
public static boolean stairsOn = true;
public static boolean slabsOn = true;
public static boolean carpetsOn = true;
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,waterlogged=false");
public static boolean handSitting = true;
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 HandRequirement offReq = HandRequirement.restrictive;
public static boolean offBlock = true;
public static boolean offFood = false;
public static boolean offUsable = true;
public static List<String> offWhitelist = List.of("minecraft:torch","minecraft:soul_torch","minecraft:redstone_torch");
public static List<String> offBlacklist = new ArrayList<>();
}
}

View file

@ -0,0 +1,100 @@
package one.oth3r.sit.file;
import com.google.gson.annotations.SerializedName;
import net.minecraft.block.BlockState;
import net.minecraft.registry.Registries;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.state.State;
import net.minecraft.util.Identifier;
import one.oth3r.sit.utl.Utl;
import java.util.ArrayList;
public class CustomBlock {
@SerializedName("block-ids")
private ArrayList<String> blockIds = new ArrayList<>();
@SerializedName("block-tags")
private ArrayList<String> blockTags = new ArrayList<>();
@SerializedName("blockstates")
private ArrayList<String> blockStates = new ArrayList<>();
public CustomBlock() {}
public CustomBlock(ArrayList<String> blockIds, ArrayList<String> blockTags, ArrayList<String> blockStates) {
this.blockIds = blockIds;
this.blockTags = blockTags;
this.blockStates = blockStates;
}
public ArrayList<String> getBlockIds() {
return blockIds;
}
public ArrayList<String> getBlockTags() {
return blockTags;
}
public ArrayList<String> getBlockStates() {
return blockStates;
}
/**
* checks if the blockstate matches the CustomBlock params
* @param blockState the blockState to check
* @return if the blockstate is allowed by the CustomBlock rules
*/
public boolean isValid(BlockState blockState) {
boolean blockType = checkBlockType(blockState);
if (!blockType) return false;
// now check if the state is one of the acceptable states
for (String state : blockStates) {
// if there is a NOT (!) blockstate
if (state.startsWith("!")) {
// if it is contained in the block, return false
// remove the '!'
String fixedState = state.substring(1);
if (blockState.getEntries().entrySet().stream().map(State.PROPERTY_MAP_PRINTER).anyMatch(s -> s.equals(fixedState))) return false;
}
// else check if the blockstate matches, if not return false
else if (blockState.getEntries().entrySet().stream().map(State.PROPERTY_MAP_PRINTER).noneMatch(s -> s.equals(state))) return false;
}
// if here, all passes have passed
return true;
}
/**
* returns if the block is the correct type or not
* @param blockState the blockstate to check
* @return if the type of block is matching the CustomBlock rules (e.g. if it is wood, ect.)
*/
private boolean checkBlockType(BlockState blockState) {
// for all the entered blocks
for (String id : blockIds) {
// if there is a match for the NOT(!) blocks, return false immediately
if (id.startsWith("!") && id.substring(1).equals(Utl.getBlockID(blockState))) return false;
// if there is a match for the block, return true immediately
if (id.equalsIgnoreCase(Utl.getBlockID(blockState))) return true;
}
// a boolean to check if one of the blocks are in a filtered tag
boolean tagCheck = false;
// for all the entered tags
for (String tag : blockTags) {
// substring to remove # and if needed, !
// if there is a math for the NOT(!) tag, return false
if (tag.startsWith("!") && blockState.isIn(TagKey.of(Registries.BLOCK.getKey(), new Identifier(tag.substring(2))))) return false;
// if there is a match, return true
if (blockState.isIn(TagKey.of(Registries.BLOCK.getKey(), Identifier.tryParse(tag.substring(1))))) tagCheck = true;
}
// not returning true in the loop because there might be a (!) not tag that the block might fall into, after the block was already in another tag
return tagCheck;
}
}

View file

@ -0,0 +1,90 @@
package one.oth3r.sit.file;
import net.fabricmc.loader.api.FabricLoader;
import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Utl;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
public interface CustomFile <T extends CustomFile<T>> {
void reset();
/**
* saves the current instance to file
*/
default void save() {
if (!getFile().exists()) {
Data.LOGGER.info(String.format("Creating new `%s`", getFile().getName()));
}
try (BufferedWriter writer = Files.newBufferedWriter(getFile().toPath(), StandardCharsets.UTF_8)) {
writer.write(Utl.getGson().toJson(this));
} catch (Exception e) {
Data.LOGGER.error(String.format("ERROR SAVING '%s`: %s", getFile().getName(), e.getMessage()));
}
}
/**
* loads the file to the current instance using updateFromReader()
*/
default void load() {
File file = getFile();
if (!file.exists()) fileNotExist();
// try reading the file
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
updateFromReader(reader);
} catch (Exception e) {
Data.LOGGER.error(String.format("ERROR LOADING '%s`: %s", file.getName(),e.getMessage()));
}
}
default void updateFromReader(BufferedReader reader) {
// try to read the json
T file;
try {
file = Utl.getGson().fromJson(reader, getFileClass());
} catch (Exception e) {
throw new NullPointerException();
}
// throw null if the fileData is null or version is null
if (file == null) throw new NullPointerException();
// update the instance
updateToNewFile(file);
}
@NotNull
Class<T> getFileClass();
void updateToNewFile(T newFile);
/**
* logic for the file not existing when loading, defaults to saving
*/
default void fileNotExist() {
// try to make the config directory
try {
Files.createDirectories(Paths.get(getDirectory()));
} catch (Exception e) {
Data.LOGGER.error("Failed to create config directory. Canceling all config loading...");
return;
}
save();
}
String getFileName();
default String getDirectory() {
return FabricLoader.getInstance().getConfigDir().toFile()+"/";
}
default File getFile() {
return new File(getDirectory()+getFileName());
}
}

View file

@ -0,0 +1,65 @@
package one.oth3r.sit.file;
import com.google.gson.annotations.SerializedName;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.util.Identifier;
import java.util.ArrayList;
public class CustomItem {
@SerializedName("item-ids")
private ArrayList<String> itemIDs = new ArrayList<>();
@SerializedName("item-tags")
private ArrayList<String> itemTags = new ArrayList<>();
public CustomItem() {}
public CustomItem(ArrayList<String> itemIDs, ArrayList<String> itemTags) {
this.itemIDs = itemIDs;
this.itemTags = itemTags;
}
public ArrayList<String> getItemIDs() {
return itemIDs;
}
public ArrayList<String> getItemTags() {
return itemTags;
}
/**
* returns if the block is the correct type or not
* @param itemStack the blockstate to check
* @return if the type of block is matching the CustomBlock rules (e.g. if it is wood, ect.)
*/
public boolean checkItem(ItemStack itemStack) {
String itemId = Registries.ITEM.getId(itemStack.getItem()).toString();
// check the custom item ids
for (String id : itemIDs) {
// if there is a match for the NOT(!) item, its filtered, false
if (id.startsWith("!") && id.substring(1).equalsIgnoreCase(itemId)) return false;
// if there is a match for the item, return true immediately
if (id.equalsIgnoreCase(itemId)) return true;
}
// a boolean to check if one of the items are in a filtered tag
boolean tagCheck = false;
// check the custom item tags
for (String tag : itemTags) {
// substring to remove # and if needed, "!"
// if a NOT tag
if (tag.startsWith("!")) {
// if there is a math for the NOT(!) tag, return false
if (itemStack.isIn(TagKey.of(Registries.ITEM.getKey(), new Identifier(tag.substring(2))))) return false;
}
// else (normal tag), if there is a match, set tagCheck to true
else if (itemStack.isIn(TagKey.of(Registries.ITEM.getKey(), new Identifier(tag.substring(1))))) tagCheck = true;
}
// not returning true in the loop because there might be a (!) not tag that the item might fall into, after the item was already in another tag
return tagCheck;
}
}

View file

@ -0,0 +1,96 @@
package one.oth3r.sit.file;
import net.minecraft.server.network.ServerPlayerEntity;
import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Utl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
public class FileData {
/**
* Sit! config file
*/
private static ServerConfig serverConfig = new ServerConfig();
public static ServerConfig getServerConfig() {
return serverConfig;
}
public static void setServerConfig(ServerConfig newServerConfig) {
serverConfig = new ServerConfig(newServerConfig);
}
/**
* The default sitting config for all new players
*/
private static SittingConfig sittingConfig = new SittingConfig();
public static SittingConfig getSittingConfig() {
return sittingConfig;
}
public static void setSittingConfig(SittingConfig newSittingConfig) {
sittingConfig = new SittingConfig(newSittingConfig);
}
/**
* the sitting config stored per player on the server
*/
private static final HashMap<ServerPlayerEntity, SittingConfig> playerSettings = new HashMap<>();
public static void clearPlayerSettings() {
playerSettings.clear();
}
public static void setPlayerSetting(ServerPlayerEntity player, SittingConfig config) {
playerSettings.put(player, config);
}
public static void removePlayerSetting(ServerPlayerEntity player) {
playerSettings.remove(player);
}
public static SittingConfig getPlayerSetting(ServerPlayerEntity player) {
return playerSettings.getOrDefault(player, sittingConfig);
}
/**
* loads all config files to memory
*/
public static void loadFiles() {
getServerConfig().load();
getSittingConfig().load();
// if loading file and is on supported server on client, send the new settings over
if (Data.isClient() && Data.isSupportedServer()) {
Utl.sendSettingsPackets();
}
}
public static class Defaults {
public static final ArrayList<SittingBlock> SITTING_BLOCKS = new ArrayList<>(Arrays.asList(
new SittingBlock(new ArrayList<>(),new ArrayList<>(Arrays.asList("#minecraft:campfires")), new ArrayList<>(Arrays.asList("lit=false")),.437),
new SittingBlock(new ArrayList<>(Arrays.asList("!minecraft:crimson_stem","!minecraft:warped_stem","minecraft:polished_basalt")), new ArrayList<>(Arrays.asList("#minecraft:logs","!#minecraft:oak_logs")), new ArrayList<>(Arrays.asList("!axis=y")),1.0),
new SittingBlock(new ArrayList<>(Arrays.asList()), new ArrayList<>(Arrays.asList("#minecraft:beds")), new ArrayList<>(Arrays.asList("part=foot","occupied=false")),.5625)
));
public static final ArrayList<CustomBlock> BLACKLISTED_BLOCKS = new ArrayList<>(Arrays.asList(
new CustomBlock(new ArrayList<>(),new ArrayList<>(Arrays.asList("#minecraft:shulker_boxes")),new ArrayList<>())
));
public static final HandSetting MAIN_HAND = new HandSetting(HandSetting.SittingRequirement.EMPTY, new HandSetting.Filter(
false,new HandSetting.Filter.Presets(),
new CustomItem(
new ArrayList<>(),
new ArrayList<>(Arrays.asList("#minecraft:bookshelf_books","!#minecraft:lectern_books")))));
public static final HandSetting OFF_HAND = new HandSetting(HandSetting.SittingRequirement.FILTER, new HandSetting.Filter(
false, new HandSetting.Filter.Presets(false, true, false),
new CustomItem(new ArrayList<>(Arrays.asList("minecraft:filled_map",
"minecraft:torch", "minecraft:soul_torch","minecraft:redstone_torch",
"minecraft:lantern", "minecraft:soul_lantern")),
new ArrayList<>())));
}
}

View file

@ -0,0 +1,96 @@
package one.oth3r.sit.file;
import com.google.gson.annotations.SerializedName;
import java.util.Arrays;
import java.util.stream.Collectors;
public class HandSetting {
@SerializedName("requirement")
private SittingRequirement sittingRequirement = SittingRequirement.NONE;
@SerializedName("requirement-options") @SuppressWarnings("unused")
private final String sittingRequirementOptions = Arrays.stream(SittingRequirement.values()).map(Enum::toString).collect(Collectors.joining(", "));
@SerializedName("filter")
private Filter filter = new Filter();
public HandSetting() {}
public HandSetting(SittingRequirement sittingRequirement, Filter filter) {
this.sittingRequirement = sittingRequirement;
this.filter = filter;
}
public SittingRequirement getSittingRequirement() {
return sittingRequirement;
}
public Filter getFilter() {
return filter;
}
public enum SittingRequirement {
NONE,
FILTER,
EMPTY
}
public static class Filter {
@SerializedName("invert-filter")
private Boolean invert = false;
@SerializedName("presets")
private Presets presets = new Presets();
@SerializedName("custom-items")
private CustomItem customItems = new CustomItem();
public Filter() {}
public Filter(boolean invert, Presets presets, CustomItem customItems) {
this.invert = invert;
this.presets = presets;
this.customItems = customItems;
}
public Boolean isInverted() {
return invert;
}
public Presets getPresets() {
return presets;
}
public CustomItem getCustomItems() {
return customItems;
}
public static class Presets {
@SerializedName("block")
private boolean block = false;
@SerializedName("food")
private boolean food = false;
@SerializedName("usable")
private boolean usable = false;
public Presets() {}
public Presets(boolean block, boolean food, boolean usable) {
this.block = block;
this.food = food;
this.usable = usable;
}
public boolean isBlock() {
return block;
}
public boolean isFood() {
return food;
}
public boolean isUsable() {
return usable;
}
}
}
}

View file

@ -1,10 +1,11 @@
package one.oth3r.sit; package one.oth3r.sit.file;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import net.minecraft.text.MutableText; import net.minecraft.text.MutableText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import one.oth3r.sit.file.Config; import one.oth3r.sit.Sit;
import one.oth3r.sit.utl.Data;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -75,20 +76,30 @@ public class LangReader {
public static LangReader of(String translationKey, Object... placeholders) { public static LangReader of(String translationKey, Object... placeholders) {
return new LangReader(translationKey, placeholders); return new LangReader(translationKey, placeholders);
} }
public static void loadLanguageFile() { public static void loadLanguageFile() {
try {
ClassLoader classLoader = Sit.class.getClassLoader(); ClassLoader classLoader = Sit.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("assets/sit/lang/"+ Config.lang+".json"); try {
InputStream inputStream = classLoader.getResourceAsStream("assets/sit-oth3r/lang/" + FileData.getServerConfig().getLang() +".json");
// if the input stream is null, the language file wasn't found
if (inputStream == null) { if (inputStream == null) {
inputStream = classLoader.getResourceAsStream("assets/sit/lang/"+ Config.defaults.lang+".json"); // try loading the default language file
Config.lang = Config.defaults.lang; inputStream = classLoader.getResourceAsStream("assets/sit-oth3r/lang/" + new ServerConfig().getLang() +".json");
Data.LOGGER.error("COULDN'T LOAD THE LANGUAGE FILE. RESETTING TO en_us.");
} }
if (inputStream == null) throw new IllegalArgumentException("CANT LOAD THE LANGUAGE FILE. DIRECTIONHUD WILL BREAK.");
// if the input stream is still null, throw an exception
if (inputStream == null) throw new IllegalArgumentException("UNABLE TO LOAD THE ENGLISH LANGUAGE FILE.");
Type type = new TypeToken<Map<String, String>>(){}.getType(); Type type = new TypeToken<Map<String, String>>(){}.getType();
Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); Reader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
languageMap.putAll(new Gson().fromJson(reader, type)); languageMap.putAll(new Gson().fromJson(reader, type));
// close the input stream
inputStream.close();
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); Data.LOGGER.error(e.getMessage());
} }
} }
public static String getLanguageValue(String key) { public static String getLanguageValue(String key) {

View file

@ -0,0 +1,373 @@
package one.oth3r.sit.file;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import net.minecraft.util.Hand;
import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Utl;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Properties;
public class ServerConfig implements CustomFile<ServerConfig> {
@SerializedName("version")
private Double version = 2.0;
@SerializedName("lang")
private String lang = "en_us";
@SerializedName("lang-options")
private final String langOptions = "en_us, it_it, pt_br, tr_tr, zh_tw";
@SerializedName("keep-active")
private Boolean keepActive = true;
@SerializedName("sit-while-seated")
private Boolean sitWhileSeated = false;
@SerializedName("preset-blocks")
private PresetBlocks presetBlocks = new PresetBlocks();
@SerializedName("custom-enabled")
private Boolean customEnabled = false;
@SerializedName("custom-blocks")
private ArrayList<SittingBlock> sittingBlocks = FileData.Defaults.SITTING_BLOCKS;
@SerializedName("blacklisted-blocks")
private ArrayList<CustomBlock> blacklistedBlocks = FileData.Defaults.BLACKLISTED_BLOCKS;
public ServerConfig() {}
public ServerConfig(ServerConfig serverConfig) {
this.version = serverConfig.version;
this.lang = serverConfig.lang;
this.keepActive = serverConfig.keepActive;
this.sitWhileSeated = serverConfig.sitWhileSeated;
this.presetBlocks = serverConfig.presetBlocks;
this.customEnabled = serverConfig.customEnabled;
this.sittingBlocks = serverConfig.sittingBlocks;
this.blacklistedBlocks = serverConfig.blacklistedBlocks;
}
public ServerConfig(Double version, String lang, boolean keepActive, boolean sitWhileSeated,
PresetBlocks presetBlocks, boolean customEnabled,
ArrayList<SittingBlock> sittingBlocks, ArrayList<CustomBlock> blacklistedBlocks) {
this.version = version;
this.lang = lang;
this.keepActive = keepActive;
this.sitWhileSeated = sitWhileSeated;
this.presetBlocks = presetBlocks;
this.customEnabled = customEnabled;
this.sittingBlocks = sittingBlocks;
this.blacklistedBlocks = blacklistedBlocks;
}
public Double getVersion() {
return version;
}
public String getLang() {
return lang;
}
public boolean isKeepActive() {
return keepActive;
}
public boolean canSitWhileSeated() {
return sitWhileSeated;
}
public PresetBlocks getPresetBlocks() {
return presetBlocks;
}
public Boolean isCustomEnabled() {
return customEnabled;
}
public ArrayList<SittingBlock> getSittingBlocks() {
return sittingBlocks;
}
public ArrayList<CustomBlock> getBlacklistedBlocks() {
return blacklistedBlocks;
}
public static class PresetBlocks {
@SerializedName("stairs")
private boolean stairs = true;
@SerializedName("slabs")
private boolean slabs = true;
@SerializedName("carpets")
private boolean carpets = true;
@SerializedName("full-blocks")
private boolean fullBlocks = false;
public PresetBlocks() {}
public PresetBlocks(boolean stairs, boolean slabs, boolean carpets, boolean fullBlocks) {
this.stairs = stairs;
this.slabs = slabs;
this.carpets = carpets;
this.fullBlocks = fullBlocks;
}
public boolean isStairs() {
return stairs;
}
public boolean isSlabs() {
return slabs;
}
public boolean isCarpets() {
return carpets;
}
public boolean isFullBlocks() {
return fullBlocks;
}
}
@Override
public void reset() {
updateToNewFile(new ServerConfig());
}
@Override
public @NotNull Class<ServerConfig> getFileClass() {
return ServerConfig.class;
}
@Override
public void updateToNewFile(ServerConfig newFile) {
this.version = newFile.version;
this.lang = newFile.lang;
this.keepActive = newFile.keepActive;
this.sitWhileSeated = newFile.sitWhileSeated;
this.presetBlocks = newFile.presetBlocks;
this.customEnabled = newFile.customEnabled;
this.sittingBlocks = newFile.sittingBlocks;
this.blacklistedBlocks = newFile.blacklistedBlocks;
}
@Override
public String getFileName() {
return "server-config.json";
}
@Override
public String getDirectory() {
return Data.CONFIG_DIR;
}
@Override
public void fileNotExist() {
CustomFile.super.fileNotExist();
// try checking the old/legacy config directory for the file
if (Legacy.getLegacyFile().exists()) {
Data.LOGGER.info("Updating Sit!.properties to sit!/config.json");
Legacy.run();
}
}
protected static class Legacy {
/**
* gets the legacy file, from the old directory for fabric, and the same one for spigot
*/
public static File getLegacyFile() {
// strip the new directory
return new File(Data.CONFIG_DIR.substring(0, Data.CONFIG_DIR.length()-5)+"Sit!.properties");
}
/**
* updates the old Sit!.properties to config.json
*/
public static void run() {
// shouldn't happen, only call if the file exists
File file = getLegacyFile();
if (!file.exists()) return;
// update to the new system
try (FileInputStream fileStream = new FileInputStream(file)) {
Properties properties = new Properties();
properties.load(fileStream);
String ver = (String) properties.computeIfAbsent("version", a -> String.valueOf(new ServerConfig().getVersion()));
// if the old version system (v1.0) remove "v"
if (ver.contains("v")) ver = ver.substring(1);
loadVersion(properties,Double.parseDouble(ver));
} catch (Exception e) {
Data.LOGGER.error("Error loading legacy config file: {}", e.getMessage());
}
// delete the old file
try {
Files.delete(file.toPath());
Data.LOGGER.info("Deleted " + file.getName());
} catch (Exception e) {
Data.LOGGER.error("Failed to delete the old Sit! config.");
}
}
/**
* converts the legacy hand requirement enum to the new one
* @param requirement the old string
*/
private static HandSetting.SittingRequirement handRequirementUpdater(String requirement) {
return switch (requirement) {
case "restrictive" -> HandSetting.SittingRequirement.FILTER;
case "none" -> HandSetting.SittingRequirement.NONE;
default -> HandSetting.SittingRequirement.EMPTY;
};
}
/**
* gets a list of custom blocks from the legacy way of entering custom sit blocks
*/
private static ArrayList<SittingBlock> getCustomBlocks(ArrayList<String> fix) {
//eg. minecraft:campfire|.46|1|lit=false
ArrayList<SittingBlock> out = new ArrayList<>();
for (String entry : fix) {
String[] split = entry.split("\\|");
// skip if not the right size
if (split.length < 3 || split.length > 4) continue;
// if the other entries aren't correct, skip
if (!Utl.Num.isNum(split[2])) continue;
// make the block states list if possible
ArrayList<String> blockstates = new ArrayList<>();
// if there are blockstates
if (split.length == 4) {
blockstates.addAll(Arrays.asList(split[3].split(",")));
}
// add if everything is A-OK
out.add(new SittingBlock(
new ArrayList<>(Arrays.asList(split[0])),
new ArrayList<>(),blockstates,Double.parseDouble(split[1])));
}
return out;
}
private static ArrayList<String> getFilterList(ArrayList<String> whitelist, ArrayList<String> blacklist) {
ArrayList<String> out = new ArrayList<>(whitelist);
// add a ! in front of every entry of the blacklist
out.addAll(blacklist.stream().map(e -> "!"+e).toList());
return out;
}
public static void loadVersion(Properties properties, double version) {
try {
Type listType = new TypeToken<ArrayList<String>>() {}.getType();
ServerConfig defaultConfig = new ServerConfig();
// load the latest config
ServerConfig serverConfig = new ServerConfig(
2.0,
(String) properties.computeIfAbsent("lang", a -> defaultConfig.getLang()),
Boolean.parseBoolean((String) properties.computeIfAbsent("keep-active", a -> String.valueOf(defaultConfig.isKeepActive()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("sit-while-seated", a -> String.valueOf(defaultConfig.canSitWhileSeated()))),
new PresetBlocks(
Boolean.parseBoolean((String) properties.computeIfAbsent("stairs", a -> String.valueOf(defaultConfig.getPresetBlocks().isStairs()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("slabs", a -> String.valueOf(defaultConfig.getPresetBlocks().isSlabs()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("carpets", a -> String.valueOf(defaultConfig.getPresetBlocks().isCarpets()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("full-blocks", a -> String.valueOf(defaultConfig.getPresetBlocks().isFullBlocks())))
),
Boolean.parseBoolean((String) properties.computeIfAbsent("custom", a -> String.valueOf(defaultConfig.isCustomEnabled()))),
getCustomBlocks(new Gson().fromJson((String)
properties.computeIfAbsent("custom-blocks", a -> "[]"), listType)),
new ArrayList<>()
);
SittingConfig defaultSittingConfig = new SittingConfig();
SittingConfig sittingConfig = new SittingConfig();
// * filters are flipped because the way they work are flipped
try {
sittingConfig = new SittingConfig(
1.0, true, Boolean.parseBoolean((String) properties.computeIfAbsent("hand.sitting", a -> String.valueOf(defaultSittingConfig.canSitWithHand()))),
new HandSetting(
handRequirementUpdater((String) properties.computeIfAbsent("hand.main.requirement", a -> String.valueOf(defaultSittingConfig.getHand(Hand.MAIN_HAND).getSittingRequirement()))),
new HandSetting.Filter(false,
new HandSetting.Filter.Presets(
!Boolean.parseBoolean((String) properties.computeIfAbsent("hand.main.block", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.MAIN_HAND).getFilter().getPresets().isBlock()))),
!Boolean.parseBoolean((String) properties.computeIfAbsent("hand.main.food", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.MAIN_HAND).getFilter().getPresets().isFood()))),
!Boolean.parseBoolean((String) properties.computeIfAbsent("hand.main.usable", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.MAIN_HAND).getFilter().getPresets().isUsable())))),
new CustomItem(getFilterList(
new Gson().fromJson((String) properties.computeIfAbsent("hand.main.whitelist", a -> "[]"), listType),
new Gson().fromJson((String) properties.computeIfAbsent("hand.main.blacklist", a -> "[]"), listType)
),
new ArrayList<>())
)
),
new HandSetting(
handRequirementUpdater((String) properties.computeIfAbsent("hand.off.requirement", a -> String.valueOf(defaultSittingConfig.getHand(Hand.OFF_HAND).getSittingRequirement()))),
new HandSetting.Filter(false,
new HandSetting.Filter.Presets(
!Boolean.parseBoolean((String) properties.computeIfAbsent("hand.off.block", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.OFF_HAND).getFilter().getPresets().isBlock()))),
!Boolean.parseBoolean((String) properties.computeIfAbsent("hand.off.food", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.OFF_HAND).getFilter().getPresets().isFood()))),
!Boolean.parseBoolean((String) properties.computeIfAbsent("hand.off.usable", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.OFF_HAND).getFilter().getPresets().isUsable())))),
new CustomItem(getFilterList(
new Gson().fromJson((String) properties.computeIfAbsent("hand.off.whitelist", a -> "[]"), listType),
new Gson().fromJson((String) properties.computeIfAbsent("hand.off.blacklist", a -> "[]"), listType)
),
new ArrayList<>())
)
)
);
} catch (JsonSyntaxException ignored) {}
// load an older version
if (version == 1.0) {
try {
sittingConfig = new SittingConfig(
1.0, true, defaultSittingConfig.canSitWithHand(),
new HandSetting(
handRequirementUpdater((String) properties.computeIfAbsent("main-hand-requirement", a -> String.valueOf(defaultSittingConfig.getHand(Hand.MAIN_HAND).getSittingRequirement()))),
new HandSetting.Filter(false,
new HandSetting.Filter.Presets(
!Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-block", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.MAIN_HAND).getFilter().getPresets().isBlock()))),
!Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-food", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.MAIN_HAND).getFilter().getPresets().isFood()))),
!Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-usable", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.MAIN_HAND).getFilter().getPresets().isUsable())))),
new CustomItem(getFilterList(
new Gson().fromJson((String) properties.computeIfAbsent("main-hand-whitelist", a -> "[]"), listType),
new Gson().fromJson((String) properties.computeIfAbsent("main-hand-blacklist", a -> "[]"), listType)
),
new ArrayList<>())
)
),
new HandSetting(
handRequirementUpdater((String) properties.computeIfAbsent("off-hand-requirement", a -> String.valueOf(defaultSittingConfig.getHand(Hand.OFF_HAND).getSittingRequirement()))),
new HandSetting.Filter(false,
new HandSetting.Filter.Presets(
!Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-block", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.OFF_HAND).getFilter().getPresets().isBlock()))),
!Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-food", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.OFF_HAND).getFilter().getPresets().isFood()))),
!Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-usable", a -> String.valueOf(!defaultSittingConfig.getHand(Hand.OFF_HAND).getFilter().getPresets().isUsable())))),
new CustomItem(getFilterList(
new Gson().fromJson((String) properties.computeIfAbsent("off-hand-whitelist", a -> "[]"), listType),
new Gson().fromJson((String) properties.computeIfAbsent("off-hand-blacklist", a -> "[]"), listType)
),
new ArrayList<>())
)
)
);
} catch (JsonSyntaxException ignored) {}
}
FileData.setServerConfig(serverConfig);
FileData.setSittingConfig(sittingConfig);
serverConfig.save();
sittingConfig.save();
} catch (Exception e) {
Data.LOGGER.error("Error loading legacy config: {}", e.getMessage());
}
}
}
}

View file

@ -0,0 +1,25 @@
package one.oth3r.sit.file;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
public class SittingBlock extends CustomBlock {
@SerializedName("sitting-height")
private Double sittingHeight = 0.5;
/**
* gets the sitting height of a block, limiting the size from 0 - 1
* @return the sitting height, clamped
*/
public Double getSittingHeight() {
return Math.max(0f, Math.min(1f, sittingHeight));
}
public SittingBlock() {}
public SittingBlock(ArrayList<String> blockIds, ArrayList<String> blockTags, ArrayList<String> blockStates, Double sittingHeight) {
super(blockIds, blockTags, blockStates);
this.sittingHeight = sittingHeight;
}
}

View file

@ -0,0 +1,103 @@
package one.oth3r.sit.file;
import com.google.gson.annotations.SerializedName;
import net.minecraft.util.Hand;
import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Utl;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
public class SittingConfig implements CustomFile<SittingConfig> {
@SerializedName("version")
private Double version = 1.0;
@SerializedName("enabled")
private Boolean enabled = true;
@SerializedName("hand-sitting")
private Boolean handSitting = true;
@SerializedName("main-hand")
private HandSetting mainHand = FileData.Defaults.MAIN_HAND;
@SerializedName("off-hand")
private HandSetting offHand = FileData.Defaults.OFF_HAND;
public SittingConfig() {}
public SittingConfig(double version, boolean enabled, boolean handSitting, HandSetting mainHand, HandSetting offHand) {
this.version = version;
this.enabled = enabled;
this.handSitting = handSitting;
this.mainHand = mainHand;
this.offHand = offHand;
}
public SittingConfig(SittingConfig sittingConfig) {
updateToNewFile(sittingConfig);
}
public Double getVersion() {
return version;
}
public Boolean getEnabled() {
return enabled;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
public boolean canSitWithHand() {
return handSitting;
}
public void setHandSitting(Boolean handSitting) {
this.handSitting = handSitting;
}
public HandSetting getHand(Hand handType) {
return handType.equals(Hand.MAIN_HAND) ? mainHand : offHand;
}
public HandSetting getMainHand() {
return mainHand;
}
public HandSetting getOffHand() {
return offHand;
}
@Override
public void reset() {
updateToNewFile(new SittingConfig());
}
@Override
public @NotNull Class<SittingConfig> getFileClass() {
return SittingConfig.class;
}
@Override
public void updateToNewFile(SittingConfig newFile) {
this.version = newFile.version;
this.enabled = newFile.enabled;
this.handSitting = newFile.handSitting;
this.mainHand = newFile.mainHand;
this.offHand = newFile.offHand;
}
@Override
public String getFileName() {
return "sitting-config.json";
}
@Override
public String getDirectory() {
return Data.CONFIG_DIR;
}
}

View file

@ -0,0 +1,34 @@
package one.oth3r.sit.mixin;
import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ReloadCommand;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Formatting;
import one.oth3r.sit.file.FileData;
import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Utl;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ReloadCommand.class)
public class ReloadCommandMixin {
@Inject(at = @At("TAIL"), method = "register")
private static void register(CommandDispatcher<ServerCommandSource> dispatcher, CallbackInfo ci) {
FileData.loadFiles();
// make sure the server isn't null
MinecraftServer server = Data.getServer();
if (server == null || server.getPlayerManager() == null) return;
// send a reloaded message to all players with permissions
for (ServerPlayerEntity player : server.getPlayerManager().getPlayerList()) {
if (player.isCreativeLevelTwoOp()) {
player.sendMessage(Utl.messageTag().append(Utl.lang("sit!.chat.reloaded").formatted(Formatting.GREEN)));
}
}
}
}

View file

@ -0,0 +1,71 @@
package one.oth3r.sit.mixin;
import net.minecraft.entity.*;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.util.math.*;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(DisplayEntity.TextDisplayEntity.class)
public abstract class TextDisplayDismountMixin extends DisplayEntity {
public TextDisplayDismountMixin(EntityType<?> entityType, World world) {
super(entityType, world);
}
@Override
public Vec3d updatePassengerForDismount(LivingEntity passenger) {
// get the passenger's horizontal rotation, rotated counterclockwise, because the method rotates it clockwise for some reason
int[][] offset = Dismounting.getDismountOffsets(passenger.getHorizontalFacing().rotateYCounterclockwise());
// new array with another slot
int[][] dismountOffsets = new int[offset.length + 1][];
// add an empty offset to the start of the array
dismountOffsets[0] = new int[]{0, 0};
// copy the original elements into the new array starting from index 1
System.arraycopy(offset, 0, dismountOffsets, 1, offset.length);
BlockPos blockPos = this.getBlockPos();
for (EntityPose entityPose : passenger.getPoses()) {
Vec3d vec3d = getDismountPos(passenger, entityPose, dismountOffsets, blockPos);
// check around the block above
if (vec3d == null) vec3d = getDismountPos(passenger, entityPose, dismountOffsets, blockPos.up());
if (vec3d != null) return vec3d;
}
return super.updatePassengerForDismount(passenger);
}
/**
* searches around the BlockPos for a stable dismount spot using the dismountOffsets
* @param passenger the passenger to check
* @param entityPose the pose of the passenger to check
* @param dismountOffsets the positions to check around the BlockPos
* @param blockPos the BlockPos to check around
* @return the Vec3d to dismount at, null if not found
*/
@Unique
private @Nullable Vec3d getDismountPos(LivingEntity passenger, EntityPose entityPose, int[][] dismountOffsets, BlockPos blockPos) {
// iterate through all dismount offsets
for (int[] offset : dismountOffsets) {
BlockPos.Mutable mutable = new BlockPos.Mutable();
mutable.set(blockPos.getX() + offset[0], blockPos.getY(), blockPos.getZ() + offset[1]);
double dismountHeight = this.getWorld().getDismountHeight(mutable);
if (Dismounting.canDismountInBlock(dismountHeight)) {
Vec3d vec3d = Vec3d.ofCenter(mutable, dismountHeight);
Box boundingBox = passenger.getBoundingBox(entityPose);
if (Dismounting.canPlaceEntityAt(this.getWorld(), passenger, boundingBox.offset(vec3d))) {
passenger.setPose(entityPose);
return vec3d;
}
}
}
return null;
}
}

View file

@ -0,0 +1,43 @@
package one.oth3r.sit.packet;
import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.network.packet.CustomPayload;
import net.minecraft.util.Identifier;
import one.oth3r.sit.utl.Data;
public class SitPayloads {
/**
* the packet that the client sends to the server
* @param value the sitting settings for the client
*/
public record SettingsPayload(String value) implements CustomPayload {
public static final Id<SettingsPayload> ID = new Id<>(Identifier.of(Data.MOD_ID,"settings_v2.0"));
public static final PacketCodec<RegistryByteBuf, SettingsPayload> CODEC = PacketCodecs.STRING.xmap(SettingsPayload::new, SettingsPayload::value).cast();
@Override
public Id<SettingsPayload> getId() {
return ID;
}
}
/**
* the packet that the server sends to the client when responding to the settings payload
*/
public record ResponsePayload(String value) implements CustomPayload {
public static final String VERSION = "response_v1.0";
public static final Id<ResponsePayload> ID = new Id<>(Identifier.of(Data.MOD_ID,VERSION));
public static final PacketCodec<RegistryByteBuf, ResponsePayload> CODEC = PacketCodecs.STRING.xmap(ResponsePayload::new, ResponsePayload::value).cast();
@Override
public Id<ResponsePayload> getId() {
return ID;
}
}
}

View file

@ -0,0 +1,31 @@
package one.oth3r.sit.screen;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import java.util.function.Supplier;
public class ClickableImageWidget extends ButtonWidget {
private final Identifier image;
public ClickableImageWidget(int x, int y, int width, int height, Tooltip tooltip, Identifier image, ButtonWidget.PressAction onPress) {
super(x, y, width, height, Text.empty(), onPress, Supplier::get);
this.image = image;
this.setTooltip(tooltip);
}
@Override
protected void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
context.setShaderColor(1.0f, 1.0f, 1.0f, this.alpha);
RenderSystem.enableBlend();
RenderSystem.enableDepthTest();
context.drawTexture(image,
this.getX(), this.getY(), 0.0f, 0.0f, this.getWidth(), this.getHeight(), this.getWidth(), this.getHeight());
context.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
}
}

View file

@ -0,0 +1,77 @@
package one.oth3r.sit.screen;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.ConfirmLinkScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import one.oth3r.sit.file.FileData;
import one.oth3r.sit.utl.Data;
public class ConfigScreen extends Screen {
protected final Screen parent;
public ConfigScreen(Screen parent) {
super(Text.translatable("sit!.screen.config"));
this.parent = parent;
}
@Override
protected void init() {
int startY = this.height / 4 + 48;
int spacing = 36;
TextureButtonWidget serverConfigButton = this.addDrawableChild(new TextureButtonWidget.Builder(Text.translatable("config.server"),
(button) -> client.setScreen(new UnderConstructionScreen(this, FileData.getServerConfig())), false)
.dimensions(250,30).texture(Identifier.of(Data.MOD_ID, "server_button"), 246, 26).build());
serverConfigButton.setPosition(this.width / 2 - (serverConfigButton.getWidth()/2), startY);
TextureButtonWidget sittingConfigButton = this.addDrawableChild(new TextureButtonWidget.Builder(Text.translatable("config.sitting"),
(button) -> client.setScreen(new UnderConstructionScreen(this, FileData.getSittingConfig())), false)
.dimensions(250,30).texture(Identifier.of(Data.MOD_ID, "sitting_button"), 246, 26).build());
sittingConfigButton.setPosition(this.width / 2 - (sittingConfigButton.getWidth()/2), startY+36);
TextureButtonWidget issuesButton = this.addDrawableChild(new TextureButtonWidget.Builder(Text.translatable("sit!.gui.button.issues"),
ConfirmLinkScreen.opening(this, "https://github.com/Oth3r/Sit/issues"), true)
.dimensions(20,20).texture(Identifier.of(Data.MOD_ID, "issues"), 15, 15).build());
issuesButton.setPosition(this.width / 2 - 125, startY + 72 + 12);
this.addDrawableChild(ButtonWidget.builder(Text.translatable("sit!.gui.button.website"),
ConfirmLinkScreen.opening(this, "https://modrinth.com/mod/sit!")
).dimensions(this.width / 2 - 100, startY + 72 + 12, 98, 20).build());
this.addDrawableChild(ButtonWidget.builder(Text.translatable("gui.done"), (button) -> {
close();
}).dimensions(this.width / 2 + 2, startY + 72 + 12, 98, 20).build());
TextureButtonWidget donateButton = this.addDrawableChild(new TextureButtonWidget.Builder(Text.translatable("sit!.gui.button.donate"),
ConfirmLinkScreen.opening(this, "https://Ko-fi.com/oth3r"), true)
.dimensions(20,20).texture(Identifier.of(Data.MOD_ID, "donate"), 15, 15).build());
donateButton.setPosition(this.width / 2 + 105, startY + 72 + 12);
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
// todo fade in like the title screen on first load?
renderBanner(context,width/2 - 64,this.height / 4 -38,1);
}
@Override
public void close() {
this.client.setScreen(parent);
}
private void renderBanner(DrawContext context, int x, int y, float alpha) {
RenderSystem.enableBlend();
context.drawTexture(Identifier.of(Data.MOD_ID, "textures/gui/banner.png"),
x, y, 0.0f, 0.0f, 128, 72, 128, 72);
RenderSystem.disableBlend();
}
}

View file

@ -0,0 +1,11 @@
package one.oth3r.sit.screen;
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
import com.terraformersmc.modmenu.api.ModMenuApi;
public class ModMenu implements ModMenuApi {
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return ConfigScreen::new;
}
}

View file

@ -0,0 +1,85 @@
package one.oth3r.sit.screen;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
public class TextureButtonWidget extends ButtonWidget {
//todo gray support
protected final Identifier texture;
protected final int textureWidth;
protected final int textureHeight;
protected final boolean tooltip;
TextureButtonWidget(int width, int height, Text message, int textureWidth, int textureHeight, Identifier texture, ButtonWidget.PressAction onPress, @Nullable ButtonWidget.NarrationSupplier narrationSupplier, boolean tooltip) {
super(0, 0, width, height, message, onPress, narrationSupplier == null ? DEFAULT_NARRATION_SUPPLIER : narrationSupplier);
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
this.texture = texture;
this.tooltip = tooltip;
if (tooltip) setTooltip(Tooltip.of(message));
}
@Override
public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) {
super.renderWidget(context, mouseX, mouseY, delta);
int x = this.getX() + this.getWidth() / 2 - this.textureWidth / 2;
int y = this.getY() + this.getHeight() / 2 - this.textureHeight / 2;
context.drawGuiTexture(this.texture, x, y, this.textureWidth, this.textureHeight);
}
@Override
public void drawMessage(DrawContext context, TextRenderer textRenderer, int color) {
if (!this.tooltip) super.drawMessage(context, textRenderer, color);
}
public static class Builder {
private final Text text;
private final ButtonWidget.PressAction onPress;
private final boolean hideText;
private int width = 150;
private int height = 20;
@Nullable
private Identifier texture;
private int textureWidth;
private int textureHeight;
@Nullable
ButtonWidget.NarrationSupplier narrationSupplier;
public Builder(Text text, ButtonWidget.PressAction onPress, boolean hideText) {
this.text = text;
this.onPress = onPress;
this.hideText = hideText;
}
public Builder dimensions(int width, int height) {
this.width = width;
this.height = height;
return this;
}
public Builder texture(Identifier texture, int width, int height) {
this.texture = texture;
this.textureWidth = width;
this.textureHeight = height;
return this;
}
public Builder narration(ButtonWidget.NarrationSupplier narrationSupplier) {
this.narrationSupplier = narrationSupplier;
return this;
}
public TextureButtonWidget build() {
if (this.texture == null) {
throw new IllegalStateException("Sprite not set");
}
return new TextureButtonWidget(width,height,text,textureWidth,textureHeight,texture,onPress,narrationSupplier,hideText);
}
}
}

View file

@ -0,0 +1,79 @@
package one.oth3r.sit.screen;
import net.minecraft.client.gui.screen.ConfirmLinkScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.tooltip.Tooltip;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
import one.oth3r.sit.file.CustomFile;
import one.oth3r.sit.file.SittingConfig;
import one.oth3r.sit.utl.Data;
import one.oth3r.sit.utl.Utl;
import java.net.URI;
import java.nio.file.Paths;
public class UnderConstructionScreen<T extends CustomFile<T>> extends Screen {
protected final Screen parent;
protected T file;
public UnderConstructionScreen(Screen parent, T file) {
super(Text.translatable("sit!.screen.config"));
this.parent = parent;
this.file = file;
}
@Override
protected void init() {
int startY = this.height / 5-4;
ButtonWidget foxPNG = this.addDrawableChild(new ClickableImageWidget(70,70,140,140, Tooltip.of(Text.of("Art by @bunnestbun")),
Identifier.of(Data.MOD_ID, "textures/gui/fox.png"), ConfirmLinkScreen.opening(this, "https://www.instagram.com/bunnestbun/")));
foxPNG.setPosition(this.width / 2 - (foxPNG.getWidth()/2), startY-35);
ButtonWidget openFileButton = this.addDrawableChild(new ButtonWidget.Builder(Text.translatable("sit!.gui.button.file"),
(button) -> Util.getOperatingSystem().open(this.file.getFile()))
.dimensions(0, 0, 118 ,20).build());
openFileButton.setPosition(this.width / 2 - 70, startY+110);
TextureButtonWidget folderButton = this.addDrawableChild(new TextureButtonWidget.Builder(Text.translatable("sit!.gui.button.folder"),
(button) -> Util.getOperatingSystem().open(this.file.getFile().getParent()), true)
.dimensions(20,20).texture(Identifier.of(Data.MOD_ID, "folder"), 15, 15).build());
folderButton.setPosition(this.width / 2 + 50, startY + 110);
TextureButtonWidget resetButton = this.addDrawableChild(new TextureButtonWidget.Builder(Text.translatable("sit!.gui.button.reset"),
(button) -> {
this.file.reset();
this.file.save();
}, true)
.dimensions(20,20).texture(Identifier.of(Data.MOD_ID, "reset_file"), 15, 15).build());
resetButton.setPosition(this.width / 2 -70, startY + 135);
ButtonWidget revertButton = this.addDrawableChild(new ButtonWidget.Builder(Text.translatable("sit!.gui.button.revert"),
(button) -> this.file.save())
.dimensions(0, 0, 118,20).build());
revertButton.setPosition(this.width / 2 - 48, startY+135);
ButtonWidget saveExitButton = this.addDrawableChild(new ButtonWidget.Builder(Text.translatable("sit!.gui.button.save"),
(button) -> {
this.file.load();
this.file.save();
// send the settings to the server if editing the sitting file and on a supported server
if (this.file instanceof SittingConfig && Data.isSupportedServer()) {
Utl.sendSettingsPackets();
}
this.client.setScreen(parent);
})
.dimensions(0, 0, 140,20).build());
saveExitButton.setPosition(this.width / 2 - 70, startY+168);
}
@Override
public void close() {
this.client.setScreen(parent);
}
}

View file

@ -0,0 +1,125 @@
package one.oth3r.sit.utl;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
public class Data {
public static final String MOD_ID = "sit-oth3r";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static final String CONFIG_DIR = FabricLoader.getInstance().getConfigDir().toFile()+"/sit!/";
public static final String ENTITY_NAME = "-sit!-entity-";
// init on server load
private static MinecraftServer server;
public static MinecraftServer getServer() {
return server;
}
public static void setServer(MinecraftServer server) {
Data.server = server;
}
// client booleans
private static boolean client = false;
private static boolean inGame = false;
private static boolean singleplayer = false;
private static boolean supportedServer = false;
public static boolean isClient() {
return client;
}
public static void setClient(boolean client) {
Data.client = client;
}
public static boolean isInGame() {
return inGame;
}
public static void setInGame(boolean inGame) {
Data.inGame = inGame;
}
public static boolean isSingleplayer() {
return singleplayer;
}
public static void setSingleplayer(boolean singleplayer) {
Data.singleplayer = singleplayer;
}
public static boolean isSupportedServer() {
return supportedServer;
}
public static void setSupportedServer(boolean supportedServer) {
Data.supportedServer = supportedServer;
}
/**
* a list of players who just joined, to check if they are mounted to a Sit! entity
* (they don't load in on the player join event for some reason)
*/
private static final HashMap<ServerPlayerEntity, Integer> checkPlayers = new HashMap<>();
public static void setCheckPlayer(ServerPlayerEntity player, Integer time) {
checkPlayers.put(player, time);
}
public static void removeCheckPlayer(ServerPlayerEntity player) {
checkPlayers.remove(player);
}
public static HashMap<ServerPlayerEntity, Integer> getCheckPlayers() {
return new HashMap<>(checkPlayers);
}
/**
* a list of players that need a sit entity spawned for them, on the server loop to stop crashing with other mods (ASYNC)
*/
private static final HashMap<ServerPlayerEntity, DisplayEntity.TextDisplayEntity> spawnList = new HashMap<>();
public static void setSpawnList(ServerPlayerEntity player, DisplayEntity.TextDisplayEntity entity) {
spawnList.put(player, entity);
}
public static void removeSpawnList(ServerPlayerEntity player) {
spawnList.remove(player);
}
public static HashMap<ServerPlayerEntity, DisplayEntity.TextDisplayEntity> getSpawnList() {
return new HashMap<>(spawnList);
}
/**
* a list of every Sit! entity in the server, bound to the player
*/
private static final HashMap<ServerPlayerEntity, DisplayEntity.TextDisplayEntity> sitEntities = new HashMap<>();
public static void addSitEntity(ServerPlayerEntity player, DisplayEntity.TextDisplayEntity entity) {
sitEntities.put(player, entity);
}
public static void removeSitEntity(DisplayEntity.TextDisplayEntity entity) {
sitEntities.values().remove(entity);
}
public static DisplayEntity.TextDisplayEntity getSitEntity(ServerPlayerEntity player) {
return sitEntities.get(player);
}
public static HashMap<ServerPlayerEntity, DisplayEntity.TextDisplayEntity> getSitEntities() {
return new HashMap<>(sitEntities);
}
}

View file

@ -0,0 +1,215 @@
package one.oth3r.sit.utl;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Formatting;
import one.oth3r.sit.command.SitCommand;
import one.oth3r.sit.file.FileData;
import one.oth3r.sit.file.LangReader;
import one.oth3r.sit.file.SittingConfig;
import one.oth3r.sit.packet.SitPayloads;
import one.oth3r.sit.screen.ConfigScreen;
import org.lwjgl.glfw.GLFW;
public class Events {
private static class Keybindings {
private static KeyBinding toggle_key;
private static KeyBinding sit_key;
private static KeyBinding config__key;
private static void register() {
toggle_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.sit!.toggle",
GLFW.GLFW_KEY_UNKNOWN,
"category.sit!"
));
sit_key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.sit!.sit",
GLFW.GLFW_KEY_UNKNOWN,
"category.sit!"
));
config__key = KeyBindingHelper.registerKeyBinding(new KeyBinding(
"key.sit!.config",
GLFW.GLFW_KEY_UNKNOWN,
"category.sit!"
));
}
private static void loopLogic(MinecraftClient client) {
ClientPlayerEntity player = client.player;
while (config__key.wasPressed()) {
client.setScreen(new ConfigScreen(client.currentScreen));
}
/// anything below uses the player object, make sure it's not null
if (player == null) return;
while (toggle_key.wasPressed()) {
if (Data.isInGame()) {
player.sendMessage(Logic.toggleSiting(), true);
}
}
while (sit_key.wasPressed()) {
// just send the sit command
if (Data.isInGame()) {
if (Data.isSupportedServer()) {
player.networkHandler.sendCommand("sit");
} else {
// unsupported server message if not in a Sit! server
player.sendMessage(Utl.lang("sit!.chat.unsupported")
.formatted(Formatting.RED), true);
}
}
}
}
}
private static class Packet {
private static void common() {
// register the data
PayloadTypeRegistry.playC2S().register(SitPayloads.SettingsPayload.ID, SitPayloads.SettingsPayload.CODEC);
PayloadTypeRegistry.playS2C().register(SitPayloads.ResponsePayload.ID, SitPayloads.ResponsePayload.CODEC);
// server receiver is common
/// receiving the sitting setting payload
ServerPlayNetworking.registerGlobalReceiver(SitPayloads.SettingsPayload.ID,((payload, context) -> Data.getServer().execute(() -> {
// save the setting on the server for that player
FileData.setPlayerSetting(context.player(),Utl.getGson().fromJson(payload.value(), SittingConfig.class));
// send the player back a response packet for confirmation
ServerPlayNetworking.send(context.player(),new SitPayloads.ResponsePayload(SitPayloads.ResponsePayload.VERSION));
// log the receiving of the packet from the player
Data.LOGGER.info(Utl.lang("sit!.console.player_settings",context.player().getName().getString()).getString());
})));
}
private static void client() {
/// receiving the response packet from the server
ClientPlayNetworking.registerGlobalReceiver(SitPayloads.ResponsePayload.ID, ((payload, context) -> {
// only update when needed
if (!Data.isSupportedServer()) {
Data.setSupportedServer(true);
Data.LOGGER.info(Utl.lang("sit!.console.connected",payload.value()).getString());
}
}));
}
}
private static void clientMisc() {
// client tick loop
ClientTickEvents.END_CLIENT_TICK.register(client -> {
assert client.player != null;
Keybindings.loopLogic(client);
});
}
/**
* registers all client connection code
*/
private static void clientConnections() {
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
Data.setInGame(true);
if (client.isInSingleplayer()) Data.setSingleplayer(true);
// send a data packet whenever joining a server
Utl.sendSettingsPackets();
});
// reset cashed things on disconnect
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> {
Data.setInGame(false);
Data.setSingleplayer(false);
Data.setSupportedServer(false);
});
}
/**
* registers all common server player connection code
*/
private static void playerConnections() {
// PLAYER JOIN
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
FileData.setPlayerSetting(handler.player, FileData.getSittingConfig());
Data.setCheckPlayer(handler.player, 5);
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
// if keep is off, remove the entity
if (!FileData.getServerConfig().isKeepActive()) {
Logic.removeEntity(handler.player);
}
FileData.removePlayerSetting(handler.player);
});
}
/**
* registers all server lifecycle events
*/
private static void serverLifecycle() {
ServerLifecycleEvents.SERVER_STARTED.register(s -> {
Data.setServer(s);
LangReader.loadLanguageFile();
// right click on block event
UseBlockCallback.EVENT.register((pl, world, hand, hitResult) -> {
if (Data.isClient() && !Data.isSingleplayer()) return ActionResult.PASS;
// get the server player
ServerPlayerEntity player = Data.getServer().getPlayerManager().getPlayer(pl.getUuid());
// make sure the player isn't null, and make sure they aren't in spectator
if (player == null || player.isSpectator()) return ActionResult.PASS;
// consume if sitting, if not pass
return Logic.sit(player,hitResult.getBlockPos(),hitResult) ? ActionResult.CONSUME : ActionResult.PASS;
});
});
ServerLifecycleEvents.SERVER_STOPPED.register(s -> {
// clear the server
Data.setServer(null);
// clear all player settings (singleplayer and such)
FileData.clearPlayerSettings();
});
// server loop setup
ServerTickEvents.END_SERVER_TICK.register(minecraftServer -> minecraftServer.execute(LoopManager::tick));
// server command setup
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SitCommand.register(dispatcher));
}
// a one call method for the common and client
public static void registerCommon() {
playerConnections();
serverLifecycle();
Packet.common();
}
public static void registerClient() {
Keybindings.register();
clientConnections();
clientMisc();
Packet.client();
}
}

View file

@ -0,0 +1,185 @@
package one.oth3r.sit.utl;
import net.minecraft.block.*;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.MutableText;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import one.oth3r.sit.file.FileData;
import one.oth3r.sit.file.SittingConfig;
import one.oth3r.sit.file.HandSetting;
import org.jetbrains.annotations.Nullable;
public class Logic {
public static boolean sit(ServerPlayerEntity player, BlockPos blockPos, @Nullable BlockHitResult hitResult) {
// cant sit if crouching
if (player.isSneaking()) return false;
// if sitting on a sit entity and sit while seated off, false
if (!FileData.getServerConfig().canSitWhileSeated() && Data.getSitEntity(player) != null) return false;
// if hit result isnt null (check the hands of the player) & the player hand checker returns false (can't sit with the items in the hand), quit
if (hitResult != null) {
if (!checkHands(player)) return false;
}
ServerWorld serverWorld = player.getServerWorld();
BlockState blockState = serverWorld.getBlockState(blockPos);
Double sitHeight = Utl.getSittingHeight(blockState,player,blockPos,hitResult);
// if the sit height is null, its not a sittable block
if (sitHeight == null) return false;
DisplayEntity.TextDisplayEntity entity = Utl.Entity.create(serverWorld,blockPos,sitHeight);
if (!checkPlayerSitAbility(entity)) return false;
Utl.Entity.spawnSit(player, entity);
return true;
}
public static boolean sitLooking(ServerPlayerEntity player) {
return sit(player, Utl.getBlockPosPlayerIsLookingAt(player.getServerWorld(),player,5),null);
}
/**
* checks the hands of the player and the items in each hand and sees if the player can sit down
*/
public static boolean checkHands(ServerPlayerEntity player) {
SittingConfig sittingConfig = FileData.getPlayerSetting(player);
// if can't sit with hand, false
if (!sittingConfig.canSitWithHand()) return false;
// a boolean that shows if the player can sit or not
boolean canSit = true;
// for each hand
for (Hand hand : Hand.values()) {
// if they can't sit, no need to run extra code
if (!canSit) break;
HandSetting handSetting = sittingConfig.getHand(hand);
switch (handSetting.getSittingRequirement()) {
case EMPTY -> canSit = player.getStackInHand(hand).isEmpty();
case FILTER -> canSit = Utl.checkItem(handSetting.getFilter(), player.getStackInHand(hand));
}
}
// return the output of the check
return canSit;
}
/**
* removes the entity bound to the player from the game, using the player
*/
public static void removeEntity(ServerPlayerEntity player) {
DisplayEntity.TextDisplayEntity entity = Data.getSitEntity(player);
// make sure the player has a sit entity bounded to them
if (entity == null) return;
// remove the entity
Utl.Entity.remove(entity);
}
/**
* spawns a sit entity for the player, they HAVE TO BE in the spawn list
*/
public static void spawnEntity(ServerPlayerEntity player) {
// return if not in the list
if (Data.getSpawnList().get(player) == null) return;
// if the player is already sitting on a sit entity, remove it before spawning a new one
if (Data.getSitEntity(player) != null) Logic.removeEntity(player);
// get the new entity
DisplayEntity.TextDisplayEntity sitEntity = Data.getSpawnList().get(player);
// spawn and ride the entity
player.getServerWorld().spawnEntity(sitEntity);
player.startRiding(sitEntity);
// add the entity to the list
Data.addSitEntity(player, sitEntity);
// remove the entity from the spawn list
Data.removeSpawnList(player);
}
/**
* checks if the player should still be sitting, e.g. the block was destroyed ect.
*/
public static void checkSittingValidity(ServerPlayerEntity player) {
DisplayEntity.TextDisplayEntity entity = Data.getSitEntity(player);
// make sure the player has a sit entity bounded to them
if (entity == null) return;
// if the entity location isn't valid anymore, remove it
if (!Utl.Entity.isValid(player,entity)) {
removeEntity(player);
}
}
/**
* checks if entity would cause the player to suffocate when sitting
* @param entity the entity
* @return true if there is no obstruction
*/
public static boolean checkPlayerSitAbility(DisplayEntity.TextDisplayEntity entity) {
// get the entity's block pos
BlockPos pos = Utl.Entity.getBlockPos(entity);
// get the poses to check above the block
BlockPos pos1 = new BlockPos(pos).add(0,1,0), pos2 = new BlockPos(pos).add(0,2,0), posBelow = new BlockPos(pos);
// doesn't check 2 blocks above if not sitting above .80 of the block
if (pos.getY() > entity.getY() - .80) {
pos2 = pos2.add(0,-1,0);
posBelow = posBelow.add(0,-1,0);
}
// check if both poses are obstructed or not
return Utl.isNotObstructed(entity.getWorld(),pos1) && Utl.isNotObstructed(entity.getWorld(),pos2)
// also check if occupied, checking below to make sure you cant sit directly on top of another sit entity
&& Utl.isNotOccupied(pos) && Utl.isNotOccupied(pos1) && Utl.isNotOccupied(pos2) && Utl.isNotOccupied(posBelow);
}
/**
* reloads the config files
*/
public static void reload() {
FileData.loadFiles();
}
/**
* toggles the sit ablity config option
* @return returns a message, that can be sent to the player
*/
public static MutableText toggleSiting() {
if (Data.isSupportedServer()) {
// get the sitting config
SittingConfig config = FileData.getSittingConfig();
// toggle the setting
config.setEnabled(!config.getEnabled());
// set the sitting config to the new value
FileData.setSittingConfig(config);
// save the changes to the file
config.save();
// send the changes to the server
Utl.sendSettingsPackets();
// get the message settings
String messageKey = "sit!.chat.sit_toggle."+(config.getEnabled()?"on":"off");
Formatting messageColor = config.getEnabled()?Formatting.GREEN:Formatting.RED;
// send the player the actionbar message
return Utl.lang("sit!.chat.sit_toggle",
Utl.lang(messageKey).formatted(messageColor));
} else {
// unsupported server message if not in a Sit! server
return Utl.lang("sit!.chat.unsupported")
.formatted(Formatting.RED);
}
}
}

View file

@ -0,0 +1,59 @@
package one.oth3r.sit.utl;
import net.minecraft.entity.Entity;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.HashMap;
public class LoopManager {
private static int time = 0;
public static void tick() {
time++;
if (time >= 5) {
time = 0;
// check all sit entities to make sure their still valid
HashMap<ServerPlayerEntity, DisplayEntity.TextDisplayEntity> entities = Data.getSitEntities();
for (ServerPlayerEntity player : entities.keySet()) {
DisplayEntity.TextDisplayEntity entity = entities.get(player);
if (player.getVehicle() == null || !player.getVehicle().equals(entity)) {
Logic.removeEntity(player);
} else {
Logic.checkSittingValidity(player);
}
}
// get the player's sit entity when they join
HashMap<ServerPlayerEntity, Integer> checkPlayers = Data.getCheckPlayers();
for (ServerPlayerEntity player : checkPlayers.keySet()) {
Integer time = checkPlayers.get(player);
// tick down or remove the player if at the end
time -= 1;
if (time <= 0) Data.removeCheckPlayer(player);
else Data.setCheckPlayer(player, time);
if (player.getVehicle() != null) {
Entity entity = player.getVehicle();
if (entity instanceof DisplayEntity.TextDisplayEntity tde && entity.getName().getString().equals(Data.ENTITY_NAME)) {
// bind the entity to the player
Data.addSitEntity(player, tde);
// check if the player is still allowed to sit
Logic.checkSittingValidity(player);
// remove the player from the check
Data.removeCheckPlayer(player);
}
}
}
// spawn entities for everyone in the spawn list
HashMap<ServerPlayerEntity, DisplayEntity.TextDisplayEntity> spawnList = Data.getSpawnList();
for (ServerPlayerEntity player : spawnList.keySet()) {
Logic.spawnEntity(player);
}
}
}
}

View file

@ -0,0 +1,521 @@
package one.oth3r.sit.utl;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.MalformedJsonException;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.block.*;
import net.minecraft.block.enums.BlockHalf;
import net.minecraft.block.enums.SlabType;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.entity.player.PlayerEntity;
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.server.world.ServerWorld;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.text.TextColor;
import net.minecraft.util.*;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RaycastContext;
import net.minecraft.world.World;
import one.oth3r.sit.file.*;
import one.oth3r.sit.packet.SitPayloads;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class Utl {
/**
* check if a block is obstructed (no collision)
* @return true if not obstructed
*/
public static boolean isNotObstructed(World world, BlockPos blockPos) {
// get the block state at the blockPos
BlockState state = world.getBlockState(blockPos);
// make sure it doesn't have a collision
return state.getCollisionShape(world,blockPos).isEmpty();
}
/**
* checks the list of sit entities and sees if any of them are occupying the block pos
*/
public static boolean isNotOccupied(BlockPos pos) {
return Data.getSitEntities().values().stream().noneMatch(entity -> entity.getBlockPos().equals(pos));
}
public static class Num {
public static boolean isInt(String string) {
try {
Integer.parseInt(string);
} catch (NumberFormatException nfe) {
return false;
}
return true;
}
public static Integer toInt(String s) {
// return an int no matter what
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
try {
return (int) Double.parseDouble(s);
} catch (NumberFormatException e2) {
return 0;
}
}
}
public static boolean isNum(String s) {
// checks if int or a double
try {
Integer.parseInt(s);
return true;
} catch (NumberFormatException e1) {
try {
Double.parseDouble(s);
return true;
} catch (NumberFormatException e2) {
return false;
}
}
}
}
public static final double HALF_BLOCK = 0.5;
public static final double CARPET = 0.062;
/**
* checks if the provided itemstack is a valid one for the provided filter
* @param filter the filter
* @param itemStack itemstack to check
* @return if true, the item isn't filtered out
*/
public static boolean checkItem(HandSetting.Filter filter, ItemStack itemStack) {
// default to true if there's nothing
if (itemStack.isEmpty()) return true;
boolean TRUE = true, FALSE = false;
if (filter.isInverted()) {
TRUE = false;
FALSE = true;
}
boolean itemcheck = filter.getCustomItems().checkItem(itemStack);
// iif the item passes the checks, return true
if (itemcheck) return TRUE;
// if none of the custom were met, try the default conditions
// get the use actions for the filters
ArrayList<UseAction> food = new ArrayList<>();
food.add(UseAction.EAT);
food.add(UseAction.DRINK);
ArrayList<UseAction> notUsable = new ArrayList<>(food);
notUsable.add(UseAction.NONE);
HandSetting.Filter.Presets presets = filter.getPresets();
// try the default conditions
if (presets.isBlock() && itemStack.getItem() instanceof BlockItem) return TRUE;
if (presets.isFood() && food.contains(itemStack.getUseAction())) return TRUE;
if (presets.isUsable() && hasItemUse(itemStack)) return TRUE;
// if nothing else is met, the item is filtered out
return FALSE;
}
/**
* get a block ID (eg. minecraft:air) from a blockstate. (it is easier with a block, but we are mostly working with block states
* @return the block ID (minecraft:air)
*/
public static String getBlockID(BlockState blockState) {
return Registries.BLOCK.getId(blockState.getBlock()).toString();
}
/**
* gets the sitting height for the provided blockstate, via memory loaded config from Data
* @param blockState the state of the block
* @param player the player to
* @param blockPos the pos of the block
* @param hit nullable, for the player interaction check
* @return null if not a valid block
*/
public static Double getSittingHeight(BlockState blockState, ServerPlayerEntity player, BlockPos blockPos, @Nullable BlockHitResult hit) {
ServerConfig config = FileData.getServerConfig();
Block block = blockState.getBlock();
// make sure that the block that is being sit on has no interaction when hand sitting
if (hit != null && hasInteraction(blockState)) {
return null;
}
// only if custom is enabled
if (config.isCustomEnabled()) {
// if the block is on the blacklist, false
if (config.getBlacklistedBlocks().stream().anyMatch(c -> c.isValid(blockState))) return null;
for (SittingBlock sittingBlock : config.getSittingBlocks()) {
// if the block is valid, true
if (sittingBlock.isValid(blockState)) return sittingBlock.getSittingHeight();
}
}
// add the default block types and check for them
if (block instanceof StairsBlock
&& config.getPresetBlocks().isStairs()
&& blockState.get(StairsBlock.HALF) == BlockHalf.BOTTOM) return HALF_BLOCK;
if (config.getPresetBlocks().isSlabs()
&& block instanceof SlabBlock
&& blockState.get(SlabBlock.TYPE) == SlabType.BOTTOM) return HALF_BLOCK;
if (config.getPresetBlocks().isCarpets()
&& block instanceof CarpetBlock) return CARPET;
if (config.getPresetBlocks().isFullBlocks()
// make sure the block is a full cube
&& blockState.isFullCube(player.getWorld(),blockPos)) return 1.0;
// at the end, return false
return null;
}
/**
* checks if a block has an interaction
* @param blockState the blockstate of the block to check
* @return if the block has an interaction or not
*/
public static boolean hasInteraction(BlockState blockState) {
return isMethodOverridden(AbstractBlock.class, blockState.getBlock().getClass(), "onUse", BlockState.class, World.class, BlockPos.class, PlayerEntity.class, BlockHitResult.class);
}
/**
* checks if an item has a use
* @param itemStack the itemstack to check
* @return if the item has a use or not
*/
public static boolean hasItemUse(ItemStack itemStack) {
return isMethodOverridden(Item.class, itemStack.getItem().getClass(), "use", World.class, PlayerEntity.class, Hand.class);
}
/**
* checks if a method in the base class has been overridden in the subclass(es) by:
* checking the subclass and its supers till the original method is found or the original method is found
* @param baseClass the base class
* @param subclass the subclass to check
* @param methodName the method to check for
* @param parameterTypes the parameterTypes for the method, see {@link java.lang.Class#getDeclaredMethod(java.lang.String, java.lang.Class[])}
* @return if the method is overridden or not
*/
public static boolean isMethodOverridden(Class<?> baseClass, Class<?> subclass, String methodName, Class<?>... parameterTypes) {
try {
// get the original method
Method superMethod = baseClass.getMethod(methodName, parameterTypes);
// the current class to check, starting with the subclass
Class<?> currentClass = subclass;
// while the class is null and the current class isn't the same as the baseclass.
while (currentClass != null && !currentClass.equals(baseClass)) {
try {
// get the submethod
Method subMethod = currentClass.getDeclaredMethod(methodName, parameterTypes);
// check if the methods are different
if (!superMethod.equals(subMethod)) {
return true;
}
} catch (NoSuchMethodException ignored) {
// method isnt in this class, bump up a class and check that one
}
currentClass = currentClass.getSuperclass();
}
} catch (NoSuchMethodException e) {
// method doesn't exist in the base class
return false;
}
// an override wasn't found
return false;
}
public static class Entity {
/**
* checks if the entity's block is still there, & is valid
*/
public static boolean isValid(ServerPlayerEntity player, @NotNull DisplayEntity.TextDisplayEntity entity) {
BlockPos blockPos = getBlockPos(entity);
// get the blockstate
BlockState blockState = player.getWorld().getBlockState(blockPos);
// check if the block is still there & the block is a valid sit block (by checking if there is a sit height for the block)
return !blockState.isAir() && getSittingHeight(blockState,player,blockPos,null) != null;
}
/**
* gets the bound block pos of the sit entity
*/
public static BlockPos getBlockPos(DisplayEntity.TextDisplayEntity entity) {
// get the block pos
BlockPos pos = new BlockPos(entity.getBlockX(),entity.getBlockY(),entity.getBlockZ());
// if above the block, subtract 1
if (isAboveBlockHeight(entity)) {
pos = pos.add(0,-1,0);
}
return pos;
}
/**
* using the entity's pitch, figure out if the player is above the block height or not
*/
public static boolean isAboveBlockHeight(DisplayEntity.TextDisplayEntity entity) {
return entity.getPitch() > 0;
}
/**
* creates the sit entity from the pos & sit height provided
* @param world the world to make the entity in
* @param blockPos the pos of the entity
* @param sitHeight the height for the entity to be at
* @return the entity at the correct height and position
*/
public static DisplayEntity.TextDisplayEntity create(World world, BlockPos blockPos, double sitHeight) {
DisplayEntity.TextDisplayEntity entity = new DisplayEntity.TextDisplayEntity(EntityType.TEXT_DISPLAY,world);
// entity flags
entity.setCustomName(Text.of(Data.ENTITY_NAME));
entity.setCustomNameVisible(false);
entity.setInvulnerable(true);
entity.setInvisible(true);
/// make a double for adjusting the entity height if some versions change the player sit height on entities again
double adjustmentY = 0;
// get the entities y level
double entityY = blockPos.getY();
entityY += sitHeight + adjustmentY;
// set the entities position
entity.updatePositionAndAngles(blockPos.getX()+.5, entityY, blockPos.getZ()+.5, 0, 0);
// change pitch based on if player is sitting below block height or not (full block height only)
if (entity.getY() == blockPos.getY() + 1) entity.setPitch(90); // below
else entity.setPitch(-90); // above
return entity;
}
/**
* removes the entity from the entity map and world, dismounting any passengers
*/
public static void remove(DisplayEntity.TextDisplayEntity entity) {
// dismount everyone
entity.removeAllPassengers();
// remove the entity
entity.setRemoved(net.minecraft.entity.Entity.RemovalReason.DISCARDED);
// remove the entity from the data set if exists
Data.removeSitEntity(entity);
}
/**
* spawns the entity and make the player sit on it
*/
public static void spawnSit(ServerPlayerEntity player, DisplayEntity.TextDisplayEntity entity) {
Data.setSpawnList(player, entity);
}
/**
* removes all sit entities loaded on the server
*/
public static void purge(ServerPlayerEntity player, boolean message) {
/// FYI it cant purge an entity from a disconnected player or unloaded chunks
// get a list of sit entities
List<? extends DisplayEntity.TextDisplayEntity> list = player.getServerWorld()
.getEntitiesByType(TypeFilter.instanceOf(DisplayEntity.TextDisplayEntity.class),
entity -> entity.getName().getString().equals(Data.ENTITY_NAME));
// amount of sit entities purged
int count = 0;
// remove each one & count
for (DisplayEntity.TextDisplayEntity entity : list) {
remove(entity);
count++;
}
// send a message if needed
if (message) {
player.sendMessage(messageTag().append(Utl.lang("sit!.chat.purged",Utl.lang("sit!.chat.purged.total",count).styled(
style -> style.withColor(Colors.LIGHT_GRAY).withItalic(true)
)).styled(
style -> style.withColor(Formatting.GREEN)
)));
}
}
}
public static MutableText messageTag() {
return Text.literal("[").append(Text.literal("Sit!").styled(
style -> style.withColor(TextColor.parse("#c400ff").result().orElse(TextColor.fromFormatting(Formatting.DARK_PURPLE))))
).append("] ");
}
/**
* gets a MutableText using the language key, if on server, using the custom lang reader
*/
public static MutableText lang(String key, Object... args) {
if (Data.isClient()) return Text.translatable(key, args);
else return LangReader.of(key, args).getTxT();
}
public static class Enum {
public static <T extends java.lang.Enum<T>> T get(Object enumString, Class<T> enumType) {
return get(enumString,enumType,enumType.getEnumConstants()[0]);
}
/**
* gets an enum from a string without returning null
* @param enumString the string of the enum
* @param enumType the class of enums
* @param defaultEnum the enum to return if a match isn't found
* @return an enum, if there isn't a match, it returns the first enum
*/
public static <T extends java.lang.Enum<T>> T get(Object enumString, Class<T> enumType, T defaultEnum) {
T[] values = enumType.getEnumConstants();
for (T all : values) {
// check if there is a match for any of the enum names
if (enumString.toString().equals(all.name())) return all;
}
// if there's no match return the first entry
return defaultEnum;
}
}
/**
* sends the settings packets to the server, if client & in game
*/
public static void sendSettingsPackets() {
if (Data.isClient() && Data.isInGame()) {
ClientPlayNetworking.send(new SitPayloads.SettingsPayload(Utl.getGson().toJson(FileData.getSittingConfig())));
}
}
/**
* gets a Gson with the LenientTypeAdapter
*/
public static Gson getGson() {
return new GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapterFactory(new LenientTypeAdapterFactory())
.create();
}
/**
* the LenientTypeAdapter, doesn't throw anything when reading a weird JSON entry, good for human entered JSONs
*/
@SuppressWarnings("unchecked")
public static class LenientTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
// Check if the type is a List, then run the custom list type adapter
if (List.class.isAssignableFrom(type.getRawType())) {
Type elementType = ((ParameterizedType) type.getType()).getActualTypeArguments()[0];
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
// the custom adapter
return (TypeAdapter<T>) new RemoveNullListTypeAdapter<>(elementAdapter);
}
return new TypeAdapter<>() {
// normal writer
public void write(JsonWriter out, T value) throws IOException {
delegate.write(out, value);
}
// custom reader
public T read(JsonReader in) throws IOException {
try {
//Try to read value using default TypeAdapter
return delegate.read(in);
} catch (JsonSyntaxException | MalformedJsonException e) {
// don't throw anything if there's a weird JSON, just return null
in.skipValue();
return null;
}
}
};
}
}
/**
* type adapter that doesnt allow null / bad entries
*/
private static class RemoveNullListTypeAdapter<E> extends TypeAdapter<List<E>> {
private final TypeAdapter<E> elementAdapter;
RemoveNullListTypeAdapter(TypeAdapter<E> elementAdapter) {
this.elementAdapter = elementAdapter;
}
@Override
public void write(JsonWriter out, List<E> value) throws IOException {
out.beginArray();
for (E element : value) {
elementAdapter.write(out, element);
}
out.endArray();
}
@Override
public List<E> read(JsonReader in) throws IOException {
List<E> list = new ArrayList<>();
in.beginArray();
while (in.hasNext()) {
try {
E element = elementAdapter.read(in);
// skip null entry
if (element == null) continue;
list.add(element);
} catch (Exception e) {
// skip invalid entry
in.skipValue();
}
}
in.endArray();
return list;
}
}
public static BlockPos getBlockPosPlayerIsLookingAt(ServerWorld world, PlayerEntity player, double range) {
// pos, adjusted to player eye level
Vec3d rayStart = player.getPos().add(0, player.getEyeHeight(player.getPose()), 0);
// extend ray by the range
Vec3d rayEnd = rayStart.add(player.getRotationVector().multiply(range));
BlockHitResult hitResult = world.raycast(new RaycastContext(rayStart, rayEnd, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, ShapeContext.absent()));
if (hitResult.getType() == HitResult.Type.BLOCK) {
return hitResult.getBlockPos();
}
return new BlockPos(player.getBlockPos());
}
}

View file

Before

Width:  |  Height:  |  Size: 9.6 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Before After
Before After

View file

@ -0,0 +1,107 @@
{
"category.sit!": "Sit!",
"config.entry.exclusion": "Put a `!` in front of a entry to exclude it!",
"config.entry.example": "Entry example: %s",
"config.server": "Server Config",
"config.server.description": "Configures the server side settings.",
"config.server.lang": "Language",
"config.server.lang.description": "The language used for the Sit! mod.",
"config.server.keep-active": "Keep Active",
"config.server.keep-active.description": "Toggles if the Sit! entity should stay, even if the player / server is offline. \n When false, the player will not be sitting when logging back in.",
"config.server.sit-while-seated": "Sit While Seated",
"config.server.sit-while-seated.description": "Toggles the ability to sit on another Sit! block while already sitting.",
"config.server.preset-blocks": "Preset Blocks",
"config.server.preset-blocks.description": "Toggles for the default Sit! blocks.",
"config.server.preset-blocks.stairs": "Stairs",
"config.server.preset-blocks.slabs": "Slabs",
"config.server.preset-blocks.carpets": "Carpets",
"config.server.preset-blocks.full-blocks": "Full Blocks",
"config.server.custom-enabled": "Custom",
"config.server.custom-enabled.description": "Toggles the use of custom blocks for sitting.",
"config.server.custom-blocks": "Custom Blocks",
"config.server.custom-blocks.description": "The list of custom sitting blocks.",
"config.server.custom-block.block-ids": "Block IDs",
"config.server.custom-block.block-ids.description": "The block id(s) for the custom sitting block.",
"config.server.custom-block.block-tags": "Block Tags",
"config.server.custom-block.block-tags.description": "The block tag(s) for the custom sitting block.",
"config.server.custom-block.blockstates": "Blockstates",
"config.server.custom-block.blockstates.description": "The blockstates that the block must have to be a custom sitting block.",
"config.server.custom-block.sitting-height": "Sitting Height",
"config.server.custom-block.sitting-height.description": "The player sitting height of the custom block.",
"config.server.blacklisted-blocks": "Blacklisted Blocks",
"config.server.blacklisted-blocks.description": "The list of blocks that arent allowed to be sat on.",
"config.sitting": "Sitting Config",
"config.sitting.description": "Configures the sitting ability, on the server each player can have their own sitting config when they use the mod.",
"config.sitting.enabled": "Enabled",
"config.sitting.enabled.description": "Toggles the ability to sit.",
"config.sitting.hand-sitting": "Hand Sitting",
"config.sitting.hand-sitting.description": "Toggles the ability to sit using hand interactions.",
"config.sitting.hand.main": "Main Hand",
"config.sitting.hand.main.description": "main hand",
"config.sitting.hand.off": "Off Hand",
"config.sitting.hand.off.description": "off hand",
"config.sitting.hand.description": "Configures %s sitting settings.",
"config.sitting.hand.requirement": "Sitting Requirement",
"config.sitting.hand.requirement.description": "The hand requirement to sit. Eg, if EMPTY, the hand has to be empty",
"config.sitting.hand.requirement.description.none": "No requirement to sit.",
"config.sitting.hand.requirement.description.empty": "The hand has to be empty to sit.",
"config.sitting.hand.requirement.description.filter": "The hand can only sit if the item in the hand matches one of the filters.",
"config.sitting.hand.filter": "Filter",
"config.sitting.hand.filter.description": "The list of items for the filter hand requirement.",
"config.sitting.hand.filter.block": "Blocks",
"config.sitting.hand.filter.block.description": "The blocks default filter.",
"config.sitting.hand.filter.food": "Foods",
"config.sitting.hand.filter.food.description": "The foods default filter.",
"config.sitting.hand.filter.usable": "Usables",
"config.sitting.hand.filter.usable.description": "The usables default filter. (Tridents, Shields, Bows)",
"config.sitting.hand.filter.custom-items": "Custom Items",
"config.sitting.hand.filter.custom-items.description": "A list of custom items to add to the filter.",
"config.sitting.hand.filter.custom-tags": "Custom Tags",
"config.sitting.hand.filter.custom-tags.description": "A list of custom item tags to add to the filter.",
"sit!.chat.toggle_sit": "%s Sitting!",
"sit!.chat.toggle_sit.on": "Enabled",
"sit!.chat.toggle_sit.off": "Disabled",
"sit!.chat.unsupported": "Sit! is not available on this server.",
"sit!.chat.reloaded": "Reloaded the config!",
"sit!.chat.purged": "Purged all loaded Sit! entities! %s",
"sit!.chat.purged.total": "(%s removed)",
"key.sit!.toggle": "Toggle Sitting",
"key.sit!.sit": "Sit",
"key.sit!.config": "Open Config",
"sit!.screen.config": "Sit! Config",
"sit!.gui.button.file": "Open File",
"sit!.gui.button.folder": "Open Folder",
"sit!.gui.button.reset": "Reset",
"sit!.gui.button.issues": "Issues",
"sit!.gui.button.donate": "Donate",
"sit!.gui.button.revert": "Revert Changes",
"sit!.gui.button.save": "Save and Close",
"sit!.gui.button.website": "Website",
"sit!.console.connected": "Connected to Sit! server: %s",
"sit!.console.player_settings": "Received custom sitting settings from %s!",
"modmenu.descriptionTranslation.sit-oth3r": "Adds sitting to minecraft! Endless customizability for hand restrictions and sittable blocks.\n Players can have their own sitting settings when using the Sit! client on the server!"
}

View file

@ -0,0 +1,83 @@
{
"category.sit!": "Sit!",
"config.entry.exclusion": "Metti un `!` davanti a un campo per escluderlo!",
"config.entry.example": "Esempio campo: %s",
"config.server": "Configura Server",
"config.server.description": "Configura altre impostazioni del server.",
"config.server.lang": "Lingua",
"config.server.lang.description": "La lingua usata dalla mod Sit!.",
"config.server.keep-active": "Mantieni Attivo",
"config.server.keep-active.description": "Decidi se l'entità Sit! deve restare, anche se il giocatore / server è offline.",
"config.server.sit-while-seated": "Siediti mentre sei seduto",
"config.server.sit-while-seated.description": "Decidi se togliere la possibilità di sedersi a un blocco Sit! mentre ti siedi.",
"config.server.preset-blocks": "Preset Blocks",
"config.server.preset-blocks.description": "Attiva per i blocchi Sit! default.",
"config.server.preset-blocks.stairs": "Scale",
"config.server.preset-blocks.slabs": "Mezzi blocchi",
"config.server.preset-blocks.carpets": "Tappeti",
"config.server.preset-blocks.full-blocks": "Blocchi interi",
"config.server.custom-enabled": "Custom",
"config.server.custom-enabled.description": "Attiva l'uso di blocchi custom per sederti.",
"config.server.custom-blocks": "Blocchi Custom",
"config.server.custom-blocks.description": "La lista dei blocchi custom per sedersi.",
"config.server.custom-block.block-ids": "ID dei blocchi",
"config.server.custom-block.block-ids.description": "L'id dei blocchi custom per sedersi.",
"config.server.custom-block.block-tags": "Tag dei blocchi",
"config.server.custom-block.block-tags.description": "I tag dei blocchi custom per sedersi.",
"config.server.custom-block.blockstates": "Stato dei blocchi",
"config.server.custom-block.blockstates.description": "Lo stato che un blocco deve avere per potercisi sedere.",
"config.server.custom-block.sitting-height": "Altezza per sedersi",
"config.server.custom-block.sitting-height.description": "L'altezza del giocatore per sedersi sul blocco custom.",
"config.server.blacklisted-blocks": "Blocchi Blacklistati",
"config.server.blacklisted-blocks.description": "La lista dei blocchi su cui non puoi sederti.",
"config.sitting": "Configura Sedersi",
"config.sitting.description": "Configura l'abilità di sedersi, sul server ogni giocatore può avere la propria configurazione quando usano la mod.",
"config.sitting.enabled": "Attivato",
"config.sitting.enabled.description": "Attiva o disattiva l'abilità di sedersi.",
"config.sitting.hand-sitting": "Siediti con la mano",
"config.sitting.hand-sitting.description": "Attiva l'abilità di sedersi usando l'interazione della mano.",
"config.sitting.hand.main": "Mano principale",
"config.sitting.hand.main.description": "mano principale",
"config.sitting.hand.off": "Seconda mano",
"config.sitting.hand.off.description": "seconda mano",
"config.sitting.hand.description": "Configura %s impostazioni per sedersi.",
"config.sitting.hand.requirement": "Requisiti per Sedersi",
"config.sitting.hand.requirement.description": "I requisiti per sedersi. Ad esempio, se EMPTY, la mano deve essere libera",
"config.sitting.hand.requirement.description.none": "Nessun requisito per sedersi.",
"config.sitting.hand.requirement.description.empty": "La mano deve essere libera per sedersi.",
"config.sitting.hand.requirement.description.filter": "La mano può farti sedere solo se il blocco è in un filtro.",
"config.sitting.hand.filter": "Filtro",
"config.sitting.hand.filter.description": "La lista di oggetti nel filtro della mano.",
"config.sitting.hand.filter.block": "Blocchi",
"config.sitting.hand.filter.block.description": "Filtro default per i blocchi.",
"config.sitting.hand.filter.food": "Cibi",
"config.sitting.hand.filter.food.description": "Il filtro dei cibi default.",
"config.sitting.hand.filter.usable": "Usabili",
"config.sitting.hand.filter.usable.description": "La lista del filtro Usabili. (Tridenti, Scudi, Archi)",
"config.sitting.hand.filter.custom-items": "Oggetti custom",
"config.sitting.hand.filter.custom-items.description": "Una lista degli oggetti custom da aggiungere al filtro.",
"config.sitting.hand.filter.custom-tags": "Tag Custom",
"config.sitting.hand.filter.custom-tags.description": "Una lista di tag di oggetti custom da aggiungere al filtro.",
"sit!.chat.toggle_sit": "%s Sedersi!",
"sit!.chat.toggle_sit.on": "Attivato",
"sit!.chat.toggle_sit.off": "Disattivato",
"sit!.chat.unsupported": "Sit! non è disponibile in questo server.",
"sit!.chat.reloaded": "Ricaricata la configurazione!",
"sit!.chat.purged": "Rimosse tutte le entità Sit! caricate! %s",
"sit!.chat.purged.total": "(%s rimosso)",
"key.sit!.toggle": "Attiva Sedersi",
"key.sit!.sit": "Siediti",
"key.sit!.config": "Apri Impostazioni",
"sit!.screen.config": "Impostazioni Sit!",
"sit!.gui.button.file": "Apri File",
"sit!.gui.button.folder": "Apri Cartella",
"sit!.gui.button.reset": "Resetta",
"sit!.gui.button.issues": "Problemi",
"sit!.gui.button.donate": "Dona",
"sit!.gui.button.revert": "Annulla i Cambiamenti",
"sit!.gui.button.save": "Salva ed Esci",
"sit!.gui.button.website": "Sito Web",
"sit!.console.connected": "Connesso al server Sit!: %s",
"sit!.console.player_settings": "Ricevute impostazioni custom da %s!",
"modmenu.descriptionTranslation.sit-oth3r": "Aggiunge l'abilità di sedersi a minecraft! infiniti modi di personalizzare le restrizioni della mano e dei blocchi su cui puoi sederti.\nI giocatori possono avere le loro impostazioni per sedersi quando usano un client Sit! nel server!"
}

View file

@ -0,0 +1,83 @@
{
"category.sit!": "Sit!",
"config.entry.exclusion": "Coloque um `!` antes de uma entrada para excluí-la!",
"config.entry.example": "Exemplo de entrada: %s",
"config.server": "Configuração do Servidor",
"config.server.description": "Configure as configurações do lado do servidor.",
"config.server.lang": "Idioma",
"config.server.lang.description": "A linguagem utilizada para o mod Sit!",
"config.server.keep-active": "Manter Ativo",
"config.server.keep-active.description": "Ativa ou desativa se a entidade Sit! deve permanecer, mesmo que o jogador ou o servidor esteja offline. \nQuando desativado, o jogador não estará sentado ao fazer login novamente.",
"config.server.sit-while-seated": "Sentar Enquanto Está Sentado",
"config.server.sit-while-seated.description": "Ativa ou desativa a capacidade de sentar em outro bloco Sit! enquanto já está sentado.",
"config.server.preset-blocks": "Blocos predefinidos",
"config.server.preset-blocks.description": "Ativa ou desativa os blocos Sit! padrão.",
"config.server.preset-blocks.stairs": "Escadas",
"config.server.preset-blocks.slabs": "Lajes",
"config.server.preset-blocks.carpets": "Tapetes",
"config.server.preset-blocks.full-blocks": "Blocos Inteiros",
"config.server.custom-enabled": "Customizado",
"config.server.custom-enabled.description": "Ativa ou desativa o uso de blocos personalizados para sentar.",
"config.server.custom-blocks": "Blocos Personalizados",
"config.server.custom-blocks.description": "A lista de blocos personalizados para sentar.",
"config.server.custom-block.block-ids": "IDs dos Blocos",
"config.server.custom-block.block-ids.description": "O(s) ID(s) do bloco para o bloco personalizado de sentar.",
"config.server.custom-block.block-tags": "Tags dos Blocos",
"config.server.custom-block.block-tags.description": "A(s) tag(s) do bloco para o bloco personalizado de sentar.",
"config.server.custom-block.blockstates": "Estados do bloco",
"config.server.custom-block.blockstates.description": "Os estados do bloco que o bloco deve ter para ser um bloco personalizado de sentar.",
"config.server.custom-block.sitting-height": "Altura de Sentar",
"config.server.custom-block.sitting-height.description": "A altura de sentar do jogador para o bloco personalizado.",
"config.server.blacklisted-blocks": "Blocos Bloqueados",
"config.server.blacklisted-blocks.description": "A lista de blocos nos quais não é permitido sentar.",
"config.sitting": "Configuração de Sentar",
"config.sitting.description": "Configura a habilidade de sentar; no servidor, cada jogador pode ter sua própria configuração de sentar ao usar o mod.",
"config.sitting.enabled": "Habilitado",
"config.sitting.enabled.description": "Alterna a capacidade de sentar.",
"config.sitting.hand-sitting": "Sentar com as Mãos",
"config.sitting.hand-sitting.description": "Alterna a capacidade de sentar usando interações com a mão.",
"config.sitting.hand.main": "Mão Principal",
"config.sitting.hand.main.description": "mão principal",
"config.sitting.hand.off": "Mão Secundária",
"config.sitting.hand.off.description": "mão secundária",
"config.sitting.hand.description": "Configura as configurações de sentar do %s.",
"config.sitting.hand.requirement": "Requisito para Sentar",
"config.sitting.hand.requirement.description": "A exigência da mão para sentar. Por exemplo, se for VAZIA, a mão deve estar vazia.",
"config.sitting.hand.requirement.description.none": "Nenhum requisito para sentar.",
"config.sitting.hand.requirement.description.empty": "A mão tem que estar vazia para sentar.",
"config.sitting.hand.requirement.description.filter": "A mão só pode sentar se o item na mão corresponder a um dos filtros.",
"config.sitting.hand.filter": "Filtro",
"config.sitting.hand.filter.description": "A lista de itens para a exigência de filtro da mão.",
"config.sitting.hand.filter.block": "Blocos",
"config.sitting.hand.filter.block.description": "O filtro padrão dos blocos.",
"config.sitting.hand.filter.food": "Alimentos",
"config.sitting.hand.filter.food.description": "O filtro padrão dos alimentos.",
"config.sitting.hand.filter.usable": "Usáveis",
"config.sitting.hand.filter.usable.description": "O filtro padrão dos itens utilizáveis. (Tridentes, Escudos, Arcos)",
"config.sitting.hand.filter.custom-items": "Itens Customizados",
"config.sitting.hand.filter.custom-items.description": "Uma lista de itens customizados para adicionar ao filtro.",
"config.sitting.hand.filter.custom-tags": "Tags Customizadas",
"config.sitting.hand.filter.custom-tags.description": "Uma lista de tags de itens customizados para adicionar ao filtro.",
"sit!.chat.toggle_sit": "%s Sentado!",
"sit!.chat.toggle_sit.on": "Habilitado",
"sit!.chat.toggle_sit.off": "Desabilitado",
"sit!.chat.unsupported": "Sit! não está disponível neste servidor.",
"sit!.chat.reloaded": "Configuração recarregada!",
"sit!.chat.purged": "Eliminou todas as entidades Sit! carregadas! %s",
"sit!.chat.purged.total": "(%s removido)",
"key.sit!.toggle": "Alternar Sentar",
"key.sit!.sit": "Sentar",
"key.sit!.config": "Abrir Configuração",
"sit!.screen.config": "Configuração do Sit!",
"sit!.gui.button.file": "Abrir Arquivo",
"sit!.gui.button.folder": "Abrir Pasta",
"sit!.gui.button.reset": "Reiniciar",
"sit!.gui.button.issues": "Problemas",
"sit!.gui.button.donate": "Doar",
"sit!.gui.button.revert": "Reverter Alterações",
"sit!.gui.button.save": "Salvar e Fechar",
"sit!.gui.button.website": "Website",
"sit!.console.connected": "Conectado ao servidor Sit!: %s",
"sit!.console.player_settings": "Recebidas configurações de sentar personalizadas de %s!",
"modmenu.descriptionTranslation.sit-oth3r": "Adiciona a função de sentar ao Minecraft! Personalização infinita para restrições de mão e blocos onde é possível sentar. Os jogadores podem ter suas próprias configurações de sentar ao usar o cliente Sit! no servidor!"
}

View file

@ -0,0 +1,83 @@
{
"category.sit!": "Sit!",
"config.entry.exclusion": "Bir girdiyi hariç tutmak için önüne '!' koyun!",
"config.entry.example": "Girdi örneği: %s",
"config.server": "Sunucu Ayarları",
"config.server.description": "Sunucu tarafında ayarları yapılandırır.",
"config.server.lang": "Dil",
"config.server.lang.description": "Sit! modu için kullanılan dil.",
"config.server.keep-active": "Aktif Tut",
"config.server.keep-active.description": "Oyuncu / sunucu offline olduğunda, varlığın aynı halde kalıp kalmayacağını açar kapatır.\nYanlış olarak ayarlandığında, oyuncu giriş yaptığında oturuyor halde olmayacaktır.",
"config.server.sit-while-seated": "Binerken otur",
"config.server.sit-while-seated.description": "Halihazırda oturuyorken başka bir Sit! bloğuna oturma özelliğini açar kapatır.",
"config.server.preset-blocks": "Önceden Ayarlı Bloklar",
"config.server.preset-blocks.description": "Varsayılan Sit! bloklarını açar kapatır.",
"config.server.preset-blocks.stairs": "Merdivenler",
"config.server.preset-blocks.slabs": "Basamaklar",
"config.server.preset-blocks.carpets": "Halılar",
"config.server.preset-blocks.full-blocks": "Tam Bloklar",
"config.server.custom-enabled": "Özel",
"config.server.custom-enabled.description": "Oturma için özel blokların kullanımını açar kapatır.",
"config.server.custom-blocks": "Özel Bloklar",
"config.server.custom-blocks.description": "Özel oturma bloklarının listesi.",
"config.server.custom-block.block-ids": "Blok ID'leri",
"config.server.custom-block.block-ids.description": "Özel oturma bloğu için blok id(leri).",
"config.server.custom-block.block-tags": "Blok Etiketleri",
"config.server.custom-block.block-tags.description": "Özel oturma bloğu için blok etiket(leri).",
"config.server.custom-block.blockstates": "Blok durumu",
"config.server.custom-block.blockstates.description": "Bloğun özel oturma bloğu olması gerektiğini belirten blok durumu.",
"config.server.custom-block.sitting-height": "Oturma Yüksekliği",
"config.server.custom-block.sitting-height.description": "Özel blok için oyuncunun oturma yüksekliği.",
"config.server.blacklisted-blocks": "Karalistedeki Bloklar",
"config.server.blacklisted-blocks.description": "Oturulmasına izin verilmeyen bloklar listesi.",
"config.sitting": "Oturma Ayarları",
"config.sitting.description": "Oturma özelliğini yapılandırır, sunucuda her oyuncu modu kullandığında kendi oturma yapılandırmasına sahip olabilir.",
"config.sitting.enabled": "Aktif",
"config.sitting.enabled.description": "Oturma özelliğini açar kapatır.",
"config.sitting.hand-sitting": "El ile Oturma",
"config.sitting.hand-sitting.description": "El etkileşimlerini kullanarak oturma özelliğini açar kapatır.",
"config.sitting.hand.main": "Ana El",
"config.sitting.hand.main.description": "oyuncunun kullandığı ana el",
"config.sitting.hand.off": "Diğer El",
"config.sitting.hand.off.description": "oyuncunun kullandığı diğer el",
"config.sitting.hand.description": "%s oturma ayarlarını ayarlar.",
"config.sitting.hand.requirement": "Oturma Gereksinimi",
"config.sitting.hand.requirement.description": "Oturmak için el gereksinimi. Ör: eğer BOŞ ise oturmak için elin boş olması gerek",
"config.sitting.hand.requirement.description.none": "Oturmak için gereksinim yok.",
"config.sitting.hand.requirement.description.empty": "Oturmak için elin boş olması gerek.",
"config.sitting.hand.requirement.description.filter": "El ile oturmak için, eldeki öğe filtrelerden biriyle eşleşmelidir.",
"config.sitting.hand.filter": "Filtre",
"config.sitting.hand.filter.description": "El gereksinimi için filtrelenecek eşyaların listesi.",
"config.sitting.hand.filter.block": "Bloklar",
"config.sitting.hand.filter.block.description": "Blokların varsayılan filtresi.",
"config.sitting.hand.filter.food": "Yiyecekler",
"config.sitting.hand.filter.food.description": "Yiyeceklerin varsayılan filtresi.",
"config.sitting.hand.filter.usable": "Kullanılabilenler",
"config.sitting.hand.filter.usable.description": "Kullanılabilenlerin varsayılan listesi. (Mızraklar, Kalkanlar, Yaylar)",
"config.sitting.hand.filter.custom-items": "Özel Eşyalar",
"config.sitting.hand.filter.custom-items.description": "Filtreye eklenecek özel eşyaların listesi.",
"config.sitting.hand.filter.custom-tags": "Özel Etiketler",
"config.sitting.hand.filter.custom-tags.description": "Filtreye eklenecek özel etiketlerin listesi.",
"sit!.chat.toggle_sit": "%s oturuyor!",
"sit!.chat.toggle_sit.on": "Aktif",
"sit!.chat.toggle_sit.off": "Pasif",
"sit!.chat.unsupported": "Sunucuda Sit! modu mevcut değil.",
"sit!.chat.reloaded": "Ayarlamalar yeniden yüklendi!",
"sit!.chat.purged": "Yüklenmiş bütün Sit! canlıları yok edildi! %s",
"sit!.chat.purged.total": "(%s kaldırıldı)",
"key.sit!.toggle": "Oturmayı Aç / Kapat",
"key.sit!.sit": "Otur",
"key.sit!.config": "Ayarlandırmaları aç",
"sit!.screen.config": "Sit! Ayarlandırmaları",
"sit!.gui.button.file": "Dosya Aç",
"sit!.gui.button.folder": "Klasörü Aç",
"sit!.gui.button.reset": "Sıfırla",
"sit!.gui.button.issues": "Sorunlar",
"sit!.gui.button.donate": "Bağış yap",
"sit!.gui.button.revert": "Değişiklikleri Geri Al",
"sit!.gui.button.save": "Kaydet ve Kapat",
"sit!.gui.button.website": "İnternet Sitesi",
"sit!.console.connected": "Sit! sunucusuna bağlanıldı: %s",
"sit!.console.player_settings": "Özel oturma ayarları %s alındı!",
"modmenu.descriptionTranslation.sit-oth3r": "Minecraft'a oturmayı ekler! El kısıtlamaları ve oturulabilir bloklar için sınırsız özelleştirme.\nOyuncular Sit! modunu kendi client'inde kullanırken sunucuda kendi oturma ayarlarına sahip olabilirler!"
}

View file

@ -0,0 +1,83 @@
{
"category.sit!": "Sit!",
"config.entry.exclusion": "在條目前方加上 `!` 以排除它!",
"config.entry.example": "條目範例: %s",
"config.server": "伺服器設定",
"config.server.description": "設定伺服器端的設定。",
"config.server.lang": "語言",
"config.server.lang.description": "Sit! 模組使用的語言。",
"config.server.keep-active": "保持活動",
"config.server.keep-active.description": "切換坐下! 實體是否應該保留,即使玩家/伺服器離線。 \n 當設定為 false 時,玩家在重新登入時將不會坐下。",
"config.server.sit-while-seated": "坐下時坐下",
"config.server.sit-while-seated.description": "切換在已經坐下的情況下,是否能夠坐在另一個坐下! 方塊上的能力。",
"config.server.preset-blocks": "預設方塊",
"config.server.preset-blocks.description": "預設 Sit! 方塊的切換。",
"config.server.preset-blocks.stairs": "階梯",
"config.server.preset-blocks.slabs": "半磚",
"config.server.preset-blocks.carpets": "地毯",
"config.server.preset-blocks.full-blocks": "完整方塊",
"config.server.custom-enabled": "自訂",
"config.server.custom-enabled.description": "切換是否使用自訂方塊來坐下。",
"config.server.custom-blocks": "自訂方塊",
"config.server.custom-blocks.description": "自訂坐下方塊的清單。",
"config.server.custom-block.block-ids": "方塊 ID",
"config.server.custom-block.block-ids.description": "自訂坐下方塊的方塊 ID。",
"config.server.custom-block.block-tags": "方塊標籤",
"config.server.custom-block.block-tags.description": "自訂坐下方塊的方塊標籤。",
"config.server.custom-block.blockstates": "方塊狀態",
"config.server.custom-block.blockstates.description": "方塊必須具備的方塊狀態,才能成為自訂坐下方塊。",
"config.server.custom-block.sitting-height": "坐下高度",
"config.server.custom-block.sitting-height.description": "自訂方塊的玩家坐下高度。",
"config.server.blacklisted-blocks": "黑名單方塊",
"config.server.blacklisted-blocks.description": "不允許坐下的方塊清單。",
"config.sitting": "坐下設定",
"config.sitting.description": "設定坐下能力,在伺服器上,每個玩家在使用模組時都可以擁有自己的坐下設定。",
"config.sitting.enabled": "啟用",
"config.sitting.enabled.description": "切換坐下能力。",
"config.sitting.hand-sitting": "手持坐下",
"config.sitting.hand-sitting.description": "切換使用手部互動來坐下的能力。",
"config.sitting.hand.main": "慣用手",
"config.sitting.hand.main.description": "慣用手",
"config.sitting.hand.off": "非慣用手",
"config.sitting.hand.off.description": "非慣用手",
"config.sitting.hand.description": "設定 %s 坐下設定。",
"config.sitting.hand.requirement": "坐下需求",
"config.sitting.hand.requirement.description": "坐下的手部需求。例如,如果設定為 EMPTY則手部必須是空的",
"config.sitting.hand.requirement.description.none": "沒有坐下需求。",
"config.sitting.hand.requirement.description.empty": "手部必須是空的才能坐下。",
"config.sitting.hand.requirement.description.filter": "只有當手持的物品符合其中一個篩選器時,才能坐下。",
"config.sitting.hand.filter": "篩選器",
"config.sitting.hand.filter.description": "篩選器手部需求的物品清單。",
"config.sitting.hand.filter.block": "方塊",
"config.sitting.hand.filter.block.description": "方塊的預設篩選器。",
"config.sitting.hand.filter.food": "食物",
"config.sitting.hand.filter.food.description": "食物的預設篩選器。",
"config.sitting.hand.filter.usable": "可使用物品",
"config.sitting.hand.filter.usable.description": "可使用物品的預設篩選器。(三叉戟、盾牌、弓)",
"config.sitting.hand.filter.custom-items": "自訂物品",
"config.sitting.hand.filter.custom-items.description": "要新增到篩選器的自訂物品清單。",
"config.sitting.hand.filter.custom-tags": "自訂標籤",
"config.sitting.hand.filter.custom-tags.description": "要新增到篩選器的自訂物品標籤清單。",
"sit!.chat.toggle_sit": "%s 坐下!",
"sit!.chat.toggle_sit.on": "已啟用",
"sit!.chat.toggle_sit.off": "已停用",
"sit!.chat.unsupported": "此伺服器不支援坐下! 功能。",
"sit!.chat.reloaded": "設定已重新載入!",
"sit!.chat.purged": "已清除所有已載入的 Sit! 實體! %s",
"sit!.chat.purged.total": "(已移除 %s 個)",
"key.sit!.toggle": "調整設定",
"key.sit!.sit": "坐下",
"key.sit!.config": "開啟設定",
"sit!.screen.config": "坐下! 設定",
"sit!.gui.button.file": "開啟檔案",
"sit!.gui.button.folder": "開啟資料夾",
"sit!.gui.button.reset": "重置",
"sit!.gui.button.issues": "問題",
"sit!.gui.button.donate": "贊助",
"sit!.gui.button.revert": "還原變更",
"sit!.gui.button.save": "儲存並關閉",
"sit!.gui.button.website": "網站",
"sit!.console.connected": "已連線至 Sit! 伺服器: %s",
"sit!.console.player_settings": "已從 %s 收到自訂坐下設定!",
"modmenu.descriptionTranslation.sit-oth3r": "在 Minecraft 中加入坐下的動作!可自訂無盡的動作限制和可坐下的方塊。\n當玩家在伺服器上使用 Sit! 用戶端時,可以擁有自己的坐下設定!"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,51 +0,0 @@
{
"config.sit.empty": "Empty",
"config.sit.restrictive": "Restrictive",
"config.sit.none": "None",
"config.sit.category.general": "General",
"config.sit.category.general.tooltip": "General settings",
"config.sit.category.main_hand": "Main Hand",
"config.sit.category.main_hand.tooltip": "Main hand settings",
"config.sit.category.off_hand": "Off Hand",
"config.sit.category.off_hand.tooltip": "Off hand settings",
"config.sit.general.keep_active": "Keep Active",
"config.sit.general.keep_active.description": "Keeps the entities active even when logging off / shutting down",
"config.sit.general.sittable": "Sittable Blocks",
"config.sit.general.sittable.description": "Toggle the ability to sit on different block types.",
"config.sit.general.sit_while_seated": "Sit While Seated",
"config.sit.general.sit_while_seated.description": "Toggle the ability to sit on other blocks while already being seated on one.",
"config.sit.general.sittable.stairs": "Stairs",
"config.sit.general.sittable.slabs": "Slabs",
"config.sit.general.sittable.carpets": "Carpets",
"config.sit.general.sittable.full_blocks": "Full Blocks",
"config.sit.general.sittable.custom": "Custom",
"config.sit.general.sittable.custom.description": "Enables adding custom blocks to sit on.",
"config.sit.general.sittable_blocks": "Custom Sittable Blocks",
"config.sit.general.sittable_blocks.description": "Add custom sittable blocks!",
"config.sit.general.sittable_blocks.description_2": "Example: %s",
"config.sit.general.sittable_blocks.description_4": "First entry: custom block",
"config.sit.general.sittable_blocks.description_5": "Second entry: sitting height (number from 0-1 eg 0.52)",
"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",
"config.sit.hand.requirements.description_3": "Restrictive = set restrictions for hand state",
"config.sit.hand.requirements.description_4": "None = can sit whenever",
"config.sit.hand.restrictions": "Restrictions",
"config.sit.hand.restrictions.description": "Toggle preset hand restrictions for sitting.",
"config.sit.hand.restrictions.blocks": "Blocks",
"config.sit.hand.restrictions.food": "Food",
"config.sit.hand.restrictions.usable": "Usable",
"config.sit.hand.restrictions.usable.description": "eg. bows, tridents, shield",
"config.sit.hand.whitelist": "Whitelist",
"config.sit.hand.whitelist.description": "Make a custom whitelist for items that the player can use to sit with.",
"config.sit.hand.blacklist": "Blacklist",
"config.sit.hand.blacklist.description": "Make a custom blacklist for items that the player can't use to sit with.",
"config.sit.hand.list.description": "Example: ",
"config.sit.hand.list.description_2": "\"minecraft:torch\"",
"key.sit.command.reloaded": "Reloaded the config!",
"key.sit.command.purged": "Purged all active chair entities!"
}

View file

@ -1,51 +0,0 @@
{
"config.sit.empty": "Vacía",
"config.sit.restrictive": "Restrictiva",
"config.sit.none": "Ninguna",
"config.sit.category.general": "General",
"config.sit.category.general.tooltip": "Configuración general",
"config.sit.category.main_hand": "Mano principal",
"config.sit.category.main_hand.tooltip": "Ajustes de la mano principal",
"config.sit.category.off_hand": "Mano secundaria",
"config.sit.category.off_hand.tooltip": "Ajustes de la mano secundaria",
"config.sit.general.keep_active": "Mantener activo",
"config.sit.general.keep_active.description": "Mantener entidades activas incluso al cerrar sesión o al apagar el servidor",
"config.sit.general.sittable": "Bloques aptos para sentarse",
"config.sit.general.sittable.description": "Activa/Desactiva los tipos de bloques en los que se puede sentar.",
"config.sit.general.sit_while_seated": "Cambiar de asiento",
"config.sit.general.sit_while_seated.description": "Permite cambiar de asiento estando sentado.",
"config.sit.general.sittable.stairs": "Escaleras",
"config.sit.general.sittable.slabs": "Losas",
"config.sit.general.sittable.carpets": "Alfombras",
"config.sit.general.sittable.full_blocks": "Bloques Enteros",
"config.sit.general.sittable.custom": "Personalizados",
"config.sit.general.sittable.custom.description": "Permitir sentarse en los bloques personalizados.",
"config.sit.general.sittable_blocks": "Bloques Personalizados",
"config.sit.general.sittable_blocks.description": "¡Añadir bloques personalizados para sentarse!",
"config.sit.general.sittable_blocks.description_2": "Ejemplo: %s",
"config.sit.general.sittable_blocks.description_4": "Primera entrada: bloque personalizado",
"config.sit.general.sittable_blocks.description_5": "Segunda entrada: altura del asiento (número entre 0 y 1, por ejemplo, 0.52)",
"config.sit.general.sittable_blocks.description_6": "Tercera entrada: Altura del bloque (en que altura del bloque el jugador aparece al pararse)",
"config.sit.general.sittable_blocks.description_7": "Cuarta entrada (opcional): estado del bloque requerido para sentarse (Usa una exclamación (!) para excluir estados)",
"config.sit.general.sittable_blocks.description_8": "¡Separar diferentes entradas con \"|\"!",
"config.sit.hand": "Configuración de mano",
"config.sit.hand.requirements": "Requisitos",
"config.sit.hand.requirements.description": "Requisitos de la mano para sentarse.",
"config.sit.hand.requirements.description_2": "Vacía = la mano debe estar vacía",
"config.sit.hand.requirements.description_3": "Restrictiva = establecer restricciones para el estado de la mano",
"config.sit.hand.requirements.description_4": "Ninguna = sin restricciones ni requisitos",
"config.sit.hand.restrictions": "Restricciones",
"config.sit.hand.restrictions.description": "Activa/Desactiva las restricciones preestablecidas de la mano para sentarse.",
"config.sit.hand.restrictions.blocks": "Bloques",
"config.sit.hand.restrictions.food": "Comida",
"config.sit.hand.restrictions.usable": "Utilizable",
"config.sit.hand.restrictions.usable.description": "Ejemplos: arcos, tridentes, escudo",
"config.sit.hand.whitelist": "Lista blanca",
"config.sit.hand.whitelist.description": "Crea una lista blanca personalizada para objetos con los que el jugador puede sentarse.",
"config.sit.hand.blacklist": "Lista negra",
"config.sit.hand.blacklist.description": "Crea una lista negra personalizada para objetos con los que el jugador no puede sentarse.",
"config.sit.hand.list.description": "Ejemplo: ",
"config.sit.hand.list.description_2": "\"minecraft:torch\"",
"key.sit.command.reloaded": "¡Configuración recargada!",
"key.sit.command.purged": "¡Se purgaron a todas las entidades activas de sillas!"
}

View file

@ -1,51 +0,0 @@
{
"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": "Пример: %s",
"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": "Настройки руки",
"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,9 +1,9 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"id": "oth3r-sit", "id": "sit-oth3r",
"version": "${version}", "version": "${version}",
"name": "Sit!", "name": "Sit!",
"description": "Adds sitting to minecraft! Endless customizability for hand restrictions and sittable blocks.", "description": "Adds sitting to minecraft! Endless customizability for hand restrictions and sittable blocks.\n Players can have their own sitting settings when using the Sit! client on the server!",
"authors": [ "authors": [
"Oth3r" "Oth3r"
], ],
@ -12,7 +12,7 @@
"sources": "https://github.com/Oth3r/Sit" "sources": "https://github.com/Oth3r/Sit"
}, },
"license": "LGPL-3.0-only", "license": "LGPL-3.0-only",
"icon": "assets/sit/icon.png", "icon": "assets/sit-oth3r/icon.png",
"environment": "*", "environment": "*",
"entrypoints": { "entrypoints": {
"main": [ "main": [
@ -22,17 +22,19 @@
"one.oth3r.sit.SitClient" "one.oth3r.sit.SitClient"
], ],
"modmenu": [ "modmenu": [
"one.oth3r.sit.ModMenu" "one.oth3r.sit.screen.ModMenu"
] ]
}, },
"depends": { "depends": {
"fabricloader": ">=0.14.21", "fabricloader": ">=0.14.21",
"minecraft": "=${minecraft_version}", "minecraft": ">=${min_minecraft_version} <=${minecraft_version}",
"java": ">=17", "fabric": "*"
"fabric-api": "*"
}, },
"suggests": { "suggests": {
"yet-another-config-lib": "*",
"modmenu": "*" "modmenu": "*"
} },
"accessWidener": "sit!.accesswidener",
"mixins": [
"sit!.mixins.json"
]
} }

View file

@ -0,0 +1,3 @@
accessWidener v2 named
accessible field net/minecraft/state/State PROPERTY_MAP_PRINTER Ljava/util/function/Function;

View file

@ -0,0 +1,13 @@
{
"required": true,
"minVersion": "0.8",
"package": "one.oth3r.sit.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"ReloadCommandMixin",
"TextDisplayDismountMixin"
],
"injectors": {
"defaultRequire": 1
}
}