From f33bfa1d696de9cdc628d1b232cd7b8b89f83942 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 9 May 2024 19:44:33 -0500 Subject: [PATCH] Fix monoliths due to duplicate IDs This would specifically happen if moving from one MC version that has more blockstate attributes to one with fewer. If that was done then the both blockstates would act like they were the same, screwing up the ID map. --- .../fullData/FullDataPointIdMap.java | 69 ++++++++++++++++++- .../core/sql/dto/FullDataSourceV2DTO.java | 9 ++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index 61a29f3e5..0cb0df50f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.dataObjects.fullData; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; @@ -118,6 +119,8 @@ public class FullDataPointIdMap /** @return -1 if the list is empty */ public int getMaxValidId() { return this.entryList.size() - 1; } + public int size() { return this.entryList.size(); } + public boolean isEmpty() { return this.entryList.isEmpty(); } public DhSectionPos getPos() { return this.pos; } @@ -169,9 +172,67 @@ public class FullDataPointIdMap } } + /** allows for adding duplicate {@link Entry} */ + private void add(Entry biomeBlockStateEntry, boolean useWriteLocks) + { + try + { + if (useWriteLocks) + { + this.readWriteLock.writeLock().lock(); + } + + + int id = this.entryList.size(); + this.entryList.add(biomeBlockStateEntry); + this.idMap.put(biomeBlockStateEntry, id); + } + finally + { + if (useWriteLocks) + { + this.readWriteLock.writeLock().unlock(); + } + } + } + /** - * Adds each entry from the given map to this map. + * Adds every {@link Entry} from inputMap into this map.
+ * Allows duplicate entries.

+ * + * Allowing duplicate entries should be done if a datasource is just being read in and + * a merge step isn't being done afterwards. If duplicates are removed it may cause + * the ID's to get out of sync since everything will be shifted down after the removed + * ID(s). + */ + public void addAll(FullDataPointIdMap inputMap) + { + try + { + //LOGGER.trace("adding {" + this.pos + ", " + this.entryList.size() + "} and {" + inputMap.pos + ", " + inputMap.entryList.size() + "}"); + + inputMap.readWriteLock.readLock().lock(); + this.readWriteLock.writeLock().lock(); + + ArrayList entriesToMerge = inputMap.entryList; + for (int i = 0; i < entriesToMerge.size(); i++) + { + Entry entity = entriesToMerge.get(i); + this.add(entity, false); + } + } + finally + { + this.readWriteLock.writeLock().unlock(); + inputMap.readWriteLock.readLock().unlock(); + + //LOGGER.trace("finished merging {" + this.pos + ", " + this.entryList.size() + "} and {" + inputMap.pos + ", " + inputMap.entryList.size() + "}"); + } + } + + /** + * Adds each entry from the given map to this map.

* * Note: when using this function be careful about re-mapping the * same data source multiple times. @@ -302,6 +363,12 @@ public class FullDataPointIdMap //LOGGER.trace("deserialized " + pos + " " + newMap.entryList.size() + "-" + entityCount); + if (newMap.size() != entityCount) + { + // if the mappings are out of sync then the LODs will render incorrectly due to IDs being wrong + LodUtil.assertNotReach("ID maps failed to deserialize for pos: "+pos+", incorrect entity count. Expected count ["+entityCount+"], actual count ["+newMap.size()+"]"); + } + return newMap; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java index 728a6d350..99ba7e88a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; @@ -171,7 +172,13 @@ public class FullDataSourceV2DTO implements IBaseDTO throw new NullPointerException("No level wrapper present, unable to deserialize data map. This should only be used for unit tests."); } - dataSource.mapping.mergeAndReturnRemappedEntityIds(readBlobToDataMapping(this.compressedMappingByteArray, dataSource.getPos(), levelWrapper, compressionModeEnum)); + FullDataPointIdMap newMap = readBlobToDataMapping(this.compressedMappingByteArray, dataSource.getPos(), levelWrapper, compressionModeEnum); + dataSource.mapping.addAll(newMap); + if (dataSource.mapping.size() != newMap.size()) + { + // if the mappings are out of sync then the LODs will render incorrectly due to IDs being wrong + LodUtil.assertNotReach("ID maps out of sync for pos: "+this.pos); + } } dataSource.lastModifiedUnixDateTime = this.lastModifiedUnixDateTime;