basic upgrade structure

This commit is contained in:
Ellpeck 2020-04-16 00:39:53 +02:00
parent fffcb9da6d
commit f7957bfc0a
18 changed files with 223 additions and 73 deletions

View file

@ -1,7 +1,9 @@
package de.ellpeck.prettypipes;
import com.mojang.datafixers.types.Func;
import de.ellpeck.prettypipes.blocks.pipe.*;
import de.ellpeck.prettypipes.items.ExtractionUpgradeItem;
import de.ellpeck.prettypipes.items.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;
@ -29,8 +31,14 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.IForgeRegistry;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;
import java.util.function.Supplier;
@Mod.EventBusSubscriber(bus = Bus.MOD)
public final class Registry {
@ -46,7 +54,6 @@ public final class Registry {
public static Capability<PipeNetwork> pipeNetworkCapability;
public static Item wrenchItem;
public static Item extractionUpgradeItem;
public static Block pipeBlock;
public static TileEntityType<PipeTileEntity> pipeTileEntity;
@ -61,14 +68,15 @@ public final class Registry {
@SubscribeEvent
public static void registerItems(RegistryEvent.Register<Item> event) {
event.getRegistry().registerAll(
wrenchItem = new WrenchItem().setRegistryName("wrench"),
extractionUpgradeItem = new ExtractionUpgradeItem().setRegistryName("extraction_upgrade")
IForgeRegistry<Item> registry = event.getRegistry();
registry.registerAll(
wrenchItem = new WrenchItem().setRegistryName("wrench")
);
registry.registerAll(createTieredModule("extraction_module", ExtractionModuleItem::new));
ForgeRegistries.BLOCKS.getValues().stream()
.filter(b -> b.getRegistryName().getNamespace().equals(PrettyPipes.ID))
.forEach(b -> event.getRegistry().register(new BlockItem(b, new Item.Properties().group(GROUP)).setRegistryName(b.getRegistryName())));
.forEach(b -> registry.register(new BlockItem(b, new Item.Properties().group(GROUP)).setRegistryName(b.getRegistryName())));
}
@SubscribeEvent
@ -88,6 +96,13 @@ public final class Registry {
);
}
private static Item[] createTieredModule(String name, Function<ModuleTier, Item> item) {
List<Item> items = new ArrayList<>();
for (ModuleTier tier : ModuleTier.values())
items.add(item.apply(tier).setRegistryName(tier.name().toLowerCase(Locale.ROOT) + "_" + name));
return items.toArray(new Item[0]);
}
public static void setup(FMLCommonSetupEvent event) {
CapabilityManager.INSTANCE.register(PipeNetwork.class, new Capability.IStorage<PipeNetwork>() {
@Nullable

View file

@ -9,17 +9,14 @@ import net.minecraft.block.material.Material;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.item.BlockItemUseContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.EnumProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.tileentity.AbstractFurnaceTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.Util;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
@ -155,7 +152,7 @@ public class PipeBlock extends ContainerBlock {
public static void onStateChanged(World world, BlockPos pos, BlockState newState) {
PipeTileEntity tile = Utility.getTileEntity(PipeTileEntity.class, world, pos);
if (tile != null && !tile.isConnectedInventory())
Utility.dropInventory(tile, tile.upgrades);
Utility.dropInventory(tile, tile.modules);
PipeNetwork network = PipeNetwork.get(world);
int connections = 0;
@ -183,7 +180,7 @@ public class PipeBlock extends ContainerBlock {
if (state.getBlock() != newState.getBlock()) {
PipeTileEntity tile = Utility.getTileEntity(PipeTileEntity.class, worldIn, pos);
if (tile != null) {
Utility.dropInventory(tile, tile.upgrades);
Utility.dropInventory(tile, tile.modules);
for (PipeItem item : tile.items)
item.drop(worldIn);
}

View file

@ -1,6 +1,6 @@
package de.ellpeck.prettypipes.blocks.pipe;
import de.ellpeck.prettypipes.items.UpgradeItem;
import de.ellpeck.prettypipes.items.IModule;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.ContainerType;
@ -15,7 +15,7 @@ public class PipeContainer extends Container {
super(type, id);
for (int i = 0; i < 3; i++)
this.addSlot(new SlotItemHandler(tile.upgrades, i, 62 + i * 18, 17));
this.addSlot(new SlotItemHandler(tile.modules, i, 62 + i * 18, 17));
for (int l = 0; l < 3; ++l)
for (int j1 = 0; j1 < 9; ++j1)
@ -46,7 +46,7 @@ public class PipeContainer extends Container {
// shift into this container here
// mergeItemStack with the slots that newStack should go into
// return an empty stack if mergeItemStack fails
if (newStack.getItem() instanceof UpgradeItem) {
if (newStack.getItem() instanceof IModule) {
if (!this.mergeItemStack(newStack, 0, 3, false))
return ItemStack.EMPTY;
}

View file

@ -2,18 +2,17 @@ package de.ellpeck.prettypipes.blocks.pipe;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.items.UpgradeItem;
import de.ellpeck.prettypipes.network.NetworkEdge;
import de.ellpeck.prettypipes.items.IModule;
import de.ellpeck.prettypipes.network.PipeItem;
import de.ellpeck.prettypipes.network.PipeNetwork;
import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.container.Container;
import net.minecraft.inventory.container.INamedContainerProvider;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.profiler.IProfiler;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
@ -31,13 +30,14 @@ import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class PipeTileEntity extends TileEntity implements INamedContainerProvider, ITickableTileEntity {
public final ItemStackHandler upgrades = new ItemStackHandler(3) {
public final ItemStackHandler modules = new ItemStackHandler(3) {
@Override
public boolean isItemValid(int slot, @Nonnull ItemStack stack) {
return stack.getItem() instanceof UpgradeItem;
return stack.getItem() instanceof IModule;
}
};
public final List<PipeItem> items = new ArrayList<>();
@ -59,7 +59,7 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.put("upgrades", this.upgrades.serializeNBT());
compound.put("modules", this.modules.serializeNBT());
ListNBT list = new ListNBT();
for (PipeItem item : this.items)
list.add(item.serializeNBT());
@ -69,7 +69,7 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
@Override
public void read(CompoundNBT compound) {
this.upgrades.deserializeNBT(compound.getCompound("upgrades"));
this.modules.deserializeNBT(compound.getCompound("modules"));
this.items.clear();
ListNBT list = compound.getList("items", Constants.NBT.TAG_COMPOUND);
for (int i = 0; i < list.size(); i++)
@ -88,57 +88,44 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
public void tick() {
if (!this.world.isAreaLoaded(this.pos, 1))
return;
IProfiler profiler = this.world.getProfiler();
profiler.startSection("ticking_modules");
this.streamModules().forEach(m -> m.tick(this));
profiler.endSection();
profiler.startSection("ticking_items");
for (int i = this.items.size() - 1; i >= 0; i--)
this.items.get(i).updateInPipe(this);
profiler.endSection();
}
// TODO make this extraction module stuff proper
PipeNetwork network = PipeNetwork.get(this.world);
for (int i = 0; i < this.upgrades.getSlots(); i++) {
if (this.upgrades.getStackInSlot(i).getItem() != Registry.extractionUpgradeItem)
continue;
BlockState state = this.getBlockState();
for (Direction dir : Direction.values()) {
if (!state.get(PipeBlock.DIRECTIONS.get(dir)).isConnected())
continue;
IItemHandler handler = this.getItemHandler(dir);
if (handler != null) {
for (int j = 0; j < handler.getSlots(); j++) {
ItemStack stack = handler.extractItem(j, 64, true);
if (!stack.isEmpty() && network.tryInsertItem(this.pos, this.pos.offset(dir), stack)) {
handler.extractItem(j, 64, false);
return;
}
}
}
}
return;
}
public boolean isConnected(Direction dir) {
return this.getBlockState().get(PipeBlock.DIRECTIONS.get(dir)).isConnected();
}
public BlockPos getAvailableDestination(ItemStack stack) {
for (int i = 0; i < this.upgrades.getSlots(); i++) {
if (this.upgrades.getStackInSlot(i).getItem() == Registry.extractionUpgradeItem)
return null;
}
if (this.streamModules().anyMatch(u -> !u.canAcceptItem(this, stack)))
return null;
for (Direction dir : Direction.values()) {
IItemHandler handler = this.getItemHandler(dir);
if (handler == null)
continue;
if (ItemHandlerHelper.insertItem(handler, stack, true).isEmpty())
return this.pos.offset(dir);
if (!ItemHandlerHelper.insertItem(handler, stack, true).isEmpty())
continue;
if (this.streamModules().anyMatch(u -> !u.isAvailableDestination(this, stack, handler)))
continue;
return this.pos.offset(dir);
}
return null;
}
// TODO module priority
public int getPriority() {
return 0;
return this.streamModules().mapToInt(u -> u.getPriority(this)).max().orElse(0);
}
private IItemHandler getItemHandler(Direction dir) {
BlockState state = this.getBlockState();
if (!state.get(PipeBlock.DIRECTIONS.get(dir)).isConnected())
public IItemHandler getItemHandler(Direction dir) {
if (!this.isConnected(dir))
return null;
TileEntity tile = this.world.getTileEntity(this.pos.offset(dir));
if (tile == null)
@ -153,4 +140,17 @@ public class PipeTileEntity extends TileEntity implements INamedContainerProvide
public boolean isConnectedInventory() {
return Arrays.stream(Direction.values()).anyMatch(this::isConnectedInventory);
}
private Stream<IModule> streamModules() {
Stream.Builder<IModule> builder = Stream.builder();
for (int i = 0; i < this.modules.getSlots(); i++) {
ItemStack stack = this.modules.getStackInSlot(i);
if (stack.isEmpty())
continue;
Item item = stack.getItem();
if (item instanceof IModule)
builder.accept((IModule) item);
}
return builder.build();
}
}

View file

@ -0,0 +1,42 @@
package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.blocks.pipe.PipeTileEntity;
import de.ellpeck.prettypipes.network.PipeNetwork;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraftforge.items.IItemHandler;
public class ExtractionModuleItem extends ModuleItem {
private final int maxExtraction;
private final int speed;
public ExtractionModuleItem(ModuleTier tier) {
this.maxExtraction = tier.forTier(1, 8, 64);
this.speed = tier.forTier(20, 15, 10);
}
@Override
public void tick(PipeTileEntity tile) {
if (tile.getWorld().getGameTime() % this.speed != 0)
return;
PipeNetwork network = PipeNetwork.get(tile.getWorld());
for (Direction dir : Direction.values()) {
IItemHandler handler = tile.getItemHandler(dir);
if (handler == null)
continue;
for (int j = 0; j < handler.getSlots(); j++) {
ItemStack stack = handler.extractItem(j, this.maxExtraction, true);
if (!stack.isEmpty() && network.tryInsertItem(tile.getPos(), tile.getPos().offset(dir), stack)) {
handler.extractItem(j, this.maxExtraction, false);
return;
}
}
}
}
@Override
public boolean canAcceptItem(PipeTileEntity tile, ItemStack stack) {
return false;
}
}

View file

@ -1,5 +0,0 @@
package de.ellpeck.prettypipes.items;
public class ExtractionUpgradeItem extends UpgradeItem {
}

View file

@ -0,0 +1,17 @@
package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.blocks.pipe.PipeTileEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.items.IItemHandler;
public interface IModule {
void tick(PipeTileEntity tile);
boolean canAcceptItem(PipeTileEntity tile, ItemStack stack);
boolean isAvailableDestination(PipeTileEntity tile, ItemStack stack, IItemHandler destination);
int getPriority(PipeTileEntity tile);
}

View file

@ -0,0 +1,33 @@
package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.blocks.pipe.PipeTileEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.items.IItemHandler;
public class ModuleItem extends Item implements IModule {
public ModuleItem() {
super(new Properties().group(Registry.GROUP).maxStackSize(16));
}
@Override
public void tick(PipeTileEntity tile) {
}
@Override
public boolean canAcceptItem(PipeTileEntity tile, ItemStack stack) {
return true;
}
@Override
public boolean isAvailableDestination(PipeTileEntity tile, ItemStack stack, IItemHandler destination) {
return true;
}
@Override
public int getPriority(PipeTileEntity tile) {
return 0;
}
}

View file

@ -0,0 +1,21 @@
package de.ellpeck.prettypipes.items;
public enum ModuleTier {
LOW,
MEDIUM,
HIGH;
public final <T> T forTier(T low, T medium, T high) {
switch (this) {
case LOW:
return low;
case MEDIUM:
return medium;
case HIGH:
return high;
default:
throw new RuntimeException();
}
}
}

View file

@ -1,10 +0,0 @@
package de.ellpeck.prettypipes.items;
import de.ellpeck.prettypipes.Registry;
import net.minecraft.item.Item;
public class UpgradeItem extends Item {
public UpgradeItem() {
super(new Properties().group(Registry.GROUP).maxStackSize(16));
}
}

View file

@ -1,6 +1,7 @@
package de.ellpeck.prettypipes.network;
import com.google.common.collect.Streams;
import de.ellpeck.prettypipes.PrettyPipes;
import de.ellpeck.prettypipes.Registry;
import de.ellpeck.prettypipes.Utility;
import de.ellpeck.prettypipes.blocks.pipe.PipeBlock;
@ -123,12 +124,16 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
PipeTileEntity startPipe = this.getPipe(startPipePos);
if (startPipe == null)
return false;
this.startProfile("find_destination");
for (BlockPos pipePos : this.getOrderedDestinations(startPipePos)) {
PipeTileEntity pipe = this.getPipe(pipePos);
BlockPos dest = pipe.getAvailableDestination(stack);
if (dest != null)
if (dest != null) {
this.endProfile();
return this.routeItemToLocation(startPipePos, startInventory, pipe.getPos(), dest, itemSupplier);
}
}
this.endProfile();
return false;
}
@ -140,7 +145,9 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
PipeTileEntity startPipe = this.getPipe(startPipePos);
if (startPipe == null)
return false;
this.startProfile("get_path");
GraphPath<BlockPos, NetworkEdge> path = this.dijkstra.getPath(startPipePos, destPipe);
this.endProfile();
if (path == null)
return false;
PipeItem item = itemSupplier.get();
@ -161,9 +168,11 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
}
private void refreshNode(BlockPos pos, BlockState state) {
this.startProfile("refresh_node");
this.graph.removeAllEdges(new ArrayList<>(this.graph.edgesOf(pos)));
for (NetworkEdge edge : this.createAllEdges(pos, state, false))
this.addEdge(edge);
this.endProfile();
}
private void addEdge(NetworkEdge edge) {
@ -173,12 +182,14 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
}
private List<NetworkEdge> createAllEdges(BlockPos pos, BlockState state, boolean allAround) {
this.startProfile("create_all_edges");
List<NetworkEdge> edges = new ArrayList<>();
for (Direction dir : Direction.values()) {
NetworkEdge edge = this.createEdge(pos, state, dir, allAround);
if (edge != null)
edges.add(edge);
}
this.endProfile();
return edges;
}
@ -189,6 +200,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
BlockState currState = this.world.getBlockState(currPos);
if (!(currState.getBlock() instanceof PipeBlock))
return null;
this.startProfile("create_edge");
NetworkEdge edge = new NetworkEdge();
edge.startPipe = pos;
edge.pipes.add(pos);
@ -199,6 +211,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
// 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;
}
@ -221,6 +234,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
if (!found)
break;
}
this.endProfile();
return null;
}
@ -231,6 +245,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
private List<BlockPos> getOrderedDestinations(BlockPos node) {
List<BlockPos> ret = this.nodeToConnectedNodes.get(node);
if (ret == null) {
this.startProfile("compile_connected_nodes");
ShortestPathAlgorithm.SingleSourcePaths<BlockPos, NetworkEdge> paths = this.dijkstra.getPaths(node);
// sort destinations first by their priority (eg trash pipes should be last)
// and then by their distance from the specified node
@ -238,6 +253,7 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
.sorted(Comparator.<BlockPos>comparingInt(p -> this.getPipe(p).getPriority()).reversed().thenComparing(paths::getWeight))
.collect(Collectors.toList());
this.nodeToConnectedNodes.put(node, ret);
this.endProfile();
}
return ret;
}
@ -254,8 +270,10 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
private void edgeModified(GraphEdgeChangeEvent<BlockPos, NetworkEdge> e) {
// uncache all connection infos that contain the removed edge's vertices
this.startProfile("clear_node_cache");
this.nodeToConnectedNodes.values().removeIf(
nodes -> nodes.stream().anyMatch(n -> n.equals(e.getEdgeSource()) || n.equals(e.getEdgeTarget())));
this.endProfile();
}
@Override
@ -265,4 +283,12 @@ public class PipeNetwork implements ICapabilitySerializable<CompoundNBT>, GraphL
@Override
public void vertexRemoved(GraphVertexChangeEvent<BlockPos> e) {
}
private void startProfile(String name) {
this.world.getProfiler().startSection(() -> PrettyPipes.ID + ":pipe_network_" + name);
}
private void endProfile() {
this.world.getProfiler().endSection();
}
}

View file

@ -1,6 +1,8 @@
{
"item.prettypipes.wrench": "Pipe Wrench",
"item.prettypipes.extraction_upgrade": "Extraction Upgrade",
"item.prettypipes.low_extraction_module": "Low Extraction Module",
"item.prettypipes.medium_extraction_module": "Medium Extraction Module",
"item.prettypipes.high_extraction_module": "High Extraction Module",
"block.prettypipes.pipe": "Pipe",
"itemGroup.prettypipes": "Pretty Pipes",
"container.prettypipes.pipe": "Pipe"

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/high_extraction_module"
}
}

View file

@ -1,6 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/extraction_upgrade"
"layer0": "prettypipes:item/low_extraction_module"
}
}

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "prettypipes:item/medium_extraction_module"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B