diff --git a/api/build.gradle b/api/build.gradle index 6da058fd..2cd2d84e 100644 --- a/api/build.gradle +++ b/api/build.gradle @@ -1,4 +1,5 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import io.papermc.paperweight.userdev.ReobfArtifactConfiguration plugins { id 'net.minecrell.plugin-yml.bukkit' @@ -7,28 +8,37 @@ plugins { group = 'org.parallelmc' -version = '4.2.0' +version = '4.4.0' description = 'A set of utilities and features for use on the Parallel Minecraft server' java { - toolchain.languageVersion.set(JavaLanguageVersion.of(17)) + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) } +paperweight.reobfArtifactConfiguration = ReobfArtifactConfiguration.getMOJANG_PRODUCTION() + compileJava.options.encoding = "UTF-8" compileTestJava.options.encoding = "UTF-8" processResources.filteringCharset = "UTF-8" +repositories { + mavenCentral() + maven { url = "https://repo.papermc.io/repository/maven-public/" } +} dependencies { + //compileOnly 'io.papermc.paper:paper-api:1.20.6-R0.1-SNAPSHOT' + paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.21.1-R0.1-SNAPSHOT") + implementation 'org.jetbrains:annotations:24.0.1' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2' - implementation 'net.dv8tion:JDA:5.0.0-beta.10' - compileOnly 'me.clip:placeholderapi:2.11.3' + implementation 'net.dv8tion:JDA:5.0.0-beta.24' + compileOnly 'me.clip:placeholderapi:2.11.6' compileOnly fileTree('libs') { include '*.jar' } - compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.8' - implementation 'mysql:mysql-connector-java:8.0.33' - compileOnly 'com.comphenix.protocol:ProtocolLib:5.0.0' - compileOnly 'dev.esophose:playerparticles:8.4' + compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.10' + implementation 'com.mysql:mysql-connector-j:8.3.0' + compileOnly 'com.comphenix.protocol:ProtocolLib:5.3.0' + compileOnly 'dev.esophose:playerparticles:8.6' implementation 'org.reflections:reflections:0.10.2' } @@ -39,17 +49,17 @@ artifacts { tasks { assemble { - dependsOn(reobfJar) + //dependsOn(reobfJar) } build { - dependsOn(reobfJar) + //dependsOn(reobfJar) } compileJava { // Set the release flag. This configures what version bytecode the compiler will emit, as well as what JDK APIs are usable. // See https://openjdk.java.net/jeps/247 for more information. - options.release.set(17) + options.release.set(21) } shadowJar { @@ -67,7 +77,7 @@ bukkit { main = 'parallelmc.parallelutils.ParallelUtils' - apiVersion = '1.20' + apiVersion = '1.20.5' depend = ['PlaceholderAPI', 'ProtocolLib'] softDepend = ['Multiverse-Core', 'FractalForest', 'WorldGuard', 'VoteParty', 'PlayerParticles', 'ProNouns'] @@ -279,6 +289,20 @@ bukkit { description = 'Opens Maggies Shop' usage = '/openmaggieshop (player)' } + points { + description = 'View your advancement points' + usage = '/points' + } + recalculatepoints { + description = 'Recalculates advancement points for all players' + usage = '/recalculatepoints' + permissionMessage = 'You do not have permission.' + permission = 'parallelutils.recalculatepoints' + } + openpointsredemption { + description = 'Opens the point redemption shop for a player' + usage = '/openpointsredemption (player)' + } } permissions { @@ -471,5 +495,12 @@ bukkit { description = 'Gives access to ChestShop debug commands' setDefault('FALSE') } + 'parallelutils.resources.unenforced' { + description = 'Prevents being kicked from not accepting the resource pack' + setDefault('FALSE') + } + 'parallelutils.recalculatepoints' { + description = 'Gives access to the /recalculatepoints dev command' + } } } \ No newline at end of file diff --git a/api/src/main/java/parallelmc/parallelutils/Constants.java b/api/src/main/java/parallelmc/parallelutils/Constants.java index a6bf0d7a..ec72b3c4 100644 --- a/api/src/main/java/parallelmc/parallelutils/Constants.java +++ b/api/src/main/java/parallelmc/parallelutils/Constants.java @@ -1,8 +1,12 @@ package parallelmc.parallelutils; +import java.util.ArrayList; + public class Constants { - public static final Version VERSION = new Version(4, 2, 0); + public static final Version VERSION = new Version(4, 4, 0); public static final String PLUGIN_NAME = "ParallelUtils"; public static final String DEFAULT_WORLD = "world2"; + + public static final String[] OVERWORLD_TYPE_WORLDS = {"world", "world2", "world_skyteaser"}; } diff --git a/api/src/main/java/parallelmc/parallelutils/ParallelUtils.java b/api/src/main/java/parallelmc/parallelutils/ParallelUtils.java index 774304f5..0c888d3b 100644 --- a/api/src/main/java/parallelmc/parallelutils/ParallelUtils.java +++ b/api/src/main/java/parallelmc/parallelutils/ParallelUtils.java @@ -77,8 +77,8 @@ public void onEnable() { config = this.getConfig(); config.options().copyDefaults(true); - config.options().parseComments(true); - config.options().setHeader(List.of(HEADER)); + //config.options().parseComments(true); + config.options().header(HEADER); // Read config this.saveDefaultConfig(); diff --git a/api/src/main/java/parallelmc/parallelutils/commands/Commands.java b/api/src/main/java/parallelmc/parallelutils/commands/Commands.java index d1feadec..0ac3d7fc 100644 --- a/api/src/main/java/parallelmc/parallelutils/commands/Commands.java +++ b/api/src/main/java/parallelmc/parallelutils/commands/Commands.java @@ -5,7 +5,7 @@ import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.command.*; -import org.bukkit.craftbukkit.v1_20_R1.command.ServerCommandSender; +import org.bukkit.craftbukkit.command.ServerCommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -220,9 +220,9 @@ public static Location convertLocation(CommandSender sender, String sx, String s */ public static List getTargetedBlockTabHelper(@NotNull Player player, int depth) { ArrayList list = new ArrayList<>(); - Block targetedBlock = player.getTargetBlockExact(5); + Block targetedBlock = player.getTargetBlock(null,5); - if (targetedBlock != null && targetedBlock.isSolid()) { + if (targetedBlock != null && !targetedBlock.isEmpty()) { // Autofill targeted coords if (depth >= 1) { list.add(String.format("%d", targetedBlock.getZ())); diff --git a/api/src/main/resources/config.yml b/api/src/main/resources/config.yml index 0b115ee1..6a853a2a 100644 --- a/api/src/main/resources/config.yml +++ b/api/src/main/resources/config.yml @@ -76,4 +76,6 @@ announcements: # Bits and Bobs disable-ender-chests: false -prevent-spawner-mining: false \ No newline at end of file +prevent-spawner-mining: false +enable-calling-bell: true +enable-sweethearts: true diff --git a/build.gradle b/build.gradle index 498c0d63..40ca693a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,18 +1,21 @@ +import io.papermc.paperweight.userdev.ReobfArtifactConfiguration + plugins { id 'java' - id 'com.github.johnrengelman.shadow' version '8.1.1' - id("io.papermc.paperweight.userdev") version "1.5.5" - id "xyz.jpenilla.run-paper" version "2.1.0" + id 'io.github.goooler.shadow' version '8.1.7' + id "io.papermc.paperweight.userdev" version "1.7.1" + id "xyz.jpenilla.run-paper" version "2.3.0" id 'net.minecrell.plugin-yml.bukkit' version '0.6.0' } -dependencies { - paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.20.1-R0.1-SNAPSHOT") +java { + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) } +paperweight.reobfArtifactConfiguration = ReobfArtifactConfiguration.getMOJANG_PRODUCTION() -java { - toolchain.languageVersion.set(JavaLanguageVersion.of(17)) +dependencies { + paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.21.1-R0.1-SNAPSHOT") } compileJava.options.encoding = "UTF-8" @@ -21,12 +24,12 @@ processResources.filteringCharset = "UTF-8" allprojects { apply plugin: 'java' - apply plugin: 'com.github.johnrengelman.shadow' + apply plugin: 'io.github.goooler.shadow' apply plugin: "io.papermc.paperweight.userdev" repositories { mavenCentral() - maven { url = "https://papermc.io/repo/repository/maven-public/" } + maven { url = "https://repo.papermc.io/repository/maven-public/" } maven { url = 'https://oss.sonatype.org/content/repositories/snapshots/' } maven { url = "https://maven.enginehub.org/repo/" } maven { url = 'https://repo.md-5.net/content/groups/public/' } @@ -36,15 +39,15 @@ allprojects { maven { url = 'https://repo.codemc.org/repository/maven-public/' } maven { url = 'https://repo.rosewooddev.io/repository/public/' } maven { url = 'https://jitpack.io' } - } dependencies { - paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.20.1-R0.1-SNAPSHOT") + paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.21.1-R0.1-SNAPSHOT") + //paperweight.paperDevBundle("1.20.6-R0.1-SNAPSHOT") - compileOnly "io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT" + //compileOnly "io.papermc.paper:paper-api:1.20.6-R0.1-SNAPSHOT" compileOnly 'net.luckperms:api:5.4' - compileOnly 'dev.esophose:playerparticles:8.4' - compileOnly "com.github.MilkBowl:VaultAPI:1.7" + compileOnly 'dev.esophose:playerparticles:8.6' + compileOnly "com.github.MilkBowl:VaultAPI:1.7.1" } } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fae08049..48c0a02c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/modules/build.gradle b/modules/build.gradle index 33d6254d..e9b72011 100644 --- a/modules/build.gradle +++ b/modules/build.gradle @@ -1,14 +1,17 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import io.papermc.paperweight.tasks.RemapJar +import io.papermc.paperweight.userdev.ReobfArtifactConfiguration group = 'org.parallelmc' java { - toolchain.languageVersion.set(JavaLanguageVersion.of(17)) + toolchain.languageVersion.set(JavaLanguageVersion.of(21)) } +paperweight.reobfArtifactConfiguration = ReobfArtifactConfiguration.getMOJANG_PRODUCTION() + compileJava.options.encoding = "UTF-8" compileTestJava.options.encoding = "UTF-8" processResources.filteringCharset = "UTF-8" @@ -19,14 +22,14 @@ dependencies { compileOnly 'org.jetbrains:annotations:24.0.1' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.2' - compileOnly 'net.dv8tion:JDA:5.0.0-beta.10' - compileOnly 'me.clip:placeholderapi:2.11.3' + compileOnly 'net.dv8tion:JDA:5.0.0-beta.24' + compileOnly 'me.clip:placeholderapi:2.11.6' compileOnly fileTree('libs') { include '*.jar' } - compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.8' - compileOnly 'mysql:mysql-connector-java:8.0.33' - compileOnly 'com.comphenix.protocol:ProtocolLib:5.0.0' - compileOnly 'dev.esophose:playerparticles:8.4' - compileOnly "io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT" + compileOnly 'com.sk89q.worldguard:worldguard-bukkit:7.0.10' + compileOnly 'com.mysql:mysql-connector-j:8.3.0' + compileOnly 'com.comphenix.protocol:ProtocolLib:5.3.0' + compileOnly 'dev.esophose:playerparticles:8.6' + compileOnly "io.papermc.paper:paper-api:1.21.1-R0.1-SNAPSHOT" } def names = [] @@ -63,7 +66,8 @@ names.each { mod -> def name = (String) "${mod}" exList.remove(name) - archiveClassifier.set("shadow-${mod}") + archiveBaseName.set("") + archiveClassifier.set("${mod}") from sourceSets.main.output @@ -74,23 +78,23 @@ names.each { mod -> // Figure out how to exclude META-INF } } - -names.each { mod -> - task "reobf-${mod}" (type: RemapJar) { - dependsOn "shadow-${mod}" - group = "ReobfMod" - description = "Remap the compiled module jar to Spigot's obfuscated runtime names" - - fromNamespace = reobfJar.fromNamespace - toNamespace = reobfJar.toNamespace - mappingsFile = reobfJar.mappingsFile - remapClasspath.from(reobfJar.remapClasspath.getFrom()) - remapper.from(reobfJar.project.configurations.named("remapper")) - remapperArgs = reobfJar.remapperArgs - - - inputJar.set(new File("$buildDir/libs/modules-shadow-${mod}.jar")) - //outputJar.set(new File("$buildDir/libs/${mod}-${project.version}.jar")) - outputJar.set(new File("$buildDir/libs/${mod}.jar")) - } -} \ No newline at end of file +// +//names.each { mod -> +// task "reobf-${mod}" (type: RemapJar) { +// dependsOn "shadow-${mod}" +// group = "ReobfMod" +// description = "Remap the compiled module jar to Spigot's obfuscated runtime names" +// +// fromNamespace = reobfJar.fromNamespace +// toNamespace = reobfJar.toNamespace +// mappingsFile = reobfJar.mappingsFile +// remapClasspath.from(reobfJar.remapClasspath.getFrom()) +// remapper.from(reobfJar.project.configurations.named("remapper")) +// remapperArgs = reobfJar.remapperArgs +// +// +// inputJar.set(new File("$buildDir/libs/modules-shadow-${mod}.jar")) +// //outputJar.set(new File("$buildDir/libs/${mod}-${project.version}.jar")) +// outputJar.set(new File("$buildDir/libs/${mod}.jar")) +// } +//} \ No newline at end of file diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/beehiveinspector/README.md b/modules/src/main/java/parallelmc/parallelutils/modules/beehiveinspector/README.md deleted file mode 100644 index 0fb63c94..00000000 --- a/modules/src/main/java/parallelmc/parallelutils/modules/beehiveinspector/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This is a plugin version of a datapack module made by Gamemode4. For licensing of this module, please refer to the included license file. -https://github.com/Gamemode4Dev/GM4_Datapacks \ No newline at end of file diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/BitsAndBobs.java b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/BitsAndBobs.java index 90029bbb..01b54521 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/BitsAndBobs.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/BitsAndBobs.java @@ -14,7 +14,6 @@ import parallelmc.parallelutils.modules.bitsandbobs.minimodules.togglepvp.TogglePvpCommand; import parallelmc.parallelutils.modules.bitsandbobs.minimodules.togglepvp.TogglePvpManager; -import java.net.URLClassLoader; import java.util.List; import java.util.logging.Level; @@ -62,6 +61,7 @@ public void onEnable() { manager.registerEvents(new OnPvp(), plugin); manager.registerEvents(new ShardLotto(), plugin); manager.registerEvents(new ChickenFeatherDrops(), plugin); + manager.registerEvents(new EntityTweaks(), plugin); if (config.getBoolean("disable-ender-chests", false)) { manager.registerEvents(new DisableEnderChest(), plugin); @@ -70,6 +70,18 @@ public void onEnable() { if (config.getBoolean("prevent-spawner-mining", false)) { manager.registerEvents(new PreventSpawnerMining(), plugin); } + + if (config.getBoolean("enable-ziprails", true)) { + manager.registerEvents(new Ziprails(), plugin); + } + + if (config.getBoolean("enable-calling-bell", true)) { + manager.registerEvents(new CallingBell(), plugin); + } + + if (config.getBoolean("enable-sweethearts", true)) { + manager.registerEvents(new Sweethearts(), plugin); + } } @Override diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/beehiveinspector/LICENSE.md b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/LICENSE.md similarity index 100% rename from modules/src/main/java/parallelmc/parallelutils/modules/beehiveinspector/LICENSE.md rename to modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/LICENSE.md diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/README.md b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/README.md new file mode 100644 index 00000000..e6a04d8d --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/README.md @@ -0,0 +1,8 @@ +The following Bits & Bobs modules are plugin versions of datapack modules made by Gamemode 4: + +- Beehive Inspector +- Calling Bell +- SweetHearts + +For licensing of these modules, please refer to the included license file. +https://github.com/Gamemode4Dev/GM4_Datapacks \ No newline at end of file diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/CallingBell.java b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/CallingBell.java new file mode 100644 index 00000000..4c50f0f6 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/CallingBell.java @@ -0,0 +1,151 @@ +package parallelmc.parallelutils.modules.bitsandbobs.minimodules; + +import org.bukkit.*; +import org.bukkit.advancement.Advancement; +import org.bukkit.advancement.AdvancementProgress; +import org.bukkit.block.Block; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import parallelmc.parallelutils.Constants; +import parallelmc.parallelutils.ParallelUtils; + +import java.util.*; +import java.util.logging.Level; + +public class CallingBell implements Listener { + + private static final HashMap> traderSummoners = new HashMap<>(); + private static final String[] OVERWORLD_TYPE_WORLDS = Constants.OVERWORLD_TYPE_WORLDS; + private static final int DAY_CHECK_INTERVAL = 1200; + + public CallingBell() { + PluginManager manager = Bukkit.getPluginManager(); + Plugin plugin = manager.getPlugin(Constants.PLUGIN_NAME); + if (plugin == null) { + ParallelUtils.log(Level.SEVERE, "Unable to enable CallingBell. Plugin " + Constants.PLUGIN_NAME + + " does not exist!"); + return; + } + + // If this minute-long timer is too long, we can reduce it + plugin.getServer().getScheduler().runTaskTimer(plugin, this::dailyListReset, 0L, DAY_CHECK_INTERVAL); + } + + @EventHandler + public void onPlayerRightClickBell(PlayerInteractEvent event) { + // Check if player clicked a bell holding an emerald + Player player = event.getPlayer(); + Block block = event.getClickedBlock(); + if (block == null) { + return; + } + + if (!event.getClickedBlock().getType().equals(Material.BELL)) { + return; + } + + PlayerInventory inventory = player.getInventory(); + if (!inventory.getItemInMainHand().getType().equals(Material.EMERALD) && + !inventory.getItemInOffHand().getType().equals(Material.EMERALD)) { + return; + } + + // Check if player right-clicked + if (!event.getAction().isRightClick()) { + return; + } + + // Check if player is in an overworld-type world + Location bellLocation = block.getLocation(); + World world = bellLocation.getWorld(); + if (!Arrays.asList(OVERWORLD_TYPE_WORLDS).contains(world.getName())) { + return; + } + + // Check if player hasn't summoned the villager yet in this world + // If they haven't, remove emerald and summon trader + // If they have, add them and their world into (or back into) the hashmap + UUID playerUUID = player.getUniqueId(); + ArrayList worldsOnCooldown = new ArrayList<>(); + if (traderSummoners.containsKey(playerUUID)) { + worldsOnCooldown = traderSummoners.get(playerUUID); + if (worldsOnCooldown.contains(world)) { + return; + } + } + worldsOnCooldown.add(world); + traderSummoners.put(playerUUID, worldsOnCooldown); + + inventory.remove(new ItemStack(Material.EMERALD, 1)); + + // This is done to center the spawning trader on the bell - it also overwrites the original location + bellLocation.add(0.5, 1, 0.5); + world.spawnEntity(bellLocation, EntityType.WANDERING_TRADER); + + // Add particles and sound + world.spawnParticle(Particle.HAPPY_VILLAGER,bellLocation.getX(), bellLocation.getY() + 0.5, + bellLocation.getZ(), 5, 0.3, 0.3, 0.3); + world.spawnParticle(Particle.LARGE_SMOKE, bellLocation.getX(), bellLocation.getY() + 1, + bellLocation.getZ(), 10, 0.25, 0.5, 0.25, 0); + world.playSound(bellLocation, Sound.ENTITY_WANDERING_TRADER_REAPPEARED, SoundCategory.NEUTRAL, + 1, 1); + + // Check/give advancement + awardAdvancement(player); + + } + + public void dailyListReset() { + ArrayList worldsToReset = new ArrayList<>(); + // Check each world + for (String worldString : OVERWORLD_TYPE_WORLDS) { + // Check if world is null + World world = Bukkit.getWorld(worldString); + if (world == null) { + ParallelUtils.log(Level.WARNING, "World " + worldString + + " is registered as an overworld-type world but the world is not loaded!"); + continue; + } + + // Perform day check on the world - if it's a new day within the interval, add the world to the + // worldsToReset list + long worldTime = world.getTime(); + if (worldTime >= 0 && worldTime < DAY_CHECK_INTERVAL) { + worldsToReset.add(world); + } + } + + // For each traderSummoners entry, remove all reset worlds from each player's worldsOnCooldown list + // If there are no worlds left in the list after that, remove the player from the traderSummoners hashmap + for (Map.Entry> entry : traderSummoners.entrySet()) { + UUID playerUUID = entry.getKey(); + ArrayList worldsOnCooldown = entry.getValue(); + worldsOnCooldown.removeAll(worldsToReset); + if (worldsOnCooldown.isEmpty()) { + traderSummoners.remove(playerUUID); + } else { + traderSummoners.put(playerUUID, worldsOnCooldown); + } + } + } + + public void awardAdvancement(Player player) { + Advancement a = Bukkit.getAdvancement(new NamespacedKey("platy", + "village/calling_bell")); + if (a != null) { + AdvancementProgress avp = player.getAdvancementProgress(a); + if (!avp.isDone()) { + for (String criteria : avp.getRemainingCriteria()) { + avp.awardCriteria(criteria); + } + } + } + } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/EntityTweaks.java b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/EntityTweaks.java new file mode 100644 index 00000000..0fe3eb53 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/EntityTweaks.java @@ -0,0 +1,26 @@ +package parallelmc.parallelutils.modules.bitsandbobs.minimodules; + +import org.bukkit.NamespacedKey; +import org.bukkit.attribute.Attribute; +import org.bukkit.attribute.AttributeInstance; +import org.bukkit.entity.Wolf; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.CreatureSpawnEvent; + +public class EntityTweaks implements Listener { + + @EventHandler + public void onShibaInuSpawn(CreatureSpawnEvent event) { + if (event.getEntity() instanceof Wolf wolf) { + // Check if the wolf is a shiba inu variant + if (wolf.getVariant().getKey().equals(NamespacedKey.fromString("parallel:shiba_inu"))) { + // Set scale to 0.8 + AttributeInstance scale = wolf.getAttribute(Attribute.GENERIC_SCALE); + if (scale != null) { + scale.setBaseValue(0.8D); + } + } + } + } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/KeepSpecialItems.java b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/KeepSpecialItems.java deleted file mode 100644 index b81a7c78..00000000 --- a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/KeepSpecialItems.java +++ /dev/null @@ -1,81 +0,0 @@ -package parallelmc.parallelutils.modules.bitsandbobs.minimodules; - -import net.minecraft.nbt.CompoundTag; -import org.bukkit.Material; -import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.player.PlayerRespawnEvent; -import org.bukkit.inventory.ItemStack; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.UUID; - - -public class KeepSpecialItems implements Listener { - - private final HashMap> specialItemsLogger = new HashMap<>(); - - private final String[] SPECIAL_ITEMS = {"CustomHat", "CustomTrophy"}; - - @EventHandler(priority = EventPriority.HIGHEST) - public void onPlayerDeath(PlayerDeathEvent event) { - // Creates an empty list for all items to be prevented from dropping - ArrayList preventedDrops = new ArrayList<>(); - - for (ItemStack item : event.getDrops()) { - // TODO: This probably doesn't need to be dependent on item type. Just checking the tag is a bit more futureproof - if (item.getType() == Material.PAPER) { - // TODO: Try to change this code to use item.getItemMeta().getPersistentDataContainer() - net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item); // does this even work lol - // Grabs the NMS items compound and checks if it's null - CompoundTag compound = (nmsItem.hasTag()) ? nmsItem.getTag() : new CompoundTag(); - - if (compound == null) continue; - - if (compound.contains("CustomHat")) { - preventedDrops.add(item); - } - } else if (item.getType() == Material.PLAYER_HEAD) { - net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item); // does this even work lol - // Grabs the NMS items compound and checks if it's null - CompoundTag compound = (nmsItem.hasTag()) ? nmsItem.getTag() : new CompoundTag(); - - if (compound == null) continue; - - if (compound.contains("CustomTrophy")) { - preventedDrops.add(item); - } - } - } - // If the list of special drops has items in it, add a new player entry to the logger hashmap and remove the - // dropped items - if (!preventedDrops.isEmpty()) { - // Gets the UUID of the player - UUID uuid = event.getEntity().getUniqueId(); - specialItemsLogger.put(uuid, preventedDrops); - - // Removes each special item from the original list of dropped items - for (ItemStack item : preventedDrops) { - event.getDrops().remove(item); - } - } - } - - @EventHandler - public void onPlayerRespawn(PlayerRespawnEvent event) { - // If the special items hashmap contains the player, then give them their special items back and remove them - // from the hashmap - if (specialItemsLogger.containsKey(event.getPlayer().getUniqueId())) { - ArrayList items = specialItemsLogger.get(event.getPlayer().getUniqueId()); - for (ItemStack item : items) { - event.getPlayer().getInventory().addItem(item); - } - // Removes the player from the hashmap - specialItemsLogger.remove(event.getPlayer().getUniqueId()); - } - } -} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/SpecialItems.java b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/SpecialItems.java index 071b3b59..37aeac8f 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/SpecialItems.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/SpecialItems.java @@ -1,10 +1,11 @@ package parallelmc.parallelutils.modules.bitsandbobs.minimodules; -import net.minecraft.nbt.CompoundTag; +import net.minecraft.core.component.DataComponents; +import net.minecraft.world.item.component.CustomData; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.NamespacedKey; -import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack; +import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; @@ -36,53 +37,40 @@ public class SpecialItems implements Listener { private final String[] SPECIAL_ITEMS = {"CustomHat", "CustomTrophy"}; + @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerDeath(PlayerDeathEvent event) { // Creates an empty list for all items to be prevented from dropping ArrayList preventedDrops = new ArrayList<>(); for (ItemStack item : event.getDrops()) { - // TODO: This probably doesn't need to be dependent on item type. Just checking the tag is a bit more futureproof - // TODO: The leather horse armor add is a band-aid because I'm lazy, will fix later - if (item.getType() == Material.PAPER || item.getType() == Material.LEATHER_HORSE_ARMOR) { - // TODO: Try to change this code to use item.getItemMeta().getPersistentDataContainer() - // TODO: Make this 2-part check not jank - hopefully transition entirely to persistentdatacontainer - NamespacedKey hatKey = new NamespacedKey(plugin, "CustomHat"); - PersistentDataContainer container = item.getItemMeta().getPersistentDataContainer(); - if (container.has(hatKey, PersistentDataType.INTEGER)) { - preventedDrops.add(item); - continue; - } - - net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item); // does this even work lol - // Grabs the NMS items compound and checks if it's null - CompoundTag compound = (nmsItem.hasTag()) ? nmsItem.getTag() : new CompoundTag(); - - if (compound == null) continue; - - if (compound.contains("CustomHat")) { - preventedDrops.add(item); - } - } else if (item.getType() == Material.PLAYER_HEAD) { - net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item); // does this even work lol - // Grabs the NMS items compound and checks if it's null - CompoundTag compound = (nmsItem.hasTag()) ? nmsItem.getTag() : new CompoundTag(); + // TODO: The NMS check down below won't be needed anymore if we start making every hat and trophy through PU + NamespacedKey hatKeyOld = new NamespacedKey(plugin, "CustomHat"); + NamespacedKey hatKey = new NamespacedKey(plugin, "ParallelHat"); + PersistentDataContainer container = item.getItemMeta().getPersistentDataContainer(); + if (container.has(hatKeyOld, PersistentDataType.INTEGER) || container.has(hatKey, PersistentDataType.STRING)) { + preventedDrops.add(item); + continue; + } - if (compound == null) continue; + net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item); // does this even work lol + // Grabs the custom data component (returns an empty component if it's null) + CustomData customData = nmsItem.getComponents().getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); - if (compound.contains("CustomTrophy")) { - preventedDrops.add(item); - } + if (customData.contains("CustomHat") || customData.contains("CustomTrophy")) { + preventedDrops.add(item); } + } + // If the list of special drops has items in it, add a new player entry to the logger hashmap and remove the - // dropped items + // items from dropping upon death if (!preventedDrops.isEmpty()) { // Gets the UUID of the player UUID uuid = event.getEntity().getUniqueId(); specialItemsLogger.put(uuid, preventedDrops); - // Removes each special item from the original list of dropped items + // Removes each special item from dropping upon death for (ItemStack item : preventedDrops) { event.getDrops().remove(item); } @@ -112,20 +100,28 @@ public void onPlayerCraft(PrepareItemCraftEvent event) { if (itemMeta == null) { // itemMeta could be null, so we have to check this continue; } - NamespacedKey hatKey = new NamespacedKey(plugin, "CustomHat"); + NamespacedKey hatKeyOld = new NamespacedKey(plugin, "CustomHat"); + NamespacedKey hatKey = new NamespacedKey(plugin, "ParallelHat"); + NamespacedKey dyeableKey = new NamespacedKey(plugin, "Dyeable"); NamespacedKey modifyKey = new NamespacedKey(plugin, "NoModify"); PersistentDataContainer container = itemMeta.getPersistentDataContainer(); - if (container.has(hatKey, PersistentDataType.INTEGER) || container.has(modifyKey, PersistentDataType.INTEGER)) { - ingredients.setResult(null); // Sets the crafting output to null if a CustomHat tag is found + // skip this item if it has the dyeableKey + if (container.has(dyeableKey, PersistentDataType.INTEGER)) { + continue; + } + // otherwise, if it's a hat or has the NoModify key, cancel the recipe + if (container.has(hatKeyOld, PersistentDataType.INTEGER) + || container.has(hatKey, PersistentDataType.STRING) + || container.has(modifyKey, PersistentDataType.INTEGER)) { + ingredients.setResult(null); break; } // Now we have to use NMS if the item doesn't have a PersistentDataContainer net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item); // does this even work lol - // Grabs the NMS items compound and checks if it's null - CompoundTag compound = (nmsItem.hasTag()) ? nmsItem.getTag() : new CompoundTag(); - if (compound == null) continue; + // Grabs the custom data component (returns an empty component if it's null) + CustomData customData = nmsItem.getComponents().getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); - if (compound.contains("CustomHat")) { + if (customData.contains("CustomHat")) { ingredients.setResult(null); break; } @@ -143,21 +139,29 @@ public void onArmorCauldronDye(PlayerInteractEvent event) { if (item.getType() == Material.LEATHER_HORSE_ARMOR) { ItemMeta itemMeta = item.getItemMeta(); if (itemMeta != null) { // itemMeta could be null, so we have to check this - NamespacedKey hatKey = new NamespacedKey(plugin, "CustomHat"); + NamespacedKey hatKeyOld = new NamespacedKey(plugin, "CustomHat"); + NamespacedKey hatKey = new NamespacedKey(plugin, "ParallelHat"); + NamespacedKey dyeableKey = new NamespacedKey(plugin, "Dyeable"); NamespacedKey modifyKey = new NamespacedKey(plugin, "NoModify"); PersistentDataContainer container = itemMeta.getPersistentDataContainer(); - if (container.has(hatKey, PersistentDataType.INTEGER) || container.has(modifyKey, PersistentDataType.INTEGER)) { + // skip this item if it has the dyeableKey + if (container.has(dyeableKey, PersistentDataType.INTEGER)) { + return; + } + // otherwise, if it's a hat or has the NoModify key, cancel the recipe + if (container.has(hatKeyOld, PersistentDataType.INTEGER) + || container.has(hatKey, PersistentDataType.STRING) + || container.has(modifyKey, PersistentDataType.INTEGER)) { event.setCancelled(true); return; } // Now we have to use NMS if the item doesn't have a PersistentDataContainer net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item); // does this even work lol - // Grabs the NMS items compound and checks if it's null - CompoundTag compound = (nmsItem.hasTag()) ? nmsItem.getTag() : new CompoundTag(); - if (compound == null) return; + // Grabs the custom data component (returns an empty component if it's null) + CustomData customData = nmsItem.getComponents().getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); - if (compound.contains("CustomHat")) { + if (customData.contains("CustomHat")) { event.setCancelled(true); } } diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/Sweethearts.java b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/Sweethearts.java new file mode 100644 index 00000000..b293d512 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/Sweethearts.java @@ -0,0 +1,171 @@ +package parallelmc.parallelutils.modules.bitsandbobs.minimodules; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.*; +import org.bukkit.advancement.Advancement; +import org.bukkit.advancement.AdvancementProgress; +import org.bukkit.attribute.Attribute; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntityResurrectEvent; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import parallelmc.parallelutils.Constants; +import parallelmc.parallelutils.ParallelUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.UUID; +import java.util.logging.Level; + +public class Sweethearts implements Listener { + + private final HashMap healthDonors = new HashMap<>(); + + public Sweethearts() { + PluginManager manager = Bukkit.getPluginManager(); + Plugin plugin = manager.getPlugin(Constants.PLUGIN_NAME); + if (plugin == null) { + ParallelUtils.log(Level.SEVERE, "Unable to enable Sweethearts. Plugin " + Constants.PLUGIN_NAME + + " does not exist!"); + return; + } + + // had some weird issues when checking for sneaking in an event + // so using a runnable seems to be the play + + plugin.getServer().getScheduler().runTaskTimer(plugin, this::checkForDonors, 0L, 16L); + } + + public void checkForDonors() { + for (Player donor : Bukkit.getOnlinePlayers()) { + UUID donorUUID = donor.getUniqueId(); + if (donor.isSneaking()) { + // If the donor is sneaking but not holding a poppy, return + if (!(donor.getInventory().getItemInMainHand().getType().equals(Material.POPPY) || + donor.getInventory().getItemInOffHand().getType().equals(Material.POPPY))) { + return; + } + // Get a list of all players in a 3-block radius (ty Paper for the epic function) + Location donorLocation = donor.getLocation(); + Collection nearbyPlayersCollection = + donor.getWorld().getNearbyEntitiesByType(Player.class, donorLocation, 3); + ArrayList nearbyPlayers = new ArrayList<>(nearbyPlayersCollection); + // Check if there are more players than just the donor themselves within a 3-block radius + if (!(nearbyPlayers.size() > 1)) { + return; + } + + // Iterate over all players within the radius and find the closest player + double smallestDistance = Double.MAX_VALUE; + Player recipient = null; + Location recipientLocation = null; + + for (Player player : nearbyPlayers) { + if (!(player.getUniqueId().equals(donorUUID))) { + Location playerLocation = player.getLocation(); + double distance = donorLocation.distanceSquared(playerLocation); // using distance squared to avoid expensive square roots + if (distance < smallestDistance) { + smallestDistance = distance; + recipient = player; + recipientLocation = player.getLocation(); + } + } + } + + // Check if recipient needs to be healed + double recipientHealth = recipient.getHealth(); + double recipientMaxHealth = recipient.getAttribute(Attribute.GENERIC_MAX_HEALTH).getBaseValue(); + if (!(recipientHealth < recipientMaxHealth)) { + return; + } + + // Add donor and recipient to healthDonor list - key gets overridden if it's already in there + healthDonors.put(donorUUID, recipient.getUniqueId()); + + // Give 1 health from donor to recipient + // If the recipient needs less than one heart, give them that + donor.damage(1); + if (recipientHealth + 1 > recipientMaxHealth) { + recipient.setHealth(recipientMaxHealth); + } else { + recipient.setHealth(recipientHealth + 1); + } + + // Spawn particles (idk why picking the function with the right parameters is so finicky) + World world = donorLocation.getWorld(); + world.spawnParticle(Particle.DAMAGE_INDICATOR, donorLocation.getX(), donorLocation.getY() + 2, + donorLocation.getZ(), 5, 0, 0, 0, 0.255, null); + world.spawnParticle(Particle.HEART, recipientLocation.getX(), recipientLocation.getY() + 2, + recipientLocation.getZ(), 2, 0.2, 0.2, 0.2, 1, null); + } + else { + if (healthDonors.containsKey(donorUUID)) { + // Check if health > 0 to prevent clashing with the death event + if (donor.getHealth() > 0) { + // Remove donor from the donor list + healthDonors.remove(donorUUID); + } + } + } + } + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent event) { + Player donor = event.getPlayer(); + UUID donorUUID = donor.getUniqueId(); + // Check if the player was a donor, and if so, remove the player from the healthDonor map and + // add a custom death message + if (healthDonors.containsKey(donorUUID)) { + // Build and send the death message text component + UUID recipientUUID = healthDonors.get(donorUUID); + Player recipient = Bukkit.getPlayer(recipientUUID); + final TextComponent deathMessage = Component.text(donor.getName() + " was shot through the heart and " + + recipient.getName() + " was to blame") + .color(NamedTextColor.WHITE); + event.deathMessage(deathMessage); + + // Remove the player from the healthDonor map + healthDonors.remove(donorUUID); + + // Award advancement + awardAdvancement(donor); + } + } + + @EventHandler + public void onTotemUse(EntityResurrectEvent event) { + // Check if entity is being resurrected by a totem - if the event isn't cancelled, then they have a totem + if (!event.isCancelled()) { + Entity entity = event.getEntity(); + // Check if entity is a player + if (entity instanceof Player donor) { + // Check if entity is an active donor - if so, award the advancement + UUID donorUUID = donor.getUniqueId(); + if (healthDonors.containsKey(donorUUID)) { + awardAdvancement(donor); + } + } + } + } + + public void awardAdvancement(Player donor) { + Advancement a = Bukkit.getAdvancement(new NamespacedKey("platy", + "forestry/sweethearts_death")); + if (a != null) { + AdvancementProgress avp = donor.getAdvancementProgress(a); + if (!avp.isDone()) { + for (String criteria : avp.getRemainingCriteria()) { + avp.awardCriteria(criteria); + } + } + } + } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/Ziprails.java b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/Ziprails.java new file mode 100644 index 00000000..127c5a30 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/bitsandbobs/minimodules/Ziprails.java @@ -0,0 +1,104 @@ +package parallelmc.parallelutils.modules.bitsandbobs.minimodules; + +import org.bukkit.*; +import org.bukkit.block.data.Attachable; +import org.bukkit.entity.Minecart; +import org.bukkit.event.Listener; +import org.bukkit.persistence.PersistentDataType; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.bukkit.util.Vector; +import parallelmc.parallelutils.Constants; +import parallelmc.parallelutils.ParallelUtils; + +import java.util.logging.Level; + +public class Ziprails implements Listener { + + private static final double MINECART_DEFAULT_MAX_SPEED = 0.4d; + NamespacedKey key; + + public Ziprails() { + PluginManager manager = Bukkit.getPluginManager(); + Plugin plugin = manager.getPlugin(Constants.PLUGIN_NAME); + if (plugin == null) { + ParallelUtils.log(Level.SEVERE, "Unable to enable Ziprails. Plugin " + Constants.PLUGIN_NAME + + " does not exist!"); + return; + } + + key = new NamespacedKey(plugin, "isZipping"); + + plugin.getServer().getScheduler().runTaskTimer(plugin, this::checkForZiprails, 0L, 4L); + } + + public void checkForZiprails() { + for (World world : Bukkit.getWorlds()) { + // Should only be returning a list of loaded minecarts + for (Minecart minecart : world.getEntitiesByClass(Minecart.class)) { + // Check if minecart is moving + Vector cartVelocity = minecart.getVelocity(); + if (cartVelocity.isZero()) { + continue; + } + + Location cartLocation = minecart.getLocation(); + // Check if minecart is on ziprail + Location blockAboveCart = cartLocation.clone().add(0, 1, 0); + Material blockMaterial = world.getBlockAt(blockAboveCart).getType(); + if (blockMaterial == Material.TRIPWIRE || blockMaterial == Material.TRIPWIRE_HOOK) { + Attachable blockdata = (Attachable) world.getBlockAt(blockAboveCart).getBlockData(); + // Cases for when the block above the minecart is an attachable ziprail + if (blockdata.isAttached()) { + // If the minecart isn't zipping, link it to the ziprail + if (!isZipping(minecart)) { + linkMinecart(minecart, cartLocation); + } + setMaxXZVelocity(minecart, cartVelocity); + } + } else { + if (isZipping(minecart)) { + unlinkMinecart(minecart); + } + } + } + } + } + + public boolean isZipping(Minecart minecart) { + return minecart.getPersistentDataContainer().has(key, PersistentDataType.INTEGER); + } + + public void linkMinecart(Minecart minecart, Location cartLocation) { + minecart.setGravity(false); + + // Makes sure that all ziprail cars are at the same y level + Location newCartLocation = cartLocation.clone(); + newCartLocation.setY(Math.floor(cartLocation.getY() + 0.36250001192093)); // this is the y value GM4 uses lol + minecart.teleport(newCartLocation); + + minecart.getPersistentDataContainer().set(key, PersistentDataType.INTEGER, 1); + minecart.getWorld().playSound(cartLocation, Sound.ENTITY_ITEM_FRAME_PLACE, SoundCategory.NEUTRAL, 1, 1); + } + + public void unlinkMinecart(Minecart minecart) { + minecart.setGravity(true); + minecart.getPersistentDataContainer().remove(key); + } + + public void setMaxXZVelocity(Minecart minecart, Vector cartVelocity) { + // A normalized vector is used here to make sure the cart remains going the right way along the x or z axis + Vector normalized = cartVelocity.normalize(); + + if (cartVelocity.getX() != 0) { + cartVelocity.setX(normalized.getX() * MINECART_DEFAULT_MAX_SPEED); + cartVelocity.setZ(0); + } + if (cartVelocity.getZ() != 0) { + cartVelocity.setZ(normalized.getZ() * MINECART_DEFAULT_MAX_SPEED); + cartVelocity.setX(0); + } + + minecart.setVelocity(cartVelocity); + } +} \ No newline at end of file diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmParticleHandler.java b/modules/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmParticleHandler.java index 650c383d..a6850b0f 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmParticleHandler.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/charms/handlers/impl/CharmParticleHandler.java @@ -4,7 +4,7 @@ import org.bukkit.Material; import org.bukkit.Particle; import org.bukkit.block.data.BlockData; -import org.bukkit.craftbukkit.v1_20_R1.block.data.CraftBlockData; +import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; @@ -134,7 +134,7 @@ public BukkitRunnable getRunnable(Player player, ItemStack item, CharmOptions op // Data switch (particle) { - case ITEM_CRACK, BLOCK_CRACK, BLOCK_DUST, FALLING_DUST, BLOCK_MARKER -> { + case ITEM, BLOCK, FALLING_DUST, BLOCK_MARKER -> { // Takes Material as input EncapsulatedType dataMatType = settingsMap.get("dataMaterial"); @@ -147,7 +147,7 @@ public BukkitRunnable getRunnable(Player player, ItemStack item, CharmOptions op try { Material material = Material.valueOf(dataMat); - if (particle == Particle.ITEM_CRACK) { + if (particle == Particle.ITEM) { if (material.isItem()) { ItemStack itemStack = new ItemStack(material); @@ -163,7 +163,7 @@ public BukkitRunnable getRunnable(Player player, ItemStack item, CharmOptions op if (dataBlockDataType.getType() == Types.STRING) { String blockData = (String) dataBlockDataType.getVal(); - BlockData data = CraftBlockData.newData(material, blockData); + BlockData data = material.createBlockData(blockData); builder.data(data); } diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/charms/listeners/PlayerJoinContainerListenerOverwrite.java b/modules/src/main/java/parallelmc/parallelutils/modules/charms/listeners/PlayerJoinContainerListenerOverwrite.java index db205e48..f3de7410 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/charms/listeners/PlayerJoinContainerListenerOverwrite.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/charms/listeners/PlayerJoinContainerListenerOverwrite.java @@ -5,7 +5,7 @@ import net.minecraft.world.inventory.*; import net.minecraft.world.item.ItemStack; import org.bukkit.Bukkit; -import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer; +import org.bukkit.craftbukkit.entity.CraftPlayer; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -26,9 +26,9 @@ public PlayerJoinContainerListenerOverwrite() { Field field; Field field1; try { - field = ServerPlayer.class.getDeclaredField("cW"); + field = ServerPlayer.class.getDeclaredField("containerSynchronizer"); field.setAccessible(true); - field1 = ServerPlayer.class.getDeclaredField("cX"); + field1 = ServerPlayer.class.getDeclaredField("containerListener"); field1.setAccessible(true); } catch (NoSuchFieldException e) { e.printStackTrace(); diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/charms/util/EnchantGlow.java b/modules/src/main/java/parallelmc/parallelutils/modules/charms/util/EnchantGlow.java index 09306767..c787c15e 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/charms/util/EnchantGlow.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/charms/util/EnchantGlow.java @@ -1,22 +1,30 @@ package parallelmc.parallelutils.modules.charms.util; import io.papermc.paper.enchantments.EnchantmentRarity; +import io.papermc.paper.registry.set.RegistryKeySet; +import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; import org.bukkit.NamespacedKey; import org.bukkit.enchantments.Enchantment; import org.bukkit.enchantments.EnchantmentTarget; import org.bukkit.entity.EntityCategory; +import org.bukkit.entity.EntityType; import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.EquipmentSlotGroup; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.ItemType; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import parallelmc.parallelutils.ParallelUtils; -import java.lang.reflect.Field; import java.util.Set; public class EnchantGlow extends Enchantment { + + private final NamespacedKey key; + public EnchantGlow(@NotNull NamespacedKey key) { - super(key); + this.key = key; } @Override @@ -36,7 +44,7 @@ public int getStartLevel() { @Override public @NotNull EnchantmentTarget getItemTarget() { - return EnchantmentTarget.ALL; + return null; } @Override @@ -74,13 +82,33 @@ public boolean isDiscoverable() { return false; } + @Override + public int getMinModifiedCost(int level) { + return 0; + } + + @Override + public int getMaxModifiedCost(int level) { + return 0; + } + + @Override + public int getAnvilCost() { + return 0; + } + @Override public @NotNull EnchantmentRarity getRarity() { - return EnchantmentRarity.COMMON; + return null; } @Override - public float getDamageIncrease(int level, @NotNull EntityCategory entityCategory) { + public float getDamageIncrease(int i, @NotNull EntityCategory entityCategory) { + return 0; + } + + @Override + public float getDamageIncrease(int i, @NotNull EntityType entityType) { return 0; } @@ -89,6 +117,36 @@ public float getDamageIncrease(int level, @NotNull EntityCategory entityCategory return Set.of(); } + @Override + public @NotNull Set getActiveSlotGroups() { + return Set.of(); + } + + @Override + public @NotNull Component description() { + return null; + } + + @Override + public @NotNull RegistryKeySet getSupportedItems() { + return null; + } + + @Override + public @Nullable RegistryKeySet getPrimaryItems() { + return null; + } + + @Override + public int getWeight() { + return 0; + } + + @Override + public @NotNull RegistryKeySet getExclusiveWith() { + return null; + } + @Override public @NotNull String translationKey() { return ""; @@ -97,19 +155,34 @@ public float getDamageIncrease(int level, @NotNull EntityCategory entityCategory public static EnchantGlow instance = null; public static void registerFakeGlow(ParallelUtils puPlugin) { - try { - Field f = Enchantment.class.getDeclaredField("acceptingNew"); - f.setAccessible(true); - f.set(null, true); - } catch (Exception e) { - e.printStackTrace(); - } +// try { +// Field f = Enchantment.class.getDeclaredField("acceptingNew"); +// f.setAccessible(true); +// f.set(null, true); +// } catch (Exception e) { +// e.printStackTrace(); +// } try { instance = new EnchantGlow(new NamespacedKey(puPlugin, "glow")); - Enchantment.registerEnchantment(instance); + } catch (Exception e) { e.printStackTrace(); } } + + @Override + public @NotNull NamespacedKey getKey() { + return key; + } + + @Override + public @NotNull Key key() { + return super.key(); + } + + @Override + public @NotNull String getTranslationKey() { + return null; + } } diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/custommobs b/modules/src/main/java/parallelmc/parallelutils/modules/custommobs index ab8a8e4c..fb1ae501 160000 --- a/modules/src/main/java/parallelmc/parallelutils/modules/custommobs +++ b/modules/src/main/java/parallelmc/parallelutils/modules/custommobs @@ -1 +1 @@ -Subproject commit ab8a8e4cb50468704b049bd7a1503b1cf3d54037 +Subproject commit fb1ae5019ccfedd48b583c824471a7d17568bf07 diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/parallelchat/ParallelChat.java b/modules/src/main/java/parallelmc/parallelutils/modules/parallelchat/ParallelChat.java index 326b7e63..6bff785c 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/parallelchat/ParallelChat.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/parallelchat/ParallelChat.java @@ -389,6 +389,16 @@ public static void sendParallelMessageTo(Player player, Component message) { Component text = MiniMessage.miniMessage().deserialize("[P] ").append(message); player.sendMessage(text); } + + /** + * Sends a chat message to a player with the ParallelUtils prefix after waiting a specified number of ticks + * @param player The player to send the message to + * @param wait The number of ticks to wait until sending the message + * @param message The component to send + */ + public static void sendDelayedParallelMessageTo(Player player, long wait, String message) { + Bukkit.getScheduler().runTaskLater(Instance.puPlugin, () -> sendParallelMessageTo(player, message), wait); + } /** * Sends a chat message to a player diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/parallelflags/session/CustomArmorHealth.java b/modules/src/main/java/parallelmc/parallelutils/modules/parallelflags/session/CustomArmorHealth.java index 38d6e3ca..6068db8d 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/parallelflags/session/CustomArmorHealth.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/parallelflags/session/CustomArmorHealth.java @@ -9,6 +9,8 @@ import com.sk89q.worldguard.session.handler.Handler; import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; +import org.bukkit.damage.DamageSource; +import org.bukkit.damage.DamageType; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; @@ -157,7 +159,7 @@ public void tick(LocalPlayer player, ApplicableRegionSet set) { } EntityDamageEvent event = new EntityDamageEvent(bukkitPlayer.getPlayer(), - EntityDamageEvent.DamageCause.CUSTOM, damageAmount); + EntityDamageEvent.DamageCause.CUSTOM, DamageSource.builder(DamageType.GENERIC).build(),damageAmount); bukkitPlayer.getPlayer().setLastDamageCause(event); bukkitPlayer.getPlayer().damage(player.getMaxHealth()); @@ -179,7 +181,7 @@ public void tick(LocalPlayer player, ApplicableRegionSet set) { } EntityDamageEvent event = new EntityDamageEvent(bukkitPlayer.getPlayer(), - EntityDamageEvent.DamageCause.CUSTOM, damageAmount); + EntityDamageEvent.DamageCause.CUSTOM, DamageSource.builder(DamageType.GENERIC).build(), damageAmount); bukkitPlayer.getPlayer().setLastDamageCause(event); } diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/parallelitems b/modules/src/main/java/parallelmc/parallelutils/modules/parallelitems index 6b0c59ac..14288534 160000 --- a/modules/src/main/java/parallelmc/parallelutils/modules/parallelitems +++ b/modules/src/main/java/parallelmc/parallelutils/modules/parallelitems @@ -1 +1 @@ -Subproject commit 6b0c59ac6486e7653aea1ffe7629f378ecc17d23 +Subproject commit 14288534ae0b3c0e2a4d20dadf01eba46eea1297 diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/ParallelResources.java b/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/ParallelResources.java index 4d2277eb..9484dca6 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/ParallelResources.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/ParallelResources.java @@ -8,6 +8,8 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; +import org.bukkit.scheduler.BukkitRunnable; +import org.bukkit.scheduler.BukkitTask; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import parallelmc.parallelutils.Constants; @@ -63,6 +65,8 @@ public class ParallelResources extends ParallelModule { @Nullable private File packSquashConfig = null; + private boolean doneLoading = false; + private final HashMap resourceHashes = new HashMap<>(); public ParallelResources(ParallelClassLoader classLoader, List dependents) { @@ -104,6 +108,7 @@ public void onLoad() { if (!resourcesFile.exists()) { resourcesConfig.set("domain", "resources.parallelmc.org"); resourcesConfig.set("port", 4444); + resourcesConfig.set("has_proxy", false); resourcesConfig.set("https", false); resourcesConfig.set("https_keystore", null); resourcesConfig.set("https_keystore_pass", null); @@ -117,6 +122,7 @@ public void onLoad() { // Initialize server int port = resourcesConfig.getInt("port", 4444); + boolean has_proxy = resourcesConfig.getBoolean("has_proxy", false); boolean https = resourcesConfig.getBoolean("https", false); String keystore = resourcesConfig.getString("https_keystore", null); String keystore_pass = resourcesConfig.getString("https_keystore_pass", null); @@ -125,9 +131,15 @@ public void onLoad() { server = new ResourceServer(port, https, keystore != null ? new File(resourcesDir, keystore) : null, keystore_pass); - String https_head = https ? "https://" : "http://"; + // Use HTTPS if there's a proxy + String https_head = (https|has_proxy) ? "https://" : "http://"; - base_url = https_head + domain + ":" + port + "/"; + // URL does not include port if there's a proxy + if (has_proxy) { + base_url = https_head + domain + "/"; + } else { + base_url = https_head + domain + ":" + port + "/"; + } // Try loading packsquash @@ -165,6 +177,7 @@ public void onLoad() { if (!base_zip.exists()) { ParallelUtils.log(Level.WARNING, "Base zip does not exist! Will not continue"); + doneLoading = true; return; } @@ -206,7 +219,37 @@ public void onLoad() { packsTemp.add(base_temp); // Squash all the files - packs = squashFiles(packsTemp, resourcesDir, squashOut); + + + ParallelResources resources = this; + BukkitTask squashTask = new BukkitRunnable() { + @Override + public void run() { + try { + handler = new ResourcePackHandle(puPlugin, resources, warning_component); + + List packs = squashFiles(packsTemp, resourcesDir, squashOut); + + for (File f : packs) { + String trimmed_name = f.getName().replace(".zip", ""); + server.addResource(trimmed_name, f); + resourceHashes.put(trimmed_name, createSha1(f)); + } + + + + } catch (Exception e) { + ParallelUtils.log(Level.SEVERE, "Exception while loading ParallelResources! Quitting..."); + doneLoading = true; + return; + } + + serverThread = new Thread(server); + serverThread.start(); + doneLoading = true; + } + }.runTaskAsynchronously(puPlugin); + } else { // Just put them right in the final output directory packs = generatePacks(squashOut, base_zip, resourceMods); @@ -215,28 +258,28 @@ public void onLoad() { File base_final = new File(modOut, base_zip.getName()); Files.copy(base_zip.toPath(), base_final.toPath()); packs.add(base_final); - } - for (File f : packs) { - String trimmed_name = f.getName().replace(".zip", ""); - server.addResource(trimmed_name, f); - resourceHashes.put(trimmed_name, createSha1(f)); - } + for (File f : packs) { + String trimmed_name = f.getName().replace(".zip", ""); + server.addResource(trimmed_name, f); + resourceHashes.put(trimmed_name, createSha1(f)); + } - handler = new ResourcePackHandle(puPlugin, this, warning_component); + handler = new ResourcePackHandle(puPlugin, this, warning_component); + + serverThread = new Thread(server); + serverThread.start(); + } } catch (IOException e) { e.printStackTrace(); ParallelUtils.log(Level.SEVERE, "IOException while loading ParallelResources! Quitting..."); - return; } catch (Exception e) { e.printStackTrace(); ParallelUtils.log(Level.SEVERE, "Exception while loading ParallelResources! Quitting..."); - return; } - serverThread = new Thread(server); - serverThread.start(); + doneLoading = true; } @Override @@ -362,7 +405,7 @@ private List squashFiles(@NotNull List inFiles, @NotNull File resour out = new File(outDir, f.getName()); Files.copy(f.toPath(), out.toPath()); } - squashed.add(f); + squashed.add(out); } purgeDirectory(tempDir.toFile()); @@ -566,4 +609,8 @@ public byte[] getHash(@NotNull String world) { return resourceHashes.get(world); } + public boolean doneLoading() { + return doneLoading; + } + } diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/ResourceServer.java b/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/ResourceServer.java index dece4bf5..7401a207 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/ResourceServer.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/ResourceServer.java @@ -71,6 +71,8 @@ public void configure(HttpsParameters params) { } }); + server = httpsServer; + } else { server = HttpServer.create(new InetSocketAddress(port), 0); } diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/events/ResourcePackHandle.java b/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/events/ResourcePackHandle.java index 36143659..36ef99c0 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/events/ResourcePackHandle.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/parallelresources/events/ResourcePackHandle.java @@ -10,9 +10,11 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerResourcePackStatusEvent; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import parallelmc.parallelutils.ParallelUtils; import parallelmc.parallelutils.modules.parallelresources.ParallelResources; +import java.util.Arrays; import java.util.logging.Level; public class ResourcePackHandle implements Listener { @@ -34,7 +36,15 @@ public ResourcePackHandle(ParallelUtils puPlugin, ParallelResources resources, C public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); - if (!applyPack(player)) { + if (!resources.doneLoading()) { + if (player.hasPermission("parallelutils.resources.unenforced")) { + return; + } else { + player.kick(Component.text("Server is still starting! Please check back in a minute.")); + } + } + + if (!applyPack(player, null)) { ParallelUtils.log(Level.SEVERE, "UNABLE TO APPLY RESOURCE PACK!"); } } @@ -43,7 +53,7 @@ public void onPlayerJoin(PlayerJoinEvent event) { public void onWorldChange(PlayerChangedWorldEvent event) { Player player = event.getPlayer(); - if (!applyPack(player)) { + if (!applyPack(player, event.getFrom())) { ParallelUtils.log(Level.SEVERE, "UNABLE TO APPLY RESOURCE PACK!"); } } @@ -54,10 +64,12 @@ public void onResourcePackStatus(PlayerResourcePackStatusEvent event) { switch (status) { case DECLINED -> { + if (event.getPlayer().hasPermission("parallelutils.resources.unenforced")) return; ParallelUtils.log(Level.WARNING, "Event " + status.toString() + " occurred! Declining join"); event.getPlayer().kick(warningMessage); } case FAILED_DOWNLOAD -> { + if (event.getPlayer().hasPermission("parallelutils.resources.unenforced")) return; ParallelUtils.log(Level.WARNING, "Event " + status.toString() + " occurred! Declining join"); event.getPlayer().kick(warningMessage.append( Component.text("If you believe this was an error, please contact staff on Discord."))); @@ -65,22 +77,43 @@ public void onResourcePackStatus(PlayerResourcePackStatusEvent event) { } } - public boolean applyPack(@NotNull Player player) { + public boolean applyPack(@NotNull Player player, @Nullable World previousWorld) { World world = player.getWorld(); String worldName = world.getName(); byte[] hash = resources.getHash(worldName); + ParallelUtils.log(Level.INFO, "Applying pack for world " + worldName); + if (hash == null) { ParallelUtils.log(Level.INFO, "Tried to get pack for invalid world. Defaulting to base"); worldName = "base"; hash = resources.getHash("base"); } + ParallelUtils.log(Level.INFO, "Found hash for world " + worldName); + + + if (previousWorld != null) { + String previousName = previousWorld.getName(); + byte[] previousHash = resources.getHash(previousName); + + if (previousHash == null) { + previousHash = resources.getHash("base"); + } + + // If the packs for each world are the same, just don't even apply the pack + if (Arrays.equals(previousHash, hash)) { + return true; + } + } + String resourceUrl = resources.getResourceUrl(worldName); - player.setResourcePack(resourceUrl, hash, warningMessage, true); + ParallelUtils.log(Level.INFO, "Got resource URL " + resourceUrl); + + player.setResourcePack(resourceUrl, hash, warningMessage, !player.hasPermission("parallelutils.resources.unenforced")); return true; } diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/paralleltutorial/ParallelTutorial.java b/modules/src/main/java/parallelmc/parallelutils/modules/paralleltutorial/ParallelTutorial.java index 4557de8e..e4be51df 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/paralleltutorial/ParallelTutorial.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/paralleltutorial/ParallelTutorial.java @@ -18,6 +18,7 @@ import org.bukkit.scheduler.BukkitTask; import org.bukkit.util.EulerAngle; import org.bukkit.util.Vector; +import org.checkerframework.checker.units.qual.A; import org.jetbrains.annotations.NotNull; import parallelmc.parallelutils.Constants; import parallelmc.parallelutils.ParallelClassLoader; @@ -51,7 +52,7 @@ public class ParallelTutorial extends ParallelModule { // mostly for use in server crashes/shutdown public HashMap runningTutorials = new HashMap<>(); - public HashMap armorStands = new HashMap<>(); + public HashMap displayEntities = new HashMap<>(); public HashMap startPoints = new HashMap<>(); @@ -243,11 +244,11 @@ public void run() { if (debug) ParallelChat.sendParallelMessageTo(player, "Debug mode enabled! Please open console to view debug output."); startPoints.put(player, player.getLocation()); loop = new BukkitRunnable() { - ArmorStand stand; + Display display; @Override public void run() { - if (stand != null && player.getLocation().distanceSquared(stand.getLocation()) > 256) { - Bukkit.getScheduler().runTask(puPlugin, () -> player.teleport(stand.getLocation())); + if (display != null && player.getLocation().distanceSquared(display.getLocation()) > 256) { + Bukkit.getScheduler().runTask(puPlugin, () -> player.teleport(display.getLocation())); } // only run the next instruction if the current one is finished if (instructionFinished) { @@ -257,73 +258,63 @@ public void run() { case "START" -> { Bukkit.getScheduler().runTask(puPlugin, () -> { Location start = new Location(world, Double.parseDouble(i.args()[0]), Double.parseDouble(i.args()[1]), Double.parseDouble(i.args()[2])); - stand = (ArmorStand)world.spawnEntity(start, EntityType.ARMOR_STAND, CreatureSpawnEvent.SpawnReason.COMMAND); - stand.setGravity(false); - stand.setVisible(false); - stand.setBasePlate(false); - stand.setInvulnerable(true); - stand.setHeadPose(EulerAngle.ZERO); + display = (Display)world.spawnEntity(start, EntityType.ITEM_DISPLAY, CreatureSpawnEvent.SpawnReason.COMMAND); + display.setRotation(0, 0); player.setGameMode(GameMode.SPECTATOR); player.setFlySpeed(0F); - // force player to spectate the armor stand + // force player to spectate the display // the player's actual model will be stuck back at the start - forceSpectate(player, stand.getEntityId()); - armorStands.put(player, stand); + forceSpectate(player, display.getEntityId()); + displayEntities.put(player, display); instructionFinished = true; }); } case "MOVE" -> { - final Location a = stand.getLocation(); + final Location a = display.getLocation(); final Location b = new Location(world, Double.parseDouble(i.args()[0]), Double.parseDouble(i.args()[1]), Double.parseDouble(i.args()[2])); - final float duration = Float.parseFloat(i.args()[3]) * 20f; + final int steps = Integer.parseInt(i.args()[3]); + final ArrayList nodes = new ArrayList<>(); + for (float l = 0; l < steps; l += 1f) { + float t = l / steps; + Location point = new Location(world, + a.getX() + (b.getX() - a.getX()) * t, + a.getY() + (b.getY() - a.getY()) * t, + a.getZ() + (b.getZ() - a.getZ()) * t); + if (isBlock) { + lookAt(point, lookAt); + } + else { + point.setYaw((float)lookAt.getX()); + point.setPitch((float)lookAt.getY()); + } + nodes.add(point); + } + if (isBlock) + lookAt(b, lookAt); + else { + b.setYaw((float)lookAt.getX()); + b.setPitch((float)lookAt.getY()); + } + nodes.add(b); + + display.teleport(nodes.get(0)); + if (debug) ParallelUtils.log(Level.WARNING, "Teleporting to " + nodes.get(0)); + display.setTeleportDuration(19); new BukkitRunnable() { - float steps = 0f; + int index = 1; @Override public void run() { - if (steps == duration) { - if (lookAt != null) { - if (isBlock) { - Vector look = lookAt(stand, lookAt); - if (debug) ParallelUtils.log(Level.WARNING, "Calculated look vector: " + look.getX() + " " + look.getY()); - b.setYaw((float) look.getX()); - b.setPitch((float) look.getY()); - } - else { - b.setYaw((float) lookAt.getX()); - b.setPitch((float) lookAt.getY()); - } - } - stand.teleport(b); + if (index >= nodes.size()) { + display.setTeleportDuration(0); instructionFinished = true; this.cancel(); + return; } - else { - float t = steps / duration; - if (t < 0f) - t = 0f; - else if (t > 1F) - t = 1f; - Location point = new Location(world, - a.getX() + (b.getX() - a.getX()) * t, - a.getY() + (b.getY() - a.getY()) * t, - a.getZ() + (b.getZ() - a.getZ()) * t); - if (lookAt != null) { - if (isBlock) { - Vector look = lookAt(stand, lookAt); - if (debug) ParallelUtils.log(Level.WARNING, "Calculated look vector: " + look.getX() + " " + look.getY()); - point.setYaw((float) look.getX()); - point.setPitch((float)look.getY()); - } - else { - point.setYaw((float) lookAt.getX()); - point.setPitch((float) lookAt.getY()); - } - } - stand.teleport(point); - steps++; - } + display.teleport(nodes.get(index)); + if (debug) ParallelUtils.log(Level.WARNING, "Teleporting to " + nodes.get(index)); + index++; } - }.runTaskTimer(puPlugin, 1L, 1L); + }.runTaskTimer(puPlugin, 1L, 19L); } case "TELEPORT" -> { final Location newPoint = new Location(world, Double.parseDouble(i.args()[0]), Double.parseDouble(i.args()[1]), Double.parseDouble(i.args()[2])); @@ -334,21 +325,18 @@ public void run() { if(player.teleport(newPoint)) { if (lookAt != null) { if (isBlock) { - Vector look = lookAt(stand, lookAt); - if (debug) ParallelUtils.log(Level.WARNING, "Calculated look vector: " + look.getX() + " " + look.getY()); - newPoint.setYaw((float) look.getX()); - newPoint.setPitch((float) look.getY()); + lookAt(newPoint, lookAt); } else { newPoint.setYaw((float) lookAt.getX()); newPoint.setPitch((float) lookAt.getY()); } } - if (stand.teleport(newPoint)) { - forceSpectate(player, stand.getEntityId()); + if (display.teleport(newPoint)) { + forceSpectate(player, display.getEntityId()); if (debug) { - ParallelUtils.log(Level.WARNING, "Armor Stand teleported!"); - ParallelUtils.log(Level.WARNING, "Armor Stand looking at: " + stand.getLocation().getYaw() + " " + stand.getLocation().getPitch()); + ParallelUtils.log(Level.WARNING, "Display teleported!"); + ParallelUtils.log(Level.WARNING, "Display looking at: " + display.getLocation().getYaw() + " " + display.getLocation().getPitch()); ParallelUtils.log(Level.WARNING, "Should be looking at: " + lookAt.getX() + " " + lookAt.getY()); } instructionFinished = true; @@ -403,7 +391,7 @@ public void run() { public void endTutorialFor(Player player, boolean debug) { Location endPoint = startPoints.get(player); - ArmorStand stand = armorStands.get(player); + Display display = displayEntities.get(player); if (debug) ParallelUtils.log(Level.WARNING, "Ending tutorial..."); new BukkitRunnable() { @Override @@ -414,11 +402,11 @@ public void run() { if (debug) ParallelUtils.log(Level.WARNING, "Player teleported!"); // making the player spectate themselves brings them back to the start forceSpectate(player, player.getEntityId()); - if (debug) ParallelUtils.log(Level.WARNING, "armorStands HashMap " + (stand != null ? "DOES" : "DOES NOT") + " contain the player before deletion."); - if (stand != null) { - if (debug) ParallelUtils.log(Level.WARNING, "Armor stand marked for removal"); - stand.remove(); - armorStands.remove(player); + if (debug) ParallelUtils.log(Level.WARNING, "displayEntities HashMap " + (display != null ? "DOES" : "DOES NOT") + " contain the player before deletion."); + if (display != null) { + if (debug) ParallelUtils.log(Level.WARNING, "Display marked for removal"); + display.remove(); + displayEntities.remove(player); } player.setGameMode(GameMode.SURVIVAL); player.setFlySpeed(0.1F); @@ -429,14 +417,14 @@ public void run() { } }.runTaskTimer(puPlugin, 0L, 2L); if (debug) { - ParallelUtils.log(Level.WARNING, "Checking status of armor stand in a few ticks..."); + ParallelUtils.log(Level.WARNING, "Checking status of display in a few ticks..."); new BukkitRunnable() { @Override public void run() { - if (stand.isDead() && !stand.isValid()) - ParallelUtils.log(Level.WARNING, "Armor Stand removed successfully!"); + if (display.isDead() && !display.isValid()) + ParallelUtils.log(Level.WARNING, "Display removed successfully!"); else - ParallelUtils.log(Level.WARNING, "Armor Stand was NOT removed!"); + ParallelUtils.log(Level.WARNING, "Display was NOT removed!"); } }.runTaskLater(puPlugin, 10L); } @@ -445,16 +433,16 @@ public void run() { /* Since we can't start runnables during shutdown we have to do this This may still result in Player moved too quickly errors, testing is needed - This will also be unable to check if the armor stand is truly despawned + This will also be unable to check if the display is truly despawned */ public void handleShutdown(Player player) { Location endPoint = startPoints.get(player); - ArmorStand stand = armorStands.get(player); + Display display = displayEntities.get(player); player.teleport(endPoint); forceSpectate(player, player.getEntityId()); - if (stand != null) { - stand.remove(); - armorStands.remove(player); + if (display != null) { + display.remove(); + displayEntities.remove(player); } player.setGameMode(GameMode.SURVIVAL); player.setFlySpeed(0.1F); @@ -464,23 +452,23 @@ public void handleShutdown(Player player) { public void handleDisconnectedPlayer(Player player, boolean debug) { if (debug) ParallelUtils.log(Level.WARNING, "Ending tutorial..."); - ArmorStand stand = armorStands.get(player); - if (debug) ParallelUtils.log(Level.WARNING, "armorStands HashMap " + (stand != null ? "DOES" : "DOES NOT") + " contain the player before deletion."); - if (stand != null) { - if (debug) ParallelUtils.log(Level.WARNING, "Armor stand marked for removal"); - stand.remove(); - armorStands.remove(player); + Display display = displayEntities.get(player); + if (debug) ParallelUtils.log(Level.WARNING, "displayEntities HashMap " + (display != null ? "DOES" : "DOES NOT") + " contain the player before deletion."); + if (display != null) { + if (debug) ParallelUtils.log(Level.WARNING, "Display stand marked for removal"); + display.remove(); + displayEntities.remove(player); } runningTutorials.remove(player); if (debug) { - ParallelUtils.log(Level.WARNING, "Checking status of armor stand in a few ticks..."); + ParallelUtils.log(Level.WARNING, "Checking status of display stand in a few ticks..."); new BukkitRunnable() { @Override public void run() { - if (stand.isDead() && !stand.isValid()) - ParallelUtils.log(Level.WARNING, "Armor Stand removed successfully!"); + if (display.isDead() && !display.isValid()) + ParallelUtils.log(Level.WARNING, "Display Stand removed successfully!"); else - ParallelUtils.log(Level.WARNING, "Armor Stand was NOT removed!"); + ParallelUtils.log(Level.WARNING, "Display Stand was NOT removed!"); } }.runTaskLater(puPlugin, 10L); } @@ -513,58 +501,32 @@ private void forceSpectate(Player player, int entityId) { } } - private Vector lookAt(ArmorStand stand, Vector block) { - Vector eyes = stand.getEyeLocation().toVector(); - double d = block.getX() - eyes.getX(); - double e = block.getY() - eyes.getY(); - double f = block.getZ() - eyes.getZ(); + private void lookAt(Location loc, Vector block) { + double d = block.getX() - loc.getX(); + double e = block.getY() - loc.getY(); + double f = block.getZ() - loc.getZ(); double g = Math.sqrt(d * d + f * f); - return new Vector(Mth.wrapDegrees((float)(Mth.atan2(f, d) * 57.2957763671875) - 90.0F), Mth.wrapDegrees((float)(-(Mth.atan2(e, g) * 57.2957763671875))), 0); + Vector result = new Vector(Mth.wrapDegrees((float)(Mth.atan2(f, d) * 57.2957763671875) - 90.0F), Mth.wrapDegrees((float)(-(Mth.atan2(e, g) * 57.2957763671875))), 0); + loc.setYaw((float)result.getX()); + loc.setPitch((float)result.getY()); } - private Vector faceBlock(ArmorStand stand, Vector block) { - Vector eyes = stand.getEyeLocation().toVector(); - Vector blockPos = new Vector(block.getX(), block.getY(), block.getZ()); - double diffX = blockPos.getX() - eyes.getX(); - double diffY = blockPos.getY() - eyes.getY(); - double diffZ = blockPos.getZ() - eyes.getZ(); - - double diffXZ = Math.sqrt(diffX * diffX + diffZ * diffZ); - - float yaw = (float)Math.toDegrees(Math.atan2(diffZ, diffX)) - 90F; - float pitch = (float)-Math.toDegrees(Math.atan2(diffY, diffXZ)); - float prevYaw = stand.getLocation().getYaw(); - float prevPitch = stand.getLocation().getPitch(); - // use a rotational lerp to prevent snapping - return new Vector(rotLerp(prevYaw, yaw, 30f), rotLerp(prevPitch, pitch, 30f), 0); - } - private float rotLerp(float pSourceAngle, float pTargetAngle, float pMaximumChange) + private float rotLerp(float source, float target, float t) { - float f = Mth.wrapDegrees(pTargetAngle - pSourceAngle); - - if (f > pMaximumChange) - { - f = pMaximumChange; - } - - if (f < -pMaximumChange) - { - f = -pMaximumChange; - } - - float f1 = pSourceAngle + f; - - if (f1 < 0.0F) - { - f1 += 360.0F; - } - else if (f1 > 360.0F) - { - f1 -= 360.0F; + source += 180f; + target += 180f; + float diff = Math.abs(target - source); + if (diff > 180f) { + if (target > source) + source += 360f; + else + target += 360f; } - - return f1; + float interp = source + (target - source) * t; + if (interp >= 0f && interp <= 360f) + return interp - 180f; + return interp % 360f - 180f; } } diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/paralleltutorial/commands/ParallelReloadTutorials.java b/modules/src/main/java/parallelmc/parallelutils/modules/paralleltutorial/commands/ParallelReloadTutorials.java index 8920a219..6e9f1fc2 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/paralleltutorial/commands/ParallelReloadTutorials.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/paralleltutorial/commands/ParallelReloadTutorials.java @@ -15,7 +15,7 @@ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command return true; } } - if (ParallelTutorial.get().runningTutorials.size() > 0) { + if (!ParallelTutorial.get().runningTutorials.isEmpty()) { commandSender.sendMessage("Cannot reload tutorials while there are players in a tutorial!"); return true; } diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/points/Config.java b/modules/src/main/java/parallelmc/parallelutils/modules/points/Config.java new file mode 100644 index 00000000..b9c2d8ea --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/points/Config.java @@ -0,0 +1,19 @@ +package parallelmc.parallelutils.modules.points; + +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class Config implements parallelmc.parallelutils.Config { + @Override + public @NotNull List getHardDepends() { + return List.of("ParallelChat"); + } + + @Override + public @NotNull List getSoftDepends() { + return new ArrayList<>(); + } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/points/Points.java b/modules/src/main/java/parallelmc/parallelutils/modules/points/Points.java new file mode 100644 index 00000000..d26fd443 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/points/Points.java @@ -0,0 +1,282 @@ +package parallelmc.parallelutils.modules.points; + +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.advancement.Advancement; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginManager; +import org.jetbrains.annotations.NotNull; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import parallelmc.parallelutils.Constants; +import parallelmc.parallelutils.ParallelClassLoader; +import parallelmc.parallelutils.ParallelModule; +import parallelmc.parallelutils.ParallelUtils; +import parallelmc.parallelutils.modules.points.commands.OpenPointsRedemption; +import parallelmc.parallelutils.modules.points.commands.RecalculatePoints; +import parallelmc.parallelutils.modules.points.commands.ViewPoints; +import parallelmc.parallelutils.modules.points.events.OnAdvancementDone; +import parallelmc.parallelutils.modules.points.gui.PointsRedeemInventory; +import parallelmc.parallelutils.util.GUIManager; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.logging.Level; + +public class Points extends ParallelModule { + public Points(ParallelClassLoader classLoader, List dependents) { super(classLoader, dependents); } + + private final HashMap advancementMap = new HashMap<>(); + + private final HashMap playerPoints = new HashMap<>(); + + private final List redeemableItems = new ArrayList<>(); + + private static Points Instance; + + private ParallelUtils puPlugin; + + @Override + public void onLoad() { } + + @Override + public void onEnable() { + PluginManager manager = Bukkit.getPluginManager(); + Plugin plugin = manager.getPlugin(Constants.PLUGIN_NAME); + + if (plugin == null) { + ParallelUtils.log(Level.SEVERE, "Unable to enable Points. Plugin " + Constants.PLUGIN_NAME + " does not exist!"); + return; + } + + puPlugin = (ParallelUtils) plugin; + + if (!puPlugin.registerModule(this)) { + ParallelUtils.log(Level.SEVERE, "Unable to register module Points! Module may already be registered. Quitting..."); + } + + manager.registerEvents(new OnAdvancementDone(), puPlugin); + + puPlugin.getCommand("points").setExecutor(new ViewPoints()); + puPlugin.getCommand("recalculatepoints").setExecutor(new RecalculatePoints(puPlugin)); + puPlugin.getCommand("openpointsredemption").setExecutor(new OpenPointsRedemption()); + + loadAdvancements(); + loadItems(); + loadPlayerPoints(); + + Instance = this; + } + + @Override + public void onDisable() { + savePlayerPoints(); + } + + @Override + public void onUnload() { } + + @Override + public @NotNull String getName() { return "Points"; } + + public int getPointsForAdvancement(Advancement advancement) { + return advancementMap.getOrDefault(advancement.getKey().asString(), -1); + } + + public void awardPoints(Player player, int amount) { + playerPoints.put(player.getUniqueId(), getPlayerPoints(player) + amount); + } + + public int getPlayerPoints(Player player) { + return playerPoints.getOrDefault(player.getUniqueId(), 0); + } + + public List getRedeemableItems() { return redeemableItems; } + + public void openPointsRedemptionFor(Player player) { + GUIManager.get().openInventoryForPlayer(player, new PointsRedeemInventory()); + } + + private void loadAdvancements() { + File file = new File(puPlugin.getDataFolder(), "points.yml"); + FileConfiguration pointsConfig = new YamlConfiguration(); + try { + if (file.createNewFile()) { + ParallelUtils.log(Level.WARNING, "points.yml does not exist. Creating..."); + } + pointsConfig.load(file); + } + catch (IOException e) { + ParallelUtils.log(Level.SEVERE, "Failed to create or read points.yml\n" + e); + return; + } catch (Exception e) { + ParallelUtils.log(Level.SEVERE, "Failed to load points.yml\n" + e); + return; + } + for (String key : pointsConfig.getKeys(true)) { + if (pointsConfig.isConfigurationSection(key)) + continue; + int value = pointsConfig.getInt(key); + String[] split = key.split("\\."); + if (split.length != 2) { + ParallelUtils.log(Level.WARNING, "Invalid advancement key: " + key); + continue; + } + Advancement advancement = Bukkit.getAdvancement(new NamespacedKey(split[0], split[1])); + if (advancement == null) { + ParallelUtils.log(Level.WARNING, "Unknown advancement: " + key); + continue; + } + advancementMap.put(advancement.getKey().asString(), value); + } + ParallelUtils.log(Level.WARNING, "Loaded " + advancementMap.size() + " advancement points."); + } + + private void loadPlayerPoints() { + Path path = Path.of(puPlugin.getDataFolder().getAbsolutePath() + "/player_points.json"); + if (!path.toFile().exists()) { + ParallelUtils.log(Level.WARNING, "Points JSON file does not exist, skipping loading."); + return; + } + String data; + try { + data = Files.readString(path); + JSONParser parser = new JSONParser(); + JSONArray arr = (JSONArray)parser.parse(data); + for (Object o : arr) { + JSONObject json = (JSONObject) o; + UUID uuid = UUID.fromString((String)json.get("uuid")); + int points = ((Long)json.get("points")).intValue(); + playerPoints.put(uuid, points); + } + ParallelUtils.log(Level.INFO, "Loaded " + playerPoints.size() + " player points entries."); + } catch (IOException | NullPointerException e) { + ParallelUtils.log(Level.SEVERE, "Failed to load player points!\n" + e.getMessage()); + } catch (ParseException e) { + ParallelUtils.log(Level.SEVERE, "Failed to parse player points data!\n" + e.getMessage()); + } + } + + @SuppressWarnings("unchecked") + private void savePlayerPoints() { + Path path = Path.of(puPlugin.getDataFolder().getAbsolutePath() + "/player_points.json"); + JSONArray json = new JSONArray(); + for (Map.Entry e : playerPoints.entrySet()) { + JSONObject entry = new JSONObject(); + entry.put("uuid", e.getKey().toString()); + entry.put("points", e.getValue()); + json.add(entry); + } + try { + Files.writeString(path, json.toJSONString()); + ParallelUtils.log(Level.INFO, "Saved " + playerPoints.size() + " player points entries."); + } catch (IOException e) { + ParallelUtils.log(Level.SEVERE, "Failed to save player points entries!\n" + e.getMessage()); + } + } + + public int recalculatePlayerPoints() { + playerPoints.clear(); + Path path; + try { + //path = Path.of(puPlugin.getServer().getWorldContainer().getCanonicalPath(), "world", "advancements"); + path = Path.of(puPlugin.getServer().getWorldContainer().getCanonicalPath(), Constants.DEFAULT_WORLD, "advancements"); + ParallelUtils.log(Level.INFO, "Using path: " + path); + } catch (IOException e) { + ParallelUtils.log(Level.SEVERE, "Failed to find path to advancements folder! \n" + e.getMessage()); + return -1; + } + File[] files = path.toFile().listFiles(); + if (files == null) { + ParallelUtils.log(Level.SEVERE, "Failed to get files from advancements folder!"); + return -1; + } + for (File file : files) { + // this is horrible practice but its pretty safe to assume nothing in here will have more than 1 period + String[] split = file.getName().split("\\."); + // check it anyway + if (split.length != 2) { + ParallelUtils.log(Level.SEVERE, "A file has more than one period...somehow. Skipping!"); + continue; + } + UUID uuid; + try { + uuid = UUID.fromString(split[0]); + } catch (IllegalArgumentException e) { + ParallelUtils.log(Level.WARNING, split[0] + " failed to parse into a UUID. Skipping!"); + continue; + } + + String data; + try { + data = Files.readString(file.toPath()); + JSONParser parser = new JSONParser(); + JSONObject obj = (JSONObject) parser.parse(data); + + for (Object o : obj.entrySet()) { + // gee, who put this here + @SuppressWarnings("unchecked") + Map.Entry entry = (Map.Entry) o; + + String advancement = entry.getKey(); + // every advancement json file has the DataVersion at the very bottom + // so treat this as the "terminator" of sorts + if (advancement.equals("DataVersion")) + break; + JSONObject value = entry.getValue(); + + int points = advancementMap.getOrDefault(advancement, -1); + + if (points > -1) { + if ((Boolean)value.get("done")) { + playerPoints.put(uuid, playerPoints.getOrDefault(uuid, 0) + points); + } + } + } + } catch (IOException | NullPointerException e) { + ParallelUtils.log(Level.SEVERE, "Failed to load file " + file.getName() + "!\n" + e.getMessage()); + } catch (ParseException e) { + ParallelUtils.log(Level.SEVERE, "Failed to parse " + file.getName() + "!\n" + e.getMessage()); + } + } + return playerPoints.size(); + } + + public void loadItems() { + File itemFile = new File(puPlugin.getDataFolder(), "points_items.yml"); + FileConfiguration itemConfig = new YamlConfiguration(); + try { + if (itemFile.createNewFile()) { + ParallelUtils.log(Level.WARNING, "points_items.yml does not exist! Creating..."); + } + itemConfig.load(itemFile); + } catch (IOException e) { + ParallelUtils.log(Level.SEVERE, "Failed to create or read points_items.yml\n" + e); + // return false; + } catch (Exception e) { + ParallelUtils.log(Level.SEVERE, "Failed to load points_items.yml\n" + e); + // return false; + } + for (String key : itemConfig.getKeys(false)) { + Material material = Material.valueOf(itemConfig.getString(key + ".material")); + int modelData = itemConfig.getInt(key + ".modeldata"); + int cost = itemConfig.getInt(key + ".cost"); + String permission = itemConfig.getString(key + ".permission"); + List commands = (List)itemConfig.getList(key + ".commands"); + redeemableItems.add(new RedeemableItem(material, cost, permission, modelData, commands)); + } + ParallelUtils.log(Level.WARNING, "Loaded " + redeemableItems.size() + " redeemable items."); + } + + public static Points get() { return Instance; } + +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/points/RedeemableItem.java b/modules/src/main/java/parallelmc/parallelutils/modules/points/RedeemableItem.java new file mode 100644 index 00000000..c42cb4b2 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/points/RedeemableItem.java @@ -0,0 +1,48 @@ +package parallelmc.parallelutils.modules.points; + +import org.bukkit.Material; + +import java.util.List; + +// an item that can be redeemed for advancement points +public class RedeemableItem { + private final Material material; + + private final int modelData; + + private final int requiredPoints; + + private final String permission; + + private final List commands; + + public RedeemableItem(Material material, int requiredPoints, String permission, List commands) { + this.material = material; + this.modelData = -1; + this.requiredPoints = requiredPoints; + this.permission = permission; + this.commands = commands; + } + + public RedeemableItem(Material material, int requiredPoints, String permission, int modelData, List commands) { + this.material = material; + this.modelData = modelData; + this.requiredPoints = requiredPoints; + this.permission = permission; + this.commands = commands; + } + + public Material getMaterial() { + return material; + } + + public int getModelData() { + return modelData; + } + + public int getRequiredPoints() { return requiredPoints; } + + public String getPermission() { return permission; } + + public List getCommands() { return commands; } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/points/commands/OpenPointsRedemption.java b/modules/src/main/java/parallelmc/parallelutils/modules/points/commands/OpenPointsRedemption.java new file mode 100644 index 00000000..efa8e571 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/points/commands/OpenPointsRedemption.java @@ -0,0 +1,27 @@ +package parallelmc.parallelutils.modules.points.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.points.Points; + +public class OpenPointsRedemption implements CommandExecutor { + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (commandSender.isOp() || commandSender instanceof ConsoleCommandSender) { + if (args.length == 0) + return false; + Player player = Bukkit.getPlayer(args[0]); + if (player == null) { + commandSender.sendMessage("Could not find player " + args[0]); + return true; + } + Points.get().openPointsRedemptionFor(player); + } + return true; + } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/points/commands/RecalculatePoints.java b/modules/src/main/java/parallelmc/parallelutils/modules/points/commands/RecalculatePoints.java new file mode 100644 index 00000000..ee8afff5 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/points/commands/RecalculatePoints.java @@ -0,0 +1,39 @@ +package parallelmc.parallelutils.modules.points.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.ParallelUtils; +import parallelmc.parallelutils.modules.points.Points; + +public class RecalculatePoints implements CommandExecutor { + private final ParallelUtils puPlugin; + + public RecalculatePoints(ParallelUtils puPlugin) { + this.puPlugin = puPlugin; + } + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (commandSender instanceof Player player) { + if (!player.hasPermission("parallelutils.recalculatepoints")) { + return true; + } + } + commandSender.sendMessage("Recalculating advancement points for all players, this might take a while..."); + Bukkit.getScheduler().runTaskAsynchronously(puPlugin, () -> { + long start = System.currentTimeMillis(); + int result = Points.get().recalculatePlayerPoints(); + long end = System.currentTimeMillis(); + if (result == -1) { + commandSender.sendMessage("Failed to recalculate, see the console for any errors."); + } + else { + commandSender.sendMessage("Recalculated advancement points for " + result + " players in " + (end - start) + "ms"); + } + }); + return true; + } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/points/commands/ViewPoints.java b/modules/src/main/java/parallelmc/parallelutils/modules/points/commands/ViewPoints.java new file mode 100644 index 00000000..898c4157 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/points/commands/ViewPoints.java @@ -0,0 +1,20 @@ +package parallelmc.parallelutils.modules.points.commands; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.points.Points; + +public class ViewPoints implements CommandExecutor { + @Override + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + if (commandSender instanceof Player player) { + int points = Points.get().getPlayerPoints(player); + ParallelChat.sendParallelMessageTo(player, "You currently have " + points + " advancement points!"); + } + return true; + } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/points/events/OnAdvancementDone.java b/modules/src/main/java/parallelmc/parallelutils/modules/points/events/OnAdvancementDone.java new file mode 100644 index 00000000..b671c0ad --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/points/events/OnAdvancementDone.java @@ -0,0 +1,28 @@ +package parallelmc.parallelutils.modules.points.events; + +import org.bukkit.advancement.Advancement; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerAdvancementDoneEvent; +import parallelmc.parallelutils.ParallelUtils; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.points.Points; + +import java.util.logging.Level; + +public class OnAdvancementDone implements Listener { + @EventHandler + public void onAdvancementDone(PlayerAdvancementDoneEvent event) { + Advancement advancement = event.getAdvancement(); + Player player = event.getPlayer(); + int points = Points.get().getPointsForAdvancement(advancement); + if (points == -1) { + ParallelUtils.log(Level.WARNING, "Advancement " + advancement.getKey().asString() + " has no associated point value! Skipping..."); + return; + } + Points.get().awardPoints(player, points); + // wait 1 tick to send the message so it shows after the advancement + ParallelChat.sendDelayedParallelMessageTo(player, 1, "You've received " + points + " advancement points!"); + } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/points/gui/PointsRedeemInventory.java b/modules/src/main/java/parallelmc/parallelutils/modules/points/gui/PointsRedeemInventory.java new file mode 100644 index 00000000..ba5522d5 --- /dev/null +++ b/modules/src/main/java/parallelmc/parallelutils/modules/points/gui/PointsRedeemInventory.java @@ -0,0 +1,100 @@ +package parallelmc.parallelutils.modules.points.gui; + +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; +import parallelmc.parallelutils.modules.parallelchat.ParallelChat; +import parallelmc.parallelutils.modules.points.Points; +import parallelmc.parallelutils.modules.points.RedeemableItem; +import parallelmc.parallelutils.util.GUIInventory; + +import java.util.ArrayList; +import java.util.List; + +public class PointsRedeemInventory extends GUIInventory { + + private static List redeemableItems = new ArrayList<>(); + + public PointsRedeemInventory() { + super(45, Component.text("Advancement Points Redemption", NamedTextColor.GOLD, TextDecoration.BOLD)); + + ItemStack exit = new ItemStack(Material.BARRIER); + ItemMeta meta = exit.getItemMeta(); + meta.displayName(Component.text("Exit GUI", NamedTextColor.RED).decoration(TextDecoration.ITALIC, false)); + exit.setItemMeta(meta); + + inventory.setItem(40, exit); + } + + + @Override + public void onOpen(Player player) { + redeemableItems = Points.get().getRedeemableItems(); + int playerPoints = Points.get().getPlayerPoints(player); + ItemStack head = new ItemStack(Material.PLAYER_HEAD); + SkullMeta skull = (SkullMeta)head.getItemMeta(); + skull.displayName(Component.text(player.getName(), NamedTextColor.YELLOW).decoration(TextDecoration.ITALIC, false)); + skull.setPlayerProfile(player.getPlayerProfile()); + skull.lore(List.of(Component.text("Advancement Points: ", NamedTextColor.GOLD).decoration(TextDecoration.ITALIC, false) + .append(Component.text(playerPoints, NamedTextColor.YELLOW).decoration(TextDecoration.ITALIC, false)))); + head.setItemMeta(skull); + inventory.setItem(4, head); + + List items = Points.get().getRedeemableItems(); + int slot = 9; + for (RedeemableItem item : items) { + ItemStack i = new ItemStack(item.getMaterial()); + ItemMeta meta = i.getItemMeta(); + if (item.getModelData() != -1) { + meta.setCustomModelData(item.getModelData()); + } + meta.displayName(i.displayName().color(NamedTextColor.YELLOW).decoration(TextDecoration.BOLD, true).decoration(TextDecoration.ITALIC, false)); + List lore = List.of( + Component.empty(), + Component.text("Required Points: ", NamedTextColor.GOLD).decoration(TextDecoration.ITALIC, false) + .append(Component.text(item.getRequiredPoints(), playerPoints >= item.getRequiredPoints() ? NamedTextColor.GREEN : NamedTextColor.RED)) + + ); + meta.lore(lore); + i.setItemMeta(meta); + inventory.setItem(slot, i); + slot++; + } + } + + @Override + public void onSlotClicked(Player player, int slotNum, ItemStack itemClicked) { + if (slotNum > 8 && slotNum < 36) { + RedeemableItem clicked = redeemableItems.get(slotNum - 9); + // yes this looks very stupid I know + if (player.hasPermission(clicked.getPermission())) { + player.playSound(player.getLocation(), Sound.BLOCK_FIRE_EXTINGUISH, 1, 1); + player.closeInventory(); + ParallelChat.sendParallelMessageTo(player, "You have already redeemed that item!"); + return; + } + if (player.getInventory().firstEmpty() == -1) { + player.playSound(player.getLocation(), Sound.BLOCK_FIRE_EXTINGUISH, 1, 1); + player.closeInventory(); + ParallelChat.sendParallelMessageTo(player, "Your inventory is too full!"); + return; + } + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 1); + player.closeInventory(); + ParallelChat.sendParallelMessageTo(player, Component.text("Successfully redeemed the ", NamedTextColor.GREEN).append(itemClicked.displayName())); + for (String s : redeemableItems.get(slotNum - 9).getCommands()) { + Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), s.replace("%player%", player.getName())); + } + } + if (slotNum == 49) { + player.closeInventory(); + } + } +} diff --git a/modules/src/main/java/parallelmc/parallelutils/modules/sunkentreasure/events/TreasureChecker.java b/modules/src/main/java/parallelmc/parallelutils/modules/sunkentreasure/events/TreasureChecker.java index 02bb503b..5786df44 100644 --- a/modules/src/main/java/parallelmc/parallelutils/modules/sunkentreasure/events/TreasureChecker.java +++ b/modules/src/main/java/parallelmc/parallelutils/modules/sunkentreasure/events/TreasureChecker.java @@ -49,7 +49,7 @@ public void onBreakBlock(BlockBreakEvent event) { ItemStack heldItem = player.getInventory().getItemInMainHand(); Map enchantments = heldItem.getEnchantments(); - Integer enchantVal = enchantments.get(Enchantment.LOOT_BONUS_BLOCKS); + Integer enchantVal = enchantments.get(Enchantment.FORTUNE); ParallelUtils.log(Level.INFO, "" + enchantVal); diff --git a/settings.gradle b/settings.gradle index 59907575..7bb50d64 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,7 +1,7 @@ pluginManagement { repositories { gradlePluginPortal() - maven { url = "https://papermc.io/repo/repository/maven-public/" } + maven { url = "https://repo.papermc.io/repository/maven-public/" } } }