Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.gregtechceu.gtceu.api.machine.property.GTMachineModelProperties;
import com.gregtechceu.gtceu.api.machine.trait.MachineTrait;
import com.gregtechceu.gtceu.api.machine.trait.multiblock.MultiblockMachineTrait;
import com.gregtechceu.gtceu.api.mui.MultiblockSchemaInfo;
import com.gregtechceu.gtceu.api.multiblock.MultiblockWorldSavedData;
import com.gregtechceu.gtceu.api.multiblock.pattern.*;
import com.gregtechceu.gtceu.api.multiblock.util.AbstractStructureHelper;
Expand Down Expand Up @@ -522,7 +523,8 @@ public InteractionResult onUse(ExtendedUseOnContext context) {
}

if (structureHelper != null) {
structureHelper.populate(resultStructure, pattern, null, getFrontFacing(), getUpwardsFacing(),
structureHelper.populate(new MultiblockSchemaInfo(), resultStructure, pattern, null,
getFrontFacing(), getUpwardsFacing(),
isFlipped());
Long2ReferenceMap<BlockState> blocks = new Long2ReferenceOpenHashMap<>();
resultStructure.forEach((pos, state) -> blocks.put(pos.asLong(), state.getBlockState()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import brachy.modularui.drawable.SchemaRenderer;
import brachy.modularui.widgets.SchemaWidget;
import com.google.common.collect.HashBasedTable;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.*;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
Expand Down Expand Up @@ -47,9 +48,16 @@ public class MultiblockSchemaInfo {
@Setter
private SchemaRenderer renderer;
@Getter
private final Reference2IntMap<Block> blockCounts = new Reference2IntOpenHashMap<>();
@Setter
private Reference2IntMap<Block> blockCounts = new Reference2IntOpenHashMap<>();
@Getter
private final Long2ObjectMap<BlockInfo> userGlobalBlockPreferences = new Long2ObjectOpenHashMap<>();
@Getter
protected final HashBasedTable<MultiPredicate, BasePredicate, BlockInfo> blockPreferences = HashBasedTable
.create();
@Getter
protected final HashBasedTable<MultiPredicate, BasePredicate, IntIntPair> minMaxPreferences = HashBasedTable
.create();

@Getter
private final Int2IntMap userSliceRepeats = new Int2IntArrayMap();
Expand Down Expand Up @@ -92,7 +100,7 @@ public void refreshSchema(MultiblockMachineDefinition multiblockDefinition, Dire
}
}

this.structureHelper.populate(resultStructure, pattern, this.userGlobalBlockPreferences,
this.structureHelper.populate(this, resultStructure, pattern, this.userGlobalBlockPreferences,
frontFacing, upFacing, isFlipped);

Long2ReferenceMap<BlockState> schemaMap = new Long2ReferenceOpenHashMap<>();
Expand All @@ -115,12 +123,24 @@ public void refreshSchema(MultiblockMachineDefinition multiblockDefinition, Dire
}
}

public int getMinCount(MultiPredicate predicate, BasePredicate basePredicate) {
if (!minMaxPreferences.contains(predicate, basePredicate))
return basePredicate.getMinCount();
return minMaxPreferences.get(predicate, basePredicate).leftInt();
}

public int getMaxCount(MultiPredicate predicate, BasePredicate basePredicate) {
if (!minMaxPreferences.contains(predicate, basePredicate))
return basePredicate.getMaxCount();
return minMaxPreferences.get(predicate, basePredicate).rightInt();
}

public void clearUserPreferences() {
this.userSliceRepeats.clear();
this.userDimensions.clear();
}

public void putPredicatePreference(MultiPredicate predicate, BasePredicate basePredicate, BlockInfo info) {
this.structureHelper.getBlockPreferences().put(predicate, basePredicate, info);
this.blockPreferences.put(predicate, basePredicate, info);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.gregtechceu.gtceu.api.block.MetaMachineBlock;
import com.gregtechceu.gtceu.api.block.property.GTBlockStateProperties;
import com.gregtechceu.gtceu.api.mui.MultiblockSchemaInfo;
import com.gregtechceu.gtceu.api.multiblock.MultiPredicate;
import com.gregtechceu.gtceu.api.multiblock.pattern.IBlockPattern;
import com.gregtechceu.gtceu.api.multiblock.predicates.BasePredicate;
Expand All @@ -11,14 +12,10 @@
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;

import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.IntIntPair;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter;
import org.jetbrains.annotations.Nullable;

import java.util.Map;
Expand All @@ -28,11 +25,6 @@ public abstract class AbstractStructureHelper {
public static final Direction[] DIRECTIONS_IN_ORDER = { Direction.NORTH, Direction.SOUTH, Direction.WEST,
Direction.EAST, Direction.UP, Direction.DOWN };

@Getter
protected final HashBasedTable<MultiPredicate, BasePredicate, BlockInfo> blockPreferences = HashBasedTable
.create();
protected final HashBasedTable<MultiPredicate, BasePredicate, IntIntPair> minMaxPreferences = HashBasedTable
.create();
protected @Nullable Block controllerBlock;

public static AbstractStructureHelper blockPattern(Int2IntMap sliceRepeats) {
Expand All @@ -43,48 +35,35 @@ public static AbstractStructureHelper expandable(IntList sliceRepeats) {
return new ExpandablePatternHelper(sliceRepeats);
}

public Table<MultiPredicate, BasePredicate, IntIntPair> getMinMaxPreferences() {
return this.minMaxPreferences;
}

public void populate(Map<BlockPos, BlockInfo> resultStructure, IBlockPattern pattern,
public void populate(MultiblockSchemaInfo info, Map<BlockPos, BlockInfo> resultStructure, IBlockPattern pattern,
@Nullable Long2ObjectMap<BlockInfo> userBlockPreferences,
Direction frontFacing, Direction upFacing, boolean isFlipped) {
setup(pattern, frontFacing, upFacing, isFlipped);
if (userBlockPreferences != null && !userBlockPreferences.isEmpty()) {
populateWithUserBlockPreferences(resultStructure, pattern, userBlockPreferences, frontFacing, upFacing,
populateWithUserBlockPreferences(info, resultStructure, pattern, userBlockPreferences, frontFacing,
upFacing,
isFlipped);
}
populateFromPattern(resultStructure, pattern, frontFacing, upFacing, isFlipped);
populateFromPattern(info, resultStructure, pattern, frontFacing, upFacing, isFlipped);
fixRotationsAndFacing(resultStructure, frontFacing, upFacing, this.controllerBlock);
}

protected void setup(IBlockPattern pattern, Direction frontFacing, Direction upFacing, boolean isFlipped) {}

protected abstract void populateWithUserBlockPreferences(Map<BlockPos, BlockInfo> resultStructure,
protected abstract void populateWithUserBlockPreferences(MultiblockSchemaInfo info,
Map<BlockPos, BlockInfo> resultStructure,
IBlockPattern pattern,
Long2ObjectMap<BlockInfo> userBlockPreferences,
Direction frontFacing, Direction upFacing,
boolean isFlipped);

protected abstract void populateFromPattern(Map<BlockPos, BlockInfo> resultStructure, IBlockPattern pattern,
protected abstract void populateFromPattern(MultiblockSchemaInfo info, Map<BlockPos, BlockInfo> resultStructure,
IBlockPattern pattern,
Direction frontFacing, Direction upFacing, boolean isFlipped);

public abstract MultiPredicate getPredicateFromPos(IBlockPattern pattern, BlockPos pos,
Direction frontFacing, Direction upFacing, boolean isFlipped);

protected int getMinCount(MultiPredicate predicate, BasePredicate basePredicate) {
if (!minMaxPreferences.contains(predicate, basePredicate))
return basePredicate.getMinCount();
return minMaxPreferences.get(predicate, basePredicate).leftInt();
}

protected int getMaxCount(MultiPredicate predicate, BasePredicate basePredicate) {
if (!minMaxPreferences.contains(predicate, basePredicate))
return basePredicate.getMaxCount();
return minMaxPreferences.get(predicate, basePredicate).rightInt();
}

protected static int countPopulatedGlobal(Map<BlockPos, BlockInfo> resultStructure, BasePredicate basePredicate) {
return (int) resultStructure.values().stream()
.filter(blockInfo -> basePredicate.getCandidates().contains(blockInfo))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package com.gregtechceu.gtceu.api.multiblock.util;

import com.gregtechceu.gtceu.api.machine.MultiblockMachineDefinition;
import com.gregtechceu.gtceu.api.machine.multiblock.MultiblockControllerMachine;
import com.gregtechceu.gtceu.api.multiblock.PredicateContext;

import com.gregtechceu.gtceu.client.renderer.AABBHighlightRenderer;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;

import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;

import java.util.Comparator;
import java.util.Map;

import static com.gregtechceu.gtceu.api.machine.multiblock.MultiblockControllerMachine.DEFAULT_STRUCTURE;

public class AutobuildHelper {

// todo remove
public static Long2ObjectMap<BlockInfo> readBlockPreferences(CompoundTag tag) {
Long2ObjectMap<BlockInfo> blockPreferences = new Long2ObjectOpenHashMap<>();
if (!tag.contains("blockPreferences", CompoundTag.TAG_LIST)) {
return blockPreferences;
}

var preferences = tag.getList("blockPreferences", CompoundTag.TAG_COMPOUND);
for (int i = 0; i < preferences.size(); i++) {
CompoundTag preference = preferences.getCompound(i);
BlockState state = NbtUtils.readBlockState(BuiltInRegistries.BLOCK.asLookup(),
preference.getCompound("state"));
if (state.isAir()) continue;
blockPreferences.put(preference.getLong("pos"), BlockInfo.fromBlockState(state));
}
return blockPreferences;
}

/*
* iterate over every position in the structure
* for each block
* - if that block is part of the structure and valid in that predicate, add to alreadyPlaced
* - if that block can be replaced(air, tall grass, etc? block property replaceable) add to replaceableBlocks
* - if that block CANT be replaced and not valid, add to canNotPlace,
* maybe add what already exists there to another list for reporting(invalidBlocks)?
*
*
* for each replaceableBlock
* - if that candidate from the predicate exists in the inventory, add to some blocksToRemove list
* (small size for chunked building)
* figure out the auto placement action
* - if the candidate does not exist, add to blocksMissing(for later reporting)
*/

public static void autobuild(ServerPlayer player, ItemStack item, MultiblockMachineDefinition definition,
MultiblockControllerMachine controller, Map<BlockPos, BlockInfo> blocksToPlace,
AbstractStructureHelper structureHelper) {
Long2ObjectOpenHashMap<BlockState> alreadyValidPlaced = new Long2ObjectOpenHashMap<>();
Long2ObjectOpenHashMap<BlockState> replaceableBlocks = new Long2ObjectOpenHashMap<>();
Long2ObjectOpenHashMap<BlockState> canNotPlaceBlocks = new Long2ObjectOpenHashMap<>();

Level level = player.level();

Block controllerBlock = controller.getDefinition().getBlock();
BlockPos schemaControllerPos = BlockPos.ZERO;
for (var entry : blocksToPlace.entrySet()) {
if (entry.getValue().getBlockState().is(controllerBlock)) {
schemaControllerPos = entry.getKey();
break;
}
}

BlockPos controllerOffset = controller.getBlockPos().subtract(schemaControllerPos);

PredicateContext cxt = new PredicateContext(null);
cxt.updateLevel(level);
int checkedBlocks = 0;
for (var entry : blocksToPlace.entrySet()) {
BlockPos pos = entry.getKey().offset(controllerOffset);
var blockState = level.getBlockState(pos);

var predicate = structureHelper.getPredicateFromPos(
definition.getStructurePatterns().get(DEFAULT_STRUCTURE).get(),
entry.getKey(), controller.getFrontFacing(), controller.getUpwardsFacing(), controller.isFlipped());

cxt.updatePos(pos);
var innerPredicate = predicate.getPredicateAtPos(cxt);
if (innerPredicate != null) {
alreadyValidPlaced.put(pos.asLong(), blockState);
} else {
if (blockState.canBeReplaced()) {
replaceableBlocks.put(pos.asLong(), blocksToPlace.get(entry.getKey()).getBlockState());
checkedBlocks++;
} else {
canNotPlaceBlocks.put(pos.asLong(), blockState);
}
}
if (checkedBlocks > 32) {
break;
}
}

// Step 1. Making the "what we want" list
Map<Item, Integer> whatWeWant = new Object2IntArrayMap<>();
for (var entry : replaceableBlocks.long2ObjectEntrySet()) {
whatWeWant.merge(entry.getValue().getBlock().asItem(), 1, Integer::sum);
}

// Step 2. Try to fetch
Map<Item, Integer> whatWeHave = new Object2IntArrayMap<>();
for (var entry : whatWeWant.entrySet()) {
Item desiredItem = entry.getKey();
int desiredAmount = entry.getValue();
var slotIndex = player.getInventory().findSlotMatchingItem(new ItemStack(desiredItem));
if (slotIndex == -1) continue;
var playerSlotStack = player.getInventory().getItem(slotIndex);
int toDeduct = Math.min(playerSlotStack.getCount(), desiredAmount);
playerSlotStack.shrink(toDeduct);
whatWeHave.put(desiredItem, toDeduct);
}

// Step 3. Place what was fetched
for (var entry : replaceableBlocks.long2ObjectEntrySet()
.stream()
.sorted(Comparator.comparingLong(Long2ObjectMap.Entry::getLongKey))
.toList()) {
var blockState = entry.getValue();
var blocksLeft = whatWeHave.merge(blockState.getBlock().asItem(), -1, Integer::sum);
if (blocksLeft < 0) continue;
whatWeWant.merge(blockState.getBlock().asItem(), -1, Integer::sum);
level.setBlockAndUpdate(BlockPos.of(entry.getLongKey()), blockState);
}

if (!whatWeWant.isEmpty()) {
player.displayClientMessage(Component.translatable("gtceu.autobuild.missing_blocks").withStyle(ChatFormatting.RED), false);
}
for (var entry : whatWeWant.entrySet()) {
if (entry.getValue() > 0) {
player.displayClientMessage(Component.literal(entry.getKey().toString() + " " + entry.getValue()), false);
}
}

for (var entry : canNotPlaceBlocks.long2ObjectEntrySet()) {
AABBHighlightRenderer.INSTANCE.addHighlight(AABBHighlightRenderer.builder()
.aabb(BlockPos.of(entry.getLongKey()))
.colorARGB(255, 180, 0, 0)
.thickness(0.025)
.durationMillis(10000)
.phaseMillis(750)
.build());
}
}
}
Loading
Loading