347 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			347 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.level.lighting;
 | |
| 
 | |
| import it.unimi.dsi.fastutil.longs.Long2ByteMap;
 | |
| import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
 | |
| import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
 | |
| import it.unimi.dsi.fastutil.longs.Long2ObjectMaps;
 | |
| import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
 | |
| import it.unimi.dsi.fastutil.longs.LongIterator;
 | |
| import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 | |
| import it.unimi.dsi.fastutil.longs.LongSet;
 | |
| import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
 | |
| import it.unimi.dsi.fastutil.objects.ObjectIterator;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.SectionPos;
 | |
| import net.minecraft.world.level.LightLayer;
 | |
| import net.minecraft.world.level.chunk.DataLayer;
 | |
| import net.minecraft.world.level.chunk.LightChunkGetter;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 
 | |
| public abstract class LayerLightSectionStorage<M extends DataLayerStorageMap<M>> {
 | |
| 	private final LightLayer layer;
 | |
| 	protected final LightChunkGetter chunkSource;
 | |
| 	protected final Long2ByteMap sectionStates = new Long2ByteOpenHashMap();
 | |
| 	private final LongSet columnsWithSources = new LongOpenHashSet();
 | |
| 	protected volatile M visibleSectionData;
 | |
| 	protected final M updatingSectionData;
 | |
| 	protected final LongSet changedSections = new LongOpenHashSet();
 | |
| 	protected final LongSet sectionsAffectedByLightUpdates = new LongOpenHashSet();
 | |
| 	protected final Long2ObjectMap<DataLayer> queuedSections = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
 | |
| 	/**
 | |
| 	 * Section column positions (section positions with Y=0) that need to be kept even if some of their sections could otherwise be removed.
 | |
| 	 */
 | |
| 	private final LongSet columnsToRetainQueuedDataFor = new LongOpenHashSet();
 | |
| 	/**
 | |
| 	 * Set of section positions that can be removed, because their light won't affect any blocks.
 | |
| 	 */
 | |
| 	private final LongSet toRemove = new LongOpenHashSet();
 | |
| 	protected volatile boolean hasInconsistencies;
 | |
| 
 | |
| 	protected LayerLightSectionStorage(LightLayer layer, LightChunkGetter chunkSource, M updatingSectionData) {
 | |
| 		this.layer = layer;
 | |
| 		this.chunkSource = chunkSource;
 | |
| 		this.updatingSectionData = updatingSectionData;
 | |
| 		this.visibleSectionData = updatingSectionData.copy();
 | |
| 		this.visibleSectionData.disableCache();
 | |
| 		this.sectionStates.defaultReturnValue((byte)0);
 | |
| 	}
 | |
| 
 | |
| 	protected boolean storingLightForSection(long sectionPos) {
 | |
| 		return this.getDataLayer(sectionPos, true) != null;
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	protected DataLayer getDataLayer(long sectionPos, boolean cached) {
 | |
| 		return this.getDataLayer(cached ? this.updatingSectionData : this.visibleSectionData, sectionPos);
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	protected DataLayer getDataLayer(M map, long sectionPos) {
 | |
| 		return map.getLayer(sectionPos);
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	protected DataLayer getDataLayerToWrite(long sectionPos) {
 | |
| 		DataLayer dataLayer = this.updatingSectionData.getLayer(sectionPos);
 | |
| 		if (dataLayer == null) {
 | |
| 			return null;
 | |
| 		} else {
 | |
| 			if (this.changedSections.add(sectionPos)) {
 | |
| 				dataLayer = dataLayer.copy();
 | |
| 				this.updatingSectionData.setLayer(sectionPos, dataLayer);
 | |
| 				this.updatingSectionData.clearCache();
 | |
| 			}
 | |
| 
 | |
| 			return dataLayer;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	public DataLayer getDataLayerData(long sectionPos) {
 | |
| 		DataLayer dataLayer = this.queuedSections.get(sectionPos);
 | |
| 		return dataLayer != null ? dataLayer : this.getDataLayer(sectionPos, false);
 | |
| 	}
 | |
| 
 | |
| 	protected abstract int getLightValue(long levelPos);
 | |
| 
 | |
| 	protected int getStoredLevel(long levelPos) {
 | |
| 		long l = SectionPos.blockToSection(levelPos);
 | |
| 		DataLayer dataLayer = this.getDataLayer(l, true);
 | |
| 		return dataLayer.get(
 | |
| 			SectionPos.sectionRelative(BlockPos.getX(levelPos)),
 | |
| 			SectionPos.sectionRelative(BlockPos.getY(levelPos)),
 | |
| 			SectionPos.sectionRelative(BlockPos.getZ(levelPos))
 | |
| 		);
 | |
| 	}
 | |
| 
 | |
| 	protected void setStoredLevel(long levelPos, int lightLevel) {
 | |
| 		long l = SectionPos.blockToSection(levelPos);
 | |
| 		DataLayer dataLayer;
 | |
| 		if (this.changedSections.add(l)) {
 | |
| 			dataLayer = this.updatingSectionData.copyDataLayer(l);
 | |
| 		} else {
 | |
| 			dataLayer = this.getDataLayer(l, true);
 | |
| 		}
 | |
| 
 | |
| 		dataLayer.set(
 | |
| 			SectionPos.sectionRelative(BlockPos.getX(levelPos)),
 | |
| 			SectionPos.sectionRelative(BlockPos.getY(levelPos)),
 | |
| 			SectionPos.sectionRelative(BlockPos.getZ(levelPos)),
 | |
| 			lightLevel
 | |
| 		);
 | |
| 		SectionPos.aroundAndAtBlockPos(levelPos, this.sectionsAffectedByLightUpdates::add);
 | |
| 	}
 | |
| 
 | |
| 	protected void markSectionAndNeighborsAsAffected(long sectionPos) {
 | |
| 		int i = SectionPos.x(sectionPos);
 | |
| 		int j = SectionPos.y(sectionPos);
 | |
| 		int k = SectionPos.z(sectionPos);
 | |
| 
 | |
| 		for (int l = -1; l <= 1; l++) {
 | |
| 			for (int m = -1; m <= 1; m++) {
 | |
| 				for (int n = -1; n <= 1; n++) {
 | |
| 					this.sectionsAffectedByLightUpdates.add(SectionPos.asLong(i + m, j + n, k + l));
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected DataLayer createDataLayer(long sectionPos) {
 | |
| 		DataLayer dataLayer = this.queuedSections.get(sectionPos);
 | |
| 		return dataLayer != null ? dataLayer : new DataLayer();
 | |
| 	}
 | |
| 
 | |
| 	protected boolean hasInconsistencies() {
 | |
| 		return this.hasInconsistencies;
 | |
| 	}
 | |
| 
 | |
| 	protected void markNewInconsistencies(LightEngine<M, ?> lightEngine) {
 | |
| 		if (this.hasInconsistencies) {
 | |
| 			this.hasInconsistencies = false;
 | |
| 			LongIterator objectIterator = this.toRemove.iterator();
 | |
| 
 | |
| 			while (objectIterator.hasNext()) {
 | |
| 				long l = (Long)objectIterator.next();
 | |
| 				DataLayer dataLayer = this.queuedSections.remove(l);
 | |
| 				DataLayer dataLayer2 = this.updatingSectionData.removeLayer(l);
 | |
| 				if (this.columnsToRetainQueuedDataFor.contains(SectionPos.getZeroNode(l))) {
 | |
| 					if (dataLayer != null) {
 | |
| 						this.queuedSections.put(l, dataLayer);
 | |
| 					} else if (dataLayer2 != null) {
 | |
| 						this.queuedSections.put(l, dataLayer2);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			this.updatingSectionData.clearCache();
 | |
| 			objectIterator = this.toRemove.iterator();
 | |
| 
 | |
| 			while (objectIterator.hasNext()) {
 | |
| 				long l = (Long)objectIterator.next();
 | |
| 				this.onNodeRemoved(l);
 | |
| 				this.changedSections.add(l);
 | |
| 			}
 | |
| 
 | |
| 			this.toRemove.clear();
 | |
| 			ObjectIterator<Entry<DataLayer>> objectIteratorx = Long2ObjectMaps.fastIterator(this.queuedSections);
 | |
| 
 | |
| 			while (objectIteratorx.hasNext()) {
 | |
| 				Entry<DataLayer> entry = (Entry<DataLayer>)objectIteratorx.next();
 | |
| 				long m = entry.getLongKey();
 | |
| 				if (this.storingLightForSection(m)) {
 | |
| 					DataLayer dataLayer2 = (DataLayer)entry.getValue();
 | |
| 					if (this.updatingSectionData.getLayer(m) != dataLayer2) {
 | |
| 						this.updatingSectionData.setLayer(m, dataLayer2);
 | |
| 						this.changedSections.add(m);
 | |
| 					}
 | |
| 
 | |
| 					objectIteratorx.remove();
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			this.updatingSectionData.clearCache();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected void onNodeAdded(long sectionPos) {
 | |
| 	}
 | |
| 
 | |
| 	protected void onNodeRemoved(long sectionPos) {
 | |
| 	}
 | |
| 
 | |
| 	protected void setLightEnabled(long sectionPos, boolean lightEnabled) {
 | |
| 		if (lightEnabled) {
 | |
| 			this.columnsWithSources.add(sectionPos);
 | |
| 		} else {
 | |
| 			this.columnsWithSources.remove(sectionPos);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected boolean lightOnInSection(long sectionPos) {
 | |
| 		long l = SectionPos.getZeroNode(sectionPos);
 | |
| 		return this.columnsWithSources.contains(l);
 | |
| 	}
 | |
| 
 | |
| 	protected boolean lightOnInColumn(long columnPos) {
 | |
| 		return this.columnsWithSources.contains(columnPos);
 | |
| 	}
 | |
| 
 | |
| 	public void retainData(long sectionColumnPos, boolean retain) {
 | |
| 		if (retain) {
 | |
| 			this.columnsToRetainQueuedDataFor.add(sectionColumnPos);
 | |
| 		} else {
 | |
| 			this.columnsToRetainQueuedDataFor.remove(sectionColumnPos);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected void queueSectionData(long sectionPos, @Nullable DataLayer data) {
 | |
| 		if (data != null) {
 | |
| 			this.queuedSections.put(sectionPos, data);
 | |
| 			this.hasInconsistencies = true;
 | |
| 		} else {
 | |
| 			this.queuedSections.remove(sectionPos);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected void updateSectionStatus(long sectionPos, boolean isEmpty) {
 | |
| 		byte b = this.sectionStates.get(sectionPos);
 | |
| 		byte c = LayerLightSectionStorage.SectionState.hasData(b, !isEmpty);
 | |
| 		if (b != c) {
 | |
| 			this.putSectionState(sectionPos, c);
 | |
| 			int i = isEmpty ? -1 : 1;
 | |
| 
 | |
| 			for (int j = -1; j <= 1; j++) {
 | |
| 				for (int k = -1; k <= 1; k++) {
 | |
| 					for (int l = -1; l <= 1; l++) {
 | |
| 						if (j != 0 || k != 0 || l != 0) {
 | |
| 							long m = SectionPos.offset(sectionPos, j, k, l);
 | |
| 							byte d = this.sectionStates.get(m);
 | |
| 							this.putSectionState(m, LayerLightSectionStorage.SectionState.neighborCount(d, LayerLightSectionStorage.SectionState.neighborCount(d) + i));
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	protected void putSectionState(long sectionPos, byte sectionState) {
 | |
| 		if (sectionState != 0) {
 | |
| 			if (this.sectionStates.put(sectionPos, sectionState) == 0) {
 | |
| 				this.initializeSection(sectionPos);
 | |
| 			}
 | |
| 		} else if (this.sectionStates.remove(sectionPos) != 0) {
 | |
| 			this.removeSection(sectionPos);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void initializeSection(long sectionPos) {
 | |
| 		if (!this.toRemove.remove(sectionPos)) {
 | |
| 			this.updatingSectionData.setLayer(sectionPos, this.createDataLayer(sectionPos));
 | |
| 			this.changedSections.add(sectionPos);
 | |
| 			this.onNodeAdded(sectionPos);
 | |
| 			this.markSectionAndNeighborsAsAffected(sectionPos);
 | |
| 			this.hasInconsistencies = true;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	private void removeSection(long sectionPos) {
 | |
| 		this.toRemove.add(sectionPos);
 | |
| 		this.hasInconsistencies = true;
 | |
| 	}
 | |
| 
 | |
| 	protected void swapSectionMap() {
 | |
| 		if (!this.changedSections.isEmpty()) {
 | |
| 			M dataLayerStorageMap = this.updatingSectionData.copy();
 | |
| 			dataLayerStorageMap.disableCache();
 | |
| 			this.visibleSectionData = dataLayerStorageMap;
 | |
| 			this.changedSections.clear();
 | |
| 		}
 | |
| 
 | |
| 		if (!this.sectionsAffectedByLightUpdates.isEmpty()) {
 | |
| 			LongIterator longIterator = this.sectionsAffectedByLightUpdates.iterator();
 | |
| 
 | |
| 			while (longIterator.hasNext()) {
 | |
| 				long l = longIterator.nextLong();
 | |
| 				this.chunkSource.onLightUpdate(this.layer, SectionPos.of(l));
 | |
| 			}
 | |
| 
 | |
| 			this.sectionsAffectedByLightUpdates.clear();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public LayerLightSectionStorage.SectionType getDebugSectionType(long sectionPos) {
 | |
| 		return LayerLightSectionStorage.SectionState.type(this.sectionStates.get(sectionPos));
 | |
| 	}
 | |
| 
 | |
| 	protected static class SectionState {
 | |
| 		public static final byte EMPTY = 0;
 | |
| 		private static final int MIN_NEIGHBORS = 0;
 | |
| 		private static final int MAX_NEIGHBORS = 26;
 | |
| 		private static final byte HAS_DATA_BIT = 32;
 | |
| 		private static final byte NEIGHBOR_COUNT_BITS = 31;
 | |
| 
 | |
| 		public static byte hasData(byte sectionState, boolean hasData) {
 | |
| 			return (byte)(hasData ? sectionState | 32 : sectionState & -33);
 | |
| 		}
 | |
| 
 | |
| 		public static byte neighborCount(byte sectionState, int neighborCount) {
 | |
| 			if (neighborCount >= 0 && neighborCount <= 26) {
 | |
| 				return (byte)(sectionState & -32 | neighborCount & 31);
 | |
| 			} else {
 | |
| 				throw new IllegalArgumentException("Neighbor count was not within range [0; 26]");
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		public static boolean hasData(byte sectionState) {
 | |
| 			return (sectionState & 32) != 0;
 | |
| 		}
 | |
| 
 | |
| 		public static int neighborCount(byte sectionState) {
 | |
| 			return sectionState & 31;
 | |
| 		}
 | |
| 
 | |
| 		public static LayerLightSectionStorage.SectionType type(byte sectionState) {
 | |
| 			if (sectionState == 0) {
 | |
| 				return LayerLightSectionStorage.SectionType.EMPTY;
 | |
| 			} else {
 | |
| 				return hasData(sectionState) ? LayerLightSectionStorage.SectionType.LIGHT_AND_DATA : LayerLightSectionStorage.SectionType.LIGHT_ONLY;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	public static enum SectionType {
 | |
| 		EMPTY("2"),
 | |
| 		LIGHT_ONLY("1"),
 | |
| 		LIGHT_AND_DATA("0");
 | |
| 
 | |
| 		private final String display;
 | |
| 
 | |
| 		private SectionType(final String display) {
 | |
| 			this.display = display;
 | |
| 		}
 | |
| 
 | |
| 		public String display() {
 | |
| 			return this.display;
 | |
| 		}
 | |
| 	}
 | |
| }
 |