Skip to content

Commit

Permalink
Tweak aura API and add aura handler test
Browse files Browse the repository at this point in the history
  • Loading branch information
TheCodex6824 committed Dec 8, 2024
1 parent e7810c0 commit ee6c8fa
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,29 @@ public PhaseModifier(float auraMod, float baseMod) {
new PhaseModifier(0.1f, 1.0f),
new PhaseModifier(0.15f, 1.05f)
};
protected static final int TICK_RATE = 20;

protected volatile int lastMoonPhase;
protected volatile long totalWorldTime;
protected long lastUpdateTime;

@Override
public void gameTick(World world) {
lastMoonPhase = world.provider.getMoonPhase(world.getWorldTime()) % PHASE_TO_AURA_MODS.length;
totalWorldTime = world.getTotalWorldTime();
}

@Override
public void processWorld(IAuraWorld world, long totalWorldTime) {
lastUpdateTime = totalWorldTime;
public void auraTick(IAuraWorld world) {
// the abs call deals with overflow or negative world time
if (Math.abs(totalWorldTime - lastUpdateTime) >= TICK_RATE) {
lastUpdateTime = totalWorldTime;
for (IAuraChunk chunk : world.getAllAuraChunks()) {
equalizeWithNeighbors(world, chunk);
generateVisAndFlux(world, chunk);
checkForRifts(world, chunk);
}
}
}

protected void equalizeWithNeighbors(IAuraWorld world, IAuraChunk center) {
Expand Down Expand Up @@ -134,21 +145,4 @@ protected void checkForRifts(IAuraWorld world, IAuraChunk chunk) {
}
}

@Override
public void processChunk(IAuraWorld world, IAuraChunk chunk, long totalWorldTime) {
equalizeWithNeighbors(world, chunk);
generateVisAndFlux(world, chunk);
checkForRifts(world, chunk);
}

protected int tickRate() {
return 20;
}

@Override
public boolean needsUpdate(long totalWorldTime) {
// the abs call deals with overflow of world time
return Math.abs(totalWorldTime - lastUpdateTime) >= tickRate();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
public interface IAuraChunk {

public ChunkPos getPosition();
public boolean isModified();

public short getBase();
public void setBase(short newBase);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
public interface IAuraProcessor {

public void gameTick(World world);
public void processWorld(IAuraWorld auraWorld, long totalWorldTime);
public void processChunk(IAuraWorld auraWorld, IAuraChunk chunk, long totalWorldTime);
public boolean needsUpdate(long totalWorldTime);
public void auraTick(IAuraWorld auraWorld);

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class AtomicAuraChunk extends AuraChunk implements IAuraChunk {
private AtomicInteger baseAtomic;
private AtomicDouble visAtomic;
private AtomicDouble fluxAtomic;
private volatile boolean modified;

public AtomicAuraChunk(Chunk chunk, short base, float vis, float flux) {
super(chunk, base, vis, flux);
Expand Down Expand Up @@ -63,11 +64,17 @@ public short getBase() {
@Override
public void setBase(short base) {
baseAtomic.set(Math.max(Math.min(base, Short.MAX_VALUE), 0));
modified = true;
}

@Override
public boolean compareAndSetBase(short compare, short newValue) {
return baseAtomic.compareAndSet(compare, newValue);
boolean res = baseAtomic.compareAndSet(compare, newValue);
if (res) {
modified = true;
}

return res;
}

@Override
Expand All @@ -80,14 +87,17 @@ public short addBase(short add) {
// at least on my platform, compareAndSet returns false if current == target
// can't find any docs explaining it so just avoid trying it
if (current == target || visAtomic.compareAndSet(current, target)) {
modified = true;
return (short) (target - current);
}
}
}

@Override
public short getAndSetBase(short set) {
return (short) baseAtomic.getAndSet(Math.max(set, 0));
short res = (short) baseAtomic.getAndSet(Math.max(set, 0));
modified = true;
return res;
}

@Override
Expand All @@ -98,11 +108,17 @@ public float getVis() {
@Override
public void setVis(float newVis) {
visAtomic.set(newVis);
modified = true;
}

@Override
public boolean compareAndSetVis(float compare, float newValue) {
return visAtomic.compareAndSet(compare, newValue);
boolean res = visAtomic.compareAndSet(compare, newValue);
if (res) {
modified = true;
}

return res;
}

@Override
Expand All @@ -111,14 +127,17 @@ public float addVis(float add) {
double current = visAtomic.get();
float target = (float) Math.max(Math.min(current + add, Float.MAX_VALUE), 0);
if (current == target || visAtomic.compareAndSet(current, target)) {
modified = true;
return (float) (target - current);
}
}
}

@Override
public float getAndSetVis(float set) {
return (float) visAtomic.getAndSet(Math.max(set, 0));
float res = (float) visAtomic.getAndSet(Math.max(set, 0));
modified = true;
return res;
}

@Override
Expand All @@ -129,11 +148,17 @@ public float getFlux() {
@Override
public void setFlux(float newFlux) {
fluxAtomic.set(newFlux);
modified = true;
}

@Override
public boolean compareAndSetFlux(float compare, float newValue) {
return fluxAtomic.compareAndSet(compare, newValue);
boolean res = fluxAtomic.compareAndSet(compare, newValue);
if (res) {
modified = true;
}

return res;
}

@Override
Expand All @@ -142,14 +167,17 @@ public float addFlux(float add) {
double current = fluxAtomic.floatValue();
float target = (float) Math.max(Math.min(current + add, Float.MAX_VALUE), 0);
if (current == target || fluxAtomic.compareAndSet(current, target)) {
modified = true;
return (float) (target - current);
}
}
}

@Override
public float getAndSetFlux(float set) {
return (float) fluxAtomic.getAndSet(Math.max(set, 0));
float res = (float) fluxAtomic.getAndSet(Math.max(set, 0));
modified = true;
return res;
}

@Override
Expand All @@ -162,4 +190,9 @@ public void setLoc(ChunkPos loc) {
super.setLoc(loc);
}

@Override
public boolean isModified() {
return modified;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@

package thecodex6824.thaumcraftfix.common.aura;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.Semaphore;

import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import thaumcraft.Thaumcraft;
import thaumcraft.common.lib.events.ServerEvents;
import thaumcraft.common.world.aura.AuraChunk;
import thaumcraft.common.world.aura.AuraHandler;
import thaumcraft.common.world.aura.AuraThread;
import thaumcraft.common.world.aura.AuraWorld;
Expand All @@ -38,78 +36,60 @@
public class GenericAuraThread extends AuraThread implements IListeningAuraThread {

private final IAuraProcessor processor;
// a separate lock is used instead of synchronized(this) to avoid issues if someone is also
// doing that for some reason (very unlikely but you never know...)
private final Lock waitLock;
private final Condition waitCondition;
private final Semaphore waitSem;
// these must be volatile since they are set and read by different threads
private volatile boolean stop;
private volatile long totalWorldTime;

public GenericAuraThread(int dim, IAuraProcessor worldProcessor) {
super(dim);
waitLock = new ReentrantLock();
waitCondition = waitLock.newCondition();
waitSem = new Semaphore(0);
processor = worldProcessor;
}

private void signalAuraLoop() {
waitLock.lock();
try {
waitCondition.signal();
}
finally {
waitLock.unlock();
}
}

@Override
public void notifyUpdate(World world) {
totalWorldTime = world.getTotalWorldTime();
processor.gameTick(world);
signalAuraLoop();
waitSem.release();
}

@Override
public void unloadChunk(World world, Chunk chunk) {
AuraWorld auraWorld = AuraHandler.getAuraWorld(dim);
if (auraWorld instanceof IAuraWorld) {
IAuraChunk aura = ((IAuraWorld) auraWorld).getAuraChunk(chunk.x, chunk.z);
if (aura.isModified()) {
chunk.markDirty();
}
}
// actually removing the chunk is done by Thaumcraft after it saves it
}

@Override
public void run() {
Thaumcraft.log.info("Starting aura thread for dim {}", dim);
while (!stop) {
waitLock.lock();
// this is guaranteed to be immune to spurious wakeups
try {
waitCondition.await();
waitSem.acquire();
}
catch (InterruptedException ex) {
// this is fine, we already check if the wakeup is valid or not
}
finally {
// If interrupted, it is not specified if we hold the lock or not
// Since unlocking without holding it will probably throw an exception, catch it
try {
waitLock.unlock();
}
catch (Exception ex) {}
// something probably wants us to stop but doesn't know about stop()
stop = true;
}

// we may have been woken up by someone calling stop()
// we may have been woken up by someone calling stop() (or got interrupted) so check again
if (stop) {
break;
}

// check if we need to update (this may have been a spurious wakeup)
if (processor.needsUpdate(totalWorldTime)) {
AuraWorld auraWorld = AuraHandler.getAuraWorld(dim);
if (auraWorld instanceof IAuraWorld) {
IAuraWorld world = (IAuraWorld) auraWorld;
processor.processWorld(world, totalWorldTime);
for (AuraChunk auraChunk : auraWorld.getAuraChunks().values()) {
processor.processChunk(world, (IAuraChunk) auraChunk, totalWorldTime);
}
}
else {
Thaumcraft.log.info("Aura for dim {} was unloaded, stopping aura thread", dim);
stop();
break;
}
AuraWorld auraWorld = AuraHandler.getAuraWorld(dim);
if (auraWorld instanceof IAuraWorld) {
processor.auraTick((IAuraWorld) auraWorld);
}
else {
Thaumcraft.log.info("Aura for dim {} was unloaded, stopping aura thread", dim);
stop();
break;
}
}

Expand All @@ -120,7 +100,7 @@ public void run() {
@Override
public void stop() {
stop = true;
signalAuraLoop();
waitSem.release();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.relauncher.Side;
import thaumcraft.common.lib.utils.PosXY;
import thaumcraft.common.world.aura.AuraChunk;
import thaumcraft.common.world.aura.AuraWorld;
import thecodex6824.thaumcraftfix.ThaumcraftFix;
import thecodex6824.thaumcraftfix.api.aura.IAuraChunk;
Expand Down Expand Up @@ -73,4 +75,9 @@ public void markPositionForRift(BlockPos position, boolean useInexactSpawning) {
});
}

public void addAuraChunk(AuraChunk chunk) {
ChunkPos pos = chunk.getLoc();
getAuraChunks().put(new PosXY(pos.x, pos.z), chunk);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@
package thecodex6824.thaumcraftfix.common.aura;

import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;

public interface IListeningAuraThread {

public void notifyUpdate(World world);
public void unloadChunk(World world, Chunk chunk);

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import net.minecraft.world.World;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
Expand Down Expand Up @@ -67,6 +68,17 @@ public static void onWorldTick(WorldTickEvent event) {
}
}

@SubscribeEvent
public static void onChunkUnload(ChunkEvent.Unload event) {
if (!event.getWorld().isRemote) {
World world = event.getWorld();
AuraThread thread = ServerEvents.auraThreads.get(world.provider.getDimension());
if (thread instanceof IListeningAuraThread) {
((IListeningAuraThread) thread).unloadChunk(world, event.getChunk());
}
}
}

@SubscribeEvent
public static void onRiftTrigger(RiftTriggerEvent event) {
BlockPos pos = event.getPosition();
Expand Down
Loading

0 comments on commit ee6c8fa

Please sign in to comment.