mirror of
https://github.com/TicTicBoooom-Mods/MasterfulMachinery.git
synced 2026-01-19 00:47:25 +01:00
398 lines
18 KiB
Java
398 lines
18 KiB
Java
package com.ticticboooom.mods.mm.data;
|
|
|
|
import com.google.gson.JsonElement;
|
|
import com.google.gson.JsonObject;
|
|
import com.mojang.datafixers.util.Pair;
|
|
import com.mojang.serialization.Codec;
|
|
import com.mojang.serialization.DataResult;
|
|
import com.mojang.serialization.JsonOps;
|
|
import com.ticticboooom.mods.mm.MM;
|
|
import com.ticticboooom.mods.mm.block.ControllerBlock;
|
|
import com.ticticboooom.mods.mm.block.MachinePortBlock;
|
|
import com.ticticboooom.mods.mm.block.tile.IMachinePortTile;
|
|
import com.ticticboooom.mods.mm.data.model.structure.*;
|
|
import com.ticticboooom.mods.mm.exception.InvalidStructureDefinitionException;
|
|
import com.ticticboooom.mods.mm.helper.RLUtils;
|
|
import com.ticticboooom.mods.mm.registration.MMLoader;
|
|
import com.ticticboooom.mods.mm.registration.MMPorts;
|
|
import com.ticticboooom.mods.mm.registration.RecipeTypes;
|
|
import lombok.Getter;
|
|
import lombok.SneakyThrows;
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.block.BlockState;
|
|
import net.minecraft.inventory.IInventory;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.item.crafting.IRecipe;
|
|
import net.minecraft.item.crafting.IRecipeSerializer;
|
|
import net.minecraft.item.crafting.IRecipeType;
|
|
import net.minecraft.network.PacketBuffer;
|
|
import net.minecraft.state.Property;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.tags.ITag;
|
|
import net.minecraft.tileentity.TileEntity;
|
|
import net.minecraft.util.ResourceLocation;
|
|
import net.minecraft.util.Rotation;
|
|
import net.minecraft.util.math.BlockPos;
|
|
import net.minecraft.util.math.vector.Vector3i;
|
|
import net.minecraft.world.World;
|
|
import net.minecraftforge.fml.RegistryObject;
|
|
import net.minecraftforge.registries.ForgeRegistries;
|
|
|
|
import javax.annotation.Nullable;
|
|
import java.util.*;
|
|
|
|
public class MachineStructureRecipe implements IRecipe<IInventory> {
|
|
private final ResourceLocation rl;
|
|
@Getter
|
|
private String name;
|
|
@Getter
|
|
private List<List<MachineStructureRecipeKeyModel>> models;
|
|
@Getter
|
|
private final List<String> controllerId;
|
|
private String id;
|
|
|
|
public MachineStructureRecipe(List<MachineStructureRecipeKeyModel> models, List<String> controllerId, String id, ResourceLocation rl, String name) {
|
|
this.rl = rl;
|
|
this.name = name;
|
|
List<MachineStructureRecipeKeyModel> rotated = new ArrayList<>();
|
|
List<MachineStructureRecipeKeyModel> rotated1 = new ArrayList<>();
|
|
List<MachineStructureRecipeKeyModel> rotated2 = new ArrayList<>();
|
|
|
|
for (MachineStructureRecipeKeyModel model : models) {
|
|
BlockPos rotatedPos = new BlockPos(model.getPos().getX(), model.getPos().getY(), model.getPos().getZ()).rotate(Rotation.CLOCKWISE_90);
|
|
BlockPos rotatedPos1 = new BlockPos(model.getPos().getX(), model.getPos().getY(), model.getPos().getZ()).rotate(Rotation.CLOCKWISE_180);
|
|
BlockPos rotatedPos2 = new BlockPos(model.getPos().getX(), model.getPos().getY(), model.getPos().getZ()).rotate(Rotation.COUNTERCLOCKWISE_90);
|
|
|
|
rotated.add(new MachineStructureRecipeKeyModel(new MachineStructureBlockPos(rotatedPos.getX(), rotatedPos.getY(), rotatedPos.getZ()), model.getTag(), model.getBlock(), model.getProperties(), model.getPort()));
|
|
rotated1.add(new MachineStructureRecipeKeyModel(new MachineStructureBlockPos(rotatedPos1.getX(), rotatedPos1.getY(), rotatedPos1.getZ()), model.getTag(), model.getBlock(), model.getProperties(), model.getPort()));
|
|
rotated2.add(new MachineStructureRecipeKeyModel(new MachineStructureBlockPos(rotatedPos2.getX(), rotatedPos2.getY(), rotatedPos2.getZ()), model.getTag(), model.getBlock(), model.getProperties(), model.getPort()));
|
|
}
|
|
|
|
this.models = new ArrayList<>();
|
|
this.models.add(models);
|
|
this.models.add(rotated);
|
|
this.models.add(rotated1);
|
|
this.models.add(rotated2);
|
|
this.models.add(mirror(models));
|
|
this.models.add(mirror(rotated));
|
|
this.models.add(mirror(rotated1));
|
|
this.models.add(mirror(rotated2));
|
|
this.controllerId = controllerId;
|
|
this.id = id;
|
|
}
|
|
|
|
private List<MachineStructureRecipeKeyModel> mirror(List<MachineStructureRecipeKeyModel> models) {
|
|
List<MachineStructureRecipeKeyModel> mirrored = new ArrayList<>();
|
|
for (MachineStructureRecipeKeyModel model : models) {
|
|
BlockPos mirroredPos = new BlockPos(-model.getPos().getX(), model.getPos().getY(), model.getPos().getZ());
|
|
mirrored.add(new MachineStructureRecipeKeyModel(new MachineStructureBlockPos(mirroredPos.getX(), mirroredPos.getY(), mirroredPos.getZ()), model.getTag(), model.getBlock(), model.getProperties(), model.getPort()));
|
|
}
|
|
return mirrored;
|
|
}
|
|
|
|
@Override
|
|
public boolean matches(IInventory p_77569_1_, World p_77569_2_) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getCraftingResult(IInventory p_77572_1_) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public boolean canFit(int p_194133_1_, int p_194133_2_) {
|
|
return false;
|
|
}
|
|
|
|
public int matches(BlockPos controllerPos, World world, String controllerId) {
|
|
if (!this.controllerId.contains(controllerId)) {
|
|
return -1;
|
|
}
|
|
|
|
int index = 0;
|
|
for (List<MachineStructureRecipeKeyModel> model : models) {
|
|
if (matchesWithRotation(controllerPos, world, model)) {
|
|
return index;
|
|
}
|
|
index++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
public boolean matchesWithRotation(BlockPos controllerPos, World world, List<MachineStructureRecipeKeyModel> items) {
|
|
for (MachineStructureRecipeKeyModel model : items) {
|
|
if (!innerBlockMatch(controllerPos, world, model)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
public boolean innerBlockMatch(BlockPos controllerPos, World world, MachineStructureRecipeKeyModel model) {
|
|
BlockPos pos = controllerPos.add(model.getPos().getX(), model.getPos().getY(), model.getPos().getZ());
|
|
BlockState blockState = world.getBlockState(pos);
|
|
boolean valid = false;
|
|
if (!model.getTag().equals("")) {
|
|
String[] split = model.getTag().split(":");
|
|
if (split.length != 2) {
|
|
MM.LOG.fatal("too many : (colons) in structure tag: {}", model.getTag());
|
|
return false;
|
|
}
|
|
ITag<Block> tag = BlockTags.getCollection().get(new ResourceLocation(split[0], split[1]));
|
|
if (tag == null) {
|
|
MM.LOG.fatal("no existing block tag for structure tag: {}", model.getTag());
|
|
return false;
|
|
}
|
|
valid = tag.contains(blockState.getBlock());
|
|
} else if (!model.getBlock().equals("")) {
|
|
valid = blockState.getBlock().getRegistryName().toString().equals(model.getBlock());
|
|
} else if (model.getPort() != null) {
|
|
MachineStructurePort structurePort = model.getPort();
|
|
TileEntity portBlockEntity = world.getTileEntity(pos);
|
|
if (portBlockEntity instanceof IMachinePortTile && blockState.getBlock() instanceof MachinePortBlock) {
|
|
IMachinePortTile portTile = (IMachinePortTile) portBlockEntity;
|
|
MachinePortBlock portBlock = ((MachinePortBlock) blockState.getBlock());
|
|
if (portTile.isInput() == structurePort.isInput() &&
|
|
portBlock.getPortTypeId().equals(RLUtils.toRL(structurePort.getType()))) {
|
|
List<String> controllerIds = structurePort.getControllerId() != null ? structurePort.getControllerId() : this.controllerId;
|
|
valid = controllerIds.contains(portBlock.getControllerId());
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!valid) {
|
|
return false;
|
|
}
|
|
|
|
if (model.getProperties() == null) {
|
|
return true;
|
|
}
|
|
|
|
|
|
for (Map.Entry<String, String> stringStringEntry : model.getProperties().entrySet()) {
|
|
for (Map.Entry<Property<?>, Comparable<?>> propertyEntry : blockState.getValues().entrySet()) {
|
|
if (propertyEntry.getKey().getName().equals(stringStringEntry.getKey())) {
|
|
Optional<?> o = propertyEntry.getKey().parseValue(stringStringEntry.getValue());
|
|
return propertyEntry.getValue().equals(o.get());
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public ArrayList<BlockPos> getPorts(BlockPos controllerPos, World world, int index) {
|
|
ArrayList<BlockPos> result = new ArrayList<>();
|
|
for (MachineStructureRecipeKeyModel model : models.get(index)) {
|
|
BlockPos pos = controllerPos.add(model.getPos().toVector());
|
|
BlockState state = world.getBlockState(pos);
|
|
if (state.getBlock() instanceof MachinePortBlock) {
|
|
result.add(pos);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
public String getStructureId() {
|
|
return id;
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getRecipeOutput() {
|
|
return ItemStack.EMPTY;
|
|
}
|
|
|
|
@Override
|
|
public ResourceLocation getId() {
|
|
return rl;
|
|
}
|
|
|
|
@Override
|
|
public IRecipeSerializer<?> getSerializer() {
|
|
return RecipeTypes.STRUCTURE.get();
|
|
}
|
|
|
|
@Override
|
|
public IRecipeType<?> getType() {
|
|
return RecipeTypes.MACHINE_STRUCTURE;
|
|
}
|
|
|
|
public static final class Serializer implements IRecipeSerializer<MachineStructureRecipe> {
|
|
|
|
|
|
@Override
|
|
public MachineStructureRecipe read(ResourceLocation rl, JsonObject obj) {
|
|
try {
|
|
JsonElement controllerIdJson = obj.get("controllerId");
|
|
List<String> ids = new ArrayList<>();
|
|
if (controllerIdJson.isJsonPrimitive()) {
|
|
ids.add(controllerIdJson.getAsString());
|
|
} else {
|
|
for (JsonElement jsonElement : controllerIdJson.getAsJsonArray()) {
|
|
ids.add(jsonElement.getAsString());
|
|
}
|
|
}
|
|
String id = obj.get("id").getAsString();
|
|
String name = "";
|
|
if (obj.has("name")) {
|
|
name = obj.get("name").getAsString();
|
|
} else {
|
|
name = id;
|
|
}
|
|
DataResult<Pair<List<List<String>>, JsonElement>> apply = JsonOps.INSTANCE.withDecoder(Codec.list(Codec.list(Codec.STRING))).apply(obj.getAsJsonArray("layout"));
|
|
List<List<String>> layout = apply.result().get().getFirst();
|
|
|
|
List<MachineStructureRecipeKeyModel> result = getResult(obj.getAsJsonObject("legend"), layout);
|
|
|
|
validateStructure(result, ids, id, rl);
|
|
MM.LOG.debug("Added structure '{}' with id '{}'", rl, id);
|
|
return new MachineStructureRecipe(result, ids, id, rl, name);
|
|
} catch (InvalidStructureDefinitionException e) {
|
|
MM.LOG.error("InvalidStructureDefinition: " + e.getMessage());
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private List<MachineStructureRecipeKeyModel> getResult(JsonObject legend, List<List<String>> layout) throws InvalidStructureDefinitionException {
|
|
HashMap<Character, MachineStructureRecipeLegendModel> model = new HashMap<>();
|
|
for (Map.Entry<String, JsonElement> entry : legend.entrySet()) {
|
|
DataResult<Pair<MachineStructureRecipeLegendModel, JsonElement>> apply = JsonOps.INSTANCE.withDecoder(MachineStructureRecipeLegendModel.CODEC).apply(entry.getValue());
|
|
MachineStructureRecipeLegendModel first = apply.result().get().getFirst();
|
|
model.put(entry.getKey().charAt(0), first);
|
|
}
|
|
ArrayList<MachineStructureRecipeKeyModel> result = new ArrayList<>();
|
|
Vector3i controllerPos = getControllerPos(layout);
|
|
|
|
int y = 0;
|
|
int z = 0;
|
|
for (List<String> layer : layout) {
|
|
for (String row : layer) {
|
|
for (int x = 0; x < row.length(); x++) {
|
|
char c = row.charAt(x);
|
|
if (c == 'C') {
|
|
continue;
|
|
}
|
|
if (c == ' ') {
|
|
continue;
|
|
}
|
|
MachineStructureRecipeLegendModel machineStructureRecipeLegendModel = model.get(c);
|
|
BlockPos pos = new BlockPos(x, y, z).subtract(new BlockPos(controllerPos));
|
|
result.add(new MachineStructureRecipeKeyModel(new MachineStructureBlockPos(pos.getX(), pos.getY(), pos.getZ()), machineStructureRecipeLegendModel.getTag(), machineStructureRecipeLegendModel.getBlock(), machineStructureRecipeLegendModel.getProperties(), machineStructureRecipeLegendModel.getPort()));
|
|
}
|
|
z++;
|
|
}
|
|
y++;
|
|
z = 0;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private Vector3i getControllerPos(List<List<String>> layout) throws InvalidStructureDefinitionException {
|
|
int y = 0;
|
|
int z = 0;
|
|
for (List<String> layer : layout) {
|
|
for (String row : layer) {
|
|
for (int x = 0; x < row.length(); x++) {
|
|
if (row.charAt(x) == 'C') {
|
|
return new Vector3i(x, y, z);
|
|
}
|
|
}
|
|
z++;
|
|
}
|
|
y++;
|
|
z = 0;
|
|
}
|
|
throw new InvalidStructureDefinitionException("'C' AKA controller not found in layout section");
|
|
}
|
|
|
|
@SneakyThrows
|
|
@Nullable
|
|
@Override
|
|
public MachineStructureRecipe read(ResourceLocation rl, PacketBuffer buf) {
|
|
List<String> controllerId = new ArrayList<>();
|
|
int idCount = buf.readInt();
|
|
for (int i = 0; i < idCount; i++) {
|
|
controllerId.add(buf.readString());
|
|
}
|
|
String id = buf.readString();
|
|
String name = buf.readString();
|
|
try {
|
|
MachineStructureObject machineStructureObject = buf.func_240628_a_(MachineStructureObject.CODEC);
|
|
List<MachineStructureRecipeKeyModel> models = machineStructureObject.getInner();
|
|
return new MachineStructureRecipe(models, controllerId, id, rl, name);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public void write(PacketBuffer buf, MachineStructureRecipe recipe) {
|
|
buf.writeInt(recipe.controllerId.size());
|
|
for (String s : recipe.controllerId) {
|
|
buf.writeString(s);
|
|
}
|
|
buf.writeString(recipe.id);
|
|
buf.writeString(recipe.name);
|
|
try {
|
|
buf.func_240629_a_(MachineStructureObject.CODEC, new MachineStructureObject(recipe.getModels().get(0)));
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public IRecipeSerializer<?> setRegistryName(ResourceLocation name) {
|
|
return this;
|
|
}
|
|
|
|
@Override
|
|
public ResourceLocation getRegistryName() {
|
|
return new ResourceLocation(MM.ID, "machine_structure");
|
|
}
|
|
|
|
@Override
|
|
public Class<IRecipeSerializer<?>> getRegistryType() {
|
|
return RecipeTypes.STRUCTURE.get().getRegistryType();
|
|
}
|
|
|
|
private void validateStructure(List<MachineStructureRecipeKeyModel> models, List<String> controllerId, String id, ResourceLocation rl) throws InvalidStructureDefinitionException {
|
|
for (MachineStructureRecipeKeyModel model : models) {
|
|
if (!model.getBlock().equals("")) {
|
|
if (RLUtils.isRL(model.getBlock())) {
|
|
if (!ForgeRegistries.BLOCKS.containsKey(RLUtils.toRL(model.getBlock()))) {
|
|
throw new InvalidStructureDefinitionException("Block: " + model.getBlock() + " is not an existing block in the game");
|
|
}
|
|
} else {
|
|
throw new InvalidStructureDefinitionException("Block: " + model.getBlock() + " is defined but not a valid block id (ResourceLocation)");
|
|
}
|
|
} else if (!model.getTag().equals("")) {
|
|
if (!RLUtils.isRL(model.getTag())) {
|
|
throw new InvalidStructureDefinitionException("Block Tag: " + model.getBlock() + " is defined but not a valid block tag id (ResourceLocation)");
|
|
}
|
|
} else if (model.getPort() != null) {
|
|
if (!MMPorts.PORTS.containsKey(RLUtils.toRL(model.getPort().getType()))) {
|
|
throw new InvalidStructureDefinitionException("Port: " + model.getPort() + " is defined but not a valid port type id (ResourceLocation)");
|
|
} else if (!controllerId.containsAll(model.getPort().getControllerId())) {
|
|
throw new InvalidStructureDefinitionException("Port: " + model.getPort() + " is defined but not a valid port controller id specified (ResourceLocation)");
|
|
}
|
|
} else {
|
|
throw new InvalidStructureDefinitionException("You must define at least 1 'block' or 'tag' per port within the 'data' object");
|
|
}
|
|
}
|
|
|
|
for (String s : controllerId) {
|
|
boolean controllerIdFound = false;
|
|
for (RegistryObject<ControllerBlock> block : MMLoader.BLOCKS) {
|
|
controllerIdFound = block.get().getControllerId().equals(s) || controllerIdFound;
|
|
}
|
|
if (!controllerIdFound) {
|
|
throw new InvalidStructureDefinitionException("controllerId: " + s + " does not exist");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|