pipe frames, part 1

This commit is contained in:
Ellpeck 2020-04-20 03:37:23 +02:00
parent 1865eaa8cb
commit cfee160573
8 changed files with 241 additions and 27 deletions

View file

@ -1,14 +1,13 @@
package de.ellpeck.prettypipes;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.items.ModuleItem;
import de.ellpeck.prettypipes.entities.PipeFrameEntity;
import de.ellpeck.prettypipes.entities.PipeFrameRenderer;
import de.ellpeck.prettypipes.items.*;
import de.ellpeck.prettypipes.pipe.modules.LowPriorityModuleItem;
import de.ellpeck.prettypipes.pipe.modules.SpeedModuleItem;
import de.ellpeck.prettypipes.pipe.modules.extraction.ExtractionModuleContainer;
import de.ellpeck.prettypipes.pipe.modules.extraction.ExtractionModuleGui;
import de.ellpeck.prettypipes.pipe.modules.extraction.ExtractionModuleItem;
import de.ellpeck.prettypipes.items.ModuleTier;
import de.ellpeck.prettypipes.items.WrenchItem;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.packets.PacketHandler;
import de.ellpeck.prettypipes.pipe.*;
@ -26,6 +25,8 @@ import net.minecraft.block.Block;
import net.minecraft.client.gui.ScreenManager;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.RenderTypeLookup;
import net.minecraft.entity.EntityClassification;
import net.minecraft.entity.EntityType;
import net.minecraft.inventory.container.ContainerType;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
@ -41,6 +42,7 @@ import net.minecraftforge.common.extensions.IForgeContainerType;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import net.minecraftforge.fml.client.registry.RenderingRegistry;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
@ -72,6 +74,8 @@ public final class Registry {
public static Block pipeBlock;
public static TileEntityType<PipeTileEntity> pipeTileEntity;
public static EntityType<PipeFrameEntity> pipeFrameEntity;
public static ContainerType<MainPipeContainer> pipeContainer;
public static ContainerType<ExtractionModuleContainer> extractionModuleContainer;
public static ContainerType<FilterModuleContainer> filterModuleContainer;
@ -90,7 +94,8 @@ public final class Registry {
IForgeRegistry<Item> registry = event.getRegistry();
registry.registerAll(
wrenchItem = new WrenchItem().setRegistryName("wrench"),
new Item(new Item.Properties().group(GROUP)).setRegistryName("blank_module")
new Item(new Item.Properties().group(GROUP)).setRegistryName("blank_module"),
new PipeFrameItem().setRegistryName("pipe_frame")
);
registry.registerAll(createTieredModule("extraction_module", ExtractionModuleItem::new));
registry.registerAll(createTieredModule("filter_module", FilterModuleItem::new));
@ -112,7 +117,14 @@ public final class Registry {
}
@SubscribeEvent
public static void registerContainer(RegistryEvent.Register<ContainerType<?>> event) {
public static void registerEntities(RegistryEvent.Register<EntityType<?>> event) {
event.getRegistry().registerAll(
pipeFrameEntity = (EntityType<PipeFrameEntity>) EntityType.Builder.<PipeFrameEntity>create(PipeFrameEntity::new, EntityClassification.MISC).build("pipe_frame").setRegistryName("pipe_frame")
);
}
@SubscribeEvent
public static void registerContainers(RegistryEvent.Register<ContainerType<?>> event) {
event.getRegistry().registerAll(
// this needs to be registered manually since it doesn't send the module slot
pipeContainer = (ContainerType<MainPipeContainer>) IForgeContainerType.create((windowId, inv, data) -> new MainPipeContainer(pipeContainer, windowId, inv.player, data.readBlockPos())).setRegistryName("pipe"),
@ -159,6 +171,7 @@ public final class Registry {
public static void setup(FMLClientSetupEvent event) {
RenderTypeLookup.setRenderLayer(pipeBlock, RenderType.cutout());
ClientRegistry.bindTileEntityRenderer(pipeTileEntity, PipeRenderer::new);
RenderingRegistry.registerEntityRenderingHandler(pipeFrameEntity, PipeFrameRenderer::new);
ScreenManager.registerFactory(pipeContainer, MainPipeGui::new);
ScreenManager.registerFactory(extractionModuleContainer, ExtractionModuleGui::new);

View file

@ -0,0 +1,66 @@
package de.ellpeck.prettypipes.entities;
import de.ellpeck.prettypipes.network.NetworkLocation;
import de.ellpeck.prettypipes.network.PipeNetwork;
import de.ellpeck.prettypipes.pipe.PipeTileEntity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.item.ItemFrameEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.IPacket;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.fml.network.NetworkHooks;
import java.util.List;
public class PipeFrameEntity extends ItemFrameEntity {
private static final DataParameter<Integer> AMOUNT = EntityDataManager.createKey(PipeFrameEntity.class, DataSerializers.VARINT);
public PipeFrameEntity(EntityType<PipeFrameEntity> type, World world) {
super(type, world);
}
public PipeFrameEntity(EntityType<PipeFrameEntity> type, World world, BlockPos pos, Direction dir) {
this(type, world);
this.hangingPosition = pos;
this.updateFacingWithBoundingBox(dir);
}
@Override
protected void registerData() {
super.registerData();
this.dataManager.register(AMOUNT, 0);
}
@Override
public void tick() {
super.tick();
if (this.world.isRemote)
return;
if (this.ticksExisted % 40 != 0)
return;
PipeNetwork network = PipeNetwork.get(this.world);
BlockPos hangingPos = this.getHangingPosition().offset(this.getHorizontalFacing().getOpposite());
BlockPos node = network.getNodeFromPipe(hangingPos);
if (node == null)
return;
ItemStack stack = this.getDisplayedItem();
List<NetworkLocation> items = network.getOrderedNetworkItems(node);
int amount = items.stream().mapToInt(i -> i.getItemAmount(stack)).sum();
this.dataManager.set(AMOUNT, amount);
}
public int getAmount() {
return this.dataManager.get(AMOUNT);
}
@Override
public IPacket<?> createSpawnPacket() {
return NetworkHooks.getEntitySpawningPacket(this);
}
}

View file

@ -0,0 +1,45 @@
package de.ellpeck.prettypipes.entities;
import com.mojang.blaze3d.matrix.MatrixStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.Matrix4f;
import net.minecraft.client.renderer.Vector3f;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.client.renderer.entity.ItemFrameRenderer;
import net.minecraft.entity.item.ItemFrameEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.Vec3d;
public class PipeFrameRenderer extends ItemFrameRenderer {
public PipeFrameRenderer(EntityRendererManager renderManagerIn) {
super(renderManagerIn, Minecraft.getInstance().getItemRenderer());
}
@Override
public void render(ItemFrameEntity entityIn, float entityYaw, float partialTicks, MatrixStack matrixStackIn, IRenderTypeBuffer bufferIn, int packedLightIn) {
super.render(entityIn, entityYaw, partialTicks, matrixStackIn, bufferIn, packedLightIn);
if (entityIn.getDisplayedItem().isEmpty())
return;
matrixStackIn.push();
Direction direction = entityIn.getHorizontalFacing();
Vec3d vec3d = this.getRenderOffset(entityIn, partialTicks);
matrixStackIn.translate(-vec3d.getX(), -vec3d.getY(), -vec3d.getZ());
matrixStackIn.translate(direction.getXOffset() * 0.05, direction.getYOffset() * 0.05, direction.getZOffset() * 0.05);
matrixStackIn.rotate(Vector3f.XP.rotationDegrees(entityIn.rotationPitch));
matrixStackIn.rotate(Vector3f.YP.rotationDegrees(180.0F - entityIn.rotationYaw));
FontRenderer font = this.getFontRendererFromRenderManager();
int amount = ((PipeFrameEntity) entityIn).getAmount();
String ammountStrg = String.valueOf(amount);
float x = 0.5F - font.getStringWidth(ammountStrg) / 2F;
Matrix4f matrix4f = matrixStackIn.getLast().getPositionMatrix();
matrixStackIn.translate(0, -0.13F, 0);
matrixStackIn.scale(-0.02F, -0.02F, 0.02F);
font.renderString(ammountStrg, x, 0, 0xFFFFFF, true, matrix4f, bufferIn, false, 0, packedLightIn);
matrixStackIn.pop();
}
}

View file

@ -0,0 +1,65 @@
package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.entities.PipeFrameEntity;
import de.ellpeck.prettypipes.pipe.PipeBlock;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.item.HangingEntity;
import net.minecraft.entity.item.ItemFrameEntity;
import net.minecraft.entity.item.PaintingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUseContext;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
public class PipeFrameItem extends Item {
public PipeFrameItem() {
super(new Properties().group(Registry.GROUP));
}
// HangingEntityItem copypasta mostly, since it hardcodes the entities bleh
@Override
public ActionResultType onItemUse(ItemUseContext context) {
BlockPos blockpos = context.getPos();
Direction direction = context.getFace();
BlockPos blockpos1 = blockpos.offset(direction);
PlayerEntity playerentity = context.getPlayer();
ItemStack itemstack = context.getItem();
if (playerentity != null && !this.canPlace(playerentity, direction, itemstack, blockpos1)) {
return ActionResultType.FAIL;
} else {
World world = context.getWorld();
HangingEntity hangingentity = new PipeFrameEntity(Registry.pipeFrameEntity, world, blockpos1, direction);
CompoundNBT compoundnbt = itemstack.getTag();
if (compoundnbt != null) {
EntityType.applyItemNBT(world, playerentity, hangingentity, compoundnbt);
}
if (hangingentity.onValidSurface()) {
if (!world.isRemote) {
hangingentity.playPlaceSound();
world.addEntity(hangingentity);
}
itemstack.shrink(1);
return ActionResultType.SUCCESS;
} else {
return ActionResultType.CONSUME;
}
}
}
protected boolean canPlace(PlayerEntity playerIn, Direction directionIn, ItemStack itemStackIn, BlockPos posIn) {
BlockState state = playerIn.world.getBlockState(posIn.offset(directionIn.getOpposite()));
if (!(state.getBlock() instanceof PipeBlock))
return false;
return !directionIn.getAxis().isVertical() && playerIn.canPlayerEdit(posIn, directionIn, itemStackIn);
}
}

View file

@ -13,8 +13,6 @@ import java.util.List;
public class NetworkEdge extends DefaultWeightedEdge implements INBTSerializable<CompoundNBT> {
public BlockPos startPipe;
public BlockPos endPipe;
public final List<BlockPos> pipes = new ArrayList<>();
public NetworkEdge() {
@ -24,11 +22,17 @@ public class NetworkEdge extends DefaultWeightedEdge implements INBTSerializable
this.deserializeNBT(nbt);
}
public BlockPos getStartPipe() {
return this.pipes.get(0);
}
public BlockPos getEndPipe() {
return this.pipes.get(this.pipes.size() - 1);
}
@Override
public CompoundNBT serializeNBT() {
CompoundNBT nbt = new CompoundNBT();
nbt.put("start", NBTUtil.writeBlockPos(this.startPipe));
nbt.put("end", NBTUtil.writeBlockPos(this.endPipe));
ListNBT list = new ListNBT();
for (BlockPos pos : this.pipes)
list.add(NBTUtil.writeBlockPos(pos));
@ -38,8 +42,6 @@ public class NetworkEdge extends DefaultWeightedEdge implements INBTSerializable
@Override
public void deserializeNBT(CompoundNBT nbt) {
this.startPipe = NBTUtil.readBlockPos(nbt.getCompound("start"));
this.endPipe = NBTUtil.readBlockPos(nbt.getCompound("end"));
this.pipes.clear();
ListNBT list = nbt.getList("pipes", Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < list.size(); i++)

View file

@ -44,6 +44,12 @@ public class NetworkLocation {
return -1;
}
public int getItemAmount(ItemStack stack) {
return this.items.values().stream()
.filter(i -> i.isItemEqual(stack))
.mapToInt(ItemStack::getCount).sum();
}
public boolean isEmpty() {
return this.items == null || this.items.isEmpty();
}

View file

@ -115,8 +115,10 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
// if we only have one neighbor, then there can't be any new connections
if (neighbors.size() <= 1 && !this.isNode(pos))
return;
for (NetworkEdge edge : neighbors)
this.refreshNode(edge.endPipe, this.world.getBlockState(edge.endPipe));
for (NetworkEdge edge : neighbors) {
BlockPos end = edge.getEndPipe();
this.refreshNode(end, this.world.getBlockState(end));
}
}
public boolean tryInsertItem(BlockPos startPipePos, BlockPos startInventory, ItemStack stack, boolean preventOversending) {
@ -132,7 +134,9 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
if (startPipe == null)
return false;
this.startProfile("find_destination");
for (BlockPos pipePos : this.getOrderedDestinations(startPipePos)) {
for (BlockPos pipePos : this.getOrderedNetworkNodes(startPipePos)) {
if (pipePos.equals(startPipePos))
continue;
PipeTileEntity pipe = this.getPipe(pipePos);
BlockPos dest = pipe.getAvailableDestination(stack, false, preventOversending);
if (dest != null) {
@ -179,7 +183,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
return Collections.emptyList();
this.startProfile("get_network_items");
List<NetworkLocation> info = new ArrayList<>();
for (BlockPos dest : this.getOrderedDestinations(node)) {
for (BlockPos dest : this.getOrderedNetworkNodes(node)) {
PipeTileEntity pipe = this.getPipe(dest);
if (!pipe.canNetworkSee())
continue;
@ -214,16 +218,30 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
}
private void addEdge(NetworkEdge edge) {
this.graph.addEdge(edge.startPipe, edge.endPipe, edge);
this.graph.addEdge(edge.getStartPipe(), edge.getEndPipe(), edge);
// only use size - 1 so that nodes aren't counted twice for multi-edge paths
this.graph.setEdgeWeight(edge, edge.pipes.size() - 1);
}
private List<NetworkEdge> createAllEdges(BlockPos pos, BlockState state, boolean allAround) {
public BlockPos getNodeFromPipe(BlockPos pos) {
if (this.isNode(pos))
return pos;
BlockState state = this.world.getBlockState(pos);
if (!(state.getBlock() instanceof PipeBlock))
return null;
for (Direction dir : Direction.values()) {
NetworkEdge edge = this.createEdge(pos, state, dir, false);
if (edge != null)
return edge.getEndPipe();
}
return null;
}
private List<NetworkEdge> createAllEdges(BlockPos pos, BlockState state, boolean ignoreCurrBlocked) {
this.startProfile("create_all_edges");
List<NetworkEdge> edges = new ArrayList<>();
for (Direction dir : Direction.values()) {
NetworkEdge edge = this.createEdge(pos, state, dir, allAround);
NetworkEdge edge = this.createEdge(pos, state, dir, ignoreCurrBlocked);
if (edge != null)
edges.add(edge);
}
@ -231,8 +249,8 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
return edges;
}
private NetworkEdge createEdge(BlockPos pos, BlockState state, Direction dir, boolean allAround) {
if (!allAround && !state.get(PipeBlock.DIRECTIONS.get(dir)).isConnected())
private NetworkEdge createEdge(BlockPos pos, BlockState state, Direction dir, boolean ignoreCurrBlocked) {
if (!ignoreCurrBlocked && !state.get(PipeBlock.DIRECTIONS.get(dir)).isConnected())
return null;
BlockPos currPos = pos.offset(dir);
BlockState currState = this.world.getBlockState(currPos);
@ -240,7 +258,6 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
return null;
this.startProfile("create_edge");
NetworkEdge edge = new NetworkEdge();
edge.startPipe = pos;
edge.pipes.add(pos);
edge.pipes.add(currPos);
@ -248,7 +265,6 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
// if we found a vertex, we can stop since that's the next node
// we do this here since the first offset pipe also needs to check this
if (this.isNode(currPos)) {
edge.endPipe = edge.pipes.get(edge.pipes.size() - 1);
this.endProfile();
return edge;
}
@ -276,7 +292,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
return null;
}
private List<BlockPos> getOrderedDestinations(BlockPos node) {
private List<BlockPos> getOrderedNetworkNodes(BlockPos node) {
List<BlockPos> ret = this.nodeToConnectedNodes.get(node);
if (ret == null) {
this.startProfile("compile_connected_nodes");
@ -284,7 +300,6 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
// sort destinations first by their priority (eg trash pipes should be last)
// and then by their distance from the specified node
ret = Streams.stream(new BreadthFirstIterator<>(this.graph, node))
.filter(p -> !p.equals(node))
.sorted(Comparator.<BlockPos>comparingInt(p -> this.getPipe(p).getPriority()).reversed().thenComparing(paths::getWeight))
.collect(Collectors.toList());
this.nodeToConnectedNodes.put(node, ret);
@ -296,9 +311,9 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
public void clearDestinationCache(BlockPos... nodes) {
this.startProfile("clear_node_cache");
// remove caches for the nodes
for(BlockPos node : nodes)
for (BlockPos node : nodes)
this.nodeToConnectedNodes.keySet().remove(node);
// remove caches that contain the node as a destination
// remove caches that contain the nodes as a destination
this.nodeToConnectedNodes.values().removeIf(cached -> Arrays.stream(nodes).anyMatch(cached::contains));
this.endProfile();
}

View file

@ -57,6 +57,8 @@ public class RetrievalModuleItem extends ModuleItem {
if (locations == null)
locations = network.getOrderedNetworkItems(tile.getPos());
for (NetworkLocation location : locations) {
if (location.pipePos.equals(tile.getPos()))
continue;
int slot = location.getStackSlot(filtered);
if (slot < 0)
continue;