Entity API Lookup ()

* Entity API Lookup

* Update fabric-api-lookup-api-v1/src/main/java/net/fabricmc/fabric/api/lookup/v1/entity/EntityApiLookup.java

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>

* Update according to review

* Check for valid entity

* Use synchronized block on REGISTERED_SELVES accesses

Co-authored-by: Juuxel <6596629+Juuxel@users.noreply.github.com>
This commit is contained in:
deirn 2021-12-03 20:34:42 +07:00 committed by modmuss50
parent 691a79b5ca
commit 16d92c47a6
18 changed files with 643 additions and 22 deletions

View file

@ -26,6 +26,13 @@ See the javadoc of `ItemApiLookup` for a full usage example.
The way to query API instances from item stacks.
It exposes a `find` function to retrieve an API instance, and multiple `register*` functions to register APIs for items.
# Retrieving APIs from entities
See the javadoc of `EntityApiLookup` for a full usage example.
## [`EntityApiLookup`](src/main/java/net/fabricmc/fabric/api/lookup/v1/entity/EntityApiLookup.java)
The way to query API instances from entities.
Exposes a `find` function to retrieve an API instance, and multiple `register*` functions to register APIs for entity types.
# Retrieving APIs from custom objects
The subpackage `custom` provides helper classes to accelerate implementations of `ApiLookup`s for custom objects,
similar to the existing `BlockApiLookup`, but with different query parameters.

View file

@ -7,5 +7,6 @@ moduleDependencies(project, [
])
dependencies {
testmodImplementation project(path: ':fabric-rendering-v1', configuration: 'namedElements')
testmodImplementation project(path: ':fabric-object-builder-api-v1', configuration: 'namedElements')
}

View file

@ -0,0 +1,176 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.api.lookup.v1.entity;
import java.util.function.BiFunction;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.impl.lookup.entity.EntityApiLookupImpl;
/**
* An object that allows retrieving APIs from entities.
* Instances of this interface can be obtained through {@link #get}
*
* <p>When trying to {@link #find} an API for an entity, the provider registered for the entity type will be queried if it exists.
* If it doesn't exist, or if it returns {@code null}, the fallback providers will be queried in order.
*
* <p><h3>Usage Example</h3>
* Let's pretend that we have the following interface that we want to attach to entities.
* <pre>{@code
* public interface Leveled {
* int getLevel();
* }
* }</pre>
*
* <p>We need to create the EntityApiLookup. We don't need any context so we use {@link Void}.
* <pre>{@code
* public class MyApi {
* public static final EntityApiLookup<Leveled, Void> LEVELED_ENTITY = EntityApiLookup.get(new Identifier("mymod:leveled_entity"), Leveled.class, Void.class);
* }
* }</pre>
*
* <p>Now we can query instances of {@code Leveled}.
* <pre>{@code
* Leveled leveled = MyApi.LEVELED_ENTITY.find(entity, null);
* if (leveled != null) {
* // Do something with the API.
* System.out.println("Entity " + entity.getEntityName() + " is level " + leveled.getLevel());
* }
* }</pre>
*
* <p>For query to return useful result, we must expose the API.
* <pre>{@code
* // If the entity directly implements the interface, registerSelf can be used.
* public class LeveledPigEntity extends PigEntity implements Leveled {
* ...
* }
* MyApi.LEVELED_ENTITY.registerSelf(LEVELED_PIG_ENTITY_TYPE);
*
* // Otherwise, registerForType can be used.
* MyApi.LEVELED_ENTITY.registerForType((zombieEntity, ignored) -> {
* // Return a Leveled instance for your entity here, or null if there's none.
* // The context is Void in this case, so it can be ignored.
* }, EntityType.ZOMBIE);
*
* // Generic fallback, to interface with anything, for example if we want to all other entity level defaults to 1.
* MyApi.LEVELED_ENTITY.registerFallback((entity, ignored) -> {
* // Return something if available, or null otherwise.
* });
* }</pre>
*
* @param <A> the type of the API we want to query.
* @param <C> the type of the additional context object. Completely arbitrary.
* If no context is necessary, {@link Void} should be used and {@code null} instances should be passed.
*/
@ApiStatus.NonExtendable
public interface EntityApiLookup<A, C> {
/**
* Retrieve the {@link EntityApiLookup} associated with an identifier, or create it if it didn't exist yet.
*
* @param lookupId the unique identifier of the lookup.
* @param apiClass the class of the API.
* @param contextClass the class of the additional context.
* @return the unique lookup with the passed lookupId.
* @throws IllegalArgumentException If another {@code apiClass} or another {@code contextClass} was already registered with the same identifier.
*/
static <A, C> EntityApiLookup<A, C> get(Identifier lookupId, Class<A> apiClass, Class<C> contextClass) {
return EntityApiLookupImpl.get(lookupId, apiClass, contextClass);
}
/**
* Attempt to retrieve an API from an entity.
*
* @param entity the entity.
* @param context additional context for the query, defined by type parameter C.
* @return The retrieved API, or {@code null} if no API was found.
*/
@Nullable
A find(Entity entity, C context);
/**
* Expose the API for the passed entities that directly implements it.
*
* <p>Implementation note: this is checked once after the first server started event fired by creating entity instances using the types.
*
* @param entityTypes the entity types for which the API are exposed to.
* @throws IllegalArgumentException if the entity is not an instance of the API class.
*/
void registerSelf(EntityType<?>... entityTypes);
/**
* Expose the API for instances of the entity type.
* This overload allows using the correct entity class directly.
*
* @param <T> the entity class for which the API is exposed to
* @param provider the provider: returns an API if it's available in the entity with specified context, or {@code null} otherwise.
* @param entityType the entity type.
*/
@SuppressWarnings("unchecked")
default <T extends Entity> void registerForType(BiFunction<T, C, @Nullable A> provider, EntityType<T> entityType) {
registerForTypes((entity, context) -> provider.apply((T) entity, context), entityType);
}
/**
* Expose the API for instances of the entity types.
* This overload allows for registering multiple entity types at once,
* but due to how generics work in java, the provider has to cast to the correct type if necessary.
*
* @param provider the provider.
* @param entityTypes the entity types for which the API are exposed to.
*/
void registerForTypes(EntityApiProvider<A, C> provider, EntityType<?>... entityTypes);
/**
* Expose the API for all queries: the provider will be invoked if no object was found using the entity providers.
* May have big performance impact on all queries, use cautiously.
*/
void registerFallback(EntityApiProvider<A, C> fallbackProvider);
/**
* Returns the API class of this lookup.
*/
Class<A> apiClass();
/**
* Returns the context class of this lookup.
*/
Class<C> contextClass();
/**
* Returns the provider for the passed entity type (registered with one of the {@code register} functions), or null if none was registered (yet).
* Queries should go through {@link #find}, only use this to inspect registered providers!
*/
@Nullable
EntityApiProvider<A, C> getProvider(EntityType<?> entityType);
interface EntityApiProvider<A, C> {
/**
* Return an instance of API {@code A} if available in the given entity with the given context, or {@code null} otherwise.
*
* @param entity the entity.
* @param context additional context for the query.
*/
@Nullable
A find(Entity entity, C context);
}
}

View file

@ -59,6 +59,20 @@
* </ul>
* </p>
*
* <p><h2>Retrieving APIs from entities</h2>
* <ul>
* <li>A query for an entity API takes an entity and additional context of type {@code C},
* and uses that to find an object of type {@code A}, or {@code null} if there's no such object.</li>
* <li>{@link net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup EntityApiLookup&lt;A, C&gt;} instances provide a
* {@link net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup#find find()} function that does the query, and registration happens
* primarily through {@link net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup#registerSelf registerSelf()} and
* {@link net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup#registerForTypes registerForTypes()}.</li>
* <li>These instances can be accessed through {@link net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup#get EntityApiLookup#get()}
* and should be stored in a {@code public static final} field.</li>
* <li>See {@link net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup EntityApiLookup} for example code.</li>
* </ul>
* </p>
*
* <p><h2>Retrieving APIs from custom game objects</h2>
* <ul>
* <li>The subpackage {@code custom} provides helper classes to accelerate implementations of {@code ApiLookup}s for custom objects,

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.lookup;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.impl.lookup.entity.EntityApiLookupImpl;
public class ApiLookupImpl implements ModInitializer {
@Override
public void onInitialize() {
ServerLifecycleEvents.SERVER_STARTED.register(EntityApiLookupImpl::checkSelfImplementingTypes);
}
}

View file

@ -0,0 +1,171 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.impl.lookup.entity;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.fabricmc.fabric.api.lookup.v1.custom.ApiLookupMap;
import net.fabricmc.fabric.api.lookup.v1.custom.ApiProviderMap;
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
public class EntityApiLookupImpl<A, C> implements EntityApiLookup<A, C> {
private static final Logger LOGGER = LogManager.getLogger("fabric-api-lookup-api-v1/entity");
private static final ApiLookupMap<EntityApiLookup<?, ?>> LOOKUPS = ApiLookupMap.create(EntityApiLookupImpl::new);
private static final Map<Class<?>, Set<EntityType<?>>> REGISTERED_SELVES = new HashMap<>();
private static boolean checkEntityLookup = true;
private final Class<A> apiClass;
private final Class<C> contextClass;
private final ApiProviderMap<EntityType<?>, EntityApiProvider<A, C>> providerMap = ApiProviderMap.create();
private final List<EntityApiProvider<A, C>> fallbackProviders = new CopyOnWriteArrayList<>();
private EntityApiLookupImpl(Class<A> apiClass, Class<C> contextClass) {
this.apiClass = apiClass;
this.contextClass = contextClass;
}
@SuppressWarnings("unchecked")
public static <A, C> EntityApiLookup<A, C> get(Identifier lookupId, Class<A> apiClass, Class<C> contextClass) {
return (EntityApiLookup<A, C>) LOOKUPS.getLookup(lookupId, apiClass, contextClass);
}
public static void checkSelfImplementingTypes(MinecraftServer server) {
if (checkEntityLookup) {
checkEntityLookup = false;
synchronized (REGISTERED_SELVES) {
REGISTERED_SELVES.forEach((apiClass, entityTypes) -> {
for (EntityType<?> entityType : entityTypes) {
Entity entity = entityType.create(server.getOverworld());
if (entity == null) {
String errorMessage = String.format(
"Failed to register self-implementing entities for API class %s. Can not create entity of type %s.",
apiClass.getCanonicalName(),
Registry.ENTITY_TYPE.getId(entityType)
);
throw new NullPointerException(errorMessage);
}
if (!apiClass.isInstance(entity)) {
String errorMessage = String.format(
"Failed to register self-implementing entities. API class %s is not assignable from entity class %s.",
apiClass.getCanonicalName(),
entity.getClass().getCanonicalName()
);
throw new IllegalArgumentException(errorMessage);
}
}
});
}
}
}
@Override
@Nullable
public A find(Entity entity, C context) {
Objects.requireNonNull(entity, "Entity may not be null.");
if (EntityPredicates.VALID_ENTITY.test(entity)) {
EntityApiProvider<A, C> provider = providerMap.get(entity.getType());
if (provider != null) {
A instance = provider.find(entity, context);
if (instance != null) {
return instance;
}
}
for (EntityApiProvider<A, C> fallback : fallbackProviders) {
A instance = fallback.find(entity, context);
if (instance != null) {
return instance;
}
}
}
return null;
}
@SuppressWarnings("unchecked")
@Override
public void registerSelf(EntityType<?>... entityTypes) {
synchronized (REGISTERED_SELVES) {
REGISTERED_SELVES.computeIfAbsent(apiClass, c -> new LinkedHashSet<>()).addAll(Arrays.asList(entityTypes));
}
registerForTypes((entity, context) -> (A) entity, entityTypes);
}
@Override
public void registerForTypes(EntityApiProvider<A, C> provider, EntityType<?>... entityTypes) {
Objects.requireNonNull(provider, "EntityApiProvider may not be null.");
if (entityTypes.length == 0) {
throw new IllegalArgumentException("Must register at least one EntityType instance with an EntityApiProvider.");
}
for (EntityType<?> entityType : entityTypes) {
if (providerMap.putIfAbsent(entityType, provider) != null) {
LOGGER.warn("Encountered duplicate API provider registration for entity type: " + Registry.ENTITY_TYPE.getId(entityType));
}
}
}
@Override
public void registerFallback(EntityApiProvider<A, C> fallbackProvider) {
Objects.requireNonNull(fallbackProvider, "EntityApiProvider may not be null.");
fallbackProviders.add(fallbackProvider);
}
@Override
public Class<A> apiClass() {
return apiClass;
}
@Override
public Class<C> contextClass() {
return contextClass;
}
@Override
@Nullable
public EntityApiProvider<A, C> getProvider(EntityType<?> entityType) {
return providerMap.get(entityType);
}
}

View file

@ -20,6 +20,11 @@
"fabric-api-base": "*",
"fabric-lifecycle-events-v1": "*"
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.impl.lookup.ApiLookupImpl"
]
},
"description": "A universal way to expose and query APIs",
"mixins": [
"fabric-api-lookup-api-v1.mixins.json"

View file

@ -35,6 +35,7 @@ import net.fabricmc.fabric.test.lookup.api.ItemApis;
import net.fabricmc.fabric.test.lookup.api.ItemInsertable;
import net.fabricmc.fabric.test.lookup.compat.InventoryExtractableProvider;
import net.fabricmc.fabric.test.lookup.compat.InventoryInsertableProvider;
import net.fabricmc.fabric.test.lookup.entity.FabricEntityApiLookupTest;
import net.fabricmc.fabric.test.lookup.item.FabricItemApiLookupTest;
public class FabricApiLookupTest implements ModInitializer {
@ -51,6 +52,9 @@ public class FabricApiLookupTest implements ModInitializer {
public static BlockEntityType<CobbleGenBlockEntity> COBBLE_GEN_BLOCK_ENTITY_TYPE;
// Testing for item api lookups is done in the `item` package.
public static final InspectorBlock INSPECTOR_BLOCK = new InspectorBlock(FabricBlockSettings.of(Material.METAL));
public static final BlockItem INSPECTOR_ITEM = new BlockItem(INSPECTOR_BLOCK, new Item.Settings().group(ItemGroup.MISC));
@Override
public void onInitialize() {
Identifier chute = new Identifier(MOD_ID, "chute");
@ -73,7 +77,12 @@ public class FabricApiLookupTest implements ModInitializer {
testLookupRegistry();
testSelfRegistration();
Identifier inspector = new Identifier(FabricApiLookupTest.MOD_ID, "inspector");
Registry.register(Registry.BLOCK, inspector, INSPECTOR_BLOCK);
Registry.register(Registry.ITEM, inspector, INSPECTOR_ITEM);
FabricItemApiLookupTest.onInitialize();
FabricEntityApiLookupTest.onInitialize();
}
private static void testLookupRegistry() {

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.lookup;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.test.lookup.entity.FabricEntityApiLookupTestClient;
@Environment(EnvType.CLIENT)
public class FabricApiLookupTestClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
FabricEntityApiLookupTestClient.onInitializeClient();
}
}

View file

@ -14,18 +14,24 @@
* limitations under the License.
*/
package net.fabricmc.fabric.test.lookup.item;
package net.fabricmc.fabric.test.lookup;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.fabricmc.fabric.test.lookup.api.Inspectable;
import net.fabricmc.fabric.test.lookup.entity.FabricEntityApiLookupTest;
import net.fabricmc.fabric.test.lookup.item.FabricItemApiLookupTest;
public class InspectorBlock extends Block {
public InspectorBlock(Settings settings) {
super(settings);
@ -34,7 +40,7 @@ public class InspectorBlock extends Block {
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ItemStack stack = player.getStackInHand(hand);
Inspectable inspectable = Inspectable.LOOKUP.find(stack, null);
Inspectable inspectable = FabricItemApiLookupTest.INSPECTABLE.find(stack, null);
if (inspectable != null) {
if (!world.isClient()) {
@ -46,4 +52,17 @@ public class InspectorBlock extends Block {
return ActionResult.PASS;
}
@Override
public void onSteppedOn(World world, BlockPos pos, BlockState state, Entity entity) {
if (!world.isClient()) {
Inspectable inspectable = FabricEntityApiLookupTest.INSPECTABLE.find(entity, null);
if (inspectable != null) {
for (ServerPlayerEntity player : world.getServer().getPlayerManager().getPlayerList()) {
player.sendMessage(inspectable.inspect(), true);
}
}
}
}
}

View file

@ -14,22 +14,13 @@
* limitations under the License.
*/
package net.fabricmc.fabric.test.lookup.item;
package net.fabricmc.fabric.test.lookup.api;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.fabricmc.fabric.api.lookup.v1.item.ItemApiLookup;
/**
* An item that may provide an arbitrary text for display.
* An object that may provide an arbitrary text for display.
*/
public interface Inspectable {
/**
* @return A text to print when a player right-clicks the Inspector block with this item.
*/
Text inspect();
ItemApiLookup<Inspectable, Void> LOOKUP =
ItemApiLookup.get(new Identifier("testmod:inspectable"), Inspectable.class, Void.class);
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.lookup.entity;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.entity.mob.CreeperEntity;
import net.minecraft.entity.passive.PigEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricDefaultAttributeRegistry;
import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder;
import net.fabricmc.fabric.test.lookup.FabricApiLookupTest;
import net.fabricmc.fabric.test.lookup.api.Inspectable;
public class FabricEntityApiLookupTest {
public static final EntityApiLookup<Inspectable, Void> INSPECTABLE =
EntityApiLookup.get(new Identifier(FabricApiLookupTest.MOD_ID, "inspectable"), Inspectable.class, Void.class);
public static final EntityType<InspectablePigEntity> INSPECTABLE_PIG = FabricEntityTypeBuilder.create()
.spawnGroup(SpawnGroup.CREATURE)
.entityFactory(InspectablePigEntity::new)
.dimensions(EntityDimensions.changing(0.9F, 0.9F))
.trackRangeChunks(10)
.build();
public static void onInitialize() {
Registry.register(Registry.ENTITY_TYPE, new Identifier(FabricApiLookupTest.MOD_ID, "inspectable_pig"), INSPECTABLE_PIG);
FabricDefaultAttributeRegistry.register(INSPECTABLE_PIG, PigEntity.createPigAttributes());
INSPECTABLE.registerSelf(INSPECTABLE_PIG);
INSPECTABLE.registerForTypes(
(entity, context) -> () -> new LiteralText("registerForTypes: " + entity.getClass().getName()),
EntityType.PLAYER,
EntityType.COW);
INSPECTABLE.registerFallback((entity, context) -> {
if (entity instanceof CreeperEntity) {
return () -> new LiteralText("registerFallback: CreeperEntity");
} else {
return null;
}
});
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.lookup.entity;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry;
@Environment(EnvType.CLIENT)
public class FabricEntityApiLookupTestClient {
public static void onInitializeClient() {
EntityRendererRegistry.register(FabricEntityApiLookupTest.INSPECTABLE_PIG, InspectablePigEntityRenderer::new);
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.lookup.entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.passive.PigEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.world.World;
import net.fabricmc.fabric.test.lookup.api.Inspectable;
public class InspectablePigEntity extends PigEntity implements Inspectable {
public InspectablePigEntity(EntityType<? extends PigEntity> entityType, World world) {
super(entityType, world);
}
@Override
public Text inspect() {
return new LiteralText("InspectablePigEntity");
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.fabricmc.fabric.test.lookup.entity;
import net.minecraft.client.render.entity.EntityRendererFactory;
import net.minecraft.client.render.entity.PigEntityRenderer;
import net.minecraft.entity.passive.PigEntity;
import net.minecraft.util.Identifier;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@Environment(EnvType.CLIENT)
public class InspectablePigEntityRenderer extends PigEntityRenderer {
private static final Identifier TEXTURE = new Identifier("missingno");
public InspectablePigEntityRenderer(EntityRendererFactory.Context context) {
super(context);
}
@Override
public Identifier getTexture(PigEntity pigEntity) {
return TEXTURE;
}
}

View file

@ -18,7 +18,6 @@ package net.fabricmc.fabric.test.lookup.item;
import static net.fabricmc.fabric.test.lookup.FabricApiLookupTest.ensureException;
import net.minecraft.block.Material;
import net.minecraft.item.Item;
import net.minecraft.item.Items;
import net.minecraft.item.ToolItem;
@ -26,20 +25,21 @@ import net.minecraft.text.LiteralText;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings;
import net.fabricmc.fabric.api.lookup.v1.item.ItemApiLookup;
import net.fabricmc.fabric.test.lookup.FabricApiLookupTest;
import net.fabricmc.fabric.test.lookup.api.Inspectable;
public class FabricItemApiLookupTest {
// Use /setblock to place it in the world.
public static final InspectorBlock INSPECTOR = new InspectorBlock(FabricBlockSettings.of(Material.METAL));
public static final ItemApiLookup<Inspectable, Void> INSPECTABLE =
ItemApiLookup.get(new Identifier("testmod:inspectable"), Inspectable.class, Void.class);
public static final InspectableItem HELLO_ITEM = new InspectableItem("Hello Fabric API tester!");
public static void onInitialize() {
Registry.register(Registry.BLOCK, new Identifier(FabricApiLookupTest.MOD_ID, "inspector"), INSPECTOR);
Registry.register(Registry.ITEM, new Identifier(FabricApiLookupTest.MOD_ID, "hello"), HELLO_ITEM);
// Diamonds and diamond blocks can be inspected and will also print their name.
Inspectable.LOOKUP.registerForItems((stack, ignored) -> () -> {
INSPECTABLE.registerForItems((stack, ignored) -> () -> {
if (stack.hasCustomName()) {
return stack.getName();
} else {
@ -47,9 +47,9 @@ public class FabricItemApiLookupTest {
}
}, Items.DIAMOND, Items.DIAMOND_BLOCK);
// Test registerSelf
Inspectable.LOOKUP.registerSelf(HELLO_ITEM);
INSPECTABLE.registerSelf(HELLO_ITEM);
// Tools report their mining level
Inspectable.LOOKUP.registerFallback((stack, ignored) -> {
INSPECTABLE.registerFallback((stack, ignored) -> {
Item item = stack.getItem();
if (item instanceof ToolItem) {
@ -64,7 +64,7 @@ public class FabricItemApiLookupTest {
private static void testSelfRegistration() {
ensureException(() -> {
Inspectable.LOOKUP.registerSelf(Items.WATER_BUCKET);
INSPECTABLE.registerSelf(Items.WATER_BUCKET);
}, "The ItemApiLookup should have prevented self-registration of incompatible items.");
}
}

View file

@ -21,6 +21,8 @@ import net.minecraft.item.ItemGroup;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.fabricmc.fabric.test.lookup.api.Inspectable;
public class InspectableItem extends Item implements Inspectable {
private final String inspectionResult;

View file

@ -11,6 +11,9 @@
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.lookup.FabricApiLookupTest"
],
"client": [
"net.fabricmc.fabric.test.lookup.FabricApiLookupTestClient"
]
}
}