Skip to content

Commit

Permalink
Merge remote-tracking branch 'Cleptomania/metadatav2' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Dream-Master committed Feb 7, 2024
2 parents 21cfaf0 + 09bc926 commit 57304c6
Show file tree
Hide file tree
Showing 14 changed files with 334 additions and 56 deletions.
2 changes: 1 addition & 1 deletion dependencies.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Add your dependencies here

dependencies {

api("com.github.GTNewHorizons:GTNHLib:0.2.3:dev")
}
23 changes: 14 additions & 9 deletions src/main/java/com/gtnewhorizons/neid/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ public class Constants {
/**
* The total number of bits used by a block's metadata
*/
public static final int BITS_PER_METADATA = 4;
public static final int BITS_PER_METADATA = 16;
public static final int VANILLA_BITS_PER_METADATA = 4;

public static final int METADATA_COUNT = 1 << BITS_PER_METADATA;
public static final int VANILLA_METADATA_COUNT = 1 << VANILLA_BITS_PER_METADATA;

/**
* MAX_BLOCK_ID is calculated to 1 less bit than the total bits per ID to accomodate signed values. The value is
* stored as 16 bits, but it is signed so the maximum ID we can actually use is 1 bit less. Vanilla minecraft
Expand All @@ -29,6 +32,9 @@ public class Constants {
public static final int BLOCK_ID_MASK = (1 << BITS_PER_ID) - 1;
public static final int VANILLA_BLOCK_ID_MASK = VANILLA_MAX_BLOCK_ID;

public static final int METADATA_MASK = (1 << BITS_PER_METADATA) - 1;
public static final int VANILLA_METADATA_MASK = (1 << VANILLA_BITS_PER_METADATA) - 1;

/**
* Number of block stored in a single EBS, this is the same as vanilla.
*/
Expand All @@ -37,29 +43,28 @@ public class Constants {
/**
* This number is the total bytes in an ExtendedBlockStorage. It is: LSB + MSB + Metadata + Skylight Data +
* Blocklight Data. In vanilla: 8 + 4 + 4 + 4 + 4 = 24 bits per block = 3 bytes per block * 4,096 blocks per EBS =
* 12288 bytes per EBS Our equation: 16 + 0 + 4 + 4 + 4 = 32 bits per block = 3.5 bytes per block * 4,096 blocks per
* EBS = 14336 bytes per EBS
* 12288 bytes per EBS Our equation: 16 + 0 + 16 + 4 + 4 = 40 bits per block = 5 bytes per block * 4,096 blocks per
* EBS = 20480 bytes per EBS
*/
public static final int BYTES_PER_EBS = 14336;
public static final int BYTES_PER_EBS = 20480;
public static final int VANILLA_BYTES_PER_EBS = 12288;

/**
* This number is the total bytes stored in a chunk. It is: Number of EBS in a chunk(16) * BYTES_PER_EBS +
* MAX_BIOME_ID(256) In vanilla: 16 * 12288 + 256 = 196864 Our equation: 16 * BYTES_PER_EBS(14336) + 256 = 229632
* MAX_BIOME_ID(256) In vanilla: 16 * 12288 + 256 = 196864 Our equation: 16 * BYTES_PER_EBS(20480) + 256 = 327936
*/
public static final int BYTES_PER_CHUNK = 229632;
public static final int BYTES_PER_CHUNK = 327936;
public static final int VANILLA_BYTES_PER_CHUNK = 196864;

/**
* This number is the total bytes stored in an EBS, minute the lighting data. It is: (LSB + MSB + Metadata) *
* BLOCKS_PER_EBS(4096) In vanilla: 8 + 4 + 4 = 16 bits per block = 2 bytes per block * 4096 = 8192 Our equation: 16
* + 0 + 4 = 20 bits per block = 2.5 bytes per block. We have to round up because we use a whole byte for the
* metadata + msb, despite not using the msb anymore. So 3 bytes per block * 4096 = 12288.
* + 0 + 16 = 20 bits per block = 4 bytes per block * 4096 = 16384.
*
* If you look at vanilla source code, this value will be 2048 because seemingly Vanilla handles less EBS per chunk?
* Unsure, but Forge ASM's this value to 8192, so that is what we are looking for to modify.
*/
public static final int BYTES_PER_EBS_MINUS_LIGHTING = 12288;
public static final int BYTES_PER_EBS_MINUS_LIGHTING = 16384;
public static final int VANILLA_BYTES_PER_EBS_MINUS_LIGHTING = 8192;

public static final int MAX_DATA_WATCHER_ID = 127;
Expand Down
20 changes: 19 additions & 1 deletion src/main/java/com/gtnewhorizons/neid/NEID.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
package com.gtnewhorizons.neid;

import com.gtnewhorizon.gtnhlib.config.ConfigException;
import com.gtnewhorizon.gtnhlib.config.ConfigurationManager;

import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;

@Mod(modid = "neid", name = "NotEnoughIDs", version = Tags.VERSION, dependencies = "after:battlegear2@[1.3.0,);")
@Mod(
modid = "neid",
name = "NotEnoughIDs",
version = Tags.VERSION,
dependencies = "after:battlegear2@[1.3.0,);" + " required-after:gtnhlib@[0.2.1,);")
public class NEID {

@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
try {
ConfigurationManager.registerConfig(NEIDConfig.class);
} catch (ConfigException e) {
throw new RuntimeException("Failed to register NotEnoughIDs config!");
}
}

}
43 changes: 17 additions & 26 deletions src/main/java/com/gtnewhorizons/neid/NEIDConfig.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
package com.gtnewhorizons.neid;

import java.io.File;

import net.minecraft.launchwrapper.Launch;
import net.minecraftforge.common.config.Configuration;
import com.gtnewhorizon.gtnhlib.config.Config;

/**
* The modid registered here is uppercased, this differs from the actual mod id in that the real one is lowercase. This
* is done because the old pre GTNHLib config file was named uppercase, so we're just keeping that.
*/
@Config(modid = "NEID", category = "neid")
public class NEIDConfig {

static Configuration config;
public static boolean catchUnregisteredBlocks;
public static boolean removeInvalidBlocks;
public static boolean postNeidWorldsSupport;
public static boolean extendDataWatcher;
@Config.Comment("Causes a crash when a block has not been registered(e.g. has an id of -1)")
public static boolean CatchUnregisteredBlocks = false;

@Config.Comment("Remove invalid (corrupted) blocks from the game.")
public static boolean RemoveInvalidBlocks = false;

@Config.Comment("If true, only blocks with IDs > 4095 will disappear after removing NEID. Metadatas outside of the range 0-15 will be set to 0.")
public static boolean PostNeidWorldsSupport = true;

@Config.Comment("Extend DataWatch IDs. Vanilla limit is 31, new limit is 127.")
public static boolean ExtendDataWatcher = false;

static {
NEIDConfig.config = new Configuration(new File(Launch.minecraftHome, "config/NEID.cfg"));
NEIDConfig.catchUnregisteredBlocks = NEIDConfig.config.getBoolean("CatchUnregisteredBlocks", "NEID", false, "");
NEIDConfig.removeInvalidBlocks = NEIDConfig.config
.getBoolean("RemoveInvalidBlocks", "NEID", false, "Remove invalid (corrupted) blocks from the game.");
NEIDConfig.postNeidWorldsSupport = NEIDConfig.config.getBoolean(
"PostNeidWorldsSupport",
"NEID",
true,
"If true, only blocks with IDs > 4095 will disappear after removing NEID.");
NEIDConfig.extendDataWatcher = NEIDConfig.config.getBoolean(
"ExtendDataWatcher",
"NEID",
false,
"Extend DataWatcher IDs. Vanilla limit is 31, new limit is 127.");
NEIDConfig.config.save();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public String[] getTargetClass() {

@Override
public void transform(final ClassNode cn, final boolean obfuscated) {
if (NEIDConfig.extendDataWatcher) {
if (NEIDConfig.ExtendDataWatcher) {
final MethodNode method = AsmUtil.findMethod(cn, Name.MFQM_preInit);
AsmUtil.modifyIntConstantInMethod(method, 31, 127);
}
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/gtnewhorizons/neid/mixins/Mixins.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public enum Mixins {
"minecraft.MixinS24PacketBlockAction",
"minecraft.MixinS26PacketMapChunkBulk",
"minecraft.MixinItemInWorldManager",
"minecraft.MixinAnvilChunkLoader"
"minecraft.MixinAnvilChunkLoader",
"minecraft.MixinBlock"
).setApplyIf(() -> true)),
VANILLA_STARTUP_CLIENT(new Builder("Start Vanilla Client").addTargetedMod(TargetedMod.VANILLA)
.setSide(Side.CLIENT).setPhase(Phase.EARLY).addMixinClasses(
Expand All @@ -37,7 +38,7 @@ public enum Mixins {
VANILLA_STARTUP_DATAWATCHER(new Builder("Start Vanilla DataWatcher").addTargetedMod(TargetedMod.VANILLA)
.setSide(Side.BOTH).setPhase(Phase.EARLY).addMixinClasses(
"minecraft.MixinDataWatcher"
).setApplyIf(() -> NEIDConfig.extendDataWatcher));
).setApplyIf(() -> NEIDConfig.ExtendDataWatcher));
// spotless:on
private final List<String> mixinClasses;
private final List<TargetedMod> targetedMods;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.gtnewhorizons.neid.mixins.early.minecraft;

import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.world.chunk.NibbleArray;
import net.minecraft.world.chunk.storage.AnvilChunkLoader;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;

Expand All @@ -23,11 +24,11 @@ public class MixinAnvilChunkLoader {
target = "Lnet/minecraft/nbt/NBTTagCompound;setByteArray(Ljava/lang/String;[B)V",
ordinal = 0),
require = 1)
private void neid$overrideSetByteArray(NBTTagCompound nbt, String s, byte[] oldbrokenbytes,
private void neid$overrideWriteLSBArray(NBTTagCompound nbt, String s, byte[] oldbrokenbytes,
@Local(ordinal = 0) ExtendedBlockStorage ebs) {
IExtendedBlockStorageMixin ebsMixin = (IExtendedBlockStorageMixin) ebs;
nbt.setByteArray("Blocks16", ebsMixin.getBlockData());
if (NEIDConfig.postNeidWorldsSupport) {
if (NEIDConfig.PostNeidWorldsSupport) {
final short[] data = ebsMixin.getBlock16BArray();
final byte[] lsbData = new byte[data.length];
byte[] msbData = null;
Expand Down Expand Up @@ -58,13 +59,45 @@ public class MixinAnvilChunkLoader {
}
}

@Redirect(
method = "writeChunkToNBT",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/nbt/NBTTagCompound;setByteArray(Ljava/lang/String;[B)V",
ordinal = 2),
require = 1)
private void neid$overrideWriteMetadataArray(NBTTagCompound nbt, String s, byte[] oldbrokenbytes,
@Local(ordinal = 0) ExtendedBlockStorage ebs) {
IExtendedBlockStorageMixin ebsMixin = (IExtendedBlockStorageMixin) ebs;
nbt.setByteArray("Data16", ebsMixin.getBlockMeta());
if (NEIDConfig.PostNeidWorldsSupport) {
final short[] data = ebsMixin.getBlock16BMetaArray();
final byte[] metaData = new byte[data.length / 2];
for (int i = 0; i < data.length; i += 2) {
int meta1 = data[i];
int meta2 = data[i + 1];

if (meta1 < 0 || meta1 > 15) {
meta1 = 0;
}
if (meta2 < 0 || meta2 > 15) {
meta2 = 0;
}

metaData[i / 2] = (byte) (meta2 << 4 | meta1);
final int meta = data[i];
}
nbt.setByteArray("Data", metaData);
}
}

@Redirect(
method = "readChunkFromNBT",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;setBlockLSBArray([B)V"),
require = 1)
private void neid$overrideSetLSBArray(ExtendedBlockStorage ebs, byte[] oldbrokenbytes,
private void neid$overrideReadLSBArray(ExtendedBlockStorage ebs, byte[] oldbrokenbytes,
@Local(ordinal = 1) NBTTagCompound nbt) {
IExtendedBlockStorageMixin ebsMixin = (IExtendedBlockStorageMixin) ebs;
if (nbt.hasKey("Blocks16")) {
Expand Down Expand Up @@ -96,7 +129,31 @@ public class MixinAnvilChunkLoader {
target = "Lnet/minecraft/nbt/NBTTagCompound;hasKey(Ljava/lang/String;I)Z",
ordinal = 0),
require = 1)
private boolean neid$nukeMSBCheck(NBTTagCompound nbttagcompound1, String s, int i) {
private boolean neid$overrideReadMSBArray(NBTTagCompound nbttagcompound1, String s, int i) {
return false;
}

@Redirect(
method = "readChunkFromNBT",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/chunk/storage/ExtendedBlockStorage;setBlockMetadataArray(Lnet/minecraft/world/chunk/NibbleArray;)V"),
require = 1)
private void neid$overrideReadMetadataArray(ExtendedBlockStorage ebs, NibbleArray oldBrokenNibbleArray,
@Local(ordinal = 1) NBTTagCompound nbt) {
IExtendedBlockStorageMixin ebsMixin = (IExtendedBlockStorageMixin) ebs;
if (nbt.hasKey("Data16")) {
ebsMixin.setBlockMeta(nbt.getByteArray("Data16"), 0);
} else if (nbt.hasKey("Data")) {
final short[] out = ebsMixin.getBlock16BMetaArray();
final byte[] metaData = nbt.getByteArray("Data");
for (int i = 0; i < out.length; i += 2) {
final byte meta = metaData[i / 2];
out[i] = (short) (meta & 0xF);
out[i + 1] = (short) ((meta >> 4) & 0xF);
}
} else {
assert false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.gtnewhorizons.neid.mixins.early.minecraft;

import net.minecraft.block.Block;
import net.minecraft.init.Blocks;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;

import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;

/**
* This mixin exists to completely re-work the way that block harvestability is calculated. Forge adds this system
* on-top of vanilla, which creates a pre-populated array on each block with the size of the maximum metadata value. For
* instance, in vanilla we get a String array, and an int array both of size 16, pre-populated with null and -1,
* respectively. This is awful for scalability with adding more possible Blocks, and increasing the metadata to 16-bits
* from 4. This uproots the under-workings of that system to use a default and dynamic HashMaps for metadata specific
* values, so we only use the memory we need, because most things don't even take advantage of this system, especially
* based on block metadatas. The public methods of this system maintain API compatibility, so anything using those
* should still function perfectly the same. The only potential breakage is if something were to reflect/ASM/mixin with
* the private values/methods we're overwriting.
*/
@Mixin(Block.class)
public class MixinBlock {

@Unique
private String neid$defaultHarvestTool = null;

@Unique
private int neid$defaultHarvestLevel = -1;

@Unique
private Int2ObjectOpenHashMap<String> harvestToolMap = new Int2ObjectOpenHashMap<>();

@Unique
private Int2IntOpenHashMap harvestLevelMap = new Int2IntOpenHashMap();

@Shadow(remap = false)
private String[] harvestTool = null;

@Shadow(remap = false)
private int[] harvestLevel = null;

/**
* @author Cleptomania
* @reason Support 16-bit metadata without using GBs of memory. Classdump of full GTNH shows nothing else
* ASMing/mixin to this.
*/
@Overwrite(remap = false)
public void setHarvestLevel(String toolClass, int level) {
this.neid$defaultHarvestTool = toolClass;
this.neid$defaultHarvestLevel = level;
}

/**
* @author Cleptomania
* @reason Support 16-bit metadata without using GBs of memory. Classdump of full GTNH shows nothing else
* ASMing/mixin to this.
*/
@Overwrite(remap = false)
public void setHarvestLevel(String toolClass, int level, int metadata) {
this.harvestToolMap.put(metadata, toolClass);
this.harvestLevelMap.put(metadata, level);
}

/**
* @author Cleptomania
* @reason Support 16-bit metadata without using GBs of memory. Classdump of full GTNH shows nothing else
* ASMing/mixin to this.
*/
@Overwrite(remap = false)
public String getHarvestTool(int metadata) {
return this.harvestToolMap.getOrDefault(metadata, this.neid$defaultHarvestTool);
}

/**
* @author Cleptmania
* @reason Support 16-bit metadata without using GBs of memory. Classdump of full GTNH shows nothing else
* ASMing/mixin to this.
*/
@Overwrite(remap = false)
public int getHarvestLevel(int metadata) {
return this.harvestLevelMap.getOrDefault(metadata, this.neid$defaultHarvestLevel);
}

/**
* @author Cleptomania
* @reason Support 16-bit metadata without using GBs of memory. Classdump of full GTNH shows nothing else
* ASMing/mixin to this.
*/
@Overwrite(remap = false)
public boolean isToolEffective(String type, int metadata) {
Block self = ((Block) (Object) this);
if ("pickaxe".equals(type)
&& (self == Blocks.redstone_ore || self == Blocks.lit_redstone_ore || self == Blocks.obsidian))
return false;
String harvestTool = this.getHarvestTool(metadata);
if (harvestTool == null) return false;
return harvestTool.equals(type);
}

}
Loading

0 comments on commit 57304c6

Please sign in to comment.