1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-09-01 18:17:55 +00:00

Merge pull request #10 from Toad-Dev/fabric-merith

Bugfixes
This commit is contained in:
Merith
2021-05-16 13:00:47 -07:00
committed by GitHub
9 changed files with 150 additions and 235 deletions

View File

@@ -31,17 +31,3 @@ jobs:
with:
name: cc-restiched
path: build/libs
- name: Upload Coverage
run: bash <(curl -s https://codecov.io/bash)
continue-on-error: true
- name: Lint Lua code
run: |
test -d bin || mkdir bin
test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate
chmod +x bin/illuaminate
bin/illuaminate lint
- name: Check whitespace
run: python3 tools/check-lines.py

View File

@@ -43,8 +43,8 @@ If you need help getting started with CC: Tweaked, want to show off your latest
Main Known issue
* Mods that add blocks that can be used as peripherals for CC:T On forge, dont work with CC:R.
* This is because of the differences between forge and fabric, and that mod devs, to my knowledge have not agreed upon a standard method in which to implement cross compatibility between mods,
* Storage Peripherals throw a java "StackOverflowError" when using `pushItems()`,
* Work around, you are probably using `pushItems(chest, 1)` or simular. please use `pushItems(chest, 1, nil, 1)`.
* [Fixed (d10f297c): please report if bug persists]</br> ~~Storage Peripherals throw a java "StackOverflowError" when using `pushItems()`,~~
* ~~Work around, you are probably using `pushItems(chest, 1)` or similar. please use `pushItems(chest, 1, nil, 1)`.~~
## Known Working mods that add Peripherals
* Please let me know of other mods that work with this one

View File

@@ -14,8 +14,8 @@ import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormatElement;
import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.util.math.Vector4f;
import net.minecraft.util.math.Matrix4f;
import net.minecraft.util.math.Quaternion;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
@@ -50,41 +50,31 @@ public final class ModelTransformer {
}
private static BakedQuad doTransformQuad(VertexFormat format, BakedQuad quad, Matrix4f transform) {
int[] vertexData = quad.getVertexData()
.clone();
int offset = 0;
int[] vertexData = quad.getVertexData().clone();
BakedQuad copy = new BakedQuad(vertexData, -1, quad.getFace(), ((BakedQuadAccess)quad).getSprite(), true);
for (int i = 0; i < format.getElements()
.size(); ++i) // For each vertex element
{
VertexFormatElement element = format.getElements()
.get(i);
if (element.getType() == VertexFormatElement.Type.POSITION && element.getFormat() == VertexFormatElement.Format.FLOAT && element.getSize() == 3) // When we find a position
// element
int offsetBytes = 0;
for (int v = 0; v < 4; ++v) {
for (VertexFormatElement element : format.getElements()) // For each vertex element
{
for (int j = 0; j < 4; ++j) // For each corner of the quad
int start = offsetBytes / Integer.BYTES;
if (element.getType() == VertexFormatElement.Type.POSITION && element.getFormat() == VertexFormatElement.Format.FLOAT) // When we find a position element
{
int start = offset + j * format.getVertexSize();
if ((start % 4) == 0) {
start = start / 4;
Vector4f pos = new Vector4f(Float.intBitsToFloat(vertexData[start]),
Float.intBitsToFloat(vertexData[start+1]),
Float.intBitsToFloat(vertexData[start+2]),
1);
// Extract the position
Quaternion pos = new Quaternion(Float.intBitsToFloat(vertexData[start]),
Float.intBitsToFloat(vertexData[start + 1]),
Float.intBitsToFloat(vertexData[start + 2]),
1);
// Transform the position
pos.transform(transform);
// Transform the position
transform.multiply(pos);
// Insert the position
vertexData[start] = Float.floatToRawIntBits(pos.getX());
vertexData[start + 1] = Float.floatToRawIntBits(pos.getY());
vertexData[start + 2] = Float.floatToRawIntBits(pos.getZ());
}
// Insert the position
vertexData[start] = Float.floatToRawIntBits(pos.getX());
vertexData[start+1] = Float.floatToRawIntBits(pos.getY());
vertexData[start+2] = Float.floatToRawIntBits(pos.getZ());
}
offsetBytes += element.getSize();
}
offset += element.getSize();
}
return copy;
}

View File

@@ -78,9 +78,10 @@ public class TurtleSmartItemModel implements BakedModel {
Identifier overlay = turtle.getOverlay(stack);
boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS;
String label = turtle.getLabel(stack);
boolean flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
// TODO Make TurtleTool render for turtle items again.
TurtleModelCombination combo = new TurtleModelCombination(colour != -1, !(leftUpgrade instanceof TurtleTool) ? leftUpgrade : null, !(rightUpgrade instanceof TurtleTool) ? rightUpgrade : null, overlay, christmas, flip);
// TODO make upside down turtle items render properly (currently inivisible)
//boolean flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
boolean flip = false;
TurtleModelCombination combo = new TurtleModelCombination(colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip);
BakedModel model = TurtleSmartItemModel.this.m_cachedModels.get(combo);
if (model == null) {

View File

@@ -28,7 +28,6 @@ public class GenericPeripheralProvider
ArrayList<SaturatedMethod> saturated = new ArrayList<>( 0 );
// This seems to add inventory methods, how???
List<NamedMethod<PeripheralMethod>> tileMethods = PeripheralMethod.GENERATOR.getMethods( tile.getClass() );
if( !tileMethods.isEmpty() ) addSaturated( saturated, tile, tileMethods );

View File

@@ -13,6 +13,8 @@ import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.asm.GenericSource;
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.ItemStorage;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ChestBlock;
@@ -60,10 +62,7 @@ public class InventoryMethods implements GenericSource
@LuaFunction( mainThread = true )
public static int size( Inventory inventory )
{
// Get appropriate inventory for source peripheral
inventory = extractHandler(inventory);
return inventory.size();
return extractHandler(inventory).size();
}
/**
@@ -73,9 +72,13 @@ public class InventoryMethods implements GenericSource
* @return The name of this inventory, or {@code nil} if not present.
*/
@LuaFunction( mainThread = true )
public static String name( Nameable inventory )
public static String name( Inventory inventory )
{
return inventory.hasCustomName() ? inventory.getName().asString() : null;
if ( inventory instanceof Nameable ) {
Nameable i = (Nameable)inventory;
return i.hasCustomName() ? i.getName().asString() : null;
}
return null;
}
/**
@@ -95,14 +98,13 @@ public class InventoryMethods implements GenericSource
@LuaFunction( mainThread = true )
public static Map<Integer, Map<String, ?>> list( Inventory inventory )
{
// Get appropriate inventory for source peripheral
inventory = extractHandler(inventory);
ItemStorage itemStorage = extractHandler(inventory);
Map<Integer, Map<String, ?>> result = new HashMap<>();
int size = inventory.size();
int size = itemStorage.size();
for( int i = 0; i < size; i++ )
{
ItemStack stack = inventory.getStack( i );
ItemStack stack = itemStorage.getStack( i );
if( !stack.isEmpty() ) result.put( i + 1, ItemData.fillBasic( new HashMap<>( 4 ), stack ) );
}
@@ -122,12 +124,11 @@ public class InventoryMethods implements GenericSource
@LuaFunction( mainThread = true )
public static Map<String, ?> getItemDetail( Inventory inventory, int slot ) throws LuaException
{
// Get appropriate inventory
inventory = extractHandler(inventory);
ItemStorage itemStorage = extractHandler(inventory);
assertBetween( slot, 1, inventory.size(), "Slot out of range (%s)" );
assertBetween( slot, 1, itemStorage.size(), "Slot out of range (%s)" );
ItemStack stack = inventory.getStack( slot - 1 );
ItemStack stack = itemStorage.getStack( slot - 1 );
return stack.isEmpty() ? null : ItemData.fill( new HashMap<>(), stack );
}
@@ -162,23 +163,22 @@ public class InventoryMethods implements GenericSource
String toName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
) throws LuaException
{
// Get appropriate inventory for source peripheral
from = extractHandler(from);
ItemStorage fromStorage = extractHandler( from );
// Find location to transfer to
IPeripheral location = computer.getAvailablePeripheral( toName );
if( location == null ) throw new LuaException( "Target '" + toName + "' does not exist" );
Inventory to = extractHandler( location.getTarget() );
if( to == null ) throw new LuaException( "Target '" + toName + "' is not an inventory" );
ItemStorage toStorage = extractHandler( location.getTarget() );
if( toStorage == null ) throw new LuaException( "Target '" + toName + "' is not an inventory" );
// Validate slots
int actualLimit = limit.orElse( Integer.MAX_VALUE );
assertBetween( fromSlot, 1, from.size(), "From slot out of range (%s)" );
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, to.size(), "To slot out of range (%s)" );
assertBetween( fromSlot, 1, fromStorage.size(), "From slot out of range (%s)" );
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, toStorage.size(), "To slot out of range (%s)" );
if( actualLimit <= 0 ) return 0;
return moveItem( from, fromSlot - 1, to, toSlot.orElse( 0 ) - 1, actualLimit );
return moveItem( fromStorage, fromSlot - 1, toStorage, toSlot.orElse( 0 ) - 1, actualLimit );
}
/**
@@ -213,55 +213,36 @@ public class InventoryMethods implements GenericSource
) throws LuaException
{
// Get appropriate inventory for source peripheral
to = extractHandler(to);
ItemStorage toStorage = extractHandler( to );
// Find location to transfer to
IPeripheral location = computer.getAvailablePeripheral( fromName );
if( location == null ) throw new LuaException( "Source '" + fromName + "' does not exist" );
Inventory from = extractHandler( location.getTarget() );
if( from == null ) throw new LuaException( "Source '" + fromName + "' is not an inventory" );
ItemStorage fromStorage = extractHandler( location.getTarget() );
if( fromStorage == null ) throw new LuaException( "Source '" + fromName + "' is not an inventory" );
// Validate slots
int actualLimit = limit.orElse( Integer.MAX_VALUE );
assertBetween( fromSlot, 1, from.size(), "From slot out of range (%s)" );
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, to.size(), "To slot out of range (%s)" );
assertBetween( fromSlot, 1, fromStorage.size(), "From slot out of range (%s)" );
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, toStorage.size(), "To slot out of range (%s)" );
if( actualLimit <= 0 ) return 0;
return moveItem( from, fromSlot - 1, to, toSlot.orElse( 0 ) - 1, actualLimit );
return moveItem( fromStorage, fromSlot - 1, toStorage, toSlot.orElse( 0 ) - 1, actualLimit );
}
/**
* Extracts the most appropriate inventory from the object
* e.g., the correct inventory for a double chest or a sided inventory.
*
* @param object The handler to move from.
* @return The appropriate Inventory.
*/
@Nullable
private static Inventory extractHandler( @Nullable Object object )
private static ItemStorage extractHandler( @Nullable Object object )
{
Inventory inventory = null;
if (object instanceof BlockEntity ) {
BlockEntity blockEntity = (BlockEntity) object;
World world = blockEntity.getWorld();
BlockPos blockPos = blockEntity.getPos();
BlockState blockState = world.getBlockState(blockPos);
Block block = blockState.getBlock();
if (block instanceof InventoryProvider) {
inventory = ((InventoryProvider)block).getInventory(blockState, world, blockPos);
} else if (blockEntity instanceof Inventory) {
inventory = (Inventory)blockEntity;
if (inventory instanceof ChestBlockEntity && block instanceof ChestBlock) {
inventory = ChestBlock.getInventory((ChestBlock) block, blockState, world, blockPos, true);
}
if ( object instanceof BlockEntity ) {
Inventory inventory = InventoryUtil.getInventory((BlockEntity) object);
if ( inventory != null ) {
return ItemStorage.wrap(inventory);
}
}
return inventory;
return null;
}
/**
@@ -274,109 +255,36 @@ public class InventoryMethods implements GenericSource
* @param limit The max number to move. {@link Integer#MAX_VALUE} for no limit.
* @return The number of items moved.
*/
private static int moveItem( Inventory from, int fromSlot, Inventory to, int toSlot, final int limit )
private static int moveItem( ItemStorage from, int fromSlot, ItemStorage to, int toSlot, final int limit )
{
/* ORIGINAL FORGE CODE
// See how much we can get out of this slot
// ItemStack extracted = from.extractItem( fromSlot, limit, true );
if( extracted.isEmpty() ) return 0;
// Limit the amount to extract
int extractCount = Math.min( extracted.getCount(), limit );
extracted.setCount( extractCount );
// ItemStack remainder = toSlot < 0 ? bItemHandlerHelper.insertItem( to, extracted, false ) : to.insertItem( toSlot, extracted, false );
int inserted = remainder.isEmpty() ? extractCount : extractCount - remainder.getCount();
if( inserted <= 0 ) return 0;
// Remove the item from the original inventory. Technically this could fail, but there's little we can do
// about that.
from.extractItem( fromSlot, inserted, false );
*/
// Vanilla minecraft inventory manipulation code
Boolean recurse = false;
ItemStack source = from.getStack( fromSlot );
int count = 0;
// If target slot was selected, only push items to that slot.
if (toSlot >= 0) {
int space = amountStackCanAddFrom(to.getStack(toSlot), source, to);
if (space == 0) return 0;
count = space;
}
// If target slot not selected, push items where they will fit, possibly
// across slots (by recurring on this method).
else if (toSlot < 0) {
recurse = true;
int[] result = getFirstValidSlotAndSpace(source, to);
toSlot = result[0];
if(toSlot < 0) return 0;
count = result[1];
}
// Respect slot restrictions
if (!to.isValid(toSlot, source)) { return 0; }
// Compare count available in target ItemStack to limit specified.
count = Math.min(count, limit);
if (count == 0) return 0;
// Mutate destination and source ItemStack
ItemStack destination = to.getStack(toSlot);
if (destination.isEmpty()) {
ItemStack newStack = source.copy();
newStack.setCount(count);
to.setStack(toSlot, newStack);
} else {
destination.increment(count);
}
source.decrement(count);
if (source.isEmpty()) from.setStack(fromSlot, ItemStack.EMPTY);
to.markDirty();
from.markDirty();
// Recurse if no explicit destination slot and more items exist in source slot
// and limit hasn't been reached. Else, return items moved.
if (recurse && !source.isEmpty()) return count + moveItem(from, fromSlot, to, -1, limit - count);
return count;
}
// Maybe there is a nicer existing way to do this in the minecraft codebase. I couldn't find it.
private static int[] getFirstValidSlotAndSpace(ItemStack fromStack, Inventory inventory) {
for (int i = 0; i < inventory.size(); i++) {
ItemStack stack = inventory.getStack(i);
int space = amountStackCanAddFrom(stack, fromStack, inventory);
if (space > 0) {
return new int[]{i, space};
}
}
return new int[]{-1, 0};
}
private static int amountStackCanAddFrom(ItemStack existingStack, ItemStack fromStack, Inventory inventory) {
if (fromStack.isEmpty()) {
// Moving nothing is easy
if (limit == 0) {
return 0;
}
else if (existingStack.isEmpty()) {
return Math.min(Math.min(existingStack.getMaxCount(),
inventory.getMaxCountPerStack()),
fromStack.getCount());
}
else if (InventoryMethods.areItemsEqual(existingStack, fromStack) &&
existingStack.isStackable() &&
existingStack.getCount() < existingStack.getMaxCount() &&
existingStack.getCount() < inventory.getMaxCountPerStack()) {
int stackSpace = existingStack.getMaxCount() - existingStack.getCount();
int invSpace = inventory.getMaxCountPerStack() - existingStack.getCount();
return Math.min(Math.min(stackSpace, invSpace), fromStack.getCount());
}
return 0;
}
private static boolean areItemsEqual(ItemStack stack1, ItemStack stack2) {
return stack1.getItem() == stack2.getItem() && ItemStack.areTagsEqual(stack1, stack2);
// Get stack to move
ItemStack stack = InventoryUtil.takeItems(limit, from, fromSlot, 1, fromSlot);
if (stack.isEmpty()) {
return 0;
}
int stackCount = stack.getCount();
// Move items in
ItemStack remainder;
if (toSlot < 0) {
remainder = InventoryUtil.storeItems(stack, to);
} else {
remainder = InventoryUtil.storeItems(stack, to, toSlot, 1, toSlot);
}
// Calculate items moved
int count = stackCount - remainder.getCount();
if (!remainder.isEmpty()) {
// Put the remainder back
InventoryUtil.storeItems(remainder, from, fromSlot, 1, fromSlot);
}
return count;
}
}

View File

@@ -22,6 +22,7 @@ import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.DropConsumer;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.item.*;
import org.apache.commons.lang3.tuple.Pair;
import net.minecraft.block.BlockState;
@@ -29,16 +30,6 @@ import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.BoatItem;
import net.minecraft.item.BucketItem;
import net.minecraft.item.GlassBottleItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.item.LilyPadItem;
import net.minecraft.item.SignItem;
import net.minecraft.text.LiteralText;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
@@ -224,27 +215,18 @@ public class TurtlePlaceCommand implements ITurtleCommand {
// Place on the entity
boolean placed = false;
ActionResult cancelResult = hitEntity.interactAt(turtlePlayer, hitPos, Hand.MAIN_HAND);
if (cancelResult == null) {
cancelResult = hitEntity.interactAt(turtlePlayer, hitPos, Hand.MAIN_HAND);
}
if (cancelResult != null && cancelResult.isAccepted()) {
placed = true;
} else if (hitEntity instanceof LivingEntity) {
// See EntityPlayer.interactOn
cancelResult = stackCopy.useOnEntity(turtlePlayer, (LivingEntity) hitEntity, Hand.MAIN_HAND);
}
else {
cancelResult = hitEntity.interact(turtlePlayer, Hand.MAIN_HAND);
if (cancelResult != null && cancelResult.isAccepted()) {
placed = true;
} else if (cancelResult == null) {
if (hitEntity.interact(turtlePlayer, Hand.MAIN_HAND) == ActionResult.CONSUME) {
placed = true;
} else {
placed = stackCopy.useOnEntity(turtlePlayer, (LivingEntity) hitEntity, Hand.MAIN_HAND)
.isAccepted();
if (placed) {
turtlePlayer.loadInventory(stackCopy);
}
}
}
else if (hitEntity instanceof LivingEntity) {
placed = stackCopy.useOnEntity(turtlePlayer, (LivingEntity) hitEntity, Hand.MAIN_HAND).isAccepted();
if (placed) turtlePlayer.loadInventory(stackCopy);
}
}
@@ -304,11 +286,9 @@ public class TurtlePlaceCommand implements ITurtleCommand {
BlockEntity existingTile = turtle.getWorld()
.getBlockEntity(position);
if (placementContext.canPlace()) {
if (stackCopy.useOnBlock(context) == ActionResult.SUCCESS) {
placed = true;
turtlePlayer.loadInventory(stackCopy);
}
if (stackCopy.useOnBlock(context).isAccepted()) {
placed = true;
turtlePlayer.loadInventory(stackCopy);
}
if (!placed && (item instanceof BucketItem || item instanceof BoatItem || item instanceof LilyPadItem || item instanceof GlassBottleItem)) {

View File

@@ -8,6 +8,11 @@ package dan200.computercraft.shared.util;
import javax.annotation.Nonnull;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ChestBlock;
import net.minecraft.block.InventoryProvider;
import net.minecraft.block.entity.ChestBlockEntity;
import org.apache.commons.lang3.tuple.Pair;
import net.minecraft.block.entity.BlockEntity;
@@ -33,9 +38,20 @@ public final class InventoryUtil {
// Look for tile with inventory
int y = pos.getY();
if (y >= 0 && y < world.getHeight()) {
BlockEntity tileEntity = world.getBlockEntity(pos);
if (tileEntity instanceof Inventory) {
return (Inventory) tileEntity;
// Check if block is InventoryProvider
BlockState blockState = world.getBlockState(pos);
Block block = blockState.getBlock();
if (block instanceof InventoryProvider) {
return ((InventoryProvider)block).getInventory(blockState, world, pos);
}
// Check if block is BlockEntity w/ Inventory
if (block.hasBlockEntity()) {
BlockEntity tileEntity = world.getBlockEntity(pos);
Inventory inventory = getInventory(tileEntity);
if (inventory != null) {
return inventory;
}
}
}
@@ -55,6 +71,23 @@ public final class InventoryUtil {
return null;
}
public static Inventory getInventory(BlockEntity tileEntity) {
World world = tileEntity.getWorld();
BlockPos pos = tileEntity.getPos();
BlockState blockState = world.getBlockState(pos);
Block block = blockState.getBlock();
if (tileEntity instanceof Inventory) {
Inventory inventory = (Inventory)tileEntity;
if (inventory instanceof ChestBlockEntity && block instanceof ChestBlock) {
return ChestBlock.getInventory((ChestBlock) block, blockState, world, pos, true);
}
return inventory;
}
return null;
}
@Nonnull
public static ItemStack storeItems(@Nonnull ItemStack itemstack, ItemStorage inventory, int begin) {
return storeItems(itemstack, inventory, 0, inventory.size(), begin);

View File

@@ -35,6 +35,9 @@ public interface ItemStorage {
int size();
@Nonnull
ItemStack getStack(int slot);
@Nonnull
ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate);
@@ -57,6 +60,12 @@ public interface ItemStorage {
return this.inventory.size();
}
@Override
@Nonnull
public ItemStack getStack(int slot) {
return this.inventory.getStack(slot);
}
@Override
@Nonnull
public ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate) {
@@ -203,6 +212,15 @@ public interface ItemStorage {
return this.size;
}
@Override
@Nonnull
public ItemStack getStack(int slot) {
if (slot < this.start || slot >= this.start + this.size) {
return ItemStack.EMPTY;
}
return this.parent.getStack(slot - this.start );
}
@Nonnull
@Override
public ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate) {