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 @@ -56,9 +56,9 @@ jobs:
if-no-files-found: error
retention-days: 30
path: |
build/libs/SkysGrassSlabs-1.1.0.112021.jar
build/libs/SkysGrassSlabs-1.1.0.112021-sources.jar
build/libs/SkysGrassSlabs-1.1.0.112021-javadoc.jar
build/libs/SkysGrassSlabs-1.1.1.112021.jar
build/libs/SkysGrassSlabs-1.1.1.112021-sources.jar
build/libs/SkysGrassSlabs-1.1.1.112021-javadoc.jar
build/release/SHA256SUMS
CHANGELOG.md

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

## 1.1.1.112021 - Minecraft 1.12.2

- Added a Config screen with help for each setting. Save your changes and
restart Minecraft to apply them.
- Fixed gaps in new grass slopes where neighbouring decoration had already
placed grass or flowers. Trees, crops and occupied blocks stay protected.
- Kept exposed grass beneath Building Pieces steps and corners grassy.
Full-cover slabs keep their usual dirt support.
- Replaced the warnings for intentional legacy slab registrations with one
clear recovery notice. Saved IDs and recovery rules are unchanged.
- Fixed the Mods page, Config button and launch variables after Eclipse rebuilds.

## 1.1.0.112021 - Minecraft 1.12.2

- Added optional shared grass behaviour with Sky's Building Pieces.
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ turf recipe in the recipe book.
## Smoother terrain

By default, grass slabs soften suitable one-block slopes in new Overworld
chunks. Existing terrain stays as it is. You can turn generation off in
chunks. Existing terrain stays as it is. To turn generation off, select the mod
in **Mods → Config**, save your choice and restart Minecraft. You can also edit
`config/skysgrassslabs.cfg`; see [configuration](docs/CONFIGURATION.md).

The mod works on its own. If you install
Expand Down
82 changes: 78 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ dependencies {
testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.10.2'
}

apply from: 'gradle/runtime-log-checks.gradle'

def integrationTestClasses = layout.buildDirectory.dir('integration-test-mod/classes')
def compileIntegrationTestMod = tasks.register('compileIntegrationTestMod', JavaCompile) {
group = 'verification'
Expand Down Expand Up @@ -278,11 +280,16 @@ def prepareIntegrationTest = tasks.register('prepareIntegrationTest') {
into mods
}
new File(runDirectory, 'eula.txt').setText('eula=true\n', 'UTF-8')
['banned-players.json', 'banned-ips.json', 'ops.json', 'whitelist.json'].each { name ->
new File(runDirectory, name).setText('[]\n', 'UTF-8')
}
new File(runDirectory, 'server.properties').setText('''\
level-name=world
level-seed=skys-grass-slabs-integration
level-type=default
online-mode=false
server-ip=127.0.0.1
server-port=0
generate-structures=false
spawn-animals=false
spawn-monsters=false
Expand All @@ -298,6 +305,7 @@ def createIntegrationProcess = { String phase, Object dependency ->
dependsOn dependency
workingDir integrationRunDirectory
systemProperty 'skysgrassslabs.integrationPhase', phase.toLowerCase(Locale.ROOT)
systemProperty 'log4j.configurationFile', file('src/integrationTest/resources/log4j2.xml').toURI().toString()
configureFromForgeRun(delegate, 'runServer')
doLast {
File marker = new File(integrationRunDirectory.get().asFile,
Expand All @@ -310,6 +318,14 @@ def createIntegrationProcess = { String phase, Object dependency ->
if (values.getProperty("${phase.toLowerCase(Locale.ROOT)}_complete") != 'true') {
throw new GradleException("${phase} integration did not complete: ${marker}")
}
File log = new File(integrationRunDirectory.get().asFile, 'logs/latest.log')
File archived = layout.buildDirectory.file("integration-logs/${phase.toLowerCase(Locale.ROOT)}.log").get().asFile
archived.parentFile.mkdirs()
archived.bytes = log.bytes
project.ext.auditRuntimeLog(archived, [
kind: 'development', side: 'server',
directory: integrationRunDirectory.get().asFile
])
}
}
}
Expand Down Expand Up @@ -389,6 +405,7 @@ def clientIntegrationProcess = tasks.register('clientIntegrationProcess', JavaEx
dependsOn prepareClientIntegrationTest
workingDir clientIntegrationRunDirectory
systemProperty 'skysgrassslabs.clientProbe', 'true'
systemProperty 'log4j.configurationFile', file('src/integrationTest/resources/log4j2.xml').toURI().toString()
configureFromForgeRun(delegate, 'runClient')
doLast {
File runDirectory = clientIntegrationRunDirectory.get().asFile
Expand All @@ -398,12 +415,14 @@ def clientIntegrationProcess = tasks.register('clientIntegrationProcess', JavaEx
}
Properties values = new Properties()
marker.withInputStream { values.load(it) }
['models_verified', 'recipe_book_verified', 'first_world_rendered',
['models_verified', 'recipe_book_verified', 'config_screen_verified', 'first_world_rendered',
'reload_rendered'].each { key ->
if (values.getProperty(key) != 'true') {
throw new GradleException("Development client failed ${key}: ${values}")
}
}
project.ext.auditRuntimeLog(new File(runDirectory, 'logs/latest.log'),
[kind: 'development', side: 'client', directory: runDirectory])
}
}

Expand Down Expand Up @@ -478,6 +497,9 @@ def preparePackagedRuntime = { String taskName, String directoryName, Object fix
outputs.dir(runDirectory)
outputs.upToDateWhen { false }
doLast {
if (fixtureArchive != null) {
project.ext.verifyRuntimeFixture(file(fixtureArchive))
}
project.delete(runDirectory)
project.copy {
from packagedServerBase
Expand All @@ -497,13 +519,16 @@ def preparePackagedRuntime = { String taskName, String directoryName, Object fix
from zipTree(file(fixtureArchive))
into new File(runDirectory, 'world')
}
project.ext.writeRuntimeFixtureContext(runDirectory, file(fixtureArchive))
}
new File(runDirectory, 'eula.txt').setText('eula=true\n', 'UTF-8')
new File(runDirectory, 'server.properties').setText('''\
level-name=world
level-seed=skys-grass-slabs-packaged
level-type=default
online-mode=false
server-ip=127.0.0.1
server-port=0
generate-structures=false
spawn-animals=false
spawn-monsters=false
Expand All @@ -522,12 +547,20 @@ def createPackagedServerProcess = { String taskName, String phase, String direct
File runDirectory = layout.buildDirectory.dir(directoryName).get().asFile
workingDir runDirectory
doFirst {
['banned-players.json', 'banned-ips.json', 'ops.json', 'whitelist.json'].each { name ->
File list = new File(runDirectory, name)
if (!list.exists()) {
list.setText('[]\n', 'UTF-8')
}
}
List<String> command = [
java8Launcher.get().executablePath.asFile.absolutePath,
'-Djava.net.preferIPv4Stack=true',
'-Dforge.logging.console.level=info',
"-Dskysgrassslabs.integrationPhase=${phase}"
]
command.add('-Dlog4j.configurationFile=' +
file('src/integrationTest/resources/log4j2.xml').toURI().toString())
if (phase.startsWith('forward-sylvester')) {
command.addAll(['-Xms1G', '-Xmx8G'])
}
Expand All @@ -549,6 +582,12 @@ def createPackagedServerProcess = { String taskName, String phase, String direct
if (values.getProperty(key) != 'true') {
throw new GradleException("Packaged ${phase} did not complete: ${marker}")
}
File log = new File(runDirectory, 'logs/latest.log')
File archived = layout.buildDirectory.file("runtime-logs/${taskName}.log").get().asFile
archived.parentFile.mkdirs()
archived.bytes = log.bytes
project.ext.auditRuntimeLog(archived,
[kind: 'packaged', side: 'server', directory: runDirectory])
}
}
}
Expand Down Expand Up @@ -632,6 +671,8 @@ level-name=world
level-seed=skys-grass-slabs-orespawn-mineralogy
level-type=default
online-mode=false
server-ip=127.0.0.1
server-port=0
generate-structures=false
spawn-animals=false
spawn-monsters=false
Expand Down Expand Up @@ -766,6 +807,8 @@ def prepareSylvesterForwardUpgradeTest = tasks.register(
new File(runDirectory, 'server.properties').setText('''\
level-name=world
online-mode=false
server-ip=127.0.0.1
server-port=0
generate-structures=false
spawn-animals=false
spawn-monsters=false
Expand Down Expand Up @@ -944,6 +987,7 @@ def packagedClientProcess = tasks.register('packagedClientProcess', Exec) {
]
commandLine java8Launcher.get().executablePath.asFile.absolutePath,
'-Xms512m', '-Xmx2g', '-Dskysgrassslabs.clientProbe=true',
'-Dlog4j.configurationFile=' + file('src/integrationTest/resources/log4j2.xml').toURI().toString(),
"-Djava.library.path=${natives.absolutePath}",
'-Dminecraft.launcher.brand=skysgrassslabs-validation',
'-Dminecraft.launcher.version=1',
Expand All @@ -965,13 +1009,15 @@ tasks.register('packagedClientIntegrationTest') {
}
Properties values = new Properties()
marker.withInputStream { values.load(it) }
['models_verified', 'recipe_book_verified', 'first_world_rendered',
['models_verified', 'recipe_book_verified', 'config_screen_verified', 'first_world_rendered',
'reload_rendered'].each { key ->
if (values.getProperty(key) != 'true') {
throw new GradleException("Packaged client failed ${key}: ${values}")
}
}
verifyPackagedRun(runDirectory, 'Packaged client')
project.ext.auditRuntimeLog(new File(runDirectory, 'logs/latest.log'),
[kind: 'packaged', side: 'client', directory: runDirectory])
}
}

Expand Down Expand Up @@ -1081,6 +1127,7 @@ tasks.register('writeReleaseChecksums') {
group = 'verification'
dependsOn tasks.named('verifyReleaseArtifacts')
def output = layout.buildDirectory.file('release/SHA256SUMS')
inputs.files(releaseFiles.map { names -> names.collect { layout.buildDirectory.file("libs/${it}") } })
outputs.file(output)
doLast {
File libs = layout.buildDirectory.dir('libs').get().asFile
Expand Down Expand Up @@ -1194,8 +1241,27 @@ idea {
module { downloadSources = true; downloadJavadoc = true }
}
eclipse {
classpath { downloadSources = true; downloadJavadoc = true }
synchronizationTasks 'genEclipseRuns'
classpath {
downloadSources = true
downloadJavadoc = true
file.whenMerged { classpath ->
// 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 &&
it.path in ['src/main/resources', 'build/resources/main']
}
// Keep one entry when Gradle merges an existing Eclipse classpath.
resources.drop(1).each { classpath.entries.remove(it) }
resources.take(1).each { entry ->
entry.path = 'build/resources/main'
}
}
}
synchronizationTasks 'processResources', 'genEclipseRuns'
}

tasks.named('eclipseClasspath') {
dependsOn tasks.named('processResources')
}

def prepareEclipseResources = tasks.register('prepareEclipseResources') {
Expand Down Expand Up @@ -1239,6 +1305,7 @@ tasks.register('isolateEclipseProductionRuns') {
include 'run*.launch'
}.files.each { File launch ->
String contents = launch.getText('UTF-8')
contents = contents.replace('${MC_VERSION}', project.minecraft_version.toString())
contents = contents.replace(
'key="MC_VERSION" value="${MC_VERSION}"',
"key=\"MC_VERSION\" value=\"${minecraft_version}\"")
Expand Down Expand Up @@ -1288,13 +1355,20 @@ tasks.register('verifyEclipseProductionClasspath') {
throw new GradleException('Eclipse Buildship uses the wrong Gradle home')
}
File metadata = file('bin/main/mcmod.info')
def eclipseClasspath = new groovy.xml.XmlSlurper().parse(file('.classpath'))
def resourceFolders = eclipseClasspath.classpathentry.findAll { it.@kind == 'src' }*.@path*.text()
if (resourceFolders.contains('src/main/resources') ||
resourceFolders.count { it == 'build/resources/main' } != 1) {
throw new GradleException('Eclipse must copy processed resources, not overwrite them with templates')
}
def parsed = new JsonSlurper().parseText(metadata.getText('UTF-8'))
if (parsed[0].version != project.mod_version || parsed[0].mcversion != project.minecraft_version) {
throw new GradleException('Eclipse output contains incorrect mcmod.info')
}
['runClient.launch', 'runServer.launch'].each { name ->
String contents = file(name).getText('UTF-8').replace('\\', '/')
if (!contents.contains('org.eclipse.jdt.launching.ATTR_EXCLUDE_TEST_CODE') ||
contents.contains('${MC_VERSION}') ||
contents.contains('src/test') || contents.contains('bin/test') || contents.contains('junit-')) {
throw new GradleException("${name} exposes test code or dependencies")
}
Expand Down
37 changes: 37 additions & 0 deletions docs/BUILDING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Building and checking this branch

Use the Gradle wrapper with Java 17 and set `SGS_JAVA8_HOME` to the Java 8
installation used for Minecraft. Run these commands from the checkout:

```text
gradlew clean check build javadoc genEclipseRuns verifyEclipseProductionClasspath verifyReleaseArtifacts writeReleaseChecksums
```

The checks include unit tests and disposable fresh/reload servers. Test mods
are not included in the main jar or ordinary Eclipse launches.

## Runtime logs

Each integration server log is saved and checked before the next process can
overwrite it. An unexpected warning, error, crash, missing recovery notice or
remaining legacy-prefix warning fails the check.

To check another captured verification log:

```text
gradlew verifyRuntimeLog -PruntimeLog=<log> -PruntimeDirectory=<disposable game folder> -PruntimeKind=packaged -PruntimeSide=server
```

Use `development` only for a mapped development launch and `client` for a
client log. Offline-server warnings are accepted only when that test folder
is configured for loopback networking on an automatically allocated port.
Use `-PrecoveryMessage=false` when the original legacy mod is installed.

The development allowances cover specific old-Forge launcher and class-scanner
diagnostics, with their expected file names and paired messages. They do not
permit arbitrary corrupt jars or missing classes. Packaged runs do not inherit
those allowances. Unrelated warnings remain visible in the game logs.

Cross-version fixtures have separate, reviewed diagnostics tied to their archive
checksums. These cover the test-only generator mod and exact vanilla numeric-ID
changes. They are not applied to ordinary worlds or used to silence the game.
11 changes: 7 additions & 4 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Configuration

Forge creates `config/skysgrassslabs.cfg` after the mod is started for the
first time. Stop the game or server before editing it, then restart to apply
changes. On a dedicated server, the server's configuration controls world
generation and world upgrades.
Open **Mods**, select **Sky's Grass Slabs** and choose **Config** to change
world generation and the legacy replacement option. Save with **Done**, then
restart Minecraft to apply your choices. Settings are not changed mid-world.

You can also edit `config/skysgrassslabs.cfg` while the game or server is
stopped. On a dedicated server, edit the server's file and restart it; the
client's Config screen does not change a remote server.

## World smoothing

Expand Down
5 changes: 5 additions & 0 deletions docs/INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Optional content integration

`coversSupport(state, world, pos)` reports whether a piece covers the whole
horizontal footprint. Support repair leaves grass beneath narrower steps and
corners intact, and that exposed soil can regrow. Full-cover slabs and turf
retain their previous support rules.

Grass Slabs works on its own. Content and terrain mods can use version 1 of
`zone.moddev.mc.skysgrassslabs.api.GrassSlabsApi` without changing its saved IDs.

Expand Down
20 changes: 20 additions & 0 deletions docs/RELEASE-1.1.1.112021.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Sky's Grass Slabs 1.1.1 for Minecraft 1.12.2

You can now change the world-generation and legacy replacement settings from
**Mods → Sky's Grass Slabs → Config**. Each setting has help text. Save your
changes and restart Minecraft to apply them.

Grass slab smoothing now handles small plants that neighbouring decoration has
already placed on a slope. Trees, crops and occupied blocks stay protected,
and existing terrain is unchanged.

With Building Pieces installed, exposed grass beneath steps and corners stays
grassy. Full-cover slabs keep their usual dirt support. Grass Slabs still works
on its own, with the same blocks, recipes, configuration defaults and saved
migration progress.

Legacy recovery now explains its intentional registrations with one startup
notice. The historical IDs and recovery rules have not changed.

Requires Minecraft 1.12.2, Forge 14.23.5.2859 and Java 8. Back up your world before
updating mods or moving a world from an older Minecraft version.
2 changes: 1 addition & 1 deletion docs/WORLD-UPGRADES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ world has been checked in game.

## Sky's Grass Slabs content

Install `SkysGrassSlabs-1.1.0.112021.jar` before opening a world that used the
Install `SkysGrassSlabs-1.1.1.112021.jar` before opening a world that used the
Minecraft 1.10.2 or 1.11.2 release. The permanent dirt slab, grass slab, path
slab and turf IDs are unchanged.

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ curseforge_project_id=1677588
mod_id=skysgrassslabs
mod_name=Sky's Grass Slabs
mod_license=LGPL-2.1-only
mod_version=1.1.0.112021
mod_version=1.1.1.112021
mod_group=zone.moddev.mc.skysgrassslabs
mod_authors=SkyBlade1978
mod_description=Adds dirt, grass and path slabs, turf, natural grass spreading and smoother Overworld terrain.
Loading
Loading