complete rewrite

This commit is contained in:
Oth3r 2024-07-11 13:43:54 -05:00
commit 1316686d85
24 changed files with 1808 additions and 871 deletions

View file

@ -9,7 +9,7 @@ yarn_mappings=1.21+build.1
loader_version=0.15.11 loader_version=0.15.11
# Mod Properties # Mod Properties
mod_version=1.1.6+1.21 mod_version=1.2.0.0+1.21
maven_group=one.oth3r maven_group=one.oth3r
archives_base_name=sit! archives_base_name=sit!

View file

@ -1,270 +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.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.HandType;
import one.oth3r.sit.Utl.PlayerSettings;
import one.oth3r.sit.Utl.PlayerSettings.Setting;
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(HandType.main,player.getMainHandStack());
itemMap.put(HandType.off,player.getOffHandStack());
// if sneaking cant sit
if (player.isSneaking()) return false;
// for both hands
for (HandType type:HandType.values()) {
ItemStack targetStack = itemMap.get(type);
// if req is empty and the item isn't empty, false
if (PlayerSettings.getRequirement(player,type).equals(config.HandRequirement.empty) && !targetStack.isEmpty()) return false;
// if req is restrictive
if (PlayerSettings.getRequirement(player,type).equals(config.HandRequirement.restrictive)) {
// if item is in blacklist, false
if (checkList(PlayerSettings.getList(player,type,Setting.blacklist),targetStack)) return false;
// if item is NOT in whitelist
if (!checkList(PlayerSettings.getList(player,type,Setting.whitelist),targetStack)) {
// if block is restricted and items is block, false, ect
if (PlayerSettings.getToggle(player,type,Setting.block) && (targetStack.getItem() instanceof BlockItem)) return false;
if (PlayerSettings.getToggle(player,type,Setting.food) && food.contains(targetStack.getUseAction())) return false;
if (PlayerSettings.getToggle(player,type,Setting.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.isEmpty()) {
for (HashMap<String,Object> map:getCustomBlocks().values()) {
String blockID = Registries.BLOCK.getId(block).toString();
if (map.get("block").equals(blockID)) {
if (!map.containsKey("state")) return true;
String[] states = ((String) map.get("state")).split(",\\s*");
boolean matching = true;
for (String state:states) {
if (state.charAt(0) == '!') {
if (blockState.toString().contains(state.substring(1))) matching = false;
} else if (!blockState.toString().contains(state)) matching = false;
}
return matching;
}
}
}
return false;
}
public static boolean isAboveBlockheight(Entity entity) {
return entity.getPitch()<0;
}
public static void setEntity(BlockPos pos, World world, Entity entity) {
Block block = world.getBlockState(pos).getBlock();
entity.setCustomName(Text.of(Sit.ENTITY_NAME));
entity.setCustomNameVisible(false);
double hitBoxY = 0.5;
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+.47, pos.getZ() + 0.5, 0, 0);
entity.setInvulnerable(true);
if (block instanceof StairsBlock) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+.27, pos.getZ() + 0.5, 0, 0);
hitBoxY = 2;
}
if (block instanceof SlabBlock) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+.27, pos.getZ() + 0.5, 0, 0);
hitBoxY = 1;
}
if (block instanceof CarpetBlock) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()-.17, pos.getZ() + 0.5, 0, 0);
hitBoxY = 0.125;
}
if (world.getBlockState(pos).isFullCube(world,pos.add(0,1,0))) {
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+.78, pos.getZ() + 0.5, 0, 0);
hitBoxY = 2;
}
if (config.customOn && !config.customBlocks.isEmpty()) {
for (HashMap<String,Object> map:getCustomBlocks().values()) {
String blockID = Registries.BLOCK.getId(block).toString();
if (map.get("block").equals(blockID)) {
double input = Math.max(Math.min(Double.parseDouble((String) map.get("height")),1),0);
entity.updatePositionAndAngles(pos.getX() + 0.5, pos.getY()+input-.22, pos.getZ() + 0.5, 0, 0);
hitBoxY = Double.parseDouble((String) map.get("hitbox"));
}
}
}
//1.20.2 mounting pos change (shifts everything down by .25)
double oneTwentyTwo = .25;
entity.updatePositionAndAngles(entity.getX(),entity.getY()+oneTwentyTwo,entity.getZ(),0,0);
entity.setBoundingBox(Box.of(Vec3d.of(pos),1.5,hitBoxY,1.5));
//change pitch based on if player is sitting below block height or not
if (entity.getY() <= pos.getY()+.35+oneTwentyTwo) entity.setPitch(90); // below
else entity.setPitch(-90); // above
}
public static 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, PlayerSettings.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) -> {
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();
if (!checkLogic(player)) return ActionResult.PASS;
// todo interactions entity to make the hitbox?
// make the entity first before checking to make sure the blocks around are fine
DisplayEntity.TextDisplayEntity entity = new DisplayEntity.TextDisplayEntity(EntityType.TEXT_DISPLAY,player.getServerWorld());
setEntity(pos,world,entity);
if (checkBlocks(pos,world,isAboveBlockheight(entity))) {
if (entities.containsKey(player)) {
if (!config.sitWhileSeated) return ActionResult.PASS;
entities.get(player).setRemoved(Entity.RemovalReason.DISCARDED);
entities.remove(player);
}
player.getServerWorld().spawnEntity(entity);
player.startRiding(entity);
entities.put(player,entity);
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,194 +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;
public class ModMenu implements ModMenuApi {
private static MutableText lang(String key, Object... args) {
return Utl.lang("config."+key,args);
}
@Override
public ConfigScreenFactory<?> getModConfigScreenFactory() {
return parent -> YetAnotherConfigLib.createBuilder().save(config::save)
.title(Text.of("Sit!"))
.category(ConfigCategory.createBuilder()
.name(lang("category.general"))
.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())
.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("example",
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.withColor(TextColor.fromFormatting(Formatting.GRAY)))))
.append("\n\n").append(lang("general.sittable_blocks.description.2").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
.append("\n").append(lang("general.sittable_blocks.description.3").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.RED))))
.append("\n").append(lang("general.sittable_blocks.description.4").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
.append("\n").append(lang("general.sittable_blocks.description.5").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GOLD))))
.append("\n\n").append(lang("general.sittable_blocks.description.6").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"))
.option(Option.<config.HandRequirement>createBuilder()
.name(lang("hand.requirement"))
.description(OptionDescription.of(lang("hand.requirement.description")
.append("\n\n").append(lang("hand.requirement.description.2",lang("hand.requirement.empty")).styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
.append("\n").append(lang("hand.requirement.description.3",lang("hand.requirement.restrictive")).styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
.append("\n").append(lang("hand.requirement.description.4",lang("hand.requirement.none")).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 -> lang("hand.requirement."+v.toString())))
.build())
.group(OptionGroup.createBuilder()
.name(lang("hand.restriction"))
.description(OptionDescription.of(lang("hand.restriction.description")))
.option(Option.<Boolean>createBuilder()
.name(lang("hand.restriction.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.restriction.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.restriction.usable"))
.description(OptionDescription.of(lang("hand.restriction.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.restriction.whitelist"))
.description(OptionDescription.of(lang("hand.restriction.list.description")
.append("\n\n").append(lang("example",
Text.empty().append(Utl.Assets.LIST).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.restriction.blacklist"))
.description(OptionDescription.of(lang("hand.restriction.list.description")
.append("\n\n").append(lang("example",
Text.empty().append(Utl.Assets.LIST).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"))
.option(Option.<config.HandRequirement>createBuilder()
.name(lang("hand.requirement"))
.description(OptionDescription.of(lang("hand.requirement.description")
.append("\n\n").append(lang("hand.requirement.description.2",lang("hand.requirement.empty")).styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
.append("\n").append(lang("hand.requirement.description.3",lang("hand.requirement.restrictive")).styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
.append("\n").append(lang("hand.requirement.description.4",lang("hand.requirement.none")).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 -> lang("hand.requirement."+v.toString())))
.build())
.group(OptionGroup.createBuilder()
.name(lang("hand.restriction"))
.description(OptionDescription.of(lang("hand.restriction.description")))
.option(Option.<Boolean>createBuilder()
.name(lang("hand.restriction.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.restriction.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.restriction.usable"))
.description(OptionDescription.of(lang("hand.restriction.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.restriction.whitelist"))
.description(OptionDescription.of(lang("hand.restriction.list.description")
.append("\n\n").append(lang("example",
Text.empty().append(Utl.Assets.LIST).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.restriction.blacklist"))
.description(OptionDescription.of(lang("hand.restriction.list.description")
.append("\n\n").append(lang("example",
Text.empty().append(Utl.Assets.LIST).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);
}
}

View file

@ -5,10 +5,15 @@ import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.CommandManager;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
import one.oth3r.sit.file.Data;
import one.oth3r.sit.packet.SitPayloads;
import one.oth3r.sit.utl.Events;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -16,31 +21,29 @@ import java.lang.reflect.Type;
import java.util.HashMap; 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 final String MOD_ID = "oth3r-sit";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static String CONFIG_DIR = FabricLoader.getInstance().getConfigDir().toFile()+"/sit!/";
public static HashMap<ServerPlayerEntity, HashMap<String,String>> playerSettings = new HashMap<>(); public static HashMap<ServerPlayerEntity, HashMap<String,String>> playerSettings = new HashMap<>();
public static final String ENTITY_NAME = "-sit!-entity-"; public static final String ENTITY_NAME = "-sit!-entity-";
public static MinecraftServer server; public static MinecraftServer server;
public static CommandManager commandManager; public static CommandManager commandManager;
public static boolean isClient = false;
public static boolean client = false;
public static boolean singleplayer = false;
@Override @Override
public void onInitialize() { public void onInitialize() {
//todo future: Data.loadFiles(true);
// make it so it updates the sitting height and pos based on the block so if it changed while offline it still works (or if stair changes shape) Events.registerCommon();
// inner stair offset & custom support for that ig
config.load();
Events.register();
//PACKETS //PACKETS
ServerPlayNetworking.registerGlobalReceiver(PacketBuilder.getIdentifier(), PayloadTypeRegistry.playC2S().register(SitPayloads.SettingsPayload.ID, SitPayloads.SettingsPayload.CODEC);
(server, player, handler, buf, responseSender) -> { ServerPlayNetworking.registerGlobalReceiver(SitPayloads.SettingsPayload.ID,((payload, context) -> server.execute(() -> {
// copy to not throw errors
PacketBuilder packet = new PacketBuilder(buf.copy());
server.execute(() -> {
Type hashMapToken = new TypeToken<HashMap<String, Object>>() {}.getType(); Type hashMapToken = new TypeToken<HashMap<String, Object>>() {}.getType();
Gson gson = new GsonBuilder().disableHtmlEscaping().create(); Gson gson = new GsonBuilder().disableHtmlEscaping().create();
playerSettings.put(player,gson.fromJson(packet.getMessage(),hashMapToken)); playerSettings.put(context.player(),gson.fromJson(payload.value(),hashMapToken));
}); })));
});
} }
} }

View file

@ -1,25 +1,40 @@
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 net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import one.oth3r.sit.file.Data;
import one.oth3r.sit.packet.SitPayloads;
import one.oth3r.sit.utl.Utl;
public class SitClient implements ClientModInitializer { public class SitClient implements ClientModInitializer {
public static boolean inGame = false; private static boolean IN_GAME = false;
@Override @Override
public void onInitializeClient() { public void onInitializeClient() {
Sit.isClient = true; Sit.client = true;
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> { ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> {
inGame = true; IN_GAME = true;
if (client.isInSingleplayer()) Sit.singleplayer = true;
// send a data packet whenever joining a server // send a data packet whenever joining a server
client.execute(SitClient::sendPackets); sendSettingsPackets();
}); });
// reset inGame
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> inGame = false); // reset cashed things
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> {
IN_GAME = false;
if (Sit.singleplayer) {
// flip the single player switch
Sit.singleplayer = false;
} }
public static void sendPackets() { });
Gson gson = new GsonBuilder().disableHtmlEscaping().create(); }
new PacketBuilder(gson.toJson(Utl.PlayerSettings.getHandSettings())).send();
/**
* sends the settings packets to the server
*/
public static void sendSettingsPackets() {
if (IN_GAME) ClientPlayNetworking.send(new SitPayloads.SettingsPayload(Utl.getGson().toJson(Data.getHandConfig())));
} }
} }

View file

@ -1,74 +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 net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Utl {
public static MutableText lang(String key, Object... args) {
if (Sit.isClient) return Text.translatable(key, args);
else return LangReader.of(key, args).getTxT();
}
public enum HandType {
main,
off
}
public static class PlayerSettings {
public enum Setting {
requirement,
block,
food,
usable,
whitelist,
blacklist
}
/**
* Gets a HashMap of all player configurable settings.
* @return a map with player hand settings.
*/
public static HashMap<String,String> getHandSettings() {
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
HashMap<String,String> settings = new HashMap<>();
// main hand
settings.put("hand.main."+Setting.requirement,String.valueOf(config.mainReq));
settings.put("hand.main."+Setting.block,String.valueOf(config.mainBlock));
settings.put("hand.main."+Setting.food,String.valueOf(config.mainFood));
settings.put("hand.main."+Setting.usable,String.valueOf(config.mainUsable));
settings.put("hand.main."+Setting.whitelist,gson.toJson(config.mainWhitelist));
settings.put("hand.main."+Setting.blacklist,gson.toJson(config.mainBlacklist));
// copy but offhand
settings.put("hand.off."+Setting.requirement,String.valueOf(config.offReq));
settings.put("hand.off."+Setting.block,String.valueOf(config.offBlock));
settings.put("hand.off."+Setting.food,String.valueOf(config.offFood));
settings.put("hand.off."+Setting.usable,String.valueOf(config.offUsable));
settings.put("hand.off."+Setting.whitelist,gson.toJson(config.offWhitelist));
settings.put("hand.off."+Setting.blacklist,gson.toJson(config.offBlacklist));
return settings;
}
// returns specific items from the player config
public static config.HandRequirement getRequirement(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, Setting setting) {
Type listType = new TypeToken<ArrayList<String>>() {}.getType();
return new Gson().fromJson(Sit.playerSettings.get(player).get("hand."+type+"."+setting),listType);
}
public static boolean getToggle(ServerPlayerEntity player, HandType type, Setting setting) {
return Boolean.parseBoolean(Sit.playerSettings.get(player).get("hand."+type+"."+setting));
}
}
public static class Assets {
public static final String CUSTOM_BLOCKS = "\"minecraft:campfire|0.255|1|lit=false\"";
public static final String REQUIREMENT_OPTIONS = String.format("%s, %s, %s",
config.HandRequirement.empty,config.HandRequirement.restrictive,config.HandRequirement.none);
public static final String LIST = "\"minecraft:torch\"";
}
}

View file

@ -1,21 +1,20 @@
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.entity.EntityType;
import net.minecraft.entity.decoration.DisplayEntity;
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.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 net.minecraft.world.World; import one.oth3r.sit.utl.Logic;
import one.oth3r.sit.Sit;
import one.oth3r.sit.utl.Utl;
import one.oth3r.sit.file.Data;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -29,11 +28,13 @@ 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
@ -45,48 +46,37 @@ public class SitCommand {
String[] args = arg.split(" "); String[] args = arg.split(" ");
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(Utl.lang("msg.reloaded").getString()); Sit.LOGGER.info(Utl.lang("msg.reloaded").getString());
} }
return 1; return 1;
} }
if (args[0].equalsIgnoreCase("sit")) { if (args[0].equalsIgnoreCase("sit")) {
// todo make the command target the block that the player is looking at, if not looking at a block default to below
BlockPos pos = player.getBlockPos(); BlockPos pos = player.getBlockPos();
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);
} }
World world = player.getWorld();
// if already sitting, ignore // if already sitting, ignore
if (Events.entities.containsKey(player)) return 1; if (Data.getSitEntity(player) != null) return 1;
// make entity first to check the blocks
DisplayEntity.TextDisplayEntity entity = new DisplayEntity.TextDisplayEntity(EntityType.TEXT_DISPLAY,player.getServerWorld()); // try to make the player sit
Events.setEntity(pos,world,entity); Logic.sit(player,pos,null);
if (Events.checkBlocks(pos,world,Events.isAboveBlockheight(entity))) {
player.getServerWorld().spawnEntity(entity);
player.startRiding(entity);
Events.entities.put(player,entity);
return 1;
}
} }
if (args[0].equalsIgnoreCase("reload")) { if (args[0].equalsIgnoreCase("reload")) {
config.load(); Logic.reload();
player.sendMessage(Utl.lang("msg.reloaded").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN)))); player.sendMessage(Utl.lang("msg.reloaded").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))));
} }
if (args[0].equalsIgnoreCase("purgeChairEntities")) {
String cmd = "kill @e[type=minecraft:text_display,name=\""+Sit.ENTITY_NAME+"\"]"; if (args[0].equalsIgnoreCase("purgeChairEntities")) Utl.Entity.purge(player,true);
try {
ParseResults<ServerCommandSource> parse =
Sit.commandManager.getDispatcher().parse(cmd, player.getCommandSource());
Sit.commandManager.getDispatcher().execute(parse);
player.sendMessage(Utl.lang("msg.purged"));
} catch (CommandSyntaxException e) {
player.sendMessage(Utl.lang("msg.purged"));
e.printStackTrace();
}
}
return 1; return 1;
} }
} }

View file

@ -1,256 +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 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) {}
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 Utl.lang("config."+key, args).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");
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# "+ Utl.lang("config."+
"general.sittable_blocks.description")
.append("\n# ").append(lang("example",Utl.Assets.CUSTOM_BLOCKS))
.append("\n# ").append(lang("general.sittable_blocks.description.2"))
.append("\n# ").append(lang("general.sittable_blocks.description.3"))
.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")).getString());
file.write("\ncustom-blocks="+gson.toJson(customBlocks));
file.write("\n\n# "+lang("hand"));
file.write("\n# "+ Utl.lang("config."+
"hand.requirement.description")
.append("\n# ").append(lang("hand.requirement.description.2",HandRequirement.empty))
.append("\n# ").append(lang("hand.requirement.description.3",HandRequirement.restrictive))
.append("\n# ").append(lang("hand.requirement.description.4",HandRequirement.none)).getString());
file.write("\n# "+lang("hand.requirement.options",Utl.Assets.REQUIREMENT_OPTIONS));
file.write("\nhand.main.requirement=" + mainReq);
file.write("\n#");
file.write("\nhand.off.requirement=" + offReq);
file.write("\n\n# "+lang("hand.restriction"));
file.write("\n# "+lang("hand.restriction.description"));
file.write("\nhand.main.block=" + mainBlock);
file.write("\nhand.main.food=" + mainFood);
file.write("\nhand.main.usable=" + mainUsable);
file.write("\n#");
file.write("\nhand.off.block=" + offBlock);
file.write("\nhand.off.food=" + offFood);
file.write("\nhand.off.usable=" + offUsable);
file.write("\n\n# "+lang("hand.restriction.list"));
file.write("\n# "+lang("hand.restriction.list.description"));
file.write("\n# "+lang("example",Utl.Assets.LIST));
file.write("\nhand.main.whitelist="+gson.toJson(mainWhitelist));
file.write("\nhand.main.blacklist="+gson.toJson(mainBlacklist));
file.write("\n#");
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 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,92 @@
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.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<>();
@SerializedName("sittingHeight")
private Double sittingHeight = 0.5;
public CustomBlock() {}
public CustomBlock(ArrayList<String> blockIds, ArrayList<String> blockTags, ArrayList<String> blockStates, Double sittingHeight) {
this.blockIds = blockIds;
this.blockTags = blockTags;
this.blockStates = blockStates;
this.sittingHeight = sittingHeight;
}
public ArrayList<String> getBlockIds() {
return blockIds;
}
public ArrayList<String> getBlockTags() {
return blockTags;
}
public ArrayList<String> getBlockStates() {
return blockStates;
}
public Double getSittingHeight() {
return sittingHeight;
}
/**
* checks if the blockstate matches the CustomBlock params
* @param blockState the blockState to check
* @return if the blockstate is allowed by the CustomBlock rules
*/
public boolean isValid(BlockState blockState) {
boolean blockType = checkBlockType(blockState);
if (!blockType) return false;
// now check if the state is one of the acceptable states
for (String state : blockStates) {
// if there is a NOT (!) blockstate, and it is contained in the block, return false
if (state.startsWith("!") && blockState.toString().contains(state.substring(1))) return false;
// else check if the blockstate matches, if not return false
else if (!blockState.toString().contains(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;
}
// 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(), Identifier.of(tag.substring(2))))) return false;
// if there is a match, return true
if (blockState.isIn(TagKey.of(Registries.BLOCK.getKey(), Identifier.of(tag.substring(1))))) return true;
}
return false;
}
}

View file

@ -0,0 +1,102 @@
package one.oth3r.sit.file;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.HashMap;
public class Data {
/**
* Sit! config file
*/
private static ServerConfig serverConfig = new ServerConfig();
public static ServerConfig getServerConfig() {
return new ServerConfig(serverConfig);
}
public static void setServerConfig(ServerConfig newServerConfig) {
serverConfig = new ServerConfig(newServerConfig);
}
/**
* The default hand config for all new players
*/
private static HandConfig handConfig = new HandConfig();
public static HandConfig getHandConfig() {
return new HandConfig(handConfig);
}
public static void setHandConfig(HandConfig newHandConfig) {
handConfig = new HandConfig(newHandConfig);
}
/**
* the hand config stored per player on the server
*/
private static final HashMap<ServerPlayerEntity, HandConfig> playerSettings = new HashMap<>();
public static void clearPlayerSettings() {
playerSettings.clear();
}
public static void setPlayerSetting(ServerPlayerEntity player, HandConfig config) {
playerSettings.put(player, config);
}
public static void removePlayerSetting(ServerPlayerEntity player) {
playerSettings.remove(player);
}
public static HandConfig getPlayerSetting(ServerPlayerEntity player) {
return playerSettings.getOrDefault(player,handConfig);
}
/**
* 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);
}
/**
* a list of players who just joined, to check if they are mounted to a Sit! entity
*/
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);
}
/**
* loads all config files to memory
* @param tryLegacy try to load the legacy file, usually only used on server startup
*/
public static void loadFiles(boolean tryLegacy) {
ServerConfig.load(tryLegacy);
HandConfig.load();
}
}

View file

@ -0,0 +1,97 @@
package one.oth3r.sit.file;
import com.google.gson.annotations.SerializedName;
import net.minecraft.util.Hand;
import one.oth3r.sit.Sit;
import one.oth3r.sit.utl.Utl;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
public class HandConfig {
@SerializedName("version")
private Double version = 1.0;
@SerializedName("hand-sitting")
private boolean handSitting = true;
@SerializedName("main-hand")
private HandSetting mainHand = new HandSetting(HandSetting.SittingRequirement.EMPTY, new HandSetting.Filter());
@SerializedName("off-hand")
private HandSetting offHand = new HandSetting(HandSetting.SittingRequirement.FILTER,
new HandSetting.Filter(false,true,false,new ArrayList<>(),new ArrayList<>())); // todo fill out some fox examples sake
public HandConfig() {}
public HandConfig(double version, boolean handSitting, HandSetting mainHand, HandSetting offHand) {
this.version = version;
this.handSitting = handSitting;
this.mainHand = mainHand;
this.offHand = offHand;
}
public HandConfig(HandConfig handConfig) {
this.version = handConfig.version;
this.handSitting = handConfig.handSitting;
this.mainHand = handConfig.mainHand;
this.offHand = handConfig.offHand;
}
public Double getVersion() {
return version;
}
public boolean canSitWithHand() {
return handSitting;
}
public HandSetting getHand(Hand handType) {
return handType.equals(Hand.MAIN_HAND) ? mainHand : offHand;
}
public HandSetting getMainHand() {
return mainHand;
}
public HandSetting getOffHand() {
return offHand;
}
public static File getFile() {
return new File(Sit.CONFIG_DIR+"hand-config.json");
}
/**
* loads the Config file to Data
*/
public static void load() {
File file = getFile();
if (!file.exists()) save();
// try reading the file
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
Updater.HandConfigFile.run(reader);
} catch (Exception e) {
Sit.LOGGER.error(String.format("ERROR LOADING '%s`: %s", file.getName(),e.getMessage()));
}
// save after loading
save();
}
/**
* saves Data.config to config.json
*/
public static void save() {
if (!getFile().exists()) {
Sit.LOGGER.info(String.format("Creating new `%s`", getFile().getName()));
}
try (BufferedWriter writer = Files.newBufferedWriter(getFile().toPath(), StandardCharsets.UTF_8)) {
writer.write(Utl.getGson().toJson(Data.getHandConfig()));
} catch (Exception e) {
Sit.LOGGER.error(String.format("ERROR SAVING '%s`: %s", getFile().getName(), e.getMessage()));
}
}
}

View file

@ -0,0 +1,79 @@
package one.oth3r.sit.file;
import com.google.gson.annotations.SerializedName;
import java.util.ArrayList;
public class HandSetting {
public boolean mainBlock;
@SerializedName("requirement")
private SittingRequirement sittingRequirement = SittingRequirement.NONE;
@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("block")
private boolean block = false;
@SerializedName("food")
private boolean food = false;
@SerializedName("usable")
private boolean usable = false;
@SerializedName("custom-items")
private ArrayList<String> customItems = new ArrayList<>();
@SerializedName("custom-tags")
private ArrayList<String> customTags = new ArrayList<>();
public Filter() {}
public Filter(boolean block, boolean food, boolean usable, ArrayList<String> customItems, ArrayList<String> customTags) {
this.block = block;
this.food = food;
this.usable = usable;
this.customItems = customItems;
this.customTags = customTags;
}
public boolean isBlock() {
return block;
}
public boolean isFood() {
return food;
}
public boolean isUsable() {
return usable;
}
public ArrayList<String> getCustomItems() {
return customItems;
}
public ArrayList<String> getCustomTags() {
return customTags;
}
}
}

View file

@ -1,10 +1,10 @@
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 java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -75,20 +75,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/lang/"+ Data.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/lang/"+ new ServerConfig().getLang() +".json");
//todo error message and reset back 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 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(); Sit.LOGGER.error(e.getMessage());
} }
} }
public static String getLanguageValue(String key) { public static String getLanguageValue(String key) {

View file

@ -0,0 +1,177 @@
package one.oth3r.sit.file;
import com.google.gson.annotations.SerializedName;
import one.oth3r.sit.Sit;
import one.oth3r.sit.utl.Utl;
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;
import java.util.ArrayList;
public class ServerConfig {
@SerializedName("version")
private Double version = 2.0;
@SerializedName("lang")
private String lang = "en_us";
@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<CustomBlock> customBlocks = new ArrayList<>();
@SerializedName("blacklisted-blocks")
private ArrayList<String> blacklistedBlocks = new ArrayList<>();
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.customBlocks = serverConfig.customBlocks;
this.blacklistedBlocks = serverConfig.blacklistedBlocks;
}
public ServerConfig(Double version, String lang, boolean keepActive, boolean sitWhileSeated, PresetBlocks presetBlocks, boolean customEnabled, ArrayList<CustomBlock> customBlocks, ArrayList<String> blacklistedBlocks) {
this.version = version;
this.lang = lang;
this.keepActive = keepActive;
this.sitWhileSeated = sitWhileSeated;
this.presetBlocks = presetBlocks;
this.customEnabled = customEnabled;
this.customBlocks = customBlocks;
this.blacklistedBlocks = blacklistedBlocks;
}
public Double getVersion() {
return version;
}
public String getLang() {
return lang;
}
public boolean isKeepActive() {
return keepActive;
}
public boolean isSitWhileSeated() {
return sitWhileSeated;
}
public PresetBlocks getPresetBlocks() {
return presetBlocks;
}
public Boolean isCustomEnabled() {
return customEnabled;
}
public ArrayList<CustomBlock> getCustomBlocks() {
return customBlocks;
}
public ArrayList<String> getBlacklistedBlocks() {
return blacklistedBlocks;
}
public static class PresetBlocks {
@SerializedName("stairs")
private boolean stairs = true;
@SerializedName("slabs")
private boolean slabs = true;
@SerializedName("carpets")
private boolean carpets = true;
@SerializedName("full-blocks")
private boolean fullBlocks = false;
public PresetBlocks() {}
public PresetBlocks(boolean stairs, boolean slabs, boolean carpets, boolean fullBlocks) {
this.stairs = stairs;
this.slabs = slabs;
this.carpets = carpets;
this.fullBlocks = fullBlocks;
}
public boolean isStairs() {
return stairs;
}
public boolean isSlabs() {
return slabs;
}
public boolean isCarpets() {
return carpets;
}
public boolean isFullBlocks() {
return fullBlocks;
}
}
public static File getFile() {
return new File(Sit.CONFIG_DIR+"server-config.json");
}
/**
* loads the directionhud Config file to Data.config
*/
public static void load(boolean tryLegacy) {
File file = getFile();
if (!file.exists()) {
// try to make the config directory
try {
Files.createDirectories(Paths.get(Sit.CONFIG_DIR));
} catch (Exception e) {
Sit.LOGGER.error("Failed to create config directory. Canceling all config loading...");
return;
}
// if loading from legacy, try checking the old config directory for the file
if (tryLegacy && Updater.ServerConfigFile.Legacy.getLegacyFile().exists()) {
Sit.LOGGER.info("Updating Sit!.properties to sit!/config.json");
Updater.ServerConfigFile.Legacy.run();
}
save();
}
try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
Updater.ServerConfigFile.run(reader);
} catch (Exception e) {
Sit.LOGGER.error(String.format("ERROR LOADING '%s`: %s", file.getName(),e.getMessage()));
}
// save after loading
save();
}
/**
* saves Data.config to config.json
*/
public static void save() {
if (!getFile().exists()) {
Sit.LOGGER.info(String.format("Creating new `%s`", getFile().getName()));
}
try (BufferedWriter writer = Files.newBufferedWriter(getFile().toPath(), StandardCharsets.UTF_8)) {
writer.write(Utl.getGson().toJson(Data.getServerConfig()));
} catch (Exception e) {
Sit.LOGGER.info(String.format("ERROR SAVING '%s`: %s", getFile().getName(), e.getMessage()));
}
}
}

View file

@ -0,0 +1,288 @@
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.minecraft.util.Hand;
import one.oth3r.sit.Sit;
import one.oth3r.sit.utl.Utl;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.util.*;
public class Updater {
public static class HandConfigFile {
/**
* runs the updater from the file reader and sets the loaded settings when finished
* @param reader the file reader
* @throws NullPointerException if the file is null
*/
public static void run(BufferedReader reader)
throws NullPointerException {
// try to read the json
HandConfig handConfig;
try {
handConfig = Utl.getGson().fromJson(reader, HandConfig.class);
} catch (Exception e) {
throw new NullPointerException();
}
// throw null if the fileData is null or version is null
if (handConfig == null) throw new NullPointerException();
// get the file version
Double version = handConfig.getVersion();
// if there's no version, throw
if (version == null) throw new NullPointerException();
// update the config (using the non-null version)
handConfig = update(handConfig);
// set the config in the mod data
Data.setHandConfig(handConfig);
}
/**
* updates the file
*/
public static HandConfig update(HandConfig old) {
HandConfig serverConfig = new HandConfig(old);
return serverConfig;
}
}
public static class ServerConfigFile {
public static final double VERSION = 1.0;
/**
* runs the updater from the file reader and sets the loaded settings when finished
* @param reader the file reader
* @throws NullPointerException if the file is null
*/
public static void run(BufferedReader reader)
throws NullPointerException {
// try to read the json
ServerConfig serverConfig;
try {
serverConfig = Utl.getGson().fromJson(reader, ServerConfig.class);
} catch (Exception e) {
throw new NullPointerException();
}
// throw null if the fileData is null or version is null
if (serverConfig == null) throw new NullPointerException();
// get the file version
Double version = serverConfig.getVersion();
// if there's no version, throw
if (version == null) throw new NullPointerException();
// update the config (using the non-null version)
serverConfig = update(serverConfig);
System.out.println("updated");
System.out.println(serverConfig.getBlacklistedBlocks());
// set the config in the mod data
Data.setServerConfig(serverConfig);
}
/**
* updates the file
*/
public static ServerConfig update(ServerConfig old) {
ServerConfig serverConfig = new ServerConfig(old);
return serverConfig;
}
public 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(Sit.CONFIG_DIR.substring(0,Sit.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(VERSION));
// 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) {
Sit.LOGGER.error("Error loading legacy config file: {}", e.getMessage());
}
// delete the old file
try {
Files.delete(file.toPath());
Sit.LOGGER.info("Deleted " + file.getName());
} catch (Exception e) {
Sit.LOGGER.error("Failed to delete the old Sit! config.");
}
}
/**
* gets a list of custom blocks from the legacy way of entering custom sit blocks
*/
private static ArrayList<CustomBlock> getCustomBlocks(ArrayList<String> fix) {
//eg. minecraft:campfire|.46|1|lit=false
ArrayList<CustomBlock> 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 CustomBlock(
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 {
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
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.isSitWhileSeated()))),
new ServerConfig.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<>()
);
HandConfig defaultHandConfig = new HandConfig();
HandConfig handConfig = null;
try {
handConfig = new HandConfig(
1.0, Boolean.parseBoolean((String) properties.computeIfAbsent("hand.sitting", a -> String.valueOf(defaultHandConfig.canSitWithHand()))),
new HandSetting(
Utl.Enum.get(properties.computeIfAbsent("hand.main.requirement", a -> String.valueOf(defaultHandConfig.getHand(Hand.MAIN_HAND).getSittingRequirement())),HandSetting.SittingRequirement.class,HandSetting.SittingRequirement.FILTER),
new HandSetting.Filter(
Boolean.parseBoolean((String) properties.computeIfAbsent("hand.main.block", a -> String.valueOf(defaultHandConfig.getHand(Hand.MAIN_HAND).getFilter().isBlock()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("hand.main.food", a -> String.valueOf(defaultHandConfig.getHand(Hand.MAIN_HAND).getFilter().isFood()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("hand.main.usable", a -> String.valueOf(defaultHandConfig.getHand(Hand.MAIN_HAND).getFilter().isUsable()))),
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(
Utl.Enum.get(properties.computeIfAbsent("hand.off.requirement", a -> String.valueOf(defaultHandConfig.getHand(Hand.OFF_HAND).getSittingRequirement())),HandSetting.SittingRequirement.class,HandSetting.SittingRequirement.FILTER),
new HandSetting.Filter(
Boolean.parseBoolean((String) properties.computeIfAbsent("hand.off.block", a -> String.valueOf(defaultHandConfig.getHand(Hand.OFF_HAND).getFilter().isBlock()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("hand.off.food", a -> String.valueOf(defaultHandConfig.getHand(Hand.OFF_HAND).getFilter().isFood()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("hand.off.usable", a -> String.valueOf(defaultHandConfig.getHand(Hand.OFF_HAND).getFilter().isUsable()))),
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 {
handConfig = new HandConfig(
1.0, defaultHandConfig.canSitWithHand(),
new HandSetting(
Utl.Enum.get(properties.computeIfAbsent("main-hand-requirement", a -> String.valueOf(defaultHandConfig.getHand(Hand.MAIN_HAND).getSittingRequirement())),HandSetting.SittingRequirement.class,HandSetting.SittingRequirement.FILTER),
new HandSetting.Filter(
Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-block", a -> String.valueOf(defaultHandConfig.getHand(Hand.MAIN_HAND).getFilter().isBlock()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-food", a -> String.valueOf(defaultHandConfig.getHand(Hand.MAIN_HAND).getFilter().isFood()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("main-hand-usable", a -> String.valueOf(defaultHandConfig.getHand(Hand.MAIN_HAND).getFilter().isUsable()))),
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(
Utl.Enum.get(properties.computeIfAbsent("off-hand-requirement", a -> String.valueOf(defaultHandConfig.getHand(Hand.OFF_HAND).getSittingRequirement())),HandSetting.SittingRequirement.class,HandSetting.SittingRequirement.FILTER),
new HandSetting.Filter(
Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-block", a -> String.valueOf(defaultHandConfig.getHand(Hand.OFF_HAND).getFilter().isBlock()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-food", a -> String.valueOf(defaultHandConfig.getHand(Hand.OFF_HAND).getFilter().isFood()))),
Boolean.parseBoolean((String) properties.computeIfAbsent("off-hand-usable", a -> String.valueOf(defaultHandConfig.getHand(Hand.OFF_HAND).getFilter().isUsable()))),
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) {}
}
Data.setServerConfig(serverConfig);
Data.setHandConfig(handConfig);
ServerConfig.save();
HandConfig.save();
} catch (Exception e) {
Sit.LOGGER.error("Error loading legacy config: {}", e.getMessage());
}
}
}
}
}

View file

@ -0,0 +1,74 @@
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 class TextDisplayDismountMixin extends DisplayEntity {
public TextDisplayDismountMixin(EntityType<?> type, World world) {
super(type, world);
}
@Override
public void refreshData(boolean shouldLerp, float lerpProgress) {}
@Override
public Vec3d updatePassengerForDismount(LivingEntity passenger) {
int[][] offset = Dismounting.getDismountOffsets(Direction.NORTH);
// 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

@ -7,10 +7,10 @@ import net.minecraft.network.packet.CustomPayload;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import one.oth3r.sit.Sit; import one.oth3r.sit.Sit;
public class CustomPayloads { public class SitPayloads {
public record SettingsPayload(String value) implements CustomPayload { public record SettingsPayload(String value) implements CustomPayload {
public static final Id<SettingsPayload> ID = new Id<>(Identifier.of(Sit.MOD_ID,"settings_v1.1")); public static final Id<SettingsPayload> ID = new Id<>(Identifier.of(Sit.MOD_ID,"settings_v2.0"));
public static final PacketCodec<RegistryByteBuf, SettingsPayload> CODEC = PacketCodecs.STRING.xmap(SettingsPayload::new, SettingsPayload::value).cast(); public static final PacketCodec<RegistryByteBuf, SettingsPayload> CODEC = PacketCodecs.STRING.xmap(SettingsPayload::new, SettingsPayload::value).cast();
@ -18,10 +18,5 @@ public class CustomPayloads {
public Id<SettingsPayload> getId() { public Id<SettingsPayload> getId() {
return ID; return ID;
} }
@Override
public String value() {
return value;
}
} }
} }

View file

@ -0,0 +1,185 @@
package one.oth3r.sit.screen;
import com.terraformersmc.modmenu.api.ModMenuApi;
public class ModMenu implements ModMenuApi {
// private static MutableText lang(String key, Object... args) {
// return Utl.lang("config."+key,args);
// }
// @Override
// public ConfigScreenFactory<?> getModConfigScreenFactory() {
// return parent -> YetAnotherConfigLib.createBuilder().save(config::save)
// .title(Text.of("Sit!"))
// .category(ConfigCategory.createBuilder()
// .name(lang("category.general"))
// .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())
// .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("example",
// 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.withColor(TextColor.fromFormatting(Formatting.GRAY)))))
// .append("\n\n").append(lang("general.sittable_blocks.description.2").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
// .append("\n").append(lang("general.sittable_blocks.description.3").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.RED))))
// .append("\n").append(lang("general.sittable_blocks.description.4").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
// .append("\n").append(lang("general.sittable_blocks.description.5").styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GOLD))))
// .append("\n\n").append(lang("general.sittable_blocks.description.6").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"))
// .option(Option.<config.HandRequirement>createBuilder()
// .name(lang("hand.requirement"))
// .description(OptionDescription.of(lang("hand.requirement.description")
// .append("\n\n").append(lang("hand.requirement.description.2",lang("hand.requirement.empty")).styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
// .append("\n").append(lang("hand.requirement.description.3",lang("hand.requirement.restrictive")).styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
// .append("\n").append(lang("hand.requirement.description.4",lang("hand.requirement.none")).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 -> lang("hand.requirement."+v.toString())))
// .build())
// .group(OptionGroup.createBuilder()
// .name(lang("hand.restriction"))
// .description(OptionDescription.of(lang("hand.restriction.description")))
// .option(Option.<Boolean>createBuilder()
// .name(lang("hand.restriction.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.restriction.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.restriction.usable"))
// .description(OptionDescription.of(lang("hand.restriction.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.restriction.whitelist"))
// .description(OptionDescription.of(lang("hand.restriction.list.description")
// .append("\n\n").append(lang("example",
// Text.empty().append(Utl.Assets.LIST).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.restriction.blacklist"))
// .description(OptionDescription.of(lang("hand.restriction.list.description")
// .append("\n\n").append(lang("example",
// Text.empty().append(Utl.Assets.LIST).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"))
// .option(Option.<config.HandRequirement>createBuilder()
// .name(lang("hand.requirement"))
// .description(OptionDescription.of(lang("hand.requirement.description")
// .append("\n\n").append(lang("hand.requirement.description.2",lang("hand.requirement.empty")).styled(style -> style.withColor(TextColor.fromFormatting(Formatting.AQUA))))
// .append("\n").append(lang("hand.requirement.description.3",lang("hand.requirement.restrictive")).styled(style -> style.withColor(TextColor.fromFormatting(Formatting.GREEN))))
// .append("\n").append(lang("hand.requirement.description.4",lang("hand.requirement.none")).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 -> lang("hand.requirement."+v.toString())))
// .build())
// .group(OptionGroup.createBuilder()
// .name(lang("hand.restriction"))
// .description(OptionDescription.of(lang("hand.restriction.description")))
// .option(Option.<Boolean>createBuilder()
// .name(lang("hand.restriction.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.restriction.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.restriction.usable"))
// .description(OptionDescription.of(lang("hand.restriction.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.restriction.whitelist"))
// .description(OptionDescription.of(lang("hand.restriction.list.description")
// .append("\n\n").append(lang("example",
// Text.empty().append(Utl.Assets.LIST).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.restriction.blacklist"))
// .description(OptionDescription.of(lang("hand.restriction.list.description")
// .append("\n\n").append(lang("example",
// Text.empty().append(Utl.Assets.LIST).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);
// }
}

View file

@ -0,0 +1,69 @@
package one.oth3r.sit.utl;
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.server.network.ServerPlayerEntity;
import net.minecraft.util.ActionResult;
import one.oth3r.sit.Sit;
import one.oth3r.sit.command.SitCommand;
import one.oth3r.sit.file.Data;
public class Events {
public static void playerConnections() {
// PLAYER JOIN
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
Data.setPlayerSetting(handler.player,Data.getHandConfig());
Data.setCheckPlayer(handler.player, 5);
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
// if keep is off, remove the entity
if (!Data.getServerConfig().isKeepActive()) {
Logic.removeEntity(handler.player);
}
Data.removePlayerSetting(handler.player);
});
}
public static void server() {
ServerLifecycleEvents.SERVER_STARTED.register(s -> {
Sit.server = s;
Sit.commandManager = s.getCommandManager();
// right click on block event
UseBlockCallback.EVENT.register((pl, world, hand, hitResult) -> {
// get the server player
ServerPlayerEntity player = Sit.server.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 all player settings (singleplayer and such)
Data.clearPlayerSettings();
});
}
public static void misc() {
// loop setup
ServerTickEvents.END_SERVER_TICK.register(minecraftServer -> minecraftServer.execute(LoopManager::tick));
// command setup
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> SitCommand.register(dispatcher));
}
public static void registerCommon() {
playerConnections();
server();
}
}

View file

@ -0,0 +1,117 @@
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.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import one.oth3r.sit.file.Data;
import one.oth3r.sit.file.HandConfig;
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 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;
}
/**
* 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) {
HandConfig handConfig = Data.getPlayerSetting(player);
// if can't sit with hand, false
if (!handConfig.canSitWithHand()) return false;
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 = handConfig.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 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);
}
/**
* 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);
// 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);
// check if both poses are obstructed or not
return Utl.isNotObstructed(entity.getWorld(),pos1) && Utl.isNotObstructed(entity.getWorld(),pos2);
}
/**
* reloads the config files
*/
public static void reload() {
Data.loadFiles(false);
}
}

View file

@ -0,0 +1,56 @@
package one.oth3r.sit.utl;
import net.minecraft.entity.Entity;
import net.minecraft.entity.decoration.DisplayEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import one.oth3r.sit.Sit;
import one.oth3r.sit.file.Data;
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
// todo 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)
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(Sit.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);
}
}
}
}
}
}

View file

@ -0,0 +1,367 @@
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.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries;
import net.minecraft.registry.tag.TagKey;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.TypeFilter;
import net.minecraft.util.UseAction;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import one.oth3r.sit.Sit;
import one.oth3r.sit.file.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class Utl {
/**
* check if a block is obstructed (no collision / custom list)
* @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();
}
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.49;
public static final double CARPET = 0.05;
/**
* 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 theres nothing
if (itemStack.isEmpty()) return true;
String itemId = Registries.ITEM.getId(itemStack.getItem()).toString();
// check the custom item ids
for (String id : filter.getCustomItems()) {
// 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;
}
// check the custom item tags
for (String tag : filter.getCustomTags()) {
// substring to remove # and if needed, !
// if there is a math for the NOT(!) tag, return false
if (tag.startsWith("!") && itemStack.isIn(TagKey.of(Registries.ITEM.getKey(), Identifier.of(tag.substring(2))))) return false;
// if there is a match, return true
if (itemStack.isIn(TagKey.of(Registries.ITEM.getKey(), Identifier.of(tag.substring(1))))) 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);
// try the default conditions
if (filter.isBlock() && itemStack.getItem() instanceof BlockItem) return true;
if (filter.isFood() && food.contains(itemStack.getUseAction())) return true;
if (filter.isUsable() && !notUsable.contains(itemStack.getUseAction())) return true;
// if nothing else is met, the item is filtered out
return false;
}
/**
* get a block ID (namespace, 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 = Data.getServerConfig();
Block block = blockState.getBlock();
// only if custom is enabled
if (config.isCustomEnabled()) {
// if the block is on the blacklist, false
if (config.getBlacklistedBlocks().contains(getBlockID(blockState))) return null;
for (CustomBlock customBlock : config.getCustomBlocks()) {
// if the block is valid, true
if (customBlock.isValid(blockState)) return customBlock.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)
// make sure there isn't an action for the block IF the hit isn't null (manual command / no right click check)
&& (hit == null || !blockState.onUse(player.getWorld(), player, hit).isAccepted())) return 1.0;
// at the end, return false
return null;
}
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.setCustomName(Text.of(Sit.ENTITY_NAME));
entity.setCustomNameVisible(false);
entity.setInvulnerable(true);
entity.setInvisible(true);
entity.updatePositionAndAngles(blockPos.getX()+.5, blockPos.getY()+sitHeight, blockPos.getZ()+.5, 0, 0);
// // 1.20.2 mounting pos change (shifts everything down by .25)
// double oneTwentyTwo = .25;
// entity.updatePositionAndAngles(entity.getX(),entity.getY()+oneTwentyTwo,entity.getZ(),0,0);
// 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) {
player.getServerWorld().spawnEntity(entity);
player.startRiding(entity);
// add the entity to the list
Data.addSitEntity(player, entity);
}
/**
* removes all sit entities loaded on the server
*/
public static void purge(ServerPlayerEntity player, boolean message) {
// todo test if it can 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(Sit.ENTITY_NAME));
// remove each one
for (DisplayEntity.TextDisplayEntity entity : list) {
remove(entity);
}
// send a message if needed
if (message) {
// todo maybe a count for the message for debuging
player.sendMessage(Utl.lang("msg.purged"));
}
}
}
public static MutableText lang(String key, Object... args) {
if (Sit.client) return Text.translatable(key, args);
else return LangReader.of(key, args).getTxT();
}
// public static class ConfigExamples {
// public static final String CUSTOM_BLOCKS = "\"minecraft:campfire|0.255|1|lit=false\"";
// public static final String REQUIREMENT_OPTIONS = String.format("%s, %s, %s",
// configFile.HandRequirement.empty,configFile.HandRequirement.restrictive,configFile.HandRequirement.none);
// public static final String LIST = "\"minecraft:torch\"";
// }
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;
}
}
/**
* 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
*/
public static class LenientTypeAdapterFactory implements TypeAdapterFactory {
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
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;
}
}
};
}
}
}

View file

@ -22,7 +22,7 @@
"one.oth3r.sit.SitClient" "one.oth3r.sit.SitClient"
], ],
"modmenu": [ "modmenu": [
"one.oth3r.sit.ModMenu" "one.oth3r.sit.screen.ModMenu"
] ]
}, },
"depends": { "depends": {
@ -34,5 +34,8 @@
"suggests": { "suggests": {
"yet-another-config-lib": "*", "yet-another-config-lib": "*",
"modmenu": "*" "modmenu": "*"
} },
"mixins": [
"sit!.mixins.json"
]
} }

View file

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