mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-03 07:03:00 +00:00
Get everything compiling
Game tests are running but not passing. Well, on Fabric, there's some fun classpath issues on NF. Classic. - Update NF to use new transfer code. Generic peripherals don't work yet, as the capabilities aren't wired up correctly. Suspect we're gonna have to tear out that code.
This commit is contained in:
@@ -68,5 +68,5 @@ tasks.ideaSyncTask {
|
||||
tasks.named("checkDependencyConsistency", DependencyCheck::class.java) {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
// Minecraft depends on asm, but Fabric forces it to a more recent version
|
||||
override(libs.findLibrary("asm").get(), "9.8")
|
||||
override(libs.findLibrary("asm").get(), "9.9")
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ import net.minecraft.client.data.models.ItemModelGenerators;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSources;
|
||||
import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
|
||||
import net.minecraft.client.resources.model.AtlasIds;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.RegistrySetBuilder;
|
||||
import net.minecraft.data.AtlasIds;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.registries.RegistryPatchGenerator;
|
||||
|
||||
@@ -23,6 +23,7 @@ import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
|
||||
import net.minecraft.world.level.storage.loot.functions.CopyComponentsFunction;
|
||||
import net.minecraft.world.level.storage.loot.functions.CopyNameFunction;
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||
import net.minecraft.world.level.storage.loot.predicates.AnyOfCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
|
||||
import net.minecraft.world.level.storage.loot.predicates.LootItemBlockStatePropertyCondition;
|
||||
@@ -92,7 +93,7 @@ class LootTableProvider {
|
||||
private static void namedBlockDrop(BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add, Supplier<? extends Block> wrapper) {
|
||||
blockDrop(
|
||||
add, wrapper,
|
||||
LootItem.lootTableItem(wrapper.get()).apply(CopyNameFunction.copyName(CopyNameFunction.NameSource.BLOCK_ENTITY)),
|
||||
LootItem.lootTableItem(wrapper.get()).apply(CopyNameFunction.copyName(new CopyNameFunction.Source(LootContextParams.BLOCK_ENTITY))),
|
||||
ExplosionCondition.survivesExplosion()
|
||||
);
|
||||
}
|
||||
@@ -100,7 +101,7 @@ class LootTableProvider {
|
||||
private static void computerDrop(BiConsumer<ResourceKey<LootTable>, LootTable.Builder> add, Supplier<? extends Block> block) {
|
||||
blockDrop(
|
||||
add, block,
|
||||
LootItem.lootTableItem(block.get()).apply(CopyComponentsFunction.copyComponents(CopyComponentsFunction.Source.BLOCK_ENTITY)),
|
||||
LootItem.lootTableItem(block.get()).apply(CopyComponentsFunction.copyComponentsFromBlockEntity(LootContextParams.BLOCK_ENTITY)),
|
||||
AnyOfCondition.anyOf(
|
||||
BlockNamedEntityLootCondition.BUILDER,
|
||||
HasComputerIdLootCondition.BUILDER,
|
||||
|
||||
@@ -447,7 +447,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
}
|
||||
|
||||
private static ItemStack playerHead(String name, String uuid) {
|
||||
return DataComponentUtil.createStack(Items.PLAYER_HEAD, DataComponents.PROFILE, new ResolvableProfile(new GameProfile(UUID.fromString(uuid), name)));
|
||||
return DataComponentUtil.createStack(Items.PLAYER_HEAD, DataComponents.PROFILE, ResolvableProfile.createResolved(new GameProfile(UUID.fromString(uuid), name)));
|
||||
}
|
||||
|
||||
private ShapedSpecBuilder customShaped(RecipeCategory category, ItemStack result) {
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class AbstractEnergyMethods<T> implements GenericPeripheral {
|
||||
* @return The energy stored in this block, in FE.
|
||||
*/
|
||||
@LuaFunction(mainThread = true)
|
||||
public abstract int getEnergy(T energy);
|
||||
public abstract long getEnergy(T energy);
|
||||
|
||||
/**
|
||||
* Get the maximum amount of energy this block can store.
|
||||
@@ -49,5 +49,5 @@ public abstract class AbstractEnergyMethods<T> implements GenericPeripheral {
|
||||
* @return The energy capacity of this block.
|
||||
*/
|
||||
@LuaFunction(mainThread = true)
|
||||
public abstract int getEnergyCapacity(T energy);
|
||||
public abstract long getEnergyCapacity(T energy);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ public @interface WithMinecraft {
|
||||
}
|
||||
|
||||
public static void bootstrap() {
|
||||
SharedConstants.tryDetectVersion();
|
||||
ServiceLoader.load(SetupHook.class, SetupHook.class.getClassLoader()).forEach(SetupHook::run);
|
||||
SharedConstants.tryDetectVersion();
|
||||
Bootstrap.bootStrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import net.minecraft.gametest.framework.GameTestServer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.Services;
|
||||
import net.minecraft.server.WorldStem;
|
||||
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
|
||||
import net.minecraft.server.level.progress.LevelLoadListener;
|
||||
import net.minecraft.server.packs.repository.PackRepository;
|
||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
@@ -22,7 +22,10 @@ import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
@Mixin(GameTestServer.class)
|
||||
abstract class GameTestServerMixin extends MinecraftServer {
|
||||
GameTestServerMixin(Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer fixerUpper, Services services, ChunkProgressListenerFactory progressListenerFactory) {
|
||||
GameTestServerMixin(
|
||||
Thread serverThread, LevelStorageSource.LevelStorageAccess storageSource, PackRepository packRepository,
|
||||
WorldStem worldStem, Proxy proxy, DataFixer fixerUpper, Services services, LevelLoadListener progressListenerFactory
|
||||
) {
|
||||
super(serverThread, storageSource, packRepository, worldStem, proxy, fixerUpper, services, progressListenerFactory);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import dan200.computercraft.gametest.api.*
|
||||
import dan200.computercraft.shared.ModRegistry
|
||||
import dan200.computercraft.test.core.assertArrayEquals
|
||||
import dan200.computercraft.test.core.computer.getApi
|
||||
import net.minecraft.client.input.KeyEvent
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.gametest.framework.GameTestHelper
|
||||
@@ -155,8 +156,8 @@ class Computer_Test {
|
||||
// Press a key on the client
|
||||
thenOnClient {
|
||||
val screen = minecraft.screen as AbstractComputerScreen<*>
|
||||
screen.keyPressed(GLFW.GLFW_KEY_A, 0, 0)
|
||||
screen.keyReleased(GLFW.GLFW_KEY_A, 0, 0)
|
||||
screen.keyPressed(KeyEvent(GLFW.GLFW_KEY_A, 0, 0))
|
||||
screen.keyReleased(KeyEvent(GLFW.GLFW_KEY_A, 0, 0))
|
||||
}
|
||||
// And assert it is handled and sent back to the client
|
||||
thenIdle(2)
|
||||
|
||||
@@ -122,7 +122,7 @@ class Disk_Drive_Test {
|
||||
thenWaitUntil {
|
||||
val drive = helper.getBlockEntity(drivePos, DiskDriveBlockEntity::class.java)
|
||||
if (!drive.getItem(0).has(ModRegistry.DataComponents.DISK_ID.get())) {
|
||||
helper.fail("Disk has no item", drivePos)
|
||||
helper.abort("Disk has no item", drivePos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class Monitor_Test {
|
||||
val tile = context.getBlockEntity(pos, MonitorBlockEntity::class.java)
|
||||
|
||||
if (tile.width != 1 || tile.height != 1) {
|
||||
context.fail("Tile has width and height of ${tile.width}x${tile.height}, but should be 1x1", pos)
|
||||
context.abort("Tile has width and height of ${tile.width}x${tile.height}, but should be 1x1", pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,9 @@ class Recipe_Test {
|
||||
|
||||
val profile = GameProfile(UUID.fromString("f3c8d69b-0776-4512-8434-d1b2165909eb"), "dan200")
|
||||
|
||||
val tag = DataComponentPatch.builder().set(DataComponents.PROFILE, ResolvableProfile(profile)).build()
|
||||
val tag =
|
||||
DataComponentPatch.builder().set(DataComponents.PROFILE, ResolvableProfile.createResolved(profile))
|
||||
.build()
|
||||
assertEquals(tag, result.componentsPatch, "Expected NBT tags to be the same")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,7 +680,7 @@ class Turtle_Test {
|
||||
|
||||
val villager = helper.getEntity(EntityType.VILLAGER)
|
||||
val expectedY = helper.absolutePos(pos).y - 0.125
|
||||
if (villager.y < expectedY) helper.fail("Expected villager at y>=$expectedY, but at ${villager.y}", pos)
|
||||
if (villager.y < expectedY) helper.abort("Expected villager at y>=$expectedY, but at ${villager.y}", pos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -697,7 +697,7 @@ class Turtle_Test {
|
||||
helper.assertEntityNotPresent(EntityType.SHEEP)
|
||||
val count = helper.getBlockEntity(turtlePos, TurtleBlockEntity::class.java)
|
||||
.countItem(Items.WHITE_WOOL)
|
||||
if (count == 0) helper.fail("Expected turtle to have white wool", turtlePos)
|
||||
if (count == 0) helper.abort("Expected turtle to have white wool", turtlePos)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ fun Minecraft.isRenderingStable(): Boolean = (this as MinecraftExtensions).`comp
|
||||
fun GameTestSequence.thenOnClient(task: ClientTestHelper.() -> Unit): GameTestSequence {
|
||||
var future: CompletableFuture<Void>? = null
|
||||
thenExecute { future = Minecraft.getInstance().submit { task(ClientTestHelper()) } }
|
||||
thenWaitUntil { if (!future!!.isDone) fail("Not done task yet") }
|
||||
thenWaitUntil { if (!future!!.isDone) abort("Not done task yet") }
|
||||
thenExecute {
|
||||
try {
|
||||
future!!.get()
|
||||
@@ -59,10 +59,10 @@ fun GameTestSequence.thenScreenshot(name: String? = null, showGui: Boolean = fal
|
||||
thenWaitUntil {
|
||||
if (Minecraft.getInstance().isRenderingStable()) {
|
||||
val idleFor = ++counter
|
||||
if (idleFor <= 20) fail("Only idle for $idleFor ticks")
|
||||
if (idleFor <= 20) abort("Only idle for $idleFor ticks")
|
||||
} else {
|
||||
counter = 0
|
||||
fail("Waiting for client to finish rendering")
|
||||
abort("Waiting for client to finish rendering")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ fun GameTestSequence.thenScreenshot(name: String? = null, showGui: Boolean = fal
|
||||
// Take a screenshot and wait for it to have finished.
|
||||
val hasScreenshot = AtomicBoolean()
|
||||
thenOnClient { screenshot("$fullName.png") { hasScreenshot.set(true) } }
|
||||
thenWaitUntil { if (!hasScreenshot.get()) fail("Screenshot does not exist") }
|
||||
thenWaitUntil { if (!hasScreenshot.get()) abort("Screenshot does not exist") }
|
||||
thenOnClient { minecraft.options.hideGui = false }
|
||||
|
||||
return this
|
||||
@@ -92,7 +92,7 @@ fun ServerPlayer.setupForTest() {
|
||||
*/
|
||||
fun GameTestHelper.positionAtArmorStand() {
|
||||
val stand = getEntity(EntityType.ARMOR_STAND)
|
||||
val player = level.randomPlayer ?: fail("Player does not exist")
|
||||
val player = level.randomPlayer ?: abort("Player does not exist")
|
||||
|
||||
player.setupForTest()
|
||||
player.connection.teleport(stand.x, stand.y, stand.z, stand.yRot, stand.xRot)
|
||||
@@ -103,7 +103,7 @@ fun GameTestHelper.positionAtArmorStand() {
|
||||
*/
|
||||
fun GameTestHelper.positionAt(pos: BlockPos, yRot: Float = 0.0f, xRot: Float = 0.0f) {
|
||||
val absolutePos = absolutePos(pos)
|
||||
val player = level.randomPlayer ?: fail("Player does not exist")
|
||||
val player = level.randomPlayer ?: abort("Player does not exist")
|
||||
|
||||
player.setupForTest()
|
||||
player.connection.teleport(absolutePos.x + 0.5, absolutePos.y + 0.5, absolutePos.z + 0.5, yRot, xRot)
|
||||
|
||||
@@ -97,11 +97,11 @@ fun GameTestSequence.thenComputerOk(name: String? = null, marker: String = Compu
|
||||
|
||||
thenWaitUntil {
|
||||
val computer = ComputerState.get(label)
|
||||
if (computer == null || !computer.isDone(marker)) fail("Computer '$label' has not reached $marker yet.")
|
||||
if (computer == null || !computer.isDone(marker)) abort("Computer '$label' has not reached $marker yet.")
|
||||
}
|
||||
thenExecuteFailFast {
|
||||
val error = ComputerState.get(label)!!.check(marker)
|
||||
if (error != null) fail(error)
|
||||
if (error != null) abort(error)
|
||||
}
|
||||
return this
|
||||
}
|
||||
@@ -128,7 +128,7 @@ fun GameTestSequence.thenOnComputer(name: String? = null, action: suspend LuaTas
|
||||
thenWaitUntil {
|
||||
if (!monitor!!.isFinished) {
|
||||
val runningFor = (test as GameTestInfoAccessor).`computercraft$getTick`() - self.lastTick
|
||||
fail("Computer '$label' has not finished yet (running for $runningFor ticks).")
|
||||
abort("Computer '$label' has not finished yet (running for $runningFor ticks).")
|
||||
}
|
||||
}
|
||||
thenExecuteFailFast { monitor!!.check() }
|
||||
@@ -155,25 +155,25 @@ fun GameTestHelper.immediate(run: () -> Unit) {
|
||||
// Helper functions for failing tests
|
||||
|
||||
/** Raise a [GameTestAssertException]. */
|
||||
fun GameTestHelper.fail(message: String): Nothing = throw assertionException(Component.literal(message))
|
||||
fun GameTestHelper.abort(message: String): Nothing = throw assertionException(Component.literal(message))
|
||||
|
||||
/** Raise a [GameTestAssertException] at a position. */
|
||||
fun GameTestHelper.fail(message: String, pos: BlockPos): Nothing =
|
||||
fun GameTestHelper.abort(message: String, pos: BlockPos): Nothing =
|
||||
throw assertionException(pos, Component.literal(message))
|
||||
|
||||
/** Assert a condition is true, or raise a [GameTestAssertException] if not. */
|
||||
fun GameTestHelper.assertTrue(condition: Boolean, message: String) = assertTrue(condition, Component.literal(message))
|
||||
|
||||
/** Raise a [GameTestAssertException]. */
|
||||
fun GameTestSequence.fail(message: String): Nothing =
|
||||
fun GameTestSequence.abort(message: String): Nothing =
|
||||
throw GameTestAssertException(
|
||||
Component.literal(message),
|
||||
((this as GameTestSequenceAccessor).parent as GameTestInfoAccessor).`computercraft$getTick`(),
|
||||
)
|
||||
|
||||
/** Fail with an optional context message. */
|
||||
private fun GameTestHelper.fail(message: String?, detail: String, pos: BlockPos): Nothing {
|
||||
fail(if (message.isNullOrEmpty()) detail else "$message: $detail", pos)
|
||||
private fun GameTestHelper.abort(message: String?, detail: String, pos: BlockPos): Nothing {
|
||||
abort(if (message.isNullOrEmpty()) detail else "$message: $detail", pos)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,7 +186,7 @@ fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boole
|
||||
*/
|
||||
fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boolean, message: String) {
|
||||
val state = getBlockState(pos)
|
||||
if (!predicate(state)) fail(message, state.toString(), pos)
|
||||
if (!predicate(state)) abort(message, state.toString(), pos)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,9 +196,9 @@ fun <T : Comparable<T>> GameTestHelper.assertBlockHas(pos: BlockPos, property: P
|
||||
val state = getBlockState(pos)
|
||||
if (!state.hasProperty(property)) {
|
||||
val id = RegistryHelper.getKeyOrThrow(BuiltInRegistries.BLOCK, state.block)
|
||||
fail(message, "block $id does not have property ${property.name}", pos)
|
||||
abort(message, "block $id does not have property ${property.name}", pos)
|
||||
} else if (state.getValue(property) != value) {
|
||||
fail(message, "${property.name} is ${state.getValue(property)}, expected $value", pos)
|
||||
abort(message, "${property.name} is ${state.getValue(property)}, expected $value", pos)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,8 +208,8 @@ fun <T : Comparable<T>> GameTestHelper.assertBlockHas(pos: BlockPos, property: P
|
||||
fun GameTestHelper.getContainerAt(pos: BlockPos): Container =
|
||||
when (val container: BlockEntity? = level.getBlockEntity(absolutePos(pos))) {
|
||||
is Container -> container
|
||||
null -> fail("Expected a container at $pos, found nothing", pos)
|
||||
else -> fail("Expected a container at $pos, found ${getName(container.type)}", pos)
|
||||
null -> abort("Expected a container at $pos, found nothing", pos)
|
||||
else -> abort("Expected a container at $pos, found ${getName(container.type)}", pos)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,7 +254,7 @@ private fun GameTestHelper.assertContainerExactlyImpl(pos: BlockPos, container:
|
||||
|
||||
if (slot >= 0) {
|
||||
val invItems = (0 until container.containerSize).map { container.getItem(it) }.dropLastWhile { it.isEmpty }
|
||||
fail(
|
||||
abort(
|
||||
"""
|
||||
Items do not match (first mismatch at slot $slot).
|
||||
Expected: ${formatItems(items)}
|
||||
@@ -278,15 +278,15 @@ fun GameTestHelper.assertPeripheral(pos: BlockPos, direction: Direction = Direct
|
||||
val peripheral = getPeripheralAt(pos, direction)
|
||||
val block = getBlockState(pos).block.name.string
|
||||
when {
|
||||
peripheral == null -> fail("No peripheral for '$block'", pos)
|
||||
peripheral.type != type -> fail("Peripheral for '$block' is of type ${peripheral.type}, expected $type", pos)
|
||||
peripheral == null -> abort("No peripheral for '$block'", pos)
|
||||
peripheral.type != type -> abort("Peripheral for '$block' is of type ${peripheral.type}, expected $type", pos)
|
||||
}
|
||||
}
|
||||
|
||||
fun GameTestHelper.assertNoPeripheral(pos: BlockPos, direction: Direction = Direction.UP) {
|
||||
val peripheral = getPeripheralAt(pos, direction)
|
||||
val block = getBlockState(pos).block.name
|
||||
if (peripheral != null) fail("Expected no peripheral for '$block', got a ${peripheral.type}", pos)
|
||||
if (peripheral != null) abort("Expected no peripheral for '$block', got a ${peripheral.type}", pos)
|
||||
}
|
||||
|
||||
fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: String? = null) {
|
||||
@@ -295,7 +295,7 @@ fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: Strin
|
||||
if (!matcher.matches(actual)) {
|
||||
val description = StringDescription()
|
||||
matcher.describeMismatch(actual, description)
|
||||
fail(if (message.isNullOrEmpty()) description.toString() else "$message: $description")
|
||||
abort(if (message.isNullOrEmpty()) description.toString() else "$message: $description")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: Strin
|
||||
fun GameTestHelper.assertItemEntityCountIs(expected: Item, count: Int) {
|
||||
val actualCount = getEntities(EntityType.ITEM).sumOf { if (it.item.`is`(expected)) it.item.count else 0 }
|
||||
if (actualCount != count) {
|
||||
fail("Expected $count ${ItemStack(expected).itemName.string} items to exist (found $actualCount)")
|
||||
abort("Expected $count ${ItemStack(expected).itemName.string} items to exist (found $actualCount)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,9 +318,9 @@ private fun getName(type: BlockEntityType<*>): ResourceLocation =
|
||||
fun <T : Entity> GameTestHelper.getEntity(type: EntityType<T>): T {
|
||||
val entities = getEntities(type)
|
||||
when (entities.size) {
|
||||
0 -> fail("No $type entities")
|
||||
0 -> abort("No $type entities")
|
||||
1 -> return entities[0]
|
||||
else -> fail("Multiple $type entities (${entities.size} in bounding box)")
|
||||
else -> abort("Multiple $type entities (${entities.size} in bounding box)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,7 +369,7 @@ fun GameTestHelper.assertNotCraftable(vararg items: ItemStack) {
|
||||
|
||||
val recipe = level.server.recipeManager.getRecipeFor(RecipeType.CRAFTING, input, level)
|
||||
|
||||
if (recipe.isPresent) fail("Expected no recipe to match $items")
|
||||
if (recipe.isPresent) abort("Expected no recipe to match $items")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,7 +381,7 @@ fun GameTestHelper.craftItem(vararg items: ItemStack): ItemStack {
|
||||
val input = CraftingInput.of(3, 3, container)
|
||||
|
||||
val recipe = level.server.recipeManager.getRecipeFor(RecipeType.CRAFTING, input, level).getOrNull()
|
||||
?: fail("No recipe matches $items")
|
||||
?: throw assertionException("No recipe matches $items")
|
||||
return recipe.value.assemble(input, level.registryAccess())
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ object TestHooks {
|
||||
}
|
||||
|
||||
fun getTestOrigin(server: MinecraftServer): BlockPos {
|
||||
val spawn = server.overworld().sharedSpawnPos
|
||||
val spawn = server.respawnData.pos()
|
||||
return BlockPos(spawn.x, -59, spawn.y)
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,11 @@ public final class ForgeClientHooks {
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void drawHighlight(RenderHighlightEvent.Block event) {
|
||||
if (ClientHooks.drawHighlight(event.getPoseStack(), event.getMultiBufferSource(), event.getCamera(), event.getTarget())) {
|
||||
public static void drawHighlight(ExtractBlockOutlineRenderStateEvent event) {
|
||||
// TODO: Highlighting. Need to see what Fabric is doing here.
|
||||
/*if (ClientHooks.drawHighlight(event.getPoseStack(), event.getMultiBufferSource(), event.getCamera(), event.getTarget())) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@@ -58,7 +59,7 @@ public final class ForgeClientHooks {
|
||||
@SubscribeEvent
|
||||
public static void onRenderInHand(RenderHandEvent event) {
|
||||
if (ClientHooks.onRenderHeldItem(
|
||||
event.getPoseStack(), event.getMultiBufferSource(), event.getPackedLight(),
|
||||
event.getPoseStack(), event.getSubmitNodeCollector(), event.getPackedLight(),
|
||||
event.getHand(), event.getInterpolatedPitch(), event.getEquipProgress(), event.getSwingProgress(), event.getItemStack()
|
||||
)) {
|
||||
event.setCanceled(true);
|
||||
@@ -70,7 +71,7 @@ public final class ForgeClientHooks {
|
||||
public static void onRenderInFrame(RenderItemInFrameEvent event) {
|
||||
var state = event.getItemFrameRenderState().getRenderData(ITEM_FRAME_STATE);
|
||||
if (state != null && ClientHooks.onRenderItemFrame(
|
||||
event.getPoseStack(), event.getMultiBufferSource(), event.getItemFrameRenderState(), state, event.getPackedLight()
|
||||
event.getPoseStack(), event.getSubmitNodeCollector(), event.getItemFrameRenderState(), state
|
||||
)) {
|
||||
event.setCanceled(true);
|
||||
}
|
||||
|
||||
@@ -5,21 +5,21 @@
|
||||
package dan200.computercraft.shared.peripheral.generic.methods;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import net.neoforged.neoforge.energy.IEnergyStorage;
|
||||
import net.neoforged.neoforge.transfer.energy.EnergyHandler;
|
||||
|
||||
/**
|
||||
* Fluid methods for Forge's {@link IEnergyStorage}.
|
||||
* Fluid methods for Forge's {@link EnergyHandler}.
|
||||
*/
|
||||
public final class EnergyMethods extends AbstractEnergyMethods<IEnergyStorage> {
|
||||
public final class EnergyMethods extends AbstractEnergyMethods<EnergyHandler> {
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public int getEnergy(IEnergyStorage energy) {
|
||||
return energy.getEnergyStored();
|
||||
public long getEnergy(EnergyHandler energy) {
|
||||
return energy.getAmountAsLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public int getEnergyCapacity(IEnergyStorage energy) {
|
||||
return energy.getMaxEnergyStored();
|
||||
public long getEnergyCapacity(EnergyHandler energy) {
|
||||
return energy.getCapacityAsLong();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,28 +13,35 @@ import dan200.computercraft.shared.util.CapabilityUtil;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||
import net.neoforged.neoforge.fluids.FluidStack;
|
||||
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
|
||||
import net.neoforged.neoforge.transfer.ResourceHandler;
|
||||
import net.neoforged.neoforge.transfer.ResourceHandlerUtil;
|
||||
import net.neoforged.neoforge.transfer.fluid.FluidResource;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import static dan200.computercraft.shared.util.ArgumentHelpers.getRegistryEntry;
|
||||
|
||||
/**
|
||||
* Fluid methods for Forge's {@link IFluidHandler}.
|
||||
* Fluid methods for Forge's fluid {@link ResourceHandler}.
|
||||
*/
|
||||
public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
|
||||
public final class FluidMethods extends AbstractFluidMethods<FluidMethods.StorageWrapper> {
|
||||
public record StorageWrapper(ResourceHandler<FluidResource> storage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public Map<Integer, Map<String, ?>> tanks(IFluidHandler fluids) {
|
||||
public Map<Integer, Map<String, ?>> tanks(FluidMethods.StorageWrapper wrapper) {
|
||||
var storage = wrapper.storage();
|
||||
Map<Integer, Map<String, ?>> result = new HashMap<>();
|
||||
var size = fluids.getTanks();
|
||||
var size = storage.size();
|
||||
for (var i = 0; i < size; i++) {
|
||||
var stack = fluids.getFluidInTank(i);
|
||||
var stack = storage.getResource(i).toStack(storage.getAmountAsInt(i));
|
||||
if (!stack.isEmpty()) result.put(i + 1, ForgeDetailRegistries.FLUID_STACK.getBasicDetails(stack));
|
||||
}
|
||||
|
||||
@@ -44,7 +51,7 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public int pushFluid(
|
||||
IFluidHandler from, IComputerAccess computer,
|
||||
FluidMethods.StorageWrapper from, IComputerAccess computer,
|
||||
String toName, Optional<Integer> limit, Optional<String> fluidName
|
||||
) throws LuaException {
|
||||
var fluid = fluidName.isPresent()
|
||||
@@ -61,15 +68,13 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
|
||||
int actualLimit = limit.orElse(Integer.MAX_VALUE);
|
||||
if (actualLimit <= 0) throw new LuaException("Limit must be > 0");
|
||||
|
||||
return fluid == null
|
||||
? moveFluid(from, actualLimit, to)
|
||||
: moveFluid(from, new FluidStack(fluid, actualLimit), to);
|
||||
return moveFluid(from.storage(), to, fluid, actualLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public int pullFluid(
|
||||
IFluidHandler to, IComputerAccess computer,
|
||||
FluidMethods.StorageWrapper to, IComputerAccess computer,
|
||||
String fromName, Optional<Integer> limit, Optional<String> fluidName
|
||||
) throws LuaException {
|
||||
var fluid = fluidName.isPresent()
|
||||
@@ -86,13 +91,11 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
|
||||
int actualLimit = limit.orElse(Integer.MAX_VALUE);
|
||||
if (actualLimit <= 0) throw new LuaException("Limit must be > 0");
|
||||
|
||||
return fluid == null
|
||||
? moveFluid(from, actualLimit, to)
|
||||
: moveFluid(from, new FluidStack(fluid, actualLimit), to);
|
||||
return moveFluid(from, to.storage(), fluid, actualLimit);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static IFluidHandler extractHandler(IPeripheral peripheral) {
|
||||
private static ResourceHandler<FluidResource> extractHandler(IPeripheral peripheral) {
|
||||
var object = peripheral.getTarget();
|
||||
var direction = peripheral instanceof dan200.computercraft.shared.peripheral.generic.GenericPeripheral sided ? sided.side() : null;
|
||||
|
||||
@@ -102,11 +105,10 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
|
||||
var level = blockEntity.getLevel();
|
||||
if (!(level instanceof ServerLevel serverLevel)) return null;
|
||||
|
||||
var result = CapabilityUtil.getCapability(serverLevel, Capabilities.FluidHandler.BLOCK, blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, direction);
|
||||
var result = CapabilityUtil.getCapability(serverLevel, Capabilities.Fluid.BLOCK, blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, direction);
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
if (object instanceof IFluidHandler handler) return handler;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -114,49 +116,14 @@ public final class FluidMethods extends AbstractFluidMethods<IFluidHandler> {
|
||||
* Move fluid from one handler to another.
|
||||
*
|
||||
* @param from The handler to move from.
|
||||
* @param fluid The fluid to extract.
|
||||
* @param limit The maximum amount of fluid to move.
|
||||
* @param to The handler to move to.
|
||||
* @return The amount of fluid moved.
|
||||
*/
|
||||
private static int moveFluid(IFluidHandler from, int limit, IFluidHandler to) {
|
||||
return moveFluid(from, from.drain(limit, IFluidHandler.FluidAction.SIMULATE), limit, to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move fluid from one handler to another.
|
||||
*
|
||||
* @param from The handler to move from.
|
||||
* @param fluid The fluid and limit to move.
|
||||
* @param to The handler to move to.
|
||||
* @return The amount of fluid moved.
|
||||
*/
|
||||
private static int moveFluid(IFluidHandler from, FluidStack fluid, IFluidHandler to) {
|
||||
return moveFluid(from, from.drain(fluid, IFluidHandler.FluidAction.SIMULATE), fluid.getAmount(), to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move fluid from one handler to another.
|
||||
*
|
||||
* @param from The handler to move from.
|
||||
* @param extracted The fluid which is extracted from {@code from}.
|
||||
* @param limit The maximum amount of fluid to move.
|
||||
* @param to The handler to move to.
|
||||
* @return The amount of fluid moved.
|
||||
*/
|
||||
private static int moveFluid(IFluidHandler from, FluidStack extracted, int limit, IFluidHandler to) {
|
||||
if (extracted.getAmount() <= 0) return 0;
|
||||
|
||||
// Limit the amount to extract.
|
||||
extracted = extracted.copy();
|
||||
extracted.setAmount(Math.min(extracted.getAmount(), limit));
|
||||
|
||||
var inserted = to.fill(extracted.copy(), IFluidHandler.FluidAction.EXECUTE);
|
||||
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.
|
||||
extracted.setAmount(inserted);
|
||||
from.drain(extracted, IFluidHandler.FluidAction.EXECUTE);
|
||||
return inserted;
|
||||
private static int moveFluid(ResourceHandler<FluidResource> from, ResourceHandler<FluidResource> to, @Nullable Fluid fluid, int limit) {
|
||||
Predicate<FluidResource> predicate = fluid == null ? x -> true : x -> x.is(fluid);
|
||||
var moved = ResourceHandlerUtil.moveFirst(from, to, predicate, limit, null);
|
||||
return moved == null ? 0 : moved.amount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@ import dan200.computercraft.shared.platform.ForgeContainerTransfer;
|
||||
import dan200.computercraft.shared.util.CapabilityUtil;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.neoforged.neoforge.capabilities.Capabilities;
|
||||
import net.neoforged.neoforge.items.IItemHandler;
|
||||
import net.neoforged.neoforge.items.wrapper.InvWrapper;
|
||||
import net.neoforged.neoforge.transfer.ResourceHandler;
|
||||
import net.neoforged.neoforge.transfer.item.ItemResource;
|
||||
import net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -26,22 +28,26 @@ import java.util.Optional;
|
||||
import static dan200.computercraft.core.util.ArgumentHelpers.assertBetween;
|
||||
|
||||
/**
|
||||
* Inventory methods for Forge's {@link IItemHandler}.
|
||||
* Inventory methods for Forge's {@link ResourceHandler}.
|
||||
*/
|
||||
public final class InventoryMethods extends AbstractInventoryMethods<IItemHandler> {
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public int size(IItemHandler inventory) {
|
||||
return inventory.getSlots();
|
||||
public final class InventoryMethods extends AbstractInventoryMethods<InventoryMethods.StorageWrapper> {
|
||||
public record StorageWrapper(ResourceHandler<ItemResource> storage) {
|
||||
}
|
||||
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public Map<Integer, Map<String, ?>> list(IItemHandler inventory) {
|
||||
public int size(StorageWrapper inventory) {
|
||||
return inventory.storage().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public Map<Integer, Map<String, ?>> list(StorageWrapper wrapper) {
|
||||
var storage = wrapper.storage();
|
||||
Map<Integer, Map<String, ?>> result = new HashMap<>();
|
||||
var size = inventory.getSlots();
|
||||
var size = storage.size();
|
||||
for (var i = 0; i < size; i++) {
|
||||
var stack = inventory.getStackInSlot(i);
|
||||
var stack = storage.getResource(i).toStack(storage.getAmountAsInt(i));
|
||||
if (!stack.isEmpty()) result.put(i + 1, VanillaDetailRegistries.ITEM_STACK.getBasicDetails(stack));
|
||||
}
|
||||
|
||||
@@ -51,24 +57,29 @@ public final class InventoryMethods extends AbstractInventoryMethods<IItemHandle
|
||||
@Override
|
||||
@Nullable
|
||||
@LuaFunction(mainThread = true)
|
||||
public Map<String, ?> getItemDetail(IItemHandler inventory, int slot) throws LuaException {
|
||||
assertBetween(slot, 1, inventory.getSlots(), "Slot out of range (%s)");
|
||||
public Map<String, ?> getItemDetail(StorageWrapper wrapper, int slot) throws LuaException {
|
||||
var storage = wrapper.storage();
|
||||
assertBetween(slot, 1, storage.size(), "Slot out of range (%s)");
|
||||
|
||||
var stack = inventory.getStackInSlot(slot - 1);
|
||||
var stack = storage.getResource(slot - 1).toStack(storage.getAmountAsInt(slot - 1));
|
||||
return stack.isEmpty() ? null : VanillaDetailRegistries.ITEM_STACK.getDetails(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public long getItemLimit(IItemHandler inventory, int slot) throws LuaException {
|
||||
assertBetween(slot, 1, inventory.getSlots(), "Slot out of range (%s)");
|
||||
return inventory.getSlotLimit(slot - 1);
|
||||
public long getItemLimit(StorageWrapper wrapper, int slot) throws LuaException {
|
||||
var storage = wrapper.storage();
|
||||
assertBetween(slot, 1, storage.size(), "Slot out of range (%s)");
|
||||
|
||||
// FIXME: The capacity will be 0 if the resource is empty (or not valid). If empty, we try with dirt.
|
||||
var item = storage.getResource(slot - 1);
|
||||
return storage.getCapacityAsLong(slot - 1, item.isEmpty() ? ItemResource.of(Items.DIRT) : item);
|
||||
}
|
||||
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public int pushItems(
|
||||
IItemHandler from, IComputerAccess computer,
|
||||
StorageWrapper from, IComputerAccess computer,
|
||||
String toName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
|
||||
) throws LuaException {
|
||||
// Find location to transfer to
|
||||
@@ -80,17 +91,17 @@ public final class InventoryMethods extends AbstractInventoryMethods<IItemHandle
|
||||
|
||||
// Validate slots
|
||||
int actualLimit = limit.orElse(Integer.MAX_VALUE);
|
||||
assertBetween(fromSlot, 1, from.getSlots(), "From slot out of range (%s)");
|
||||
if (toSlot.isPresent()) assertBetween(toSlot.get(), 1, to.getSlots(), "To slot out of range (%s)");
|
||||
assertBetween(fromSlot, 1, from.storage().size(), "From slot out of range (%s)");
|
||||
if (toSlot.isPresent()) assertBetween(toSlot.get(), 1, to.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(from.storage(), fromSlot - 1, to, toSlot.orElse(0) - 1, actualLimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
@LuaFunction(mainThread = true)
|
||||
public int pullItems(
|
||||
IItemHandler to, IComputerAccess computer,
|
||||
StorageWrapper to, IComputerAccess computer,
|
||||
String fromName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
|
||||
) throws LuaException {
|
||||
// Find location to transfer to
|
||||
@@ -102,15 +113,15 @@ public final class InventoryMethods extends AbstractInventoryMethods<IItemHandle
|
||||
|
||||
// Validate slots
|
||||
int actualLimit = limit.orElse(Integer.MAX_VALUE);
|
||||
assertBetween(fromSlot, 1, from.getSlots(), "From slot out of range (%s)");
|
||||
if (toSlot.isPresent()) assertBetween(toSlot.get(), 1, to.getSlots(), "To slot out of range (%s)");
|
||||
assertBetween(fromSlot, 1, from.size(), "From slot out of range (%s)");
|
||||
if (toSlot.isPresent()) assertBetween(toSlot.get(), 1, to.storage().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(from, fromSlot - 1, to.storage(), toSlot.orElse(0) - 1, actualLimit);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static IItemHandler extractHandler(IPeripheral peripheral) {
|
||||
private static ResourceHandler<ItemResource> extractHandler(IPeripheral peripheral) {
|
||||
var object = peripheral.getTarget();
|
||||
var direction = peripheral instanceof dan200.computercraft.shared.peripheral.generic.GenericPeripheral sided ? sided.side() : null;
|
||||
|
||||
@@ -124,8 +135,7 @@ public final class InventoryMethods extends AbstractInventoryMethods<IItemHandle
|
||||
if (result != null) return result;
|
||||
}
|
||||
|
||||
if (object instanceof IItemHandler handler) return handler;
|
||||
if (object instanceof Container container) return new InvWrapper(container);
|
||||
if (object instanceof Container container) return VanillaContainerWrapper.of(container);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -139,7 +149,7 @@ public final class InventoryMethods extends AbstractInventoryMethods<IItemHandle
|
||||
* @param limit The max number to move. {@link Integer#MAX_VALUE} for no limit.
|
||||
* @return The number of items moved.
|
||||
*/
|
||||
private static int moveItem(IItemHandler from, int fromSlot, IItemHandler to, int toSlot, final int limit) {
|
||||
private static int moveItem(ResourceHandler<ItemResource> from, int fromSlot, ResourceHandler<ItemResource> to, int toSlot, final int limit) {
|
||||
var fromWrapper = new ForgeContainerTransfer(from).singleSlot(fromSlot);
|
||||
var toWrapper = new ForgeContainerTransfer(to);
|
||||
if (toSlot >= 0) toWrapper = toWrapper.singleSlot(toSlot);
|
||||
|
||||
@@ -8,7 +8,6 @@ import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.MenuProvider;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
@@ -35,11 +34,6 @@ class FakePlayerExt extends FakePlayer {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startRiding(Entity vehicle, boolean force) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityDimensions getDefaultDimensions(Pose pose) {
|
||||
return DIMENSIONS;
|
||||
|
||||
@@ -4,20 +4,21 @@
|
||||
|
||||
package dan200.computercraft.shared.platform;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.neoforged.neoforge.items.IItemHandler;
|
||||
import net.neoforged.neoforge.transfer.ResourceHandler;
|
||||
import net.neoforged.neoforge.transfer.item.ItemResource;
|
||||
import net.neoforged.neoforge.transfer.transaction.Transaction;
|
||||
|
||||
public class ForgeContainerTransfer implements ContainerTransfer.Slotted {
|
||||
private final IItemHandler handler;
|
||||
private final ResourceHandler<ItemResource> handler;
|
||||
private final int offset;
|
||||
private final int limit;
|
||||
private final int slots;
|
||||
|
||||
public ForgeContainerTransfer(IItemHandler handler) {
|
||||
this(handler, 0, handler.getSlots(), handler.getSlots());
|
||||
public ForgeContainerTransfer(ResourceHandler<ItemResource> handler) {
|
||||
this(handler, 0, handler.size(), handler.size());
|
||||
}
|
||||
|
||||
public ForgeContainerTransfer(IItemHandler handler, int offset, int limit, int slots) {
|
||||
public ForgeContainerTransfer(ResourceHandler<ItemResource> handler, int offset, int limit, int slots) {
|
||||
this.handler = handler;
|
||||
this.offset = offset;
|
||||
this.limit = limit;
|
||||
@@ -48,43 +49,45 @@ public class ForgeContainerTransfer implements ContainerTransfer.Slotted {
|
||||
}
|
||||
|
||||
public static int moveItem(ForgeContainerTransfer src, ForgeContainerTransfer dest, int maxAmount) {
|
||||
var targetSlot = 0;
|
||||
var hasItem = false;
|
||||
|
||||
var movedStack = ItemStack.EMPTY;
|
||||
var moved = 0;
|
||||
|
||||
outer:
|
||||
for (var srcSlot = 0; srcSlot < src.limit; srcSlot++) {
|
||||
var actualSrcSlot = src.mapSlot(srcSlot);
|
||||
var stack = src.handler.extractItem(actualSrcSlot, maxAmount, true);
|
||||
if (stack.isEmpty()) continue;
|
||||
var resource = src.handler.getResource(actualSrcSlot);
|
||||
if (resource.isEmpty()) continue;
|
||||
|
||||
// Pick the first item in the inventory to be the one we transfer, skipping those that match.
|
||||
if (movedStack.isEmpty()) {
|
||||
movedStack = stack.copy();
|
||||
if (stack.getMaxStackSize() < maxAmount) maxAmount = stack.getMaxStackSize();
|
||||
} else if (!ItemStack.isSameItemSameComponents(stack, movedStack)) {
|
||||
continue;
|
||||
// Check how much can be extracted and inserted.
|
||||
int maxExtracted;
|
||||
try (var transaction = Transaction.openRoot()) {
|
||||
maxExtracted = src.handler.extract(actualSrcSlot, resource, maxAmount, transaction);
|
||||
}
|
||||
if (maxExtracted == 0) continue;
|
||||
|
||||
hasItem = true;
|
||||
|
||||
try (var transaction = Transaction.openRoot()) {
|
||||
// check how much can be inserted
|
||||
var accepted = dest.insert(resource, maxExtracted, transaction);
|
||||
if (accepted == 0) continue;
|
||||
|
||||
// Extract or rollback.
|
||||
if (src.handler.extract(actualSrcSlot, resource, accepted, transaction) == accepted) {
|
||||
transaction.commit();
|
||||
return accepted;
|
||||
}
|
||||
}
|
||||
|
||||
for (; targetSlot < dest.limit; targetSlot++) {
|
||||
var oldCount = stack.getCount();
|
||||
stack = dest.handler.insertItem(dest.mapSlot(targetSlot), stack, false);
|
||||
|
||||
var transferred = oldCount - stack.getCount();
|
||||
var extracted = src.handler.extractItem(actualSrcSlot, transferred, false);
|
||||
|
||||
moved += transferred;
|
||||
|
||||
// We failed to extract as much as we should have. This should never happen, but goodness knows.
|
||||
if (extracted.getCount() < transferred) break outer;
|
||||
|
||||
if (moved >= maxAmount) return moved;
|
||||
if (stack.isEmpty()) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (moved == 0) return movedStack.isEmpty() ? NO_ITEMS : NO_SPACE;
|
||||
return moved;
|
||||
return hasItem ? NO_SPACE : NO_ITEMS;
|
||||
}
|
||||
|
||||
private int insert(ItemResource item, int amount, Transaction transaction) {
|
||||
var inserted = 0;
|
||||
for (var i = 0; i < limit; i++) {
|
||||
inserted += handler.insert(mapSlot(i), item, amount - inserted, transaction);
|
||||
if (inserted == amount) break;
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,9 +61,9 @@ import net.neoforged.neoforge.common.ItemAbilities;
|
||||
import net.neoforged.neoforge.common.Tags;
|
||||
import net.neoforged.neoforge.common.extensions.IMenuTypeExtension;
|
||||
import net.neoforged.neoforge.event.EventHooks;
|
||||
import net.neoforged.neoforge.items.wrapper.InvWrapper;
|
||||
import net.neoforged.neoforge.registries.DeferredHolder;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
import net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
@@ -128,17 +128,17 @@ public class PlatformHelperImpl implements PlatformHelper {
|
||||
|
||||
@Override
|
||||
public ContainerTransfer.Slotted wrapContainer(Container container) {
|
||||
return new ForgeContainerTransfer(new InvWrapper(container));
|
||||
return new ForgeContainerTransfer(VanillaContainerWrapper.of(container));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ContainerTransfer getContainer(ServerLevel level, BlockPos pos, Direction side) {
|
||||
var inventory = level.getCapability(Capabilities.ItemHandler.BLOCK, pos, side);
|
||||
var inventory = level.getCapability(Capabilities.Item.BLOCK, pos, side);
|
||||
if (inventory != null) return new ForgeContainerTransfer(inventory);
|
||||
|
||||
var entity = InventoryUtil.getEntityContainer(level, pos, side);
|
||||
return entity == null ? null : new ForgeContainerTransfer(new InvWrapper(entity));
|
||||
return entity == null ? null : new ForgeContainerTransfer(VanillaContainerWrapper.of(entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,8 +7,8 @@ public net.minecraft.client.gui.components.ChatComponent allMessages
|
||||
|
||||
# ItemPocketRenderer/ItemPrintoutRenderer
|
||||
public net.minecraft.client.renderer.ItemInHandRenderer calculateMapTilt(F)F
|
||||
public net.minecraft.client.renderer.ItemInHandRenderer renderMapHand(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;ILnet/minecraft/world/entity/HumanoidArm;)V
|
||||
public net.minecraft.client.renderer.ItemInHandRenderer renderPlayerArm(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;IFFLnet/minecraft/world/entity/HumanoidArm;)V
|
||||
public net.minecraft.client.renderer.ItemInHandRenderer renderMapHand(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;ILnet/minecraft/world/entity/HumanoidArm;)V
|
||||
public net.minecraft.client.renderer.ItemInHandRenderer renderPlayerArm(Lcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/SubmitNodeCollector;IFFLnet/minecraft/world/entity/HumanoidArm;)V
|
||||
|
||||
# SpeakerInstance/SpeakerManager
|
||||
public com.mojang.blaze3d.audio.Channel pumpBuffers(I)V
|
||||
|
||||
@@ -26,7 +26,7 @@ CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles a
|
||||
[[dependencies.computercraft]]
|
||||
modId="neoforge"
|
||||
type="required"
|
||||
versionRange="[${neoVersion},21.9)"
|
||||
versionRange="[${neoVersion},21.11)"
|
||||
ordering="NONE"
|
||||
side="BOTH"
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ package dan200.computercraft.shared.platform;
|
||||
import dan200.computercraft.test.shared.WithMinecraft;
|
||||
import dan200.computercraft.test.shared.platform.ContainerTransferContract;
|
||||
import net.minecraft.world.Container;
|
||||
import net.neoforged.neoforge.items.wrapper.InvWrapper;
|
||||
import net.neoforged.neoforge.transfer.item.VanillaContainerWrapper;
|
||||
|
||||
@WithMinecraft
|
||||
public class ForgeContainerTransferTest implements ContainerTransferContract {
|
||||
@Override
|
||||
public ContainerTransfer.Slotted wrap(Container container) {
|
||||
return new ForgeContainerTransfer(new InvWrapper(container));
|
||||
return new ForgeContainerTransfer(VanillaContainerWrapper.of(container));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
package dan200.computercraft.test.shared;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import net.neoforged.api.distmarker.Dist;
|
||||
import net.neoforged.fml.loading.FMLLoader;
|
||||
import net.neoforged.fml.loading.LoadingModList;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -17,7 +20,18 @@ import java.util.Map;
|
||||
public final class NeoSetupHook implements WithMinecraft.SetupHook {
|
||||
@Override
|
||||
public void run() {
|
||||
// Feature flags require the loaded mod list to be available, so populate it with some empty data.
|
||||
LoadingModList.of(List.of(), List.of(), List.of(), List.of(), Map.of());
|
||||
// Bits of Minecraft depend on the loader being present. Do some nasty things to inject it.
|
||||
// TODO: Switch to using NF's JUnit support instead.
|
||||
try {
|
||||
var ctor = FMLLoader.class.getDeclaredConstructor(ClassLoader.class, String[].class, Dist.class, boolean.class, Path.class);
|
||||
ctor.setAccessible(true);
|
||||
var loader = ctor.newInstance(null, new String[0], Dist.CLIENT, false, Path.of("."));
|
||||
|
||||
var modListField = FMLLoader.class.getDeclaredField("loadingModList");
|
||||
modListField.setAccessible(true);
|
||||
modListField.set(loader, LoadingModList.of(List.of(), List.of(), List.of(), List.of(), List.of(), Map.of()));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public class TestMod {
|
||||
if (TestHooks.onBeforeDestroyBlock(e.getLevel(), e.getPos(), e.getState())) e.setCanceled(true);
|
||||
});
|
||||
|
||||
if (FMLEnvironment.dist == Dist.CLIENT) TestMod.onInitializeClient();
|
||||
if (FMLEnvironment.getDist() == Dist.CLIENT) TestMod.onInitializeClient();
|
||||
|
||||
var tests = TestHooks.loadTests();
|
||||
modBus.addListener((RegisterEvent event) -> {
|
||||
|
||||
Reference in New Issue
Block a user