From 5f151466762bfc695282cb3cff748290926c2fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sun, 13 Feb 2022 17:01:36 +0100 Subject: [PATCH] Handle edge case where devices get removed while element is unloaded, so we call dispose on the provider in that case. Since dispose is only called through element now, delay this to after the device update, so adapters can call unmount first. Also, assign a new ID to groups when they structurally change. --- .../bus/AbstractGroupingDeviceBusElement.java | 54 ++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/src/main/java/li/cil/oc2/common/bus/AbstractGroupingDeviceBusElement.java b/src/main/java/li/cil/oc2/common/bus/AbstractGroupingDeviceBusElement.java index 5037c08c..44fb0319 100644 --- a/src/main/java/li/cil/oc2/common/bus/AbstractGroupingDeviceBusElement.java +++ b/src/main/java/li/cil/oc2/common/bus/AbstractGroupingDeviceBusElement.java @@ -135,12 +135,33 @@ public abstract class AbstractGroupingDeviceBusElement newEntries = queryResult.getEntries(); - final HashSet oldEntries = groups.get(index); - if (Objects.equals(newEntries, oldEntries)) { + final HashSet entries = groups.get(index); + if (Objects.equals(newEntries, entries)) { + if (entries.isEmpty()) { + // If we do not have any entries, we still need to check if there's any + // remaining data of previously known devices, so we can call dispose + // on the appropriate provider. If we don't do this here, we may delay + // this indefinitely, if no new devices are detected for this index. + final CompoundTag devicesTag = groupData[index]; + if (!devicesTag.isEmpty()) { + final Iterator iterator = devicesTag.getAllKeys().iterator(); + while (iterator.hasNext()) { + final String dataKey = iterator.next(); + if (devicesTag.contains(dataKey, NBTTagIds.TAG_COMPOUND)) { + final CompoundTag tag = devicesTag.getCompound(dataKey); + onEntryRemoved(dataKey, tag, queryResult.getQuery()); + } + iterator.remove(); + } + } + } + return; } - final HashSet removedEntries = new HashSet<>(oldEntries); + final boolean hadOldEntries = !entries.isEmpty(); + + final HashSet removedEntries = new HashSet<>(entries); removedEntries.removeAll(newEntries); for (final TEntry entry : removedEntries) { devices.removeInt(entry.getDevice()); @@ -148,20 +169,25 @@ public abstract class AbstractGroupingDeviceBusElement addedEntries = new HashSet<>(newEntries); - addedEntries.removeAll(oldEntries); + addedEntries.removeAll(entries); for (final TEntry entry : addedEntries) { devices.put(entry.getDevice(), entry.getDeviceEnergyConsumption().orElse(0)); onEntryAdded(entry); } - oldEntries.removeAll(removedEntries); - oldEntries.addAll(newEntries); + entries.removeAll(removedEntries); + entries.addAll(newEntries); + // Remove serialized data for devices that were present before, but are gone now. final CompoundTag devicesTag = groupData[index]; for (final TEntry entry : removedEntries) { entry.getDeviceDataKey().ifPresent(devicesTag::remove); } + // Deserialize data for found devices, if we have existing data for them. Also collect + // the list of serialized data we have for devices that have gone missing without being + // explicitly removed. This can happen if a device is removed while the bus element is + // unloaded. We need to call dispose on the provider if we detect this. final HashSet invalidDataKeys = new HashSet<>(devicesTag.getAllKeys()); for (final TEntry entry : addedEntries) { entry.getDeviceDataKey().ifPresent(key -> { @@ -183,7 +209,23 @@ public abstract class AbstractGroupingDeviceBusElement