Add Trial Spawner support and improve config handling
Introduces mixins and accessors for Trial Spawner logic, enabling Silk Touch mining and correct data transfer for Trial Spawners. Refactors config file handling to use FabricLoader and Path APIs for better compatibility. Updates mod metadata, adds a logo, and improves dropped item naming for both spawner types.
This commit is contained in:
@@ -1,8 +1,14 @@
|
||||
package com.kasetoatz.silkspawners.accessors;
|
||||
|
||||
import net.minecraft.block.spawner.TrialSpawnerData;
|
||||
import net.minecraft.block.spawner.TrialSpawnerLogic;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface ITrialSpawnerLogic
|
||||
{
|
||||
TrialSpawnerLogic.FullConfig silkspawners$getFullConfig();
|
||||
void silkspawners$setFullConfig(World world, TrialSpawnerLogic.FullConfig fullConfig);
|
||||
void silkspawners$setData(TrialSpawnerData data);
|
||||
Text silkspawners$getEntityName(World world);
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@ package com.kasetoatz.silkspawners.config;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.util.crash.CrashException;
|
||||
import net.minecraft.util.crash.CrashReport;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class Config
|
||||
{
|
||||
@@ -19,34 +18,34 @@ public class Config
|
||||
public static boolean REQUIRE_PICKAXE = true;
|
||||
public static boolean REQUIRE_SILK_TOUCH = true;
|
||||
|
||||
private static final File config = new File(MinecraftClient.getInstance().runDirectory, "config/silkspawners.json");
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final Path FILE = FabricLoader.getInstance().getConfigDir().resolve("silkspawners.json");
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
public static void load()
|
||||
{
|
||||
if (!config.exists())
|
||||
if (!Files.exists(FILE))
|
||||
{
|
||||
save();
|
||||
return;
|
||||
}
|
||||
try (FileReader reader = new FileReader(config))
|
||||
try
|
||||
{
|
||||
JsonObject json = gson.fromJson(reader, JsonObject.class);
|
||||
if (json.has("enable-spawner"))
|
||||
JsonObject data = GSON.fromJson(Files.readString(FILE), JsonObject.class);
|
||||
if (data.has("enable-spawner"))
|
||||
{
|
||||
ENABLE_SPAWNER = json.get("enable-spawner").getAsBoolean();
|
||||
ENABLE_SPAWNER = data.get("enable-spawner").getAsBoolean();
|
||||
}
|
||||
if (json.has("enable-trial-spawner"))
|
||||
if (data.has("enable-trial-spawner"))
|
||||
{
|
||||
ENABLE_TRIAL_SPAWNER = json.get("enable-trial-spawner").getAsBoolean();
|
||||
ENABLE_TRIAL_SPAWNER = data.get("enable-trial-spawner").getAsBoolean();
|
||||
}
|
||||
if (json.has("require-pickaxe"))
|
||||
if (data.has("require-pickaxe"))
|
||||
{
|
||||
REQUIRE_PICKAXE = json.get("require-pickaxe").getAsBoolean();
|
||||
REQUIRE_PICKAXE = data.get("require-pickaxe").getAsBoolean();
|
||||
}
|
||||
if (json.has("require-silk-touch"))
|
||||
if (data.has("require-silk-touch"))
|
||||
{
|
||||
REQUIRE_SILK_TOUCH = json.get("require-silk-touch").getAsBoolean();
|
||||
REQUIRE_SILK_TOUCH = data.get("require-silk-touch").getAsBoolean();
|
||||
}
|
||||
save();
|
||||
}
|
||||
@@ -58,14 +57,14 @@ public class Config
|
||||
|
||||
public static void save()
|
||||
{
|
||||
JsonObject json = new JsonObject();
|
||||
json.addProperty("enable-spawner", ENABLE_SPAWNER);
|
||||
json.addProperty("enable-trial-spawner", ENABLE_TRIAL_SPAWNER);
|
||||
json.addProperty("require-pickaxe", REQUIRE_PICKAXE);
|
||||
json.addProperty("require-silk-touch", REQUIRE_SILK_TOUCH);
|
||||
try (FileWriter writer = new FileWriter(config))
|
||||
try
|
||||
{
|
||||
gson.toJson(json, writer);
|
||||
JsonObject data = new JsonObject();
|
||||
data.addProperty("enable-spawner", ENABLE_SPAWNER);
|
||||
data.addProperty("enable-trial-spawner", ENABLE_TRIAL_SPAWNER);
|
||||
data.addProperty("require-pickaxe", REQUIRE_PICKAXE);
|
||||
data.addProperty("require-silk-touch", REQUIRE_SILK_TOUCH);
|
||||
Files.writeString(FILE, GSON.toJson(data));
|
||||
}
|
||||
catch (IOException exc)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.kasetoatz.silkspawners.mixin;
|
||||
|
||||
import com.kasetoatz.silkspawners.accessors.ITrialSpawnerLogic;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.MobSpawnerBlockEntity;
|
||||
import net.minecraft.block.entity.TrialSpawnerBlockEntity;
|
||||
import net.minecraft.block.enums.TrialSpawnerState;
|
||||
import net.minecraft.block.spawner.TrialSpawnerData;
|
||||
import net.minecraft.block.spawner.TrialSpawnerLogic;
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.component.type.NbtComponent;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
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 java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(BlockItem.class)
|
||||
public abstract class BlockItemMixin
|
||||
{
|
||||
@Inject(method="copyComponentsToBlockEntity", at=@At(value="INVOKE", target="Lnet/minecraft/block/entity/BlockEntity;markDirty()V"))
|
||||
private static void copyComponentsToBlockEntity(World world, BlockPos pos, ItemStack stack, CallbackInfo ci, @Local BlockEntity entity)
|
||||
{
|
||||
if (entity instanceof MobSpawnerBlockEntity spawner)
|
||||
{
|
||||
Optional<EntityType<?>> entityType = Objects.requireNonNullElse(stack.get(DataComponentTypes.CUSTOM_DATA), NbtComponent.DEFAULT).copyNbt().get("entityType", EntityType.CODEC);
|
||||
if (entityType.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
spawner.setEntityType(entityType.get(), world.getRandom());
|
||||
}
|
||||
else if (entity instanceof TrialSpawnerBlockEntity trialSpawner)
|
||||
{
|
||||
NbtCompound nbt = Objects.requireNonNullElse(stack.get(DataComponentTypes.CUSTOM_DATA), NbtComponent.DEFAULT).copyNbt();
|
||||
Optional<TrialSpawnerLogic.FullConfig> fullConfig = nbt.get("fullConfig", TrialSpawnerLogic.FullConfig.CODEC.codec());
|
||||
Optional<TrialSpawnerData.Packed> data = nbt.get("data", TrialSpawnerData.Packed.CODEC.codec());
|
||||
Optional<String> state = nbt.getString("state");
|
||||
ITrialSpawnerLogic logic = ((ITrialSpawnerLogic)(Object)trialSpawner.getSpawner());
|
||||
if (fullConfig.isEmpty() || data.isEmpty() || state.isEmpty() || logic == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
logic.silkspawners$setFullConfig(world, fullConfig.get());
|
||||
TrialSpawnerData newData = new TrialSpawnerData();
|
||||
newData.unpack(data.get());
|
||||
logic.silkspawners$setData(newData);
|
||||
trialSpawner.setSpawnerState(world, TrialSpawnerState.valueOf(state.get()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,27 +2,37 @@ package com.kasetoatz.silkspawners.mixin;
|
||||
|
||||
import com.kasetoatz.silkspawners.accessors.IMobSpawnerLogic;
|
||||
import com.kasetoatz.silkspawners.accessors.ITrialSpawnerLogic;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.MobSpawnerBlockEntity;
|
||||
import net.minecraft.block.entity.Spawner;
|
||||
import net.minecraft.block.entity.TrialSpawnerBlockEntity;
|
||||
import net.minecraft.block.spawner.TrialSpawnerData;
|
||||
import net.minecraft.block.spawner.TrialSpawnerLogic;
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.component.type.NbtComponent;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.enchantment.Enchantments;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.tag.ItemTags;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Rarity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@@ -32,46 +42,62 @@ import static com.kasetoatz.silkspawners.config.Config.*;
|
||||
@Mixin(Block.class)
|
||||
public class BlockMixin
|
||||
{
|
||||
@Redirect(method="dropStacks(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/entity/BlockEntity;Lnet/minecraft/entity/Entity;Lnet/minecraft/item/ItemStack;)V", at=@At(value="INVOKE", target="Lnet/minecraft/block/Block;getDroppedStacks(Lnet/minecraft/block/BlockState;Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/entity/BlockEntity;Lnet/minecraft/entity/Entity;Lnet/minecraft/item/ItemStack;)Ljava/util/List;"))
|
||||
private static List<ItemStack> getDroppedStacks(BlockState state, ServerWorld world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack stack)
|
||||
@Unique
|
||||
private static boolean shouldNotDrop(BlockEntity entity, Block block, ItemStack tool, Registry<Enchantment> enchantments) {
|
||||
return !(entity instanceof Spawner) ||
|
||||
(!ENABLE_SPAWNER && block instanceof SpawnerBlock) ||
|
||||
(!ENABLE_TRIAL_SPAWNER && block instanceof TrialSpawnerBlock) ||
|
||||
(REQUIRE_PICKAXE && !tool.isIn(ItemTags.PICKAXES)) ||
|
||||
(REQUIRE_SILK_TOUCH && EnchantmentHelper.getLevel(enchantments.getOrThrow(Enchantments.SILK_TOUCH), tool) < 1);
|
||||
}
|
||||
|
||||
@Inject(method="getDroppedStacks(Lnet/minecraft/block/BlockState;Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/entity/BlockEntity;Lnet/minecraft/entity/Entity;Lnet/minecraft/item/ItemStack;)Ljava/util/List;", at=@At("HEAD"), cancellable=true)
|
||||
private static void getDroppedStacks(BlockState state, ServerWorld world, BlockPos pos, @Nullable BlockEntity blockEntity, @Nullable Entity entity, ItemStack tool, CallbackInfoReturnable<List<ItemStack>> cir)
|
||||
{
|
||||
System.out.println(stack);
|
||||
if (!(blockEntity instanceof Spawner))
|
||||
if (shouldNotDrop(blockEntity, state.getBlock(), tool, world.getRegistryManager().getOrThrow(RegistryKeys.ENCHANTMENT)))
|
||||
{
|
||||
return getDroppedStacks(state, world, pos, blockEntity, entity, stack);
|
||||
}
|
||||
if (!ENABLE_SPAWNER && state.getBlock() instanceof SpawnerBlock)
|
||||
{
|
||||
return List.of();
|
||||
}
|
||||
if (!ENABLE_TRIAL_SPAWNER && state.getBlock() instanceof TrialSpawnerBlock)
|
||||
{
|
||||
return List.of();
|
||||
}
|
||||
if (REQUIRE_PICKAXE && !stack.isIn(ItemTags.PICKAXES))
|
||||
{
|
||||
return List.of();
|
||||
}
|
||||
if (REQUIRE_SILK_TOUCH && EnchantmentHelper.getLevel(world.getRegistryManager().getOrThrow(RegistryKeys.ENCHANTMENT).getOrThrow(Enchantments.SILK_TOUCH), stack) < 1)
|
||||
{
|
||||
return List.of();
|
||||
return;
|
||||
}
|
||||
ItemStack item = state.getBlock().asItem().getDefaultStack();
|
||||
NbtComponent component = Objects.requireNonNullElse(item.get(DataComponentTypes.CUSTOM_DATA), NbtComponent.DEFAULT);
|
||||
if (blockEntity instanceof MobSpawnerBlockEntity spawner)
|
||||
{
|
||||
EntityType<?> type = ((IMobSpawnerLogic)spawner.getLogic()).silkspawners$getEntityType(world, world.getRandom(), pos);
|
||||
Text name = Text.translatable("block.minecraft.spawner").formatted(Rarity.RARE.getFormatting());
|
||||
if (type != null)
|
||||
{
|
||||
component.apply(nbtCompound -> nbtCompound.put("entityType", EntityType.CODEC, type));
|
||||
component = component.apply(nbtCompound -> nbtCompound.put("entityType", EntityType.CODEC, type));
|
||||
name = Text.empty().append(type.getName()).append(" ").append(Text.translatable("block.minecraft.spawner")).formatted(Rarity.RARE.getFormatting());
|
||||
}
|
||||
|
||||
item.set(DataComponentTypes.ITEM_NAME, name);
|
||||
}
|
||||
else if (blockEntity instanceof TrialSpawnerBlockEntity trialSpawner)
|
||||
{
|
||||
component.apply(nbtCompound -> nbtCompound.put("fullConfig", TrialSpawnerLogic.FullConfig.CODEC.codec(), ((ITrialSpawnerLogic)(Object)trialSpawner.getSpawner()).silkspawners$getFullConfig()));
|
||||
ITrialSpawnerLogic logic = (ITrialSpawnerLogic)(Object)trialSpawner.getSpawner();
|
||||
if (logic == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
component = component.apply(nbtCompound -> {
|
||||
nbtCompound.put("fullConfig", TrialSpawnerLogic.FullConfig.CODEC.codec(), logic.silkspawners$getFullConfig());
|
||||
nbtCompound.put("data", TrialSpawnerData.Packed.CODEC.codec(), trialSpawner.getSpawner().getData().pack());
|
||||
nbtCompound.putString("state", trialSpawner.getSpawnerState().name());
|
||||
});
|
||||
Text entityName = logic.silkspawners$getEntityName(world);
|
||||
Text name = Text.translatable("block.minecraft.trial_spawner").formatted(Rarity.RARE.getFormatting());
|
||||
if (entityName != null)
|
||||
{
|
||||
name = Text.empty().append(logic.silkspawners$getEntityName(world)).append(" ").append(Text.translatable("block.minecraft.trial_spawner")).formatted(Rarity.RARE.getFormatting());
|
||||
}
|
||||
item.set(DataComponentTypes.ITEM_NAME, name);
|
||||
}
|
||||
item.set(DataComponentTypes.CUSTOM_DATA, component);
|
||||
return List.of(item);
|
||||
cir.setReturnValue(List.of(item));
|
||||
}
|
||||
|
||||
@ModifyArg(method="dropStacks(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/entity/BlockEntity;Lnet/minecraft/entity/Entity;Lnet/minecraft/item/ItemStack;)V", at=@At(value="INVOKE", target="Lnet/minecraft/block/BlockState;onStacksDropped(Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/item/ItemStack;Z)V"), index=3)
|
||||
private static boolean dropStacks(boolean original, @Local(argsOnly=true) BlockState state, @Local(argsOnly=true) World world, @Local(argsOnly=true) BlockEntity blockEntity, @Local(argsOnly=true) ItemStack tool)
|
||||
{
|
||||
return shouldNotDrop(blockEntity, state.getBlock(), tool, world.getRegistryManager().getOrThrow(RegistryKeys.ENCHANTMENT));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,67 @@
|
||||
package com.kasetoatz.silkspawners.mixin;
|
||||
|
||||
import com.kasetoatz.silkspawners.accessors.ITrialSpawnerLogic;
|
||||
import net.minecraft.block.spawner.MobSpawnerEntry;
|
||||
import net.minecraft.block.spawner.TrialSpawnerConfig;
|
||||
import net.minecraft.block.spawner.TrialSpawnerData;
|
||||
import net.minecraft.block.spawner.TrialSpawnerLogic;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.storage.NbtReadView;
|
||||
import net.minecraft.storage.ReadView;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.ErrorReporter;
|
||||
import net.minecraft.world.World;
|
||||
import org.slf4j.Logger;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Mixin(TrialSpawnerLogic.class)
|
||||
public class TrialSpawnerLogicMixin implements ITrialSpawnerLogic
|
||||
public abstract class TrialSpawnerLogicMixin implements ITrialSpawnerLogic
|
||||
{
|
||||
@Shadow private TrialSpawnerLogic.FullConfig fullConfig;
|
||||
@Mutable @Shadow @Final private TrialSpawnerData data;
|
||||
@Shadow @Final private static Logger LOGGER;
|
||||
@Shadow public abstract TrialSpawnerConfig getConfig();
|
||||
|
||||
@Override
|
||||
public TrialSpawnerLogic.FullConfig silkspawners$getFullConfig()
|
||||
{
|
||||
return fullConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void silkspawners$setFullConfig(World world, TrialSpawnerLogic.FullConfig fullConfig)
|
||||
{
|
||||
this.fullConfig = fullConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void silkspawners$setData(TrialSpawnerData data)
|
||||
{
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Text silkspawners$getEntityName(World world)
|
||||
{
|
||||
try (ErrorReporter.Logging logging = new ErrorReporter.Logging(LOGGER))
|
||||
{
|
||||
Optional<MobSpawnerEntry> entry = getConfig().spawnPotentials().getOrEmpty(world.getRandom());
|
||||
if (entry.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
ReadView readView = NbtReadView.create(logging, world.getRegistryManager(), entry.get().entity());
|
||||
Optional<EntityType<?>> entity = EntityType.fromData(readView);
|
||||
return entity.map(EntityType::getName).orElse(null);
|
||||
}
|
||||
catch (Exception ignored)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
"id": "silkspawners",
|
||||
"version": "${version}",
|
||||
"name": "SilkSpawners",
|
||||
"description": "",
|
||||
"authors": [],
|
||||
"contact": {},
|
||||
"description": "Mod that allows mining Spawners & Trial Spawners with Silk Touch.",
|
||||
"authors": ["KaseToatz"],
|
||||
"contact": {
|
||||
"email": "kasetoatz@kasetoatz.com",
|
||||
"homepage": "https://modrinth.com/mod/silkspawners",
|
||||
"issues": "https://git.kasetoatz.com/KaseToatz/SilkSpawners/issues",
|
||||
"sources": "https://git.kasetoatz.com/KaseToatz/SilkSpawners"
|
||||
},
|
||||
"license": "MIT",
|
||||
"icon": "assets/silkspawners/icon.png",
|
||||
"icon": "logo.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
|
||||
BIN
src/main/resources/logo.png
Normal file
BIN
src/main/resources/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
@@ -4,6 +4,7 @@
|
||||
"package": "com.kasetoatz.silkspawners.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"mixins": [
|
||||
"BlockItemMixin",
|
||||
"BlockMixin",
|
||||
"MobSpawnerLogicMixin",
|
||||
"TrialSpawnerLogicMixin"
|
||||
|
||||
Reference in New Issue
Block a user