This commit is contained in:
HoosierTransfer 2024-05-06 17:16:31 -04:00
parent ca7a57e381
commit 82b663ac31
2 changed files with 161 additions and 112 deletions

View File

@ -1,6 +1,5 @@
package net.hoosiertransfer.Alfheim.lighting; package net.hoosiertransfer.Alfheim.lighting;
import net.hoosiertransfer.Alfheim.IChunkLightingData;
import net.hoosiertransfer.Alfheim.util.ClampUtil; import net.hoosiertransfer.Alfheim.util.ClampUtil;
import net.hoosiertransfer.Alfheim.util.DeduplicatedLongQueue; import net.hoosiertransfer.Alfheim.util.DeduplicatedLongQueue;
import net.hoosiertransfer.teavm.ReentrantLock; import net.hoosiertransfer.teavm.ReentrantLock;
@ -35,24 +34,21 @@ public class LightingEngine {
private boolean updating = false; private boolean updating = false;
private static final int private static final int L_X = 26,
L_X = 26,
L_Y = 8, L_Y = 8,
L_Z = 26, L_Z = 26,
L_L = 4; L_L = 4;
private static final int private static final int S_Z = 0,
S_Z = 0, S_X = S_Z + L_Z,
S_X = S_Z + L_Z, S_Y = S_X + L_X,
S_Y = S_X + L_X, S_L = S_Y + L_Y;
S_L = S_Y + L_Y;
private static final long private static final long M_X = (1L << L_X) - 1,
M_X = (1L << L_X) - 1, M_Y = (1L << L_Y) - 1,
M_Y = (1L << L_Y) - 1, M_Z = (1L << L_Z) - 1,
M_Z = (1L << L_Z) - 1, M_L = (1L << L_L) - 1,
M_L = (1L << L_L) - 1, M_POS = (M_Y << S_Y) | (M_X << S_X) | (M_Z << S_Z);
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 Y_CHECK = 1L << (S_Y + L_Y);
@ -61,7 +57,8 @@ public class LightingEngine {
static { static {
for (byte i = 0; i < 6; ++i) { for (byte i = 0; i < 6; ++i) {
final Vec3i offset = EnumFacing._VALUES[i].getDirectionVec(); 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) { public void processLightUpdatesForType(final EnumSkyBlock lightType) {
// We only want to perform updates if we're being called from a tick event on the client. // We only want to perform updates if we're being called from a tick event on
// There are many locations in the client code that will end up making calls to this method, usually from other threads. // 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()) if (world.isRemote && !isCallingFromMainThread())
return; return;
@ -158,26 +157,35 @@ public class LightingEngine {
if (lock.tryLock()) if (lock.tryLock())
return; return;
// If we cannot lock, something has gone wrong... Only one thread should ever acquire the lock. // If we cannot lock, something has gone wrong... Only one thread should ever
// Validate that we're on the right thread immediately, so we can gather information. // acquire the lock.
// It is NEVER valid to call World methods from a thread other than the owning thread of the world instance. // 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(); final Thread current = Thread.currentThread();
if (current != ownerThread) { 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( 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" + "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" + "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" + +
"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."); "You should report this issue to our issue tracker with the following stacktrace information.");
illegalAccessException.printStackTrace(); 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(); lock.lock();
} }
@ -206,9 +214,12 @@ public class LightingEngine {
final byte newLight = calculateNewLightFromCursor(lightType); final byte newLight = calculateNewLightFromCursor(lightType);
if (oldLight < newLight) 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) 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"); profiler.endStartSection("enqueueBrightening");
@ -222,7 +233,14 @@ public class LightingEngine {
final byte newLight = (byte) (currentData >> S_L & M_L); final byte newLight = (byte) (currentData >> S_L & M_L);
if (newLight > getCursorCachedLight(lightType)) 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"); profiler.endStartSection("enqueueDarkening");
@ -236,12 +254,14 @@ public class LightingEngine {
final byte oldLight = getCursorCachedLight(lightType); final byte oldLight = getCursorCachedLight(lightType);
if (oldLight != 0) 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"); 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) { for (byte currentLight = MAX_LIGHT_LEVEL; currentLight >= 0; --currentLight) {
currentQueue = darkeningQueues[currentLight]; currentQueue = darkeningQueues[currentLight];
@ -264,7 +284,8 @@ public class LightingEngine {
// Only darken neighbors if we indeed became darker // Only darken neighbors if we indeed became darker
if (calculateNewLightFromCursor(luminosity, opacity, lightType) < currentLight) { 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; byte newLight = luminosity;
fetchNeighborDataFromCursor(lightType); fetchNeighborDataFromCursor(lightType);
@ -282,18 +303,27 @@ public class LightingEngine {
final MutableBlockPos neighborPos = neighborInfo.mutableBlockPos; 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); enqueueDarkening(neighborPos, neighborInfo.key, neighborLight, neighborChunk, lightType);
} else /*Only use for new light calculation if not*/ { } 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 // 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); newLight = (byte) Math.max(newLight, neighborLight - opacity);
} }
} }
// Schedule brightening since light level was set to 0 // Schedule brightening since light level was set to 0
enqueueBrighteningFromCursor(newLight, lightType); 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*/ { } else /*
enqueueBrighteningFromCursor(currentLight, lightType); // Do not spread to neighbors immediately to avoid scheduling multiple times * 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()) { while (nextItem()) {
final byte oldLight = getCursorCachedLight(lightType); 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) { if (oldLight == currentLight) {
world.notifyLightSet(currentPos); world.notifyLightSet(currentPos);
@ -346,14 +377,16 @@ public class LightingEngine {
neighborChunk = neighborInfo.chunk = getChunk(neighborPos); neighborChunk = neighborInfo.chunk = getChunk(neighborPos);
if (neighborChunk != null) { 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); 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 x = blockPos.getX() & 15;
final int y = blockPos.getY(); final int y = blockPos.getY();
final int z = blockPos.getZ() & 15; final int z = blockPos.getZ() & 15;
@ -363,7 +396,8 @@ public class LightingEngine {
else if (type == EnumSkyBlock.SKY) else if (type == EnumSkyBlock.SKY)
return !chunk.getWorld().provider.getHasNoSky() ? (byte) storage.getExtSkylightValue(x, y & 15, z) : 0; return !chunk.getWorld().provider.getHasNoSky() ? (byte) storage.getExtSkylightValue(x, y & 15, z) : 0;
else 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) { private byte calculateNewLightFromCursor(final EnumSkyBlock lightType) {
@ -409,7 +443,8 @@ public class LightingEngine {
final BlockPos neighborBlockPos = neighborInfo.mutableBlockPos; 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) if (newLight > neighborInfo.light)
enqueueBrightening(neighborBlockPos, neighborInfo.key, newLight, neighborChunk, lightType); enqueueBrightening(neighborBlockPos, neighborInfo.key, newLight, neighborChunk, lightType);
@ -420,24 +455,28 @@ public class LightingEngine {
enqueueBrightening(currentPos, currentData, newLight, currentChunk, 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) { private void enqueueBrightening(final BlockPos blockPos, final long longPos, final byte newLight, final Chunk chunk,
final EnumSkyBlock lightType) {
brighteningQueues[newLight].enqueue(longPos); brighteningQueues[newLight].enqueue(longPos);
chunk.setLightFor(lightType, blockPos, newLight); 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); darkeningQueues[oldLight].enqueue(longPos);
chunk.setLightFor(lightType, blockPos, 0); chunk.setLightFor(lightType, blockPos, 0);
} }
private static MutableBlockPos decodeWorldCoord(final MutableBlockPos mutableBlockPos, final long longPos) { 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) { 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() { private boolean nextItem() {
@ -464,7 +503,9 @@ public class LightingEngine {
private byte getCursorCachedLight(final EnumSkyBlock lightType) { private byte getCursorCachedLight(final EnumSkyBlock lightType) {
return currentChunk.alfheim$getCachedLightFor(lightType, currentPos); 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) { private byte getCursorLuminosity(final IBlockState state, final EnumSkyBlock lightType) {
@ -475,7 +516,8 @@ public class LightingEngine {
return 0; 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) { 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); return world.getChunkProvider().getLoadedChunk(blockPos.getX() >> 4, blockPos.getZ() >> 4);
} }
private static final class NeighborInfo { private static final class NeighborInfo {
public final MutableBlockPos mutableBlockPos = new MutableBlockPos(); public final MutableBlockPos mutableBlockPos = new MutableBlockPos();

View File

@ -14,14 +14,21 @@ import net.lax1dude.eaglercraft.v1_8.internal.buffer.ByteBuffer;
/** /**
* Copyright (c) 2023-2024 lax1dude. All Rights Reserved. * 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 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * DISCLAIMED.
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * DIRECT,
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * (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 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
* *
@ -30,201 +37,202 @@ public class VFile2 {
public static final String pathSeperator = "/"; public static final String pathSeperator = "/";
public static final String[] altPathSeperator = new String[] { "\\" }; public static final String[] altPathSeperator = new String[] { "\\" };
public static String normalizePath(String p) { 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); p = p.replace(altPathSeperator[i], pathSeperator);
} }
if(p.startsWith(pathSeperator)) { if (p.startsWith(pathSeperator)) {
p = p.substring(1); p = p.substring(1);
} }
if(p.endsWith(pathSeperator)) { if (p.endsWith(pathSeperator)) {
p = p.substring(0, p.length() - pathSeperator.length()); p = p.substring(0, p.length() - pathSeperator.length());
} }
return p; return p;
} }
public static String[] splitPath(String p) { public static String[] splitPath(String p) {
String[] pth = normalizePath(p).split(pathSeperator); 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(); pth[i] = pth[i].trim();
} }
return pth; return pth;
} }
protected String path; protected String path;
public static String createPath(Object... p) { public static String createPath(Object... p) {
ArrayList<String> r = new ArrayList(); ArrayList<String> r = new ArrayList();
for(int i = 0; i < p.length; ++i) { for (int i = 0; i < p.length; ++i) {
if(p[i] == null) { if (p[i] == null) {
continue; continue;
} }
String gg = p[i].toString(); String gg = p[i].toString();
if(gg == null) { if (gg == null) {
continue; continue;
} }
String[] parts = splitPath(gg); String[] parts = splitPath(gg);
for(int j = 0; j < parts.length; ++j) { for (int j = 0; j < parts.length; ++j) {
if(parts[j] == null || parts[j].equals(".")) { if (parts[j] == null || parts[j].equals(".")) {
continue; continue;
}else if(parts[j].equals("..") && r.size() > 0) { } else if (parts[j].equals("..") && r.size() > 0) {
int k = r.size() - 1; int k = r.size() - 1;
if(!r.get(k).equals("..")) { if (!r.get(k).equals("..")) {
r.remove(k); r.remove(k);
}else { } else {
r.add(".."); r.add("..");
} }
}else { } else {
r.add(parts[j]); r.add(parts[j]);
} }
} }
} }
if(r.size() > 0) { if (r.size() > 0) {
StringBuilder s = new StringBuilder(); StringBuilder s = new StringBuilder();
for(int i = 0; i < r.size(); ++i) { for (int i = 0; i < r.size(); ++i) {
if(i > 0) { if (i > 0) {
s.append(pathSeperator); s.append(pathSeperator);
} }
s.append(r.get(i)); s.append(r.get(i));
} }
return s.toString(); return s.toString();
}else { } else {
return null; return null;
} }
} }
public VFile2(Object... p) { public VFile2(Object... p) {
this.path = createPath(p); this.path = createPath(p);
} }
public InputStream getInputStream() { public InputStream getInputStream() {
assertNotRelative(); assertNotRelative();
return new VFileInputStream(PlatformFilesystem.eaglerRead(path)); return new VFileInputStream(PlatformFilesystem.eaglerRead(path));
} }
public OutputStream getOutputStream() { public OutputStream getOutputStream() {
assertNotRelative(); assertNotRelative();
return new VFileOutputStream(this); return new VFileOutputStream(this);
} }
public String toString() { public String toString() {
return path; return path;
} }
public boolean isRelative() { public boolean isRelative() {
return path == null || path.contains(".."); return path == null || path.contains("..");
} }
public void assertNotRelative() { 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() { public boolean canRead() {
return !isRelative() && PlatformFilesystem.eaglerExists(path); return !isRelative() && PlatformFilesystem.eaglerExists(path);
} }
public String getPath() { public String getPath() {
return path.equals("unnamed") ? null : path; return path.equals("unnamed") ? null : path;
} }
public String getName() { public String getName() {
int i = path.lastIndexOf(pathSeperator); int i = path.lastIndexOf(pathSeperator);
return i == -1 ? path : path.substring(i + 1); return i == -1 ? path : path.substring(i + 1);
} }
public static String getNameFromPath(String path) { public static String getNameFromPath(String path) {
path = normalizePath(path); path = normalizePath(path);
int i = path.lastIndexOf(pathSeperator); int i = path.lastIndexOf(pathSeperator);
return i == -1 ? path : path.substring(i + 1); return i == -1 ? path : path.substring(i + 1);
} }
public boolean canWrite() { public boolean canWrite() {
return !isRelative(); return !isRelative();
} }
public String getParent() { public String getParent() {
if(path == null) { if (path == null) {
return null; return null;
} }
int i = path.lastIndexOf(pathSeperator); int i = path.lastIndexOf(pathSeperator);
return i == -1 ? ".." : path.substring(0, i); return i == -1 ? ".." : path.substring(0, i);
} }
public int hashCode() { public int hashCode() {
return path == null ? 0 : path.hashCode(); return path == null ? 0 : path.hashCode();
} }
public boolean equals(Object o) { 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() { public boolean exists() {
return !isRelative() && PlatformFilesystem.eaglerExists(path); return !isRelative() && PlatformFilesystem.eaglerExists(path);
} }
public boolean delete() { public boolean delete() {
return !isRelative() && PlatformFilesystem.eaglerDelete(path); return !isRelative() && PlatformFilesystem.eaglerDelete(path);
} }
public boolean renameTo(String p) { public boolean renameTo(String p) {
if(!isRelative() && PlatformFilesystem.eaglerMove(path, p)) { if (!isRelative() && PlatformFilesystem.eaglerMove(path, p)) {
return true; return true;
} }
return false; return false;
} }
public boolean renameTo(VFile2 p) { public boolean renameTo(VFile2 p) {
return renameTo(p.path); return renameTo(p.path);
} }
public int length() { public int length() {
return isRelative() ? -1 : PlatformFilesystem.eaglerSize(path); return isRelative() ? -1 : PlatformFilesystem.eaglerSize(path);
} }
public byte[] getAllBytes() { public byte[] getAllBytes() {
assertNotRelative(); assertNotRelative();
if(!exists()) { if (!exists()) {
return null; return null;
} }
ByteBuffer readBuffer = PlatformFilesystem.eaglerRead(path); ByteBuffer readBuffer = PlatformFilesystem.eaglerRead(path);
byte[] copyBuffer = PlatformRuntime.castNativeByteBuffer(readBuffer); byte[] copyBuffer = PlatformRuntime.castNativeByteBuffer(readBuffer);
if(copyBuffer != null) { if (copyBuffer != null) {
return copyBuffer; return copyBuffer;
} }
try { try {
copyBuffer = new byte[readBuffer.remaining()]; copyBuffer = new byte[readBuffer.remaining()];
readBuffer.get(copyBuffer); readBuffer.get(copyBuffer);
return copyBuffer; return copyBuffer;
}finally { } finally {
PlatformRuntime.freeByteBuffer(readBuffer); PlatformRuntime.freeByteBuffer(readBuffer);
} }
} }
public String getAllChars() { public String getAllChars() {
assertNotRelative(); assertNotRelative();
if(!exists()) { if (!exists()) {
return null; return null;
} }
return new String(getAllBytes(), StandardCharsets.UTF_8); return new String(getAllBytes(), StandardCharsets.UTF_8);
} }
public String[] getAllLines() { public String[] getAllLines() {
assertNotRelative(); assertNotRelative();
if(!exists()) { if (!exists()) {
return null; return null;
} }
return EagUtils.linesArray(new String(getAllBytes(), StandardCharsets.UTF_8)); return EagUtils.linesArray(new String(getAllBytes(), StandardCharsets.UTF_8));
} }
public void setAllChars(String bytes) { public void setAllChars(String bytes) {
setAllBytes(bytes.getBytes(StandardCharsets.UTF_8)); setAllBytes(bytes.getBytes(StandardCharsets.UTF_8));
} }
public void setAllBytes(byte[] bytes) { public void setAllBytes(byte[] bytes) {
assertNotRelative(); assertNotRelative();
ByteBuffer copyBuffer = PlatformRuntime.castPrimitiveByteArray(bytes); ByteBuffer copyBuffer = PlatformRuntime.castPrimitiveByteArray(bytes);
if(copyBuffer != null) { if (copyBuffer != null) {
PlatformFilesystem.eaglerWrite(path, copyBuffer); PlatformFilesystem.eaglerWrite(path, copyBuffer);
return; return;
} }
@ -233,7 +241,7 @@ public class VFile2 {
copyBuffer.put(bytes); copyBuffer.put(bytes);
copyBuffer.flip(); copyBuffer.flip();
PlatformFilesystem.eaglerWrite(path, copyBuffer); PlatformFilesystem.eaglerWrite(path, copyBuffer);
}finally { } finally {
PlatformRuntime.freeByteBuffer(copyBuffer); PlatformRuntime.freeByteBuffer(copyBuffer);
} }
} }