HexCasting/Fabric/src/main/java/at/petrak/hexcasting/fabric/xplat/FabricXplatImpl.java

493 lines
18 KiB
Java

package at.petrak.hexcasting.fabric.xplat;
import at.petrak.hexcasting.api.HexAPI;
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.ResolvedPattern;
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import at.petrak.hexcasting.api.casting.iota.IotaType;
import at.petrak.hexcasting.api.mod.HexConfig;
import at.petrak.hexcasting.api.mod.HexTags;
import at.petrak.hexcasting.api.pigment.ColorProvider;
import at.petrak.hexcasting.api.pigment.FrozenPigment;
import at.petrak.hexcasting.api.player.AltioraAbility;
import at.petrak.hexcasting.api.player.FlightAbility;
import at.petrak.hexcasting.api.player.Sentinel;
import at.petrak.hexcasting.common.lib.HexItems;
import at.petrak.hexcasting.common.network.IMessage;
import at.petrak.hexcasting.fabric.cc.HexCardinalComponents;
import at.petrak.hexcasting.fabric.interop.gravity.GravityApiInterop;
import at.petrak.hexcasting.fabric.interop.trinkets.TrinketsApiInterop;
import at.petrak.hexcasting.fabric.recipe.FabricUnsealedIngredient;
import at.petrak.hexcasting.interop.HexInterop;
import at.petrak.hexcasting.interop.pehkui.PehkuiInterop;
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 com.jamieswhiteshirt.reachentityattributes.ReachEntityAttributes;
import com.mojang.serialization.Lifecycle;
import net.fabricmc.api.EnvType;
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents;
import net.fabricmc.fabric.api.event.player.UseItemCallback;
import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.core.*;
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.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.*;
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.AlternativeLootItemCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
import net.minecraft.world.level.storage.loot.predicates.MatchTool;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import virtuoel.pehkui.api.ScaleTypes;
import java.util.List;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Stream;
import static at.petrak.hexcasting.api.HexAPI.modLoc;
public class FabricXplatImpl implements IXplatAbstractions {
@Override
public Platform platform() {
return Platform.FABRIC;
}
@Override
public boolean isPhysicalClient() {
return FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT;
}
@Override
public boolean isModPresent(String id) {
return FabricLoader.getInstance().isModLoaded(id);
}
@Override
public void initPlatformSpecific() {
if (this.isModPresent(HexInterop.Fabric.GRAVITY_CHANGER_API_ID)) {
GravityApiInterop.init();
}
if (this.isModPresent(HexInterop.Fabric.TRINKETS_API_ID)) {
TrinketsApiInterop.init();
}
}
@Override
public double getReachDistance(Player player) {
return ReachEntityAttributes.getReachDistance(player, 5.0);
}
@Override
public void sendPacketToPlayer(ServerPlayer target, IMessage packet) {
ServerPlayNetworking.send(target, packet.getFabricId(), packet.toBuf());
}
@Override
public void sendPacketNear(Vec3 pos, double radius, ServerLevel dimension, IMessage packet) {
var pkt = ServerPlayNetworking.createS2CPacket(packet.getFabricId(), packet.toBuf());
var nears = PlayerLookup.around(dimension, pos, radius);
for (var p : nears) {
p.connection.send(pkt);
}
}
@Override
public Packet<?> toVanillaClientboundPacket(IMessage message) {
return ServerPlayNetworking.createS2CPacket(message.getFabricId(), message.toBuf());
}
@Override
public void setBrainsweepAddlData(Mob mob) {
var cc = HexCardinalComponents.BRAINSWEPT.get(mob);
cc.setBrainswept(true);
// CC API does the syncing for us
}
@Override
public void setColorizer(Player target, FrozenPigment colorizer) {
var cc = HexCardinalComponents.FAVORED_COLORIZER.get(target);
cc.setColorizer(colorizer);
}
@Override
public void setSentinel(Player target, @Nullable Sentinel sentinel) {
var cc = HexCardinalComponents.SENTINEL.get(target);
cc.setSentinel(sentinel);
}
@Override
public void setFlight(ServerPlayer target, FlightAbility flight) {
var cc = HexCardinalComponents.FLIGHT.get(target);
cc.setFlight(flight);
}
public void setAltiora(Player target, @Nullable AltioraAbility altiora) {
var cc = HexCardinalComponents.ALTIORA.get(target);
cc.setAltiora(altiora);
}
public void setStaffcastImage(ServerPlayer target, CastingImage image) {
var cc = HexCardinalComponents.STAFFCAST_IMAGE.get(target);
cc.setImage(image);
}
@Override
public void setPatterns(ServerPlayer target, List<ResolvedPattern> patterns) {
var cc = HexCardinalComponents.PATTERNS.get(target);
cc.setPatterns(patterns);
}
@Override
public boolean isBrainswept(Mob mob) {
var cc = HexCardinalComponents.BRAINSWEPT.get(mob);
return cc.isBrainswept();
}
@Override
public @Nullable FlightAbility getFlight(ServerPlayer player) {
var cc = HexCardinalComponents.FLIGHT.get(player);
return cc.getFlight();
}
@Override
public @Nullable AltioraAbility getAltiora(Player player) {
var cc = HexCardinalComponents.ALTIORA.get(player);
return cc.getAltiora();
}
@Override
public FrozenPigment getColorizer(Player player) {
var cc = HexCardinalComponents.FAVORED_COLORIZER.get(player);
return cc.getColorizer();
}
@Override
public Sentinel getSentinel(Player player) {
var cc = HexCardinalComponents.SENTINEL.get(player);
return cc.getSentinel();
}
@Override
public CastingVM getStaffcastVM(ServerPlayer player, InteractionHand hand) {
var cc = HexCardinalComponents.STAFFCAST_IMAGE.get(player);
return cc.getVM(hand);
}
@Override
public List<ResolvedPattern> getPatternsSavedInUi(ServerPlayer player) {
var cc = HexCardinalComponents.PATTERNS.get(player);
return cc.getPatterns();
}
@Override
public void clearCastingData(ServerPlayer player) {
this.setStaffcastImage(player, null);
this.setPatterns(player, List.of());
}
@Override
public @Nullable
ADMediaHolder findMediaHolder(ItemStack stack) {
var cc = HexCardinalComponents.MEDIA_HOLDER.maybeGet(stack);
return cc.orElse(null);
}
@Override
public @Nullable ADMediaHolder findMediaHolder(ServerPlayer player) {
var cc = HexCardinalComponents.MEDIA_HOLDER.maybeGet(player);
return cc.orElse(null);
}
@Override
public @Nullable
ADIotaHolder findDataHolder(ItemStack stack) {
var cc = HexCardinalComponents.IOTA_HOLDER.maybeGet(stack);
return cc.orElse(null);
}
@Override
public @Nullable
ADIotaHolder findDataHolder(Entity entity) {
var cc = HexCardinalComponents.IOTA_HOLDER.maybeGet(entity);
return cc.orElse(null);
}
@Override
public @Nullable
ADHexHolder findHexHolder(ItemStack stack) {
var cc = HexCardinalComponents.HEX_HOLDER.maybeGet(stack);
return cc.orElse(null);
}
@Override
public boolean isColorizer(ItemStack stack) {
return HexCardinalComponents.COLORIZER.isProvidedBy(stack);
}
@Override
public ColorProvider getColorProvider(FrozenPigment colorizer) {
var cc = HexCardinalComponents.COLORIZER.maybeGet(colorizer.item());
return cc.map(col -> col.provideColor(colorizer.owner())).orElse(ColorProvider.MISSING);
}
@Override
public <T extends BlockEntity> BlockEntityType<T> createBlockEntityType(BiFunction<BlockPos, BlockState, T> func,
Block... blocks) {
return FabricBlockEntityTypeBuilder.create(func::apply, blocks).build();
}
@Override
@SuppressWarnings("UnstableApiUsage")
public boolean tryPlaceFluid(Level level, InteractionHand hand, BlockPos pos, Fluid fluid) {
Storage<FluidVariant> target = FluidStorage.SIDED.find(level, pos, Direction.UP);
if (target == null) {
return false;
}
try (Transaction transaction = Transaction.openOuter()) {
long insertedAmount = target.insert(FluidVariant.of(fluid), FluidConstants.BUCKET, transaction);
if (insertedAmount > 0) {
transaction.commit();
return true;
}
}
return false;
}
@Override
@SuppressWarnings("UnstableApiUsage")
public boolean drainAllFluid(Level level, BlockPos pos) {
Storage<FluidVariant> target = FluidStorage.SIDED.find(level, pos, Direction.UP);
if (target == null) {
return false;
}
try (Transaction transaction = Transaction.openOuter()) {
boolean any = false;
for (var view : target) {
long extracted = view.extract(view.getResource(), view.getAmount(), transaction);
if (extracted > 0) {
any = true;
}
}
if (any) {
transaction.commit();
return true;
}
}
return false;
}
@Override
public Ingredient getUnsealedIngredient(ItemStack stack) {
return FabricUnsealedIngredient.of(stack);
}
private static Supplier<CreativeModeTab> TAB = Suppliers.memoize(() -> FabricItemGroupBuilder.create(
modLoc("creative_tab"))
.icon(HexItems::tabIcon)
.build());
@Override
public CreativeModeTab getTab() {
return TAB.get();
}
// do a stupid hack from botania
private static List<ItemStack> stacks(Item... items) {
return Stream.of(items).map(ItemStack::new).toList();
}
private static final List<List<ItemStack>> HARVEST_TOOLS_BY_LEVEL = List.of(
stacks(Items.WOODEN_PICKAXE, Items.WOODEN_AXE, Items.WOODEN_HOE, Items.WOODEN_SHOVEL),
stacks(Items.STONE_PICKAXE, Items.STONE_AXE, Items.STONE_HOE, Items.STONE_SHOVEL),
stacks(Items.IRON_PICKAXE, Items.IRON_AXE, Items.IRON_HOE, Items.IRON_SHOVEL),
stacks(Items.DIAMOND_PICKAXE, Items.DIAMOND_AXE, Items.DIAMOND_HOE, Items.DIAMOND_SHOVEL),
stacks(Items.NETHERITE_PICKAXE, Items.NETHERITE_AXE, Items.NETHERITE_HOE, Items.NETHERITE_SHOVEL)
);
@Override
public boolean isCorrectTierForDrops(Tier tier, BlockState bs) {
if (!bs.requiresCorrectToolForDrops()) {
return true;
}
int level = HexConfig.server()
.opBreakHarvestLevelBecauseForgeThoughtItWasAGoodIdeaToImplementHarvestTiersUsingAnHonestToGodTopoSort();
for (var tool : HARVEST_TOOLS_BY_LEVEL.get(level)) {
if (tool.isCorrectToolForDrops(bs)) {
return true;
}
}
return false;
}
@Override
public Item.Properties addEquipSlotFabric(EquipmentSlot slot) {
return new FabricItemSettings().equipmentSlot(s -> slot);
}
private static final IXplatTags TAGS = new IXplatTags() {
@Override
public TagKey<Item> amethystDust() {
return HexTags.Items.create(new ResourceLocation("c", "amethyst_dusts"));
}
@Override
public TagKey<Item> gems() {
return HexTags.Items.create(new ResourceLocation("c", "gems"));
}
};
@Override
public IXplatTags tags() {
return TAGS;
}
@Override
public LootItemCondition.Builder isShearsCondition() {
return AlternativeLootItemCondition.alternative(
MatchTool.toolMatches(ItemPredicate.Builder.item().of(Items.SHEARS)),
MatchTool.toolMatches(ItemPredicate.Builder.item().of(
HexTags.Items.create(new ResourceLocation("c", "shears"))))
);
}
@Override
public String getModName(String namespace) {
if (namespace.equals("c")) {
return "Common";
}
Optional<ModContainer> container = FabricLoader.getInstance().getModContainer(namespace);
if (container.isPresent()) {
return container.get().getMetadata().getName();
}
return namespace;
}
private static final Supplier<Registry<ActionRegistryEntry>> ACTION_REGISTRY = Suppliers.memoize(() ->
FabricRegistryBuilder.from(new MappedRegistry<ActionRegistryEntry>(
ResourceKey.createRegistryKey(modLoc("action")),
Lifecycle.stable(), null))
.buildAndRegister()
);
private static final Supplier<Registry<SpecialHandler.Factory<?>>> SPECIAL_HANDLER_REGISTRY =
Suppliers.memoize(() ->
FabricRegistryBuilder.from(new MappedRegistry<SpecialHandler.Factory<?>>(
ResourceKey.createRegistryKey(modLoc("special_handler")),
Lifecycle.stable(), null))
.buildAndRegister()
);
private static final Supplier<Registry<IotaType<?>>> IOTA_TYPE_REGISTRY = Suppliers.memoize(() ->
FabricRegistryBuilder.from(new DefaultedRegistry<IotaType<?>>(
HexAPI.MOD_ID + ":null", ResourceKey.createRegistryKey(modLoc("iota_type")),
Lifecycle.stable(), null))
.buildAndRegister()
);
private static final Supplier<Registry<EvalSound>> EVAL_SOUNDS_REGISTRY = Suppliers.memoize(() ->
FabricRegistryBuilder.from(new DefaultedRegistry<EvalSound>(
HexAPI.MOD_ID + ":nothing", ResourceKey.createRegistryKey(modLoc("eval_sound")),
Lifecycle.stable(), null))
.buildAndRegister()
);
@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_SOUNDS_REGISTRY.get();
}
@Override
public boolean isBreakingAllowed(Level world, BlockPos pos, BlockState state, Player player) {
return PlayerBlockBreakEvents.BEFORE.invoker()
.beforeBlockBreak(world, player, pos, state, world.getBlockEntity(pos));
}
@Override
public boolean isPlacingAllowed(Level world, BlockPos pos, ItemStack blockStack, Player player) {
ItemStack cached = player.getMainHandItem();
player.setItemInHand(InteractionHand.MAIN_HAND, blockStack.copy());
var success = UseItemCallback.EVENT.invoker().interact(player, world, InteractionHand.MAIN_HAND);
player.setItemInHand(InteractionHand.MAIN_HAND, cached);
return success.getResult() == InteractionResult.PASS; // No other mod tried to consume this
}
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;
}
}