2018-06-19 14:04:05 -04:00
/*
2022-02-28 13:41:23 -08:00
* Minecraft Forge - Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
2018-06-19 14:04:05 -04:00
*/
2021-11-26 18:57:41 +00:00
package net.minecraftforge.registries ;
2018-06-19 14:04:05 -04:00
2022-03-16 15:36:00 -05:00
import net.minecraft.core.Registry ;
import net.minecraft.resources.ResourceKey ;
2021-07-17 00:08:49 -05:00
import net.minecraft.resources.ResourceLocation ;
2018-06-19 14:04:05 -04:00
2019-12-17 04:50:13 -05:00
import javax.annotation.Nonnull ;
2019-09-25 18:01:39 -04:00
import javax.annotation.Nullable ;
2018-06-19 14:04:05 -04:00
import java.util.Objects ;
import java.util.Optional ;
import java.util.function.Consumer ;
import java.util.function.Function ;
import java.util.function.Predicate ;
import java.util.function.Supplier ;
import java.util.stream.Stream ;
2019-09-11 16:00:32 +02:00
public final class RegistryObject < T extends IForgeRegistryEntry <? super T >> implements Supplier < T >
2018-06-19 14:04:05 -04:00
{
2019-09-10 16:02:07 -07:00
private final ResourceLocation name ;
@Nullable
2018-06-19 14:04:05 -04:00
private T value ;
2022-03-16 15:36:00 -05:00
/**
* @deprecated The uniqueness of registry super types will not be guaranteed starting in 1.19. Use {@link #of(ResourceLocation, ResourceLocation, String)}.
*/
@Deprecated ( forRemoval = true , since = "1.18.2" )
2019-09-25 18:01:39 -04:00
public static < T extends IForgeRegistryEntry < T > , U extends T > RegistryObject < U > of ( final ResourceLocation name , Supplier < Class <? super T >> registryType ) {
2018-06-19 14:04:05 -04:00
return new RegistryObject <> ( name , registryType );
}
2019-09-25 18:01:39 -04:00
public static < T extends IForgeRegistryEntry < T > , U extends T > RegistryObject < U > of ( final ResourceLocation name , IForgeRegistry < T > registry ) {
2019-09-10 16:02:07 -07:00
return new RegistryObject <> ( name , registry );
}
2022-03-16 15:36:00 -05:00
/**
* @deprecated The uniqueness of registry super types will not be guaranteed starting in 1.19. Use {@link #of(ResourceLocation, ResourceLocation, String)}.
*/
@Deprecated ( forRemoval = true , since = "1.18.2" )
2020-06-12 15:28:43 -07:00
public static < T extends IForgeRegistryEntry < T > , U extends T > RegistryObject < U > of ( final ResourceLocation name , final Class < T > baseType , String modid ) {
return new RegistryObject <> ( name , baseType , modid );
}
2022-03-16 15:36:00 -05:00
/**
* Factory for a {@link RegistryObject} that stores the value of an object in a registry once it is ready.
*
* @param name The name of the object to look up in a forge registry
* @param key The key of the forge registry to grab from {@link RegistryManager#ACTIVE}
* @param modid The mod id calling context
* @return A {@link RegistryObject} that stores the value of an object in a registry once it is ready
*/
public static < T extends IForgeRegistryEntry < T > , U extends T > RegistryObject < U > of ( final ResourceLocation name , final ResourceKey <? extends Registry < T >> key , String modid ) {
return new RegistryObject <> ( name , key . location (), modid );
}
/**
* Factory for a {@link RegistryObject} that stores the value of an object in a registry once it is ready.
*
* @param name The name of the object to look up in a forge registry
* @param registryName The name of the forge registry to grab from {@link RegistryManager#ACTIVE}
* @param modid The mod id calling context
* @return A {@link RegistryObject} that stores the value of an object in a registry once it is ready
*/
public static < T extends IForgeRegistryEntry < T > , U extends T > RegistryObject < U > of ( final ResourceLocation name , final ResourceLocation registryName , String modid ) {
return new RegistryObject <> ( name , registryName , modid );
}
2018-06-19 14:04:05 -04:00
private static RegistryObject <?> EMPTY = new RegistryObject <> ();
2019-09-10 16:02:07 -07:00
private static < T extends IForgeRegistryEntry <? super T >> RegistryObject < T > empty () {
2018-06-19 14:04:05 -04:00
@SuppressWarnings ( "unchecked" )
RegistryObject < T > t = ( RegistryObject < T > ) EMPTY ;
return t ;
}
private RegistryObject () {
2019-09-10 16:02:07 -07:00
this . name = null ;
2018-06-19 14:04:05 -04:00
}
2022-03-16 15:36:00 -05:00
@Deprecated ( forRemoval = true , since = "1.18.2" )
2019-09-25 18:01:39 -04:00
private < V extends IForgeRegistryEntry < V >> RegistryObject ( ResourceLocation name , Supplier < Class <? super V >> registryType )
2018-06-19 14:04:05 -04:00
{
2019-09-10 16:02:07 -07:00
this ( name , RegistryManager . ACTIVE . < V > getRegistry ( registryType . get ()));
2018-06-19 14:04:05 -04:00
}
2019-09-10 16:02:07 -07:00
@SuppressWarnings ( "unchecked" )
2019-09-25 18:01:39 -04:00
private < V extends IForgeRegistryEntry < V >> RegistryObject ( ResourceLocation name , IForgeRegistry < V > registry )
2019-09-10 16:02:07 -07:00
{
if ( registry == null )
throw new IllegalArgumentException ( "Invalid registry argument, must not be null" );
2019-09-25 18:01:39 -04:00
this . name = name ;
2019-09-10 16:02:07 -07:00
ObjectHolderRegistry . addHandler ( pred ->
{
if ( pred . test ( registry . getRegistryName ()))
2022-02-08 22:05:38 +01:00
this . updateReference (( IForgeRegistry <? extends T > ) registry );
2019-09-10 16:02:07 -07:00
});
2022-02-08 22:05:38 +01:00
this . updateReference ((( IForgeRegistry <? extends T > ) registry ));
2019-09-10 16:02:07 -07:00
}
2020-06-12 15:28:43 -07:00
@SuppressWarnings ( "unchecked" )
2022-03-16 15:36:00 -05:00
@Deprecated ( forRemoval = true , since = "1.18.2" )
2020-06-12 15:28:43 -07:00
private < V extends IForgeRegistryEntry < V >> RegistryObject ( final ResourceLocation name , final Class < V > baseType , final String modid )
{
this . name = name ;
final Throwable callerStack = new Throwable ( "Calling Site from mod: " + modid );
ObjectHolderRegistry . addHandler ( new Consumer < Predicate < ResourceLocation >> ()
{
private IForgeRegistry < V > registry ;
@Override
public void accept ( Predicate < ResourceLocation > pred )
{
if ( registry == null )
{
this . registry = RegistryManager . ACTIVE . getRegistry ( baseType );
if ( registry == null )
2022-03-16 15:36:00 -05:00
throw new IllegalStateException ( "Unable to find registry for type " + baseType . getName () + " for mod \"" + modid + "\". Check the 'caused by' to see further stack." , callerStack );
2020-06-12 15:28:43 -07:00
}
if ( pred . test ( registry . getRegistryName ()))
2022-02-08 22:05:38 +01:00
RegistryObject . this . updateReference (( IForgeRegistry <? extends T > ) registry );
2020-06-12 15:28:43 -07:00
}
});
2022-02-08 22:05:38 +01:00
IForgeRegistry < V > registry = RegistryManager . ACTIVE . getRegistry ( baseType );
// allow registry to be null, this might be for a custom registry that does not exist yet
if ( registry != null )
{
this . updateReference ((( IForgeRegistry <? extends T > ) registry ));
}
2020-06-12 15:28:43 -07:00
}
2022-03-16 15:36:00 -05:00
@SuppressWarnings ( "unchecked" )
private < V extends IForgeRegistryEntry < V >> RegistryObject ( final ResourceLocation name , final ResourceLocation registryName , final String modid )
{
this . name = name ;
final Throwable callerStack = new Throwable ( "Calling Site from mod: " + modid );
ObjectHolderRegistry . addHandler ( new Consumer <> ()
{
private IForgeRegistry < V > registry ;
@Override
public void accept ( Predicate < ResourceLocation > pred )
{
if ( registry == null )
{
this . registry = RegistryManager . ACTIVE . getRegistry ( registryName );
if ( registry == null )
throw new IllegalStateException ( "Unable to find registry with key " + registryName + " for mod \"" + modid + "\". Check the 'caused by' to see further stack." , callerStack );
}
if ( pred . test ( registry . getRegistryName ()))
RegistryObject . this . updateReference (( IForgeRegistry <? extends T > ) registry );
}
});
IForgeRegistry < V > registry = RegistryManager . ACTIVE . getRegistry ( registryName );
// allow registry to be null, this might be for a custom registry that does not exist yet
if ( registry != null )
{
this . updateReference ((( IForgeRegistry <? extends T > ) registry ));
}
}
2019-09-10 16:02:07 -07:00
/**
* Directly retrieves the wrapped Registry Object. This value will automatically be updated when the backing registry is updated.
2020-04-24 19:18:57 -07:00
* Will throw NPE if the value is null, use isPresent to check first. Or use any of the other guarded functions.
2019-09-10 16:02:07 -07:00
*/
2019-09-25 18:01:39 -04:00
@Override
2019-12-17 04:50:13 -05:00
@Nonnull
2019-09-10 16:02:07 -07:00
public T get ()
2018-06-19 14:04:05 -04:00
{
2019-12-17 04:50:13 -05:00
T ret = this . value ;
2020-04-05 00:45:59 -04:00
Objects . requireNonNull ( ret , () -> "Registry Object not present: " + this . name );
2019-12-17 04:50:13 -05:00
return ret ;
2018-06-19 14:04:05 -04:00
}
2022-02-08 22:05:38 +01:00
@Deprecated ( since = "1.18.1" ) // TODO: make package-private
2019-09-25 18:01:39 -04:00
public void updateReference ( IForgeRegistry <? extends T > registry )
{
2022-02-08 22:05:38 +01:00
this . value = registry . containsKey ( this . name ) ? registry . getValue ( getId ()) : null ;
2019-09-25 18:01:39 -04:00
}
2020-04-24 19:18:57 -07:00
public ResourceLocation getId ()
2019-09-25 18:01:39 -04:00
{
return this . name ;
}
2018-06-19 14:04:05 -04:00
public Stream < T > stream () {
2019-09-10 16:02:07 -07:00
return isPresent () ? Stream . of ( get ()) : Stream . of ();
2018-06-19 14:04:05 -04:00
}
2019-09-10 16:02:07 -07:00
2018-06-19 14:04:05 -04:00
/**
* Return {@code true} if there is a mod object present, otherwise {@code false}.
*
* @return {@code true} if there is a mod object present, otherwise {@code false}
*/
public boolean isPresent () {
2020-04-24 19:18:57 -07:00
return this . value != null ;
2018-06-19 14:04:05 -04:00
}
/**
* If a mod object is present, invoke the specified consumer with the object,
* otherwise do nothing.
*
* @param consumer block to be executed if a mod object is present
* @throws NullPointerException if mod object is present and {@code consumer} is
* null
*/
public void ifPresent ( Consumer <? super T > consumer ) {
2020-04-24 19:18:57 -07:00
if ( isPresent ())
2019-09-10 16:02:07 -07:00
consumer . accept ( get ());
2018-06-19 14:04:05 -04:00
}
/**
* If a mod object is present, and the mod object matches the given predicate,
* return an {@code RegistryObject} describing the value, otherwise return an
* empty {@code RegistryObject}.
*
* @param predicate a predicate to apply to the mod object, if present
* @return an {@code RegistryObject} describing the value of this {@code RegistryObject}
* if a mod object is present and the mod object matches the given predicate,
* otherwise an empty {@code RegistryObject}
* @throws NullPointerException if the predicate is null
*/
public RegistryObject < T > filter ( Predicate <? super T > predicate ) {
Objects . requireNonNull ( predicate );
if ( ! isPresent ())
return this ;
else
2019-09-10 16:02:07 -07:00
return predicate . test ( get ()) ? this : empty ();
2018-06-19 14:04:05 -04:00
}
/**
* If a mod object is present, apply the provided mapping function to it,
* and if the result is non-null, return an {@code Optional} describing the
* result. Otherwise return an empty {@code Optional}.
*
* @apiNote This method supports post-processing on optional values, without
* the need to explicitly check for a return status.
*
* @param <U> The type of the result of the mapping function
* @param mapper a mapping function to apply to the mod object, if present
* @return an {@code Optional} describing the result of applying a mapping
* function to the mod object of this {@code RegistryObject}, if a mod object is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the mapping function is null
*/
public < U > Optional < U > map ( Function <? super T , ? extends U > mapper ) {
Objects . requireNonNull ( mapper );
if ( ! isPresent ())
return Optional . empty ();
else {
2019-09-10 16:02:07 -07:00
return Optional . ofNullable ( mapper . apply ( get ()));
2018-06-19 14:04:05 -04:00
}
}
/**
* If a value is present, apply the provided {@code Optional}-bearing
* mapping function to it, return that result, otherwise return an empty
* {@code Optional}. This method is similar to {@link #map(Function)},
* but the provided mapper is one whose result is already an {@code Optional},
* and if invoked, {@code flatMap} does not wrap it with an additional
* {@code Optional}.
*
* @param <U> The type parameter to the {@code Optional} returned by
* @param mapper a mapping function to apply to the mod object, if present
* the mapping function
* @return the result of applying an {@code Optional}-bearing mapping
* function to the value of this {@code Optional}, if a value is present,
* otherwise an empty {@code Optional}
* @throws NullPointerException if the mapping function is null or returns
* a null result
*/
public < U > Optional < U > flatMap ( Function <? super T , Optional < U >> mapper ) {
Objects . requireNonNull ( mapper );
if ( ! isPresent ())
return Optional . empty ();
else {
2019-09-10 16:02:07 -07:00
return Objects . requireNonNull ( mapper . apply ( get ()));
2018-06-19 14:04:05 -04:00
}
}
2020-04-24 19:18:57 -07:00
2019-09-25 18:01:39 -04:00
/**
* If a mod object is present, lazily apply the provided mapping function to it,
* returning a supplier for the transformed result. If this object is empty, or the
* mapping function returns {@code null}, the supplier will return {@code null}.
*
* @apiNote This method supports post-processing on optional values, without
* the need to explicitly check for a return status.
*
* @param <U> The type of the result of the mapping function
* @param mapper A mapping function to apply to the mod object, if present
* @return A {@code Supplier} lazily providing the result of applying a mapping
* function to the mod object of this {@code RegistryObject}, if a mod object is present,
* otherwise a supplier returning {@code null}
* @throws NullPointerException if the mapping function is {@code null}
*/
public < U > Supplier < U > lazyMap ( Function <? super T , ? extends U > mapper ) {
Objects . requireNonNull ( mapper );
return () -> isPresent () ? mapper . apply ( get ()) : null ;
}
2018-06-19 14:04:05 -04:00
/**
* Return the mod object if present, otherwise return {@code other}.
*
* @param other the mod object to be returned if there is no mod object present, may
* be null
* @return the mod object, if present, otherwise {@code other}
*/
public T orElse ( T other ) {
2019-09-25 18:01:39 -04:00
return isPresent () ? get () : other ;
2018-06-19 14:04:05 -04:00
}
/**
* Return the mod object if present, otherwise invoke {@code other} and return
* the result of that invocation.
*
* @param other a {@code Supplier} whose result is returned if no mod object
* is present
* @return the mod object if present otherwise the result of {@code other.get()}
* @throws NullPointerException if mod object is not present and {@code other} is
* null
*/
public T orElseGet ( Supplier <? extends T > other ) {
2019-09-25 18:01:39 -04:00
return isPresent () ? get () : other . get ();
2018-06-19 14:04:05 -04:00
}
/**
* Return the contained mod object, if present, otherwise throw an exception
* to be created by the provided supplier.
*
* @apiNote A method reference to the exception constructor with an empty
* argument list can be used as the supplier. For example,
* {@code IllegalStateException::new}
*
* @param <X> Type of the exception to be thrown
* @param exceptionSupplier The supplier which will return the exception to
* be thrown
* @return the present mod object
* @throws X if there is no mod object present
* @throws NullPointerException if no mod object is present and
* {@code exceptionSupplier} is null
*/
public < X extends Throwable > T orElseThrow ( Supplier <? extends X > exceptionSupplier ) throws X {
2020-04-24 19:18:57 -07:00
if ( isPresent ()) {
2019-09-10 16:02:07 -07:00
return get ();
2018-06-19 14:04:05 -04:00
} else {
throw exceptionSupplier . get ();
}
}
@Override
public boolean equals ( Object obj )
{
if ( this == obj ) return true ;
if ( obj instanceof RegistryObject ) {
2019-09-10 16:02:07 -07:00
return Objects . equals ((( RegistryObject <?> ) obj ). name , name );
2018-06-19 14:04:05 -04:00
}
return false ;
}
@Override
public int hashCode ()
{
return Objects . hashCode ( name );
}
}