Skip to content

Commit

Permalink
Add helper methods to expose components through API²
Browse files Browse the repository at this point in the history
  • Loading branch information
Pyrofab committed Apr 9, 2021
1 parent 83f8de5 commit 99c5126
Show file tree
Hide file tree
Showing 16 changed files with 278 additions and 21 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ dependencies {
modImplementation fabricApi.module("fabric-item-api-v1", rootProject.fabric_api_version)
modImplementation fabricApi.module("fabric-item-groups-v0", rootProject.fabric_api_version)
modImplementation fabricApi.module("fabric-events-interaction-v0", rootProject.fabric_api_version)
modImplementation fabricApi.module("fabric-api-lookup-api-v1", rootProject.fabric_api_version)
modRuntime fabricApi.module("fabric-networking-v0", rootProject.rootProject.fabric_api_version)
modRuntime fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)
modRuntime fabricApi.module("fabric-mining-levels-v0", rootProject.fabric_api_version)
Expand Down
1 change: 1 addition & 0 deletions cardinal-components-block/build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dependencies {
api project(":cardinal-components-base")
api project(":cardinal-components-util")
modApi fabricApi.module("fabric-api-lookup-api-v1", rootProject.fabric_api_version)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,14 @@
import dev.onyxstudios.cca.api.v3.component.ComponentV3;
import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundTag;
import org.jetbrains.annotations.ApiStatus;

/**
* A helper interface for components that entirely rely on contextual information like {@link BlockState}s.
* @deprecated use {@link net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup} instead
*/
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "3.0.0")
public interface BlockComponent extends ComponentV3 {
@Override
default void readFromNbt(CompoundTag tag) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ public interface BlockComponentFactoryRegistry {
* @throws NullPointerException if any of the arguments is {@code null}
* @see BlockComponent
* @see BlockComponents
* @deprecated use {@link net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup} instead
*/
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "3.0.0")
<C extends Component> void registerFor(Identifier blockId, ComponentKey<? super C> key, BlockComponentProvider<C> factory);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

/**
Expand All @@ -55,7 +56,10 @@
* @see BlockComponents
* @see BlockComponentFactoryRegistry
* @since 2.5.0
* @deprecated use {@link net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup} instead
*/
@Deprecated
@ApiStatus.ScheduledForRemoval(inVersion = "3.0.0")
@FunctionalInterface
public interface BlockComponentProvider<C extends Component> {
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,123 @@
import dev.onyxstudios.cca.api.v3.component.ComponentKey;
import dev.onyxstudios.cca.internal.block.InternalBlockComponentProvider;
import nerdhub.cardinal.components.api.component.Component;
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;

import java.util.NoSuchElementException;
import java.util.function.BiFunction;

/**
* This class consists exclusively of static methods that return a {@link Component} by querying some block context.
*/
@ApiStatus.Experimental
public final class BlockComponents {
/**
* Retrieves a context-less {@link BlockApiLookup} for the given {@link ComponentKey}.
*
* <p>The component must also be exposed to the lookup by registering a provider
* for relevant block entities.
*
* @param key the key denoting the component for which a block lookup will be retrieved
* @param <C> the type of the component/API
* @return a {@link BlockApiLookup} for retrieving instances of {@code C}
* @see #exposeApi(ComponentKey, BlockApiLookup)
* @see #exposeApi(ComponentKey, BlockApiLookup, BiFunction)
*/
@ApiStatus.Experimental
public static <C extends Component> BlockApiLookup<C, Void> getApiLookup(ComponentKey<C> key) {
return BlockApiLookup.get(key.getId(), key.getComponentClass(), Void.class);
}

/**
* Exposes an API for all block entities to which a given component is attached.
*
* @see #exposeApi(ComponentKey, BlockApiLookup, BiFunction)
*/
@ApiStatus.Experimental
public static <A, T> void exposeApi(ComponentKey<? extends A> key, BlockApiLookup<A, T> apiLookup) {
apiLookup.registerFallback((world, pos, state, blockEntity, context) -> {
if (blockEntity != null) {
// yes you can cast <? extends A> to <A>
@SuppressWarnings("unchecked") A ret = key.getNullable(blockEntity);
return ret;
}
return null;
});
}

/**
* Exposes an API for all block entities to which a given component is attached.
*
* <p><h3>Usage Example</h3>
* Let us pretend we have the {@code FLUID_CONTAINER} API, as defined in {@link BlockApiLookup}'s usage example.
*
* <pre>{@code
* public interface FluidContainerCompound extends Component {
* ComponentKey<FluidContainerCompound> KEY = ComponentRegistry.register(new Identifier("mymod:fluid_container_compound"), FluidContainerCompound.class);
*
* FluidContainer get(Direction side);
* }
* }</pre>
*
* <pre>{@code
* @Override
* public void onInitialize() {
* BlockComponents.exposeApi(
* FluidContainerCompound.KEY,
* MyApi.FLUID_CONTAINER,
* FluidContainerCompound::get
* );
* }
* }</pre>
*/
@ApiStatus.Experimental
public static <A, T, C extends Component> void exposeApi(ComponentKey<C> key, BlockApiLookup<A, T> apiLookup, BiFunction<? super C, ? super T, ? extends A> mapper) {
apiLookup.registerFallback((world, pos, state, blockEntity, context) -> {
if (blockEntity != null) {
C ret = key.getNullable(blockEntity);
if (ret != null) return mapper.apply(ret, context);
}
return null;
});
}

/**
* Exposes an API for block entities of a given type, assuming the given component is attached.
*
* <p>This method should be preferred to other overloads as it is more performant than the more generic alternatives.
* If the component is not {@linkplain BlockComponentFactoryRegistry#registerFor(Class, ComponentKey, BlockEntityComponentFactory) attached}
* to one of the {@code types}, calling {@link BlockApiLookup#find(World, BlockPos, Object)}
* on the corresponding block will throw a {@link NoSuchElementException}.
*
* @see #exposeApi(ComponentKey, BlockApiLookup, BiFunction)
*/
@ApiStatus.Experimental
public static <A, T, C extends Component> void exposeApi(ComponentKey<C> key, BlockApiLookup<A, T> apiLookup, BiFunction<? super C, ? super T, ? extends A> mapper, BlockEntityType<?>... types) {
apiLookup.registerForBlockEntities((blockEntity, context) -> mapper.apply(key.get(blockEntity), context), types);
}

/**
* @deprecated use {@link ComponentKey#get(Object) KEY.get(blockEntity)} instead
*/
@Deprecated
public static <C extends Component> @Nullable C get(ComponentKey<C> key, BlockEntity blockEntity) {
return get(key, blockEntity, null);
}

/**
* @deprecated use {@link BlockApiLookup} if you need additional context, otherwise call {@link ComponentKey#get(Object) KEY.get(blockEntity)}
*/
@Deprecated
@ApiStatus.ScheduledForRemoval
public static <C extends Component> @Nullable C get(ComponentKey<C> key, BlockEntity blockEntity, @Nullable Direction side) {
World world = blockEntity.getWorld();

Expand All @@ -58,18 +156,38 @@ public final class BlockComponents {
return key.getNullable(blockEntity);
}

/**
* @deprecated use {@link BlockApiLookup} if you need additional context or if you want to query a BE-less block, otherwise call {@link ComponentKey#get(Object) KEY.get(world.getBlockEntity(pos))}
*/
@Deprecated
@ApiStatus.ScheduledForRemoval
public static <C extends Component> @Nullable C get(ComponentKey<C> key, BlockView world, BlockPos pos) {
return get(key, world, pos, null);
}

/**
* @deprecated use {@link BlockApiLookup} if you need additional context or if you want to query a BE-less block, otherwise call {@link ComponentKey#get(Object) KEY.get(world.getBlockEntity(pos))}
*/
@Deprecated
@ApiStatus.ScheduledForRemoval
public static <C extends Component> @Nullable C get(ComponentKey<C> key, BlockState blockState, BlockView world, BlockPos pos) {
return get(key, blockState, world, pos, null);
}

/**
* @deprecated use {@link BlockApiLookup} if you need additional context or if you want to query a BE-less block, otherwise call {@link ComponentKey#get(Object) KEY.get(world.getBlockEntity(pos))}
*/
@Deprecated
@ApiStatus.ScheduledForRemoval
public static <C extends Component> @Nullable C get(ComponentKey<C> key, BlockView world, BlockPos pos, @Nullable Direction side) {
return get(key, world.getBlockState(pos), world, pos, side);
}

/**
* @deprecated use {@link BlockApiLookup} if you need additional context or if you want to query a BE-less block, otherwise call {@link ComponentKey#get(Object) KEY.get(world.getBlockEntity(pos))}
*/
@Deprecated
@ApiStatus.ScheduledForRemoval
public static <C extends Component> @Nullable C get(ComponentKey<C> key, BlockState blockState, BlockView world, BlockPos pos, @Nullable Direction side) {
@Nullable C res = getFromBlock(key, world, pos, side, blockState);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
},
"depends": {
"fabric-api-base": "*",
"fabric-api-lookup-api-v1": "*",
"cardinal-components-base": "<3.0.0-"
},
"authors": [
Expand Down
10 changes: 10 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
------------------------------------------------------
Version 2.8.0
------------------------------------------------------
Additions
- Added helper methods in `BlockComponents` to expose block components through Fabric API API-API API

Changes
- Methods and classes in `cardinal-components-block` which purpose was to access components on regular blocks have been
scheduled for removal as they are now superseded by API²

------------------------------------------------------
Version 2.7.13
------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ yarn_mappings=5
loader_version=0.10.6+build.214

#Fabric api
fabric_api_version=0.25.1+build.416-1.16
fabric_api_version=0.32.5+1.16

#Publishing
mod_version = 2.7.13
mod_version = 2.8.0
curseforge_id = 318449
curseforge_versions = 1.16.2; 1.16.3; 1.16.4; 1.16.5
changelog_url = https://github.com/OnyxStudios/Cardinal-Components-API/blob/master/changelog.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
package dev.onyxstudios.componenttest;

import com.google.common.reflect.TypeToken;
import dev.onyxstudios.cca.api.v3.block.BlockComponents;
import dev.onyxstudios.cca.api.v3.component.ComponentContainer;
import dev.onyxstudios.cca.api.v3.component.ComponentRegistryV3;
import dev.onyxstudios.cca.api.v3.util.ComponentContainerMetafactory;
Expand All @@ -35,11 +36,13 @@
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.fabricmc.fabric.api.event.player.UseItemCallback;
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder;
import net.minecraft.block.Blocks;
import net.minecraft.block.Material;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.mob.ZombieEntity;
Expand All @@ -49,11 +52,13 @@
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.math.Direction;
import net.minecraft.util.registry.Registry;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.annotation.Nullable;
import java.util.Objects;
import java.util.UUID;
import java.util.function.BiFunction;

Expand All @@ -76,6 +81,8 @@ public class CardinalComponentsTest {
public static final EntityType<VitalityZombieEntity> VITALITY_ZOMBIE = Registry.register(Registry.ENTITY_TYPE, "componenttest:vita_zombie",
FabricEntityTypeBuilder.create(SpawnGroup.MONSTER, VitalityZombieEntity::new).dimensions(EntityType.ZOMBIE.getDimensions()).build());

public static final BlockApiLookup<Vita, Direction> VITA_API_LOOKUP = BlockApiLookup.get(CardinalComponentsTest.id("sided_vita"), Vita.class, Direction.class);

public static Identifier id(String path) {
return new Identifier("componenttest", path);
}
Expand Down Expand Up @@ -143,6 +150,13 @@ public static void init() {
LOGGER.info("{} vitality: {}", stack, TestComponents.ALT_VITA.get(stack).getVitality()); // init components
return TypedActionResult.pass(stack);
});

VITA_API_LOOKUP.registerForBlocks(
(world, pos, state, blockEntity, context) -> TestComponents.VITA.get(Objects.requireNonNull(world.getExistingChunk(pos.getX() >> 4, pos.getZ() >> 4))),
VITALITY_CONDENSER
);
BlockComponents.exposeApi(TestComponents.VITA, VITA_API_LOOKUP, (vita, side) -> side == Direction.UP ? vita : null, BlockEntityType.END_PORTAL);
BlockComponents.exposeApi(VitaCompound.KEY, VITA_API_LOOKUP, VitaCompound::get, BlockEntityType.END_GATEWAY);
}

public interface TestContainerFactory {
Expand Down
18 changes: 2 additions & 16 deletions src/testmod/java/dev/onyxstudios/componenttest/TestComponents.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,14 @@
import dev.onyxstudios.componenttest.vita.*;
import nerdhub.cardinal.components.api.ComponentRegistry;
import nerdhub.cardinal.components.api.ComponentType;
import net.minecraft.block.entity.EndGatewayBlockEntity;
import net.minecraft.block.entity.EndPortalBlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.HostileEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Items;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import net.minecraft.world.CollisionView;
import net.minecraft.world.chunk.Chunk;

import java.util.Objects;
import java.util.UUID;
import java.util.function.BiFunction;

Expand Down Expand Up @@ -97,19 +94,8 @@ public void registerChunkComponentFactories(ChunkComponentFactoryRegistry regist

@Override
public void registerBlockComponentFactories(BlockComponentFactoryRegistry registry) {
registry.registerFor(CardinalComponentsTest.id("vita_condenser"), VITA,
(state, world, pos, side) -> {
if (world instanceof CollisionView)
return VITA.get(Objects.requireNonNull(((CollisionView) world).getExistingChunk(pos.getX() >> 4, pos.getZ() >> 4)));
if (world instanceof Chunk) return VITA.get(world);
return null;
});
registry.registerFor(EndGatewayBlockEntity.class, VitaCompound.KEY, VitaCompound::new);
registry.registerFor(EndPortalBlockEntity.class, VITA, SyncedVita::new);
registry.registerFor(
new Identifier("end_gateway"),
VITA,
(state, world, pos, side) -> side != Direction.UP ? VITA.maybeGet(world.getBlockEntity(pos)).orElse(null) : null
);
}

@Override
Expand Down
Loading

0 comments on commit 99c5126

Please sign in to comment.