mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 22:10:18 +00:00
Hold object lock per placement
- Keep the object read lock across the bake lookup, block pass, grounding and stilt pass so a placement reads one consistent snapshot
This commit is contained in:
1 parent
be30285718
commit
be2fe12524
1 file changed
+299
-294
+299
-294
@@ -475,212 +475,38 @@ final class IrisObjectPlacementRunner {
|
|||||||
int topLayer = Integer.MIN_VALUE;
|
int topLayer = Integer.MIN_VALUE;
|
||||||
int vacuumLowest = Integer.MAX_VALUE;
|
int vacuumLowest = Integer.MAX_VALUE;
|
||||||
int vacuumHighest = Integer.MIN_VALUE;
|
int vacuumHighest = Integer.MIN_VALUE;
|
||||||
ObjectPlacementBake bake = ObjectPlacementBake.of(self, spin, ceilingHang, translating ? translateOffset : null);
|
self.readLock.lock();
|
||||||
PlacementStates palette = new PlacementStates(bake.palette(), config.getRotation(), spinx, spiny, spinz,
|
try {
|
||||||
preventingDecay, placer.isDebugSmartBore(), excludedStiltMaterials);
|
ObjectPlacementBake bake = ObjectPlacementBake.of(self, spin, ceilingHang, translating ? translateOffset : null);
|
||||||
double[] exact = warped || hasEdits || eroding ? bake.exact(self, spin) : null;
|
PlacementStates palette = new PlacementStates(bake.palette(), config.getRotation(), spinx, spiny, spinz,
|
||||||
String[] markers = config.getMarkers().isNotEmpty() && placer.getEngine() != null
|
preventingDecay, placer.isDebugSmartBore(), excludedStiltMaterials);
|
||||||
? resolveMarkers(config, rng, x, yv, z, rdata, bake) : null;
|
double[] exact = warped || hasEdits || eroding ? bake.exact(self, spin) : null;
|
||||||
int[] tileBlocks = bake.tileBlocks();
|
String[] markers = config.getMarkers().isNotEmpty() && placer.getEngine() != null
|
||||||
TileData[] tileValues = bake.tileValues();
|
? resolveMarkers(config, rng, x, yv, z, rdata, bake) : null;
|
||||||
int tileCursor = 0;
|
int[] tileBlocks = bake.tileBlocks();
|
||||||
int count = bake.count();
|
TileData[] tileValues = bake.tileValues();
|
||||||
|
int tileCursor = 0;
|
||||||
|
int count = bake.count();
|
||||||
|
|
||||||
for (int index = 0; index < count; index++) {
|
|
||||||
int entry = bake.paletteIndex(index);
|
|
||||||
TileData tile = null;
|
|
||||||
if (tileBlocks != null && tileCursor < tileBlocks.length && tileBlocks[tileCursor] == index) {
|
|
||||||
tile = tileValues[tileCursor++];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stilting && palette.stiltLayer(entry)) {
|
|
||||||
int layer = bake.layerY(index);
|
|
||||||
if (layer < lowest) {
|
|
||||||
lowest = layer;
|
|
||||||
}
|
|
||||||
if (layer > topLayer) {
|
|
||||||
topLayer = layer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NativeBlockState data = palette.decayed(entry);
|
|
||||||
boolean edited = false;
|
|
||||||
if (hasEdits) {
|
|
||||||
double exactX = exact[index * 3];
|
|
||||||
double exactY = exact[index * 3 + 1];
|
|
||||||
double exactZ = exact[index * 3 + 2];
|
|
||||||
for (IrisObjectReplace j : config.getEdit()) {
|
|
||||||
if (rng.chance(j.getChance())) {
|
|
||||||
for (NativeBlockState k : j.getFind(rdata)) {
|
|
||||||
if (j.isExact() ? k.matches(data) : IrisObjectShaping.materialKey(k).equals(IrisObjectShaping.materialKey(data))) {
|
|
||||||
NativeBlockState newData = j.getReplace(rng, exactX + x, exactY + y, exactZ + z, rdata);
|
|
||||||
|
|
||||||
boolean sameMaterial = IrisObjectShaping.materialKey(newData).equals(IrisObjectShaping.materialKey(data));
|
|
||||||
if (sameMaterial && !(newData.isCustom() || data.isCustom()))
|
|
||||||
data = BlockDataMergeSupport.merge(data, newData);
|
|
||||||
else
|
|
||||||
data = newData;
|
|
||||||
edited = true;
|
|
||||||
|
|
||||||
Optional<TileData> t = j.getReplace().getTile(rng, x, y, z, rdata);
|
|
||||||
if (t.isPresent()) {
|
|
||||||
tile = t.get();
|
|
||||||
} else if (!sameMaterial) {
|
|
||||||
tile = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StateFacts facts = edited ? palette.factsOf(data) : palette.facts(entry);
|
|
||||||
NativeBlockState rotated = facts.rotated();
|
|
||||||
if (rotated == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
xx = x + bake.offsetX(index);
|
|
||||||
int yy = y + bake.offsetY(index);
|
|
||||||
zz = z + bake.offsetZ(index);
|
|
||||||
|
|
||||||
if (warped) {
|
|
||||||
double exactX = exact[index * 3];
|
|
||||||
double exactY = exact[index * 3 + 1];
|
|
||||||
double exactZ = exact[index * 3 + 2];
|
|
||||||
xx += surfaceWarp.fitDouble(-warpHalf, warpHalf, exactX + x, exactY + y, exactZ + z);
|
|
||||||
zz += surfaceWarp.fitDouble(-warpHalf, warpHalf, exactZ + z, exactY + y, exactX + x);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paint) {
|
|
||||||
int surface = paintProjection.surfaceY(xx, zz);
|
|
||||||
if (surface == IrisPaintSurfaceProjection.MISSING) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!B.isVineBlock(rotated)) {
|
|
||||||
yy = bake.offsetY(index) + Math.floorDiv(self.h, 2) + surface + yrand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.isMeld() && !rawStructurePiece && !placer.isSolid(xx, yy, zz)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean canopy = bake.canopy(index);
|
|
||||||
if (canopy && !placer.beginTreeCanopy(new BlockPosition(xx, yy, zz))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
data = rotated;
|
|
||||||
if (facts.waterloggable()) {
|
|
||||||
data = waterlogCandidate || facts.authoredWaterlogged()
|
|
||||||
? IrisProceduralBlocks.normalizeWaterlogging(data, placer.get(xx, yy, zz), waterlogCandidate)
|
|
||||||
: facts.dry();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rawStructurePiece && B.isVineBlock(data)) {
|
|
||||||
data = attachVineFaces(placer, data, xx, yy, zz);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Short-circuit order matters for cost only: the mantle read is paid solely
|
|
||||||
// for vine blocks. Both operands are pure, so the value is unchanged.
|
|
||||||
boolean wouldReplace = !rawStructurePiece && B.isVineBlock(data) && B.isSolid(placer.get(xx, yy, zz));
|
|
||||||
boolean place = shouldPlaceObjectBlock(rawStructurePiece, facts.air(), wouldReplace);
|
|
||||||
|
|
||||||
if (data.isCustom() || place) {
|
|
||||||
placer.set(xx, yy, zz, data);
|
|
||||||
if (heightmap != null) {
|
|
||||||
Position2 pos = new Position2(xx, zz);
|
|
||||||
Integer currentHeight = heightmap.get(pos);
|
|
||||||
if (currentHeight == null || currentHeight < yy) {
|
|
||||||
heightmap.put(pos, yy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tile != null) {
|
|
||||||
placer.setTile(xx, yy, zz, tile);
|
|
||||||
}
|
|
||||||
if (markers != null && markers[index] != null) {
|
|
||||||
placer.setData(xx, yy, zz, new MatterMarker(markers[index]));
|
|
||||||
}
|
|
||||||
if (listener != null) {
|
|
||||||
listener.accept(new BlockPosition(xx, yy, zz), data);
|
|
||||||
}
|
|
||||||
if (vacuuming && yy < vacuumLowest) {
|
|
||||||
vacuumLowest = yy;
|
|
||||||
}
|
|
||||||
if (vacuuming && yy > vacuumHighest) {
|
|
||||||
vacuumHighest = yy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (canopy) {
|
|
||||||
placer.endTreeCanopy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (grounding != null) {
|
|
||||||
for (TreeGrounding.Extension extension : grounding.extensions()) {
|
|
||||||
for (int rootY = extension.topY(); rootY >= extension.bottomY(); rootY--) {
|
|
||||||
NativeBlockState rootState = normalizePlacedWaterlogging(extension.state(), placer,
|
|
||||||
extension.x(), rootY, extension.z(), waterlogCandidate);
|
|
||||||
placer.set(extension.x(), rootY, extension.z(), rootState);
|
|
||||||
if (listener != null) {
|
|
||||||
listener.accept(new BlockPosition(extension.x(), rootY, extension.z()), rootState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stilting) {
|
|
||||||
IrisStiltSettings settings = stiltSettings;
|
|
||||||
|
|
||||||
double erodeCentroidX = 0;
|
|
||||||
double erodeCentroidZ = 0;
|
|
||||||
double erodeMaxDist = 1;
|
|
||||||
if (eroding) {
|
|
||||||
int centroidCount = 0;
|
|
||||||
for (int index = 0; index < count; index++) {
|
|
||||||
if (bake.layerY(index) == lowest && palette.shouldStilt(bake.paletteIndex(index))) {
|
|
||||||
erodeCentroidX += exact[index * 3];
|
|
||||||
erodeCentroidZ += exact[index * 3 + 2];
|
|
||||||
centroidCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (centroidCount > 0) {
|
|
||||||
erodeCentroidX /= centroidCount;
|
|
||||||
erodeCentroidZ /= centroidCount;
|
|
||||||
}
|
|
||||||
for (int index = 0; index < count; index++) {
|
|
||||||
if (bake.layerY(index) == lowest && palette.shouldStilt(bake.paletteIndex(index))) {
|
|
||||||
double dx = exact[index * 3] - erodeCentroidX;
|
|
||||||
double dz = exact[index * 3 + 2] - erodeCentroidZ;
|
|
||||||
double dist = Math.sqrt(dx * dx + dz * dz);
|
|
||||||
if (dist > erodeMaxDist) {
|
|
||||||
erodeMaxDist = dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int targetLayer = ceilingHang ? topLayer : lowest;
|
|
||||||
for (int index = 0; index < count; index++) {
|
for (int index = 0; index < count; index++) {
|
||||||
int entry = bake.paletteIndex(index);
|
int entry = bake.paletteIndex(index);
|
||||||
if (!palette.shouldStilt(entry)) {
|
TileData tile = null;
|
||||||
continue;
|
if (tileBlocks != null && tileCursor < tileBlocks.length && tileBlocks[tileCursor] == index) {
|
||||||
|
tile = tileValues[tileCursor++];
|
||||||
}
|
}
|
||||||
|
|
||||||
NativeBlockState d;
|
if (stilting && palette.stiltLayer(entry)) {
|
||||||
if (settings != null && settings.getPalette() != null) {
|
int layer = bake.layerY(index);
|
||||||
d = config.getStiltSettings().getPalette().get(rng, x, y, z, rdata);
|
if (layer < lowest) {
|
||||||
} else {
|
lowest = layer;
|
||||||
d = palette.stiltDefault(entry);
|
}
|
||||||
|
if (layer > topLayer) {
|
||||||
|
topLayer = layer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bake.layerY(index) != targetLayer)
|
NativeBlockState data = palette.decayed(entry);
|
||||||
continue;
|
boolean edited = false;
|
||||||
d = palette.rotate(d);
|
|
||||||
|
|
||||||
if (hasEdits) {
|
if (hasEdits) {
|
||||||
double exactX = exact[index * 3];
|
double exactX = exact[index * 3];
|
||||||
double exactY = exact[index * 3 + 1];
|
double exactY = exact[index * 3 + 1];
|
||||||
@@ -688,16 +514,21 @@ final class IrisObjectPlacementRunner {
|
|||||||
for (IrisObjectReplace j : config.getEdit()) {
|
for (IrisObjectReplace j : config.getEdit()) {
|
||||||
if (rng.chance(j.getChance())) {
|
if (rng.chance(j.getChance())) {
|
||||||
for (NativeBlockState k : j.getFind(rdata)) {
|
for (NativeBlockState k : j.getFind(rdata)) {
|
||||||
if (d == null) {
|
if (j.isExact() ? k.matches(data) : IrisObjectShaping.materialKey(k).equals(IrisObjectShaping.materialKey(data))) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (j.isExact() ? k.matches(d) : IrisObjectShaping.materialKey(k).equals(IrisObjectShaping.materialKey(d))) {
|
|
||||||
NativeBlockState newData = j.getReplace(rng, exactX + x, exactY + y, exactZ + z, rdata);
|
NativeBlockState newData = j.getReplace(rng, exactX + x, exactY + y, exactZ + z, rdata);
|
||||||
|
|
||||||
if (IrisObjectShaping.materialKey(newData).equals(IrisObjectShaping.materialKey(d))) {
|
boolean sameMaterial = IrisObjectShaping.materialKey(newData).equals(IrisObjectShaping.materialKey(data));
|
||||||
d = BlockDataMergeSupport.merge(d, newData);
|
if (sameMaterial && !(newData.isCustom() || data.isCustom()))
|
||||||
} else {
|
data = BlockDataMergeSupport.merge(data, newData);
|
||||||
d = newData;
|
else
|
||||||
|
data = newData;
|
||||||
|
edited = true;
|
||||||
|
|
||||||
|
Optional<TileData> t = j.getReplace().getTile(rng, x, y, z, rdata);
|
||||||
|
if (t.isPresent()) {
|
||||||
|
tile = t.get();
|
||||||
|
} else if (!sameMaterial) {
|
||||||
|
tile = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -705,10 +536,13 @@ final class IrisObjectPlacementRunner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d == null || !palette.shouldStiltPlaced(d))
|
StateFacts facts = edited ? palette.factsOf(data) : palette.facts(entry);
|
||||||
|
NativeBlockState rotated = facts.rotated();
|
||||||
|
if (rotated == null) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
xx = x + bake.offsetX(index);
|
xx = x + bake.offsetX(index);
|
||||||
|
int yy = y + bake.offsetY(index);
|
||||||
zz = z + bake.offsetZ(index);
|
zz = z + bake.offsetZ(index);
|
||||||
|
|
||||||
if (warped) {
|
if (warped) {
|
||||||
@@ -719,105 +553,276 @@ final class IrisObjectPlacementRunner {
|
|||||||
zz += surfaceWarp.fitDouble(-warpHalf, warpHalf, exactZ + z, exactY + y, exactX + x);
|
zz += surfaceWarp.fitDouble(-warpHalf, warpHalf, exactZ + z, exactY + y, exactX + x);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (organic) {
|
if (paint) {
|
||||||
int startY = targetLayer + y;
|
int surface = paintProjection.surfaceY(xx, zz);
|
||||||
int maxScan = settings != null ? Math.max(1, settings.getOrganicMaxScan()) : 48;
|
if (surface == IrisPaintSurfaceProjection.MISSING) {
|
||||||
int jitterMax = settings != null ? Math.max(0, settings.getOrganicJitter()) : 3;
|
continue;
|
||||||
double scratch = settings != null ? Math.max(0, Math.min(1, settings.getOrganicScratch())) : 0.55;
|
|
||||||
long colHash = ((long) xx * 341873128712L) ^ ((long) zz * 132897987541L);
|
|
||||||
int jitter = jitterMax > 0 ? (int) (Math.abs(colHash) % (jitterMax + 1)) : 0;
|
|
||||||
|
|
||||||
if (ceilingHang) {
|
|
||||||
int scan = 0;
|
|
||||||
int solidY = startY + 1;
|
|
||||||
while (scan < maxScan && !placer.isSolid(xx, solidY, zz)) {
|
|
||||||
solidY++;
|
|
||||||
scan++;
|
|
||||||
}
|
|
||||||
int topBound = (scan < maxScan ? solidY - 1 : startY + Math.min(maxScan, 8)) - jitter;
|
|
||||||
int total = topBound - startY;
|
|
||||||
for (int j = startY; j <= topBound; j++) {
|
|
||||||
if (scratch > 0 && total > 0) {
|
|
||||||
double ratio = (double) (j - startY) / total;
|
|
||||||
if (ratio > (1.0 - scratch)) {
|
|
||||||
long sh = ((long) xx * 341873128712L) ^ ((long) j * 132897987541L) ^ ((long) zz * 735791245321L);
|
|
||||||
double skipChance = (ratio - (1.0 - scratch)) / scratch;
|
|
||||||
if ((Math.abs(sh) % 1000) / 1000.0 < skipChance * 0.7) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
placer.set(xx, j, zz, normalizePlacedWaterlogging(d, placer, xx, j, zz, waterlogCandidate));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int scan = 0;
|
|
||||||
int solidY = startY - 1;
|
|
||||||
while (scan < maxScan && !placer.isSolid(xx, solidY, zz)) {
|
|
||||||
solidY--;
|
|
||||||
scan++;
|
|
||||||
}
|
|
||||||
int bottomBound = (scan < maxScan ? solidY + 1 : startY - Math.min(maxScan, 8)) + jitter;
|
|
||||||
int total = startY - bottomBound;
|
|
||||||
for (int j = startY; j >= bottomBound; j--) {
|
|
||||||
if (scratch > 0 && total > 0) {
|
|
||||||
double ratio = (double) (startY - j) / total;
|
|
||||||
if (ratio > (1.0 - scratch)) {
|
|
||||||
long sh = ((long) xx * 341873128712L) ^ ((long) j * 132897987541L) ^ ((long) zz * 735791245321L);
|
|
||||||
double skipChance = (ratio - (1.0 - scratch)) / scratch;
|
|
||||||
if ((Math.abs(sh) % 1000) / 1000.0 < skipChance * 0.7) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
placer.set(xx, j, zz, normalizePlacedWaterlogging(d, placer, xx, j, zz, waterlogCandidate));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (!B.isVineBlock(rotated)) {
|
||||||
|
yy = bake.offsetY(index) + Math.floorDiv(self.h, 2) + surface + yrand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.isMeld() && !rawStructurePiece && !placer.isSolid(xx, yy, zz)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int highest = placer.getHighest(xx, zz, self.getLoader(), true);
|
boolean canopy = bake.canopy(index);
|
||||||
|
if (canopy && !placer.beginTreeCanopy(new BlockPosition(xx, yy, zz))) {
|
||||||
int lowerBound = highest - 1;
|
continue;
|
||||||
if (settings != null) {
|
|
||||||
lowerBound -= config.getStiltSettings().getOverStilt() - rng.i(0, config.getStiltSettings().getYRand());
|
|
||||||
if (settings.getYMax() != 0)
|
|
||||||
lowerBound -= Math.min(config.getStiltSettings().getYMax() - (lowest + y - highest), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eroding) {
|
try {
|
||||||
double dx = exact[index * 3] - erodeCentroidX;
|
data = rotated;
|
||||||
double dz = exact[index * 3 + 2] - erodeCentroidZ;
|
if (facts.waterloggable()) {
|
||||||
double normalizedDist = Math.sqrt(dx * dx + dz * dz) / erodeMaxDist;
|
data = waterlogCandidate || facts.authoredWaterlogged()
|
||||||
normalizedDist = Math.min(normalizedDist, 1.0);
|
? IrisProceduralBlocks.normalizeWaterlogging(data, placer.get(xx, yy, zz), waterlogCandidate)
|
||||||
int totalDepth = (lowest + y) - lowerBound;
|
: facts.dry();
|
||||||
int erodeDepth = (int) (totalDepth * Math.pow(1.0 - normalizedDist, 1.5));
|
|
||||||
lowerBound = (lowest + y) - erodeDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int j = lowest + y; j > lowerBound; j--) {
|
|
||||||
NativeBlockState fluidState = placer.get(xx, j, zz);
|
|
||||||
if (B.isFluid(fluidState)) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (eroding) {
|
|
||||||
int depth = (lowest + y) - j;
|
if (!rawStructurePiece && B.isVineBlock(data)) {
|
||||||
int totalDepth = (lowest + y) - lowerBound;
|
data = attachVineFaces(placer, data, xx, yy, zz);
|
||||||
double depthRatio = totalDepth > 0 ? (double) depth / totalDepth : 0;
|
}
|
||||||
if (depthRatio > 0.4) {
|
|
||||||
long hash = ((long) (xx * 341873128712L) ^ ((long) j * 132897987541L) ^ ((long) zz * 735791245321L));
|
// Short-circuit order matters for cost only: the mantle read is paid solely
|
||||||
double skipChance = (depthRatio - 0.4) / 0.6;
|
// for vine blocks. Both operands are pure, so the value is unchanged.
|
||||||
if ((Math.abs(hash) % 1000) / 1000.0 < skipChance * 0.7) {
|
boolean wouldReplace = !rawStructurePiece && B.isVineBlock(data) && B.isSolid(placer.get(xx, yy, zz));
|
||||||
continue;
|
boolean place = shouldPlaceObjectBlock(rawStructurePiece, facts.air(), wouldReplace);
|
||||||
|
|
||||||
|
if (data.isCustom() || place) {
|
||||||
|
placer.set(xx, yy, zz, data);
|
||||||
|
if (heightmap != null) {
|
||||||
|
Position2 pos = new Position2(xx, zz);
|
||||||
|
Integer currentHeight = heightmap.get(pos);
|
||||||
|
if (currentHeight == null || currentHeight < yy) {
|
||||||
|
heightmap.put(pos, yy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tile != null) {
|
||||||
|
placer.setTile(xx, yy, zz, tile);
|
||||||
|
}
|
||||||
|
if (markers != null && markers[index] != null) {
|
||||||
|
placer.setData(xx, yy, zz, new MatterMarker(markers[index]));
|
||||||
|
}
|
||||||
|
if (listener != null) {
|
||||||
|
listener.accept(new BlockPosition(xx, yy, zz), data);
|
||||||
|
}
|
||||||
|
if (vacuuming && yy < vacuumLowest) {
|
||||||
|
vacuumLowest = yy;
|
||||||
|
}
|
||||||
|
if (vacuuming && yy > vacuumHighest) {
|
||||||
|
vacuumHighest = yy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (canopy) {
|
||||||
|
placer.endTreeCanopy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grounding != null) {
|
||||||
|
for (TreeGrounding.Extension extension : grounding.extensions()) {
|
||||||
|
for (int rootY = extension.topY(); rootY >= extension.bottomY(); rootY--) {
|
||||||
|
NativeBlockState rootState = normalizePlacedWaterlogging(extension.state(), placer,
|
||||||
|
extension.x(), rootY, extension.z(), waterlogCandidate);
|
||||||
|
placer.set(extension.x(), rootY, extension.z(), rootState);
|
||||||
|
if (listener != null) {
|
||||||
|
listener.accept(new BlockPosition(extension.x(), rootY, extension.z()), rootState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stilting) {
|
||||||
|
IrisStiltSettings settings = stiltSettings;
|
||||||
|
|
||||||
|
double erodeCentroidX = 0;
|
||||||
|
double erodeCentroidZ = 0;
|
||||||
|
double erodeMaxDist = 1;
|
||||||
|
if (eroding) {
|
||||||
|
int centroidCount = 0;
|
||||||
|
for (int index = 0; index < count; index++) {
|
||||||
|
if (bake.layerY(index) == lowest && palette.shouldStilt(bake.paletteIndex(index))) {
|
||||||
|
erodeCentroidX += exact[index * 3];
|
||||||
|
erodeCentroidZ += exact[index * 3 + 2];
|
||||||
|
centroidCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (centroidCount > 0) {
|
||||||
|
erodeCentroidX /= centroidCount;
|
||||||
|
erodeCentroidZ /= centroidCount;
|
||||||
|
}
|
||||||
|
for (int index = 0; index < count; index++) {
|
||||||
|
if (bake.layerY(index) == lowest && palette.shouldStilt(bake.paletteIndex(index))) {
|
||||||
|
double dx = exact[index * 3] - erodeCentroidX;
|
||||||
|
double dz = exact[index * 3 + 2] - erodeCentroidZ;
|
||||||
|
double dist = Math.sqrt(dx * dx + dz * dz);
|
||||||
|
if (dist > erodeMaxDist) {
|
||||||
|
erodeMaxDist = dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int targetLayer = ceilingHang ? topLayer : lowest;
|
||||||
|
for (int index = 0; index < count; index++) {
|
||||||
|
int entry = bake.paletteIndex(index);
|
||||||
|
if (!palette.shouldStilt(entry)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeBlockState d;
|
||||||
|
if (settings != null && settings.getPalette() != null) {
|
||||||
|
d = config.getStiltSettings().getPalette().get(rng, x, y, z, rdata);
|
||||||
|
} else {
|
||||||
|
d = palette.stiltDefault(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bake.layerY(index) != targetLayer)
|
||||||
|
continue;
|
||||||
|
d = palette.rotate(d);
|
||||||
|
|
||||||
|
if (hasEdits) {
|
||||||
|
double exactX = exact[index * 3];
|
||||||
|
double exactY = exact[index * 3 + 1];
|
||||||
|
double exactZ = exact[index * 3 + 2];
|
||||||
|
for (IrisObjectReplace j : config.getEdit()) {
|
||||||
|
if (rng.chance(j.getChance())) {
|
||||||
|
for (NativeBlockState k : j.getFind(rdata)) {
|
||||||
|
if (d == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (j.isExact() ? k.matches(d) : IrisObjectShaping.materialKey(k).equals(IrisObjectShaping.materialKey(d))) {
|
||||||
|
NativeBlockState newData = j.getReplace(rng, exactX + x, exactY + y, exactZ + z, rdata);
|
||||||
|
|
||||||
|
if (IrisObjectShaping.materialKey(newData).equals(IrisObjectShaping.materialKey(d))) {
|
||||||
|
d = BlockDataMergeSupport.merge(d, newData);
|
||||||
|
} else {
|
||||||
|
d = newData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (B.isVineBlock(d)) {
|
if (d == null || !palette.shouldStiltPlaced(d))
|
||||||
d = attachVineFaces(placer, d, xx, j, zz);
|
continue;
|
||||||
|
|
||||||
|
xx = x + bake.offsetX(index);
|
||||||
|
zz = z + bake.offsetZ(index);
|
||||||
|
|
||||||
|
if (warped) {
|
||||||
|
double exactX = exact[index * 3];
|
||||||
|
double exactY = exact[index * 3 + 1];
|
||||||
|
double exactZ = exact[index * 3 + 2];
|
||||||
|
xx += surfaceWarp.fitDouble(-warpHalf, warpHalf, exactX + x, exactY + y, exactZ + z);
|
||||||
|
zz += surfaceWarp.fitDouble(-warpHalf, warpHalf, exactZ + z, exactY + y, exactX + x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (organic) {
|
||||||
|
int startY = targetLayer + y;
|
||||||
|
int maxScan = settings != null ? Math.max(1, settings.getOrganicMaxScan()) : 48;
|
||||||
|
int jitterMax = settings != null ? Math.max(0, settings.getOrganicJitter()) : 3;
|
||||||
|
double scratch = settings != null ? Math.max(0, Math.min(1, settings.getOrganicScratch())) : 0.55;
|
||||||
|
long colHash = ((long) xx * 341873128712L) ^ ((long) zz * 132897987541L);
|
||||||
|
int jitter = jitterMax > 0 ? (int) (Math.abs(colHash) % (jitterMax + 1)) : 0;
|
||||||
|
|
||||||
|
if (ceilingHang) {
|
||||||
|
int scan = 0;
|
||||||
|
int solidY = startY + 1;
|
||||||
|
while (scan < maxScan && !placer.isSolid(xx, solidY, zz)) {
|
||||||
|
solidY++;
|
||||||
|
scan++;
|
||||||
|
}
|
||||||
|
int topBound = (scan < maxScan ? solidY - 1 : startY + Math.min(maxScan, 8)) - jitter;
|
||||||
|
int total = topBound - startY;
|
||||||
|
for (int j = startY; j <= topBound; j++) {
|
||||||
|
if (scratch > 0 && total > 0) {
|
||||||
|
double ratio = (double) (j - startY) / total;
|
||||||
|
if (ratio > (1.0 - scratch)) {
|
||||||
|
long sh = ((long) xx * 341873128712L) ^ ((long) j * 132897987541L) ^ ((long) zz * 735791245321L);
|
||||||
|
double skipChance = (ratio - (1.0 - scratch)) / scratch;
|
||||||
|
if ((Math.abs(sh) % 1000) / 1000.0 < skipChance * 0.7) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
placer.set(xx, j, zz, normalizePlacedWaterlogging(d, placer, xx, j, zz, waterlogCandidate));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int scan = 0;
|
||||||
|
int solidY = startY - 1;
|
||||||
|
while (scan < maxScan && !placer.isSolid(xx, solidY, zz)) {
|
||||||
|
solidY--;
|
||||||
|
scan++;
|
||||||
|
}
|
||||||
|
int bottomBound = (scan < maxScan ? solidY + 1 : startY - Math.min(maxScan, 8)) + jitter;
|
||||||
|
int total = startY - bottomBound;
|
||||||
|
for (int j = startY; j >= bottomBound; j--) {
|
||||||
|
if (scratch > 0 && total > 0) {
|
||||||
|
double ratio = (double) (startY - j) / total;
|
||||||
|
if (ratio > (1.0 - scratch)) {
|
||||||
|
long sh = ((long) xx * 341873128712L) ^ ((long) j * 132897987541L) ^ ((long) zz * 735791245321L);
|
||||||
|
double skipChance = (ratio - (1.0 - scratch)) / scratch;
|
||||||
|
if ((Math.abs(sh) % 1000) / 1000.0 < skipChance * 0.7) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
placer.set(xx, j, zz, normalizePlacedWaterlogging(d, placer, xx, j, zz, waterlogCandidate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int highest = placer.getHighest(xx, zz, self.getLoader(), true);
|
||||||
|
|
||||||
|
int lowerBound = highest - 1;
|
||||||
|
if (settings != null) {
|
||||||
|
lowerBound -= config.getStiltSettings().getOverStilt() - rng.i(0, config.getStiltSettings().getYRand());
|
||||||
|
if (settings.getYMax() != 0)
|
||||||
|
lowerBound -= Math.min(config.getStiltSettings().getYMax() - (lowest + y - highest), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eroding) {
|
||||||
|
double dx = exact[index * 3] - erodeCentroidX;
|
||||||
|
double dz = exact[index * 3 + 2] - erodeCentroidZ;
|
||||||
|
double normalizedDist = Math.sqrt(dx * dx + dz * dz) / erodeMaxDist;
|
||||||
|
normalizedDist = Math.min(normalizedDist, 1.0);
|
||||||
|
int totalDepth = (lowest + y) - lowerBound;
|
||||||
|
int erodeDepth = (int) (totalDepth * Math.pow(1.0 - normalizedDist, 1.5));
|
||||||
|
lowerBound = (lowest + y) - erodeDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = lowest + y; j > lowerBound; j--) {
|
||||||
|
NativeBlockState fluidState = placer.get(xx, j, zz);
|
||||||
|
if (B.isFluid(fluidState)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (eroding) {
|
||||||
|
int depth = (lowest + y) - j;
|
||||||
|
int totalDepth = (lowest + y) - lowerBound;
|
||||||
|
double depthRatio = totalDepth > 0 ? (double) depth / totalDepth : 0;
|
||||||
|
if (depthRatio > 0.4) {
|
||||||
|
long hash = ((long) (xx * 341873128712L) ^ ((long) j * 132897987541L) ^ ((long) zz * 735791245321L));
|
||||||
|
double skipChance = (depthRatio - 0.4) / 0.6;
|
||||||
|
if ((Math.abs(hash) % 1000) / 1000.0 < skipChance * 0.7) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (B.isVineBlock(d)) {
|
||||||
|
d = attachVineFaces(placer, d, xx, j, zz);
|
||||||
|
}
|
||||||
|
placer.set(xx, j, zz, normalizePlacedWaterlogging(d, placer, xx, j, zz, waterlogCandidate));
|
||||||
}
|
}
|
||||||
placer.set(xx, j, zz, normalizePlacedWaterlogging(d, placer, xx, j, zz, waterlogCandidate));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
self.readLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vacuuming && vacuumLowest != Integer.MAX_VALUE && placer.getEngine() != null) {
|
if (vacuuming && vacuumLowest != Integer.MAX_VALUE && placer.getEngine() != null) {
|
||||||
|
|||||||
Reference in new issue
Block a user