refactor: port inventory tile trait to java

Keep TIInventoryTile as AE2's public rebuild API and JInventoryTile as the registered mixin input, preserving both generated interfaces and flattened sided-slot behavior.
This commit is contained in:
Algent committed 2026-08-30 13:56:05 +02:00
1 parent 48cbb7ff3e
commit 4e8d238b57
6 files changed
+391 -144

No files matched your search

+6 -5
View File
@@ -25,10 +25,11 @@ No trustworthy tool will produce a maintainable Java port automatically. A decom
The binary and source-level consumer audits, low-coupling Phase 3 queue, audit-derived immediate compatibility gate,
focused profile, first traversal optimization, complete redstone interaction helper unit, `MicroRecipe` port, and the
first five Java-trait ports are complete. `TPartialOcclusionTile` proved the no-field path, `TSlottedTile` proved state
first six Java-trait ports are complete. `TPartialOcclusionTile` proved the no-field path, `TSlottedTile` proved state
and lifecycle rewriting, `TRedstoneTile` removed the measured redstone allocation, `TTileChangeTile` exercised mutable
state plus inherited access, and `TFluidHandlerTile` preserved ordered Forge fluid distribution. All retain their exact
runtime interfaces. See `JAVA_MIGRATION_PROFILE.md`.
state plus inherited access, `TFluidHandlerTile` preserved ordered Forge fluid distribution, and
`TIInventoryTile`/`JInventoryTile` preserved AE2's direct rebuild linkage plus flattened sided inventory routing. All
retain their exact runtime interfaces. See `JAVA_MIGRATION_PROFILE.md`.
That audit found and this branch has now repaired one existing regression: Schematica 1.12.6 reflects the original private
`MultiPartRegistry$` field `codechicken$multipart$MultiPartRegistry$$typeMap` and casts it to a Scala mutable map. The
@@ -360,7 +361,7 @@ Exit condition: identified steady-state Scala allocation sites are removed with
- [ ] Preserve pass-through interface behavior.
- [x] Keep the completed OpenComputers `TSlottedTile.v_partMap` mutation/rebinding case green and cover ProjectRed/
Extra Utilities behavior before `TRedstoneTile`.
- [ ] Explicitly cover AE2's `TIInventoryTile.rebuildSlotMap` before the inventory trait.
- [x] Explicitly cover AE2's `TIInventoryTile.rebuildSlotMap` before the inventory trait.
Exit condition: built-in behavior is implemented in Java while the established runtime composition mechanism remains stable.
@@ -416,7 +417,7 @@ The work should move from low to high risk while keeping a buildable mixed-langu
Extensive tests are both possible and recommended here. Their purpose is not to prove that the current Scala behavior is ideal; it is to make its observable behavior explicit before translation, so every later difference is intentional.
The repository now has a Java-8-compatible JUnit Jupiter suite and a small Forge integration runner. The current
baseline is 141 plain-JVM tests and 37 Forge server tests. Minecraft 1.7.10 does not provide the modern GameTest
baseline is 153 plain-JVM tests and 70 Forge server tests. Minecraft 1.7.10 does not provide the modern GameTest
framework, so game-dependent coverage continues through the existing narrow Forge harness rather than a second test
framework.
+53
View File
@@ -1640,3 +1640,56 @@ multipart fluid distribution. This is a structural removal, not a throughput or
- Complete plain-JVM suite: 153 tests, 0 failures, 0 errors.
- Java 8 Forge dedicated-server suite: 62 tests, 0 failures, 0 errors.
- The frozen consumer inventory and audited source consumers contain no `TFluidHandlerTile` reference.
## 2026-08-30 — TIInventoryTile/JInventoryTile Java-trait port
AE2 is the load-bearing consumer for this port. `CableBusPart.notifyNeighbors` checks a generated tile with
`instanceof TIInventoryTile` and directly invokes `rebuildSlotMap()`. The registered trait input is separately named
`JInventoryTile`; preserving only one of those types would break either registration or AE2 linkage.
### Observable behavior
No known runtime divergence. Each generated tile starts with distinct empty inventory-list and slot-map instances.
Binding appends only `IInventory` parts in order, removal deletes the matching inventory, and clear empties both the
superclass part list and inventory state. Copying from another inventory tile shares its inventory list by identity and
rebuilds a distinct slot map; copying from a plain tile leaves both current references untouched.
The slot map still flattens every inventory in list order and routes each global slot to its original inventory and
local index. The fixed name, custom-name flag, stack limit, usability and no-op open/close behavior are unchanged.
Sided access still exposes only `ISidedInventory` slots, while its global base advances across every inventory;
insert/extract checks still delegate to sided inventories and return true for ordinary inventories.
### Generated ABI and raw input divergence
At Forge runtime `TIInventoryTile` remains a public interface extending exactly `ISidedInventory`, with no fields and
the exact same 28 methods: 20 behavior/inventory methods, four public state accessors, and four super accessors.
`JInventoryTile` remains a public child interface with no fields and the exact same 36 methods, adding four private-
field accessors and four `JInventoryTile` super bridges. The generated composite still owns the two private fields
`codechicken$multipart$scalatraits$JInventoryTile$$invList` and
`codechicken$multipart$scalatraits$JInventoryTile$$slotMap`, with `LinkedList` and `scala.Tuple2[]` descriptors.
The raw dev artifact now contains a Java interface `TIInventoryTile`, a concrete Java `JInventoryTile` mixin input,
and package-private `JInventoryTileAccess` instead of the Scala interface, `$class` helper and concrete forwarding
class. No frozen consumer references `TIInventoryTile$class` or names `JInventoryTile`; AE2's load-bearing
`TIInventoryTile.rebuildSlotMap()` descriptor is unchanged. As with the other Java-trait ports, consumers compiling
against the untransformed dev jar should use a transformed compile stub when they name the registered mixin type.
### Transformer constraint
The Java-trait stack analyzer has no case for JVM opcode `NEWARRAY` (`188`). Creating the primitive result array in
`getAccessibleSlotsFromSide` initially failed trait registration with `scala.MatchError: 188`. The method now collects
the same ordered boxed slots and delegates only the final `int[]` allocation/copy to package-private
`JInventoryTileAccess`. Reference-array allocation remains inside the trait because its `ANEWARRAY` opcode is already
supported. The helper adds no public surface.
### Validation
- `TIInventoryTileFunctionalTest`: 7 Forge tests covering list/map lifecycle and identity, flattened routing, the
direct AE2-shaped rebuild call, fixed metadata, sided global offsets, and insert/extract delegation.
- `ForgeEnvironmentSmokeTest.generatesAndCachesInventoryTileClassWithBothPublicTraitInterfaces`: exact 28- and
36-method runtime interfaces, inheritance, generated private fields, distinct initialization, setter rebinding, and
generated-class reuse.
- All eight tests passed against the untouched Scala implementation first and pass unchanged after the port.
- Complete plain-JVM suite: 153 tests, 0 failures, 0 errors.
- Java 8 Forge dedicated-server suite: 70 tests, 0 failures, 0 errors.
- Raw `javap` descriptors for both public types match the reference, including `scala.Tuple2[]` state accessors.
+21 -13
View File
@@ -12,8 +12,8 @@ what breaks, and what is left. The other documents hold the reasoning.
| `JAVA_MIGRATION_MANUAL_CHECKS.md` | What no automated test can cover, and must be checked by hand in a client |
| `JAVA_MIGRATION_PROFILE.md` | The focused baseline, first measured result, findings, and exact rerun command |
Branch: `algent/java`. Base: `master`. 81 commits including separate characterization and port commits for
`TFluidHandlerTile`.
Branch: `algent/java`. Base: `master`. 83 commits including separate characterization and port commits for
`TIInventoryTile`/`JInventoryTile`.
## The one rule that matters
@@ -67,7 +67,7 @@ awk -F'"' '/<testsuite /{t+=$4;f+=$8;e+=$10} END{print "tests="t" failures="f" e
grep -o 'tests="[0-9]*" skipped="[0-9]*" failures="[0-9]*" errors="[0-9]*"' run/server/junit-out/TEST-*.xml
```
Current baseline: **153 plain-JVM tests, 62 Forge server tests, all passing at their last completed runs.**
Current baseline: **153 plain-JVM tests, 70 Forge server tests, all passing at their last completed runs.**
### ABI diff against the reference
@@ -193,6 +193,10 @@ as trait-owned state without checking its owner. `TFluidHandlerTile` therefore k
writes in package-private `TFluidHandlerTileAccess`. A static helper outside the transformed class is enough when no
trait-to-superclass cast is involved.
**Java traits cannot allocate primitive arrays.** The stack analyzer has no case for JVM opcode `NEWARRAY` and throws
`MatchError: 188` while registering the trait. `JInventoryTile` therefore converts its collected sided slots to
`int[]` in package-private `JInventoryTileAccess`. Reference arrays use `ANEWARRAY` and remain safe inside traits.
**Type the access shim's parameters `Object`.** `TTileChangeTileAccess` first typed them as the trait itself and cast
to `TileMultipart` inside. Because the untransformed trait extends `TileMultipart`, javac elided the cast, and once
Forge rewrote the trait to an interface the verifier rejected the `getfield`. `TRedstoneTileAccess` only escapes this
@@ -210,7 +214,7 @@ All eight load-bearing `$class` helpers from the inventory, both registries, and
`TFacePart`, the `TIconHitEffects` unit, `TItemMultiPart`/`JItemMultiPart`, `TEdgePart`, `Saw`, `MicroMaterialRegistry`,
`MultiPartRegistry`, `TileMultipart`, `TMultiPart`, `TickScheduler`, `BlockMultipart`, the complete
`IRedstonePart`/`RedstoneInteractions` unit, `MicroRecipe`, and the `TPartialOcclusionTile`, `TSlottedTile`, and
`TRedstoneTile`, `TTileChangeTile`, and `TFluidHandlerTile` Java-trait ports.
`TRedstoneTile`, `TTileChangeTile`, `TFluidHandlerTile`, and `TIInventoryTile`/`JInventoryTile` Java-trait ports.
Plus the six marker interfaces: `TSlottedPart`, `IRandomDisplayTick`, `INeighborTileChange`, `TRandomUpdateTick`,
`ISidedHollowConnect`, `IMicroMaterialRender`, plus `MultipartHelper`, `TileCache`, `PacketScheduler` and the `ControlKeyModifer` pair.
@@ -225,7 +229,7 @@ across the port. It is also where the two shim constraints in the gotchas list w
`rayTraceAll`'s index production is now characterized, closing the gap the read-path cleanup left: the index written
into `ExtendedMOP.data` is what `reduceMOP` hands back to every click, activate, harvest and pick block.
87 Java files, 41 Scala files, ~5,556 Scala lines left (non-blank; that is the metric this figure has always used).
89 Java files, 40 Scala files, ~5,453 Scala lines left (non-blank; that is the metric this figure has always used).
## What is left, and in what order
@@ -294,6 +298,13 @@ field initialization, setter rebinding, and class-cache behavior. No shipping co
`$class` or closure classes. A narrow access helper is required for `FluidStack.amount` because the transformer treats
all direct field reads inside a Java trait as trait-owned state.
`TIInventoryTile` and `JInventoryTile` are now Java without collapsing their distinct roles. AE2 still casts generated
tiles to the public 28-method `TIInventoryTile` interface and calls `rebuildSlotMap`; the registered `JInventoryTile`
still rewrites to a 36-method child interface carrying its private-state accessors and both super-bridge layers. Seven
behavior tests freeze inventory-list sharing, flattened routing, sided offsets and the direct rebuild call. The
generated composite retains its two private prefixed fields and per-tile initialization. Primitive `int[]` creation is
isolated in `JInventoryTileAccess` because the transformer cannot analyze `NEWARRAY`.
**Medium.** The `handler` packages on both sides, `ItemMicroPart`,
`MicroblockPlacement`, `PlacementGrids`, `BlockMicroMaterial`, `MissingMicroMaterial`, `ConfigContent`,
`DefaultContent`.
@@ -302,13 +313,10 @@ all direct field reads inside a Java trait as trait-owned state.
`multipart/scalatraits/`. The no-field, stateful, measured hot-query, and inherited-member cases are all complete
without a generator change.
Take `TIInventoryTile` next. AE2's `CableBusPart.notifyNeighbors` does `tile() instanceof TIInventoryTile` then
calls `rebuildSlotMap()`, so the runtime interface must keep that method public; freeze that plus the inventory-field
behavior first. Note it is `JInventoryTile`, the concrete class, that is registered — do not collapse the two, because
AE2 casts to the trait.
`TRandomDisplayTickTile` is registered client-only and reads inherited `partList`, so it needs both a shim and the
client-side test work. It also extends `TileMultipartClient`, which is still Scala.
Take the client tile pair next: `TileMultipartClient` and `TRandomDisplayTickTile`. The latter is the only Scala file
left in `multipart/scalatraits/`, is registered client-only, and reads inherited `partList`, so it needs both an access
shim and client-side characterization rather than relying on the dedicated-server harness. Audit the base trait's
consumer-visible shape before deciding whether to port them together.
**Phase 6/7, last.** `Microblock` and the microblock shape hierarchy, `MicroblockGenerator`, `MultipartGenerator`, and
all of `multipart/asm/`. The ASM subsystem should be last; freeze generated-class fixtures before touching it.
@@ -322,7 +330,7 @@ all of `multipart/asm/`. The ASM subsystem should be last; freeze generated-clas
work and is required before release claims about real TPS.
- The server side of flag-sensitive pass-through registration is automated; client-side exclusion still belongs to
the packaged-client/manual compatibility run.
- Shipping consumers compiled against the old `TSlottedTile` and `TRedstoneTile` interfaces remain binary-compatible
- Shipping consumers compiled against the old `TSlottedTile`, `TRedstoneTile`, and `TIInventoryTile` interfaces remain binary-compatible
because Forge still exposes those exact interfaces at runtime. A consumer recompiled against the untransformed dev
jar instead sees concrete Java mixin inputs and can emit class/field opcodes that are invalid after Forge rewrites
them to interfaces. Provide a transformed compile stub or downstream source guidance before claiming
@@ -0,0 +1,224 @@
package codechicken.multipart.scalatraits;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import codechicken.multipart.TMultiPart;
import codechicken.multipart.TileMultipart;
import scala.Tuple2;
/** Registered Java trait input that distributes inventory access among multipart inventories. */
public class JInventoryTile extends TileMultipart implements TIInventoryTile {
private LinkedList<IInventory> invList = new LinkedList<>();
@SuppressWarnings("unchecked")
private Tuple2<IInventory, Object>[] slotMap = (Tuple2<IInventory, Object>[]) new Tuple2[0];
@Override
public LinkedList<IInventory> invList() {
return invList;
}
@Override
public void invList_$eq(LinkedList<IInventory> inventories) {
invList = inventories;
}
@Override
public Tuple2<IInventory, Object>[] slotMap() {
return slotMap;
}
@Override
public void slotMap_$eq(Tuple2<IInventory, Object>[] slots) {
slotMap = slots;
}
@Override
public void codechicken$multipart$scalatraits$TIInventoryTile$$super$copyFrom(TileMultipart that) {
super.copyFrom(that);
}
@Override
public void codechicken$multipart$scalatraits$TIInventoryTile$$super$bindPart(TMultiPart part) {
super.bindPart(part);
}
@Override
public void codechicken$multipart$scalatraits$TIInventoryTile$$super$partRemoved(TMultiPart part, int position) {
super.partRemoved(part, position);
}
@Override
public void codechicken$multipart$scalatraits$TIInventoryTile$$super$clearParts() {
super.clearParts();
}
@Override
public void copyFrom(TileMultipart that) {
codechicken$multipart$scalatraits$TIInventoryTile$$super$copyFrom(that);
if (that instanceof TIInventoryTile) {
invList = ((TIInventoryTile) that).invList();
rebuildSlotMap();
}
}
@Override
public void bindPart(TMultiPart part) {
codechicken$multipart$scalatraits$TIInventoryTile$$super$bindPart(part);
if (part instanceof IInventory) {
invList.add((IInventory) part);
rebuildSlotMap();
}
}
@Override
public void partRemoved(TMultiPart part, int position) {
codechicken$multipart$scalatraits$TIInventoryTile$$super$partRemoved(part, position);
if (part instanceof IInventory) {
invList.remove(part);
rebuildSlotMap();
}
}
@Override
@SuppressWarnings("unchecked")
public void clearParts() {
codechicken$multipart$scalatraits$TIInventoryTile$$super$clearParts();
invList.clear();
slotMap = (Tuple2<IInventory, Object>[]) new Tuple2[0];
}
@Override
@SuppressWarnings("unchecked")
public void rebuildSlotMap() {
int totalSlots = 0;
for (IInventory inventory : invList) {
totalSlots += inventory.getSizeInventory();
}
Tuple2<IInventory, Object>[] rebuilt = (Tuple2<IInventory, Object>[]) new Tuple2[totalSlots];
int index = 0;
for (IInventory inventory : invList) {
for (int slot = 0; slot < inventory.getSizeInventory(); slot++) {
rebuilt[index++] = new Tuple2<IInventory, Object>(inventory, Integer.valueOf(slot));
}
}
slotMap = rebuilt;
}
@Override
public int getSizeInventory() {
return slotMap.length;
}
@Override
public ItemStack getStackInSlot(int slot) {
Tuple2<IInventory, Object> entry = slotMap[slot];
return entry._1().getStackInSlot(((Number) entry._2()).intValue());
}
@Override
public ItemStack decrStackSize(int slot, int amount) {
Tuple2<IInventory, Object> entry = slotMap[slot];
return entry._1().decrStackSize(((Number) entry._2()).intValue(), amount);
}
@Override
public ItemStack getStackInSlotOnClosing(int slot) {
Tuple2<IInventory, Object> entry = slotMap[slot];
return entry._1().getStackInSlotOnClosing(((Number) entry._2()).intValue());
}
@Override
public void setInventorySlotContents(int slot, ItemStack stack) {
Tuple2<IInventory, Object> entry = slotMap[slot];
entry._1().setInventorySlotContents(((Number) entry._2()).intValue(), stack);
}
@Override
public String getInventoryName() {
return "Multipart Inventory";
}
@Override
public boolean hasCustomInventoryName() {
return false;
}
@Override
public int getInventoryStackLimit() {
return 64;
}
@Override
public boolean isUseableByPlayer(EntityPlayer player) {
return true;
}
@Override
public void openInventory() {}
@Override
public void closeInventory() {}
@Override
public boolean isItemValidForSlot(int slot, ItemStack stack) {
Tuple2<IInventory, Object> entry = slotMap[slot];
return entry._1().isItemValidForSlot(((Number) entry._2()).intValue(), stack);
}
@Override
public int[] getAccessibleSlotsFromSide(int side) {
List<Integer> accessibleSlots = new ArrayList<>();
int base = 0;
for (IInventory inventory : invList) {
if (inventory instanceof ISidedInventory) {
int[] localSlots = ((ISidedInventory) inventory).getAccessibleSlotsFromSide(side);
for (int localSlot : localSlots) {
accessibleSlots.add(Integer.valueOf(localSlot + base));
}
}
base += inventory.getSizeInventory();
}
return JInventoryTileAccess.toIntArray(accessibleSlots);
}
@Override
public boolean canInsertItem(int slot, ItemStack stack, int side) {
Tuple2<IInventory, Object> entry = slotMap[slot];
IInventory inventory = entry._1();
return !(inventory instanceof ISidedInventory)
|| ((ISidedInventory) inventory).canInsertItem(((Number) entry._2()).intValue(), stack, side);
}
@Override
public boolean canExtractItem(int slot, ItemStack stack, int side) {
Tuple2<IInventory, Object> entry = slotMap[slot];
IInventory inventory = entry._1();
return !(inventory instanceof ISidedInventory)
|| ((ISidedInventory) inventory).canExtractItem(((Number) entry._2()).intValue(), stack, side);
}
}
/** Keeps primitive-array allocation outside the generated trait transformer. */
final class JInventoryTileAccess {
private JInventoryTileAccess() {}
static int[] toIntArray(List<Integer> values) {
int[] result = new int[values.size()];
for (int index = 0; index < result.length; index++) {
result[index] = values.get(index).intValue();
}
return result;
}
}
@@ -0,0 +1,87 @@
package codechicken.multipart.scalatraits;
import java.util.LinkedList;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.ISidedInventory;
import net.minecraft.item.ItemStack;
import codechicken.multipart.TMultiPart;
import codechicken.multipart.TileMultipart;
import scala.Tuple2;
/** Consumer-facing inventory contract implemented by the generated multipart tile. */
public interface TIInventoryTile extends ISidedInventory {
void codechicken$multipart$scalatraits$TIInventoryTile$$super$copyFrom(TileMultipart that);
void codechicken$multipart$scalatraits$TIInventoryTile$$super$bindPart(TMultiPart part);
void codechicken$multipart$scalatraits$TIInventoryTile$$super$partRemoved(TMultiPart part, int position);
void codechicken$multipart$scalatraits$TIInventoryTile$$super$clearParts();
LinkedList<IInventory> invList();
void invList_$eq(LinkedList<IInventory> inventories);
Tuple2<IInventory, Object>[] slotMap();
void slotMap_$eq(Tuple2<IInventory, Object>[] slots);
void copyFrom(TileMultipart that);
void bindPart(TMultiPart part);
void partRemoved(TMultiPart part, int position);
void clearParts();
void rebuildSlotMap();
@Override
int getSizeInventory();
@Override
ItemStack getStackInSlot(int slot);
@Override
ItemStack decrStackSize(int slot, int amount);
@Override
ItemStack getStackInSlotOnClosing(int slot);
@Override
void setInventorySlotContents(int slot, ItemStack stack);
@Override
String getInventoryName();
@Override
boolean hasCustomInventoryName();
@Override
int getInventoryStackLimit();
@Override
boolean isUseableByPlayer(EntityPlayer player);
@Override
void openInventory();
@Override
void closeInventory();
@Override
boolean isItemValidForSlot(int slot, ItemStack stack);
@Override
int[] getAccessibleSlotsFromSide(int side);
@Override
boolean canInsertItem(int slot, ItemStack stack, int side);
@Override
boolean canExtractItem(int slot, ItemStack stack, int side);
}
@@ -1,126 +0,0 @@
package codechicken.multipart.scalatraits
import net.minecraft.inventory.{IInventory, ISidedInventory}
import codechicken.multipart.{TMultiPart, TileMultipart}
import java.util.LinkedList
import net.minecraft.item.ItemStack
import net.minecraft.entity.player.EntityPlayer
import scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer
trait TIInventoryTile extends TileMultipart with ISidedInventory {
var invList = new LinkedList[IInventory]()
var slotMap = Array[(IInventory, Int)]()
override def copyFrom(that: TileMultipart) {
super.copyFrom(that)
if (that.isInstanceOf[TIInventoryTile]) {
invList = that.asInstanceOf[TIInventoryTile].invList
rebuildSlotMap()
}
}
override def bindPart(part: TMultiPart) {
super.bindPart(part)
if (part.isInstanceOf[IInventory]) {
invList += part.asInstanceOf[IInventory]
rebuildSlotMap()
}
}
override def partRemoved(part: TMultiPart, p: Int) {
super.partRemoved(part, p)
if (part.isInstanceOf[IInventory]) {
invList -= part.asInstanceOf[IInventory]
rebuildSlotMap()
}
}
override def clearParts() {
super.clearParts()
invList.clear()
slotMap = Array()
}
def rebuildSlotMap() {
slotMap = Array.ofDim(invList.map(_.getSizeInventory).sum)
var i = 0
for (inv <- invList; s <- 0 until inv.getSizeInventory) {
slotMap(i) = (inv, s)
i += 1
}
}
def getSizeInventory: Int = slotMap.length
def getStackInSlot(i: Int) = {
val (inv, slot) = slotMap(i)
inv.getStackInSlot(slot)
}
def decrStackSize(i: Int, j: Int) = {
val (inv, slot) = slotMap(i)
inv.decrStackSize(slot, j)
}
def getStackInSlotOnClosing(i: Int) = {
val (inv, slot) = slotMap(i)
inv.getStackInSlotOnClosing(slot)
}
def setInventorySlotContents(i: Int, itemstack: ItemStack) = {
val (inv, slot) = slotMap(i)
inv.setInventorySlotContents(slot, itemstack)
}
def getInventoryName = "Multipart Inventory"
def hasCustomInventoryName = false
def getInventoryStackLimit = 64
def isUseableByPlayer(entityplayer: EntityPlayer) = true
def openInventory() {}
def closeInventory() {}
def isItemValidForSlot(i: Int, itemstack: ItemStack) = {
val (inv, slot) = slotMap(i)
inv.isItemValidForSlot(slot, itemstack)
}
def getAccessibleSlotsFromSide(side: Int) = {
val buf = new ArrayBuffer[Int]()
var base = 0
for (inv <- invList) {
inv match {
case isided: ISidedInventory =>
buf ++= isided.getAccessibleSlotsFromSide(side).map(_ + base)
case _ =>
}
base += inv.getSizeInventory
}
buf.toArray
}
def canInsertItem(i: Int, itemstack: ItemStack, j: Int) = {
val (inv, slot) = slotMap(i)
inv match {
case isided: ISidedInventory => isided.canInsertItem(slot, itemstack, j)
case _ => true
}
}
def canExtractItem(i: Int, itemstack: ItemStack, j: Int) = {
val (inv, slot) = slotMap(i)
inv match {
case isided: ISidedInventory => isided.canExtractItem(slot, itemstack, j)
case _ => true
}
}
}
/** To handle obfuscation issues, this is registered as a java trait.
*/
class JInventoryTile extends TileMultipart with TIInventoryTile {}