mirror of
https://github.com/FabricMC/fabric.git
synced 2025-04-21 03:10:54 -04:00
Entity API Lookup (#1836)
* 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:
parent
691a79b5ca
commit
16d92c47a6
18 changed files with 643 additions and 22 deletions
fabric-api-lookup-api-v1
README.mdbuild.gradle
src
main
java/net/fabricmc/fabric
api/lookup/v1
impl/lookup
resources
testmod
java/net/fabricmc/fabric/test/lookup
FabricApiLookupTest.javaFabricApiLookupTestClient.javaInspectorBlock.java
api
entity
FabricEntityApiLookupTest.javaFabricEntityApiLookupTestClient.javaInspectablePigEntity.javaInspectablePigEntityRenderer.java
item
resources
|
@ -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.
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<A, C>} 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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -11,6 +11,9 @@
|
|||
"entrypoints": {
|
||||
"main": [
|
||||
"net.fabricmc.fabric.test.lookup.FabricApiLookupTest"
|
||||
],
|
||||
"client": [
|
||||
"net.fabricmc.fabric.test.lookup.FabricApiLookupTestClient"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue