From ced4c0d186efe9469f167a9eea0513f3eaf27281 Mon Sep 17 00:00:00 2001 From: unurled Date: Fri, 6 Sep 2024 09:29:26 +0200 Subject: [PATCH] add of SR-Core and many more things --- build.gradle.kts | 148 +- gradle.properties | 3 +- .../java/me/unurled/sacredrealms/sr/SR.java | 65 +- .../me/unurled/sacredrealms/sr/SRLoader.java | 37 +- .../sr/commands/CommandManager.java | 23 +- .../sr/commands/admin/AttributeCommand.java | 10 +- .../sr/commands/admin/ClientBuildCommand.java | 15 +- .../sr/commands/admin/CutsceneCommand.java | 166 + .../sr/commands/admin/EntityTypeCommand.java | 31 +- .../sr/commands/admin/ItemCommand.java | 20 +- .../sr/commands/admin/LevelCommand.java | 2 +- .../sr/commands/admin/SpawnEntityCommand.java | 17 +- .../sr/commands/admin/TreasureCommand.java | 23 +- .../sr/commands/player/DifficultyCommand.java | 10 +- .../player/ResetAdventureCommand.java | 13 +- .../sr/components/block/Block.java | 64 + .../sr/components/block/BlockItem.java | 72 + .../sr/components/block/BlockManager.java | 84 + .../clientbuild/ClientBuildDeserializer.java | 24 +- .../clientbuild/ClientBuildManager.java | 8 +- .../clientbuild/ClientBuildSerializer.java | 15 +- .../sr/components/combat/CombatManager.java | 46 +- .../sr/components/cutscene/Action.java | 26 + .../sr/components/cutscene/Cutscene.java | 239 + .../components/cutscene/CutsceneManager.java | 168 + .../cutscene/EntityCinematicAction.java | 7 + .../sr/components/cutscene/Frame.java | 113 + .../sr/components/cutscene/Marker.java | 146 + .../sr/components/cutscene/PlayerOrigin.java | 23 + .../components/cutscene/PlayerRecording.java | 82 + .../components/cutscene/gson/ActionGson.java | 63 + .../cutscene/gson/EntityActionGson.java | 64 + .../sr/components/cutscene/npc/ArmSwing.java | 37 + .../components/cutscene/npc/EntityFrame.java | 66 + .../sr/components/entity/EntityManager.java | 61 +- .../sr/components/entity/SREntity.java | 32 +- .../sr/components/entity/SREntityType.java | 16 +- .../sacredrealms/sr/components/item/Item.java | 4 +- .../sr/components/item/ItemManager.java | 9 +- .../sr/components/npc/NPCEntity.java | 10 + .../sr/components/npc/NPCManager.java | 64 + .../components/pack/ResourcePackManager.java | 158 + .../animatedjava/AnimatedJavaManager.java | 5 + .../pack/background/Background.java | 223 + .../pack/background/BackgroundManager.java | 139 + .../pack/hosts/HostingProvider.java | 29 + .../components/pack/hosts/RustProvider.java | 151 + .../sr/components/packet/PacketManager.java | 12 + .../sr/components/player/PlayerManager.java | 38 +- .../sr/components/player/SRPlayer.java | 14 +- .../player/listener/PlayerBlockEvent.java | 2 +- .../treasure/ItemStackMapSerializer.java | 60 + .../treasure/TreasureDeserializer.java | 34 +- .../treasure/TreasureGenerator.java | 2 +- .../components/treasure/TreasureManager.java | 46 +- .../treasure/TreasureSerializer.java | 20 +- .../sacredrealms/sr/config/ConfigManager.java | 40 + .../sacredrealms/sr/config/Settings.java | 95 + .../sacredrealms/sr/constants/Keys.java | 9 + .../sacredrealms/sr/data/DataManager.java | 37 +- .../unurled/sacredrealms/sr/data/Redis.java | 21 +- .../unurled/sacredrealms/sr/gui/BackItem.java | 2 +- .../sr/gui/attributes/AttributeItem.java | 2 +- .../sr/gui/difficulty/DifficultyItem.java | 6 +- .../sr/gui/entitytype/EntityArmorItem.java | 45 +- .../sr/gui/entitytype/EntityTypeGUI.java | 6 +- .../sr/gui/treasure/CancelItem.java | 45 + .../sr/gui/treasure/ConfirmItem.java | 80 + .../sr/gui/treasure/DeleteItem.java | 64 + .../sr/gui/treasure/TreasureGUI.java | 58 +- .../sacredrealms/sr/managers/Manager.java | 51 - .../sacredrealms/sr/managers/Managers.java | 79 - .../sacredrealms/sr/utils/ChunkWrapper.java | 65 + .../sacredrealms/sr/utils/Component.java | 7 + .../sacredrealms/sr/utils/CutsceneUtil.java | 67 + .../unurled/sacredrealms/sr/utils/File.java | 70 + .../sr/utils/HTTPRequestMultipartBody.java | 156 + .../unurled/sacredrealms/sr/utils/Items.java | 50 +- .../sacredrealms/sr/utils/SRPlayerUtils.java | 21 +- .../sr/utils/character/CharRepo.java | 235 + .../sr/utils/character/CharacterArranger.java | 39 + .../sr/utils/character/ConfiguredChar.java | 104 + .../sr/utils/gson/EntityGson.java | 97 + .../sr/utils/gson/LocationSerializer.java | 51 + .../sr/utils/gson/MapSerializer.java | 34 + src/main/resources/backgrounds/b0.png | Bin 0 -> 1615 bytes src/main/resources/backgrounds/b1.png | Bin 0 -> 1609 bytes src/main/resources/backgrounds/b128.png | Bin 0 -> 1621 bytes src/main/resources/backgrounds/b16.png | Bin 0 -> 1614 bytes src/main/resources/backgrounds/b2.png | Bin 0 -> 1611 bytes src/main/resources/backgrounds/b32.png | Bin 0 -> 1595 bytes src/main/resources/backgrounds/b4.png | Bin 0 -> 1615 bytes src/main/resources/backgrounds/b64.png | Bin 0 -> 1614 bytes src/main/resources/backgrounds/b8.png | Bin 0 -> 1612 bytes src/main/resources/config.yml | 5 +- src/main/resources/paper-plugin.yml | 59 - src/main/resources/rp/assets.ajmeta | 1 + .../rp/assets/animated_java/models/empty.json | 1 + .../animated_java/models/item/sr/bottom.json | 1 + .../animated_java/models/item/sr/top.json | 1 + .../item/sr/treasure_chest_bottom.png | Bin 0 -> 309 bytes .../textures/item/sr/treasure_chest_metal.png | Bin 0 -> 125 bytes .../textures/item/sr/treasure_chest_top.png | Bin 0 -> 265 bytes .../textures/item/transparent.png | Bin 0 -> 75 bytes .../rp/assets/minecraft/font/custom.json | 11 + .../rp/assets/minecraft/font/negative.json | 580 ++ .../minecraft/models/item/leather_helmet.json | 1 + .../shaders/core/rendertype_text.fsh | 31 + .../shaders/core/rendertype_text.json | 28 + .../shaders/core/rendertype_text.vsh | 47 + .../rp/assets/minecraft/textures/dark.png | Bin 0 -> 875 bytes .../rp/assets/minecraft/textures/font/bg.png | Bin 0 -> 591 bytes .../minecraft/textures/font/left_close_bg.png | Bin 0 -> 597 bytes .../textures/font/right_close_bg.png | Bin 0 -> 596 bytes .../assets/minecraft/textures/gui/icons.png | Bin 0 -> 1278 bytes .../assets/minecraft/textures/gui/widgets.png | Bin 0 -> 18615 bytes .../minecraft/textures/misc/pumpkinblur.png | Bin 0 -> 2172 bytes .../rp/assets/space/font/default.json | 679 ++ .../resources/rp/assets/space/lang/en_us.json | 8291 +++++++++++++++++ .../space/textures/font/space_nosplit.png | Bin 0 -> 70 bytes .../space/textures/font/space_split.png | Bin 0 -> 336 bytes src/main/resources/rp/pack.mcmeta | 7 + 122 files changed, 13914 insertions(+), 524 deletions(-) create mode 100644 src/main/java/me/unurled/sacredrealms/sr/commands/admin/CutsceneCommand.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/block/Block.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/block/BlockItem.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/block/BlockManager.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Action.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Cutscene.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/CutsceneManager.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/EntityCinematicAction.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Frame.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Marker.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/PlayerOrigin.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/PlayerRecording.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/gson/ActionGson.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/gson/EntityActionGson.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/npc/ArmSwing.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/cutscene/npc/EntityFrame.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/npc/NPCEntity.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/npc/NPCManager.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/pack/ResourcePackManager.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/pack/animatedjava/AnimatedJavaManager.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/pack/background/Background.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/pack/background/BackgroundManager.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/pack/hosts/HostingProvider.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/pack/hosts/RustProvider.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/packet/PacketManager.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/components/treasure/ItemStackMapSerializer.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/config/ConfigManager.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/config/Settings.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/constants/Keys.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/gui/treasure/CancelItem.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/gui/treasure/ConfirmItem.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/gui/treasure/DeleteItem.java delete mode 100644 src/main/java/me/unurled/sacredrealms/sr/managers/Manager.java delete mode 100644 src/main/java/me/unurled/sacredrealms/sr/managers/Managers.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/ChunkWrapper.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/CutsceneUtil.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/File.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/HTTPRequestMultipartBody.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/character/CharRepo.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/character/CharacterArranger.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/character/ConfiguredChar.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/gson/EntityGson.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/gson/LocationSerializer.java create mode 100644 src/main/java/me/unurled/sacredrealms/sr/utils/gson/MapSerializer.java create mode 100644 src/main/resources/backgrounds/b0.png create mode 100644 src/main/resources/backgrounds/b1.png create mode 100644 src/main/resources/backgrounds/b128.png create mode 100644 src/main/resources/backgrounds/b16.png create mode 100644 src/main/resources/backgrounds/b2.png create mode 100644 src/main/resources/backgrounds/b32.png create mode 100644 src/main/resources/backgrounds/b4.png create mode 100644 src/main/resources/backgrounds/b64.png create mode 100644 src/main/resources/backgrounds/b8.png delete mode 100644 src/main/resources/paper-plugin.yml create mode 100644 src/main/resources/rp/assets.ajmeta create mode 100644 src/main/resources/rp/assets/animated_java/models/empty.json create mode 100644 src/main/resources/rp/assets/animated_java/models/item/sr/bottom.json create mode 100644 src/main/resources/rp/assets/animated_java/models/item/sr/top.json create mode 100644 src/main/resources/rp/assets/animated_java/textures/item/sr/treasure_chest_bottom.png create mode 100644 src/main/resources/rp/assets/animated_java/textures/item/sr/treasure_chest_metal.png create mode 100644 src/main/resources/rp/assets/animated_java/textures/item/sr/treasure_chest_top.png create mode 100644 src/main/resources/rp/assets/animated_java/textures/item/transparent.png create mode 100644 src/main/resources/rp/assets/minecraft/font/custom.json create mode 100644 src/main/resources/rp/assets/minecraft/font/negative.json create mode 100644 src/main/resources/rp/assets/minecraft/models/item/leather_helmet.json create mode 100644 src/main/resources/rp/assets/minecraft/shaders/core/rendertype_text.fsh create mode 100644 src/main/resources/rp/assets/minecraft/shaders/core/rendertype_text.json create mode 100644 src/main/resources/rp/assets/minecraft/shaders/core/rendertype_text.vsh create mode 100644 src/main/resources/rp/assets/minecraft/textures/dark.png create mode 100644 src/main/resources/rp/assets/minecraft/textures/font/bg.png create mode 100644 src/main/resources/rp/assets/minecraft/textures/font/left_close_bg.png create mode 100644 src/main/resources/rp/assets/minecraft/textures/font/right_close_bg.png create mode 100644 src/main/resources/rp/assets/minecraft/textures/gui/icons.png create mode 100644 src/main/resources/rp/assets/minecraft/textures/gui/widgets.png create mode 100644 src/main/resources/rp/assets/minecraft/textures/misc/pumpkinblur.png create mode 100644 src/main/resources/rp/assets/space/font/default.json create mode 100644 src/main/resources/rp/assets/space/lang/en_us.json create mode 100644 src/main/resources/rp/assets/space/textures/font/space_nosplit.png create mode 100644 src/main/resources/rp/assets/space/textures/font/space_split.png create mode 100644 src/main/resources/rp/pack.mcmeta diff --git a/build.gradle.kts b/build.gradle.kts index 412e183..e9d6daf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,8 +1,10 @@ +import net.minecrell.pluginyml.bukkit.BukkitPluginDescription + plugins { `java-library` - id("io.papermc.paperweight.userdev") version "1.7.1" - id("io.github.goooler.shadow") version "8.1.7" - id("org.sonarqube") version "4.4.1.3373" + id("io.papermc.paperweight.userdev") version "1.7.2" + id("org.sonarqube") version "5.1.0.4882" + id("net.minecrell.plugin-yml.paper") version "0.6.0" } // from 1.20.5+ upward, spigot is not supported @@ -13,23 +15,42 @@ group = "me.unurled.sacredrealms" version = "0.1.0" description = "The main SR plugin." -val mcVersion = "1.21-R0.1-SNAPSHOT" -val redisVersion = "5.2.0-beta2" -val invuiVersion = "1.32" +val mcVersion = "1.21.1-R0.1-SNAPSHOT" +val redisVersion = "5.2.0-beta4" +val invuiVersion = "1.36" +val gsonVersion = "2.11.0" val javaVersion = 21 repositories { mavenCentral() - maven("https://repo.xenondevs.xyz/releases") + maven { + name = "invui" + url = uri("https://repo.xenondevs.xyz/releases/") + } + maven { + name = "packetevents" + url = uri("https://repo.codemc.io/repository/maven-releases/") + } + maven { + name = "unurled" + url = uri("https://repo.unurled.me/releases") + } } dependencies { paperweight.paperDevBundle(mcVersion) - implementation("redis.clients:jedis:${redisVersion}") - compileOnly("xyz.xenondevs.invui:invui:${invuiVersion}") + + paperLibrary("com.google.code.gson", "gson", gsonVersion) + + paperLibrary("redis.clients", "jedis", redisVersion) + + + paperLibrary("com.github.retrooper", "packetevents-spigot", "2.4.0") + + paperLibrary("me.unurled", "SR-Core", "0.1.0") } java { @@ -45,37 +66,92 @@ tasks { javadoc { options.encoding = Charsets.UTF_8.name() } - processResources { - filteringCharset = Charsets.UTF_8.name() - val props = mapOf( - "name" to project.name, - "version" to project.version, - "description" to project.description, - "apiVersion" to "1.20" - ) - inputs.properties(props) - filesMatching("paper-plugin.yml") { - expand(props) - } - } - build { - dependsOn(shadowJar) - } - shadowJar { - archiveClassifier.set("") - relocate("com.google", "libs.com.google") - relocate("org.apache", "libs.org.apache") - relocate("org.intellij", "libs.org.intellij") - relocate("org.jetbrains", "libs.org.jetbrains") - relocate("org.json", "libs.org.json") - relocate("redis", "libs.redis") -// relocate("xyz.xenondevs", "libs.xyz.xenondevs") - exclude("org.slf4j") - } sonar { properties { property("sonar.projectKey", "Sacred-Realms") property("sonar.host.url", "https://qube.unurled.me") } } +} + +paper { + main = "me.unurled.sacredrealms.sr.SR" + + bootstrapper = "me.unurled.sacredrealms.sr.SRBootstrap" + loader = "me.unurled.sacredrealms.sr.SRLoader" + hasOpenClassloader = false + + generateLibrariesJson = true + foliaSupported = false + + apiVersion = "1.21" + + load = BukkitPluginDescription.PluginLoadOrder.STARTUP + author = "unurled" + + prefix = "SR" + + defaultPermission = BukkitPluginDescription.Permission.Default.OP + + permissions { + register("sr.*") { + children = listOf( + "sr.tutorial", + "sr.spawn", + "sr.attributes", + "sr.clientbuild", + "sr.level", + "sr.entitytype", + "sr.spawnentity", + "sr.admin.item", + "sr.resetadventure", + "sr.treasure.manage", + "sr.difficulty.*", + "sr.cutscene" + ) + description = "Gives access to all Sacred Realms permissions." + } + register("sr.tutorial") { + description = "Gives access to the tutorial command." + default = BukkitPluginDescription.Permission.Default.NOT_OP + } + register("sr.spawn") { + description = "Gives access to the spawn command." + default = BukkitPluginDescription.Permission.Default.NOT_OP + } + register("sr.attributes") { + description = "Gives access to the attributes command." + } + register("sr.clientbuild") { + description = "Gives access to the clientbuild command." + } + register("sr.level") { + description = "Gives access to the level command." + } + register("sr.entitytype") { + description = "Gives access to the entitytype command." + } + register("sr.spawnentity") { + description = "Gives access to the spawnentity command." + } + register("sr.admin.item") { + description = "Gives access to the admin item command." + } + register("sr.resetadventure") { + description = "Gives access to the resetadventure command." + } + register("sr.treasure.manage") { + description = "Gives access to the treasure manage command." + } + register("sr.difficulty.*") { + description = "Gives access to all difficulty commands." + children = listOf( + "sr.difficulty.self", + "sr.difficulty.manage" + ) + } + register("sr.cutscene") { + description = "Gives access to the cutscene command." + } + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 89a0681..c9d5d7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ -systemProp.sonar.gradle.skipCompile=true \ No newline at end of file +systemProp.sonar.gradle.skipCompile=true +org.gradle.jvmargs=-Xmx16384M \ No newline at end of file diff --git a/src/main/java/me/unurled/sacredrealms/sr/SR.java b/src/main/java/me/unurled/sacredrealms/sr/SR.java index 8e85f72..79ec323 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/SR.java +++ b/src/main/java/me/unurled/sacredrealms/sr/SR.java @@ -1,34 +1,73 @@ package me.unurled.sacredrealms.sr; -import me.unurled.sacredrealms.sr.managers.Managers; +import com.github.retrooper.packetevents.PacketEvents; +import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder; +import java.util.List; +import me.unurled.sacredrealms.sr.components.block.BlockManager; +import me.unurled.sacredrealms.sr.components.clientbuild.ClientBuildManager; +import me.unurled.sacredrealms.sr.components.combat.CombatManager; +import me.unurled.sacredrealms.sr.components.entity.EntityManager; +import me.unurled.sacredrealms.sr.components.item.ItemManager; +import me.unurled.sacredrealms.sr.components.npc.NPCManager; +import me.unurled.sacredrealms.sr.components.pack.ResourcePackManager; +import me.unurled.sacredrealms.sr.components.pack.animatedjava.AnimatedJavaManager; +import me.unurled.sacredrealms.sr.components.pack.background.BackgroundManager; +import me.unurled.sacredrealms.sr.components.player.PlayerManager; +import me.unurled.sacredrealms.sr.components.treasure.TreasureManager; +import me.unurled.sacredrealms.sr.config.ConfigManager; +import me.unurled.sacredrealms.sr.data.DataManager; +import me.unurled.srcore.SRCore; import org.bukkit.plugin.java.JavaPlugin; import xyz.xenondevs.invui.InvUI; public final class SR extends JavaPlugin { - private static SR instance; - private Managers managers; + private static SR plugin; - public static SR getInstance() { - return instance; + public static SR getPlugin() { + return plugin; + } + + @Override + public void onLoad() { + PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this)); + // On Bukkit, calling this here is essential, hence the name "load" + PacketEvents.getAPI().load(); } @Override public void onEnable() { - instance = this; + plugin = this; + + SRCore.getInstance().setPlugin(this); + + PacketEvents.getAPI().init(); + + SRCore.getInstance() + .getManagers() + .register( + List.of( + ConfigManager.class, + DataManager.class, + AnimatedJavaManager.class, + PlayerManager.class, + CombatManager.class, + ItemManager.class, + EntityManager.class, + NPCManager.class, + BlockManager.class, + ClientBuildManager.class, + TreasureManager.class, + BackgroundManager.class, + ResourcePackManager.class)); InvUI.getInstance().setPlugin(this); - - managers = new Managers(); } @Override public void onDisable() { - managers.unload(); - managers = null; - } + SRCore.getInstance().unload(); - public Managers getManagers() { - return managers; + PacketEvents.getAPI().terminate(); } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/SRLoader.java b/src/main/java/me/unurled/sacredrealms/sr/SRLoader.java index 53899f6..439162a 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/SRLoader.java +++ b/src/main/java/me/unurled/sacredrealms/sr/SRLoader.java @@ -1,8 +1,15 @@ package me.unurled.sacredrealms.sr; +import com.google.gson.Gson; import io.papermc.paper.plugin.loader.PluginClasspathBuilder; import io.papermc.paper.plugin.loader.PluginLoader; import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; import org.eclipse.aether.artifact.DefaultArtifact; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.repository.RemoteRepository; @@ -12,15 +19,37 @@ public class SRLoader implements PluginLoader { @Override public void classloader(@NotNull PluginClasspathBuilder classpathBuilder) { - MavenLibraryResolver resolver = new MavenLibraryResolver(); resolver.addDependency( - new Dependency(new DefaultArtifact("xyz.xenondevs.invui:inventory-access-r20:1.32"), null)); - resolver.addDependency( - new Dependency(new DefaultArtifact("xyz.xenondevs.invui:invui-core:1.32"), null)); + new Dependency(new DefaultArtifact("xyz.xenondevs.invui:invui:pom:1.36"), null)); resolver.addRepository( new RemoteRepository.Builder("invui", "default", "https://repo.xenondevs.xyz/releases/") .build()); classpathBuilder.addLibrary(resolver); + + PluginLibraries pluginLibraries = load(); + pluginLibraries.asDependencies().forEach(resolver::addDependency); + pluginLibraries.asRepositories().forEach(resolver::addRepository); + classpathBuilder.addLibrary(resolver); + } + + public PluginLibraries load() { + try (var in = getClass().getResourceAsStream("/paper-libraries.json")) { + return new Gson() + .fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), PluginLibraries.class); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private record PluginLibraries(Map repositories, List dependencies) { + public Stream asDependencies() { + return dependencies.stream().map(d -> new Dependency(new DefaultArtifact(d), null)); + } + + public Stream asRepositories() { + return repositories.entrySet().stream() + .map(e -> new RemoteRepository.Builder(e.getKey(), "default", e.getValue()).build()); + } } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/CommandManager.java b/src/main/java/me/unurled/sacredrealms/sr/commands/CommandManager.java index 876b39f..929c597 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/CommandManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/CommandManager.java @@ -1,15 +1,17 @@ package me.unurled.sacredrealms.sr.commands; import static me.unurled.sacredrealms.sr.utils.Logger.error; -import static me.unurled.sacredrealms.sr.utils.Logger.log; import io.papermc.paper.command.brigadier.BasicCommand; import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.plugin.bootstrap.BootstrapContext; import io.papermc.paper.plugin.lifecycle.event.LifecycleEventManager; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; +import java.util.Collection; +import java.util.List; import me.unurled.sacredrealms.sr.commands.admin.AttributeCommand; import me.unurled.sacredrealms.sr.commands.admin.ClientBuildCommand; +import me.unurled.sacredrealms.sr.commands.admin.CutsceneCommand; import me.unurled.sacredrealms.sr.commands.admin.EntityTypeCommand; import me.unurled.sacredrealms.sr.commands.admin.ItemCommand; import me.unurled.sacredrealms.sr.commands.admin.LevelCommand; @@ -17,12 +19,22 @@ import me.unurled.sacredrealms.sr.commands.admin.SpawnEntityCommand; import me.unurled.sacredrealms.sr.commands.admin.TreasureCommand; import me.unurled.sacredrealms.sr.commands.player.DifficultyCommand; import me.unurled.sacredrealms.sr.commands.player.ResetAdventureCommand; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; +import org.jetbrains.annotations.NotNull; public class CommandManager extends Manager { private static void registerCommand( - String command, String description, BootstrapContext ctx, Class clazz) { + String command, String description, @NotNull BootstrapContext ctx, Class clazz) { + registerCommand(command, description, List.of(), ctx, clazz); + } + + private static void registerCommand( + String command, + String description, + @NotNull Collection aliases, + @NotNull BootstrapContext ctx, + Class clazz) { LifecycleEventManager man = ctx.getLifecycleManager(); try { @@ -31,9 +43,8 @@ public class CommandManager extends Manager { LifecycleEvents.COMMANDS, event -> { final Commands commands = event.registrar(); - commands.register(command, description, instance); + commands.register(command, description, aliases, instance); }); - log("Registered command: " + command); } catch (Exception e) { error("Failed to register command: " + command + " - " + e.getMessage()); } @@ -54,5 +65,7 @@ public class CommandManager extends Manager { "resetadventure", "Reset the adventure of the player.", ctx, ResetAdventureCommand.class); registerCommand( "difficulty", "Set the difficulty of the player.", ctx, DifficultyCommand.class); + + registerCommand("cutscene", "Cutscene commands.", List.of("cs"), ctx, CutsceneCommand.class); } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/AttributeCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/AttributeCommand.java index 6857da7..6b7b180 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/AttributeCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/AttributeCommand.java @@ -13,7 +13,7 @@ import me.unurled.sacredrealms.sr.components.attributes.Attribute; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; import me.unurled.sacredrealms.sr.gui.attributes.AttributesGUI; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -192,7 +192,7 @@ public class AttributeCommand implements BasicCommand { * Executes the command with the given {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command ignoring repeated spaces + * @param args the arguments of the command ignoring repeated spaces */ @Override public void execute(@NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { @@ -252,12 +252,12 @@ public class AttributeCommand implements BasicCommand { * Suggests possible completions for the given command {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command including repeated spaces + * @param args the arguments of the command including repeated spaces * @return a collection of suggestions */ @Override - public @NotNull Collection suggest(@NotNull CommandSourceStack commandSourceStack, - @NotNull String[] args) { + public @NotNull Collection suggest( + @NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { CommandSender sender = commandSourceStack.getSender(); if (!sender.hasPermission("sr.attributes")) { return List.of(); diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/ClientBuildCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/ClientBuildCommand.java index e8c7a5d..4ab0f51 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/ClientBuildCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/ClientBuildCommand.java @@ -16,7 +16,7 @@ import me.unurled.sacredrealms.sr.components.clientbuild.ClientBuild; import me.unurled.sacredrealms.sr.components.clientbuild.ClientBuildManager; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; @@ -179,6 +179,7 @@ public class ClientBuildCommand implements BasicCommand, Listener { if (build != null) sender.sendMessage(build.getName()); } } + @EventHandler public void onBlockPlace(@NotNull BlockPlaceEvent e) { Player p = e.getPlayer(); @@ -226,7 +227,7 @@ public class ClientBuildCommand implements BasicCommand, Listener { * Executes the command with the given {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command ignoring repeated spaces + * @param args the arguments of the command ignoring repeated spaces */ @Override public void execute(@NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { @@ -282,18 +283,18 @@ public class ClientBuildCommand implements BasicCommand, Listener { * Suggests possible completions for the given command {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command including repeated spaces + * @param args the arguments of the command including repeated spaces * @return a collection of suggestions */ @Override - public @NotNull Collection suggest(@NotNull CommandSourceStack commandSourceStack, - @NotNull String[] args) { + public @NotNull Collection suggest( + @NotNull CommandSourceStack commandSourceStack, @NotNull String @NotNull [] args) { if (args.length == 1) { return List.of("create", DELETE, "list", MODIFY, "save", DISPLAY); } else if (args.length == 2 && (args[0].equalsIgnoreCase(DELETE) - || args[0].equalsIgnoreCase(MODIFY) - || args[0].equalsIgnoreCase(DISPLAY))) { + || args[0].equalsIgnoreCase(MODIFY) + || args[0].equalsIgnoreCase(DISPLAY))) { ClientBuildManager clientBuildManager = Manager.getInstance(ClientBuildManager.class); List buildNames = clientBuildManager.getBuildNames(); if (nameCompletions.isEmpty() || !new HashSet<>(nameCompletions).containsAll(buildNames)) { diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/CutsceneCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/CutsceneCommand.java new file mode 100644 index 0000000..5cc580f --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/CutsceneCommand.java @@ -0,0 +1,166 @@ +package me.unurled.sacredrealms.sr.commands.admin; + +import static me.unurled.sacredrealms.sr.utils.Component.textComp; + +import io.papermc.paper.command.brigadier.BasicCommand; +import io.papermc.paper.command.brigadier.CommandSourceStack; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import me.unurled.sacredrealms.sr.components.cutscene.Cutscene; +import me.unurled.sacredrealms.sr.components.cutscene.CutsceneManager; +import me.unurled.srcore.api.Manager; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CutsceneCommand implements BasicCommand { + + private CutsceneManager cutsceneManager; + + /** + * Returns the permission for the root command used in {@link #canUse(CommandSender)} by default. + * + * @return the permission for the root command used in {@link #canUse(CommandSender)} + */ + @Override + public @Nullable String permission() { + return "cutscenes.admin"; + } + + /** + * Executes the command with the given {@link CommandSourceStack} and arguments. + * + * @param sender the commandSourceStack of the command + * @param args the arguments of the command ignoring repeated spaces + */ + @Override + public void execute(@NotNull CommandSourceStack sender, @NotNull String @NotNull [] args) { + if (cutsceneManager == null) { + cutsceneManager = Manager.getInstance(CutsceneManager.class); + } + // /cs stop, list, reload, play, create, remove + if (args.length == 1) { + if (args[0].equals("stop")) { + if (sender.getSender() instanceof Player p) { + cutsceneManager.stopCutscene(p); + } else { + sender + .getSender() + .sendMessage( + textComp("You must be a player to stop a cutscene. Use /cs stop ")); + } + } else if (args[0].equals("list")) { + sender + .getSender() + .sendMessage( + textComp("Cutscenes: " + cutsceneManager.getCutscenesId().toString())); + } else if (args[0].equals("reload")) { + + } + } else if (args.length == 2) { + if (args[0].equals("play")) { + if (sender.getSender() instanceof Player p) { + Cutscene cutscene = cutsceneManager.getCutscene(args[1]); + cutsceneManager.playCutscene(cutscene, p); + } else { + sender + .getSender() + .sendMessage( + textComp( + "You must be a player to play a cutscene. Use /cs play ")); + } + } else if (args[0].equals("stop")) { + Player p = Bukkit.getPlayer(args[1]); + if (p != null) { + cutsceneManager.stopCutscene(p); + } + } else if (args[0].equals("create")) { + String name = args[1]; + cutsceneManager.createCutscene(name); + sender.getSender().sendMessage(textComp("Created cutscene " + name)); + } else if (args[0].equals("remove")) { + String name = args[1]; + cutsceneManager.removeCutscene(name); + sender.getSender().sendMessage(textComp("Removed cutscene " + name)); + } + } else if (args.length == 3) { + if (args[0].equals("play")) { + Player p = Bukkit.getPlayer(args[2]); + if (p != null) { + Cutscene cutscene = cutsceneManager.getCutscene(args[1]); + cutsceneManager.playCutscene(cutscene, p); + } + } + } else { + sender + .getSender() + .sendMessage( + textComp( + "Usage: /cs ")); + } + } + + /** + * Suggests possible completions for the given command {@link CommandSourceStack} and arguments. + * + * @param sender the commandSourceStack of the command + * @param args the arguments of the command including repeated spaces + * @return a collection of suggestions + */ + @Override + public @NotNull Collection suggest( + @NotNull CommandSourceStack sender, @NotNull String[] args) { + if (cutsceneManager == null) { + cutsceneManager = Manager.getInstance(CutsceneManager.class); + } + List complete = new ArrayList<>(); + complete.add("stop"); + if (sender.getSender().hasPermission("cutscenes.admin")) { + complete.add("play"); + complete.add("list"); + complete.add("reload"); + complete.add("create"); + complete.add("remove"); + } + List result = new ArrayList<>(); + if (args.length == 1) { + for (String s : complete) { + if (s.toLowerCase().startsWith(args[0].toLowerCase())) { + result.add(s); + } + } + return result; + } else if (args.length == 2) { + if (sender.getSender().hasPermission("cutscenes.admin")) { + if (args[0].equalsIgnoreCase("stop")) { + for (Player p : Bukkit.getOnlinePlayers()) { + if (p.getName().toLowerCase().startsWith(args[1].toLowerCase())) { + result.add(p.getName()); + } + } + } else if (args[0].equalsIgnoreCase("play") || args[0].equalsIgnoreCase("remove")) { + for (String s : cutsceneManager.getCutscenesId()) { + if (s.toLowerCase().startsWith(args[1].toLowerCase())) { + result.add(s); + } + } + } + } + return result; + } else if (args.length == 3) { + if (sender.getSender().hasPermission("cutscenes.admin")) { + if (args[0].equalsIgnoreCase("play")) { + for (Player p : Bukkit.getOnlinePlayers()) { + if (p.getName().toLowerCase().startsWith(args[2].toLowerCase())) { + result.add(p.getName()); + } + } + } + } + } + return List.of(); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/EntityTypeCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/EntityTypeCommand.java index f88f7c4..995d798 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/EntityTypeCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/EntityTypeCommand.java @@ -12,7 +12,7 @@ import java.util.List; import me.unurled.sacredrealms.sr.components.entity.EntityManager; import me.unurled.sacredrealms.sr.components.entity.SREntityType; import me.unurled.sacredrealms.sr.gui.entitytype.EntityTypeGUI; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.command.CommandSender; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @@ -33,7 +33,7 @@ public class EntityTypeCommand implements BasicCommand { } Window window = - Window.single().setViewer(p).setTitle(args[1]).setGui(EntityTypeGUI.listGui()).build(); + Window.single().setViewer(p).setTitle("List").setGui(EntityTypeGUI.listGui()).build(); window.open(); } @@ -74,8 +74,7 @@ public class EntityTypeCommand implements BasicCommand { try { EntityType type = EntityType.valueOf(args[4]); EntityManager em = Manager.getInstance(EntityManager.class); - if (setType(sender, args, em, type)) { - } + if (setType(sender, args, em, type)) {} } catch (IllegalArgumentException e) { sender.sendMessage("Invalid entity type."); } @@ -91,7 +90,7 @@ public class EntityTypeCommand implements BasicCommand { if (!em.getTypes().stream().map(SREntityType::getId).toList().contains(args[1])) { sender.sendMessage("Invalid entity type."); } -// em.removeEntityType(em.getType(args[1])); + // em.removeEntityType(em.getType(args[1])); } private static void create(@NotNull CommandSender sender, @NotNull String @NotNull [] args) { @@ -109,13 +108,13 @@ public class EntityTypeCommand implements BasicCommand { sender.sendMessage(textComp("Entity type created successfully.")); if (sender instanceof Player p) { // opens gui - Window window = - Window.single() - .setViewer(p) - .setTitle(args[1]) - .setGui(EntityTypeGUI.createGui(type)) - .build(); - window.open(); + Window window = + Window.single() + .setViewer(p) + .setTitle(args[1]) + .setGui(EntityTypeGUI.createGui(type)) + .build(); + window.open(); } } @@ -137,7 +136,7 @@ public class EntityTypeCommand implements BasicCommand { * Executes the command with the given {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command ignoring repeated spaces + * @param args the arguments of the command ignoring repeated spaces */ @Override public void execute(@NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { @@ -165,12 +164,12 @@ public class EntityTypeCommand implements BasicCommand { * Suggests possible completions for the given command {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command including repeated spaces + * @param args the arguments of the command including repeated spaces * @return a collection of suggestions */ @Override - public @NotNull Collection suggest(@NotNull CommandSourceStack commandSourceStack, - @NotNull String[] args) { + public @NotNull Collection suggest( + @NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { CommandSender sender = commandSourceStack.getSender(); if (sender.hasPermission("sr.entitytype")) { if (args.length == 1) { diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/ItemCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/ItemCommand.java index b991851..b26d54d 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/ItemCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/ItemCommand.java @@ -15,7 +15,7 @@ import me.unurled.sacredrealms.sr.components.item.ItemType; import me.unurled.sacredrealms.sr.components.item.Rarity; import me.unurled.sacredrealms.sr.components.item.abilities.Ability; import me.unurled.sacredrealms.sr.components.item.enchantments.Enchantment; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.command.CommandSender; @@ -380,7 +380,7 @@ public class ItemCommand implements BasicCommand { .map( name -> { if (name.name().startsWith(args[3].toUpperCase())) return name.name(); - return null; + return ""; }) .toList(); } @@ -392,8 +392,8 @@ public class ItemCommand implements BasicCommand { p.getInventory().setItemInMainHand(newItem.toItemStack()); } - @Nullable - private static List modifyCompletion(@NotNull String @NotNull [] args) { + private static @NotNull List modifyCompletion(@NotNull String @NotNull [] args) { + if (args.length < 3) return List.of(); if (args[2].equalsIgnoreCase(RARITY)) { return Arrays.stream(Rarity.values()).map(Enum::name).toList(); } else if (args[2].equalsIgnoreCase("type")) { @@ -403,7 +403,7 @@ public class ItemCommand implements BasicCommand { .map( name -> { if (name.name().startsWith(args[3].toUpperCase())) return name.name(); - return null; + return ""; }) .toList(); } else if (args[2].equalsIgnoreCase(ENCHANTMENTS)) { @@ -414,12 +414,12 @@ public class ItemCommand implements BasicCommand { return List.of("add", REMOVE); } else if (args[2].equalsIgnoreCase(CUSTOMMODELDATA)) { try { - Integer.parseInt(args[3]); + if (args.length == 4) Integer.parseInt(args[3]); } catch (NumberFormatException e) { return List.of("1"); } } - return null; + return List.of(); } @Nullable @@ -507,14 +507,14 @@ public class ItemCommand implements BasicCommand { return List.of(""); } - if (args.length == 1) { + if (args.length <= 1) { return List.of("list", "create", DELETE, "give", MODIFY); } if (args.length == 2 && (args[0].equalsIgnoreCase(DELETE) - || args[0].equalsIgnoreCase("give") - || args[0].equalsIgnoreCase(MODIFY))) { + || args[0].equalsIgnoreCase("give") + || args[0].equalsIgnoreCase(MODIFY))) { ItemManager im = Manager.getInstance(ItemManager.class); return im.getItemIDs(); } diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/LevelCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/LevelCommand.java index 3e86fd7..ac3496c 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/LevelCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/LevelCommand.java @@ -6,7 +6,7 @@ import java.util.Collection; import java.util.List; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/SpawnEntityCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/SpawnEntityCommand.java index 4c72b47..d4c2c01 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/SpawnEntityCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/SpawnEntityCommand.java @@ -9,7 +9,7 @@ import java.util.Collection; import java.util.List; import me.unurled.sacredrealms.sr.components.entity.EntityManager; import me.unurled.sacredrealms.sr.components.entity.SREntityType; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.World; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -28,11 +28,12 @@ public class SpawnEntityCommand implements BasicCommand { return; } SREntityType eType = em.getType(type); - if (type == null) { + if (eType == null) { sender.sendMessage("Invalid entity type."); return; } em.spawnEntity(eType, amount, x, y, z, world); + sender.sendMessage("Spawned " + amount + " " + eType.getName() + "s."); } private int getAmount(CommandSender sender, String @NotNull [] args) { @@ -50,7 +51,7 @@ public class SpawnEntityCommand implements BasicCommand { * Executes the command with the given {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command ignoring repeated spaces + * @param args the arguments of the command ignoring repeated spaces */ @Override public void execute(@NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { @@ -81,7 +82,7 @@ public class SpawnEntityCommand implements BasicCommand { } } if (args[3].equals("~")) { - y = p.getLocation().getX(); + y = p.getLocation().getY(); } else { try { y = Double.parseDouble(args[3]); @@ -91,7 +92,7 @@ public class SpawnEntityCommand implements BasicCommand { } } if (args[4].equals("~")) { - z = p.getLocation().getX(); + z = p.getLocation().getZ(); } else { try { z = Double.parseDouble(args[4]); @@ -138,12 +139,12 @@ public class SpawnEntityCommand implements BasicCommand { * Suggests possible completions for the given command {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command including repeated spaces + * @param args the arguments of the command including repeated spaces * @return a collection of suggestions */ @Override - public @NotNull Collection suggest(@NotNull CommandSourceStack commandSourceStack, - @NotNull String[] args) { + public @NotNull Collection suggest( + @NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { CommandSender sender = commandSourceStack.getSender(); if (sender.hasPermission("sr.spawn-entity")) { if (args.length == 1) { diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/TreasureCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/TreasureCommand.java index bfb8673..aef590a 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/admin/TreasureCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/admin/TreasureCommand.java @@ -11,7 +11,7 @@ import java.util.Set; import me.unurled.sacredrealms.sr.components.treasure.Treasure; import me.unurled.sacredrealms.sr.components.treasure.TreasureManager; import me.unurled.sacredrealms.sr.gui.treasure.TreasureGUI; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import net.kyori.adventure.text.Component; import org.bukkit.Material; import org.bukkit.block.Block; @@ -54,11 +54,10 @@ public class TreasureCommand implements BasicCommand { for (Treasure treasure : instance.getTreasures()) { String s = String.format( - "%sx: %s, y: %s, z: %s", + "x: %s, y: %s, z: %s", treasure.getLocation().getBlockX(), treasure.getLocation().getBlockY(), treasure.getLocation().getBlockZ(), - treasure.getLocation().toString(), treasure.getLocation().getBlockX(), treasure.getLocation().getBlockY(), treasure.getLocation().getBlockZ()); @@ -85,10 +84,12 @@ public class TreasureCommand implements BasicCommand { Window window = Window.single() .setViewer(p) - .setTitle("Attributes") + .setTitle("Modifiy Treasure Chest") .setGui(TreasureGUI.createGui(treasure)) .build(); window.open(); + } else { + p.sendMessage(textComp("Target block is not a chest.")); } } @@ -112,7 +113,10 @@ public class TreasureCommand implements BasicCommand { } else { treasure = new Treasure(instance.getNextId(), block.getLocation()); } + block.setBlockData(Material.AIR.createBlockData()); instance.addTreasure(treasure); + } else { + p.sendMessage(textComp("Target block is not a chest.")); } } @@ -120,10 +124,11 @@ public class TreasureCommand implements BasicCommand { * Executes the command with the given {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command ignoring repeated spaces + * @param args the arguments of the command ignoring repeated spaces */ @Override - public void execute(@NotNull CommandSourceStack commandSourceStack, @NotNull String @NotNull [] args) { + public void execute( + @NotNull CommandSourceStack commandSourceStack, @NotNull String @NotNull [] args) { CommandSender sender = commandSourceStack.getSender(); if (args.length == 0) { sender.sendMessage("Usage: /treasure "); @@ -143,12 +148,12 @@ public class TreasureCommand implements BasicCommand { * Suggests possible completions for the given command {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command including repeated spaces + * @param args the arguments of the command including repeated spaces * @return a collection of suggestions */ @Override - public @NotNull Collection suggest(@NotNull CommandSourceStack commandSourceStack, - @NotNull String[] args) { + public @NotNull Collection suggest( + @NotNull CommandSourceStack commandSourceStack, @NotNull String @NotNull [] args) { if (args.length == 0) { return List.of(CREATE, MODIFY, LIST, DELETE); } diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/player/DifficultyCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/player/DifficultyCommand.java index 50b236f..31f0ec8 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/player/DifficultyCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/player/DifficultyCommand.java @@ -14,7 +14,7 @@ import me.unurled.sacredrealms.sr.components.difficulty.Difficulty; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; import me.unurled.sacredrealms.sr.gui.difficulty.DifficultyGUI; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; @@ -164,7 +164,7 @@ public class DifficultyCommand implements BasicCommand { * Executes the command with the given {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command ignoring repeated spaces + * @param args the arguments of the command ignoring repeated spaces */ @Override public void execute(@NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { @@ -180,12 +180,12 @@ public class DifficultyCommand implements BasicCommand { * Suggests possible completions for the given command {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command including repeated spaces + * @param args the arguments of the command including repeated spaces * @return a collection of suggestions */ @Override - public @NotNull Collection suggest(@NotNull CommandSourceStack commandSourceStack, - @NotNull String[] args) { + public @NotNull Collection suggest( + @NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { CommandSender sender = commandSourceStack.getSender(); if (args.length == 0 && sender.hasPermission(DIFFICULTY_MANAGE)) { return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList(); diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/player/ResetAdventureCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/player/ResetAdventureCommand.java index 6af0e51..2d69158 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/player/ResetAdventureCommand.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/player/ResetAdventureCommand.java @@ -12,7 +12,7 @@ import java.util.Objects; import me.unurled.sacredrealms.sr.SR; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.data.DataManager; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; @@ -39,7 +39,7 @@ public class ResetAdventureCommand implements BasicCommand { } return; } - OfflinePlayer player = SR.getInstance().getServer().getOfflinePlayer(args[0]); + OfflinePlayer player = SR.getPlugin().getServer().getOfflinePlayer(args[0]); boolean status = resetAdventure(player); if (status) { @@ -62,12 +62,11 @@ public class ResetAdventureCommand implements BasicCommand { return false; } - /** * Executes the command with the given {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command ignoring repeated spaces + * @param args the arguments of the command ignoring repeated spaces */ @Override public void execute(@NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { @@ -107,12 +106,12 @@ public class ResetAdventureCommand implements BasicCommand { * Suggests possible completions for the given command {@link CommandSourceStack} and arguments. * * @param commandSourceStack the commandSourceStack of the command - * @param args the arguments of the command including repeated spaces + * @param args the arguments of the command including repeated spaces * @return a collection of suggestions */ @Override - public @NotNull Collection suggest(@NotNull CommandSourceStack commandSourceStack, - @NotNull String[] args) { + public @NotNull Collection suggest( + @NotNull CommandSourceStack commandSourceStack, @NotNull String[] args) { CommandSender sender = commandSourceStack.getSender(); if (sender.hasPermission("sr.resetadventure")) { if (args.length == 1) { diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/block/Block.java b/src/main/java/me/unurled/sacredrealms/sr/components/block/Block.java new file mode 100644 index 0000000..f7eb165 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/block/Block.java @@ -0,0 +1,64 @@ +package me.unurled.sacredrealms.sr.components.block; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class Block { + + private final Material BROKEN_MATERIAL = Material.AIR; + private final Material HITBOX_MATERIAL = Material.BARRIER; + + private Location location; + + private ItemDisplay entity; + + private BlockItem item; + + public Block(@NotNull Location location, @NotNull BlockItem blockItem) { + this.placeBlock(location, blockItem); + } + + private void placeBlock(@NotNull Location location, BlockItem blockItem) { + World world = location.getWorld(); + + world.setBlockData(location, HITBOX_MATERIAL.createBlockData()); + Location entityLocation = getLocationFromBlock(location); + + world.spawn( + entityLocation, + ItemDisplay.class, + entity -> { + ItemStack item = blockItem.getItem(1); + entity.setItemStack(item); + entity.setPersistent(true); + entity.setInvulnerable(true); + + this.entity = entity; + this.location = location; + this.item = blockItem; + }); + } + + public void breakBlock() { + this.entity.remove(); + World world = this.location.getWorld(); + world.setBlockData(this.location, BROKEN_MATERIAL.createBlockData()); + } + + public BlockItem getBlockItem() { + return this.item; + } + + private @NotNull Location getLocationFromBlock(@NotNull Location location) { + Location entityLocation = location.clone(); + entityLocation.setX(location.getX() + 0.5); + entityLocation.setY(location.getY() + 0.5); + entityLocation.setZ(location.getZ() + 0.5); + + return entityLocation; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/block/BlockItem.java b/src/main/java/me/unurled/sacredrealms/sr/components/block/BlockItem.java new file mode 100644 index 0000000..e61443d --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/block/BlockItem.java @@ -0,0 +1,72 @@ +package me.unurled.sacredrealms.sr.components.block; + +import static me.unurled.sacredrealms.sr.utils.Component.textComp; +import static me.unurled.sacredrealms.sr.utils.Logger.error; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import me.unurled.sacredrealms.sr.constants.Keys; +import net.kyori.adventure.text.Component; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; + +public class BlockItem { + + private static final Material BLOCK_MATERIAL = Material.STONE; + + private final Component displayName; + private final List lore; + private final int customModelData; + + public BlockItem(@NotNull ItemStack itemStack) { + this.displayName = itemStack.getItemMeta().displayName(); + this.lore = itemStack.getItemMeta().lore(); + this.customModelData = itemStack.getItemMeta().getCustomModelData(); + } + + public BlockItem(@NotNull Map configObject) { + String rawDisplayName = (String) configObject.get(Keys.BLOCK_DISPLAYNAME); + List rawLore = new ArrayList<>(); + if (configObject.get(Keys.BLOCK_LORE) instanceof List) { + try { + rawLore = (List) configObject.get(Keys.BLOCK_LORE); + } catch (ClassCastException e) { + error("Failed to cast lore to List " + e.getMessage()); + } + } + + this.displayName = textComp(rawDisplayName); + this.customModelData = (int) configObject.get(Keys.BLOCK_CUSTOMMODELDATA); + this.lore = new ArrayList<>(); + + for (String loreItem : rawLore) { + this.lore.add(textComp(loreItem)); + } + } + + public BlockItem(Component displayName, List lore, int customModelData) { + this.displayName = displayName; + this.lore = lore; + this.customModelData = customModelData; + } + + public Component getDisplayName() { + return this.displayName; + } + + public ItemStack getItem(int amount) { + ItemStack item = new ItemStack(BLOCK_MATERIAL, amount); + ItemMeta itemMeta = item.getItemMeta(); + + itemMeta.displayName(this.displayName); + itemMeta.lore(this.lore); + itemMeta.setCustomModelData(this.customModelData); + + item.setItemMeta(itemMeta); + + return item; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/block/BlockManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/block/BlockManager.java new file mode 100644 index 0000000..d45cea4 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/block/BlockManager.java @@ -0,0 +1,84 @@ +package me.unurled.sacredrealms.sr.components.block; + +import static me.unurled.sacredrealms.sr.utils.Items.locationToString; + +import java.util.HashMap; +import java.util.Map; +import me.unurled.sacredrealms.sr.SR; +import me.unurled.srcore.api.Manager; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Sound; +import org.bukkit.World; +import org.bukkit.entity.ItemDisplay; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class BlockManager extends Manager { + + private final Map blocks = new HashMap<>(); + + public void addBlock(@NotNull Location location, @NotNull BlockItem blockItem) { + Block block = new Block(location, blockItem); + blocks.put(locationToString(location), block); + } + + public void removeBlock(@NotNull Location location) { + String blockID = locationToString(location); + + if (blocks.containsKey(blockID)) { + Block block = blocks.get(blockID); + block.breakBlock(); + blocks.remove(blockID); + } + } + + public void breakBlock(Location loc, Player player) { + String blockID = locationToString(loc); + if (blocks.containsKey(blockID)) { + Block block = blocks.get(blockID); + block.breakBlock(); + + player.playSound(loc, Sound.BLOCK_STONE_BREAK, 1, 1); + + if (player.getGameMode() == GameMode.SURVIVAL) { + World world = loc.getWorld(); + world.dropItemNaturally(loc, block.getBlockItem().getItem(1)); + } + + blocks.remove(blockID); + } + } + + private @NotNull Location getLocationFromEntity(@NotNull Location location) { + Location entityLocation = location.clone(); + entityLocation.setX(location.getX() - 0.5); + entityLocation.setY(location.getY() - 0.5); + entityLocation.setZ(location.getZ() - 0.5); + + return entityLocation; + } + + @Override + public void load() { + Bukkit.getScheduler() + .runTask( + SR.getPlugin(), + () -> { + for (World world : SR.getPlugin().getServer().getWorlds()) { + if (world == null) { + continue; + } + for (ItemDisplay entity : world.getEntitiesByClass(ItemDisplay.class)) { + Location location = getLocationFromEntity(entity.getLocation()); + ItemStack itemStack = entity.getItemStack(); + BlockItem blockItem = new BlockItem(itemStack); + entity.remove(); + this.addBlock(location, blockItem); + } + } + }); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildDeserializer.java b/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildDeserializer.java index 4249c1b..190da47 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildDeserializer.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildDeserializer.java @@ -2,15 +2,17 @@ package me.unurled.sacredrealms.sr.components.clientbuild; import static me.unurled.sacredrealms.sr.utils.Logger.error; +import com.google.gson.Gson; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonParseException; import java.lang.reflect.Type; +import me.unurled.sacredrealms.sr.utils.gson.LocationSerializer; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.NotNull; public class ClientBuildDeserializer implements JsonDeserializer { @@ -31,28 +33,18 @@ public class ClientBuildDeserializer implements JsonDeserializer { * @throws JsonParseException if json is not in the expected format of {@code typeofT} */ @Override - public ClientBuild deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + public ClientBuild deserialize( + @NotNull JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { String[] data = json.getAsString().split(";"); ClientBuild build = new ClientBuild(data[0]); + LocationSerializer locDes = new LocationSerializer(); for (String block : data[1].split(",")) { String[] spl = block.replace("{", "").replace("}", "").split("\\|"); - World world = Bukkit.getWorld(spl[0]); - if (world == null) { - error("World " + spl[0] + " does not exist"); - return build; - } - Location loc; + Location loc = locDes.deserialize(new Gson().toJsonTree(spl[0]), Location.class, context); BlockData bData; try { - loc = - new Location( - world, - Integer.parseInt(spl[1]), - Integer.parseInt(spl[2]), - Integer.parseInt(spl[3])); - - bData = Bukkit.createBlockData(spl[4]); + bData = Bukkit.createBlockData(spl[1]); } catch (NumberFormatException e) { error("Invalid block data: " + block); continue; diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildManager.java index 2827361..5254dcb 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildManager.java @@ -15,7 +15,7 @@ import me.unurled.sacredrealms.sr.SR; import me.unurled.sacredrealms.sr.commands.admin.ClientBuildCommand; import me.unurled.sacredrealms.sr.data.DataHandler; import me.unurled.sacredrealms.sr.data.DataManager; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.block.data.BlockData; @@ -41,7 +41,7 @@ public class ClientBuildManager extends Manager { @Override public void load() { super.load(); - Bukkit.getPluginManager().registerEvents(new ClientBuildCommand(), SR.getInstance()); + Bukkit.getPluginManager().registerEvents(new ClientBuildCommand(), SR.getPlugin()); } public List getBuilds() { @@ -83,7 +83,7 @@ public class ClientBuildManager extends Manager { if (dh == null) { error("Failed to get DataHandler instance, can't load client builds."); error("Retrying in 10 seconds."); - Bukkit.getScheduler().runTaskLaterAsynchronously(SR.getInstance(), this::loadData, 200L); + Bukkit.getScheduler().runTaskLaterAsynchronously(SR.getPlugin(), this::loadData, 200L); return; } List keys = dh.getKeysAll("sr.clientbuild"); @@ -130,7 +130,7 @@ public class ClientBuildManager extends Manager { } Bukkit.getScheduler() .runTaskAsynchronously( - SR.getInstance(), + SR.getPlugin(), () -> { for (String name : names) { ClientBuild build = getBuild(name); diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildSerializer.java b/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildSerializer.java index 3c739dc..765d30b 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildSerializer.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/clientbuild/ClientBuildSerializer.java @@ -6,8 +6,10 @@ import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import java.lang.reflect.Type; import java.util.Map.Entry; +import me.unurled.sacredrealms.sr.utils.gson.LocationSerializer; import org.bukkit.Location; import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.NotNull; public class ClientBuildSerializer implements JsonSerializer { @@ -27,19 +29,16 @@ public class ClientBuildSerializer implements JsonSerializer { * @return a JsonElement corresponding to the specified object. */ @Override - public JsonElement serialize(ClientBuild src, Type typeOfSrc, JsonSerializationContext context) { + public JsonElement serialize( + @NotNull ClientBuild src, Type typeOfSrc, JsonSerializationContext context) { StringBuilder s = new StringBuilder(); s.append(src.getName()); s.append(";"); + LocationSerializer locSer = new LocationSerializer(); for (Entry entry : src.getBlocks().entrySet()) { s.append("{"); - s.append(entry.getKey().getWorld().getName()); - s.append("|"); - s.append(entry.getKey().getBlockX()); - s.append("|"); - s.append(entry.getKey().getBlockY()); - s.append("|"); - s.append(entry.getKey().getBlockZ()); + JsonElement j = locSer.serialize(entry.getKey(), Location.class, context); + s.append(j); s.append("|"); s.append(entry.getValue().getAsString()); s.append("},"); diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/combat/CombatManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/combat/CombatManager.java index cf07b9b..10ec8f0 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/combat/CombatManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/combat/CombatManager.java @@ -12,10 +12,12 @@ import java.util.UUID; import me.unurled.sacredrealms.sr.components.attributes.Attribute; import me.unurled.sacredrealms.sr.components.entity.EntityManager; import me.unurled.sacredrealms.sr.components.entity.SREntity; +import me.unurled.sacredrealms.sr.components.entity.SREntityType; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; -import me.unurled.sacredrealms.sr.managers.Manager; import me.unurled.sacredrealms.sr.utils.Items; +import me.unurled.srcore.api.Manager; +import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Mob; @@ -23,6 +25,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.entity.EntityDamageByBlockEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; import org.bukkit.inventory.EntityEquipment; import org.jetbrains.annotations.NotNull; @@ -33,7 +36,10 @@ public class CombatManager extends Manager { private final Map> historyMobDamage = new HashMap<>(); private static void playerVictim( - @NotNull EntityDamageByEntityEvent e, Player player, @NotNull Mob mob, PlayerManager pm) { + @NotNull EntityDamageByEntityEvent e, + Player player, + @NotNull Mob mob, + @NotNull PlayerManager pm) { // get equipment of damager EntityEquipment equipment = mob.getEquipment(); Double dStrength = Items.getTotalAttribute(equipment, Attribute.STRENGTH); @@ -92,7 +98,7 @@ public class CombatManager extends Manager { if (player1.getHealth() <= 0) { player.damage(player.getHealth()); } - updateActionBar(player, player1); + updateActionBar(player); } spawnIndicator(player, false, damage); @@ -100,6 +106,14 @@ public class CombatManager extends Manager { // TODO: check for status effects (apply status effects depending item of damager) } + @EventHandler + public void onDamage(@NotNull EntityDamageEvent e) { + if (e.getCause() != DamageCause.ENTITY_ATTACK || !(e.getEntity() instanceof Player)) { + e.setDamage(0.0); + e.setCancelled(true); + } + } + @EventHandler public void onDamage(@NotNull EntityDamageByBlockEvent e) { // TODO: Implement @@ -190,6 +204,16 @@ public class CombatManager extends Manager { } else if (luck > 0 && luck < 1000 && RANDOM.nextBoolean()) { // chance of critical hit d.playSound(d, Sound.ENTITY_PLAYER_ATTACK_CRIT, 1.0f, 1.0f); + for (int i = 0; i < 10; i++) { + d.spawnParticle( + Particle.CRIT, + entity.getLocation(), + 10, + 0.5 + (double) i / 10, + 0.5 + (double) i / 10, + 0.5 + (double) i / 10, + 0.1); + } // damage * 2 damage = damage * 2; } @@ -201,17 +225,31 @@ public class CombatManager extends Manager { EntityManager em = Manager.getInstance(EntityManager.class); SREntity ent = em.getEntity(entity.getUniqueId()); + if (ent != null) { + ent.setHealth(ent.getHealth() - damage); + } + if (entity.getHealth() <= 0) { die(entity, d, pm, ent); return; } + // change entity health in name + if (ent != null) { + SREntityType type = em.getType(ent.getTypeId()); + + if (type != null) { + ent.applyName(type, entity); + } + } + spawnIndicator(entity, false, damage); // TODO: check for status effects (apply status effects depending item of damager) } private void die( - LivingEntity entity, @NotNull Player d, @NotNull PlayerManager pm, SREntity ent) { + @NotNull LivingEntity entity, @NotNull Player d, @NotNull PlayerManager pm, SREntity ent) { + entity.setHealth(0.0); // entity is dead, give experience to player SRPlayer player1 = pm.getPlayer(d.getUniqueId()); if (player1 != null && ent != null) { diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Action.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Action.java new file mode 100644 index 0000000..f398c8e --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Action.java @@ -0,0 +1,26 @@ +package me.unurled.sacredrealms.sr.components.cutscene; + +import java.util.List; +import org.bukkit.Location; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Entity; + +public enum Action { + IDLE(), + MOVE(Location.class), + TELEPORT(Location.class), + SPAWN(Location.class), + DESPAWN(), + HIT(Entity.class), + SNEAK(), + JUMP(), + PLACE(Location.class, BlockData.class), + INTERACT(Location.class, org.bukkit.event.block.Action.class), + BREAK(Location.class); + + private final List types; + + Action(Class... types) { + this.types = List.of(types); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Cutscene.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Cutscene.java new file mode 100644 index 0000000..d0e109c --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Cutscene.java @@ -0,0 +1,239 @@ +package me.unurled.sacredrealms.sr.components.cutscene; + +import static me.unurled.sacredrealms.sr.utils.Component.textComp; +import static me.unurled.sacredrealms.sr.utils.CutsceneUtil.showBlackScreen; +import static me.unurled.sacredrealms.sr.utils.Logger.error; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import me.unurled.sacredrealms.sr.SR; +import me.unurled.srcore.api.Manager; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitTask; +import org.jetbrains.annotations.NotNull; + +/** + * Cutscene class + * + *

Represents a cutscene + * + *

{@code
+ * {
+ *   "id": "cutscene1",
+ *   "name": "Cutscene 1",
+ *   "markers": [1, 2, 3], // Marker IDs
+ *   "start": {
+ *      "world": "world",
+ *      "x": 0,
+ *      "y": 0,
+ *      "z": 0,
+ *      "yaw": 0,
+ *      "pitch": 0
+ *   },
+ *   "end": {
+ *      "world": "world",
+ *      "x": 0,
+ *      "y": 0,
+ *      "z": 0,
+ *      "yaw": 0,
+ *      "pitch": 0
+ *    }
+ *    "blackBorders": true,
+ *    "startFadeToBlack": true,
+ *    "endFadeToBlack": true
+ * }
+ * }
+ */ +public class Cutscene { + + private final String id; + private final String name; + private final List markers; + private final List markerIds; + private CutsceneManager cutsceneManager; + private Location start; + private Location end; + private Map tasks; + private boolean blackBorders; + private boolean startFadeToBlack; + private boolean endFadeToBlack; + + public Cutscene(String id, String name) { + this.id = id; + this.name = name; + this.markers = new ArrayList<>(); + this.markerIds = new ArrayList<>(); + this.tasks = new HashMap<>(); + cutsceneManager = Manager.getInstance(CutsceneManager.class); + if (cutsceneManager == null) { + error("CutsceneManager is null, could not create a Cutscene"); + } + } + + public Cutscene(@NotNull String id, String name, @NotNull ArrayList markers) { + this.id = id.toUpperCase(); + this.name = name; + this.markers = markers; + markerIds = new ArrayList<>(); + for (Marker marker : markers) { + markerIds.add(marker.getId()); + } + if (markers.isEmpty()) return; + if (markers.getFirst().getFrames().isEmpty()) return; + start = markers.getFirst().getFrames().getFirst().getLocation(); + end = markers.getLast().getFrames().getLast().getLocation(); + this.tasks = new HashMap<>(); + cutsceneManager = Manager.getInstance(CutsceneManager.class); + if (cutsceneManager == null) { + error("CutsceneManager is null, could not create a Cutscene"); + } + } + + public void setMarkers() { + for (Integer i : markerIds) { + markers.add(Marker.getMarker(i)); + } + } + + public void addMarker(Marker marker) { + this.markers.add(marker); + } + + public String getId() { + return this.id.toUpperCase(); + } + + public String getName() { + return this.name; + } + + public List getMarkers() { + return this.markers; + } + + public Location getStart() { + return this.start; + } + + public void setStart(Location start) { + this.start = start; + } + + public Location getEnd() { + return this.end; + } + + public void setEnd(Location end) { + this.end = end; + } + + public void addMarkerId(Integer id) { + this.markerIds.add(id); + } + + public long time() { + long time = 0; + for (Marker marker : this.markers) { + if (marker.getOverAllTime() != null) time += marker.getOverAllTime(); + } + return time; + } + + public void play(@NotNull Player p) { + if (cutsceneManager.isPlayingCutscene(p)) { + p.sendMessage(textComp("You are already playing a cutscene!")); + } + cutsceneManager.addCurrentPlayingCutscene(this, p); + + // hide toolbar and make it spectate entity + p.setGameMode(GameMode.SPECTATOR); + + // set black borders + if (blackBorders) { + p.getInventory().setHelmet(new ItemStack(Material.PUMPKIN)); + p.updateInventory(); + } + + if (startFadeToBlack) { + showBlackScreen(p, 2, 1, 1); + } + + BukkitTask task = + Bukkit.getScheduler() + .runTaskAsynchronously( + SR.getPlugin(), + () -> { + int finalI = 0; + for (Marker marker : markers) { + for (int i = 0; i < marker.getFrames().size(); i++) { + int finalI1 = i; + Bukkit.getScheduler() + .runTaskLater( + SR.getPlugin(), + () -> { + p.teleport(marker.getFrames().get(finalI1).getLocation()); + marker.getFrames().get(finalI1).play(); + }, + (marker.getTimeBetweenFrames() * finalI) / 50); + finalI++; + } + } + if (endFadeToBlack) { + Bukkit.getScheduler() + .runTask(SR.getPlugin(), () -> showBlackScreen(p, 2, 1, 1)); + } + }); + Bukkit.getScheduler() + .runTaskAsynchronously( + SR.getPlugin(), + () -> { + try { + Thread.sleep(time()); + } catch (InterruptedException e) { + error("Error while playing cutscene: " + e.getMessage()); + } + Bukkit.getScheduler().runTask(SR.getPlugin(), () -> stop(p)); + }); + tasks.put(p.getUniqueId(), task); + } + + public void stop(UUID player) { + Player p = Bukkit.getPlayer(player); + if (p == null) return; + stop(p); + } + + public void stop(@NotNull Player p) { + if (cutsceneManager.isPlayingCutscene(p)) { + if (cutsceneManager.getPlayingCutscene(p).equals(this.id)) { + cutsceneManager.removeCurrentPlayingCutscene(this, p); + tasks.get(p.getUniqueId()).cancel(); + p.sendMessage(Component.text("You stopped playing the cutscene!")); + } + } else { + p.sendMessage(Component.text("You are not playing a cutscene!")); + } + } + + public void calculateMarkers() { + if (markers.isEmpty()) return; + for (int i = 0; i < markers.size(); i++) { + if (i < markers.size() - 1) { + markers.get(i).calculateFrames(markers.get(i + 1)); + } else { + ArrayList frames = new ArrayList<>(); + frames.add(new Frame(markers.get(i).getStart())); + markers.get(i).setFrames(frames); + } + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/CutsceneManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/CutsceneManager.java new file mode 100644 index 0000000..7615a98 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/CutsceneManager.java @@ -0,0 +1,168 @@ +package me.unurled.sacredrealms.sr.components.cutscene; + +import static me.unurled.sacredrealms.sr.utils.Component.textComp; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import me.unurled.srcore.api.Manager; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CutsceneManager extends Manager { + // TODO: spectator to hide toolbar (make it ride an invisible entity to prevent movement) + // TODO: save modifications if any + // TODO: do you set player back to original position ? + // TODO: make a system to easily create cutscenesw + + private List cutscenes; + + // cutscene id, player uuid + private Map currentPlayingCutscenes; + + private Map playerOrigins; + + private List cutsceneIds; + + /** Load the manager */ + @Override + public void load() { + super.load(); + cutscenes = new ArrayList<>(); + currentPlayingCutscenes = new HashMap<>(); + playerOrigins = new HashMap<>(); + cutsceneIds = new ArrayList<>(); + } + + /** Unload the manager */ + @Override + public void unload() { + super.unload(); + cutscenes.clear(); + currentPlayingCutscenes.clear(); + playerOrigins.clear(); + cutsceneIds.clear(); + } + + public void createCutscene(String id) { + Cutscene cutscene = new Cutscene(id, id); + addCutscene(cutscene); + } + + public void removeCutscene(String id) { + Cutscene cutscene = getCutscene(id); + removeCutscene(cutscene); + } + + public void addCutscene(Cutscene cutscene) { + cutscenes.add(cutscene); + cutsceneIds.add(cutscene.getId()); + } + + public void removeCutscene(Cutscene cutscene) { + cutscenes.remove(cutscene); + cutsceneIds.remove(cutscene.getId().toUpperCase()); + } + + public void addCurrentPlayingCutscene(@NotNull Cutscene cutscene, @NotNull Player p) { + currentPlayingCutscenes.put(cutscene.getId(), p.getUniqueId()); + } + + public void removeCurrentPlayingCutscene(@NotNull Cutscene cutscene, @NotNull Player p) { + currentPlayingCutscenes.remove(cutscene.getId(), p.getUniqueId()); + } + + public List getCutscenesId() { + return cutsceneIds; + } + + public void stopCutscene(Player p) { + for (String string : currentPlayingCutscenes.keySet()) { + if (currentPlayingCutscenes.get(string).equals(p.getUniqueId())) { + currentPlayingCutscenes.remove(string, p.getUniqueId()); + Cutscene cutscene = getCutscene(string); + if (cutscene != null) { + cutscene.stop(p); + PlayerOrigin.restore(playerOrigins.get(p.getUniqueId()), p); + playerOrigins.remove(p.getUniqueId()); + } + return; + } + } + } + + public void stopCutscenesForAllPlayers() { + for (String string : currentPlayingCutscenes.keySet()) { + Cutscene cutscene = getCutscene(string); + if (cutscene != null) { + UUID uuid = currentPlayingCutscenes.get(string); + cutscene.stop(uuid); + currentPlayingCutscenes.remove(cutscene.getId(), uuid); + Player player = Bukkit.getPlayer(uuid); + if (player == null) return; + PlayerOrigin.restore(playerOrigins.get(uuid), player); + playerOrigins.remove(uuid); + } + } + } + + public void playCutscene(Cutscene cutscene, Player p) { + if (cutscene == null) { + p.sendMessage(textComp("There was an error with the cutscene!")); + return; + } + if (cutscenes.contains(cutscene) && !currentPlayingCutscenes.containsValue(p.getUniqueId())) { + currentPlayingCutscenes.put(cutscene.getId(), p.getUniqueId()); + setPlayerOrigins(p); + cutscene.play(p); + } + } + + @Nullable + public Cutscene getCutscene(Player p) { + for (Entry entry : currentPlayingCutscenes.entrySet()) { + if (entry.getValue().equals(p.getUniqueId())) return getCutscene(entry.getKey()); + } + return null; + } + + @Nullable + public Cutscene getCutscene(@NotNull String id) { + if (!cutsceneIds.contains(id.toUpperCase())) { + return null; + } + for (Cutscene cutscene : cutscenes) { + if (cutscene.getId().equalsIgnoreCase(id.toUpperCase())) return cutscene; + } + return null; + } + + public boolean isPlayingCutscene(@NotNull Player p) { + return currentPlayingCutscenes.containsValue(p.getUniqueId()); + } + + @Nullable + public String getPlayingCutscene(@NotNull Player p) { + for (Entry entry : currentPlayingCutscenes.entrySet()) { + if (entry.getValue().equals(p.getUniqueId())) return entry.getKey(); + } + return null; + } + + public void setPlayerOrigins(@NotNull Player p) { + playerOrigins.put( + p.getUniqueId(), + new PlayerOrigin( + p.getLocation(), + p.getAllowFlight(), + p.isFlying(), + p.getWalkSpeed(), + p.getFlySpeed(), + p.getGameMode())); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/EntityCinematicAction.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/EntityCinematicAction.java new file mode 100644 index 0000000..edd82b1 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/EntityCinematicAction.java @@ -0,0 +1,7 @@ +package me.unurled.sacredrealms.sr.components.cutscene; + +import org.bukkit.entity.Player; + +public class EntityCinematicAction { + private Player p; +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Frame.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Frame.java new file mode 100644 index 0000000..ff15b51 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Frame.java @@ -0,0 +1,113 @@ +package me.unurled.sacredrealms.sr.components.cutscene; + +import static me.unurled.sacredrealms.sr.utils.Logger.error; + +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import java.util.Map; +import me.unurled.sacredrealms.sr.utils.gson.LocationSerializer; +import org.bukkit.Location; +import org.bukkit.block.data.BlockData; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Frame class + * + *

example of serializable frame: + * + *

{@code
+ * {
+ *   "id": 1,
+ *   "entities": "entities serializable",
+ *   "blocks": "blocks serializable",
+ *   "action": "action", // if type is entity
+ *   "location": {
+ *      "world": "world",
+ *      "x": 0,
+ *      "y": 0,
+ *      "z": 0,
+ *      "yaw": 0,
+ *      "pitch": 0
+ *   }
+ * }
+ * }
+ */ +public class Frame { + private static int idCounter = 0; + private final Location location; + private final int id; + @Nullable private Map blocks; + + public Frame(Location location) { + this.location = location; + id = idCounter; + idCounter++; + blocks = null; + } + + public Frame(Location location, int ID) { + this.location = location; + this.id = ID; + blocks = null; + } + + @Contract("_ -> new") + public static @NotNull Frame deserialize(@NotNull JsonElement jsonElement) { + int id = jsonElement.getAsJsonObject().get("id").getAsInt(); + Location loc = null; + try { + LocationSerializer locationDeserializer = new LocationSerializer(); + loc = + locationDeserializer.deserialize( + jsonElement.getAsJsonObject().get("location"), Location.class, null); + } catch (Exception e) { + error("Error deserializing location in Frame with ID: " + id); + } + + Frame frame = new Frame(loc, id); + + try { + frame.setBlocks((Map) jsonElement.getAsJsonObject().get("blocks")); + } catch (Exception e) { + error("Error deserializing blocks in Frame with ID: " + id); + } + return frame; + } + + public Location getLocation() { + return location; + } + + /** Play the frame aka move stuff around :shrug: */ + public void play() {} + + public int getId() { + return id; + } + + public @Nullable Map getBlocks() { + return blocks; + } + + public void setBlocks(@Nullable Map blocks) { + this.blocks = blocks; + } + + public JsonElement serialize() { + String s = + "{" + + "\"id\":" + + id + + "," + + "," + + "\"blocks\":" + + blocks + + "," + + "\"location\":" + + location.serialize() + + "}"; + return new JsonPrimitive(s); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Marker.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Marker.java new file mode 100644 index 0000000..f36bb9a --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/Marker.java @@ -0,0 +1,146 @@ +package me.unurled.sacredrealms.sr.components.cutscene; + +import static me.unurled.sacredrealms.sr.utils.CutsceneUtil.interpolateFrames; + +import java.util.ArrayList; +import java.util.List; +import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * Marker class + * + *
{@code
+ * {
+ *   "id": 1,
+ *   "frames": [ 1, 2, 3 ], // Frame IDs
+ *   "timeBetweenFrames": 20,
+ *   "overAllTime": 60,
+ *   "start": {
+ *      "world": "world",
+ *      "x": 0,
+ *      "y": 0,
+ *      "z": 0,
+ *      "yaw": 0,
+ *      "pitch": 0
+ *   }
+ * }
+ * }
+ */ +public class Marker { + + private static int idCounter = 0; + private static List markers = new ArrayList<>(); + private final int id; + private List frames; + private Long timeBetweenFrames; + private Long overAllTime; + private Location start; + + public Marker(Integer id) { + frames = new ArrayList<>(); + timeBetweenFrames = 0L; + overAllTime = 0L; + if (id == null) { + this.id = idCounter; + idCounter++; + } else { + this.id = id; + } + } + + public Marker(@NotNull List frames, long timeBetweenFrames, Location start) { + this.frames = frames; + this.timeBetweenFrames = timeBetweenFrames; + this.overAllTime = timeBetweenFrames * frames.size(); + this.start = start; + id = idCounter; + idCounter++; + } + + public static List getMarkers() { + return markers; + } + + public static void setMarkers(List markers) { + Marker.markers = markers; + } + + public static @Nullable Marker getMarker(int ID) { + for (Marker marker : markers) { + if (marker.getId() == ID) { + return marker; + } + } + return null; + } + + public List getFrames() { + return frames; + } + + public void setFrames(List frames) { + this.frames = frames; + } + + public Long getTimeBetweenFrames() { + return timeBetweenFrames; + } + + public void setTimeBetweenFrames(Long timeBetweenFrames) { + this.timeBetweenFrames = timeBetweenFrames; + } + + public Long getOverAllTime() { + return overAllTime; + } + + public void setOverAllTime(Long overAllTime) { + this.overAllTime = overAllTime; + } + + public Location getStart() { + return start; + } + + public void setStart(Location start) { + this.start = start; + } + + public void calculateFrames(Marker endMarker) { + frames = interpolateFrames(this, endMarker, timeBetweenFrames); + } + + public int getId() { + return id; + } + + public void addFrame(Frame frame) { + frames.add(frame); + } + + public void removeFrame(Frame frame) { + frames.remove(frame); + } + + public void removeFrame(int index) { + frames.remove(index); + } + + @Override + public String toString() { + return "Marker{" + + "ID=" + + id + + ", frames=" + + frames + + ", timeBetweenFrames=" + + timeBetweenFrames + + ", overAllTime=" + + overAllTime + + ", start=" + + start + + '}'; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/PlayerOrigin.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/PlayerOrigin.java new file mode 100644 index 0000000..f61fcd5 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/PlayerOrigin.java @@ -0,0 +1,23 @@ +package me.unurled.sacredrealms.sr.components.cutscene; + +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public record PlayerOrigin( + Location location, + boolean allowFlight, + boolean flying, + float walkSpeed, + float flySpeed, + GameMode gameMode) { + public static void restore(@NotNull PlayerOrigin origin, @NotNull Player p) { + p.teleport(origin.location()); + p.setAllowFlight(origin.allowFlight()); + p.setFlying(origin.flying()); + p.setWalkSpeed(origin.walkSpeed()); + p.setFlySpeed(origin.flySpeed()); + p.setGameMode(origin.gameMode()); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/PlayerRecording.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/PlayerRecording.java new file mode 100644 index 0000000..553c999 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/PlayerRecording.java @@ -0,0 +1,82 @@ +package me.unurled.sacredrealms.sr.components.cutscene; + +import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose; +import io.papermc.paper.event.player.PlayerArmSwingEvent; +import me.unurled.sacredrealms.sr.components.cutscene.npc.ArmSwing; +import me.unurled.sacredrealms.sr.components.cutscene.npc.EntityFrame; +import org.bukkit.entity.Player; +import org.bukkit.entity.Pose; +import org.bukkit.event.EventHandler; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.PlayerInventory; +import org.jetbrains.annotations.NotNull; + +public class PlayerRecording { + private final Player player; + private final EntityFrame entityFrame; + private ArmSwing swing; + + public PlayerRecording(Player player, EntityFrame entityFrame) { + this.player = player; + this.entityFrame = entityFrame; + } + + private static EntityPose toEntityPose(Pose pose) { + return pose == Pose.SNEAKING ? EntityPose.SITTING : EntityPose.valueOf(pose.name()); + } + + private static Pose toBukkitPose(EntityPose pose) { + return pose == EntityPose.SITTING ? Pose.SNEAKING : Pose.valueOf(pose.name()); + } + + @EventHandler + public void onArmSwing(@NotNull PlayerArmSwingEvent e) { + if (e.getPlayer().getUniqueId() != player.getUniqueId()) { + return; + } + addSwing( + e.getHand() == EquipmentSlot.HAND + ? ArmSwing.RIGHT + : (e.getHand() == EquipmentSlot.OFF_HAND ? ArmSwing.LEFT : ArmSwing.BOTH)); + } + + private void addSwing(ArmSwing swing) { + if (this.swing != null && this.swing != swing) { + this.swing = ArmSwing.BOTH; + return; + } + + this.swing = swing; + } + + public EntityFrame captureFrame() { + PlayerInventory inv = player.getInventory(); + Pose pose = player.isInsideVehicle() ? Pose.SITTING : player.getPose(); + EntityFrame data = + new EntityFrame( + player.getLocation(), + toEntityPose(pose), + swing, + inv.getItemInMainHand().isEmpty() ? null : inv.getItemInMainHand(), + inv.getItemInOffHand().isEmpty() ? null : inv.getItemInOffHand(), + inv.getHelmet(), + inv.getChestplate(), + inv.getLeggings(), + inv.getBoots()); + swing = null; + return data; + } + + public void applyState(@NotNull EntityFrame value) { + player.teleport(value.location()); + player.setPose(toBukkitPose(value.pose())); + PlayerInventory inv = player.getInventory(); + inv.setItemInMainHand(value.mainHand()); + inv.setItemInOffHand(value.offHand()); + inv.setHelmet(value.helmet()); + inv.setChestplate(value.chestplate()); + inv.setLeggings(value.leggings()); + inv.setBoots(value.boots()); + player.updateInventory(); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/gson/ActionGson.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/gson/ActionGson.java new file mode 100644 index 0000000..1259c26 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/gson/ActionGson.java @@ -0,0 +1,63 @@ +package me.unurled.sacredrealms.sr.components.cutscene.gson; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import me.unurled.sacredrealms.sr.components.cutscene.Action; +import org.jetbrains.annotations.NotNull; + +public class ActionGson { + public class Deserializer implements JsonDeserializer { + + /** + * Gson invokes this call-back method during deserialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects for any + * non-trivial field of the returned object. However, you should never invoke it on the same + * type passing {@code json} since that will cause an infinite loop (Gson will call your + * call-back method again). + * + * @param json The Json data being deserialized + * @param typeOfT The type of the Object to deserialize to + * @param context + * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T} + * @throws JsonParseException if json is not in the expected format of {@code typeofT} + */ + @Override + public Action deserialize( + @NotNull JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return Action.valueOf(json.getAsString()); + } + } + + public class Serializer implements JsonSerializer { + + /** + * Gson invokes this call-back method during serialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any + * non-trivial field of the {@code src} object. However, you should never invoke it on the + * {@code src} object itself since that will cause an infinite loop (Gson will call your + * call-back method again). + * + * @param src the object that needs to be converted to Json. + * @param typeOfSrc the actual type (fully genericized version) of the source object. + * @param context + * @return a JsonElement corresponding to the specified object. + */ + @Override + public JsonElement serialize(Action src, Type typeOfSrc, JsonSerializationContext context) { + return new JsonPrimitive(src.name()); + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/gson/EntityActionGson.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/gson/EntityActionGson.java new file mode 100644 index 0000000..4311514 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/gson/EntityActionGson.java @@ -0,0 +1,64 @@ +package me.unurled.sacredrealms.sr.components.cutscene.gson; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import java.util.Map; +import me.unurled.sacredrealms.sr.components.cutscene.Action; +import org.bukkit.entity.Entity; + +public class EntityActionGson { + public class Deserializer implements JsonDeserializer> { + + /** + * Gson invokes this call-back method during deserialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects for any + * non-trivial field of the returned object. However, you should never invoke it on the same + * type passing {@code json} since that will cause an infinite loop (Gson will call your + * call-back method again). + * + * @param json The Json data being deserialized + * @param typeOfT The type of the Object to deserialize to + * @param context + * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T} + * @throws JsonParseException if json is not in the expected format of {@code typeofT} + */ + @Override + public Map deserialize( + JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + return null; + } + } + + public class Serializer implements JsonSerializer> { + + /** + * Gson invokes this call-back method during serialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any + * non-trivial field of the {@code src} object. However, you should never invoke it on the + * {@code src} object itself since that will cause an infinite loop (Gson will call your + * call-back method again). + * + * @param src the object that needs to be converted to Json. + * @param typeOfSrc the actual type (fully genericized version) of the source object. + * @param context + * @return a JsonElement corresponding to the specified object. + */ + @Override + public JsonElement serialize( + Map src, Type typeOfSrc, JsonSerializationContext context) { + return null; + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/npc/ArmSwing.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/npc/ArmSwing.java new file mode 100644 index 0000000..4e8c93e --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/npc/ArmSwing.java @@ -0,0 +1,37 @@ +package me.unurled.sacredrealms.sr.components.cutscene.npc; + +import static me.unurled.sacredrealms.sr.components.packet.PacketManager.sendPacketTo; + +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityAnimation.EntityAnimationType; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +public enum ArmSwing { + LEFT, + RIGHT, + BOTH; + + public static void swingArm(@NotNull ArmSwing arm, Player p, int entityId) { + // Swing the arm + if (arm.isLeft()) { + sendPacketTo( + new WrapperPlayServerEntityAnimation(entityId, EntityAnimationType.SWING_OFF_HAND), p); + } + if (arm.isRight()) { + sendPacketTo( + new WrapperPlayServerEntityAnimation(entityId, EntityAnimationType.SWING_MAIN_ARM), p); + } + } + + @Contract(pure = true) + public @NotNull Boolean isLeft() { + return this == LEFT || this == BOTH; + } + + @Contract(pure = true) + public @NotNull Boolean isRight() { + return this == RIGHT || this == BOTH; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/npc/EntityFrame.java b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/npc/EntityFrame.java new file mode 100644 index 0000000..12abaa0 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/cutscene/npc/EntityFrame.java @@ -0,0 +1,66 @@ +package me.unurled.sacredrealms.sr.components.cutscene.npc; + +import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; +import me.unurled.sacredrealms.sr.utils.Items; +import me.unurled.sacredrealms.sr.utils.gson.LocationSerializer; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public record EntityFrame( + Location location, + EntityPose pose, + ArmSwing swing, + ItemStack mainHand, + ItemStack offHand, + ItemStack helmet, + ItemStack chestplate, + ItemStack leggings, + ItemStack boots) { + + public static @NotNull EntityFrame deserialize(@NotNull JsonElement jsonElement) { + Location location = + new LocationSerializer() + .deserialize(jsonElement.getAsJsonObject().get("location"), Location.class, null); + EntityPose pose = EntityPose.valueOf(jsonElement.getAsJsonObject().get("pose").getAsString()); + ArmSwing swing = ArmSwing.valueOf(jsonElement.getAsJsonObject().get("swing").getAsString()); + ItemStack mainHand = Items.deserialize(jsonElement.getAsJsonObject().get("mainHand")); + ItemStack offHand = Items.deserialize(jsonElement.getAsJsonObject().get("offHand")); + ItemStack helmet = Items.deserialize(jsonElement.getAsJsonObject().get("helmet")); + ItemStack chestplate = Items.deserialize(jsonElement.getAsJsonObject().get("chestplate")); + ItemStack leggings = Items.deserialize(jsonElement.getAsJsonObject().get("leggings")); + ItemStack boots = Items.deserialize(jsonElement.getAsJsonObject().get("boots")); + return new EntityFrame( + location, pose, swing, mainHand, offHand, helmet, chestplate, leggings, boots); + } + + public @NotNull JsonElement serialize() { + String s = + """ + { + "location": %s, + "pose": %s, + "swing": %s, + "mainHand": %s, + "offHand": %s, + "helmet": %s, + "chestplate": %s, + "leggings": %s, + "boots": %s + } + """ + .formatted( + location.serialize(), + pose.name(), + swing.name(), + mainHand.serialize(), + offHand.serialize(), + helmet.serialize(), + chestplate.serialize(), + leggings.serialize(), + boots.serialize()); + return new JsonPrimitive(s); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/entity/EntityManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/entity/EntityManager.java index efc8c93..3640e22 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/entity/EntityManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/entity/EntityManager.java @@ -8,18 +8,23 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.UUID; +import me.unurled.sacredrealms.sr.components.attributes.Attribute; import me.unurled.sacredrealms.sr.components.item.ItemStackDeserializer; import me.unurled.sacredrealms.sr.components.item.ItemStackSerializer; import me.unurled.sacredrealms.sr.components.player.PotionEffectDeserializer; import me.unurled.sacredrealms.sr.data.DataHandler; import me.unurled.sacredrealms.sr.data.DataManager; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Mob; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +import org.bukkit.event.entity.EntityCombustEvent; import org.bukkit.event.entity.EntitySpawnEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; @@ -70,6 +75,25 @@ public class EntityManager extends Manager { }); } + /** Save the data */ + @Override + public void saveData() { + // for every entity if not player kill it + killAllEntities(); + } + + public void killAllEntities() { + entities.forEach( + (uuid, srEntity) -> { + Entity e = Bukkit.getEntity(uuid); + if (e instanceof LivingEntity liv && !(e instanceof Player)) { + liv.setHealth(0.0); + e.remove(); + } + }); + entities.clear(); + } + public void saveType(@NotNull SREntityType type) { // save type DataManager dm = Manager.getInstance(DataManager.class); @@ -100,8 +124,11 @@ public class EntityManager extends Manager { entities.put(entity.getUuid(), entity); } - public void addEntity(@NotNull Entity e) { - entities.put(e.getUniqueId(), new SREntity(e)); + public void addEntity(@NotNull SREntityType type, @NotNull Entity e) { + entities.put( + e.getUniqueId(), + new SREntity( + e, type.getAttributes().get(Attribute.HEALTH), type.getExperience(), type.getId())); } public boolean isSREntity(@NotNull UUID uuid) { @@ -131,7 +158,8 @@ public class EntityManager extends Manager { // spawn entity List srEntities = new ArrayList<>(); for (int i = 0; i < amount; i++) { - Entity e = world.spawnEntity(new Location(world, x, y, z), type.getType()); + Entity e = + world.spawnEntity(new Location(world, x, y, z), type.getType(), SpawnReason.CUSTOM); if (e instanceof Mob mob) { if (type.getHandItem() != null) { mob.getEquipment().setItemInMainHand(type.getHandItem()); @@ -143,7 +171,13 @@ public class EntityManager extends Manager { mob.getEquipment().setItemInOffHand(type.getSecondHandItem()); } } - SREntity srEntity = new SREntity(e); + SREntity srEntity = + new SREntity( + e, type.getAttributes().get(Attribute.HEALTH), type.getExperience(), type.getId()); + + if (type.getName() != null && !type.getName().isEmpty()) { + srEntity.applyName(type, e); + } addEntity(srEntity); srEntities.add(srEntity); } @@ -153,12 +187,19 @@ public class EntityManager extends Manager { @EventHandler public void onEntitySpawn(@NotNull EntitySpawnEvent e) { - if (e.getEntity() instanceof Player) return; - - if (isSREntity(e.getEntity().getUniqueId())) return; - - if (!(e.getEntity() instanceof Mob)) return; + if (e.getEntity().getEntitySpawnReason() != SpawnReason.CUSTOM + && e.getEntity().getEntitySpawnReason() != SpawnReason.COMMAND) { + e.setCancelled(true); + } + } + /** + * cancel entity burn + * + * @param e + */ + @EventHandler + public void onEntityBurn(@NotNull EntityCombustEvent e) { e.setCancelled(true); } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/entity/SREntity.java b/src/main/java/me/unurled/sacredrealms/sr/components/entity/SREntity.java index 19d086b..63ac464 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/entity/SREntity.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/entity/SREntity.java @@ -1,16 +1,23 @@ package me.unurled.sacredrealms.sr.components.entity; +import static me.unurled.sacredrealms.sr.utils.Component.textComp; + import java.util.UUID; +import me.unurled.sacredrealms.sr.components.attributes.Attribute; import org.bukkit.entity.Entity; import org.jetbrains.annotations.NotNull; public class SREntity { private final UUID uuid; - private double health = 100; - private Long experience = 0L; + private final String typeId; + private double health; + private Long experience; - public SREntity(@NotNull Entity e) { + public SREntity(@NotNull Entity e, double health, Long exp, String typeId) { uuid = e.getUniqueId(); + this.health = health; + this.experience = exp; + this.typeId = typeId; } public UUID getUuid() { @@ -32,4 +39,23 @@ public class SREntity { public void setHealth(double health) { this.health = health; } + + public String getTypeId() { + return typeId; + } + + public void applyName(@NotNull SREntityType type, @NotNull Entity e) { + e.customName( + textComp( + "Lv." + + type.getLevel() + + " " + + type.getName() + + "" + + health + + "/" + + type.getAttributes().get(Attribute.HEALTH) + + "❤")); + e.setCustomNameVisible(true); + } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/entity/SREntityType.java b/src/main/java/me/unurled/sacredrealms/sr/components/entity/SREntityType.java index ecf9110..1b2754f 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/entity/SREntityType.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/entity/SREntityType.java @@ -136,7 +136,7 @@ public class SREntityType { } public EntityType getType() { - return type = type == null ? EntityType.ZOMBIE : type; + return type == null ? EntityType.ZOMBIE : type; } public void setType(EntityType type) { @@ -144,7 +144,7 @@ public class SREntityType { } public ItemStack getItem() { - return item == null ? new ItemStack(Material.BARRIER) : item; + return item == null ? new ItemStack(Material.AIR) : item; } public void setItem(ItemStack item) { @@ -153,10 +153,10 @@ public class SREntityType { public List getArmor() { if (armor.isEmpty()) { - armor.add(new ItemStack(Material.BARRIER)); - armor.add(new ItemStack(Material.BARRIER)); - armor.add(new ItemStack(Material.BARRIER)); - armor.add(new ItemStack(Material.BARRIER)); + armor.add(new ItemStack(Material.AIR)); + armor.add(new ItemStack(Material.AIR)); + armor.add(new ItemStack(Material.AIR)); + armor.add(new ItemStack(Material.AIR)); } return armor; } @@ -167,7 +167,7 @@ public class SREntityType { } public ItemStack getHandItem() { - return handItem == null ? new ItemStack(Material.BARRIER) : handItem; + return handItem == null ? new ItemStack(Material.AIR) : handItem; } @SuppressWarnings("unused") @@ -176,7 +176,7 @@ public class SREntityType { } public ItemStack getSecondHandItem() { - return secondHandItem == null ? new ItemStack(Material.BARRIER) : secondHandItem; + return secondHandItem == null ? new ItemStack(Material.AIR) : secondHandItem; } @SuppressWarnings("unused") diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/item/Item.java b/src/main/java/me/unurled/sacredrealms/sr/components/item/Item.java index 669c448..9662781 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/item/Item.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/item/Item.java @@ -150,7 +150,7 @@ public class Item { } public Double getAttribute(Attribute attribute) { - return attributes.get(attribute); + return attributes.getOrDefault(attribute, 0.0); } public void addAttribute(Attribute attribute, double value) { @@ -244,7 +244,7 @@ public class Item { + description + "\", \"customModelData\": " + customModelData - + "\", \"rarity\": \"" + + ", \"rarity\": \"" + rarity + "\", \"type\": \"" + type diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemManager.java index 2572df5..b4319f0 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/item/ItemManager.java @@ -1,13 +1,14 @@ package me.unurled.sacredrealms.sr.components.item; +import static me.unurled.sacredrealms.sr.utils.Logger.error; + import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.util.HashMap; import java.util.List; -import me.unurled.sacredrealms.sr.SR; import me.unurled.sacredrealms.sr.data.DataHandler; import me.unurled.sacredrealms.sr.data.DataManager; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.NamespacedKey; import org.bukkit.inventory.ItemStack; import org.bukkit.persistence.PersistentDataType; @@ -42,7 +43,7 @@ public class ItemManager extends Manager { DataHandler dh = dm.getDataHandler(); if (dh == null) { - SR.getInstance().getLogger().severe("DataHandler is null in ItemManager.loadData()!"); + error("DataHandler is null in ItemManager.loadData()!"); return; } @@ -69,10 +70,12 @@ public class ItemManager extends Manager { items.put(item.getId(), item); } + @Nullable public Item getItem(String id) { return items.get(id); } + @Nullable public Item getItem(ItemStack item) { if (item == null) return null; if (item.getItemMeta() == null) return null; diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/npc/NPCEntity.java b/src/main/java/me/unurled/sacredrealms/sr/components/npc/NPCEntity.java new file mode 100644 index 0000000..7c4c92b --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/npc/NPCEntity.java @@ -0,0 +1,10 @@ +package me.unurled.sacredrealms.sr.components.npc; + +public class NPCEntity { + + private final int id; + + public NPCEntity(int id) { + this.id = id; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/npc/NPCManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/npc/NPCManager.java new file mode 100644 index 0000000..e0a1af4 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/npc/NPCManager.java @@ -0,0 +1,64 @@ +package me.unurled.sacredrealms.sr.components.npc; + + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.UUID; +import me.unurled.sacredrealms.sr.SR; +import me.unurled.sacredrealms.sr.utils.ChunkWrapper; +import me.unurled.srcore.api.Manager; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.world.ChunkLoadEvent; +import org.jetbrains.annotations.NotNull; + +public class NPCManager extends Manager { + + private final Map> npcPerChunk = new HashMap<>(); + public Map npcs = new HashMap<>(); + + public void register(@NotNull NPCEntity entity) { + // TODO: Register the entity + /*ChunkWrapper cw = new ChunkWrapper(entity.getClonedLocation().getChunk()); + if (!npcPerChunk.containsKey(cw)) { + npcPerChunk.put(cw, new HashSet<>()); + } + npcPerChunk.get(cw).add(entity); + entity.spawn();*/ + } + + public void removeEntity(NPCEntity entity) { + /*try { + entity.despawn(); + } catch (Exception e) { + error("Failed to despawn entity: " + entity.getId()); + } + ChunkWrapper cw = new ChunkWrapper(entity.getClonedLocation().getChunk()); + if (npcPerChunk.containsKey(cw)) { + npcPerChunk.get(cw).remove(entity); + }*/ + } + + public void addEntity(NPCEntity entity) { +// npcs.put(entity.getEntity().getUniqueId(), entity); + } + + @EventHandler + public void onChunkLoad(@NotNull ChunkLoadEvent e) { + ChunkWrapper cw = new ChunkWrapper(e.getChunk()); + if (!npcPerChunk.containsKey(cw)) { + return; + } + Bukkit.getScheduler() + .scheduleSyncDelayedTask( + SR.getPlugin(), + () -> { + HashSet npc = npcPerChunk.get(cw); + for (NPCEntity entity : npc) { + register(entity); + } + }, + 20L * 2); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/pack/ResourcePackManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/pack/ResourcePackManager.java new file mode 100644 index 0000000..801a26a --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/pack/ResourcePackManager.java @@ -0,0 +1,158 @@ +package me.unurled.sacredrealms.sr.components.pack; + +import static me.unurled.sacredrealms.sr.utils.File.getFileChecksum; +import static me.unurled.sacredrealms.sr.utils.File.zipDirectory; +import static me.unurled.sacredrealms.sr.utils.File.zipFile; +import static me.unurled.sacredrealms.sr.utils.Logger.error; +import static me.unurled.sacredrealms.sr.utils.Logger.log; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; +import java.util.zip.ZipOutputStream; +import me.unurled.sacredrealms.sr.SR; +import me.unurled.sacredrealms.sr.components.pack.hosts.HostingProvider; +import me.unurled.sacredrealms.sr.components.pack.hosts.RustProvider; +import me.unurled.sacredrealms.sr.config.Settings; +import me.unurled.sacredrealms.sr.data.DataHandler; +import me.unurled.sacredrealms.sr.data.DataManager; +import me.unurled.srcore.api.Manager; +import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; + +public class ResourcePackManager extends Manager { + + private static final String RESOURCE_PACK_ZIP = "resource_pack.zip"; + private final HostingProvider hostingProvider; + + public ResourcePackManager() { + this.hostingProvider = + new RustProvider( + Settings.RESOURCE_PACK_URL.toString(), Settings.RESOURCE_PACK_API.toString()); + } + + @NotNull + private static File packFile(String fileName) { + try { + new File(SR.getPlugin().getDataFolder(), "pack").mkdirs(); + + String[] filesInclude = {"assets", "pack.mcmeta"}; + + FileOutputStream fos = + new FileOutputStream(new File(SR.getPlugin().getDataFolder(), fileName)); + ZipOutputStream zipOut = new ZipOutputStream(fos); + + zipOut.setLevel(9); + + for (String filePath : filesInclude) { + File fileToZip = new File(SR.getPlugin().getDataFolder() + "/pack", filePath); + if (fileToZip.isDirectory()) { + zipDirectory(fileToZip, fileToZip.getName(), zipOut); + } else { + zipFile(fileToZip, fileToZip.getName(), zipOut); + } + } + zipOut.close(); + fos.close(); + log("ZIP file created successfully!"); + } catch (IOException e) { + error("Failed to create resource pack"); + } + return new File(SR.getPlugin().getDataFolder(), fileName); + } + + /** Save the data */ + @Override + public void saveData() { + super.saveData(); + DataManager dm = Manager.getInstance(DataManager.class); + DataHandler dh = dm.getDataHandler(); + dh.set("pack.url", hostingProvider.getMinecraftPackURL()); + dh.set("pack.uuid", hostingProvider.getPackUUID().toString()); + dh.set("pack.sha1", hostingProvider.getPackUUID().toString()); + } + + /** Load the data */ + @Override + public void loadData() { + super.loadData(); + DataManager dm = Manager.getInstance(DataManager.class); + DataHandler dh = dm.getDataHandler(); + String packUUID = dh.get("pack.uuid"); + if (packUUID == null) { + return; + } + hostingProvider.setPackUUID(UUID.fromString(packUUID)); + hostingProvider.setPackURL(dh.get("pack.url")); + hostingProvider.setSHA1(dh.get("pack.sha1").getBytes()); + } + + /** + * upload a file to the hosting provider and return the url + * + * @param file the file to upload + */ + public void uploadAsync(final File file) { + Bukkit.getScheduler() + .runTaskAsynchronously( + SR.getPlugin(), + () -> { + // start the call + boolean success; + try { + success = hostingProvider.uploadPack(file); + } catch (Exception e) { + error("Failed to upload resource pack (exception) " + e.getMessage()); + return; + } + if (!success) { + error("Failed to upload resource pack."); + } + }); + } + + /** Load the manager */ + @Override + public void load() { + super.load(); + String zipPath = SR.getPlugin().getDataFolder() + "/" + RESOURCE_PACK_ZIP; + + Bukkit.getScheduler() + .runTaskAsynchronously( + SR.getPlugin(), + () -> { + if (new File(SR.getPlugin().getDataFolder(), "pack").exists() + && new File(zipPath).exists()) { + log("Resource pack exists"); + // create a new zip with files + + File tmpFile = packFile("tmp.zip"); + try { + String tmpSHA = getFileChecksum(tmpFile.getAbsolutePath()); + String rpSHA = getFileChecksum(zipPath); + + if (tmpSHA.equals(rpSHA)) { + log("Resource pack is up to date"); + return; + } else { + log("Resource pack is outdated"); + log(tmpSHA + " " + rpSHA); + } + } catch (IOException | NoSuchAlgorithmException e) { + error("Failed to check resource pack hash"); + } + } + // create the resource pack + File rp = packFile(RESOURCE_PACK_ZIP); + + // upload the resource pack + uploadAsync(rp); + }); + } + + public HostingProvider getHostingProvider() { + return hostingProvider; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/pack/animatedjava/AnimatedJavaManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/pack/animatedjava/AnimatedJavaManager.java new file mode 100644 index 0000000..46b444c --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/pack/animatedjava/AnimatedJavaManager.java @@ -0,0 +1,5 @@ +package me.unurled.sacredrealms.sr.components.pack.animatedjava; + +import me.unurled.srcore.api.Manager; + +public class AnimatedJavaManager extends Manager {} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/pack/background/Background.java b/src/main/java/me/unurled/sacredrealms/sr/components/pack/background/Background.java new file mode 100644 index 0000000..910a5ce --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/pack/background/Background.java @@ -0,0 +1,223 @@ +package me.unurled.sacredrealms.sr.components.pack.background; + +import me.unurled.sacredrealms.sr.utils.character.CharRepo; +import me.unurled.sacredrealms.sr.utils.character.ConfiguredChar; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; + +public class Background { + private ConfiguredChar left, + offset1, + offset2, + offset4, + offset8, + offset16, + offset32, + offset64, + offset128, + right; + private int leftMargin, rightMargin; + + private Background() {} + + public Background( + ConfiguredChar left, + ConfiguredChar offset1, + ConfiguredChar offset2, + ConfiguredChar offset4, + ConfiguredChar offset8, + ConfiguredChar offset16, + ConfiguredChar offset32, + ConfiguredChar offset64, + ConfiguredChar offset128, + ConfiguredChar right, + int leftMargin, + int rightMargin, + boolean removeShadow) { + this.left = left; + this.offset1 = offset1; + this.offset2 = offset2; + this.offset4 = offset4; + this.offset8 = offset8; + this.offset16 = offset16; + this.offset32 = offset32; + this.offset64 = offset64; + this.offset128 = offset128; + this.right = right; + this.leftMargin = leftMargin; + this.rightMargin = rightMargin; + } + + @Contract(value = " -> new", pure = true) + public static @NotNull Builder builder() { + return new Builder(); + } + + public ConfiguredChar getLeft() { + return left; + } + + public ConfiguredChar getOffset1() { + return offset1; + } + + public ConfiguredChar getOffset2() { + return offset2; + } + + public ConfiguredChar getOffset4() { + return offset4; + } + + public ConfiguredChar getOffset8() { + return offset8; + } + + public ConfiguredChar getOffset16() { + return offset16; + } + + public ConfiguredChar getOffset32() { + return offset32; + } + + public ConfiguredChar getOffset64() { + return offset64; + } + + public ConfiguredChar getOffset128() { + return offset128; + } + + public ConfiguredChar getRight() { + return right; + } + + public String getBackGroundImage(int n) { + String offset = CharRepo.getShortestNegChars(n + rightMargin + 2); + n = n + leftMargin + rightMargin + 2; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(left.getCharacter()); + while (n >= 128) { + stringBuilder.append(CharRepo.NEG1.getCh()); + stringBuilder.append(offset128.getCharacter()); + n -= 128; + } + if (n - 64 >= 0) { + stringBuilder.append(CharRepo.NEG1.getCh()); + stringBuilder.append(offset64.getCharacter()); + n -= 64; + } + if (n - 32 >= 0) { + stringBuilder.append(CharRepo.NEG1.getCh()); + stringBuilder.append(offset32.getCharacter()); + n -= 32; + } + if (n - 16 >= 0) { + stringBuilder.append(CharRepo.NEG1.getCh()); + stringBuilder.append(offset16.getCharacter()); + n -= 16; + } + if (n - 8 >= 0) { + stringBuilder.append(CharRepo.NEG1.getCh()); + stringBuilder.append(offset8.getCharacter()); + n -= 8; + } + if (n - 4 >= 0) { + stringBuilder.append(CharRepo.NEG1.getCh()); + stringBuilder.append(offset4.getCharacter()); + n -= 4; + } + if (n - 2 >= 0) { + stringBuilder.append(CharRepo.NEG1.getCh()); + stringBuilder.append(offset2.getCharacter()); + n -= 2; + } + if (n - 1 >= 0) { + stringBuilder.append(CharRepo.NEG1.getCh()); + stringBuilder.append(offset1.getCharacter()); + } + stringBuilder.append(CharRepo.NEG1.getCh()); + stringBuilder.append(right.getCharacter()); + stringBuilder.append(offset); + return stringBuilder.toString(); + } + + public static class Builder { + + private final Background background; + + public Builder() { + this.background = new Background(); + } + + @Contract(value = " -> new", pure = true) + public static @NotNull Builder of() { + return new Builder(); + } + + public Builder left(ConfiguredChar configuredChar) { + background.left = configuredChar; + return this; + } + + public Builder right(ConfiguredChar configuredChar) { + background.right = configuredChar; + return this; + } + + public Builder offset1(ConfiguredChar configuredChar) { + background.offset1 = configuredChar; + return this; + } + + public Builder offset2(ConfiguredChar configuredChar) { + background.offset2 = configuredChar; + return this; + } + + public Builder offset4(ConfiguredChar configuredChar) { + background.offset4 = configuredChar; + return this; + } + + public Builder offset8(ConfiguredChar configuredChar) { + background.offset8 = configuredChar; + return this; + } + + public Builder offset16(ConfiguredChar configuredChar) { + background.offset16 = configuredChar; + return this; + } + + public Builder offset32(ConfiguredChar configuredChar) { + background.offset32 = configuredChar; + return this; + } + + public Builder offset64(ConfiguredChar configuredChar) { + background.offset64 = configuredChar; + return this; + } + + public Builder offset128(ConfiguredChar configuredChar) { + background.offset128 = configuredChar; + return this; + } + + public Builder leftMargin(int margin) { + background.leftMargin = margin; + return this; + } + + public Builder rightMargin(int margin) { + background.rightMargin = margin; + return this; + } + + public Background build() { + return background; + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/pack/background/BackgroundManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/pack/background/BackgroundManager.java new file mode 100644 index 0000000..05fbcaf --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/pack/background/BackgroundManager.java @@ -0,0 +1,139 @@ +package me.unurled.sacredrealms.sr.components.pack.background; + +import static me.unurled.sacredrealms.sr.utils.Logger.warn; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import me.unurled.sacredrealms.sr.SR; +import me.unurled.sacredrealms.sr.utils.character.CharacterArranger; +import me.unurled.sacredrealms.sr.utils.character.ConfiguredChar; +import me.unurled.srcore.api.Manager; +import org.jetbrains.annotations.NotNull; + +public class BackgroundManager extends Manager { + + private final Map backgroundMap; + + public BackgroundManager() { + this.backgroundMap = new HashMap<>(); + } + + @Override + public void loadData() { + File bgFolder = new File(SR.getPlugin().getDataFolder(), "backgrounds"); + if (!bgFolder.exists() && bgFolder.mkdirs()) { + saveDefaultBackgrounds(); + } + + int height = 14; + int ascent = 8; + + String key = "b0"; + + Background background = + Background.builder() + .leftMargin(1) + .rightMargin(1) + .left( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b0") + .height(14) + .ascent(7) + .width(1) + .build()) + .offset1( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b1") + .height(height) + .ascent(ascent) + .width(1) + .build()) + .offset2( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b2") + .height(height) + .ascent(ascent) + .width(1) + .build()) + .offset4( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b4") + .height(height) + .ascent(ascent) + .width(1) + .build()) + .offset8( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b8") + .height(height) + .ascent(ascent) + .width(1) + .build()) + .offset16( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b16") + .height(height) + .ascent(ascent) + .width(1) + .build()) + .offset32( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b32") + .height(height) + .ascent(ascent) + .width(1) + .build()) + .offset64( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b64") + .height(height) + .ascent(ascent) + .width(1) + .build()) + .offset128( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b128") + .height(height) + .ascent(ascent) + .width(1) + .build()) + .right( + ConfiguredChar.builder() + .character(CharacterArranger.getAndIncrease()) + .png("b0") + .height(14) + .ascent(7) + .width(1) + .build()) + .build(); + + if (!registerBackGround(key, background)) { + warn("Found duplicated background: " + key); + } + } + + public boolean registerBackGround(@NotNull String key, @NotNull Background backGround) { + if (backgroundMap.containsKey(key)) { + return false; + } + backgroundMap.put(key, backGround); + return true; + } + + private void saveDefaultBackgrounds() { + String[] bgList = new String[] {"b0", "b1", "b2", "b4", "b8", "b16", "b32", "b64", "b128"}; + for (String bg : bgList) { + SR.getPlugin().saveResource("backgrounds" + File.separator + bg + ".png", false); + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/pack/hosts/HostingProvider.java b/src/main/java/me/unurled/sacredrealms/sr/components/pack/hosts/HostingProvider.java new file mode 100644 index 0000000..b8bddbe --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/pack/hosts/HostingProvider.java @@ -0,0 +1,29 @@ +package me.unurled.sacredrealms.sr.components.pack.hosts; + +import java.io.File; +import java.util.UUID; + +public interface HostingProvider { + + boolean uploadPack(File resourcePack) throws InterruptedException; + + String getPackURL(); + + void setPackURL(String packURL); + + String getMinecraftPackURL(); + + void setMinecraftPackURL(String minecraftPackURL); + + byte[] getSHA1(); + + void setSHA1(byte[] sha1); + + String getOriginalSHA1(); + + void setOriginalSHA1(String originalSHA1); + + UUID getPackUUID(); + + void setPackUUID(UUID packUUID); +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/pack/hosts/RustProvider.java b/src/main/java/me/unurled/sacredrealms/sr/components/pack/hosts/RustProvider.java new file mode 100644 index 0000000..dc77cd9 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/pack/hosts/RustProvider.java @@ -0,0 +1,151 @@ +package me.unurled.sacredrealms.sr.components.pack.hosts; + +import static me.unurled.sacredrealms.sr.utils.Logger.error; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.UUID; +import me.unurled.sacredrealms.sr.utils.HTTPRequestMultipartBody; +import org.jetbrains.annotations.NotNull; + +public class RustProvider implements HostingProvider { + + public static final String FAILED = "Failed to upload pack to Rust server: "; + public static final String HTTPS = "https://"; + private final String address; + private final String APIKEY; + private String packUrl; + private String minecraftPackURL; + private String sha1; + private UUID packUUID; + + public RustProvider(@NotNull String address, @NotNull String apiKey) { + String address1; + if (address.startsWith(HTTPS) || address.startsWith("http://")) { + address1 = address; + } else { + address1 = HTTPS + address; + } + address1 += address.endsWith("/") ? "" : "/"; + this.address = address1; + this.APIKEY = apiKey; + } + + @Override + public boolean uploadPack(File resourcePack) throws InterruptedException { + try (HttpClient httpClient = HttpClient.newHttpClient()) { + HTTPRequestMultipartBody multipartBody = + new HTTPRequestMultipartBody.Builder() + .addPart("file", resourcePack, null, resourcePack.getName()) + .build(); + + HttpRequest httpRequest = + HttpRequest.newBuilder() + .header( + "Content-Type", "multipart/form-data; boundary=" + multipartBody.getBoundary()) + .header("API_KEY", APIKEY) + .uri(new URI(this.address + "post")) + .POST(HttpRequest.BodyPublishers.ofByteArray(multipartBody.getBody())) + .build(); + + HttpResponse response = + httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() != 200) { + error(FAILED + response.body()); + return false; + } + + String stringRes = response.body(); + + if (stringRes.startsWith("[") && stringRes.endsWith("]")) { + stringRes = stringRes.substring(1, stringRes.length() - 1); + } + + String[] split = stringRes.split(","); + if (split.length != 2) { + error(FAILED + response.body()); + return false; + } + + String path = split[0].split(":")[1].replace("\"", "").trim(); + sha1 = split[1].split(":")[1].replace("\"", "").trim().replace("}", ""); + packUrl = this.address + "files/" + path; + + minecraftPackURL = packUrl.replace(HTTPS, "http://"); + + packUUID = UUID.nameUUIDFromBytes(sha1.getBytes()); + + } catch (IOException | URISyntaxException e) { + error(FAILED + e.getMessage()); + return false; + } catch (InterruptedException e) { + error(FAILED + e.getMessage()); + throw e; + } + return true; + } + + @Override + public String getPackURL() { + return packUrl; + } + + @Override + public void setPackURL(String packURL) { + this.packUrl = packURL; + } + + @Override + public String getMinecraftPackURL() { + return minecraftPackURL; + } + + @Override + public void setMinecraftPackURL(String minecraftPackURL) { + this.minecraftPackURL = minecraftPackURL; + } + + @Override + public byte[] getSHA1() { + int len = sha1.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = + (byte) + ((Character.digit(sha1.charAt(i), 16) << 4) + + Character.digit(sha1.charAt(i + 1), 16)); + } + return data; + } + + @Override + public void setSHA1(byte[] sha1) { + this.sha1 = new String(sha1); + } + + @Override + public String getOriginalSHA1() { + return sha1; + } + + @Override + public void setOriginalSHA1(String originalSHA1) { + this.sha1 = originalSHA1; + } + + @Override + public UUID getPackUUID() { + return packUUID; + } + + @Override + public void setPackUUID(UUID packUUID) { + this.packUUID = packUUID; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/packet/PacketManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/packet/PacketManager.java new file mode 100644 index 0000000..1f57cc9 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/packet/PacketManager.java @@ -0,0 +1,12 @@ +package me.unurled.sacredrealms.sr.components.packet; + +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.wrapper.PacketWrapper; +import me.unurled.srcore.api.Manager; +import org.bukkit.entity.Player; + +public class PacketManager extends Manager { + public static void sendPacketTo(T packet, Player player) { + PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/player/PlayerManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/player/PlayerManager.java index 69de02a..c1d3f26 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/player/PlayerManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/player/PlayerManager.java @@ -2,19 +2,24 @@ package me.unurled.sacredrealms.sr.components.player; import static me.unurled.sacredrealms.sr.utils.Component.textComp; import static me.unurled.sacredrealms.sr.utils.Logger.error; +import static me.unurled.sacredrealms.sr.utils.Logger.log; import static me.unurled.sacredrealms.sr.utils.SRPlayerUtils.updateActionBar; import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import java.net.URI; import java.util.HashMap; import java.util.UUID; import me.unurled.sacredrealms.sr.SR; +import me.unurled.sacredrealms.sr.components.pack.ResourcePackManager; import me.unurled.sacredrealms.sr.data.DataHandler; import me.unurled.sacredrealms.sr.data.DataManager; import me.unurled.sacredrealms.sr.events.player.PlayerLevelUpEvent; -import me.unurled.sacredrealms.sr.managers.Manager; import me.unurled.sacredrealms.sr.utils.Items; +import me.unurled.srcore.api.Manager; +import net.kyori.adventure.resource.ResourcePackInfo; +import net.kyori.adventure.resource.ResourcePackRequest; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -22,6 +27,7 @@ import org.bukkit.event.entity.FoodLevelChangeEvent; import org.bukkit.event.player.PlayerItemHeldEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerResourcePackStatusEvent.Status; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; @@ -134,10 +140,36 @@ public class PlayerManager extends Manager { BukkitTask task = Bukkit.getScheduler() .runTaskTimerAsynchronously( - SR.getInstance(), () -> updateActionBar(e.getPlayer()), 0, 20L); + SR.getPlugin(), () -> updateActionBar(e.getPlayer()), 0, 20L); actionBarTasks.put(e.getPlayer().getUniqueId(), task); e.getPlayer().updateInventory(); + + // player resource pack give + ResourcePackManager rpm = Manager.getInstance(ResourcePackManager.class); + if (rpm == null) { + error("ResourcePackManager is null, Can't give player resource pack."); + return; + } + try { + URI uri = new URI(rpm.getHostingProvider().getMinecraftPackURL()); + ResourcePackRequest request = + ResourcePackRequest.addingRequest( + ResourcePackInfo.resourcePackInfo( + rpm.getHostingProvider().getPackUUID(), + uri, + rpm.getHostingProvider().getOriginalSHA1())); + e.getPlayer().sendResourcePacks(request); + if (e.getPlayer().getResourcePackStatus() != null + && !e.getPlayer().getResourcePackStatus().equals(Status.ACCEPTED)) { + e.getPlayer().kick(textComp("You must accept the resource pack.")); + } + } catch (Exception ex) { + error("Failed to send resource pack to player. " + ex.getMessage()); + log(rpm.getHostingProvider().getOriginalSHA1()); + log(rpm.getHostingProvider().getPackUUID().toString()); + log(rpm.getHostingProvider().getMinecraftPackURL()); + } } @Nullable @@ -225,7 +257,7 @@ public class PlayerManager extends Manager { public void levelUp(SRPlayer p, int previousLevel) { PlayerLevelUpEvent event = new PlayerLevelUpEvent(p, previousLevel); - event.callEvent(); + Bukkit.getScheduler().runTaskAsynchronously(SR.getPlugin(), event::callEvent); Player player = Bukkit.getPlayer(p.getUuid()); if (player != null) { player.sendMessage(event.getMessage()); diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java b/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java index b9998e1..bc49cd5 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java @@ -14,7 +14,7 @@ import me.unurled.sacredrealms.sr.components.attributes.Attribute; import me.unurled.sacredrealms.sr.components.difficulty.Difficulty; import me.unurled.sacredrealms.sr.components.item.Item; import me.unurled.sacredrealms.sr.events.player.PlayerKillEvent; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; @@ -31,15 +31,15 @@ public class SRPlayer { private static final List levelRequirements = List.of(100L, 200L, 400L, 800L, 1600L, 3200L, 6400L, 12800L, 25600L, 51200L); - @Expose private UUID uuid; + @Expose private final UUID uuid; + @Expose private final Map attributes = new EnumMap<>(Attribute.class); + @Expose private final List treasuresOpened = new ArrayList<>(); @Expose private int level = 1; @Expose private Long experience = 0L; - private double health = 100; - @Expose private Map attributes = new EnumMap<>(Attribute.class); + private double health = -1; private Map> itemAttributes = new EnumMap<>(Attribute.class); @Expose private List potionEffects = new ArrayList<>(); @Expose private Inventory inventory; - @Expose private List treasuresOpened = new ArrayList<>(); @Expose private Difficulty difficulty; private boolean isClientBuilder = false; private boolean firstTime = true; @@ -238,6 +238,9 @@ public class SRPlayer { } public double getHealth() { + if (health == -1) { + health = getAttribute(Attribute.HEALTH); + } return health; } @@ -257,6 +260,7 @@ public class SRPlayer { public void setExperience(Long experience) { // checks if experience is above next level requirements + if (difficulty == null) difficulty = Difficulty.NORMAL; if (experience >= levelRequirements.get(level - 1) * difficulty.getXpMultiplier()) { PlayerManager pm = Manager.getInstance(PlayerManager.class); setLevel(level + 1); diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/player/listener/PlayerBlockEvent.java b/src/main/java/me/unurled/sacredrealms/sr/components/player/listener/PlayerBlockEvent.java index f54e18f..84f5c4d 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/player/listener/PlayerBlockEvent.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/player/listener/PlayerBlockEvent.java @@ -2,7 +2,7 @@ package me.unurled.sacredrealms.sr.components.player.listener; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.GameMode; import org.bukkit.Material; import org.bukkit.entity.Player; diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/ItemStackMapSerializer.java b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/ItemStackMapSerializer.java new file mode 100644 index 0000000..5a377a5 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/ItemStackMapSerializer.java @@ -0,0 +1,60 @@ +package me.unurled.sacredrealms.sr.components.treasure; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import me.unurled.sacredrealms.sr.components.item.ItemStackDeserializer; +import me.unurled.sacredrealms.sr.components.item.ItemStackSerializer; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class ItemStackMapSerializer + implements JsonSerializer>, + JsonDeserializer> { + + @Override + public Map deserialize( + @NotNull JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + Map map = new HashMap<>(); + JsonObject jsonObject = json.getAsJsonObject(); + + Gson gson = + new GsonBuilder() + .registerTypeAdapter(ItemStack.class, new ItemStackDeserializer()) + .create(); + + for (Map.Entry entry : jsonObject.entrySet()) { + Integer key = Integer.parseInt(entry.getKey()); + ItemStack itemStack = gson.fromJson(entry.getValue(), ItemStack.class); + map.put(key, itemStack); + } + + return map; + } + + @Override + public JsonElement serialize( + @NotNull Map src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject jsonObject = new JsonObject(); + + Gson gson = + new GsonBuilder().registerTypeAdapter(ItemStack.class, new ItemStackSerializer()).create(); + + for (Map.Entry entry : src.entrySet()) { + JsonElement itemStackJson = gson.toJsonTree(entry.getValue(), ItemStack.class); + jsonObject.add(entry.getKey().toString(), itemStackJson); + } + + return jsonObject; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureDeserializer.java b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureDeserializer.java index 2729a73..2758adb 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureDeserializer.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureDeserializer.java @@ -12,9 +12,8 @@ import io.leangen.geantyref.TypeToken; import java.lang.reflect.Type; import java.util.Map; import me.unurled.sacredrealms.sr.components.item.ItemStackDeserializer; -import org.bukkit.Bukkit; +import me.unurled.sacredrealms.sr.utils.gson.LocationSerializer; import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.inventory.ItemStack; public class TreasureDeserializer implements JsonDeserializer { @@ -42,34 +41,31 @@ public class TreasureDeserializer implements JsonDeserializer { new GsonBuilder() .registerTypeAdapter(ItemStack.class, new ItemStackDeserializer()) .create(); + try { - Integer id = gson.fromJson("id", Integer.class); - String loc = gson.fromJson("location", String.class); - String[] parts = loc.split(","); - if (parts.length != 6) { - return null; - } - World world = Bukkit.getServer().getWorld(parts[0]); - double x = Double.parseDouble(parts[1]); - double y = Double.parseDouble(parts[2]); - double z = Double.parseDouble(parts[3]); - float pitch = Float.parseFloat(parts[4]); - float yaw = Float.parseFloat(parts[5]); - Location location = new Location(world, x, y, z, pitch, yaw); - String permission = gson.fromJson("permission", String.class); - String items = gson.fromJson("items", String.class); + Integer id = json.getAsJsonObject().get("id").getAsInt(); + Gson locGson = + new GsonBuilder() + .registerTypeAdapter(Location.class, new LocationSerializer()) + .registerTypeAdapter( + new TypeToken>() {}.getType(), + new ItemStackMapSerializer()) + .create(); + Location location = locGson.fromJson(json.getAsJsonObject().get("location"), Location.class); + String permission = json.getAsJsonObject().get("permission").getAsString(); + String items = json.getAsJsonObject().get("items").getAsString(); Type hash = new TypeToken>() {}.getType(); Map item = gson.fromJson(items, hash); if (permission == null || item == null) { + error("Error deserializing treasure: " + id + " is missing required fields"); return null; } return new Treasure(id, location, item, permission); - } catch (Exception e) { error("Error deserializing treasure: " + e.getMessage()); + return null; } - return null; } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureGenerator.java b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureGenerator.java index b15edbc..c2ad429 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureGenerator.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureGenerator.java @@ -11,7 +11,7 @@ import me.unurled.sacredrealms.sr.components.item.Item; import me.unurled.sacredrealms.sr.components.item.ItemManager; import me.unurled.sacredrealms.sr.components.item.Rarity; import me.unurled.sacredrealms.sr.components.player.SRPlayer; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureManager.java index af87414..7da5ef9 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureManager.java @@ -1,6 +1,8 @@ package me.unurled.sacredrealms.sr.components.treasure; import static me.unurled.sacredrealms.sr.utils.Component.textComp; +import static me.unurled.sacredrealms.sr.utils.Logger.error; +import static me.unurled.sacredrealms.sr.utils.Logger.log; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -15,7 +17,7 @@ import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; import me.unurled.sacredrealms.sr.data.DataHandler; import me.unurled.sacredrealms.sr.data.DataManager; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; @@ -34,16 +36,20 @@ public class TreasureManager extends Manager { private int nextId = 0; + private void saveTreasure(@NotNull Treasure treasure) { + DataHandler dh = Manager.getInstance(DataManager.class).getDataHandler(); + Gson gson = + new GsonBuilder().registerTypeAdapter(Treasure.class, new TreasureSerializer()).create(); + dh.set("treasures." + treasure.getId(), gson.toJson(treasure)); + } + /** Save the data */ @Override public void saveData() { // save all treasures for (Treasure treasure : treasures.values()) { // serialize treasure - DataHandler dh = Manager.getInstance(DataManager.class).getDataHandler(); - Gson gson = - new GsonBuilder().registerTypeAdapter(Treasure.class, new TreasureSerializer()).create(); - dh.set("treasures." + treasure.getId(), gson.toJson(treasure)); + saveTreasure(treasure); } } @@ -54,9 +60,15 @@ public class TreasureManager extends Manager { DataHandler dh = Manager.getInstance(DataManager.class).getDataHandler(); Gson gson = new GsonBuilder().registerTypeAdapter(Treasure.class, new TreasureDeserializer()).create(); + + log("Loading " + dh.getKeysAll("treasures").size() + " treasures"); for (String key : dh.getKeysAll("treasures")) { - // deserialize treasure - addTreasure(gson.fromJson(dh.get("treasures." + key), Treasure.class)); + Treasure treasure = gson.fromJson(dh.get(key), Treasure.class); + if (treasure == null) { + error("Error loading treasure with id " + key); + continue; + } + addTreasure(treasure); } } @@ -118,6 +130,15 @@ public class TreasureManager extends Manager { treasures.remove(treasure.getId()); } + public void setBlocks(int id, @NotNull Map items) { + Treasure treasure = getTreasureById(id); + if (treasure == null) { + return; + } + treasure.setItems(items); + saveTreasure(treasure); + } + public int getNextId() { return nextId++; } @@ -159,8 +180,17 @@ public class TreasureManager extends Manager { if (sr == null) { return; } - treasure = TreasureGenerator.generateTreasure(treasure, sr); + if (sr.hasOpenedTreasure(treasure.getId())) { + p.sendMessage("You have already opened this chest"); + p.playSound(p.getLocation(), Sound.BLOCK_ANVIL_BREAK, 1f, 0.5f); + return; + } + if (treasure.getItems().isEmpty()) { // it generates items if not already set + treasure = TreasureGenerator.generateTreasure(treasure, sr); + } + log(treasure.getItems().toString()); p.openInventory(treasure.getInventory()); + p.playSound(p.getLocation(), Sound.BLOCK_CHEST_OPEN, 1f, 1f); SRPlayer srPlayer = Manager.getInstance(PlayerManager.class).getPlayer(p.getUniqueId()); if (srPlayer != null) { srPlayer.addTreasureOpened(treasure.getId()); diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureSerializer.java b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureSerializer.java index 173eaed..5404806 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureSerializer.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/treasure/TreasureSerializer.java @@ -7,9 +7,14 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; +import io.leangen.geantyref.TypeToken; import java.lang.reflect.Type; +import java.util.Map; import me.unurled.sacredrealms.sr.components.item.ItemStackSerializer; +import me.unurled.sacredrealms.sr.utils.gson.LocationSerializer; +import org.bukkit.Location; import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; public class TreasureSerializer implements JsonSerializer { @@ -29,14 +34,21 @@ public class TreasureSerializer implements JsonSerializer { * @return a JsonElement corresponding to the specified object. */ @Override - public JsonElement serialize(Treasure src, Type typeOfSrc, JsonSerializationContext context) { + public JsonElement serialize( + @NotNull Treasure src, Type typeOfSrc, JsonSerializationContext context) { JsonObject obj = new JsonObject(); obj.add("id", new JsonPrimitive(src.getId())); - obj.add("location", new JsonPrimitive(src.getLocation().toString())); + LocationSerializer locSer = new LocationSerializer(); + obj.add("location", locSer.serialize(src.getLocation(), Location.class, context)); obj.add("permission", new JsonPrimitive(src.getPermission())); Gson gson = - new GsonBuilder().registerTypeAdapter(ItemStack.class, new ItemStackSerializer()).create(); - obj.add("items", new JsonPrimitive(gson.toJson(src.getItems()))); + new GsonBuilder() + .registerTypeAdapter(ItemStack.class, new ItemStackSerializer()) + .registerTypeAdapter( + new TypeToken>() {}.getType(), new ItemStackMapSerializer()) + .create(); + Type hash = new TypeToken>() {}.getType(); + obj.add("items", gson.toJsonTree(src.getItems(), hash)); return obj; } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/config/ConfigManager.java b/src/main/java/me/unurled/sacredrealms/sr/config/ConfigManager.java new file mode 100644 index 0000000..e0b3da9 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/config/ConfigManager.java @@ -0,0 +1,40 @@ +package me.unurled.sacredrealms.sr.config; + +import java.io.File; +import me.unurled.sacredrealms.sr.SR; +import me.unurled.srcore.api.Manager; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +public class ConfigManager extends Manager { + + public static final String CONFIG_YML = "config.yml"; + private FileConfiguration config; + + /** Load the data */ + @Override + public void loadData() { + if (!SR.getPlugin().getDataFolder().exists()) { + SR.getPlugin().getDataFolder().mkdirs(); + } + File configFile = new File(SR.getPlugin().getDataFolder(), CONFIG_YML); + if (!configFile.exists()) { + SR.getPlugin().saveResource(CONFIG_YML, false); + config = YamlConfiguration.loadConfiguration(configFile); + } + config = YamlConfiguration.loadConfiguration(configFile); + } + + @Override + public void saveData() { + if (!new File(SR.getPlugin().getDataFolder() + CONFIG_YML).exists()) { + SR.getPlugin().saveDefaultConfig(); + } else { + SR.getPlugin().saveConfig(); + } + } + + public FileConfiguration getConfig() { + return config; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/config/Settings.java b/src/main/java/me/unurled/sacredrealms/sr/config/Settings.java new file mode 100644 index 0000000..1aaa130 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/config/Settings.java @@ -0,0 +1,95 @@ +package me.unurled.sacredrealms.sr.config; + +import static me.unurled.sacredrealms.sr.utils.Logger.error; + +import java.io.File; +import java.util.List; +import me.unurled.sacredrealms.sr.SR; +import me.unurled.srcore.api.Manager; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public enum Settings { + REDIS_HOST("redis.host"), + REDIS_PORT("redis.port"), + RESOURCE_PACK_URL("resource_pack"), + RESOURCE_PACK_API("resource_pack_api"); + + private final String path; + + Settings(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + + public @Nullable Object getValue() { + ConfigManager cm = Manager.getInstance(ConfigManager.class); + if (cm != null) { + return cm.getConfig().get(path); + } + return null; + } + + public void setValue(Object value) { + SR.getPlugin().getConfig().set(path, value); + try { + SR.getPlugin().getConfig().save(new File(SR.getPlugin().getDataFolder(), "config.yml")); + } catch (Exception e) { + error("Failed to save config.yml"); + } + } + + @Override + public String toString() { + try { + return (String) getValue(); + + } catch (NullPointerException e) { + return ""; + } + } + + @Nullable + public Integer toInt() { + try { + return (int) getValue(); + } catch (NullPointerException e) { + return null; + } + } + + public @NotNull Component toComponent() { + Object value = getValue(); + if (value != null) { + return MiniMessage.miniMessage().deserialize(value.toString()); + } else { + return Component.empty(); + } + } + + public Boolean toBool() { + return (Boolean) getValue(); + } + + public List toStringList() { + ConfigManager cm = Manager.getInstance(ConfigManager.class); + if (cm != null) { + return cm.getConfig().getStringList(path); + } + return List.of(); + } + + public @Nullable ConfigurationSection toConfigSection() { + ConfigManager cm = Manager.getInstance(ConfigManager.class); + if (cm != null) { + return cm.getConfig().getConfigurationSection(path); + } + return null; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/constants/Keys.java b/src/main/java/me/unurled/sacredrealms/sr/constants/Keys.java new file mode 100644 index 0000000..86a68b1 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/constants/Keys.java @@ -0,0 +1,9 @@ +package me.unurled.sacredrealms.sr.constants; + +public class Keys { + + public static final String BLOCK_KEY = "key"; + public static final String BLOCK_DISPLAYNAME = "displayName"; + public static final String BLOCK_LORE = "lore"; + public static final String BLOCK_CUSTOMMODELDATA = "customModelData"; +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/data/DataManager.java b/src/main/java/me/unurled/sacredrealms/sr/data/DataManager.java index 785c286..11a3d10 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/data/DataManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/data/DataManager.java @@ -1,18 +1,10 @@ package me.unurled.sacredrealms.sr.data; -import static me.unurled.sacredrealms.sr.utils.Logger.error; - -import java.io.File; -import java.io.IOException; -import me.unurled.sacredrealms.sr.SR; -import me.unurled.sacredrealms.sr.managers.Manager; -import org.bukkit.configuration.file.FileConfiguration; +import me.unurled.srcore.api.Manager; /** The data and config manager */ public class DataManager extends Manager { - private FileConfiguration config; - private DataHandler dh; /** Load the manager */ @@ -22,33 +14,6 @@ public class DataManager extends Manager { dh = new Redis(); } - /** Save the data */ - @Override - public void saveData() { - try { - if (config != null) config.save(new File(SR.getInstance().getDataFolder(), "config.yml")); - } catch (IOException e) { - error("Failed to save config.yml"); - } - } - - /** Load the data */ - @Override - public void loadData() { - SR.getInstance().saveConfig(); - SR.getInstance().reloadConfig(); - config = SR.getInstance().getConfig(); - } - - /** - * Get the config - * - * @return The config - */ - public FileConfiguration getConfig() { - return config; - } - /** * Get the data handler * diff --git a/src/main/java/me/unurled/sacredrealms/sr/data/Redis.java b/src/main/java/me/unurled/sacredrealms/sr/data/Redis.java index 4c253f9..15342f3 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/data/Redis.java +++ b/src/main/java/me/unurled/sacredrealms/sr/data/Redis.java @@ -4,7 +4,9 @@ import static me.unurled.sacredrealms.sr.utils.Logger.error; import java.util.List; import me.unurled.sacredrealms.sr.SR; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.sacredrealms.sr.config.ConfigManager; +import me.unurled.sacredrealms.sr.config.Settings; +import me.unurled.srcore.api.Manager; import org.jetbrains.annotations.NotNull; import redis.clients.jedis.JedisPooled; @@ -13,20 +15,25 @@ public class Redis implements DataHandler { JedisPooled client; public Redis() { - DataManager dm = Manager.getInstance(DataManager.class); - if (dm != null) { - String host = dm.getConfig().getString("redis.host", "127.0.0.1"); - int port = dm.getConfig().getInt("redis.port", 6379); + ConfigManager cm = Manager.getInstance(ConfigManager.class); + if (cm != null) { + String host = Settings.REDIS_HOST.toString(); + Integer port = Settings.REDIS_PORT.toInt(); + if (host == null || port == null) { + error("Failed to get Redis host or port from config, shutting down server."); + SR.getPlugin().getServer().shutdown(); + return; + } try { client = new JedisPooled(host, port); client.get("test"); } catch (Exception e) { error("Failed to connect to Redis, shutting down server."); - SR.getInstance().getServer().shutdown(); + SR.getPlugin().getServer().shutdown(); } } else { error("Failed to get DataManager instance. Can't connect to Redis, shutting down server."); - SR.getInstance().getServer().shutdown(); + SR.getPlugin().getServer().shutdown(); } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/BackItem.java b/src/main/java/me/unurled/sacredrealms/sr/gui/BackItem.java index 9354aa2..77b8bd3 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/gui/BackItem.java +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/BackItem.java @@ -16,7 +16,7 @@ public class BackItem extends PageItem { public ItemProvider getItemProvider(PagedGui gui) { ItemBuilder builder = new ItemBuilder(Material.RED_STAINED_GLASS_PANE); builder - .setDisplayName("$7Previous Page") + .setDisplayName("§cPrevious Page") .addLoreLines( gui.hasPreviousPage() ? "§7Go to page §e" + gui.getCurrentPage() + "§7/§e" + gui.getPageAmount() diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/attributes/AttributeItem.java b/src/main/java/me/unurled/sacredrealms/sr/gui/attributes/AttributeItem.java index d677451..98d5c3b 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/gui/attributes/AttributeItem.java +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/attributes/AttributeItem.java @@ -6,7 +6,7 @@ import static me.unurled.sacredrealms.sr.utils.SRPlayerUtils.syncSRToPlayer; import me.unurled.sacredrealms.sr.components.attributes.Attribute; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyItem.java b/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyItem.java index d7f2ae0..c7bdad1 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyItem.java +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyItem.java @@ -3,7 +3,7 @@ package me.unurled.sacredrealms.sr.gui.difficulty; import me.unurled.sacredrealms.sr.components.difficulty.Difficulty; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; @@ -18,7 +18,7 @@ import xyz.xenondevs.invui.window.Window; public class DifficultyItem extends AbstractItem { - private Difficulty difficulty; + private final Difficulty difficulty; public DifficultyItem(Difficulty difficulty) { this.difficulty = difficulty; @@ -90,6 +90,8 @@ public class DifficultyItem extends AbstractItem { SRPlayer sr = pm.getPlayer(player.getUniqueId()); if (sr != null) { sr.setDifficulty(difficulty); + player.closeInventory(); + player.sendMessage("Set difficulty to " + difficulty.name() + "!"); } } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/entitytype/EntityArmorItem.java b/src/main/java/me/unurled/sacredrealms/sr/gui/entitytype/EntityArmorItem.java index f287650..23f071d 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/gui/entitytype/EntityArmorItem.java +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/entitytype/EntityArmorItem.java @@ -1,6 +1,11 @@ package me.unurled.sacredrealms.sr.gui.entitytype; +import static me.unurled.sacredrealms.sr.utils.Items.name; + +import java.util.ArrayList; +import java.util.List; import me.unurled.sacredrealms.sr.components.entity.SREntityType; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.InventoryClickEvent; @@ -14,15 +19,41 @@ import xyz.xenondevs.invui.window.Window; public class EntityArmorItem extends AbstractItem { + private static final ItemStack MAINHAND = name(new ItemStack(Material.BARRIER), "Main Hand"); + private static final ItemStack SECONDHAND = name(new ItemStack(Material.BARRIER), "Off Hand"); + private static final List ARMOR = new ArrayList<>(); private final ItemStack stack; - public EntityArmorItem(SREntityType type, int armorSlot) { - if (armorSlot == 4) { - stack = type.getHandItem(); - } else if (armorSlot == 5) { - stack = type.getSecondHandItem(); - } else { - stack = type.getArmor().get(armorSlot); + public EntityArmorItem(@NotNull SREntityType type, int armorSlot) { + + ARMOR.add(name(new ItemStack(Material.BARRIER), "Helmet")); + ARMOR.add(name(new ItemStack(Material.BARRIER), "Chestplate")); + ARMOR.add(name(new ItemStack(Material.BARRIER), "Leggings")); + ARMOR.add(name(new ItemStack(Material.BARRIER), "Boots")); + + switch (armorSlot) { + case 4 -> { + if (type.getHandItem().getType().equals(Material.AIR)) { + stack = MAINHAND; + } else { + stack = type.getHandItem(); + } + } + case 5 -> { + if (type.getSecondHandItem().getType().equals(Material.AIR)) { + stack = SECONDHAND; + } else { + stack = type.getSecondHandItem(); + } + } + default -> { + if (type.getArmor().size() <= armorSlot + || type.getArmor().get(armorSlot).getType().equals(Material.AIR)) { + stack = ARMOR.get(armorSlot); + } else { + stack = type.getArmor().get(armorSlot); + } + } } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/entitytype/EntityTypeGUI.java b/src/main/java/me/unurled/sacredrealms/sr/gui/entitytype/EntityTypeGUI.java index e94d0a7..87ebd7f 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/gui/entitytype/EntityTypeGUI.java +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/entitytype/EntityTypeGUI.java @@ -6,7 +6,7 @@ import me.unurled.sacredrealms.sr.components.entity.EntityManager; import me.unurled.sacredrealms.sr.components.entity.SREntityType; import me.unurled.sacredrealms.sr.gui.BackItem; import me.unurled.sacredrealms.sr.gui.ForwardItem; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; import org.bukkit.Material; import org.jetbrains.annotations.NotNull; import xyz.xenondevs.invui.gui.Gui; @@ -48,13 +48,13 @@ public class EntityTypeGUI { return Gui.normal() .setStructure( "# # # # # # # # #", - "# T B O I S N L #", // Type, Behavior, lOot, Item display, Stats, Name, Level + "# T V O I S N L #", // Type, behaVior, lOot, Item display, Stats, Name, Level "# X X X P X X X #", // exPerience "# H C E B X M A #", // Helmet, Chestplate, lEgging, Boots, Main hand, second hAnd "# # # # # # # # #") .addIngredient('#', border) .addIngredient('T', new EntityTypeItem(type)) - .addIngredient('B', new EntityBehaviorItem(type)) + .addIngredient('V', new EntityBehaviorItem(type)) .addIngredient('O', new EntityLootItem()) .addIngredient('I', new EntityItemDisplay(type)) .addIngredient('S', new EntityStatsItem(type)) diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/CancelItem.java b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/CancelItem.java new file mode 100644 index 0000000..ba1f061 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/CancelItem.java @@ -0,0 +1,45 @@ +package me.unurled.sacredrealms.sr.gui.treasure; + +import static me.unurled.sacredrealms.sr.utils.Items.cancelItem; + +import me.unurled.sacredrealms.sr.components.treasure.Treasure; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.item.Item; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; +import xyz.xenondevs.invui.window.Window; + +public class CancelItem extends AbstractItem { + + public CancelItem() {} + + /** + * Gets the {@link ItemProvider}. This method gets called every time a {@link Window} is notified + * ({@link #notifyWindows()}). + * + * @return The {@link ItemProvider} + */ + @Override + public ItemProvider getItemProvider() { + return new ItemBuilder(cancelItem()); + } + + /** + * A method called if the {@link ItemStack} associated to this {@link Item} has been clicked by a + * player. + * + * @param clickType The {@link ClickType} the {@link Player} performed. + * @param player The {@link Player} who clicked on the {@link ItemStack}. + * @param event The {@link InventoryClickEvent} associated with this click. + */ + @Override + public void handleClick( + @NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + player.closeInventory(); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/ConfirmItem.java b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/ConfirmItem.java new file mode 100644 index 0000000..7ff5ab4 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/ConfirmItem.java @@ -0,0 +1,80 @@ +package me.unurled.sacredrealms.sr.gui.treasure; + +import static me.unurled.sacredrealms.sr.utils.Component.textComp; +import static me.unurled.sacredrealms.sr.utils.Logger.log; + +import java.util.HashMap; +import java.util.Map; +import me.unurled.sacredrealms.sr.components.treasure.Treasure; +import me.unurled.sacredrealms.sr.components.treasure.TreasureManager; +import me.unurled.srcore.api.Manager; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.item.Item; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; +import xyz.xenondevs.invui.window.Window; + +public class ConfirmItem extends AbstractItem { + + private final Treasure treasure; + + public ConfirmItem(Treasure treasure) { + this.treasure = treasure; + } + + /** + * Gets the {@link ItemProvider}. This method gets called every time a {@link Window} is notified + * ({@link #notifyWindows()}). + * + * @return The {@link ItemProvider} + */ + @Override + public ItemProvider getItemProvider() { + ItemStack confirm = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); + ItemMeta confirmMeta = confirm.getItemMeta(); + confirmMeta.displayName(textComp("Confirm")); + confirm.setItemMeta(confirmMeta); + return new ItemBuilder(confirm); + } + + /** + * A method called if the {@link ItemStack} associated to this {@link Item} has been clicked by a + * player. + * + * @param clickType The {@link ClickType} the {@link Player} performed. + * @param player The {@link Player} who clicked on the {@link ItemStack}. + * @param event The {@link InventoryClickEvent} associated with this click. + */ + @Override + public void handleClick( + @NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + Map items = new HashMap<>(); + for (int i = 0; i < player.getInventory().getContents().length; i++) { + if (i < 9 || i > 35) { + log("Skipping slot " + i); + continue; + } + if (event.getClickedInventory() == null) { + log("Clicked inventory is null"); + continue; + } + ItemStack content = event.getClickedInventory().getItem(i); + if (content != null && content.getType() != Material.AIR) { + items.put(i, content); + log("Added item to items: " + content + " at slot " + i); + } + } + log("Items: " + items); + TreasureManager tm = Manager.getInstance(TreasureManager.class); + tm.setBlocks(treasure.getId(), items); + + player.closeInventory(); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/DeleteItem.java b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/DeleteItem.java new file mode 100644 index 0000000..2a97258 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/DeleteItem.java @@ -0,0 +1,64 @@ +package me.unurled.sacredrealms.sr.gui.treasure; + +import static me.unurled.sacredrealms.sr.utils.Component.textComp; + +import me.unurled.sacredrealms.sr.components.treasure.Treasure; +import me.unurled.sacredrealms.sr.components.treasure.TreasureManager; +import me.unurled.srcore.api.Manager; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.item.Item; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; +import xyz.xenondevs.invui.window.Window; + +public class DeleteItem extends AbstractItem { + + private final Treasure treasure; + + public DeleteItem(Treasure treasure) { + this.treasure = treasure; + } + + /** + * Gets the {@link ItemProvider}. This method gets called every time a {@link Window} is notified + * ({@link #notifyWindows()}). + * + * @return The {@link ItemProvider} + */ + @Override + public ItemProvider getItemProvider() { + ItemStack delete = new ItemStack(Material.RED_STAINED_GLASS_PANE); + ItemMeta deleteMeta = delete.getItemMeta(); + deleteMeta.displayName(textComp("Delete")); + delete.setItemMeta(deleteMeta); + return new ItemBuilder(delete); + } + + /** + * A method called if the {@link ItemStack} associated to this {@link Item} has been clicked by a + * player. + * + * @param clickType The {@link ClickType} the {@link Player} performed. + * @param player The {@link Player} who clicked on the {@link ItemStack}. + * @param event The {@link InventoryClickEvent} associated with this click. + */ + @Override + public void handleClick( + @NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + // delete treasure + if (!player.hasPermission("sr.treasure.delete")) { + return; + } + TreasureManager treasureManager = Manager.getInstance(TreasureManager.class); + treasureManager.removeTreasure(treasure); + player.sendMessage(textComp("Treasure deleted!")); + player.closeInventory(); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java index 2170819..3caa159 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java @@ -1,16 +1,15 @@ package me.unurled.sacredrealms.sr.gui.treasure; -import static me.unurled.sacredrealms.sr.utils.Component.textComp; -import static me.unurled.sacredrealms.sr.utils.Items.cancelItem; import static me.unurled.sacredrealms.sr.utils.Items.glassPane; +import java.util.Map; import me.unurled.sacredrealms.sr.components.treasure.Treasure; -import org.bukkit.Material; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.gui.Gui.Builder.Normal; +import xyz.xenondevs.invui.inventory.Inventory; +import xyz.xenondevs.invui.inventory.VirtualInventory; +import xyz.xenondevs.invui.inventory.event.UpdateReason; import xyz.xenondevs.invui.item.Item; import xyz.xenondevs.invui.item.impl.SimpleItem; @@ -22,36 +21,31 @@ public class TreasureGUI { public static Gui createGui(@NotNull Treasure treasure) { ItemStack itemStack = glassPane(); Item pane = new SimpleItem(itemStack); - Normal gui = - Gui.normal() - .setStructure(".........", "abcdefghi", "jklmnopqr", "stuvwxyz$", "...123...") - .addIngredient('.', pane); - if (!treasure.getItems().isEmpty()) { - for (int i = 0; i < treasure.getItems().size(); i++) { - Item item = new SimpleItem(treasure.getItems().get(i)); - if (i == 28) { - gui.addIngredient('$', item); - } else { - gui.addIngredient((char) (i + 'a'), item); - } + + Inventory inventory = new VirtualInventory(27); + inventory.setPreUpdateHandler( + (itemPreUpdateEvent) -> { + if (itemPreUpdateEvent.isAdd() + || itemPreUpdateEvent.isRemove() + || itemPreUpdateEvent.isSwap()) { + itemPreUpdateEvent.setCancelled(false); + } + }); + + Map items = treasure.getItems(); + if (!items.isEmpty()) { + for (int i = 0; i < items.size(); i++) { + inventory.setItem(UpdateReason.SUPPRESSED, i, items.get(i)); } } - ItemStack cancel = cancelItem(); + Gui gui = Gui.empty(9, 5); + gui.fill(pane, true); + gui.fillRectangle(0, 1, 9, inventory, true); + gui.setItem(39, new DeleteItem(treasure)); + gui.setItem(40, new CancelItem()); + gui.setItem(41, new ConfirmItem(treasure)); - ItemStack confirm = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); - ItemMeta confirmMeta = confirm.getItemMeta(); - confirmMeta.displayName(textComp("Confirm")); - confirm.setItemMeta(confirmMeta); - - ItemStack delete = new ItemStack(Material.RED_STAINED_GLASS_PANE); - ItemMeta deleteMeta = delete.getItemMeta(); - deleteMeta.displayName(textComp("Delete")); - delete.setItemMeta(deleteMeta); - - gui.addIngredient('1', new SimpleItem(delete)); - gui.addIngredient('2', new SimpleItem(cancel)); - gui.addIngredient('3', new SimpleItem(confirm)); - return gui.build(); + return gui; } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/managers/Manager.java b/src/main/java/me/unurled/sacredrealms/sr/managers/Manager.java deleted file mode 100644 index 976c2ac..0000000 --- a/src/main/java/me/unurled/sacredrealms/sr/managers/Manager.java +++ /dev/null @@ -1,51 +0,0 @@ -package me.unurled.sacredrealms.sr.managers; - -import me.unurled.sacredrealms.sr.SR; -import org.bukkit.Bukkit; -import org.bukkit.event.Listener; - -public class Manager implements Listener { - - /** Constructor for the Manager class */ - public Manager() { - SR.getInstance().getManagers().addManager(this); - Bukkit.getScheduler() - .runTaskAsynchronously( - SR.getInstance(), - () -> { - load(); - Bukkit.getPluginManager().registerEvents(this, SR.getInstance()); - }); - } - - /** - * Get an instance of a manager - * - * @param clazz The class of the manager - * @return The instance of the manager - * @param The type of the manager - */ - public static T getInstance(Class clazz) { - return clazz.cast(SR.getInstance().getManagers().getManager(clazz)); - } - - /** Load the manager */ - public void load() { - loadData(); - } - - /** Unload the manager */ - public void unload() { - saveData(); - } - - /** Save the data */ - public void saveData() { - /* method empty, so it isn't required by the extended child to implement it */ - } - - /** Load the data */ - public void loadData() { - /* method empty, so it isn't required by the extended child to implement it */ - } -} diff --git a/src/main/java/me/unurled/sacredrealms/sr/managers/Managers.java b/src/main/java/me/unurled/sacredrealms/sr/managers/Managers.java deleted file mode 100644 index ce038c6..0000000 --- a/src/main/java/me/unurled/sacredrealms/sr/managers/Managers.java +++ /dev/null @@ -1,79 +0,0 @@ -package me.unurled.sacredrealms.sr.managers; - -import static me.unurled.sacredrealms.sr.utils.Logger.error; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.List; -import me.unurled.sacredrealms.sr.SR; -import me.unurled.sacredrealms.sr.components.clientbuild.ClientBuildManager; -import me.unurled.sacredrealms.sr.components.combat.CombatManager; -import me.unurled.sacredrealms.sr.components.entity.EntityManager; -import me.unurled.sacredrealms.sr.components.item.ItemManager; -import me.unurled.sacredrealms.sr.components.player.PlayerManager; -import me.unurled.sacredrealms.sr.components.treasure.TreasureManager; -import me.unurled.sacredrealms.sr.data.DataManager; -import org.bukkit.Bukkit; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class Managers { - - private final List managers; - - public Managers() { - managers = new ArrayList<>(); - // register managers here (like a new instance of them) - - register( - List.of( - DataManager.class, - PlayerManager.class, - CombatManager.class, - ItemManager.class, - EntityManager.class, - ClientBuildManager.class, - TreasureManager.class)); - } - - private void register(@NotNull List> clazz) { - int i = 0; - for (Class c : clazz) { - Bukkit.getScheduler() - .runTaskLater( - SR.getInstance(), - () -> { - try { - c.getDeclaredConstructor().newInstance(); - } catch (InstantiationException - | IllegalAccessException - | InvocationTargetException - | NoSuchMethodException e) { - error("Failed to register manager: " + c.getSimpleName()); - } - }, - 10L * i); - i++; - } - } - - public void addManager(Manager manager) { - managers.add(manager); - } - - public void unload() { - for (Manager manager : managers) { - manager.unload(); - } - } - - @Nullable - public Manager getManager(Class clazz) { - for (Manager manager : managers) { - if (manager.getClass().equals(clazz)) { - return manager; - } - } - return null; - } -} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/ChunkWrapper.java b/src/main/java/me/unurled/sacredrealms/sr/utils/ChunkWrapper.java new file mode 100644 index 0000000..780939e --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/ChunkWrapper.java @@ -0,0 +1,65 @@ +package me.unurled.sacredrealms.sr.utils; + +import org.bukkit.Chunk; +import org.bukkit.World; +import org.jetbrains.annotations.NotNull; + +public class ChunkWrapper { + + private int x; + private int z; + private World world; + + public ChunkWrapper(@NotNull Chunk chunk) { + this.x = chunk.getX(); + this.z = chunk.getZ(); + this.world = chunk.getWorld(); + } + + @Override + public int hashCode() { + int hash = 17; + hash = hash * 31 + x; + hash = hash * 31 + z; + hash = hash * 31 + world.hashCode(); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (!(obj instanceof ChunkWrapper)) return false; + return ((ChunkWrapper) obj).x == this.x + && ((ChunkWrapper) obj).z == this.z + && ((ChunkWrapper) obj).world.equals(this.world); + } + + @Override + public String toString() { + return "(" + x + ", " + z + ") [" + world + "]"; + } + + public int getX() { + return x; + } + + public void setX(int x) { + this.x = x; + } + + public int getZ() { + return z; + } + + public void setZ(int z) { + this.z = z; + } + + public World getWorld() { + return world; + } + + public void setWorld(World world) { + this.world = world; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/Component.java b/src/main/java/me/unurled/sacredrealms/sr/utils/Component.java index 9754212..a3c78d8 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/utils/Component.java +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/Component.java @@ -1,5 +1,7 @@ package me.unurled.sacredrealms.sr.utils; +import java.util.List; +import java.util.stream.Collectors; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.minimessage.MiniMessage; import org.jetbrains.annotations.NotNull; @@ -29,4 +31,9 @@ public class Component { } return (TextComponent) miniMessage.deserialize(msg); } + + public static List fromStringList( + @NotNull List list) { + return list.stream().map(Component::comp).collect(Collectors.toList()); + } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/CutsceneUtil.java b/src/main/java/me/unurled/sacredrealms/sr/utils/CutsceneUtil.java new file mode 100644 index 0000000..019fc40 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/CutsceneUtil.java @@ -0,0 +1,67 @@ +package me.unurled.sacredrealms.sr.utils; + +import static me.unurled.sacredrealms.sr.utils.Component.textComp; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import me.unurled.sacredrealms.sr.components.cutscene.Frame; +import me.unurled.sacredrealms.sr.components.cutscene.Marker; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.title.Title; +import net.kyori.adventure.title.Title.Times; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class CutsceneUtil { + + private static final String FONT = "custom"; + + private CutsceneUtil() {} + + public static void showBlackScreen(@NotNull Player p, int duration, int fadeIn, int fadeOut) { + // /title @s title {"text":"\uE000","color":"black"} + Title title = + Title.title( + textComp("\uE000").font(Key.key(FONT)), + textComp(""), + Times.times( + Duration.ofSeconds(fadeIn), + Duration.ofSeconds(duration), + Duration.ofSeconds(fadeOut))); + p.showTitle(title); + } + + public static @NotNull List interpolateFrames( + @NotNull Marker startMarker, @NotNull Marker endMarker, long stepMillis) { + long duration = startMarker.getOverAllTime(); + + int numFrames = (int) (duration / stepMillis); + + double xDiff = (endMarker.getStart().getX() - startMarker.getStart().getX()) / numFrames; + double yDiff = (endMarker.getStart().getY() - startMarker.getStart().getY()) / numFrames; + double zDiff = (endMarker.getStart().getZ() - startMarker.getStart().getZ()) / numFrames; + double yawDiff = (endMarker.getStart().getYaw() - startMarker.getStart().getYaw()) / numFrames; + double pitchDiff = + (endMarker.getStart().getPitch() - startMarker.getStart().getPitch()) / numFrames; + + ArrayList frames = new ArrayList<>(); + + for (int i = 0; i < numFrames; i++) { + + double x = startMarker.getStart().getX() + xDiff * i; + double y = startMarker.getStart().getY() + yDiff * i; + double z = startMarker.getStart().getZ() + zDiff * i; + double yaw = startMarker.getStart().getYaw() + yawDiff * i; + double pitch = startMarker.getStart().getPitch() + pitchDiff * i; + + Frame frame = + new Frame( + new Location(startMarker.getStart().getWorld(), x, y, z, (float) yaw, (float) pitch)); + + frames.add(frame); + } + return frames; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/File.java b/src/main/java/me/unurled/sacredrealms/sr/utils/File.java new file mode 100644 index 0000000..f185393 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/File.java @@ -0,0 +1,70 @@ +package me.unurled.sacredrealms.sr.utils; + +import java.io.FileInputStream; +import java.io.IOException; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import org.jetbrains.annotations.NotNull; + +public class File { + + private File() {} + + public static @NotNull String getFileChecksum(String filePath) + throws IOException, NoSuchAlgorithmException { + MessageDigest sha = MessageDigest.getInstance("MD5"); + + try (FileInputStream fis = new FileInputStream(filePath)) { + DigestInputStream dis = new DigestInputStream(fis, sha); + while (dis.read() != -1) + ; + dis.close(); + } + + // Convert the byte array to a hexadecimal string + byte[] bytes = sha.digest(); + StringBuilder sb = new StringBuilder(); + for (byte aByte : bytes) { + sb.append(String.format("%02x", aByte)); + } + + return sb.toString(); + } + + public static void zipFile( + @NotNull java.io.File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException { + if (fileToZip.isHidden()) { + return; + } + try (FileInputStream fis = new FileInputStream(fileToZip)) { + ZipEntry zipEntry = new ZipEntry(fileName); + zipEntry.setTime(0); + zipOut.putNextEntry(zipEntry); + byte[] bytes = new byte[1024]; + int length; + while ((length = fis.read(bytes)) >= 0) { + zipOut.write(bytes, 0, length); + } + zipOut.closeEntry(); + } + } + + public static void zipDirectory( + @NotNull java.io.File folderToZip, String folderName, ZipOutputStream zipOut) + throws IOException { + java.io.File[] children = folderToZip.listFiles(); + if (children == null) { + return; + } + for (java.io.File childFile : children) { + if (childFile.isDirectory()) { + zipDirectory(childFile, folderName + "/" + childFile.getName(), zipOut); + } else { + zipFile(childFile, folderName + "/" + childFile.getName(), zipOut); + } + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/HTTPRequestMultipartBody.java b/src/main/java/me/unurled/sacredrealms/sr/utils/HTTPRequestMultipartBody.java new file mode 100644 index 0000000..31e9c59 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/HTTPRequestMultipartBody.java @@ -0,0 +1,156 @@ +package me.unurled.sacredrealms.sr.utils; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.List; + +public class HTTPRequestMultipartBody { + private final byte[] bytes; + private String boundary; + + private HTTPRequestMultipartBody(byte[] bytes, String boundary) { + this.bytes = bytes; + this.boundary = boundary; + } + + public String getBoundary() { + return boundary; + } + + public void setBoundary(String boundary) { + this.boundary = boundary; + } + + public String getContentType() { + return "multipart/form-data; boundary=" + this.getBoundary(); + } + + public byte[] getBody() { + return this.bytes; + } + + public static class Builder { + + public static final String CONTENT_TYPE = "Content-Type: application/octet-stream\r\n\r\n"; + List parts; + + public Builder() { + this.parts = new ArrayList<>(); + } + + public Builder addPart(String fieldName, String fieldValue) { + MultiPartRecord part = new MultiPartRecord(); + part.setFieldName(fieldName); + part.setContent(fieldValue); + String defaultMimetype = "text/plain"; + part.setContentType(defaultMimetype); + this.parts.add(part); + return this; + } + + public Builder addPart(String fieldName, String fieldValue, String contentType) { + MultiPartRecord part = new MultiPartRecord(); + part.setFieldName(fieldName); + part.setContent(fieldValue); + part.setContentType(contentType); + this.parts.add(part); + return this; + } + + public Builder addPart( + String fieldName, Object fieldValue, String contentType, String fileName) { + MultiPartRecord part = new MultiPartRecord(); + part.setFieldName(fieldName); + part.setContent(fieldValue); + part.setContentType(contentType); + part.setFilename(fileName); + this.parts.add(part); + return this; + } + + public HTTPRequestMultipartBody build() throws IOException { + String boundary = new BigInteger(256, new SecureRandom()).toString(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (MultiPartRecord multiPartRecord : parts) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder + .append("--") + .append(boundary) + .append("\r\n") + .append("Content-Disposition: form-data; name=\"") + .append(multiPartRecord.getFieldName()); + if (multiPartRecord.getFilename() != null) { + stringBuilder.append("\"; filename=\"").append(multiPartRecord.getFilename()); + } + out.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8)); + out.write(("\"\r\n").getBytes(StandardCharsets.UTF_8)); + Object content = multiPartRecord.getContent(); + if (content instanceof String cont) { + out.write(("\r\n\r\n").getBytes(StandardCharsets.UTF_8)); + out.write(cont.getBytes(StandardCharsets.UTF_8)); + } else if (content instanceof byte[] cont) { + out.write(CONTENT_TYPE.getBytes(StandardCharsets.UTF_8)); + out.write(cont); + } else if (content instanceof File file) { + out.write(CONTENT_TYPE.getBytes(StandardCharsets.UTF_8)); + Files.copy(file.toPath(), out); + } else { + out.write(CONTENT_TYPE.getBytes(StandardCharsets.UTF_8)); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(out); + objectOutputStream.writeObject(content); + objectOutputStream.flush(); + } + out.write("\r\n".getBytes(StandardCharsets.UTF_8)); + } + out.write(("--" + boundary + "--\r\n").getBytes(StandardCharsets.UTF_8)); + + return new HTTPRequestMultipartBody(out.toByteArray(), boundary); + } + + public static class MultiPartRecord { + private String fieldName; + private String filename; + private String contentType; + private Object content; + + public String getFieldName() { + return fieldName; + } + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public Object getContent() { + return content; + } + + public void setContent(Object content) { + this.content = content; + } + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java b/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java index 69a93ee..1ebb3d3 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java @@ -4,6 +4,7 @@ import static me.unurled.sacredrealms.sr.utils.Component.comp; import static me.unurled.sacredrealms.sr.utils.Component.textComp; import static me.unurled.sacredrealms.sr.utils.SRPlayerUtils.syncSRToPlayer; +import com.google.gson.JsonElement; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -11,9 +12,12 @@ import java.util.concurrent.atomic.AtomicReference; import me.unurled.sacredrealms.sr.components.attributes.Attribute; import me.unurled.sacredrealms.sr.components.item.Item; import me.unurled.sacredrealms.sr.components.item.ItemManager; +import me.unurled.sacredrealms.sr.components.item.ItemStackDeserializer; +import me.unurled.sacredrealms.sr.components.item.ItemStackSerializer; import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; -import me.unurled.sacredrealms.sr.managers.Manager; +import me.unurled.srcore.api.Manager; +import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.EntityEquipment; @@ -68,21 +72,10 @@ public class Items { ItemManager im = Manager.getInstance(ItemManager.class); if (im == null) return 0.0; if (item == null) return 0.0; - if (item.getType().equals(Material.AIR)) return 0.0; - PersistentDataContainer p = item.getItemMeta().getPersistentDataContainer(); - if (p.has(ItemManager.ID, PersistentDataType.STRING)) { - Item i = im.getItem(p.get(ItemManager.ID, PersistentDataType.STRING)); - if (i != null) { - return i.getAttribute(attribute); - } - } - if (p.has(attribute.getKey(), PersistentDataType.DOUBLE)) { - try { - return p.get(attribute.getKey(), PersistentDataType.DOUBLE); - } catch (Exception e) { - return 0.0; - } - } + + Item it = im.getItem(item); + if (it != null) return it.getAttribute(attribute); + return 0.0; } @@ -171,4 +164,29 @@ public class Items { syncSRToPlayer(p, player); } + + public static @NotNull ItemStack name(@NotNull ItemStack stack, String name) { + ItemMeta meta = stack.getItemMeta(); + meta.displayName(textComp(name)); + stack.setItemMeta(meta); + return stack; + } + + public static @NotNull String locationToString(@NotNull Location location) { + return location.getWorld().getName() + + "_z" + + location.getBlockX() + + "y" + + location.getBlockY() + + "z" + + location.getBlockZ(); + } + + public static JsonElement serialize(ItemStack itemStack) { + return new ItemStackSerializer().serialize(itemStack, ItemStack.class, null); + } + + public static @NotNull ItemStack deserialize(@NotNull JsonElement jsonElement) { + return new ItemStackDeserializer().deserialize(jsonElement, ItemStack.class, null); + } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/SRPlayerUtils.java b/src/main/java/me/unurled/sacredrealms/sr/utils/SRPlayerUtils.java index 8da2e86..c28975b 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/utils/SRPlayerUtils.java +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/SRPlayerUtils.java @@ -3,9 +3,12 @@ package me.unurled.sacredrealms.sr.utils; import static me.unurled.sacredrealms.sr.utils.Component.comp; import java.util.List; +import java.util.Random; import me.unurled.sacredrealms.sr.SR; import me.unurled.sacredrealms.sr.components.attributes.Attribute; +import me.unurled.sacredrealms.sr.components.player.PlayerManager; import me.unurled.sacredrealms.sr.components.player.SRPlayer; +import me.unurled.srcore.api.Manager; import org.bukkit.attribute.AttributeInstance; import org.bukkit.entity.Display; import org.bukkit.entity.Entity; @@ -18,6 +21,8 @@ import org.jetbrains.annotations.NotNull; public class SRPlayerUtils { + private static final Random RANDOM = new Random(); + private SRPlayerUtils() {} public static void syncPlayerToSR(@NotNull Player p, @NotNull SRPlayer sr) { @@ -58,7 +63,11 @@ public class SRPlayerUtils { p.updateInventory(); } - public static void updateActionBar(@NotNull Player p, @NotNull SRPlayer sr) { + public static void updateActionBar(@NotNull Player p) { + PlayerManager pm = Manager.getInstance(PlayerManager.class); + if (pm == null) return; + SRPlayer sr = pm.getPlayer(p.getUniqueId()); + if (sr == null) return; int maxHealth = (int) sr.getAttribute(Attribute.HEALTH); int health = (int) sr.getHealth(); int mana = (int) sr.getAttribute(Attribute.MANA); @@ -80,11 +89,17 @@ public class SRPlayerUtils { Vector spawnVector = location.add(facingVector.multiply(0.75)); // summon entity + + double randomX = RANDOM.nextDouble() * 0.5 - 0.25; + double randomZ = RANDOM.nextDouble() * 0.5 - 0.25; + spawnVector.add(new Vector(randomX, 0, randomZ)); + TextDisplay indicators = eventEntity .getWorld() .spawn( - spawnVector.toLocation(eventEntity.getWorld()).add(0, 0.7, 0), TextDisplay.class); + spawnVector.toLocation(eventEntity.getWorld()).add(0.0, 0.5, 0.0), + TextDisplay.class); String prefix = "-"; if (isHeal) prefix = "+"; @@ -114,6 +129,6 @@ public class SRPlayerUtils { } height++; } - }.runTaskTimer(SR.getInstance(), 0, 1); + }.runTaskTimer(SR.getPlugin(), 0, 1); } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/character/CharRepo.java b/src/main/java/me/unurled/sacredrealms/sr/utils/character/CharRepo.java new file mode 100644 index 0000000..8b5363d --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/character/CharRepo.java @@ -0,0 +1,235 @@ +package me.unurled.sacredrealms.sr.utils.character; + +import org.jetbrains.annotations.NotNull; + +public enum CharRepo { + A('A', 5), + a('a', 5), + B('B', 5), + b('b', 5), + C('C', 5), + c('c', 5), + D('D', 5), + d('d', 5), + E('E', 5), + e('e', 5), + F('F', 5), + f('f', 4), + G('G', 5), + g('g', 5), + H('H', 5), + h('h', 5), + I('I', 3), + i('i', 1), + J('J', 5), + j('j', 5), + K('K', 5), + k('k', 4), + L('L', 5), + l('l', 1), + M('M', 5), + m('m', 5), + N('N', 5), + n('n', 5), + O('O', 5), + o('o', 5), + P('P', 5), + p('p', 5), + Q('Q', 5), + q('q', 5), + R('R', 5), + r('r', 5), + S('S', 5), + s('s', 5), + T('T', 5), + t('t', 4), + U('U', 5), + u('u', 5), + V('V', 5), + v('v', 5), + W('W', 5), + w('w', 5), + X('X', 5), + x('x', 5), + Y('Y', 5), + y('y', 5), + Z('Z', 5), + z('z', 5), + NUM_1('1', 5), + NUM_2('2', 5), + NUM_3('3', 5), + NUM_4('4', 5), + NUM_5('5', 5), + NUM_6('6', 5), + NUM_7('7', 5), + NUM_8('8', 5), + NUM_9('9', 5), + NUM_0('0', 5), + EXCLAMATION_POINT('!', 1), + AT_SYMBOL('@', 6), + NUM_SIGN('#', 5), + DOLLAR_SIGN('$', 5), + PERCENT('%', 5), + UP_ARROW('^', 5), + AMPERSAND('&', 5), + ASTERISK('*', 5), + LEFT_PARENTHESIS('(', 4), + RIGHT_PERENTHESIS(')', 4), + MINUS('-', 5), + UNDERSCORE('_', 5), + PLUS_SIGN('+', 5), + EQUALS_SIGN('=', 5), + LEFT_CURL_BRACE('{', 4), + RIGHT_CURL_BRACE('}', 4), + LEFT_BRACKET('[', 3), + RIGHT_BRACKET(']', 3), + COLON(':', 1), + SEMI_COLON(';', 1), + DOUBLE_QUOTE('"', 3), + SINGLE_QUOTE('\'', 1), + LEFT_ARROW('<', 4), + RIGHT_ARROW('>', 4), + QUESTION_MARK('?', 5), + SLASH('/', 5), + BACK_SLASH('\\', 5), + LINE('|', 1), + TILDE('~', 5), + TICK('`', 2), + PERIOD('.', 1), + COMMA(',', 1), + SPACE(' ', 3), + DEFAULT('\u0000', 0), + + // spacing + + NEG1('\uF801', -1, -3), + NEG2('\uF802', -2, -4), + NEG3('\uF803', -3, -5), + NEG4('\uF804', -4, -5), + NEG5('\uF805', -5, -7), + NEG6('\uF806', -6, -8), + NEG7('\uF807', -7, -9), + NEG8('\uF808', -8, -10), + NEG16('\uF809', -16, -18), + NEG32('\uF80A', -32, -34), + NEG64('\uF80B', -64, -66), + NEG128('\uF80C', -128, -130), + NEG256('\uF80D', -256, -258), + NEG512('\uF80E', -512, -514), + NEG1024('\uF80F', -1024, -1026), + + POS1('\uF821', 1, 0), + POS2('\uF822', 2, 1), + POS4('\uF824', 4, 3), + POS8('\uF828', 8, 7), + POS16('\uF829', 16, 15), + POS32('\uF82A', 32, 31), + POS64('\uF82B', 64, 63), + POS128('\uF82C', 128, 127), + POS256('\uF82D', 256, 255), + POS512('\uF82E', 512, 511), + POS1024('\uF82F', 1024, 1023); + + private final char ch; + private final int width; + private final int effectiveWidth; + + CharRepo(char ch, int width) { + this.ch = ch; + this.width = width; + this.effectiveWidth = width; + } + + CharRepo(char ch, int width, int effectiveWidth) { + this.ch = ch; + this.width = width; + this.effectiveWidth = effectiveWidth; + } + + public static CharRepo getDefaultFontInfo(char c) { + for (CharRepo dFI : CharRepo.values()) { + if (dFI.getCh() == c) return dFI; + } + return CharRepo.DEFAULT; + } + + public static int getPixelWidth(@NotNull String str) { + int val = 0; + for (char ch : str.toCharArray()) { + val += CharRepo.getDefaultFontInfo(ch).getEffectiveWidth(); + val += 1; // acount for spacing + } + return val; + } + + /** + * get negative characters + * + * @param n n > 0 + * @return chars + */ + public static String getShortestNegChars(int n) { + StringBuilder stringBuilder = new StringBuilder(); + while (n >= 128) { + stringBuilder.append(CharRepo.NEG128.getCh()); + n -= 128; + } + if (n - 64 >= 0) { + stringBuilder.append(CharRepo.NEG64.getCh()); + n -= 64; + } + if (n - 32 >= 0) { + stringBuilder.append(CharRepo.NEG32.getCh()); + n -= 32; + } + if (n - 16 >= 0) { + stringBuilder.append(CharRepo.NEG16.getCh()); + n -= 16; + } + if (n - 8 >= 0) { + stringBuilder.append(CharRepo.NEG8.getCh()); + n -= 8; + } + if (n - 7 >= 0) { + stringBuilder.append(CharRepo.NEG7.getCh()); + n -= 7; + } + if (n - 6 >= 0) { + stringBuilder.append(CharRepo.NEG6.getCh()); + n -= 6; + } + if (n - 5 >= 0) { + stringBuilder.append(CharRepo.NEG5.getCh()); + n -= 5; + } + if (n - 4 >= 0) { + stringBuilder.append(CharRepo.NEG4.getCh()); + n -= 4; + } + if (n - 3 >= 0) { + stringBuilder.append(CharRepo.NEG3.getCh()); + n -= 3; + } + if (n - 2 >= 0) { + stringBuilder.append(CharRepo.NEG2.getCh()); + n -= 2; + } + if (n - 1 >= 0) { + stringBuilder.append(CharRepo.NEG1.getCh()); + } + return stringBuilder.toString(); + } + + public char getCh() { + return ch; + } + + public int getEffectiveWidth() { + return effectiveWidth; + } + + public int getBoldLength() { + if (this == CharRepo.SPACE) return this.width; + return this.width + 1; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/character/CharacterArranger.java b/src/main/java/me/unurled/sacredrealms/sr/utils/character/CharacterArranger.java new file mode 100644 index 0000000..4732de7 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/character/CharacterArranger.java @@ -0,0 +1,39 @@ +package me.unurled.sacredrealms.sr.utils.character; + +public class CharacterArranger { + public static char currentChar; + + public static void increase() { + currentChar = (char) (currentChar + '\u0001'); + } + + /** + * Get and increase the char arranged automatically by the plugin + * + * @return char + */ + public static char getAndIncrease() { + char temp = currentChar; + increase(); + return temp; + } + + /** + * Increase and get the char arranged automatically by the plugin + * + * @return char + */ + public static char increaseAndGet() { + increase(); + return currentChar; + } + + /** + * Reset the initial char + * + * @param c char + */ + public static void reset(char c) { + currentChar = c; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/character/ConfiguredChar.java b/src/main/java/me/unurled/sacredrealms/sr/utils/character/ConfiguredChar.java new file mode 100644 index 0000000..aa3e553 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/character/ConfiguredChar.java @@ -0,0 +1,104 @@ +package me.unurled.sacredrealms.sr.utils.character; + +import static me.unurled.sacredrealms.sr.utils.Logger.warn; + +public class ConfiguredChar { + private char character; + private String pngFile; + private int height; + private int width; + private int ascent; + + private ConfiguredChar() {} + + public ConfiguredChar(char character, String pngFile, int height, int width, int ascent) { + this.character = character; + this.pngFile = pngFile; + this.height = height; + this.width = width; + this.ascent = ascent; + } + + public static Builder builder() { + return new Builder(); + } + + public char getCharacter() { + return character; + } + + public String getPngFile() { + return pngFile; + } + + public int getHeight() { + return height; + } + + public int getWidth() { + return width; + } + + public int getAscent() { + return ascent; + } + + public String getFile() { + return pngFile + ".png"; + } + + public static class Builder { + + private final ConfiguredChar configuredChar; + + public Builder() { + this.configuredChar = new ConfiguredChar(); + } + + public static Builder of() { + return new Builder(); + } + + public Builder character(char character) { + configuredChar.character = character; + return this; + } + + public Builder png(String png) { + configuredChar.pngFile = png; + return this; + } + + public Builder height(int height) { + configuredChar.height = height; + return this; + } + + public Builder ascent(int ascent) { + configuredChar.ascent = ascent; + if (ascent >= configuredChar.height) { + warn("Invalid config for " + configuredChar.pngFile); + warn("Ascent " + ascent + " should be no higher than Height " + configuredChar.height); + } + return this; + } + + public Builder descent(int descent) { + if (descent < 0) { + warn("Invalid config for " + configuredChar.pngFile); + warn("Descent " + descent + " should be no lower than 0"); + } + configuredChar.ascent = configuredChar.height - descent; + return this; + } + + public Builder width(int width) { + configuredChar.width = width; + return this; + } + + public ConfiguredChar build() { + return configuredChar; + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/gson/EntityGson.java b/src/main/java/me/unurled/sacredrealms/sr/utils/gson/EntityGson.java new file mode 100644 index 0000000..dba8817 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/gson/EntityGson.java @@ -0,0 +1,97 @@ +package me.unurled.sacredrealms.sr.utils.gson; + +import static me.unurled.sacredrealms.sr.utils.Logger.error; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import java.lang.reflect.Type; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import org.bukkit.Location; +import org.bukkit.craftbukkit.entity.CraftEntity; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.NotNull; + +public class EntityGson { + public class Deserializer implements JsonDeserializer { + + /** + * Gson invokes this call-back method during deserialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects for any + * non-trivial field of the returned object. However, you should never invoke it on the same + * type passing {@code json} since that will cause an infinite loop (Gson will call your + * call-back method again). + * + * @param json The Json data being deserialized + * @param typeOfT The type of the Object to deserialize to + * @param context + * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T} + * @throws JsonParseException if json is not in the expected format of {@code typeofT} + */ + @Override + public Entity deserialize( + @NotNull JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + // get type from json, get location from json, then create entity and paste nbt data + EntityType type = EntityType.valueOf(json.getAsJsonObject().get("type").getAsString()); + Location location = + new LocationSerializer() + .deserialize(json.getAsJsonObject().get("location"), Location.class, context); + Entity e = location.getWorld().spawnEntity(location, type); + net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) e).getHandle(); + try { + nmsEntity.load(TagParser.parseTag(json.getAsJsonObject().get("entity").getAsString())); + } catch (CommandSyntaxException ex) { + error("Failed to parse entity NBT data " + ex.getMessage()); + } + e.teleportAsync(location); + return e; + } + } + + public class Serializer implements JsonSerializer { + + /** + * Gson invokes this call-back method during serialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any + * non-trivial field of the {@code src} object. However, you should never invoke it on the + * {@code src} object itself since that will cause an infinite loop (Gson will call your + * call-back method again). + * + * @param src the object that needs to be converted to Json. + * @param typeOfSrc the actual type (fully genericized version) of the source object. + * @param context + * @return a JsonElement corresponding to the specified object. + */ + @Override + public JsonElement serialize( + @NotNull Entity src, Type typeOfSrc, JsonSerializationContext context) { + net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) src).getHandle(); + CompoundTag nbt = new CompoundTag(); + nmsEntity.save(nbt); + LocationSerializer locationSerializer = new LocationSerializer(); + String s = + "{\"type\":" + + src.getType().name() + + ", \"location\":" + + locationSerializer.serialize(src.getLocation(), Location.class, context) + + ", \"entity\":" + + nbt.getAsString() + + "}"; + return new JsonPrimitive(s); + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/gson/LocationSerializer.java b/src/main/java/me/unurled/sacredrealms/sr/utils/gson/LocationSerializer.java new file mode 100644 index 0000000..9ed1d1b --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/gson/LocationSerializer.java @@ -0,0 +1,51 @@ +package me.unurled.sacredrealms.sr.utils.gson; + +import com.google.gson.Gson; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import io.leangen.geantyref.TypeToken; +import java.lang.reflect.Type; +import java.util.Map; +import org.bukkit.Location; +import org.jetbrains.annotations.NotNull; + +public class LocationSerializer implements JsonSerializer, JsonDeserializer { + + /** + * Gson invokes this call-back method during serialization when it encounters a field of the + * specified type. + * + *

In the implementation of this call-back method, you should consider invoking {@link + * JsonSerializationContext#serialize(Object, Type)} method to create JsonElements for any + * non-trivial field of the {@code src} object. However, you should never invoke it on the {@code + * src} object itself since that will cause an infinite loop (Gson will call your call-back method + * again). + * + * @param src the object that needs to be converted to Json. + * @param typeOfSrc the actual type (fully genericized version) of the source object. + * @param context + * @return a JsonElement corresponding to the specified object. + */ + @Override + public JsonElement serialize( + @NotNull Location src, Type typeOfSrc, JsonSerializationContext context) { + Gson gson = new Gson(); + + return gson.toJsonTree(src.serialize()); + } + + @Override + public Location deserialize( + JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) + throws JsonParseException { + Gson gson = new Gson(); + + Type typ = new TypeToken>() {}.getType(); + + return Location.deserialize(gson.fromJson(jsonElement, typ)); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/gson/MapSerializer.java b/src/main/java/me/unurled/sacredrealms/sr/utils/gson/MapSerializer.java new file mode 100644 index 0000000..6516b60 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/gson/MapSerializer.java @@ -0,0 +1,34 @@ +package me.unurled.sacredrealms.sr.utils.gson; + +import com.google.gson.Gson; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import io.leangen.geantyref.TypeToken; +import java.lang.reflect.Type; +import java.util.Map; + +public class MapSerializer + implements JsonSerializer>, JsonDeserializer> { + + @Override + public Map deserialize( + JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) + throws JsonParseException { + + Type typ = new TypeToken>() {}.getType(); + + return new Gson().fromJson(jsonElement, typ); + } + + @Override + public JsonElement serialize( + Map stringObjectMap, + Type type, + JsonSerializationContext jsonSerializationContext) { + return new Gson().toJsonTree(stringObjectMap); + } +} diff --git a/src/main/resources/backgrounds/b0.png b/src/main/resources/backgrounds/b0.png new file mode 100644 index 0000000000000000000000000000000000000000..1130ab394164edce32b2ecefaf5ff31d314b60fe GIT binary patch literal 1615 zcmbVMO>f*p7fN3jCV&_dyGBF zZgN80_yHWK`WN62R4O@?Gshm`#tp7L^w8J)VI|e}L$&-do_XeZpZ8p^80AAeFm=ZuGst{TRNf6M-!-b=nCOl3CZ zY9Q=sRsh;CZrq#}lJPswil?ZO!RMdnEBf;i4(GO;JVt;(U}h9MAmj^|l` zu&Se6)2WqL*Ov^js8}frEme+cMmkUjIx<1(+=r}aX!B|jCy1CcrG?|#xb|rV#YkyYjnr$Xo8>bGAht=;*jTA0%Ni4v-aG_176Dm_uKGuX zaCSwd4oW6A4?*Vj+M9xOOF^|N`$~De|F- znlO^C8mkfvgF5&0e6QQxDOD=Run;SaZc&*-)qOTonk1x`SDNNb^y0{bxY$y1!f3!- z9qL+xc0es3uMey+^tt8J&cF?@&v+Ot_s5DI)Y)I|=dgd!65M00)CyegS{`3-TVWfw zE#Benj+>@I%IA>+`*%tS$B&NRo2NdMb=i`OEjw#LRozRB9qmr z7u;M-%M}rLHxK5tlaT<@H68etxAF tZ}czx%AWsqa`Ml~(~}>sxi5x>(Ox@$@0aAxe|5|6ZEnZEfAjrMF9F6h_2~cr literal 0 HcmV?d00001 diff --git a/src/main/resources/backgrounds/b1.png b/src/main/resources/backgrounds/b1.png new file mode 100644 index 0000000000000000000000000000000000000000..ae76d6ab6bf79e8aa309674fbf1b223aa6522dc5 GIT binary patch literal 1609 zcmbVM%Wl&^6m=y)KfoKK=Rw9%u8_m!+nizfxTb+Q&~ZtR6X?s8L$npGqn0c<@=V)c7-Qu5CT)7I9s75XgB{z#KKu=giR%%! ziKagt+!aZeY=n=e&fqK1`%2}+vWCNm!8%4E`xmwb+c2TO8JqTl(kh$oxv!8QogO_r*V0>i{WL~eL z$w{l=RLP*=UYBWcNjEWk0Svk$Mg zTdnm%cEtcTLaERbDl+K08)KzWMA})YXvTOu40OngDMUiJ?Z+NlN(}BmKY8(zZ#1#n zHDcQIeZ1sUy*slHCEKm4KeJE3zT0q`4dyis@2=+?b_48b;xr8ECS4r!2KDbIGyCU- zfa6C8|IJe!%BpS1swknL`_ogi$zM<31_F&cg-}+VfIw$e={YEvEQ>iAI9UV}`(d90 z=|5Ux0xM-#4QaubdyuVTTh98M_>%7ZTl&e0H&6PU7X2UT)3mZa&3YW>sijxJSk>T} z6vg_pUgNz(Luv+x$ydGBGZ#MUsm(K>E?_2qzw_`G%=63baJjSifo{-FMA0OZWFJW|>Un#53+6n|1y8VWGMU{Cx5>n0?so4g z6$Nc<1Pisd5JYUR&_XOkEc_E}tklNR!Y}&>D(eH6WHXukzTfxwee>O!pF6vKa^IvR zN!#nQwFP)ygL}v3P4NHm=57j)Ey?VqOp>n~&!2CjUu>|rmNX6$3qdr6|x$;!*5G{K1Xqu-CRaIS~7-LkmRbo|5-M5b+ z0~@-AZMYPS9kc3~78?F!aF+&6XQ6gx=nURmxh+KEXj-@1RlBCj)0U>&HV_z^VJLu5 zvej6So)TvVM+`NV(KJj%$YYc<5}z*%SB6jrIYdc88)w5hLB+HlNic}{&`Knq;5 zoOb%o1FEqOi&!i&!0H7yX>-A|HvbKE@%bkQP+QL{e2k4HiV7E*m|1}|h6Ndm&Kj!; z(-v69ms84SRv^rSc{YhNoiZYL+Tgr1Qfht_GBQjRqr(@&IN;sv$PWgrMg((ZP|Z+q zRWYg!-E`o>=22`p7!RNx55nf^&rlOL48YmHLZN2@BFJCC0d<-@jR+(hMx@2GByP#5 zD5EpYJ3Ix)5S`hN!t0f{Ov@?<VNxT0FB@<)$(OAPvNDQ$Selx6y|N|-o`^Pt54NyQ;o%2pWf>e6596i*>&UFlhd#7 z9-3VH`u6Sq=bu-k^=&&_*Q^b%_M&&v)lIj}Q;#M-6kDG+Jboixet7u6{e1A(XXa{8 IPhYw59ln+Ju>b%7 literal 0 HcmV?d00001 diff --git a/src/main/resources/backgrounds/b16.png b/src/main/resources/backgrounds/b16.png new file mode 100644 index 0000000000000000000000000000000000000000..83d0f34e7b65cc65f93ffc8d4fc670af5ad69b79 GIT binary patch literal 1614 zcmbVMO>5jn7*?Qx)FC0X6iiNoLPL^P(nwlc(hwy$D>q(jxyD(r3q53ZG};}!(x}qd zyY?Z6+ybFJ1w!a==(*5C`!90LA1Jgxp|AEs*kx^p2z`uZo_XHq{h0UF&i2zAOShLa zO}o+Ah<4$73!a_*?sjKN>150om1WsOE7bL5(`>|1sa0D)sUjsXax zI?fdx8+mnS&Jgj6m7-8W=BQ?*J$a}?9i&cu$clzGuV!(Ah*@J=Shk64pC-VBHMrui z98QcU%;H0y@my7awHs_PkV;kqc@A~6e8vF8HjWz`3$>)N8rXRAPdn|_qgEJ zF0bTa$@uya$h=c~Q;>Gasgh+^%HcdyJ9Ej%aZQZw?TI{*qw4;t4LqWXhdN}eS6O)zV4cQPCB=mT8YM$|*W}~4%lS(0!)h8g-S)F>G z=(B0DAOauf!IXA1;6QpoNlamtOx1{%e60`By0ql1^Tb#5=wj&~m%K&N7nJCyNS}q3 z4QSry&`&MBcE+k3&on94|MN9jJJqD-aOiyXu<5z*({yc~19b^K`SGJ&*>q|wOE?haOhyK?P?$T7}>9mvIwMx))qD~%$J zy=y~nN^c6i=xsv?d4e|gx#&yuK9A5>=+XWNJ8WWOp`X#rH|KokJ7>;sJKLMLZrs0N z7{;y6Rtk{2@9Qe~*J$Rc&9eG{loV@UJbY*x;60>zv$%N^IoRZJ6L%xheJ zSPUo5WA2EdNJXYgz`8XyA1JNLf%<~!dijh4&^8Qf9}BspY3-ua8%GevOpt}>vU{8h zXIGT!u;60j2xu->(d4vU2&PrhRcbiT)XtnTBA$)W!#$bBYE*uAYJ&)v7QP8tBNlF0 zq|tRf>Jo}qf51(O@e~@WSSH73p&srMkKz{O_zV-!Rrg@RGz@7cD>ciw=mfqAy4X_26h|x~ zE7-FVLK4eeU5TtH5w6uB++*=d;)!NF-ybM`SY>~{KZgBLvnk@oL|6&-606x*;a01e z2vCd=s8zzmOeygHypVAG*znSJszX_o4c(F@B=mT8YF>!fv(ZqZNu^N6s}taxyh^=* z2&U6=fdUtcV7h-a5McBxB{7ASD$yfWi1j|Gb#BQyUm{hSod7`sk^qwz;ppU`Q6|Hhkky$6RdZy k{mJ9J*Xu>S_rDv)-TTI?@AOV literal 0 HcmV?d00001 diff --git a/src/main/resources/backgrounds/b32.png b/src/main/resources/backgrounds/b32.png new file mode 100644 index 0000000000000000000000000000000000000000..31ce74f3f69fc0debf4f9ad90c6f58b9b1f59582 GIT binary patch literal 1595 zcmbVNzi-<{6lPE~Xk9o<7o$Ln0|OKZ5|T$!7Rf_c*RkwaK&TqF0J}g39gjzeu*hS{ zlO=o15@aaasY8*iOQsH8Gt@w~&K>(F6zIRuNBtn|Lbd}FB6%dg_r34^xTjZpooA~n zpRFj0vfAEl?ZX|xdGDjUaP9p4#qV(YIN$xYRFqFXtXz)N#?8nzWqZ*N#v`C>j4h(Ej~zg&kBBU*6FgdW9CKb z##iXcgaARxB}J2Tn3ZJWt5aSA>*}?kqA5fk`|4)p5FG`3XiF3vIl8Gaj4^UNow|-~ zhTdajVaqhI2R{vCVmrik(fm-sT@l4(zqK=W22Z{^kTNHRF&>Zgv8{`uZN71Z_TOAa8nK~VcxswGWp7p2@jfiUI?S&A;Z zr#UzFc_~H(TH!TS!Khtz-1yFv%h@E+1Yn;4Lb-uR_%< z4Lh3UbWNL>4l(UV*d-Xxpn-^z`1CT=#$C%M*du215)^tSqLSVSju?qWkx~daNok)O zdDd4^okp@HhN1w+pw6DE2m;d1N=Y-u+bv&(nDr!yNbJQ89?JY+%=EnpU{5|E5S_ zYtiAYd8!RpwFKEsN+{Opd`BGc@8_#wf~H+SD66{LS6Nk79;x%!$&w7bUj#GS@qh#A zHRYbcN)gL3E%;_1vUOuS8CQuf>B;rd->-Pfq%Uc|+en|Ml?`at=P)Y`wF<_lhRUoc z#{cUz{dQphE#NTKs%1S-;h~<$JOkHT%**L!QD^EV&te0~mxqCC!(?{@xq4gUa&(%$a0etX(`@gL@Z>`(vz literal 0 HcmV?d00001 diff --git a/src/main/resources/backgrounds/b4.png b/src/main/resources/backgrounds/b4.png new file mode 100644 index 0000000000000000000000000000000000000000..0532849a5a21ae128a90a7643944ea03c19c018a GIT binary patch literal 1615 zcmbVMO>f*p7&f9vrKnsgp^DSU6%`0Axc+jDp4upnc3Y@)*jcM z&2Dn!#(@(j5QmB%z#rhi&)`^zKhR5m0I&DMO3931Q8^gF^-Zd0x@ zVbAjt(1!6~dtOp@Dm2Q(NEWgA>aV{|BzbH;>83a>dtxm2FDmi);$XloPFcjw?T6^W zoB%;CG)41#Qq*J~n+sk7<7V11(E_4RV{@l5h>p`k)Kir}f$drhV~m2xreWZ_S@aF^ zu;)5Bg4@EF_yO@lw7N_%SMeb^N*=9@!C!2SwJwR{%w{ut=G&?oIc^jIf#-Oh1qiD? zFEpK7MSXA0kcgU9veZ%)s9~g;n(EjDsY@U7vZXERRh%GV&YYHxYvabJ1(35AS5B+R z!Z>G+n21~yx(2M+dyJsNtUgJ$kdX=DBRRg6a>r5T4B_q$bG5Yd}EV!E0UtQWD5~@XPLe@MB2bLEM zT%Wie>Ef?(NHAVPQ^n=*{3_JP1J5ToB7Wxz6l#W3P2URUj0{zkQ*c=3bR?Xz7@4RI zBk8G$s=zR)b1%>Ld%eR-4dnzD5~a}|l?7DY7Yn6nO8P~uX~9H4iA{)$EhQ&Gw~M`u zcdacRY*{Sp2#aoQL4^*(h`JGX!=YI3PZXOr*KU!X&IqFQ){=9s6JOJ_H%ouFmlg@taxNgSJ+|38o?^&08`zcVG$e~is~(sgzumR6_85DT z-9#J;7nBPUSA+zA079I&TZO_=h_j|whF>hyi z>C(*M*!|(@&4euk2M2WAx+I^;}QRr(|D**}81?Wcg?F?48mzsQg zoqM5n;kVn8Xbq!1Bgb>yB z3^wOFR?9o1+Qha@;^AK>ggP#D=G5^|Lw8x&rYp6JV`sSYwXT#YHO+p%Z}c5QWF6D; zJS4D9+tv}G=Ua(n13k%SCk!>nc^0KI5{X(cvXVaCh>OHIol zMNC6r$V*(hk@beoLvBJ3Vn}3;SgXXQT_Hu@6>p&~KksvZxebES$5btGT)N2R;wFYM zPRLYr-rP#TT!CC{WE>VZQRZwBO-dIsU{YjFA$pTaEl(t?wqp?W6ZIQ(c z1CC8|Av-L}qhojI+F-tz30Ym+b}m2hJJ zV;14jV!e;%DGprG60{!WSgft_iCBf}<5e$Ghh0D!FS^^;cu`hB1*6x|lni`W1S9)? z7m)NF`OVEX#R{n{pyUg47w#+r1mrRh-pcqgHL!cV0fPe(@Vo{&@0e z@6T`kzV_ecBRBtg@pSk0i4zYi^M|@K&ky|kiTw2+rQH5|?8+VG7vt-@OU1-*EH2d^ Ie}C=PYlIT_NB{r; literal 0 HcmV?d00001 diff --git a/src/main/resources/backgrounds/b8.png b/src/main/resources/backgrounds/b8.png new file mode 100644 index 0000000000000000000000000000000000000000..87d0de7057c4e24b7e088c945f51ab455fd99cc6 GIT binary patch literal 1612 zcmbVMO^f727|wzV7-&( zGu<;sZ(aotioe0L;K74G#GfGe3w+ZbVMcmC*pQD()l<*=ydU-caCG?O-ktaFSeA8f zG)#`+y93X=J8!}J_iumt06uS5!>=35dgp%o+_HZ8^*zhF{kIyQn$z?t<+^Z~(3x}= zMFnWfdiZ!zF@7cu%H%|qG5+U|KQU4w#wWd$q}4!9)$pp8&#n%~{OXKH0zZC)9xf;l z6w)xXC}w3t7cpM)QW&?>E=Eg;Ig9arYY?5LM`)mHi9*M-IUxjvk;D37;APRL$S1z% zk_c{_5E_Iu=%e+8!CWnJdYpW*HU@t&o*Gk8*PYMj&OC5*J#oD#0s`OleH##Vb6Fa; zu*>Gbh9Qv+uT^D~E>X+KGJS4h3{qD<6jeuCHtRS+#M}j|T+bn`PfMWS9j-dBXG`OP zyK*KAS(*m0UWcuw+URDgUqIb0-!K5NP1DZCRxL%*nP|+x1-P*e$X0YSzO1BsEE|1Z zb9rz9G9R?wRCG{FW^_H)dbY{b(MB@z1BalGPE;xMy!rUr2AMD;V+>jIZ4%mkIQ9bS z`E-|jLi&`D6*SdC<(D_10U7%NB@y+b8&Iej!3=vPSa6!_x?te2D%eE2RXM?^3nLxq znXbVwsPnGRj|PLITIXs83yC)92~#Ch-G@u1X-Y?BV_3=MD2Xw|#ZgMoy*%s*o(r1< zfv}mk>)Tlv?AgK#`<}$$oBhpx0sHeTmm(K?Hs@K-_WQe$&31cPOOpAr zm*=t!~^_QpKzp=`^B4pjpQyClIYymYn+{@eR9px%Agd-Ztr5O7u;nufxixtei;b zr!H=taofhTN{ajcd@a|mHK`38I$t~NdM^BQU0aqwT|-aC=1T$n{QfA}AMgCkMfLo% fTTk!a{p-ZChW}dMKs*1j?f0XD!{oQmpMU=!Y?$%3 literal 0 HcmV?d00001 diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 0f7515f..c89e4a1 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,3 +1,6 @@ redis: host: "127.0.0.1" - port: 6379 \ No newline at end of file + port: 6379 + +resource_pack: "http://127.0.0.1:8080" +resource_pack_api: "your_api_key_here" diff --git a/src/main/resources/paper-plugin.yml b/src/main/resources/paper-plugin.yml deleted file mode 100644 index d2e2db9..0000000 --- a/src/main/resources/paper-plugin.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: $name -version: $version -main: me.unurled.sacredrealms.sr.SR -api-version: "$apiVersion" -description: $description -loader: me.unurled.sacredrealms.sr.SRLoader -bootstrapper: me.unurled.sacredrealms.sr.SRBootstrap - -permissions: - sr.*: - description: Gives access to all Sacred Realms permissions - default: op - children: - sr.tutorial: true - sr.spawn: true - sr.attributes: true - sr.clientbuild: true - sr.level: true - sr.entitytype: true - sr.spawn-entity: true - sr.admin.item: true - sr.resetadventure: true - sr.treasure.manage: true - sr.difficulty.*: true - sr.tutorial: - default: not op - description: When the player joins and doesn't have finished the tutorial. - sr.spawn: - default: not op - description: When the player have discovered the spawn. - sr.attributes: - default: op - description: When the player has permission for the command /attributes set - sr.clientbuild: - default: op - description: When the player has permission for the command /clientbuild - sr.level: - default: op - description: When the player has permission for the command /level - sr.entitytype: - default: op - description: When the player has permission for the command /entitytype - sr.spawn-entity: - default: op - description: When the player has permission for the command /spawn - sr.admin.item: - default: op - description: When the player has permission for the command /item - sr.resetadventure: - default: op - description: When the player has permission for the command /resetadventure - sr.treasure.manage: - default: op - description: When the player has permission for the command /treasure create - sr.difficulty.*: - description: gives permission for all difficulty commands - children: - sr.difficulty.manage: true - sr.difficulty.self: true \ No newline at end of file diff --git a/src/main/resources/rp/assets.ajmeta b/src/main/resources/rp/assets.ajmeta new file mode 100644 index 0000000..aa69089 --- /dev/null +++ b/src/main/resources/rp/assets.ajmeta @@ -0,0 +1 @@ +{"sr":{"files":["assets/animated_java/models/empty.json","assets/animated_java/models/item/sr/top.json","assets/animated_java/models/item/sr/bottom.json","assets/animated_java/textures/item/sr/treasure_chest_top.png","assets/animated_java/textures/item/sr/treasure_chest_bottom.png","assets/animated_java/textures/item/sr/treasure_chest_metal.png","assets/animated_java/textures/item/transparent.png"]}} diff --git a/src/main/resources/rp/assets/animated_java/models/empty.json b/src/main/resources/rp/assets/animated_java/models/empty.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/src/main/resources/rp/assets/animated_java/models/empty.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/src/main/resources/rp/assets/animated_java/models/item/sr/bottom.json b/src/main/resources/rp/assets/animated_java/models/item/sr/bottom.json new file mode 100644 index 0000000..e366e93 --- /dev/null +++ b/src/main/resources/rp/assets/animated_java/models/item/sr/bottom.json @@ -0,0 +1 @@ +{"textures":{"1":"animated_java:item/sr/treasure_chest_bottom"},"elements":[{"from":[2,8,2],"to":[14,16,14],"faces":{"north":{"uv":[6,0,12,4],"texture":"#1"},"east":{"uv":[6,4,12,8],"texture":"#1"},"south":{"uv":[6,8,12,12],"texture":"#1"},"west":{"uv":[0,12,6,16],"texture":"#1"},"up":{"uv":[6,6,0,0],"texture":"#1"},"down":{"uv":[6,6,0,12],"texture":"#1"}}}],"display":{"head":{"rotation":[0,180,0]}}} diff --git a/src/main/resources/rp/assets/animated_java/models/item/sr/top.json b/src/main/resources/rp/assets/animated_java/models/item/sr/top.json new file mode 100644 index 0000000..5a32706 --- /dev/null +++ b/src/main/resources/rp/assets/animated_java/models/item/sr/top.json @@ -0,0 +1 @@ +{"textures":{"0":"animated_java:item/sr/treasure_chest_top","2":"animated_java:item/sr/treasure_chest_metal"},"elements":[{"from":[7,6,20],"to":[9,10,22],"rotation":{"angle":0,"axis":"y","origin":[8,6,21]},"faces":{"north":{"uv":[0,0,2,4],"texture":"#2"},"east":{"uv":[2,0,4,4],"texture":"#2"},"south":{"uv":[0,4,2,8],"texture":"#2"},"west":{"uv":[4,0,6,4],"texture":"#2"},"up":{"uv":[4,6,2,4],"texture":"#2"},"down":{"uv":[6,4,4,6],"texture":"#2"}}},{"from":[2,8,8],"to":[14,12,20],"rotation":{"angle":0,"axis":"y","origin":[3,8,9]},"faces":{"north":{"uv":[6,0,12,2],"texture":"#0"},"east":{"uv":[6,2,12,4],"texture":"#0"},"south":{"uv":[6,4,12,6],"texture":"#0"},"west":{"uv":[6,6,12,8],"texture":"#0"},"up":{"uv":[6,6,0,0],"texture":"#0"},"down":{"uv":[6,6,0,12],"texture":"#0"}}}],"display":{"head":{"rotation":[0,180,0]}}} diff --git a/src/main/resources/rp/assets/animated_java/textures/item/sr/treasure_chest_bottom.png b/src/main/resources/rp/assets/animated_java/textures/item/sr/treasure_chest_bottom.png new file mode 100644 index 0000000000000000000000000000000000000000..bc0659bc100ecca14618206707baa17aee1d4ae7 GIT binary patch literal 309 zcmV-50m}Y~P)Px#?@2^KR9Hvlmq8AKFc3xCtqF+>R~`ZH;i0*Q9>J9h6BD+OfuN)b?f=7!L}g3+ z`}$|*2jck@y5RUUmk@LEQ3FsPvzo8-uD3400FO3r!+!UEF97%FXxyP%hH(Wzs`j4% z^V0mT14#X+H7--IIe<$JFt^G9sGHCNAPpyK3N!$vG=u?|nhL0S6l(ww$J`u14yST} zbzpM<1{g|0MsRAvB^OjVUb8A8d*sTPm5E3W8#{p)n*tJjz7}1g-qa|yH{{qXV zQUh2ul^Q@cmLQO>UImbD)_h=?0;H~d+=28qZUBsJ)??TMzf1Zd0Zvy400000NkvXX Hu0mjf<)wG6 literal 0 HcmV?d00001 diff --git a/src/main/resources/rp/assets/animated_java/textures/item/sr/treasure_chest_metal.png b/src/main/resources/rp/assets/animated_java/textures/item/sr/treasure_chest_metal.png new file mode 100644 index 0000000000000000000000000000000000000000..77989f6bd4e5d12d82a7cfa08e2c0f9a5158ae43 GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|%spKkLoEE0 z?`%vyUeBg;;PByQ!PyqOBp$C|!QkoY=d#Wzp$P#0040F{ literal 0 HcmV?d00001 diff --git a/src/main/resources/rp/assets/animated_java/textures/item/sr/treasure_chest_top.png b/src/main/resources/rp/assets/animated_java/textures/item/sr/treasure_chest_top.png new file mode 100644 index 0000000000000000000000000000000000000000..5be2dc0685e21fe5b49b4c48402e00350556b22b GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}hdo^!Ln7Rh zOD?DVIPc)R;k6dCvfX1zN4^V_4y|Te_y3l0N3>_4{}$$ZHEj}lhq_-$q!iAwVo0&v zrF`M10s91@WS4dZ!!|xC_6sV!6NH$U-!UY~a0fdFNl9FAvC!D0xNyPJNK1zkNnTGn zy_ly&IC$v$$~b(@;rMj4J^#mFr30-K + +uniform sampler2D Sampler0; +uniform vec4 ColorModulator; +uniform float FogStart,FogEnd; +uniform vec4 FogColor; +uniform float GameTime; + +in float vertexDistance; +in vec4 vertexColor; +in vec2 texCoord0; +in float depthLevel; +%SHADER_0% +out vec4 fragColor; + +void main() { + vec4 texColor = texture(Sampler0, texCoord0); + vec4 color = texColor * vertexColor * ColorModulator;%SHADER_1% + if (color.a < 0.1) { + discard; + } + if (texColor.a == 254.0/255.0) { + if (depthLevel == 1000.00) { + discard; + } else { + color = vec4(texColor.rgb, 1.0) * vertexColor * ColorModulator; + } + } + fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor); +} \ No newline at end of file diff --git a/src/main/resources/rp/assets/minecraft/shaders/core/rendertype_text.json b/src/main/resources/rp/assets/minecraft/shaders/core/rendertype_text.json new file mode 100644 index 0000000..df3a26e --- /dev/null +++ b/src/main/resources/rp/assets/minecraft/shaders/core/rendertype_text.json @@ -0,0 +1,28 @@ +{ + "blend": { + "func": "add", + "srcrgb": "srcalpha", + "dstrgb": "1-srcalpha" + }, + "vertex": "rendertype_text", + "fragment": "rendertype_text", + "attributes": [ + "Position", + "Color", + "UV0", + "UV2" + ], + "samplers": [ + { "name": "Sampler0" }, + { "name": "Sampler2" } + ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, + { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, + { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "GameTime", "type": "float", "count": 1, "values": [ 1.0 ] }, + { "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] } + ] +} \ No newline at end of file diff --git a/src/main/resources/rp/assets/minecraft/shaders/core/rendertype_text.vsh b/src/main/resources/rp/assets/minecraft/shaders/core/rendertype_text.vsh new file mode 100644 index 0000000..ddd3bba --- /dev/null +++ b/src/main/resources/rp/assets/minecraft/shaders/core/rendertype_text.vsh @@ -0,0 +1,47 @@ +#version 150 +#moj_import + +in vec3 Position; +in vec4 Color; +in vec2 UV0; +in ivec2 UV2; + +uniform sampler2D Sampler2; +uniform mat4 ModelViewMat; +uniform mat4 ProjMat; +uniform float GameTime; +uniform int FogShape; +uniform vec2 ScreenSize; + +out float vertexDistance; +out vec4 vertexColor; +out vec2 texCoord0; +out float depthLevel; +//% SHADER_0 % +void main() { + vec4 vertex = vec4(Position, 1.0); + vertexDistance = fog_distance(Position, FogShape); + depthLevel = Position.z; + texCoord0 = UV0; + if(Color.xyz == vec3(255., 254., 253.) / 255.) {\ + n + + vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0); + vertex.y += 1; + vertex.x += 1; + gl_Position = ProjMat * ModelViewMat * vertex; + } else if(Color.xyz == vec3(254., 254., 254.) / 255.) { + vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0); + vertex.z *= 1.001; + vertex.x *= 1.001; + gl_Position = ProjMat * ModelViewMat * vertex; + } else if(Color.xyz == vec3(253., 254., 254.) / 255.) { + vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0); + vertex.z *= 1.001001; + vertex.x *= 1.001001; + gl_Position = ProjMat * ModelViewMat * vertex; + } else { + vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0); + gl_Position = ProjMat * ModelViewMat * vertex; + } +//% SHADER_1 % % SHADER_2 % % SHADER_3 % +} \ No newline at end of file diff --git a/src/main/resources/rp/assets/minecraft/textures/dark.png b/src/main/resources/rp/assets/minecraft/textures/dark.png new file mode 100644 index 0000000000000000000000000000000000000000..96b520575acc929514a6a20140385ab125032337 GIT binary patch literal 875 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G$6%U;1OBOz`%C|gc+x5^GP!> zFw1(nIEGZrd3(i>kwJmyz=p>9^9srC!mN9%(w^~7DA~^3V9UmkIEqF?U<8JMe2Eg% V|H&y~{lMJA;OXk;vd$@?2>_`HgFFBL literal 0 HcmV?d00001 diff --git a/src/main/resources/rp/assets/minecraft/textures/font/bg.png b/src/main/resources/rp/assets/minecraft/textures/font/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..f710ffdae17b0509cbdf87aed156f8835baab156 GIT binary patch literal 591 zcmV-V0EX>4Tx04R}tkv&MmKpe$iQ^g{c4t5Yx$WWc^qN0LJtwIqhlv<%x2a`*`ph-iL z;^HW{799LotU9+0Yt2!bCV&JIqBE>hzEl0u6Z503ls?%w0>9UwF+Of|b=fT~$W zIuRGMxmB^}6#)z*gjq~W%+%*nsU$qd*FAiEy^HcJ?{j~SJ|%B5z$X&Nm~L3a8^lwa zmd<&fILu0tLVQjQ9~IOScuZ9kzyiE`*9EdpyN-GOD0zt zj2sK7LWSh`!T;cQw`O5t+)WC`f$kUE{ulvzc7bNyw!e>UyLkfmpMfi_?XNa~nNQN| zZ7p&Hgtmc;>$WEE0hc?#z>_W+k|X(P3WWmjen#Jv1Nv`)-Zi(k);>-jfDCn&ya5gl zfzcvmuY0^Z(AnF+XIlOJ06b`N_x~_7@Bjb+24YJ`L;(K){{a7>y{D4^000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j~bL5EUGoQtsyf000?uMObu0Z*6U5Zgc=ca%Ew3 zWn>_CX>@2HM@dakSAh-}0000cNklYH7?OYymxP_2-BB3((4j-t*wiplV)Llx dA)|SS0ssr31QMBBwQ&Fd002ovPDHLkV1hD)@EQOB literal 0 HcmV?d00001 diff --git a/src/main/resources/rp/assets/minecraft/textures/font/left_close_bg.png b/src/main/resources/rp/assets/minecraft/textures/font/left_close_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..2fba8c59f8b52fcc73543b6cba1afac1a6365609 GIT binary patch literal 597 zcmV-b0;>IqP)EX>4Tx04R}tkv&MmKpe$iQ^g{c4t5Yx$WWc^qN0LJtwIqhlv<%x2a`*`ph-iL z;^HW{799LotU9+0Yt2!bCV&JIqBE>hzEl0u6Z503ls?%w0>9UwF+Of|b=fT~$W zIuRGMxmB^}6#)z*gjq~W%+%*nsU$qd*FAiEy^HcJ?{j~SJ|%B5z$X&Nm~L3a8^lwa zmd<&fILu0tLVQjQ9~IOScuZ9kzyiE`*9EdpyN-GOD0zt zj2sK7LWSh`!T;cQw`O5t+)WC`f$kUE{ulvzc7bNyw!e>UyLkfmpMfi_?XNa~nNQN| zZ7p&Hgtmc;>$WEE0hc?#z>_W+k|X(P3WWmjen#Jv1Nv`)-Zi(k);>-jfDCn&ya5gl zfzcvmuY0^Z(AnF+XIlOJ06b`N_x~_7@Bjb+24YJ`L;(K){{a7>y{D4^000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j~bL5ECPNZe&~l000?uMObu0Z*6U5Zgc=ca%Ew3 zWn>_CX>@2HM@dakSAh-}0000iNklYH7=FOc&hDt4o!wDf@=W+N5;B~L5|@o? jrk3VIhYndY3{L<6F-8XuvK;*q00000NkvXXu0mjfar^KI literal 0 HcmV?d00001 diff --git a/src/main/resources/rp/assets/minecraft/textures/font/right_close_bg.png b/src/main/resources/rp/assets/minecraft/textures/font/right_close_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..80c06686762535797fd347ce89470e1114395d4a GIT binary patch literal 596 zcmV-a0;~OrP)EX>4Tx04R}tkv&MmKpe$iQ^g{c4t5Yx$WWc^qN0LJtwIqhlv<%x2a`*`ph-iL z;^HW{799LotU9+0Yt2!bCV&JIqBE>hzEl0u6Z503ls?%w0>9UwF+Of|b=fT~$W zIuRGMxmB^}6#)z*gjq~W%+%*nsU$qd*FAiEy^HcJ?{j~SJ|%B5z$X&Nm~L3a8^lwa zmd<&fILu0tLVQjQ9~IOScuZ9kzyiE`*9EdpyN-GOD0zt zj2sK7LWSh`!T;cQw`O5t+)WC`f$kUE{ulvzc7bNyw!e>UyLkfmpMfi_?XNa~nNQN| zZ7p&Hgtmc;>$WEE0hc?#z>_W+k|X(P3WWmjen#Jv1Nv`)-Zi(k);>-jfDCn&ya5gl zfzcvmuY0^Z(AnF+XIlOJ06b`N_x~_7@Bjb+24YJ`L;(K){{a7>y{D4^000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j~bL5ETXgIYuV{000?uMObu0Z*6U5Zgc=ca%Ew3 zWn>_CX>@2HM@dakSAh-}0000hNklYH7+%26&hDt4o!wDF?qI literal 0 HcmV?d00001 diff --git a/src/main/resources/rp/assets/minecraft/textures/gui/icons.png b/src/main/resources/rp/assets/minecraft/textures/gui/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..ae0f1742358568b2076a8b1f109dce26c89b0d74 GIT binary patch literal 1278 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|GzJDH#>|k2k_cZPtK|G#y~LFK zq*T3%+ybCH1A~oyML}Y6c4~=2Qfhi;o~_dR-TRdkGE;1o!cBb*d<&dYGcrA@ic*8C z{6dnevXd=SlGK4GRO#s87`^C$wiq3C7Jno3LrBRlk!VTY?YLN@(NJHOES|C zrsowyy$=iseUOa4p`L+0vhvi5%rw^sxO0*-3ljCR3iLt37-|zsf!>C?8^vU}YM=^; z=^(qTJaZG%Q+-nN(n~UIm5fZy!1f|!!ZTBlB_J-b0;(_e%}+_qfqDyQBv=f{)dvNo zJ~S+Wk)jWe6(9#5uc>*!$SeZJTGfMP%D|M-ly zF|l;wZf|Bsfp+`P3X3LkL`6if1g>_eaWLq0ZE|zeoy6gmJIR<|vH!82W7FCM=>UP; z8qr_k8QKxtO^Y~^QWMu^EPgo0*vaLLj`i=i`RQ$UBR9Bg_VU}#>!cTd<

0YP)R@ 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