diff options
Diffstat (limited to 'src/main/java/gregtech/api/threads')
3 files changed, 312 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java b/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java new file mode 100644 index 0000000000..5e3b36f473 --- /dev/null +++ b/src/main/java/gregtech/api/threads/GT_Runnable_Cable_Update.java @@ -0,0 +1,87 @@ +package gregtech.api.threads; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizon.gtnhlib.util.CoordinatePacker; + +import gregtech.GT_Mod; +import gregtech.api.interfaces.tileentity.IMachineBlockUpdateable; +import gregtech.api.metatileentity.BaseMetaPipeEntity; +import gregtech.api.metatileentity.implementations.GT_MetaPipeEntity_Cable; +import gregtech.common.GT_Proxy; + +public class GT_Runnable_Cable_Update extends GT_Runnable_MachineBlockUpdate { + + protected GT_Runnable_Cable_Update(World aWorld, int posX, int posY, int posZ) { + super(aWorld, posX, posY, posZ); + } + + public static void setCableUpdateValues(World aWorld, int posX, int posY, int posZ) { + if (isEnabled) { + EXECUTOR_SERVICE.submit(new GT_Runnable_Cable_Update(aWorld, posX, posY, posZ)); + } + } + + @Override + public void run() { + int posX, posY, posZ; + try { + while (!tQueue.isEmpty()) { + final long packedCoords = tQueue.dequeueLong(); + posX = CoordinatePacker.unpackX(packedCoords); + posY = CoordinatePacker.unpackY(packedCoords); + posZ = CoordinatePacker.unpackZ(packedCoords); + + final TileEntity tTileEntity; + + GT_Proxy.TICK_LOCK.lock(); + try { + // we dont want to go over cables that are in unloaded chunks + // keeping the lock just to make sure no CME happens + if (world.blockExists(posX, posY, posZ)) { + tTileEntity = world.getTileEntity(posX, posY, posZ); + } else { + tTileEntity = null; + } + } finally { + GT_Proxy.TICK_LOCK.unlock(); + } + + // See if the block itself needs an update + if (tTileEntity instanceof IMachineBlockUpdateable) + ((IMachineBlockUpdateable) tTileEntity).onMachineBlockUpdate(); + + // Now see if we should add the nearby blocks to the queue: + // only add blocks the cable is connected to + if (tTileEntity instanceof BaseMetaPipeEntity metaPipe + && metaPipe.getMetaTileEntity() instanceof GT_MetaPipeEntity_Cable cable) { + for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { + final ForgeDirection side = ForgeDirection.VALID_DIRECTIONS[i]; + if (cable.isConnectedAtSide(side)) { + final long tCoords = CoordinatePacker + .pack(posX + side.offsetX, posY + side.offsetY, posZ + side.offsetZ); + if (visited.add(tCoords)) { + tQueue.enqueue(tCoords); + } + } + } + } + } + } catch (Exception e) { + GT_Mod.GT_FML_LOGGER.error( + "Well this update was broken... " + initialX + + ", " + + initialY + + ", " + + initialZ + + ", mWorld={" + + world.getProviderName() + + " @dimId " + + world.provider.dimensionId + + "}", + e); + } + } +} diff --git a/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java b/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java new file mode 100644 index 0000000000..339556e246 --- /dev/null +++ b/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java @@ -0,0 +1,184 @@ +package gregtech.api.threads; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; + +import com.gtnewhorizon.gtnhlib.util.CoordinatePacker; + +import gregtech.GT_Mod; +import gregtech.api.GregTech_API; +import gregtech.api.interfaces.tileentity.IMachineBlockUpdateable; +import gregtech.common.GT_Proxy; +import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; + +public class GT_Runnable_MachineBlockUpdate implements Runnable { + + // used by runner thread + protected final int initialX, initialY, initialZ; + protected final World world; + protected final LongSet visited = new LongOpenHashSet(); + protected final LongArrayFIFOQueue tQueue = new LongArrayFIFOQueue(); + + // Threading + private static final ThreadFactory THREAD_FACTORY = r -> { + Thread thread = new Thread(r); + thread.setName("GT_MachineBlockUpdate"); + return thread; + }; + protected static ExecutorService EXECUTOR_SERVICE; + + // This class should never be initiated outside of this class! + protected GT_Runnable_MachineBlockUpdate(World aWorld, int posX, int posY, int posZ) { + this.world = aWorld; + this.initialX = posX; + this.initialY = posY; + this.initialZ = posZ; + final long coords = CoordinatePacker.pack(posX, posY, posZ); + visited.add(coords); + tQueue.enqueue(coords); + } + + public static boolean isEnabled() { + return isEnabled; + } + + public static void setEnabled() { + GT_Runnable_MachineBlockUpdate.isEnabled = true; + } + + public static void setDisabled() { + GT_Runnable_MachineBlockUpdate.isEnabled = false; + } + + public static void setEnabled(boolean isEnabled) { + GT_Runnable_MachineBlockUpdate.isEnabled = isEnabled; + } + + public static boolean isCurrentThreadEnabled() { + return perThreadEnable.get(); + } + + public static void setCurrentThreadEnabled(boolean perThreadEnable) { + GT_Runnable_MachineBlockUpdate.perThreadEnable.set(perThreadEnable); + } + + protected static boolean isEnabled = true; + protected static final ThreadLocal<Boolean> perThreadEnable = ThreadLocal.withInitial(() -> true); + + public static void setMachineUpdateValues(World aWorld, int posX, int posY, int posZ) { + if (isEnabled() && isCurrentThreadEnabled()) { + EXECUTOR_SERVICE.submit(new GT_Runnable_MachineBlockUpdate(aWorld, posX, posY, posZ)); + } + } + + public static void initExecutorService() { + EXECUTOR_SERVICE = Executors.newFixedThreadPool( + Math.max( + 1, + (Runtime.getRuntime() + .availableProcessors() * 2 + / 3)), + THREAD_FACTORY); + } + + public static void shutdownExecutorService() { + try { + GT_Mod.GT_FML_LOGGER.info("Shutting down Machine block update executor service"); + EXECUTOR_SERVICE.shutdown(); // Disable new tasks from being submitted + // Wait a while for existing tasks to terminate + if (!EXECUTOR_SERVICE.awaitTermination(60, TimeUnit.SECONDS)) { + EXECUTOR_SERVICE.shutdownNow(); // Cancel currently executing tasks + // Wait a while for tasks to respond to being cancelled + if (!EXECUTOR_SERVICE.awaitTermination(60, TimeUnit.SECONDS)) { + GT_Mod.GT_FML_LOGGER.error( + "Well this didn't terminated well... GT_Runnable_MachineBlockUpdate.shutdownExecutorService"); + } + } + } catch (InterruptedException ie) { + GT_Mod.GT_FML_LOGGER.error("Well this interruption got interrupted...", ie); + // (Re-)Cancel if current thread also interrupted + EXECUTOR_SERVICE.shutdownNow(); + // Preserve interrupt status + Thread.currentThread() + .interrupt(); + } catch (Exception e) { + GT_Mod.GT_FML_LOGGER.error("Well this didn't terminated well...", e); + // (Re-)Cancel in case + EXECUTOR_SERVICE.shutdownNow(); + } finally { + GT_Mod.GT_FML_LOGGER.info("Leaving... GT_Runnable_MachineBlockUpdate.shutdownExecutorService"); + } + } + + @Override + public void run() { + int posX, posY, posZ; + try { + while (!tQueue.isEmpty()) { + final long packedCoords = tQueue.dequeueLong(); + posX = CoordinatePacker.unpackX(packedCoords); + posY = CoordinatePacker.unpackY(packedCoords); + posZ = CoordinatePacker.unpackZ(packedCoords); + + final TileEntity tTileEntity; + final boolean isMachineBlock; + + // This might load a chunk... which might load a TileEntity... which might get added to + // `loadedTileEntityList`... which might be in the process + // of being iterated over during `UpdateEntities()`... which might cause a + // ConcurrentModificationException. So, lock that shit. + GT_Proxy.TICK_LOCK.lock(); + try { + tTileEntity = world.getTileEntity(posX, posY, posZ); + isMachineBlock = GregTech_API + .isMachineBlock(world.getBlock(posX, posY, posZ), world.getBlockMetadata(posX, posY, posZ)); + } finally { + GT_Proxy.TICK_LOCK.unlock(); + } + + // See if the block itself needs an update + if (tTileEntity instanceof IMachineBlockUpdateable) + ((IMachineBlockUpdateable) tTileEntity).onMachineBlockUpdate(); + + // Now see if we should add the nearby blocks to the queue: + // 1) If we've visited less than 5 blocks, then yes + // 2) If the tile says we should recursively updated (pipes don't, machine blocks do) + // 3) If the block at the coordinates is marked as a machine block + if (visited.size() < 5 + || (tTileEntity instanceof IMachineBlockUpdateable + && ((IMachineBlockUpdateable) tTileEntity).isMachineBlockUpdateRecursive()) + || isMachineBlock) { + for (int i = 0; i < ForgeDirection.VALID_DIRECTIONS.length; i++) { + final ForgeDirection side = ForgeDirection.VALID_DIRECTIONS[i]; + final long tCoords = CoordinatePacker + .pack(posX + side.offsetX, posY + side.offsetY, posZ + side.offsetZ); + if (visited.add(tCoords)) { + tQueue.enqueue(tCoords); + } + } + } + } + } catch (Exception e) { + GT_Mod.GT_FML_LOGGER.error( + "Well this update was broken... " + initialX + + ", " + + initialY + + ", " + + initialZ + + ", mWorld={" + + world.getProviderName() + + " @dimId " + + world.provider.dimensionId + + "}", + e); + } + } +} diff --git a/src/main/java/gregtech/api/threads/GT_Runnable_Sound.java b/src/main/java/gregtech/api/threads/GT_Runnable_Sound.java new file mode 100644 index 0000000000..9021a60e89 --- /dev/null +++ b/src/main/java/gregtech/api/threads/GT_Runnable_Sound.java @@ -0,0 +1,41 @@ +package gregtech.api.threads; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import gregtech.api.util.GT_PlayedSound; +import gregtech.api.util.GT_Utility; + +public class GT_Runnable_Sound implements Runnable { + + private final double mX, mY, mZ; + private final int mTimeUntilNextSound; + private final World mWorld; + private final ResourceLocation mSoundResourceLocation; + private final float mSoundStrength, mSoundModulation; + + public GT_Runnable_Sound(World aWorld, double aX, double aY, double aZ, int aTimeUntilNextSound, + ResourceLocation aSoundResourceLocation, float aSoundStrength, float aSoundModulation) { + mWorld = aWorld; + mX = aX; + mY = aY; + mZ = aZ; + mTimeUntilNextSound = aTimeUntilNextSound; + mSoundResourceLocation = aSoundResourceLocation; + mSoundStrength = aSoundStrength; + mSoundModulation = aSoundModulation; + } + + @Override + public void run() { + try { + GT_PlayedSound tSound; + if (GT_Utility.sPlayedSoundMap.containsKey(tSound = new GT_PlayedSound(mSoundResourceLocation, mX, mY, mZ))) + return; + mWorld.playSound(mX, mY, mZ, mSoundResourceLocation.toString(), mSoundStrength, mSoundModulation, false); + GT_Utility.sPlayedSoundMap.put(tSound, mTimeUntilNextSound); + } catch (Throwable e) { + /**/ + } + } +} |