Looks like automagical relighting (#838)

Fixes #686
Dieser Commit ist enthalten in:
dordsor21 2021-01-11 19:29:16 +00:00 committet von GitHub
Ursprung 74a2f02003
Commit b4d7562b87
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
46 geänderte Dateien mit 932 neuen und 142 gelöschten Zeilen

Datei anzeigen

@ -1,11 +1,13 @@
package com.boydti.fawe.bukkit;
import com.boydti.fawe.FAWEPlatformAdapterImpl;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.IFawe;
import com.boydti.fawe.beta.implementation.cache.preloader.AsyncPreloader;
import com.boydti.fawe.beta.implementation.cache.preloader.Preloader;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.BukkitQueueHandler;
import com.boydti.fawe.bukkit.adapter.NMSAdapter;
import com.boydti.fawe.bukkit.listener.BrushListener;
import com.boydti.fawe.bukkit.listener.BukkitImageListener;
import com.boydti.fawe.bukkit.listener.CFIPacketListener;
@ -58,6 +60,7 @@ public class FaweBukkit implements IFawe, Listener {
private BukkitImageListener imageListener;
private CFIPacketListener packetListener;
private final boolean chunksStretched;
private final FAWEPlatformAdapterImpl platformAdapter;
public FaweBukkit(Plugin plugin) {
this.plugin = plugin;
@ -81,6 +84,8 @@ public class FaweBukkit implements IFawe, Listener {
chunksStretched =
Integer.parseInt(Bukkit.getBukkitVersion().split("-")[0].split("\\.")[1]) >= 16;
platformAdapter = new NMSAdapter();
//PlotSquared support is limited to Spigot/Paper as of 02/20/2020
TaskManager.IMP.later(this::setupPlotSquared, 0);
@ -294,6 +299,11 @@ public class FaweBukkit implements IFawe, Listener {
return chunksStretched;
}
@Override
public FAWEPlatformAdapterImpl getPlatformAdapter() {
return platformAdapter;
}
private void setupPlotSquared() {
Plugin plotSquared = this.plugin.getServer().getPluginManager().getPlugin("PlotSquared");
if (plotSquared == null) {

Datei anzeigen

@ -0,0 +1,7 @@
package com.boydti.fawe.bukkit.adapter;
public interface BukkitGetBlocks {
void send(int mask, boolean lighting);
}

Datei anzeigen

@ -1,5 +1,8 @@
package com.boydti.fawe.bukkit.adapter;
import com.boydti.fawe.FAWEPlatformAdapterImpl;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.implementation.chunk.ChunkHolder;
import com.boydti.fawe.config.Settings;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.math.BlockVector3;
@ -10,7 +13,7 @@ import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.util.Map;
import java.util.function.Function;
public class NMSAdapter {
public class NMSAdapter implements FAWEPlatformAdapterImpl {
public static int createPalette(int[] blockToPalette, int[] paletteToBlock, int[] blocksCopy,
int[] num_palette_buffer, char[] set, Map<BlockVector3, Integer> ticking_blocks, boolean fastmode) {
int air = 0;
@ -182,4 +185,12 @@ public class NMSAdapter {
num_palette_buffer[0] = num_palette;
return air;
}
@Override
public void sendChunk(IChunkGet chunk, int mask, boolean lighting) {
if (!(chunk instanceof BukkitGetBlocks)) {
throw new IllegalArgumentException("(IChunkGet) chunk not of type BukkitGetBlocks");
}
((BukkitGetBlocks) chunk).send(mask, lighting);
}
}

Datei anzeigen

@ -7,6 +7,7 @@ import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.lighting.HeightMapType;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.BukkitGetBlocks;
import com.boydti.fawe.bukkit.adapter.DelegateLock;
import com.boydti.fawe.bukkit.adapter.mc1_15_2.nbt.LazyCompoundTag_1_15_2;
import com.boydti.fawe.config.Settings;
@ -74,7 +75,7 @@ import javax.annotation.Nullable;
import static org.slf4j.LoggerFactory.getLogger;
public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
public class BukkitGetBlocks_1_15_2 extends CharGetBlocks implements BukkitGetBlocks {
private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_15_2.class);
@ -90,6 +91,7 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
private boolean createCopy = false;
private BukkitGetBlocks_1_15_2_Copy copy = null;
private boolean forceLoadSections = true;
private boolean lightUpdate = false;
public BukkitGetBlocks_1_15_2(World world, int chunkX, int chunkZ) {
this(((CraftWorld) world).getHandle(), chunkX, chunkZ);
@ -120,6 +122,37 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
return copy;
}
@Override
public void setLightingToGet(char[][] light) {
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void setSkyLightingToGet(char[][] light) {
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
BitArray bitArray = new BitArray(9, 256);
bitArray.fromRaw(data);
nmsChunk.heightMap.get(HeightMap.Type.valueOf(type.name())).a(bitArray.getData());
}
public int getChunkZ() {
return chunkZ;
}
@ -141,6 +174,34 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {
SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer);
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition);
if (nibble != null) {
lightUpdate = true;
synchronized (nibble) {
byte[] bytes = nibble.getCloneIfSet();
if (bytes != NibbleArray.EMPTY_NIBBLE) {
Arrays.fill(bytes, (byte) 0);
}
}
}
if (sky) {
SectionPosition sectionPositionSky = SectionPosition.a(nmsChunk.getPos(), layer);
NibbleArray nibbleSky = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPositionSky);
if (nibble != null) {
lightUpdate = true;
synchronized (nibbleSky) {
byte[] bytes = nibbleSky.getCloneIfSet();
if (bytes != NibbleArray.EMPTY_NIBBLE) {
Arrays.fill(bytes, (byte) 0);
}
}
}
}
}
@Override
public CompoundTag getTile(int x, int y, int z) {
TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + (
@ -436,33 +497,10 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
Map<HeightMapType, int[]> heightMaps = set.getHeightMaps();
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
BitArray bitArray = new BitArray(9, 256);
bitArray.fromRaw(entry.getValue());
nmsChunk.heightMap.get(HeightMap.Type.valueOf(entry.getKey().name())).a(bitArray.getData());
}
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();
}
BukkitGetBlocks_1_15_2.this.setHeightmapToGet(entry.getKey(), entry.getValue());
}
BukkitGetBlocks_1_15_2.this.setLightingToGet(set.getLight());
BukkitGetBlocks_1_15_2.this.setSkyLightingToGet(set.getSkyLight());
Runnable[] syncTasks = null;
@ -584,7 +622,9 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_15_2.sendChunk(nmsWorld, chunkX, chunkZ, finalMask, finalLightUpdate);
if (Settings.IMP.LIGHTING.MODE == 0 || !Settings.IMP.LIGHTING.DELAY_PACKET_SENDING) {
this.send(finalMask, finalLightUpdate);
}
if (finalizer != null) {
finalizer.run();
}
@ -636,6 +676,11 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
}
}
@Override
public synchronized void send(int mask, boolean lighting) {
BukkitAdapter_1_15_2.sendChunk(world, chunkX, chunkZ, mask, lighting);
}
@Override
public synchronized char[] update(int layer, char[] data) {
ChunkSection section = getSections(true)[layer];

Datei anzeigen

@ -104,6 +104,15 @@ public class BukkitGetBlocks_1_15_2_Copy implements IChunkGet {
return false;
}
@Override
public void setLightingToGet(char[][] lighting) {}
@Override
public void setSkyLightingToGet(char[][] lighting) {}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {}
protected void storeBiomes(BiomeStorage biomeStorage) {
this.biomeStorage = new BiomeStorage(BukkitAdapter_1_15_2.getBiomeArray(biomeStorage).clone());
}
@ -122,6 +131,9 @@ public class BukkitGetBlocks_1_15_2_Copy implements IChunkGet {
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {}
@Override
public boolean trim(boolean aggressive, int layer) {
return false;

Datei anzeigen

@ -7,6 +7,7 @@ import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.lighting.HeightMapType;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.BukkitGetBlocks;
import com.boydti.fawe.bukkit.adapter.DelegateLock;
import com.boydti.fawe.bukkit.adapter.mc1_16_1.nbt.LazyCompoundTag_1_16_1;
import com.boydti.fawe.config.Settings;
@ -74,7 +75,7 @@ import javax.annotation.Nullable;
import static org.slf4j.LoggerFactory.getLogger;
public class BukkitGetBlocks_1_16_1 extends CharGetBlocks {
public class BukkitGetBlocks_1_16_1 extends CharGetBlocks implements BukkitGetBlocks {
private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_16_1.class);
@ -90,6 +91,7 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks {
private boolean createCopy = false;
private BukkitGetBlocks_1_16_1_Copy copy = null;
private boolean forceLoadSections = true;
private boolean lightUpdate = false;
public BukkitGetBlocks_1_16_1(World world, int chunkX, int chunkZ) {
this(((CraftWorld) world).getHandle(), chunkX, chunkZ);
@ -121,6 +123,37 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks {
return copy;
}
@Override
public void setLightingToGet(char[][] light) {
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void setSkyLightingToGet(char[][] light) {
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256);
bitArray.fromRaw(data);
nmsChunk.heightMap.get(HeightMap.Type.valueOf(type.name())).a(bitArray.getData());
}
public int getChunkZ() {
return chunkZ;
}
@ -142,6 +175,34 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks {
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {
SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer);
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition);
if (nibble != null) {
lightUpdate = true;
synchronized (nibble) {
byte[] bytes = nibble.getCloneIfSet();
if (bytes != NibbleArray.EMPTY_NIBBLE) {
Arrays.fill(bytes, (byte) 0);
}
}
}
if (sky) {
SectionPosition sectionPositionSky = SectionPosition.a(nmsChunk.getPos(), layer);
NibbleArray nibbleSky = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPositionSky);
if (nibble != null) {
lightUpdate = true;
synchronized (nibbleSky) {
byte[] bytes = nibbleSky.getCloneIfSet();
if (bytes != NibbleArray.EMPTY_NIBBLE) {
Arrays.fill(bytes, (byte) 0);
}
}
}
}
}
@Override
public CompoundTag getTile(int x, int y, int z) {
TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + (
@ -438,33 +499,10 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks {
Map<HeightMapType, int[]> heightMaps = set.getHeightMaps();
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256);
bitArray.fromRaw(entry.getValue());
nmsChunk.heightMap.get(HeightMap.Type.valueOf(entry.getKey().name())).a(bitArray.getData());
}
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();
}
BukkitGetBlocks_1_16_1.this.setHeightmapToGet(entry.getKey(), entry.getValue());
}
BukkitGetBlocks_1_16_1.this.setLightingToGet(set.getLight());
BukkitGetBlocks_1_16_1.this.setSkyLightingToGet(set.getSkyLight());
Runnable[] syncTasks = null;
@ -586,7 +624,9 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks {
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_16_1.sendChunk(nmsWorld, chunkX, chunkZ, finalMask, finalLightUpdate);
if (Settings.IMP.LIGHTING.MODE == 0 || !Settings.IMP.LIGHTING.DELAY_PACKET_SENDING) {
this.send(finalMask, finalLightUpdate);
}
if (finalizer != null) {
finalizer.run();
}
@ -638,6 +678,11 @@ public class BukkitGetBlocks_1_16_1 extends CharGetBlocks {
}
}
@Override
public synchronized void send(int mask, boolean lighting) {
BukkitAdapter_1_16_1.sendChunk(world, chunkX, chunkZ, mask, lighting);
}
@Override
public synchronized char[] update(int layer, char[] data) {
ChunkSection section = getSections(true)[layer];

Datei anzeigen

@ -104,6 +104,15 @@ public class BukkitGetBlocks_1_16_1_Copy implements IChunkGet {
return false;
}
@Override
public void setLightingToGet(char[][] lighting) {}
@Override
public void setSkyLightingToGet(char[][] lighting) {}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {}
protected void storeBiomes(BiomeStorage biomeStorage) {
this.biomeStorage = new BiomeStorage(BukkitAdapter_1_16_1.getBiomeArray(biomeStorage).clone());
}
@ -122,6 +131,9 @@ public class BukkitGetBlocks_1_16_1_Copy implements IChunkGet {
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(base)) : null;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {}
@Override
public boolean trim(boolean aggressive, int layer) {
return false;

Datei anzeigen

@ -7,6 +7,7 @@ import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.lighting.HeightMapType;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.BukkitGetBlocks;
import com.boydti.fawe.bukkit.adapter.DelegateLock;
import com.boydti.fawe.bukkit.adapter.mc1_16_2.nbt.LazyCompoundTag_1_16_2;
import com.boydti.fawe.config.Settings;
@ -75,7 +76,7 @@ import java.util.function.Function;
import static org.slf4j.LoggerFactory.getLogger;
public class BukkitGetBlocks_1_16_2 extends CharGetBlocks {
public class BukkitGetBlocks_1_16_2 extends CharGetBlocks implements BukkitGetBlocks {
private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_16_2.class);
@ -91,6 +92,7 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks {
private boolean createCopy = false;
private BukkitGetBlocks_1_16_2_Copy copy = null;
private boolean forceLoadSections = true;
private boolean lightUpdate = false;
public BukkitGetBlocks_1_16_2(World world, int chunkX, int chunkZ) {
this(((CraftWorld) world).getHandle(), chunkX, chunkZ);
@ -121,6 +123,37 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks {
return copy;
}
@Override
public void setLightingToGet(char[][] light) {
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void setSkyLightingToGet(char[][] light) {
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256);
bitArray.fromRaw(data);
nmsChunk.heightMap.get(HeightMap.Type.valueOf(type.name())).a(bitArray.getData());
}
public int getChunkZ() {
return chunkZ;
}
@ -142,6 +175,34 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks {
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {
SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer);
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition);
if (nibble != null) {
lightUpdate = true;
synchronized (nibble) {
byte[] bytes = nibble.getCloneIfSet();
if (bytes != NibbleArray.EMPTY_NIBBLE) {
Arrays.fill(bytes, (byte) 0);
}
}
}
if (sky) {
SectionPosition sectionPositionSky = SectionPosition.a(nmsChunk.getPos(), layer);
NibbleArray nibbleSky = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPositionSky);
if (nibble != null) {
lightUpdate = true;
synchronized (nibbleSky) {
byte[] bytes = nibbleSky.getCloneIfSet();
if (bytes != NibbleArray.EMPTY_NIBBLE) {
Arrays.fill(bytes, (byte) 0);
}
}
}
}
}
@Override
public CompoundTag getTile(int x, int y, int z) {
TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + (
@ -441,33 +502,10 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks {
Map<HeightMapType, int[]> heightMaps = set.getHeightMaps();
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256);
bitArray.fromRaw(entry.getValue());
nmsChunk.heightMap.get(HeightMap.Type.valueOf(entry.getKey().name())).a(bitArray.getData());
}
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();
}
BukkitGetBlocks_1_16_2.this.setHeightmapToGet(entry.getKey(), entry.getValue());
}
BukkitGetBlocks_1_16_2.this.setLightingToGet(set.getLight());
BukkitGetBlocks_1_16_2.this.setSkyLightingToGet(set.getSkyLight());
Runnable[] syncTasks = null;
@ -589,7 +627,9 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks {
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_16_2.sendChunk(nmsWorld, chunkX, chunkZ, finalMask, finalLightUpdate);
if (Settings.IMP.LIGHTING.MODE == 0 || !Settings.IMP.LIGHTING.DELAY_PACKET_SENDING) {
this.send(finalMask, finalLightUpdate);
}
if (finalizer != null) {
finalizer.run();
}
@ -641,6 +681,11 @@ public class BukkitGetBlocks_1_16_2 extends CharGetBlocks {
}
}
@Override
public synchronized void send(int mask, boolean lighting) {
BukkitAdapter_1_16_2.sendChunk(world, chunkX, chunkZ, mask, lighting);
}
@Override
public synchronized char[] update(int layer, char[] data) {
ChunkSection section = getSections(true)[layer];

Datei anzeigen

@ -105,6 +105,15 @@ public class BukkitGetBlocks_1_16_2_Copy implements IChunkGet {
return false;
}
@Override
public void setLightingToGet(char[][] lighting) {}
@Override
public void setSkyLightingToGet(char[][] lighting) {}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {}
protected void storeBiomes(BiomeStorage biomeStorage) {
this.biomeStorage = new BiomeStorage(biomeStorage.g, BukkitAdapter_1_16_2.getBiomeArray(biomeStorage).clone());
}
@ -123,6 +132,9 @@ public class BukkitGetBlocks_1_16_2_Copy implements IChunkGet {
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {}
@Override
public boolean trim(boolean aggressive, int layer) {
return false;

Datei anzeigen

@ -7,6 +7,7 @@ import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.lighting.HeightMapType;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.BukkitGetBlocks;
import com.boydti.fawe.bukkit.adapter.DelegateLock;
import com.boydti.fawe.bukkit.adapter.mc1_16_4.nbt.LazyCompoundTag_1_16_4;
import com.boydti.fawe.config.Settings;
@ -75,7 +76,7 @@ import java.util.function.Function;
import static org.slf4j.LoggerFactory.getLogger;
public class BukkitGetBlocks_1_16_4 extends CharGetBlocks {
public class BukkitGetBlocks_1_16_4 extends CharGetBlocks implements BukkitGetBlocks {
private static final Logger log = LoggerFactory.getLogger(BukkitGetBlocks_1_16_4.class);
@ -91,6 +92,7 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks {
private boolean createCopy = false;
private BukkitGetBlocks_1_16_4_Copy copy = null;
private boolean forceLoadSections = true;
private boolean lightUpdate = false;
public BukkitGetBlocks_1_16_4(World world, int chunkX, int chunkZ) {
this(((CraftWorld) world).getHandle(), chunkX, chunkZ);
@ -121,6 +123,37 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks {
return copy;
}
@Override
public void setLightingToGet(char[][] light) {
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.BLOCK);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void setSkyLightingToGet(char[][] light) {
if (light != null) {
lightUpdate = true;
try {
fillLightNibble(light, EnumSkyBlock.SKY);
} catch (Throwable e) {
e.printStackTrace();
}
}
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256);
bitArray.fromRaw(data);
nmsChunk.heightMap.get(HeightMap.Type.valueOf(type.name())).a(bitArray.getData());
}
public int getChunkZ() {
return chunkZ;
}
@ -142,6 +175,34 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks {
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {
SectionPosition sectionPosition = SectionPosition.a(nmsChunk.getPos(), layer);
NibbleArray nibble = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.BLOCK).a(sectionPosition);
if (nibble != null) {
lightUpdate = true;
synchronized (nibble) {
byte[] bytes = nibble.getCloneIfSet();
if (bytes != NibbleArray.EMPTY_NIBBLE) {
Arrays.fill(bytes, (byte) 0);
}
}
}
if (sky) {
SectionPosition sectionPositionSky = SectionPosition.a(nmsChunk.getPos(), layer);
NibbleArray nibbleSky = world.getChunkProvider().getLightEngine().a(EnumSkyBlock.SKY).a(sectionPositionSky);
if (nibble != null) {
lightUpdate = true;
synchronized (nibbleSky) {
byte[] bytes = nibbleSky.getCloneIfSet();
if (bytes != NibbleArray.EMPTY_NIBBLE) {
Arrays.fill(bytes, (byte) 0);
}
}
}
}
}
@Override
public CompoundTag getTile(int x, int y, int z) {
TileEntity tileEntity = getChunk().getTileEntity(new BlockPosition((x & 15) + (
@ -441,33 +502,10 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks {
Map<HeightMapType, int[]> heightMaps = set.getHeightMaps();
for (Map.Entry<HeightMapType, int[]> entry : heightMaps.entrySet()) {
BitArrayUnstretched bitArray = new BitArrayUnstretched(9, 256);
bitArray.fromRaw(entry.getValue());
nmsChunk.heightMap.get(HeightMap.Type.valueOf(entry.getKey().name())).a(bitArray.getData());
}
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();
}
BukkitGetBlocks_1_16_4.this.setHeightmapToGet(entry.getKey(), entry.getValue());
}
BukkitGetBlocks_1_16_4.this.setLightingToGet(set.getLight());
BukkitGetBlocks_1_16_4.this.setSkyLightingToGet(set.getSkyLight());
Runnable[] syncTasks = null;
@ -589,7 +627,9 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks {
nmsChunk.mustNotSave = false;
nmsChunk.markDirty();
// send to player
BukkitAdapter_1_16_4.sendChunk(nmsWorld, chunkX, chunkZ, finalMask, finalLightUpdate);
if (Settings.IMP.LIGHTING.MODE == 0 || !Settings.IMP.LIGHTING.DELAY_PACKET_SENDING) {
this.send(finalMask, finalLightUpdate);
}
if (finalizer != null) {
finalizer.run();
}
@ -641,6 +681,11 @@ public class BukkitGetBlocks_1_16_4 extends CharGetBlocks {
}
}
@Override
public synchronized void send(int mask, boolean lighting) {
BukkitAdapter_1_16_4.sendChunk(world, chunkX, chunkZ, mask, lighting);
}
@Override
public synchronized char[] update(int layer, char[] data) {
ChunkSection section = getSections(true)[layer];

Datei anzeigen

@ -105,6 +105,15 @@ public class BukkitGetBlocks_1_16_4_Copy implements IChunkGet {
return false;
}
@Override
public void setLightingToGet(char[][] lighting) {}
@Override
public void setSkyLightingToGet(char[][] lighting) {}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {}
protected void storeBiomes(BiomeStorage biomeStorage) {
this.biomeStorage = new BiomeStorage(biomeStorage.g, BukkitAdapter_1_16_4.getBiomeArray(biomeStorage).clone());
}
@ -123,6 +132,9 @@ public class BukkitGetBlocks_1_16_4_Copy implements IChunkGet {
return base != null ? BukkitAdapter.adapt(CraftBlock.biomeBaseToBiome(world.r().b(IRegistry.ay), base)) : null;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {}
@Override
public boolean trim(boolean aggressive, int layer) {
return false;

Datei anzeigen

@ -0,0 +1,9 @@
package com.boydti.fawe;
import com.boydti.fawe.beta.IChunkGet;
public interface FAWEPlatformAdapterImpl {
void sendChunk(IChunkGet chunk, int mask, boolean lighting);
}

Datei anzeigen

@ -361,7 +361,7 @@ public class FaweAPI {
} else {
relighter.removeLighting();
}
relighter.sendChunks();
relighter.flush();
return count;
}

Datei anzeigen

@ -43,4 +43,6 @@ public interface IFawe {
return true;
}
FAWEPlatformAdapterImpl getPlatformAdapter();
}

Datei anzeigen

@ -39,6 +39,12 @@ public class CombinedBlocks implements IBlocks {
return bitMask;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {
primary.removeSectionLighting(layer, sky);
secondary.removeSectionLighting(layer, sky);
}
@Override
public boolean hasSection(int layer) {
return primary.hasSection(layer) || secondary.hasSection(layer);

Datei anzeigen

@ -3,6 +3,7 @@ package com.boydti.fawe.beta;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor;
import com.boydti.fawe.beta.implementation.processors.MultiBatchProcessor;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.extent.Extent;
import com.sk89q.worldedit.math.BlockVector3;
@ -126,4 +127,11 @@ public interface IBatchProcessor {
}
return this;
}
/**
* Default to CUSTOM ProcessorScope as we want custom processors people add to be before we write history, but after FAWE does it's own stuff.
*/
default ProcessorScope getScope() {
return ProcessorScope.CUSTOM;
}
}

Datei anzeigen

@ -42,6 +42,8 @@ public interface IBlocks extends Trimable {
.map(layer -> (1 << layer)).sum();
}
void removeSectionLighting(int layer, boolean sky);
boolean trim(boolean aggressive, int layer);
IBlocks reset();

Datei anzeigen

@ -56,4 +56,10 @@ public interface IChunkGet extends IBlocks, Trimable, InputExtent, ITileInput {
default IChunkGet getCopy() {
return null;
}
void setLightingToGet(char[][] lighting);
void setSkyLightingToGet(char[][] lighting);
void setHeightmapToGet(HeightMapType type, int[] data);
}

Datei anzeigen

@ -50,8 +50,6 @@ public interface IChunkSet extends IBlocks, OutputExtent {
void setSkyLightLayer(int layer, char[] toSet);
void removeSectionLighting(int layer, boolean sky);
void setFullBright(int layer);
void setEntity(CompoundTag tag);

Datei anzeigen

@ -44,6 +44,11 @@ public class FallbackChunkGet implements IChunkGet {
return extent.getBiomeType(bx + x, y, bz + z);
}
@Override
public void removeSectionLighting(int layer, boolean sky) {
// do nothing
}
@Override
public BlockState getBlock(int x, int y, int z) {
return extent.getBlock(bx + x, y, bz + z);
@ -106,6 +111,21 @@ public class FallbackChunkGet implements IChunkGet {
return false;
}
@Override
public void setLightingToGet(char[][] lighting) {
// do nothing
}
@Override
public void setSkyLightingToGet(char[][] lighting) {
// do nothing
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
// do nothing
}
@Override
public boolean trim(boolean aggressive) {
return true;

Datei anzeigen

@ -38,6 +38,9 @@ public final class NullChunkGet implements IChunkGet {
return BiomeTypes.FOREST;
}
@Override
public void removeSectionLighting(int layer, boolean sky) {}
@NotNull
public BlockState getBlock(int x, int y, int z) {
return BlockTypes.AIR.getDefaultState();
@ -69,6 +72,15 @@ public final class NullChunkGet implements IChunkGet {
return false;
}
@Override
public void setLightingToGet(char[][] lighting) {}
@Override
public void setSkyLightingToGet(char[][] lighting) {}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {}
public boolean trim(boolean aggressive) {
return true;
}

Datei anzeigen

@ -151,6 +151,25 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return createCopy;
}
@Override
public void setLightingToGet(char[][] lighting) {
delegate.setLightingToGet(this, lighting);
}
@Override
public void setSkyLightingToGet(char[][] lighting) {
delegate.setSkyLightingToGet(this, lighting);
}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {
delegate.setHeightmapToGet(this, type, data);
}
public void flushLightToGet(boolean heightmaps) {
delegate.flushLightToGet(this, heightmaps);
}
private static final IBlockDelegate BOTH = new IBlockDelegate() {
@Override
public IChunkGet get(ChunkHolder chunk) {
@ -187,6 +206,7 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
@Override
public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
chunk.chunkSet.removeSectionLighting(layer, sky);
chunk.chunkExisting.removeSectionLighting(layer, sky);
}
@Override
@ -264,6 +284,27 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
@Override public int[] getHeightMap(ChunkHolder chunk, HeightMapType type) {
return chunk.chunkExisting.getHeightMap(type);
}
@Override
public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) {
chunk.chunkExisting.setLightingToGet(chunk.chunkSet.getLight());
chunk.chunkExisting.setSkyLightingToGet(chunk.chunkSet.getSkyLight());
}
@Override
public void setLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.chunkExisting.setLightingToGet(lighting);
}
@Override
public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.chunkExisting.setSkyLightingToGet(lighting);
}
@Override
public void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data) {
chunk.chunkExisting.setHeightmapToGet(type, data);
}
};
private static final IBlockDelegate GET = new IBlockDelegate() {
@ -382,6 +423,26 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
@Override public int[] getHeightMap(ChunkHolder chunk, HeightMapType type) {
return chunk.chunkExisting.getHeightMap(type);
}
@Override
public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) {
// Do nothing as no lighting to flush to GET
}
@Override
public void setLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.chunkExisting.setLightingToGet(lighting);
}
@Override
public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.chunkExisting.setSkyLightingToGet(lighting);
}
@Override
public void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data) {
chunk.chunkExisting.setHeightmapToGet(type, data);
}
};
private static final IBlockDelegate SET = new IBlockDelegate() {
@ -421,7 +482,9 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
@Override
public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
chunk.chunkSet.removeSectionLighting(layer, sky);
chunk.getOrCreateGet();
chunk.delegate = BOTH;
chunk.removeSectionLighting(layer, sky);
}
@Override
@ -524,6 +587,39 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
chunk.chunkExisting.trim(false);
return chunk.getHeightMap(type);
}
@Override
public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
chunk.chunkExisting.trim(false);
chunk.chunkExisting.setLightingToGet(chunk.chunkSet.getLight());
chunk.chunkExisting.setSkyLightingToGet(chunk.chunkSet.getSkyLight());
}
@Override
public void setLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
chunk.chunkExisting.trim(false);
chunk.chunkExisting.setLightingToGet(lighting);
}
@Override
public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
chunk.chunkExisting.trim(false);
chunk.chunkExisting.setSkyLightingToGet(lighting);
}
@Override
public void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data) {
chunk.getOrCreateGet();
chunk.delegate = BOTH;
chunk.chunkExisting.trim(false);
chunk.chunkExisting.setHeightmapToGet(type, data);
}
};
private static final IBlockDelegate NULL = new IBlockDelegate() {
@ -597,8 +693,9 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
@Override
public void removeSectionLighting(ChunkHolder chunk, int layer, boolean sky) {
chunk.getOrCreateGet();
chunk.getOrCreateSet();
chunk.delegate = SET;
chunk.delegate = BOTH;
chunk.removeSectionLighting(layer, sky);
}
@ -667,6 +764,35 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
chunk.chunkExisting.trim(false);
return chunk.getHeightMap(type);
}
@Override
public void flushLightToGet(ChunkHolder chunk, boolean heightmaps) {
// Do nothing as no light to flush
}
@Override
public void setLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.getOrCreateGet();
chunk.delegate = GET;
chunk.chunkExisting.trim(false);
chunk.setLightingToGet(lighting);
}
@Override
public void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting) {
chunk.getOrCreateGet();
chunk.delegate = GET;
chunk.chunkExisting.trim(false);
chunk.setSkyLightingToGet(lighting);
}
@Override
public void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data) {
chunk.getOrCreateGet();
chunk.delegate = GET;
chunk.chunkExisting.trim(false);
chunk.setHeightmapToGet(type, data);
}
};
@Override
@ -947,5 +1073,13 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
int getOpacity(ChunkHolder chunk, int x, int y, int z);
int[] getHeightMap(ChunkHolder chunk, HeightMapType type);
void flushLightToGet(ChunkHolder chunk, boolean heightmaps);
void setLightingToGet(ChunkHolder chunk, char[][] lighting);
void setSkyLightingToGet(ChunkHolder chunk, char[][] lighting);
void setHeightmapToGet(ChunkHolder chunk, HeightMapType type, int[] data);
}
}

Datei anzeigen

@ -177,6 +177,15 @@ public final class NullChunk implements IQueueChunk {
return false;
}
@Override
public void setLightingToGet(char[][] lighting) {}
@Override
public void setSkyLightingToGet(char[][] lighting) {}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {}
@Nullable
public <T extends Future<T>> T call(@Nullable IChunkSet set, @Nullable Runnable finalize) {
return null;

Datei anzeigen

@ -1,9 +1,11 @@
package com.boydti.fawe.beta.implementation.lighting;
import com.boydti.fawe.Fawe;
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.RelightMode;
import com.boydti.fawe.object.RunnableVal;
import com.boydti.fawe.object.collection.BlockVectorSet;
import com.boydti.fawe.util.MathMan;
@ -34,6 +36,7 @@ import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
public class NMSRelighter implements Relighter {
@ -64,11 +67,17 @@ public class NMSRelighter implements Relighter {
private final Map<Long, long[][][] /* z y x */> lightQueue;
private final AtomicBoolean lightLock = new AtomicBoolean(false);
private final ConcurrentHashMap<Long, long[][][]> concurrentLightQueue;
private final RelightMode relightMode;
private final int maxY;
private final boolean calculateHeightMaps;
private final ReentrantLock lightingLock;
private boolean removeFirst;
public NMSRelighter(IQueueExtent<IQueueChunk> queue, boolean calculateHeightMaps) {
this(queue, calculateHeightMaps, null);
}
public NMSRelighter(IQueueExtent<IQueueChunk> queue, boolean calculateHeightMaps, RelightMode relightMode) {
this.queue = queue;
this.skyToRelight = new Long2ObjectOpenHashMap<>(12);
this.lightQueue = new Long2ObjectOpenHashMap<>(12);
@ -77,12 +86,19 @@ public class NMSRelighter implements Relighter {
this.heightMaps = new Long2ObjectOpenHashMap<>(12);
this.maxY = queue.getMaxY();
this.calculateHeightMaps = calculateHeightMaps;
this.relightMode = relightMode != null ? relightMode : RelightMode.valueOf(Settings.IMP.LIGHTING.MODE);
this.lightingLock = new ReentrantLock();
}
@Override public boolean isEmpty() {
return skyToRelight.isEmpty() && lightQueue.isEmpty() && extentdSkyToRelight.isEmpty() && concurrentLightQueue.isEmpty();
}
@Override
public synchronized ReentrantLock getLock() {
return lightingLock;
}
@Override public synchronized void removeAndRelight(boolean sky) {
removeFirst = true;
fixLightingSafe(sky);
@ -778,6 +794,7 @@ public class NMSRelighter implements Relighter {
}
}
fixBlockLighting();
sendChunks();
} catch (Throwable e) {
e.printStackTrace();
}
@ -786,7 +803,11 @@ public class NMSRelighter implements Relighter {
public void fixBlockLighting() {
synchronized (lightQueue) {
while (!lightLock.compareAndSet(false, true)) {
;
try {
lightLock.wait(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
updateBlockLight(this.lightQueue);
@ -796,7 +817,7 @@ public class NMSRelighter implements Relighter {
}
}
public synchronized void sendChunks() {
public synchronized void flush() {
Iterator<Map.Entry<Long, Integer>> iter = chunksToSend.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Long, Integer> entry = iter.next();
@ -827,6 +848,40 @@ public class NMSRelighter implements Relighter {
}
}
public synchronized void sendChunks() {
RunnableVal<Object> runnable = new RunnableVal<Object>() {
@Override
public void run(Object value) {
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);
if (calculateHeightMaps && heightMaps != null) {
Map<HeightMapType, int[]> heightMapList = heightMaps.get(pair);
if (heightMapList != null) {
for (Map.Entry<HeightMapType, int[]> heightMapEntry : heightMapList.entrySet()) {
chunk.setHeightMap(heightMapEntry.getKey(), heightMapEntry.getValue());
}
}
}
chunk.flushLightToGet(true);
Fawe.imp().getPlatformAdapter().sendChunk(chunk.getOrCreateGet(), bitMask, true);
iter.remove();
}
}
};
if (Settings.IMP.LIGHTING.ASYNC) {
runnable.run();
} else {
TaskManager.IMP.sync(runnable);
}
}
public synchronized void fixSkyLighting() {
// Order chunks
Map<Long, RelightSkyEntry> map = getSkyMap();
@ -913,7 +968,7 @@ public class NMSRelighter implements Relighter {
for (RelightSkyEntry chunk : chunks) { // Propagate skylight
int layer = y >> 4;
byte[] mask = chunk.mask;
if ((y & 15) == 15 && chunk.fix[layer] != SkipReason.NONE) {
if (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]);
}
@ -945,7 +1000,7 @@ public class NMSRelighter implements Relighter {
BlockMaterial material = state.getMaterial();
int opacity = material.getLightOpacity();
int brightness = material.getLightValue();
if (brightness > 1) {
if (brightness != iChunk.getEmmittedLight(x, y, z)) {
addLightUpdate(bx + x, y, bz + z);
}
@ -957,25 +1012,26 @@ public class NMSRelighter implements Relighter {
if (heightMapList.get(HeightMapType.OCEAN_FLOOR)[j] == 0 && material.isSolid()) {
heightMapList.get(HeightMapType.OCEAN_FLOOR)[j] = y + 1;
}
Map<Property<?>, Object> states = state.getStates();
try {
if (heightMapList.get(HeightMapType.MOTION_BLOCKING)[j] == 0 && (material.isSolid() || material.isLiquid() || (
state.getStates().containsKey(waterLogged) && state.getState(waterLogged)))) {
states.containsKey(waterLogged) && state.getState(waterLogged)))) {
heightMapList.get(HeightMapType.MOTION_BLOCKING)[j] = y + 1;
}
} catch (Exception ignored) {
log.debug("Error calculating waterlogged state for BlockState: " + state.getBlockType().getId() + ". States:");
log.debug(state.getStates().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
log.debug(states.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining(", ", "{", "}")));
}
try {
if (heightMapList.get(HeightMapType.MOTION_BLOCKING_NO_LEAVES)[j] == 0 && (material.isSolid() || material.isLiquid() || (
state.getStates().containsKey(waterLogged) && state.getState(waterLogged))) && !state.getBlockType().getId()
states.containsKey(waterLogged) && state.getState(waterLogged))) && !state.getBlockType().getId()
.toLowerCase().contains("leaves")) {
heightMapList.get(HeightMapType.MOTION_BLOCKING_NO_LEAVES)[j] = y + 1;
}
} catch (Exception ignored) {
log.debug("Error calculating waterlogged state for BlockState: " + state.getBlockType().getId() + ". States:");
log.debug(state.getStates().entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
log.debug(states.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue())
.collect(Collectors.joining(", ", "{", "}")));
}
}

Datei anzeigen

@ -1,5 +1,7 @@
package com.boydti.fawe.beta.implementation.lighting;
import java.util.concurrent.locks.ReentrantLock;
public class NullRelighter implements Relighter {
public static NullRelighter INSTANCE = new NullRelighter();
@ -46,4 +48,9 @@ public class NullRelighter implements Relighter {
public boolean isEmpty() {
return true;
}
@Override
public ReentrantLock getLock() {
return null;
}
}

Datei anzeigen

@ -0,0 +1,62 @@
package com.boydti.fawe.beta.implementation.lighting;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.boydti.fawe.config.Settings;
import com.sk89q.worldedit.extent.Extent;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
public class RelightProcessor implements IBatchProcessor {
private final Relighter relighter;
public RelightProcessor(Relighter relighter) {
if (relighter instanceof NullRelighter) {
throw new IllegalArgumentException("NullRelighter cannot be used for a RelightProcessor");
}
this.relighter = relighter;
}
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
if (Settings.IMP.LIGHTING.MODE == 2) {
relighter.addChunk(chunk.getX(), chunk.getZ(), null, chunk.getBitMask());
} else if (Settings.IMP.LIGHTING.MODE == 1) {
byte[] fix = new byte[16];
boolean relight = false;
for (int i = 15; i >= 0; i--) {
if (!set.hasSection(i)) {
fix[i] = Relighter.SkipReason.AIR;
continue;
}
relight = true;
break;
}
if (relight) {
relighter.addChunk(chunk.getX(), chunk.getZ(), fix, chunk.getBitMask());
}
}
return set;
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
return CompletableFuture.completedFuture(set);
}
@Override
public @Nullable Extent construct(Extent child) {
throw new UnsupportedOperationException("Processing only");
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.READING_SET_BLOCKS;
}
}

Datei anzeigen

@ -1,5 +1,7 @@
package com.boydti.fawe.beta.implementation.lighting;
import java.util.concurrent.locks.ReentrantLock;
public interface Relighter {
/**
@ -66,6 +68,8 @@ public interface Relighter {
*/
boolean isEmpty();
ReentrantLock getLock();
class SkipReason {
public static final byte NONE = 0;
public static final byte AIR = 1;

Datei anzeigen

@ -51,4 +51,9 @@ public class BatchProcessorHolder implements IBatchProcessorHolder {
IBatchProcessor tmp = getProcessor();
return super.toString() + "{" + (tmp == this ? "" : getProcessor()) + "}";
}
@Override
public ProcessorScope getScope() {
return getProcessor().getScope();
}
}

Datei anzeigen

@ -81,4 +81,9 @@ public class ChunkSendProcessor implements IBatchProcessor {
public Extent construct(Extent child) {
throw new UnsupportedOperationException("Processing only");
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.READING_SET_BLOCKS;
}
}

Datei anzeigen

@ -49,4 +49,9 @@ public final class EmptyBatchProcessor implements IBatchProcessor {
private EmptyBatchProcessor() {
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.ADDING_BLOCKS;
}
}

Datei anzeigen

@ -9,21 +9,30 @@ import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.util.StringMan;
import com.google.common.cache.LoadingCache;
import com.sk89q.worldedit.extent.Extent;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.Map;
import java.util.function.Supplier;
public class MultiBatchProcessor implements IBatchProcessor {
private static final Logger log = LoggerFactory.getLogger(MultiBatchProcessor.class);
private IBatchProcessor[] processors;
private final LoadingCache<Class<?>, Map<Long, Filter>> classToThreadIdToFilter =
FaweCache.IMP.createCache((Supplier<Map<Long, Filter>>) ConcurrentHashMap::new);
public MultiBatchProcessor(IBatchProcessor... processors) {
this.processors = processors;
}
@ -65,18 +74,31 @@ public class MultiBatchProcessor implements IBatchProcessor {
@Override
public IChunkSet processSet(IChunk chunk, IChunkGet get, IChunkSet set) {
Map<Integer, Set<IBatchProcessor>> ordered = new HashMap<>();
try {
IChunkSet chunkSet = set;
for (int i = processors.length - 1; i >= 0; i--) {
IBatchProcessor processor = processors[i];
if (processor instanceof Filter) {
chunkSet = ((IBatchProcessor) classToThreadIdToFilter.getUnchecked(processor.getClass())
.computeIfAbsent(Thread.currentThread().getId(), k -> ((Filter) processor).fork())).processSet(chunk, get, chunkSet);
} else {
chunkSet = processor.processSet(chunk, get, chunkSet);
for (IBatchProcessor processor : processors) {
if (processor.getScope() != ProcessorScope.ADDING_BLOCKS) {
ordered.merge(processor.getScope().intValue(), new HashSet<>(Collections.singleton(processor)), (existing, theNew) -> {
existing.add(processor);
return existing;
});
continue;
}
if (chunkSet == null) {
return null;
chunkSet = processSet(processor, chunk, get, chunkSet);
}
if (ordered.size() > 0) {
for (int i = 1; i <= 4; i++) {
Set<IBatchProcessor> processors = ordered.get(i);
if (processors == null) {
continue;
}
for (IBatchProcessor processor : processors) {
chunkSet = processSet(processor,chunk, get, chunkSet);
if (chunkSet == null) {
return null;
}
}
}
}
return chunkSet;
@ -86,11 +108,25 @@ public class MultiBatchProcessor implements IBatchProcessor {
}
}
@Nullable
private IChunkSet processSet(IBatchProcessor processor, IChunk chunk, IChunkGet get, IChunkSet chunkSet) {
if (processor instanceof Filter) {
chunkSet = ((IBatchProcessor) classToThreadIdToFilter.getUnchecked(processor.getClass())
.computeIfAbsent(Thread.currentThread().getId(), k -> ((Filter) processor).fork())).processSet(chunk, get, chunkSet);
} else {
chunkSet = processor.processSet(chunk, get, chunkSet);
}
return chunkSet;
}
@Override
public Future<IChunkSet> postProcessSet(IChunk chunk, IChunkGet get, IChunkSet set) {
try {
for (int i = processors.length - 1 ; i >= 0; i--) {
IBatchProcessor processor = processors[i];
for (IBatchProcessor processor : processors) {
// We do NOT want to edit blocks in post processing
if (processor.getScope() != ProcessorScope.READING_SET_BLOCKS) {
continue;
}
set = processor.postProcessSet(chunk, get, set).get();
if (set == null) {
return null;
@ -163,4 +199,13 @@ public class MultiBatchProcessor implements IBatchProcessor {
public String toString() {
return super.toString() + "{" + StringMan.join(processors, ",") + "}";
}
@Override
public ProcessorScope getScope() {
int scope = 0;
for (IBatchProcessor processor : processors) {
scope = Math.max(scope, processor.getScope().intValue());
}
return ProcessorScope.valueOf(0);
}
}

Datei anzeigen

@ -36,4 +36,9 @@ public final class NullProcessor implements IBatchProcessor {
private NullProcessor() {
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.ADDING_BLOCKS;
}
}

Datei anzeigen

@ -0,0 +1,40 @@
package com.boydti.fawe.beta.implementation.processors;
/**
* The scope of a processor.
* Order in which processors are executed:
* - ADDING_BLOCKS (processors that strictly ADD blocks to an edit ONLY)
* - CHANGING_BLOCKS (processors that strictly ADD or CHANGE blocks being set)
* - REMOVING_BLOCKS (processors that string ADD, CHANGE or REMOVE blocks being set)
* - CUSTOM (processors that do not specify a SCOPE)
* - READING_SET_BLOCKS (processors that do not alter blocks at all, and read the blocks that are actually going to set, e.g. history processors)
*/
public enum ProcessorScope {
ADDING_BLOCKS(0), CHANGING_BLOCKS(1), REMOVING_BLOCKS(2), CUSTOM(3), READING_SET_BLOCKS(4);
private final int value;
ProcessorScope(int value) {
this.value = value;
}
public int intValue() {
return this.value;
}
public static ProcessorScope valueOf(int value) {
switch (value) {
case 0:
return ProcessorScope.ADDING_BLOCKS;
case 1:
return ProcessorScope.CHANGING_BLOCKS;
case 2:
return ProcessorScope.REMOVING_BLOCKS;
case 4:
return ProcessorScope.READING_SET_BLOCKS;
case 3:
default:
return ProcessorScope.CUSTOM;
}
}
}

Datei anzeigen

@ -14,6 +14,7 @@ import com.boydti.fawe.beta.implementation.filter.block.CharFilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.processors.EmptyBatchProcessor;
import com.boydti.fawe.beta.implementation.processors.ExtentBatchProcessorHolder;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.util.MathMan;
import com.boydti.fawe.util.MemUtil;
@ -352,4 +353,9 @@ public class SingleThreadQueueExtent extends ExtentBatchProcessorHolder implemen
public ChunkFilterBlock initFilterBlock() {
return new CharFilterBlock(this);
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.ADDING_BLOCKS;
}
}

Datei anzeigen

@ -623,6 +623,15 @@ public class MCAChunk implements IChunk {
return createCopy;
}
@Override
public void setLightingToGet(char[][] lighting) {}
@Override
public void setSkyLightingToGet(char[][] lighting) {}
@Override
public void setHeightmapToGet(HeightMapType type, int[] data) {}
@Override
public Future call(IChunkSet set, Runnable finalize) {
return null;

Datei anzeigen

@ -6,6 +6,7 @@ import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.boydti.fawe.object.HistoryExtent;
import com.boydti.fawe.util.EditSessionBuilder;
import com.boydti.fawe.util.MainUtil;
@ -212,6 +213,11 @@ public abstract class AbstractChangeSet implements ChangeSet, IBatchProcessor {
return (Future<IChunkSet>) addWriteTask(() -> processSet(chunk, get, set));
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.READING_SET_BLOCKS;
}
public abstract void addTileCreate(CompoundTag tag);
public abstract void addTileRemove(CompoundTag tag);

Datei anzeigen

@ -2,6 +2,7 @@ package com.boydti.fawe.object.extent;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.util.ExtentTraverser;
import com.boydti.fawe.util.WEManager;
@ -147,4 +148,9 @@ public abstract class FaweRegionExtent extends ResettableExtent implements IBatc
}
return super.createEntity(location, entity);
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.READING_SET_BLOCKS;
}
}

Datei anzeigen

@ -5,6 +5,7 @@ import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.exception.FaweException;
import com.sk89q.jnbt.CompoundTag;
@ -355,4 +356,9 @@ public class NullExtent extends FaweRegionExtent implements IBatchProcessor {
public Extent construct(Extent child) {
throw reason;
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.ADDING_BLOCKS;
}
}

Datei anzeigen

@ -1,5 +1,6 @@
package com.boydti.fawe.regions;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.sk89q.worldedit.entity.Player;
import com.sk89q.worldedit.regions.IDelegateRegion;
import com.sk89q.worldedit.regions.Region;
@ -24,4 +25,9 @@ public class FaweMask implements IDelegateRegion {
public Region clone() {
throw new UnsupportedOperationException("Clone not supported");
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.REMOVING_BLOCKS;
}
}

Datei anzeigen

@ -4,6 +4,10 @@ import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
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.RelightProcessor;
import com.boydti.fawe.beta.implementation.lighting.Relighter;
import com.boydti.fawe.beta.implementation.processors.LimitExtent;
import com.boydti.fawe.beta.implementation.queue.ParallelQueueExtent;
import com.boydti.fawe.config.Settings;
@ -67,6 +71,7 @@ public class EditSessionBuilder {
private EditSessionEvent event;
private String command;
private RelightMode relightMode;
private Relighter relighter;
private Boolean wnaMode;
/**
@ -396,6 +401,14 @@ public class EditSessionBuilder {
allowedRegions = new Region[]{RegionWrapper.GLOBAL()};
// this.extent = new HeightBoundExtent(this.extent, this.limit, 0, world.getMaxY());
}
// There's no need to do lighting (and it'll also just be a pain to implement) if we're not placing chunks
if (placeChunks && ((relightMode != null && relightMode != RelightMode.NONE) || (relightMode == null && Settings.IMP.LIGHTING.MODE > 0))) {
relighter = new NMSRelighter(queue, Settings.IMP.LIGHTING.DO_HEIGHTMAPS,
relightMode != null ? relightMode : RelightMode.valueOf(Settings.IMP.LIGHTING.MODE));
extent.addProcessor(new RelightProcessor(relighter));
} else {
relighter = NullRelighter.INSTANCE;
}
if (limit != null && !limit.isUnlimited() && regionExtent != null) {
this.extent = new LimitExtent(regionExtent, limit);
} else if (limit != null && !limit.isUnlimited()) {
@ -454,6 +467,10 @@ public class EditSessionBuilder {
return blockBag;
}
public Relighter getRelighter() {
return relighter;
}
public boolean isWNAMode() {
return wnaMode;
}
@ -462,5 +479,4 @@ public class EditSessionBuilder {
public Region[] getAllowedRegions() {
return allowedRegions;
}
}

Datei anzeigen

@ -20,6 +20,8 @@
package com.sk89q.worldedit;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.implementation.lighting.NullRelighter;
import com.boydti.fawe.beta.implementation.lighting.Relighter;
import com.boydti.fawe.config.Caption;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.FaweLimit;
@ -224,12 +226,12 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
private final int maxY;
private final List<WatchdogTickingExtent> watchdogExtents = new ArrayList<>(2);
private final Relighter relighter;
private final boolean wnaMode;
@Nullable
private final Region[] allowedRegions;
@Deprecated
public EditSession(@NotNull EventBus bus, World world, @Nullable Player player,
@Nullable FaweLimit limit, @Nullable AbstractChangeSet changeSet,
@ -264,6 +266,7 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
this.maxY = world.getMaxY();
this.blockBag = builder.getBlockBag();
this.history = changeSet != null;
this.relighter = builder.getRelighter();
this.wnaMode = builder.isWNAMode();
this.allowedRegions = builder.getAllowedRegions() != null ? builder.getAllowedRegions().clone() : null;
}
@ -1093,6 +1096,25 @@ public class EditSession extends PassthroughExtent implements AutoCloseable {
}
// Reset limit
limit.set(originalLimit);
try {
if (relighter != null && !(relighter instanceof NullRelighter)) {
// Only relight once!
if (!relighter.getLock().tryLock()) {
relighter.getLock().lock();
relighter.getLock().unlock();
} else {
if (Settings.IMP.LIGHTING.REMOVE_FIRST) {
relighter.removeAndRelight(true);
} else {
relighter.fixSkyLighting();
relighter.fixBlockLighting();
}
}
}
} catch (Throwable e) {
player.printError(TranslatableComponent.of("fawe.error.lighting"));
e.printStackTrace();
}
// Enqueue it
if (getChangeSet() != null) {
if (Settings.IMP.HISTORY.COMBINE_STAGES) {

Datei anzeigen

@ -23,6 +23,7 @@ import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.Filter;
import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.implementation.filter.block.ExtentFilterBlock;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.changeset.AbstractChangeSet;
import com.boydti.fawe.object.clipboard.WorldCopyClipboard;
@ -707,6 +708,9 @@ public interface Extent extends InputExtent, OutputExtent {
}
default Extent addPostProcessor(IBatchProcessor processor) {
if (processor.getScope() == ProcessorScope.READING_SET_BLOCKS) {
throw new IllegalArgumentException("You cannot alter blocks in a PostProcessor");
}
return processor.construct(this);
}

Datei anzeigen

@ -28,6 +28,7 @@ import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.filter.block.CharFilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.filter.block.FilterBlock;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.google.common.cache.LoadingCache;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.mask.Mask;
@ -128,4 +129,9 @@ public class MaskingExtent extends AbstractDelegateExtent implements IBatchProce
public Filter fork() {
return new MaskingExtent(getExtent(), this.mask.copy(), this.threadIdToFilter);
}
@Override
public ProcessorScope getScope() {
return ProcessorScope.REMOVING_BLOCKS;
}
}

Datei anzeigen

@ -25,6 +25,7 @@ import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.boydti.fawe.config.Settings;
import com.boydti.fawe.object.collection.BlockVectorSet;
import com.sk89q.worldedit.math.BlockVector2;
@ -774,5 +775,4 @@ public class CuboidRegion extends AbstractRegion implements FlatRegion {
return null;
}
}

Datei anzeigen

@ -25,6 +25,7 @@ import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.filter.block.ChunkFilterBlock;
import com.boydti.fawe.beta.implementation.processors.ProcessorScope;
import com.boydti.fawe.object.FaweLimit;
import com.boydti.fawe.object.extent.SingleRegionExtent;
import com.sk89q.worldedit.extent.Extent;
@ -365,4 +366,9 @@ public interface Region extends Iterable<BlockVector3>, Cloneable, IBatchProcess
}
return new SingleRegionExtent(child, FaweLimit.MAX, this);
}
@Override
default ProcessorScope getScope() {
return ProcessorScope.REMOVING_BLOCKS;
}
}

Datei anzeigen

@ -104,6 +104,7 @@
"fawe.error.worldedit.some.fails.blockbag": "Missing blocks: {0}",
"fawe.error.mask.angle": "Cannot combine degree with block-step",
"fawe.error.invalid-flag": "The flag {0} is not applicable here",
"fawe.error.lighting": "Error when attempting lighting. You may need to reload the chunks to see the edit.",
"fawe.cancel.worldedit.cancel.count": "Cancelled {0} edits.",
"fawe.cancel.worldedit.cancel.reason.confirm": "Use //confirm to execute {2}",