1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-01-15 03:35:42 +00:00

Register turtle upgrade models separately

ITurtleUpgrade.getModel has always been rather error-prone to use, due
to its client-only nature. As ModelResourceLocation is now client-only
again in Forge 1.19.1, no seems a good time to fix this.

The getter for models is now a separate interface inside a new
dan200.computercraft.api.client package. These are registered
per-TurtleUpgradeSerialiser (as those should correspond to class
anyway). It's a little ugly, and we may rename the XxxSerialiser classes
to something more general in a future update.

I'm not wild about the interface here either - happy to change it in
future versions too.

   * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Also clean up the generic arguments to IUpgradeBase/UpgradeSerialiser a
little bit. It's not great (wish Java had HKTs), but it's better.
This commit is contained in:
Jonathan Coates 2022-07-28 19:02:38 +01:00
parent 50fe7935a3
commit c43d851e63
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
23 changed files with 374 additions and 148 deletions

View File

@ -0,0 +1,57 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
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 net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import javax.annotation.Nonnull;
public final class ComputerCraftAPIClient
{
private ComputerCraftAPIClient()
{
}
/**
* Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
*
* This may be called at any point after registry creation, though it is recommended to call it within {@link FMLClientSetupEvent}.
*
* @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( @Nonnull TurtleUpgradeSerialiser<T> serialiser, @Nonnull TurtleUpgradeModeller<T> modeller )
{
getInstance().registerTurtleUpgradeModeller( serialiser, modeller );
}
private static IComputerCraftAPIClient instance;
@Nonnull
private static IComputerCraftAPIClient getInstance()
{
if( instance != null ) return instance;
try
{
return instance = (IComputerCraftAPIClient) Class.forName( "dan200.computercraft.client.ComputerCraftAPIClientImpl" )
.getField( "INSTANCE" ).get( null );
}
catch( ReflectiveOperationException e )
{
throw new IllegalStateException( "Cannot find ComputerCraft API", e );
}
}
public interface IComputerCraftAPIClient
{
<T extends ITurtleUpgrade> void registerTurtleUpgradeModeller( @Nonnull TurtleUpgradeSerialiser<T> serialiser, @Nonnull TurtleUpgradeModeller<T> modeller );
}
}

View File

@ -0,0 +1,68 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
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 javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Provides models for a {@link ITurtleUpgrade}.
*
* @param <T> The type of turtle upgrade this modeller applies to.
* @see ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller) To register a modeller.
*/
public interface TurtleUpgradeModeller<T extends ITurtleUpgrade>
{
/**
* Obtain the model to be used when rendering a turtle peripheral.
*
* When the current turtle is {@literal null}, this function should be constant for a given upgrade and side.
*
* @param upgrade The upgrade that you're getting the model for.
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models!
* @param side Which side of the turtle (left or right) the upgrade resides on.
* @return The model that you wish to be used to render your upgrade.
*/
@Nonnull
TransformedModel getModel( @Nonnull T upgrade, @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side );
/**
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getCraftingItem()
* crafting item}.
*
* This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
* model type. It will not appear correct for 3D models with additional depth, such as blocks.
*
* @param <T> The type of the turtle upgrade.
* @return The constructed modeller.
*/
@SuppressWarnings( "unchecked" )
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> flatItem()
{
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.FLAT_ITEM;
}
/**
* Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
*
* @param left The model to use on the left.
* @param right The model to use on the right.
* @param <T> The type of the turtle upgrade.
* @return The constructed modeller.
*/
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided( ModelResourceLocation left, ModelResourceLocation right )
{
return ( upgrade, turtle, side ) -> TransformedModel.of( side == TurtleSide.LEFT ? left : right );
}
}

View File

@ -0,0 +1,31 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.client.turtle;
import com.mojang.math.Matrix4f;
import com.mojang.math.Transformation;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
class TurtleUpgradeModellers
{
private static final Transformation leftTransform = getMatrixFor( -0.40625f );
private static final Transformation rightTransform = getMatrixFor( 0.40625f );
private static Transformation getMatrixFor( float offset )
{
return new Transformation( new Matrix4f( new float[] {
0.0f, 0.0f, -1.0f, 1.0f + offset,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f,
} ) );
}
static final TurtleUpgradeModeller<ITurtleUpgrade> FLAT_ITEM = ( upgrade, turtle, side ) ->
TransformedModel.of( upgrade.getCraftingItem(), side == TurtleSide.LEFT ? leftTransform : rightTransform );
}

View File

@ -33,7 +33,7 @@ import java.util.function.Function;
* @see IPocketUpgrade
* @see PocketUpgradeDataProvider
*/
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T, PocketUpgradeSerialiser<?>>
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T>
{
/**
* The ID for the associated registry.
@ -69,7 +69,7 @@ public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends Upgra
@Nonnull
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simple( @Nonnull Function<ResourceLocation, T> factory )
{
class Impl extends SimpleSerialiser<T, PocketUpgradeSerialiser<?>> implements PocketUpgradeSerialiser<T>
class Impl extends SimpleSerialiser<T> implements PocketUpgradeSerialiser<T>
{
private Impl( Function<ResourceLocation, T> constructor )
{
@ -92,7 +92,7 @@ public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends Upgra
@Nonnull
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simpleWithCustomItem( @Nonnull BiFunction<ResourceLocation, ItemStack, T> factory )
{
class Impl extends SerialiserWithCraftingItem<T, PocketUpgradeSerialiser<?>> implements PocketUpgradeSerialiser<T>
class Impl extends SerialiserWithCraftingItem<T> implements PocketUpgradeSerialiser<T>
{
private Impl( BiFunction<ResourceLocation, ItemStack, T> factory )
{

View File

@ -5,14 +5,9 @@
*/
package dan200.computercraft.api.turtle;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.level.BlockEvent;
@ -85,21 +80,6 @@ public interface ITurtleUpgrade extends IUpgradeBase
return TurtleCommandResult.failure();
}
/**
* Called to obtain the model to be used when rendering a turtle peripheral.
*
* This can be obtained from {@link net.minecraft.client.renderer.ItemModelShaper#getItemModel(ItemStack)},
* {@link net.minecraft.client.resources.model.ModelManager#getModel(ModelResourceLocation)} or any other
* source.
*
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models!
* @param side Which side of the turtle (left or right) the upgrade resides on.
* @return The model that you wish to be used to render your upgrade.
*/
@Nonnull
@OnlyIn( Dist.CLIENT )
TransformedModel getModel( @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side );
/**
* Called once per tick for each turtle which has the upgrade equipped.
*

View File

@ -6,6 +6,8 @@
package dan200.computercraft.api.turtle;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.client.ComputerCraftAPIClient;
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
@ -54,13 +56,22 @@ import java.util.function.Function;
* }
* }</pre>
*
* Finally, we need to register a model for our upgrade. This is done with
* {@link ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller)}:
*
* <pre>{@code
* // Register our model inside FMLClientSetupEvent
* ComputerCraftAPIClient.registerTurtleUpgradeModeller(MY_UPGRADE.get(), TurtleUpgradeModeller.flatItem())
* }</pre>
*
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
*
* @param <T> The type of turtle upgrade this is responsible for serialising.
* @see ITurtleUpgrade
* @see TurtleUpgradeDataProvider
* @see TurtleUpgradeModeller
*/
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T, TurtleUpgradeSerialiser<?>>
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T>
{
/**
* The ID for the associated registry.
@ -96,7 +107,7 @@ public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends Upgra
@Nonnull
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simple( @Nonnull Function<ResourceLocation, T> factory )
{
class Impl extends SimpleSerialiser<T, TurtleUpgradeSerialiser<?>> implements TurtleUpgradeSerialiser<T>
class Impl extends SimpleSerialiser<T> implements TurtleUpgradeSerialiser<T>
{
private Impl( Function<ResourceLocation, T> constructor )
{
@ -119,7 +130,7 @@ public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends Upgra
@Nonnull
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simpleWithCustomItem( @Nonnull BiFunction<ResourceLocation, ItemStack, T> factory )
{
class Impl extends SerialiserWithCraftingItem<T, TurtleUpgradeSerialiser<?>> implements TurtleUpgradeSerialiser<T>
class Impl extends SerialiserWithCraftingItem<T> implements TurtleUpgradeSerialiser<T>
{
private Impl( BiFunction<ResourceLocation, ItemStack, T> factory )
{

View File

@ -5,8 +5,6 @@
*/
package dan200.computercraft.api.upgrades;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
@ -36,10 +34,9 @@ import java.util.function.Function;
* @param <T> The base class of upgrades.
* @param <R> The upgrade serialiser to register for.
*/
public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends UpgradeSerialiser<?, R>> implements DataProvider
public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private final DataGenerator generator;
private final String name;
@ -134,7 +131,7 @@ public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends Upgr
try
{
@SuppressWarnings( "unchecked" ) var result = (T) upgrade.serialiser().fromJson( upgrade.id(), json );
var result = upgrade.serialiser().fromJson( upgrade.id(), json );
upgrades.add( result );
}
catch( IllegalArgumentException | JsonParseException e )
@ -176,7 +173,7 @@ public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends Upgr
* @param serialise Augment the generated JSON with additional fields.
* @param <R> The type of upgrade serialiser.
*/
public static record Upgrade<R extends UpgradeSerialiser<?, R>>(
public record Upgrade<R extends UpgradeSerialiser<?>>(
ResourceLocation id, R serialiser, Consumer<JsonObject> serialise
)
{

View File

@ -19,12 +19,11 @@ import javax.annotation.Nonnull;
*
* However, it may sometimes be useful to implement this if you have some shared logic between upgrade types.
*
* @param <R> The serialiser for this upgrade category, either {@code TurtleUpgradeSerialiser<?>} or {@code PocketUpgradeSerialiser<?>}.
* @param <T> The upgrade that this class can serialise and deserialise.
* @see TurtleUpgradeSerialiser
* @see PocketUpgradeSerialiser
*/
public interface UpgradeSerialiser<T extends IUpgradeBase, R extends UpgradeSerialiser<?, R>>
public interface UpgradeSerialiser<T extends IUpgradeBase>
{
/**
* Read this upgrade from a JSON file in a datapack.

View File

@ -6,10 +6,13 @@
package dan200.computercraft.client;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.client.ComputerCraftAPIClient;
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.render.TileEntityMonitorRenderer;
import dan200.computercraft.client.render.TileEntityTurtleRenderer;
import dan200.computercraft.client.render.TurtleModelLoader;
import dan200.computercraft.client.turtle.TurtleModemModeller;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
@ -130,6 +133,18 @@ public final class ClientRegistry
BlockEntityRenderers.register( Registry.ModBlockEntities.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new );
BlockEntityRenderers.register( Registry.ModBlockEntities.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new );
ComputerCraftAPIClient.registerTurtleUpgradeModeller( Registry.ModTurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
new ModelResourceLocation( "computercraft:turtle_speaker_upgrade_left", "inventory" ),
new ModelResourceLocation( "computercraft:turtle_speaker_upgrade_right", "inventory" )
) );
ComputerCraftAPIClient.registerTurtleUpgradeModeller( Registry.ModTurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
new ModelResourceLocation( "computercraft:turtle_crafting_table_left", "inventory" ),
new ModelResourceLocation( "computercraft:turtle_crafting_table_right", "inventory" )
) );
ComputerCraftAPIClient.registerTurtleUpgradeModeller( Registry.ModTurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller( false ) );
ComputerCraftAPIClient.registerTurtleUpgradeModeller( Registry.ModTurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller( true ) );
ComputerCraftAPIClient.registerTurtleUpgradeModeller( Registry.ModTurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem() );
event.enqueueWork( () -> {
registerContainers();

View File

@ -0,0 +1,29 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client;
import dan200.computercraft.api.client.ComputerCraftAPIClient;
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
import javax.annotation.Nonnull;
public final class ComputerCraftAPIClientImpl implements ComputerCraftAPIClient.IComputerCraftAPIClient
{
public static final ComputerCraftAPIClientImpl INSTANCE = new ComputerCraftAPIClientImpl();
private ComputerCraftAPIClientImpl()
{
}
@Override
public <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller( @Nonnull TurtleUpgradeSerialiser<T> serialiser, @Nonnull TurtleUpgradeModeller<T> modeller )
{
TurtleUpgradeModellers.register( serialiser, modeller );
}
}

View File

@ -12,6 +12,7 @@ import com.mojang.math.Vector3f;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.turtle.blocks.TileTurtle;
import dan200.computercraft.shared.util.DirectionUtil;
@ -148,7 +149,7 @@ public class TileEntityTurtleRenderer implements BlockEntityRenderer<TileTurtle>
transform.mulPose( Vector3f.XN.rotationDegrees( toolAngle ) );
transform.translate( 0.0f, -0.5f, -0.5f );
TransformedModel model = upgrade.getModel( turtle.getAccess(), side );
TransformedModel model = TurtleUpgradeModellers.getModel( upgrade, turtle.getAccess(), side );
model.getMatrix().push( transform );
renderModel( transform, renderer, lightmapCoord, overlayLight, model.getModel(), null );
transform.popPose();

View File

@ -9,6 +9,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Transformation;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
import dan200.computercraft.shared.turtle.items.ItemTurtle;
import dan200.computercraft.shared.util.Holiday;
import dan200.computercraft.shared.util.HolidayUtil;
@ -106,11 +107,11 @@ public class TurtleSmartItemModel extends BakedModelWrapper<BakedModel>
}
if( combo.leftUpgrade() != null )
{
parts.add( new TransformedBakedModel( combo.leftUpgrade().getModel( null, TurtleSide.LEFT ) ).composeWith( transformation ) );
parts.add( new TransformedBakedModel( TurtleUpgradeModellers.getModel( combo.leftUpgrade(), null, TurtleSide.LEFT ) ).composeWith( transformation ) );
}
if( combo.rightUpgrade() != null )
{
parts.add( new TransformedBakedModel( combo.rightUpgrade().getModel( null, TurtleSide.RIGHT ) ).composeWith( transformation ) );
parts.add( new TransformedBakedModel( TurtleUpgradeModellers.getModel( combo.rightUpgrade(), null, TurtleSide.RIGHT ) ).composeWith( transformation ) );
}
return parts;

View File

@ -0,0 +1,59 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.turtle;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.nbt.CompoundTag;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem>
{
private final ModelResourceLocation leftOffModel;
private final ModelResourceLocation rightOffModel;
private final ModelResourceLocation leftOnModel;
private final ModelResourceLocation rightOnModel;
public TurtleModemModeller( boolean advanced )
{
if( advanced )
{
leftOffModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_off_left", "inventory" );
rightOffModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_off_right", "inventory" );
leftOnModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_on_left", "inventory" );
rightOnModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_on_right", "inventory" );
}
else
{
leftOffModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_off_left", "inventory" );
rightOffModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_off_right", "inventory" );
leftOnModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_on_left", "inventory" );
rightOnModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_on_right", "inventory" );
}
}
@Nonnull
@Override
public TransformedModel getModel( @Nonnull TurtleModem upgrade, @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side )
{
boolean active = false;
if( turtle != null )
{
CompoundTag turtleNBT = turtle.getUpgradeNBTData( side );
active = turtleNBT.contains( "active" ) && turtleNBT.getBoolean( "active" );
}
return side == TurtleSide.LEFT
? TransformedModel.of( active ? leftOnModel : leftOffModel )
: TransformedModel.of( active ? rightOnModel : rightOffModel );
}
}

View File

@ -0,0 +1,72 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.client.turtle;
import com.mojang.math.Transformation;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
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.shared.TurtleUpgrades;
import dan200.computercraft.shared.UpgradeManager;
import net.minecraft.client.Minecraft;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
public final class TurtleUpgradeModellers
{
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<>();
/**
* In order to avoid a double lookup of {@link ITurtleUpgrade} to {@link UpgradeManager.UpgradeWrapper} to
* {@link TurtleUpgradeModeller}, we maintain a cache here.
* <p>
* Turtle upgrades may be removed as part of datapack reloads, so we use a weak map to avoid the memory leak.
*/
private static final WeakHashMap<ITurtleUpgrade, TurtleUpgradeModeller<?>> modelCache = new WeakHashMap<>();
private TurtleUpgradeModellers()
{
}
public static <T extends ITurtleUpgrade> void register( @Nonnull TurtleUpgradeSerialiser<T> serialiser, @Nonnull TurtleUpgradeModeller<T> modeller )
{
synchronized( turtleModels )
{
if( turtleModels.containsKey( serialiser ) )
{
throw new IllegalStateException( "Modeller already registered for serialiser" );
}
turtleModels.put( serialiser, modeller );
}
}
public static TransformedModel getModel( @Nonnull ITurtleUpgrade upgrade, @Nullable ITurtleAccess access, @Nonnull TurtleSide side )
{
@SuppressWarnings( "unchecked" )
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent( upgrade, TurtleUpgradeModellers::getModeller );
return modeller.getModel( upgrade, access, side );
}
private static TurtleUpgradeModeller<?> getModeller( ITurtleUpgrade upgradeA )
{
var wrapper = TurtleUpgrades.instance().getWrapper( upgradeA );
if( wrapper == null ) return NULL_TURTLE_MODELLER;
var modeller = turtleModels.get( wrapper.serialiser() );
return modeller == null ? NULL_TURTLE_MODELLER : modeller;
}
}

View File

@ -21,10 +21,9 @@ import java.util.function.BiFunction;
*
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
*
* @param <R> The serialiser for this upgrade category, either {@code TurtleUpgradeSerialiser<?>} or {@code PocketUpgradeSerialiser<?>}.
* @param <T> The upgrade that this class can serialise and deserialise.
*/
public abstract class SerialiserWithCraftingItem<T extends IUpgradeBase, R extends UpgradeSerialiser<?, R>> implements UpgradeSerialiser<T, R>
public abstract class SerialiserWithCraftingItem<T extends IUpgradeBase> implements UpgradeSerialiser<T>
{
private final BiFunction<ResourceLocation, ItemStack, T> factory;

View File

@ -19,10 +19,9 @@ import java.util.function.Function;
*
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
*
* @param <R> The serialiser for this upgrade category, either {@code TurtleUpgradeSerialiser<?>} or {@code PocketUpgradeSerialiser<?>}.
* @param <T> The upgrade that this class can serialise and deserialise.
*/
public abstract class SimpleSerialiser<T extends IUpgradeBase, R extends UpgradeSerialiser<?, R>> implements UpgradeSerialiser<T, R>
public abstract class SimpleSerialiser<T extends IUpgradeBase> implements UpgradeSerialiser<T>
{
private final Function<ResourceLocation, T> constructor;

View File

@ -37,12 +37,12 @@ import java.util.stream.Collectors;
* @see TurtleUpgrades
* @see PocketUpgrades
*/
public class UpgradeManager<R extends UpgradeSerialiser<? extends T, R>, T extends IUpgradeBase> extends SimpleJsonResourceReloadListener
public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends IUpgradeBase> extends SimpleJsonResourceReloadListener
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().disableHtmlEscaping().create();
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
public static record UpgradeWrapper<R extends UpgradeSerialiser<? extends T, R>, T extends IUpgradeBase>(
public record UpgradeWrapper<R extends UpgradeSerialiser<? extends T>, T extends IUpgradeBase>(
@Nonnull String id, @Nonnull T upgrade, @Nonnull R serialiser, @Nonnull String modId
) {}
@ -66,6 +66,12 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T, R>, T exten
return wrapper == null ? null : wrapper.upgrade();
}
@Nullable
public UpgradeWrapper<R, T> getWrapper( @Nonnull T upgrade )
{
return currentWrappers.get( upgrade );
}
@Nullable
public String getOwner( @Nonnull T upgrade )
{

View File

@ -145,6 +145,7 @@ public final class RepeatArgumentType<T, U> implements ArgumentType<List<T>>
return new RepeatArgumentType.Template( this, child, isList, new SimpleCommandExceptionType( message ) );
}
@Nonnull
@Override
public RepeatArgumentType.Template unpack( RepeatArgumentType<?, ?> argumentType )
{
@ -164,6 +165,7 @@ public final class RepeatArgumentType<T, U> implements ArgumentType<List<T>>
Info info, ArgumentTypeInfo.Template<?> child, boolean flatten, SimpleCommandExceptionType some
) implements ArgumentTypeInfo.Template<RepeatArgumentType<?, ?>>
{
@Nonnull
@Override
@SuppressWarnings( { "unchecked", "rawtypes" } )
public RepeatArgumentType<?, ?> instantiate( @NotNull CommandBuildContext commandBuildContext )
@ -172,6 +174,7 @@ public final class RepeatArgumentType<T, U> implements ArgumentType<List<T>>
return flatten ? RepeatArgumentType.someFlat( (ArgumentType) child, some() ) : RepeatArgumentType.some( child, some() );
}
@Nonnull
@Override
public ArgumentTypeInfo<RepeatArgumentType<?, ?>, ?> type()
{

View File

@ -46,7 +46,7 @@ public class UpgradesLoadedMessage implements NetworkMessage
pocketUpgrades = fromBytes( buf, RegistryManager.ACTIVE.getRegistry( PocketUpgradeSerialiser.REGISTRY_ID ) );
}
private <R extends UpgradeSerialiser<? extends T, R>, T extends IUpgradeBase> Map<String, UpgradeManager.UpgradeWrapper<R, T>> fromBytes(
private <R extends UpgradeSerialiser<? extends T>, T extends IUpgradeBase> Map<String, UpgradeManager.UpgradeWrapper<R, T>> fromBytes(
@Nonnull FriendlyByteBuf buf, @Nonnull IForgeRegistry<R> registry
)
{
@ -76,7 +76,7 @@ public class UpgradesLoadedMessage implements NetworkMessage
toBytes( buf, PocketUpgradeSerialiser.registry(), pocketUpgrades );
}
private <R extends UpgradeSerialiser<? extends T, R>, T extends IUpgradeBase> void toBytes(
private <R extends UpgradeSerialiser<? extends T>, T extends IUpgradeBase> void toBytes(
@Nonnull FriendlyByteBuf buf, IForgeRegistry<R> registry, Map<String, UpgradeManager.UpgradeWrapper<R, T>> upgrades
)
{
@ -87,7 +87,7 @@ public class UpgradesLoadedMessage implements NetworkMessage
var serialiser = entry.getValue().serialiser();
@SuppressWarnings( "unchecked" )
var unwrapedSerialiser = (UpgradeSerialiser<T, R>) serialiser;
var unwrapedSerialiser = (UpgradeSerialiser<T>) serialiser;
buf.writeResourceLocation( Objects.requireNonNull( registry.getKey( serialiser ), "Serialiser is not registered!" ) );
unwrapedSerialiser.toNetwork( buf, entry.getValue().upgrade() );

View File

@ -5,25 +5,18 @@
*/
package dan200.computercraft.shared.turtle.upgrades;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.AbstractTurtleUpgrade;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleUpgradeType;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nonnull;
public class TurtleCraftingTable extends AbstractTurtleUpgrade
{
private static final ModelResourceLocation leftModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_left", "inventory" );
private static final ModelResourceLocation rightModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_right", "inventory" );
public TurtleCraftingTable( ResourceLocation id, ItemStack stack )
{
super( id, TurtleUpgradeType.PERIPHERAL, "upgrade.minecraft.crafting_table.adjective", stack );
@ -34,12 +27,4 @@ public class TurtleCraftingTable extends AbstractTurtleUpgrade
{
return new CraftingTablePeripheral( turtle );
}
@Nonnull
@Override
@OnlyIn( Dist.CLIENT )
public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side )
{
return TransformedModel.of( side == TurtleSide.LEFT ? leftModel : rightModel );
}
}

View File

@ -5,21 +5,16 @@
*/
package dan200.computercraft.shared.turtle.upgrades;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.*;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nonnull;
@ -63,30 +58,10 @@ public class TurtleModem extends AbstractTurtleUpgrade
private final boolean advanced;
private final ModelResourceLocation leftOffModel;
private final ModelResourceLocation rightOffModel;
private final ModelResourceLocation leftOnModel;
private final ModelResourceLocation rightOnModel;
public TurtleModem( ResourceLocation id, ItemStack stack, boolean advanced )
{
super( id, TurtleUpgradeType.PERIPHERAL, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack );
this.advanced = advanced;
if( advanced )
{
leftOffModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_off_left", "inventory" );
rightOffModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_off_right", "inventory" );
leftOnModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_on_left", "inventory" );
rightOnModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_on_right", "inventory" );
}
else
{
leftOffModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_off_left", "inventory" );
rightOffModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_off_right", "inventory" );
leftOnModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_on_left", "inventory" );
rightOnModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_on_right", "inventory" );
}
}
@Override
@ -102,23 +77,6 @@ public class TurtleModem extends AbstractTurtleUpgrade
return TurtleCommandResult.failure();
}
@Nonnull
@Override
@OnlyIn( Dist.CLIENT )
public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side )
{
boolean active = false;
if( turtle != null )
{
CompoundTag turtleNBT = turtle.getUpgradeNBTData( side );
active = turtleNBT.contains( "active" ) && turtleNBT.getBoolean( "active" );
}
return side == TurtleSide.LEFT
? TransformedModel.of( active ? leftOnModel : leftOffModel )
: TransformedModel.of( active ? rightOnModel : rightOffModel );
}
@Override
public void update( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side )
{

View File

@ -5,7 +5,6 @@
*/
package dan200.computercraft.shared.turtle.upgrades;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.AbstractTurtleUpgrade;
import dan200.computercraft.api.turtle.ITurtleAccess;
@ -13,20 +12,14 @@ import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleUpgradeType;
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nonnull;
public class TurtleSpeaker extends AbstractTurtleUpgrade
{
private static final ModelResourceLocation leftModel = new ModelResourceLocation( "computercraft:turtle_speaker_upgrade_left", "inventory" );
private static final ModelResourceLocation rightModel = new ModelResourceLocation( "computercraft:turtle_speaker_upgrade_right", "inventory" );
private static class Peripheral extends UpgradeSpeakerPeripheral
{
final ITurtleAccess turtle;
@ -61,14 +54,6 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade
return new TurtleSpeaker.Peripheral( turtle );
}
@Nonnull
@Override
@OnlyIn( Dist.CLIENT )
public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side )
{
return TransformedModel.of( side == TurtleSide.LEFT ? leftModel : rightModel );
}
@Override
public void update( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide turtleSide )
{

View File

@ -5,11 +5,8 @@
*/
package dan200.computercraft.shared.turtle.upgrades;
import com.mojang.math.Matrix4f;
import com.mojang.math.Transformation;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.*;
import dan200.computercraft.shared.TurtlePermissions;
import dan200.computercraft.shared.turtle.core.TurtleBrain;
@ -37,8 +34,6 @@ import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.ToolActions;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
@ -88,14 +83,6 @@ public class TurtleTool extends AbstractTurtleUpgrade
return true;
}
@Nonnull
@Override
@OnlyIn( Dist.CLIENT )
public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side )
{
return TransformedModel.of( getCraftingItem(), side == TurtleSide.LEFT ? Transforms.leftTransform : Transforms.rightTransform );
}
@Nonnull
@Override
public TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction )
@ -276,22 +263,6 @@ public class TurtleTool extends AbstractTurtleUpgrade
DropConsumer.clearAndDrop( turtle.getLevel(), turtle.getPosition(), direction );
}
private static class Transforms
{
static final Transformation leftTransform = getMatrixFor( -0.40625f );
static final Transformation rightTransform = getMatrixFor( 0.40625f );
private static Transformation getMatrixFor( float offset )
{
return new Transformation( new Matrix4f( new float[] {
0.0f, 0.0f, -1.0f, 1.0f + offset,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f,
} ) );
}
}
protected boolean isTriviallyBreakable( BlockGetter reader, BlockPos pos, BlockState state )
{
return state.is( ComputerCraftTags.Blocks.TURTLE_ALWAYS_BREAKABLE )