Merge branch '1.20.4' into 1.20.1

# Conflicts:
#	gradle.properties
#	src/main/java/one/oth3r/sit/Events.java
This commit is contained in:
Oth3r 2024-12-21 12:47:08 -06:00
commit 1298e1940a
57 changed files with 3083 additions and 1146 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")
}

9
changelog.md Normal file
View file

@ -0,0 +1,9 @@
# v1.2.1
Quick bugfix update. :)
### block interaction checker
now it actually works (based on config sadly)
* added `interaction-blocks` to the server config, any block added to this list will block sitting with the hand
### other changes
* added refreshes to the config files on startup and reload to write missing parts to disk
* bumped the `server-config.json` version
* fixed `usable` hand filter not working

View file

@ -4,16 +4,16 @@ org.gradle.parallel=true
# Fabric Properties # Fabric Properties
# check these on https://fabricmc.net/develop # check these on https://fabricmc.net/develop
min_minecraft_version=1.20.1
minecraft_version=1.20.1 minecraft_version=1.20.1
yarn_mappings=1.20.1+build.10 yarn_mappings=1.20.1+build.10
loader_version=0.14.24 loader_version=0.14.24
# Mod Properties # Mod Properties
mod_version=1.1.10+1.20.1 mod_version=1.2.1+1.20.1
maven_group=one.oth3r maven_group=one.oth3r
archives_base_name=sit! file_name=sit!
# Dependencies # Dependencies
fabric_version=0.91.0+1.20.1 fabric_version=0.91.0+1.20.1
modmenu_version=7.0.0 modmenu_version=7.0.0
yacl_version=3.4.1+1.20.1-fabric

View file

@ -1,279 +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"));
}
}
}
entity.updatePositionAndAngles(entity.getX(),entity.getY(),entity.getZ(),0,0);
entity.setBoundingBox(Box.of(Vec3d.of(pos),1.5,hitBoxY,1.5));
//change pitch based on if player is sitting below block height or not
if (entity.getY() <= pos.getY()+.35) entity.setPitch(90); // 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,35 +0,0 @@
package one.oth3r.sit;
import io.netty.buffer.ByteBuf;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.Identifier;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
public class PacketBuilder {
public static final String SETTINGS = "settings_v1.0";
private final String message;
private final PacketByteBuf packetByteBuf = PacketByteBufs.create();
public PacketBuilder(ByteBuf buf) {
// Read any data sent in the packet
message = buf.toString(StandardCharsets.UTF_8);
packetByteBuf.writeBytes(buf);
}
public PacketBuilder(String message) {
this.message = message;
packetByteBuf.writeBytes(ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)).array());
}
public static Identifier getIdentifier() {
// only 1 packet rn
return new Identifier(Sit.MOD_ID, SETTINGS);
}
public void send() {
ClientPlayNetworking.send(getIdentifier(), packetByteBuf);
}
public String getMessage() {
return this.message;
}
}

View file

@ -1,54 +1,17 @@
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) // save the files to populate all missing config options
// inner stair offset & custom support for that ig FileData.saveFiles();
Config.load(); Events.registerCommon();
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,101 @@
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;
/// BLOCK STATE CHECKER
// 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,101 @@
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
file.update();
// load the file to the current object
loadFileData(file);
}
@NotNull
Class<T> getFileClass();
/**
* loads the data from the file object into the current object
* @param newFile the file to take the properties from
*/
void loadFileData(T newFile);
/**
* updates the file based on the version number of the current instance
*/
void update();
/**
* 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,110 @@
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();
}
}
/**
* saves all config files
*/
public static void saveFiles() {
getSittingConfig().save();
getServerConfig().save();
}
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 ArrayList<CustomBlock> INTERACTION_BLOCKS = new ArrayList<>(Arrays.asList(
new CustomBlock(new ArrayList<>(Arrays.asList("minecraft:crafter")),new ArrayList<>(Arrays.asList(
"#minecraft:shulker_boxes","#c:player_workstations/furnaces","#c:player_workstations/crafting_tables",
"#c:villager_job_sites","#minecraft:trapdoors","#c:chests")),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,390 @@
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.1;
@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;
@SerializedName("interaction-blocks")
private ArrayList<CustomBlock> interactionBlocks = FileData.Defaults.INTERACTION_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;
this.interactionBlocks = serverConfig.interactionBlocks;
}
public ServerConfig(Double version, String lang, boolean keepActive, boolean sitWhileSeated,
PresetBlocks presetBlocks, boolean customEnabled,
ArrayList<SittingBlock> sittingBlocks, ArrayList<CustomBlock> blacklistedBlocks,
ArrayList<CustomBlock> interactionBlocks) {
this.version = version;
this.lang = lang;
this.keepActive = keepActive;
this.sitWhileSeated = sitWhileSeated;
this.presetBlocks = presetBlocks;
this.customEnabled = customEnabled;
this.sittingBlocks = sittingBlocks;
this.blacklistedBlocks = blacklistedBlocks;
this.interactionBlocks = interactionBlocks;
}
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 ArrayList<CustomBlock> getInteractionBlocks() {
return interactionBlocks;
}
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() {
loadFileData(new ServerConfig());
}
@Override
public @NotNull Class<ServerConfig> getFileClass() {
return ServerConfig.class;
}
@Override
public void loadFileData(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 void update() {
/// update to 2.1, just a new list, nothing to change
if (version == 2.0) {
version = 2.1;
}
}
@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<>(), FileData.Defaults.INTERACTION_BLOCKS
);
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,98 @@
package one.oth3r.sit.file;
import com.google.gson.annotations.SerializedName;
import net.minecraft.util.Hand;
import one.oth3r.sit.utl.Data;
import org.jetbrains.annotations.NotNull;
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) {
loadFileData(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() {
loadFileData(new SittingConfig());
}
@Override
public @NotNull Class<SittingConfig> getFileClass() {
return SittingConfig.class;
}
@Override
public void loadFileData(SittingConfig newFile) {
this.version = newFile.version;
this.enabled = newFile.enabled;
this.handSitting = newFile.handSitting;
this.mainHand = newFile.mainHand;
this.offHand = newFile.offHand;
}
@Override
public void update() {}
@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,39 @@
package one.oth3r.sit.packet;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import one.oth3r.sit.utl.Data;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
public class PacketSender {
private final PacketByteBuf data;
private final PacketType type;
public PacketSender(PacketType type, String data) {
this.type = type;
this.data = PacketByteBufs.create()
.writeBytes(ByteBuffer.wrap(data.getBytes(StandardCharsets.UTF_8)));
}
public void sendToPlayer(ServerPlayerEntity player) {
ServerPlayNetworking.send(player,getIdentifier(type),data);
}
public void sendToServer() {
ClientPlayNetworking.send(getIdentifier(type),data);
}
public static Identifier getIdentifier(PacketType packetType) {
return new Identifier(Data.MOD_ID, packetType.getId());
}
public static String getPacketData(PacketByteBuf buf) {
return buf.toString(StandardCharsets.UTF_8);
}
}

View file

@ -0,0 +1,16 @@
package one.oth3r.sit.packet;
public enum PacketType {
RESPONSE("response_v1.0"),
SETTINGS("settings_v2.0");
final String id;
PacketType(String id) {
this.id = id;
}
public String 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,217 @@
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.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.PacketSender;
import one.oth3r.sit.packet.PacketType;
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() {
// server receiver is common
/// receiving the sitting setting payload
ServerPlayNetworking.registerGlobalReceiver(PacketSender.getIdentifier(PacketType.SETTINGS),
((server, player, handler, buf, responseSender) -> {
String packetData = PacketSender.getPacketData(buf);
server.execute(() -> {
// save the setting on the server for that player
FileData.setPlayerSetting(player,Utl.getGson().fromJson(packetData, SittingConfig.class));
// send the player back a response packet for confirmation
new PacketSender(PacketType.RESPONSE,PacketType.RESPONSE.getId()).sendToPlayer(player);
// log the receiving of the packet from the player
Data.LOGGER.info(Utl.lang("sit!.console.player_settings",player.getName().getString()).getString());
});
}));
}
private static void client() {
/// receiving the response packet from the server
ClientPlayNetworking.registerGlobalReceiver(PacketSender.getIdentifier(PacketType.RESPONSE),
((client, handler, buf, responseSender) -> {
String packetData = PacketSender.getPacketData(buf);
client.execute(() -> {
if (!Data.isSupportedServer()) {
Data.setSupportedServer(true);
Data.LOGGER.info(Utl.lang("sit!.console.connected",packetData).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,186 @@
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();
FileData.saveFiles();
}
/**
* 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,469 @@
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.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.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.PacketSender;
import one.oth3r.sit.packet.PacketType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
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() && !notUsable.contains(itemStack.getUseAction())) 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 && blockIsInList(config.getInteractionBlocks(), blockState)) {
return null;
}
// only if custom is enabled
if (config.isCustomEnabled()) {
// if the block is on the blacklist, false
if (blockIsInList(config.getBlacklistedBlocks(),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 blockstate is in the list provided
* @return
*/
public static boolean blockIsInList(ArrayList<CustomBlock> blockList, BlockState blockState) {
return blockList.stream().anyMatch(c -> c.isValid(blockState));
}
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()) {
new PacketSender(PacketType.SETTINGS, Utl.getGson().toJson(FileData.getSittingConfig())).sendToServer();
}
}
/**
* 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
}
}