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.1.2603001.jar
build/libs/SkysTerrainSmoother-0.1.1.2603001-sources.jar
build/libs/SkysTerrainSmoother-0.1.1.2603001-javadoc.jar
build/libs/SkysTerrainSmoother-0.1.2.2603001.jar
build/libs/SkysTerrainSmoother-0.1.2.2603001-sources.jar
build/libs/SkysTerrainSmoother-0.1.2.2603001-javadoc.jar
build/release/SHA256SUMS
CHANGELOG.md

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

## 0.1.2.2603001

- Add falling gravel slabs for gravel slopes and sand/gravel transitions, with cutting and combining recipes.
- Smooth snow-block and packed-ice terrain with matching pieces.
- Add powder-snow slabs, steps and corners that retain sinking and freezing.
- Give new grass pieces their snowy appearance straight away, including after snow decoration.
- Keep different crafting results on separate recipe-book buttons.


## 0.1.1.2603001

- Gentle surface terraces use full-width slabs at smoothing levels 2 and 3, giving each rise a wider tread.
Expand Down
24 changes: 16 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Sky's Terrain Smoother adds smaller transitions to new Overworld terrain,
including cave floors. It uses slabs, steps and corners from Sky's Grass Slabs
and Sky's Building Pieces, with matching materials for grass, dirt, stone,
sandstone, sand and hardened clay.
sandstone, sand, gravel, hardened clay, snow and packed ice.

## Smoothing levels

Expand Down Expand Up @@ -32,18 +32,26 @@ ceilings stay unchanged. Cave passages need three clear blocks above the floor,
so low tunnels keep their headroom. To see the effect in an existing world,
travel beyond the terrain you've already generated.

New grass pieces pick up the surrounding snow straight away. Snow added later
during world generation is checked again before that new chunk is finished.

## Sand and other materials

Ordinary sand and red sand get their own horizontal slabs. Three matching sand
Ordinary sand, red sand and gravel get their own horizontal slabs. Three matching
blocks in a row make six slabs, and two slabs side by side make a full block.
Like sand blocks, the slabs fall when unsupported. Sand uses slabs at every
smoothing level.
Like their full blocks, the slabs fall when unsupported. These materials use
slabs at every smoothing level.

Mineralogy is supported without another add-on. When it's installed, the
smoother uses its natural rock slabs and the matching steps and corners
included in Building Pieces. Disabled native slab types are skipped.

Ores, gravel, wood and decorative stone aren't smoothing materials.
Powder snow gets its own slabs, steps and corners, so it stays soft and keeps
its sinking and freezing behaviour. Leather boots work on the actual partial
surface. Empty parts of a piece do not freeze you or add powder-snow fog.
See [snow and frozen terrain](docs/FROZEN-TERRAIN.md) for the recipes.

Ores, wood and decorative stone aren't smoothing materials.

For BOP grass and soils, add the
[Terrain Smoother Biomes O' Plenty add-on](https://github.com/MinecraftModDevelopmentMods/SkysTerrainSmoother-BiomesOPlenty-Addon).
Expand All @@ -52,9 +60,9 @@ For BOP grass and soils, add the

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.1.2603001**.
- Sky's Grass Slabs **1.2.1.2603001**.
- Sky's Building Pieces **0.3.10.2603001**.
- Sky's Terrain Smoother **0.1.2.2603001**.

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

Expand Down
14 changes: 12 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ tasks.named('jar', Jar) {
'Implementation-Title': base.archivesName.get(),
'Implementation-Version': project.version,
'Implementation-Vendor': project.mod_authors,
'Built-On-Java': '21',
'Built-On-Java': project.java_version,
'Built-On': "${project.minecraft_version}-${project.forge_version}"
])
}
Expand Down Expand Up @@ -649,6 +649,10 @@ tasks.register('verifyReleaseArtifacts') {
}
ZipFile zip = new ZipFile(new File(libs, releaseFiles.get()[0]))
try {
def manifest = new java.util.jar.Manifest(zip.getInputStream(zip.getEntry('META-INF/MANIFEST.MF')))
if (manifest.mainAttributes.getValue('Built-On-Java') != project.java_version.toString()) {
throw new GradleException('The production manifest has the wrong Java version')
}
Set<String> names = zip.entries().collect { it.name } as Set<String>
['META-INF/mods.toml', 'META-INF/LICENSE', 'META-INF/LICENSE.spdx', 'META-INF/NOTICE',
'zone/moddev/mc/skysterrainsmoother/SkysTerrainSmoother.class'].each { required ->
Expand Down Expand Up @@ -676,7 +680,7 @@ tasks.register('verifyReleaseArtifacts') {
byte[] auditBytes = zip.getInputStream(entry).bytes
if (entry.name.endsWith('.class') &&
(((auditBytes[6] & 255) << 8) | (auditBytes[7] & 255)) != 69) {
throw new GradleException("Non-Java-21 production class: ${entry.name}")
throw new GradleException("Non-Java-25 production class: ${entry.name}")
}
String content = new String(zip.getInputStream(entry).bytes,
StandardCharsets.ISO_8859_1).toLowerCase(Locale.ROOT)
Expand Down Expand Up @@ -1135,3 +1139,9 @@ tasks.register('integrationForwardTest') {
}

tasks.named('check') { dependsOn tasks.named('integrationForwardTest') }

tasks.register('verifyCraftingResources', Exec) {
group = 'verification'
commandLine providers.gradleProperty('pythonExecutable').getOrElse('python'), 'tools/test_powder_snow_resources.py'
}
tasks.named('check') { dependsOn tasks.named('verifyCraftingResources') }
14 changes: 7 additions & 7 deletions docs/BUILDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ Build the exact Grass Slabs and Building Pieces source commits listed in
`gradle/content-dependencies.properties` with their `assemble` tasks first. The build checks both their source commits
and main/development jar checksums.

The source lock records locally qualified Grass Slabs 1.2.0.2603001 and Building Pieces
0.3.9.2603001.
The source lock records Grass Slabs 1.2.1.2603001 and Building Pieces
0.3.10.2603001, with the checksums of their released jars.

The default layout uses sibling checkouts. For another layout, pass
`-PgrassSlabsDirectory=<checkout>` and
Expand Down Expand Up @@ -56,10 +56,10 @@ do not prevent an ordinary build; `prepareMineralogyDevelopmentRun` explains
what is needed before the combined profile can run. The jars are copied, not
modified. Development and packaged runs use the same jars.

If a sibling build has moved on, keep the qualified jars under
`.gradle/qualification-dependencies/` with their original filenames.
The profiles prefer those copies, but still require the locked checksums.
An explicit runtime-jar property takes precedence over either location.
The profiles keep verified copies under `.gradle/qualification-dependencies/`,
keyed by their locked checksums. Later sibling builds can then change without
breaking Eclipse refreshes. An explicit runtime-jar property takes precedence;
it must still match the lock.

New configurations use smoothing level 3. An existing saved level is kept, so
choose level 3 in Mods > Config and restart if that test folder already has a
Expand All @@ -75,7 +75,7 @@ resources there. Neither task opens the manual profile's saved worlds.
The build generates unlock advancements from `src/main/recipe-book/recipes.json`.
The runtime checks compare those entries with the registered recipes and exercise
inventory discovery, crafting-table discovery and recipe-book persistence.
All four sand recipes retain their original IDs.
Sand and gravel recipes retain their existing IDs.

Recipe unlocks have no shared advancement parent. This avoids a long recursive
visibility chain when loading completed progress. Inventory and crafting-table
Expand Down
25 changes: 25 additions & 0 deletions docs/FROZEN-TERRAIN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Snow and frozen terrain

Snow-block and packed-ice slopes use matching Building Pieces slabs, steps and
corners. Powder snow uses separate pieces that keep its sinking, freezing and
leather-boot behaviour. All three smoothing levels work with these materials.
Existing terrain stays unchanged.

Grass transitions also pick up snow from nearby ground and exposed cold slopes.
Snowy pieces cover the visible grass beneath them without changing that grass
block in the saved world. Removing or thawing a piece restores the normal look.

## Crafting powder-snow pieces

- One powder-snow bucket makes two slabs and returns an empty bucket.
- Two slabs placed diagonally make four steps.
- Two steps side by side make four corners.
- Two corners stacked in the crafting grid make one step.
- Two steps stacked in the grid make one slab.
- Two slabs and an empty bucket make a powder-snow bucket.

Matching pieces also combine when placed together. A partial piece cannot fill
a bucket on its own; rebuild it first. Breaking a piece returns that piece.

For solid snow slabs, place two snow blocks diagonally to make four slabs.
The vanilla recipe of three snow blocks in a row still makes snow layers.
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.1.2603001
mod_version=0.1.2.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
22 changes: 11 additions & 11 deletions gradle/content-dependencies.properties
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Exact local content candidates. Publication remains separate.
# Released content dependencies used by this branch.
grass.repository=MinecraftModDevelopmentMods/SkysGrassSlabs
grass.kind=local-candidate
grass.commit=014f8d16f0a5ef83c4cc236d37590afac56a6087
grass.version=1.2.0.2603001
grass.sha256=20AE5DC12C97E8DC1D348282BE50697909E98BBCE8C3BD40E4CF910E7D85F7D9
grass.development.sha256=20AE5DC12C97E8DC1D348282BE50697909E98BBCE8C3BD40E4CF910E7D85F7D9
grass.kind=release
grass.commit=225e5eb176cbfff7389cb2b1c0f581c5b7f62eed
grass.version=1.2.1.2603001
grass.sha256=55E417284B1F5982E88EF4B2B69F8B766A7EA473F3AE30DE9CE3AF8E2D988DFA
grass.development.sha256=55E417284B1F5982E88EF4B2B69F8B766A7EA473F3AE30DE9CE3AF8E2D988DFA
pieces.repository=MinecraftModDevelopmentMods/SkysBuildingPieces
pieces.kind=local-candidate
pieces.commit=c4df80b09d366b293cd3d06b5b92b376b34fafe3
pieces.version=0.3.9.2603001
pieces.sha256=A21755D614D3953D08CE1545153800DA1722A63EEC330BC868C8C1A7DF76D298
pieces.development.sha256=A21755D614D3953D08CE1545153800DA1722A63EEC330BC868C8C1A7DF76D298
pieces.kind=release
pieces.commit=80865bc1ce045bd3f271f35b47415d9b3acbd99e
pieces.version=0.3.10.2603001
pieces.sha256=103D5B1DA1F123407DDEB453840AAE652F6F798D8CFB8E3ACCEA1F6FCB1E686D
pieces.development.sha256=103D5B1DA1F123407DDEB453840AAE652F6F798D8CFB8E3ACCEA1F6FCB1E686D
16 changes: 15 additions & 1 deletion gradle/mineralogy-runs.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,15 @@ import java.nio.file.Files
// These are optional development mods, never compile or production dependencies.
def runtimeLock = new Properties()
file('gradle/mineralogy-runtime.properties').withInputStream { runtimeLock.load(it) }
def retainedRuntimeJars = ['mineralogy', 'orespawn'].collectEntries { mod ->
File siblingJar = file(runtimeLock.getProperty(mod + '.development.path'))
String checksum = runtimeLock.getProperty(mod + '.development.sha256')
[(mod): file(".gradle/qualification-dependencies/${checksum}/${siblingJar.name}")]
}

def runtimeSources = ['mineralogy', 'orespawn'].collectEntries { mod ->
File siblingJar = file(runtimeLock.getProperty(mod + '.development.path'))
File retainedJar = file('.gradle/qualification-dependencies/' + siblingJar.name)
File retainedJar = retainedRuntimeJars[mod]
// Sibling builds can change during other work. A retained copy still has to
// pass the same checksum check before it can enter a development profile.
[(mod): file(providers.gradleProperty(mod + 'RuntimeJar')
Expand Down Expand Up @@ -65,6 +71,14 @@ def installRuntimeMods = {
throw new GradleException("The ordinary Mineralogy profile must not contain test mods: ${candidate}")
}
}
runtimeSources.each { mod, source ->
File retained = retainedRuntimeJars[mod]
if (source.canonicalFile != retained.canonicalFile) {
// Keep this verified build when another project rebuilds the sibling jar.
Files.createDirectories(retained.parentFile.toPath())
Files.copy(source.toPath(), retained.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING)
}
}
// Copy only the declared jars. Never sync or clear a player's mods, config or saves.
copy {
from runtimeSources.values(), extraMods
Expand Down
2 changes: 1 addition & 1 deletion gradle/release.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ tasks.register('verifyReleaseBundleContract') {
group = 'verification'
dependsOn tasks.named('writeReleaseChecksums')
doLast {
File bundle = layout.buildDirectory.dir('release-bundle-test').get().asFile
File bundle = layout.buildDirectory.dir("release-bundle-test/${project.version}").get().asFile
List<String> filenames = releaseFiles.get().collect { it.toString() }
copy {
from layout.buildDirectory.dir('libs')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ static int run(ServerLevel world) {
checks++;

for (BlockState source : new BlockState[] { Blocks.COAL_ORE.defaultBlockState(),
Blocks.GRAVEL.defaultBlockState(), Blocks.OAK_PLANKS.defaultBlockState(), Blocks.INFESTED_STONE.defaultBlockState(),
Blocks.COAL_BLOCK.defaultBlockState(), Blocks.OAK_PLANKS.defaultBlockState(), Blocks.INFESTED_STONE.defaultBlockState(),
Blocks.CHEST.defaultBlockState() }) {
fixture(world, floor, 6);
world.setBlock(floor, source, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public void tick(TickEvent.ClientTickEvent.Post event) {
return;
}
RecipePreviewModels.check(minecraft, recipeDisplays);
PowderSnowClientProbe.run(minecraft);
checkBiomeTints(minecraft);
zone.moddev.mc.skysterrainsmoother.SkysTerrainSmoother.logger.info("TERRAIN_SMOOTHER_CLIENT_WORLD_PASS");
minecraft.stop();
Expand All @@ -57,7 +58,8 @@ public void tick(TickEvent.ClientTickEvent.Post event) {

for (net.minecraft.world.level.block.Block block : new net.minecraft.world.level.block.Block[] {
zone.moddev.mc.skysterrainsmoother.content.SandContent.slab,
zone.moddev.mc.skysterrainsmoother.content.SandContent.redSlab }) {
zone.moddev.mc.skysterrainsmoother.content.SandContent.redSlab,
zone.moddev.mc.skysterrainsmoother.content.SandContent.gravelSlab }) {
for (net.minecraft.world.level.block.state.BlockState state : block.getStateDefinition().getPossibleStates()) {
checkModel(minecraft, state);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,82 @@ static int run(ServerLevel world) {
checks += checkSupport(world, floor, corner, false, true);
}
checks += shrinkingSlab(world, material);
checks += snowyTransitions(world, material);
zone.moddev.mc.skysterrainsmoother.SkysTerrainSmoother.logger.info("TERRAIN_FINE_GRASS_SUPPORT_PASS checks=" + checks);
return checks;
}

private static int snowyTransitions(ServerLevel world, TerrainMaterial material) {
BlockPos floor = new BlockPos(4207, 210, 4200);
BlockPos target = floor.above();
BlockPos lowerSnow = target.east();
LevelChunk owner = world.getChunkAt(floor);
BlockState snow = Blocks.SNOW.defaultBlockState();
int checks = 0;

for (int level = 1; level <= 3; level++) {
BlockPos higher = level == 3 ? target.north().west() : target.north();
BlockState dry = switch (level) {
case 1 -> material.slab;
case 2 -> material.step(0);
default -> material.corner(0);
};
BlockState snowy = dry.setValue(GrassBlock.SNOWY, true);

for (boolean lateSnow : new boolean[] { false, true }) {
prepare(world, floor);
world.setBlock(higher, Blocks.GRASS_BLOCK.defaultBlockState(), 2);
LevelChunk snowChunk = world.getChunkAt(lowerSnow);
if (!lateSnow) {
world.setBlock(higher.above(), snow, 2);
snowChunk.setBlockState(lowerSnow, snow, 0);
}

int loaded = world.getChunkSource().getLoadedChunksCount();
SkysTerrainSmoother.engine.smooth(world, owner, level);
RuntimeProbe.require(world.getBlockState(target).equals(lateSnow ? dry : snowy),
"generated grass has the right snow appearance before ticking at level " + level
+ " / snow placed later: " + lateSnow);

if (lateSnow) {
// Match decoration writes that haven't notified the piece of its new neighbour.
snowChunk.setBlockState(lowerSnow, snow, 0);
world.getChunkAt(higher).setBlockState(higher.above(), snow, 0);
world.getChunkAt(higher).setBlockState(higher,
Blocks.GRASS_BLOCK.defaultBlockState().setValue(GrassBlock.SNOWY, true), 0);
owner.setBlockState(floor, Blocks.DIAMOND_ORE.defaultBlockState(), 0);
RuntimeProbe.require(world.getBlockState(target).equals(dry),
"late snow test starts with the generated piece still green");
ProbeWorld.repair(world, floor);
RuntimeProbe.require(world.getBlockState(target).equals(snowy),
"late decoration gives the same piece its snow cap at level " + level);
RuntimeProbe.require(world.getBlockState(floor).is(Blocks.DIAMOND_ORE),
"refreshing the snow appearance preserves an ore placed under the piece");
checks += 3;
}

RuntimeProbe.require(world.getBlockState(lowerSnow).equals(snow)
&& world.getBlockState(higher.above()).equals(snow),
"the grass cap uses the snow around it without removing either layer");
RuntimeProbe.require(world.getChunkSource().getLoadedChunksCount() == loaded,
"snow appearance reads never load chunks");
world.setBlock(lowerSnow, Blocks.AIR.defaultBlockState(), 3);
if (level != 3) {
RuntimeProbe.require(world.getBlockState(target).getValue(GrassBlock.SNOWY),
"the higher terrace still supplies snow after the lower snow is removed");
checks++;
}
world.setBlock(higher.above(), Blocks.AIR.defaultBlockState(), 3);
world.getBlockState(target).randomTick(world, target, RandomSource.create(1));
RuntimeProbe.require(world.getBlockState(target).equals(dry),
"removing the surrounding snow restores the green piece at level " + level);
checks += 4;
}
}
SkysTerrainSmoother.logger.info("TERRAIN_SNOWY_TRANSITIONS_PASS checks={}", checks);
return checks;
}

private static int checkSupport(ServerLevel world, BlockPos floor, BlockState piece,
boolean fullCover, boolean snow) {
RuntimeProbe.require(world.getBlockState(floor).getBlock() == (fullCover ? Blocks.DIRT : Blocks.GRASS_BLOCK),
Expand Down
Loading
Loading