1
0
Fork 0

alfheim lighting engine

This commit is contained in:
HoosierTransfer 2024-04-14 18:36:35 -04:00
parent 7e49cad89c
commit 27ca7c5b40
41 changed files with 134419 additions and 1290760 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
package net.hoosiertransfer.Alfheim;
import net.minecraft.util.BlockPos;
import net.minecraft.world.EnumSkyBlock;
public interface IChunkLightingData {
short[] alfheim$getNeighborLightChecks();
void alfheim$setNeighborLightChecks(final short[] data);
boolean alfheim$isLightInitialized();
void alfheim$setLightInitialized(final boolean lightInitialized);
void alfheim$setSkylightUpdatedPublic();
void alfheim$initNeighborLightChecks();
byte alfheim$getCachedLightFor(final EnumSkyBlock enumSkyBlock, final BlockPos pos);
}

View File

@ -0,0 +1,15 @@
package net.hoosiertransfer.Alfheim;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.BlockPos;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.IBlockAccess;
public interface ILightInfoProvider {
int alfheim$getLightFor(final IBlockAccess iBlockAccess, final EnumSkyBlock lightType, final BlockPos blockPos);
boolean alfheim$useNeighborBrightness(final EnumFacing facing, final IBlockAccess blockAccess, final BlockPos blockPos);
int alfheim$getLightOpacity(final EnumFacing facing, final IBlockAccess blockAccess, final BlockPos blockPos);
}

View File

@ -0,0 +1,13 @@
package net.hoosiertransfer.Alfheim;
import net.minecraft.util.BlockPos;
import net.minecraft.world.EnumSkyBlock;
/**
* @author Luna Lage (Desoroxxx)
* @since 1.0
*/
public interface ILightLevelProvider {
int alfheim$getLight(final EnumSkyBlock lightType, final BlockPos blockPos);
}

View File

@ -0,0 +1,6 @@
package net.hoosiertransfer.Alfheim;
public interface ILightUpdatesProcessor {
void alfheim$processLightUpdates();
}

View File

@ -0,0 +1,13 @@
package net.hoosiertransfer.Alfheim;
import net.hoosiertransfer.Alfheim.lighting.LightingEngine;
/**
* @author Luna Lage (Desoroxxx)
* @author Angeline (@jellysquid)
* @since 1.0
*/
public interface ILightingEngineProvider {
LightingEngine alfheim$getLightingEngine();
}

View File

@ -0,0 +1,13 @@
package net.hoosiertransfer.Alfheim;
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.IBlockAccess;
public interface ILitBlock {
int alfheim$getLightFor(final IBlockState blockState, final IBlockAccess blockAccess, final EnumSkyBlock lightType, final BlockPos blockPos);
boolean alfheim$useNeighborBrightness(final IBlockState blockState, final EnumFacing facing, final IBlockAccess blockAccess, final BlockPos blockPos);
int alfheim$getLightOpacity(final IBlockState blockState, final EnumFacing facing, final IBlockAccess blockAccess, final BlockPos blockPos);
}

View File

@ -0,0 +1,11 @@
package net.hoosiertransfer.Alfheim.lighting;
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.BlockPos;
import net.minecraft.world.IBlockAccess;
public class LightUtil {
public static int getLightValueForState(final IBlockState blockState, final IBlockAccess blockAccess, final BlockPos blockPos) {
return blockState.getLightValue(blockAccess, blockPos);
}
}

View File

@ -0,0 +1,500 @@
package net.hoosiertransfer.Alfheim.lighting;
import net.hoosiertransfer.Alfheim.IChunkLightingData;
import net.hoosiertransfer.Alfheim.util.ClampUtil;
import net.hoosiertransfer.Alfheim.util.DeduplicatedLongQueue;
import net.hoosiertransfer.teavm.ReentrantLock;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.profiler.Profiler;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.Vec3i;
import net.minecraft.util.BlockPos.MutableBlockPos;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
public class LightingEngine {
private static final byte MAX_LIGHT_LEVEL = 15;
private final Thread ownerThread = Thread.currentThread();
private final World world;
private final Profiler profiler;
private final DeduplicatedLongQueue[] lightUpdateQueues = new DeduplicatedLongQueue[EnumSkyBlock.values().length];
private final DeduplicatedLongQueue[] darkeningQueues = new DeduplicatedLongQueue[MAX_LIGHT_LEVEL + 1];
private final DeduplicatedLongQueue[] brighteningQueues = new DeduplicatedLongQueue[MAX_LIGHT_LEVEL + 1];
private final DeduplicatedLongQueue initialBrightenings;
private final DeduplicatedLongQueue initialDarkenings;
private boolean updating = false;
private static final int
L_X = 26,
L_Y = 8,
L_Z = 26,
L_L = 4;
private static final int
S_Z = 0,
S_X = S_Z + L_Z,
S_Y = S_X + L_X,
S_L = S_Y + L_Y;
private static final long
M_X = (1L << L_X) - 1,
M_Y = (1L << L_Y) - 1,
M_Z = (1L << L_Z) - 1,
M_L = (1L << L_L) - 1,
M_POS = (M_Y << S_Y) | (M_X << S_X) | (M_Z << S_Z);
private static final long Y_CHECK = 1L << (S_Y + L_Y);
private static final long[] neighborShifts = new long[6];
static {
for (byte i = 0; i < 6; ++i) {
final Vec3i offset = EnumFacing._VALUES[i].getDirectionVec();
neighborShifts[i] = ((long) offset.getY() << S_Y) | ((long) offset.getX() << S_X) | ((long) offset.getZ() << S_Z);
}
}
private static final long M_CHUNK = ((M_X >> 4) << (4 + S_X)) | ((M_Z >> 4) << (4 + S_Z));
private final MutableBlockPos currentPos = new MutableBlockPos();
private Chunk currentChunk;
private long currentChunkIdentifier;
private long currentData;
private boolean isNeighborDataValid = false;
private final NeighborInfo[] neighborInfos = new NeighborInfo[6];
private DeduplicatedLongQueue currentQueue;
private ReentrantLock lock = new ReentrantLock();
public LightingEngine(final World world) {
this.world = world;
profiler = world.theProfiler;
initialBrightenings = new DeduplicatedLongQueue(16384);
initialDarkenings = new DeduplicatedLongQueue(16384);
for (int i = 0; i < EnumSkyBlock.values().length; ++i)
lightUpdateQueues[i] = new DeduplicatedLongQueue(16384);
for (int i = 0; i < darkeningQueues.length; ++i)
darkeningQueues[i] = new DeduplicatedLongQueue(16384);
for (int i = 0; i < brighteningQueues.length; ++i)
brighteningQueues[i] = new DeduplicatedLongQueue(16384);
for (int i = 0; i < neighborInfos.length; ++i)
neighborInfos[i] = new NeighborInfo();
}
public void scheduleLightUpdate(final EnumSkyBlock lightType, final BlockPos pos) {
lock();
try {
scheduleLightUpdate(lightType, encodeWorldCoord(pos));
} finally {
lock.unlock();
}
}
private void scheduleLightUpdate(final EnumSkyBlock lightType, final long blockPos) {
lightUpdateQueues[lightType.ordinal()].enqueue(blockPos);
}
public void processLightUpdatesForType(final EnumSkyBlock lightType) {
// We only want to perform updates if we're being called from a tick event on the client.
// There are many locations in the client code that will end up making calls to this method, usually from other threads.
if (world.isRemote && !isCallingFromMainThread())
return;
final DeduplicatedLongQueue queue = lightUpdateQueues[lightType.ordinal()];
// Quickly check if the queue is empty before we acquire a more expensive lock.
if (queue.isEmpty())
return;
profiler.startSection("process");
lock();
try {
processLightUpdatesForTypeInner(lightType, queue);
} finally {
lock.unlock();
}
profiler.endSection();
}
private boolean isCallingFromMainThread() {
return Minecraft.getMinecraft().isCallingFromMinecraftThread();
}
public void processLightUpdates() {
profiler.startSection("processSky");
processLightUpdatesForType(EnumSkyBlock.SKY);
profiler.endStartSection("processBlock");
processLightUpdatesForType(EnumSkyBlock.BLOCK);
profiler.endSection();
}
private void lock() {
if (lock.tryLock())
return;
// If we cannot lock, something has gone wrong... Only one thread should ever acquire the lock.
// Validate that we're on the right thread immediately, so we can gather information.
// It is NEVER valid to call World methods from a thread other than the owning thread of the world instance.
final Thread current = Thread.currentThread();
if (current != ownerThread) {
final IllegalAccessException illegalAccessException = new IllegalAccessException(String.format("World is owned by '%s' (ID: %s)," + " but was accessed from thread '%s' (ID: %s)", ownerThread.getName(), ownerThread.getId(), current.getName(), current.getId()));
System.out.println(
"Something (likely another mod) has attempted to modify the world's state from the wrong thread!\n" +
"This is *bad practice* and can cause severe issues in your game.\n" +
"Alfheim has done as best as it can to mitigate this violation, but it may negatively impact performance or introduce stalls.\n" +
"In a future release, this violation may result in a hard crash instead of the current soft warning.\n" +
"You should report this issue to our issue tracker with the following stacktrace information.");
illegalAccessException.printStackTrace();
}
// Wait for the lock to be released. This will likely introduce unwanted stalls, but will mitigate the issue.
lock.lock();
}
private void processLightUpdatesForTypeInner(final EnumSkyBlock lightType, final DeduplicatedLongQueue queue) {
// Avoid nested calls
if (updating)
throw new IllegalStateException("Already processing updates!");
updating = true;
currentChunkIdentifier = -1; // Reset chunk cache
currentQueue = queue;
if (currentQueue != null)
currentQueue.clearSet();
profiler.startSection("prepare");
// Process the queued updates and enqueue them for further processing
while (nextItem()) {
if (currentChunk == null)
continue;
final byte oldLight = getCursorCachedLight(lightType);
final byte newLight = calculateNewLightFromCursor(lightType);
if (oldLight < newLight)
initialBrightenings.enqueue(((long) newLight << S_L) | currentData); // Don't enqueue directly for brightening to avoid duplicate scheduling
else if (oldLight > newLight)
initialDarkenings.enqueue(currentData); // Don't enqueue directly for darkening to avoid duplicate scheduling
}
profiler.endStartSection("enqueueBrightening");
currentQueue = initialBrightenings;
if (currentQueue != null)
currentQueue.clearSet();
while (nextItem()) {
final byte newLight = (byte) (currentData >> S_L & M_L);
if (newLight > getCursorCachedLight(lightType))
enqueueBrightening(currentPos, currentData & M_POS, newLight, currentChunk, lightType); // Sets the light to newLight to only schedule once. Clear leading bits of curData for later
}
profiler.endStartSection("enqueueDarkening");
currentQueue = initialDarkenings;
if (currentQueue != null)
currentQueue.clearSet();
while (nextItem()) {
final byte oldLight = getCursorCachedLight(lightType);
if (oldLight != 0)
enqueueDarkening(currentPos, currentData, oldLight, currentChunk, lightType); // Sets the light to zero to only schedule once
}
profiler.endStartSection("process");
// Iterate through enqueued updates (brightening and darkening in parallel) from brightest to darkest so that we only need to iterate once
for (byte currentLight = MAX_LIGHT_LEVEL; currentLight >= 0; --currentLight) {
currentQueue = darkeningQueues[currentLight];
if (currentQueue != null)
currentQueue.clearSet();
while (nextItem()) {
// Don't darken if we got brighter due to some other change
if (getCursorCachedLight(lightType) >= currentLight)
continue;
final IBlockState blockState = currentChunk.getBlockState(currentPos);
final byte luminosity = getCursorLuminosity(blockState, lightType);
final byte opacity; // If luminosity is high enough, opacity is irrelevant
if (luminosity >= MAX_LIGHT_LEVEL - 1)
opacity = 1;
else
opacity = getPosOpacity(currentPos, blockState);
// Only darken neighbors if we indeed became darker
if (calculateNewLightFromCursor(luminosity, opacity, lightType) < currentLight) {
// Need to calculate new light value from neighbors IGNORING neighbors which are scheduled for darkening
byte newLight = luminosity;
fetchNeighborDataFromCursor(lightType);
for (final NeighborInfo neighborInfo : neighborInfos) {
final Chunk neighborChunk = neighborInfo.chunk;
if (neighborChunk == null)
continue;
final byte neighborLight = neighborInfo.light;
if (neighborLight == 0)
continue;
final MutableBlockPos neighborPos = neighborInfo.mutableBlockPos;
if (currentLight - getPosOpacity(neighborPos, neighborChunk.getBlockState(neighborPos)) >= neighborLight) /*Schedule neighbor for darkening if we possibly light it*/ {
enqueueDarkening(neighborPos, neighborInfo.key, neighborLight, neighborChunk, lightType);
} else /*Only use for new light calculation if not*/ {
// If we can't darken the neighbor, no one else can (because of processing order) -> safe to let us be illuminated by it
newLight = (byte) Math.max(newLight, neighborLight - opacity);
}
}
// Schedule brightening since light level was set to 0
enqueueBrighteningFromCursor(newLight, lightType);
} else /*We didn't become darker, so we need to re-set our initial light value (was set to zero) and notify neighbors*/ {
enqueueBrighteningFromCursor(currentLight, lightType); // Do not spread to neighbors immediately to avoid scheduling multiple times
}
}
currentQueue = brighteningQueues[currentLight];
if (currentQueue != null)
currentQueue.clearSet();
while (nextItem()) {
final byte oldLight = getCursorCachedLight(lightType);
// Only process this if nothing else has happened at this position since scheduling
if (oldLight == currentLight) {
world.notifyLightSet(currentPos);
if (currentLight > 1)
spreadLightFromCursor(currentLight, lightType);
}
}
}
profiler.endSection();
updating = false;
}
private void fetchNeighborDataFromCursor(final EnumSkyBlock lightType) {
// Only update if curPos was changed
if (isNeighborDataValid)
return;
isNeighborDataValid = true;
for (int i = 0; i < neighborInfos.length; ++i) {
final NeighborInfo neighborInfo = neighborInfos[i];
final long neighborLongPos = neighborInfo.key = currentData + neighborShifts[i];
if ((neighborLongPos & Y_CHECK) != 0) {
neighborInfo.chunk = null;
continue;
}
final MutableBlockPos neighborPos = decodeWorldCoord(neighborInfo.mutableBlockPos, neighborLongPos);
final Chunk neighborChunk;
if ((neighborLongPos & M_CHUNK) == currentChunkIdentifier)
neighborChunk = neighborInfo.chunk = currentChunk;
else
neighborChunk = neighborInfo.chunk = getChunk(neighborPos);
if (neighborChunk != null) {
final ExtendedBlockStorage neighborSection = neighborChunk.getBlockStorageArray()[neighborPos.getY() >> 4];
neighborInfo.light = getCachedLightFor(neighborChunk, neighborSection, neighborPos, lightType);
}
}
}
private static byte getCachedLightFor(final Chunk chunk, final ExtendedBlockStorage storage, final BlockPos blockPos, final EnumSkyBlock type) {
final int x = blockPos.getX() & 15;
final int y = blockPos.getY();
final int z = blockPos.getZ() & 15;
if (storage == Chunk.NULL_BLOCK_STORAGE)
return type == EnumSkyBlock.SKY && chunk.canSeeSky(blockPos) ? (byte) type.defaultLightValue : 0;
else if (type == EnumSkyBlock.SKY)
return !chunk.getWorld().provider.getHasNoSky() ? (byte) storage.getExtSkylightValue(x, y & 15, z) : 0;
else
return type == EnumSkyBlock.BLOCK ? (byte) storage.getExtBlocklightValue(x, y & 15, z) : (byte) type.defaultLightValue;
}
private byte calculateNewLightFromCursor(final EnumSkyBlock lightType) {
final IBlockState blockState = currentChunk.getBlockState(currentPos);
final byte luminosity = getCursorLuminosity(blockState, lightType);
final byte opacity;
if (luminosity >= MAX_LIGHT_LEVEL - 1)
opacity = 1;
else
opacity = getPosOpacity(currentPos, blockState);
return calculateNewLightFromCursor(luminosity, opacity, lightType);
}
private byte calculateNewLightFromCursor(final byte luminosity, final byte opacity, final EnumSkyBlock lightType) {
if (luminosity >= MAX_LIGHT_LEVEL - opacity)
return luminosity;
byte newLight = luminosity;
fetchNeighborDataFromCursor(lightType);
for (final NeighborInfo neighborInfo : neighborInfos) {
if (neighborInfo.chunk == null)
continue;
newLight = (byte) Math.max(neighborInfo.light - opacity, newLight);
}
return newLight;
}
private void spreadLightFromCursor(final byte currentLight, final EnumSkyBlock lightType) {
fetchNeighborDataFromCursor(lightType);
for (final NeighborInfo neighborInfo : neighborInfos) {
final Chunk neighborChunk = neighborInfo.chunk;
if (neighborChunk == null || currentLight < neighborInfo.light)
continue;
final BlockPos neighborBlockPos = neighborInfo.mutableBlockPos;
final byte newLight = (byte) (currentLight - getPosOpacity(neighborBlockPos, neighborChunk.getBlockState(neighborBlockPos)));
if (newLight > neighborInfo.light)
enqueueBrightening(neighborBlockPos, neighborInfo.key, newLight, neighborChunk, lightType);
}
}
private void enqueueBrighteningFromCursor(final byte newLight, final EnumSkyBlock lightType) {
enqueueBrightening(currentPos, currentData, newLight, currentChunk, lightType);
}
private void enqueueBrightening(final BlockPos blockPos, final long longPos, final byte newLight, final Chunk chunk, final EnumSkyBlock lightType) {
brighteningQueues[newLight].enqueue(longPos);
chunk.setLightFor(lightType, blockPos, newLight);
}
private void enqueueDarkening(final BlockPos blockPos, final long longPos, final byte oldLight, final Chunk chunk, final EnumSkyBlock lightType) {
darkeningQueues[oldLight].enqueue(longPos);
chunk.setLightFor(lightType, blockPos, 0);
}
private static MutableBlockPos decodeWorldCoord(final MutableBlockPos mutableBlockPos, final long longPos) {
return mutableBlockPos.setPos((int) (longPos >> S_X & M_X) - (1 << L_X - 1), (int) (longPos >> S_Y & M_Y), (int) (longPos >> S_Z & M_Z) - (1 << L_Z - 1));
}
private static long encodeWorldCoord(final BlockPos pos) {
return ((long) pos.getY() << S_Y) | ((long) pos.getX() + (1 << L_X - 1) << S_X) | ((long) pos.getZ() + (1 << L_Z - 1) << S_Z);
}
private boolean nextItem() {
if (currentQueue.isEmpty()) {
currentQueue = null;
return false;
}
currentData = currentQueue.dequeue();
isNeighborDataValid = false;
decodeWorldCoord(currentPos, currentData);
final long chunkIdentifier = currentData & M_CHUNK;
if (currentChunkIdentifier != chunkIdentifier) {
currentChunk = getChunk(currentPos);
currentChunkIdentifier = chunkIdentifier;
}
return true;
}
private byte getCursorCachedLight(final EnumSkyBlock lightType) {
return currentChunk.alfheim$getCachedLightFor(lightType, currentPos);
// return ((IChunkLightingData) currentChunk).alfheim$getCachedLightFor(lightType, currentPos); // OK TEAVM YOU ARE THE WORST THING I HAVE HAD TO WORK WITH YET
}
private byte getCursorLuminosity(final IBlockState state, final EnumSkyBlock lightType) {
if (lightType == EnumSkyBlock.SKY) {
if (currentChunk.canSeeSky(currentPos))
return (byte) EnumSkyBlock.SKY.defaultLightValue;
else
return 0;
}
return (byte) ClampUtil.clampMinFirst(LightUtil.getLightValueForState(state, world, currentPos), 0, MAX_LIGHT_LEVEL);
}
private byte getPosOpacity(final BlockPos blockPos, final IBlockState blockState) {
return (byte) ClampUtil.clampMinFirst(blockState.getBlock().getLightOpacity(), 1, MAX_LIGHT_LEVEL);
}
private Chunk getChunk(final BlockPos blockPos) {
return world.getChunkProvider().getLoadedChunk(blockPos.getX() >> 4, blockPos.getZ() >> 4);
}
private static final class NeighborInfo {
public final MutableBlockPos mutableBlockPos = new MutableBlockPos();
public Chunk chunk;
public byte light;
public long key;
}
}

View File

@ -0,0 +1,343 @@
package net.hoosiertransfer.Alfheim.util;
/**
* A utility class that offers efficient clamping methods.
* <p>
* These functions are optimized for speed and provided for every primitive datatype.
*
* @author Luna Lage (Desoroxxx)
* @since 0.5
*/
@SuppressWarnings({"unused", "ManualMinMaxCalculation", "DuplicatedCode"})
public final class ClampUtil {
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
* <p>
* This will log the result to check if clampMinFirst or clampMaxFirst should be used.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static byte clampTest(final byte input, final byte min, final byte max) {
if (input < min) {
return min;
} else if (input > max) {
return max;
} else {
return input;
}
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
* <p>
* This will log the result to check if clampMinFirst or clampMaxFirst should be used.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static short clampTest(final short input, final short min, final short max) {
if (input < min) {
return min;
} else if (input > max) {
return max;
} else {
return input;
}
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
* <p>
* This will log the result to check if clampMinFirst or clampMaxFirst should be used.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static int clampTest(final int input, final int min, final int max) {
if (input < min) {
return min;
} else if (input > max) {
return max;
} else {
return input;
}
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
* <p>
* This will log the result to check if clampMinFirst or clampMaxFirst should be used.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static long clampTest(final long input, final long min, final long max) {
if (input < min) {
return min;
} else if (input > max) {
return max;
} else {
return input;
}
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
* <p>
* This will log the result to check if clampMinFirst or clampMaxFirst should be used.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static float clampTest(final float input, final float min, final float max) {
if (input < min) {
return min;
} else if (input > max) {
return max;
} else {
return input;
}
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
* <p>
* This will log the result to check if clampMinFirst or clampMaxFirst should be used.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static double clampTest(final double input, final double min, final double max) {
if (input < min) {
return min;
} else if (input > max) {
return max;
} else {
return input;
}
}
/**
* Clamps a value within a specified range [min, max], checking for the minimum value first.
* <p>
* If the input is less than min, it returns min. If the input is greater than max, it returns max.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static byte clampMinFirst(final byte input, final byte min, final byte max) {
return input < min ? min : input > max ? max : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the minimum value first.
* <p>
* If the input is less than min, it returns min. If the input is greater than max, it returns max.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static short clampMinFirst(final short input, final short min, final short max) {
return input < min ? min : input > max ? max : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the minimum value first.
* <p>
* If the input is less than min, it returns min. If the input is greater than max, it returns max.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static int clampMinFirst(final int input, final int min, final int max) {
return input < min ? min : input > max ? max : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the minimum value first.
* <p>
* If the input is less than min, it returns min. If the input is greater than max, it returns max.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static long clampMinFirst(final long input, final long min, final long max) {
return input < min ? min : input > max ? max : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the minimum value first.
* <p>
* If the input is less than min, it returns min. If the input is greater than max, it returns max.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static float clampMinFirst(final float input, final float min, final float max) {
return input < min ? min : input > max ? max : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the minimum value first.
* <p>
* If the input is less than min, it returns min. If the input is greater than max, it returns max.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
*
* @return The clamped value
*/
public static double clampMinFirst(final double input, final double min, final double max) {
return input < min ? min : input > max ? max : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
* @return The clamped value
*/
public static byte clampMaxFirst(final byte input, final byte min, final byte max) {
return input > max ? max : input < min ? min : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
* @return The clamped value
*/
public static short clampMaxFirst(final short input, final short min, final short max) {
return input > max ? max : input < min ? min : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
* @return The clamped value
*/
public static int clampMaxFirst(final int input, final int min, final int max) {
return input > max ? max : input < min ? min : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
* @return The clamped value
*/
public static long clampMaxFirst(final long input, final long min, final long max) {
return input > max ? max : input < min ? min : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
* @return The clamped value
*/
public static float clampMaxFirst(final float input, final float min, final float max) {
return input > max ? max : input < min ? min : input;
}
/**
* Clamps a value within a specified range [min, max], checking for the maximum value first.
* <p>
* If the input is greater than max, it returns max. If the input is less than min, it returns min.
* Otherwise, it returns the input.
*
* @param input The input value to clamp
* @param min The minimum value to clamp to
* @param max The maximum value to clamp to
* @return The clamped value
*/
public static double clampMaxFirst(final double input, final double min, final double max) {
return input > max ? max : input < min ? min : input;
}
}

View File

@ -0,0 +1,61 @@
package net.hoosiertransfer.Alfheim.util;
import java.util.HashSet;
import java.util.LinkedList;
/**
* A queue implementation for long values that are deduplicated on addition.
* <p>
* This is achieved by storing the values in a HashSet and a LinkedList.
*/
public final class DeduplicatedLongQueue {
private final HashSet<Long> set;
private final LinkedList<Long> queue;
/**
* Creates a new deduplicated queue with the given capacity.
*
* @param capacity The capacity of the deduplicated queue
*/
public DeduplicatedLongQueue(final int capacity) {
set = new HashSet<>(capacity);
queue = new LinkedList<>();
}
/**
* Adds a value to the queue.
*
* @param value The value to add to the queue
*/
public void enqueue(final long value) {
if (set.add(value))
queue.addLast(value);
}
/**
* Removes and returns the first value in the queue.
*
* @return The first value in the queue
*/
public long dequeue() {
Long value = queue.removeFirst();
return value != null ? value : 0;
}
/**
* Returns whether the queue is empty.
*
* @return {@code true} if the queue is empty, {@code false} otherwise
*/
public boolean isEmpty() {
return queue.isEmpty();
}
/**
* Clears the deduplication set.
*/
public void clearSet() {
set.clear();
}
}

View File

@ -0,0 +1,6 @@
package net.hoosiertransfer.Alfheim.util;
public enum EnumBoundaryFacing {
IN,
OUT
}

View File

@ -0,0 +1,83 @@
package net.hoosiertransfer.Alfheim.util;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider;
/**
* Represents a slice of a world containing a collection of chunks.
*
* @author Luna Lage (Desoroxxx)
* @author Angeline (@jellysquid)
* @since 1.0
*/
public class WorldChunkSlice {
private static final int DIAMETER = 5;
private static final int RADIUS = DIAMETER / 2;
private final int x, z;
private final Chunk[] chunks;
/**
* Initializes a {@link WorldChunkSlice} object using a given chunk provider and coordinates.
*
* @param chunkProvider The chunk provider to get chunks from
* @param x The X-coordinate of the center chunk
* @param z The Z-coordinate of the center chunk
*/
public WorldChunkSlice(final IChunkProvider chunkProvider, final int x, final int z) {
chunks = new Chunk[DIAMETER * DIAMETER];
for (int xDiff = -RADIUS; xDiff <= RADIUS; xDiff++)
for (int zDiff = -RADIUS; zDiff <= RADIUS; zDiff++)
chunks[((xDiff + RADIUS) * DIAMETER) + (zDiff + RADIUS)] = chunkProvider.getLoadedChunk(x + xDiff, z + zDiff);
this.x = x - RADIUS;
this.z = z - RADIUS;
}
/**
* Checks if all chunks within a radius around a coordinate are loaded.
*
* @param x The X-coordinate to check around
* @param z The Z-coordinate to check around
* @param radius The radius around the coordinates to check
* @return true if all chunks are loaded, false otherwise
*/
public boolean isLoaded(final int x, final int z, final int radius) {
final int xStart = ((x - radius) >> 4) - this.x;
final int zStart = ((z - radius) >> 4) - this.z;
final int xEnd = ((x + radius) >> 4) - this.x;
final int zEnd = ((z + radius) >> 4) - this.z;
for (int currentX = xStart; currentX <= xEnd; ++currentX)
for (int currentZ = zStart; currentZ <= zEnd; ++currentZ)
if (getChunk(currentX, currentZ) == null)
return false;
return true;
}
/**
* Retrieves the chunk that includes the provided world coordinates.
*
* @param x The X-coordinate in the world
* @param z The Z-coordinate in the world
* @return The Chunk object that includes these coordinates
*/
public Chunk getChunkFromWorldCoords(final int x, final int z) {
return getChunk((x >> 4) - this.x, (z >> 4) - this.z);
}
/**
* Retrieves the chunk located at the given coordinates within this chunk slice.
*
* @param x The X-coordinate within the slice
* @param z The Z-coordinate within the slice
* @return The Chunk object at these coordinates
*/
private Chunk getChunk(final int x, final int z) {
return chunks[(x * DIAMETER) + z];
}
}

View File

@ -0,0 +1,44 @@
package net.hoosiertransfer.teavm;
public class ReentrantLock {
private Thread owner = null;
private int lockCount = 0;
public synchronized void lock() {
Thread callingThread = Thread.currentThread();
while (lockCount > 0 && owner != callingThread) {
try {
wait();
} catch (InterruptedException e) {
// Handle exception
}
}
owner = callingThread;
lockCount++;
}
public synchronized void unlock() {
if (Thread.currentThread() == owner) {
lockCount--;
if (lockCount == 0) {
owner = null;
notify();
}
}
}
public synchronized boolean isHeldByCurrentThread() {
return owner == Thread.currentThread();
}
public synchronized boolean tryLock() {
Thread callingThread = Thread.currentThread();
if (lockCount == 0 || owner == callingThread) {
owner = callingThread;
lockCount++;
return true;
} else {
return false;
}
}
}

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import net.hoosiertransfer.Alfheim.ILightingEngineProvider;
import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2; import net.lax1dude.eaglercraft.v1_8.internal.vfs2.VFile2;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
@ -89,6 +90,7 @@ public class EaglerChunkLoader extends AnvilChunkLoader {
@Override @Override
public void saveChunk(World var1, Chunk var2) throws IOException, MinecraftException { public void saveChunk(World var1, Chunk var2) throws IOException, MinecraftException {
var1.alfheim$getLightingEngine().processLightUpdates();
NBTTagCompound chunkData = new NBTTagCompound(); NBTTagCompound chunkData = new NBTTagCompound();
this.writeChunkToNBT(var2, var1, chunkData); this.writeChunkToNBT(var2, var1, chunkData);
NBTTagCompound fileData = new NBTTagCompound(); NBTTagCompound fileData = new NBTTagCompound();

View File

@ -1,6 +1,11 @@
package net.minecraft.block; package net.minecraft.block;
import java.util.List; import java.util.List;
import net.hoosiertransfer.Alfheim.ILightInfoProvider;
import net.hoosiertransfer.Alfheim.ILightLevelProvider;
import net.hoosiertransfer.Alfheim.ILitBlock;
import net.hoosiertransfer.Alfheim.util.ClampUtil;
import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom;
import net.minecraft.block.material.MapColor; import net.minecraft.block.material.MapColor;
@ -30,6 +35,7 @@ import net.minecraft.util.RegistryNamespacedDefaultedByKey;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.util.StatCollector; import net.minecraft.util.StatCollector;
import net.minecraft.util.Vec3; import net.minecraft.util.Vec3;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.Explosion; import net.minecraft.world.Explosion;
import net.minecraft.world.IBlockAccess; import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -54,7 +60,7 @@ import net.minecraft.world.World;
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
public class Block { public class Block implements ILitBlock {
/**+ /**+
* ResourceLocation for the Air block * ResourceLocation for the Air block
*/ */
@ -978,7 +984,13 @@ public class Block {
* opacity * opacity
*/ */
public float getAmbientOcclusionLightValue() { public float getAmbientOcclusionLightValue() {
return this.isBlockNormalCube() ? 0.2F : 1.0F; final byte lightValue = (byte) ClampUtil.clampMinFirst(this.getLightValue() - 1, 0, 15);
if (lightValue == 0) {
return this.isBlockNormalCube() ? 0.2F : 1.0F;
} else {
return 1.0F;
}
} }
/**+ /**+
@ -1660,4 +1672,40 @@ public class Block {
public boolean eaglerShadersShouldRenderGlassHighlights() { public boolean eaglerShadersShouldRenderGlassHighlights() {
return false; return false;
} }
public int alfheim$getLightFor(final IBlockState blockState, final IBlockAccess blockAccess, final EnumSkyBlock lightType, final BlockPos blockPos) {
int lightLevel = ((ILightLevelProvider) blockAccess).alfheim$getLight(lightType, blockPos);
if (lightLevel == 15)
return lightLevel;
if (!blockState.getBlock().getUseNeighborBrightness())
return lightLevel;
for (EnumFacing facing : EnumFacing._VALUES) {
if (((ILightInfoProvider) blockState).alfheim$useNeighborBrightness(facing, blockAccess, blockPos)) {
int opacity = ((ILightInfoProvider) blockState).alfheim$getLightOpacity(facing, blockAccess, blockPos);
final int neighborLightLevel = ((ILightLevelProvider) blockAccess).alfheim$getLight(lightType, blockPos.offset(facing));
if (opacity == 0 && (lightType != EnumSkyBlock.SKY || neighborLightLevel != EnumSkyBlock.SKY.defaultLightValue))
opacity = 1;
lightLevel = Math.max(lightLevel, neighborLightLevel - opacity);
if (lightLevel == 15)
return lightLevel;
}
}
return lightLevel;
}
public boolean alfheim$useNeighborBrightness(final IBlockState blockState, final EnumFacing facing, final IBlockAccess blockAccess, final BlockPos blockPos) {
return facing == EnumFacing.UP;
}
@Override
public int alfheim$getLightOpacity(final IBlockState blockState, final EnumFacing facing, final IBlockAccess blockAccess, final BlockPos blockPos) {
return 0;
}
} }

View File

@ -1,6 +1,8 @@
package net.minecraft.block; package net.minecraft.block;
import java.util.List; import java.util.List;
import net.hoosiertransfer.Alfheim.ILitBlock;
import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom;
import net.minecraft.block.material.Material; import net.minecraft.block.material.Material;
@ -21,6 +23,9 @@ import net.minecraft.util.IStringSerializable;
import net.minecraft.world.IBlockAccess; import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World; import net.minecraft.world.World;
import static net.minecraft.block.BlockSlab.EnumBlockHalf.TOP;
/**+ /**+
* This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code.
* *
@ -41,7 +46,7 @@ import net.minecraft.world.World;
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
public abstract class BlockSlab extends Block { public abstract class BlockSlab extends Block {
public static PropertyEnum<BlockSlab.EnumBlockHalf> HALF; public static PropertyEnum<BlockSlab.EnumBlockHalf> HALF;
public BlockSlab(Material materialIn) { public BlockSlab(Material materialIn) {
@ -204,4 +209,14 @@ public abstract class BlockSlab extends Block {
} }
return super.onBlockActivated(world, blockpos, var3, entityplayer, var5, var6, var7, var8); return super.onBlockActivated(world, blockpos, var3, entityplayer, var5, var6, var7, var8);
} }
public boolean alfheim$useNeighborBrightness(final IBlockState blockState, final EnumFacing facing, final IBlockAccess blockAccess, final BlockPos blockPos) {
if (facing.getAxis() != EnumFacing.Axis.Y)
return false;
if (((BlockSlab) (Object) blockState.getBlock()).isFullCube())
return false;
return facing == (blockState.getValue(HALF) == TOP ? EnumFacing.DOWN : EnumFacing.UP);
}
} }

View File

@ -28,6 +28,8 @@ import net.minecraft.world.Explosion;
import net.minecraft.world.IBlockAccess; import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World; import net.minecraft.world.World;
import static net.minecraft.block.BlockStairs.EnumHalf.TOP;
/**+ /**+
* This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code.
* *
@ -704,4 +706,11 @@ public class BlockStairs extends Block {
return this.name; return this.name;
} }
} }
public boolean alfheim$useNeighborBrightness(final IBlockState blockState, final EnumFacing facing, final IBlockAccess blockAccess, final BlockPos blockPos) {
if (facing.getAxis() != EnumFacing.Axis.Y)
return false;
return facing == (blockState.getValue(HALF) == TOP ? EnumFacing.DOWN : EnumFacing.UP);
}
} }

View File

@ -21,10 +21,16 @@ import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import net.hoosiertransfer.Alfheim.ILightInfoProvider;
import net.hoosiertransfer.Alfheim.ILitBlock;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.properties.IProperty; import net.minecraft.block.properties.IProperty;
import net.minecraft.util.BlockPos;
import net.minecraft.util.Cartesian; import net.minecraft.util.Cartesian;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.MapPopulator; import net.minecraft.util.MapPopulator;
import net.minecraft.world.EnumSkyBlock;
import net.minecraft.world.IBlockAccess;
/**+ /**+
* This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code.
@ -46,7 +52,7 @@ import net.minecraft.util.MapPopulator;
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
public class BlockState { public class BlockState implements ILightInfoProvider {
private static final Joiner COMMA_JOINER = Joiner.on(", "); private static final Joiner COMMA_JOINER = Joiner.on(", ");
private static final Function<IProperty, String> GET_NAME_FUNC = new Function<IProperty, String>() { private static final Function<IProperty, String> GET_NAME_FUNC = new Function<IProperty, String>() {
public String apply(IProperty iproperty) { public String apply(IProperty iproperty) {
@ -114,7 +120,20 @@ public class BlockState {
.add("properties", Iterables.transform(this.properties, GET_NAME_FUNC)).toString(); .add("properties", Iterables.transform(this.properties, GET_NAME_FUNC)).toString();
} }
static class StateImplementation extends BlockStateBase { public int alfheim$getLightFor(IBlockAccess iBlockAccess, EnumSkyBlock lightType, BlockPos blockPos) {
return ((ILitBlock) block).alfheim$getLightFor(((IBlockState) this), iBlockAccess, lightType, blockPos); // i probably need to cast the StateImplementation to IBlockState instead of this
}
public boolean alfheim$useNeighborBrightness(EnumFacing facing, IBlockAccess blockAccess, BlockPos blockPos) {
return ((ILitBlock) block).alfheim$useNeighborBrightness(((IBlockState) this), facing, blockAccess, blockPos); // i probably need to cast the StateImplementation to IBlockState instead of this
}
public int alfheim$getLightOpacity(EnumFacing facing, IBlockAccess blockAccess, BlockPos blockPos) {
return ((ILitBlock) block).alfheim$getLightOpacity(((IBlockState) this), facing, blockAccess, blockPos); // i probably need to cast the StateImplementation to IBlockState instead of this
}
static class StateImplementation extends BlockStateBase implements ILightInfoProvider {
private final Block block; private final Block block;
private final ImmutableMap<IProperty, Comparable> properties; private final ImmutableMap<IProperty, Comparable> properties;
private ImmutableTable<IProperty, Comparable, IBlockState> propertyValueTable; private ImmutableTable<IProperty, Comparable, IBlockState> propertyValueTable;
@ -186,10 +205,26 @@ public class BlockState {
} }
} }
public int getLightValue(IBlockAccess blockAccess, BlockPos pos) {
return this.block.getLightValue();
}
private Map<IProperty, Comparable> getPropertiesWithValue(IProperty property, Comparable value) { private Map<IProperty, Comparable> getPropertiesWithValue(IProperty property, Comparable value) {
HashMap hashmap = Maps.newHashMap(this.properties); HashMap hashmap = Maps.newHashMap(this.properties);
hashmap.put(property, value); hashmap.put(property, value);
return hashmap; return hashmap;
} }
public int alfheim$getLightFor(IBlockAccess iBlockAccess, EnumSkyBlock lightType, BlockPos blockPos) {
return ((ILitBlock) block).alfheim$getLightFor(((IBlockState) this), iBlockAccess, lightType, blockPos);
}
public boolean alfheim$useNeighborBrightness(EnumFacing facing, IBlockAccess blockAccess, BlockPos blockPos) {
return ((ILitBlock) block).alfheim$useNeighborBrightness(((IBlockState) this), facing, blockAccess, blockPos);
}
public int alfheim$getLightOpacity(EnumFacing facing, IBlockAccess blockAccess, BlockPos blockPos) {
return ((ILitBlock) block).alfheim$getLightOpacity(((IBlockState) this), facing, blockAccess, blockPos);
}
} }
} }

View File

@ -6,6 +6,8 @@ import com.google.common.collect.ImmutableMap;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.properties.IProperty; import net.minecraft.block.properties.IProperty;
import net.minecraft.util.BlockPos;
import net.minecraft.world.IBlockAccess;
/**+ /**+
* This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code. * This portion of EaglercraftX contains deobfuscated Minecraft 1.8 source code.
@ -42,4 +44,6 @@ public interface IBlockState {
ImmutableMap<IProperty, Comparable> getProperties(); ImmutableMap<IProperty, Comparable> getProperties();
Block getBlock(); Block getBlock();
int getLightValue(IBlockAccess blockAccess, BlockPos pos);
} }

View File

@ -20,6 +20,7 @@ import org.apache.commons.lang3.Validate;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import net.hoosiertransfer.CullingMod; import net.hoosiertransfer.CullingMod;
import net.hoosiertransfer.Alfheim.ILightUpdatesProcessor;
import net.lax1dude.eaglercraft.v1_8.Display; import net.lax1dude.eaglercraft.v1_8.Display;
import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion; import net.lax1dude.eaglercraft.v1_8.EaglerXBungeeVersion;
@ -328,6 +329,10 @@ public class Minecraft implements IThreadListener {
Bootstrap.register(); Bootstrap.register();
} }
public boolean isCallingFromMinecraftThread() {
return Thread.currentThread() == this.mcThread;
}
public void run() { public void run() {
CullingMod.intialize(); CullingMod.intialize();
this.running = true; this.running = true;
@ -1616,6 +1621,9 @@ public class Minecraft implements IThreadListener {
if (!this.isGamePaused) { if (!this.isGamePaused) {
this.renderGlobal.updateClouds(); this.renderGlobal.updateClouds();
} }
this.mcProfiler.endStartSection("processRenderGlobalLightUpdates");
if (!isGamePaused)
((ILightUpdatesProcessor) renderGlobal).alfheim$processLightUpdates();
this.mcProfiler.endStartSection("level"); this.mcProfiler.endStartSection("level");
if (!this.isGamePaused) { if (!this.isGamePaused) {

View File

@ -102,6 +102,10 @@ public class ChunkProviderClient implements IChunkProvider {
return chunk == null ? this.blankChunk : chunk; return chunk == null ? this.blankChunk : chunk;
} }
public Chunk getLoadedChunk(int var1, int var2) {
return (Chunk) this.chunkMapping.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(var1, var2));
}
/**+ /**+
* Two modes of operation: if passed true, save all Chunks in * Two modes of operation: if passed true, save all Chunks in
* one go. If passed false, save up to two chunks. Return true * one go. If passed false, save up to two chunks. Return true

View File

@ -3,6 +3,7 @@ package net.minecraft.client.renderer;
import java.util.BitSet; import java.util.BitSet;
import java.util.List; import java.util.List;
import net.hoosiertransfer.Alfheim.util.ClampUtil;
import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.GlStateManager;
import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer; import net.lax1dude.eaglercraft.v1_8.opengl.WorldRenderer;
import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager; import net.lax1dude.eaglercraft.v1_8.opengl.ext.deferred.DeferredStateManager;
@ -53,7 +54,7 @@ public class BlockModelRenderer {
public boolean renderModel(IBlockAccess blockAccessIn, IBakedModel modelIn, IBlockState blockStateIn, public boolean renderModel(IBlockAccess blockAccessIn, IBakedModel modelIn, IBlockState blockStateIn,
BlockPos blockPosIn, WorldRenderer worldRendererIn, boolean checkSides) { BlockPos blockPosIn, WorldRenderer worldRendererIn, boolean checkSides) {
boolean flag = Minecraft.isAmbientOcclusionEnabled() && blockStateIn.getBlock().getLightValue() == 0 boolean flag = Minecraft.isAmbientOcclusionEnabled() && ClampUtil.clampMinFirst(blockStateIn.getLightValue(blockAccessIn, blockPosIn) -1, 0, 15) == 0
&& modelIn.isAmbientOcclusion(); && modelIn.isAmbientOcclusion();
try { try {

View File

@ -8,6 +8,8 @@ import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.hoosiertransfer.Alfheim.ILightUpdatesProcessor;
import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom;
import net.lax1dude.eaglercraft.v1_8.HString; import net.lax1dude.eaglercraft.v1_8.HString;
import net.lax1dude.eaglercraft.v1_8.Keyboard; import net.lax1dude.eaglercraft.v1_8.Keyboard;
@ -115,7 +117,7 @@ import net.minecraft.world.chunk.Chunk;
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListener { public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListener, ILightUpdatesProcessor {
private static final Logger logger = LogManager.getLogger(); private static final Logger logger = LogManager.getLogger();
private static final ResourceLocation locationMoonPhasesPng = new ResourceLocation( private static final ResourceLocation locationMoonPhasesPng = new ResourceLocation(
"textures/environment/moon_phases.png"); "textures/environment/moon_phases.png");
@ -182,6 +184,8 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene
private double prevRenderSortZ; private double prevRenderSortZ;
private boolean displayListEntitiesDirty = true; private boolean displayListEntitiesDirty = true;
private final Set<BlockPos> setLightUpdates = Sets.<BlockPos>newHashSet();
public RenderGlobal(Minecraft mcIn) { public RenderGlobal(Minecraft mcIn) {
this.mc = mcIn; this.mc = mcIn;
this.renderManager = mcIn.getRenderManager(); this.renderManager = mcIn.getRenderManager();
@ -2108,6 +2112,7 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene
} }
public void notifyLightSet(BlockPos blockpos) { public void notifyLightSet(BlockPos blockpos) {
this.setLightUpdates.add(blockpos.toImmutable());
int i = blockpos.getX(); int i = blockpos.getX();
int j = blockpos.getY(); int j = blockpos.getY();
int k = blockpos.getZ(); int k = blockpos.getZ();
@ -2514,4 +2519,33 @@ public class RenderGlobal implements IWorldAccess, IResourceManagerReloadListene
return "" + Minecraft.getDebugFPS() + "fps | C: " + j + "/" + i + ", E: " + this.countEntitiesRendered + "+" + k return "" + Minecraft.getDebugFPS() + "fps | C: " + j + "/" + i + ", E: " + this.countEntitiesRendered + "+" + k
+ ", " + renderDispatcher.getDebugInfo(); + ", " + renderDispatcher.getDebugInfo();
} }
public void alfheim$processLightUpdates() {
if (setLightUpdates.isEmpty())
return;
final Iterator<BlockPos> iterator = setLightUpdates.iterator();
final float lightUpdateLimit = 2048 + ((float) setLightUpdates.size() / 4); // Todo: Rework this once again, this is currently pretty dumb.
// It fixed the issue where the FPS on lower end hardware would plummet for a few seconds.
// But it also reduced how smooth the frame rate was on higher end hardware.
// Updating blocks is costly and will take a long time, this is why lower end hardware plummets for a few seconds.
// Higher end hardware instead has somewhat of a FPS "buffer" which can handle it fine over multiple frames thus reducing frame-time spikes.
// The technically the best way to do this (that I hadn't though of before) was to add current "FPS" to the equation.
// If the FPS is low more updates would be done in one frame if it is high we can afford spreading light updates over multiple frames.
short lightUpdatesProcessed = 0;
while (iterator.hasNext() && lightUpdatesProcessed < lightUpdateLimit) {
final BlockPos blockpos = iterator.next();
iterator.remove();
final int x = blockpos.getX();
final int y = blockpos.getY();
final int z = blockpos.getZ();
markBlocksForUpdate(x, y, z, x, y, z);
lightUpdatesProcessed++;
}
}
} }

View File

@ -207,7 +207,7 @@ public class GameSettings {
public boolean enableSound = true; public boolean enableSound = true;
public boolean disableAlpha = true; public boolean disableAlpha = false;
public boolean skipHandRender = false; public boolean skipHandRender = false;

View File

@ -5,6 +5,7 @@ import java.util.ArrayList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import net.hoosiertransfer.Alfheim.ILightingEngineProvider;
import net.minecraft.network.Packet; import net.minecraft.network.Packet;
import net.minecraft.network.PacketBuffer; import net.minecraft.network.PacketBuffer;
import net.minecraft.network.play.INetHandlerPlayClient; import net.minecraft.network.play.INetHandlerPlayClient;
@ -44,6 +45,7 @@ public class S21PacketChunkData implements Packet<INetHandlerPlayClient> {
this.chunkX = chunkIn.xPosition; this.chunkX = chunkIn.xPosition;
this.chunkZ = chunkIn.zPosition; this.chunkZ = chunkIn.zPosition;
this.field_149279_g = parFlag; this.field_149279_g = parFlag;
chunkIn.alfheim$getLightingEngine().processLightUpdates();
this.extractedData = func_179756_a(chunkIn, parFlag, !chunkIn.getWorld().provider.getHasNoSky(), parInt1); this.extractedData = func_179756_a(chunkIn, parFlag, !chunkIn.getWorld().provider.getHasNoSky(), parInt1);
} }

View File

@ -1,8 +1,10 @@
package net.minecraft.util; package net.minecraft.util;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import com.google.common.collect.AbstractIterator; import com.google.common.collect.AbstractIterator;
import com.google.common.collect.Lists;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
@ -317,6 +319,11 @@ public class BlockPos extends Vec3i {
return new BlockPos(i, j, k); return new BlockPos(i, j, k);
} }
public BlockPos toImmutable()
{
return this;
}
/**+ /**+
* Create an Iterable that returns all positions in the box * Create an Iterable that returns all positions in the box
* specified by the given corners * specified by the given corners
@ -409,7 +416,7 @@ public class BlockPos extends Vec3i {
}; };
} }
public static final class MutableBlockPos extends BlockPos { public static class MutableBlockPos extends BlockPos {
public MutableBlockPos() { public MutableBlockPos() {
this(0, 0, 0); this(0, 0, 0);
@ -431,6 +438,36 @@ public class BlockPos extends Vec3i {
return this.z; return this.z;
} }
public BlockPos.MutableBlockPos setPos(Entity entityIn)
{
return this.setPos(entityIn.posX, entityIn.posY, entityIn.posZ);
}
public BlockPos.MutableBlockPos setPos(double xIn, double yIn, double zIn)
{
return this.setPos(MathHelper.floor_double(xIn), MathHelper.floor_double(yIn), MathHelper.floor_double(zIn));
}
public BlockPos.MutableBlockPos setPos(Vec3i vec)
{
return this.setPos(vec.getX(), vec.getY(), vec.getZ());
}
public BlockPos.MutableBlockPos move(EnumFacing facing)
{
return this.move(facing, 1);
}
public BlockPos.MutableBlockPos move(EnumFacing facing, int p_189534_2_)
{
return this.setPos(this.x + facing.getFrontOffsetX() * p_189534_2_, this.y + facing.getFrontOffsetY() * p_189534_2_, this.z + facing.getFrontOffsetZ() * p_189534_2_);
}
public void setY(int yIn)
{
this.y = yIn;
}
public BlockPos.MutableBlockPos setPos(int x_, int y_, int z_) { public BlockPos.MutableBlockPos setPos(int x_, int y_, int z_) {
this.x = x_; this.x = x_;
this.y = y_; this.y = y_;
@ -444,5 +481,104 @@ public class BlockPos extends Vec3i {
this.z = parInt3; this.z = parInt3;
return this; return this;
} }
public BlockPos toImmutable()
{
return new BlockPos(this);
}
} }
public static final class PooledMutableBlockPos extends BlockPos.MutableBlockPos
{
private boolean released;
private static final List<BlockPos.PooledMutableBlockPos> POOL = Lists.<BlockPos.PooledMutableBlockPos>newArrayList();
private PooledMutableBlockPos(int xIn, int yIn, int zIn)
{
super(xIn, yIn, zIn);
}
public static BlockPos.PooledMutableBlockPos retain()
{
return retain(0, 0, 0);
}
public static BlockPos.PooledMutableBlockPos retain(double xIn, double yIn, double zIn)
{
return retain(MathHelper.floor_double(xIn), MathHelper.floor_double(yIn), MathHelper.floor_double(zIn));
}
public static BlockPos.PooledMutableBlockPos retain(Vec3i vec)
{
return retain(vec.getX(), vec.getY(), vec.getZ());
}
public static BlockPos.PooledMutableBlockPos retain(int xIn, int yIn, int zIn)
{
synchronized (POOL)
{
if (!POOL.isEmpty())
{
BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos = (BlockPos.PooledMutableBlockPos)POOL.remove(POOL.size() - 1);
if (blockpos$pooledmutableblockpos != null && blockpos$pooledmutableblockpos.released)
{
blockpos$pooledmutableblockpos.released = false;
blockpos$pooledmutableblockpos.setPos(xIn, yIn, zIn);
return blockpos$pooledmutableblockpos;
}
}
}
return new BlockPos.PooledMutableBlockPos(xIn, yIn, zIn);
}
public void release()
{
synchronized (POOL)
{
if (POOL.size() < 100)
{
POOL.add(this);
}
this.released = true;
}
}
public BlockPos.PooledMutableBlockPos setPos(int xIn, int yIn, int zIn)
{
if (this.released)
{
this.released = false;
}
return (BlockPos.PooledMutableBlockPos)super.setPos(xIn, yIn, zIn);
}
public BlockPos.PooledMutableBlockPos setPos(Entity entityIn)
{
return (BlockPos.PooledMutableBlockPos)super.setPos(entityIn);
}
public BlockPos.PooledMutableBlockPos setPos(double xIn, double yIn, double zIn)
{
return (BlockPos.PooledMutableBlockPos)super.setPos(xIn, yIn, zIn);
}
public BlockPos.PooledMutableBlockPos setPos(Vec3i vec)
{
return (BlockPos.PooledMutableBlockPos)super.setPos(vec);
}
public BlockPos.PooledMutableBlockPos move(EnumFacing facing)
{
return (BlockPos.PooledMutableBlockPos)super.move(facing);
}
public BlockPos.PooledMutableBlockPos move(EnumFacing facing, int p_189534_2_)
{
return (BlockPos.PooledMutableBlockPos)super.move(facing, p_189534_2_);
}
}
} }

View File

@ -52,7 +52,7 @@ public enum EnumFacing implements IStringSerializable {
/**+ /**+
* All Facings with horizontal axis in order S-W-N-E * All Facings with horizontal axis in order S-W-N-E
*/ */
private static final EnumFacing[] HORIZONTALS = new EnumFacing[4]; public static final EnumFacing[] HORIZONTALS = new EnumFacing[4];
private static final Map<String, EnumFacing> NAME_LOOKUP = Maps.newHashMap(); private static final Map<String, EnumFacing> NAME_LOOKUP = Maps.newHashMap();
private EnumFacing(int indexIn, int oppositeIn, int horizontalIndexIn, String nameIn, private EnumFacing(int indexIn, int oppositeIn, int horizontalIndexIn, String nameIn,
@ -208,6 +208,18 @@ public enum EnumFacing implements IStringSerializable {
} }
} }
public int getXOffset() {
return this.directionVec.getX();
}
public int getYOffset() {
return this.directionVec.getY();
}
public int getZOffset() {
return this.directionVec.getZ();
}
/**+ /**+
* Returns a offset that addresses the block in front of this * Returns a offset that addresses the block in front of this
* facing. * facing.

View File

@ -1,5 +1,7 @@
package net.minecraft.world; package net.minecraft.world;
import net.hoosiertransfer.Alfheim.ILightInfoProvider;
import net.hoosiertransfer.Alfheim.ILightLevelProvider;
import net.minecraft.block.material.Material; import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState; import net.minecraft.block.state.IBlockState;
import net.minecraft.init.Blocks; import net.minecraft.init.Blocks;
@ -29,7 +31,7 @@ import net.minecraft.world.chunk.Chunk;
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
public class ChunkCache implements IBlockAccess { public class ChunkCache implements IBlockAccess, ILightLevelProvider {
protected int chunkX; protected int chunkX;
protected int chunkZ; protected int chunkZ;
protected Chunk[][] chunkArray; protected Chunk[][] chunkArray;
@ -105,34 +107,35 @@ public class ChunkCache implements IBlockAccess {
} }
private int getLightForExt(EnumSkyBlock pos, BlockPos parBlockPos) { private int getLightForExt(EnumSkyBlock pos, BlockPos parBlockPos) {
if (pos == EnumSkyBlock.SKY && this.worldObj.provider.getHasNoSky()) { return ((ILightInfoProvider) getBlockState(parBlockPos)).alfheim$getLightFor(((ChunkCache) (Object) this), pos, parBlockPos);
return Chunk.getNoSkyLightValue(); // if (pos == EnumSkyBlock.SKY && this.worldObj.provider.getHasNoSky()) {
} else if (parBlockPos.getY() >= 0 && parBlockPos.getY() < 256) { // return Chunk.getNoSkyLightValue();
if (this.getBlockState(parBlockPos).getBlock().getUseNeighborBrightness()) { // } else if (parBlockPos.getY() >= 0 && parBlockPos.getY() < 256) {
int l = 0; // if (this.getBlockState(parBlockPos).getBlock().getUseNeighborBrightness()) {
// int l = 0;
EnumFacing[] facings = EnumFacing._VALUES; // EnumFacing[] facings = EnumFacing._VALUES;
BlockPos tmp = new BlockPos(0, 0, 0); // BlockPos tmp = new BlockPos(0, 0, 0);
for (int i = 0; i < facings.length; ++i) { // for (int i = 0; i < facings.length; ++i) {
int k = this.getLightFor(pos, parBlockPos.offsetEvenFaster(facings[i], tmp)); // int k = this.getLightFor(pos, parBlockPos.offsetEvenFaster(facings[i], tmp));
if (k > l) { // if (k > l) {
l = k; // l = k;
} // }
if (l >= 15) { // if (l >= 15) {
return l; // return l;
} // }
} // }
return l; // return l;
} else { // } else {
int i = (parBlockPos.getX() >> 4) - this.chunkX; // int i = (parBlockPos.getX() >> 4) - this.chunkX;
int j = (parBlockPos.getZ() >> 4) - this.chunkZ; // int j = (parBlockPos.getZ() >> 4) - this.chunkZ;
return this.chunkArray[i][j].getLightFor(pos, parBlockPos); // return this.chunkArray[i][j].getLightFor(pos, parBlockPos);
} // }
} else { // } else {
return pos.defaultLightValue; // return pos.defaultLightValue;
} // }
} }
/**+ /**+
@ -163,4 +166,8 @@ public class ChunkCache implements IBlockAccess {
public WorldType getWorldType() { public WorldType getWorldType() {
return this.worldObj.getWorldType(); return this.worldObj.getWorldType();
} }
public int alfheim$getLight(final EnumSkyBlock lightType, final BlockPos blockPos) {
return getLightFor(lightType, blockPos);
}
} }

View File

@ -10,6 +10,10 @@ import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import net.hoosiertransfer.Alfheim.ILightInfoProvider;
import net.hoosiertransfer.Alfheim.ILightLevelProvider;
import net.hoosiertransfer.Alfheim.ILightingEngineProvider;
import net.hoosiertransfer.Alfheim.lighting.LightingEngine;
import net.lax1dude.eaglercraft.v1_8.EagRuntime; import net.lax1dude.eaglercraft.v1_8.EagRuntime;
import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom;
import java.util.Set; import java.util.Set;
@ -80,7 +84,7 @@ import net.minecraft.world.storage.WorldInfo;
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
public abstract class World implements IBlockAccess { public abstract class World implements IBlockAccess, ILightingEngineProvider, ILightLevelProvider {
private int field_181546_a = 63; private int field_181546_a = 63;
protected boolean scheduledUpdatesAreImmediate; protected boolean scheduledUpdatesAreImmediate;
/**+ /**+
@ -150,6 +154,8 @@ public abstract class World implements IBlockAccess {
int[] lightUpdateBlockList; int[] lightUpdateBlockList;
public final boolean isRemote; public final boolean isRemote;
private LightingEngine alfheim$lightingEngine;
protected World(ISaveHandler saveHandlerIn, WorldInfo info, WorldProvider providerIn, Profiler profilerIn, protected World(ISaveHandler saveHandlerIn, WorldInfo info, WorldProvider providerIn, Profiler profilerIn,
boolean client) { boolean client) {
this.ambientTickCountdown = this.rand.nextInt(12000); this.ambientTickCountdown = this.rand.nextInt(12000);
@ -162,6 +168,8 @@ public abstract class World implements IBlockAccess {
this.provider = providerIn; this.provider = providerIn;
this.worldBorder = providerIn.getWorldBorder(); this.worldBorder = providerIn.getWorldBorder();
this.isRemote = client; this.isRemote = client;
alfheim$lightingEngine = new LightingEngine((World) (Object) this);
} }
public World init() { public World init() {
@ -529,43 +537,12 @@ public abstract class World implements IBlockAccess {
} }
public int getLight(BlockPos pos, boolean checkNeighbors) { public int getLight(BlockPos pos, boolean checkNeighbors) {
if (pos.getX() >= -30000000 && pos.getZ() >= -30000000 && pos.getX() < 30000000 && pos.getZ() < 30000000) { if (!checkNeighbors)
if (checkNeighbors && this.getBlockState(pos).getBlock().getUseNeighborBrightness()) { return getLight(pos);
int i1 = this.getLight(pos.up(), false);
int i = this.getLight(pos.east(), false);
int j = this.getLight(pos.west(), false);
int k = this.getLight(pos.south(), false);
int l = this.getLight(pos.north(), false);
if (i > i1) {
i1 = i;
}
if (j > i1) { final IBlockState blockState = getBlockState(pos);
i1 = j;
}
if (k > i1) { return Math.max(((ILightInfoProvider) blockState).alfheim$getLightFor(((World) (Object) this), EnumSkyBlock.BLOCK, pos), ((ILightInfoProvider) blockState).alfheim$getLightFor(((World) (Object) this), EnumSkyBlock.SKY, pos) - skylightSubtracted);
i1 = k;
}
if (l > i1) {
i1 = l;
}
return i1;
} else if (pos.getY() < 0) {
return 0;
} else {
if (pos.getY() >= 256) {
pos = new BlockPos(pos.getX(), 255, pos.getZ());
}
Chunk chunk = this.getChunkFromBlockCoords(pos);
return chunk.getLightSubtracted(pos, this.skylightSubtracted);
}
} else {
return 15;
}
} }
/**+ /**+
@ -605,45 +582,7 @@ public abstract class World implements IBlockAccess {
} }
public int getLightFromNeighborsFor(EnumSkyBlock type, BlockPos pos) { public int getLightFromNeighborsFor(EnumSkyBlock type, BlockPos pos) {
if (this.provider.getHasNoSky() && type == EnumSkyBlock.SKY) { return ((ILightInfoProvider) getBlockState(pos)).alfheim$getLightFor(((World) (Object) this), type, pos);
return Chunk.getNoSkyLightValue();
} else {
if (pos.getY() < 0) {
pos = new BlockPos(pos.getX(), 0, pos.getZ());
}
if (!this.isValid(pos)) {
return type.defaultLightValue;
} else if (!this.isBlockLoaded(pos)) {
return type.defaultLightValue;
} else if (this.getBlockState(pos).getBlock().getUseNeighborBrightness()) {
int i1 = this.getLightFor(type, pos.up());
int i = this.getLightFor(type, pos.east());
int j = this.getLightFor(type, pos.west());
int k = this.getLightFor(type, pos.south());
int l = this.getLightFor(type, pos.north());
if (i > i1) {
i1 = i;
}
if (j > i1) {
i1 = j;
}
if (k > i1) {
i1 = k;
}
if (l > i1) {
i1 = l;
}
return i1;
} else {
Chunk chunk = this.getChunkFromBlockCoords(pos);
return chunk.getLightFor(type, pos);
}
}
} }
public int getLightFor(EnumSkyBlock type, BlockPos pos) { public int getLightFor(EnumSkyBlock type, BlockPos pos) {
@ -2325,118 +2264,9 @@ public abstract class World implements IBlockAccess {
} }
public boolean checkLightFor(EnumSkyBlock lightType, BlockPos pos) { public boolean checkLightFor(EnumSkyBlock lightType, BlockPos pos) {
if (!this.isAreaLoaded(pos, 17, false)) { alfheim$lightingEngine.scheduleLightUpdate(lightType, pos);
return false;
} else {
int i = 0;
int j = 0;
this.theProfiler.startSection("getBrightness");
int k = this.getLightFor(lightType, pos);
int l = this.getRawLight(pos, lightType);
int i1 = pos.getX();
int j1 = pos.getY();
int k1 = pos.getZ();
if (l > k) {
this.lightUpdateBlockList[j++] = 133152;
} else if (l < k) {
this.lightUpdateBlockList[j++] = 133152 | k << 18;
while (i < j) { return true;
int l1 = this.lightUpdateBlockList[i++];
int i2 = (l1 & 63) - 32 + i1;
int j2 = (l1 >> 6 & 63) - 32 + j1;
int k2 = (l1 >> 12 & 63) - 32 + k1;
int l2 = l1 >> 18 & 15;
BlockPos blockpos = new BlockPos(i2, j2, k2);
int i3 = this.getLightFor(lightType, blockpos);
if (i3 == l2) {
this.setLightFor(lightType, blockpos, 0);
if (l2 > 0) {
int j3 = MathHelper.abs_int(i2 - i1);
int k3 = MathHelper.abs_int(j2 - j1);
int l3 = MathHelper.abs_int(k2 - k1);
if (j3 + k3 + l3 < 17) {
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
EnumFacing[] facings = EnumFacing._VALUES;
for (int m = 0; m < facings.length; ++m) {
EnumFacing enumfacing = facings[m];
int i4 = i2 + enumfacing.getFrontOffsetX();
int j4 = j2 + enumfacing.getFrontOffsetY();
int k4 = k2 + enumfacing.getFrontOffsetZ();
blockpos$mutableblockpos.func_181079_c(i4, j4, k4);
int l4 = Math.max(1,
this.getBlockState(blockpos$mutableblockpos).getBlock().getLightOpacity());
i3 = this.getLightFor(lightType, blockpos$mutableblockpos);
if (i3 == l2 - l4 && j < this.lightUpdateBlockList.length) {
this.lightUpdateBlockList[j++] = i4 - i1 + 32 | j4 - j1 + 32 << 6
| k4 - k1 + 32 << 12 | l2 - l4 << 18;
}
}
}
}
}
}
i = 0;
}
this.theProfiler.endSection();
this.theProfiler.startSection("checkedPosition < toCheckCount");
while (i < j) {
int i5 = this.lightUpdateBlockList[i++];
int j5 = (i5 & 63) - 32 + i1;
int k5 = (i5 >> 6 & 63) - 32 + j1;
int l5 = (i5 >> 12 & 63) - 32 + k1;
BlockPos blockpos1 = new BlockPos(j5, k5, l5);
int i6 = this.getLightFor(lightType, blockpos1);
int j6 = this.getRawLight(blockpos1, lightType);
if (j6 != i6) {
this.setLightFor(lightType, blockpos1, j6);
if (j6 > i6) {
int k6 = Math.abs(j5 - i1);
int l6 = Math.abs(k5 - j1);
int i7 = Math.abs(l5 - k1);
boolean flag = j < this.lightUpdateBlockList.length - 6;
if (k6 + l6 + i7 < 17 && flag) {
if (this.getLightFor(lightType, blockpos1.west()) < j6) {
this.lightUpdateBlockList[j++] = j5 - 1 - i1 + 32 + (k5 - j1 + 32 << 6)
+ (l5 - k1 + 32 << 12);
}
if (this.getLightFor(lightType, blockpos1.east()) < j6) {
this.lightUpdateBlockList[j++] = j5 + 1 - i1 + 32 + (k5 - j1 + 32 << 6)
+ (l5 - k1 + 32 << 12);
}
if (this.getLightFor(lightType, blockpos1.down()) < j6) {
this.lightUpdateBlockList[j++] = j5 - i1 + 32 + (k5 - 1 - j1 + 32 << 6)
+ (l5 - k1 + 32 << 12);
}
if (this.getLightFor(lightType, blockpos1.up()) < j6) {
this.lightUpdateBlockList[j++] = j5 - i1 + 32 + (k5 + 1 - j1 + 32 << 6)
+ (l5 - k1 + 32 << 12);
}
if (this.getLightFor(lightType, blockpos1.north()) < j6) {
this.lightUpdateBlockList[j++] = j5 - i1 + 32 + (k5 - j1 + 32 << 6)
+ (l5 - 1 - k1 + 32 << 12);
}
if (this.getLightFor(lightType, blockpos1.south()) < j6) {
this.lightUpdateBlockList[j++] = j5 - i1 + 32 + (k5 - j1 + 32 << 6)
+ (l5 + 1 - k1 + 32 << 12);
}
}
}
}
}
this.theProfiler.endSection();
return true;
}
} }
/**+ /**+
@ -3210,4 +3040,12 @@ public abstract class World implements IBlockAccess {
short short1 = 128; short short1 = 128;
return i >= -short1 && i <= short1 && j >= -short1 && j <= short1; return i >= -short1 && i <= short1 && j >= -short1 && j <= short1;
} }
public LightingEngine alfheim$getLightingEngine() {
return alfheim$lightingEngine;
}
public int alfheim$getLight(final EnumSkyBlock lightType, final BlockPos blockPos) {
return getLightFor(lightType, blockPos);
}
} }

View File

@ -6,6 +6,13 @@ import java.util.Arrays;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.hoosiertransfer.Alfheim.IChunkLightingData;
import net.hoosiertransfer.Alfheim.ILightingEngineProvider;
import net.hoosiertransfer.Alfheim.lighting.LightUtil;
import net.hoosiertransfer.Alfheim.lighting.LightingEngine;
import net.hoosiertransfer.Alfheim.util.EnumBoundaryFacing;
import net.hoosiertransfer.Alfheim.util.WorldChunkSlice;
import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom; import net.lax1dude.eaglercraft.v1_8.EaglercraftRandom;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -81,6 +88,15 @@ public class Chunk {
private int queuedLightChecks; private int queuedLightChecks;
private List<BlockPos> tileEntityPosQueue; private List<BlockPos> tileEntityPosQueue;
public static final ExtendedBlockStorage NULL_BLOCK_STORAGE = null;
private LightingEngine alfheim$lightingEngine;
private boolean alfheim$isLightInitialized;
private short[] alfheim$neighborLightChecks;
private static final EnumSkyBlock[] ENUM_SKY_BLOCK_VALUES = EnumSkyBlock.values();
private static final EnumFacing[] ENUM_FACING_HORIZONTAL = EnumFacing.Plane.HORIZONTAL.facings();
private static final EnumFacing.AxisDirection[] ENUM_AXIS_DIRECTION_VALUES = EnumFacing.AxisDirection.values();
public Chunk(World worldIn, int x, int z) { public Chunk(World worldIn, int x, int z) {
this.storageArrays = new ExtendedBlockStorage[16]; this.storageArrays = new ExtendedBlockStorage[16];
this.blockBiomeArray = new byte[256]; this.blockBiomeArray = new byte[256];
@ -101,6 +117,8 @@ public class Chunk {
Arrays.fill(this.precipitationHeightMap, -999); Arrays.fill(this.precipitationHeightMap, -999);
Arrays.fill(this.blockBiomeArray, (byte) -1); Arrays.fill(this.blockBiomeArray, (byte) -1);
alfheim$lightingEngine = worldIn.alfheim$getLightingEngine();
} }
public Chunk(World worldIn, ChunkPrimer primer, int x, int z) { public Chunk(World worldIn, ChunkPrimer primer, int x, int z) {
@ -231,8 +249,10 @@ public class Chunk {
ExtendedBlockStorage extendedblockstorage = this.storageArrays[i1 >> 4]; ExtendedBlockStorage extendedblockstorage = this.storageArrays[i1 >> 4];
if (extendedblockstorage != null) { if (extendedblockstorage != null) {
extendedblockstorage.setExtSkylightValue(j, i1 & 15, k, k1); extendedblockstorage.setExtSkylightValue(j, i1 & 15, k, k1);
this.worldObj.notifyLightSet( alfheim$initSkylightForSection(storageArrays[i1 >> 4]);
new BlockPos((this.xPosition << 4) + j, i1, (this.zPosition << 4) + k)); // do i comment this out?
// this.worldObj.notifyLightSet(
// new BlockPos((this.xPosition << 4) + j, i1, (this.zPosition << 4) + k));
} }
} }
@ -261,44 +281,22 @@ public class Chunk {
} }
private void recheckGaps(boolean parFlag) { private void recheckGaps(boolean parFlag) {
this.worldObj.theProfiler.startSection("recheckGaps"); if (!worldObj.isAreaLoaded(new BlockPos((xPosition << 4) + 8, 0, (zPosition << 4) + 8), 16))
if (this.worldObj.isAreaLoaded(new BlockPos(this.xPosition * 16 + 8, 0, this.zPosition * 16 + 8), 16)) { return;
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
if (this.updateSkylightColumns[i + j * 16]) {
this.updateSkylightColumns[i + j * 16] = false;
int k = this.getHeightValue(i, j);
int l = this.xPosition * 16 + i;
int i1 = this.zPosition * 16 + j;
int j1 = Integer.MAX_VALUE;
EnumFacing[] facings = EnumFacing.Plane.HORIZONTAL.facingsArray; final WorldChunkSlice slice = new WorldChunkSlice(worldObj.getChunkProvider(), xPosition, zPosition);
for (int m = 0; m < facings.length; ++m) {
EnumFacing enumfacing = facings[m];
j1 = Math.min(j1, this.worldObj.getChunksLowestHorizon(l + enumfacing.getFrontOffsetX(),
i1 + enumfacing.getFrontOffsetZ()));
}
this.checkSkylightNeighborHeight(l, i1, j1); for (int x = 0; x < 16; ++x) {
for (int z = 0; z < 16; ++z) {
if (!alfheim$recheckGapsForColumn(slice, x, z))
continue;
for (int m = 0; m < facings.length; ++m) { if (parFlag)
EnumFacing enumfacing1 = facings[m]; return;
this.checkSkylightNeighborHeight(l + enumfacing1.getFrontOffsetX(), }
i1 + enumfacing1.getFrontOffsetZ(), k); }
}
if (parFlag) { isGapLightingUpdated = false;
this.worldObj.theProfiler.endSection();
return;
}
}
}
}
this.isGapLightingUpdated = false;
}
this.worldObj.theProfiler.endSection();
} }
/**+ /**+
@ -331,91 +329,24 @@ public class Chunk {
* sky-light for a given block inside a chunk. * sky-light for a given block inside a chunk.
*/ */
private void relightBlock(int x, int y, int z) { private void relightBlock(int x, int y, int z) {
int i = this.heightMap[z << 4 | x] & 255; int heightMapY = heightMap[z << 4 | x] & 255;
int j = i; int newHeightMapY = Math.max(y, heightMapY);
if (y > i) {
j = y;
}
while (j > 0 && this.getBlockLightOpacity(x, j - 1, z) == 0) { while (newHeightMapY > 0 && getBlockLightOpacity(x, newHeightMapY - 1, z) == 0)
--j; --newHeightMapY;
}
if (j != i) { if (newHeightMapY == heightMapY)
this.worldObj.markBlocksDirtyVertical(x + this.xPosition * 16, z + this.zPosition * 16, j, i); return;
this.heightMap[z << 4 | x] = j;
int k = this.xPosition * 16 + x;
int l = this.zPosition * 16 + z;
if (!this.worldObj.provider.getHasNoSky()) {
if (j < i) {
for (int j1 = j; j1 < i; ++j1) {
ExtendedBlockStorage extendedblockstorage2 = this.storageArrays[j1 >> 4];
if (extendedblockstorage2 != null) {
extendedblockstorage2.setExtSkylightValue(x, j1 & 15, z, 15);
this.worldObj.notifyLightSet(
new BlockPos((this.xPosition << 4) + x, j1, (this.zPosition << 4) + z));
}
}
} else {
for (int i1 = i; i1 < j; ++i1) {
ExtendedBlockStorage extendedblockstorage = this.storageArrays[i1 >> 4];
if (extendedblockstorage != null) {
extendedblockstorage.setExtSkylightValue(x, i1 & 15, z, 0);
this.worldObj.notifyLightSet(
new BlockPos((this.xPosition << 4) + x, i1, (this.zPosition << 4) + z));
}
}
}
int k1 = 15; heightMap[z << 4 | x] = newHeightMapY;
while (j > 0 && k1 > 0) { if (!worldObj.provider.getHasNoSky())
--j; alfheim$relightSkylightColumn(x, z, heightMapY, newHeightMapY);
int i2 = this.getBlockLightOpacity(x, j, z);
if (i2 == 0) {
i2 = 1;
}
k1 -= i2; final int heightMapY1 = heightMap[z << 4 | x];
if (k1 < 0) {
k1 = 0;
}
ExtendedBlockStorage extendedblockstorage1 = this.storageArrays[j >> 4]; if (heightMapY1 < heightMapMinimum)
if (extendedblockstorage1 != null) { heightMapMinimum = heightMapY1;
extendedblockstorage1.setExtSkylightValue(x, j & 15, z, k1);
}
}
}
int l1 = this.heightMap[z << 4 | x];
int j2 = i;
int k2 = l1;
if (l1 < i) {
j2 = l1;
k2 = i;
}
if (l1 < this.heightMapMinimum) {
this.heightMapMinimum = l1;
}
if (!this.worldObj.provider.getHasNoSky()) {
EnumFacing[] facings = EnumFacing.Plane.HORIZONTAL.facingsArray;
for (int m = 0; m < facings.length; ++m) {
EnumFacing enumfacing = facings[m];
this.updateSkylightNeighborHeight(k + enumfacing.getFrontOffsetX(),
l + enumfacing.getFrontOffsetZ(), j2, k2);
}
this.updateSkylightNeighborHeight(k, l, j2, k2);
}
if (!this.worldObj.isRemote) {
++EaglerMinecraftServer.counterLightUpdate;
}
this.isModified = true;
}
} }
public int getBlockLightOpacity(BlockPos blockpos) { public int getBlockLightOpacity(BlockPos blockpos) {
@ -577,7 +508,9 @@ public class Chunk {
extendedblockstorage = this.storageArrays[j >> 4] = new ExtendedBlockStorage(j >> 4 << 4, extendedblockstorage = this.storageArrays[j >> 4] = new ExtendedBlockStorage(j >> 4 << 4,
!this.worldObj.provider.getHasNoSky()); !this.worldObj.provider.getHasNoSky());
flag = j >= i1;
alfheim$initSkylightForSection(extendedblockstorage);
// flag = j >= i1;
} }
extendedblockstorage.set(i, j & 15, k, state); extendedblockstorage.set(i, j & 15, k, state);
@ -605,10 +538,10 @@ public class Chunk {
this.relightBlock(i, j, k); this.relightBlock(i, j, k);
} }
if (j1 != k1 && (j1 < k1 || this.getLightFor(EnumSkyBlock.SKY, pos) > 0 // if (j1 != k1 && (j1 < k1 || this.getLightFor(EnumSkyBlock.SKY, pos) > 0
|| this.getLightFor(EnumSkyBlock.BLOCK, pos) > 0)) { // || this.getLightFor(EnumSkyBlock.BLOCK, pos) > 0)) {
this.propagateSkylightOcclusion(i, k); // this.propagateSkylightOcclusion(i, k);
} // }
} }
if (block1 instanceof ITileEntityProvider) { if (block1 instanceof ITileEntityProvider) {
@ -642,17 +575,9 @@ public class Chunk {
} }
public int getLightFor(EnumSkyBlock enumskyblock, BlockPos blockpos) { public int getLightFor(EnumSkyBlock enumskyblock, BlockPos blockpos) {
int i = blockpos.getX() & 15; alfheim$lightingEngine.processLightUpdatesForType(enumskyblock);
int j = blockpos.getY();
int k = blockpos.getZ() & 15; return alfheim$getCachedLightFor(enumskyblock, blockpos);
ExtendedBlockStorage extendedblockstorage = this.storageArrays[j >> 4];
return extendedblockstorage == null
? (this.canSeeSky(blockpos) ? enumskyblock.defaultLightValue : getNoSkyLightValue())
: (enumskyblock == EnumSkyBlock.SKY
? (this.worldObj.provider.getHasNoSky() ? getNoSkyLightValue()
: extendedblockstorage.getExtSkylightValue(i, j & 15, k))
: (enumskyblock == EnumSkyBlock.BLOCK ? extendedblockstorage.getExtBlocklightValue(i, j & 15, k)
: enumskyblock.defaultLightValue));
} }
public void setLightFor(EnumSkyBlock enumskyblock, BlockPos blockpos, int i) { public void setLightFor(EnumSkyBlock enumskyblock, BlockPos blockpos, int i) {
@ -678,6 +603,7 @@ public class Chunk {
} }
public int getLightSubtracted(BlockPos blockpos, int i) { public int getLightSubtracted(BlockPos blockpos, int i) {
alfheim$lightingEngine.processLightUpdates();
int j = blockpos.getX() & 15; int j = blockpos.getX() & 15;
int k = blockpos.getY(); int k = blockpos.getY();
int l = blockpos.getZ() & 15; int l = blockpos.getZ() & 15;
@ -830,6 +756,33 @@ public class Chunk {
this.worldObj.loadEntities(this.entityLists[i]); this.worldObj.loadEntities(this.entityLists[i]);
} }
final Chunk chunk = (Chunk) (Object) this;
for (final EnumFacing facing : EnumFacing.HORIZONTALS) {
final int xOffset = facing.getXOffset();
final int zOffset = facing.getZOffset();
final Chunk nChunk = worldObj.getChunkProvider().getLoadedChunk(chunk.xPosition + xOffset, chunk.zPosition + zOffset);
if (nChunk == null)
continue;
for (final EnumSkyBlock lightType : ENUM_SKY_BLOCK_VALUES) {
for (final EnumFacing.AxisDirection axisDir : ENUM_AXIS_DIRECTION_VALUES) {
// Merge flags upon loading of a chunk. This ensures that all flags are always already on the IN boundary below
alfheim$mergeFlags(lightType, chunk, nChunk, facing, axisDir);
alfheim$mergeFlags(lightType, nChunk, chunk, facing.getOpposite(), axisDir);
// Check everything that might have been canceled due to this chunk not being loaded.
// Also, pass in chunks if already known
// The boundary to the neighbor chunk (both ways)
alfheim$scheduleRelightChecksForBoundary(chunk, nChunk, null, lightType, xOffset, zOffset, axisDir);
alfheim$scheduleRelightChecksForBoundary(nChunk, chunk, null, lightType, -xOffset, -zOffset, axisDir);
// The boundary to the diagonal neighbor (since the checks in that chunk were aborted if this chunk wasn't loaded, see alfheim$scheduleRelightChecksForBoundary)
alfheim$scheduleRelightChecksForBoundary(nChunk, null, chunk, lightType, (zOffset != 0 ? axisDir.getOffset() : 0), (xOffset != 0 ? axisDir.getOffset() : 0), facing.getAxisDirection() == EnumFacing.AxisDirection.POSITIVE ? EnumFacing.AxisDirection.NEGATIVE : EnumFacing.AxisDirection.POSITIVE);
}
}
}
} }
/**+ /**+
@ -1014,7 +967,7 @@ public class Chunk {
this.field_150815_m = true; this.field_150815_m = true;
if (!this.isLightPopulated && this.isTerrainPopulated) { if (!this.isLightPopulated && this.isTerrainPopulated) {
this.func_150809_p(); this.checkLight();
} }
while (!this.tileEntityPosQueue.isEmpty()) { while (!this.tileEntityPosQueue.isEmpty()) {
@ -1225,38 +1178,27 @@ public class Chunk {
} }
public void func_150809_p() { public void checkLight() {
this.isTerrainPopulated = true; this.isTerrainPopulated = true;
this.isLightPopulated = true;
BlockPos blockpos = new BlockPos(this.xPosition << 4, 0, this.zPosition << 4);
if (!this.worldObj.provider.getHasNoSky()) {
if (this.worldObj.isAreaLoaded(blockpos.add(-1, 0, -1),
blockpos.add(16, this.worldObj.func_181545_F(), 16))) {
label92: for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
if (!this.func_150811_f(i, j)) {
this.isLightPopulated = false;
break label92;
}
}
}
if (this.isLightPopulated) { final Chunk chunk = (Chunk) (Object) this;
EnumFacing[] facings = EnumFacing.Plane.HORIZONTAL.facingsArray;
for (int i = 0; i < facings.length; ++i) {
EnumFacing enumfacing = facings[i];
int k = enumfacing.getAxisDirection() == EnumFacing.AxisDirection.POSITIVE ? 16 : 1;
this.worldObj.getChunkFromBlockCoords(blockpos.offset(enumfacing, k))
.func_180700_a(enumfacing.getOpposite());
}
this.func_177441_y(); if (!chunk.alfheim$isLightInitialized())
} alfheim$initChunkLighting(chunk, worldObj);
} else {
this.isLightPopulated = false; for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
if (x == 0 && z == 0)
continue;
final Chunk nChunk = worldObj.getChunkProvider().getLoadedChunk(chunk.xPosition + x, chunk.zPosition + z);
if (nChunk == null || !nChunk.alfheim$isLightInitialized())
return;
} }
} }
chunk.setLightPopulated(true);
} }
private void func_177441_y() { private void func_177441_y() {
@ -1400,4 +1342,309 @@ public class Chunk {
public static enum EnumCreateEntityType { public static enum EnumCreateEntityType {
IMMEDIATE, QUEUED, CHECK; IMMEDIATE, QUEUED, CHECK;
} }
private boolean alfheim$recheckGapsForColumn(final WorldChunkSlice slice, final int x, final int z) {
final int i = x + z * 16;
if (updateSkylightColumns[i]) {
updateSkylightColumns[i] = false;
final int x1 = this.xPosition * 16 + x;
final int z1 = this.zPosition * 16 + z;
alfheim$recheckGapsSkylightNeighborHeight(slice, x1, z1, getHeightValue(x, z), alfheim$recheckGapsGetLowestHeight(slice, x1, z1));
return true;
}
return false;
}
private int alfheim$recheckGapsGetLowestHeight(final WorldChunkSlice slice, final int x, final int z) {
int max = Integer.MAX_VALUE;
for (final EnumFacing facing : ENUM_FACING_HORIZONTAL) {
final Chunk chunk = slice.getChunkFromWorldCoords(x + facing.getXOffset(), z + facing.getZOffset());
if (chunk != null)
max = Math.min(max, chunk.getLowestHeight());
}
return max;
}
private void alfheim$recheckGapsSkylightNeighborHeight(final WorldChunkSlice slice, final int x, final int z, final int height, final int max) {
alfheim$checkSkylightNeighborHeight(slice, x, z, max);
for (final EnumFacing facing : ENUM_FACING_HORIZONTAL)
alfheim$checkSkylightNeighborHeight(slice, x + facing.getXOffset(), z + facing.getFrontOffsetZ(), height);
}
private void alfheim$checkSkylightNeighborHeight(final WorldChunkSlice slice, final int x, final int z, final int maxValue) {
if (slice.getChunkFromWorldCoords(x, z) == null)
return;
final int y = slice.getChunkFromWorldCoords(x, z).getHeightValue(x & 15, z & 15);
if (y > maxValue)
alfheim$updateSkylightNeighborHeight(slice, x, z, maxValue, y + 1);
else if (y < maxValue)
alfheim$updateSkylightNeighborHeight(slice, x, z, y, maxValue + 1);
}
private void alfheim$updateSkylightNeighborHeight(final WorldChunkSlice slice, final int x, final int z, final int startY, final int endY) {
if (endY < startY)
return;
if (!slice.isLoaded(x, z, 16))
return;
for (int y = startY; y < endY; ++y)
worldObj.checkLightFor(EnumSkyBlock.SKY, new BlockPos(x, y, z));
isModified = true;
}
private static void alfheim$mergeFlags(final EnumSkyBlock lightType, final Chunk inChunk, final Chunk outChunk, final EnumFacing dir, final EnumFacing.AxisDirection axisDirection) {
// final IChunkLightingData outChunkLightingData = (IChunkLightingData) outChunk; // I REALLY NEED TO SUBMIT A BUG REPORT FOR THIS WEIRD PROBLEM WHERE FUNCTIONS GET DELETED IF YOU DO THIS IN TEAVM
if (outChunk.alfheim$getNeighborLightChecks() == null)
return;
inChunk.alfheim$initNeighborLightChecks();
final int inIndex = alfheim$getFlagIndex(lightType, dir, axisDirection, EnumBoundaryFacing.IN);
final int outIndex = alfheim$getFlagIndex(lightType, dir.getOpposite(), axisDirection, EnumBoundaryFacing.OUT);
inChunk.alfheim$getNeighborLightChecks()[inIndex] |= outChunk.alfheim$getNeighborLightChecks()[outIndex];
// No need to call Chunk.setModified() since checks are not deleted from outChunk
}
private void alfheim$scheduleRelightChecksForBoundary(final Chunk chunk, Chunk nChunk, Chunk sChunk, final EnumSkyBlock lightType, final int xOffset, final int zOffset, final EnumFacing.AxisDirection axisDirection) {
if (chunk.alfheim$getNeighborLightChecks() == null)
return;
final int flagIndex = alfheim$getFlagIndex(lightType, xOffset, zOffset, axisDirection, EnumBoundaryFacing.IN); // OUT checks from neighbor are already merged
final int flags = chunk.alfheim$getNeighborLightChecks()[flagIndex];
if (flags == 0)
return;
if (nChunk == null) {
nChunk = worldObj.getChunkProvider().getLoadedChunk(chunk.xPosition + xOffset, chunk.zPosition + zOffset);
if (nChunk == null)
return;
}
if (sChunk == null) {
sChunk = worldObj.getChunkProvider()
.getLoadedChunk(chunk.xPosition + (zOffset != 0 ? axisDirection.getOffset() : 0), chunk.zPosition + (xOffset != 0 ? axisDirection.getOffset() : 0));
if (sChunk == null)
return; // Cancel, since the checks in the corner columns require the corner column of sChunk
}
final int reverseIndex = alfheim$getFlagIndex(lightType, -xOffset, -zOffset, axisDirection, EnumBoundaryFacing.OUT);
chunk.alfheim$getNeighborLightChecks()[flagIndex] = 0;
if (nChunk.alfheim$getNeighborLightChecks() != null)
nChunk.alfheim$getNeighborLightChecks()[reverseIndex] = 0; // Clear only now that it's clear that the checks are processed
chunk.setChunkModified();
nChunk.setChunkModified();
// Get the area to check
// Start in the corner...
int xMin = chunk.xPosition << 4;
int zMin = chunk.zPosition << 4;
// Move to other side of chunk if the direction is positive
if ((xOffset | zOffset) > 0) {
xMin += 15 * xOffset;
zMin += 15 * zOffset;
}
// Shift to other half if necessary (shift perpendicular to dir)
if (axisDirection == EnumFacing.AxisDirection.POSITIVE) {
xMin += 8 * (zOffset & 1); //x & 1 is same as abs(x) for x=-1,0,1
zMin += 8 * (xOffset & 1);
}
// Get maximal values (shift perpendicular to dir)
final int xMax = xMin + 7 * (zOffset & 1);
final int zMax = zMin + 7 * (xOffset & 1);
for (int y = 0; y < 16; ++y)
if ((flags & (1 << y)) != 0)
for (int x = xMin; x <= xMax; ++x)
for (int z = zMin; z <= zMax; ++z)
alfheim$scheduleRelightChecksForColumn(lightType, x, z, y << 4, (y << 4) + 15);
}
private void alfheim$initSkylightForSection(final ExtendedBlockStorage extendedBlockStorage) {
if (worldObj.provider.getHasNoSky())
return;
for (int x = 0; x < 16; ++x) {
for (int z = 0; z < 16; ++z) {
if (((Chunk) (Object) this).getHeightValue(x, z) > extendedBlockStorage.getYLocation())
continue;
for (int y = 0; y < 16; ++y)
extendedBlockStorage.setExtSkylightValue(x, y, z, EnumSkyBlock.SKY.defaultLightValue);
}
}
}
private void alfheim$scheduleRelightChecksForColumn(final EnumSkyBlock lightType, final int x, final int z, final int yMin, final int yMax) {
final BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
for (int y = yMin; y <= yMax; ++y)
worldObj.checkLightFor(lightType, mutableBlockPos.setPos(x, y, z));
}
private static int alfheim$getFlagIndex(final EnumSkyBlock lightType, final int xOffset, final int zOffset, final EnumFacing.AxisDirection axisDirection, final EnumBoundaryFacing boundaryFacing) {
return (lightType == EnumSkyBlock.BLOCK ? 0 : 16) | ((xOffset + 1) << 2) | ((zOffset + 1) << 1) | (axisDirection.getOffset() + 1) | boundaryFacing.ordinal();
}
private static int alfheim$getFlagIndex(final EnumSkyBlock lightType, final EnumFacing facing, final EnumFacing.AxisDirection axisDirection, final EnumBoundaryFacing boundaryFacing) {
return alfheim$getFlagIndex(lightType, facing.getXOffset(), facing.getZOffset(), axisDirection, boundaryFacing);
}
private static void alfheim$initChunkLighting(final Chunk chunk, final World world) {
final int xBase = chunk.xPosition << 4;
final int zBase = chunk.zPosition << 4;
final BlockPos.PooledMutableBlockPos mutableBlockPos = BlockPos.PooledMutableBlockPos.retain(xBase, 0, zBase);
if (world.isAreaLoaded(mutableBlockPos.add(-16, 0, -16), mutableBlockPos.add(31, 255, 31), false)) {
final ExtendedBlockStorage[] extendedBlockStorage = chunk.getBlockStorageArray();
for (int i = 0; i < extendedBlockStorage.length; ++i) {
final ExtendedBlockStorage storage = extendedBlockStorage[i];
if (storage == Chunk.NULL_BLOCK_STORAGE)
continue;
int yBase = i * 16;
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
mutableBlockPos.setPos(xBase + x, yBase + y, zBase + z);
if (LightUtil.getLightValueForState(storage.get(x, y, z), world, mutableBlockPos) > 0)
world.checkLightFor(EnumSkyBlock.BLOCK, mutableBlockPos);
}
}
}
}
if (!world.provider.getHasNoSky())
chunk.alfheim$setSkylightUpdatedPublic();
chunk.alfheim$setLightInitialized(true);
}
mutableBlockPos.release();
}
private void alfheim$relightSkylightColumn(final int x, final int z, final int height1, final int height2) {
final int yMin = Math.min(height1, height2);
final int yMax = Math.max(height1, height2) - 1;
final Chunk chunk = ((Chunk) (Object) this);
final ExtendedBlockStorage[] sections = chunk.getBlockStorageArray();
final int xBase = (chunk.xPosition << 4) + x;
final int zBase = (chunk.zPosition << 4) + z;
alfheim$scheduleRelightChecksForColumn(EnumSkyBlock.SKY, xBase, zBase, yMin, yMax);
if (sections[yMin >> 4] == Chunk.NULL_BLOCK_STORAGE && yMin > 0) {
worldObj.checkLightFor(EnumSkyBlock.SKY, new BlockPos(xBase, yMin - 1, zBase));
}
short emptySections = 0;
for (int sec = yMax >> 4; sec >= yMin >> 4; --sec) {
if (sections[sec] == Chunk.NULL_BLOCK_STORAGE) {
emptySections |= (short) (1 << sec);
}
}
if (emptySections != 0) {
for (final EnumFacing facing : EnumFacing.HORIZONTALS) {
final int xOffset = facing.getXOffset();
final int zOffset = facing.getZOffset();
final boolean neighborColumnExists =
(((x + xOffset) | (z + zOffset)) & 16) == 0
//Checks whether the position is at the specified border (the 16 bit is set for both 15+1 and 0-1)
|| worldObj.getChunkProvider().getLoadedChunk(chunk.xPosition + xOffset, chunk.zPosition + zOffset) != null;
if (neighborColumnExists) {
for (int sec = yMax >> 4; sec >= yMin >> 4; --sec) {
if ((emptySections & (1 << sec)) != 0)
alfheim$scheduleRelightChecksForColumn(EnumSkyBlock.SKY, xBase + xOffset, zBase + zOffset, sec << 4, (sec << 4) + 15);
}
} else {
chunk.alfheim$initNeighborLightChecks();
final EnumFacing.AxisDirection axisDirection = ((facing.getAxis() == EnumFacing.Axis.X ? z : x) & 15) < 8 ? EnumFacing.AxisDirection.NEGATIVE : EnumFacing.AxisDirection.POSITIVE;
chunk.alfheim$getNeighborLightChecks()[alfheim$getFlagIndex(EnumSkyBlock.SKY, facing, axisDirection, EnumBoundaryFacing.OUT)] |= emptySections;
chunk.setChunkModified();
}
}
}
}
public short[] alfheim$getNeighborLightChecks() {
return alfheim$neighborLightChecks;
}
public void alfheim$setNeighborLightChecks(final short[] data) {
alfheim$neighborLightChecks = data;
}
public LightingEngine alfheim$getLightingEngine() {
return alfheim$lightingEngine;
}
public boolean alfheim$isLightInitialized() {
return alfheim$isLightInitialized;
}
public void alfheim$setLightInitialized(final boolean lightInitialized) {
alfheim$isLightInitialized = lightInitialized;
}
public void alfheim$setSkylightUpdatedPublic() {
func_177441_y();
}
public void alfheim$initNeighborLightChecks() {
if (alfheim$getNeighborLightChecks() == null)
alfheim$setNeighborLightChecks(new short[32]);
}
public byte alfheim$getCachedLightFor(final EnumSkyBlock lightType, final BlockPos blockPos) {
final int x = blockPos.getX() & 15;
final int y = blockPos.getY();
final int z = blockPos.getZ() & 15;
final ExtendedBlockStorage extendedblockstorage = storageArrays[y >> 4];
if (extendedblockstorage == Chunk.NULL_BLOCK_STORAGE)
return canSeeSky(blockPos) ? (byte) lightType.defaultLightValue : 0;
else if (lightType == EnumSkyBlock.SKY)
return !worldObj.provider.getHasNoSky() ? (byte) extendedblockstorage.getExtSkylightValue(x, y & 15, z) : 0;
else
return lightType == EnumSkyBlock.BLOCK ? (byte) extendedblockstorage.getExtBlocklightValue(x, y & 15, z) : (byte) lightType.defaultLightValue;
}
} }

View File

@ -41,6 +41,8 @@ public interface IChunkProvider {
*/ */
Chunk provideChunk(int var1, int var2); Chunk provideChunk(int var1, int var2);
Chunk getLoadedChunk(int var1, int var2);
/**+ /**+
* Will return back a chunk, if it doesn't exist and its not a * Will return back a chunk, if it doesn't exist and its not a
* MP client it will generates all the blocks for the specified * MP client it will generates all the blocks for the specified

View File

@ -6,6 +6,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityList; import net.minecraft.entity.EntityList;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagList;
import net.minecraft.nbt.NBTTagShort;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.BlockPos; import net.minecraft.util.BlockPos;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
@ -13,6 +14,7 @@ import net.minecraft.world.NextTickListEntry;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.NibbleArray; import net.minecraft.world.chunk.NibbleArray;
import net.hoosiertransfer.Alfheim.IChunkLightingData;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
@ -38,6 +40,7 @@ import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
*/ */
public abstract class AnvilChunkLoader implements IChunkLoader { public abstract class AnvilChunkLoader implements IChunkLoader {
private static final Logger logger = LogManager.getLogger("AnvilChunkLoader"); private static final Logger logger = LogManager.getLogger("AnvilChunkLoader");
private static final String NEIGHBOR_LIGHT_CHECKS_KEY = "NeighborLightChecks";
/**+ /**+
* Wraps readChunkFromNBT. Checks the coordinates and several * Wraps readChunkFromNBT. Checks the coordinates and several
@ -73,6 +76,9 @@ public abstract class AnvilChunkLoader implements IChunkLoader {
* last update time. * last update time.
*/ */
protected void writeChunkToNBT(Chunk chunkIn, World worldIn, NBTTagCompound parNBTTagCompound) { protected void writeChunkToNBT(Chunk chunkIn, World worldIn, NBTTagCompound parNBTTagCompound) {
alfheim$writeNeighborLightChecksToNBT(chunkIn, parNBTTagCompound);
parNBTTagCompound.setBoolean("LightPopulated", chunkIn.alfheim$isLightInitialized());
parNBTTagCompound.setByte("V", (byte) 1); parNBTTagCompound.setByte("V", (byte) 1);
parNBTTagCompound.setInteger("xPos", chunkIn.xPosition); parNBTTagCompound.setInteger("xPos", chunkIn.xPosition);
parNBTTagCompound.setInteger("zPos", chunkIn.zPosition); parNBTTagCompound.setInteger("zPos", chunkIn.zPosition);
@ -285,6 +291,48 @@ public abstract class AnvilChunkLoader implements IChunkLoader {
} }
} }
alfheim$readNeighborLightChecksFromNBT(chunk, parNBTTagCompound);
chunk.alfheim$setLightInitialized(parNBTTagCompound.getBoolean("LightPopulated"));
return chunk; return chunk;
} }
private static void alfheim$readNeighborLightChecksFromNBT(final Chunk chunk, final NBTTagCompound compound) {
if (!compound.hasKey(NEIGHBOR_LIGHT_CHECKS_KEY, 9))
return;
final NBTTagList tagList = compound.getTagList(NEIGHBOR_LIGHT_CHECKS_KEY, 2);
if (tagList.tagCount() != 32) {
return;
}
chunk.alfheim$initNeighborLightChecks();
final short[] neighborLightChecks = chunk.alfheim$getNeighborLightChecks();
for (int i = 0; i < 32; ++i)
neighborLightChecks[i] = ((NBTTagShort) tagList.get(i)).getShort();
}
private static void alfheim$writeNeighborLightChecksToNBT(final Chunk chunk, final NBTTagCompound compound) {
final short[] neighborLightChecks = chunk.alfheim$getNeighborLightChecks();
if (neighborLightChecks == null)
return;
boolean empty = true;
final NBTTagList list = new NBTTagList();
for (final short flags : neighborLightChecks) {
list.appendTag(new NBTTagShort(flags));
if (flags != 0)
empty = false;
}
if (!empty)
compound.setTag(NEIGHBOR_LIGHT_CHECKS_KEY, list);
}
} }

View File

@ -45,6 +45,8 @@ public class ChunkProviderDebug implements IChunkProvider {
this.world = worldIn; this.world = worldIn;
} }
/**+ /**+
* Will return back a chunk, if it doesn't exist and its not a * Will return back a chunk, if it doesn't exist and its not a
* MP client it will generates all the blocks for the specified * MP client it will generates all the blocks for the specified
@ -79,6 +81,10 @@ public class ChunkProviderDebug implements IChunkProvider {
return chunk; return chunk;
} }
public Chunk getLoadedChunk(int parInt1, int parInt2) {
return this.provideChunk(parInt1, parInt2);
}
public static IBlockState func_177461_b(int parInt1, int parInt2) { public static IBlockState func_177461_b(int parInt1, int parInt2) {
IBlockState iblockstate = null; IBlockState iblockstate = null;
if (parInt1 > 0 && parInt2 > 0 && parInt1 % 2 != 0 && parInt2 % 2 != 0) { if (parInt1 > 0 && parInt2 > 0 && parInt1 % 2 != 0 && parInt2 % 2 != 0) {

View File

@ -181,6 +181,10 @@ public class ChunkProviderEnd implements IChunkProvider {
return chunk; return chunk;
} }
public Chunk getLoadedChunk(int parInt1, int parInt2) {
return this.provideChunk(parInt1, parInt2);
}
/**+ /**+
* generates a subset of the level's terrain data. Takes 7 * generates a subset of the level's terrain data. Takes 7
* arguments: the [empty] noise array, the position, and the * arguments: the [empty] noise array, the position, and the

View File

@ -157,6 +157,10 @@ public class ChunkProviderFlat implements IChunkProvider {
return chunk; return chunk;
} }
public Chunk getLoadedChunk(int var1, int var2) {
return this.provideChunk(var1, var2);
}
/**+ /**+
* Checks to see if a chunk exists at x, z * Checks to see if a chunk exists at x, z
*/ */

View File

@ -247,6 +247,10 @@ public class ChunkProviderGenerate implements IChunkProvider {
return chunk; return chunk;
} }
public Chunk getLoadedChunk(int var1, int var2) {
return this.provideChunk(var1, var2);
}
private void func_147423_a(int parInt1, int parInt2, int parInt3) { private void func_147423_a(int parInt1, int parInt2, int parInt3) {
this.field_147426_g = this.noiseGen6.generateNoiseOctaves(this.field_147426_g, parInt1, parInt3, 5, 5, this.field_147426_g = this.noiseGen6.generateNoiseOctaves(this.field_147426_g, parInt1, parInt3, 5, 5,
(double) this.settings.depthNoiseScaleX, (double) this.settings.depthNoiseScaleZ, (double) this.settings.depthNoiseScaleX, (double) this.settings.depthNoiseScaleZ,

View File

@ -264,6 +264,10 @@ public class ChunkProviderHell implements IChunkProvider {
return chunk; return chunk;
} }
public Chunk getLoadedChunk(int i, int j) {
return this.provideChunk(i, j);
}
/**+ /**+
* generates a subset of the level's terrain data. Takes 7 * generates a subset of the level's terrain data. Takes 7
* arguments: the [empty] noise array, the position, and the * arguments: the [empty] noise array, the position, and the

View File

@ -24,6 +24,7 @@ import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.EmptyChunk; import net.minecraft.world.chunk.EmptyChunk;
import net.minecraft.world.chunk.IChunkProvider; import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.chunk.storage.IChunkLoader; import net.minecraft.world.chunk.storage.IChunkLoader;
import net.hoosiertransfer.Alfheim.ILightingEngineProvider;
import net.lax1dude.eaglercraft.v1_8.HString; import net.lax1dude.eaglercraft.v1_8.HString;
import net.lax1dude.eaglercraft.v1_8.log4j.LogManager; import net.lax1dude.eaglercraft.v1_8.log4j.LogManager;
import net.lax1dude.eaglercraft.v1_8.log4j.Logger; import net.lax1dude.eaglercraft.v1_8.log4j.Logger;
@ -157,6 +158,10 @@ public class ChunkProviderServer implements IChunkProvider {
: this.loadChunk(i, j)) : chunk; : this.loadChunk(i, j)) : chunk;
} }
public Chunk getLoadedChunk(int i, int j) {
return (Chunk) this.id2ChunkMap.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(i, j));
}
private Chunk loadChunkFromFile(int x, int z) { private Chunk loadChunkFromFile(int x, int z) {
if (this.chunkLoader == null) { if (this.chunkLoader == null) {
return null; return null;
@ -211,7 +216,7 @@ public class ChunkProviderServer implements IChunkProvider {
public void populate(IChunkProvider ichunkprovider, int i, int j) { public void populate(IChunkProvider ichunkprovider, int i, int j) {
Chunk chunk = this.provideChunk(i, j); Chunk chunk = this.provideChunk(i, j);
if (!chunk.isTerrainPopulated()) { if (!chunk.isTerrainPopulated()) {
chunk.func_150809_p(); chunk.checkLight();
if (this.serverChunkGenerator != null) { if (this.serverChunkGenerator != null) {
this.serverChunkGenerator.populate(ichunkprovider, i, j); this.serverChunkGenerator.populate(ichunkprovider, i, j);
chunk.setChunkModified(); chunk.setChunkModified();
@ -236,6 +241,7 @@ public class ChunkProviderServer implements IChunkProvider {
* if all chunks have been saved. * if all chunks have been saved.
*/ */
public boolean saveChunks(boolean flag, IProgressUpdate var2) { public boolean saveChunks(boolean flag, IProgressUpdate var2) {
worldObj.alfheim$getLightingEngine().processLightUpdates();
int i = 0; int i = 0;
ArrayList arraylist = Lists.newArrayList(this.loadedChunks); ArrayList arraylist = Lists.newArrayList(this.loadedChunks);
@ -278,6 +284,7 @@ public class ChunkProviderServer implements IChunkProvider {
if (!this.worldObj.disableLevelSaving) { if (!this.worldObj.disableLevelSaving) {
for (int i = 0; i < 100; ++i) { for (int i = 0; i < 100; ++i) {
if (!this.droppedChunksSet.isEmpty()) { if (!this.droppedChunksSet.isEmpty()) {
//!!! i probably should do a ((ILightingEngineProvider) world).alfheim$getLightingEngine().processLightUpdates(); here
Long olong = (Long) this.droppedChunksSet.iterator().next(); Long olong = (Long) this.droppedChunksSet.iterator().next();
Chunk chunk = (Chunk) this.id2ChunkMap.getValueByKey(olong.longValue()); Chunk chunk = (Chunk) this.id2ChunkMap.getValueByKey(olong.longValue());
if (chunk != null) { if (chunk != null) {