Skip to content

Commit 4fd7fa7

Browse files
committed
Rewrite block entities in Java
Fixed bugs / code adjusted to match vanilla: BaseContainerBlockEntity.readNbt: recreate item list before reading
1 parent 935e94c commit 4fd7fa7

37 files changed

+908
-690
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package juuxel.adorn.block;
2+
3+
import juuxel.adorn.block.entity.AdornBlockEntityType;
4+
import juuxel.adorn.block.entity.BrewerBlockEntity;
5+
import juuxel.adorn.block.entity.DrawerBlockEntity;
6+
import juuxel.adorn.block.entity.KitchenCupboardBlockEntity;
7+
import juuxel.adorn.block.entity.KitchenSinkBlockEntity;
8+
import juuxel.adorn.block.entity.ShelfBlockEntity;
9+
import juuxel.adorn.block.entity.TradingStationBlockEntity;
10+
import juuxel.adorn.lib.registry.Registered;
11+
import juuxel.adorn.lib.registry.Registrar;
12+
import juuxel.adorn.lib.registry.RegistrarFactory;
13+
import juuxel.adorn.platform.PlatformBridges;
14+
import net.minecraft.block.Block;
15+
import net.minecraft.block.entity.BlockEntity;
16+
import net.minecraft.block.entity.BlockEntityType;
17+
import net.minecraft.block.entity.BlockEntityType.BlockEntityFactory;
18+
import net.minecraft.registry.RegistryKeys;
19+
20+
import java.util.function.Supplier;
21+
22+
public final class AdornBlockEntities {
23+
public static final Registrar<BlockEntityType<?>> BLOCK_ENTITIES = RegistrarFactory.get().create(RegistryKeys.BLOCK_ENTITY_TYPE);
24+
25+
public static final Registered<BlockEntityType<ShelfBlockEntity>> SHELF = register("shelf", ShelfBlockEntity::new, ShelfBlock.class);
26+
public static final Registered<BlockEntityType<DrawerBlockEntity>> DRAWER = register("drawer", DrawerBlockEntity::new, DrawerBlock.class);
27+
public static final Registered<BlockEntityType<KitchenCupboardBlockEntity>> KITCHEN_CUPBOARD =
28+
register("kitchen_cupboard", KitchenCupboardBlockEntity::new, KitchenCupboardBlock.class);
29+
public static final Registered<BlockEntityType<KitchenSinkBlockEntity>> KITCHEN_SINK =
30+
register("kitchen_sink", PlatformBridges.Companion.getBlockEntities()::createKitchenSink, KitchenSinkBlock.class);
31+
public static final Registered<BlockEntityType<TradingStationBlockEntity>> TRADING_STATION =
32+
register("trading_station", TradingStationBlockEntity::new, AdornBlocks.INSTANCE::getTRADING_STATION);
33+
public static final Registered<BlockEntityType<BrewerBlockEntity>> BREWER =
34+
register("brewer", PlatformBridges.Companion.getBlockEntities()::createBrewer, AdornBlocks.INSTANCE::getBREWER);
35+
36+
private static <E extends BlockEntity> Registered<BlockEntityType<E>> register(String name, BlockEntityFactory<E> factory, Supplier<? extends Block> block) {
37+
return BLOCK_ENTITIES.register(name, () -> BlockEntityType.Builder.create(factory, block.get()).build(null));
38+
}
39+
40+
private static <E extends BlockEntity> Registered<BlockEntityType<E>> register(String name, BlockEntityFactory<E> factory, Class<? extends Block> blockClass) {
41+
return BLOCK_ENTITIES.register(name, () -> new AdornBlockEntityType<>(factory, blockClass::isInstance));
42+
}
43+
44+
public static void init() {
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package juuxel.adorn.block.entity;
2+
3+
import net.minecraft.block.Block;
4+
import net.minecraft.block.BlockState;
5+
import net.minecraft.block.entity.BlockEntity;
6+
import net.minecraft.block.entity.BlockEntityType;
7+
8+
import java.util.Set;
9+
import java.util.function.Predicate;
10+
11+
public final class AdornBlockEntityType<E extends BlockEntity> extends BlockEntityType<E> {
12+
private final Predicate<Block> blockPredicate;
13+
14+
public AdornBlockEntityType(BlockEntityFactory<? extends E> factory, Predicate<Block> blockPredicate) {
15+
super(factory, Set.of(), null);
16+
this.blockPredicate = blockPredicate;
17+
}
18+
19+
@Override
20+
public boolean supports(BlockState state) {
21+
return blockPredicate.test(state.getBlock());
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package juuxel.adorn.block.entity;
2+
3+
import net.minecraft.block.BlockState;
4+
import net.minecraft.block.entity.BlockEntityType;
5+
import net.minecraft.block.entity.LootableContainerBlockEntity;
6+
import net.minecraft.inventory.Inventories;
7+
import net.minecraft.item.ItemStack;
8+
import net.minecraft.nbt.NbtCompound;
9+
import net.minecraft.text.Text;
10+
import net.minecraft.util.collection.DefaultedList;
11+
import net.minecraft.util.math.BlockPos;
12+
13+
/**
14+
* A container block entity that might not have a menu.
15+
* This class handles the serialisation and the container logic.
16+
*/
17+
public abstract class BaseContainerBlockEntity extends LootableContainerBlockEntity {
18+
private final int size;
19+
private DefaultedList<ItemStack> items;
20+
21+
public BaseContainerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state, int size) {
22+
super(type, pos, state);
23+
items = DefaultedList.ofSize(size, ItemStack.EMPTY);
24+
this.size = size;
25+
}
26+
27+
@Override
28+
protected void writeNbt(NbtCompound nbt) {
29+
super.writeNbt(nbt);
30+
if (!writeLootTable(nbt)) {
31+
Inventories.writeNbt(nbt, items);
32+
}
33+
}
34+
35+
@Override
36+
public void readNbt(NbtCompound nbt) {
37+
super.readNbt(nbt);
38+
items = DefaultedList.ofSize(size, ItemStack.EMPTY);
39+
if (!readLootTable(nbt)) {
40+
Inventories.readNbt(nbt, items);
41+
}
42+
}
43+
44+
@Override
45+
protected DefaultedList<ItemStack> method_11282() {
46+
return items;
47+
}
48+
49+
@Override
50+
protected void setInvStackList(DefaultedList<ItemStack> list) {
51+
items = list;
52+
}
53+
54+
@Override
55+
public int size() {
56+
return size;
57+
}
58+
59+
@Override
60+
protected Text getContainerName() {
61+
return getCachedState().getBlock().getName();
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
package juuxel.adorn.block.entity;
2+
3+
import juuxel.adorn.block.AdornBlockEntities;
4+
import juuxel.adorn.block.BrewerBlock;
5+
import juuxel.adorn.item.AdornItems;
6+
import juuxel.adorn.menu.BrewerMenu;
7+
import juuxel.adorn.recipe.AdornRecipes;
8+
import juuxel.adorn.recipe.BrewerInventory;
9+
import juuxel.adorn.recipe.FluidBrewingRecipe;
10+
import net.minecraft.block.BlockState;
11+
import net.minecraft.entity.player.PlayerInventory;
12+
import net.minecraft.inventory.SidedInventory;
13+
import net.minecraft.item.ItemStack;
14+
import net.minecraft.menu.Menu;
15+
import net.minecraft.menu.property.PropertyDelegate;
16+
import net.minecraft.nbt.NbtCompound;
17+
import net.minecraft.recipe.RecipeEntry;
18+
import net.minecraft.util.ItemScatterer;
19+
import net.minecraft.util.math.BlockPos;
20+
import net.minecraft.util.math.Direction;
21+
import net.minecraft.util.math.MathHelper;
22+
import net.minecraft.world.World;
23+
import org.jetbrains.annotations.Nullable;
24+
25+
public abstract class BrewerBlockEntity extends BaseContainerBlockEntity implements SidedInventory, BrewerInventory {
26+
private static final String NBT_PROGRESS = "Progress";
27+
public static final int CONTAINER_SIZE = 4;
28+
public static final int INPUT_SLOT = 0;
29+
public static final int LEFT_INGREDIENT_SLOT = 1;
30+
public static final int RIGHT_INGREDIENT_SLOT = 2;
31+
public static final int FLUID_CONTAINER_SLOT = 3;
32+
public static final int MAX_PROGRESS = 200;
33+
public static final int FLUID_CAPACITY_IN_BUCKETS = 2;
34+
35+
private int progress = 0;
36+
private final PropertyDelegate propertyDelegate = new PropertyDelegate() {
37+
@Override
38+
public int get(int index) {
39+
return switch (index) {
40+
case 0 -> progress;
41+
default -> throw new IllegalArgumentException("Unknown property: " + index);
42+
};
43+
}
44+
45+
@Override
46+
public void set(int index, int value) {
47+
switch (index) {
48+
case 0 -> progress = value;
49+
default -> throw new IllegalArgumentException("Unknown property: " + index);
50+
}
51+
}
52+
53+
@Override
54+
public int size() {
55+
return 1;
56+
}
57+
};
58+
59+
public BrewerBlockEntity(BlockPos pos, BlockState state) {
60+
super(AdornBlockEntities.BREWER.get(), pos, state, CONTAINER_SIZE);
61+
}
62+
63+
@Override
64+
protected Menu createMenu(int syncId, PlayerInventory inv) {
65+
return new BrewerMenu(syncId, inv, this, propertyDelegate, getFluidReference());
66+
}
67+
68+
@Override
69+
protected void writeNbt(NbtCompound nbt) {
70+
super.writeNbt(nbt);
71+
nbt.putInt(NBT_PROGRESS, progress);
72+
}
73+
74+
@Override
75+
public void readNbt(NbtCompound nbt) {
76+
super.readNbt(nbt);
77+
progress = nbt.getInt(NBT_PROGRESS);
78+
}
79+
80+
@Override
81+
public int[] getAvailableSlots(Direction side) {
82+
var facing = getCachedState().get(BrewerBlock.Companion.getFACING());
83+
84+
if (side == facing.rotateYClockwise()) {
85+
return new int[] { LEFT_INGREDIENT_SLOT };
86+
} else if (side == facing.rotateYCounterclockwise()) {
87+
return new int[] { RIGHT_INGREDIENT_SLOT };
88+
} else if (side == facing.getOpposite()) {
89+
return new int[] { FLUID_CONTAINER_SLOT };
90+
} else if (side == Direction.UP) {
91+
return new int[] { INPUT_SLOT };
92+
} else if (side == Direction.DOWN) {
93+
return new int[] { INPUT_SLOT, FLUID_CONTAINER_SLOT };
94+
} else {
95+
return new int[0];
96+
}
97+
}
98+
99+
@Override
100+
public boolean isValid(int slot, ItemStack stack) {
101+
if (slot == INPUT_SLOT && !(stack.isOf(AdornItems.INSTANCE.getMUG()) && getStack(slot).isEmpty())) return false;
102+
if (slot == FLUID_CONTAINER_SLOT && !getStack(slot).isEmpty()) return false;
103+
return true;
104+
}
105+
106+
@Override
107+
public boolean canInsert(int slot, ItemStack stack, @Nullable Direction dir) {
108+
return dir != Direction.DOWN && isValid(slot, stack);
109+
}
110+
111+
@Override
112+
public boolean canExtract(int slot, ItemStack stack, Direction dir) {
113+
return dir == Direction.DOWN && (slot != FLUID_CONTAINER_SLOT || canExtractFluidContainer());
114+
}
115+
116+
public int calculateComparatorOutput() {
117+
// If brewing has finished
118+
var mugStack = getStack(INPUT_SLOT);
119+
if (!mugStack.isEmpty() && !mugStack.isOf(AdornItems.INSTANCE.getMUG())) {
120+
return 15;
121+
}
122+
123+
var progressFraction = (float) progress / MAX_PROGRESS;
124+
var level = progressFraction * 14;
125+
return MathHelper.ceil(level);
126+
}
127+
128+
protected abstract boolean canExtractFluidContainer();
129+
protected abstract void tryExtractFluidContainer();
130+
131+
private boolean isActive() {
132+
return progress != 0;
133+
}
134+
135+
private static void decrementIngredient(BrewerBlockEntity brewer, int slot) {
136+
var stack = brewer.getStack(slot);
137+
// TODO: Use stack-aware version on Fabric (and Neo if available)
138+
var remainder = stack.getItem().getRecipeRemainder();
139+
stack.decrement(1);
140+
141+
if (remainder != null) {
142+
if (stack.isEmpty()) {
143+
brewer.setStack(slot, new ItemStack(remainder));
144+
} else {
145+
ItemScatterer.spawn(brewer.world, brewer.pos.getX() + 0.5, brewer.pos.getY() + 0.5, brewer.pos.getZ() + 0.5, new ItemStack(remainder));
146+
}
147+
}
148+
}
149+
150+
public static void tick(World world, BlockPos pos, BlockState state, BrewerBlockEntity brewer) {
151+
var originallyActive = brewer.isActive();
152+
brewer.tryExtractFluidContainer();
153+
154+
var dirty = false;
155+
var hasMug = !brewer.getStack(INPUT_SLOT).isEmpty();
156+
157+
if (hasMug != state.get(BrewerBlock.Companion.getHAS_MUG())) {
158+
world.setBlockState(pos, state.with(BrewerBlock.Companion.getHAS_MUG(), hasMug));
159+
}
160+
161+
var recipe = world.getRecipeManager().getFirstMatch(AdornRecipes.BREWING_TYPE.get(), brewer, world).map(RecipeEntry::value).orElse(null);
162+
163+
if (recipe != null && brewer.getStack(INPUT_SLOT).isOf(AdornItems.INSTANCE.getMUG())) {
164+
if (brewer.progress++ >= MAX_PROGRESS) {
165+
decrementIngredient(brewer, LEFT_INGREDIENT_SLOT);
166+
decrementIngredient(brewer, RIGHT_INGREDIENT_SLOT);
167+
brewer.setStack(INPUT_SLOT, recipe.craft(brewer, world.getRegistryManager()));
168+
169+
if (recipe instanceof FluidBrewingRecipe fluidRecipe) {
170+
brewer.getFluidReference().decrement(fluidRecipe.fluid().amount(), fluidRecipe.fluid().unit());
171+
}
172+
}
173+
174+
dirty = true;
175+
} else {
176+
if (brewer.progress != 0) {
177+
brewer.progress = 0;
178+
dirty = true;
179+
}
180+
}
181+
182+
var activeNow = brewer.isActive();
183+
if (originallyActive != activeNow) {
184+
dirty = true;
185+
var newState = state.with(BrewerBlock.Companion.getACTIVE(), activeNow);
186+
world.setBlockState(pos, newState);
187+
}
188+
189+
if (dirty) {
190+
markDirty(world, pos, state);
191+
}
192+
}
193+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package juuxel.adorn.block.entity;
2+
3+
import juuxel.adorn.block.AdornBlockEntities;
4+
import juuxel.adorn.menu.DrawerMenu;
5+
import juuxel.adorn.util.ExtensionsKt;
6+
import net.minecraft.block.BlockState;
7+
import net.minecraft.entity.player.PlayerInventory;
8+
import net.minecraft.menu.Menu;
9+
import net.minecraft.util.math.BlockPos;
10+
11+
public final class DrawerBlockEntity extends SimpleContainerBlockEntity {
12+
public DrawerBlockEntity(BlockPos pos, BlockState state) {
13+
super(AdornBlockEntities.DRAWER.get(), pos, state, 15);
14+
}
15+
16+
@Override
17+
protected Menu createMenu(int syncId, PlayerInventory inv) {
18+
return new DrawerMenu(syncId, inv, this, ExtensionsKt.menuContextOf(this));
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package juuxel.adorn.block.entity;
2+
3+
import juuxel.adorn.block.AdornBlockEntities;
4+
import juuxel.adorn.menu.KitchenCupboardMenu;
5+
import juuxel.adorn.util.ExtensionsKt;
6+
import net.minecraft.block.BlockState;
7+
import net.minecraft.entity.player.PlayerInventory;
8+
import net.minecraft.menu.Menu;
9+
import net.minecraft.util.math.BlockPos;
10+
11+
public final class KitchenCupboardBlockEntity extends SimpleContainerBlockEntity {
12+
public KitchenCupboardBlockEntity(BlockPos pos, BlockState state) {
13+
super(AdornBlockEntities.KITCHEN_CUPBOARD.get(), pos, state, 15);
14+
}
15+
16+
@Override
17+
protected Menu createMenu(int syncId, PlayerInventory inv) {
18+
return new KitchenCupboardMenu(syncId, inv, this, ExtensionsKt.menuContextOf(this));
19+
}
20+
}

0 commit comments

Comments
 (0)