Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,9 @@ jobs:
if-no-files-found: error
retention-days: 30
path: |
build/libs/SkysTerrainSmoother-0.1.0.2603001.jar
build/libs/SkysTerrainSmoother-0.1.0.2603001-sources.jar
build/libs/SkysTerrainSmoother-0.1.0.2603001-javadoc.jar
build/libs/SkysTerrainSmoother-0.1.1.2603001.jar
build/libs/SkysTerrainSmoother-0.1.1.2603001-sources.jar
build/libs/SkysTerrainSmoother-0.1.1.2603001-javadoc.jar
build/release/SHA256SUMS
CHANGELOG.md

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.1.1.2603001

- Gentle surface terraces use full-width slabs at smoothing levels 2 and 3, giving each rise a wider tread.

## 0.1.0.2603001

- Updated Terrain Smoother for Minecraft 26.3 on Forge.
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ Set `worldgen.smoothingLevel` in `config/skysterrainsmoother-common.toml`:
3. **Slabs, steps and corners** - also uses pieces covering a quarter of a
slab's footprint.

Every smoothing piece is half a block tall. If a material doesn't have steps
or corners, it uses its slab at higher levels wherever a slab fits safely.
Every smoothing piece is half a block tall. Gentle surface terraces use a
full-width slab when there is room on both sides of a one-block rise. If a
material doesn't have steps or corners, it uses its slab at higher levels
wherever a slab fits safely.

Narrow mountain ledges keep their downhill edge at its original height, rather
than gaining a row of slabs that recreates a full-block step. Wider terraces
Expand Down Expand Up @@ -52,7 +54,7 @@ For **Minecraft 26.3**, use **Forge 66.0.2** and **Java 25**, with:

- Sky's Grass Slabs **1.2.0.2603001**.
- Sky's Building Pieces **0.3.9.2603001**.
- Sky's Terrain Smoother **0.1.0.2603001**.
- Sky's Terrain Smoother **0.1.1.2603001**.

Install all three main jars on both the client and server.

Expand Down
11 changes: 11 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,11 @@ eclipse {
downloadSources = true
downloadJavadoc = true
file.whenMerged { classpath ->
// Forge supplies MixinExtras at launch. Its common jar is only needed for compilation.
classpath.entries.removeAll { entry ->
entry instanceof org.gradle.plugins.ide.eclipse.model.Library &&
file(entry.path).name.startsWith('mixinextras-common-')
}
// Eclipse copies these files on every build. Use Gradle's expanded metadata, never the template.
def resources = classpath.entries.findAll {
it instanceof org.gradle.plugins.ide.eclipse.model.SourceFolder &&
Expand Down Expand Up @@ -889,6 +894,12 @@ tasks.register('verifyEclipseProductionClasspath') {
}
File metadata = file('bin/main/META-INF/mods.toml')
def eclipseClasspath = new groovy.xml.XmlSlurper().parse(file('.classpath'))
def mixinExtrasLibraries = eclipseClasspath.classpathentry.findAll {
it.@kind == 'lib' && file(it.@path.text()).name.startsWith('mixinextras-')
}.collect { file(it.@path.text()).name }
if (mixinExtrasLibraries.size() != 1 || !mixinExtrasLibraries.first().startsWith('mixinextras-forge-')) {
throw new GradleException('Eclipse must use the MixinExtras runtime supplied by Forge')
}
def piecesLibraries = eclipseClasspath.classpathentry.findAll {
it.@kind == 'lib' && it.@path.text().tokenize('/\\').last().startsWith('SkysBuildingPieces-')
}*.@path*.text().collect { file(it).canonicalFile }
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ release_enabled=true
curseforge_project_id=1768953
mod_name=Sky's Terrain Smoother
mod_license=LGPL-2.1-only
mod_version=0.1.0.2603001
mod_version=0.1.1.2603001
mod_group=zone.moddev.mc.skysterrainsmoother
mod_authors=SkyBlade1978
mod_description=Smooths new Overworld surfaces with existing slabs, steps and corners. Optional Mineralogy support.
Expand Down
3 changes: 2 additions & 1 deletion gradle/development-log4j2.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] [%logger]: %msg%n"/>
</Console>
<File name="DevelopmentLog" fileName="logs/latest.log" append="false">
<!-- Forge opens latest.log when it takes over logging from bootstrap. -->
<File name="DevelopmentLog" fileName="logs/development-bootstrap.log" append="false">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level] [%logger]: %msg%n"/>
</File>
</Appenders>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ static int run(ServerLevel world) {
BlockPos upperCap = origin.relative(uphill).above(2);
RuntimeProbe.require(MaterialCatalogue.instance().piece(world.getBlockState(lowCap)) == material,
"the foot of a steep slope still receives its matching transition");
BlockState step = material.step(direction(uphill));
if (level >= 2 && step != null) {
RuntimeProbe.require(world.getBlockState(lowCap).equals(step),
"the foot of consecutive rises keeps its uphill step");
checks++;
}
requireOpenDownhillEdge(world, upperCap, uphill.getOpposite(),
"consecutive mountain ledges, level " + level + " / " + uphill);
RuntimeProbe.require(world.getChunkSource().getLoadedChunksCount() == loadedBefore,
Expand All @@ -48,9 +54,11 @@ static int run(ServerLevel world) {

prepare(world, origin, uphill, source, true, roofed);
SkysTerrainSmoother.engine.smooth(world, owner, level);
RuntimeProbe.require(MaterialCatalogue.instance()
.piece(world.getBlockState(origin.relative(uphill).above())) == material,
"the bottom of a wider terrace is smoothed");
BlockPos terraceCap = origin.relative(uphill).above();
BlockState expected = roofed && level >= 2 && step != null ? step : material.slab;
RuntimeProbe.require(world.getBlockState(terraceCap).equals(expected),
"gentle surface terraces use a slab and cave terraces keep their existing shape: "
+ source + " / " + uphill + " / level " + level + " / roofed " + roofed);
RuntimeProbe.require(MaterialCatalogue.instance()
.piece(world.getBlockState(origin.relative(uphill, 3).above(2))) == material,
"a wider upper terrace retains room for its own transition");
Expand All @@ -75,7 +83,7 @@ static int run(ServerLevel world) {
checks += 2;
}

checks += borders(world) + lateLowerFloor(world, origin);
checks += borders(world) + gentleBorders(world) + lateLowerFloor(world, origin);
zone.moddev.mc.skysterrainsmoother.SkysTerrainSmoother.logger.info("TERRAIN_SMOOTHER_TRANSITIONS_PASS checks=" + checks);
return checks;
}
Expand Down Expand Up @@ -117,6 +125,40 @@ private static int borders(ServerLevel world) {
return checks;
}

private static int gentleBorders(ServerLevel world) {
TerrainMaterial material = MaterialCatalogue.instance().source(Blocks.STONE.defaultBlockState());
int checks = 0;
for (Direction uphill : new Direction[] { Direction.EAST, Direction.SOUTH }) {
BlockPos origin = uphill == Direction.EAST ? new BlockPos(3614, 210, 3656)
: new BlockPos(3656, 210, 3614);
BlockPos low = origin.relative(uphill);
LevelChunk lowerOwner = world.getChunkAt(low);
LevelChunk upperOwner = world.getChunkAt(low.relative(uphill));
for (boolean reverse : new boolean[] { false, true }) {
prepare(world, origin, uphill, Blocks.STONE.defaultBlockState(), true, false);
int loadedBefore = world.getChunkSource().getLoadedChunksCount();
SkysTerrainSmoother.engine.smooth(world, reverse ? upperOwner : lowerOwner, 3);
SkysTerrainSmoother.engine.smooth(world, reverse ? lowerOwner : upperOwner, 3);
RuntimeProbe.require(world.getBlockState(low.above()).equals(material.slab),
"the same gentle slab fits in both chunk orders: " + uphill);
RuntimeProbe.require(world.getChunkSource().getLoadedChunksCount() == loadedBefore,
"terrace lookahead never loads a chunk");
checks += 2;
}
}
return checks;
}

private static int direction(Direction uphill) {
return switch (uphill) {
case NORTH -> 0;
case EAST -> 1;
case SOUTH -> 2;
case WEST -> 3;
default -> throw new IllegalArgumentException("Expected horizontal uphill direction");
};
}

private static int lateLowerFloor(ServerLevel world, BlockPos origin) {
LevelChunk owner = world.getChunkAt(origin);
int checks = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
public final class SkysTerrainSmoother {
public static final String ID = "skysterrainsmoother";
public static final String NAME = "Sky's Terrain Smoother";
public static final String VERSION = "0.1.0.2603001";
public static final String VERSION = "0.1.1.2603001";
public static final Logger logger = LogManager.getLogger();
public static final SmoothingEngine engine = new SmoothingEngine(MaterialCatalogue.instance());
public static boolean enabled = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ private boolean validate(LevelAccessor world, int chunkX, int chunkZ, List<Featu
}
}

result = transitionResult(world, placement.pos.below(), replacement,
Footprint.mask(cardinal, diagonal, placement.level), placement.level);
result = transitionResult(world, owner, placement.pos.below(), replacement,
cardinal, diagonal, placement.level, placement.cave);
}

repairs.put(placement, result == null ? Blocks.AIR.defaultBlockState() : result);
Expand Down Expand Up @@ -397,8 +397,8 @@ private int smooth(LevelAccessor world, ChunkAccess owner, int level, boolean af
}
}

BlockState result = transitionResult(world, support, material,
Footprint.mask(cardinal, diagonal, level), level);
BlockState result = transitionResult(world, owner, support, material,
cardinal, diagonal, level, cave);

if (result != null) {
buffer.add(localBlockX | localBlockZ << 4 | (floorY + 1) << 8, result, material, cave, above);
Expand Down Expand Up @@ -477,13 +477,36 @@ private static boolean sameState(BlockState first, BlockState second) {
return first.equals(second);
}

private BlockState transitionResult(LevelAccessor world, BlockPos support, TerrainMaterial material, int mask, int level) {
private BlockState transitionResult(LevelAccessor world, ChunkAccess owner, BlockPos support,
TerrainMaterial material, int cardinal, int diagonal, int level, boolean cave) {
int mask = Footprint.mask(cardinal, diagonal, level);
if (mask == 0) {
return null;
}

int lowerEdges = lowerEdges(world, support);
int safeMask = Footprint.trimDownhillEdges(mask, lowerEdges);
BlockState result = catalogue.result(material, safeMask, level);

// Missing finer shapes fall back to slabs. Check the real result, not just the requested footprint.
return result != null && (touchingEdges(result) & lowerEdges) == 0 ? result : null;
// Missing finer shapes fall back to slabs. Check the actual piece against the downhill edges.
if (result == null || (touchingEdges(result) & lowerEdges) != 0) {
return null;
}

if (!cave && level >= 2 && lowerEdges == 0 && result != material.slab && Integer.bitCount(cardinal) == 1) {
int uphill = Integer.numberOfTrailingZeros(cardinal);
if (safeMask == Footprint.EDGES[uphill]) {
// A full slab gives a broader tread where both sides of a one-block rise have room.
int dx = DX[uphill];
int dz = DZ[uphill];
if (hasTerrainFloor(world, owner, support.offset(-dx, 0, -dz))
&& hasTerrainFloor(world, owner, support.offset(2 * dx, 1, 2 * dz))) {
return material.slab;
}
}
}

return result;
}

private static BlockState supportAfterPlacement(TerrainMaterial material, BlockState piece) {
Expand Down Expand Up @@ -586,23 +609,27 @@ private boolean higher(LevelAccessor world, ChunkAccess owner, int localX, int l
int worldX = (owner.getPos().x() << 4) + localX;
int worldZ = (owner.getPos().z() << 4) + localZ;

ChunkAccess neighbour = loaded(world, worldX >> 4, worldZ >> 4);
return hasTerrainFloor(world, owner, new BlockPos(worldX, lowerY + 1, worldZ));
}

private boolean hasTerrainFloor(LevelAccessor world, ChunkAccess owner, BlockPos floor) {
int chunkX = floor.getX() >> 4;
int chunkZ = floor.getZ() >> 4;
ChunkAccess neighbour = chunkX == owner.getPos().x() && chunkZ == owner.getPos().z()
? owner : loaded(world, chunkX, chunkZ);
if (neighbour == null) {
return false;
}

// Compare logical floors at this height, including existing caps over BOP's native soil.
BlockPos floor = new BlockPos(worldX, lowerY + 1, worldZ);
// Read the same logical height before and after a neighbouring column is capped.
BlockState state = neighbour.getBlockState(floor);
BlockState above = neighbour.getBlockState(floor.above());

TerrainMaterial source = catalogue.source(state);
TerrainMaterial cap = catalogue.piece(above);

if (cap != null) {
return source != null || catalogue.source(state) == cap
|| cap.supportAfterPlacement != null && state.equals(cap.supportAfterPlacement)
return source != null || cap.supportAfterPlacement != null && state.equals(cap.supportAfterPlacement)
|| oreSupport(state);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ProjectContractTest {
void targetMetadataAndToolchainMatchTheNativeRuntime() throws Exception {
Properties metadata = new Properties();
metadata.load(new java.io.StringReader(text(Paths.get("gradle.properties"))));
assertEquals("0.1.0.2603001", metadata.getProperty("mod_version"));
assertEquals("0.1.1.2603001", metadata.getProperty("mod_version"));
assertEquals(metadata.getProperty("mod_version"), SkysTerrainSmoother.VERSION);
assertEquals("26.3", metadata.getProperty("minecraft_version"));
assertEquals("66.0.2", metadata.getProperty("forge_version"));
Expand Down
Loading