major rewrite to pattern buffers#4910
Conversation
screret
left a comment
There was a problem hiding this comment.
please don't use var for 'simple' types (e.g. ones without nested generics); it makes code harder to read in the github review UI.
| @Getter | ||
| @SaveField | ||
| protected final InternalSlot[] internalInventory = new InternalSlot[MAX_PATTERN_COUNT]; | ||
| protected final InternalSlot[] workerSlotStorage = new InternalSlot[MAX_PATTERN_COUNT]; |
There was a problem hiding this comment.
renaming this field will delete all patterns that are currently stored in the buffer, so you'll need to add some backwards compatibility code for this.
TagCompatibilityFixer.java and its uses have examples you could reference for that.
| private final List<WorkerItemHandler> workerItemHandlers = new ArrayList<>(); | ||
| private final List<WorkerFluidHandler> workerFluidHandlers = new ArrayList<>(); | ||
| private final List<RecipeHandlerList> workerHandlerLists = new ArrayList<>(); | ||
| private @Nullable List<RecipeHandlerList> workerHandlersView; |
There was a problem hiding this comment.
Collections.unmodifiableList always mirrors the source list so this should be sufficient:
| private @Nullable List<RecipeHandlerList> workerHandlersView; | |
| private final @UnmodifiableView List<RecipeHandlerList> workerHandlersView = Collections.unmodifiableList(workerHandlerLists); |
Also apply the other suggestions in this class so this works :)
| }); | ||
|
|
||
| workerHandlerLists.add(new WorkerRHL(itemH, fluidH)); | ||
| workerHandlersView = null; |
There was a problem hiding this comment.
For the unmodifiable list
| workerHandlersView = null; |
| workerItemHandlers.remove(last); | ||
| workerFluidHandlers.remove(last); | ||
| workerHandlerLists.remove(last); | ||
| workerHandlersView = null; |
There was a problem hiding this comment.
For the unmodifiable list
| workerHandlersView = null; |
| if (workerHandlersView == null) | ||
| workerHandlersView = Collections.unmodifiableList(workerHandlerLists); | ||
| return workerHandlersView; |
There was a problem hiding this comment.
| if (workerHandlersView == null) | |
| workerHandlersView = Collections.unmodifiableList(workerHandlerLists); | |
| return workerHandlersView; | |
| return workerHandlersView; |
| } | ||
|
|
||
| @Override | ||
| public List<RecipeHandlerList> getRecipeHandlers() { |
There was a problem hiding this comment.
| public List<RecipeHandlerList> getRecipeHandlers() { | |
| public @UnmodifiableView List<RecipeHandlerList> getRecipeHandlers() { |
| var slot = workerSlots.get(i); | ||
| boolean empty = slot.isItemEmpty() && slot.isFluidEmpty(); | ||
| if (empty) workerPatterns.set(i, null); | ||
| var cur = workerPatterns.get(i); |
There was a problem hiding this comment.
rename this variable to something more descriptive, like currentPattern
What
Remake of pattern buffers to improve performance
Implementation Details
The base greg buffers have 27 pattern slots, each is acting like a distinct input bus for a multiblock, so its basically like slapping 27 distinct dual hatches on the multiblock and it has to scan them all for recipes. So i made it so each buffer has only 1 distinct bus, for all its patterns, but it has blocking mode with allow the same pattern to be pushed and there is additional bus for each proxy. So 1 buffer + 3 proxy is 4 distinct busses for the multi to scan for recipes. It also allows to make bigger buffers for mod developers without them being unusable due to lag. From what i understand it should work roughly the same because a multi will do only 1 type of a recipe at once anyway. But because there is another bus for each proxy, 4 machines can make 4 recipes, all machines also see all busses so if only 1 pattern is used at the time all machines can do it in parallel and each can use max parallels because blocking allows the same pattern to be pushed. It also does one additional check before running the recipe handler, it checks if any item or fluid the slot has currently in its inventory matches any from the recipe and only if it does it runs the recipe handler. It also short circuits when the slot is just empty. The blocking mode with allow the same works like that each pattern pushed is held in a list and only pattern that is the same as the one in the list is allowed to be pushed to the slot, and it also will push the same pattern to the same slot always. I also added caching on some things that get queried more than once per tick.
AI Usage
Agent Used
ClaudeCode Sonet 4.6
Agent Usage Description
I used it to dig into the code so I can get a wider view of the repo easier. It was also used in very early stages of developing to make some base and boiler plate I can work with. The whole system, what it does and how it works, i came up with myself in 100%.
Outcome
Heavily optimizes pattern buffers.
How Was This Tested
It is a part of my addon that I added to my instance of monifactory. I use it heavily and they are not lagging my server almost at all. For comparsion when I first unlocked pattern buffers, i made one and used it on my ABS tower, my TPS fell down to 3 after I connected it to ME system instantly. Now I moved the code from my addon here and tested on dev instance with a few RHFs and LCRs, and they work as expected.
Additional Information
The comparsion chart bellow shows base buffers vs expanded from gtmutils vs insane buffers, where the insane are the ones I made with my custom logic. The optimization mentioned is done in another PR here

Potential Compatibility Issues
If the buffer has anything inside its slots, after this update it will lost those runtime items.