diff --git a/src/main/java/net/hoosiertransfer/Alfheim/lighting/LightingEngine.java b/src/main/java/net/hoosiertransfer/Alfheim/lighting/LightingEngine.java index ddd4aac..d6255d7 100644 --- a/src/main/java/net/hoosiertransfer/Alfheim/lighting/LightingEngine.java +++ b/src/main/java/net/hoosiertransfer/Alfheim/lighting/LightingEngine.java @@ -1,6 +1,5 @@ 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; @@ -35,24 +34,21 @@ public class LightingEngine { private boolean updating = false; - private static final int - L_X = 26, + 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 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 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); @@ -61,7 +57,8 @@ public class LightingEngine { 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); + neighborShifts[i] = ((long) offset.getY() << S_Y) | ((long) offset.getX() << S_X) + | ((long) offset.getZ() << S_Z); } } @@ -114,8 +111,10 @@ public class LightingEngine { } 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. + // 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; @@ -158,26 +157,35 @@ public class LightingEngine { 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. + // 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())); + 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.threadId(), current.getName(), current.threadId())); System.out.println( - "Something (likely another mod) has attempted to modify the world's state from the wrong thread!\n" + + "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" + + "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. + // Wait for the lock to be released. This will likely introduce unwanted stalls, + // but will mitigate the issue. lock.lock(); } @@ -206,9 +214,12 @@ public class LightingEngine { 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 + 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 + initialDarkenings.enqueue(currentData); // Don't enqueue directly for darkening to avoid duplicate + // scheduling } profiler.endStartSection("enqueueBrightening"); @@ -222,7 +233,14 @@ public class LightingEngine { 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 + 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"); @@ -236,12 +254,14 @@ public class LightingEngine { final byte oldLight = getCursorCachedLight(lightType); if (oldLight != 0) - enqueueDarkening(currentPos, currentData, oldLight, currentChunk, lightType); // Sets the light to zero to only schedule once + 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 + // 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]; @@ -264,7 +284,8 @@ public class LightingEngine { // 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 + // Need to calculate new light value from neighbors IGNORING neighbors which are + // scheduled for darkening byte newLight = luminosity; fetchNeighborDataFromCursor(lightType); @@ -282,18 +303,27 @@ public class LightingEngine { final MutableBlockPos neighborPos = neighborInfo.mutableBlockPos; - if (currentLight - getPosOpacity(neighborPos, neighborChunk.getBlockState(neighborPos)) >= neighborLight) /*Schedule neighbor for darkening if we possibly light it*/ { + 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 + } 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 + } 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 } } @@ -305,7 +335,8 @@ public class LightingEngine { while (nextItem()) { final byte oldLight = getCursorCachedLight(lightType); - // Only process this if nothing else has happened at this position since scheduling + // Only process this if nothing else has happened at this position since + // scheduling if (oldLight == currentLight) { world.notifyLightSet(currentPos); @@ -346,14 +377,16 @@ public class LightingEngine { neighborChunk = neighborInfo.chunk = getChunk(neighborPos); if (neighborChunk != null) { - final ExtendedBlockStorage neighborSection = neighborChunk.getBlockStorageArray()[neighborPos.getY() >> 4]; + 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) { + 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; @@ -363,7 +396,8 @@ public class LightingEngine { 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; + return type == EnumSkyBlock.BLOCK ? (byte) storage.getExtBlocklightValue(x, y & 15, z) + : (byte) type.defaultLightValue; } private byte calculateNewLightFromCursor(final EnumSkyBlock lightType) { @@ -409,7 +443,8 @@ public class LightingEngine { final BlockPos neighborBlockPos = neighborInfo.mutableBlockPos; - final byte newLight = (byte) (currentLight - getPosOpacity(neighborBlockPos, neighborChunk.getBlockState(neighborBlockPos))); + final byte newLight = (byte) (currentLight + - getPosOpacity(neighborBlockPos, neighborChunk.getBlockState(neighborBlockPos))); if (newLight > neighborInfo.light) enqueueBrightening(neighborBlockPos, neighborInfo.key, newLight, neighborChunk, lightType); @@ -420,24 +455,28 @@ public class LightingEngine { enqueueBrightening(currentPos, currentData, newLight, currentChunk, lightType); } - private void enqueueBrightening(final BlockPos blockPos, final long longPos, final byte newLight, final Chunk chunk, final EnumSkyBlock 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) { + 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)); + 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); + 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() { @@ -464,7 +503,9 @@ public class LightingEngine { 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 + // 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) { @@ -475,7 +516,8 @@ public class LightingEngine { return 0; } - return (byte) ClampUtil.clampMinFirst(LightUtil.getLightValueForState(state, world, currentPos), 0, MAX_LIGHT_LEVEL); + return (byte) ClampUtil.clampMinFirst(LightUtil.getLightValueForState(state, world, currentPos), 0, + MAX_LIGHT_LEVEL); } private byte getPosOpacity(final BlockPos blockPos, final IBlockState blockState) { @@ -486,7 +528,6 @@ public class LightingEngine { return world.getChunkProvider().getLoadedChunk(blockPos.getX() >> 4, blockPos.getZ() >> 4); } - private static final class NeighborInfo { public final MutableBlockPos mutableBlockPos = new MutableBlockPos(); diff --git a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFile2.java b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFile2.java index 88c0ecf..fbcfa1c 100644 --- a/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFile2.java +++ b/src/main/java/net/lax1dude/eaglercraft/v1_8/internal/vfs2/VFile2.java @@ -14,14 +14,21 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer; /** * Copyright (c) 2023-2024 lax1dude. All Rights Reserved. * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * @@ -30,201 +37,202 @@ public class VFile2 { public static final String pathSeperator = "/"; public static final String[] altPathSeperator = new String[] { "\\" }; - + public static String normalizePath(String p) { - for(int i = 0; i < altPathSeperator.length; ++i) { + for (int i = 0; i < altPathSeperator.length; ++i) { p = p.replace(altPathSeperator[i], pathSeperator); } - if(p.startsWith(pathSeperator)) { + if (p.startsWith(pathSeperator)) { p = p.substring(1); } - if(p.endsWith(pathSeperator)) { + if (p.endsWith(pathSeperator)) { p = p.substring(0, p.length() - pathSeperator.length()); } return p; } - + public static String[] splitPath(String p) { String[] pth = normalizePath(p).split(pathSeperator); - for(int i = 0; i < pth.length; ++i) { + for (int i = 0; i < pth.length; ++i) { pth[i] = pth[i].trim(); } return pth; } - + protected String path; - + public static String createPath(Object... p) { ArrayList r = new ArrayList(); - for(int i = 0; i < p.length; ++i) { - if(p[i] == null) { + for (int i = 0; i < p.length; ++i) { + if (p[i] == null) { continue; } String gg = p[i].toString(); - if(gg == null) { + if (gg == null) { continue; } String[] parts = splitPath(gg); - for(int j = 0; j < parts.length; ++j) { - if(parts[j] == null || parts[j].equals(".")) { + for (int j = 0; j < parts.length; ++j) { + if (parts[j] == null || parts[j].equals(".")) { continue; - }else if(parts[j].equals("..") && r.size() > 0) { + } else if (parts[j].equals("..") && r.size() > 0) { int k = r.size() - 1; - if(!r.get(k).equals("..")) { + if (!r.get(k).equals("..")) { r.remove(k); - }else { + } else { r.add(".."); } - }else { + } else { r.add(parts[j]); } } } - if(r.size() > 0) { + if (r.size() > 0) { StringBuilder s = new StringBuilder(); - for(int i = 0; i < r.size(); ++i) { - if(i > 0) { + for (int i = 0; i < r.size(); ++i) { + if (i > 0) { s.append(pathSeperator); } s.append(r.get(i)); } return s.toString(); - }else { + } else { return null; } } - + public VFile2(Object... p) { this.path = createPath(p); } - + public InputStream getInputStream() { assertNotRelative(); return new VFileInputStream(PlatformFilesystem.eaglerRead(path)); } - + public OutputStream getOutputStream() { assertNotRelative(); return new VFileOutputStream(this); } - + public String toString() { return path; } - + public boolean isRelative() { return path == null || path.contains(".."); } - + public void assertNotRelative() { - if(isRelative()) throw new EaglerFileSystemException("Relative paths are not allowed: " + path); + if (isRelative()) + throw new EaglerFileSystemException("Relative paths are not allowed: " + path); } - + public boolean canRead() { return !isRelative() && PlatformFilesystem.eaglerExists(path); } - + public String getPath() { return path.equals("unnamed") ? null : path; } - + public String getName() { int i = path.lastIndexOf(pathSeperator); return i == -1 ? path : path.substring(i + 1); } - + public static String getNameFromPath(String path) { path = normalizePath(path); int i = path.lastIndexOf(pathSeperator); return i == -1 ? path : path.substring(i + 1); } - + public boolean canWrite() { return !isRelative(); } - + public String getParent() { - if(path == null) { + if (path == null) { return null; } int i = path.lastIndexOf(pathSeperator); return i == -1 ? ".." : path.substring(0, i); } - + public int hashCode() { return path == null ? 0 : path.hashCode(); } - + public boolean equals(Object o) { - return path != null && o != null && (o instanceof VFile2) && path.equals(((VFile2)o).path); + return path != null && o != null && (o instanceof VFile2) && path.equals(((VFile2) o).path); } - + public boolean exists() { return !isRelative() && PlatformFilesystem.eaglerExists(path); } - + public boolean delete() { return !isRelative() && PlatformFilesystem.eaglerDelete(path); } - + public boolean renameTo(String p) { - if(!isRelative() && PlatformFilesystem.eaglerMove(path, p)) { + if (!isRelative() && PlatformFilesystem.eaglerMove(path, p)) { return true; } return false; } - + public boolean renameTo(VFile2 p) { return renameTo(p.path); } - + public int length() { return isRelative() ? -1 : PlatformFilesystem.eaglerSize(path); } - + public byte[] getAllBytes() { assertNotRelative(); - if(!exists()) { + if (!exists()) { return null; } ByteBuffer readBuffer = PlatformFilesystem.eaglerRead(path); byte[] copyBuffer = PlatformRuntime.castNativeByteBuffer(readBuffer); - if(copyBuffer != null) { + if (copyBuffer != null) { return copyBuffer; } try { copyBuffer = new byte[readBuffer.remaining()]; readBuffer.get(copyBuffer); return copyBuffer; - }finally { + } finally { PlatformRuntime.freeByteBuffer(readBuffer); } } - + public String getAllChars() { assertNotRelative(); - if(!exists()) { + if (!exists()) { return null; } return new String(getAllBytes(), StandardCharsets.UTF_8); } - + public String[] getAllLines() { assertNotRelative(); - if(!exists()) { + if (!exists()) { return null; } return EagUtils.linesArray(new String(getAllBytes(), StandardCharsets.UTF_8)); } - + public void setAllChars(String bytes) { setAllBytes(bytes.getBytes(StandardCharsets.UTF_8)); } - + public void setAllBytes(byte[] bytes) { assertNotRelative(); ByteBuffer copyBuffer = PlatformRuntime.castPrimitiveByteArray(bytes); - if(copyBuffer != null) { + if (copyBuffer != null) { PlatformFilesystem.eaglerWrite(path, copyBuffer); return; } @@ -233,7 +241,7 @@ public class VFile2 { copyBuffer.put(bytes); copyBuffer.flip(); PlatformFilesystem.eaglerWrite(path, copyBuffer); - }finally { + } finally { PlatformRuntime.freeByteBuffer(copyBuffer); } }