2018-06-19 14:04:05 -04:00
/*
2022-04-14 03:42:00 +00:00
* Copyright (c) Forge Development LLC and contributors
2022-02-28 13:41:23 -08:00
* 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-04-06 16:13:17 -05:00
import net.minecraft.core.Holder ;
2024-05-12 10:53:10 -07:00
import net.minecraft.core.MappedRegistry ;
2022-03-16 15:36:00 -05:00
import net.minecraft.core.Registry ;
2022-11-22 13:32:47 -06:00
import net.minecraft.core.registries.BuiltInRegistries ;
2022-03-16 15:36:00 -05:00
import net.minecraft.resources.ResourceKey ;
2021-07-17 00:08:49 -05:00
import net.minecraft.resources.ResourceLocation ;
2022-04-06 16:13:17 -05:00
import org.jetbrains.annotations.NotNull ;
import org.jetbrains.annotations.Nullable ;
2019-09-25 18:01:39 -04:00
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 ;
2024-05-12 10:53:10 -07:00
public final class RegistryObject < T > implements Supplier < T > {
2022-04-06 16:13:17 -05:00
@Nullable
2019-09-10 16:02:07 -07:00
private final ResourceLocation name ;
2022-04-06 16:13:17 -05:00
@Nullable
2026-10-12 22:18:32 +02:00
private final ResourceKey < T > key ;
2019-09-10 16:02:07 -07:00
@Nullable
2018-06-19 14:04:05 -04:00
private T value ;
2022-04-06 16:13:17 -05:00
@Nullable
2024-05-12 10:53:10 -07:00
private Supplier < Holder < T >> holder ;
2018-06-19 14:04:05 -04:00
2022-04-01 15:49:46 -05:00
/**
* Factory for a {@link RegistryObject} that stores the value of an object from the provided forge registry once it is ready.
*
* @param name the name of the object to look up in the forge registry
* @param registry the forge registry
* @return a {@link RegistryObject} that stores the value of an object from the provided forge registry once it is ready
*/
2024-05-12 10:53:10 -07:00
public static < T , U extends T > RegistryObject < U > create ( final ResourceLocation name , IForgeRegistry < T > registry ) {
2022-06-07 15:31:26 -05:00
return new RegistryObject < U > ( name , registry );
2022-04-01 15:49:46 -05:00
}
/**
* Factory for a {@link RegistryObject} that stores the value of an object from a registry once it is ready based on a lookup of the provided registry key.
* <p>
* If a registry with the given key cannot be found, an exception will be thrown when trying to fill this RegistryObject.
* Use {@link #createOptional(ResourceLocation, ResourceKey, String)} for RegistryObjects of optional registries.
*
* @param name the name of the object to look up in a registry
2022-11-22 13:32:47 -06:00
* @param registryKey the key of the registry. Supports lookups on {@link BuiltInRegistries} and {@link RegistryManager#ACTIVE}.
2022-04-01 15:49:46 -05:00
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #createOptional(ResourceLocation, ResourceKey, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceLocation, String)
*/
2024-05-12 10:53:10 -07:00
public static < T , U extends T > RegistryObject < U > create ( final ResourceLocation name , final ResourceKey <? extends Registry < T >> registryKey , String modid ) {
2022-04-01 15:49:46 -05:00
return new RegistryObject <> ( name , registryKey . location (), modid , false );
}
/**
* Factory for a {@link RegistryObject} that optionally stores the value of an object from a registry once it is ready if the registry exists
* based on a lookup of the provided registry key.
* <p>
* If a registry with the given key cannot be found, it will be silently ignored and this RegistryObject will not be filled.
* Use {@link #create(ResourceLocation, ResourceKey, String)} for RegistryObjects that should throw exceptions on missing registry.
*
* @param name the name of the object to look up in a registry
2022-11-22 13:32:47 -06:00
* @param registryKey the key of the registry. Supports lookups on {@link BuiltInRegistries} and {@link RegistryManager#ACTIVE}.
2022-04-01 15:49:46 -05:00
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #create(ResourceLocation, ResourceKey, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceLocation, String)
*/
2024-05-12 10:53:10 -07:00
public static < T , U extends T > RegistryObject < U > createOptional ( final ResourceLocation name , final ResourceKey <? extends Registry < T >> registryKey , String modid ) {
2022-04-01 15:49:46 -05:00
return new RegistryObject <> ( name , registryKey . location (), modid , true );
}
/**
* Factory for a {@link RegistryObject} that stores the value of an object from a registry once it is ready based on a lookup of the provided registry name.
* <p>
* If a registry with the given name cannot be found, an exception will be thrown when trying to fill this RegistryObject.
* Use {@link #createOptional(ResourceLocation, ResourceLocation, String)} for RegistryObjects of optional registries.
2022-03-16 15:36:00 -05:00
*
2022-04-01 15:49:46 -05:00
* @param name the name of the object to look up in a registry
2022-11-22 13:32:47 -06:00
* @param registryName the name of the registry. Supports lookups on {@link BuiltInRegistries} and {@link RegistryManager#ACTIVE}.
2022-04-01 15:49:46 -05:00
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #createOptional(ResourceLocation, ResourceLocation, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceKey, String)
2022-03-16 15:36:00 -05:00
*/
2024-05-12 10:53:10 -07:00
public static < T , U extends T > RegistryObject < U > create ( final ResourceLocation name , final ResourceLocation registryName , String modid ) {
2022-04-01 15:49:46 -05:00
return new RegistryObject <> ( name , registryName , modid , false );
2022-03-16 15:36:00 -05:00
}
/**
2022-04-01 15:49:46 -05:00
* Factory for a {@link RegistryObject} that optionally stores the value of an object from a registry once it is ready if the registry exists
* based on a lookup of the provided registry name.
* <p>
* If a registry with the given name cannot be found, it will be silently ignored and this RegistryObject will not be filled.
* Use {@link #create(ResourceLocation, ResourceLocation, String)} for RegistryObjects that should throw exceptions on missing registry.
2022-03-16 15:36:00 -05:00
*
2022-04-01 15:49:46 -05:00
* @param name the name of the object to look up in a registry
2022-11-22 13:32:47 -06:00
* @param registryName the name of the registry. Supports lookups on {@link BuiltInRegistries} and {@link RegistryManager#ACTIVE}.
2022-04-01 15:49:46 -05:00
* @param modid the mod id calling context
* @return a {@link RegistryObject} that stores the value of an object from a registry once it is ready
* @see #create(ResourceLocation, ResourceLocation, String)
* @see #create(ResourceLocation, IForgeRegistry)
* @see #create(ResourceLocation, ResourceKey, String)
2022-03-16 15:36:00 -05:00
*/
2024-05-12 10:53:10 -07:00
public static < T , U extends T > RegistryObject < U > createOptional ( final ResourceLocation name , final ResourceLocation registryName , String modid ) {
2022-04-01 15:49:46 -05:00
return new RegistryObject <> ( name , registryName , modid , true );
2022-03-16 15:36:00 -05:00
}
2022-04-06 16:13:17 -05:00
private static final RegistryObject <?> EMPTY = new RegistryObject <> ();
2018-06-19 14:04:05 -04:00
2022-04-01 15:49:46 -05:00
private static < 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 ;
2022-06-07 15:31:26 -05:00
this . key = null ;
2018-06-19 14:04:05 -04:00
}
2019-09-10 16:02:07 -07:00
@SuppressWarnings ( "unchecked" )
2024-05-12 10:53:10 -07:00
private RegistryObject ( ResourceLocation name , IForgeRegistry <?> 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 ;
2022-06-07 15:31:26 -05:00
this . key = ( ResourceKey < T > ) ResourceKey . create ( registry . getRegistryKey (), name );
2026-10-12 22:18:32 +02:00
ObjectHolderRegistry . addHandler ( registry . getRegistryName (), () -> this . updateReference (( IForgeRegistry <? extends T > ) registry ));
2022-06-07 15:31:26 -05:00
this . updateReference (( IForgeRegistry <? extends T > ) registry );
2020-06-12 15:28:43 -07:00
}
2024-05-12 10:53:10 -07:00
private RegistryObject ( final ResourceLocation name , final ResourceLocation registryName , final String modid , boolean optionalRegistry ) {
2022-03-16 15:36:00 -05:00
this . name = name ;
2022-04-06 16:13:17 -05:00
this . key = ResourceKey . create ( ResourceKey . createRegistryKey ( registryName ), name );
2026-10-12 22:18:32 +02:00
if ( ! optionalRegistry ) {
final Throwable callerStack = new Throwable ( "Calling Site from mod: " + modid );
ObjectHolderRegistry . addValidator (() -> {
if ( ! registryExists ( registryName ))
throw new IllegalStateException ( "Unable to find registry with key " + registryName + " for mod \"" + modid + "\". Check the 'caused by' to see further stack." , callerStack );
});
}
ObjectHolderRegistry . addHandler ( registryName , () -> this . updateReference ( registryName ));
2022-04-01 15:49:46 -05:00
this . updateReference ( registryName );
2022-03-16 15:36:00 -05:00
}
2019-09-10 16:02:07 -07:00
/**
2022-04-06 16:13:17 -05:00
* Retrieves the wrapped object in the registry.
* This value will automatically be updated when the backing registry is updated.
*
* @throws NullPointerException If the value is null. Use {@link #isPresent()} to check if the value exists first.
* @see #isPresent()
* @see #orElse(Object)
* @see #orElseGet(Supplier)
* @see #orElseThrow(Supplier)
2019-09-10 16:02:07 -07:00
*/
2022-04-06 16:13:17 -05:00
@NotNull
2019-09-25 18:01:39 -04:00
@Override
2024-05-12 10:53:10 -07:00
public T get () {
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-04-06 16:13:17 -05:00
@SuppressWarnings ( "unchecked" )
2024-05-12 10:53:10 -07:00
void updateReference ( IForgeRegistry <? extends T > registry ) {
2022-06-07 15:31:26 -05:00
if ( this . name == null || this . key == null )
2022-04-06 16:13:17 -05:00
return ;
2024-05-12 10:53:10 -07:00
var wrapper = registry instanceof ForgeRegistry forge ? forge . getWrapper () : null ;
if ( registry . containsKey ( this . name )) {
2022-04-06 16:13:17 -05:00
this . value = registry . getValue ( this . name );
2024-05-12 10:53:10 -07:00
this . holder = () -> ( Holder < T > ) registry . getHolder ( this . name ). orElse ( null );
} else if ( wrapper != null && ! wrapper . isIntrusive ()) {
this . value = null ;
this . holder = () -> wrapper . createRegistrationLookup (). getOrThrow ( this . key );
} else {
2022-04-06 16:13:17 -05:00
this . value = null ;
this . holder = null ;
}
2022-04-01 15:49:46 -05:00
}
2022-04-06 16:13:17 -05:00
@SuppressWarnings ( "unchecked" )
2024-05-12 10:53:10 -07:00
void updateReference ( Registry <? extends T > registry ) {
2022-06-07 15:31:26 -05:00
if ( this . name == null || this . key == null )
2022-04-06 16:13:17 -05:00
return ;
2024-05-12 10:53:10 -07:00
var mapped = registry instanceof MappedRegistry m ? m : null ;
if ( registry . containsKey ( this . name )) {
2022-04-06 16:13:17 -05:00
this . value = registry . get ( this . name );
2024-05-12 10:53:10 -07:00
this . holder = () -> (( Registry < T > ) registry ). getHolder ( this . key ). orElse ( null );
} else if ( mapped != null && ! mapped . isIntrusive ()) {
this . value = null ;
this . holder = () -> mapped . createRegistrationLookup (). getOrThrow ( this . key );
} else {
2022-04-06 16:13:17 -05:00
this . value = null ;
this . holder = null ;
}
2022-04-01 15:49:46 -05:00
}
@SuppressWarnings ( "unchecked" )
2024-05-12 10:53:10 -07:00
void updateReference ( ResourceLocation registryName ) {
2022-04-06 16:13:17 -05:00
if ( this . name == null )
return ;
2024-05-12 10:53:10 -07:00
2022-04-01 15:49:46 -05:00
IForgeRegistry <? extends T > forgeRegistry = RegistryManager . ACTIVE . getRegistry ( registryName );
2024-05-12 10:53:10 -07:00
if ( forgeRegistry != null ) {
2022-04-01 15:49:46 -05:00
updateReference ( forgeRegistry );
return ;
}
2022-11-22 13:32:47 -06:00
Registry <? extends T > vanillaRegistry = ( Registry <? extends T > ) BuiltInRegistries . REGISTRY . get ( registryName );
2024-05-12 10:53:10 -07:00
if ( vanillaRegistry != null ) {
2022-04-01 15:49:46 -05:00
updateReference ( vanillaRegistry );
return ;
}
this . value = null ;
2022-04-06 16:13:17 -05:00
this . holder = null ;
2022-04-01 15:49:46 -05:00
}
2024-05-12 10:53:10 -07:00
void updateReference ( RegisterEvent event ) {
2022-06-07 15:31:26 -05:00
IForgeRegistry <? extends T > forgeRegistry = event . getForgeRegistry ();
2024-05-12 10:53:10 -07:00
if ( forgeRegistry != null ) {
2022-06-07 15:31:26 -05:00
updateReference ( forgeRegistry );
return ;
}
Registry <? extends T > vanillaRegistry = event . getVanillaRegistry ();
if ( vanillaRegistry != null )
updateReference ( vanillaRegistry );
else
this . value = null ;
}
2024-05-12 10:53:10 -07:00
private static boolean registryExists ( ResourceLocation registryName ) {
2022-04-01 15:49:46 -05:00
return RegistryManager . ACTIVE . getRegistry ( registryName ) != null
2022-11-22 13:32:47 -06:00
|| BuiltInRegistries . REGISTRY . containsKey ( registryName );
2019-09-25 18:01:39 -04:00
}
2024-05-12 10:53:10 -07:00
public ResourceLocation getId () {
2019-09-25 18:01:39 -04:00
return this . name ;
}
2022-04-06 16:13:17 -05:00
/**
* Returns the resource key that points to the registry and name of this registry object.
2022-06-07 15:31:26 -05:00
* Nullable only if this RegistryObject is empty and has no name.
2022-04-06 16:13:17 -05:00
*
* @return the resource key that points to the registry and name of this registry object
*/
@Nullable
2024-05-12 10:53:10 -07:00
public ResourceKey < T > getKey () {
2022-04-06 16:13:17 -05:00
return this . key ;
}
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 ();
}
}
2022-04-06 16:13:17 -05:00
/**
* Returns an optional {@link Holder} instance pointing to this RegistryObject's name and value.
* <p>
* This should <b>only</b> be used in cases where vanilla code requires passing in a Holder.
* Mod-written code should rely on RegistryObjects or Suppliers instead.
* <p>
2022-11-22 13:32:47 -06:00
* The returned optional will be empty if the registry does not exist or if {@link #isPresent() returns false}.
2022-04-06 16:13:17 -05:00
*
* @return an optional {@link Holder} instance pointing to this RegistryObject's name and value
*/
@NotNull
2024-05-12 10:53:10 -07:00
public Optional < Holder < T >> getHolder () {
return Optional . ofNullable ( this . holder == null ? null : this . holder . get ());
2022-04-06 16:13:17 -05:00
}
2018-06-19 14:04:05 -04:00
@Override
2024-05-12 10:53:10 -07:00
public boolean equals ( Object obj ) {
2018-06-19 14:04:05 -04:00
if ( this == obj ) return true ;
2026-07-21 21:16:17 +03:00
if ( obj instanceof RegistryObject <?> o ) {
return o . key == key && Objects . equals ( o . name , name );
2018-06-19 14:04:05 -04:00
}
return false ;
}
@Override
2024-05-12 10:53:10 -07:00
public int hashCode () {
2018-06-19 14:04:05 -04:00
return Objects . hashCode ( name );
}
}