From 02c4e905a56d0017490aaa3e514abe22fd8c278c Mon Sep 17 00:00:00 2001 From: hortiSquash <45213805+hortiSquash@users.noreply.github.com> Date: Thu, 28 Jul 2022 15:41:11 +0200 Subject: [PATCH] More Optimized Unloaders (Serpulo) (#7249) * Update Unloader.java * some comments * deleted highUnloadPriority --- core/src/mindustry/world/Block.java | 2 - .../blocks/distribution/StackConveyor.java | 1 - .../world/blocks/storage/StorageBlock.java | 1 - .../world/blocks/storage/Unloader.java | 168 ++++++++---------- 4 files changed, 78 insertions(+), 94 deletions(-) diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index b649bd312c..dd338b4f5e 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -324,8 +324,6 @@ public class Block extends UnlockableContent implements Senseable{ public boolean quickRotate = true; /** Main subclass. Non-anonymous. */ public @Nullable Class subclass; - /** Determines if this block gets a higher unloader priority. */ - public boolean highUnloadPriority = false; /** Scroll position for certain blocks. */ public float selectScroll; /** Building that is created for this block. Initialized in init() via reflection. Set manually if modded. */ diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index b48fe664a5..dd71c782e6 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -51,7 +51,6 @@ public class StackConveyor extends Block implements Autotiler{ hasItems = true; itemCapacity = 10; conveyorPlacement = true; - highUnloadPriority = true; underBullets = true; priority = TargetPriority.transport; diff --git a/core/src/mindustry/world/blocks/storage/StorageBlock.java b/core/src/mindustry/world/blocks/storage/StorageBlock.java index 490a47c044..d681389ca5 100644 --- a/core/src/mindustry/world/blocks/storage/StorageBlock.java +++ b/core/src/mindustry/world/blocks/storage/StorageBlock.java @@ -26,7 +26,6 @@ public class StorageBlock extends Block{ flags = EnumSet.of(BlockFlag.storage); allowResupply = true; envEnabled = Env.any; - highUnloadPriority = true; } @Override diff --git a/core/src/mindustry/world/blocks/storage/Unloader.java b/core/src/mindustry/world/blocks/storage/Unloader.java index 98964c0353..2a4553b2ed 100644 --- a/core/src/mindustry/world/blocks/storage/Unloader.java +++ b/core/src/mindustry/world/blocks/storage/Unloader.java @@ -6,6 +6,7 @@ import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; import arc.util.io.*; +import arc.util.pooling.*; import mindustry.annotations.Annotations.*; import mindustry.entities.units.*; import mindustry.gen.*; @@ -62,7 +63,7 @@ public class Unloader extends Block{ float loadFactor; boolean canLoad; boolean canUnload; - int index; + int lastUsed; @Override public String toString(){ @@ -71,95 +72,93 @@ public class Unloader extends Block{ ", loadFactor=" + loadFactor + ", canLoad=" + canLoad + ", canUnload=" + canUnload + - ", index=" + index + + ", lastUsed=" + lastUsed + '}'; } } public class UnloaderBuild extends Building{ public float unloadTimer = 0f; - public Item sortItem = null; - public int offset = 0; public int rotations = 0; - public Seq possibleBlocks = new Seq<>(); - public int[] lastUsed; + private final int itemsLength = content.items().size; + public Item sortItem = null; + public ContainerStat dumpingFrom, dumpingTo; + public final Seq possibleBlocks = new Seq<>(); - protected final Comparator comparator = Structs.comps( - //sort so it gives priority for blocks that can only either recieve or give (not both), and then by load, and then by last use + protected final Comparator comparator = (x, y) -> { + //sort so it gives priority for blocks that can only either receive or give (not both), and then by load, and then by last use //highest = unload from, lowest = unload to + int unloadPriority = Boolean.compare(x.canUnload && !x.canLoad, y.canUnload && !y.canLoad); //priority to receive if it cannot give + if (unloadPriority != 0) return unloadPriority; + int loadPriority = Boolean.compare(x.canUnload || !x.canLoad, y.canUnload || !y.canLoad); //priority to give if it cannot receive + if (loadPriority != 0) return loadPriority; + int loadFactor = Float.compare(x.loadFactor, y.loadFactor); + if (loadFactor != 0) return loadFactor; + return Integer.compare(y.lastUsed, x.lastUsed); //inverted + }; - Structs.comps( - Structs.comparingBool(e -> e.building.block.highUnloadPriority && !e.canLoad), //stackConveyors and Storage - Structs.comps( - Structs.comparingBool(e -> e.canUnload && !e.canLoad), //priority to give - Structs.comparingBool(e -> e.canUnload || !e.canLoad) //priority to receive - ) - ), - Structs.comps( - Structs.comparingFloat(e -> e.loadFactor), - Structs.comparingInt(e -> -lastUsed[e.index]) - ) - ); + private boolean isPossibleItem(Item item){ + boolean hasProvider = false, + hasReceiver = false, + isDistinct = false; + + for(int i = 0; i < possibleBlocks.size; i++){ + var pb = possibleBlocks.get(i); + var other = pb.building; + + //set the stats of buildings in possibleBlocks while we are at it + pb.canLoad = !(other.block instanceof StorageBlock) && other.acceptItem(this, item); + pb.canUnload = other.canUnload() && other.items != null && other.items.has(item); + + //thats also handling framerate issues and slow conveyor belts, to avoid skipping items if nulloader + if((hasProvider && pb.canLoad) || (hasReceiver && pb.canUnload)) isDistinct = true; + hasProvider |= pb.canUnload; + hasReceiver |= pb.canLoad; + } + return isDistinct; + } + + @Override + public void onProximityUpdate(){ + //filter all blocks in the proximity that will never be able to trade items + + super.onProximityUpdate(); + Pools.freeAll(possibleBlocks, true); + possibleBlocks.clear(); + + for(int i = 0; i < proximity.size; i++){ + var other = proximity.get(i); + if(!other.interactable(team)) continue; //avoid blocks of the wrong team + ContainerStat pb = Pools.obtain(ContainerStat.class, ContainerStat::new); + + //partial check + boolean canLoad = !(other.block instanceof StorageBlock); + boolean canUnload = other.canUnload() && other.items != null; + + if(canLoad || canUnload){ //avoid blocks that can neither give nor receive items + pb.building = other; + //TODO store the partial canLoad/canUnload? + possibleBlocks.add(pb); + } + } + } @Override public void updateTile(){ - if(((unloadTimer += delta()) < speed) || (proximity.size < 2)) return; + if(((unloadTimer += delta()) < speed) || (possibleBlocks.size < 2)) return; Item item = null; boolean any = false; - int itemslength = content.items().size; - - //initialize possibleBlocks only if the new size is bigger than the previous, to avoid unnecessary allocations - if(possibleBlocks.size != proximity.size){ - int tmp = possibleBlocks.size; - possibleBlocks.setSize(proximity.size); - for(int i = tmp; i < proximity.size; i++){ - possibleBlocks.set(i, new ContainerStat()); - } - lastUsed = new int[proximity.size]; - } if(sortItem != null){ - item = sortItem; - - for(int pos = 0; pos < proximity.size; pos++){ - var other = proximity.get(pos); - boolean interactable = other.interactable(team); - - //set the stats of all buildings in possibleBlocks - ContainerStat pb = possibleBlocks.get(pos); - pb.building = other; - pb.canUnload = interactable && other.canUnload() && other.items != null && other.items.has(sortItem); - pb.canLoad = interactable && !(other.block instanceof StorageBlock) && other.acceptItem(this, sortItem); - pb.index = pos; - } + if(isPossibleItem(sortItem)) item = sortItem; }else{ - //select the next item for nulloaders - //inspired of nextIndex() but for all proximity at once, and also way more powerful - for(int i = 0; i < itemslength; i++){ - int total = (rotations + i + 1) % itemslength; - boolean hasProvider = false; - boolean hasReceiver = false; - boolean isDistinct = false; + //selects the next item for nulloaders + //inspired of nextIndex() but for all "proximity" (possibleBlocks) at once, and also way more powerful + for(int i = 0; i < itemsLength; i++){ + int total = (rotations + i + 1) % itemsLength; Item possibleItem = content.item(total); - for(int pos = 0; pos < proximity.size; pos++){ - var other = proximity.get(pos); - boolean interactable = other.interactable(team); - - //set the stats of all buildings in possibleBlocks while we are at it - ContainerStat pb = possibleBlocks.get(pos); - pb.building = other; - pb.canUnload = interactable && other.canUnload() && other.items != null && other.items.has(possibleItem); - pb.canLoad = interactable && !(other.block instanceof StorageBlock) && other.acceptItem(this, possibleItem); - pb.index = pos; - - //the part handling framerate issues and slow conveyor belts, to avoid skipping items - if(hasProvider && pb.canLoad) isDistinct = true; - if(hasReceiver && pb.canUnload) isDistinct = true; - hasProvider = hasProvider || pb.canUnload; - hasReceiver = hasReceiver || pb.canLoad; - } - if(isDistinct){ + if(isPossibleItem(possibleItem)){ item = possibleItem; break; } @@ -167,17 +166,19 @@ public class Unloader extends Block{ } if(item != null){ - //only compute the load factor if a transfer is possible - for(int pos = 0; pos < proximity.size; pos++){ - ContainerStat pb = possibleBlocks.get(pos); + rotations = item.id; //next rotation for nulloaders //TODO maybe if(sortItem == null) + + for(int i = 0; i < possibleBlocks.size; i++){ + var pb = possibleBlocks.get(i); var other = pb.building; pb.loadFactor = (other.getMaximumAccepted(item) == 0) || (other.items == null) ? 0 : other.items.get(item) / (float)other.getMaximumAccepted(item); + pb.lastUsed = (pb.lastUsed + 1) % Integer.MAX_VALUE; //increment the priority if not used } possibleBlocks.sort(comparator); - ContainerStat dumpingFrom = null; - ContainerStat dumpingTo = null; + dumpingTo = null; + dumpingFrom = null; //choose the building to accept the item for(int i = 0; i < possibleBlocks.size; i++){ @@ -195,22 +196,14 @@ public class Unloader extends Block{ } } - //increment the priority if not used - for(int i = 0; i < possibleBlocks.size; i++){ - lastUsed[i] = (lastUsed[i] + 1) % 2147483647; - } - //trade the items - //TODO && dumpingTo != dumpingFrom ? if(dumpingFrom != null && dumpingTo != null && (dumpingFrom.loadFactor != dumpingTo.loadFactor || !dumpingFrom.canLoad)){ dumpingTo.building.handleItem(this, item); dumpingFrom.building.removeStack(item, 1); - lastUsed[dumpingFrom.index] = 0; - lastUsed[dumpingTo.index] = 0; + dumpingTo.lastUsed = 0; + dumpingFrom.lastUsed = 0; any = true; } - - if(sortItem == null) rotations = item.id; } if(any){ @@ -218,11 +211,6 @@ public class Unloader extends Block{ }else{ unloadTimer = Math.min(unloadTimer, speed); } - - if(proximity.size > 0){ - offset++; - offset %= proximity.size; - } } @Override