From 67c4167d8de20f05e4d4f36d74ac4fc457359c90 Mon Sep 17 00:00:00 2001 From: MattiDragon Date: Sat, 30 Sep 2023 17:06:52 +0300 Subject: [PATCH] Rework caching and update deps --- build.gradle | 4 +- changelog/2.0.6+1.20.1.md | 7 ++ gradle.properties | 10 +- .../client/config/ConfigScreenFactory.java | 32 ++--- .../entity/StorageDrawerBlockEntity.java | 26 +++- .../network/NetworkRegistry.java | 5 +- .../network/NetworkStorageCache.java | 118 +++++++++++++----- .../network/UpdateHandler.java | 6 +- 8 files changed, 150 insertions(+), 58 deletions(-) create mode 100644 changelog/2.0.6+1.20.1.md diff --git a/build.gradle b/build.gradle index ee0fda2..4cd9ed8 100644 --- a/build.gradle +++ b/build.gradle @@ -17,8 +17,9 @@ repositories { maven { url "https://maven.blamejared.com" } maven { url "https://maven.isxander.dev/releases" } maven { url "https://maven.terraformersmc.com" } - maven { url "https://jitpack.io" } + maven { url 'https://maven.quiltmc.org/repository/release/' } maven { url 'https://maven.cafeteria.dev/releases/' } + maven { url "https://jitpack.io" } } // Has to run before dependencies so that we get access to proper dependency configurations @@ -103,7 +104,6 @@ java { withSourcesJar() } - publishing { publications.create("mavenJava", MavenPublication) { from components.java diff --git a/changelog/2.0.6+1.20.1.md b/changelog/2.0.6+1.20.1.md new file mode 100644 index 0000000..449ef4c --- /dev/null +++ b/changelog/2.0.6+1.20.1.md @@ -0,0 +1,7 @@ +* Rework network caching + * Fix issues with items being deleted + * Probably improve performance in cases with rapidly changing networks + * Properly handle drawers in unloaded chunks +* Update dependencies + * Now compatible with latest YACL + * Probably fixed some bugs somewhere \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 5f231ba..2f659a5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,15 +4,15 @@ minecraft_version=1.20.1 yarn_mappings=1.20.1+build.10 loader_version=0.14.21 -mod_version=2.0.5 +mod_version=2.0.6 maven_group=io.github.mattidragon archives_base_name=ExtendedDrawers -fabric_version=0.86.1+1.20.1 -graphlib_version=1.1.1+1.20 +fabric_version=0.89.0+1.20.1 +graphlib_version=1.4.0+1.20 patchouli_version=1.20.1-81-FABRIC -mixinextras_version=0.2.0-beta.9 -yacl_version=3.1.0+1.20 +mixinextras_version=0.2.0-rc.5 +yacl_version=3.2.1+1.20 modmenu_version=7.2.1 configtoolkit_version=1.0.0 noindium_version=1.1.0+1.20 \ No newline at end of file diff --git a/src/client/java/io/github/mattidragon/extendeddrawers/client/config/ConfigScreenFactory.java b/src/client/java/io/github/mattidragon/extendeddrawers/client/config/ConfigScreenFactory.java index a0ad5b1..8c30e95 100644 --- a/src/client/java/io/github/mattidragon/extendeddrawers/client/config/ConfigScreenFactory.java +++ b/src/client/java/io/github/mattidragon/extendeddrawers/client/config/ConfigScreenFactory.java @@ -2,7 +2,8 @@ import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.controller.*; -import dev.isxander.yacl3.gui.ImageRenderer; +import dev.isxander.yacl3.gui.image.ImageRenderer; +import dev.isxander.yacl3.gui.image.ImageRendererManager; import io.github.mattidragon.extendeddrawers.ExtendedDrawers; import io.github.mattidragon.extendeddrawers.client.renderer.AbstractDrawerBlockEntityRenderer; import io.github.mattidragon.extendeddrawers.config.ConfigData; @@ -31,13 +32,12 @@ import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import java.util.function.Function; import static io.github.mattidragon.extendeddrawers.ExtendedDrawers.id; import static io.github.mattidragon.extendeddrawers.config.ConfigData.DEFAULT; public class ConfigScreenFactory { - public static final Function FLOAT_FORMATTER; + public static final ValueFormatter FLOAT_FORMATTER; static { var format = NumberFormat.getNumberInstance(Locale.ROOT); @@ -134,13 +134,13 @@ private static ConfigCategory createMiscCategory(MutableMiscCategory instance) { .option(Option.createBuilder() .name(Text.translatable("config.extended_drawers.misc.frontBreakingBehaviour")) .binding(DEFAULT.misc().frontBreakingBehaviour(), instance::frontBreakingBehaviour, instance::frontBreakingBehaviour) - .controller(option -> EnumControllerBuilder.create(option).enumClass(CreativeBreakingBehaviour.class).valueFormatter(CreativeBreakingBehaviour::getDisplayName)) + .controller(option -> EnumControllerBuilder.create(option).enumClass(CreativeBreakingBehaviour.class).formatValue(CreativeBreakingBehaviour::getDisplayName)) .description(value -> creativeBreakingBehaviourDescription(Text.translatable("config.extended_drawers.misc.frontBreakingBehaviour.description"), value)) .build()) .option(Option.createBuilder() .name(Text.translatable("config.extended_drawers.misc.sideBreakingBehaviour")) .binding(DEFAULT.misc().sideBreakingBehaviour(), instance::sideBreakingBehaviour, instance::sideBreakingBehaviour) - .controller(option -> EnumControllerBuilder.create(option).enumClass(CreativeBreakingBehaviour.class).valueFormatter(CreativeBreakingBehaviour::getDisplayName)) + .controller(option -> EnumControllerBuilder.create(option).enumClass(CreativeBreakingBehaviour.class).formatValue(CreativeBreakingBehaviour::getDisplayName)) .description(value -> creativeBreakingBehaviourDescription(Text.translatable("config.extended_drawers.misc.sideBreakingBehaviour.description"), value)) .build()) .option(Option.createBuilder() @@ -204,25 +204,25 @@ private static OptionGroup createIconGroup(MutableClientCategory.MutableIconGrou .name(Text.translatable("config.extended_drawers.client.lockedIcon")) .binding(DEFAULT.client().icons().lockedIcon(), icons::lockedIcon, icons::lockedIcon) .customController(IdentifierController::new) - .description(id -> OptionDescription.createBuilder().customImage(ImageRenderer.getOrMakeSync(id, () -> Optional.of(new IconRenderer(id)))).text(Text.translatable("config.extended_drawers.client.lockedIcon.description")).build()) + .description(id -> OptionDescription.createBuilder().customImage(ImageRendererManager.registerImage(id, () -> () -> new IconRenderer(id)).thenApply(Optional::of)).text(Text.translatable("config.extended_drawers.client.lockedIcon.description")).build()) .build()) .option(Option.createBuilder() .name(Text.translatable("config.extended_drawers.client.voidingIcon")) .binding(DEFAULT.client().icons().voidingIcon(), icons::voidingIcon, icons::voidingIcon) .customController(IdentifierController::new) - .description(id -> OptionDescription.createBuilder().customImage(ImageRenderer.getOrMakeSync(id, () -> Optional.of(new IconRenderer(id)))).text(Text.translatable("config.extended_drawers.client.voidingIcon.description")).build()) + .description(id -> OptionDescription.createBuilder().customImage(ImageRendererManager.registerImage(id, () -> () -> new IconRenderer(id)).thenApply(Optional::of)).text(Text.translatable("config.extended_drawers.client.voidingIcon.description")).build()) .build()) .option(Option.createBuilder() .name(Text.translatable("config.extended_drawers.client.hiddenIcon")) .binding(DEFAULT.client().icons().hiddenIcon(), icons::hiddenIcon, icons::hiddenIcon) .customController(IdentifierController::new) - .description(id -> OptionDescription.createBuilder().customImage(ImageRenderer.getOrMakeSync(id, () -> Optional.of(new IconRenderer(id)))).text(Text.translatable("config.extended_drawers.client.hiddenIcon.description")).build()) + .description(id -> OptionDescription.createBuilder().customImage(ImageRendererManager.registerImage(id, () -> () -> new IconRenderer(id)).thenApply(Optional::of)).text(Text.translatable("config.extended_drawers.client.hiddenIcon.description")).build()) .build()) .option(Option.createBuilder() .name(Text.translatable("config.extended_drawers.client.dupingIcon")) .binding(DEFAULT.client().icons().dupingIcon(), icons::dupingIcon, icons::dupingIcon) .customController(IdentifierController::new) - .description(id -> OptionDescription.createBuilder().customImage(ImageRenderer.getOrMakeSync(id, () -> Optional.of(new IconRenderer(id)))).text(Text.translatable("config.extended_drawers.client.dupingIcon.description")).build()) + .description(id -> OptionDescription.createBuilder().customImage(ImageRendererManager.registerImage(id, () -> () -> new IconRenderer(id)).thenApply(Optional::of)).text(Text.translatable("config.extended_drawers.client.dupingIcon.description")).build()) .build()) .build(); } @@ -233,31 +233,31 @@ private static OptionGroup createLayoutGroup(MutableClientCategory.MutableLayout var smallItemScale = Option.createBuilder() .name(Text.translatable("config.extended_drawers.client.smallItemScale")) .binding(DEFAULT.client().layout().smallItemScale(), instance::smallItemScale, instance::smallItemScale) - .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 2f).step(0.05f).valueFormatter(FLOAT_FORMATTER)) + .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 2f).step(0.05f).formatValue(FLOAT_FORMATTER)) .description(OptionDescription.createBuilder().customImage(CompletableFuture.completedFuture(Optional.of(layoutRenderer))).text(Text.translatable("config.extended_drawers.client.smallItemScale.description")).build()) .build(); var largeItemScale = Option.createBuilder() .name(Text.translatable("config.extended_drawers.client.largeItemScale")) .binding(DEFAULT.client().layout().largeItemScale(), instance::largeItemScale, instance::largeItemScale) - .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 2f).step(0.05f).valueFormatter(FLOAT_FORMATTER)) + .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 2f).step(0.05f).formatValue(FLOAT_FORMATTER)) .description(OptionDescription.createBuilder().customImage(CompletableFuture.completedFuture(Optional.of(layoutRenderer))).text(Text.translatable("config.extended_drawers.client.largeItemScale.description")).build()) .build(); var smallTextScale = Option.createBuilder() .name(Text.translatable("config.extended_drawers.client.smallTextScale")) .binding(DEFAULT.client().layout().smallTextScale(), instance::smallTextScale, instance::smallTextScale) - .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 2f).step(0.05f).valueFormatter(FLOAT_FORMATTER)) + .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 2f).step(0.05f).formatValue(FLOAT_FORMATTER)) .description(OptionDescription.createBuilder().customImage(CompletableFuture.completedFuture(Optional.of(layoutRenderer))).text(Text.translatable("config.extended_drawers.client.smallTextScale.description")).build()) .build(); var largeTextScale = Option.createBuilder() .name(Text.translatable("config.extended_drawers.client.largeTextScale")) .binding(DEFAULT.client().layout().largeTextScale(), instance::largeTextScale, instance::largeTextScale) - .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 2f).step(0.05f).valueFormatter(FLOAT_FORMATTER)) + .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 2f).step(0.05f).formatValue(FLOAT_FORMATTER)) .description(OptionDescription.createBuilder().customImage(CompletableFuture.completedFuture(Optional.of(layoutRenderer))).text(Text.translatable("config.extended_drawers.client.largeTextScale.description")).build()) .build(); var textOffset = Option.createBuilder() .name(Text.translatable("config.extended_drawers.client.textOffset")) .binding(DEFAULT.client().layout().textOffset(), instance::textOffset, instance::textOffset) - .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 1f).step(0.05f).valueFormatter(FLOAT_FORMATTER)) + .controller(option -> FloatSliderControllerBuilder.create(option).range(0f, 1f).step(0.05f).formatValue(FLOAT_FORMATTER)) .description(OptionDescription.createBuilder().customImage(CompletableFuture.completedFuture(Optional.of(layoutRenderer))).text(Text.translatable("config.extended_drawers.client.textOffset.description")).build()) .build(); @@ -297,7 +297,7 @@ public void init(Option smallItemScale, Option largeItemScale, Opt } @Override - public int render(DrawContext context, int x, int y, int renderWidth) { + public int render(DrawContext context, int x, int y, int renderWidth, float tickDelta) { if (!initialized) return 0; var atlas = MinecraftClient.getInstance().getSpriteAtlas(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); @@ -373,7 +373,7 @@ public void close() { private record IconRenderer(Identifier id) implements ImageRenderer { @Override - public int render(DrawContext graphics, int x, int y, int renderWidth) { + public int render(DrawContext graphics, int x, int y, int renderWidth, float tickDelta) { var blockAtlas = MinecraftClient.getInstance().getSpriteAtlas(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); var sprite = blockAtlas.apply(id); diff --git a/src/main/java/io/github/mattidragon/extendeddrawers/block/entity/StorageDrawerBlockEntity.java b/src/main/java/io/github/mattidragon/extendeddrawers/block/entity/StorageDrawerBlockEntity.java index 8f7db5b..fda405b 100644 --- a/src/main/java/io/github/mattidragon/extendeddrawers/block/entity/StorageDrawerBlockEntity.java +++ b/src/main/java/io/github/mattidragon/extendeddrawers/block/entity/StorageDrawerBlockEntity.java @@ -1,5 +1,6 @@ package io.github.mattidragon.extendeddrawers.block.entity; +import io.github.mattidragon.extendeddrawers.network.NetworkRegistry; import io.github.mattidragon.extendeddrawers.network.UpdateHandler; import io.github.mattidragon.extendeddrawers.storage.DrawerStorage; import net.minecraft.block.Block; @@ -21,8 +22,9 @@ public StorageDrawerBlockEntity(BlockEntityType type, BlockPos pos, BlockStat } public void onSlotChanged(boolean sortingChanged) { - markDirty(); if (world instanceof ServerWorld serverWorld) { + // Using this instead of markDirty to handle cases where drawer is in unloaded chunks (why doesn't minecraft save in unloaded chunks?) + world.getWorldChunk(pos).setNeedsSaving(true); UpdateHandler.scheduleUpdate(serverWorld, pos, sortingChanged ? UpdateHandler.ChangeType.CONTENT : UpdateHandler.ChangeType.COUNT); var state = getCachedState(); world.updateListeners(pos, state, state, Block.NOTIFY_LISTENERS); @@ -35,4 +37,26 @@ public void onSlotChanged(boolean sortingChanged) { @Override public abstract void writeNbt(NbtCompound nbt); + + @Override + public void markRemoved() { + super.markRemoved(); + if (world instanceof ServerWorld serverWorld) { + NetworkRegistry.UNIVERSE.getServerGraphWorld(serverWorld) + .getAllGraphsAt(pos) + .map(graph -> graph.getGraphEntity(NetworkRegistry.STORAGE_CACHE_TYPE)) + .forEach(cache -> cache.onNodeUnloaded(pos)); + } + } + + @Override + public void cancelRemoval() { + super.cancelRemoval(); + if (world instanceof ServerWorld serverWorld) { + NetworkRegistry.UNIVERSE.getServerGraphWorld(serverWorld) + .getAllGraphsAt(pos) + .map(graph -> graph.getGraphEntity(NetworkRegistry.STORAGE_CACHE_TYPE)) + .forEach(cache -> cache.onNodeReloaded(pos)); + } + } } diff --git a/src/main/java/io/github/mattidragon/extendeddrawers/network/NetworkRegistry.java b/src/main/java/io/github/mattidragon/extendeddrawers/network/NetworkRegistry.java index dc48d8d..9b5474c 100644 --- a/src/main/java/io/github/mattidragon/extendeddrawers/network/NetworkRegistry.java +++ b/src/main/java/io/github/mattidragon/extendeddrawers/network/NetworkRegistry.java @@ -15,7 +15,10 @@ public class NetworkRegistry { public static final GraphUniverse UNIVERSE = GraphUniverse.builder() .saveMode(SaveMode.INCREMENTAL) .build(id("drawers")); - public static final GraphEntityType STORAGE_CACHE_TYPE = GraphEntityType.of(id("storage_cache"), NetworkStorageCache::new); + public static final GraphEntityType STORAGE_CACHE_TYPE = GraphEntityType.of(id("storage_cache"), + NetworkStorageCache::new, + nbt -> new NetworkStorageCache(), + NetworkStorageCache::split); public static final GraphEntityType UPDATE_HANDLER_TYPE = GraphEntityType.of(id("update_handler"), UpdateHandler::new); public static void register() { diff --git a/src/main/java/io/github/mattidragon/extendeddrawers/network/NetworkStorageCache.java b/src/main/java/io/github/mattidragon/extendeddrawers/network/NetworkStorageCache.java index 27f4a4a..657a64b 100644 --- a/src/main/java/io/github/mattidragon/extendeddrawers/network/NetworkStorageCache.java +++ b/src/main/java/io/github/mattidragon/extendeddrawers/network/NetworkStorageCache.java @@ -1,9 +1,12 @@ package io.github.mattidragon.extendeddrawers.network; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.kneelawk.graphlib.api.graph.BlockGraph; import com.kneelawk.graphlib.api.graph.GraphEntityContext; import com.kneelawk.graphlib.api.graph.NodeHolder; -import com.kneelawk.graphlib.api.graph.user.GraphEntity; -import com.kneelawk.graphlib.api.graph.user.GraphEntityType; +import com.kneelawk.graphlib.api.graph.user.*; +import com.kneelawk.graphlib.api.util.LinkPos; import io.github.mattidragon.extendeddrawers.block.entity.StorageDrawerBlockEntity; import io.github.mattidragon.extendeddrawers.storage.DrawerStorage; import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant; @@ -14,8 +17,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; /** * Caches storages of all slots in networks to make lookup less expensive. @@ -23,8 +25,9 @@ @SuppressWarnings("UnstableApiUsage") public class NetworkStorageCache implements GraphEntity { private GraphEntityContext context; - @Nullable - private CombinedStorage cachedStorage = null; + private final CombinedStorage cachedStorage = new CombinedStorage<>(new ArrayList<>()); + private final Multimap positions = HashMultimap.create(); + private final Set missingPositions = new HashSet<>(); /** * Helper to easily get the cached storage from a world and pos. @@ -35,45 +38,35 @@ public static CombinedStorage get(ServerWorld world, .map(graph -> graph.getGraphEntity(NetworkRegistry.STORAGE_CACHE_TYPE)) .map(NetworkStorageCache::get) .findFirst() - .orElseGet(() -> new CombinedStorage<>(List.of())); + .orElseGet(() -> new CombinedStorage<>(new ArrayList<>())); } - @NotNull - private List getStorages() { - return new ArrayList<>( - context.getGraph() - .getNodes() - .map(NodeHolder::getBlockPos) - .map(context.getBlockWorld()::getBlockEntity) - .filter(StorageDrawerBlockEntity.class::isInstance) - .map(StorageDrawerBlockEntity.class::cast) - .flatMap(StorageDrawerBlockEntity::streamStorages) - .sorted() - .toList()); + public CombinedStorage get() { + addMissingStorages(); + return cachedStorage; } - public void update(UpdateHandler.ChangeType changeType) { - switch (changeType) { - case STRUCTURE -> cachedStorage = null; - case CONTENT -> { - if (cachedStorage != null) { - cachedStorage.parts.sort(null); + private void addMissingStorages() { + if (!missingPositions.isEmpty()) { + missingPositions.forEach(pos -> { + if (context.getBlockWorld().getBlockEntity(pos) instanceof StorageDrawerBlockEntity drawer) { + drawer.streamStorages().forEach(cachedStorage.parts::add); } - } - case COUNT -> {} + }); + missingPositions.clear(); + sort(); } } - public CombinedStorage get() { - if (cachedStorage == null) { - cachedStorage = new CombinedStorage<>(getStorages()); - } - return cachedStorage; + public void sort() { + cachedStorage.parts.sort(null); } @Override public void onInit(@NotNull GraphEntityContext context) { this.context = context; + missingPositions.clear(); + context.getGraph().getNodes().map(NodeHolder::getBlockPos).forEach(missingPositions::add); } @Override @@ -91,8 +84,69 @@ public void onInit(@NotNull GraphEntityContext context) { return null; } + @Override + public void onNodeCreated(@NotNull NodeHolder node, @Nullable NodeEntity nodeEntity) { + GraphEntity.super.onNodeCreated(node, nodeEntity); + missingPositions.add(node.getBlockPos()); + } + + @Override + public void onNodeDestroyed(@NotNull NodeHolder node, @Nullable NodeEntity nodeEntity, Map linkEntities) { + GraphEntity.super.onNodeDestroyed(node, nodeEntity, linkEntities); + var pos = node.getBlockPos(); + + // Remove storages from cache + positions.get(pos).forEach(cachedStorage.parts::remove); + missingPositions.remove(pos); + } + + public void onNodeUnloaded(BlockPos pos) { + positions.get(pos).forEach(cachedStorage.parts::remove); + missingPositions.add(pos); + } + + public void onNodeReloaded(BlockPos pos) { + missingPositions.add(pos); + } + @Override public void merge(@NotNull NetworkStorageCache other) { + this.positions.putAll(other.positions); + this.missingPositions.addAll(other.missingPositions); + this.cachedStorage.parts.addAll(other.cachedStorage.parts); + this.sort(); + } + + public @NotNull NetworkStorageCache split(@NotNull BlockGraph originalGraph, @NotNull BlockGraph newGraph) { + var newCache = new NetworkStorageCache(); + + // Split position based storage cache + for (var iterator = positions.entries().iterator(); iterator.hasNext(); ) { + var entry = iterator.next(); + var pos = entry.getKey(); + var storage = entry.getValue(); + + if (newGraph.getNodesAt(pos).findAny().isPresent()) { + iterator.remove(); + newCache.positions.put(pos, storage); + } + } + + // Split positions missing from position cache + for (var iterator = missingPositions.iterator(); iterator.hasNext(); ) { + var pos = iterator.next(); + if (newGraph.getNodesAt(pos).findAny().isPresent()) { + iterator.remove(); + newCache.missingPositions.add(pos); + } + } + + // Update storage of new cache and sort the storage of this one for good measure. + newCache.cachedStorage.parts.addAll(newCache.positions.values()); + newCache.sort(); + sort(); + + return newCache; } } diff --git a/src/main/java/io/github/mattidragon/extendeddrawers/network/UpdateHandler.java b/src/main/java/io/github/mattidragon/extendeddrawers/network/UpdateHandler.java index 303ea14..7476b1d 100644 --- a/src/main/java/io/github/mattidragon/extendeddrawers/network/UpdateHandler.java +++ b/src/main/java/io/github/mattidragon/extendeddrawers/network/UpdateHandler.java @@ -32,7 +32,11 @@ public void onTick() { if (queuedUpdate == null) return; if (!(context.getBlockWorld() instanceof ServerWorld world)) return; - context.getGraph().getGraphEntity(NetworkRegistry.STORAGE_CACHE_TYPE).update(queuedUpdate); + NetworkStorageCache networkStorageCache = context.getGraph().getGraphEntity(NetworkRegistry.STORAGE_CACHE_TYPE); + // Structure updates are handled elsewhere and count updates don't matter + if (queuedUpdate == ChangeType.CONTENT) { + networkStorageCache.sort(); + } context.getGraph() .getNodes()