Got rid of a mixin.

This commit is contained in:
Florian Nücke
2022-02-06 23:41:31 +01:00
parent cb21c17b52
commit 95eb8997cb
6 changed files with 63 additions and 159 deletions

View File

@@ -1,34 +0,0 @@
/* SPDX-License-Identifier: MIT */
package li.cil.oc2.common.ext;
import li.cil.oc2.common.mixin.ChunkMapMixin;
import li.cil.oc2.common.util.ChunkUtils;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.world.level.chunk.ChunkAccess;
/**
* Interface injected into the {@link ChunkAccess} class.
* <p>
* Tracks a "lazy unsaved" flag, which is converted into the regular unsaved flag
* before certain manual save operations.
*
* @see ChunkUtils
* @see ChunkMapMixin
*/
public interface ChunkAccessExt {
/**
* Set the lazy unsaved flag for this instance.
* <p>
* This method is used by the utility methods in {@link ChunkUtils}.
*/
void setLazyUnsaved();
/**
* Set the unsaved flag for this instance, if the lazy unsaved flag is set,
* then clears the lazy unsaved flag.
* <p>
* This method is invoked from mixins injected into the {@link ChunkMap} class.
*/
void applyAndClearLazyUnsaved();
}

View File

@@ -1,37 +0,0 @@
/* SPDX-License-Identifier: MIT */
package li.cil.oc2.common.mixin;
import li.cil.oc2.common.ext.ChunkAccessExt;
import li.cil.oc2.common.util.ChunkUtils;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
/**
* Tracks a "lazy unsaved" flag per {@link ChunkAccess} instance, to allow
* marking chunks as needing saving just in time for "hard" saves.
*
* @see ChunkMapMixin <c>ChunkMapMixin</c> for more information
* @see ChunkUtils
*/
@Mixin(ChunkAccess.class)
public abstract class ChunkAccessMixin implements ChunkAccessExt {
@Shadow
protected volatile boolean unsaved;
private volatile boolean lazyUnsaved;
@Override
public void setLazyUnsaved() {
lazyUnsaved = true;
}
@Override
public void applyAndClearLazyUnsaved() {
if (!unsaved && lazyUnsaved) {
unsaved = true;
}
lazyUnsaved = false;
}
}

View File

@@ -1,65 +0,0 @@
/* SPDX-License-Identifier: MIT */
package li.cil.oc2.common.mixin;
import com.mojang.datafixers.DataFixer;
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import li.cil.oc2.common.ext.ChunkAccessExt;
import li.cil.oc2.common.util.ChunkUtils;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.storage.ChunkStorage;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.nio.file.Path;
/**
* Hooks into {@link ChunkMap} saving code-paths for "hard" save operations.
* <p>
* Minecraft immediately serializes all chunk data, including {@link BlockEntity} NBT.
* This is a massive performance issue for blocks with state that changes every tick,
* such as computers and things accepting energy.
* <p>
* To avoid this per-frame serialization operations, we track a "lazy unsaved" flag per
* {@link ChunkAccess} using the {@link ChunkAccessMixin}, and flush this flag into the
* real unsaved flag during "hard" save operations, just before the flag would be
* checked. These save operations include:
* <ul>
* <li>Chunk unloaded.</li>
* <li>Game paused (singleplayer).</li>
* <li>Save command.</li>
* <li>Server stopped.</li>
* </ul>
* <p>
* The flag is set using the injected interface {@link ChunkAccessExt}, via the utility
* methods in {@link ChunkUtils}.
*
* @see ChunkAccessMixin
* @see ChunkUtils
*/
@Mixin(ChunkMap.class)
public abstract class ChunkMapMixin extends ChunkStorage {
@Shadow private volatile Long2ObjectLinkedOpenHashMap<ChunkHolder> visibleChunkMap;
public ChunkMapMixin(final Path path, final DataFixer dataFixer, final boolean sync) {
super(path, dataFixer, sync);
}
@Inject(method = "saveAllChunks", at = {@At(value = "HEAD")})
private void beforeAsyncSave(final CallbackInfo ci) {
visibleChunkMap.values().forEach(holder -> {
if (holder.wasAccessibleSinceLastSave()) {
final ChunkAccess chunkToSave = holder.getChunkToSave().getNow(null);
if (chunkToSave instanceof ChunkAccessExt ext) {
ext.applyAndClearLazyUnsaved();
}
}
});
}
}

View File

@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: MIT */
package li.cil.oc2.common.mixin;
import li.cil.oc2.common.util.ChunkUtils;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.world.level.chunk.ChunkSource;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerChunkCache.class)
public abstract class ServerChunkCacheMixin extends ChunkSource {
@Inject(method = "save", at = @At("HEAD"))
private void applyLazyUnsavedChunks(final CallbackInfo ci) {
ChunkUtils.applyChunkLazyUnsaved();
}
}

View File

@@ -3,7 +3,7 @@
package li.cil.oc2.common.util;
import li.cil.oc2.api.API;
import li.cil.oc2.common.ext.ChunkAccessExt;
import li.cil.oc2.common.mixin.ServerChunkCacheMixin;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.ChunkPos;
@@ -13,8 +13,18 @@ import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import java.util.Collections;
import java.util.Set;
import java.util.WeakHashMap;
@Mod.EventBusSubscriber(modid = API.MOD_ID, bus = Mod.EventBusSubscriber.Bus.FORGE)
public final class ChunkUtils {
/**
* All chunks marked for lazy saving. The lazy unsaved state will be applied when
* chunks unload and when chunks get explicitly saved, via the {@link ServerChunkCacheMixin}.
*/
private static final Set<ChunkAccess> UNSAVED_CHUNKS = Collections.synchronizedSet(Collections.newSetFromMap(new WeakHashMap<>()));
/**
* This will mark a chunk unsaved lazily, right before an attempt to save it would be made due
* to of these events:
@@ -36,9 +46,7 @@ public final class ChunkUtils {
* @param chunkAccess the chunk to set the flag for.
*/
public static void setLazyUnsaved(final ChunkAccess chunkAccess) {
if (chunkAccess instanceof ChunkAccessExt ext) {
ext.setLazyUnsaved();
}
UNSAVED_CHUNKS.add(chunkAccess);
}
/**
@@ -99,10 +107,24 @@ public final class ChunkUtils {
}
}
/**
* Marks the specified as unsaved, if it was marked as lazy unsaved before
* and clears the lazy unsaved flags.
*
* @param chunk the chunk to apply the lazy unsaved state for.
*/
public static void applyChunkLazyUnsaved() {
for (final ChunkAccess chunk : UNSAVED_CHUNKS) {
chunk.setUnsaved(true);
}
UNSAVED_CHUNKS.clear();
}
@SubscribeEvent
public static void handleChunkUnload(final ChunkEvent.Unload event) {
if (event.getChunk() instanceof ChunkAccessExt ext) {
ext.applyAndClearLazyUnsaved();
final ChunkAccess chunk = event.getChunk();
if (UNSAVED_CHUNKS.remove(chunk)) {
chunk.setUnsaved(true);
}
}
}

View File

@@ -1,19 +1,18 @@
{
"minVersion": "0.8",
"compatibilityLevel": "JAVA_17",
"required": true,
"package": "li.cil.oc2.common.mixin",
"refmap": "mixins.oc2.refmap.json",
"mixins": [
"ChunkAccessMixin",
"ChunkMapMixin"
],
"client": [
"FrustumMixin",
"LevelRendererMixin",
"MinecraftMixin"
],
"injectors": {
"defaultRequire": 1
}
"minVersion": "0.8.5",
"compatibilityLevel": "JAVA_17",
"required": true,
"package": "li.cil.oc2.common.mixin",
"refmap": "mixins.oc2.refmap.json",
"mixins": [
"ServerChunkCacheMixin"
],
"client": [
"FrustumMixin",
"LevelRendererMixin",
"MinecraftMixin"
],
"injectors": {
"defaultRequire": 1
}
}