Init for multiloader

This commit is contained in:
lukasabbe
2025-06-12 20:34:08 +02:00
commit 8f402847a0
78 changed files with 2313 additions and 0 deletions
@@ -0,0 +1,24 @@
package com.lukasabbe.bookshelfinspector;
import com.lukasabbe.bookshelfinspector.network.Handlers;
import com.lukasabbe.bookshelfinspector.network.packets.ModCheckPayload;
import com.lukasabbe.bookshelfinspector.platform.Services;
import net.minecraft.server.MinecraftServer;
public class BookshelfInspector {
public static MinecraftServer serverInstance;
public static Handlers networkHandlers;
public static void init() {
registerEvents();
networkHandlers = new Handlers();
}
private static void registerEvents(){
Services.EVENTS_HELPER.registerOnPlayerJoinEvent((player, server) -> {
serverInstance = server;
Services.NETWORK_HELPER.sendPacketFromServer(player, new ModCheckPayload(true));
});
}
}
@@ -0,0 +1,23 @@
package com.lukasabbe.bookshelfinspector;
import com.lukasabbe.bookshelfinspector.config.Config;
import com.lukasabbe.bookshelfinspector.data.BookData;
import com.lukasabbe.bookshelfinspector.data.BookShelfData;
import com.lukasabbe.bookshelfinspector.platform.Services;
public class BookshelfInspectorClient {
public static BookData currentBookData = BookData.empty();
public static BookShelfData bookShelfData = new BookShelfData();
public static boolean modAvailable = false;
public static Config config = new Config();
public static void clientInit(){
config.loadConfig();
Services.EVENTS_HELPER.registerOnPlayerDisconnect(() -> {
modAvailable = false;
bookShelfData = new BookShelfData();
});
}
}
@@ -0,0 +1,17 @@
package com.lukasabbe.bookshelfinspector;
import net.minecraft.resources.ResourceLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Constants {
public static final String MOD_ID = "bookshelfinspector";
public static final String MOD_NAME = "Bookshelf Inspector";
public static final Logger LOG = LoggerFactory.getLogger(MOD_NAME);
//Packets
public static final ResourceLocation BOOK_SHELF_INVENTORY_REQUEST_PACKET_ID = ResourceLocation.fromNamespaceAndPath(MOD_ID,"book_shelf_inventory_request");
public static final ResourceLocation BOOK_SHELF_INVENTORY_PACKET_ID = ResourceLocation.fromNamespaceAndPath(MOD_ID,"book_shelf_inventory");
public static final ResourceLocation MOD_CHECK_PACKET_ID = ResourceLocation.fromNamespaceAndPath(MOD_ID,"mod_check");
public static final ResourceLocation LECTERN_INVENTORY_REQUEST_PACKET_ID = ResourceLocation.fromNamespaceAndPath(MOD_ID, "lectern_inventory_request");
}
@@ -0,0 +1,67 @@
package com.lukasabbe.bookshelfinspector.config;
import com.lukasabbe.bookshelfinspector.platform.Services;
import org.yaml.snakeyaml.Yaml;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
public class Config {
public boolean lecternToggle = true;
public int scale = 10;
public boolean useRoman = false;
public void loadConfig(){
Path configPath = Services.PLATFORM.getConfigPath("bookshelfinspector-config.yml");
if(!Files.exists(configPath))createConfig(configPath);
Yaml yaml = new Yaml();
try{
Map<String, Object> configMap = yaml.load(new FileReader(configPath.toFile()));
if(configMap.containsKey("lectern-toggle")){
lecternToggle = (boolean) configMap.get("lectern-toggle");
}
if(configMap.containsKey("scale")){
scale = (int) configMap.get("scale");
}
if(configMap.containsKey("roman")){
useRoman = (boolean) configMap.get("roman");
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
private void createConfig(Path configPath){
Path defaultConfigPath = Services.PLATFORM.getFileInModContainer("bookshelfinspector", "bookshelfinspector-config.yml");
try {
Files.copy(defaultConfigPath, configPath);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void saveConfig(){
Path configPath = Services.PLATFORM.getConfigPath("bookshelfinspector-config.yml");
if(!Files.exists(configPath)) createConfig(configPath);
Yaml yaml = new Yaml();
try{
Map<String, Object> configMap = yaml.load(new FileReader(configPath.toFile()));
configMap.put("lectern-toggle",lecternToggle);
configMap.put("scale",scale);
configMap.put("roman", useRoman);
FileWriter writer = new FileWriter(configPath.toString());
yaml.dump(configMap,writer);
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,20 @@
package com.lukasabbe.bookshelfinspector.data;
import net.minecraft.core.BlockPos;
import net.minecraft.world.item.ItemStack;
public class BookData {
public ItemStack itemStack;
public BlockPos pos;
public int slotId;
public BookData(ItemStack itemStack, BlockPos pos, int slotId) {
this.itemStack = itemStack;
this.pos = pos;
this.slotId = slotId;
}
public static BookData empty(){
return new BookData(ItemStack.EMPTY, null, -1);
}
}
@@ -0,0 +1,10 @@
package com.lukasabbe.bookshelfinspector.data;
import net.minecraft.core.BlockPos;
public class BookShelfData {
public boolean isCurrentBookDataToggled = false;
public BlockPos latestPos = null;
public boolean requestSent = false;
public int currentSlotInt = -1;
}
@@ -0,0 +1,10 @@
package com.lukasabbe.bookshelfinspector.data;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.level.block.Block;
public class Tags {
public static final TagKey<Block> CHISELED_BOOKSHELVES = TagKey.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath("c", "chiseled_bookshelves"));
}
@@ -0,0 +1,15 @@
package com.lukasabbe.bookshelfinspector.mixin;
import net.minecraft.world.level.block.ChiseledBookShelfBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
import java.util.OptionalInt;
@Mixin(ChiseledBookShelfBlock.class)
public interface BookshelfInvoker {
@Invoker("getHitSlot")
OptionalInt invokerGetSlotForHitPos(BlockHitResult hit, BlockState state);
}
@@ -0,0 +1,25 @@
package com.lukasabbe.bookshelfinspector.mixin;
import com.lukasabbe.bookshelfinspector.Constants;
import com.lukasabbe.bookshelfinspector.renderer.Inspector;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import org.spongepowered.asm.mixin.Final;
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;
@Mixin(LocalPlayer.class)
public class BookshelfMixin {
@Shadow @Final protected Minecraft minecraft;
@Unique private final Inspector bookshelfInspectorMultiloader$inspector = new Inspector();
@Inject(method = "tick", at= @At(value = "INVOKE", target = "Ljava/util/List;iterator()Ljava/util/Iterator;"))
public void injectTick(CallbackInfo ci){
bookshelfInspectorMultiloader$inspector.inspect(this.minecraft);
}
}
@@ -0,0 +1,24 @@
package com.lukasabbe.bookshelfinspector.mixin;
import com.lukasabbe.bookshelfinspector.Constants;
import com.lukasabbe.bookshelfinspector.renderer.HudRenderer;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.GuiGraphics;
import org.spongepowered.asm.mixin.Final;
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;
@Mixin(Gui.class)
public class InGameHudMixin {
@Shadow @Final private Minecraft minecraft;
@Inject(method = "render",at=@At("RETURN"))
public void render(GuiGraphics context, DeltaTracker tickCounter, CallbackInfo ci){
HudRenderer.hudRender(context, minecraft);
}
}
@@ -0,0 +1,21 @@
package com.lukasabbe.bookshelfinspector.network;
import com.lukasabbe.bookshelfinspector.network.client.BookShelfInventoryHandlerServer;
import com.lukasabbe.bookshelfinspector.network.client.ModServerPayloadHandler;
import com.lukasabbe.bookshelfinspector.network.server.BookShelfInventoryRequestServerPayloadHandler;
import com.lukasabbe.bookshelfinspector.network.server.LecternInventoryRequestServerPayloadHandler;
public class Handlers {
public BookShelfInventoryHandlerServer bookShelfInventoryHandlerServer;
public ModServerPayloadHandler modServerPayloadHandler;
public BookShelfInventoryRequestServerPayloadHandler bookShelfInventoryRequestServerPayloadHandler;
public LecternInventoryRequestServerPayloadHandler lecternInventoryRequestServerPayloadHandler;
public Handlers(){
bookShelfInventoryHandlerServer = new BookShelfInventoryHandlerServer();
modServerPayloadHandler = new ModServerPayloadHandler();
bookShelfInventoryRequestServerPayloadHandler = new BookShelfInventoryRequestServerPayloadHandler();
lecternInventoryRequestServerPayloadHandler = new LecternInventoryRequestServerPayloadHandler();
}
}
@@ -0,0 +1,24 @@
package com.lukasabbe.bookshelfinspector.network.client;
import com.lukasabbe.bookshelfinspector.BookshelfInspectorClient;
import com.lukasabbe.bookshelfinspector.data.BookData;
import com.lukasabbe.bookshelfinspector.network.packets.BookShelfInventoryPayload;
import com.lukasabbe.bookshelfinspector.platform.handlers.ClientPayloadHandler;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.item.Items;
public class BookShelfInventoryHandlerServer implements ClientPayloadHandler<BookShelfInventoryPayload> {
@Override
public void receive(BookShelfInventoryPayload bookShelfInventoryPayload, LocalPlayer player) {
BookshelfInspectorClient.bookShelfData.requestSent = false;
if(bookShelfInventoryPayload.itemStack().is(Items.AIR)){
BookshelfInspectorClient.bookShelfData.isCurrentBookDataToggled = false;
BookshelfInspectorClient.currentBookData = BookData.empty();
BookshelfInspectorClient.currentBookData.slotId = -2;
}
else{
BookshelfInspectorClient.bookShelfData.isCurrentBookDataToggled = true;
BookshelfInspectorClient.currentBookData = new BookData(bookShelfInventoryPayload.itemStack(),bookShelfInventoryPayload.pos(),bookShelfInventoryPayload.slotNum());
}
}
}
@@ -0,0 +1,15 @@
package com.lukasabbe.bookshelfinspector.network.client;
import com.lukasabbe.bookshelfinspector.BookshelfInspectorClient;
import com.lukasabbe.bookshelfinspector.Constants;
import com.lukasabbe.bookshelfinspector.network.packets.ModCheckPayload;
import com.lukasabbe.bookshelfinspector.platform.handlers.ClientPayloadHandler;
import net.minecraft.client.player.LocalPlayer;
public class ModServerPayloadHandler implements ClientPayloadHandler<ModCheckPayload> {
@Override
public void receive(ModCheckPayload payload, LocalPlayer player) {
Constants.LOG.info("[bookshelfinspector] Connected to server");
BookshelfInspectorClient.modAvailable = true;
}
}
@@ -0,0 +1,22 @@
package com.lukasabbe.bookshelfinspector.network.packets;
import com.lukasabbe.bookshelfinspector.Constants;
import net.minecraft.core.BlockPos;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
public record BookShelfInventoryPayload(ItemStack itemStack, BlockPos pos, int slotNum) implements CustomPacketPayload {
public static final CustomPacketPayload.Type<BookShelfInventoryPayload> ID = new CustomPacketPayload.Type<>(Constants.BOOK_SHELF_INVENTORY_PACKET_ID);
public static final StreamCodec<RegistryFriendlyByteBuf, BookShelfInventoryPayload> CODEC = StreamCodec.composite(
ItemStack.OPTIONAL_STREAM_CODEC, BookShelfInventoryPayload::itemStack,
BlockPos.STREAM_CODEC, BookShelfInventoryPayload::pos,
ByteBufCodecs.INT, BookShelfInventoryPayload::slotNum,
BookShelfInventoryPayload::new
);
@Override
public @NotNull Type<? extends CustomPacketPayload> type() { return ID; }
}
@@ -0,0 +1,22 @@
package com.lukasabbe.bookshelfinspector.network.packets;
import com.lukasabbe.bookshelfinspector.Constants;
import net.minecraft.core.BlockPos;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import org.jetbrains.annotations.NotNull;
public record BookShelfInventoryRequestPayload(BlockPos pos, int slotNum) implements CustomPacketPayload {
public static final CustomPacketPayload.Type<BookShelfInventoryRequestPayload> ID = new CustomPacketPayload.Type<>(Constants.BOOK_SHELF_INVENTORY_REQUEST_PACKET_ID);
public static final StreamCodec<RegistryFriendlyByteBuf, BookShelfInventoryRequestPayload> CODEC = StreamCodec.composite(
BlockPos.STREAM_CODEC,BookShelfInventoryRequestPayload::pos,
ByteBufCodecs.INT, BookShelfInventoryRequestPayload::slotNum,
BookShelfInventoryRequestPayload::new);
@Override
public @NotNull Type<? extends CustomPacketPayload> type() { return ID; }
}
@@ -0,0 +1,20 @@
package com.lukasabbe.bookshelfinspector.network.packets;
import com.lukasabbe.bookshelfinspector.Constants;
import net.minecraft.core.BlockPos;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import org.jetbrains.annotations.NotNull;
public record LecternInventoryRequestPayload(BlockPos pos) implements CustomPacketPayload {
public static final CustomPacketPayload.Type<LecternInventoryRequestPayload> ID = new CustomPacketPayload.Type<>(Constants.LECTERN_INVENTORY_REQUEST_PACKET_ID);
public static final StreamCodec<RegistryFriendlyByteBuf, LecternInventoryRequestPayload> CODEC = StreamCodec.composite(
BlockPos.STREAM_CODEC,LecternInventoryRequestPayload::pos,
LecternInventoryRequestPayload::new);
@Override
public @NotNull Type<? extends CustomPacketPayload> type() { return ID; }
}
@@ -0,0 +1,20 @@
package com.lukasabbe.bookshelfinspector.network.packets;
import com.lukasabbe.bookshelfinspector.Constants;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import org.jetbrains.annotations.NotNull;
public record ModCheckPayload(boolean modActivated) implements CustomPacketPayload {
public static final CustomPacketPayload.Type<ModCheckPayload> ID = new CustomPacketPayload.Type<>(Constants.MOD_CHECK_PACKET_ID);
public static final StreamCodec<RegistryFriendlyByteBuf, ModCheckPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.BOOL, ModCheckPayload::modActivated,
ModCheckPayload::new
);
@Override
public @NotNull Type<? extends CustomPacketPayload> type() { return ID; }
}
@@ -0,0 +1,25 @@
package com.lukasabbe.bookshelfinspector.network.server;
import com.lukasabbe.bookshelfinspector.BookshelfInspector;
import com.lukasabbe.bookshelfinspector.network.packets.BookShelfInventoryPayload;
import com.lukasabbe.bookshelfinspector.network.packets.BookShelfInventoryRequestPayload;
import com.lukasabbe.bookshelfinspector.platform.Services;
import com.lukasabbe.bookshelfinspector.platform.handlers.ServerPayloadHandler;
import com.lukasabbe.bookshelfinspector.util.BookshelfTools;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
public class BookShelfInventoryRequestServerPayloadHandler implements ServerPayloadHandler<BookShelfInventoryRequestPayload> {
@Override
public void receive(BookShelfInventoryRequestPayload bookShelfInventoryRequestPayload, ServerPlayer player) {
if(BookshelfInspector.serverInstance == null) return;
ItemStack stack = BookshelfTools.getItemById(bookShelfInventoryRequestPayload.pos(),bookShelfInventoryRequestPayload.slotNum(), player);
if(stack == null){
Services.NETWORK_HELPER.sendPacketFromServer(player, new BookShelfInventoryPayload(Items.AIR.getDefaultInstance(), bookShelfInventoryRequestPayload.pos(), bookShelfInventoryRequestPayload.slotNum()));
return;
}
Services.NETWORK_HELPER.sendPacketFromServer(player, new BookShelfInventoryPayload(stack, bookShelfInventoryRequestPayload.pos(), bookShelfInventoryRequestPayload.slotNum()));
}
}
@@ -0,0 +1,26 @@
package com.lukasabbe.bookshelfinspector.network.server;
import com.lukasabbe.bookshelfinspector.BookshelfInspector;
import com.lukasabbe.bookshelfinspector.network.packets.BookShelfInventoryPayload;
import com.lukasabbe.bookshelfinspector.network.packets.LecternInventoryRequestPayload;
import com.lukasabbe.bookshelfinspector.platform.Services;
import com.lukasabbe.bookshelfinspector.platform.handlers.ServerPayloadHandler;
import com.lukasabbe.bookshelfinspector.util.LecternTools;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
public class LecternInventoryRequestServerPayloadHandler implements ServerPayloadHandler<LecternInventoryRequestPayload> {
@Override
public void receive(LecternInventoryRequestPayload lecternInventoryRequestPayload, ServerPlayer player) {
if(BookshelfInspector.serverInstance == null) return;
ItemStack stack = LecternTools.getItemStack(lecternInventoryRequestPayload.pos(), player);
if(stack == null){
Services.NETWORK_HELPER.sendPacketFromServer(player, new BookShelfInventoryPayload(Items.AIR.getDefaultInstance(), lecternInventoryRequestPayload.pos(), 0));
return;
}
Services.NETWORK_HELPER.sendPacketFromServer(player, new BookShelfInventoryPayload(stack, lecternInventoryRequestPayload.pos(), 0));
}
}
@@ -0,0 +1,34 @@
package com.lukasabbe.bookshelfinspector.platform;
import com.lukasabbe.bookshelfinspector.Constants;
import com.lukasabbe.bookshelfinspector.platform.services.IEventHelper;
import com.lukasabbe.bookshelfinspector.platform.services.INetworkHelper;
import com.lukasabbe.bookshelfinspector.platform.services.IPlatformHelper;
import java.util.ServiceLoader;
// Service loaders are a built-in Java feature that allow us to locate implementations of an interface that vary from one
// environment to another. In the context of MultiLoader we use this feature to access a mock API in the common code that
// is swapped out for the platform specific implementation at runtime.
public class Services {
// In this example we provide a platform helper which provides information about what platform the mod is running on.
// For example this can be used to check if the code is running on Forge vs Fabric, or to ask the modloader if another
// mod is loaded.
public static final IPlatformHelper PLATFORM = load(IPlatformHelper.class);
public static final INetworkHelper NETWORK_HELPER = load(INetworkHelper.class);
public static final IEventHelper EVENTS_HELPER = load(IEventHelper.class);
// This code is used to load a service for the current environment. Your implementation of the service must be defined
// manually by including a text file in META-INF/services named with the fully qualified class name of the service.
// Inside the file you should write the fully qualified class name of the implementation to load for the platform. For
// example our file on Forge points to ForgePlatformHelper while Fabric points to FabricPlatformHelper.
public static <T> T load(Class<T> clazz) {
final T loadedService = ServiceLoader.load(clazz)
.findFirst()
.orElseThrow(() -> new NullPointerException("Failed to load service for " + clazz.getName()));
Constants.LOG.debug("Loaded {} for service {}", loadedService, clazz);
return loadedService;
}
}
@@ -0,0 +1,8 @@
package com.lukasabbe.bookshelfinspector.platform.handlers;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
public interface ClientPayloadHandler<T extends CustomPacketPayload> {
void receive(T payload, LocalPlayer player);
}
@@ -0,0 +1,5 @@
package com.lukasabbe.bookshelfinspector.platform.handlers;
public interface OnPlayerDisconnectEvent {
void onDisconnect();
}
@@ -0,0 +1,8 @@
package com.lukasabbe.bookshelfinspector.platform.handlers;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
public interface OnPlayerJoinEvent {
void onPlayerJoin(ServerPlayer handler, MinecraftServer server);
}
@@ -0,0 +1,8 @@
package com.lukasabbe.bookshelfinspector.platform.handlers;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.server.level.ServerPlayer;
public interface ServerPayloadHandler<T extends CustomPacketPayload> {
void receive(T payload, ServerPlayer player);
}
@@ -0,0 +1,9 @@
package com.lukasabbe.bookshelfinspector.platform.services;
import com.lukasabbe.bookshelfinspector.platform.handlers.OnPlayerDisconnectEvent;
import com.lukasabbe.bookshelfinspector.platform.handlers.OnPlayerJoinEvent;
public interface IEventHelper {
void registerOnPlayerJoinEvent(OnPlayerJoinEvent event);
void registerOnPlayerDisconnect(OnPlayerDisconnectEvent event);
}
@@ -0,0 +1,9 @@
package com.lukasabbe.bookshelfinspector.platform.services;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import net.minecraft.server.level.ServerPlayer;
public interface INetworkHelper {
void sendPacketFromServer(ServerPlayer player, CustomPacketPayload payload);
void sendPacketFromClient(CustomPacketPayload payload);
}
@@ -0,0 +1,20 @@
package com.lukasabbe.bookshelfinspector.platform.services;
import java.nio.file.Path;
public interface IPlatformHelper {
String getPlatformName();
boolean isModLoaded(String modId);
boolean isDevelopmentEnvironment();
Path getConfigPath(String file);
Path getFileInModContainer(String mod, String fileName);
default String getEnvironmentName() {
return isDevelopmentEnvironment() ? "development" : "production";
}
}
@@ -0,0 +1,90 @@
package com.lukasabbe.bookshelfinspector.renderer;
import com.lukasabbe.bookshelfinspector.BookshelfInspectorClient;
import com.lukasabbe.bookshelfinspector.Constants;
import com.lukasabbe.bookshelfinspector.data.BookData;
import com.lukasabbe.bookshelfinspector.util.RomanNumerals;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.Holder;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.tags.EnchantmentTags;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.ItemEnchantments;
public class HudRenderer {
public static void hudRender(GuiGraphics context, Minecraft client){
if(!BookshelfInspectorClient.modAvailable) return;
if(client.options.hideGui) return;
if(!BookshelfInspectorClient.bookShelfData.isCurrentBookDataToggled) return;
final BookData currentBookData = BookshelfInspectorClient.currentBookData;
final int screenWidth = client.getWindow().getGuiScaledWidth();
final int screenHeight = client.getWindow().getGuiScaledHeight();
final int x = screenWidth / 2;
final int y = screenHeight / 2;
final ItemStack itemStack = currentBookData.itemStack;
int color = 0xFFFFFFFF;
final Integer colorValue = itemStack.getRarity().color().getColor();
if(colorValue != null){
color = colorValue;
}
float scaleFactor = ((float) BookshelfInspectorClient.config.scale /10);
drawScaledText(context, itemStack.getHoverName(), x,y+((int)(10*scaleFactor)), color, client.font);
ItemEnchantments storedComponents = itemStack.getComponents().get(DataComponents.STORED_ENCHANTMENTS);
if(storedComponents != null){
int i = ((int)(20*scaleFactor));
for(Holder<Enchantment> enchantment : storedComponents.keySet()){
String lvl = "";
final int level = storedComponents.getLevel(enchantment);
if(level != 1)
lvl = String.valueOf(level);
final MutableComponent enchantmentText;
if(!BookshelfInspectorClient.config.useRoman || level == -1)
enchantmentText = enchantment.value().description().copy().append(" " + lvl);
else if (level != 1)
enchantmentText = enchantment.value().description().copy().append(" " + RomanNumerals.toRoman(level));
else
enchantmentText = enchantment.value().description().copy();
if(enchantment.is(EnchantmentTags.CURSE)) {
ComponentUtils.mergeStyles(enchantmentText, Style.EMPTY.withColor(ChatFormatting.RED));
}else {
ComponentUtils.mergeStyles(enchantmentText, Style.EMPTY.withColor(ChatFormatting.GRAY));
}
drawScaledText(context, enchantmentText, x,y+i, 0xFFFFFFFF,client.font);
i+=(int)(10*scaleFactor);
}
}
var writtenBookContentComponent = itemStack.getComponents().get(DataComponents.WRITTEN_BOOK_CONTENT);
if(writtenBookContentComponent != null){
drawScaledText(context, Component.translatable("book.byAuthor",writtenBookContentComponent.author()), x,y+(int)(20*scaleFactor), 0xFFFFFFFF, client.font);
}
}
private static void drawScaledText(GuiGraphics context, Component text, int centerX, int y, int color, Font textRenderer){
PoseStack stack = context.pose();
stack.pushPose();
stack.translate(centerX,y,0);
final float scale = (float) BookshelfInspectorClient.config.scale / 10;
stack.scale(scale, scale, scale);
stack.translate(-centerX,-y,0);
context.drawCenteredString(textRenderer,text,centerX,y,color);
stack.popPose();
}
}
@@ -0,0 +1,121 @@
package com.lukasabbe.bookshelfinspector.renderer;
import com.lukasabbe.bookshelfinspector.BookshelfInspectorClient;
import com.lukasabbe.bookshelfinspector.data.BookData;
import com.lukasabbe.bookshelfinspector.data.Tags;
import com.lukasabbe.bookshelfinspector.mixin.BookshelfInvoker;
import com.lukasabbe.bookshelfinspector.network.packets.BookShelfInventoryRequestPayload;
import com.lukasabbe.bookshelfinspector.network.packets.LecternInventoryRequestPayload;
import com.lukasabbe.bookshelfinspector.platform.Services;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ChiseledBookShelfBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import java.util.OptionalInt;
import static com.lukasabbe.bookshelfinspector.BookshelfInspectorClient.*;
public class Inspector {
public void inspect(Minecraft client){
if(!modAvailable) return;
if(client.cameraEntity == null || client.player == null) return;
//Send raycast max 5 blocks
HitResult hit = client.cameraEntity.pick(5f,0f,false);
//find block hit, if not found block returns
final HitResult.Type type = hit.getType();
if(type != HitResult.Type.BLOCK) {
resetBookShelfData();
return;
}
final BlockHitResult blockHitResult = (BlockHitResult) hit;
BlockPos pos = blockHitResult.getBlockPos();
if(bookShelfData.latestPos == null)
bookShelfData.latestPos = pos;
//If you look at a new block
if(!bookShelfData.latestPos.equals(pos)){
resetBookShelfData();
currentBookData = BookData.empty();
}
bookShelfData.latestPos = pos;
if(client.player.level().getBlockState(pos).is(Tags.CHISELED_BOOKSHELVES)){
bookShelfInspect(pos, blockHitResult, client);
}else if(client.player.level().getBlockState(pos).is(Blocks.LECTERN) && config.lecternToggle){
lecternInspect(pos);
}else{
bookShelfData.requestSent = false; // Just for servers that don't have the latest version of mod
if(!bookShelfData.isCurrentBookDataToggled) return;
resetBookShelfData();
}
}
private void lecternInspect(BlockPos pos){
//Checks if there is saved data.
final BookData currentBookData = BookshelfInspectorClient.currentBookData;
if(currentBookData.pos != null && currentBookData.pos.equals(pos)) return;
if(!bookShelfData.requestSent){
bookShelfData.requestSent = true;
Services.NETWORK_HELPER.sendPacketFromClient(new LecternInventoryRequestPayload(pos));
}
}
private void bookShelfInspect(BlockPos pos, BlockHitResult blockHitResult, Minecraft client){
final BlockState blockState = client.player.level().getBlockState(pos);
//Gets index position for a book in the bookshelf
ChiseledBookShelfBlock bookshelfBlock = (ChiseledBookShelfBlock) blockState.getBlock();
OptionalInt optionalInt = ((BookshelfInvoker)bookshelfBlock).invokerGetSlotForHitPos(blockHitResult,blockState);
//if the position is empty, return
if(optionalInt.isEmpty()) {
resetBookShelfData();
return;
}
//Checks if there is saved data.
final BookData currentBookData = BookshelfInspectorClient.currentBookData;
//Changes the id for the new one if it's new.
final int temp = bookShelfData.currentSlotInt;
final int slotNum = optionalInt.getAsInt();
bookShelfData.currentSlotInt = slotNum;
if(currentBookData.slotId!= slotNum && currentBookData.slotId!=-2 && !bookShelfData.requestSent){
bookShelfData.requestSent = true;
Services.NETWORK_HELPER.sendPacketFromClient(new BookShelfInventoryRequestPayload(pos, slotNum));
}
else {
if(temp == slotNum)
bookShelfData.isCurrentBookDataToggled = currentBookData.slotId != -2;
else{
bookShelfData.isCurrentBookDataToggled = false;
BookshelfInspectorClient.currentBookData = BookData.empty();
}
}
}
private void resetBookShelfData(){
if(!bookShelfData.isCurrentBookDataToggled) return;
bookShelfData.isCurrentBookDataToggled = false;
currentBookData = BookData.empty();
}
}
@@ -0,0 +1,28 @@
package com.lukasabbe.bookshelfinspector.util;
import com.lukasabbe.bookshelfinspector.BookshelfInspector;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.ChiseledBookShelfBlockEntity;
import java.util.Optional;
public class BookshelfTools {
public static ItemStack getItemById(BlockPos pos, int slotNum, Player player){
final Level world = BookshelfInspector.serverInstance.getPlayerList().getPlayer(player.getUUID()).level();
final Optional<ChiseledBookShelfBlockEntity> blockEntityOptional = world.getBlockEntity(pos, BlockEntityType.CHISELED_BOOKSHELF);
if(blockEntityOptional.isEmpty()) return null;
final ChiseledBookShelfBlockEntity blockEntity = blockEntityOptional.get();
final ItemStack stack = blockEntity.getItem(slotNum);
if(stack.isEmpty()) return null;
return stack;
}
}
@@ -0,0 +1,24 @@
package com.lukasabbe.bookshelfinspector.util;
import com.lukasabbe.bookshelfinspector.BookshelfInspector;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
import java.util.Optional;
public class LecternTools {
public static ItemStack getItemStack(BlockPos pos, Player player){
final Level world = BookshelfInspector.serverInstance.getPlayerList().getPlayer(player.getUUID()).level();
Optional<LecternBlockEntity> blockEntityOptional = world.getBlockEntity(pos, BlockEntityType.LECTERN);
if(blockEntityOptional.isEmpty()) return null;
LecternBlockEntity lecternBlock = blockEntityOptional.get();
return lecternBlock.getBook();
}
}
@@ -0,0 +1,33 @@
package com.lukasabbe.bookshelfinspector.util;
import java.util.TreeMap;
public class RomanNumerals {
private final static TreeMap<Integer, String> map = new TreeMap<>();
static {
map.put(1000, "M");
map.put(900, "CM");
map.put(500, "D");
map.put(400, "CD");
map.put(100, "C");
map.put(90, "XC");
map.put(50, "L");
map.put(40, "XL");
map.put(10, "X");
map.put(9, "IX");
map.put(5, "V");
map.put(4, "IV");
map.put(1, "I");
}
public static String toRoman(int number){
int key = map.floorKey(number);
if(number == key){
return map.get(number);
}
return map.get(key) + toRoman(number-key);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

@@ -0,0 +1,10 @@
{
"bookshelfinspector.config.title": "Bookshelf Inspector Config",
"bookshelfinspector.config.category": "Settings",
"bookshelfinspector.config.lectern.toggle": "Turn OFF or ON lectern support",
"bookshelfinspector.config.lectern.toggle.tooltip": "This will turn OFF/ON the visibility of lectern inspection",
"bookshelfinspector.config.scale": "Change the scale of the text",
"bookshelfinspector.config.scale.tooltip": "Change the scale of the text by dragging the slider",
"bookshelfinspector.config.roman_scale": "Use roman numerals!",
"bookshelfinspector.config.roman_scale.tooltip": "Use roman numerals or arabic numerals"
}
@@ -0,0 +1,10 @@
{
"bookshelfinspector.config.title": "Bookshelf Inspector Config",
"bookshelfinspector.config.category": "Inställningar",
"bookshelfinspector.config.lectern.toggle": "Stäng AV eller PÅ läspulpet stöd",
"bookshelfinspector.config.lectern.toggle.tooltip": "Denna inställning kommer stänga av eller på synligeten av läspulpet inspektion",
"bookshelfinspector.config.scale": "Ändra storleken på texten",
"bookshelfinspector.config.scale.tooltip": "Du kan göra det igenom att dra på den nedan",
"bookshelfinspector.config.roman_scale": "Använd romerska siffror",
"bookshelfinspector.config.roman_scale.tooltip": "Använd romerska siffror eller arabiska siffror"
}
@@ -0,0 +1,3 @@
lectern-toggle: true
scale: 10
roman: false
@@ -0,0 +1,19 @@
{
"required": true,
"minVersion": "0.8",
"package": "com.lukasabbe.bookshelfinspector.mixin",
"refmap": "${mod_id}.refmap.json",
"compatibilityLevel": "JAVA_18",
"mixins": [
],
"client": [
"BookshelfMixin",
"InGameHudMixin",
"BookshelfInvoker"
],
"server": [],
"injectors": {
"defaultRequire": 1
}
}
@@ -0,0 +1,6 @@
{
"replace": false,
"values": [
"minecraft:chiseled_bookshelf"
]
}
+6
View File
@@ -0,0 +1,6 @@
{
"pack": {
"description": "${mod_name}",
"pack_format": 8
}
}