diff --git a/settings.gradle b/settings.gradle index 98c5241..49ddd33 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,7 +13,8 @@ pluginManagement { } } -include 'textile-api-base' -include("textile-api-gui") -include 'textile-lifecycle-events-v1' -include("textile-recipe-api-v1") +include('textile-api-base') +include('textile-api-gui') +include('textile-lifecycle-events-v1') +include('textile-recipe-api-v1') +include('textile-utils-api-v1') diff --git a/textile-utils-api-v1/build.gradle b/textile-utils-api-v1/build.gradle new file mode 100644 index 0000000..cd0588d --- /dev/null +++ b/textile-utils-api-v1/build.gradle @@ -0,0 +1,10 @@ +archivesBaseName = 'textile-utils-api-v1' +version = '1.0.0' + +dependencies { + implementation project(':textile-api-base') +} + +minecraft { + accessWidener "src/main/resources/textile-utils-api-v1.accessWidener" +} diff --git a/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/Block/CustomBlock.java b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/Block/CustomBlock.java new file mode 100644 index 0000000..52eb08b --- /dev/null +++ b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/Block/CustomBlock.java @@ -0,0 +1,7 @@ +package net.textilemc.textile.utils.Block; + +import net.textilemc.textile.utils.util.Identifier; + +public interface CustomBlock { + Identifier getStringId(); +} diff --git a/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/Pair.java b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/Pair.java new file mode 100644 index 0000000..3dec4b1 --- /dev/null +++ b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/Pair.java @@ -0,0 +1,16 @@ +package net.textilemc.textile.utils; + +public class Pair { + private final T left; + private final M right; + public Pair(T left, M right) { + this.left = left; + this.right = right; + } + public T getLeft() { + return left; + } + public M getRight() { + return right; + } +} diff --git a/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/RegistrationManagerEntrypoint.java b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/RegistrationManagerEntrypoint.java new file mode 100644 index 0000000..01320c7 --- /dev/null +++ b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/RegistrationManagerEntrypoint.java @@ -0,0 +1,25 @@ +package net.textilemc.textile.utils; + +import net.fabricmc.api.ModInitializer; +import net.minecraft.block.Block; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class RegistrationManagerEntrypoint implements ModInitializer { + + public static Logger LOGGER = LogManager.getLogger("Textile API"); + + @Override + public void onInitialize () { + LOGGER.info("Block registrar - Loading up!"); + } + + // Cannot return 0 or else MC will hang on the TitleScreen + public static int getNextBlockId() { + for (int i = 1; i < Block.BLOCKS.length; i++) { + if (Block.BLOCKS[i] == null) + return i; + } + return -1; + } +} diff --git a/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/SidedRegistry.java b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/SidedRegistry.java new file mode 100644 index 0000000..fd3732d --- /dev/null +++ b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/SidedRegistry.java @@ -0,0 +1,18 @@ +package net.textilemc.textile.utils; + +import net.minecraft.block.Block; +import net.minecraft.block.Material; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; + +public class SidedRegistry { + + public static Pair registerBlockWithItem(int id, Material material) { + Block BLOCK = new Block(id, material); + if (Block.BLOCKS[id] != null) { + Item.ITEMS[id] = new BlockItem(id - 256); + } + return new Pair<>(BLOCK, Item.ITEMS[id]); + } + +} diff --git a/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/mixin/BlockCacherMixin.java b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/mixin/BlockCacherMixin.java new file mode 100644 index 0000000..8a4d5a5 --- /dev/null +++ b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/mixin/BlockCacherMixin.java @@ -0,0 +1,123 @@ +package net.textilemc.textile.utils.mixin; + +import net.minecraft.block.Block; +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.Item; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.util.Util; +import net.textilemc.textile.utils.Block.CustomBlock; +import net.textilemc.textile.utils.util.Identifier; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + +@Mixin(MinecraftClient.class) +public abstract class BlockCacherMixin { + + @Shadow + private static File mcDir; + + @Unique + private String name; + + //Before you ask, yes, caching is necessary in this case. It fails otherwise complaining about "World cannot be cast to String" + @Inject(at=@At("HEAD"), method = "joinWorld(Ljava/lang/String;)V") + public void cacheName(String name, CallbackInfo info) { + this.name = name; + } + + @Inject(at=@At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;joinWorld(Lnet/minecraft/world/World;Ljava/lang/String;)V", ordinal = 1), method = "joinWorld(Ljava/lang/String;)V") + public void removeConflictOlderVersion(CallbackInfo info) { + mapBlockToDat(new File(new File(mcDir, "saves"), name)); + + } + @Inject(at=@At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;joinWorld(Lnet/minecraft/world/World;Ljava/lang/String;)V", ordinal = 2), method = "joinWorld(Ljava/lang/String;)V") + public void removeConflictOlderVersion2(CallbackInfo info) { + mapBlockToDat(new File(new File(mcDir, "saves"), name)); + } + + @Unique + private void mapBlockToDat(File saveDir) { + if (!(new File(saveDir, "block_registry.dat")).exists()) { + System.out.println("Saving Block Registry to .minecraft/saves/" + name + "/block_registry.dat"); + CompoundTag tag = new CompoundTag(); + for (int i = 0; i < Block.BLOCKS.length; i++) { + /*if (Block.BLOCKS[i] == null) { + } else */if (!(Block.BLOCKS[i] instanceof CustomBlock)) { + CompoundTag subTag = new CompoundTag(); + subTag.putString("name", "$vanilla$"); + subTag.putByte("id", (byte) i); + tag.put("block" + i, subTag); + } else if (Block.BLOCKS[i] instanceof CustomBlock) { + CompoundTag subtag = new CompoundTag(); + subtag.putString("name", ""); + Identifier stringId = ((CustomBlock) Block.BLOCKS[i]).getStringId(); + stringId.toTag(subtag); + subtag.putByte("id", (byte) i); + tag.put("block" + i, subtag); + } + } + try { + Util.writeCompressed(tag, new FileOutputStream(new File(saveDir, "block_registry.dat"))); + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw new NullPointerException(); + } + } else { + System.out.println("Matching old Block Registry read from .minecraft/saves/"+name+"/block_registry.dat"); + CompoundTag tagFromFile; + try { + tagFromFile = Util.readCompressed(new FileInputStream(new File(saveDir, "block_registry.dat"))); + } catch (FileNotFoundException e) { + e.printStackTrace(); + throw new NullPointerException(); + } + Block[] temp = new Block[256]; + Block[] finalArray = new Block[256]; + Item[] tempItem = new Item[1024]; + Item[] finalItem = new Item[1024]; + System.arraycopy(Item.ITEMS, 0, tempItem, 0, tempItem.length); + System.arraycopy(Block.BLOCKS, 0, temp, 0, temp.length); + for (int i = 0; i < 256; i++) { + CompoundTag blockEntry = tagFromFile.getCompound("block"+i); + Identifier identifier; + if (!blockEntry.getString("name").equals("")) { + //name = blockEntry.getString("name"); + finalArray[i] = temp[i]; + finalItem[i] = tempItem[i]; + } else { + identifier = Identifier.fromTag(blockEntry); + int id = blockEntry.getByte("id"); + int currentId = temp[getCurrentIdFromBlock(identifier)].id; + finalArray[i] = temp[currentId]; + finalArray[i].id = id; + finalItem[i] = tempItem[currentId]; + finalItem[i].id = id; + } + + } + Block.BLOCKS = finalArray; + Item.ITEMS = finalItem; + } + } + + @Unique + private static int getCurrentIdFromBlock(Identifier id) { + for (int i = 0; i < 256; i++) { + if (Block.BLOCKS[i] instanceof CustomBlock) { + if ((((CustomBlock) Block.BLOCKS[i]).getStringId()).equals(id)) { + return i; + } + } + } + throw new NullPointerException("Could not find block! You should only see this if you removed the mod which it was in, in which case your world will have corrupted!! Please make a new World!!"); + } +} diff --git a/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/util/Identifier.java b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/util/Identifier.java new file mode 100644 index 0000000..2a2dfbd --- /dev/null +++ b/textile-utils-api-v1/src/main/java/net/textilemc/textile/utils/util/Identifier.java @@ -0,0 +1,34 @@ +package net.textilemc.textile.utils.util; + +import net.minecraft.nbt.CompoundTag; + +public class Identifier { + private final String namespace; + private final String path; + + + public Identifier (String namespace, String path) { + this.namespace = namespace; + this.path = path; + } + + public String getNamespace() { + return namespace; + } + + public String getPath() { + return path; + } + + public CompoundTag toTag(CompoundTag tag) { + CompoundTag subtag = new CompoundTag(); + subtag.putString("namespace", this.getNamespace()); + subtag.putString("path", this.getPath()); + tag.put("nameId", subtag); + return tag; + } + + public static Identifier fromTag(CompoundTag tag) { + return new Identifier(tag.getString("namespace"), tag.getString("path")); + } +} diff --git a/textile-utils-api-v1/src/main/resources/assets/textile-utils-api-v1/icon.png b/textile-utils-api-v1/src/main/resources/assets/textile-utils-api-v1/icon.png new file mode 100644 index 0000000..a7bd480 Binary files /dev/null and b/textile-utils-api-v1/src/main/resources/assets/textile-utils-api-v1/icon.png differ diff --git a/textile-utils-api-v1/src/main/resources/fabric.mod.json b/textile-utils-api-v1/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..b3dcacb --- /dev/null +++ b/textile-utils-api-v1/src/main/resources/fabric.mod.json @@ -0,0 +1,28 @@ +{ + "schemaVersion": 1, + "id": "textile-utils-api-v1", + "version": "${version}", + "name": "Textile Utils API (v1)", + "description": "Infdev Utils", + "authors": [ + "TextileMC" + ], + "contact": { + "issues": "https://github.com/TextileMC/textile-api/issues", + "sources": "https://github.com/TextileMC/textile-api" + }, + "entrypoints": { + "main": ["net.textilemc.textile.utils.RegistrationManagerEntrypoint"] + }, + "license": "CC0-1.0", + "icon": "assets/textile-utils-api-v1/icon.png", + "environment": "*", + "accessWidener": "textile-utils-api-v1.accessWidener", + "mixins": [ + "textile-utils-api-v1.mixins.json" + ], + "depends": { + "fabricloader": "*", + "minecraft": "20100618" + } +} diff --git a/textile-utils-api-v1/src/main/resources/textile-utils-api-v1.accessWidener b/textile-utils-api-v1/src/main/resources/textile-utils-api-v1.accessWidener new file mode 100644 index 0000000..872e7bc --- /dev/null +++ b/textile-utils-api-v1/src/main/resources/textile-utils-api-v1.accessWidener @@ -0,0 +1,6 @@ +accessWidener v1 named +## Blocks +accessible method net/minecraft/block/Block (ILnet/minecraft/block/Material;)V +mutable field net/minecraft/block/Block id I +mutable field net/minecraft/item/Item id I +mutable field net/minecraft/block/Block BLOCKS [Lnet/minecraft/block/Block; diff --git a/textile-utils-api-v1/src/main/resources/textile-utils-api-v1.mixins.json b/textile-utils-api-v1/src/main/resources/textile-utils-api-v1.mixins.json new file mode 100644 index 0000000..1fd963c --- /dev/null +++ b/textile-utils-api-v1/src/main/resources/textile-utils-api-v1.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "net.textilemc.textile.utils.mixin", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "BlockCacherMixin" + ], + "injectors": { + "defaultRequire": 1 + } +}