fix(D-01/C): missing sync + worldgen empty registry race (review)

StruggleSessionManager: add V2EquipmentHelper.sync(player) after bind
resistance update to prevent data loss on server restart during struggle

HangingCagePiece: add fallback ResourceLocation arrays for worldgen when
DataDrivenItemRegistry is empty (race with reload listener on initial
world creation). Registry-first with hardcoded fallbacks.
This commit is contained in:
NotEvil
2026-04-15 00:26:07 +02:00
parent 3d61c9e9e6
commit 3515c89f82
2 changed files with 44 additions and 42 deletions

View File

@@ -647,6 +647,7 @@ public class StruggleSessionManager {
}
if (bindStack.getItem() instanceof IHasResistance resistanceItem) {
resistanceItem.setCurrentResistance(bindStack, session.getCurrentResistance());
V2EquipmentHelper.sync(player);
}
}

View File

@@ -16,6 +16,7 @@ import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.FloatTag;
import net.minecraft.nbt.ListTag;
@@ -543,38 +544,45 @@ public class HangingCagePiece extends StructurePiece {
BlockEntity be = level.getBlockEntity(chestPos);
if (be instanceof TrappedChestBlockEntity trappedChest) {
// Random bind from data-driven ARMS items
List<DataDrivenItemDefinition> binds = DataDrivenItemRegistry.getAll().stream()
.filter(d -> d.occupiedRegions().contains(BodyRegionV2.ARMS))
.collect(Collectors.toList());
if (!binds.isEmpty()) {
DataDrivenItemDefinition chosenBind = binds.get(random.nextInt(binds.size()));
trappedChest.setBind(DataDrivenBondageItem.createStack(chosenBind.id()));
}
trappedChest.setBind(randomItemForRegion(random, BodyRegionV2.ARMS, FALLBACK_BINDS));
// Random gag from data-driven MOUTH items (50% chance)
// Random gag (50% chance)
if (random.nextFloat() < 0.50f) {
List<DataDrivenItemDefinition> gags = DataDrivenItemRegistry.getAll().stream()
.filter(d -> d.occupiedRegions().contains(BodyRegionV2.MOUTH))
.collect(Collectors.toList());
if (!gags.isEmpty()) {
DataDrivenItemDefinition chosenGag = gags.get(random.nextInt(gags.size()));
trappedChest.setGag(DataDrivenBondageItem.createStack(chosenGag.id()));
}
trappedChest.setGag(randomItemForRegion(random, BodyRegionV2.MOUTH, FALLBACK_GAGS));
}
// Random blindfold from data-driven EYES items (30% chance)
// Random blindfold (30% chance)
if (random.nextFloat() < 0.30f) {
List<DataDrivenItemDefinition> blindfolds = DataDrivenItemRegistry.getAll().stream()
.filter(d -> d.occupiedRegions().contains(BodyRegionV2.EYES))
.collect(Collectors.toList());
if (!blindfolds.isEmpty()) {
DataDrivenItemDefinition chosenBf = blindfolds.get(random.nextInt(blindfolds.size()));
trappedChest.setBlindfold(DataDrivenBondageItem.createStack(chosenBf.id()));
}
trappedChest.setBlindfold(randomItemForRegion(random, BodyRegionV2.EYES, FALLBACK_BLINDFOLDS));
}
}
}
// Fallback item IDs for worldgen when DataDrivenItemRegistry is empty (race with reload)
private static final ResourceLocation[] FALLBACK_BINDS = {
new ResourceLocation("tiedup", "ropes"),
new ResourceLocation("tiedup", "chain"),
new ResourceLocation("tiedup", "armbinder")
};
private static final ResourceLocation[] FALLBACK_GAGS = {
new ResourceLocation("tiedup", "cloth_gag"),
new ResourceLocation("tiedup", "ball_gag")
};
private static final ResourceLocation[] FALLBACK_BLINDFOLDS = {
new ResourceLocation("tiedup", "classic_blindfold")
};
private static ItemStack randomItemForRegion(RandomSource random, BodyRegionV2 region, ResourceLocation[] fallbacks) {
List<DataDrivenItemDefinition> defs = DataDrivenItemRegistry.getAll().stream()
.filter(d -> d.occupiedRegions().contains(region))
.collect(Collectors.toList());
if (!defs.isEmpty()) {
return DataDrivenBondageItem.createStack(defs.get(random.nextInt(defs.size())).id());
}
// Fallback for worldgen race condition (registry not loaded yet)
return DataDrivenBondageItem.createStack(fallbacks[random.nextInt(fallbacks.length)]);
}
static void safeSetBlock(
WorldGenLevel level,
BlockPos pos,
@@ -632,26 +640,19 @@ public class HangingCagePiece extends StructurePiece {
entityTag.putUUID("UUID", java.util.UUID.randomUUID());
// Random bind item — the damsel spawns already restrained
List<DataDrivenItemDefinition> bindDefs = DataDrivenItemRegistry.getAll().stream()
.filter(d -> d.occupiedRegions().contains(BodyRegionV2.ARMS))
.collect(Collectors.toList());
if (!bindDefs.isEmpty()) {
DataDrivenItemDefinition chosenBind = bindDefs.get(random.nextInt(bindDefs.size()));
ItemStack bindStack = DataDrivenBondageItem.createStack(chosenBind.id());
bindStack.getOrCreateTag().putString("bindMode", "full");
entityTag.put("Bind", bindStack.save(new CompoundTag()));
ItemStack bindStack = randomItemForRegion(random, BodyRegionV2.ARMS, FALLBACK_BINDS);
bindStack.getOrCreateTag().putString("bindMode", "full");
entityTag.put("Bind", bindStack.save(new CompoundTag()));
// Add directly to chunk's pending entity list
ChunkAccess chunk = level.getChunk(masterPos);
if (chunk instanceof ProtoChunk protoChunk) {
protoChunk.addEntity(entityTag);
TiedUpMod.LOGGER.info(
"[HangingCage] Scheduled {} damsel with {} at {}",
shiny ? "shiny" : "regular",
chosenBind.id(),
masterPos.toShortString()
);
}
// Add directly to chunk's pending entity list
ChunkAccess chunk = level.getChunk(masterPos);
if (chunk instanceof ProtoChunk protoChunk) {
protoChunk.addEntity(entityTag);
TiedUpMod.LOGGER.info(
"[HangingCage] Scheduled {} damsel at {}",
shiny ? "shiny" : "regular",
masterPos.toShortString()
);
}
}
}