diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 64ec31c..a8a06fa 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -25,7 +25,6 @@ dependencies { tasks { shadowJar { archiveClassifier = "" - archiveFileName = "CustomNameplates-${rootProject.properties["project_version"]}.jar" relocate ("net.kyori", "net.momirealms.customnameplates.libraries") relocate("dev.dejvokep", "net.momirealms.customnameplates.libraries") } diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index 9219867..7fa4282 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { compileOnly("com.github.ben-manes.caffeine:caffeine:${rootProject.properties["caffeine_version"]}") // COMMONS IO compileOnly("commons-io:commons-io:${rootProject.properties["commons_io_version"]}") - // lwjgl - implementation("org.lwjgl:lwjgl-freetype:3.3.4") + // TTF + compileOnly("org.lwjgl:lwjgl-freetype:${rootProject.properties["lwjgl_version"]}") + compileOnly("org.lwjgl:lwjgl:${rootProject.properties["lwjgl_version"]}") } \ No newline at end of file diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/feature/advance/AdvanceManagerImpl.java b/backend/src/main/java/net/momirealms/customnameplates/backend/feature/advance/AdvanceManagerImpl.java index 1ced981..82dd5f2 100644 --- a/backend/src/main/java/net/momirealms/customnameplates/backend/feature/advance/AdvanceManagerImpl.java +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/feature/advance/AdvanceManagerImpl.java @@ -49,13 +49,23 @@ import net.momirealms.customnameplates.api.placeholder.Placeholder; import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder; import net.momirealms.customnameplates.api.placeholder.SharedPlaceholder; import net.momirealms.customnameplates.api.util.CharacterUtils; +import net.momirealms.customnameplates.backend.util.FreeTypeUtils; import net.momirealms.customnameplates.common.util.Tuple; import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.Nullable; +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.util.freetype.FT_Face; +import org.lwjgl.util.freetype.FT_GlyphSlot; +import org.lwjgl.util.freetype.FT_Vector; +import org.lwjgl.util.freetype.FreeType; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; +import java.nio.ByteBuffer; +import java.nio.IntBuffer; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; @@ -768,16 +778,115 @@ public class AdvanceManagerImpl implements AdvanceManager { File ttfCache = new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + id + ".tmp"); if (!ttfCache.exists()) { - File ttfFile = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + path); - if (!ttfFile.exists()) { - plugin.getPluginLogger().warn(ttfFile.getAbsolutePath() + " not found"); + File ttf = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + path); + if (!ttf.exists()) { + plugin.getPluginLogger().warn(ttf.getAbsolutePath() + " not found"); return; } - if (!ttfFile.getName().endsWith(".ttf")) { - plugin.getPluginLogger().warn(ttfFile.getAbsolutePath() + " is not a .ttf"); + if (!ttf.getName().endsWith(".ttf")) { + plugin.getPluginLogger().warn(ttf.getAbsolutePath() + " is not a .ttf"); return; } + try (InputStream inputStream = new FileInputStream(ttf)) { + ByteBuffer byteBuffer = null; + FT_Face fT_Face = null; + try { + ttfCache.getParentFile().mkdirs(); + ttfCache.createNewFile(); + YamlDocument yml = plugin.getConfigManager().loadData(ttfCache); + byteBuffer = FreeTypeUtils.readResource(inputStream); + byteBuffer.flip(); + synchronized(FreeTypeUtils.LOCK) { + MemoryStack ms1 = MemoryStack.stackPush(); + try { + PointerBuffer pointerBuffer = ms1.mallocPointer(1); + FreeTypeUtils.checkFatalError(FreeType.FT_New_Memory_Face(FreeTypeUtils.initialize(), byteBuffer, 0L, pointerBuffer), "Initializing font face"); + fT_Face = FT_Face.create(pointerBuffer.get()); + } catch (Throwable t1) { + try { + ms1.close(); + } catch (Throwable t2) { + t1.addSuppressed(t2); + } + throw t1; + } + ms1.close(); + String string = FreeType.FT_Get_Font_Format(fT_Face); + if (!"TrueType".equals(string)) { + throw new IOException("Font is not in TTF format, was " + string); + } + + FreeTypeUtils.checkFatalError(FreeType.FT_Select_Charmap(fT_Face, FreeType.FT_ENCODING_UNICODE), "Find unicode charmap"); + Set codePoints = new HashSet<>(1_000); + + int pixelWidth = Math.round(size * oversample); + int pixelHeight = Math.round(size * oversample); + FreeType.FT_Set_Pixel_Sizes(fT_Face, pixelWidth, pixelHeight); + + MemoryStack ms2 = MemoryStack.stackPush(); + try { + FT_Vector fT_Vector = FreeTypeUtils.setShift(FT_Vector.malloc(ms2), 0, 0); + FreeType.FT_Set_Transform(fT_Face, null, fT_Vector); + } catch (Throwable t1) { + try { + ms2.close(); + } catch (Throwable t2) { + t1.addSuppressed(t2); + } + throw t1; + } + ms2.close(); + + MemoryStack ms3 = MemoryStack.stackPush(); + try { + IntBuffer intBuffer = ms3.mallocInt(1); + for (long l = FreeType.FT_Get_First_Char(fT_Face, intBuffer); intBuffer.get(0) != 0; l = FreeType.FT_Get_Next_Char(fT_Face, l, intBuffer)) { + codePoints.add((int) l); + } + } catch (Throwable t1) { + try { + ms3.close(); + } catch (Throwable t2) { + t1.addSuppressed(t2); + } + throw t1; + } + ms3.close(); + + for (int skippedCodePoint : skippCodePoints) { + codePoints.remove(skippedCodePoint); + } + for (int codePoint : codePoints) { + int i = FreeType.FT_Get_Char_Index(fT_Face, codePoint); + if (i != 0) { + FreeTypeUtils.checkFatalError(FreeType.FT_Load_Glyph(fT_Face, i, 4194312), "Loading glyph"); + FT_GlyphSlot fT_GlyphSlot = Objects.requireNonNull(fT_Face.glyph(), "Glyph not initialized"); + float advance = FreeTypeUtils.getAdvance(fT_GlyphSlot.advance()); + char[] theCharacter = Character.toChars(codePoint); + String unicode = CharacterUtils.char2Unicode(theCharacter); + yml.set(unicode, (advance) / oversample); + } + } + } + + yml.save(ttfCache); + } catch (Exception e) { + synchronized(FreeTypeUtils.LOCK) { + if (fT_Face != null) { + FreeType.FT_Done_Face(fT_Face); + } + } + MemoryUtil.memFree(byteBuffer); + throw e; + } + + // free resources + MemoryUtil.memFree(byteBuffer); + + } catch (IOException e) { + throw new RuntimeException(e); + } } registerCharacterFontData(id, ttfCache, (properties) -> { @@ -916,6 +1025,7 @@ public class AdvanceManagerImpl implements AdvanceManager { public void disable() { this.unload(); this.charFontWidthDataMap.clear(); + FreeTypeUtils.release(); } private void loadTemplates() { diff --git a/backend/src/main/java/net/momirealms/customnameplates/backend/util/FreeTypeUtils.java b/backend/src/main/java/net/momirealms/customnameplates/backend/util/FreeTypeUtils.java new file mode 100644 index 0000000..01a0648 --- /dev/null +++ b/backend/src/main/java/net/momirealms/customnameplates/backend/util/FreeTypeUtils.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.backend.util; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.util.freetype.FT_Vector; +import org.lwjgl.util.freetype.FreeType; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.SeekableByteChannel; + +public class FreeTypeUtils { + + public static final Object LOCK = new Object(); + private static long freeType = 0L; + + public FreeTypeUtils() { + } + + public static long initialize() { + synchronized(LOCK) { + if (freeType == 0L) { + MemoryStack memoryStack = MemoryStack.stackPush(); + try { + PointerBuffer pointerBuffer = memoryStack.mallocPointer(1); + checkFatalError(FreeType.FT_Init_FreeType(pointerBuffer), "Initializing FreeType library"); + freeType = pointerBuffer.get(); + } catch (Throwable t1) { + try { + memoryStack.close(); + } catch (Throwable t2) { + t1.addSuppressed(t2); + } + throw t1; + } + memoryStack.close(); + } + return freeType; + } + } + + public static void checkFatalError(int code, String description) { + if (code != 0) { + String var10002 = getErrorMessage(code); + throw new IllegalStateException("FreeType error: " + var10002 + " (" + description + ")"); + } + } + + private static String getErrorMessage(int code) { + String string = FreeType.FT_Error_String(code); + return string != null ? string : "Unrecognized error: 0x" + Integer.toHexString(code); + } + + public static FT_Vector setShift(FT_Vector vec, float x, float y) { + long xShift = Math.round(x * 64.0F); + long yShift = Math.round(y * 64.0F); + return vec.set(xShift, yShift); + } + + public static float getAdvance(FT_Vector vec) { + return (float) vec.x() / 64.0F; + } + + public static void release() { + synchronized(LOCK) { + if (freeType != 0L) { + FreeType.FT_Done_Library(freeType); + freeType = 0L; + } + } + } + + public static ByteBuffer readResource(InputStream inputStream) throws IOException { + ReadableByteChannel readableByteChannel = Channels.newChannel(inputStream); + if (readableByteChannel instanceof SeekableByteChannel seekableByteChannel) { + return readResource(readableByteChannel, (int)seekableByteChannel.size() + 1); + } else { + return readResource(readableByteChannel, 8192); + } + } + + private static ByteBuffer readResource(ReadableByteChannel channel, int bufSize) throws IOException { + ByteBuffer byteBuffer = MemoryUtil.memAlloc(bufSize); + try { + while(channel.read(byteBuffer) != -1) { + if (!byteBuffer.hasRemaining()) { + byteBuffer = MemoryUtil.memRealloc(byteBuffer, byteBuffer.capacity() * 2); + } + } + return byteBuffer; + } catch (IOException e) { + MemoryUtil.memFree(byteBuffer); + throw e; + } + } +} \ No newline at end of file diff --git a/bukkit/build.gradle.kts b/bukkit/build.gradle.kts index 87ac65d..6ee9de1 100644 --- a/bukkit/build.gradle.kts +++ b/bukkit/build.gradle.kts @@ -46,7 +46,7 @@ dependencies { tasks { shadowJar { - archiveFileName = "CustomNameplates-bukkit-${rootProject.properties["project_version"]}.jar" + archiveFileName = "CustomNameplates-Bukkit-${rootProject.properties["project_version"]}.jar" destinationDirectory.set(file("$rootDir/target")) relocate("net.kyori", "net.momirealms.customnameplates.libraries") relocate("org.incendo", "net.momirealms.customnameplates.libraries") @@ -62,7 +62,6 @@ tasks { relocate("com.github.benmanes.caffeine", "net.momirealms.customnameplates.libraries.caffeine") relocate("net.objecthunter.exp4j", "net.momirealms.customnameplates.libraries.exp4j") relocate("redis.clients.jedis", "net.momirealms.customnameplates.libraries.jedis") - relocate("org.apache.commons.io", "net.momirealms.customnameplates.libraries.commons.io") } } diff --git a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java index 8896c9b..133bc76 100644 --- a/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java +++ b/bukkit/src/main/java/net/momirealms/customnameplates/bukkit/BukkitCustomNameplates.java @@ -35,6 +35,7 @@ import net.momirealms.customnameplates.backend.placeholder.PlaceholderManagerImp import net.momirealms.customnameplates.backend.storage.StorageManagerImpl; import net.momirealms.customnameplates.bukkit.command.BukkitCommandManager; import net.momirealms.customnameplates.bukkit.compatibility.NameplatesExpansion; +import net.momirealms.customnameplates.bukkit.compatibility.cosmetic.MagicCosmeticsHook; import net.momirealms.customnameplates.bukkit.requirement.BukkitRequirementManager; import net.momirealms.customnameplates.bukkit.scheduler.BukkitSchedulerAdapter; import net.momirealms.customnameplates.common.dependency.Dependency; @@ -116,7 +117,8 @@ public class BukkitCustomNameplates extends CustomNameplates implements Listener Dependency.MONGODB_DRIVER_CORE, Dependency.MONGODB_DRIVER_SYNC, Dependency.MONGODB_DRIVER_BSON, Dependency.HIKARI_CP, Dependency.BYTE_BUDDY, - Dependency.COMMONS_IO + Dependency.COMMONS_IO, + Dependency.LWJGL, Dependency.LWJGL_NATIVES, Dependency.LWJGL_FREETYPE, Dependency.LWJGL_FREETYPE_NATIVES ) ); } @@ -173,6 +175,12 @@ public class BukkitCustomNameplates extends CustomNameplates implements Listener if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { new NameplatesExpansion(this).register(); } + if (Bukkit.getPluginManager().isPluginEnabled("MagicCosmetics")) { + try { + Bukkit.getPluginManager().registerEvents(new MagicCosmeticsHook(this), this.getBootstrap()); + } catch (Exception ignore) { + } + } boolean downloadFromPolymart = polymart.equals("1"); boolean downloadFromBBB = buildByBit.equals("true"); diff --git a/common/src/main/java/net/momirealms/customnameplates/common/dependency/Dependency.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/Dependency.java index 106d26c..c2f70ff 100644 --- a/common/src/main/java/net/momirealms/customnameplates/common/dependency/Dependency.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/Dependency.java @@ -27,6 +27,8 @@ package net.momirealms.customnameplates.common.dependency; import net.momirealms.customnameplates.common.dependency.relocation.Relocation; import net.momirealms.customnameplates.common.plugin.CustomNameplatesProperties; +import net.momirealms.customnameplates.common.util.Architecture; +import net.momirealms.customnameplates.common.util.Platform; import org.jetbrains.annotations.Nullable; import java.security.MessageDigest; @@ -282,7 +284,48 @@ public enum Dependency { "maven", "commons-io", Relocation.of("commons", "org{}apache{}commons") - ); + ), + LWJGL( + "org{}lwjgl", + "lwjgl", + "maven", + "lwjgl" + ), + LWJGL_NATIVES( + "org{}lwjgl", + "lwjgl", + "maven", + "lwjgl-natives-" + getNativesPath(), + "-natives-" + getNativesPath() + ) { + @Override + public String getVersion() { + return Dependency.LWJGL.getVersion(); + } + }, + LWJGL_FREETYPE( + "org{}lwjgl", + "lwjgl-freetype", + "maven", + "lwjgl-freetype" + ) { + @Override + public String getVersion() { + return Dependency.LWJGL.getVersion(); + } + }, + LWJGL_FREETYPE_NATIVES( + "org{}lwjgl", + "lwjgl-freetype", + "maven", + "lwjgl-freetype-natives-" + getNativesPath(), + "-natives-" + getNativesPath() + ) { + @Override + public String getVersion() { + return Dependency.LWJGL.getVersion(); + } + }; private final List relocations; private final String repo; @@ -367,4 +410,12 @@ public enum Dependency { public String getRepo() { return repo; } + + private static String getNativesPath() { + String base = Platform.get().getNativePath(); + if (Architecture.get() != Architecture.X64) { + base += "-" + Architecture.get().getNativePath(); + } + return base; + } } diff --git a/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/Relocation.java b/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/Relocation.java index 071138c..ca5ea69 100644 --- a/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/Relocation.java +++ b/common/src/main/java/net/momirealms/customnameplates/common/dependency/relocation/Relocation.java @@ -34,6 +34,10 @@ public final class Relocation { return new Relocation(pattern.replace("{}", "."), RELOCATION_PREFIX + id); } + public static Relocation of(String id, String pattern, String prefix) { + return new Relocation(pattern.replace("{}", "."), prefix.replace("{}", ".") + "." + RELOCATION_PREFIX + id); + } + private final String pattern; private final String relocatedPattern; diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/Architecture.java b/common/src/main/java/net/momirealms/customnameplates/common/util/Architecture.java new file mode 100644 index 0000000..865da28 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/Architecture.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +import java.util.Locale; + +public enum Architecture { + + X64(true), + X86(false), + ARM64(true), + ARM32(false), + PPC64LE(true), + RISCV64(true); + + static final Architecture current; + final boolean is64Bit; + + Architecture(boolean is64Bit) { + this.is64Bit = is64Bit; + } + + public String getNativePath() { + return name().toLowerCase(Locale.ENGLISH); + } + + public static Architecture get() { + return current; + } + + static { + String osArch = System.getProperty("os.arch"); + boolean is64Bit = osArch.contains("64") || osArch.startsWith("armv8"); + if (!osArch.startsWith("arm") && !osArch.startsWith("aarch")) { + if (osArch.startsWith("ppc")) { + if (!"ppc64le".equals(osArch)) { + throw new UnsupportedOperationException("Only PowerPC 64 LE is supported."); + } + current = PPC64LE; + } else if (osArch.startsWith("riscv")) { + if (!"riscv64".equals(osArch)) { + throw new UnsupportedOperationException("Only RISC-V 64 is supported."); + } + current = RISCV64; + } else { + current = is64Bit ? X64 : X86; + } + } else { + current = is64Bit ? ARM64 : ARM32; + } + } +} \ No newline at end of file diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/OSUtils.java b/common/src/main/java/net/momirealms/customnameplates/common/util/OSUtils.java deleted file mode 100644 index f2c11db..0000000 --- a/common/src/main/java/net/momirealms/customnameplates/common/util/OSUtils.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) <2024> - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package net.momirealms.customnameplates.common.util; - -public class OSUtils { - - public static String getOSName() { - String os = System.getProperty("os.name").toLowerCase(); - if (os.contains("win")) { - return "windows"; - } else if (os.contains("mac")) { - return "macos"; - } else if (os.contains("nux") || os.contains("nix")) { - return "linux"; - } else if (os.contains("freebsd")) { - return "freebsd"; - } else { - return "unknown"; - } - } -} diff --git a/common/src/main/java/net/momirealms/customnameplates/common/util/Platform.java b/common/src/main/java/net/momirealms/customnameplates/common/util/Platform.java new file mode 100644 index 0000000..b08bf73 --- /dev/null +++ b/common/src/main/java/net/momirealms/customnameplates/common/util/Platform.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) <2024> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.common.util; + +public enum Platform { + + FREEBSD("FreeBSD", "freebsd"), + LINUX("Linux", "linux"), + MACOS("macOS", "macos"), + WINDOWS("Windows", "windows"); + + private static final Platform current; + private final String name; + private final String nativePath; + + Platform(String name, String nativePath) { + this.name = name; + this.nativePath = nativePath; + } + + public String getName() { + return this.name; + } + + public String getNativePath() { + return nativePath; + } + + public static Platform get() { + return current; + } + + static { + String osName = System.getProperty("os.name"); + if (osName.startsWith("Windows")) { + current = WINDOWS; + } else if (osName.startsWith("FreeBSD")) { + current = FREEBSD; + } else if (!osName.startsWith("Linux") && !osName.startsWith("SunOS") && !osName.startsWith("Unix")) { + if (!osName.startsWith("Mac OS X") && !osName.startsWith("Darwin")) { + throw new LinkageError("Unknown platform: " + osName); + } + current = MACOS; + } else { + current = LINUX; + } + } +} diff --git a/common/src/main/resources/custom-nameplates.properties b/common/src/main/resources/custom-nameplates.properties index 67b7f04..3d8b82f 100644 --- a/common/src/main/resources/custom-nameplates.properties +++ b/common/src/main/resources/custom-nameplates.properties @@ -26,4 +26,5 @@ jedis=${jedis_version} h2-driver=${h2_driver_version} sqlite-driver=${sqlite_driver_version} byte-buddy=${byte_buddy_version} -commons-io=${commons_io_version} \ No newline at end of file +commons-io=${commons_io_version} +lwjgl=${lwjgl_version} \ No newline at end of file diff --git a/compatibility/build.gradle.kts b/compatibility/build.gradle.kts index 54b6949..bd3a16f 100644 --- a/compatibility/build.gradle.kts +++ b/compatibility/build.gradle.kts @@ -38,6 +38,8 @@ dependencies { compileOnly("org.geysermc.geyser:api:2.4.2-SNAPSHOT") // Floodgate compileOnly("org.geysermc.floodgate:api:2.2.3-SNAPSHOT") + // Cosmetics + compileOnly("com.github.FrancoBM12:API-MagicCosmetics:2.2.9") } java { diff --git a/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/cosmetic/MagicCosmeticsHook.java b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/cosmetic/MagicCosmeticsHook.java new file mode 100644 index 0000000..b34e661 --- /dev/null +++ b/compatibility/src/main/java/net/momirealms/customnameplates/bukkit/compatibility/cosmetic/MagicCosmeticsHook.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) <2022> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package net.momirealms.customnameplates.bukkit.compatibility.cosmetic; + +import com.francobm.magicosmetics.api.Cosmetic; +import com.francobm.magicosmetics.api.CosmeticType; +import com.francobm.magicosmetics.cache.PlayerData; +import com.francobm.magicosmetics.cache.cosmetics.Hat; +import com.francobm.magicosmetics.events.*; +import net.momirealms.customnameplates.api.CNPlayer; +import net.momirealms.customnameplates.api.CustomNameplates; +import net.momirealms.customnameplates.api.feature.tag.TagRenderer; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +import java.util.concurrent.TimeUnit; + +public class MagicCosmeticsHook implements Listener { + + private final CustomNameplates plugin; + + public MagicCosmeticsHook(CustomNameplates plugin) { + this.plugin = plugin; + } + + @EventHandler(ignoreCancelled = true) + public void onChangeCos(CosmeticChangeEquipEvent event) { + final Cosmetic cosmetic = event.getNewCosmetic(); + final Player player = event.getPlayer(); + if (cosmetic instanceof Hat hat) { + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer != null) { + TagRenderer renderer = plugin.getUnlimitedTagManager().getTagRender(cnPlayer); + if (renderer != null) { + renderer.hatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY()); + } + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onEnterBlackListWorld(PlayerChangeBlacklistEvent event) { + var player = event.getPlayer(); + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer != null) { + TagRenderer renderer = plugin.getUnlimitedTagManager().getTagRender(cnPlayer); + if (renderer != null) { + if (event.isInWorldBlacklist()) { + renderer.hatOffset(0); + } else { + PlayerData playerData = PlayerData.getPlayer(player); + if (playerData != null) { + final Cosmetic cosmetic = playerData.getHat(); + if (cosmetic instanceof Hat hat) { + renderer.hatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY()); + } + } + } + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onEquip(CosmeticEquipEvent event) { + final Cosmetic cosmetic = event.getCosmetic(); + final Player player = event.getPlayer(); + if (cosmetic instanceof Hat hat) { + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer != null) { + TagRenderer renderer = plugin.getUnlimitedTagManager().getTagRender(cnPlayer); + if (renderer != null) { + renderer.hatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY()); + } + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onUnEquip(CosmeticUnEquipEvent event) { + final Player player = event.getPlayer(); + if (event.getCosmeticType() == CosmeticType.HAT) { + CNPlayer cnPlayer = plugin.getPlayer(player.getUniqueId()); + if (cnPlayer != null) { + TagRenderer renderer = plugin.getUnlimitedTagManager().getTagRender(cnPlayer); + if (renderer != null) { + renderer.hatOffset(0); + } + } + } + } + + @EventHandler (ignoreCancelled = true) + public void onDataLoaded(PlayerDataLoadEvent event) { + for (Cosmetic cosmetic : event.getEquippedCosmetics()) { + if (cosmetic instanceof Hat hat) { + PlayerData playerData = event.getPlayerData(); + plugin.getScheduler().asyncLater(() -> { + CNPlayer cnPlayer = plugin.getPlayer(playerData.getUniqueId()); + if (cnPlayer != null) { + TagRenderer renderer = plugin.getUnlimitedTagManager().getTagRender(cnPlayer); + if (renderer != null) { + renderer.hatOffset(hat.isHideCosmetic() ? 0 : hat.getOffSetY()); + } + } + }, 100, TimeUnit.MILLISECONDS); + } + } + } +} diff --git a/gradle.properties b/gradle.properties index c392db6..b97fa77 100644 --- a/gradle.properties +++ b/gradle.properties @@ -40,6 +40,7 @@ placeholder_api_version=2.11.6 vault_version=1.7 guava_version=33.2.0-jre commons_io_version=2.17.0 +lwjgl_version=3.3.4 # Proxy settings #systemProp.socks.proxyHost=127.0.0.1