aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/threads/GT_Runnable_MachineBlockUpdate.java
blob: fedf13673df1e88b822ff7ed51dac5d1f5aed11e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package gregtech.api.threads;

import gregtech.GT_Mod;
import gregtech.api.GregTech_API;
import gregtech.api.interfaces.tileentity.IMachineBlockUpdateable;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.ChunkPosition;
import net.minecraft.world.World;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

public class GT_Runnable_MachineBlockUpdate implements Runnable {
    //used by runner thread
    private final int x, y, z;
    private final World world;
    private final Set<ChunkPosition> visited = new HashSet<>(80);

    //Threading
    private static final ThreadFactory THREAD_FACTORY= r -> {
        Thread thread=new Thread(r);
        thread.setName("GT_MachineBlockUpdate");
        return thread;
    };
    private static ExecutorService EXECUTOR_SERVICE;

    //This class should never be initiated outside of this class!
    private GT_Runnable_MachineBlockUpdate(World aWorld, int aX, int aY, int aZ) {
        this.world = aWorld;
        this.x = aX;
        this.y = aY;
        this.z = aZ;
    }

    /**
     * If the thread is idle, sets new values and remove the idle flag, otherwise, queue the cooridinates.
     */
    public static void setMachineUpdateValues(World aWorld, int aX, int aY, int aZ) {
        EXECUTOR_SERVICE.submit(new GT_Runnable_MachineBlockUpdate(aWorld,aX,aY,aZ));
    }

    public static void initExecutorService() {
        EXECUTOR_SERVICE =  Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(),THREAD_FACTORY);
        //Executors.newSingleThreadExecutor(THREAD_FACTORY);
        //Executors.newCachedThreadPool(THREAD_FACTORY);
        //Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(),THREAD_FACTORY);
    }

    public static void shutdownExecutorService() {
        try {
            EXECUTOR_SERVICE.awaitTermination(60, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            GT_Mod.GT_FML_LOGGER.error("Well this interruption got interrupted...", e);
        }
        //terminates executor permanently
        EXECUTOR_SERVICE.shutdownNow();
    }

    private boolean shouldRecurse(TileEntity aTileEntity, int aX, int aY, int aZ) {
        //no check on IGregTechTileEntity as it should call the underlying meta tile isMachineBlockUpdateRecursive
        //if (aTileEntity instanceof IGregTechTileEntity) {
        //    return ((IGregTechTileEntity) aTileEntity).isMachineBlockUpdateRecursive();
        //}
        return (aTileEntity instanceof IMachineBlockUpdateable &&
                ((IMachineBlockUpdateable) aTileEntity).isMachineBlockUpdateRecursive()) ||
                GregTech_API.isMachineBlock(world.getBlock(aX, aY, aZ), world.getBlockMetadata(aX, aY, aZ));
    }

    private void causeUpdate(TileEntity tileEntity){
        //no check for IGregTechTileEntity as it should call the underlying meta tile onMachineBlockUpdate
        if (tileEntity instanceof IMachineBlockUpdateable) {
            ((IMachineBlockUpdateable) tileEntity).onMachineBlockUpdate();
        }
    }

    private void stepToUpdateMachine(int aX, int aY, int aZ) {
        if (!visited.add(new ChunkPosition(aX, aY, aZ)))
            return;
        TileEntity tTileEntity = world.getTileEntity(aX, aY, aZ);

        causeUpdate(tTileEntity);

        if (visited.size() < 5 || shouldRecurse(tTileEntity, aX, aY, aZ)) {
            stepToUpdateMachine(aX + 1, aY, aZ);
            stepToUpdateMachine(aX - 1, aY, aZ);
            stepToUpdateMachine(aX, aY + 1, aZ);
            stepToUpdateMachine(aX, aY - 1, aZ);
            stepToUpdateMachine(aX, aY, aZ + 1);
            stepToUpdateMachine(aX, aY, aZ - 1);
        }
    }

    @Override
    public void run() {
        try {
            stepToUpdateMachine(x, y, z);
        } catch (Exception e) {
            GT_Mod.GT_FML_LOGGER.error("Well this update was broken... " + new Coordinates(x,y,z,world), e);
        }
    }

    public static class Coordinates {
        public final int mX;
        public final int mY;
        public final int mZ;
        public final World mWorld;

        public Coordinates(int mX, int mY, int mZ, World mWorld) {
            this.mX = mX;
            this.mY = mY;
            this.mZ = mZ;
            this.mWorld = mWorld;
        }

        @Override
        public String toString() {
            return "Coordinates{" +
                    "mX=" + mX +
                    ", mY=" + mY +
                    ", mZ=" + mZ +
                    ", mWorld=" + mWorld.getProviderName()+ " @dimId " + mWorld.provider.dimensionId +
                    '}';
        }
    }
}