Skip to content

Commit 236a038

Browse files
committed
Add option to debug level RNG as well as entity RNG
1 parent bbb93d9 commit 236a038

File tree

5 files changed

+144
-20
lines changed

5 files changed

+144
-20
lines changed

src/main/java/net/earthcomputer/clientcommands/mixin/debug/EntityMixin.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
package net.earthcomputer.clientcommands.mixin.debug;
22

3-
import net.earthcomputer.clientcommands.util.DebugRandom;
3+
import com.llamalad7.mixinextras.expression.Definition;
4+
import com.llamalad7.mixinextras.expression.Expression;
5+
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
46
import net.earthcomputer.clientcommands.interfaces.IEntity_Debug;
7+
import net.earthcomputer.clientcommands.util.DebugRandom;
58
import net.minecraft.util.RandomSource;
69
import net.minecraft.world.entity.Entity;
710
import net.minecraft.world.entity.EntityType;
811
import net.minecraft.world.entity.player.Player;
912
import net.minecraft.world.level.Level;
10-
import org.objectweb.asm.Opcodes;
1113
import org.spongepowered.asm.mixin.Final;
1214
import org.spongepowered.asm.mixin.Mixin;
13-
import org.spongepowered.asm.mixin.Mutable;
1415
import org.spongepowered.asm.mixin.Shadow;
1516
import org.spongepowered.asm.mixin.injection.At;
1617
import org.spongepowered.asm.mixin.injection.Inject;
@@ -21,14 +22,17 @@ public abstract class EntityMixin implements IEntity_Debug {
2122

2223
@Shadow
2324
@Final
24-
@Mutable
2525
protected RandomSource random;
2626

27-
@Inject(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/Entity;random:Lnet/minecraft/util/RandomSource;", opcode = Opcodes.PUTFIELD, shift = At.Shift.AFTER))
28-
private void onInitRandom(EntityType<?> type, Level level, CallbackInfo ci) {
27+
@Definition(id = "random", field = "Lnet/minecraft/world/entity/Entity;random:Lnet/minecraft/util/RandomSource;")
28+
@Expression("this.random = @(?)")
29+
@ModifyExpressionValue(method = "<init>", at = @At("MIXINEXTRAS:EXPRESSION"))
30+
private RandomSource onInitRandom(RandomSource original, EntityType<?> type, Level level) {
2931
if (type == DebugRandom.DEBUG_ENTITY_TYPE && !level.isClientSide()) {
30-
this.random = new DebugRandom((Entity) (Object) this);
32+
return new DebugRandom((Entity) (Object) this);
3133
}
34+
35+
return original;
3236
}
3337

3438
@Override
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package net.earthcomputer.clientcommands.mixin.debug;
2+
3+
import com.llamalad7.mixinextras.expression.Definition;
4+
import com.llamalad7.mixinextras.expression.Expression;
5+
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
6+
import com.llamalad7.mixinextras.sugar.Local;
7+
import net.earthcomputer.clientcommands.util.DebugRandom;
8+
import net.minecraft.resources.ResourceKey;
9+
import net.minecraft.server.level.ServerLevel;
10+
import net.minecraft.util.RandomSource;
11+
import net.minecraft.world.level.Level;
12+
import org.spongepowered.asm.mixin.Final;
13+
import org.spongepowered.asm.mixin.Mixin;
14+
import org.spongepowered.asm.mixin.Shadow;
15+
import org.spongepowered.asm.mixin.injection.At;
16+
import org.spongepowered.asm.mixin.injection.Inject;
17+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
18+
19+
@Mixin(Level.class)
20+
public class LevelMixin {
21+
@Shadow
22+
@Final
23+
public RandomSource random;
24+
25+
@Definition(id = "random", field = "Lnet/minecraft/world/level/Level;random:Lnet/minecraft/util/RandomSource;")
26+
@Expression("this.random = @(?)")
27+
@ModifyExpressionValue(method = "<init>", at = @At("MIXINEXTRAS:EXPRESSION"))
28+
private RandomSource onInitRandom(RandomSource original, @Local(argsOnly = true) ResourceKey<Level> dimension) {
29+
if ((Object) this instanceof ServerLevel && dimension.location().equals(DebugRandom.DEBUG_DIMENSION)) {
30+
return new DebugRandom((Level) (Object) this);
31+
}
32+
33+
return original;
34+
}
35+
36+
@Inject(method = "close", at = @At("HEAD"))
37+
private void onClose(CallbackInfo ci) {
38+
if (this.random instanceof DebugRandom debugRandom) {
39+
debugRandom.writeToFile();
40+
}
41+
}
42+
}

src/main/java/net/earthcomputer/clientcommands/mixin/debug/ServerLevelMixin.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
package net.earthcomputer.clientcommands.mixin.debug;
22

33
import net.earthcomputer.clientcommands.interfaces.IEntity_Debug;
4+
import net.earthcomputer.clientcommands.util.DebugRandom;
5+
import net.minecraft.core.Holder;
6+
import net.minecraft.core.RegistryAccess;
7+
import net.minecraft.resources.ResourceKey;
48
import net.minecraft.server.level.ServerLevel;
59
import net.minecraft.world.entity.Entity;
10+
import net.minecraft.world.level.Level;
11+
import net.minecraft.world.level.dimension.DimensionType;
12+
import net.minecraft.world.level.storage.WritableLevelData;
613
import org.spongepowered.asm.mixin.Mixin;
714
import org.spongepowered.asm.mixin.injection.At;
815
import org.spongepowered.asm.mixin.injection.Inject;
916
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
1017

1118
@Mixin(ServerLevel.class)
12-
public class ServerLevelMixin {
19+
public abstract class ServerLevelMixin extends Level {
20+
protected ServerLevelMixin(WritableLevelData levelData, ResourceKey<Level> dimension, RegistryAccess registryAccess, Holder<DimensionType> dimensionTypeRegistration, boolean isClientSide, boolean isDebug, long biomeZoomSeed, int maxChainedNeighborUpdates) {
21+
super(levelData, dimension, registryAccess, dimensionTypeRegistration, isClientSide, isDebug, biomeZoomSeed, maxChainedNeighborUpdates);
22+
}
23+
1324
@Inject(method = "tickNonPassenger", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/entity/Entity;tick()V"))
1425
private void onTickNonPassenger(Entity entity, CallbackInfo ci) {
1526
((IEntity_Debug) entity).clientcommands_tickDebugRandom();
@@ -19,4 +30,11 @@ private void onTickNonPassenger(Entity entity, CallbackInfo ci) {
1930
private void onTickPassenger(Entity vehicle, Entity passenger, CallbackInfo ci) {
2031
((IEntity_Debug) passenger).clientcommands_tickDebugRandom();
2132
}
33+
34+
@Inject(method = "tick", at = @At("HEAD"))
35+
private void onTick(CallbackInfo ci) {
36+
if (this.random instanceof DebugRandom debugRandom) {
37+
debugRandom.tick();
38+
}
39+
}
2240
}

src/main/java/net/earthcomputer/clientcommands/util/DebugRandom.java

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import net.minecraft.util.ProblemReporter;
1616
import net.minecraft.world.entity.Entity;
1717
import net.minecraft.world.entity.EntityType;
18+
import net.minecraft.world.level.Level;
1819
import net.minecraft.world.level.levelgen.LegacyRandomSource;
1920
import net.minecraft.world.level.levelgen.RandomSupport;
2021
import net.minecraft.world.level.storage.TagValueOutput;
@@ -49,27 +50,40 @@
4950
import java.util.ArrayList;
5051
import java.util.List;
5152
import java.util.Vector;
53+
import java.util.function.Supplier;
5254
import java.util.zip.GZIPInputStream;
5355
import java.util.zip.GZIPOutputStream;
5456

5557
public class DebugRandom extends LegacyRandomSource {
5658
static final Logger LOGGER = LogUtils.getLogger();
5759

5860
public static final EntityType<?> DEBUG_ENTITY_TYPE;
61+
public static final ResourceLocation DEBUG_DIMENSION;
62+
public static final boolean SAVE_ENTITY_TAG = Boolean.parseBoolean(System.getProperty("clientcommands.debugEntityRng.saveTag", "true"));
63+
5964
static {
6065
String debugEntityType = System.getProperty("clientcommands.debugEntityRng");
6166
if (debugEntityType == null) {
6267
DEBUG_ENTITY_TYPE = null;
6368
} else {
6469
DEBUG_ENTITY_TYPE = BuiltInRegistries.ENTITY_TYPE.getValue(ResourceLocation.parse(debugEntityType));
6570
}
71+
72+
String debugDimensionStr = System.getProperty("clientcommands.debugDimensionRng");
73+
if (debugDimensionStr == null) {
74+
DEBUG_DIMENSION = null;
75+
} else {
76+
DEBUG_DIMENSION = ResourceLocation.tryParse(debugDimensionStr);
77+
}
6678
}
6779

6880
private static final Object2IntMap<String> stackTraceIds = new Object2IntOpenHashMap<>();
6981
static final List<String> stackTraceById = new ArrayList<>();
7082

71-
private final Entity entity;
7283
private boolean firstTick = true;
84+
private final Supplier<CompoundTag> tagToSaveSupplier;
85+
private boolean supplyingTagToSave = false;
86+
private final Supplier<String> idSupplier;
7387

7488
private final List<IntList> stackTraces = new ArrayList<>();
7589
private IntList stackTracesThisTick = new IntArrayList();
@@ -79,7 +93,39 @@ public class DebugRandom extends LegacyRandomSource {
7993

8094
public DebugRandom(Entity entity) {
8195
super(RandomSupport.generateUniqueSeed());
82-
this.entity = entity;
96+
97+
if (SAVE_ENTITY_TAG) {
98+
tagToSaveSupplier = () -> {
99+
if (firstTick) {
100+
return new CompoundTag();
101+
} else {
102+
try (ProblemReporter.ScopedCollector collector = new ProblemReporter.ScopedCollector(LOGGER)) {
103+
TagValueOutput output = TagValueOutput.createWithContext(collector, entity.level().registryAccess());
104+
entity.saveWithoutId(output);
105+
return output.buildResult();
106+
}
107+
}
108+
};
109+
} else {
110+
tagToSaveSupplier = CompoundTag::new;
111+
}
112+
113+
this.idSupplier = entity::getStringUUID;
114+
115+
this.stackTraces.add(this.stackTracesThisTick);
116+
try {
117+
this.nbtStream = new DataOutputStream(new GZIPOutputStream(gzippedNbt));
118+
} catch (IOException e) {
119+
throw new AssertionError(e);
120+
}
121+
}
122+
123+
public DebugRandom(Level level) {
124+
super(RandomSupport.generateUniqueSeed());
125+
126+
this.tagToSaveSupplier = CompoundTag::new;
127+
this.idSupplier = () -> level.dimension().location().toString();
128+
83129
this.stackTraces.add(this.stackTracesThisTick);
84130
try {
85131
this.nbtStream = new DataOutputStream(new GZIPOutputStream(gzippedNbt));
@@ -108,14 +154,17 @@ public void tick() {
108154

109155
private void handleStackTrace(int stackTrace) {
110156
this.stackTracesThisTick.add(stackTrace);
111-
try (ProblemReporter.ScopedCollector collector = new ProblemReporter.ScopedCollector(LOGGER)) {
157+
try {
158+
boolean wasSupplyingTagToSave = supplyingTagToSave;
159+
supplyingTagToSave = true;
112160
CompoundTag tagToSave;
113-
if (firstTick) {
114-
tagToSave = new CompoundTag();
115-
} else {
116-
TagValueOutput output = TagValueOutput.createWithContext(collector, entity.level().registryAccess());
117-
entity.saveWithoutId(output);
118-
tagToSave = output.buildResult();
161+
try {
162+
tagToSave = tagToSaveSupplier.get();
163+
} finally {
164+
supplyingTagToSave = wasSupplyingTagToSave;
165+
}
166+
if (supplyingTagToSave) {
167+
tagToSave.putBoolean("clientcommands:causedByTagSaving", true);
119168
}
120169
NbtIo.writeUnnamedTagWithFallback(tagToSave, nbtStream);
121170
} catch (IOException e) {
@@ -128,7 +177,7 @@ public void writeToFile() {
128177
this.nbtStream.close();
129178
Path debugDir = ClientCommands.CONFIG_DIR.resolve("debug");
130179
Files.createDirectories(debugDir);
131-
try (DataOutputStream dataOutput = new DataOutputStream(new GZIPOutputStream(Files.newOutputStream(debugDir.resolve(this.entity.getStringUUID() + ".dat"))))) {
180+
try (DataOutputStream dataOutput = new DataOutputStream(new GZIPOutputStream(Files.newOutputStream(debugDir.resolve(this.idSupplier.get() + ".dat"))))) {
132181
dataOutput.writeInt(stackTraceById.size());
133182
for (String st : stackTraceById) {
134183
dataOutput.writeUTF(st);
@@ -142,7 +191,7 @@ public void writeToFile() {
142191
}
143192
dataOutput.write(this.gzippedNbt.toByteArray());
144193
}
145-
LOGGER.info("Written debug random for " + this.entity.getStringUUID() + " to file");
194+
LOGGER.info("Written debug random for {} to file", this.idSupplier.get());
146195
} catch (IOException e) {
147196
LOGGER.error("Error saving debug source to file", e);
148197
}
@@ -193,7 +242,7 @@ public static void main(String[] args) {
193242
return;
194243
}
195244

196-
JFrame frame = new JFrame("Debug Entity RNG");
245+
JFrame frame = new JFrame("Debug RNG");
197246
frame.add(new DebugRandomSourcePanel(randomCalls));
198247
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
199248
frame.pack();
@@ -251,6 +300,16 @@ class DebugRandomSourcePanel extends JPanel {
251300
if (value.stackTrace() == selectedStackTrace) {
252301
textArea.setBackground(Color.YELLOW);
253302
}
303+
304+
if (value.nbt().contains("clientcommands:causedByTagSaving")) {
305+
JPanel panel = new JPanel(new BorderLayout());
306+
panel.add(textArea, BorderLayout.CENTER);
307+
JLabel warningLabel = new JLabel("This stack trace was caused by saving the NBT tag during debug");
308+
warningLabel.setForeground(Color.RED);
309+
panel.add(warningLabel, BorderLayout.NORTH);
310+
return panel;
311+
}
312+
254313
return textArea;
255314
});
256315
callsInTickList.addListSelectionListener(e -> {

src/main/resources/mixins.clientcommands.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"commands.time.ClientLevelDataMixin",
2121
"commands.weather.LevelMixin",
2222
"debug.EntityMixin",
23+
"debug.LevelMixin",
2324
"debug.ServerLevelMixin",
2425
"events.EntityMixin",
2526
"events.ExperienceOrbMixin",

0 commit comments

Comments
 (0)