Skip to content

Commit d8623d4

Browse files
committedNov 1, 2024·
Added signals (basically events you can subscribe to).
- Added 2 signals for block changes. Added generic block network system + network walkers. A few new utility methods.
1 parent 773d543 commit d8623d4

20 files changed

+1123
-3
lines changed
 

‎src/main/java/sunsetsatellite/catalyst/Catalyst.java

+24
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.slf4j.Logger;
1616
import org.slf4j.LoggerFactory;
1717
import sunsetsatellite.catalyst.core.util.*;
18+
import sunsetsatellite.catalyst.core.util.network.NetworkManager;
1819
import turniplabs.halplibe.helper.NetworkHelper;
1920

2021
import java.util.*;
@@ -26,12 +27,16 @@ public class Catalyst implements ModInitializer {
2627

2728
public static final Registry<MpGuiEntry> GUIS = new Registry<>();
2829

30+
public static final Signal<BlockChangeInfo> TILE_ENTITY_BLOCK_CHANGED_SIGNAL = new Signal<>();
31+
public static final Signal<BlockChangeInfo> ANY_BLOCK_CHANGED_SIGNAL = new Signal<>();
32+
2933
static {
3034
NetworkHelper.register(PacketOpenGui.class,false,true);
3135
}
3236

3337
@Override
3438
public void onInitialize() {
39+
TILE_ENTITY_BLOCK_CHANGED_SIGNAL.connect(NetworkManager.getInstance());
3540
LOGGER.info("Catalyst initialized.");
3641
}
3742

@@ -116,6 +121,11 @@ public static <T> List<T> listOf(T... values){
116121
return new ArrayList<>(Arrays.asList(values));
117122
}
118123

124+
@SafeVarargs
125+
public static <T> Set<T> setOf(T... values){
126+
return new HashSet<>(Arrays.asList(values));
127+
}
128+
119129
public static <T,U> List<Pair<T,U>> zip(List<T> first, List<U> second){
120130
List<Pair<T,U>> list = new ArrayList<>();
121131
List<?> shortest = first.size() < second.size() ? first : second;
@@ -124,4 +134,18 @@ public static <T,U> List<Pair<T,U>> zip(List<T> first, List<U> second){
124134
}
125135
return list;
126136
}
137+
138+
/**
139+
* @param values The values to be checked
140+
* @return Returns the smallest of <code>values</code>
141+
*/
142+
public static long multiMin(long... values){
143+
long min = Long.MAX_VALUE;
144+
for (long value : values) {
145+
if(value < min){
146+
min = value;
147+
}
148+
}
149+
return min;
150+
}
127151
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package sunsetsatellite.catalyst.core.mixin;
2+
3+
import com.mojang.nbt.CompoundTag;
4+
import net.minecraft.core.world.World;
5+
import net.minecraft.core.world.save.DimensionData;
6+
import net.minecraft.core.world.save.ISaveFormat;
7+
import net.minecraft.core.world.save.LevelStorage;
8+
import net.minecraft.core.world.save.SaveHandlerBase;
9+
import org.spongepowered.asm.mixin.Final;
10+
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.Shadow;
12+
import org.spongepowered.asm.mixin.Unique;
13+
import org.spongepowered.asm.mixin.injection.At;
14+
import org.spongepowered.asm.mixin.injection.Inject;
15+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
16+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
17+
import sunsetsatellite.catalyst.core.util.mixin.interfaces.ISaveHandlerWorld;
18+
import sunsetsatellite.catalyst.core.util.network.NetworkManager;
19+
20+
import java.lang.ref.WeakReference;
21+
22+
23+
@Mixin(value = SaveHandlerBase.class,remap = false)
24+
public abstract class SaveHandlerBaseMixin implements LevelStorage, ISaveHandlerWorld {
25+
26+
@Shadow @Final
27+
ISaveFormat saveFormat;
28+
29+
@Shadow @Final
30+
String worldDirName;
31+
32+
@Unique
33+
private WeakReference<World> world;
34+
35+
@Override
36+
public World getWorld() {
37+
return world.get();
38+
}
39+
40+
@Override
41+
public void setWorld(World world) {
42+
this.world = new WeakReference<>(world);
43+
}
44+
45+
@Inject(method = "getDimensionData", at = @At("HEAD"))
46+
public void getDimensionData(int dimensionId, CallbackInfoReturnable<DimensionData> cir) {
47+
CompoundTag data = saveFormat.getDimensionDataRaw(worldDirName, dimensionId);
48+
if(data != null){
49+
NetworkManager.netsFromTag(world.get(), data.getCompound("Networks"));
50+
}
51+
}
52+
53+
@Inject(method = "saveDimensionDataRaw", at = @At("HEAD"))
54+
public void saveDimensionDataRaw(int dimensionId, CompoundTag dimensionDataTag, CallbackInfo ci) {
55+
CompoundTag networksNbt = new CompoundTag();
56+
NetworkManager.netsToTag(world.get(), networksNbt);
57+
dimensionDataTag.put("Networks", networksNbt);
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package sunsetsatellite.catalyst.core.mixin;
2+
3+
import net.minecraft.core.block.entity.TileEntity;
4+
import net.minecraft.core.world.World;
5+
import net.minecraft.core.world.save.LevelStorage;
6+
import org.spongepowered.asm.mixin.Final;
7+
import org.spongepowered.asm.mixin.Mixin;
8+
import org.spongepowered.asm.mixin.Shadow;
9+
import org.spongepowered.asm.mixin.Unique;
10+
import org.spongepowered.asm.mixin.injection.At;
11+
import org.spongepowered.asm.mixin.injection.Inject;
12+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
13+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
14+
import sunsetsatellite.catalyst.Catalyst;
15+
import sunsetsatellite.catalyst.core.util.BlockChangeInfo;
16+
import sunsetsatellite.catalyst.core.util.Vec3i;
17+
import sunsetsatellite.catalyst.core.util.mixin.interfaces.ISaveHandlerWorld;
18+
import sunsetsatellite.catalyst.core.util.network.NetworkManager;
19+
20+
@Mixin(value = World.class,remap = false)
21+
public abstract class WorldMixin {
22+
23+
@Shadow
24+
@Final
25+
public LevelStorage saveHandler;
26+
27+
@Shadow public abstract int getBlockMetadata(int x, int y, int z);
28+
29+
@Shadow public abstract int getBlockId(int x, int y, int z);
30+
31+
@Shadow public abstract TileEntity getBlockTileEntity(int x, int y, int z);
32+
33+
@Unique
34+
private final World thisAs = (World)((Object)this);
35+
36+
@Inject(method = "<init>(Lnet/minecraft/core/world/World;Lnet/minecraft/core/world/Dimension;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/world/save/LevelStorage;getDimensionData(I)Lnet/minecraft/core/world/save/DimensionData;", shift = At.Shift.BEFORE))
37+
public void init1(CallbackInfo ci){
38+
((ISaveHandlerWorld) saveHandler).setWorld(thisAs);
39+
}
40+
41+
@Inject(method = "<init>(Lnet/minecraft/core/world/save/LevelStorage;Ljava/lang/String;Lnet/minecraft/core/world/Dimension;Lnet/minecraft/core/world/type/WorldType;J)V", at = @At(value = "TAIL"))
42+
public void init2(CallbackInfo ci){
43+
((ISaveHandlerWorld) saveHandler).setWorld(thisAs);
44+
}
45+
46+
@Inject(method = "<init>(Lnet/minecraft/core/world/save/LevelStorage;Ljava/lang/String;JLnet/minecraft/core/world/Dimension;Lnet/minecraft/core/world/type/WorldType;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/world/save/LevelStorage;getDimensionData(I)Lnet/minecraft/core/world/save/DimensionData;", shift = At.Shift.BEFORE))
47+
public void init3(CallbackInfo ci){
48+
((ISaveHandlerWorld) saveHandler).setWorld(thisAs);
49+
}
50+
51+
@Inject(method = "<init>(Lnet/minecraft/core/world/World;Lnet/minecraft/core/world/Dimension;)V", at = @At("TAIL"))
52+
public void init4(CallbackInfo ci){
53+
NetworkManager.updateAllNets();
54+
}
55+
56+
@Inject(method = "<init>(Lnet/minecraft/core/world/save/LevelStorage;Ljava/lang/String;Lnet/minecraft/core/world/Dimension;Lnet/minecraft/core/world/type/WorldType;J)V", at = @At(value = "TAIL"))
57+
public void init5(CallbackInfo ci){
58+
NetworkManager.updateAllNets();
59+
}
60+
61+
@Inject(method = "<init>(Lnet/minecraft/core/world/save/LevelStorage;Ljava/lang/String;JLnet/minecraft/core/world/Dimension;Lnet/minecraft/core/world/type/WorldType;)V", at = @At("TAIL"))
62+
public void init6(CallbackInfo ci){
63+
NetworkManager.updateAllNets();
64+
}
65+
66+
@Inject(method = "setBlock", at = @At("RETURN"))
67+
public void setBlock(int x, int y, int z, int id, CallbackInfoReturnable<Boolean> cir){
68+
Catalyst.ANY_BLOCK_CHANGED_SIGNAL.emit(new BlockChangeInfo(thisAs,new Vec3i(x,y,z),id,getBlockMetadata(x,y,z)));
69+
if(getBlockTileEntity(x,y,z) != null || id == 0){
70+
Catalyst.TILE_ENTITY_BLOCK_CHANGED_SIGNAL.emit(new BlockChangeInfo(thisAs,new Vec3i(x,y,z),id,getBlockMetadata(x,y,z)));
71+
}
72+
}
73+
74+
@Inject(method = "setBlockMetadata", at = @At("RETURN"))
75+
public void setBlockMetadata(int x, int y, int z, int meta, CallbackInfoReturnable<Boolean> cir){
76+
Catalyst.ANY_BLOCK_CHANGED_SIGNAL.emit(new BlockChangeInfo(thisAs,new Vec3i(x,y,z),getBlockId(x,y,z),meta));
77+
if(getBlockTileEntity(x,y,z) != null){
78+
Catalyst.TILE_ENTITY_BLOCK_CHANGED_SIGNAL.emit(new BlockChangeInfo(thisAs,new Vec3i(x,y,z),getBlockId(x,y,z),meta));
79+
}
80+
}
81+
82+
@Inject(method = "setBlockAndMetadata", at = @At("RETURN"))
83+
public void setBlockAndMetadata(int x, int y, int z, int id, int meta, CallbackInfoReturnable<Boolean> cir){
84+
Catalyst.ANY_BLOCK_CHANGED_SIGNAL.emit(new BlockChangeInfo(thisAs,new Vec3i(x,y,z),id,meta));
85+
if(getBlockTileEntity(x,y,z) != null || id == 0){
86+
Catalyst.TILE_ENTITY_BLOCK_CHANGED_SIGNAL.emit(new BlockChangeInfo(thisAs,new Vec3i(x,y,z),id,getBlockMetadata(x,y,z)));
87+
}
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package sunsetsatellite.catalyst.core.util;
2+
3+
import net.minecraft.core.world.World;
4+
5+
public class BlockChangeInfo {
6+
7+
public int id;
8+
public int meta;
9+
public World world;
10+
public Vec3i pos;
11+
12+
public BlockChangeInfo(World world, Vec3i pos, int id, int meta) {
13+
this.id = id;
14+
this.meta = meta;
15+
this.world = world;
16+
this.pos = pos;
17+
}
18+
}

‎src/main/java/sunsetsatellite/catalyst/core/util/ConduitCapability.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ public enum ConduitCapability {
55
SIGNALUM,
66
ITEM,
77
NETWORK,
8-
CATALYST_ENERGY
8+
CATALYST_ENERGY,
9+
ELECTRIC
910
}

‎src/main/java/sunsetsatellite/catalyst/core/util/Direction.java

+8
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ public Vec3i getVec() {
6969
return vec.copy();
7070
}
7171

72+
public static Vec3i[] getVecs(){
73+
Vec3i[] vecs = new Vec3i[Direction.values().length];
74+
for (int i = 0; i < Direction.values().length; i++) {
75+
vecs[i] = Direction.values()[i].getVec();
76+
}
77+
return vecs;
78+
}
79+
7280
public Axis getAxis() {
7381
return axis;
7482
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package sunsetsatellite.catalyst.core.util;
22

3-
public interface IConduitTile {
3+
import sunsetsatellite.catalyst.core.util.network.NetworkComponentTile;
4+
5+
public interface IConduitTile extends NetworkComponentTile {
46
ConduitCapability getConduitCapability();
57
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package sunsetsatellite.catalyst.core.util;
2+
3+
import java.util.ArrayList;
4+
import java.util.Collections;
5+
import java.util.List;
6+
7+
//sorry deus, but i'm not making a dependency on another mod for a single class
8+
public class Signal<T> {
9+
10+
private final List<Listener<T>> listeners = new ArrayList<>();
11+
private final List<Listener<T>> removeQueue = new ArrayList<>();
12+
private boolean emitting = false;
13+
14+
public interface Listener<T> {
15+
void signalEmitted(Signal<T> signal, T t);
16+
}
17+
18+
public List<Listener<T>> getListeners() {
19+
return Collections.unmodifiableList(listeners);
20+
}
21+
22+
public void connect(Listener<T> listener) {
23+
if(!listeners.contains(listener)){
24+
listeners.add(listener);
25+
}
26+
}
27+
28+
public void disconnect(Listener<T> listener) {
29+
if(!emitting){
30+
listeners.remove(listener);
31+
} else {
32+
removeQueue.add(listener);
33+
}
34+
}
35+
36+
public void emit(T t) {
37+
emitting = true;
38+
for (Listener<T> listener : listeners) {
39+
listener.signalEmitted(this, t);
40+
}
41+
for (Listener<T> listener : removeQueue) {
42+
listeners.remove(listener);
43+
}
44+
removeQueue.clear();
45+
emitting = false;
46+
}
47+
}

‎src/main/java/sunsetsatellite/catalyst/core/util/Vec3f.java

+13
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,19 @@ public Vec3f set(Axis axis, double value){
179179
}
180180
}
181181

182+
public double get(Axis axis){
183+
switch (axis) {
184+
case X:
185+
return x;
186+
case Y:
187+
return y;
188+
case Z:
189+
return z;
190+
default:
191+
return 0;
192+
}
193+
}
194+
182195

183196
//creates a vec2f from any 2 non-null values of this vec3f
184197
public Vec2f toVec2f() {

‎src/main/java/sunsetsatellite/catalyst/core/util/Vec3i.java

+29
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.mojang.nbt.CompoundTag;
44
import net.minecraft.core.block.Block;
55
import net.minecraft.core.block.entity.TileEntity;
6+
import net.minecraft.core.util.helper.Axis;
67
import net.minecraft.core.util.helper.MathHelper;
78
import net.minecraft.core.world.WorldSource;
89

@@ -187,6 +188,34 @@ public Vec3i rotateY(Vec3i origin, double angle){
187188
return this;
188189
}
189190

191+
public Vec3i set(Axis axis, int value){
192+
switch (axis) {
193+
case X:
194+
this.x = value;
195+
return this;
196+
case Y:
197+
this.y = value;
198+
return this;
199+
case Z:
200+
this.z = value;
201+
return this;
202+
default:
203+
return this;
204+
}
205+
}
206+
207+
public int get(Axis axis){
208+
switch (axis) {
209+
case X:
210+
return x;
211+
case Y:
212+
return y;
213+
case Z:
214+
return z;
215+
default:
216+
return 0;
217+
}
218+
}
190219

191220
public void writeToNBT(CompoundTag tag){
192221
tag.putInt("x",this.x);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package sunsetsatellite.catalyst.core.util.mixin.interfaces;
2+
3+
import net.minecraft.core.world.World;
4+
5+
public interface ISaveHandlerWorld {
6+
7+
World getWorld();
8+
9+
void setWorld(World world);
10+
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
package sunsetsatellite.catalyst.core.util.network;
2+
3+
import com.google.common.collect.Maps;
4+
import com.mojang.nbt.CompoundTag;
5+
import com.mojang.nbt.ListTag;
6+
import net.minecraft.core.block.Block;
7+
import net.minecraft.core.block.entity.TileEntity;
8+
import net.minecraft.core.util.helper.Color;
9+
import net.minecraft.core.world.World;
10+
import org.jetbrains.annotations.NotNull;
11+
import sunsetsatellite.catalyst.Catalyst;
12+
import sunsetsatellite.catalyst.core.util.Direction;
13+
import sunsetsatellite.catalyst.core.util.Vec3i;
14+
15+
import java.util.*;
16+
17+
public class Network {
18+
19+
protected final Map<Vec3i, NetworkComponent> networkBlocks = Maps.newHashMap();
20+
protected final Map<Vec3i, BlockEntry> blocks = Maps.newHashMap();
21+
protected final World world;
22+
protected final int id;
23+
protected final NetworkPathMap NET_PATH_DATA = new NetworkPathMap();
24+
protected final Random random;
25+
public final Color color;
26+
public final @NotNull NetworkType type;
27+
28+
public Network(World world, @NotNull NetworkType type) {
29+
this(world, NetworkManager.getUID(), type);
30+
}
31+
32+
private Network(World world, int id, @NotNull NetworkType type) {
33+
this.world = world;
34+
this.id = id;
35+
this.type = type;
36+
this.random = new Random(id);
37+
color = new Color().setRGBA(random.nextInt(255),random.nextInt(255),random.nextInt(255),255);
38+
}
39+
40+
public List<NetworkPath> getPathData(Vec3i pos){
41+
List<NetworkPath> routes = NET_PATH_DATA.get(pos);
42+
if(routes == null){
43+
routes = NetworkWalker.createNetworkPaths(world,pos);
44+
if(routes == null){
45+
return Collections.emptyList();
46+
}
47+
routes.sort(Comparator.comparingInt(NetworkPath::getDistance));
48+
NET_PATH_DATA.put(pos,routes);
49+
}
50+
return routes;
51+
}
52+
53+
public int getSize() {
54+
return blocks.size();
55+
}
56+
57+
public boolean existsOnPos(int x, int y, int z) {
58+
Vec3i pos = new Vec3i(x, y, z);
59+
return blocks.containsKey(pos);
60+
}
61+
62+
public void addBlock(int x, int y, int z) {
63+
Block block = Block.blocksList[world.getBlockId(x, y, z)];
64+
byte meta = (byte) world.getBlockMetadata(x, y, z);
65+
66+
Vec3i pos = new Vec3i(x, y, z);
67+
blocks.put(pos, new BlockEntry(block, meta));
68+
if (block instanceof NetworkComponent) {
69+
networkBlocks.put(pos, (NetworkComponent) block);
70+
if(world.getBlockTileEntity(x,y,z) instanceof NetworkComponentTile){
71+
((NetworkComponentTile) world.getBlockTileEntity(x,y,z)).networkChanged(this);
72+
}
73+
update();
74+
}
75+
NET_PATH_DATA.clear();
76+
}
77+
78+
public List<Network> removeBlock(int x, int y, int z) {
79+
Vec3i pos = new Vec3i(x, y, z);
80+
NetworkComponent component = networkBlocks.get(pos);
81+
if (component != null) {
82+
if(world.getBlockTileEntity(x,y,z) instanceof NetworkComponentTile){
83+
((NetworkComponentTile) world.getBlockTileEntity(x,y,z)).removedFromNetwork(this);
84+
}
85+
}
86+
networkBlocks.remove(pos);
87+
blocks.remove(pos);
88+
update();
89+
90+
List<Vec3i> sideNets = new ArrayList<>(6);
91+
for (byte i = 0; i < 6; i++) {
92+
Vec3i offset = Direction.getVecs()[i];
93+
Vec3i side = new Vec3i(x + offset.x, y + offset.y, z + offset.z);
94+
if (blocks.containsKey(side)) {
95+
sideNets.add(side);
96+
}
97+
}
98+
99+
List<Set<Vec3i>> preNets = new ArrayList<>();
100+
boolean[] ignore = new boolean[sideNets.size()];
101+
for (byte i = 0; i < ignore.length; i++) {
102+
if (ignore[i]) {
103+
continue;
104+
}
105+
Vec3i startBlock = sideNets.get(i);
106+
Set<Vec3i> netBlocks = floodFill(startBlock);
107+
preNets.add(netBlocks);
108+
if (i < ignore.length - 1) {
109+
for (byte j = (byte) (i + 1); j < ignore.length; j++) {
110+
if (netBlocks.contains(sideNets.get(j))) {
111+
ignore[j] = true;
112+
}
113+
}
114+
}
115+
}
116+
117+
final int size = preNets.size();
118+
if (size < 2) {
119+
return null;
120+
}
121+
122+
List<Network> result = new ArrayList<>(size);
123+
for (Set<Vec3i> preNet : preNets) {
124+
Network sideNet = new Network(world,type);
125+
126+
preNet.forEach(blockPos -> {
127+
sideNet.blocks.put(blockPos, blocks.get(blockPos));
128+
NetworkComponent netBlock = networkBlocks.get(blockPos);
129+
if (netBlock != null) {
130+
sideNet.networkBlocks.put(blockPos, netBlock);
131+
TileEntity tile = world.getBlockTileEntity(blockPos.x, blockPos.y, blockPos.z);
132+
if(tile instanceof NetworkComponentTile){
133+
((NetworkComponentTile) tile).networkChanged(sideNet);
134+
}
135+
}
136+
});
137+
138+
if (sideNet.getSize() > 1) {
139+
result.add(sideNet);
140+
sideNet.update();
141+
}
142+
}
143+
NET_PATH_DATA.clear();
144+
return result;
145+
}
146+
147+
public void mergeNetwork(Network net) {
148+
if(net.isOfSameType(net)) {
149+
blocks.putAll(net.blocks);
150+
networkBlocks.putAll(net.networkBlocks);
151+
}
152+
networkBlocks.forEach((pos, networkComponent) -> {
153+
TileEntity tile = world.getBlockTileEntity(pos.x, pos.y, pos.z);
154+
if(tile instanceof NetworkComponentTile){
155+
((NetworkComponentTile) tile).networkChanged(net);
156+
}
157+
});
158+
NET_PATH_DATA.clear();
159+
}
160+
161+
public CompoundTag toTag() {
162+
CompoundTag net = new CompoundTag();
163+
ListTag positions = new ListTag();
164+
net.put("blocks", positions);
165+
net.putInt("id", id);
166+
net.putString("type",type.type);
167+
168+
blocks.forEach((pos, entry) -> {
169+
CompoundTag tag = new CompoundTag();
170+
tag.putInt("x", pos.x);
171+
tag.putInt("y", pos.y);
172+
tag.putInt("z", pos.z);
173+
tag.putInt("id", entry.block.id);
174+
tag.putInt("meta", entry.meta);
175+
positions.addTag(tag);
176+
});
177+
178+
return net;
179+
}
180+
181+
public static Network fromTag(World world, CompoundTag root) {
182+
int id = root.getInteger("id");
183+
ListTag positions = root.getList("blocks");
184+
NetworkType networkType = new NetworkType(root.getString("type"));
185+
Network net = new Network(world, id, networkType);
186+
187+
final int size = positions.tagCount();
188+
for (int i = 0; i < size; i++) {
189+
CompoundTag tag = (CompoundTag) positions.tagAt(i);
190+
Block block = Block.blocksList[tag.getInteger("id")];
191+
if (block != null) {
192+
int x = tag.getInteger("x");
193+
int y = tag.getInteger("y");
194+
int z = tag.getInteger("z");
195+
byte meta = tag.getByte("meta");
196+
net.blocks.put(new Vec3i(x, y, z), new BlockEntry(block, meta));
197+
if(NetworkManager.canBeNet(block)){
198+
net.networkBlocks.put(new Vec3i(x, y, z), (NetworkComponent) block);
199+
}
200+
}
201+
}
202+
203+
return net;
204+
}
205+
206+
private Set<Vec3i> floodFill(Vec3i start) {
207+
List<Set<Vec3i>> edges = new ArrayList<>();
208+
Set<Vec3i> result = new HashSet<>();
209+
edges.add(Catalyst.setOf(start));
210+
edges.add(new HashSet<>());
211+
212+
byte n = 0;
213+
boolean added = true;
214+
while (added) {
215+
Set<Vec3i> oldEdge = edges.get(n & 1);
216+
Set<Vec3i> newEdge = edges.get((n + 1) & 1);
217+
n = (byte) ((n + 1) & 1);
218+
oldEdge.forEach(pos -> {
219+
for (byte i = 0; i < 6; i++) {
220+
Vec3i offset = Direction.getVecs()[i];
221+
Vec3i side = new Vec3i(pos.x + offset.x, pos.y + offset.y, pos.z + offset.z);
222+
if (blocks.containsKey(side) && !result.contains(side)) {
223+
newEdge.add(side);
224+
}
225+
}
226+
});
227+
added = !oldEdge.isEmpty();
228+
result.addAll(oldEdge);
229+
oldEdge.clear();
230+
}
231+
232+
return result;
233+
}
234+
235+
public void update() {
236+
networkBlocks.forEach((pos, networkComponent) -> {
237+
TileEntity tile = world.getBlockTileEntity(pos.x, pos.y, pos.z);
238+
if(tile instanceof NetworkComponentTile){
239+
((NetworkComponentTile) tile).networkChanged(this);
240+
}
241+
});
242+
}
243+
244+
public boolean isOfSameType(NetworkComponent component){
245+
return component.getType().equals(type);
246+
}
247+
248+
public boolean isOfSameType(Network net){
249+
return net.type.equals(type);
250+
}
251+
252+
@Override
253+
public int hashCode() {
254+
return id;
255+
}
256+
257+
@Override
258+
public boolean equals(Object obj) {
259+
if (obj == this) {
260+
return true;
261+
}
262+
if (obj instanceof Network) {
263+
Network net = (Network) obj;
264+
Optional<Vec3i> optional = net.blocks.keySet().stream().findAny();
265+
if (optional.isPresent()) {
266+
Vec3i pos = optional.get();
267+
return blocks.containsKey(pos);
268+
}
269+
}
270+
return false;
271+
}
272+
273+
public String toString() {
274+
return String.format("[ID: %d, Size: %d]", id, networkBlocks.size());
275+
}
276+
277+
protected static class BlockEntry {
278+
Block block;
279+
byte meta;
280+
281+
private BlockEntry(Block block, byte meta) {
282+
this.block = block;
283+
this.meta = meta;
284+
}
285+
}
286+
287+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package sunsetsatellite.catalyst.core.util.network;
2+
3+
public interface NetworkComponent {
4+
NetworkType getType();
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package sunsetsatellite.catalyst.core.util.network;
2+
3+
import sunsetsatellite.catalyst.core.util.Direction;
4+
import sunsetsatellite.catalyst.core.util.Vec3i;
5+
6+
public interface NetworkComponentTile extends NetworkComponent {
7+
8+
Vec3i getPosition();
9+
10+
boolean isConnected(Direction direction);
11+
12+
void networkChanged(Network network);
13+
14+
void removedFromNetwork(Network network);
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
package sunsetsatellite.catalyst.core.util.network;
2+
3+
import com.mojang.nbt.CompoundTag;
4+
import com.mojang.nbt.ListTag;
5+
import net.minecraft.core.block.Block;
6+
import net.minecraft.core.world.World;
7+
import net.minecraft.core.world.WorldSource;
8+
import sunsetsatellite.catalyst.Catalyst;
9+
import sunsetsatellite.catalyst.core.util.*;
10+
11+
import java.util.*;
12+
import java.util.concurrent.atomic.AtomicInteger;
13+
14+
public class NetworkManager implements Signal.Listener<BlockChangeInfo> {
15+
16+
private static final Map<Integer, Set<Network>> NETS = new HashMap<>();
17+
private static final AtomicInteger ID_PROVIDER = new AtomicInteger(0);
18+
private static final NetworkManager INSTANCE = new NetworkManager();
19+
20+
private NetworkManager() {}
21+
22+
public static int getNetID(World world, int x, int y, int z) {
23+
Network net = getNet(world, x, y, z);
24+
return net == null ? -1 : net.hashCode();
25+
}
26+
27+
public static Signal.Listener<BlockChangeInfo> getInstance() {
28+
return INSTANCE;
29+
}
30+
31+
@Override
32+
public void signalEmitted(Signal<BlockChangeInfo> signal, BlockChangeInfo blockChanged) {
33+
if(signal != Catalyst.TILE_ENTITY_BLOCK_CHANGED_SIGNAL) return;
34+
35+
if(blockChanged.id == 0){
36+
removeBlock(blockChanged);
37+
} else {
38+
addBlock(blockChanged);
39+
}
40+
41+
}
42+
43+
public static void addBlock(BlockChangeInfo blockChanged) {
44+
int x = blockChanged.pos.x;
45+
int y = blockChanged.pos.y;
46+
int z = blockChanged.pos.z;
47+
World world = blockChanged.world;
48+
49+
if(!canBeNet(Block.blocksList[blockChanged.id])) {
50+
return;
51+
}
52+
53+
NetworkComponent component = (NetworkComponent) Block.blocksList[blockChanged.id];
54+
55+
Set<Network> nets = NETS.computeIfAbsent(world.dimension.id, i -> new HashSet<>());
56+
57+
Set<Network> sideNets = new HashSet<>();
58+
for (Network net: nets) {
59+
for (Vec3i offset: Direction.getVecs()) {
60+
int px = x + offset.x;
61+
int py = y + offset.y;
62+
int pz = z + offset.z;
63+
if (net.existsOnPos(px, py, pz)) {
64+
sideNets.add(net);
65+
}
66+
}
67+
}
68+
69+
Network net = null;
70+
int size = sideNets.size();
71+
//no nets around, create one
72+
if (size == 0) {
73+
net = new Network(world,component.getType());
74+
net.addBlock(x, y, z);
75+
for (Vec3i offset: Direction.getVecs()) {
76+
int px = x + offset.x;
77+
int py = y + offset.y;
78+
int pz = z + offset.z;
79+
if (canBeNet(world, px, py, pz)) {
80+
net.addBlock(px, py, pz);
81+
}
82+
}
83+
if (net.getSize() > 1) {
84+
nets.add(net);
85+
}
86+
}
87+
else if (size == 1) {
88+
Network potentialNet = sideNets.stream().findAny().get();
89+
if(potentialNet.isOfSameType(component)){
90+
potentialNet.addBlock(x, y, z);
91+
net = potentialNet;
92+
}
93+
}
94+
else { //multiple nets around
95+
Network[] netsArray = sideNets.toArray(new Network[size]);
96+
Network main = null;
97+
for (Network network : netsArray) {
98+
if(network.isOfSameType(component)){
99+
main = network;
100+
main.addBlock(x, y, z);
101+
for (Network otherNet : netsArray) {
102+
if(otherNet == main){
103+
continue;
104+
}
105+
if(otherNet.isOfSameType(main)){
106+
main.mergeNetwork(otherNet);
107+
nets.remove(otherNet);
108+
}
109+
}
110+
net = main;
111+
break;
112+
}
113+
}
114+
}
115+
116+
if(net == null && getNet(world, x, y, z) == null) {
117+
net = new Network(world,component.getType());
118+
net.addBlock(x, y, z);
119+
for (Vec3i offset: Direction.getVecs()) {
120+
int px = x + offset.x;
121+
int py = y + offset.y;
122+
int pz = z + offset.z;
123+
if (canBeNet(world, px, py, pz)) {
124+
net.addBlock(px, py, pz);
125+
}
126+
}
127+
if (net.getSize() > 1) {
128+
nets.add(net);
129+
}
130+
}
131+
132+
//add surrounding blocks to net if type matches
133+
for (Vec3i offset : Direction.getVecs()) {
134+
int px = x + offset.x;
135+
int py = y + offset.y;
136+
int pz = z + offset.z;
137+
if (canBeNet(world, px, py, pz) && getNet(world, px, py, pz) == null && net != null) {
138+
NetworkComponent sideComponent = (NetworkComponent) world.getBlock(x,y,z);
139+
if(net.isOfSameType(sideComponent)){
140+
net.addBlock(px, py, pz);
141+
}
142+
}
143+
}
144+
}
145+
146+
public static void removeBlock(BlockChangeInfo blockChanged) {
147+
int x = blockChanged.pos.x;
148+
int y = blockChanged.pos.y;
149+
int z = blockChanged.pos.z;
150+
World world = blockChanged.world;
151+
Set<Network> nets = NETS.get(world.dimension.id);
152+
153+
if (nets == null) {
154+
return;
155+
}
156+
157+
Network target = null;
158+
for (Network net: nets) {
159+
if (net.existsOnPos(x, y, z)) {
160+
target = net;
161+
break;
162+
}
163+
}
164+
165+
if (target != null) {
166+
List<? extends Network> sideNets = target.removeBlock(x, y, z);
167+
if (sideNets != null) {
168+
nets.remove(target);
169+
nets.addAll(sideNets);
170+
}
171+
else if (target.getSize() < 2) {
172+
nets.remove(target);
173+
}
174+
}
175+
}
176+
177+
public static int getUID() {
178+
return ID_PROVIDER.getAndIncrement();
179+
}
180+
181+
public static void netsToTag(World world, CompoundTag root) {
182+
Set<Network> nets = NETS.get(world.dimension.id);
183+
CompoundTag dimTag = new CompoundTag();
184+
root.put("dim" + world.dimension.id, dimTag);
185+
186+
if (nets == null) {
187+
return;
188+
}
189+
190+
ListTag netsList = new ListTag();
191+
dimTag.put("nets", netsList);
192+
nets.forEach(network -> {
193+
netsList.addTag(network.toTag());
194+
});
195+
}
196+
197+
public static void netsFromTag(World world, CompoundTag root) {
198+
Set<Network> nets = new HashSet<>();
199+
NETS.put(world.dimension.id, nets);
200+
201+
CompoundTag dimTag = root.getCompound("dim" + world.dimension.id);
202+
if (dimTag == null) {
203+
return;
204+
}
205+
206+
ListTag netsList = dimTag.getList("nets");
207+
final int size = netsList.tagCount();
208+
for (int i = 0; i < size; i++) {
209+
Network net = Network.fromTag(world, (CompoundTag) netsList.tagAt(i));
210+
if (net.getSize() > 1) {
211+
nets.add(net);
212+
}
213+
}
214+
}
215+
216+
public static boolean canBeNet(WorldSource world, int x, int y, int z) {
217+
Block block = Block.blocksList[world.getBlockId(x, y, z)];
218+
return canBeNet(block);
219+
}
220+
221+
public static boolean canBeNet(Block block) {
222+
return block instanceof NetworkComponent;
223+
}
224+
225+
private static Network getNet(World world, int x, int y, int z) {
226+
Set<Network> nets = NETS.get(world.dimension.id);
227+
if (nets != null) {
228+
for (Network net: nets) {
229+
if (net.existsOnPos(x, y, z)) {
230+
return net;
231+
}
232+
}
233+
}
234+
return null;
235+
}
236+
237+
public static void updateAllNets(){
238+
NETS.forEach((dimId, nets)->{
239+
for (Network net : nets) {
240+
net.update();
241+
}
242+
});
243+
}
244+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package sunsetsatellite.catalyst.core.util.network;
2+
3+
import sunsetsatellite.catalyst.core.util.Direction;
4+
5+
public class NetworkPath {
6+
7+
public final NetworkComponentTile target;
8+
public final Direction targetDirection;
9+
public final int distance;
10+
public final NetworkComponentTile[] path;
11+
12+
public NetworkPath(Direction destDirection, NetworkComponentTile[] path, int distance) {
13+
this.target = path[path.length - 1];
14+
this.targetDirection = destDirection;
15+
this.distance = distance;
16+
this.path = path;
17+
}
18+
19+
public int getDistance() {
20+
return distance;
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package sunsetsatellite.catalyst.core.util.network;
2+
3+
import sunsetsatellite.catalyst.core.util.Vec3i;
4+
5+
import java.util.HashMap;
6+
import java.util.List;
7+
8+
public class NetworkPathMap extends HashMap<Vec3i, List<NetworkPath>> {
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package sunsetsatellite.catalyst.core.util.network;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
public class NetworkType {
6+
7+
public static final NetworkType FLUID = new NetworkType("fluid");
8+
public static final NetworkType SIGNALUM = new NetworkType("signalum");
9+
public static final NetworkType ITEM = new NetworkType("item");
10+
public static final NetworkType RES_NETWORK = new NetworkType("retrostorage_network");
11+
public static final NetworkType CATALYST_ENERGY = new NetworkType("catalyst_energy");
12+
public static final NetworkType ELECTRIC = new NetworkType("electric");
13+
14+
public final @NotNull String type;
15+
16+
public NetworkType(@NotNull String type) {
17+
this.type = type;
18+
}
19+
20+
@Override
21+
public final boolean equals(Object o) {
22+
if (this == o) return true;
23+
if (!(o instanceof NetworkType)) return false;
24+
25+
NetworkType that = (NetworkType) o;
26+
return type.equals(that.type);
27+
}
28+
29+
@Override
30+
public int hashCode() {
31+
return type.hashCode();
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package sunsetsatellite.catalyst.core.util.network;
2+
3+
import com.llamalad7.mixinextras.lib.apache.commons.ArrayUtils;
4+
import net.minecraft.core.block.entity.TileEntity;
5+
import net.minecraft.core.world.World;
6+
import sunsetsatellite.catalyst.Catalyst;
7+
import sunsetsatellite.catalyst.core.util.Direction;
8+
import sunsetsatellite.catalyst.core.util.IConduitTile;
9+
import sunsetsatellite.catalyst.core.util.Vec3i;
10+
11+
import java.util.*;
12+
13+
public class NetworkWalker<T extends NetworkComponentTile> {
14+
15+
protected NetworkWalker<T> root;
16+
private final World world;
17+
private Set<T> walkedConduits;
18+
private final List<Direction> nextConduitDirections = new ArrayList<>(Direction.values().length-1);
19+
private final List<T> nextConduits = new ArrayList<>(Direction.values().length-1);
20+
private List<NetworkWalker<T>> walkers;
21+
private Vec3i currentPos;
22+
private T currentConduit;
23+
private Direction from = null;
24+
private int walkedBlocks;
25+
private boolean used;
26+
private boolean running;
27+
private boolean failed = false;
28+
29+
private T[] conduits;
30+
private final List<NetworkPath> routes;
31+
32+
public NetworkWalker(World world, Vec3i source, int walkedBlocks, List<NetworkPath> routes){
33+
this.world = world;
34+
this.walkedBlocks = walkedBlocks;
35+
this.currentPos = source;
36+
this.root = this;
37+
this.routes = routes;
38+
}
39+
40+
public static <T extends NetworkComponentTile> List<NetworkPath> createNetworkPaths(World world, Vec3i source){
41+
if(source.getTileEntity(world) instanceof NetworkComponentTile){
42+
NetworkWalker<T> walker = new NetworkWalker<>(world,source,1,new ArrayList<>());
43+
walker.traverse();
44+
return walker.isFailed() ? null : walker.routes;
45+
}
46+
return null;
47+
}
48+
49+
protected NetworkWalker<T> createSubWalker(World world, Direction nextDir, Vec3i nextPos, int walkedBlocks){
50+
NetworkWalker<T> subWalker = new NetworkWalker<>(world, nextPos, walkedBlocks, routes);
51+
subWalker.conduits = conduits;
52+
return subWalker;
53+
}
54+
55+
public void traverse(){
56+
traverse(32768);
57+
}
58+
59+
public void traverse(int max){
60+
if(used){
61+
throw new IllegalStateException("Walker already used!");
62+
}
63+
root = this;
64+
walkedConduits = new HashSet<>();
65+
int i = 0;
66+
running = true;
67+
//runs purely on side effects
68+
//noinspection StatementWithEmptyBody
69+
while(running && !walk() && i++ < max);
70+
running = false;
71+
walkedConduits = null;
72+
if(i >= max){
73+
Catalyst.LOGGER.error("Walker reached maximum amount of walks: {}", i);
74+
}
75+
used = true;
76+
}
77+
78+
private boolean walk(){
79+
if(walkers == null){
80+
if(!checkPos()){
81+
this.root.failed = true;
82+
return true;
83+
}
84+
85+
if(nextConduitDirections.isEmpty()){
86+
return true;
87+
}
88+
if(nextConduitDirections.size() == 1){
89+
currentPos = nextConduits.get(0).getPosition();
90+
currentConduit = nextConduits.get(0);
91+
from = nextConduitDirections.get(0).getOpposite();
92+
walkedBlocks++;
93+
return !isRunning();
94+
}
95+
96+
walkers = new ArrayList<>();
97+
for (int i = 0; i < nextConduitDirections.size(); i++) {
98+
Direction direction = nextConduitDirections.get(i);
99+
NetworkWalker<T> walker = createSubWalker(world, direction, currentPos.add(direction.getVec()), walkedBlocks + 1);
100+
walker.root = root;
101+
walker.currentConduit = nextConduits.get(i);
102+
walker.from = direction.getOpposite();
103+
walkers.add(walker);
104+
}
105+
}
106+
Iterator<NetworkWalker<T>> iterator = walkers.iterator();
107+
while(iterator.hasNext()){
108+
NetworkWalker<T> next = iterator.next();
109+
if(next.walk()){
110+
onRemoveSubWalker(next);
111+
iterator.remove();
112+
}
113+
}
114+
115+
return !isRunning() || walkers.isEmpty();
116+
}
117+
118+
119+
private boolean checkPos(){
120+
nextConduitDirections.clear();
121+
nextConduits.clear();
122+
if(currentConduit == null){
123+
TileEntity thisConduit = world.getBlockTileEntity(currentPos.x, currentPos.y, currentPos.z);
124+
if(!(thisConduit instanceof IConduitTile)){
125+
return false;
126+
}
127+
currentConduit = (T) thisConduit;
128+
}
129+
checkConduit(currentConduit,currentPos);
130+
root.walkedConduits.add(currentConduit);
131+
132+
for (Direction direction : getAllowedDirections()) {
133+
if(direction == from || !currentConduit.isConnected(direction)){
134+
continue;
135+
}
136+
137+
TileEntity tile = direction.getTileEntity(world, (TileEntity) currentConduit);
138+
if(tile instanceof IConduitTile){
139+
T otherConduit = (T) tile;
140+
if(!otherConduit.isConnected(direction.getOpposite()) || isWalked(otherConduit)){
141+
continue;
142+
}
143+
if(isValid(currentConduit,otherConduit,currentPos,direction)){
144+
nextConduitDirections.add(direction);
145+
nextConduits.add(otherConduit);
146+
continue;
147+
}
148+
}
149+
checkNeighbour(currentConduit, currentPos, direction, tile);
150+
}
151+
return true;
152+
}
153+
154+
protected void checkNeighbour(T conduit, Vec3i pos, Direction dirToNeighbour, TileEntity neighbour){
155+
if(conduit != conduits[conduits.length -1]) throw new IllegalStateException("Current conduit is not the last one added, you dun goofed.");
156+
if(!(neighbour instanceof IConduitTile) && neighbour.getBlockType() instanceof NetworkComponent){
157+
NetworkComponentTile[] path = new NetworkComponentTile[conduits.length+1];
158+
System.arraycopy(conduits, 0, path, 0, conduits.length);
159+
path[path.length-1] = (NetworkComponentTile) neighbour;
160+
routes.add(new NetworkPath(dirToNeighbour, path, getWalkedBlocks()));
161+
}
162+
}
163+
164+
protected boolean isValid(T conduit, T neighbourConduit, Vec3i pos, Direction dirToNeighbour){
165+
return conduit.getType() == neighbourConduit.getType();
166+
}
167+
168+
protected Direction[] getAllowedDirections() {
169+
return Direction.values();
170+
}
171+
172+
private void checkConduit(T currentConduit, Vec3i currentPos) {
173+
conduits = ArrayUtils.add(conduits, currentConduit);
174+
}
175+
176+
protected void onRemoveSubWalker(NetworkWalker<T> subWalker){}
177+
178+
protected boolean isWalked(T conduit) {
179+
return root.walkedConduits.contains(conduit);
180+
}
181+
182+
public void stop() {
183+
root.running = false;
184+
}
185+
186+
public int getWalkedBlocks() {
187+
return walkedBlocks;
188+
}
189+
190+
public boolean isRoot() {
191+
return this.root == this;
192+
}
193+
194+
public boolean isFailed() {
195+
return failed;
196+
}
197+
198+
public boolean isRunning() {
199+
return root.running;
200+
}
201+
202+
}

‎src/main/resources/catalyst.mixins.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
"compatibilityLevel": "JAVA_8",
66
"mixins": [
77
"EntityPlayerMixin",
8-
"EntityPlayerMPMixin"
8+
"EntityPlayerMPMixin",
9+
"SaveHandlerBaseMixin",
10+
"WorldMixin"
911
],
1012
"client": [
1113
"BlockModelStandardMixin",

0 commit comments

Comments
 (0)
Please sign in to comment.