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