2024-07-11 13:43:54 -05:00
|
|
|
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;
|
2024-07-23 13:49:43 -05:00
|
|
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
|
2024-07-11 13:43:54 -05:00
|
|
|
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;
|
2024-07-29 10:41:21 -05:00
|
|
|
import net.minecraft.entity.player.PlayerEntity;
|
2024-07-11 13:43:54 -05:00
|
|
|
import net.minecraft.item.BlockItem;
|
2024-11-10 10:57:53 -06:00
|
|
|
import net.minecraft.item.Item;
|
2024-07-11 13:43:54 -05:00
|
|
|
import net.minecraft.item.ItemStack;
|
2024-10-28 15:51:57 -05:00
|
|
|
import net.minecraft.item.consume.UseAction;
|
2024-07-11 13:43:54 -05:00
|
|
|
import net.minecraft.registry.Registries;
|
|
|
|
import net.minecraft.server.network.ServerPlayerEntity;
|
2024-07-29 10:41:21 -05:00
|
|
|
import net.minecraft.server.world.ServerWorld;
|
2024-07-11 13:43:54 -05:00
|
|
|
import net.minecraft.text.MutableText;
|
|
|
|
import net.minecraft.text.Text;
|
2024-10-03 15:42:51 -05:00
|
|
|
import net.minecraft.text.TextColor;
|
|
|
|
import net.minecraft.util.*;
|
2024-07-11 13:43:54 -05:00
|
|
|
import net.minecraft.util.hit.BlockHitResult;
|
2024-07-29 10:41:21 -05:00
|
|
|
import net.minecraft.util.hit.HitResult;
|
2024-07-11 13:43:54 -05:00
|
|
|
import net.minecraft.util.math.BlockPos;
|
2024-07-29 10:41:21 -05:00
|
|
|
import net.minecraft.util.math.Vec3d;
|
|
|
|
import net.minecraft.world.RaycastContext;
|
2024-07-11 13:43:54 -05:00
|
|
|
import net.minecraft.world.World;
|
|
|
|
import one.oth3r.sit.file.*;
|
2024-07-23 13:49:43 -05:00
|
|
|
import one.oth3r.sit.packet.SitPayloads;
|
2024-07-11 13:43:54 -05:00
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
2024-09-21 18:32:41 -05:00
|
|
|
import java.lang.reflect.Method;
|
2024-08-06 12:35:37 -05:00
|
|
|
import java.lang.reflect.ParameterizedType;
|
|
|
|
import java.lang.reflect.Type;
|
2024-07-11 13:43:54 -05:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
public class Utl {
|
|
|
|
|
|
|
|
/**
|
2024-10-19 17:54:35 -05:00
|
|
|
* check if a block is obstructed (no collision)
|
2024-07-11 13:43:54 -05:00
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-28 17:55:13 -05:00
|
|
|
public static final double HALF_BLOCK = 0.5;
|
|
|
|
public static final double CARPET = 0.062;
|
2024-07-11 13:43:54 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
2024-09-21 19:52:44 -05:00
|
|
|
// default to true if there's nothing
|
2024-07-11 13:43:54 -05:00
|
|
|
if (itemStack.isEmpty()) return true;
|
|
|
|
|
2024-11-10 11:18:42 -06:00
|
|
|
boolean TRUE = true, FALSE = false;
|
|
|
|
if (filter.isInverted()) {
|
|
|
|
TRUE = false;
|
|
|
|
FALSE = true;
|
|
|
|
}
|
|
|
|
|
2024-09-21 19:52:44 -05:00
|
|
|
boolean itemcheck = filter.getCustomItems().checkItem(itemStack);
|
2024-07-11 13:43:54 -05:00
|
|
|
|
2024-09-21 19:52:44 -05:00
|
|
|
// iif the item passes the checks, return true
|
2024-11-10 11:18:42 -06:00
|
|
|
if (itemcheck) return TRUE;
|
2024-08-07 12:46:21 -05:00
|
|
|
|
2024-07-11 13:43:54 -05:00
|
|
|
// 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);
|
|
|
|
|
2024-11-10 11:18:42 -06:00
|
|
|
HandSetting.Filter.Presets presets = filter.getPresets();
|
|
|
|
|
2024-07-11 13:43:54 -05:00
|
|
|
// try the default conditions
|
2024-11-10 11:18:42 -06:00
|
|
|
if (presets.isBlock() && itemStack.getItem() instanceof BlockItem) return TRUE;
|
|
|
|
if (presets.isFood() && food.contains(itemStack.getUseAction())) return TRUE;
|
|
|
|
if (presets.isUsable() && hasItemUse(itemStack)) return TRUE;
|
2024-07-11 13:43:54 -05:00
|
|
|
|
|
|
|
// if nothing else is met, the item is filtered out
|
2024-11-10 11:18:42 -06:00
|
|
|
return FALSE;
|
2024-07-11 13:43:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-10-19 17:40:47 -05:00
|
|
|
* get a block ID (eg. minecraft:air) from a blockstate. (it is easier with a block, but we are mostly working with block states
|
2024-07-11 13:43:54 -05:00
|
|
|
* @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) {
|
2024-07-23 12:27:01 -05:00
|
|
|
ServerConfig config = FileData.getServerConfig();
|
2024-07-11 13:43:54 -05:00
|
|
|
Block block = blockState.getBlock();
|
|
|
|
|
2024-08-07 11:27:40 -05:00
|
|
|
// make sure that the block that is being sit on has no interaction when hand sitting
|
2024-08-13 13:02:39 -05:00
|
|
|
if (hit != null && hasInteraction(blockState)) {
|
|
|
|
return null;
|
|
|
|
}
|
2024-08-07 11:27:40 -05:00
|
|
|
|
2024-07-11 13:43:54 -05:00
|
|
|
// only if custom is enabled
|
|
|
|
if (config.isCustomEnabled()) {
|
|
|
|
// if the block is on the blacklist, false
|
2024-10-19 17:40:37 -05:00
|
|
|
if (config.getBlacklistedBlocks().stream().anyMatch(c -> c.isValid(blockState))) return null;
|
2024-07-11 13:43:54 -05:00
|
|
|
|
2024-09-21 20:10:30 -05:00
|
|
|
for (SittingBlock sittingBlock : config.getSittingBlocks()) {
|
2024-07-11 13:43:54 -05:00
|
|
|
// if the block is valid, true
|
2024-09-21 20:10:30 -05:00
|
|
|
if (sittingBlock.isValid(blockState)) return sittingBlock.getSittingHeight();
|
2024-07-11 13:43:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2024-08-07 11:27:40 -05:00
|
|
|
&& blockState.isFullCube(player.getWorld(),blockPos)) return 1.0;
|
2024-07-11 13:43:54 -05:00
|
|
|
|
|
|
|
// at the end, return false
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2024-08-13 13:02:39 -05:00
|
|
|
/**
|
|
|
|
* checks if a block has an interaction
|
2024-10-19 17:40:47 -05:00
|
|
|
* @param blockState the blockstate of the block to check
|
|
|
|
* @return if the block has an interaction or not
|
2024-08-13 13:02:39 -05:00
|
|
|
*/
|
|
|
|
public static boolean hasInteraction(BlockState blockState) {
|
2024-09-21 18:32:41 -05:00
|
|
|
return isMethodOverridden(AbstractBlock.class, blockState.getBlock().getClass(), "onUse", BlockState.class, World.class, BlockPos.class, PlayerEntity.class, BlockHitResult.class);
|
|
|
|
}
|
|
|
|
|
2024-11-10 10:57:53 -06:00
|
|
|
/**
|
|
|
|
* checks if an item has a use
|
|
|
|
* @param itemStack the itemstack to check
|
|
|
|
* @return if the item has a use or not
|
|
|
|
*/
|
|
|
|
public static boolean hasItemUse(ItemStack itemStack) {
|
|
|
|
return isMethodOverridden(Item.class, itemStack.getItem().getClass(), "use", World.class, PlayerEntity.class, Hand.class);
|
|
|
|
}
|
|
|
|
|
2024-09-21 18:32:41 -05:00
|
|
|
/**
|
|
|
|
* checks if a method in the base class has been overridden in the subclass(es) by:
|
|
|
|
* checking the subclass and its supers till the original method is found or the original method is found
|
|
|
|
* @param baseClass the base class
|
|
|
|
* @param subclass the subclass to check
|
|
|
|
* @param methodName the method to check for
|
|
|
|
* @param parameterTypes the parameterTypes for the method, see {@link java.lang.Class#getDeclaredMethod(java.lang.String, java.lang.Class[])}
|
|
|
|
* @return if the method is overridden or not
|
|
|
|
*/
|
|
|
|
public static boolean isMethodOverridden(Class<?> baseClass, Class<?> subclass, String methodName, Class<?>... parameterTypes) {
|
|
|
|
try {
|
|
|
|
// get the original method
|
|
|
|
Method superMethod = baseClass.getMethod(methodName, parameterTypes);
|
2024-08-13 13:02:39 -05:00
|
|
|
|
2024-09-21 18:32:41 -05:00
|
|
|
// the current class to check, starting with the subclass
|
|
|
|
Class<?> currentClass = subclass;
|
|
|
|
// while the class is null and the current class isn't the same as the baseclass.
|
|
|
|
while (currentClass != null && !currentClass.equals(baseClass)) {
|
|
|
|
try {
|
|
|
|
// get the submethod
|
|
|
|
Method subMethod = currentClass.getDeclaredMethod(methodName, parameterTypes);
|
|
|
|
|
|
|
|
// check if the methods are different
|
|
|
|
if (!superMethod.equals(subMethod)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} catch (NoSuchMethodException ignored) {
|
|
|
|
// method isnt in this class, bump up a class and check that one
|
|
|
|
}
|
|
|
|
currentClass = currentClass.getSuperclass();
|
|
|
|
}
|
|
|
|
} catch (NoSuchMethodException e) {
|
|
|
|
// method doesn't exist in the base class
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// an override wasn't found
|
2024-08-13 13:02:39 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-09-21 18:32:41 -05:00
|
|
|
|
2024-07-11 13:43:54 -05:00
|
|
|
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);
|
|
|
|
|
2024-10-19 17:54:35 -05:00
|
|
|
// entity flags
|
2024-07-23 13:49:43 -05:00
|
|
|
entity.setCustomName(Text.of(Data.ENTITY_NAME));
|
2024-07-11 13:43:54 -05:00
|
|
|
entity.setCustomNameVisible(false);
|
|
|
|
entity.setInvulnerable(true);
|
|
|
|
entity.setInvisible(true);
|
|
|
|
|
2024-10-19 17:54:35 -05:00
|
|
|
/// make a double for adjusting the entity height if some versions change the player sit height on entities again
|
|
|
|
double adjustmentY = 0;
|
2024-07-11 13:43:54 -05:00
|
|
|
|
2024-10-19 17:54:35 -05:00
|
|
|
// get the entities y level
|
|
|
|
double entityY = blockPos.getY();
|
|
|
|
entityY += sitHeight + adjustmentY;
|
|
|
|
|
|
|
|
// set the entities position
|
|
|
|
entity.updatePositionAndAngles(blockPos.getX()+.5, entityY, blockPos.getZ()+.5, 0, 0);
|
2024-07-11 13:43:54 -05:00
|
|
|
|
|
|
|
// 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
|
2024-11-15 11:19:39 -06:00
|
|
|
Data.removeSitEntity(entity);
|
2024-07-11 13:43:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* spawns the entity and make the player sit on it
|
|
|
|
*/
|
|
|
|
public static void spawnSit(ServerPlayerEntity player, DisplayEntity.TextDisplayEntity entity) {
|
2024-08-17 13:27:26 -05:00
|
|
|
Data.setSpawnList(player, entity);
|
2024-07-11 13:43:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* removes all sit entities loaded on the server
|
|
|
|
*/
|
|
|
|
public static void purge(ServerPlayerEntity player, boolean message) {
|
2024-10-19 17:45:58 -05:00
|
|
|
/// FYI it cant purge an entity from a disconnected player or unloaded chunks
|
|
|
|
|
2024-07-11 13:43:54 -05:00
|
|
|
// get a list of sit entities
|
|
|
|
List<? extends DisplayEntity.TextDisplayEntity> list = player.getServerWorld()
|
|
|
|
.getEntitiesByType(TypeFilter.instanceOf(DisplayEntity.TextDisplayEntity.class),
|
2024-07-23 13:49:43 -05:00
|
|
|
entity -> entity.getName().getString().equals(Data.ENTITY_NAME));
|
2024-07-11 13:43:54 -05:00
|
|
|
|
2024-09-30 14:15:34 -05:00
|
|
|
// amount of sit entities purged
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
// remove each one & count
|
2024-07-11 13:43:54 -05:00
|
|
|
for (DisplayEntity.TextDisplayEntity entity : list) {
|
|
|
|
remove(entity);
|
2024-09-30 14:15:34 -05:00
|
|
|
count++;
|
2024-07-11 13:43:54 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// send a message if needed
|
|
|
|
if (message) {
|
2024-10-03 15:42:51 -05:00
|
|
|
player.sendMessage(messageTag().append(Utl.lang("sit!.chat.purged",Utl.lang("sit!.chat.purged.total",count).styled(
|
2024-09-30 14:15:34 -05:00
|
|
|
style -> style.withColor(Colors.LIGHT_GRAY).withItalic(true)
|
|
|
|
)).styled(
|
|
|
|
style -> style.withColor(Colors.GREEN)
|
|
|
|
)));
|
2024-07-11 13:43:54 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-30 14:14:58 -05:00
|
|
|
public static MutableText messageTag() {
|
|
|
|
return Text.literal("[").append(Text.literal("Sit!").styled(
|
|
|
|
style -> style.withColor(TextColor.parse("#c400ff").result().orElse(TextColor.fromFormatting(Formatting.DARK_PURPLE))))
|
|
|
|
).append("] ");
|
|
|
|
}
|
|
|
|
|
2024-07-23 13:49:43 -05:00
|
|
|
/**
|
|
|
|
* gets a MutableText using the language key, if on server, using the custom lang reader
|
|
|
|
*/
|
2024-07-11 13:43:54 -05:00
|
|
|
public static MutableText lang(String key, Object... args) {
|
2024-07-23 13:49:43 -05:00
|
|
|
if (Data.isClient()) return Text.translatable(key, args);
|
2024-07-11 13:43:54 -05:00
|
|
|
else return LangReader.of(key, args).getTxT();
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class Enum {
|
|
|
|
|
|
|
|
public static <T extends java.lang.Enum<T>> T get(Object enumString, Class<T> enumType) {
|
|
|
|
return get(enumString,enumType,enumType.getEnumConstants()[0]);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* gets an enum from a string without returning null
|
|
|
|
* @param enumString the string of the enum
|
|
|
|
* @param enumType the class of enums
|
|
|
|
* @param defaultEnum the enum to return if a match isn't found
|
|
|
|
* @return an enum, if there isn't a match, it returns the first enum
|
|
|
|
*/
|
|
|
|
public static <T extends java.lang.Enum<T>> T get(Object enumString, Class<T> enumType, T defaultEnum) {
|
|
|
|
T[] values = enumType.getEnumConstants();
|
|
|
|
for (T all : values) {
|
|
|
|
// check if there is a match for any of the enum names
|
|
|
|
if (enumString.toString().equals(all.name())) return all;
|
|
|
|
}
|
|
|
|
// if there's no match return the first entry
|
|
|
|
return defaultEnum;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-23 13:49:43 -05:00
|
|
|
/**
|
|
|
|
* sends the settings packets to the server, if client & in game
|
|
|
|
*/
|
|
|
|
public static void sendSettingsPackets() {
|
|
|
|
if (Data.isClient() && Data.isInGame()) {
|
|
|
|
ClientPlayNetworking.send(new SitPayloads.SettingsPayload(Utl.getGson().toJson(FileData.getSittingConfig())));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-11 13:43:54 -05:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2024-08-06 12:35:37 -05:00
|
|
|
@SuppressWarnings("unchecked")
|
2024-07-11 13:43:54 -05:00
|
|
|
public static class LenientTypeAdapterFactory implements TypeAdapterFactory {
|
|
|
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
|
|
|
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
|
|
|
|
|
2024-08-06 12:35:37 -05:00
|
|
|
// Check if the type is a List, then run the custom list type adapter
|
|
|
|
if (List.class.isAssignableFrom(type.getRawType())) {
|
|
|
|
Type elementType = ((ParameterizedType) type.getType()).getActualTypeArguments()[0];
|
|
|
|
TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
|
|
|
|
// the custom adapter
|
|
|
|
return (TypeAdapter<T>) new RemoveNullListTypeAdapter<>(elementAdapter);
|
|
|
|
}
|
|
|
|
|
2024-07-11 13:43:54 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2024-07-29 10:41:21 -05:00
|
|
|
|
2024-08-06 12:35:37 -05:00
|
|
|
/**
|
|
|
|
* type adapter that doesnt allow null / bad entries
|
|
|
|
*/
|
|
|
|
private static class RemoveNullListTypeAdapter<E> extends TypeAdapter<List<E>> {
|
|
|
|
private final TypeAdapter<E> elementAdapter;
|
|
|
|
|
|
|
|
RemoveNullListTypeAdapter(TypeAdapter<E> elementAdapter) {
|
|
|
|
this.elementAdapter = elementAdapter;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void write(JsonWriter out, List<E> value) throws IOException {
|
|
|
|
out.beginArray();
|
|
|
|
for (E element : value) {
|
|
|
|
elementAdapter.write(out, element);
|
|
|
|
}
|
|
|
|
out.endArray();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public List<E> read(JsonReader in) throws IOException {
|
|
|
|
List<E> list = new ArrayList<>();
|
|
|
|
in.beginArray();
|
|
|
|
while (in.hasNext()) {
|
|
|
|
try {
|
|
|
|
E element = elementAdapter.read(in);
|
|
|
|
// skip null entry
|
|
|
|
if (element == null) continue;
|
|
|
|
list.add(element);
|
|
|
|
} catch (Exception e) {
|
|
|
|
// skip invalid entry
|
|
|
|
in.skipValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
in.endArray();
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-29 10:41:21 -05:00
|
|
|
public static BlockPos getBlockPosPlayerIsLookingAt(ServerWorld world, PlayerEntity player, double range) {
|
|
|
|
// pos, adjusted to player eye level
|
|
|
|
Vec3d rayStart = player.getPos().add(0, player.getEyeHeight(player.getPose()), 0);
|
|
|
|
// extend ray by the range
|
|
|
|
Vec3d rayEnd = rayStart.add(player.getRotationVector().multiply(range));
|
|
|
|
|
|
|
|
BlockHitResult hitResult = world.raycast(new RaycastContext(rayStart, rayEnd, RaycastContext.ShapeType.OUTLINE, RaycastContext.FluidHandling.NONE, ShapeContext.absent()));
|
|
|
|
|
|
|
|
if (hitResult.getType() == HitResult.Type.BLOCK) {
|
|
|
|
return hitResult.getBlockPos();
|
|
|
|
}
|
|
|
|
|
|
|
|
return new BlockPos(player.getBlockPos());
|
|
|
|
}
|
2024-07-11 13:43:54 -05:00
|
|
|
}
|