HexCasting/Forge/src/main/java/at/petrak/hexcasting/forge/xplat/ForgeXplatImpl.java
2023-02-17 14:11:16 -06:00

517 lines
20 KiB
Java

package at.petrak.hexcasting.forge.xplat;
import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.api.addldata.ADColorizer;
import at.petrak.hexcasting.api.addldata.ADHexHolder;
import at.petrak.hexcasting.api.addldata.ADIotaHolder;
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
import at.petrak.hexcasting.api.casting.eval.CastingHarness;
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.mod.HexTags;
import at.petrak.hexcasting.api.player.FlightAbility;
import at.petrak.hexcasting.api.player.Sentinel;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.HexItems;
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
import at.petrak.hexcasting.common.network.IMessage;
import at.petrak.hexcasting.forge.cap.CapSyncers;
import at.petrak.hexcasting.forge.cap.HexCapabilities;
import at.petrak.hexcasting.forge.interop.curios.CuriosApiInterop;
import at.petrak.hexcasting.forge.mixin.ForgeAccessorRegistry;
import at.petrak.hexcasting.forge.network.ForgePacketHandler;
import at.petrak.hexcasting.forge.network.MsgBrainsweepAck;
import at.petrak.hexcasting.forge.recipe.ForgeUnsealedIngredient;
import at.petrak.hexcasting.interop.HexInterop;
import at.petrak.hexcasting.interop.pehkui.PehkuiInterop;
import at.petrak.hexcasting.mixin.accessor.AccessorVillager;
import at.petrak.hexcasting.xplat.IXplatAbstractions;
import at.petrak.hexcasting.xplat.IXplatTags;
import at.petrak.hexcasting.xplat.Platform;
import com.google.common.base.Suppliers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.TagKey;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.npc.Villager;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Tier;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.common.*;
import net.minecraftforge.common.loot.CanToolPerformAction;
import net.minecraftforge.event.level.BlockEvent;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidType;
import net.minecraftforge.fluids.FluidUtil;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.loading.FMLLoader;
import net.minecraftforge.network.NetworkDirection;
import net.minecraftforge.network.PacketDistributor;
import org.jetbrains.annotations.Nullable;
import virtuoel.pehkui.api.ScaleTypes;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
import static net.minecraftforge.fluids.capability.IFluidHandler.FluidAction.EXECUTE;
public class ForgeXplatImpl implements IXplatAbstractions {
@Override
public Platform platform() {
return Platform.FORGE;
}
@Override
public boolean isPhysicalClient() {
return FMLLoader.getDist() == Dist.CLIENT;
}
@Override
public boolean isModPresent(String id) {
return ModList.get().isLoaded(id);
}
@Override
public void initPlatformSpecific() {
if (this.isModPresent(HexInterop.Forge.CURIOS_API_ID)) {
CuriosApiInterop.init();
}
}
@Override
public double getReachDistance(Player player) {
return player.getAttributeValue(ForgeMod.REACH_DISTANCE.get());
}
@Override
public void brainsweep(Mob mob) {
mob.getPersistentData().putBoolean(TAG_BRAINSWEPT, true);
mob.removeFreeWill();
if (mob instanceof Villager villager) {
((AccessorVillager) villager).hex$releaseAllPois();
}
if (mob.level instanceof ServerLevel) {
ForgePacketHandler.getNetwork()
.send(PacketDistributor.TRACKING_ENTITY.with(() -> mob), MsgBrainsweepAck.of(mob));
}
}
@Override
public void setFlight(ServerPlayer player, FlightAbility flight) {
CompoundTag tag = player.getPersistentData();
tag.putBoolean(TAG_FLIGHT_ALLOWED, flight != null);
if (flight != null) {
tag.putInt(TAG_FLIGHT_TIME, flight.timeLeft());
tag.put(TAG_FLIGHT_ORIGIN, HexUtils.serializeToNBT(flight.origin()));
tag.putString(TAG_FLIGHT_DIMENSION, flight.dimension().location().toString());
tag.putDouble(TAG_FLIGHT_RADIUS, flight.radius());
} else {
tag.remove(TAG_FLIGHT_TIME);
tag.remove(TAG_FLIGHT_ORIGIN);
tag.remove(TAG_FLIGHT_DIMENSION);
tag.remove(TAG_FLIGHT_RADIUS);
}
}
@Override
public void setColorizer(Player player, FrozenColorizer colorizer) {
CompoundTag tag = player.getPersistentData();
tag.put(TAG_COLOR, colorizer.serializeToNBT());
if (player instanceof ServerPlayer serverPlayer) {
CapSyncers.syncColorizer(serverPlayer);
}
}
@Override
public void setSentinel(Player player, Sentinel sentinel) {
CompoundTag tag = player.getPersistentData();
tag.putBoolean(TAG_SENTINEL_EXISTS, sentinel.hasSentinel());
if (sentinel.hasSentinel()) {
tag.putBoolean(TAG_SENTINEL_GREATER, sentinel.extendsRange());
tag.put(TAG_SENTINEL_POSITION, HexUtils.serializeToNBT(sentinel.position()));
tag.putString(TAG_SENTINEL_DIMENSION, sentinel.dimension().location().toString());
} else {
tag.remove(TAG_SENTINEL_GREATER);
tag.remove(TAG_SENTINEL_POSITION);
tag.remove(TAG_SENTINEL_DIMENSION);
}
if (player instanceof ServerPlayer serverPlayer) {
CapSyncers.syncSentinel(serverPlayer);
}
}
@Override
public void setHarness(ServerPlayer player, CastingHarness harness) {
player.getPersistentData().put(TAG_HARNESS, harness == null ? new CompoundTag() : harness.serializeToNBT());
}
@Override
public void setPatterns(ServerPlayer player, List<ResolvedPattern> patterns) {
var listTag = new ListTag();
for (ResolvedPattern pattern : patterns) {
listTag.add(pattern.serializeToNBT());
}
player.getPersistentData().put(TAG_PATTERNS, listTag);
}
@Override
public boolean isBrainswept(Mob e) {
return e.getPersistentData().getBoolean(TAG_BRAINSWEPT);
}
@Override
public FlightAbility getFlight(ServerPlayer player) {
CompoundTag tag = player.getPersistentData();
boolean allowed = tag.getBoolean(TAG_FLIGHT_ALLOWED);
if (allowed) {
var timeLeft = tag.getInt(TAG_FLIGHT_TIME);
var origin = HexUtils.vecFromNBT(tag.getLongArray(TAG_FLIGHT_ORIGIN));
var radius = tag.getDouble(TAG_FLIGHT_RADIUS);
var dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY,
new ResourceLocation(tag.getString(TAG_FLIGHT_DIMENSION)));
return new FlightAbility(timeLeft, dimension, origin, radius);
}
return null;
}
@Override
public FrozenColorizer getColorizer(Player player) {
return FrozenColorizer.fromNBT(player.getPersistentData().getCompound(TAG_COLOR));
}
@Override
public Sentinel getSentinel(Player player) {
CompoundTag tag = player.getPersistentData();
var exists = tag.getBoolean(TAG_SENTINEL_EXISTS);
if (!exists) {
return Sentinel.none();
}
var extendsRange = tag.getBoolean(TAG_SENTINEL_GREATER);
var position = HexUtils.vecFromNBT(tag.getLongArray(TAG_SENTINEL_POSITION));
var dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY,
new ResourceLocation(tag.getString(TAG_SENTINEL_DIMENSION)));
return new Sentinel(true, extendsRange, position, dimension);
}
@Override
public CastingHarness getHarness(ServerPlayer player, InteractionHand hand) {
// This is always from a staff because we don't need to load the harness when casting from item
var ctx = new CastingEnvironment(player, hand, CastingEnvironment.CastSource.STAFF);
return CastingHarness.fromNBT(player.getPersistentData().getCompound(TAG_HARNESS), ctx);
}
@Override
public List<ResolvedPattern> getPatternsSavedInUi(ServerPlayer player) {
ListTag patternsTag = player.getPersistentData().getList(TAG_PATTERNS, Tag.TAG_COMPOUND);
List<ResolvedPattern> patterns = new ArrayList<>(patternsTag.size());
for (int i = 0; i < patternsTag.size(); i++) {
patterns.add(ResolvedPattern.fromNBT(patternsTag.getCompound(i)));
}
return patterns;
}
@Override
public void clearCastingData(ServerPlayer player) {
player.getPersistentData().remove(TAG_HARNESS);
player.getPersistentData().remove(TAG_PATTERNS);
}
@Override
public @Nullable
ADMediaHolder findMediaHolder(ItemStack stack) {
var maybeCap = stack.getCapability(HexCapabilities.MEDIA).resolve();
return maybeCap.orElse(null);
}
@Override
public @Nullable
ADIotaHolder findDataHolder(ItemStack stack) {
var maybeCap = stack.getCapability(HexCapabilities.IOTA).resolve();
return maybeCap.orElse(null);
}
@Override
public @Nullable ADIotaHolder findDataHolder(Entity entity) {
var maybeCap = entity.getCapability(HexCapabilities.IOTA).resolve();
return maybeCap.orElse(null);
}
@Override
public @Nullable
ADHexHolder findHexHolder(ItemStack stack) {
var maybeCap = stack.getCapability(HexCapabilities.STORED_HEX).resolve();
return maybeCap.orElse(null);
}
@Override
public boolean isColorizer(ItemStack stack) {
return stack.getCapability(HexCapabilities.COLOR).isPresent();
}
@Override
public int getRawColor(FrozenColorizer colorizer, float time, Vec3 position) {
var maybeColorizer = colorizer.item().getCapability(HexCapabilities.COLOR).resolve();
if (maybeColorizer.isPresent()) {
ADColorizer col = maybeColorizer.get();
return col.color(colorizer.owner(), time, position);
}
return 0xff_ff00dc; // missing color
}
@Override
public void sendPacketToPlayer(ServerPlayer target, IMessage packet) {
ForgePacketHandler.getNetwork().send(PacketDistributor.PLAYER.with(() -> target), packet);
}
@Override
public void sendPacketNear(Vec3 pos, double radius, ServerLevel dimension, IMessage packet) {
ForgePacketHandler.getNetwork().send(PacketDistributor.NEAR.with(() -> new PacketDistributor.TargetPoint(
pos.x, pos.y, pos.z, radius * radius, dimension.dimension()
)), packet);
}
@Override
public Packet<?> toVanillaClientboundPacket(IMessage message) {
return ForgePacketHandler.getNetwork().toVanillaPacket(message, NetworkDirection.PLAY_TO_CLIENT);
}
@Override
public <T extends BlockEntity> BlockEntityType<T> createBlockEntityType(BiFunction<BlockPos, BlockState, T> func,
Block... blocks) {
return BlockEntityType.Builder.of(func::apply, blocks).build(null);
}
@Override
public boolean tryPlaceFluid(Level level, InteractionHand hand, BlockPos pos, Fluid fluid) {
Optional<IFluidHandler> handler = FluidUtil.getFluidHandler(level, pos, Direction.UP).resolve();
return handler.isPresent() &&
handler.get().fill(new FluidStack(fluid, FluidType.BUCKET_VOLUME), EXECUTE) > 0;
}
@Override
public boolean drainAllFluid(Level level, BlockPos pos) {
Optional<IFluidHandler> handler = FluidUtil.getFluidHandler(level, pos, Direction.UP).resolve();
if (handler.isPresent()) {
boolean any = false;
IFluidHandler pool = handler.get();
for (int i = 0; i < pool.getTanks(); i++) {
if (!pool.drain(pool.getFluidInTank(i), EXECUTE).isEmpty()) {
any = true;
}
}
return any;
}
return false;
}
@Override
public Ingredient getUnsealedIngredient(ItemStack stack) {
return ForgeUnsealedIngredient.of(stack);
}
private static Supplier<CreativeModeTab> TAB = Suppliers.memoize(() ->
new CreativeModeTab(HexAPI.MOD_ID) {
@Override
public ItemStack makeIcon() {
return HexItems.tabIcon();
}
@Override
public void fillItemList(NonNullList<ItemStack> p_40778_) {
super.fillItemList(p_40778_);
}
});
@Override
public CreativeModeTab getTab() {
return TAB.get();
}
@Override
public boolean isCorrectTierForDrops(Tier tier, BlockState bs) {
return !bs.requiresCorrectToolForDrops() || TierSortingRegistry.isCorrectTierForDrops(tier, bs);
}
@Override
public Item.Properties addEquipSlotFabric(EquipmentSlot slot) {
return new Item.Properties();
}
private static final IXplatTags TAGS = new IXplatTags() {
@Override
public TagKey<Item> amethystDust() {
return HexTags.Items.create(new ResourceLocation("forge", "dusts/amethyst"));
}
@Override
public TagKey<Item> gems() {
return HexTags.Items.create(new ResourceLocation("forge", "gems"));
}
};
@Override
public IXplatTags tags() {
return TAGS;
}
@Override
public LootItemCondition.Builder isShearsCondition() {
return CanToolPerformAction.canToolPerformAction(ToolActions.SHEARS_DIG);
}
@Override
public String getModName(String namespace) {
if (namespace.equals("c")) {
return "Common";
}
Optional<? extends ModContainer> container = ModList.get().getModContainerById(namespace);
if (container.isPresent()) {
return container.get().getModInfo().getDisplayName();
}
return namespace;
}
private static final Supplier<Registry<ActionRegistryEntry>> ACTION_REGISTRY = Suppliers.memoize(() ->
ForgeAccessorRegistry.hex$registerSimple(
ResourceKey.createRegistryKey(modLoc("action")), null)
);
private static final Supplier<Registry<SpecialHandler.Factory<?>>> SPECIAL_HANDLER_REGISTRY =
Suppliers.memoize(() ->
ForgeAccessorRegistry.hex$registerSimple(
ResourceKey.createRegistryKey(modLoc("special_handler")), null)
);
private static final Supplier<Registry<IotaType<?>>> IOTA_TYPE_REGISTRY = Suppliers.memoize(() ->
ForgeAccessorRegistry.hex$registerDefaulted(
ResourceKey.createRegistryKey(modLoc("iota_type")),
HexAPI.MOD_ID + ":null", registry -> HexIotaTypes.NULL)
);
private static final Supplier<Registry<EvalSound>> EVAL_SOUND_REGISTRY = Suppliers.memoize(() ->
ForgeAccessorRegistry.hex$registerDefaulted(
ResourceKey.createRegistryKey(modLoc("eval_sound")),
HexAPI.MOD_ID + ":nothing", registry -> HexEvalSounds.NOTHING)
);
@Override
public Registry<ActionRegistryEntry> getActionRegistry() {
return ACTION_REGISTRY.get();
}
@Override
public Registry<SpecialHandler.Factory<?>> getSpecialHandlerRegistry() {
return SPECIAL_HANDLER_REGISTRY.get();
}
@Override
public Registry<IotaType<?>> getIotaTypeRegistry() {
return IOTA_TYPE_REGISTRY.get();
}
@Override
public Registry<EvalSound> getEvalSoundRegistry() {
return EVAL_SOUND_REGISTRY.get();
}
@Override
public boolean isBreakingAllowed(Level world, BlockPos pos, BlockState state, Player player) {
return !MinecraftForge.EVENT_BUS.post(new BlockEvent.BreakEvent(world, pos, state, player));
}
@Override
public boolean isPlacingAllowed(Level world, BlockPos pos, ItemStack blockStack, Player player) {
ItemStack cached = player.getMainHandItem();
player.setItemInHand(InteractionHand.MAIN_HAND, blockStack.copy());
var evt = ForgeHooks.onRightClickBlock(player, InteractionHand.MAIN_HAND, pos,
new BlockHitResult(Vec3.atCenterOf(pos), Direction.DOWN, pos, true));
player.setItemInHand(InteractionHand.MAIN_HAND, cached);
return !evt.isCanceled();
}
// it's literally the EXACT SAME on fabric aaa
private static PehkuiInterop.ApiAbstraction PEHKUI_API = null;
@Override
public PehkuiInterop.ApiAbstraction getPehkuiApi() {
if (!this.isModPresent(HexInterop.PEHKUI_ID)) {
throw new IllegalArgumentException("cannot get the pehkui api without pehkui");
}
if (PEHKUI_API == null) {
PEHKUI_API = new PehkuiInterop.ApiAbstraction() {
@Override
public float getScale(Entity e) {
return ScaleTypes.BASE.getScaleData(e).getScale();
}
@Override
public void setScale(Entity e, float scale) {
ScaleTypes.BASE.getScaleData(e).setScale(scale);
}
};
}
return PEHKUI_API;
}
public static final String TAG_BRAINSWEPT = "hexcasting:brainswept";
public static final String TAG_SENTINEL_EXISTS = "hexcasting:sentinel_exists";
public static final String TAG_SENTINEL_GREATER = "hexcasting:sentinel_extends_range";
public static final String TAG_SENTINEL_POSITION = "hexcasting:sentinel_position";
public static final String TAG_SENTINEL_DIMENSION = "hexcasting:sentinel_dimension";
public static final String TAG_COLOR = "hexcasting:colorizer";
public static final String TAG_FLIGHT_ALLOWED = "hexcasting:flight_allowed";
public static final String TAG_FLIGHT_TIME = "hexcasting:flight_time";
public static final String TAG_FLIGHT_ORIGIN = "hexcasting:flight_origin";
public static final String TAG_FLIGHT_DIMENSION = "hexcasting:flight_dimension";
public static final String TAG_FLIGHT_RADIUS = "hexcasting:flight_radius";
public static final String TAG_HARNESS = "hexcasting:spell_harness";
public static final String TAG_PATTERNS = "hexcasting:spell_patterns";
}