Fix relight and removelight (#475)

* Start work on lighting engine (does not build)

* Implement getLighting

* Setting, flushing light etc works. Getting light should be working..?

* Better queue/chunk handling

* Use correct location for lighting update

* Correct set location, remove debug

* cleanup a little

* Fix fixlight

* Apply to all versions for the numpties

* Remove lighting extent if not using

* Actually bitmask blocks when setting in chunks

* Initialise Maps and Dequeues with inital size

* format

* Documentation maybe
This commit is contained in:
dordsor21 2020-05-27 11:45:08 +01:00 committed by GitHub
parent 1ff5e7761b
commit bdc14c10c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1910 additions and 93 deletions

View File

@ -30,6 +30,7 @@ import net.minecraft.server.v1_14_R1.DataPaletteBlock;
import net.minecraft.server.v1_14_R1.DataPaletteLinear;
import net.minecraft.server.v1_14_R1.GameProfileSerializer;
import net.minecraft.server.v1_14_R1.IBlockData;
import net.minecraft.server.v1_14_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_14_R1.PlayerChunk;
import net.minecraft.server.v1_14_R1.PlayerChunkMap;
import net.minecraft.server.v1_14_R1.World;
@ -167,7 +168,7 @@ public final class BukkitAdapter_1_14 extends NMSAdapter {
}
}
public static void sendChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, int X, int Z, int mask) {
public static void sendChunk(net.minecraft.server.v1_14_R1.WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) {
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
if (playerChunk == null) {
return;
@ -187,6 +188,15 @@ public final class BukkitAdapter_1_14 extends NMSAdapter {
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
if (lighting) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z);
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}

View File

@ -54,10 +54,13 @@ import net.minecraft.server.v1_14_R1.DataPaletteHash;
import net.minecraft.server.v1_14_R1.DataPaletteLinear;
import net.minecraft.server.v1_14_R1.Entity;
import net.minecraft.server.v1_14_R1.EntityTypes;
import net.minecraft.server.v1_14_R1.EnumSkyBlock;
import net.minecraft.server.v1_14_R1.IBlockData;
import net.minecraft.server.v1_14_R1.LightEngineThreaded;
import net.minecraft.server.v1_14_R1.NBTTagCompound;
import net.minecraft.server.v1_14_R1.NBTTagInt;
import net.minecraft.server.v1_14_R1.NibbleArray;
import net.minecraft.server.v1_14_R1.SectionPosition;
import net.minecraft.server.v1_14_R1.TileEntity;
import net.minecraft.server.v1_14_R1.WorldServer;
import org.bukkit.World;
@ -72,6 +75,8 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
public Chunk nmsChunk;
public WorldServer world;
public int X, Z;
public NibbleArray[] blockLight = new NibbleArray[16];
public NibbleArray[] skyLight = new NibbleArray[16];
public BukkitGetBlocks_1_14(World world, int X, int Z) {
this(((CraftWorld) world).getHandle(), X, Z);
@ -119,6 +124,26 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
}
@Override
public int getSkyLight(int x, int y, int z) {
int layer = y >> 4;
if (skyLight[layer] == null) {
skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public int getEmmittedLight(int x, int y, int z) {
int layer = y >> 4;
if (blockLight[layer] == null) {
blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public CompoundTag getEntity(UUID uuid) {
Entity entity = world.getEntity(uuid);
@ -322,6 +347,29 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
}
}
boolean lightUpdate = false;
// Lighting
char[][] light = set.getLight();
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
char[][] skyLight = set.getSkyLight();
if (skyLight != null) {
lightUpdate = true;
try {
fillLightNibble(skyLight, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
Runnable[] syncTasks = null;
int bx = X << 4;
@ -427,24 +475,19 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
};
}
{//Lighting
// TODO optimize, cause this is really slow
LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e();
engine.a(nmsChunk, false);
}
Runnable callback;
if (bitMask == 0 && biomes == null) {
if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
int finalMask = bitMask;
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> {
// Set Modified
nmsChunk.d(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_14.sendChunk(nmsWorld, X, Z, finalMask);
BukkitAdapter_1_14.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate);
if (finalizer != null) finalizer.run();
};
}
@ -617,6 +660,25 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
return tmp;
}
private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
for (int Y = 0; Y < 16; Y++) {
if (light[Y] == null) {
continue;
}
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y));
if (nibble == null) {
continue;
}
synchronized (nibble) {
for (int i = 0; i < 4096; i++) {
if (light[Y][i] < 16) {
nibble.a(i, light[Y][i]);
}
}
}
}
}
@Override
public boolean hasSection(int layer) {
return getSections()[layer] != null;

View File

@ -24,6 +24,7 @@ import net.minecraft.server.v1_15_R1.DataPaletteBlock;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.GameProfileSerializer;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_15_R1.PlayerChunk;
import net.minecraft.server.v1_15_R1.PlayerChunkMap;
import net.minecraft.server.v1_15_R1.World;
@ -85,6 +86,8 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
fieldDirtyBits = PlayerChunk.class.getDeclaredField("r");
fieldDirtyBits.setAccessible(true);
fieldTickingBlockCount.setAccessible(true);
Method declaredGetVisibleChunk = PlayerChunkMap.class.getDeclaredMethod("getVisibleChunk", long.class);
declaredGetVisibleChunk.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
@ -165,7 +168,7 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
}
}
public static void sendChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, int X, int Z, int mask) {
public static void sendChunk(net.minecraft.server.v1_15_R1.WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) {
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
if (playerChunk == null) {
return;
@ -185,6 +188,15 @@ public final class BukkitAdapter_1_15 extends NMSAdapter {
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
if (lighting) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z);
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}

View File

@ -55,10 +55,13 @@ import net.minecraft.server.v1_15_R1.DataPaletteHash;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.Entity;
import net.minecraft.server.v1_15_R1.EntityTypes;
import net.minecraft.server.v1_15_R1.EnumSkyBlock;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.LightEngineThreaded;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.NBTTagInt;
import net.minecraft.server.v1_15_R1.NibbleArray;
import net.minecraft.server.v1_15_R1.SectionPosition;
import net.minecraft.server.v1_15_R1.TileEntity;
import net.minecraft.server.v1_15_R1.WorldServer;
import org.bukkit.World;
@ -75,6 +78,8 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
public Chunk nmsChunk;
public WorldServer world;
public int X, Z;
public NibbleArray[] blockLight = new NibbleArray[16];
public NibbleArray[] skyLight = new NibbleArray[16];
public BukkitGetBlocks_1_15(World world, int X, int Z) {
this(((CraftWorld) world).getHandle(), X, Z);
@ -127,6 +132,26 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
}
@Override
public int getSkyLight(int x, int y, int z) {
int layer = y >> 4;
if (skyLight[layer] == null) {
skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public int getEmmittedLight(int x, int y, int z) {
int layer = y >> 4;
if (blockLight[layer] == null) {
blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public CompoundTag getEntity(UUID uuid) {
Entity entity = world.getEntity(uuid);
@ -335,6 +360,29 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
}
}
boolean lightUpdate = false;
// Lighting
char[][] light = set.getLight();
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
char[][] skyLight = set.getSkyLight();
if (skyLight != null) {
lightUpdate = true;
try {
fillLightNibble(skyLight, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
Runnable[] syncTasks = null;
int bx = X << 4;
@ -440,25 +488,19 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
};
}
//Lighting
// TODO optimize, cause this is really slow
LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e();
//lightChunk()
engine.a(nmsChunk, false);
Runnable callback;
if (bitMask == 0 && biomes == null) {
if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
int finalMask = bitMask;
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> {
// Set Modified
//setLastSaveHadEntities
nmsChunk.d(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_15.sendChunk(nmsWorld, X, Z, finalMask);
BukkitAdapter_1_15.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate);
if (finalizer != null) finalizer.run();
};
}
@ -637,6 +679,25 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
return tmp;
}
private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
for (int Y = 0; Y < 16; Y++) {
if (light[Y] == null) {
continue;
}
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y));
if (nibble == null) {
continue;
}
synchronized (nibble) {
for (int i = 0; i < 4096; i++) {
if (light[Y][i] < 16) {
nibble.a(i, light[Y][i]);
}
}
}
}
}
@Override
public boolean hasSection(int layer) {
return getSections()[layer] != null;

View File

@ -13,12 +13,6 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import io.papermc.lib.PaperLib;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import net.jpountz.util.UnsafeUtils;
import net.minecraft.server.v1_15_R1.Block;
import net.minecraft.server.v1_15_R1.Chunk;
@ -30,6 +24,11 @@ import net.minecraft.server.v1_15_R1.DataPaletteBlock;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.GameProfileSerializer;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.LightEngine;
import net.minecraft.server.v1_15_R1.LightEngineLayer;
import net.minecraft.server.v1_15_R1.LightEngineStorage;
import net.minecraft.server.v1_15_R1.NibbleArray;
import net.minecraft.server.v1_15_R1.PacketPlayOutLightUpdate;
import net.minecraft.server.v1_15_R1.PlayerChunk;
import net.minecraft.server.v1_15_R1.PlayerChunkMap;
import net.minecraft.server.v1_15_R1.World;
@ -38,9 +37,15 @@ import org.bukkit.craftbukkit.v1_15_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
import sun.misc.Unsafe;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
public final class BukkitAdapter_1_15_2 extends NMSAdapter {
@ -60,6 +65,8 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
private final static MethodHandle methodGetVisibleChunk;
public final static MethodHandle methodSetLightNibbleArray;
private static final int CHUNKSECTION_BASE;
private static final int CHUNKSECTION_SHIFT;
@ -90,6 +97,10 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
declaredGetVisibleChunk.setAccessible(true);
methodGetVisibleChunk = MethodHandles.lookup().unreflect(declaredGetVisibleChunk);
Method declaredSetLightNibbleArray = LightEngineStorage.class.getDeclaredMethod("a", long.class, NibbleArray.class);
declaredSetLightNibbleArray.setAccessible(true);
methodSetLightNibbleArray = MethodHandles.lookup().unreflect(declaredSetLightNibbleArray);
Field tmp = DataPaletteBlock.class.getDeclaredField("j");
ReflectionUtils.setAccessibleNonFinal(tmp);
fieldLock = tmp;
@ -167,7 +178,7 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
}
}
public static void sendChunk(WorldServer nmsWorld, int X, int Z, int mask) {
public static void sendChunk(WorldServer nmsWorld, int X, int Z, int mask, boolean lighting) {
PlayerChunk playerChunk = getPlayerChunk(nmsWorld, X, Z);
if (playerChunk == null) {
return;
@ -187,6 +198,15 @@ public final class BukkitAdapter_1_15_2 extends NMSAdapter {
fieldDirtyBits.set(playerChunk, dirtyBits);
fieldDirtyCount.set(playerChunk, 64);
if (lighting) {
ChunkCoordIntPair chunkCoordIntPair = new ChunkCoordIntPair(X, Z);
PacketPlayOutLightUpdate packet = new PacketPlayOutLightUpdate(chunkCoordIntPair, nmsWorld.getChunkProvider().getLightEngine());
playerChunk.players.a(chunkCoordIntPair, false).forEach(p -> {
p.playerConnection.sendPacket(packet);
});
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}

View File

@ -38,10 +38,12 @@ import net.minecraft.server.v1_15_R1.DataPaletteHash;
import net.minecraft.server.v1_15_R1.DataPaletteLinear;
import net.minecraft.server.v1_15_R1.Entity;
import net.minecraft.server.v1_15_R1.EntityTypes;
import net.minecraft.server.v1_15_R1.EnumSkyBlock;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.LightEngineThreaded;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.NBTTagInt;
import net.minecraft.server.v1_15_R1.NibbleArray;
import net.minecraft.server.v1_15_R1.SectionPosition;
import net.minecraft.server.v1_15_R1.TileEntity;
import net.minecraft.server.v1_15_R1.WorldServer;
import org.bukkit.World;
@ -80,6 +82,8 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
public Chunk nmsChunk;
public WorldServer world;
public int X, Z;
public NibbleArray[] blockLight = new NibbleArray[16];
public NibbleArray[] skyLight = new NibbleArray[16];
public BukkitGetBlocks_1_15_2(World world, int X, int Z) {
this(((CraftWorld) world).getHandle(), X, Z);
@ -132,6 +136,26 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
return AdaptedMap.immutable(nmsTiles, posNms2We, nmsTile2We);
}
@Override
public int getSkyLight(int x, int y, int z) {
int layer = y >> 4;
if (skyLight[layer] == null) {
skyLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return skyLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public int getEmmittedLight(int x, int y, int z) {
int layer = y >> 4;
if (blockLight[layer] == null) {
blockLight[layer] = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(SectionPosition.a(nmsChunk.getPos(), layer));
}
long l = BlockPosition.a(x, y, z);
return blockLight[layer].a(SectionPosition.b(BlockPosition.b(l)), SectionPosition.b(BlockPosition.c(l)), SectionPosition.b(BlockPosition.d(l)));
}
@Override
public CompoundTag getEntity(UUID uuid) {
Entity entity = world.getEntity(uuid);
@ -347,6 +371,29 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
}
}
boolean lightUpdate = false;
// Lighting
char[][] light = set.getLight();
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
char[][] skyLight = set.getSkyLight();
if (skyLight != null) {
lightUpdate = true;
try {
fillLightNibble(skyLight, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
Runnable[] syncTasks = null;
int bx = X << 4;
@ -452,23 +499,19 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
};
}
//Lighting
// TODO optimize, cause this is really slow
LightEngineThreaded engine = (LightEngineThreaded) nmsChunk.e();
engine.a(nmsChunk, false);
Runnable callback;
if (bitMask == 0 && biomes == null) {
if (bitMask == 0 && biomes == null && !lightUpdate) {
callback = null;
} else {
int finalMask = bitMask;
int finalMask = bitMask != 0 ? bitMask : lightUpdate ? set.getBitMask() : 0;
boolean finalLightUpdate = lightUpdate;
callback = () -> {
// Set Modified
nmsChunk.d(true); // Set Modified
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_15_2.sendChunk(nmsWorld, X, Z, finalMask);
BukkitAdapter_1_15_2.sendChunk(nmsWorld, X, Z, finalMask, finalLightUpdate);
if (finalizer != null) finalizer.run();
};
}
@ -641,6 +684,25 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
return tmp;
}
private void fillLightNibble(char[][] light, EnumSkyBlock skyBlock) {
for (int Y = 0; Y < 16; Y++) {
if (light[Y] == null) {
continue;
}
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(skyBlock).a(SectionPosition.a(nmsChunk.getPos(), Y));
if (nibble == null) {
continue;
}
synchronized (nibble) {
for (int i = 0; i < 4096; i++) {
if (light[Y][i] < 16) {
nibble.a(i, light[Y][i]);
}
}
}
}
}
@Override
public boolean hasSection(int layer) {
return getSections()[layer] != null;
@ -648,6 +710,8 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
@Override
public boolean trim(boolean aggressive) {
skyLight = new NibbleArray[16];
blockLight = new NibbleArray[16];
if (aggressive) {
sections = null;
nmsChunk = null;

View File

@ -2,8 +2,11 @@ package com.boydti.fawe;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.lighting.NMSRelighter;
import com.boydti.fawe.beta.implementation.queue.ParallelQueueExtent;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RelightMode;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.SimpleChangeSetSummary;
import com.boydti.fawe.object.exception.FaweException;
@ -28,10 +31,13 @@ import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
import com.sk89q.worldedit.internal.registry.AbstractFactory;
import com.sk89q.worldedit.internal.registry.InputParser;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Location;
import com.sk89q.worldedit.util.formatting.text.Component;
import com.sk89q.worldedit.world.World;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
@ -43,7 +49,6 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
/**
* The FaweAPI class offers a few useful functions.<br>
@ -387,12 +392,52 @@ public class FaweAPI {
* - Relights in parallel (if enabled) for best performance<br>
* - Also resends chunks<br>
*
* @param world
* @param selection (assumes cuboid)
* @return
* @param world World to relight in
* @param selection Region to relight
* @param queue Queue to relight in/from
* @param mode The mode to relight with
* @return Chunks changed
*/
public static int fixLighting(World world, Region selection, @Nullable IQueueExtent queue) {
throw new UnsupportedOperationException();
public static int fixLighting(World world, Region selection, @Nullable IQueueExtent<IQueueChunk> queue, final RelightMode mode) {
final BlockVector3 bot = selection.getMinimumPoint();
final BlockVector3 top = selection.getMaximumPoint();
final int minX = bot.getBlockX() >> 4;
final int minZ = bot.getBlockZ() >> 4;
final int maxX = top.getBlockX() >> 4;
final int maxZ = top.getBlockZ() >> 4;
int count = 0;
if (queue == null) {
World unwrapped = WorldWrapper.unwrap(world);
if (unwrapped instanceof IQueueExtent) {
queue = (IQueueExtent) unwrapped;
} else if (Settings.IMP.QUEUE.PARALLEL_THREADS > 1) {
ParallelQueueExtent parallel =
new ParallelQueueExtent(Fawe.get().getQueueHandler(), world, true);
queue = parallel.getExtent();
} else {
queue = Fawe.get().getQueueHandler().getQueue(world);
}
}
NMSRelighter relighter = new NMSRelighter(queue);
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
relighter.addChunk(x, z, null, 65535);
count++;
}
}
if (mode != RelightMode.NONE) {
relighter.fixSkyLighting();
relighter.fixBlockLighting();
} else {
relighter.removeLighting();
}
relighter.sendChunks();
return count;
}
/**

View File

@ -26,6 +26,12 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput {
@Override
BlockState getBlock(int x, int y, int z);
@Override
int getSkyLight(int x, int y, int z);
@Override
int getEmmittedLight(int x, int y, int z);
default void optimize() {
}

View File

@ -28,6 +28,20 @@ public interface IChunkSet extends IBlocks, OutputExtent {
@Override
boolean setTile(int x, int y, int z, CompoundTag tile);
@Override
void setBlockLight(int x, int y, int z, int value);
@Override
void setSkyLight(int x, int y, int z, int value);
void setLightLayer(int layer, char[] toSet);
void setSkyLightLayer(int layer, char[] toSet);
void removeSectionLighting(int layer, boolean sky);
void setFullBright(int layer);
void setEntity(CompoundTag tag);
void removeEntity(UUID uuid);
@ -40,12 +54,27 @@ public interface IChunkSet extends IBlocks, OutputExtent {
return getBiomes() != null;
}
char[][] getLight();
char[][] getSkyLight();
default boolean hasLight() {
return getLight() != null;
}
// Default to avoid tricky child classes. We only need it in a few cases anyway.
default void setFastMode(boolean fastMode){}
default boolean isFastMode() {
return false;
}
//default to avoid tricky child classes. We only need it in a few cases anyway.
default void setFastMode(boolean fastMode){}
// Allow setting for bitmask for flushing lighting. Default to avoid tricky child classes.
default void setBitMask(int bitMask){}
default int getBitMask(){
return -1;
}
@Override
IChunkSet reset();

View File

@ -55,4 +55,40 @@ public interface IChunkExtent<T extends IChunk> extends Extent {
final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
return chunk.getBiomeType(x & 15, y, z & 15);
}
@Override
default void setSkyLight(int x, int y, int z, int value) {
final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
chunk.setSkyLight(x & 15, y, z & 15, value);
}
@Override
default void setBlockLight(int x, int y, int z, int value) {
final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
chunk.setSkyLight(x & 15, y, z & 15, value);
}
@Override
default int getSkyLight(int x, int y, int z) {
final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
return chunk.getSkyLight(x & 15, y, z & 15);
}
@Override
default int getEmmittedLight(int x, int y, int z) {
final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
return chunk.getEmmittedLight(x & 15, y, z & 15);
}
@Override
default int getBrightness(int x, int y, int z) {
final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
return chunk.getBrightness(x & 15, y, z & 15);
}
@Override
default int getOpacity(int x, int y, int z) {
final IChunk chunk = getOrCreateChunk(x >> 4, z >> 4);
return chunk.getOpacity(x & 15, y, z & 15);
}
}

View File

@ -66,6 +66,18 @@ public class BitSetBlocks implements IChunkSet {
return false;
}
@Override public void setBlockLight(int x, int y, int z, int value) {}
@Override public void setSkyLight(int x, int y, int z, int value) {}
@Override public void setLightLayer(int layer, char[] toSet) {}
@Override public void setSkyLightLayer(int layer, char[] toSet) {}
@Override public void removeSectionLighting(int layer, boolean sky) {}
@Override public void setFullBright(int layer) {}
@Override
public void setEntity(CompoundTag tag) {
}
@ -118,6 +130,14 @@ public class BitSetBlocks implements IChunkSet {
return null;
}
@Override public char[][] getLight() {
return new char[0][];
}
@Override public char[][] getSkyLight() {
return new char[0][];
}
@Override
public BiomeType getBiomeType(int x, int y, int z) {
return null;

View File

@ -5,24 +5,21 @@ import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.queue.Pool;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.collection.BlockVector3ChunkMap;
import com.boydti.fawe.util.MathMan;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.math.BlockVector2;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import com.sk89q.worldedit.world.block.BlockTypes;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import org.jetbrains.annotations.Range;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.IntStream;
import org.jetbrains.annotations.Range;
public class CharSetBlocks extends CharBlocks implements IChunkSet {
private static final Pool<CharSetBlocks> POOL = FaweCache.IMP.registerPool(CharSetBlocks.class, CharSetBlocks::new, Settings.IMP.QUEUE.POOL);
@ -31,10 +28,13 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
}
public BiomeType[] biomes;
public char[][] light;
public char[][] skyLight;
public BlockVector3ChunkMap<CompoundTag> tiles;
public HashSet<CompoundTag> entities;
public HashSet<UUID> entityRemoves;
private boolean fastMode = false;
private int bitMask = -1;
private CharSetBlocks() {}
@ -111,6 +111,80 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
return true;
}
@Override public void setBlockLight(int x, int y, int z, int value) {
if (light == null) {
light = new char[16][];
}
final int layer = y >> 4;
if (light[layer] == null) {
char[] c = new char[4096];
Arrays.fill(c, (char) 16);
light[layer] = c;
}
final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15);
light[y >> 4][index] = (char) value;
}
@Override public void setSkyLight(int x, int y, int z, int value) {
if (skyLight == null) {
skyLight = new char[16][];
}
final int layer = y >> 4;
if (skyLight[layer] == null) {
char[] c = new char[4096];
Arrays.fill(c, (char) 16);
skyLight[layer] = c;
}
final int index = (y & 15) << 8 | (z & 15) << 4 | (x & 15);
skyLight[y >> 4][index] = (char) value;
}
@Override public void setLightLayer(int layer, char[] toSet) {
if (light == null) {
light = new char[16][];
}
light[layer] = toSet;
}
@Override public void setSkyLightLayer(int layer, char[] toSet) {
if (skyLight == null) {
skyLight = new char[16][];
}
skyLight[layer] = toSet;
}
@Override public char[][] getLight() {
return light;
}
@Override public char[][] getSkyLight() {
return skyLight;
}
@Override public void removeSectionLighting(int layer, boolean sky) {
if (light == null) {
light = new char[16][];
}
if (light[layer] == null) {
light[layer] = new char[4096];
}
Arrays.fill(light[layer], (char) 0);
if (sky) {
if (skyLight == null) {
skyLight = new char[16][];
}
if (skyLight[layer] == null) {
skyLight[layer] = new char[4096];
}
Arrays.fill(skyLight[layer], (char) 0);
}
}
@Override public void setFullBright(int layer) {
Arrays.fill(light[layer], (char) 15);
Arrays.fill(skyLight[layer], (char) 15);
}
@Override
public boolean setBiome(BlockVector2 position, BiomeType biome) {
return setBiome(position.getX(),0, position.getZ(), biome);
@ -142,9 +216,19 @@ public class CharSetBlocks extends CharBlocks implements IChunkSet {
return fastMode;
}
@Override
public void setBitMask(int bitMask) {
this.bitMask = bitMask;
}
@Override
public int getBitMask() {
return bitMask;
}
@Override
public boolean isEmpty() {
if (biomes != null) {
if (biomes != null || light != null || skyLight != null) {
return false;
}
return IntStream.range(0, 16).noneMatch(this::hasSection);

View File

@ -41,6 +41,14 @@ public class FallbackChunkGet implements IChunkGet {
return extent.getBlock(bx + x, y, bz + z);
}
@Override public int getSkyLight(int x, int y, int z) {
return extent.getSkyLight(bx + x, y, bz + z);
}
@Override public int getEmmittedLight(int x, int y, int z) {
return extent.getEmmittedLight(bx + x, y, bz + z);
}
@Override
public CompoundTag getTile(int x, int y, int z) {
return extent.getFullBlock(bx + x, y, bz + z).getNbtData();

View File

@ -12,7 +12,6 @@ import com.sk89q.worldedit.world.block.BaseBlock
import com.sk89q.worldedit.world.block.BlockState
import com.sk89q.worldedit.world.block.BlockTypes
import java.util.Collections
import java.util.UUID
import java.util.concurrent.Future
@ -66,6 +65,14 @@ object NullChunkGet : IChunkGet {
return false
}
override fun getEmmittedLight(x: Int, y: Int, z: Int): Int {
return 15
}
override fun getSkyLight(x: Int, y: Int, z: Int): Int {
return 15
}
override fun reset(): IBlocks? {
return null
}

View File

@ -1,13 +1,13 @@
package com.boydti.fawe.beta.implementation.chunk;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.queue.Pool;
import com.boydti.fawe.config.Settings;
import com.sk89q.jnbt.CompoundTag;
@ -17,17 +17,18 @@ import com.sk89q.worldedit.world.biome.BiomeType;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockStateHolder;
import org.jetbrains.annotations.Range;
import javax.annotation.Nullable;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Future;
import javax.annotation.Nullable;
import org.jetbrains.annotations.Range;
/**
* An abstract {@link IChunk} class that implements basic get/set blocks
*/
@SuppressWarnings("rawtypes")
public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
private static final Pool<ChunkHolder> POOL = FaweCache.IMP.registerPool(ChunkHolder.class, ChunkHolder::new, Settings.IMP.QUEUE.POOL);
@ -43,6 +44,8 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
private int chunkX;
private int chunkZ;
private boolean fastmode;
private int bitMask = -1; // Allow forceful setting of bitmask (for lighting)
private boolean isInit = false; // Lighting handles queue differently. It relies on the chunk cache and not doing init.
private ChunkHolder() {
this.delegate = NULL;
@ -91,6 +94,14 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return delegate.set(this).getBiomes(); // TODO return get?
}
@Override public char[][] getLight() {
return delegate.set(this).getLight();
}
@Override public char[][] getSkyLight() {
return delegate.set(this).getSkyLight();
}
@Override
public void setBlocks(int layer, char[] data) {
delegate.set(this).setBlocks(layer, data);
@ -111,6 +122,18 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
this.fastmode = fastmode;
}
public void setBitMask(int bitMask) {
this.bitMask = bitMask;
}
public int getBitMask() {
return bitMask;
}
public boolean isInit() {
return isInit;
}
@Override
public CompoundTag getEntity(UUID uuid) {
return delegate.get(this).getEntity(uuid);
@ -139,6 +162,36 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return chunk.chunkSet.setBlock(x, y, z, block);
}
@Override
public void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value) {
chunk.chunkSet.setSkyLight(x, y, z, value);
}
@Override
public void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value) {
chunk.chunkSet.setBlockLight(x, y, z, value);
}
@Override
public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
chunk.chunkSet.removeSectionLighting(layer, sky);
}
@Override
public void setFullBright(ChunkHolder chunk, int layer){
chunk.chunkSet.setFullBright(layer);
}
@Override
public void setLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
chunk.chunkSet.setLightLayer(layer, toSet);
}
@Override
public void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
chunk.chunkSet.setSkyLightLayer(layer, toSet);
}
@Override
public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getBiomeType(x, y, z);
@ -154,6 +207,44 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
int z) {
return chunk.chunkExisting.getFullBlock(x, y, z);
}
@Override
public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
if (chunk.chunkSet.getSkyLight() != null) {
int layer = y >> 4;
if (chunk.chunkSet.getSkyLight()[layer] != null) {
int setLightValue = chunk.chunkSet.getSkyLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (setLightValue < 16) {
return setLightValue;
}
}
}
return chunk.chunkExisting.getSkyLight(x, y, z);
}
@Override
public int getEmmittedLight(ChunkHolder chunk, int x, int y, int z) {
if (chunk.chunkSet.getLight() != null) {
int layer = y >> 4;
if (chunk.chunkSet.getLight()[layer] != null) {
int setLightValue = chunk.chunkSet.getLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (setLightValue < 16) {
return setLightValue;
}
}
}
return chunk.chunkExisting.getEmmittedLight(x, y, z);
}
@Override
public int getBrightness(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getBrightness(x, y, z);
}
@Override
public int getOpacity(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getOpacity(x, y, z);
}
};
private static final IBlockDelegate GET = new IBlockDelegate() {
@ -185,6 +276,48 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return chunk.setBlock(x, y, z, block);
}
@Override
public void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value) {
chunk.getOrCreateSet();
chunk.delegate = BOTH;
chunk.setSkyLight(x, y, z, value);
}
@Override
public void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value) {
chunk.getOrCreateSet();
chunk.delegate = BOTH;
chunk.setBlockLight(x, y, z, value);
}
@Override
public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
chunk.getOrCreateSet();
chunk.delegate = BOTH;
chunk.removeSectionLighting(layer, sky);
}
@Override
public void setFullBright(ChunkHolder chunk, int layer){
chunk.getOrCreateSet();
chunk.delegate = BOTH;
chunk.setFullBright(layer);
}
@Override
public void setLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
chunk.getOrCreateSet();
chunk.delegate = BOTH;
chunk.setLightLayer(layer, toSet);
}
@Override
public void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
chunk.getOrCreateSet();
chunk.delegate = BOTH;
chunk.setSkyLightLayer(layer, toSet);
}
@Override
public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getBiomeType(x, y, z);
@ -200,6 +333,26 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
int z) {
return chunk.chunkExisting.getFullBlock(x, y, z);
}
@Override
public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getSkyLight(x, y, z);
}
@Override
public int getEmmittedLight(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getEmmittedLight(x, y, z);
}
@Override
public int getBrightness(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getBrightness(x, y, z);
}
@Override
public int getOpacity(ChunkHolder chunk, int x, int y, int z) {
return chunk.chunkExisting.getOpacity(x, y, z);
}
};
private static final IBlockDelegate SET = new IBlockDelegate() {
@ -226,6 +379,36 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return chunk.chunkSet.setBlock(x, y, z, block);
}
@Override
public void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value) {
chunk.chunkSet.setSkyLight(x, y, z, value);
}
@Override
public void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value) {
chunk.chunkSet.setBlockLight(x, y, z, value);
}
@Override
public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
chunk.chunkSet.removeSectionLighting(layer, sky);
}
@Override
public void setFullBright(ChunkHolder chunk, int layer){
chunk.chunkSet.setFullBright(layer);
}
@Override
public void setLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
chunk.chunkSet.setLightLayer(layer, toSet);
}
@Override
public void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
chunk.chunkSet.setSkyLightLayer(layer, toSet);
}
@Override
public BiomeType getBiome(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet();
@ -247,6 +430,52 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
chunk.delegate = BOTH;
return chunk.getFullBlock(x, y, z);
}
@Override
public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
if (chunk.chunkSet.getSkyLight() != null) {
int layer = y >> 4;
if (chunk.chunkSet.getSkyLight()[layer] != null) {
int setLightValue = chunk.chunkSet.getSkyLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (setLightValue < 16) {
return setLightValue;
}
}
}
chunk.getOrCreateGet();
chunk.delegate = BOTH;
return chunk.getSkyLight(x, y, z);
}
@Override
public int getEmmittedLight(ChunkHolder chunk, int x, int y, int z) {
if (chunk.chunkSet.getLight() != null) {
int layer = y >> 4;
if (chunk.chunkSet.getLight()[layer] != null) {
int setLightValue = chunk.chunkSet.getLight()[layer][(y & 15) << 8 | (z & 15) << 4 | (x & 15)];
if (setLightValue < 16) {
return setLightValue;
}
}
}
chunk.getOrCreateGet();
chunk.delegate = BOTH;
return chunk.getEmmittedLight(x, y, z);
}
@Override
public int getBrightness(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
return chunk.getBrightness(x, y, z);
}
@Override
public int getOpacity(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
return chunk.getOpacity(x, y, z);
}
};
private static final IBlockDelegate NULL = new IBlockDelegate() {
@ -303,6 +532,80 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
chunk.chunkExisting.trim(false);
return chunk.getFullBlock(x, y, z);
}
@Override
public void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value) {
chunk.getOrCreateSet();
chunk.delegate = SET;
chunk.setSkyLight(x, y, z, value);
}
@Override
public void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value) {
chunk.getOrCreateSet();
chunk.delegate = SET;
chunk.setBlockLight(x, y, z, value);
}
@Override
public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
chunk.getOrCreateSet();
chunk.delegate = SET;
chunk.removeSectionLighting(layer, sky);
}
@Override
public void setFullBright(ChunkHolder chunk, int layer){
chunk.getOrCreateSet();
chunk.delegate = SET;
chunk.setFullBright(layer);
}
@Override
public void setLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
chunk.getOrCreateSet();
chunk.delegate = SET;
chunk.setLightLayer(layer, toSet);
}
@Override
public void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet) {
chunk.getOrCreateSet();
chunk.delegate = SET;
chunk.setSkyLightLayer(layer, toSet);
}
@Override
public int getSkyLight(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet();
chunk.delegate = GET;
chunk.chunkExisting.trim(false);
return chunk.getSkyLight(x, y, z);
}
@Override
public int getEmmittedLight(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet();
chunk.delegate = GET;
chunk.chunkExisting.trim(false);
return chunk.getEmmittedLight(x, y, z);
}
@Override
public int getBrightness(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet();
chunk.delegate = GET;
chunk.chunkExisting.trim(false);
return chunk.getBrightness(x, y, z);
}
@Override
public int getOpacity(ChunkHolder chunk, int x, int y, int z) {
chunk.getOrCreateGet();
chunk.delegate = GET;
chunk.chunkExisting.trim(false);
return chunk.getOpacity(x, y, z);
}
};
@Override
@ -416,11 +719,13 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
delegate = NULL;
}
chunkExisting = null;
isInit = true;
}
@Override
public synchronized T call() {
if (chunkSet != null) {
chunkSet.setBitMask(bitMask);
return this.call(chunkSet, this::recycle);
}
return null;
@ -481,6 +786,54 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return delegate.getFullBlock(this, x, y, z);
}
@Override
public void setSkyLight(int x, int y, int z, int value) {
delegate.setSkyLight(this, x, y, z, value);
}
@Override public void removeSectionLighting(int layer, boolean sky) {
delegate.removeSectionLighting(this, layer, sky);
}
@Override public void setFullBright(int layer) {
delegate.setFullBright(this, layer);
}
@Override
public void setBlockLight(int x, int y, int z, int value) {
delegate.setBlockLight(this, x, y, z, value);
}
@Override
public void setLightLayer(int layer, char[] toSet) {
delegate.setLightLayer(this, layer, toSet);
}
@Override
public void setSkyLightLayer(int layer, char[] toSet) {
delegate.setSkyLightLayer(this, layer, toSet);
}
@Override
public int getSkyLight(int x, int y, int z) {
return delegate.getSkyLight(this, x, y, z);
}
@Override
public int getEmmittedLight(int x, int y, int z) {
return delegate.getEmmittedLight(this, x, y, z);
}
@Override
public int getBrightness(int x, int y, int z) {
return delegate.getBrightness(this, x, y, z);
}
@Override
public int getOpacity(int x, int y, int z) {
return delegate.getOpacity(this, x, y, z);
}
public interface IBlockDelegate {
<C extends Future<C>> IChunkGet get(ChunkHolder<C> chunk);
IChunkSet set(ChunkHolder chunk);
@ -494,5 +847,25 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
BlockState getBlock(ChunkHolder chunk, int x, int y, int z);
BaseBlock getFullBlock(ChunkHolder chunk, int x, int y, int z);
void setSkyLight(ChunkHolder chunk, int x, int y, int z, int value);
void setBlockLight(ChunkHolder chunk, int x, int y, int z, int value);
void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky);
void setFullBright(ChunkHolder chunk, int layer);
void setLightLayer(ChunkHolder chunk, int layer, char[] toSet);
void setSkyLightLayer(ChunkHolder chunk, int layer, char[] toSet);
int getSkyLight(ChunkHolder chunk, int x, int y, int z);
int getEmmittedLight(ChunkHolder chunk, int x, int y, int z);
int getBrightness(ChunkHolder chunk, int x, int y, int z);
int getOpacity(ChunkHolder chunk, int x, int y, int z);
}
}

View File

@ -62,6 +62,46 @@ object NullChunk : IQueueChunk<Nothing> {
return null
}
override fun getSkyLight(x: Int, y: Int, z: Int): Int {
return 15
}
override fun getLight(): Array<CharArray> {
return emptyArray()
}
override fun getSkyLight(): Array<CharArray> {
return emptyArray()
}
override fun getEmmittedLight(x: Int, y: Int, z: Int): Int {
return 15
}
override fun setSkyLight(x: Int, y: Int, z: Int, value: Int) {
}
override fun setBlockLight(x: Int, y: Int, z: Int, value: Int) {
}
override fun setFullBright(layer: Int) {
}
override fun removeSectionLighting(layer: Int, sky: Boolean) {
}
override fun setSkyLightLayer(layer: Int, toSet: CharArray?) {
}
override fun setLightLayer(layer: Int, toSet: CharArray?) {
}
override fun getBiomes(): Array<BiomeType>? {
return null
}

View File

@ -0,0 +1,645 @@
package com.boydti.fawe.beta.implementation.lighting;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.chunk.ChunkHolder;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.collection.BlockVectorSet;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.TaskManager;
import com.sk89q.worldedit.math.MutableBlockVector3;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
public class NMSRelighter implements Relighter {
private final IQueueExtent<IQueueChunk> queue;
private final Map<Long, RelightSkyEntry> skyToRelight;
private final Object present = new Object();
private final Map<Long, Integer> chunksToSend;
private final ConcurrentLinkedQueue<RelightSkyEntry> extentdSkyToRelight = new ConcurrentLinkedQueue<>();
private final Map<Long, long[][][] /* z y x */ > lightQueue;
private final AtomicBoolean lightLock = new AtomicBoolean(false);
private final ConcurrentHashMap<Long, long[][][]> concurrentLightQueue;
private final int maxY;
public final MutableBlockVector3 mutableBlockPos = new MutableBlockVector3(0, 0, 0);
private static final int DISPATCH_SIZE = 64;
private boolean removeFirst;
public NMSRelighter(IQueueExtent<IQueueChunk> queue) {
this.queue = queue;
this.skyToRelight = new Long2ObjectOpenHashMap<>(12);
this.lightQueue = new Long2ObjectOpenHashMap<>(12);
this.chunksToSend = new Long2ObjectOpenHashMap<>(12);
this.concurrentLightQueue = new ConcurrentHashMap<>(12);
this.maxY = queue.getMaxY();
}
@Override
public boolean isEmpty() {
return skyToRelight.isEmpty() && lightQueue.isEmpty() && extentdSkyToRelight.isEmpty() && concurrentLightQueue.isEmpty();
}
@Override
public synchronized void removeAndRelight(boolean sky) {
removeFirst = true;
fixLightingSafe(sky);
removeFirst = false;
}
/**
* Utility method to reduce duplicated code to ensure values are written to long[][][] without NPEs
*
* @param x x coordinate
* @param y y coordinate
* @param z z coordinate
* @param map long[][][] to add values to
*/
private void set(int x, int y, int z, long[][][] map) {
long[][] m1 = map[z];
if (m1 == null) {
m1 = map[z] = new long[16][];
}
long[] m2 = m1[x];
if (m2 == null) {
m2 = m1[x] = new long[4];
}
long value = m2[y >> 6] |= 1l << y;
}
public void addLightUpdate(int x, int y, int z) {
long index = MathMan.pairInt(x >> 4, z >> 4);
if (lightLock.compareAndSet(false, true)) {
synchronized (lightQueue) {
try {
long[][][] currentMap = lightQueue.get(index);
if (currentMap == null) {
currentMap = new long[16][][];
this.lightQueue.put(index, currentMap);
}
set(x & 15, y, z & 15, currentMap);
} finally {
lightLock.set(false);
}
}
} else {
long[][][] currentMap = concurrentLightQueue.get(index);
if (currentMap == null) {
currentMap = new long[16][][];
this.concurrentLightQueue.put(index, currentMap);
}
set(x & 15, y, z & 15, currentMap);
}
}
public synchronized void clear() {
extentdSkyToRelight.clear();
skyToRelight.clear();
chunksToSend.clear();
lightQueue.clear();
}
public boolean addChunk(int cx, int cz, byte[] fix, int bitmask) {
RelightSkyEntry toPut = new RelightSkyEntry(cx, cz, fix, bitmask);
extentdSkyToRelight.add(toPut);
return true;
}
private synchronized Map<Long, RelightSkyEntry> getSkyMap() {
RelightSkyEntry entry;
while ((entry = extentdSkyToRelight.poll()) != null) {
long pair = MathMan.pairInt(entry.x, entry.z);
RelightSkyEntry existing = skyToRelight.put(pair, entry);
if (existing != null) {
entry.bitmask |= existing.bitmask;
if (entry.fix != null) {
for (int i = 0; i < entry.fix.length; i++) {
entry.fix[i] &= existing.fix[i];
}
}
}
}
return skyToRelight;
}
public synchronized void removeLighting() {
Iterator<Map.Entry<Long, RelightSkyEntry>> iter = getSkyMap().entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, RelightSkyEntry> entry = iter.next();
RelightSkyEntry chunk = entry.getValue();
long pair = entry.getKey();
Integer existing = chunksToSend.get(pair);
chunksToSend.put(pair, chunk.bitmask | (existing != null ? existing : 0));
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z);
if (!iChunk.isInit()) {
iChunk.init(queue, chunk.x, chunk.z);
}
for (int i = 0; i < 16; i++) {
iChunk.removeSectionLighting(i, true);
}
iter.remove();
}
}
public void updateBlockLight(Map<Long, long[][][]> map) {
int size = map.size();
if (size == 0) {
return;
}
Queue<MutableBlockVector3> lightPropagationQueue = new ArrayDeque<>(32);
Queue<Object[]> lightRemovalQueue = new ArrayDeque<>(32);
Map<MutableBlockVector3, Object> visited = new HashMap<>(32);
Map<MutableBlockVector3, Object> removalVisited = new HashMap<>(32);
Iterator<Map.Entry<Long, long[][][]>> iter = map.entrySet().iterator();
while (iter.hasNext() && size-- > 0) {
Map.Entry<Long, long[][][]> entry = iter.next();
long index = entry.getKey();
long[][][] blocks = entry.getValue();
int chunkX = MathMan.unpairIntX(index);
int chunkZ = MathMan.unpairIntY(index);
int bx = chunkX << 4;
int bz = chunkZ << 4;
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunkX, chunkZ);
if (!iChunk.isInit()) {
iChunk.init(queue, chunkX, chunkZ);
}
for (int lz = 0; lz < blocks.length; lz++) {
long[][] m1 = blocks[lz];
if (m1 == null) continue;
for (int lx = 0; lx < m1.length; lx++) {
long[] m2 = m1[lx];
if (m2 == null) continue;
for (int i = 0; i < m2.length; i++) {
int yStart = i << 6;
long value = m2[i];
if (value != 0) {
for (int j = 0; j < 64; j++) {
if (((value >> j) & 1) == 1) {
int x = lx + bx;
int y = yStart + j;
int z = lz + bz;
int oldLevel = iChunk.getEmmittedLight(lx, y, lz);
int newLevel = iChunk.getBrightness(lx, y, lz);
if (oldLevel != newLevel) {
iChunk.setBlockLight(lx, y, lz, newLevel);
MutableBlockVector3 node = new MutableBlockVector3(x, y, z);
if (newLevel < oldLevel) {
removalVisited.put(node, present);
lightRemovalQueue.add(new Object[]{node, oldLevel});
} else {
visited.put(node, present);
lightPropagationQueue.add(node);
}
}
}
}
}
}
}
}
iter.remove();
}
while (!lightRemovalQueue.isEmpty()) {
Object[] val = lightRemovalQueue.poll();
MutableBlockVector3 node = (MutableBlockVector3) val[0];
int lightLevel = (int) val[1];
this.computeRemoveBlockLight(node.getX() - 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited);
this.computeRemoveBlockLight(node.getX() + 1, node.getY(), node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited);
if (node.getY() > 0) {
this.computeRemoveBlockLight(node.getX(), node.getY() - 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited);
}
if (node.getY() < 255) {
this.computeRemoveBlockLight(node.getX(), node.getY() + 1, node.getZ(), lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited);
}
this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() - 1, lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited);
this.computeRemoveBlockLight(node.getX(), node.getY(), node.getZ() + 1, lightLevel, lightRemovalQueue, lightPropagationQueue, removalVisited, visited);
}
while (!lightPropagationQueue.isEmpty()) {
MutableBlockVector3 node = lightPropagationQueue.poll();
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(node.getX() >> 4, node.getZ() >> 4);
if (!iChunk.isInit()) {
iChunk.init(queue, node.getX() >> 4, node.getZ() >> 4);
}
int lightLevel = iChunk.getEmmittedLight(node.getX() & 15, node.getY(), node.getZ() & 15);
if (lightLevel > 1) {
this.computeSpreadBlockLight(node.getX() - 1, node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited);
this.computeSpreadBlockLight(node.getX() + 1, node.getY(), node.getZ(), lightLevel, lightPropagationQueue, visited);
if (node.getY() > 0) {
this.computeSpreadBlockLight(node.getX(), node.getY() - 1, node.getZ(), lightLevel, lightPropagationQueue, visited);
}
if (node.getY() < 255) {
this.computeSpreadBlockLight(node.getX(), node.getY() + 1, node.getZ(), lightLevel, lightPropagationQueue, visited);
}
this.computeSpreadBlockLight(node.getX(), node.getY(), node.getZ() - 1, lightLevel, lightPropagationQueue, visited);
this.computeSpreadBlockLight(node.getX(), node.getY(), node.getZ() + 1, lightLevel, lightPropagationQueue, visited);
}
}
}
private void computeRemoveBlockLight(int x, int y, int z, int currentLight, Queue<Object[]> queue, Queue<MutableBlockVector3> spreadQueue, Map<MutableBlockVector3, Object> visited,
Map<MutableBlockVector3, Object> spreadVisited) {
ChunkHolder iChunk = (ChunkHolder) this.queue.getOrCreateChunk(x >> 4, z >> 4);
if (!iChunk.isInit()) {
iChunk.init(this.queue, x >> 4, z >> 4);
}
int current = iChunk.getEmmittedLight(x & 15, y, z & 15);
if (current != 0 && current < currentLight) {
iChunk.setBlockLight(x, y, z, 0);
if (current > 1) {
if (!visited.containsKey(mutableBlockPos)) {
MutableBlockVector3 index = new MutableBlockVector3(x, y, z);
visited.put(index, present);
queue.add(new Object[]{index, current});
}
}
} else if (current >= currentLight) {
mutableBlockPos.setComponents(x, y, z);
if (!spreadVisited.containsKey(mutableBlockPos)) {
MutableBlockVector3 index = new MutableBlockVector3(x, y, z);
spreadVisited.put(index, present);
spreadQueue.add(index);
}
}
}
private void computeSpreadBlockLight(int x, int y, int z, int currentLight, Queue<MutableBlockVector3> queue, Map<MutableBlockVector3, Object> visited) {
currentLight = currentLight - Math.max(1, this.queue.getOpacity(x, y, z));
if (currentLight > 0) {
ChunkHolder iChunk = (ChunkHolder) this.queue.getOrCreateChunk(x >> 4, z >> 4);
if (!iChunk.isInit()) {
iChunk.init(this.queue, x >> 4, z >> 4);
}
int current = iChunk.getEmmittedLight(x & 15, y, z & 15);
if (current < currentLight) {
iChunk.setBlockLight(x, y, z, currentLight);
mutableBlockPos.setComponents(x, y, z);
if (!visited.containsKey(mutableBlockPos)) {
visited.put(new MutableBlockVector3(x, y, z), present);
if (currentLight > 1) {
queue.add(new MutableBlockVector3(x, y, z));
}
}
}
}
}
public void fixLightingSafe(boolean sky) {
if (isEmpty()) return;
try {
if (sky) {
fixSkyLighting();
} else {
synchronized (this) {
Map<Long, RelightSkyEntry> map = getSkyMap();
Iterator<Map.Entry<Long, RelightSkyEntry>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, RelightSkyEntry> entry = iter.next();
chunksToSend.put(entry.getKey(), entry.getValue().bitmask);
iter.remove();
}
}
}
fixBlockLighting();
} catch (Throwable e) {
e.printStackTrace();
}
}
public void fixBlockLighting() {
synchronized (lightQueue) {
while (!lightLock.compareAndSet(false, true));
try {
updateBlockLight(this.lightQueue);
} finally {
lightLock.set(false);
}
}
}
public synchronized void sendChunks() {
Iterator<Map.Entry<Long, Integer>> iter = chunksToSend.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, Integer> entry = iter.next();
long pair = entry.getKey();
int bitMask = entry.getValue();
int x = MathMan.unpairIntX(pair);
int z = MathMan.unpairIntY(pair);
ChunkHolder chunk = (ChunkHolder) queue.getOrCreateChunk(x, z);
chunk.setBitMask(bitMask);
iter.remove();
}
if (Settings.IMP.LIGHTING.ASYNC) {
queue.flush();
} else {
TaskManager.IMP.sync(new RunnableVal<Object>() {
@Override public void run(Object value) {
queue.flush();
}
});
}
}
public synchronized void fixSkyLighting() {
// Order chunks
Map<Long, RelightSkyEntry> map = getSkyMap();
ArrayList<RelightSkyEntry> chunksList = new ArrayList<>(map.size());
Iterator<Map.Entry<Long, RelightSkyEntry>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, RelightSkyEntry> entry = iter.next();
chunksToSend.put(entry.getKey(), entry.getValue().bitmask);
chunksList.add(entry.getValue());
iter.remove();
}
Collections.sort(chunksList);
int size = chunksList.size();
if (size > DISPATCH_SIZE) {
int amount = (size + DISPATCH_SIZE - 1) / DISPATCH_SIZE;
for (int i = 0; i < amount; i++) {
int start = i * DISPATCH_SIZE;
int end = Math.min(size, start + DISPATCH_SIZE);
List<RelightSkyEntry> sub = chunksList.subList(start, end);
fixSkyLighting(sub);
}
} else {
fixSkyLighting(chunksList);
}
}
public void fill(byte[] mask, int chunkX, int y, int chunkZ, byte reason) {
if (y >= 16) {
Arrays.fill(mask, (byte) 15);
return;
}
switch (reason) {
case SkipReason.SOLID: {
Arrays.fill(mask, (byte) 0);
return;
}
case SkipReason.AIR: {
int bx = chunkX << 4;
int bz = chunkZ << 4;
int index = 0;
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
mask[index++] = (byte) queue.getSkyLight(bx + x, y, bz + z);
}
}
}
}
}
private void fixSkyLighting(List<RelightSkyEntry> sorted) {
RelightSkyEntry[] chunks = sorted.toArray(new RelightSkyEntry[sorted.size()]);
boolean remove = this.removeFirst;
BlockVectorSet chunkSet = null;
if (remove) {
chunkSet = new BlockVectorSet();
BlockVectorSet tmpSet = new BlockVectorSet();
for (RelightSkyEntry chunk : chunks) {
tmpSet.add(chunk.x, 0, chunk.z);
}
for (RelightSkyEntry chunk : chunks) {
int x = chunk.x;
int z = chunk.z;
if (tmpSet.contains(x + 1, 0, z) && tmpSet.contains(x - 1, 0, z) && tmpSet.contains(x, 0, z + 1) && tmpSet.contains(x, 0, z - 1)) {
chunkSet.add(x, 0, z);
}
}
}
for (int y = 255; y > 0; y--) {
for (RelightSkyEntry chunk : chunks) { // Propogate skylight
int layer = y >> 4;
byte[] mask = chunk.mask;
if ((y & 15) == 15 && chunk.fix[layer] != SkipReason.NONE) {
if ((y & 15) == 0 && layer != 0 && chunk.fix[layer - 1] == SkipReason.NONE) {
fill(mask, chunk.x, y, chunk.z, chunk.fix[layer]);
}
continue;
}
int bx = chunk.x << 4;
int bz = chunk.z << 4;
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z);
if (!iChunk.isInit()) {
iChunk.init(queue, chunk.x, chunk.z);
}
chunk.smooth = false;
if (remove && (y & 15) == 15 && chunkSet.contains(chunk.x, 0, chunk.z)) {
iChunk.removeSectionLighting(y >> 4, true);
}
for (int j = 0; j < 256; j++) {
int x = j & 15;
int z = j >> 4;
byte value = mask[j];
byte pair = MathMan.pair16(iChunk.getOpacity(x, y, z), iChunk.getBrightness(x, y, z));
int opacity = MathMan.unpair16x(pair);
int brightness = MathMan.unpair16y(pair);
if (brightness > 1) {
addLightUpdate(bx + x, y, bz + z);
}
switch (value) {
case 0:
if (opacity > 1) {
iChunk.setSkyLight(x, y, z, 0);
continue;
}
break;
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
if (opacity >= value) {
mask[j] = 0;
iChunk.setSkyLight(x, y, z, 0);
continue;
}
if (opacity <= 1) {
mask[j] = --value;
} else {
mask[j] = value = (byte) Math.max(0, value - opacity);
}
break;
case 15:
if (opacity > 1) {
value -= opacity;
mask[j] = value;
}
iChunk.setSkyLight(x, y, z, value);
continue;
}
chunk.smooth = true;
iChunk.setSkyLight(x, y, z, value);
}
}
for (RelightSkyEntry chunk : chunks) { // Smooth forwards
if (chunk.smooth) {
smoothSkyLight(chunk, y, true);
}
}
for (int i = chunks.length - 1; i >= 0; i--) { // Smooth backwards
RelightSkyEntry chunk = chunks[i];
if (chunk.smooth) {
smoothSkyLight(chunk, y, false);
}
}
}
}
public void smoothSkyLight(RelightSkyEntry chunk, int y, boolean direction) {
byte[] mask = chunk.mask;
ChunkHolder iChunk = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z);
ChunkHolder iChunkx;
ChunkHolder iChunkz;
if (!iChunk.isInit()) {
iChunk.init(queue, chunk.x, chunk.z);
}
if (direction) {
iChunkx = (ChunkHolder) queue.getOrCreateChunk(chunk.x - 1, chunk.z);
iChunkz = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z - 1);
if (!iChunkx.isInit()) {
iChunkx.init(queue, chunk.x - 1, chunk.z);
}
if (!iChunkz.isInit()) {
iChunkz.init(queue, chunk.x, chunk.z - 1);
}
for (int j = 0; j < 256; j++) {
int x = j & 15;
int z = j >> 4;
if (mask[j] >= 14 || (mask[j] == 0 && iChunk.getOpacity(x, y, z) > 1)) {
continue;
}
byte value = mask[j];
if (x != 0 && z != 0) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) ;
else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) ;
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value);
} else if (x == 0 && z == 0) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) ;
else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) ;
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value);
} else if (x == 0) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(15, y, z) - 1, value)) >= 14) ;
else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z - 1) - 1, value)) >= 14) ;
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value);
} else {
if ((value = (byte) Math.max(iChunk.getSkyLight(x - 1, y, z) - 1, value)) >= 14) ;
else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 15) - 1, value)) >= 14) ;
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value);
}
}
} else {
iChunkx = (ChunkHolder) queue.getOrCreateChunk(chunk.x + 1, chunk.z);
iChunkz = (ChunkHolder) queue.getOrCreateChunk(chunk.x, chunk.z + 1);
if (!iChunkx.isInit()) {
iChunkx.init(queue, chunk.x - 1, chunk.z);
}
if (!iChunkz.isInit()) {
iChunkz.init(queue, chunk.x, chunk.z - 1);
}
for (int j = 255; j >= 0; j--) {
int x = j & 15;
int z = j >> 4;
if (mask[j] >= 14 || (mask[j] == 0 && iChunk.getOpacity(x, y, z) > 1)) {
continue;
}
byte value = mask[j];
if ( x != 15 && z != 15) {
if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) ;
else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) ;
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value);
} else if (x == 15 && z == 15) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) ;
else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) ;
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value);
} else if (x == 15) {
if ((value = (byte) Math.max(iChunkx.getSkyLight(0, y, z) - 1, value)) >= 14) ;
else if ((value = (byte) Math.max(iChunk.getSkyLight(x, y, z + 1) - 1, value)) >= 14) ;
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value);
} else {
if ((value = (byte) Math.max(iChunk.getSkyLight(x + 1, y, z) - 1, value)) >= 14) ;
else if ((value = (byte) Math.max(iChunkz.getSkyLight(x, y, 0) - 1, value)) >= 14) ;
if (value > mask[j]) iChunk.setSkyLight(x, y, z, mask[j] = value);
}
}
}
}
private class RelightSkyEntry implements Comparable {
public final int x;
public final int z;
public final byte[] mask;
public final byte[] fix;
public int bitmask;
public boolean smooth;
public RelightSkyEntry(int x, int z, byte[] fix, int bitmask) {
this.x = x;
this.z = z;
byte[] array = new byte[256];
Arrays.fill(array, (byte) 15);
this.mask = array;
this.bitmask = bitmask;
if (fix == null) {
this.fix = new byte[(maxY + 1) >> 4];
Arrays.fill(this.fix, SkipReason.NONE);
} else {
this.fix = fix;
}
}
@Override
public String toString() {
return x + "," + z;
}
@Override
public int compareTo(Object o) {
RelightSkyEntry other = (RelightSkyEntry) o;
if (other.x < x) {
return 1;
}
if (other.x > x) {
return -1;
}
if (other.z < z) {
return 1;
}
if (other.z > z) {
return -1;
}
return 0;
}
}
}

View File

@ -0,0 +1,49 @@
package com.boydti.fawe.beta.implementation.lighting;
public class NullRelighter implements Relighter {
public static NullRelighter INSTANCE = new NullRelighter();
private NullRelighter() {
}
@Override
public boolean addChunk(int cx, int cz, byte[] fix, int bitmask) {
return false;
}
@Override
public void addLightUpdate(int x, int y, int z) {
}
@Override
public void fixLightingSafe(boolean sky) {
}
@Override
public void clear() {
}
@Override
public void removeLighting() {
}
@Override
public void fixBlockLighting() {
}
@Override
public void fixSkyLighting() {
}
@Override
public boolean isEmpty() {
return true;
}
}

View File

@ -0,0 +1,74 @@
package com.boydti.fawe.beta.implementation.lighting;
public interface Relighter {
/**
* Add a chunk to be relit when {@link Relighter#removeLighting} etc are called
*
* @param cx chunk x
* @param cz chunk z
* @param skipReason byte array of {@link SkipReason} for each chunksection in the chunk. Use case? No idea.
* @param bitmask Initial bitmask of the chunk (if being editited beforehand)
* @return Was the chunk added
*/
boolean addChunk(int cx, int cz, byte[] skipReason, int bitmask);
/**
* Add a block to be relit
*
* @param x block x
* @param y block y
* @param z block z
*/
void addLightUpdate(int x, int y, int z);
/**
* Safely? Fix block lighting
*
* @param sky whether to also relight sky light values
*/
void fixLightingSafe(boolean sky);
/**
* Remove lighting and then relight safely
*
* @param sky whether to also relight sky light values
*/
default void removeAndRelight(boolean sky) {
removeLighting();
fixLightingSafe(sky);
}
/**
* Clear all chunks and blocks to be relit
*/
void clear();
/**
* Remove all block and sky light values (set to 0 light) in all chunks added to relighter
*/
void removeLighting();
/**
* Fix block light values in all chunks added to relighter
*/
void fixBlockLighting();
/**
* Fix sky light values in all chunks added to relighter
*/
void fixSkyLighting();
/**
* Are there any block or chunk added to be relit
*
* @return is the relight stuff to be relit empty
*/
boolean isEmpty();
public static class SkipReason {
public static final byte NONE = 0;
public static final byte AIR = 1;
public static final byte SOLID = 2;
}
}

View File

@ -436,6 +436,24 @@ public class MCAChunk implements IChunk {
return true;
}
@Override public void setBlockLight(int x, int y, int z, int value) {
}
@Override public void setSkyLight(int x, int y, int z, int value) {
}
@Override public void removeSectionLighting(int layer, boolean sky) {}
@Override public void setFullBright(int layer) {}
@Override
public void setLightLayer(int layer, char[] toSet) {}
@Override
public void setSkyLightLayer(int layer, char[] toSet) {}
@Override
public void setEntity(CompoundTag entityTag) {
setModified();
@ -454,6 +472,14 @@ public class MCAChunk implements IChunk {
return this.biomes;
}
@Override public char[][] getLight() {
return new char[0][];
}
@Override public char[][] getSkyLight() {
return new char[0][];
}
@Override
public boolean setBiome(BlockVector2 pos, BiomeType biome) {
return this.setBiome(pos.getX(), 0, pos.getZ(), biome);
@ -499,6 +525,15 @@ public class MCAChunk implements IChunk {
return BlockState.getFromOrdinal(ordinal);
}
//TODO implement lighting
@Override public int getSkyLight(int x, int y, int z) {
return 0;
}
@Override public int getEmmittedLight(int x, int y, int z) {
return 0;
}
@Override
public BaseBlock getFullBlock(int x, int y, int z) {
BlockState block = getBlock(x, y, z);

View File

@ -0,0 +1,7 @@
package com.boydti.fawe.object;
public enum RelightMode {
NONE, // no relighting
OPTIMAL, // relight changed light sources and changed blocks
ALL // relight every single block
}

View File

@ -1,27 +1,26 @@
package com.boydti.fawe.util;
import static com.google.common.base.Preconditions.checkNotNull;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IQueueChunk;
import com.boydti.fawe.beta.IQueueExtent;
import com.boydti.fawe.beta.implementation.lighting.NMSRelighter;
import com.boydti.fawe.beta.implementation.lighting.NullRelighter;
import com.boydti.fawe.beta.implementation.lighting.Relighter;
import com.boydti.fawe.beta.implementation.processors.LimitProcessor;
import com.boydti.fawe.beta.implementation.queue.ParallelQueueExtent;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.logging.rollback.RollbackOptimizedHistory;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.HistoryExtent;
import com.boydti.fawe.object.NullChangeSet;
import com.boydti.fawe.object.RegionWrapper;
import com.boydti.fawe.object.RelightMode;
import com.boydti.fawe.object.brush.visualization.VirtualWorld;
import com.boydti.fawe.object.changeset.AbstractChangeSet;
import com.boydti.fawe.object.changeset.BlockBagChangeSet;
import com.boydti.fawe.object.changeset.DiskStorageHistory;
import com.boydti.fawe.object.changeset.AbstractChangeSet;
import com.boydti.fawe.object.changeset.MemoryOptimizedHistory;
import com.boydti.fawe.object.extent.FaweRegionExtent;
import com.boydti.fawe.object.extent.MultiRegionExtent;
@ -37,12 +36,17 @@ import com.sk89q.worldedit.event.extent.EditSessionEvent;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.extent.inventory.BlockBag;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.util.Identifiable;
import com.sk89q.worldedit.util.eventbus.EventBus;
import com.sk89q.worldedit.util.formatting.text.TranslatableComponent;
import com.sk89q.worldedit.world.World;
import java.util.UUID;
import javax.annotation.Nullable;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkNotNull;
public class EditSessionBuilder {
@NotNull
private World world;
@ -60,6 +64,7 @@ public class EditSessionBuilder {
private boolean threaded = true;
private EditSessionEvent event;
private String command;
private RelightMode relightMode;
/**
* An EditSession builder<br>
@ -80,7 +85,7 @@ public class EditSessionBuilder {
checkNotNull(world);
this.world = world;
}
public EditSessionBuilder player(@Nullable Player player) {
this.player = player;
return setDirty();
@ -172,6 +177,11 @@ public class EditSessionBuilder {
return setDirty();
}
public EditSessionBuilder relightMode(@Nullable RelightMode relightMode) {
this.relightMode = relightMode;
return setDirty();
}
public EditSessionBuilder checkMemory(@Nullable Boolean checkMemory) {
this.checkMemory = checkMemory;
return setDirty();
@ -402,7 +412,7 @@ public class EditSessionBuilder {
public World getWorld() {
return world;
}
public Extent getExtent() {
return extent != null ? extent : world;
}
@ -410,7 +420,7 @@ public class EditSessionBuilder {
public boolean isWrapped() {
return wrapped;
}
public Extent getBypassHistory() {
return bypassHistory;
}

View File

@ -27,9 +27,11 @@ import static com.sk89q.worldedit.regions.Regions.asFlatRegion;
import static com.sk89q.worldedit.regions.Regions.maximumBlockY;
import static com.sk89q.worldedit.regions.Regions.minimumBlockY;
import com.boydti.fawe.FaweAPI;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.config.Caption;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.RelightMode;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession;
@ -139,16 +141,15 @@ public class RegionCommands {
)
@CommandPermissions("worldedit.light.fix")
public void fixLighting(Player player) throws WorldEditException {
player.print(TextComponent.of("Temporarily not working"));
// final Location loc = player.getLocation();
// Region selection = player.getSelection();
// if (selection == null) {
// final int cx = loc.getBlockX() >> 4;
// final int cz = loc.getBlockZ() >> 4;
// selection = new CuboidRegion(BlockVector3.at(cx - 8, 0, cz - 8).multiply(16), BlockVector3.at(cx + 8, 0, cz + 8).multiply(16));
// }
// int count = FaweAPI.fixLighting(player.getWorld(), selection,null);
// player.print(Caption.of("fawe.info.lighting.propagate.selection" , count));
final Location loc = player.getLocation();
Region selection = player.getSelection();
if (selection == null) {
final int cx = loc.getBlockX() >> 4;
final int cz = loc.getBlockZ() >> 4;
selection = new CuboidRegion(BlockVector3.at(cx - 8, 0, cz - 8).multiply(16), BlockVector3.at(cx + 8, 0, cz + 8).multiply(16));
}
int count = FaweAPI.fixLighting(player.getWorld(), selection,null, RelightMode.ALL);
player.print(Caption.of("fawe.info.lighting.propagate.selection" , count));
}
// @Command(
@ -163,22 +164,21 @@ public class RegionCommands {
// player.print(TextComponent.of("Light: " + block + " | " + sky));
// }
// @Command(
// name = "/removelighting",
// desc = "Removing lighting in a selection"
// )
// @CommandPermissions("worldedit.light.remove")
// public void removeLighting(Player player) {
// player.print(TextComponent.of("Temporarily not working"));
// Region selection = player.getSelection();
// if (selection == null) {
// final int cx = player.getLocation().getBlockX() >> 4;
// final int cz = player.getLocation().getBlockZ() >> 4;
// selection = new CuboidRegion(BlockVector3.at(cx - 8, 0, cz - 8).multiply(16), BlockVector3.at(cx + 8, 0, cz + 8).multiply(16));
// }
// int count = FaweAPI.fixLighting(player.getWorld(), selection, null);
// player.print(Caption.of("fawe.info.updated.lighting.selection" , count));
// }
@Command(
name = "/removelighting",
desc = "Removing lighting in a selection"
)
@CommandPermissions("worldedit.light.remove")
public void removeLighting(Player player) {
Region selection = player.getSelection();
if (selection == null) {
final int cx = player.getLocation().getBlockX() >> 4;
final int cz = player.getLocation().getBlockZ() >> 4;
selection = new CuboidRegion(BlockVector3.at(cx - 8, 0, cz - 8).multiply(16), BlockVector3.at(cx + 8, 0, cz + 8).multiply(16));
}
int count = FaweAPI.fixLighting(player.getWorld(), selection, null, RelightMode.NONE);
player.print(Caption.of("fawe.info.updated.lighting.selection" , count));
}
@Command(
name = "/nbtinfo",

View File

@ -205,6 +205,46 @@ public class AbstractDelegateExtent implements Extent {
public boolean setBiome(BlockVector2 position, BiomeType biome) {
return extent.setBiome(position.getX(), 0, position.getZ(), biome);
}
@Override
public boolean relight(int x, int y, int z) {
return extent.relight(x, y, z);
}
@Override
public boolean relightBlock(int x, int y, int z) {
return extent.relightBlock(x, y, z);
}
@Override
public boolean relightSky(int x, int y, int z) {
return extent.relightSky(x, y, z);
}
@Override
public void setSkyLight(int x, int y, int z, int value) {
extent.setSkyLight(x, y, z, value);
}
@Override
public void setBlockLight(int x, int y, int z, int value) {
extent.setSkyLight(x, y, z, value);
}
@Override
public int getSkyLight(int x, int y, int z) {
return extent.getSkyLight(x, y, z);
}
@Override
public int getEmmittedLight(int x, int y, int z) {
return extent.getEmmittedLight(x, y, z);
}
@Override
public int getBrightness(int x, int y, int z) {
return extent.getBrightness(x, y, z);
}
@Override
public String toString() {

View File

@ -651,6 +651,18 @@ public interface Extent extends InputExtent, OutputExtent {
return count;
}
default boolean relight(int x, int y, int z) {
return false;
}
default boolean relightBlock(int x, int y, int z) {
return false;
}
default boolean relightSky(int x, int y, int z) {
return false;
}
/**
* Have an extent processed
* - Either block (Extent) processing or chunk processing

View File

@ -85,4 +85,48 @@ public interface InputExtent {
default BiomeType getBiomeType(int x, int y, int z) {
return getBiome(MutableBlockVector2.get(x, z));
}
/**
* Get the light level at the given location
*
* @param position location
* @return the light level at the location
*/
default int getEmmittedLight(MutableBlockVector3 position) {
return getEmmittedLight(position.getX(), position.getY(), position.getZ());
}
default int getEmmittedLight(int x, int y, int z) {
return 0;
}
/**
* Get the sky light level at the given location
*
* @param position location
* @return the sky light level at the location
*/
default int getSkyLight(MutableBlockVector3 position) {
return getSkyLight(position.getX(), position.getY(), position.getZ());
}
default int getSkyLight(int x, int y, int z) {
return 0;
}
default int getBrightness(MutableBlockVector3 position) {
return getBrightness(position.getX(), position.getY(), position.getZ());
}
default int getBrightness(int x, int y, int z) {
return getFullBlock(x, y, z).getMaterial().getLightValue();
}
default int getOpacity(MutableBlockVector3 position) {
return getOpacity(position.getX(), position.getY(), position.getZ());
}
default int getOpacity(int x, int y, int z) {
return getFullBlock(x, y, z).getMaterial().getLightOpacity();
}
}

View File

@ -82,6 +82,30 @@ public interface OutputExtent {
return setBiome(MutableBlockVector2.get(x, z), biome);
}
/**
* Set the light value
*
* @param position position of the block
* @param value light level to set
*/
default void setBlockLight(BlockVector3 position, int value) {
setBlockLight(position.getX(), position.getY(), position.getZ(), value);
}
default void setBlockLight(int x, int y, int z, int value) {}
/**
* Set the sky light value
*
* @param position position of the block
* @param value light level to set
*/
default void setSkyLight(BlockVector3 position, int value) {
setSkyLight(position.getX(), position.getY(), position.getZ(), value);
}
default void setSkyLight(int x, int y, int z, int value) {}
/**
* Return an {@link Operation} that should be called to tie up loose ends
* (such as to commit changes in a buffer).