From bf60a404576e0920a0b9997d6ee30a82426c3055 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Tue, 14 Apr 2020 04:21:28 +0200 Subject: [PATCH] pipe networks, part 1 --- build.gradle | 8 ++ .../java/de/ellpeck/prettypipes/Registry.java | 22 ++++ .../prettypipes/blocks/pipe/PipeBlock.java | 25 +++- .../de/ellpeck/prettypipes/events/Events.java | 43 +++++++ .../prettypipes/network/PipeNetwork.java | 118 ++++++++++++++++++ 5 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/ellpeck/prettypipes/events/Events.java create mode 100644 src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java diff --git a/build.gradle b/build.gradle index 356a872..50e474b 100644 --- a/build.gradle +++ b/build.gradle @@ -83,13 +83,20 @@ sourceSets.main.resources { } repositories { + mavenCentral() maven { url = "https://dvs1.progwml6.com/files/maven" } } +configurations { + embed + compile.extendsFrom(embed) +} + dependencies { minecraft 'net.minecraftforge:forge:1.15.2-31.1.19' + embed "org.jgrapht:jgrapht-core:1.4.0" compileOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.2:api") runtimeOnly fg.deobf("mezz.jei:jei-1.15.2:6.0.0.2") @@ -108,6 +115,7 @@ jar { "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") ]) } + from configurations.embed.collect { it.isDirectory() ? it : zipTree(it) } } task deobfJar(type: Jar) { diff --git a/src/main/java/de/ellpeck/prettypipes/Registry.java b/src/main/java/de/ellpeck/prettypipes/Registry.java index 7bb7175..3d2adcc 100644 --- a/src/main/java/de/ellpeck/prettypipes/Registry.java +++ b/src/main/java/de/ellpeck/prettypipes/Registry.java @@ -6,6 +6,7 @@ import de.ellpeck.prettypipes.blocks.pipe.PipeGui; import de.ellpeck.prettypipes.blocks.pipe.PipeTileEntity; import de.ellpeck.prettypipes.items.ExtractionUpgradeItem; import de.ellpeck.prettypipes.items.WrenchItem; +import de.ellpeck.prettypipes.network.PipeNetwork; import net.minecraft.block.Block; import net.minecraft.client.gui.ScreenManager; import net.minecraft.client.renderer.RenderType; @@ -15,8 +16,13 @@ import net.minecraft.item.BlockItem; import net.minecraft.item.Item; import net.minecraft.item.ItemGroup; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.INBT; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityType; +import net.minecraft.util.Direction; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.CapabilityInject; +import net.minecraftforge.common.capabilities.CapabilityManager; import net.minecraftforge.common.extensions.IForgeContainerType; import net.minecraftforge.event.RegistryEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -26,6 +32,8 @@ import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.registries.ForgeRegistries; +import javax.annotation.Nullable; + @Mod.EventBusSubscriber(bus = Bus.MOD) public final class Registry { @@ -36,6 +44,9 @@ public final class Registry { } }; + @CapabilityInject(PipeNetwork.class) + public static Capability pipeNetworkCapability; + public static Item wrenchItem; public static Item extractionUpgradeItem; @@ -80,7 +91,18 @@ public final class Registry { } public static void setup(FMLCommonSetupEvent event) { + CapabilityManager.INSTANCE.register(PipeNetwork.class, new Capability.IStorage() { + @Nullable + @Override + public INBT writeNBT(Capability capability, PipeNetwork instance, Direction side) { + return null; + } + @Override + public void readNBT(Capability capability, PipeNetwork instance, Direction side, INBT nbt) { + + } + }, () -> null); } public static void setupClient(FMLClientSetupEvent event) { diff --git a/src/main/java/de/ellpeck/prettypipes/blocks/pipe/PipeBlock.java b/src/main/java/de/ellpeck/prettypipes/blocks/pipe/PipeBlock.java index a2db179..8d1e600 100644 --- a/src/main/java/de/ellpeck/prettypipes/blocks/pipe/PipeBlock.java +++ b/src/main/java/de/ellpeck/prettypipes/blocks/pipe/PipeBlock.java @@ -2,6 +2,7 @@ package de.ellpeck.prettypipes.blocks.pipe; import com.google.common.collect.ImmutableMap; import de.ellpeck.prettypipes.Utility; +import de.ellpeck.prettypipes.network.PipeNetwork; import net.minecraft.block.*; import net.minecraft.block.material.Material; import net.minecraft.entity.player.PlayerEntity; @@ -149,6 +150,26 @@ public class PipeBlock extends ContainerBlock { if (tile != null) Utility.dropInventory(tile, tile.upgrades); } + + PipeNetwork network = PipeNetwork.get(world); + int connections = 0; + boolean inventory = false; + for (EnumProperty prop : DIRECTIONS.values()) { + ConnectionType value = newState.get(prop); + if (!value.isConnected()) + continue; + connections++; + if (value == ConnectionType.CONNECTED_INVENTORY) { + inventory = true; + break; + } + } + if (inventory || connections > 2) { + network.addNode(pos, newState); + } else { + network.removeNode(pos); + } + network.onPipeChanged(pos, newState); } @Override @@ -157,7 +178,9 @@ public class PipeBlock extends ContainerBlock { PipeTileEntity tile = Utility.getTileEntity(PipeTileEntity.class, worldIn, pos); if (tile != null) Utility.dropInventory(tile, tile.upgrades); - + PipeNetwork network = PipeNetwork.get(worldIn); + network.removeNode(pos); + network.onPipeChanged(pos, state); super.onReplaced(state, worldIn, pos, newState, isMoving); } } diff --git a/src/main/java/de/ellpeck/prettypipes/events/Events.java b/src/main/java/de/ellpeck/prettypipes/events/Events.java new file mode 100644 index 0000000..eced7c6 --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/events/Events.java @@ -0,0 +1,43 @@ +package de.ellpeck.prettypipes.events; + +import de.ellpeck.prettypipes.PrettyPipes; +import de.ellpeck.prettypipes.network.PipeNetwork; +import net.minecraft.particles.ParticleType; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.particles.RedstoneParticleData; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.event.AttachCapabilitiesEvent; +import net.minecraftforge.event.TickEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; +import org.jgrapht.graph.DefaultWeightedEdge; + +@Mod.EventBusSubscriber +public final class Events { + + @SubscribeEvent + public static void onWorldCaps(AttachCapabilitiesEvent event) { + event.addCapability(new ResourceLocation(PrettyPipes.ID, "network"), new PipeNetwork(event.getObject())); + } + + @SubscribeEvent + public static void onWorldTick(TickEvent.WorldTickEvent event) { + if (event.phase != TickEvent.Phase.END) + return; + if (event.world.getGameTime() % 5 != 0) + return; + + PipeNetwork network = PipeNetwork.get(event.world); + for (DefaultWeightedEdge edge : network.graph.edgeSet()) { + BlockPos start = network.graph.getEdgeSource(edge); + BlockPos end = network.graph.getEdgeTarget(edge); + + RedstoneParticleData data = new RedstoneParticleData(((start.getX() * 181923 + end.getX()) % 255) / 255F, ((start.getY() * 128391 + end.getY()) % 255) / 255F, ((start.getZ() * 123891 + end.getZ()) % 255) / 255F, 1); + ((ServerWorld) event.world).spawnParticle(data, start.getX() + 0.5F, start.getY() + 0.5F, start.getZ() + 0.5F, 1, 0, 0, 0, 0); + ((ServerWorld) event.world).spawnParticle(data, end.getX() + 0.5F, end.getY() + 0.5F, end.getZ() + 0.5F, 1, 0, 0, 0, 0); + } + } +} diff --git a/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java b/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java new file mode 100644 index 0000000..f28c7fb --- /dev/null +++ b/src/main/java/de/ellpeck/prettypipes/network/PipeNetwork.java @@ -0,0 +1,118 @@ +package de.ellpeck.prettypipes.network; + +import com.google.common.collect.Sets; +import de.ellpeck.prettypipes.Registry; +import de.ellpeck.prettypipes.blocks.pipe.PipeBlock; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.Direction; +import net.minecraft.util.Tuple; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.capabilities.ICapabilitySerializable; +import net.minecraftforge.common.util.LazyOptional; +import org.apache.commons.lang3.tuple.Pair; +import org.jgrapht.Graphs; +import org.jgrapht.alg.shortestpath.DijkstraShortestPath; +import org.jgrapht.graph.DefaultEdge; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleGraph; +import org.jgrapht.graph.SimpleWeightedGraph; +import org.jheaps.tree.FibonacciHeap; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class PipeNetwork implements ICapabilitySerializable { + + public final SimpleWeightedGraph graph = new SimpleWeightedGraph<>(DefaultWeightedEdge.class); + private final DijkstraShortestPath dijkstra = new DijkstraShortestPath<>(this.graph); + private final World world; + + public PipeNetwork(World world) { + this.world = world; + } + + @Nonnull + @Override + public LazyOptional getCapability(@Nonnull Capability cap, @Nullable Direction side) { + return cap == Registry.pipeNetworkCapability ? LazyOptional.of(() -> (T) this) : null; + } + + @Override + public CompoundNBT serializeNBT() { + return new CompoundNBT(); + } + + @Override + public void deserializeNBT(CompoundNBT nbt) { + + } + + public void addNode(BlockPos pos, BlockState state) { + if (!this.graph.containsVertex(pos)) { + this.graph.addVertex(pos); + this.refreshNode(pos, state); + } + } + + public void removeNode(BlockPos pos) { + if (this.graph.containsVertex(pos)) + this.graph.removeVertex(pos); + } + + public void onPipeChanged(BlockPos pos, BlockState state) { + Set> neighbors = this.findConnectedNodesInPipe(pos, state); + // if we only have one neighbor, then there can't be any new connections + if (neighbors.size() <= 1 && !this.graph.containsVertex(pos)) + return; + for (Pair node : neighbors) + this.refreshNode(node.getLeft(), this.world.getBlockState(node.getLeft())); + System.out.println(this.graph); + } + + private void refreshNode(BlockPos pos, BlockState state) { + Set edges = this.graph.edgesOf(pos); + this.graph.removeAllEdges(new ArrayList<>(edges)); + + for (Pair node : this.findConnectedNodesInPipe(pos, state)) { + DefaultWeightedEdge edge = this.graph.addEdge(pos, node.getLeft()); + this.graph.setEdgeWeight(edge, node.getRight()); + } + } + + private Set> findConnectedNodesInPipe(BlockPos pos, BlockState state) { + Set> set = new HashSet<>(); + this.findConnectedNodesInPipe(pos, state, set, Sets.newHashSet(pos), 0); + return set; + } + + private void findConnectedNodesInPipe(BlockPos pos, BlockState state, Set> nodes, Set seen, int iterations) { + if (!(state.getBlock() instanceof PipeBlock)) + return; + for (Direction dir : Direction.values()) { + if (!state.get(PipeBlock.DIRECTIONS.get(dir)).isConnected()) + continue; + BlockPos offset = pos.offset(dir); + if (seen.contains(offset)) + continue; + seen.add(offset); + if (this.graph.containsVertex(offset)) { + nodes.add(Pair.of(offset, iterations)); + break; + } else { + this.findConnectedNodesInPipe(offset, this.world.getBlockState(offset), nodes, seen, iterations + 1); + } + } + } + + public static PipeNetwork get(World world) { + return world.getCapability(Registry.pipeNetworkCapability).orElse(null); + } +}