mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-27 06:20:02 +00:00
Studio fixes.
- Fixes the Object and Jigsaw studio errors from the coretapper i made.
This commit is contained in:
1 parent
0dcd1900be
commit
894cef73a6
15 files changed
+1628
-83
No files matched your search
@@ -159,6 +159,17 @@ public final class ObjectStudioLayout {
|
||||
c.getInt("h"),
|
||||
c.getInt("d")
|
||||
);
|
||||
IrisData source = sources.get(pack);
|
||||
File objectFile = source == null ? null : source.getObjectLoader().findFile(cell.key());
|
||||
if (objectFile == null) {
|
||||
return null;
|
||||
}
|
||||
IrisBlockVector size = IrisObject.sampleSize(objectFile);
|
||||
if (cell.w() != Math.max(1, size.getBlockX())
|
||||
|| cell.h() != Math.max(1, size.getBlockY())
|
||||
|| cell.d() != Math.max(1, size.getBlockZ())) {
|
||||
return null;
|
||||
}
|
||||
stored.add(cell);
|
||||
storedIds.add(cell.pack() + "/" + cell.key());
|
||||
}
|
||||
@@ -217,6 +228,26 @@ public final class ObjectStudioLayout {
|
||||
return padding;
|
||||
}
|
||||
|
||||
public ObjectStudioLayout atFloor(int floorY) {
|
||||
int originY = Math.addExact(floorY, 1);
|
||||
boolean unchanged = true;
|
||||
for (GridCell cell : cells) {
|
||||
if (cell.originY() != originY) {
|
||||
unchanged = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unchanged) {
|
||||
return this;
|
||||
}
|
||||
List<GridCell> placed = new ArrayList<>(cells.size());
|
||||
for (GridCell cell : cells) {
|
||||
placed.add(new GridCell(cell.pack(), cell.key(), cell.originX(), originY, cell.originZ(),
|
||||
cell.w(), cell.h(), cell.d()));
|
||||
}
|
||||
return new ObjectStudioLayout(padding, rowWidthCap, placed);
|
||||
}
|
||||
|
||||
public List<GridCell> cells() {
|
||||
return cells;
|
||||
}
|
||||
|
||||
@@ -3815,16 +3815,15 @@ public final class JigsawStudioService implements IrisService, JigsawStudioMenuC
|
||||
message(player, "Iris Jigsaw Studio is not active in this world.");
|
||||
return false;
|
||||
}
|
||||
List<JigsawStudioBounds> bounds = new ArrayList<>(pieces.size());
|
||||
List<JigsawStudioPreviewRenderer.PreviewBounds> bounds = new ArrayList<>(pieces.size());
|
||||
for (PlacedStructurePiece piece : pieces) {
|
||||
int width = Math.addExact(Math.subtractExact(piece.getMaxX(), piece.getMinX()), 1);
|
||||
int height = Math.addExact(Math.subtractExact(piece.getMaxY(), piece.getMinY()), 1);
|
||||
int depth = Math.addExact(Math.subtractExact(piece.getMaxZ(), piece.getMinZ()), 1);
|
||||
bounds.add(new JigsawStudioBounds(
|
||||
bounds.add(new JigsawStudioPreviewRenderer.PreviewBounds(
|
||||
piece.getMinX(),
|
||||
piece.getMinY(),
|
||||
piece.getMinZ(),
|
||||
new JigsawStudioCellDimensions(width, height, depth)));
|
||||
piece.getMaxX(),
|
||||
piece.getMaxY(),
|
||||
piece.getMaxZ()));
|
||||
}
|
||||
assemblyPreviews.put(player.getUniqueId(), new AssemblyPreview(
|
||||
player.getWorld().getUID(),
|
||||
@@ -7552,10 +7551,6 @@ public final class JigsawStudioService implements IrisService, JigsawStudioMenuC
|
||||
return;
|
||||
}
|
||||
JigsawStudioPreviewRenderer.PreviewBounds preview = evaluation.previewBounds();
|
||||
JigsawStudioCellDimensions dimensions = new JigsawStudioCellDimensions(
|
||||
preview.maximumX() - preview.minimumX() + 1,
|
||||
preview.maximumY() - preview.minimumY() + 1,
|
||||
preview.maximumZ() - preview.minimumZ() + 1);
|
||||
Color color = switch (evaluation.state()) {
|
||||
case VALID -> ASSEMBLY_PREVIEW_COLOR;
|
||||
case PENDING, WARNING, STALE -> LIVE_PREVIEW_WARNING_COLOR;
|
||||
@@ -7564,11 +7559,7 @@ public final class JigsawStudioService implements IrisService, JigsawStudioMenuC
|
||||
drawBounds(
|
||||
player,
|
||||
playerLocation,
|
||||
new JigsawStudioBounds(
|
||||
preview.minimumX(),
|
||||
preview.minimumY(),
|
||||
preview.minimumZ(),
|
||||
dimensions),
|
||||
preview,
|
||||
color,
|
||||
0.85F,
|
||||
2.0D,
|
||||
@@ -7589,7 +7580,7 @@ public final class JigsawStudioService implements IrisService, JigsawStudioMenuC
|
||||
assemblyPreviews.remove(player.getUniqueId(), preview);
|
||||
return;
|
||||
}
|
||||
for (JigsawStudioBounds bounds : preview.bounds()) {
|
||||
for (JigsawStudioPreviewRenderer.PreviewBounds bounds : preview.bounds()) {
|
||||
drawBounds(player, playerLocation, bounds, ASSEMBLY_PREVIEW_COLOR, 0.85F, 2.0D, budget);
|
||||
if (budget.empty()) {
|
||||
return;
|
||||
@@ -7682,12 +7673,26 @@ public final class JigsawStudioService implements IrisService, JigsawStudioMenuC
|
||||
double step,
|
||||
ParticleBudget budget
|
||||
) {
|
||||
double minX = bounds.originX();
|
||||
double minY = bounds.originY();
|
||||
double minZ = bounds.originZ();
|
||||
double maxX = bounds.maxX() + 1.0D;
|
||||
double maxY = bounds.maxY() + 1.0D;
|
||||
double maxZ = bounds.maxZ() + 1.0D;
|
||||
drawBounds(player, playerLocation, new JigsawStudioPreviewRenderer.PreviewBounds(
|
||||
bounds.originX(), bounds.originY(), bounds.originZ(), bounds.maxX(), bounds.maxY(), bounds.maxZ()),
|
||||
color, size, step, budget);
|
||||
}
|
||||
|
||||
private static void drawBounds(
|
||||
Player player,
|
||||
Location playerLocation,
|
||||
JigsawStudioPreviewRenderer.PreviewBounds bounds,
|
||||
Color color,
|
||||
float size,
|
||||
double step,
|
||||
ParticleBudget budget
|
||||
) {
|
||||
double minX = bounds.minimumX();
|
||||
double minY = bounds.minimumY();
|
||||
double minZ = bounds.minimumZ();
|
||||
double maxX = bounds.maximumX() + 1.0D;
|
||||
double maxY = bounds.maximumY() + 1.0D;
|
||||
double maxZ = bounds.maximumZ() + 1.0D;
|
||||
drawLine(player, playerLocation, minX, minY, minZ, maxX, minY, minZ, color, size, step, budget);
|
||||
drawLine(player, playerLocation, minX, minY, maxZ, maxX, minY, maxZ, color, size, step, budget);
|
||||
drawLine(player, playerLocation, minX, maxY, minZ, maxX, maxY, minZ, color, size, step, budget);
|
||||
@@ -8657,7 +8662,7 @@ public final class JigsawStudioService implements IrisService, JigsawStudioMenuC
|
||||
private record AssemblyPreview(
|
||||
UUID worldId,
|
||||
long expiresAtMillis,
|
||||
List<JigsawStudioBounds> bounds
|
||||
List<JigsawStudioPreviewRenderer.PreviewBounds> bounds
|
||||
) {
|
||||
AssemblyPreview {
|
||||
Objects.requireNonNull(worldId, "Jigsaw Studio preview world");
|
||||
|
||||
@@ -107,7 +107,7 @@ public class ObjectStudioSaveService implements IrisService {
|
||||
}
|
||||
|
||||
String packKey = engine.getDimension() == null ? null : engine.getDimension().getLoadKey();
|
||||
studios.put(world.getUID(), new ActiveStudio(world.getUID(), layout, objectsDirs, packKey));
|
||||
studios.put(world.getUID(), new ActiveStudio(world.getUID(), generator, objectsDirs, packKey));
|
||||
IrisLogging.debug("Object Studio live-save registered: world=%s cells=%d packs=%d",
|
||||
world.getName(), layout.cells().size(), objectsDirs.size());
|
||||
}
|
||||
@@ -219,7 +219,7 @@ public class ObjectStudioSaveService implements IrisService {
|
||||
return;
|
||||
}
|
||||
|
||||
IrisObject snapshot = new IrisObject(cell.w(), cell.h(), cell.d());
|
||||
IrisObject snapshot = studio.generator.createCapture(cell);
|
||||
int originX = cell.originX();
|
||||
int originY = cell.originY();
|
||||
int originZ = cell.originZ();
|
||||
@@ -324,14 +324,16 @@ public class ObjectStudioSaveService implements IrisService {
|
||||
|
||||
private static final class ActiveStudio {
|
||||
final UUID worldId;
|
||||
final ObjectStudioGenerator generator;
|
||||
final ObjectStudioLayout layout;
|
||||
final Map<String, File> objectsDirs;
|
||||
final String packKey;
|
||||
final Map<String, Long> hashes = new ConcurrentHashMap<>();
|
||||
|
||||
ActiveStudio(UUID worldId, ObjectStudioLayout layout, Map<String, File> objectsDirs, String packKey) {
|
||||
ActiveStudio(UUID worldId, ObjectStudioGenerator generator, Map<String, File> objectsDirs, String packKey) {
|
||||
this.worldId = worldId;
|
||||
this.layout = layout;
|
||||
this.generator = generator;
|
||||
this.layout = generator.getLayout();
|
||||
this.objectsDirs = objectsDirs;
|
||||
this.packKey = packKey;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package art.arcane.iris.engine;
|
||||
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.engine.data.cache.Cache;
|
||||
import art.arcane.iris.engine.history.SavedBiomeUnavailableException;
|
||||
import art.arcane.iris.engine.platform.EngineBukkitOps;
|
||||
import art.arcane.iris.platform.bukkit.BukkitWorldBinding;
|
||||
import art.arcane.iris.spi.IrisLogging;
|
||||
@@ -262,7 +263,7 @@ final class WorldChunkMaintenance {
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChunkRegion(World world, int chunkX, int chunkZ) {
|
||||
void updateChunkRegion(World world, int chunkX, int chunkZ) {
|
||||
if (world == null || !world.isChunkLoaded(chunkX, chunkZ) || !Chunks.isSafe(world, chunkX, chunkZ)) {
|
||||
return;
|
||||
}
|
||||
@@ -274,7 +275,14 @@ final class WorldChunkMaintenance {
|
||||
warmupMantleChunkAsync(chunkX, chunkZ);
|
||||
return;
|
||||
}
|
||||
EngineBukkitOps.updateChunk(manager.getEngine(), chunk);
|
||||
try {
|
||||
EngineBukkitOps.updateChunk(manager.getEngine(), chunk);
|
||||
} catch (SavedBiomeUnavailableException unavailable) {
|
||||
if (!unavailable.isLoading() || unavailable.getSuppressed().length != 0) {
|
||||
throw unavailable;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (manager.entitySpawner.isEntitySpawningEnabledForCurrentWorld()) {
|
||||
|
||||
@@ -54,6 +54,7 @@ import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -93,22 +94,22 @@ final class WorldEntitySpawner {
|
||||
}
|
||||
|
||||
if (manager.cl.flip()) {
|
||||
CompletableFuture<Integer> future = new CompletableFuture<>();
|
||||
try {
|
||||
World realWorld = BukkitWorldBinding.world(manager.getEngine().getWorld());
|
||||
if (realWorld == null) {
|
||||
manager.entityCount = 0;
|
||||
manager.entityCountValid = false;
|
||||
} else {
|
||||
CompletableFuture<Integer> future = new CompletableFuture<>();
|
||||
boolean scheduled = J.runGlobal(() -> {
|
||||
try {
|
||||
future.complete(livingEntityCount(realWorld));
|
||||
} catch (Throwable ex) {
|
||||
future.completeExceptionally(ex);
|
||||
}
|
||||
});
|
||||
boolean scheduled = J.runGlobal(managedCompletion(
|
||||
"bukkit_world_manager_entity_count", future, () -> livingEntityCount(realWorld)));
|
||||
if (scheduled) {
|
||||
manager.entityCount = future.get(2, TimeUnit.SECONDS);
|
||||
Integer count = future.get(2, TimeUnit.SECONDS);
|
||||
if (count == null || manager.getEngine().isClosing() || manager.getEngine().isClosed()) {
|
||||
manager.entityCountValid = false;
|
||||
return false;
|
||||
}
|
||||
manager.entityCount = count;
|
||||
manager.entityCountValid = true;
|
||||
resetEntityCountFailures();
|
||||
} else {
|
||||
@@ -126,6 +127,8 @@ final class WorldEntitySpawner {
|
||||
reportEntityCountFailure("Failed to count entities; pausing Iris entity spawning until a complete count is available.", cause);
|
||||
} catch (Throwable e) {
|
||||
reportEntityCountFailure("Failed to count entities; pausing Iris entity spawning until a complete count is available.", e);
|
||||
} finally {
|
||||
future.complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +196,7 @@ final class WorldEntitySpawner {
|
||||
return false;
|
||||
}
|
||||
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
CompletableFuture<Boolean> future = new CompletableFuture<>();
|
||||
AtomicBoolean failureReported = new AtomicBoolean();
|
||||
future.whenComplete((ignored, failure) -> {
|
||||
if (failure != null) {
|
||||
@@ -202,32 +205,28 @@ final class WorldEntitySpawner {
|
||||
});
|
||||
boolean scheduled;
|
||||
try {
|
||||
scheduled = J.runRegion(world, chunkX, chunkZ, () -> {
|
||||
try {
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ) || !Chunks.isSafe(world, chunkX, chunkZ)) {
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
|
||||
spawnIn(world.getChunkAt(chunkX, chunkZ), initial);
|
||||
future.complete(null);
|
||||
} catch (Throwable e) {
|
||||
future.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
scheduled = J.runRegion(world, chunkX, chunkZ, managedCompletion(
|
||||
"bukkit_world_manager_entity_spawn", future, () -> {
|
||||
if (!world.isChunkLoaded(chunkX, chunkZ) || !Chunks.isSafe(world, chunkX, chunkZ)) {
|
||||
return true;
|
||||
}
|
||||
spawnIn(world.getChunkAt(chunkX, chunkZ), initial);
|
||||
return true;
|
||||
}));
|
||||
} catch (Throwable e) {
|
||||
future.complete(null);
|
||||
IrisLogging.reportError("Failed to schedule an Iris entity spawn for chunk " + chunkX + "," + chunkZ + ".", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!scheduled) {
|
||||
future.complete(null);
|
||||
IrisLogging.debug("Skipped Iris entity spawning because the region task was not accepted for chunk " + chunkX + "," + chunkZ + ".");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
future.get(5, TimeUnit.SECONDS);
|
||||
return true;
|
||||
return Boolean.TRUE.equals(future.get(5, TimeUnit.SECONDS));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return false;
|
||||
@@ -238,9 +237,28 @@ final class WorldEntitySpawner {
|
||||
Throwable cause = e.getCause() == null ? e : e.getCause();
|
||||
reportSpawnFailure(chunkX, chunkZ, cause, failureReported);
|
||||
return false;
|
||||
} finally {
|
||||
future.complete(null);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> Runnable managedCompletion(String operation, CompletableFuture<T> future, Supplier<T> task) {
|
||||
Runnable managed = manager.managedTask(operation, () -> {
|
||||
if (!future.isDone()) {
|
||||
future.complete(task.get());
|
||||
}
|
||||
}, () -> future.complete(null));
|
||||
return () -> {
|
||||
try {
|
||||
managed.run();
|
||||
} catch (Throwable failure) {
|
||||
if (!future.completeExceptionally(failure)) {
|
||||
IrisLogging.reportError("Failed to finish " + operation + ".", failure);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void reportEntityCountFailure(String message, Throwable error) {
|
||||
manager.entityCountValid = false;
|
||||
if (error != null) {
|
||||
|
||||
@@ -629,6 +629,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
lock.lock();
|
||||
|
||||
try {
|
||||
throwIfInitializationFailed();
|
||||
if (setup.get()) {
|
||||
return getEngine();
|
||||
}
|
||||
@@ -636,6 +637,19 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
|
||||
getWorld().setRawWorldSeed(world.getSeed());
|
||||
setupEngine();
|
||||
try {
|
||||
computeStudioGenerator();
|
||||
} catch (Throwable failure) {
|
||||
initializationFailure = failure;
|
||||
try {
|
||||
engine.close();
|
||||
} catch (Throwable closeFailure) {
|
||||
if (failure != closeFailure) {
|
||||
failure.addSuppressed(closeFailure);
|
||||
}
|
||||
}
|
||||
throw propagateEngineSetupFailure(failure);
|
||||
}
|
||||
setup.set(true);
|
||||
this.hotloader = shouldRunStudioHotload(studio, closing, jigsawStudioActive) ? new Looper() {
|
||||
@Override
|
||||
@@ -935,11 +949,11 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
if (engine != expectedEngine) {
|
||||
throw new IllegalStateException("Iris noise generation belongs to a replaced engine runtime.");
|
||||
}
|
||||
computeStudioGenerator();
|
||||
StudioGenerator selected = computeStudioGenerator();
|
||||
if (engine != expectedEngine) {
|
||||
throw new IllegalStateException("Iris noise generation changed engine runtime during Studio resolution.");
|
||||
}
|
||||
return studioGenerator;
|
||||
return selected;
|
||||
}
|
||||
|
||||
public void withExclusiveControl(Runnable r) {
|
||||
@@ -1077,10 +1091,10 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
|
||||
try {
|
||||
Engine engine = getEngine(world);
|
||||
computeStudioGenerator();
|
||||
StudioGenerator selected = computeStudioGenerator();
|
||||
TerrainChunk tc = TerrainChunk.create(d);
|
||||
if (studioGenerator != null) {
|
||||
studioGenerator.generateChunk(engine, tc, x, z);
|
||||
if (selected != null) {
|
||||
selected.generateChunk(engine, tc, x, z);
|
||||
} else {
|
||||
ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(d);
|
||||
Hunk<PlatformBiome> biomes = Hunk.viewBiomes(tc);
|
||||
@@ -1191,7 +1205,14 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
}
|
||||
}
|
||||
|
||||
private void computeStudioGenerator() {
|
||||
private StudioGenerator computeStudioGenerator() {
|
||||
if (!studio) {
|
||||
return studioGenerator;
|
||||
}
|
||||
return computeStudioGeneratorForStudio();
|
||||
}
|
||||
|
||||
private synchronized StudioGenerator computeStudioGeneratorForStudio() {
|
||||
String packKey = getEngine().getDimension().getLoadKey();
|
||||
JigsawStudioActivation.Request jigsawRequest = studio
|
||||
? JigsawStudioActivation.getGeneratorRequest(packKey)
|
||||
@@ -1204,7 +1225,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
lastJigsawStudioRequestId = jigsawRequest.requestId();
|
||||
lastMode = null;
|
||||
}
|
||||
return;
|
||||
return studioGenerator;
|
||||
}
|
||||
}
|
||||
if (lastJigsawStudioRequestId != null) {
|
||||
@@ -1221,9 +1242,10 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
||||
desired = StudioMode.OBJECT_BUFFET;
|
||||
}
|
||||
if (!desired.equals(lastMode)) {
|
||||
lastMode = desired;
|
||||
desired.inject(this);
|
||||
lastMode = desired;
|
||||
}
|
||||
return studioGenerator;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -60,6 +60,7 @@ import art.arcane.volmlib.util.matter.MatterCavern;
|
||||
import art.arcane.volmlib.util.matter.MatterUpdate;
|
||||
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
@@ -228,12 +229,33 @@ public final class EngineBukkitOps {
|
||||
}
|
||||
}
|
||||
|
||||
mantleChunk.iterate(MatterUpdate.class, (x, yf, z, value) -> {
|
||||
int y = yf + engine.getWorld().minHeight();
|
||||
if (value != null && value.isUpdate()) {
|
||||
dispatcher.update(x, y, z);
|
||||
IntArrayList completed = new IntArrayList();
|
||||
try {
|
||||
mantleChunk.iterate(MatterUpdate.class, (x, yf, z, value) -> {
|
||||
int y = yf + engine.getWorld().minHeight();
|
||||
if (value != null && value.isUpdate()) {
|
||||
dispatcher.update(x, y, z);
|
||||
completed.add((yf << 8) | ((x & 15) << 4) | (z & 15));
|
||||
}
|
||||
});
|
||||
} catch (RuntimeException | Error failure) {
|
||||
try {
|
||||
for (int index = 0; index < completed.size(); index++) {
|
||||
int position = completed.getInt(index);
|
||||
int y = position >>> 8;
|
||||
Matter section = mantleChunk.get(y >> 4);
|
||||
if (section != null && section.hasSlice(MatterUpdate.class)) {
|
||||
section.<MatterUpdate>getSlice(MatterUpdate.class)
|
||||
.set((position >> 4) & 15, y & 15, position & 15, null);
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException | Error cleanupFailure) {
|
||||
if (cleanupFailure != failure) {
|
||||
failure.addSuppressed(cleanupFailure);
|
||||
}
|
||||
}
|
||||
});
|
||||
throw failure;
|
||||
}
|
||||
mantleChunk.deleteSlices(MatterUpdate.class);
|
||||
engine.getMetrics().getUpdates().put(stopwatch.getMilliseconds());
|
||||
}
|
||||
|
||||
+48
-16
@@ -60,7 +60,7 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator {
|
||||
private final PlatformBlockState marker;
|
||||
private final AtomicBoolean layoutBuilt = new AtomicBoolean(false);
|
||||
private final Object layoutLock = new Object();
|
||||
private final Map<String, IrisObject> objectCache = new ConcurrentHashMap<>();
|
||||
private final Map<String, DisplayObject> objectCache = new ConcurrentHashMap<>();
|
||||
private final Map<String, IrisData> packData = new ConcurrentHashMap<>();
|
||||
private volatile ObjectStudioLayout layout;
|
||||
|
||||
@@ -130,7 +130,7 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator {
|
||||
|
||||
paintFrame(cell, tc, plinthY, chunkWorldX, chunkWorldZ, minHeight, maxHeight);
|
||||
|
||||
IrisObject object = loadObject(cell);
|
||||
DisplayObject object = loadObject(cell);
|
||||
if (object != null) {
|
||||
placeSlice(object, cell, tc, chunkWorldX, chunkWorldZ, minHeight, maxHeight);
|
||||
}
|
||||
@@ -196,24 +196,19 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private void placeSlice(IrisObject object, GridCell cell, TerrainChunk tc, int chunkWorldX, int chunkWorldZ, int minHeight, int maxHeight) {
|
||||
VectorMap<PlatformBlockState> blocks = object.getBlocks();
|
||||
private void placeSlice(DisplayObject object, GridCell cell, TerrainChunk tc, int chunkWorldX, int chunkWorldZ, int minHeight, int maxHeight) {
|
||||
VectorMap<PlatformBlockState> blocks = object.source().getBlocks();
|
||||
if (blocks == null || blocks.isEmpty()) return;
|
||||
|
||||
Vector3i center = object.getCenter();
|
||||
int centerX = center == null ? 0 : center.getBlockX();
|
||||
int centerY = center == null ? 0 : center.getBlockY();
|
||||
int centerZ = center == null ? 0 : center.getBlockZ();
|
||||
|
||||
int originX = cell.originX();
|
||||
int originY = cell.originY();
|
||||
int originZ = cell.originZ();
|
||||
|
||||
for (Map.Entry<IrisBlockVector, PlatformBlockState> entry : blocks) {
|
||||
IrisBlockVector signed = entry.getKey();
|
||||
int worldX = originX + signed.getBlockX() + centerX;
|
||||
int worldY = originY + signed.getBlockY() + centerY;
|
||||
int worldZ = originZ + signed.getBlockZ() + centerZ;
|
||||
int worldX = originX + signed.getBlockX() - object.minimumX();
|
||||
int worldY = originY + signed.getBlockY() - object.minimumY();
|
||||
int worldZ = originZ + signed.getBlockZ() - object.minimumZ();
|
||||
|
||||
if (worldX < chunkWorldX || worldX > chunkWorldX + 15) continue;
|
||||
if (worldZ < chunkWorldZ || worldZ > chunkWorldZ + 15) continue;
|
||||
@@ -226,17 +221,29 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private IrisObject loadObject(GridCell cell) {
|
||||
public IrisObject createCapture(GridCell cell) {
|
||||
DisplayObject displayed = loadObject(cell);
|
||||
if (displayed == null) {
|
||||
throw new IllegalStateException("Object Studio source is unavailable: " + cell.pack() + "/" + cell.key());
|
||||
}
|
||||
IrisObject capture = new IrisObject(cell.w(), cell.h(), cell.d());
|
||||
capture.setCenter(new Vector3i(-displayed.minimumX(), -displayed.minimumY(), -displayed.minimumZ()));
|
||||
return capture;
|
||||
}
|
||||
|
||||
private DisplayObject loadObject(GridCell cell) {
|
||||
String cacheKey = cell.pack() + "/" + cell.key();
|
||||
IrisObject cached = objectCache.get(cacheKey);
|
||||
DisplayObject cached = objectCache.get(cacheKey);
|
||||
if (cached != null) return cached;
|
||||
IrisData data = packData.get(cell.pack());
|
||||
if (data == null) return null;
|
||||
IrisObject loaded = data.getObjectLoader().load(cell.key());
|
||||
if (loaded != null) {
|
||||
objectCache.put(cacheKey, loaded);
|
||||
DisplayObject displayed = DisplayObject.of(loaded);
|
||||
DisplayObject existing = objectCache.putIfAbsent(cacheKey, displayed);
|
||||
return existing == null ? displayed : existing;
|
||||
}
|
||||
return loaded;
|
||||
return null;
|
||||
}
|
||||
|
||||
public Map<String, IrisData> getPackData() {
|
||||
@@ -257,6 +264,9 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator {
|
||||
layout = resumed;
|
||||
} else {
|
||||
layout = ObjectStudioLayout.build(sources, padding);
|
||||
}
|
||||
layout = layout.atFloor(Math.max(engine.getMinHeight(), ObjectStudioLayout.FLOOR_Y));
|
||||
if (layout != resumed) {
|
||||
layout.save(layoutFile);
|
||||
}
|
||||
layoutBuilt.set(true);
|
||||
@@ -300,4 +310,26 @@ public class ObjectStudioGenerator extends EnginedStudioGenerator {
|
||||
}
|
||||
return new IrisBlockVector(maxX, 0, maxZ);
|
||||
}
|
||||
|
||||
private record DisplayObject(IrisObject source, int minimumX, int minimumY, int minimumZ) {
|
||||
private static DisplayObject of(IrisObject object) {
|
||||
int minimumX = Integer.MAX_VALUE;
|
||||
int minimumY = Integer.MAX_VALUE;
|
||||
int minimumZ = Integer.MAX_VALUE;
|
||||
for (Map.Entry<IrisBlockVector, PlatformBlockState> entry : object.getBlocks()) {
|
||||
if (entry.getValue() == null) {
|
||||
continue;
|
||||
}
|
||||
IrisBlockVector position = entry.getKey();
|
||||
minimumX = Math.min(minimumX, position.getBlockX());
|
||||
minimumY = Math.min(minimumY, position.getBlockY());
|
||||
minimumZ = Math.min(minimumZ, position.getBlockZ());
|
||||
}
|
||||
if (minimumX == Integer.MAX_VALUE) {
|
||||
Vector3i center = object.getCenter();
|
||||
return new DisplayObject(object, -center.getBlockX(), -center.getBlockY(), -center.getBlockZ());
|
||||
}
|
||||
return new DisplayObject(object, minimumX, minimumY, minimumZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user