|
| 1 | +package dev.compactmods.machines.rooms.chunkloading; |
| 2 | + |
| 3 | +import java.util.*; |
| 4 | +import com.google.common.collect.ImmutableList; |
| 5 | +import dev.compactmods.machines.CompactMachines; |
| 6 | +import dev.compactmods.machines.core.Registration; |
| 7 | +import dev.compactmods.machines.data.graph.CompactMachineConnectionGraph; |
| 8 | +import dev.compactmods.machines.data.persistent.CompactMachineData; |
| 9 | +import dev.compactmods.machines.data.persistent.MachineConnections; |
| 10 | +import dev.compactmods.machines.teleportation.DimensionalPosition; |
| 11 | +import net.minecraft.server.MinecraftServer; |
| 12 | +import net.minecraft.util.math.ChunkPos; |
| 13 | +import net.minecraft.world.Dimension; |
| 14 | +import net.minecraft.world.server.ServerWorld; |
| 15 | +import net.minecraftforge.common.world.ForgeChunkManager; |
| 16 | + |
| 17 | +public class CMRoomChunkloadingManager implements IRoomChunkloadingManager { |
| 18 | + |
| 19 | + private final MinecraftServer server; |
| 20 | + private final Map<ChunkPos, UUID> tickets; |
| 21 | + |
| 22 | + // TODO - Finish and serialize data |
| 23 | + // see ForcedChunksSaveData |
| 24 | + |
| 25 | + public CMRoomChunkloadingManager(MinecraftServer server) { |
| 26 | + this.server = server; |
| 27 | + this.tickets = new HashMap<>(); |
| 28 | + } |
| 29 | + |
| 30 | + @Override |
| 31 | + public boolean roomIsLoaded(ChunkPos room) { |
| 32 | + return tickets.containsKey(room); |
| 33 | + } |
| 34 | + |
| 35 | + @Override |
| 36 | + public boolean hasAnyMachinesLoaded() { |
| 37 | + return !tickets.isEmpty(); |
| 38 | + } |
| 39 | + |
| 40 | + @Override |
| 41 | + public void onMachineChunkUnload(int machine) { |
| 42 | + final Optional<ChunkPos> attachedRoom = getConnectedRoom(machine); |
| 43 | + attachedRoom.ifPresent(room -> { |
| 44 | + final Collection<Integer> machines = getConnectedMachines(room); |
| 45 | + switch (machines.size()) { |
| 46 | + case 0: |
| 47 | + case 1: |
| 48 | + // No siblings or this is the only machine connected - unload the room |
| 49 | + setChunkForced(room, false); |
| 50 | + break; |
| 51 | + |
| 52 | + default: |
| 53 | + // More than one machine attached to the room |
| 54 | + // Need to see if any other machine is still loaded before decision |
| 55 | + final CompactMachineData machData = CompactMachineData.get(server); |
| 56 | + if (machData == null) { |
| 57 | + // ohshi- |
| 58 | + return; |
| 59 | + } |
| 60 | + |
| 61 | + // true if any connected machine is loaded; false otherwise |
| 62 | + boolean anyRoomMachineLoaded = machines.stream() |
| 63 | + .map(machData::getMachineLocation) |
| 64 | + .anyMatch(machLocation -> machLocation.map(d -> d.isLoaded(server)).orElse(false)); |
| 65 | + |
| 66 | + if(!anyRoomMachineLoaded) { |
| 67 | + setChunkForced(room, false); |
| 68 | + } |
| 69 | + break; |
| 70 | + } |
| 71 | + }); |
| 72 | + } |
| 73 | + |
| 74 | + @Override |
| 75 | + public void onMachineChunkLoad(int machine) { |
| 76 | + final Optional<ChunkPos> attachedRoom = getConnectedRoom(machine); |
| 77 | + attachedRoom.ifPresent(room -> { |
| 78 | + // If there's already a ticket for the room, early exit - another machine has it loaded |
| 79 | + if (tickets.containsKey(room)) |
| 80 | + return; |
| 81 | + |
| 82 | + setChunkForced(room, true); |
| 83 | + }); |
| 84 | + } |
| 85 | + |
| 86 | + private Optional<CompactMachineConnectionGraph> getGraph() { |
| 87 | + final MachineConnections conns = MachineConnections.get(server); |
| 88 | + if (conns == null) |
| 89 | + return Optional.empty(); |
| 90 | + |
| 91 | + return Optional.of(conns.graph); |
| 92 | + } |
| 93 | + |
| 94 | + private Optional<ChunkPos> getConnectedRoom(int machine) { |
| 95 | + return getGraph().flatMap(graph -> graph.getConnectedRoom(machine)); |
| 96 | + } |
| 97 | + |
| 98 | + |
| 99 | + private Collection<Integer> getConnectedMachines(ChunkPos room) { |
| 100 | + return getGraph().map(graph -> graph.getMachinesFor(room)) |
| 101 | + .map(connectedMachines -> { |
| 102 | + switch (connectedMachines.size()) { |
| 103 | + case 0: |
| 104 | + case 1: |
| 105 | + // release ticket |
| 106 | + return Collections.<Integer>emptySet(); |
| 107 | + |
| 108 | + default: |
| 109 | + // scan connected machines, if at least one is loaded then do not release |
| 110 | + return ImmutableList.copyOf(connectedMachines); |
| 111 | + } |
| 112 | + }).orElse(Collections.emptySet()); |
| 113 | + } |
| 114 | + |
| 115 | + private Collection<Integer> getRoomConnectedMachines(int machine) { |
| 116 | + final CompactMachineConnectionGraph graph = getGraph().orElse(null); |
| 117 | + if (graph == null) |
| 118 | + return Collections.emptySet(); |
| 119 | + |
| 120 | + return getConnectedRoom(machine) |
| 121 | + .map(this::getConnectedMachines) |
| 122 | + .orElse(Collections.emptySet()); |
| 123 | + } |
| 124 | + |
| 125 | + private void setChunkForced(ChunkPos room, boolean force) { |
| 126 | + // If trying to force and room was not previously set up |
| 127 | + if (force && !tickets.containsKey(room)) { |
| 128 | + UUID newTicket = UUID.randomUUID(); |
| 129 | + tickets.put(room, newTicket); |
| 130 | + } |
| 131 | + |
| 132 | + UUID ticket = tickets.get(room); |
| 133 | + |
| 134 | + ServerWorld compact = server.getLevel(Registration.COMPACT_DIMENSION); |
| 135 | + |
| 136 | + if (compact != null) { |
| 137 | + boolean alreadyForced = compact.getForcedChunks().stream() |
| 138 | + .anyMatch(chunkLong -> chunkLong.equals(room.toLong())); |
| 139 | + |
| 140 | + // if force status requires change |
| 141 | + if(alreadyForced != force) { |
| 142 | + // prevent deadlock? |
| 143 | + compact.getChunk(room.x, room.z); |
| 144 | + |
| 145 | + boolean changed = ForgeChunkManager.forceChunk(compact, CompactMachines.MOD_ID, |
| 146 | + ticket, room.x, room.z, force, true); |
| 147 | + |
| 148 | + if (!force && changed) { |
| 149 | + tickets.remove(room); |
| 150 | + } |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + } |
| 155 | +} |
0 commit comments