Skip to content

Commit

Permalink
Save palette along with the chunk (fixes #62)
Browse files Browse the repository at this point in the history
  • Loading branch information
Runemoro committed Jun 6, 2018
1 parent c1984e6 commit 271c3ec
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/main/java/org/dimdev/vanillafix/VanillaFix.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public void onPreInit(FMLPreInitializationEvent event) {
}

if (DEBUG_ITEM_IDS) {
for (int i = 0; i < 5000; i++) {
for (int i = 0; i < 40000; i++) {
Item item = new Item()
.setCreativeTab(CreativeTabs.FOOD)
.setUnlocalizedName("item_" + i)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.dimdev.vanillafix.idlimit;

public interface IPatchedBlockStateContainer {
void setTemporaryPalette(int[] temporaryPalette);
int[] getTemporaryPalette();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.dimdev.vanillafix.idlimit.mixins;

import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.NibbleArray;
import net.minecraft.world.chunk.storage.AnvilChunkLoader;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import org.dimdev.vanillafix.idlimit.IPatchedBlockStateContainer;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

@Mixin(AnvilChunkLoader.class)
public class MixinAnvilChunkLoader {
@Inject(method = "readChunkFromNBT", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;<init>(IZ)V", shift = At.Shift.BY, by = 2), locals = LocalCapture.CAPTURE_FAILHARD)
private void readPaletteNBT(World world, NBTTagCompound nbt, CallbackInfoReturnable<Chunk> cir,
int ignored0, int ignored1, Chunk ignored2, NBTTagList ignored3, int ingnored4, ExtendedBlockStorage[] ignored5, boolean ignored6, int ignored7,
NBTTagCompound storageNBT, int y, ExtendedBlockStorage extendedBlockStorage) {
int[] palette = storageNBT.hasKey("Palette", 11) ? storageNBT.getIntArray("Palette") : null;
((IPatchedBlockStateContainer) extendedBlockStorage.getData()).setTemporaryPalette(palette);
}

@Inject(method = "writeChunkToNBT", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/world/chunk/BlockStateContainer;getDataForNBT([BLnet/minecraft/world/chunk/NibbleArray;)Lnet/minecraft/world/chunk/NibbleArray;", ordinal = 0), locals = LocalCapture.CAPTURE_FAILHARD)
private void writePaletteNBT(Chunk chunk, World worldIn, NBTTagCompound nbt, CallbackInfo ci,
ExtendedBlockStorage[] ignored0, NBTTagList ignored1, boolean ignored2, ExtendedBlockStorage[] ignored3, int ignored4, int ignored5,
ExtendedBlockStorage extendedBlockStorage, NBTTagCompound storageNBT, byte[] blocks, NibbleArray data, NibbleArray add) {
int[] palette = ((IPatchedBlockStateContainer) extendedBlockStorage.getData()).getTemporaryPalette();
storageNBT.setIntArray("Palette", palette);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package org.dimdev.vanillafix.idlimit.mixins;

import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.BitArray;
import net.minecraft.world.chunk.BlockStateContainer;
import net.minecraft.world.chunk.IBlockStatePalette;
import net.minecraft.world.chunk.NibbleArray;
import org.dimdev.vanillafix.idlimit.IPatchedBlockStateContainer;
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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;

import java.util.*;

@Mixin(BlockStateContainer.class)
public abstract class MixinBlockStateContainer implements IPatchedBlockStateContainer {
@Shadow protected abstract IBlockState get(int index);
@Shadow protected BitArray storage;
@Shadow protected IBlockStatePalette palette;
@Shadow protected abstract void set(int index, IBlockState state);
@Shadow protected abstract void setBits(int bitsIn);

private static boolean shouldUseVFFormat;
private static boolean shouldUseVFFormatDetermined;
private int[] temporaryPalette; // index -> state id

private boolean shouldUseVFFormat() {
if (!shouldUseVFFormatDetermined) {
for (Block block : Block.REGISTRY) {
int id = Block.REGISTRY.getIDForObject(block);
if (id > 4096) {
shouldUseVFFormat = true;
break;
}
}
shouldUseVFFormatDetermined = true;
}
return shouldUseVFFormat;
}

@Override
public int[] getTemporaryPalette() {
return temporaryPalette;
}

@Override
public void setTemporaryPalette(int[] temporaryPalette) {
this.temporaryPalette = temporaryPalette;
}

/**
* @reason If this BlockStateContainer should be saved in VanillaFix format,
* store palette IDs rather than block IDs in the container's "Blocks" and
* "Data" arrays.
*/
@SuppressWarnings("deprecation")
@Inject(method = "getDataForNBT", at = @At("HEAD"), cancellable = true)
private void getDataForNBTVF(byte[] blockIds, NibbleArray data, CallbackInfoReturnable<NibbleArray> cir) {
if (!shouldUseVFFormat()) return; // Save containters in VF format only if 4096 IDs are being used TODO: only if this containter contains IDs >4096?

HashMap<IBlockState, Integer> stateIDMap = new HashMap<>();
int nextID = 0;
for (int index = 0; index < 4096; ++index) {
IBlockState state = get(index);
Integer paletteID = stateIDMap.get(state);
if (paletteID == null) {
paletteID = nextID++;
stateIDMap.put(state, paletteID);
}

int x = index & 15;
int y = index >> 8 & 15;
int z = index >> 4 & 15;

blockIds[index] = (byte) (paletteID >> 4 & 255);
data.set(x, y, z, paletteID & 15);
}

temporaryPalette = new int[nextID];
for (Map.Entry<IBlockState, Integer> entry : stateIDMap.entrySet()) {
temporaryPalette[entry.getValue()] = Block.BLOCK_STATE_IDS.get(entry.getKey());
}

cir.setReturnValue(null);
cir.cancel();
}

/**
* @reason If this BlockStateContainer is saved in VanillaFix format, treat
* the "Blocks" and "Data" arrays as palette IDs.
*/
@SuppressWarnings("deprecation")
@Inject(method = "setDataFromNBT", at = @At("HEAD"), cancellable = true)
private void setDataFromNBTVF(byte[] blockIds, NibbleArray data, NibbleArray blockIdExtension, CallbackInfo ci) {
if (temporaryPalette == null) return; // Read containers in VF format only if container was saved in VF format (has a palette)

for (int index = 0; index < 4096; ++index) {
int x = index & 15;
int y = index >> 8 & 15;
int z = index >> 4 & 15;
int paletteID = (blockIds[index] & 255) << 4 | data.get(x, y, z);

set(index, Block.BLOCK_STATE_IDS.getByValue(temporaryPalette[paletteID]));
}

temporaryPalette = null;
ci.cancel();
}
}
4 changes: 3 additions & 1 deletion src/main/resources/mixins.vanillafix.idlimit.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
"compatibilityLevel": "JAVA_8",
"mixins": [
"MixinGameData",
"MixinStatList"
"MixinStatList",
"MixinBlockStateContainer",
"MixinAnvilChunkLoader"
],
"client": [],
"injectors": {
Expand Down

0 comments on commit 271c3ec

Please sign in to comment.