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 @@ -80,9 +80,9 @@ jobs:
if-no-files-found: error
retention-days: 30
path: |
build/libs/ElectricAdvantage-2.2.1.112021.jar
build/libs/ElectricAdvantage-2.2.1.112021-sources.jar
build/libs/ElectricAdvantage-2.2.1.112021-javadoc.jar
build/libs/ElectricAdvantage-2.2.3.112021.jar
build/libs/ElectricAdvantage-2.2.3.112021-sources.jar
build/libs/ElectricAdvantage-2.2.3.112021-javadoc.jar
build/release/SHA256SUMS
CHANGELOG.txt

Expand Down
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,25 @@
/replay_pid*.log

/secret.json
# Nested local tooling and generated output
AGENTS.md
AGENT.md
CLAUDE.md
GEMINI.md
ARCHITECT.md
agent-notes/
.codex/
.agent/
.agents/
.claude/
.gemini/
.cursor/
agent-*.md
codex-*.md
*.log
*.class
__pycache__/
*.pyc
/.DS_Store
/README.txt
/forge-*-changelog.txt
12 changes: 12 additions & 0 deletions CHANGELOG.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
Electric Advantage 2.2.3.112021

- Retain unfinished drill progress when rebuilding the mining target after a world or chunk reload.
- Clear stale deferred target data when loading an untargeted tile; clamp restored progress to current target hardness.
- Preserve registry identities, saved target coordinates, inventories, energy and the existing progress NBT key.

Electric Advantage 2.2.2.112021

- Preserve a loaded hydroturbine's generator link when saved before its first tick, including full restarts and chained world upgrades.
- Clear stale turbine links when reading new entity data; keep existing entity identities and the parentPos NBT key unchanged.
- Add persistence regression tests without changing generated electricity or gameplay balance.

Electric Advantage 2.2.1.112021

- Upgrade the Minecraft 1.12.2 build to ForgeGradle 7 and Gradle 9.6.1.
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

Electric Advantage adds electric machines, generators, automation, and infrastructure to Power Advantage for Minecraft 1.12.2.

## Drill Reload Fix

Version `2.2.3.112021` keeps unfinished drill work when a chunk or world reloads.
Existing inventories, energy, orientation and saved-world names are unchanged.
The regression tests cover resumed progress and full server restarts.

## Building Minecraft 1.12.2

The `master-1.12.2` build uses ForgeGradle 7.0.34 and Gradle 9.6.1. Run Gradle with Java 17; Gradle resolves the Java 8 toolchain used for compilation.
Expand All @@ -28,3 +34,9 @@ gradlew.bat cleanEclipse verifyEclipseProductionClasspath
```

Power Advantage is required at compile time and runtime but is never bundled. Base Metals remains a required distribution dependency while its integrations stay reflection-based and absent from the compile classpath.

## Hydroturbine Persistence

Version `2.2.2.112021` retains the generator coordinate when a loaded turbine is saved before its first update. The existing `parentPos` key, entity identity and generated electricity remain unchanged. This also protects the intermediate 1.12 save during a chained upgrade to 1.13; it cannot recover a coordinate already missing from an old save.

The normal `check` task includes repeated pre-tick save tests, stale-link clearing and live-generator position checks. The Advantage Works migration harness tests the packaged jar through full 1.12 and 1.13 process restarts.
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ dependencies {
// PowerAdvantage is required at runtime but remains a separate mod jar.
compileOnly files(powerAdvantageDeobfJar)
runtimeOnly files(powerAdvantageDeobfJar)
testImplementation 'junit:junit:4.13.2'
testImplementation files(powerAdvantageDeobfJar)
}

tasks.named('processResources', ProcessResources) {
Expand Down Expand Up @@ -298,7 +300,7 @@ tasks.register('verifyReleaseConfiguration') {
group = 'verification'
description = 'Validates release identity, target metadata, and dependency policy.'
doLast {
if (project.mod_version != '2.2.1.112021') {
if (project.mod_version != '2.2.3.112021') {
throw new GradleException("Unexpected ElectricAdvantage release version: ${project.mod_version}")
}
if (project.minecraft_version != '1.12.2'
Expand Down Expand Up @@ -341,7 +343,7 @@ tasks.register('verifyReleaseConfiguration') {
String mainSource = file(
'src/main/java/com/mcmoddev/electricadvantage/ElectricAdvantage.java').getText('UTF-8')
if (!mainSource.contains('MODID = "electricadvantage"')
|| !mainSource.contains('VERSION = "2.2.1.112021"')
|| !mainSource.contains('VERSION = "2.2.3.112021"')
|| !mainSource.contains('dependencies = "required-after:poweradvantage;after:basemetals"')) {
throw new GradleException('ElectricAdvantage source identity or dependency policy changed')
}
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 @@ curseforge_project_id=240985
mod_id=electricadvantage
mod_name=Electric Advantage
mod_license=GPL-3.0-only
mod_version=2.2.1.112021
mod_version=2.2.3.112021
mod_group=com.mcmoddev.electricadvantage
mod_authors=DrCyano
mod_description=Electric-powered machines and infrastructure for the Power Advantage platform.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public class ElectricAdvantage
/** The display name for this mod */
public static final String NAME = "Electric Advantage";
/** The version of this mod, in the format major.minor.update */
public static final String VERSION = "2.2.1.112021";
public static final String VERSION = "2.2.3.112021";

public static ElectricAdvantage INSTANCE = null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ public void onUpdate(){

@Override
protected void readEntityFromNBT(NBTTagCompound root) {
parent = null;
parentCoord = null;
if(root.hasKey("parentPos")){
int[] coords = root.getIntArray("parentPos");
if(coords.length >= 3){
Expand All @@ -109,9 +111,9 @@ protected void readEntityFromNBT(NBTTagCompound root) {

@Override
protected void writeEntityToNBT(NBTTagCompound root) {
if(parent != null){
BlockPos p = parent != null ? parent.getPos() : parentCoord;
if(p != null){
int[] coords = new int[3];
BlockPos p = parent.getPos();
coords[0] = p.getX();
coords[1] = p.getY();
coords[2] = p.getZ();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public float[] getProgress() {
public void tickUpdate(boolean isServerWorld) {
if(isServerWorld){
if(deferred){
targetBlock(targetBlockCoord);
restoreTargetBlock();
}
updateFacing();

Expand Down Expand Up @@ -192,7 +192,7 @@ public void onDataFieldUpdate() {
private int oldLength = 0;
@Override public void powerUpdate(){
if(deferred){
targetBlock(targetBlockCoord);
restoreTargetBlock();
}
super.powerUpdate();
boolean flagSync = progress != oldProgress || oldEnergy != getEnergy();
Expand Down Expand Up @@ -362,12 +362,20 @@ private BlockPos[] getArea(BlockPos center){
return a;
}

private void restoreTargetBlock(){
int savedProgress = progress;
// Rebuild transient drops and hardness without discarding saved work.
targetBlock(targetBlockCoord);
progress = Math.max(0, Math.min(savedProgress, progressGoal));
}

private void deferredTargetBlock (BlockPos n){
targetBlockCoord = n;
deferred = true;
}

private void untargetBlock(){
deferred = false;
progress = 0;
progressGoal = 0;
targetBlockCoord = null;
Expand Down Expand Up @@ -467,6 +475,7 @@ protected void saveTo(NBTTagCompound tagRoot) {

@Override
protected void loadFrom(NBTTagCompound tagRoot) {
untargetBlock();
if(tagRoot.hasKey("progress")){
progress = tagRoot.getShort("progress");
}
Expand All @@ -478,7 +487,8 @@ protected void loadFrom(NBTTagCompound tagRoot) {
if(getWorld() == null){
this.deferredTargetBlock(new BlockPos(x,y,z));
} else {
this.targetBlock(new BlockPos(x,y,z));
this.deferredTargetBlock(new BlockPos(x,y,z));
this.restoreTargetBlock();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.mcmoddev.electricadvantage.entities;

import java.lang.reflect.Field;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import org.junit.Test;
import sun.misc.Unsafe;

Check warning on line 8 in src/test/java/com/mcmoddev/electricadvantage/entities/HydroturbinePersistenceTest.java

View workflow job for this annotation

GitHub Actions / Build, test, and audit

sun.misc.Unsafe is internal proprietary API and may be removed in a future release

import static org.junit.Assert.*;

public class HydroturbinePersistenceTest {
@Test public void preservesPendingGeneratorAcrossRepeatedReadSaveWithoutTick() throws Exception {
NBTTagCompound source = new NBTTagCompound();
source.setIntArray("parentPos", new int[] {164, 81, 8});
for (int pass = 0; pass < 3; pass++) {
HydroturbineEntity turbine = withoutWorld();
turbine.readEntityFromNBT(source);
NBTTagCompound saved = new NBTTagCompound();
turbine.writeEntityToNBT(saved);
assertArrayEquals(new int[] {164, 81, 8}, saved.getIntArray("parentPos"));
source = saved;
}
}

@Test public void readingUnlinkedEntityClearsPendingAndLiveLinks() throws Exception {
HydroturbineEntity turbine = withoutWorld();
NBTTagCompound source = new NBTTagCompound();
source.setIntArray("parentPos", new int[] {164, 81, 8});
turbine.readEntityFromNBT(source);
turbine.parent = new TileEntity() {};
turbine.readEntityFromNBT(new NBTTagCompound());
NBTTagCompound saved = new NBTTagCompound();
turbine.writeEntityToNBT(saved);
assertNull(turbine.parent);
assertFalse(saved.hasKey("parentPos", 11));
}

@Test public void savesLiveGeneratorPositionRatherThanAnOlderPendingLink() throws Exception {
HydroturbineEntity turbine = withoutWorld();
NBTTagCompound source = new NBTTagCompound();
source.setIntArray("parentPos", new int[] {164, 81, 8});
turbine.readEntityFromNBT(source);
TileEntity parent = new TileEntity() {};
parent.setPos(new BlockPos(32, 70, -16));
turbine.parent = parent;
NBTTagCompound saved = new NBTTagCompound();
turbine.writeEntityToNBT(saved);
assertArrayEquals(new int[] {32, 70, -16}, saved.getIntArray("parentPos"));
}

// Exercise the custom NBT methods without constructing or ticking a world.
private static HydroturbineEntity withoutWorld() throws Exception {
Field field = Unsafe.class.getDeclaredField("theUnsafe");

Check warning on line 54 in src/test/java/com/mcmoddev/electricadvantage/entities/HydroturbinePersistenceTest.java

View workflow job for this annotation

GitHub Actions / Build, test, and audit

sun.misc.Unsafe is internal proprietary API and may be removed in a future release
field.setAccessible(true);
return (HydroturbineEntity) ((Unsafe) field.get(null)).allocateInstance(HydroturbineEntity.class);

Check warning on line 56 in src/test/java/com/mcmoddev/electricadvantage/entities/HydroturbinePersistenceTest.java

View workflow job for this annotation

GitHub Actions / Build, test, and audit

sun.misc.Unsafe is internal proprietary API and may be removed in a future release
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.mcmoddev.electricadvantage.machines;

import net.minecraft.nbt.NBTTagCompound;
import org.junit.Test;

import static org.junit.Assert.*;

public class ElectricDrillPersistenceTest {
@Test public void retainsLegacyProgressBeforeWorldAttachment() {
ElectricDrillTileEntity drill = new ElectricDrillTileEntity();
drill.loadFrom(target(512));
drill.prepareDataFieldsForSync();
assertEquals(512, drill.getDataFieldArray()[1]);
}

@Test public void preservesPendingTargetThroughRepeatedSerialization() {
NBTTagCompound source = target(512);
for (int i = 0; i < 3; i++) {
ElectricDrillTileEntity drill = new ElectricDrillTileEntity();
drill.loadFrom(source);
source = new NBTTagCompound();
drill.saveTo(source);
assertEquals(512, source.getShort("progress"));
assertEquals(2049, source.getInteger("targetZ"));
}
}

@Test public void untargetedDataClearsPendingWork() {
ElectricDrillTileEntity drill = new ElectricDrillTileEntity();
drill.loadFrom(target(512));
drill.loadFrom(new NBTTagCompound());
NBTTagCompound saved = new NBTTagCompound();
drill.saveTo(saved);
assertEquals(0, saved.getShort("progress"));
assertFalse(saved.hasKey("targetX"));
}

private static NBTTagCompound target(int progress) {
NBTTagCompound tag = new NBTTagCompound();
tag.setShort("progress", (short) progress);
tag.setInteger("targetX", 2048);
tag.setInteger("targetY", 80);
tag.setInteger("targetZ", 2049);
return tag;
}
}
Loading