Flywheel/src/main/java/com/jozufozu/flywheel/light/LightUpdater.java

138 lines
4.1 KiB
Java
Raw Normal View History

package com.jozufozu.flywheel.light;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import com.jozufozu.flywheel.util.WeakHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.SectionPos;
2021-03-24 14:54:24 +01:00
import net.minecraft.world.IBlockDisplayReader;
import net.minecraft.world.LightType;
/**
* Keeps track of what chunks/sections each listener is in, so we can update exactly what needs to be updated.
*/
public class LightUpdater {
private static final Map<IBlockDisplayReader, LightUpdater> light = new HashMap<>();
public static LightUpdater get(IBlockDisplayReader world) {
return light.computeIfAbsent(world, LightUpdater::new);
}
private final LightProvider provider;
private final WeakHashSet<IMovingListener> movingListeners = new WeakHashSet<>();
private final WeakContainmentMultiMap<ILightUpdateListener> sections = new WeakContainmentMultiMap<>();
private final WeakContainmentMultiMap<ILightUpdateListener> chunks = new WeakContainmentMultiMap<>();
public LightUpdater(IBlockDisplayReader world) {
provider = BasicProvider.get(world);
}
public LightProvider getProvider() {
return provider;
}
public void tick() {
for (IMovingListener listener : movingListeners) {
if (listener.update(provider)) {
addListener(listener);
}
}
}
/**
* Add a listener.
* @param listener The object that wants to receive light update notifications.
*/
public void addListener(ILightUpdateListener listener) {
if (listener instanceof IMovingListener)
movingListeners.add(((IMovingListener) listener));
ImmutableBox box = listener.getVolume();
LongSet sections = this.sections.getAndResetContainment(listener);
LongSet chunks = this.chunks.getAndResetContainment(listener);
2021-09-05 04:57:32 +02:00
int minX = SectionPos.blockToSectionCoord(box.getMinX());
int minY = SectionPos.blockToSectionCoord(box.getMinY());
int minZ = SectionPos.blockToSectionCoord(box.getMinZ());
int maxX = SectionPos.blockToSectionCoord(box.getMaxX());
int maxY = SectionPos.blockToSectionCoord(box.getMaxY());
int maxZ = SectionPos.blockToSectionCoord(box.getMaxZ());
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
for (int y = minY; y <= maxY; y++) {
long sectionPos = SectionPos.asLong(x, y, z);
this.sections.put(sectionPos, listener);
sections.add(sectionPos);
}
long chunkPos = SectionPos.asLong(x, 0, z);
this.chunks.put(chunkPos, listener);
chunks.add(chunkPos);
}
}
}
public void removeListener(ILightUpdateListener listener) {
this.sections.remove(listener);
this.chunks.remove(listener);
}
/**
* Dispatch light updates to all registered {@link ILightUpdateListener}s.
2021-04-30 02:19:08 +02:00
* @param type The type of light that changed.
* @param sectionPos A long representing the section position where light changed.
*/
public void onLightUpdate(LightType type, long sectionPos) {
Set<ILightUpdateListener> set = sections.get(sectionPos);
if (set == null || set.isEmpty()) return;
set.removeIf(l -> l.status().shouldRemove());
ImmutableBox chunkBox = GridAlignedBB.from(SectionPos.of(sectionPos));
2021-09-12 19:29:29 +02:00
set.parallelStream().forEach(listener -> listener.onLightUpdate(provider, type, chunkBox));
}
/**
* Dispatch light updates to all registered {@link ILightUpdateListener}s
* when the server sends lighting data for an entire chunk.
*
*/
public void onLightPacket(int chunkX, int chunkZ) {
long chunkPos = SectionPos.asLong(chunkX, 0, chunkZ);
Set<ILightUpdateListener> set = chunks.get(chunkPos);
if (set == null || set.isEmpty()) return;
set.removeIf(l -> l.status().shouldRemove());
2021-09-12 19:29:29 +02:00
set.parallelStream().forEach(listener -> listener.onLightPacket(provider, chunkX, chunkZ));
}
public static long blockToSection(BlockPos pos) {
return SectionPos.asLong(pos.getX(), pos.getY(), pos.getZ());
}
public static long sectionToChunk(long sectionPos) {
return sectionPos & 0xFFFFFFFFFFF_00000L;
}
public Stream<ImmutableBox> getAllBoxes() {
return chunks.stream().map(ILightUpdateListener::getVolume);
}
public boolean isEmpty() {
return chunks.isEmpty();
}
}