mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-11-05 01:26:20 +00:00
Rewrite turtle upgrade modeller registration API
Originally we exposed a single registerTurtleUpgradeModellermethod which could be called from both Fabric (during a mod's client init) and Forge (during FMLClientSetupEvent). This was fine until we allowed upgrades to specify model dependencies, which would then automatically loaded, as this means model loading now depends on upgrade modellers being loaded. Unknown to me, this is not guaranteed to be the case on Forge - mod setup happens at the same time as resource reloading! Unfortunately there's not really a salvageable way of fixing this with the current API. Forge now uses a registration event-based system, meaning we can guarantee all modellers are loaded before models are baked.
This commit is contained in:
parent
36599b321e
commit
a617d0d566
@ -27,8 +27,13 @@ public final class ComputerCraftAPIClient {
|
||||
* @param serialiser The turtle upgrade serialiser.
|
||||
* @param modeller The upgrade modeller.
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
* @deprecated This method can lead to confusing load behaviour on Forge. Use
|
||||
* {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} on Fabric, or
|
||||
* {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} on Forge.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||
// TODO(1.20.4): Remove this
|
||||
getInstance().registerTurtleUpgradeModeller(serialiser, modeller);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.client.turtle;
|
||||
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
|
||||
/**
|
||||
* A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
||||
* <p>
|
||||
* This interface is largely intended to be used from multi-loader code, to allow sharing registration code between
|
||||
* multiple loaders.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RegisterTurtleUpgradeModeller {
|
||||
/**
|
||||
* Register a {@link TurtleUpgradeModeller}.
|
||||
*
|
||||
* @param serialiser The turtle upgrade serialiser.
|
||||
* @param modeller The upgrade modeller.
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
*/
|
||||
<T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
|
||||
}
|
@ -4,12 +4,10 @@
|
||||
|
||||
package dan200.computercraft.api.client.turtle;
|
||||
|
||||
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
||||
import dan200.computercraft.api.client.TransformedModel;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@ -21,9 +19,13 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides models for a {@link ITurtleUpgrade}.
|
||||
* <p>
|
||||
* Use {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} to register a
|
||||
* modeller on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} to register one
|
||||
* on Forge
|
||||
*
|
||||
* @param <T> The type of turtle upgrade this modeller applies to.
|
||||
* @see ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller) To register a modeller.
|
||||
* @see RegisterTurtleUpgradeModeller For multi-loader registration support.
|
||||
*/
|
||||
public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
/**
|
||||
|
@ -48,13 +48,8 @@ import java.util.function.Function;
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Finally, we need to register a model for our upgrade. This is done with
|
||||
* {@link dan200.computercraft.api.client.ComputerCraftAPIClient#registerTurtleUpgradeModeller}:
|
||||
*
|
||||
* <pre>{@code
|
||||
* // Register our model inside FMLClientSetupEvent
|
||||
* ComputerCraftAPIClient.registerTurtleUpgradeModeller(MY_UPGRADE.get(), TurtleUpgradeModeller.flatItem())
|
||||
* }</pre>
|
||||
* Finally, we need to register a model for our upgrade. The way to do this varies on mod loader, see
|
||||
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
|
||||
* <p>
|
||||
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
|
||||
*
|
||||
|
@ -5,8 +5,8 @@
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
|
||||
import dan200.computercraft.client.gui.*;
|
||||
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
@ -60,18 +60,6 @@ public final class ClientRegistry {
|
||||
* Register any client-side objects which don't have to be done on the main thread.
|
||||
*/
|
||||
public static void register() {
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
|
||||
));
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
|
||||
));
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
|
||||
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
|
||||
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), MonitorBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
||||
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
||||
@ -103,6 +91,20 @@ public final class ClientRegistry {
|
||||
);
|
||||
}
|
||||
|
||||
public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) {
|
||||
register.register(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
|
||||
));
|
||||
register.register(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
|
||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
|
||||
));
|
||||
register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
|
||||
register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
|
||||
register.register(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private static void registerItemProperty(String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
||||
|
@ -11,11 +11,14 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.PlatformHelper;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.impl.UpgradeManager;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
@ -24,14 +27,15 @@ import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A registry of {@link TurtleUpgradeModeller}s.
|
||||
*
|
||||
* @see dan200.computercraft.api.client.ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller)
|
||||
*/
|
||||
public final class TurtleUpgradeModellers {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TurtleUpgradeModellers.class);
|
||||
|
||||
private static final TurtleUpgradeModeller<ITurtleUpgrade> NULL_TURTLE_MODELLER = (upgrade, turtle, side) ->
|
||||
new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity());
|
||||
|
||||
private static final Map<TurtleUpgradeSerialiser<?>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>();
|
||||
private static volatile boolean fetchedModels;
|
||||
|
||||
/**
|
||||
* In order to avoid a double lookup of {@link ITurtleUpgrade} to {@link UpgradeManager.UpgradeWrapper} to
|
||||
@ -45,12 +49,18 @@ public final class TurtleUpgradeModellers {
|
||||
}
|
||||
|
||||
public static <T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||
synchronized (turtleModels) {
|
||||
if (turtleModels.containsKey(serialiser)) {
|
||||
throw new IllegalStateException("Modeller already registered for serialiser");
|
||||
}
|
||||
if (fetchedModels) {
|
||||
// TODO(1.20.4): Replace with an error.
|
||||
LOG.warn(
|
||||
"Turtle upgrade serialiser {} was registered too late, its models may not be loaded correctly. If you are " +
|
||||
"the mod author, you may be using a deprecated API - see https://github.com/cc-tweaked/CC-Tweaked/pull/1684 " +
|
||||
"for further information.",
|
||||
PlatformHelper.get().getRegistryKey(TurtleUpgradeSerialiser.registryId(), serialiser)
|
||||
);
|
||||
}
|
||||
|
||||
turtleModels.put(serialiser, modeller);
|
||||
if (turtleModels.putIfAbsent(serialiser, modeller) != null) {
|
||||
throw new IllegalStateException("Modeller already registered for serialiser");
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,6 +85,7 @@ public final class TurtleUpgradeModellers {
|
||||
}
|
||||
|
||||
public static Stream<ResourceLocation> getDependencies() {
|
||||
fetchedModels = true;
|
||||
return turtleModels.values().stream().flatMap(x -> x.getDependencies().stream());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.client;
|
||||
|
||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
||||
|
||||
/**
|
||||
* The Fabric-specific entrypoint for ComputerCraft's API.
|
||||
*
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI The main API
|
||||
* @see dan200.computercraft.api.client.ComputerCraftAPIClient The main client-side API
|
||||
*/
|
||||
public final class FabricComputerCraftAPIClient {
|
||||
private FabricComputerCraftAPIClient() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
||||
* <p>
|
||||
* This may be called at any point after registry creation, though it is recommended to call it within your client
|
||||
* setup step.
|
||||
* <p>
|
||||
* This method may be used as a {@link dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller}, for
|
||||
* convenient use in multi-loader code.
|
||||
*
|
||||
* @param serialiser The turtle upgrade serialiser.
|
||||
* @param modeller The upgrade modeller.
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
*/
|
||||
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||
getInstance().registerTurtleUpgradeModeller(serialiser, modeller);
|
||||
}
|
||||
|
||||
private static ComputerCraftAPIClientService getInstance() {
|
||||
return ComputerCraftAPIClientService.get();
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.client.FabricComputerCraftAPIClient;
|
||||
import dan200.computercraft.client.model.CustomModelLoader;
|
||||
import dan200.computercraft.impl.Services;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
@ -40,6 +41,7 @@ public class ComputerCraftClient {
|
||||
}
|
||||
|
||||
ClientRegistry.register();
|
||||
ClientRegistry.registerTurtleModellers(FabricComputerCraftAPIClient::registerTurtleUpgradeModeller);
|
||||
ClientRegistry.registerItemColours(ColorProviderRegistry.ITEM::register);
|
||||
ClientRegistry.registerMainThread();
|
||||
|
||||
|
@ -0,0 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.client.turtle;
|
||||
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeType;
|
||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
|
||||
/**
|
||||
* This event is fired to register {@link TurtleUpgradeModeller}s for a mod's {@linkplain TurtleUpgradeType turtle
|
||||
* upgrades}.
|
||||
* <p>
|
||||
* This event is fired during the initial resource load. Registries will be frozen, but mods may not be fully
|
||||
* initialised at this point (i.e. {@link FMLCommonSetupEvent} or {@link FMLClientSetupEvent} may not have been
|
||||
* dispatched). Subscribers should be careful not to
|
||||
*/
|
||||
public class RegisterTurtleModellersEvent extends Event implements IModBusEvent, RegisterTurtleUpgradeModeller {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public <T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||
ComputerCraftAPIClientService.get().registerTurtleUpgradeModeller(serialiser, modeller);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent;
|
||||
import dan200.computercraft.client.model.turtle.TurtleModelLoader;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
@ -13,6 +14,7 @@ import net.minecraftforge.client.event.RegisterClientReloadListenersEvent;
|
||||
import net.minecraftforge.client.event.RegisterColorHandlersEvent;
|
||||
import net.minecraftforge.client.event.RegisterShadersEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
@ -23,6 +25,9 @@ import java.io.IOException;
|
||||
*/
|
||||
@Mod.EventBusSubscriber(modid = ComputerCraftAPI.MOD_ID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
|
||||
public final class ForgeClientRegistry {
|
||||
private static final Object lock = new Object();
|
||||
private static boolean gatheredModellers = false;
|
||||
|
||||
private ForgeClientRegistry() {
|
||||
}
|
||||
|
||||
@ -31,8 +36,26 @@ public final class ForgeClientRegistry {
|
||||
event.register("turtle", TurtleModelLoader.INSTANCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turtle upgrade modellers must be loaded before we gather additional models.
|
||||
* <p>
|
||||
* Unfortunately, due to the nature of parallel mod loading (resource loading and mod setup events are fired in
|
||||
* parallel), there's no way to guarantee this using existing events. Instead, we piggyback off
|
||||
* {@link ModelEvent.RegisterAdditional}, registering models the first time the event is fired.
|
||||
*/
|
||||
private static void gatherModellers() {
|
||||
if (gatheredModellers) return;
|
||||
synchronized (lock) {
|
||||
if (gatheredModellers) return;
|
||||
|
||||
gatheredModellers = true;
|
||||
ModLoader.get().postEvent(new RegisterTurtleModellersEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void registerModels(ModelEvent.RegisterAdditional event) {
|
||||
gatherModellers();
|
||||
ClientRegistry.registerExtraModels(event::register);
|
||||
}
|
||||
|
||||
@ -41,6 +64,11 @@ public final class ForgeClientRegistry {
|
||||
ClientRegistry.registerShaders(event.getResourceProvider(), event::registerShader);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onTurtleModellers(RegisterTurtleModellersEvent event) {
|
||||
ClientRegistry.registerTurtleModellers(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onItemColours(RegisterColorHandlersEvent.Item event) {
|
||||
ClientRegistry.registerItemColours(event::register);
|
||||
|
Loading…
Reference in New Issue
Block a user