diff --git a/sources/pom.xml b/sources/pom.xml index 1b2a35df1..496572704 100644 --- a/sources/pom.xml +++ b/sources/pom.xml @@ -3,7 +3,7 @@ 4.0.0 akarin jar - 1.12.2-R0.4-RELEASE + 1.12.2-R0.4-SNAPSHOT Akarin https://github.com/Akarin-project/Akarin @@ -134,7 +134,7 @@ io.akarin legacylauncher - 1.25 + 1.26 org.spongepowered diff --git a/sources/src/main/java/io/akarin/api/internal/mixin/IMixinChunk.java b/sources/src/main/java/io/akarin/api/internal/mixin/IMixinChunk.java deleted file mode 100644 index 7dd2723b1..000000000 --- a/sources/src/main/java/io/akarin/api/internal/mixin/IMixinChunk.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.akarin.api.internal.mixin; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.annotation.Nullable; - -import net.minecraft.server.Chunk; -import net.minecraft.server.EnumSkyBlock; - -public interface IMixinChunk { - AtomicInteger getPendingLightUpdates(); - - long getLightUpdateTime(); - - boolean areNeighborsLoaded(); - - @Nullable Chunk getNeighborChunk(int index); - - CopyOnWriteArrayList getQueuedLightingUpdates(EnumSkyBlock type); - - List getNeighbors(); - - void setNeighborChunk(int index, @Nullable Chunk chunk); - - void setLightUpdateTime(long time); -} \ No newline at end of file diff --git a/sources/src/main/java/io/akarin/api/internal/mixin/IMixinWorldServer.java b/sources/src/main/java/io/akarin/api/internal/mixin/IMixinWorldServer.java deleted file mode 100644 index 03f5ad0ae..000000000 --- a/sources/src/main/java/io/akarin/api/internal/mixin/IMixinWorldServer.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.akarin.api.internal.mixin; - -import java.util.List; -import java.util.concurrent.ExecutorService; - -import net.minecraft.server.BlockPosition; -import net.minecraft.server.Chunk; -import net.minecraft.server.EnumSkyBlock; - -public interface IMixinWorldServer { - boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk chunk); - - boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List neighbors); - - ExecutorService getLightingExecutor(); -} \ No newline at end of file diff --git a/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java b/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java index 97d4e2e43..62115a4f5 100644 --- a/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java +++ b/sources/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java @@ -185,16 +185,6 @@ public class AkarinGlobalConfig { keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s")) * 1000; } - public static int asyncLightingThreads; - private static void asyncLightingThreads() { - asyncLightingThreads = getInt("core.async-lighting.executor-threads", 4); - } - - public static boolean asyncLightingWorkStealing; - private static void asyncLightingWorkStealing() { - asyncLightingWorkStealing = getBoolean("core.async-lighting.use-work-stealing", false); - } - public static boolean throwOnAsyncCaught; private static void throwOnAsyncCaught() { throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true); diff --git a/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunk.java b/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunk.java deleted file mode 100644 index 2939743c8..000000000 --- a/sources/src/main/java/io/akarin/server/mixin/cps/MixinChunk.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package io.akarin.server.mixin.cps; - -import java.util.List; -import javax.annotation.Nullable; - -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import com.google.common.collect.Lists; - -import io.akarin.api.internal.mixin.IMixinChunk; -import net.minecraft.server.BlockPosition; -import net.minecraft.server.Chunk; -import net.minecraft.server.EnumDirection; -import net.minecraft.server.MCUtil; -import net.minecraft.server.World; - -@Mixin(value = Chunk.class, remap = false) -public abstract class MixinChunk implements IMixinChunk { - private Chunk[] neighborChunks = new Chunk[4]; - private static final EnumDirection[] CARDINAL_DIRECTIONS = new EnumDirection[] {EnumDirection.NORTH, EnumDirection.SOUTH, EnumDirection.EAST, EnumDirection.WEST}; - - @Shadow @Final public World world; - @Shadow @Final public int locX; - @Shadow @Final public int locZ; - - @Override - public Chunk getNeighborChunk(int index) { - return this.neighborChunks[index]; - } - - @Override - public void setNeighborChunk(int index, @Nullable Chunk chunk) { - this.neighborChunks[index] = chunk; - } - - @Override - public List getNeighbors() { - List neighborList = Lists.newArrayList(); - for (Chunk neighbor : this.neighborChunks) { - if (neighbor != null) { - neighborList.add(neighbor); - } - } - return neighborList; - } - - @Override - public boolean areNeighborsLoaded() { - for (int i = 0; i < 4; i++) { - if (this.neighborChunks[i] == null) { - return false; - } - } - return true; - } - - private static int directionToIndex(EnumDirection direction) { - switch (direction) { - case NORTH: - return 0; - case SOUTH: - return 1; - case EAST: - return 2; - case WEST: - return 3; - default: - throw new IllegalArgumentException("Unexpected direction"); - } - } - - @Inject(method = "addEntities", at = @At("RETURN")) - public void onLoadReturn(CallbackInfo ci) { - BlockPosition origin = new BlockPosition(locX, 0, locZ); - for (EnumDirection direction : CARDINAL_DIRECTIONS) { - BlockPosition shift = origin.shift(direction); - Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ()); - if (neighbor != null) { - int neighborIndex = directionToIndex(direction); - int oppositeNeighborIndex = directionToIndex(direction.opposite()); - this.setNeighborChunk(neighborIndex, neighbor); - ((IMixinChunk) neighbor).setNeighborChunk(oppositeNeighborIndex, (Chunk) (Object) this); - } - } - } - - @Inject(method = "removeEntities", at = @At("RETURN")) - public void onUnload(CallbackInfo ci) { - BlockPosition origin = new BlockPosition(locX, 0, locZ); - for (EnumDirection direction : CARDINAL_DIRECTIONS) { - BlockPosition shift = origin.shift(direction); - Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ()); - if (neighbor != null) { - int neighborIndex = directionToIndex(direction); - int oppositeNeighborIndex = directionToIndex(direction.opposite()); - this.setNeighborChunk(neighborIndex, null); - ((IMixinChunk) neighbor).setNeighborChunk(oppositeNeighborIndex, null); - } - } - } -} diff --git a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java deleted file mode 100644 index aa8e8ac79..000000000 --- a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunk.java +++ /dev/null @@ -1,640 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package io.akarin.server.mixin.lighting; - -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.RejectedExecutionException; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.annotation.Nullable; - -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import io.akarin.api.internal.Akari; -import io.akarin.api.internal.mixin.IMixinChunk; -import io.akarin.api.internal.mixin.IMixinWorldServer; -import net.minecraft.server.BlockPosition; -import net.minecraft.server.Blocks; -import net.minecraft.server.Chunk; -import net.minecraft.server.ChunkSection; -import net.minecraft.server.EnumDirection; -import net.minecraft.server.EnumSkyBlock; -import net.minecraft.server.IBlockData; -import net.minecraft.server.MCUtil; -import net.minecraft.server.TileEntity; -import net.minecraft.server.World; -import net.minecraft.server.BlockPosition.MutableBlockPosition; - -@Mixin(value = Chunk.class, remap = false, priority = 1001) -public abstract class MixinChunk implements IMixinChunk { - - // Keeps track of block positions in this chunk currently queued for sky light update - private CopyOnWriteArrayList queuedSkyLightingUpdates = new CopyOnWriteArrayList<>(); - // Keeps track of block positions in this chunk currently queued for block light update - private CopyOnWriteArrayList queuedBlockLightingUpdates = new CopyOnWriteArrayList<>(); - private AtomicInteger pendingLightUpdates = new AtomicInteger(); - private long lightUpdateTime; - private static ExecutorService lightExecutorService; - - @Shadow(aliases = "m") private boolean isGapLightingUpdated; - @Shadow(aliases = "r") private boolean ticked; - @Shadow @Final private ChunkSection[] sections; - @Shadow @Final public int locX; - @Shadow @Final public int locZ; - @Shadow @Final public World world; - @Shadow @Final public int[] heightMap; - /** Which columns need their skylightMaps updated. */ - @Shadow(aliases = "i") @Final private boolean[] updateSkylightColumns; - /** Queue containing the BlockPosition of tile entities queued for creation */ - @Shadow(aliases = "y") @Final private ConcurrentLinkedQueue tileEntityPosQueue; - /** Boolean value indicating if the terrain is populated. */ - @Shadow(aliases = "done") private boolean isTerrainPopulated; - @Shadow(aliases = "lit") private boolean isLightPopulated; - /** Lowest value in the heightmap. */ - @Shadow(aliases = "v") private int heightMapMinimum; - - @Shadow(aliases = "b") public abstract int getHeightValue(int x, int z); - @Shadow(aliases = "g") @Nullable public abstract TileEntity createNewTileEntity(BlockPosition pos); - @Shadow(aliases = "a") @Nullable public abstract TileEntity getTileEntity(BlockPosition pos, Chunk.EnumTileEntityState state); - @Shadow @Final public abstract IBlockData getBlockData(BlockPosition pos); - @Shadow @Final public abstract IBlockData getBlockData(int x, int y, int z); - @Shadow public abstract boolean isUnloading(); - /** Checks the height of a block next to a sky-visible block and schedules a lighting update as necessary */ - @Shadow(aliases = "b") public abstract void checkSkylightNeighborHeight(int x, int z, int maxValue); - @Shadow(aliases = "a") public abstract void updateSkylightNeighborHeight(int x, int z, int startY, int endY); - @Shadow(aliases = "z") public abstract void setSkylightUpdated(); - @Shadow(aliases = "g") public abstract int getTopFilledSegment(); - @Shadow public abstract void markDirty(); - - @Inject(method = "", at = @At("RETURN")) - public void onConstruct(World worldIn, int x, int z, CallbackInfo ci) { - lightExecutorService = ((IMixinWorldServer) worldIn).getLightingExecutor(); - } - - @Override - public AtomicInteger getPendingLightUpdates() { - return this.pendingLightUpdates; - } - - @Override - public long getLightUpdateTime() { - return this.lightUpdateTime; - } - - @Override - public void setLightUpdateTime(long time) { - this.lightUpdateTime = time; - } - - @Inject(method = "b(Z)V", at = @At("HEAD"), cancellable = true) - private void onTickHead(boolean skipRecheckGaps, CallbackInfo ci) { - final List neighbors = this.getSurroundingChunks(); - if (this.isGapLightingUpdated && this.world.worldProvider.m() && !skipRecheckGaps && !neighbors.isEmpty()) { // OBFHELPER: hasSkyLight - lightExecutorService.execute(() -> { - this.recheckGapsAsync(neighbors); - }); - this.isGapLightingUpdated = false; - } - - this.ticked = true; - - if (!this.isLightPopulated && this.isTerrainPopulated && !neighbors.isEmpty()) { - lightExecutorService.execute(() -> { - this.checkLightAsync(neighbors); - }); - // set to true to avoid requeuing the same task when not finished - this.isLightPopulated = true; - } - - while (!this.tileEntityPosQueue.isEmpty()) { - BlockPosition blockpos = this.tileEntityPosQueue.poll(); - - if (this.getTileEntity(blockpos, Chunk.EnumTileEntityState.CHECK) == null && this.getBlockData(blockpos).getBlock().isTileEntity()) { // OBFHELPER: getTileEntity - TileEntity tileentity = this.createNewTileEntity(blockpos); - this.world.setTileEntity(blockpos, tileentity); - this.world.b(blockpos, blockpos); // OBFHELPER: markBlockRangeForRenderUpdate - } - } - ci.cancel(); - } - - @Redirect(method = "b(III)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.getHighestBlockYAt(Lnet/minecraft/server/BlockPosition;)Lnet/minecraft/server/BlockPosition;")) - private BlockPosition onCheckSkylightGetHeight(World world, BlockPosition pos) { - final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null); - if (chunk == null) { - return BlockPosition.ZERO; - } - - return new BlockPosition(pos.getX(), chunk.b(pos.getX() & 15, pos.getZ() & 15), pos.getZ()); // OBFHELPER: getHeightValue - } - - @Redirect(method = "a(IIII)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.areChunksLoaded(Lnet/minecraft/server/BlockPosition;I)Z")) - private boolean onAreaLoadedSkyLightNeighbor(World world, BlockPosition pos, int radius) { - return this.isAreaLoaded(); - } - - @Redirect(method = "a(IIII)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.c(Lnet/minecraft/server/EnumSkyBlock;Lnet/minecraft/server/BlockPosition;)Z")) - private boolean onCheckLightForSkylightNeighbor(World world, EnumSkyBlock enumSkyBlock, BlockPosition pos) { - return this.checkWorldLightFor(enumSkyBlock, pos); - } - - /** - * Rechecks chunk gaps async. - * - * @param neighbors A thread-safe list of surrounding neighbor chunks - */ - private void recheckGapsAsync(List neighbors) { - 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.locX * 16 + i; - int i1 = this.locZ * 16 + j; - int j1 = Integer.MAX_VALUE; - - for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) { - final Chunk chunk = this.getLightChunk((l + enumfacing.getAdjacentX()) >> 4, (i1 + enumfacing.getAdjacentZ()) >> 4, neighbors); - if (chunk == null || chunk.isUnloading()) { - continue; - } - j1 = Math.min(j1, chunk.w()); // OBFHELPER: getLowestHeight - } - - this.checkSkylightNeighborHeight(l, i1, j1); - - for (EnumDirection enumfacing1 : EnumDirection.EnumDirectionLimit.HORIZONTAL) { - this.checkSkylightNeighborHeight(l + enumfacing1.getAdjacentX(), i1 + enumfacing1.getAdjacentZ(), k); - } - } - } - - // this.isGapLightingUpdated = false; - } - } - - @Redirect(method = "n()V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.getType(Lnet/minecraft/server/BlockPosition;)Lnet/minecraft/server/IBlockData;")) - private IBlockData onRelightChecksGetBlockData(World world, BlockPosition pos) { - Chunk chunk = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), pos.getX() >> 4, pos.getZ() >> 4); - - final IMixinChunk spongeChunk = (IMixinChunk) chunk; - if (chunk == null || chunk.isUnloading() || !spongeChunk.areNeighborsLoaded()) { - return Blocks.AIR.getBlockData(); - } - - return chunk.getBlockData(pos); - } - - @Redirect(method = "n()V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.w(Lnet/minecraft/server/BlockPosition;)Z")) - private boolean onRelightChecksCheckLight(World world, BlockPosition pos) { - return this.checkWorldLight(pos); - } - - // Avoids grabbing chunk async during light check - @Redirect(method = "e(II)Z", at = @At(value = "INVOKE", target = "net/minecraft/server/World.w(Lnet/minecraft/server/BlockPosition;)Z")) - private boolean onCheckLightWorld(World world, BlockPosition pos) { - return this.checkWorldLight(pos); - } - - @Inject(method = "o()V", at = @At("HEAD"), cancellable = true) - private void checkLightHead(CallbackInfo ci) { - if (this.world.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) { - return; - } - - if (this.isUnloading()) { - return; - } - final List neighborChunks = this.getSurroundingChunks(); - if (neighborChunks.isEmpty()) { - this.isLightPopulated = false; - return; - } - - if (Akari.isPrimaryThread()) { - try { - lightExecutorService.execute(() -> { - this.checkLightAsync(neighborChunks); - }); - } catch (RejectedExecutionException ex) { - // This could happen if ServerHangWatchdog kills the server - // between the start of the method and the execute() call. - if (!this.world.getMinecraftServer().isStopped() && !lightExecutorService.isShutdown()) { - throw ex; - } - } - } else { - this.checkLightAsync(neighborChunks); - } - ci.cancel(); - } - - /** - * Checks light async. - * - * @param neighbors A thread-safe list of surrounding neighbor chunks - */ - private void checkLightAsync(List neighbors) { - this.isTerrainPopulated = true; - this.isLightPopulated = true; - BlockPosition blockpos = new BlockPosition(this.locX << 4, 0, this.locZ << 4); - - if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight - CHECK_LIGHT: - for (int i = 0; i < 16; ++i) { - for (int j = 0; j < 16; ++j) { - if (!this.checkLightAsync(i, j, neighbors)) { - this.isLightPopulated = false; - break CHECK_LIGHT; - } - } - } - - if (this.isLightPopulated) { - for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) { - int k = enumfacing.c() == EnumDirection.EnumAxisDirection.POSITIVE ? 16 : 1; // OBFHELPER: getAxisDirection - final BlockPosition pos = blockpos.shift(enumfacing, k); - final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, neighbors); - if (chunk == null) { - continue; - } - chunk.a(enumfacing.opposite()); // OBFHELPER: checkLightSide - } - - for (int i = 0; i < this.updateSkylightColumns.length; ++i) { - this.updateSkylightColumns[i] = true; - } - - this.recheckGapsAsync(neighbors); - } - } - } - - /** - * Checks light async. - * - * @param x The x position of chunk - * @param z The z position of chunk - * @param neighbors A thread-safe list of surrounding neighbor chunks - * @return True if light update was successful, false if not - */ - private boolean checkLightAsync(int x, int z, List neighbors) { - int i = this.getTopFilledSegment(); - boolean flag = false; - boolean flag1 = false; - MutableBlockPosition blockpos$mutableblockpos = new MutableBlockPosition((this.locX << 4) + x, 0, (this.locZ << 4) + z); - - for (int j = i + 16 - 1; j > this.world.getSeaLevel() || j > 0 && !flag1; --j) { - blockpos$mutableblockpos.setValues(blockpos$mutableblockpos.getX(), j, blockpos$mutableblockpos.getZ()); - int k = this.getBlockData(blockpos$mutableblockpos).c(); // OBFHELPER: getLightOpacity - - if (k == 255 && blockpos$mutableblockpos.getY() < this.world.getSeaLevel()) { - flag1 = true; - } - - if (!flag && k > 0) { - flag = true; - } else if (flag && k == 0 && !this.checkWorldLight(blockpos$mutableblockpos, neighbors)) { - return false; - } - } - - for (int l = blockpos$mutableblockpos.getY(); l > 0; --l) { - blockpos$mutableblockpos.setValues(blockpos$mutableblockpos.getX(), l, blockpos$mutableblockpos.getZ()); - - if (this.getBlockData(blockpos$mutableblockpos).d() > 0) { // getLightValue - this.checkWorldLight(blockpos$mutableblockpos, neighbors); - } - } - - return true; - } - - /** - * Thread-safe method to retrieve a chunk during async light updates. - * - * @param chunkX The x position of chunk. - * @param chunkZ The z position of chunk. - * @param neighbors A thread-safe list of surrounding neighbor chunks - * @return The chunk if available, null if not - */ - private Chunk getLightChunk(int chunkX, int chunkZ, List neighbors) { - final Chunk currentChunk = (Chunk) (Object) this; - if (currentChunk.a(chunkX, chunkZ)) { // OBFHELPER: isAtLocation - if (currentChunk.isUnloading()) { - return null; - } - return currentChunk; - } - if (neighbors == null) { - neighbors = this.getSurroundingChunks(); - if (neighbors.isEmpty()) { - return null; - } - } - for (Chunk neighbor : neighbors) { - if (neighbor.a(chunkX, chunkZ)) { // OBFHELPER: isAtLocation - if (neighbor.isUnloading()) { - return null; - } - return neighbor; - } - } - - return null; - } - - /** - * Checks if surrounding chunks are loaded thread-safe. - * - * @return True if surrounded chunks are loaded, false if not - */ - private boolean isAreaLoaded() { - if (!this.areNeighborsLoaded()) { - return false; - } - - // add diagonal chunks - final Chunk southEastChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(2); - if (southEastChunk == null) { - return false; - } - - final Chunk southWestChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(3); - if (southWestChunk == null) { - return false; - } - - final Chunk northEastChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(2); - if (northEastChunk == null) { - return false; - } - - final Chunk northWestChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(3); - if (northWestChunk == null) { - return false; - } - - return true; - } - - /** - * Gets surrounding chunks thread-safe. - * - * @return The list of surrounding chunks, empty list if not loaded - */ - private List getSurroundingChunks() { - if (!this.areNeighborsLoaded()) { - return Collections.emptyList(); - } - - // add diagonal chunks - final Chunk southEastChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(2); - if (southEastChunk == null) { - return Collections.emptyList(); - } - - final Chunk southWestChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(3); - if (southWestChunk == null) { - return Collections.emptyList(); - } - - final Chunk northEastChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(2); - if (northEastChunk == null) { - return Collections.emptyList(); - } - - final Chunk northWestChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(3); - if (northWestChunk == null) { - return Collections.emptyList(); - } - - List chunkList = this.getNeighbors(); - chunkList.add(southEastChunk); - chunkList.add(southWestChunk); - chunkList.add(northEastChunk); - chunkList.add(northWestChunk); - return chunkList; - } - - @Inject(method = "c(III)V", at = @At("HEAD"), cancellable = true) - private void onRelightBlock(int x, int y, int z, CallbackInfo ci) { - lightExecutorService.execute(() -> { - this.relightBlockAsync(x, y, z); - }); - ci.cancel(); - } - - /** - * Relight's a block async. - * - * @param x The x position - * @param y The y position - * @param z The z position - */ - private void relightBlockAsync(int x, int y, int z) { - int i = this.heightMap[z << 4 | x] & 255; - int j = i; - - if (y > i) { - j = y; - } - - while (j > 0 && this.getBlockData(x, j - 1, z).c() == 0) { // OBFHELPER: getLightOpacity - --j; - } - - if (j != i) { - this.markBlocksDirtyVerticalAsync(x + this.locX * 16, z + this.locZ * 16, j, i); - this.heightMap[z << 4 | x] = j; - int k = this.locX * 16 + x; - int l = this.locZ * 16 + z; - - if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight - if (j < i) { - for (int j1 = j; j1 < i; ++j1) { - ChunkSection extendedblockstorage2 = this.sections[j1 >> 4]; - - if (extendedblockstorage2 != Chunk.EMPTY_CHUNK_SECTION) { - extendedblockstorage2.a(x, j1 & 15, z, 15); // OBFHELPER: setSkyLight - // this.world.m(new BlockPosition((this.locX << 4) + x, j1, (this.locZ << 4) + z)); // OBFHELPER: notifyLightSet - client side - } - } - } else { - for (int i1 = i; i1 < j; ++i1) { - ChunkSection extendedblockstorage = this.sections[i1 >> 4]; - - if (extendedblockstorage != Chunk.EMPTY_CHUNK_SECTION) { - extendedblockstorage.a(x, i1 & 15, z, 0); // OBFHELPER: setSkyLight - // this.world.m(new BlockPosition((this.locX << 4) + x, i1, (this.locZ << 4) + z)); // OBFHELPER: notifyLightSet - client side - } - } - } - - int k1 = 15; - - while (j > 0 && k1 > 0) { - --j; - int i2 = this.getBlockData(x, j, z).c(); - - if (i2 == 0) { - i2 = 1; - } - - k1 -= i2; - - if (k1 < 0) { - k1 = 0; - } - - ChunkSection extendedblockstorage1 = this.sections[j >> 4]; - - if (extendedblockstorage1 != Chunk.EMPTY_CHUNK_SECTION) { - extendedblockstorage1.a(x, j & 15, z, k1); // OBFHELPER: setSkyLight - } - } - } - - 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.world.worldProvider.m()) { // OBFHELPER: hasSkyLight - for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) { - this.updateSkylightNeighborHeight(k + enumfacing.getAdjacentX(), l + enumfacing.getAdjacentZ(), j2, k2); // OBFHELPER: updateSkylightNeighborHeight - } - - this.updateSkylightNeighborHeight(k, l, j2, k2); - } - - this.markDirty(); - } - } - - /** - * Marks a vertical line of blocks as dirty async. - * Instead of calling world directly, we pass chunk safely for async light method. - * - * @param x1 - * @param z1 - * @param x2 - * @param z2 - */ - private void markBlocksDirtyVerticalAsync(int x1, int z1, int x2, int z2) { - if (x2 > z2) { - int i = z2; - z2 = x2; - x2 = i; - } - - if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight - for (int j = x2; j <= z2; ++j) { - final BlockPosition pos = new BlockPosition(x1, j, z1); - final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null); - if (chunk == null) { - continue; - } - ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.SKY, new BlockPosition(x1, j, z1), chunk); - } - } - - this.world.b(x1, x2, z1, x1, z2, z1); // OBFHELPER: markBlockRangeForRenderUpdate - } - - /** - * Checks world light thread-safe. - * - * @param lightType The type of light to check - * @param pos The block position - * @return True if light update was successful, false if not - */ - private boolean checkWorldLightFor(EnumSkyBlock lightType, BlockPosition pos) { - final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null); - if (chunk == null) { - return false; - } - - return ((IMixinWorldServer) this.world).updateLightAsync(lightType, pos, chunk); - } - - private boolean checkWorldLight(BlockPosition pos) { - return this.checkWorldLight(pos, null); - } - - /** - * Checks world light async. - * - * @param pos The block position - * @param neighbors A thread-safe list of surrounding neighbor chunks - * @return True if light update was successful, false if not - */ - private boolean checkWorldLight(BlockPosition pos, List neighbors) { - boolean flag = false; - final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, neighbors); - if (chunk == null) { - return false; - } - - if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight - flag |= ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.SKY, pos, chunk); - } - - flag = flag | ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.BLOCK, pos, chunk); - return flag; - } - - /** - * Gets the list of block positions currently queued for lighting updates. - * - * @param type The light type - * @return The list of queued block positions, empty if none - */ - @Override - public CopyOnWriteArrayList getQueuedLightingUpdates(EnumSkyBlock type) { - if (type == EnumSkyBlock.SKY) { - return this.queuedSkyLightingUpdates; - } - return this.queuedBlockLightingUpdates; - } -} diff --git a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunkProviderServer.java b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunkProviderServer.java deleted file mode 100644 index aa384fcba..000000000 --- a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinChunkProviderServer.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package io.akarin.server.mixin.lighting; - -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.Redirect; - -import io.akarin.api.internal.mixin.IMixinChunk; -import net.minecraft.server.ChunkProviderServer; -import net.minecraft.server.WorldServer; - -@Mixin(value = ChunkProviderServer.class, remap = false, priority = 1001) -public abstract class MixinChunkProviderServer { - @Shadow @Final public WorldServer world; - - @Redirect(method = "unloadChunks", at = @At( - value = "INVOKE", - target = "Lnet/minecraft/server/Chunk;isUnloading()Z" - )) - public boolean shouldUnload(IMixinChunk chunk) { - if (chunk.getPendingLightUpdates().get() > 0 || this.world.getTime() - chunk.getLightUpdateTime() < 20) { - return false; - } - return true; - } -} diff --git a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorld.java b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorld.java deleted file mode 100644 index d09d94a33..000000000 --- a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorld.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package io.akarin.server.mixin.lighting; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import net.minecraft.server.BlockPosition; -import net.minecraft.server.Chunk; -import net.minecraft.server.EnumSkyBlock; -import net.minecraft.server.IChunkProvider; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.World; - -@Mixin(value = World.class, remap = false, priority = 1002) -public abstract class MixinWorld { - @Shadow protected IChunkProvider chunkProvider; - @Shadow int[] J; // OBFHELPER: lightUpdateBlockList - - @Shadow(aliases = "c") public abstract boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos); - @Shadow public abstract MinecraftServer getMinecraftServer(); - @Shadow public abstract boolean areChunksLoaded(BlockPosition center, int radius, boolean allowEmpty); - @Shadow public abstract Chunk getChunkIfLoaded(int x, int z); -} diff --git a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorldServer.java b/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorldServer.java deleted file mode 100644 index 8461839a3..000000000 --- a/sources/src/main/java/io/akarin/server/mixin/lighting/MixinWorldServer.java +++ /dev/null @@ -1,376 +0,0 @@ -/* - * This file is part of Sponge, licensed under the MIT License (MIT). - * - * Copyright (c) SpongePowered - * Copyright (c) contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package io.akarin.server.mixin.lighting; - -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import javax.annotation.Nullable; - -import org.spongepowered.asm.mixin.Mixin; -import com.google.common.util.concurrent.ThreadFactoryBuilder; - -import io.akarin.api.internal.Akari; -import io.akarin.api.internal.mixin.IMixinChunk; -import io.akarin.api.internal.mixin.IMixinWorldServer; -import io.akarin.server.core.AkarinGlobalConfig; -import net.minecraft.server.BlockPosition; -import net.minecraft.server.Chunk; -import net.minecraft.server.EnumDirection; -import net.minecraft.server.EnumSkyBlock; -import net.minecraft.server.IBlockData; -import net.minecraft.server.MCUtil; -import net.minecraft.server.MathHelper; -import net.minecraft.server.WorldServer; -import net.minecraft.server.BlockPosition.PooledBlockPosition; - -@Mixin(value = WorldServer.class, remap = false, priority = 1002) -public abstract class MixinWorldServer extends MixinWorld implements IMixinWorldServer { - - private static final int NUM_XZ_BITS = 4; - private static final int NUM_SHORT_Y_BITS = 8; - private static final short XZ_MASK = 0xF; - private static final short Y_SHORT_MASK = 0xFF; - - private final static ExecutorService lightExecutorService = getExecutorService(); - private static ExecutorService getExecutorService() { - return AkarinGlobalConfig.asyncLightingWorkStealing ? - Executors.newFixedThreadPool(AkarinGlobalConfig.asyncLightingThreads, new ThreadFactoryBuilder().setNameFormat("Akarin Async Light Thread").build()) - : - Executors.newWorkStealingPool(AkarinGlobalConfig.asyncLightingThreads); - } - - @Override - public boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos) { // OBFHELPER: checkLightFor - return updateLightAsync(lightType, pos, null); - } - - @Override - public boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List neighbors) { - // Sponge - This check is not needed as neighbors are checked in updateLightAsync - if (false && !this.areChunksLoaded(pos, 17, false)) { - return false; - } else { - final IMixinChunk spongeChunk = (IMixinChunk) currentChunk; - int recheckIndex = 0; - int blockIndex = 0; - int current = this.getLightForAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method - int rawLight = this.getRawBlockLightAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method - int x = pos.getX(); - int y = pos.getY(); - int z = pos.getZ(); - - if (rawLight > current) { - this.J[blockIndex++] = 133152; // OBFHELPER: lightUpdateBlockList - } else if (rawLight < current) { - this.J[blockIndex++] = 133152 | current << 18; // OBFHELPER: lightUpdateBlockList - - while (recheckIndex < blockIndex) { - int blockData = this.J[recheckIndex++]; // OBFHELPER: lightUpdateBlockList - int i2 = (blockData & 63) - 32 + x; - int j2 = (blockData >> 6 & 63) - 32 + y; - int k2 = (blockData >> 12 & 63) - 32 + z; - int l2 = blockData >> 18 & 15; - BlockPosition blockpos = new BlockPosition(i2, j2, k2); - int lightLevel = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors); // Sponge - use thread safe method - - if (lightLevel == l2) { - this.setLightForAsync(lightType, blockpos, 0, currentChunk, neighbors); // Sponge - use thread safe method - - if (l2 > 0) { - int j3 = MathHelper.a(i2 - x); // abs - int k3 = MathHelper.a(j2 - y); - int l3 = MathHelper.a(k2 - z); - - if (j3 + k3 + l3 < 17) { - PooledBlockPosition mutableBlockpos = PooledBlockPosition.aquire(); - - for (EnumDirection enumfacing : EnumDirection.values()) { - int i4 = i2 + enumfacing.getAdjacentX(); - int j4 = j2 + enumfacing.getAdjacentX(); - int k4 = k2 + enumfacing.getAdjacentX(); - mutableBlockpos.setValues(i4, j4, k4); - // Sponge start - get chunk safely - final Chunk pooledChunk = this.getLightChunk(mutableBlockpos, currentChunk, neighbors); - if (pooledChunk == null) { - continue; - } - int opacity = Math.max(1, pooledChunk.getBlockData(mutableBlockpos).c()); // OBFHELPER: getLightOpacity - lightLevel = this.getLightForAsync(lightType, mutableBlockpos, currentChunk, neighbors); - // Sponge end - - if (lightLevel == l2 - opacity && blockIndex < this.J.length) { // OBFHELPER: lightUpdateBlockList - this.J[blockIndex++] = i4 - x + 32 | j4 - y + 32 << 6 | k4 - z + 32 << 12 | l2 - opacity << 18; // OBFHELPER: lightUpdateBlockList - } - } - - mutableBlockpos.free(); - } - } - } - } - - recheckIndex = 0; - } - - while (recheckIndex < blockIndex) { - int i5 = this.J[recheckIndex++]; // OBFHELPER: lightUpdateBlockList - int j5 = (i5 & 63) - 32 + x; - int k5 = (i5 >> 6 & 63) - 32 + y; - int l5 = (i5 >> 12 & 63) - 32 + z; - BlockPosition blockpos1 = new BlockPosition(j5, k5, l5); - int i6 = this.getLightForAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method - int j6 = this.getRawBlockLightAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method - - if (j6 != i6) { - this.setLightForAsync(lightType, blockpos1, j6, currentChunk, neighbors); // Sponge - use thread safe method - - if (j6 > i6) { - int k6 = Math.abs(j5 - x); - int l6 = Math.abs(k5 - y); - int i7 = Math.abs(l5 - z); - boolean flag = blockIndex < this.J.length - 6; // OBFHELPER: lightUpdateBlockList - - if (k6 + l6 + i7 < 17 && flag) { - // Sponge start - use thread safe method getLightForAsync - if (this.getLightForAsync(lightType, blockpos1.west(), currentChunk, neighbors) < j6) { - this.J[blockIndex++] = j5 - 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList - } - - if (this.getLightForAsync(lightType, blockpos1.east(), currentChunk, neighbors) < j6) { - this.J[blockIndex++] = j5 + 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList - } - - if (this.getLightForAsync(lightType, blockpos1.down(), currentChunk, neighbors) < j6) { - this.J[blockIndex++] = j5 - x + 32 + (k5 - 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList - } - - if (this.getLightForAsync(lightType, blockpos1.up(), currentChunk, neighbors) < j6) { - this.J[blockIndex++] = j5 - x + 32 + (k5 + 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList - } - - if (this.getLightForAsync(lightType, blockpos1.north(), currentChunk, neighbors) < j6) { - this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 - 1 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList - } - - if (this.getLightForAsync(lightType, blockpos1.south(), currentChunk, neighbors) < j6) { - this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 + 1 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList - } - // Sponge end - } - } - } - } - - // Sponge start - Asynchronous light updates - spongeChunk.getQueuedLightingUpdates(lightType).remove((Short) this.blockPosToShort(pos)); - spongeChunk.getPendingLightUpdates().decrementAndGet(); - for (Chunk neighborChunk : neighbors) { - final IMixinChunk neighbor = (IMixinChunk) neighborChunk; - neighbor.getPendingLightUpdates().decrementAndGet(); - } - // Sponge end - return true; - } - } - - @Override - public boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, @Nullable Chunk currentChunk) { - if (this.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) { - return false; - } - - if (currentChunk == null) { - if (!Akari.isPrimaryThread()) return false; - currentChunk = MCUtil.getLoadedChunkWithoutMarkingActive(chunkProvider, pos.getX() >> 4, pos.getZ() >> 4); - } - - final IMixinChunk spongeChunk = (IMixinChunk) currentChunk; - if (currentChunk == null || currentChunk.isUnloading() || !spongeChunk.areNeighborsLoaded()) { - return false; - } - - final short shortPos = this.blockPosToShort(pos); - if (spongeChunk.getQueuedLightingUpdates(lightType).contains(shortPos)) { - return false; - } - - final Chunk chunk = currentChunk; - spongeChunk.getQueuedLightingUpdates(lightType).add(shortPos); - spongeChunk.getPendingLightUpdates().incrementAndGet(); - spongeChunk.setLightUpdateTime(chunk.getWorld().getTime()); - - List neighbors = spongeChunk.getNeighbors(); - // add diagonal chunks - Chunk southEastChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(0)).getNeighborChunk(2); - Chunk southWestChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(0)).getNeighborChunk(3); - Chunk northEastChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(1)).getNeighborChunk(2); - Chunk northWestChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(1)).getNeighborChunk(3); - if (southEastChunk != null) { - neighbors.add(southEastChunk); - } - if (southWestChunk != null) { - neighbors.add(southWestChunk); - } - if (northEastChunk != null) { - neighbors.add(northEastChunk); - } - if (northWestChunk != null) { - neighbors.add(northWestChunk); - } - - for (Chunk neighborChunk : neighbors) { - final IMixinChunk neighbor = (IMixinChunk) neighborChunk; - neighbor.getPendingLightUpdates().incrementAndGet(); - neighbor.setLightUpdateTime(chunk.getWorld().getTime()); - } - - if (Akari.isPrimaryThread()) { // Akarin - lightExecutorService.execute(() -> { - this.checkLightAsync(lightType, pos, chunk, neighbors); - }); - } else { - this.checkLightAsync(lightType, pos, chunk, neighbors); - } - - return true; - } - - @Override - public ExecutorService getLightingExecutor() { - return lightExecutorService; - } - - // Thread safe methods to retrieve a chunk during async light updates - // Each method avoids calling getLoadedChunk and instead accesses the passed neighbor chunk list to avoid concurrency issues - public Chunk getLightChunk(BlockPosition pos, Chunk currentChunk, List neighbors) { - if (currentChunk.a(pos.getX() >> 4, pos.getZ() >> 4)) { // OBFHELPER: isAtLocation - if (currentChunk.isUnloading()) { - return null; - } - return currentChunk; - } - for (Chunk neighbor : neighbors) { - if (neighbor.a(pos.getX() >> 4, pos.getZ() >> 4)) { // OBFHELPER: isAtLocation - if (neighbor.isUnloading()) { - return null; - } - return neighbor; - } - } - - return null; - } - - private int getLightForAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List neighbors) { - if (pos.getY() < 0) { - pos = new BlockPosition(pos.getX(), 0, pos.getZ()); - } - if (!pos.isValidLocation()) { - return lightType.c; // OBFHELPER: defaultLightValue - } - - final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors); - if (chunk == null || chunk.isUnloading()) { - return lightType.c; // OBFHELPER: defaultLightValue - } - - return chunk.getBrightness(lightType, pos); - } - - private int getRawBlockLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List neighbors) { - final Chunk chunk = getLightChunk(pos, currentChunk, neighbors); - if (chunk == null || chunk.isUnloading()) { - return lightType.c; // OBFHELPER: defaultLightValue - } - if (lightType == EnumSkyBlock.SKY && chunk.c(pos)) { // OBFHELPER: canSeeSky - return 15; - } else { - IBlockData blockData = chunk.getBlockData(pos); - int blockLight = blockData.d(); // getLightValue - int rawLight = lightType == EnumSkyBlock.SKY ? 0 : blockLight; - int opacity = blockData.c(); // OBFHELPER: getLightOpacity - - if (opacity >= 15 && blockLight > 0) { - opacity = 1; - } - - if (opacity < 1) { - opacity = 1; - } - - if (opacity >= 15) { - return 0; - } else if (rawLight >= 14) { - return rawLight; - } else { - for (EnumDirection facing : EnumDirection.values()) { - BlockPosition blockpos = pos.shift(facing); - int current = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors) - opacity; - - if (current > rawLight) { - rawLight = current; - } - - if (rawLight >= 14) { - return rawLight; - } - } - - return rawLight; - } - } - } - - public void setLightForAsync(EnumSkyBlock type, BlockPosition pos, int lightValue, Chunk currentChunk, List neighbors) { - if (pos.isValidLocation()) { - final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors); - if (chunk != null && !chunk.isUnloading()) { - chunk.a(type, pos, lightValue); // OBFHELPER: setLightFor - // this.notifyLightSet(pos); // client side - } - } - } - - private short blockPosToShort(BlockPosition pos) { - short serialized = (short) setNibble(0, pos.getX() & XZ_MASK, 0, NUM_XZ_BITS); - serialized = (short) setNibble(serialized, pos.getY() & Y_SHORT_MASK, 1, NUM_SHORT_Y_BITS); - serialized = (short) setNibble(serialized, pos.getZ() & XZ_MASK, 3, NUM_XZ_BITS); - return serialized; - } - - /** - * Modifies bits in an integer. - * - * @param num Integer to modify - * @param data Bits of data to add - * @param which Index of nibble to start at - * @param bitsToReplace The number of bits to replace starting from nibble index - * @return The modified integer - */ - private int setNibble(int num, int data, int which, int bitsToReplace) { - return (num & ~(bitsToReplace << (which * 4)) | (data << (which * 4))); - } -} diff --git a/sources/src/main/resources/mixins.akarin.optimization.lighting.json b/sources/src/main/resources/mixins.akarin.optimization.lighting.json deleted file mode 100644 index 28cc496c4..000000000 --- a/sources/src/main/resources/mixins.akarin.optimization.lighting.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "required": true, - "minVersion": "0.7.10", - "package": "io.akarin.server.mixin", - "target": "@env(DEFAULT)", - "compatibilityLevel": "JAVA_8", - "server": [ - "cps.MixinChunk", - - "lighting.MixinChunk", - "lighting.MixinWorld", - "lighting.MixinWorldServer", - "lighting.MixinChunkProviderServer", - ] -} \ No newline at end of file