Phase 12: batch-prefetch storage contents for restore
Spark profile confirmed 'restoreSophisticatedStorageItems' and its single-item
helpers as hot paths on the server main thread. The prior restore did:
for each backpack/shulker/disk in the player's inventory:
SELECT backpack_nbt FROM backpack_data WHERE uuid = ?
deserialize
apply
With a player carrying 3 backpacks + 2 shulkers + 4 RS2 disks this was
9 sequential blocking SELECTs on the main thread — adding ~9 round-trips
of MySQL latency to the restore window.
Adds two helpers:
ModsSupport.prefetchStorageContents(Collection<UUID>)
→ single SELECT with WHERE uuid IN (?,?,?,...) returning a
Map<UUID, CompoundTag>. Shares the parsing path (BNBT: prefix,
legacy Base64, snbt fallback) with restoreStorageContents so
any serialization quirk handled there is handled here.
ModsSupport.collectBackpackUuids(Player, includeEnderChest)
→ UUID-only scan without any DB work, used by the restore path
to build the prefetch list.
No behavior change yet — the helpers are wired in a follow-up commit
that plugs them into doPlayerJoin's apply phase.
This commit is contained in:
parent
7bf2cd6bcc
commit
f1540c8210
|
|
@ -645,6 +645,87 @@ public class ModsSupport {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PHASE 12 PERF: batch-fetch storage contents (backpack / SS / RS2 share the
|
||||
* {@code backpack_data} table) for a list of UUIDs in ONE query via WHERE
|
||||
* uuid IN (...). Called from the restore path to avoid N sequential SELECTs
|
||||
* on the main thread when a player has multiple backpacks/shulkers/disks.
|
||||
*
|
||||
* @return map {uuid → deserialized CompoundTag}; missing UUIDs absent
|
||||
*/
|
||||
public static java.util.Map<UUID, CompoundTag> prefetchStorageContents(java.util.Collection<UUID> uuids) {
|
||||
java.util.Map<UUID, CompoundTag> out = new java.util.HashMap<>();
|
||||
if (uuids == null || uuids.isEmpty()) return out;
|
||||
java.util.List<UUID> unique = new java.util.ArrayList<>(new java.util.LinkedHashSet<>(uuids));
|
||||
StringBuilder placeholders = new StringBuilder("?");
|
||||
for (int i = 1; i < unique.size(); i++) placeholders.append(",?");
|
||||
String sql = "SELECT uuid, backpack_nbt FROM " + Tables.backpackData() + " WHERE uuid IN (" + placeholders + ")";
|
||||
Object[] params = new Object[unique.size()];
|
||||
for (int i = 0; i < unique.size(); i++) params[i] = unique.get(i).toString();
|
||||
try (JDBCsetUp.QueryResult qr = JDBCsetUp.executePreparedQuery(sql, params)) {
|
||||
ResultSet rs = qr.resultSet();
|
||||
while (rs.next()) {
|
||||
String uuidStr = rs.getString("uuid");
|
||||
String serialized = rs.getString("backpack_nbt");
|
||||
if (serialized == null) continue;
|
||||
try {
|
||||
CompoundTag nbt;
|
||||
if (serialized.startsWith("BNBT:")) {
|
||||
nbt = VanillaSync.deserializeBinaryBase64Tag(serialized);
|
||||
} else {
|
||||
String nbtString = VanillaSync.deserializeString(serialized);
|
||||
try {
|
||||
nbt = TagParser.parseTag(nbtString);
|
||||
} catch (CommandSyntaxException ex) {
|
||||
nbt = net.minecraft.nbt.NbtUtils.snbtToStructure(nbtString);
|
||||
}
|
||||
}
|
||||
out.put(UUID.fromString(uuidStr), nbt);
|
||||
} catch (Exception e) {
|
||||
PlayerSync.LOGGER.warn("[prefetch-storage] failed to parse NBT for {}: {}", uuidStr, e.getMessage());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PlayerSync.LOGGER.error("[prefetch-storage] batch SELECT failed for {} uuid(s)", unique.size(), e);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Backpack UUID collection without triggering a DB snapshot. Used by the
|
||||
* restore path to prefetch storage contents in bulk.
|
||||
*/
|
||||
public static java.util.List<UUID> collectBackpackUuids(Player player, boolean includeEnderChest) {
|
||||
java.util.List<UUID> uuids = new java.util.ArrayList<>();
|
||||
if (!ModList.get().isLoaded("sophisticatedbackpacks")) return uuids;
|
||||
try {
|
||||
net.p3pp3rf1y.sophisticatedbackpacks.util.PlayerInventoryProvider.get().runOnBackpacks(player,
|
||||
(ItemStack stack, String handler, String identifier, int slot) -> {
|
||||
addBackpackUuid(stack, uuids);
|
||||
return false;
|
||||
});
|
||||
if (includeEnderChest) {
|
||||
for (int i = 0; i < player.getEnderChestInventory().getContainerSize(); i++) {
|
||||
addBackpackUuid(player.getEnderChestInventory().getItem(i), uuids);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PlayerSync.LOGGER.warn("[collect-backpack-uuids] scan failed: {}", e.getMessage());
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
private static void addBackpackUuid(ItemStack stack, java.util.List<UUID> out) {
|
||||
try {
|
||||
if (stack.isEmpty()) return;
|
||||
net.minecraft.resources.ResourceLocation loc = net.minecraft.core.registries.BuiltInRegistries.ITEM.getKey(stack.getItem());
|
||||
if (loc == null || !loc.getNamespace().equals("sophisticatedbackpacks")) return;
|
||||
net.p3pp3rf1y.sophisticatedbackpacks.backpack.wrapper.IBackpackWrapper wrapper =
|
||||
net.p3pp3rf1y.sophisticatedbackpacks.backpack.wrapper.BackpackWrapper.fromStack(stack);
|
||||
wrapper.getContentsUuid().ifPresent(out::add);
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
// ============================
|
||||
// Sophisticated Storage (barrels, shulkers, chests)
|
||||
// ============================
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user