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.0.1.112021.jar
build/libs/SkysGrassSlabs-1.0.1.112021-sources.jar
build/libs/SkysGrassSlabs-1.0.1.112021-javadoc.jar
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/release/SHA256SUMS
CHANGELOG.md

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

## 1.1.0.112021 - Minecraft 1.12.2

- Added optional shared grass behaviour with Sky's Building Pieces.
- Let Sky's Terrain Smoother own the combined generation pass when installed.
- Preserved mixed legacy and Sky saves, custom item data and migration history.
- Added recipes to combine two dirt or grass slabs into their full block.

## 1.0.1.112021 - Minecraft 1.12.2

- Ported the complete dirt, grass, path slab and turf feature set to Minecraft
Expand Down
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ Sky's Grass Slabs adds dirt, grass and lowered path slabs, along with a thin
layer of turf. Grass spreads naturally between full blocks and slabs, while
new Overworld terrain uses grass slabs to soften suitable one block steps.

This branch is for Minecraft 1.12.2. Version `1.0.1.112021` requires Forge
This branch is for Minecraft 1.12.2. Version `1.1.0.112021` requires Forge
14.23.5.2859 or a compatible Forge 14.23.5 build and Java 8.

The mod works on its own. Sky's Building Pieces and Sky's Terrain Smoother are
optional; neither is needed to update an existing Grass Slabs world.

## Installation

Place the mod jar in the `mods` folder. It must be installed on both the client
Expand All @@ -30,7 +33,8 @@ or updating world changing mods.
- [Gameplay and recipes](docs/GAMEPLAY.md)
- [Configuration](docs/CONFIGURATION.md)
- [Upgrading an older world](docs/WORLD-UPGRADES.md)
- [Release notes](docs/RELEASE-1.0.1.112021.md)
- [Optional content integration](docs/INTEGRATION.md)
- [Release notes](docs/RELEASE-1.1.0.112021.md)

## License

Expand Down
13 changes: 13 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ ext.functional_version = versionParts[0..2].join('.')
ext.display_version = project.mod_version
ext.release_tag = project.mod_version

def pinnedJava8 = providers.environmentVariable('SGS_JAVA8_HOME')
def exactJava8Spec = pinnedJava8.isPresent() ?
org.gradle.jvm.toolchain.internal.SpecificInstallationToolchainSpec.fromJavaHome(
project.services.get(org.gradle.api.internal.provider.PropertyFactory), file(pinnedJava8.get())) : null

java {
toolchain.languageVersion = JavaLanguageVersion.of(8)
withSourcesJar()
Expand All @@ -48,6 +53,7 @@ tasks.withType(JavaCompile).configureEach {
targetCompatibility = JavaVersion.VERSION_1_8
options.encoding = 'UTF-8'
options.compilerArgs.addAll(['-Xmaxerrs', '1000'])
if (pinnedJava8.isPresent()) javaCompiler = javaToolchains.compilerFor(exactJava8Spec)
}

tasks.named('compileTestJava', JavaCompile) {
Expand All @@ -57,9 +63,15 @@ tasks.named('compileTestJava', JavaCompile) {
tasks.withType(Test).configureEach {
useJUnitPlatform()
workingDir = project.projectDir
if (pinnedJava8.isPresent()) javaLauncher = javaToolchains.launcherFor(exactJava8Spec)
}

tasks.withType(JavaExec).configureEach {
if (pinnedJava8.isPresent()) javaLauncher = javaToolchains.launcherFor(exactJava8Spec)
}

tasks.withType(Javadoc).configureEach {
if (pinnedJava8.isPresent()) javadocTool = javaToolchains.javadocToolFor(exactJava8Spec)
failOnError = false
options.encoding = 'UTF-8'
options.addStringOption('Xdoclint:none', '-quiet')
Expand Down Expand Up @@ -404,6 +416,7 @@ tasks.register('clientIntegrationTest') {
def java8Launcher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(8)
}
if (pinnedJava8.isPresent()) java8Launcher = javaToolchains.launcherFor(exactJava8Spec)
def forgeInstallerName =
"forge-${project.minecraft_version}-${project.forge_version}-installer.jar"
def forgeServerName = "forge-${project.minecraft_version}-${project.forge_version}.jar"
Expand Down
4 changes: 4 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ When enabled, suitable one block slopes in newly generated Overworld chunks
may receive grass slabs. Set this to `false` to leave new terrain unchanged.
The setting never adds or removes slabs in chunks that already exist.

With Sky's Terrain Smoother installed, this setting controls its grass material.
Terrain Smoother owns the only Sky terrain pass, even when its master switch is
off. Removing Terrain Smoother restores standalone grass slab generation.

## Legacy replacement setting

`compat.forceReplaceBuildingBricksSlabs=false`
Expand Down
1 change: 1 addition & 0 deletions docs/GAMEPLAY.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ enabled is removed without dropping an item.

- Three dirt blocks in a horizontal row make six dirt slabs.
- Three grass blocks in a horizontal row make six grass slabs.
- Two matching dirt or grass slabs side by side make the matching full block.
- A dirt block and a recognised seed make a grass block.
- A dirt slab and a recognised seed make a grass slab.
- A grass block or grass slab together with a compatible shovel makes one
Expand Down
25 changes: 25 additions & 0 deletions docs/INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Optional content integration

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.

A terrain owner calls `claimSmoothing(modid, enabled)` before Grass Slabs loads
its configuration. Ownership suppresses the separate Grass Slabs generation
pass, even when the owner's master switch is off. `grassGenerationAllowed()`
respects the player's grass setting and the legacy-generator safety checks.

During initialization, register each ordinary dirt/grass orientation with
`registerGrassForm`. Conflicting mappings and late registrations are rejected.
Do not register coarse dirt or podzol. `tickDirt`, `tickGrass` and `repairSupport`
share the grass lifecycle while retaining the registered shape and orientation.
Content mods should keep this integration optional.

`registerGrassVariant` allows other grass skins to share a dirt state without
changing its default regrowth. Non-spreading skins can opt out of spreading.
`registerGrassSupport` supplies native full-block grass/dirt pairs for support
repair. `registerSpreadRule` can match grass to its source family; return null
to use the default registered pair. Callbacks must not load chunks or write to
the world. Registration closes at load completion.

Without an integration mod, these extra mappings and rules remain empty and
the standalone generator continues to handle suitable new Overworld slopes.
13 changes: 13 additions & 0 deletions docs/RELEASE-1.1.0.112021.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Sky's Grass Slabs 1.1.0 for Minecraft 1.12.2

Adds optional integration with Sky's Building Pieces and Sky's Terrain Smoother.
Grass spreads between their matching dirt and grass pieces. Terrain Smoother
can own terrain generation without running a second grass slab pass.

The mod still works on its own. Existing blocks, orientation, configuration
defaults and saved migration progress are retained. Legacy recovery also keeps
custom item data and previously recorded chunk markers in mixed worlds.
Two matching dirt or grass slabs can be crafted back into their full block.

Requires Minecraft 1.12.2, Forge 14.23.5.2859 and Java 8. Back up existing
worlds before changing Minecraft versions or removing another mod.
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.0.1.112021.jar` before opening a world that used the
Install `SkysGrassSlabs-1.1.0.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.0.1.112021
mod_version=1.1.0.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.
2 changes: 1 addition & 1 deletion src/clientIntegrationTest/resources/mcmod.info
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"modid":"skysgrassslabsclienttest","name":"Sky's Grass Slabs Client Probe","description":"Build-only rendered client fixture.","version":"1","mcversion":"1.12.2","authorList":["MMD"],"dependencies":["required-after:skysgrassslabs@[1.0.1.112021]"]}]
[{"modid":"skysgrassslabsclienttest","name":"Sky's Grass Slabs Client Probe","description":"Build-only rendered client fixture.","version":"1","mcversion":"1.12.2","authorList":["MMD"],"dependencies":["required-after:skysgrassslabs@[1.1.0.112021]"]}]
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,10 @@ public void serverStarted(FMLServerStartedEvent event) {
String phase = System.getProperty("skysgrassslabs.integrationPhase", "fresh");
try {
Properties evidence = load(marker);
if ("upgrade-110-first".equals(phase) ||
if ("released-seed".equals(phase) || "released-upgrade".equals(phase)) {
ReleasedUpgradeChecks.run(world, "released-seed".equals(phase));
evidence.setProperty(phase.replace('-', '_') + "_complete", "true");
} else if ("upgrade-110-first".equals(phase) ||
"upgrade-110-reload".equals(phase)) {
verifyForwardUpgradeFixture(world, "1.10.2", "1.0.0.110021",
"2030960E217C3F61AE4919C91058696B02F9FAE570BE1CD7B698696EA7BEB861");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package zone.moddev.mc.skysgrassslabs.integrationtest;

import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntityChest;
import net.minecraft.util.ClassInheritanceMultiMap;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldServer;
import zone.moddev.mc.skysgrassslabs.init.ModBlocks;
import zone.moddev.mc.skysgrassslabs.world.ModWorldState;

/** Seeds a released-version save and verifies it with the next version. */
final class ReleasedUpgradeChecks {
private ReleasedUpgradeChecks() { }

static void run(WorldServer world, boolean seed) {
world.getGameRules().setOrCreateGameRule("randomTickSpeed", "0");
Block[] blocks = {ModBlocks.DIRT_SLAB, ModBlocks.GRASS_SLAB,
ModBlocks.PATH_SLAB, ModBlocks.TURF};
int states = 0;
for (Block block : blocks) {
for (int metadata = 0; metadata < (block == ModBlocks.TURF ? 1 : 2); ++metadata) {
BlockPos pos = new BlockPos(160 + states++, 100, 160);
IBlockState expected = block.getStateFromMeta(metadata);
if (seed) {
world.setBlockState(pos.down(), Blocks.DIRT.getDefaultState(), 2);
world.setBlockToAir(pos.up());
world.setBlockState(pos, expected, 2);
} else {
require(world.getBlockState(pos).equals(expected),
"Released block identity and orientation: " + block.getRegistryName()
+ " / " + metadata);
}
}
}

BlockPos chestPos = new BlockPos(160, 101, 164);
if (seed) world.setBlockState(chestPos, Blocks.CHEST.getDefaultState(), 2);
TileEntityChest chest = (TileEntityChest) world.getTileEntity(chestPos);
require(chest != null, "Released container survived");
for (int slot = 0; slot < blocks.length; ++slot) {
ItemStack expected = new ItemStack(blocks[slot], 11 + slot,
blocks[slot] == ModBlocks.TURF ? 0 : 1);
NBTTagCompound data = new NBTTagCompound();
data.setString("upgrade_note", "Saved custom item data");
data.setInteger("fixture_slot", slot);
expected.setTagCompound(data);
if (seed) chest.setInventorySlotContents(slot, expected);
else require(ItemStack.areItemStacksEqual(chest.getStackInSlot(slot), expected),
"Released container item identity, count, metadata and NBT: " + slot);
}

if (seed) {
ItemStack stack = new ItemStack(ModBlocks.GRASS_SLAB, 7, 1);
NBTTagCompound data = new NBTTagCompound();
data.setString("upgrade_entity", "Retained");
stack.setTagCompound(data);
require(world.spawnEntity(new EntityItem(world, 164.5, 101, 164.5, stack)),
"Released dropped item seeded");
ModWorldState state = ModWorldState.get(world);
state.recordChunk();
state.recordGrassBlocks(13, 0);
state.recordDirtBlocks(17, 1);
state.recordGrassItems(19);
state.recordDirtItems(23);
} else {
boolean found = false;
for (ClassInheritanceMultiMap<Entity> section :
world.getChunk(chestPos).getEntityLists()) {
for (Entity entity : section) {
if (!(entity instanceof EntityItem)) continue;
ItemStack stack = ((EntityItem) entity).getItem();
if (!stack.hasTagCompound()
|| !stack.getTagCompound().hasKey("upgrade_entity")) continue;
require(stack.getItem() == Item.getItemFromBlock(ModBlocks.GRASS_SLAB)
&& stack.getCount() == 7 && stack.getMetadata() == 1
&& "Retained".equals(stack.getTagCompound()
.getString("upgrade_entity")),
"Released dropped item identity, count, metadata and NBT");
found = true;
}
}
require(found, "Released dropped item survived");
ModWorldState state = ModWorldState.get(world);
require(ModWorldState.SCHEMA_VERSION == 1 && state.migratedChunks() == 1
&& state.migratedGrassBlocks() == 13
&& state.migratedGrassBlocksTop() == 13
&& state.migratedDirtBlocks() == 17
&& state.migratedDirtBlocksBottom() == 17
&& state.migratedGrassItems() == 19 && state.migratedDirtItems() == 23,
"Released schema-1 counters survived unchanged");
}
System.out.println("GRASS_SLABS_RELEASED_UPGRADE_PASS seed=" + seed + " states=" + states);
}

private static void require(boolean value, String message) {
if (!value) throw new AssertionError(message);
}
}
16 changes: 14 additions & 2 deletions src/main/java/zone/moddev/mc/skysgrassslabs/SkysGrassSlabs.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import net.minecraftforge.fml.common.SidedProxy;
import net.minecraftforge.fml.common.FMLCommonHandler;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLLoadCompleteEvent;
import net.minecraftforge.fml.common.event.FMLPostInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger;
Expand All @@ -14,13 +15,15 @@
import zone.moddev.mc.skysgrassslabs.event.CommonEvents;
import zone.moddev.mc.skysgrassslabs.proxy.CommonProxy;
import zone.moddev.mc.skysgrassslabs.world.GrassSlabSmoothingHandler;
import zone.moddev.mc.skysgrassslabs.api.GrassSlabsApi;
import zone.moddev.mc.skysgrassslabs.init.ModBlocks;

@Mod(modid = SkysGrassSlabs.MOD_ID, name = SkysGrassSlabs.NAME,
version = SkysGrassSlabs.VERSION, dependencies = "before:buildingbricks")
public final class SkysGrassSlabs {
public static final String MOD_ID = "skysgrassslabs";
public static final String NAME = "Sky's Grass Slabs";
public static final String VERSION = "1.0.1.112021";
public static final String VERSION = "1.1.0.112021";

@SidedProxy(
clientSide = "zone.moddev.mc.skysgrassslabs.proxy.ClientProxy",
Expand All @@ -39,14 +42,18 @@ public void preInit(FMLPreInitializationEvent event) {
LegacyMigrationHandler migration = new LegacyMigrationHandler();
MinecraftForge.EVENT_BUS.register(migration);
FMLCommonHandler.instance().bus().register(migration);
if (SkysGrassSlabsConfig.isSmoothingActive()) {
if (!GrassSlabsApi.hasSmoothingOwner() && SkysGrassSlabsConfig.isSmoothingActive()) {
MinecraftForge.EVENT_BUS.register(new GrassSlabSmoothingHandler());
}
proxy.preInit();
}

@Mod.EventHandler
public void init(FMLInitializationEvent event) {
for (int metadata = 0; metadata < 2; ++metadata) {
GrassSlabsApi.registerGrassForm(ModBlocks.DIRT_SLAB.getStateFromMeta(metadata),
ModBlocks.GRASS_SLAB.getStateFromMeta(metadata));
}
proxy.init();
}

Expand All @@ -55,6 +62,11 @@ public void postInit(FMLPostInitializationEvent event) {
BuildingBricksCompat.resolveBlocks();
}

@Mod.EventHandler
public void loadComplete(FMLLoadCompleteEvent event) {
GrassSlabsApi.freeze();
}

public SkysGrassSlabs() {
}
}
Loading
Loading