1
0
mirror of https://github.com/mcMMO-Dev/mcMMO.git synced 2026-02-20 18:53:11 +01:00

Compare commits

...

30 Commits

Author SHA1 Message Date
Warrior
be613bc9b6 Fix archery entity tracking failing often on Folia (#5260) 2026-02-13 13:14:54 -08:00
nossr50
99c2ec2959 changelog update again 2026-02-07 15:02:07 -08:00
nossr50
3a7d605450 changelog update 2026-02-07 14:58:39 -08:00
dependabot[bot]
0e0e2efb6d Bump org.assertj:assertj-core from 3.25.3 to 3.27.7 (#5259)
Bumps [org.assertj:assertj-core](https://github.com/assertj/assertj) from 3.25.3 to 3.27.7.
- [Release notes](https://github.com/assertj/assertj/releases)
- [Commits](https://github.com/assertj/assertj/compare/assertj-build-3.25.3...assertj-build-3.27.7)

---
updated-dependencies:
- dependency-name: org.assertj:assertj-core
  dependency-version: 3.27.7
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-07 14:57:38 -08:00
Warrior
a6e12c4cc1 Replace use of getIngredientList/Map (#5257) 2026-02-07 14:56:01 -08:00
Momshroom
33597e1db1 Set max display of spear master to two decimals (#5258)
* Fixed NPE when party creation doesn't use password.  Solves bug reported here: https://discord.com/channels/526933440214597677/526938425161416727/1213131451235827753

Signed-off-by: Momshroom <Momshroom@gmail.com>

* Actually don't reduce sapling drop if KnockOnWood unlocked. (Prior fix didn't)

Signed-off-by: Momshroom <Momshroom@gmail.com>

* removed unnecessary getPlayer() calls.
clarified comment
clarified location variable name

Signed-off-by: Momshroom <Momshroom@gmail.com>

* made new method more generic.
Removed no longer needed dropString variable

Signed-off-by: Momshroom <Momshroom@gmail.com>

* Added new potions to alchemy. Borrowed some config info from here: https://discord.com/channels/526933440214597677/547110990278426629/1291771464726478869

Signed-off-by: Momshroom <Momshroom@gmail.com>

* Revert regression.  Fixes salvage permissions by material type

* Format Spear Mastery to 2 decimal places in /spears output, fixing floating point issue causing long output

---------

Signed-off-by: Momshroom <Momshroom@gmail.com>
2026-02-07 14:54:58 -08:00
nossr50
c94f10568d refactor a bunch of unused, outdated, or unnecessary tech debt for compatibility for older versions that are no longer supported or were never supported 2026-02-07 14:54:25 -08:00
Nick
db985bcc52 Add spears to fill<Material>ToolsWhiteList() methods (#5254)
These methods fill the matching `<material>Tools` HashSets. Adding
corresponding spear items will allow the `is<Material>Tool()` methods
to return `true` if checking a spear of the matching material. This is
to support the Repair and Salvage configurations pulling the
appropriate repair/salvage material type if one is not specified in the
plugin's configuration files.
2026-02-05 18:27:42 -08:00
nossr50
1b8897b8ab version is outdated errors tweaked a bit 2026-01-31 11:40:11 -08:00
nossr50
95026e6016 improved spears dmg detection and fixed more errors with Excellent Enchants 2026-01-31 11:20:35 -08:00
nossr50
7738c45b72 minimum MC version is now 1.20.4 2026-01-31 11:19:36 -08:00
nossr50
080d407c14 another fix for compatibility with Excellent Enchants 2026-01-27 13:03:50 -08:00
nossr50
8f8d7756ca back to dev versions 2026-01-26 11:39:43 -08:00
nossr50
e24b5b0b75 2.2.049 2026-01-26 11:33:11 -08:00
nossr50
8ac3198386 improved compatibility with Excellent Enchants 2026-01-26 11:23:45 -08:00
nossr50
4c33f76404 update changelog 2026-01-25 12:22:45 -08:00
Warrior
77dac78bec Fix leftover sync tp in taming subskill (#5248) 2026-01-25 11:22:31 -08:00
nossr50
9c9e5313ca prevent item stack size from going outside normal bounds 2026-01-17 13:38:29 -08:00
nossr50
db5654dbe6 Merge branch 'master' of https://github.com/mcMMO-Dev/mcMMO 2026-01-17 13:31:28 -08:00
nossr50
c14b82fff1 updated notes 2026-01-17 13:31:05 -08:00
nossr50
5a9390f88f allow combat abilities to activate with spear in off-hand 2026-01-17 13:27:14 -08:00
Dominykas
4a890a28a1 Add support for sweet berry bush harvesting and replanting (#5238)
* Add support for sweet berry bush harvesting and replanting

* Accidentally left my test code for torchflower replanting.
2026-01-17 12:51:39 -08:00
Warrior
8b7cffbe49 Bump mockito version to fix compiling on java 25 (#5247) 2026-01-17 12:38:28 -08:00
nossr50
921fa713b8 back to dev builds 2025-12-30 14:47:25 -08:00
Remski01
ebe49154b4 Fix copper XP in experience.yml (#5242)
This should fix that Copper does not gives xp.
2025-12-30 14:46:55 -08:00
nossr50
1036aabc32 2.2.048 hotfix for spear manager exception Fixes #5237 2025-12-14 16:35:54 -08:00
nossr50
e2f18a9e89 2.2.047 hotfix for spears combat offhand oddities 2025-12-14 14:42:59 -08:00
nossr50
694de2ff5f fix overlooked spears interaction with other melee skills 2025-12-14 14:36:41 -08:00
nossr50
cc1a581192 back to developer snapshots 2025-12-14 14:29:38 -08:00
Robert Alan Chapton
b15365e978 Spears update (#5236)
Spears update
2025-12-14 14:23:25 -08:00
93 changed files with 6344 additions and 4878 deletions

View File

@@ -1,3 +1,75 @@
Version 2.2.050
Minimum supported Minecraft version raised to 1.20.5 (see notes)
Improved Spears damage detection (see notes)
Spear abilities can now trigger from off-hand attacks with the Spear
Further improved compatibility with Excellent Enchants (fixed more errors)
Fixed Spears command showing too many decimal places in some circumstances (Thanks Momshroom)
(Codebase) Updated Recipe API to use alternative methods (Thanks Warriorrrr)
(Codebase) Removed a bunch of unused code
(Codebase) Refactored code around evaluating Minecraft server version
NOTES:
From 2.2.050 onwards, mcMMO will require Minecraft versions 1.20.5 or newer.
Every now and then I raise the minimum supported Minecraft version to reduce maintenance burden of supporting older versions.
mcMMO no longer monitors PlayerAnimationEvent, it turns out Spigot has a DamageType just for Spear damage, avoiding the need to track player arm swings.
I changed how mcMMO detects the game version slightly, now if it fails to determine the game version it will make the assumption you are on the minimum supported version and print an error, this is just a failsafe in case the version detection mechanism ever fails.
Version 2.2.049
Combat abilities work with spear in off-hand again (see notes)
Sweet berry bushes now work with Herbalism (thanks dnocturne)
Fixed copper items not giving XP for Repair (thanks Remski01)
Fixed rare edge case where mcMMO could drop items with stack size set outside normal bounds
Fixed a bug where tamed wolves could cause errors on Folia (thanks Warriorrrr)
Improved compatibility with Excellent Enchants (fixed error spam from Flame Walker)
(Codebase) Fixed unit tests when building mcMMO with Java 25 (thanks Warriorrrr)
NOTES:
As a semi-permanent work around, mcMMO keeps track of when players swing their weapon, if they have swung it recently enough combat skills will work even if you have a spear in your off-hand.
Prior to this patch, mcMMO disabled combat abilities when spear was in off-hand to prevent the off-hand spear charge from applying combat abilities from other skills, as mcMMO was unable to determine which item (mainhand vs offhand) caused the damage from the event information alone.
Version 2.2.048
Fixed error when loading Spears skill manager on older Minecraft versions
Fixed error when using /spears command on an older Minecraft version
Version 2.2.047
Fix bug where off-hand spears damage could be attributed to various combat skills and trigger their abilities
NOTE:
Sorry guys, noticed this right after pushing 2.2.046, for now melee combat skills won't trigger if you have a spear in your off-hand as a temporary work around to mcMMO being unable to differentiate between main hand and off hand damage.
I'm working on a more robust solution to this issue, so stay tuned.
What this means in laymen's terms is if you are using a sword main hand, and you have a spear off hand, mcMMO has temporarily disabled your main hand abilities from triggering to avoid issues where off hand spear damage benefits from it and triggers abilities belonging to other weapons.
Version 2.2.046
Added Spears combat skill
Added Spears to repair.vanilla.yml and salvage.vanilla.yml (see notes)
Added various permissions related to Spears
Added /spears skill command
Added Nautilus to taming XP in experience.yml
Added Camel_Husk to taming XP in experience.yml
Added Camel_Husk to combat XP in experience.yml
Added Parched to combat XP in experience.yml
Fixed bug where converting from SQL to FlatFile would not copy data for tridents, crossbows, maces, or spears
(Codebase) Added docker-based unit tests for SQL databases (see notes)
(Codebase) Large refactor to both SQLDatabaseManager and FlatFileDatabaseManager
(Codebase) Database related errors are now more descriptive and have had their logging improved
NOTES:
This update had a lot of changes behind the scenes, please report any bugs you find to our GitHub issues page!
You will need to manually update repair.vanilla.yml and salvage.vanilla.yml to get support for Spears, or...
If you want to update salvage/repair configs the easy way, you simply can delete these config files to have mcMMO regenerate them with the new entries.
If you don't want to delete them, you can find the default values for these config files in the defaults folder at plugins\mcMMO\defaults after running this mcMMO update at least once.
You can use this default file to copy paste if you please.
Docker is ONLY required for developers compiling mcMMO from source code and ONLY for running SQL-related unit tests.
mcMMO itself does NOT require Docker to run, and servers using prebuilt releases are completely unaffected.
New SQL database unit tests use Testcontainers to spin up temporary MySQL/MariaDB instances for testing purposes.
These containers are created at test time and are never used at runtime.
If you compile mcMMO locally and do not have Docker installed, SQL-related unit tests may fail.
In this case, you can safely compile with -DskipTests to skip unit tests entirely.
Skipping tests has no impact on mcMMO functionality when running on a server.
Known Issues:
I ran into an issue where having a spear in the offhand while the main hand is empty causes attacks to be incorrectly classified as unarmed. This allows unarmed abilities to apply to spear damage. As a temporary measure, Ive disabled unarmed skills from applying to combat when a spear is equipped in the offhand while I investigate a more robust solution.
Version 2.2.045 Version 2.2.045
Green Thumb now replants some crops it was failing to replant before (see notes) Green Thumb now replants some crops it was failing to replant before (see notes)
Green Thumb now replants harvested plants faster Green Thumb now replants harvested plants faster

150
pom.xml
View File

@@ -1,8 +1,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.gmail.nossr50.mcMMO</groupId> <groupId>com.gmail.nossr50.mcMMO</groupId>
<artifactId>mcMMO</artifactId> <artifactId>mcMMO</artifactId>
<version>2.2.046-SNAPSHOT</version> <version>2.2.050-SNAPSHOT</version>
<name>mcMMO</name> <name>mcMMO</name>
<url>https://github.com/mcMMO-Dev/mcMMO</url> <url>https://github.com/mcMMO-Dev/mcMMO</url>
<scm> <scm>
@@ -13,8 +15,8 @@
</scm> </scm>
<properties> <properties>
<!-- <spigot.version>1.19-R0.1-SNAPSHOT</spigot.version>--> <spigot.version>1.20.5-R0.1-SNAPSHOT</spigot.version>
<spigot.version>1.21.10-R0.1-SNAPSHOT</spigot.version> <!-- <spigot.version>1.21.11-R0.1-SNAPSHOT</spigot.version>-->
<kyori.adventure.version>4.23.0</kyori.adventure.version> <kyori.adventure.version>4.23.0</kyori.adventure.version>
<kyori.adventure.platform.version>4.4.1-SNAPSHOT</kyori.adventure.platform.version> <kyori.adventure.platform.version>4.4.1-SNAPSHOT</kyori.adventure.platform.version>
<kyori.option.version>1.1.0</kyori.option.version> <kyori.option.version>1.1.0</kyori.option.version>
@@ -87,6 +89,17 @@
<propertiesEncoding>UTF-8</propertiesEncoding> <propertiesEncoding>UTF-8</propertiesEncoding>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version> <version>3.2.5</version>
@@ -95,6 +108,8 @@
<junitArtifactName>org.junit.jupiter:junit-jupiter</junitArtifactName> <junitArtifactName>org.junit.jupiter:junit-jupiter</junitArtifactName>
<trimStackTrace>false</trimStackTrace> <trimStackTrace>false</trimStackTrace>
<excludedGroups>skip</excludedGroups> <excludedGroups>skip</excludedGroups>
<!-- https://javadoc.io/doc/org.mockito/mockito-core/latest/org.mockito/org/mockito/Mockito.html#0.3 -->
<argLine>-javaagent:${org.mockito:mockito-core:jar}</argLine>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
@@ -182,11 +197,13 @@
</relocation> </relocation>
<relocation> <relocation>
<pattern>co.aikar.commands</pattern> <pattern>co.aikar.commands</pattern>
<shadedPattern>com.gmail.nossr50.mcmmo.acf</shadedPattern> <!-- Replace this --> <shadedPattern>com.gmail.nossr50.mcmmo.acf
</shadedPattern> <!-- Replace this -->
</relocation> </relocation>
<relocation> <relocation>
<pattern>co.aikar.locales</pattern> <pattern>co.aikar.locales</pattern>
<shadedPattern>com.gmail.nossr50.mcmmo.locales</shadedPattern> <!-- Replace this --> <shadedPattern>com.gmail.nossr50.mcmmo.locales
</shadedPattern> <!-- Replace this -->
</relocation> </relocation>
<relocation> <relocation>
<pattern>org.apache.commons.logging</pattern> <pattern>org.apache.commons.logging</pattern>
@@ -194,7 +211,8 @@
</relocation> </relocation>
<relocation> <relocation>
<pattern>org.apache.juli</pattern> <pattern>org.apache.juli</pattern>
<shadedPattern>com.gmail.nossr50.mcmmo.database.tomcat.juli</shadedPattern> <shadedPattern>com.gmail.nossr50.mcmmo.database.tomcat.juli
</shadedPattern>
</relocation> </relocation>
<relocation> <relocation>
<pattern>org.apache.tomcat</pattern> <pattern>org.apache.tomcat</pattern>
@@ -283,14 +301,28 @@
<dependency> <dependency>
<groupId>org.assertj</groupId> <groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId> <artifactId>assertj-core</artifactId>
<version>3.25.3</version> <version>3.27.7</version>
<scope>test</scope> <scope>test</scope>
<exclusions>
<!-- Exclude bytebuddy, messes with tests on java 25+ -->
<exclusion>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.dmulloy2</groupId> <groupId>net.dmulloy2</groupId>
<artifactId>ProtocolLib</artifactId> <artifactId>ProtocolLib</artifactId>
<version>5.3.0</version> <version>5.3.0</version>
<scope>compile</scope> <scope>compile</scope>
<exclusions>
<!-- Exclude bytebuddy, messes with tests on java 25+ -->
<exclusion>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</exclusion>
</exclusions>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
@@ -385,11 +417,11 @@
<version>3.0.2</version> <version>3.0.2</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<!-- <dependency>--> <!-- <dependency>-->
<!-- <groupId>io.papermc.paper</groupId>--> <!-- <groupId>io.papermc.paper</groupId>-->
<!-- <artifactId>paper-api</artifactId>--> <!-- <artifactId>paper-api</artifactId>-->
<!-- <version>1.21.8-R0.1-SNAPSHOT</version>--> <!-- <version>1.21.11-R0.1-SNAPSHOT</version>-->
<!-- </dependency>--> <!-- </dependency>-->
<dependency> <dependency>
<groupId>org.spigotmc</groupId> <groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId> <artifactId>spigot-api</artifactId>
@@ -426,28 +458,88 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<!-- JUnit 5 -->
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>
<version>5.11.0-M2</version> <version>5.11.0</version>
<scope>test</scope>
</dependency>
<!-- Testcontainers core -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
<!-- Testcontainers JUnit Jupiter integration -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
<!-- Log4j core for tests -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.25.2</version>
<scope>test</scope>
</dependency>
<!-- Log4j API -->
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.25.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.25.2</version>
<scope>test</scope>
</dependency>
<!-- MySQL Testcontainers module -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-mysql</artifactId>
<scope>test</scope>
</dependency>
<!-- MariaDB Testcontainers module -->
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-mariadb</artifactId>
<scope>test</scope>
</dependency>
<!-- MySQL JDBC driver -->
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.5.0</version>
<scope>test</scope>
</dependency>
<!-- MariaDB JDBC driver -->
<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.5.6</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId> <artifactId>mockito-core</artifactId>
<version>5.12.0</version> <version>5.21.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>5.2.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.tomcat</groupId> <groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId> <artifactId>tomcat-jdbc</artifactId>
<version>10.1.24</version> <version>11.0.14</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -458,7 +550,8 @@
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>33.2.0-jre</version> <!-- At this time Spigot is including 29.0 Guava classes that we are using --> <version>33.2.0-jre
</version> <!-- At this time Spigot is including 29.0 Guava classes that we are using -->
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -468,4 +561,15 @@
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-bom</artifactId>
<version>2.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project> </project>

View File

@@ -1,30 +0,0 @@
package com.gmail.nossr50.commands.admin;
import com.gmail.nossr50.mcMMO;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class CompatibilityCommand implements CommandExecutor {
/**
* Executes the given command, returning its success.
* <br>
* If false is returned, then the "usage" plugin.yml entry for this command (if defined) will be
* sent to the player.
*
* @param commandSender Source of the command
* @param command Command which was executed
* @param s Alias of the command which was used
* @param strings Passed command arguments
* @return true if a valid command, otherwise false
*/
@Override
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command,
@NotNull String s,
@NotNull String[] strings) {
mcMMO.getCompatibilityManager().reportCompatibilityStatus(commandSender);
return true;
}
}

View File

@@ -44,13 +44,6 @@ public class McTopCommand implements TabExecutor {
return true; return true;
} }
// Check if the command is for Maces but the MC version is not correct
if (skill == PrimarySkillType.MACES
&& !mcMMO.getCompatibilityManager().getMinecraftGameVersion()
.isAtLeast(1, 21, 0)) {
return true;
}
display(1, skill, sender, command); display(1, skill, sender, command);
return true; return true;
@@ -65,13 +58,6 @@ public class McTopCommand implements TabExecutor {
return true; return true;
} }
// Check if the command is for Maces but the MC version is not correct
if (skill == PrimarySkillType.MACES
&& !mcMMO.getCompatibilityManager().getMinecraftGameVersion()
.isAtLeast(1, 21, 0)) {
return true;
}
display(Math.abs(Integer.parseInt(args[1])), skill, sender, command); display(Math.abs(Integer.parseInt(args[1])), skill, sender, command);
return true; return true;

View File

@@ -73,7 +73,7 @@ public class AcrobaticsCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.ACROBATICS); PrimarySkillType.ACROBATICS);
return textComponents; return textComponents;

View File

@@ -92,7 +92,7 @@ public class AlchemyCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.ALCHEMY); PrimarySkillType.ALCHEMY);
return textComponents; return textComponents;

View File

@@ -93,7 +93,7 @@ public class ArcheryCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.ARCHERY); PrimarySkillType.ARCHERY);
return textComponents; return textComponents;

View File

@@ -119,7 +119,7 @@ public class AxesCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
final List<Component> textComponents = new ArrayList<>(); final List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.AXES); PrimarySkillType.AXES);
return textComponents; return textComponents;

View File

@@ -68,7 +68,7 @@ public class CrossbowsCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.CROSSBOWS); PrimarySkillType.CROSSBOWS);
return textComponents; return textComponents;

View File

@@ -71,7 +71,7 @@ public class ExcavationCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.EXCAVATION); PrimarySkillType.EXCAVATION);
return textComponents; return textComponents;

View File

@@ -1,13 +1,13 @@
package com.gmail.nossr50.commands.skills; package com.gmail.nossr50.commands.skills;
import static com.gmail.nossr50.util.Permissions.canUseSubSkill;
import com.gmail.nossr50.config.treasure.FishingTreasureConfig; import com.gmail.nossr50.config.treasure.FishingTreasureConfig;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType; import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType; import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.datatypes.treasure.Rarity; import com.gmail.nossr50.datatypes.treasure.Rarity;
import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.skills.fishing.FishingManager; import com.gmail.nossr50.skills.fishing.FishingManager;
import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.random.Probability; import com.gmail.nossr50.util.random.Probability;
import com.gmail.nossr50.util.random.ProbabilityUtil; import com.gmail.nossr50.util.random.ProbabilityUtil;
import com.gmail.nossr50.util.skills.RankUtils; import com.gmail.nossr50.util.skills.RankUtils;
@@ -118,16 +118,14 @@ public class FishingCommand extends SkillCommand {
@Override @Override
protected void permissionsCheck(Player player) { protected void permissionsCheck(Player player) {
canTreasureHunt = Permissions.canUseSubSkill(player, SubSkillType.FISHING_TREASURE_HUNTER); canTreasureHunt = canUseSubSkill(player, SubSkillType.FISHING_TREASURE_HUNTER);
canMagicHunt = Permissions.canUseSubSkill(player, SubSkillType.FISHING_MAGIC_HUNTER) canMagicHunt = canUseSubSkill(player, SubSkillType.FISHING_MAGIC_HUNTER)
&& Permissions.canUseSubSkill(player, SubSkillType.FISHING_TREASURE_HUNTER); && canUseSubSkill(player, SubSkillType.FISHING_TREASURE_HUNTER);
canShake = Permissions.canUseSubSkill(player, SubSkillType.FISHING_SHAKE); canShake = canUseSubSkill(player, SubSkillType.FISHING_SHAKE);
canFishermansDiet = Permissions.canUseSubSkill(player, canFishermansDiet = canUseSubSkill(player,
SubSkillType.FISHING_FISHERMANS_DIET); SubSkillType.FISHING_FISHERMANS_DIET);
canMasterAngler = canMasterAngler = canUseSubSkill(player, SubSkillType.FISHING_MASTER_ANGLER);
mcMMO.getCompatibilityManager().getMasterAnglerCompatibilityLayer() != null canIceFish = canUseSubSkill(player, SubSkillType.FISHING_ICE_FISHING);
&& Permissions.canUseSubSkill(player, SubSkillType.FISHING_MASTER_ANGLER);
canIceFish = Permissions.canUseSubSkill(player, SubSkillType.FISHING_ICE_FISHING);
} }
@Override @Override
@@ -185,7 +183,7 @@ public class FishingCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.FISHING); PrimarySkillType.FISHING);
return textComponents; return textComponents;

View File

@@ -187,7 +187,7 @@ public class HerbalismCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.HERBALISM); PrimarySkillType.HERBALISM);
return textComponents; return textComponents;

View File

@@ -23,7 +23,8 @@ public class MacesCommand extends SkillCommand {
super(PrimarySkillType.MACES); super(PrimarySkillType.MACES);
} }
String crippleChanceToApply, crippleChanceToApplyLucky, crippleLengthAgainstPlayers, crippleLengthAgainstMobs; String crippleChanceToApply, crippleChanceToApplyLucky, crippleLengthAgainstPlayers,
crippleLengthAgainstMobs;
@Override @Override
protected void dataCalculations(Player player, float skillValue) { protected void dataCalculations(Player player, float skillValue) {
@@ -33,7 +34,6 @@ public class MacesCommand extends SkillCommand {
MacesManager.getCrippleTickDuration(true) / 20.0D); MacesManager.getCrippleTickDuration(true) / 20.0D);
crippleLengthAgainstMobs = String.valueOf( crippleLengthAgainstMobs = String.valueOf(
MacesManager.getCrippleTickDuration(false) / 20.0D); MacesManager.getCrippleTickDuration(false) / 20.0D);
crippleChanceToApply = crippleChanceToApply =
mcMMO.p.getAdvancedConfig().getCrippleChanceToApplyOnHit(crippleRank) + "%"; mcMMO.p.getAdvancedConfig().getCrippleChanceToApplyOnHit(crippleRank) + "%";
crippleChanceToApplyLucky = String.valueOf( crippleChanceToApplyLucky = String.valueOf(
@@ -77,7 +77,7 @@ public class MacesCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.MACES); PrimarySkillType.MACES);
return textComponents; return textComponents;

View File

@@ -144,7 +144,7 @@ public class MiningCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.MINING); PrimarySkillType.MINING);
return textComponents; return textComponents;

View File

@@ -134,7 +134,7 @@ public class RepairCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.REPAIR); PrimarySkillType.REPAIR);
return textComponents; return textComponents;

View File

@@ -72,7 +72,7 @@ public class SalvageCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.SALVAGE); PrimarySkillType.SALVAGE);
return textComponents; return textComponents;

View File

@@ -97,7 +97,7 @@ public class SmeltingCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.SMELTING); PrimarySkillType.SMELTING);
return textComponents; return textComponents;

View File

@@ -0,0 +1,85 @@
package com.gmail.nossr50.commands.skills;
import static com.gmail.nossr50.datatypes.skills.SubSkillType.SPEARS_MOMENTUM;
import static com.gmail.nossr50.datatypes.skills.SubSkillType.SPEARS_SPEARS_LIMIT_BREAK;
import static com.gmail.nossr50.datatypes.skills.SubSkillType.SPEARS_SPEAR_MASTERY;
import static com.gmail.nossr50.util.skills.SkillUtils.canUseSubskill;
import static com.gmail.nossr50.util.text.TextComponentFactory.appendSubSkillTextComponents;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.skills.spears.SpearsManager;
import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.skills.CombatUtils;
import com.gmail.nossr50.util.skills.RankUtils;
import com.gmail.nossr50.util.skills.SkillUtils;
import java.util.ArrayList;
import java.util.List;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
public class SpearsCommand extends SkillCommand {
public SpearsCommand() {
super(PrimarySkillType.SPEARS);
}
String momentumChanceToApply, momentumChanceToApplyLucky, momentumDuration;
@Override
protected void dataCalculations(Player player, float skillValue) {
if (SkillUtils.canUseSubskill(player, SPEARS_MOMENTUM)) {
int momentumRank = RankUtils.getRank(player, SPEARS_MOMENTUM);
momentumDuration = String.valueOf(
SpearsManager.getMomentumTickDuration(momentumRank) / 20.0D);
momentumChanceToApply =
mcMMO.p.getAdvancedConfig().getMomentumChanceToApplyOnHit(momentumRank) + "%";
momentumChanceToApplyLucky = String.valueOf(
mcMMO.p.getAdvancedConfig().getMomentumChanceToApplyOnHit(momentumRank) * 1.33);
}
}
@Override
protected void permissionsCheck(Player player) {
}
@Override
protected List<String> statsDisplay(Player player, float skillValue, boolean hasEndurance,
boolean isLucky) {
final SpearsManager spearsManager = UserManager.getPlayer(player).getSpearsManager();
final double spearMasteryBonusDmg = spearsManager.getSpearMasteryBonusDamage();
List<String> messages = new ArrayList<>();
if (canUseSubskill(player, SPEARS_SPEARS_LIMIT_BREAK)) {
messages.add(getStatMessage(SPEARS_SPEARS_LIMIT_BREAK,
String.valueOf(CombatUtils.getLimitBreakDamageAgainstQuality(player,
SPEARS_SPEARS_LIMIT_BREAK, 1000))));
}
if (canUseSubskill(player, SPEARS_SPEAR_MASTERY)) {
messages.add(getStatMessage(SPEARS_SPEAR_MASTERY,
String.format("%.2f", spearMasteryBonusDmg)));
}
if (SkillUtils.canUseSubskill(player, SPEARS_MOMENTUM)) {
messages.add(getStatMessage(SPEARS_MOMENTUM, momentumChanceToApply)
+ (isLucky ? LocaleLoader.getString("Perks.Lucky.Bonus",
momentumChanceToApplyLucky) : ""));
messages.add(getStatMessage(true, true, SPEARS_MOMENTUM, momentumDuration));
}
return messages;
}
@Override
protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>();
appendSubSkillTextComponents(player, textComponents, PrimarySkillType.SPEARS);
return textComponents;
}
}

View File

@@ -22,8 +22,8 @@ public class SwordsCommand extends SkillCommand {
private String serratedStrikesLengthEndurance; private String serratedStrikesLengthEndurance;
private String rupturePureTickDamageAgainstPlayers, rupturePureTickDamageAgainstMobs, private String rupturePureTickDamageAgainstPlayers, rupturePureTickDamageAgainstMobs,
ruptureExplosionDamageAgainstPlayers, ruptureExplosionDamageAgainstMobs, ruptureLengthSecondsAgainstPlayers, ruptureLengthSecondsAgainstMobs,
ruptureLengthSecondsAgainstPlayers, ruptureLengthSecondsAgainstMobs, ruptureChanceToApply, ruptureChanceToApplyLucky; ruptureChanceToApply, ruptureChanceToApplyLucky;
private boolean canCounter; private boolean canCounter;
private boolean canSerratedStrike; private boolean canSerratedStrike;
@@ -56,11 +56,6 @@ public class SwordsCommand extends SkillCommand {
rupturePureTickDamageAgainstMobs = String.valueOf( rupturePureTickDamageAgainstMobs = String.valueOf(
mcMMO.p.getAdvancedConfig().getRuptureTickDamage(false, ruptureRank)); mcMMO.p.getAdvancedConfig().getRuptureTickDamage(false, ruptureRank));
ruptureExplosionDamageAgainstPlayers = String.valueOf(
mcMMO.p.getAdvancedConfig().getRuptureExplosionDamage(true, ruptureRank));
ruptureExplosionDamageAgainstMobs = String.valueOf(
mcMMO.p.getAdvancedConfig().getRuptureExplosionDamage(false, ruptureRank));
ruptureChanceToApply = ruptureChanceToApply =
mcMMO.p.getAdvancedConfig().getRuptureChanceToApplyOnHit(ruptureRank) + "%"; mcMMO.p.getAdvancedConfig().getRuptureChanceToApplyOnHit(ruptureRank) + "%";
ruptureChanceToApplyLucky = String.valueOf( ruptureChanceToApplyLucky = String.valueOf(
@@ -105,7 +100,6 @@ public class SwordsCommand extends SkillCommand {
messages.add(LocaleLoader.getString("Swords.SubSkill.Rupture.Stat.TickDamage", messages.add(LocaleLoader.getString("Swords.SubSkill.Rupture.Stat.TickDamage",
rupturePureTickDamageAgainstPlayers, rupturePureTickDamageAgainstMobs)); rupturePureTickDamageAgainstPlayers, rupturePureTickDamageAgainstMobs));
// messages.add(LocaleLoader.getString("Swords.SubSkill.Rupture.Stat.ExplosionDamage", ruptureExplosionDamageAgainstPlayers, ruptureExplosionDamageAgainstMobs));
messages.add(LocaleLoader.getString("Swords.Combat.Rupture.Note.Update.One")); messages.add(LocaleLoader.getString("Swords.Combat.Rupture.Note.Update.One"));
} }
@@ -134,7 +128,7 @@ public class SwordsCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.SWORDS); PrimarySkillType.SWORDS);
return textComponents; return textComponents;

View File

@@ -115,7 +115,7 @@ public class TamingCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, this.skill); TextComponentFactory.appendSubSkillTextComponents(player, textComponents, this.skill);
return textComponents; return textComponents;
} }

View File

@@ -50,7 +50,7 @@ public class TridentsCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.TRIDENTS); PrimarySkillType.TRIDENTS);
return textComponents; return textComponents;

View File

@@ -136,7 +136,7 @@ public class UnarmedCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.UNARMED); PrimarySkillType.UNARMED);
return textComponents; return textComponents;

View File

@@ -123,7 +123,7 @@ public class WoodcuttingCommand extends SkillCommand {
protected List<Component> getTextComponents(Player player) { protected List<Component> getTextComponents(Player player) {
List<Component> textComponents = new ArrayList<>(); List<Component> textComponents = new ArrayList<>();
TextComponentFactory.getSubSkillTextComponents(player, textComponents, TextComponentFactory.appendSubSkillTextComponents(player, textComponents,
PrimarySkillType.WOODCUTTING); PrimarySkillType.WOODCUTTING);
return textComponents; return textComponents;

View File

@@ -11,6 +11,7 @@ import net.md_5.bungee.api.ChatColor;
public class AdvancedConfig extends BukkitConfig { public class AdvancedConfig extends BukkitConfig {
int[] defaultCrippleValues = new int[]{10, 15, 20, 25}; int[] defaultCrippleValues = new int[]{10, 15, 20, 25};
int[] defaultMomentumValues = new int[]{5, 10, 15, 20, 25, 30, 35, 40, 45, 50};
public AdvancedConfig(File dataFolder) { public AdvancedConfig(File dataFolder) {
super("advanced.yml", dataFolder); super("advanced.yml", dataFolder);
@@ -884,7 +885,17 @@ public class AdvancedConfig extends BukkitConfig {
/* MACES */ /* MACES */
public double getCrippleChanceToApplyOnHit(int rank) { public double getCrippleChanceToApplyOnHit(int rank) {
String root = "Skills.Maces.Cripple.Chance_To_Apply_On_Hit.Rank_"; return config.getDouble("Skills.Maces.Cripple.Chance_To_Apply_On_Hit.Rank_" + rank,
return config.getDouble(root + rank, defaultCrippleValues[rank - 1]); defaultCrippleValues[rank - 1]);
}
/* SPEARS */
public double getMomentumChanceToApplyOnHit(int rank) {
return config.getDouble("Skills.Spears.Momentum.Chance_To_Apply_On_Hit.Rank_" + rank,
defaultMomentumValues[rank - 1]);
}
public double getSpearMasteryRankDamageMultiplier() {
return config.getDouble("Skills.Spears.SpearMastery.Rank_Damage_Multiplier", 0.4D);
} }
} }

View File

@@ -424,10 +424,6 @@ public class GeneralConfig extends BukkitConfig {
return config.getBoolean("MySQL.Server.SSL", true); return config.getBoolean("MySQL.Server.SSL", true);
} }
public boolean getMySQLDebug() {
return config.getBoolean("MySQL.Debug", false);
}
public boolean getMySQLPublicKeyRetrieval() { public boolean getMySQLPublicKeyRetrieval() {
return config.getBoolean("MySQL.Server.allowPublicKeyRetrieval", true); return config.getBoolean("MySQL.Server.allowPublicKeyRetrieval", true);
} }

View File

@@ -3,13 +3,11 @@ package com.gmail.nossr50.config.skills.alchemy;
import static com.gmail.nossr50.util.ItemUtils.customName; import static com.gmail.nossr50.util.ItemUtils.customName;
import static com.gmail.nossr50.util.PotionUtil.matchPotionType; import static com.gmail.nossr50.util.PotionUtil.matchPotionType;
import static com.gmail.nossr50.util.PotionUtil.setBasePotionType; import static com.gmail.nossr50.util.PotionUtil.setBasePotionType;
import static com.gmail.nossr50.util.PotionUtil.setUpgradedAndExtendedProperties;
import com.gmail.nossr50.config.LegacyConfigLoader; import com.gmail.nossr50.config.LegacyConfigLoader;
import com.gmail.nossr50.datatypes.skills.alchemy.AlchemyPotion; import com.gmail.nossr50.datatypes.skills.alchemy.AlchemyPotion;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.ItemUtils; import com.gmail.nossr50.util.ItemUtils;
import com.gmail.nossr50.util.LogUtils;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -49,9 +47,6 @@ public class PotionConfig extends LegacyConfigLoader {
private final List<ItemStack> concoctionsIngredientsTierSix = new ArrayList<>(); private final List<ItemStack> concoctionsIngredientsTierSix = new ArrayList<>();
private final List<ItemStack> concoctionsIngredientsTierSeven = new ArrayList<>(); private final List<ItemStack> concoctionsIngredientsTierSeven = new ArrayList<>();
private final List<ItemStack> concoctionsIngredientsTierEight = new ArrayList<>(); private final List<ItemStack> concoctionsIngredientsTierEight = new ArrayList<>();
private final AlchemyPotionConfigResult INCOMPATIBLE_POTION_RESULT = new AlchemyPotionConfigResult(
null,
AlchemyPotionConfigResultType.INCOMPATIBLE);
private final AlchemyPotionConfigResult ERROR_POTION_RESULT = new AlchemyPotionConfigResult( private final AlchemyPotionConfigResult ERROR_POTION_RESULT = new AlchemyPotionConfigResult(
null, null,
AlchemyPotionConfigResultType.ERROR); AlchemyPotionConfigResultType.ERROR);
@@ -258,15 +253,15 @@ public class PotionConfig extends LegacyConfigLoader {
if (potion_section.contains("Effects")) { if (potion_section.contains("Effects")) {
for (String effect : potion_section.getStringList("Effects")) { for (String effect : potion_section.getStringList("Effects")) {
String[] parts = effect.split(" "); String[] parts = effect.split(" ");
if (isTrickyTrialsPotionEffect(parts[0]) && !mcMMO.getCompatibilityManager() // if (isTrickyTrialsPotionEffect(parts[0]) && !mcMMO.getCompatibilityManager()
.getMinecraftGameVersion() // .minecraftGameVersion()
.isAtLeast(1, 21, 0)) { // .isAtLeast(1, 21, 0)) {
LogUtils.debug( // LogUtils.debug(
mcMMO.p.getLogger(), // mcMMO.p.getLogger(),
"Skipping potion effect " + effect + " because it is not" // "Skipping potion effect " + effect + " because it is not"
+ " compatible with the current Minecraft game version."); // + " compatible with the current Minecraft game version.");
return INCOMPATIBLE_POTION_RESULT; // return INCOMPATIBLE_POTION_RESULT;
} // }
PotionEffectType type = PotionEffectType type =
parts.length > 0 ? PotionEffectType.getByName(parts[0]) : null; parts.length > 0 ? PotionEffectType.getByName(parts[0]) : null;
@@ -298,9 +293,7 @@ public class PotionConfig extends LegacyConfigLoader {
.getKeys(false)) { .getKeys(false)) {
// Breeze Rod was only for potions after 1.21.0 // Breeze Rod was only for potions after 1.21.0
if (isTrickyTrialsIngredient(childIngredient) if (isTrickyTrialsIngredient(childIngredient)
&& !mcMMO.getCompatibilityManager() && !mcMMO.getMinecraftGameVersion().isAtLeast(1, 21, 0)) {
.getMinecraftGameVersion()
.isAtLeast(1, 21, 0)) {
continue; continue;
} }
ItemStack ingredient = loadIngredient(childIngredient); ItemStack ingredient = loadIngredient(childIngredient);
@@ -359,8 +352,6 @@ public class PotionConfig extends LegacyConfigLoader {
// set base // set base
setBasePotionType(potionMeta, potionType, extended, upgraded); setBasePotionType(potionMeta, potionType, extended, upgraded);
// Legacy only
setUpgradedAndExtendedProperties(potionType, potionMeta, upgraded, extended);
return true; return true;
} }

View File

@@ -28,8 +28,8 @@ public class DatabaseManagerFactory {
: "Flatfile") + " database"); : "Flatfile") + " database");
} }
return mcMMO.p.getGeneralConfig().getUseMySQL() ? new SQLDatabaseManager(logger, return mcMMO.p.getGeneralConfig().getUseMySQL()
MYSQL_DRIVER) ? new SQLDatabaseManager(logger, MYSQL_DRIVER)
: new FlatFileDatabaseManager(userFilePath, logger, purgeTime, startingLevel); : new FlatFileDatabaseManager(userFilePath, logger, purgeTime, startingLevel);
} }

View File

@@ -9,6 +9,7 @@ import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_GREEN_
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_MACES; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_MACES;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SERRATED_STRIKES; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SERRATED_STRIKES;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SKULL_SPLITTER; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SKULL_SPLITTER;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SPEARS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SUPER_BREAKER; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SUPER_BREAKER;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SUPER_SHOTGUN; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SUPER_SHOTGUN;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_TREE_FELLER; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_TREE_FELLER;
@@ -25,6 +26,7 @@ import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_HERBALISM;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_MACES; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_MACES;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_MINING; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_MINING;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_REPAIR; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_REPAIR;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_SPEARS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_SWORDS; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_SWORDS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_TAMING; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_TAMING;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_TRIDENTS; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_TRIDENTS;
@@ -45,6 +47,7 @@ import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_HERBALIS
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_MACES; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_MACES;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_MINING; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_MINING;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_REPAIR; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_REPAIR;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_SPEARS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_SWORDS; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_SWORDS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_TAMING; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_TAMING;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_TRIDENTS; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_TRIDENTS;
@@ -318,27 +321,26 @@ public class FlatFileDataProcessor {
throws IndexOutOfBoundsException { throws IndexOutOfBoundsException {
return switch (dataIndex) { return switch (dataIndex) {
case USERNAME_INDEX -> case USERNAME_INDEX ->
ExpectedType.STRING; //Assumption: Used to be for something, no longer used ExpectedType.STRING;
//Assumption: Used to be for something, no longer used
//Assumption: Used to be used for something, no longer used
//Assumption: Used to be used for something, no longer used //Assumption: Used to be used for something, no longer used
case 2, 3, 23, 33, HEALTHBAR, LEGACY_LAST_LOGIN -> ExpectedType.IGNORED; case 2, 3, 23, 33, HEALTHBAR, LEGACY_LAST_LOGIN -> ExpectedType.IGNORED;
case SKILLS_MINING, SKILLS_REPAIR, SKILLS_UNARMED, SKILLS_HERBALISM, SKILLS_EXCAVATION, case SKILLS_MINING, SKILLS_REPAIR, SKILLS_UNARMED, SKILLS_HERBALISM, SKILLS_EXCAVATION,
SKILLS_ARCHERY, SKILLS_ARCHERY,
SKILLS_SWORDS, SKILLS_AXES, SKILLS_WOODCUTTING, SKILLS_ACROBATICS, SKILLS_TAMING, SKILLS_SWORDS, SKILLS_AXES, SKILLS_WOODCUTTING, SKILLS_ACROBATICS, SKILLS_TAMING,
SKILLS_FISHING, SKILLS_FISHING,
SKILLS_ALCHEMY, SKILLS_CROSSBOWS, SKILLS_TRIDENTS, SKILLS_MACES, COOLDOWN_BERSERK, SKILLS_ALCHEMY, SKILLS_CROSSBOWS, SKILLS_TRIDENTS, SKILLS_MACES, SKILLS_SPEARS,
COOLDOWN_BERSERK,
COOLDOWN_GIGA_DRILL_BREAKER, COOLDOWN_TREE_FELLER, COOLDOWN_GREEN_TERRA, COOLDOWN_GIGA_DRILL_BREAKER, COOLDOWN_TREE_FELLER, COOLDOWN_GREEN_TERRA,
COOLDOWN_SERRATED_STRIKES, COOLDOWN_SERRATED_STRIKES,
COOLDOWN_SKULL_SPLITTER, COOLDOWN_SUPER_BREAKER, COOLDOWN_BLAST_MINING, COOLDOWN_SKULL_SPLITTER, COOLDOWN_SUPER_BREAKER, COOLDOWN_BLAST_MINING,
SCOREBOARD_TIPS, SCOREBOARD_TIPS,
COOLDOWN_CHIMAERA_WING, COOLDOWN_SUPER_SHOTGUN, COOLDOWN_TRIDENTS, COOLDOWN_CHIMAERA_WING, COOLDOWN_SUPER_SHOTGUN, COOLDOWN_TRIDENTS,
COOLDOWN_ARCHERY, COOLDOWN_MACES -> ExpectedType.INTEGER; COOLDOWN_ARCHERY, COOLDOWN_MACES, COOLDOWN_SPEARS -> ExpectedType.INTEGER;
case EXP_MINING, EXP_WOODCUTTING, EXP_REPAIR, EXP_UNARMED, EXP_HERBALISM, case EXP_MINING, EXP_WOODCUTTING, EXP_REPAIR, EXP_UNARMED, EXP_HERBALISM,
EXP_EXCAVATION, EXP_ARCHERY, EXP_EXCAVATION, EXP_ARCHERY,
EXP_SWORDS, EXP_AXES, EXP_ACROBATICS, EXP_TAMING, EXP_FISHING, EXP_ALCHEMY, EXP_SWORDS, EXP_AXES, EXP_ACROBATICS, EXP_TAMING, EXP_FISHING, EXP_ALCHEMY,
EXP_CROSSBOWS, EXP_CROSSBOWS,
EXP_TRIDENTS, EXP_MACES -> ExpectedType.FLOAT; EXP_TRIDENTS, EXP_MACES, EXP_SPEARS -> ExpectedType.FLOAT;
case UUID_INDEX -> ExpectedType.UUID; case UUID_INDEX -> ExpectedType.UUID;
case OVERHAUL_LAST_LOGIN -> ExpectedType.LONG; case OVERHAUL_LAST_LOGIN -> ExpectedType.LONG;
default -> throw new IndexOutOfBoundsException(); default -> throw new IndexOutOfBoundsException();

View File

@@ -9,6 +9,7 @@ import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_GREEN_
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_MACES; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_MACES;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SERRATED_STRIKES; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SERRATED_STRIKES;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SKULL_SPLITTER; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SKULL_SPLITTER;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SPEARS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SUPER_BREAKER; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SUPER_BREAKER;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SUPER_SHOTGUN; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_SUPER_SHOTGUN;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_TREE_FELLER; import static com.gmail.nossr50.database.FlatFileDatabaseManager.COOLDOWN_TREE_FELLER;
@@ -24,6 +25,7 @@ import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_HERBALISM;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_MACES; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_MACES;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_MINING; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_MINING;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_REPAIR; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_REPAIR;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_SPEARS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_SWORDS; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_SWORDS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_TAMING; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_TAMING;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_TRIDENTS; import static com.gmail.nossr50.database.FlatFileDatabaseManager.EXP_TRIDENTS;
@@ -45,6 +47,7 @@ import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_HERBALIS
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_MACES; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_MACES;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_MINING; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_MINING;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_REPAIR; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_REPAIR;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_SPEARS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_SWORDS; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_SWORDS;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_TAMING; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_TAMING;
import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_TRIDENTS; import static com.gmail.nossr50.database.FlatFileDatabaseManager.SKILLS_TRIDENTS;
@@ -114,18 +117,16 @@ public class FlatFileDataUtil {
throws IndexOutOfBoundsException { throws IndexOutOfBoundsException {
//TODO: Add UUID recovery? Might not even be worth it. //TODO: Add UUID recovery? Might not even be worth it.
return switch (index) { return switch (index) {
//We'll keep using this value for legacy compatibility reasons (not sure if needed but don't care)
case USERNAME_INDEX -> case USERNAME_INDEX ->
LEGACY_INVALID_OLD_USERNAME; //We'll keep using this value for legacy compatibility reasons (not sure if needed but don't care) LEGACY_INVALID_OLD_USERNAME;
//Assumption: Used to be for something, no longer used
//Assumption: Used to be for something, no longer used
//Assumption: Used to be used for something, no longer used
//Assumption: Used to be used for something, no longer used //Assumption: Used to be used for something, no longer used
case 2, 3, 23, 33, LEGACY_LAST_LOGIN, HEALTHBAR -> "IGNORED"; case 2, 3, 23, 33, LEGACY_LAST_LOGIN, HEALTHBAR -> "IGNORED";
case SKILLS_MINING, SKILLS_REPAIR, SKILLS_UNARMED, SKILLS_HERBALISM, SKILLS_EXCAVATION, case SKILLS_MINING, SKILLS_REPAIR, SKILLS_UNARMED, SKILLS_HERBALISM, SKILLS_EXCAVATION,
SKILLS_ARCHERY, SKILLS_ARCHERY,
SKILLS_SWORDS, SKILLS_AXES, SKILLS_WOODCUTTING, SKILLS_ACROBATICS, SKILLS_TAMING, SKILLS_SWORDS, SKILLS_AXES, SKILLS_WOODCUTTING, SKILLS_ACROBATICS, SKILLS_TAMING,
SKILLS_FISHING, SKILLS_FISHING,
SKILLS_ALCHEMY, SKILLS_CROSSBOWS, SKILLS_TRIDENTS, SKILLS_MACES -> SKILLS_ALCHEMY, SKILLS_CROSSBOWS, SKILLS_TRIDENTS, SKILLS_MACES, SKILLS_SPEARS ->
String.valueOf(startingLevel); String.valueOf(startingLevel);
case OVERHAUL_LAST_LOGIN -> String.valueOf(-1L); case OVERHAUL_LAST_LOGIN -> String.valueOf(-1L);
case COOLDOWN_BERSERK, COOLDOWN_GIGA_DRILL_BREAKER, COOLDOWN_TREE_FELLER, case COOLDOWN_BERSERK, COOLDOWN_GIGA_DRILL_BREAKER, COOLDOWN_TREE_FELLER,
@@ -133,12 +134,12 @@ public class FlatFileDataUtil {
COOLDOWN_SERRATED_STRIKES, COOLDOWN_SKULL_SPLITTER, COOLDOWN_SUPER_BREAKER, COOLDOWN_SERRATED_STRIKES, COOLDOWN_SKULL_SPLITTER, COOLDOWN_SUPER_BREAKER,
COOLDOWN_BLAST_MINING, COOLDOWN_BLAST_MINING,
COOLDOWN_SUPER_SHOTGUN, COOLDOWN_TRIDENTS, COOLDOWN_ARCHERY, COOLDOWN_MACES, COOLDOWN_SUPER_SHOTGUN, COOLDOWN_TRIDENTS, COOLDOWN_ARCHERY, COOLDOWN_MACES,
SCOREBOARD_TIPS, COOLDOWN_CHIMAERA_WING, COOLDOWN_SPEARS, SCOREBOARD_TIPS, COOLDOWN_CHIMAERA_WING,
EXP_MINING, EXP_WOODCUTTING, EXP_REPAIR, EXP_UNARMED, EXP_HERBALISM, EXP_MINING, EXP_WOODCUTTING, EXP_REPAIR, EXP_UNARMED, EXP_HERBALISM,
EXP_EXCAVATION, EXP_ARCHERY, EXP_EXCAVATION, EXP_ARCHERY,
EXP_SWORDS, EXP_AXES, EXP_ACROBATICS, EXP_TAMING, EXP_FISHING, EXP_ALCHEMY, EXP_SWORDS, EXP_AXES, EXP_ACROBATICS, EXP_TAMING, EXP_FISHING, EXP_ALCHEMY,
EXP_CROSSBOWS, EXP_CROSSBOWS,
EXP_TRIDENTS, EXP_MACES -> "0"; EXP_TRIDENTS, EXP_MACES, EXP_SPEARS -> "0";
case UUID_INDEX -> case UUID_INDEX ->
throw new IndexOutOfBoundsException(); //TODO: Add UUID recovery? Might not even be worth it. throw new IndexOutOfBoundsException(); //TODO: Add UUID recovery? Might not even be worth it.
default -> throw new IndexOutOfBoundsException(); default -> throw new IndexOutOfBoundsException();

View File

@@ -3,7 +3,6 @@ package com.gmail.nossr50.datatypes.database;
public enum UpgradeType { public enum UpgradeType {
ADD_FISHING, ADD_FISHING,
ADD_BLAST_MINING_COOLDOWN, ADD_BLAST_MINING_COOLDOWN,
ADD_SQL_INDEXES,
ADD_MOB_HEALTHBARS, ADD_MOB_HEALTHBARS,
DROP_SQL_PARTY_NAMES, DROP_SQL_PARTY_NAMES,
DROP_SPOUT, DROP_SPOUT,

View File

@@ -40,6 +40,7 @@ import com.gmail.nossr50.skills.mining.MiningManager;
import com.gmail.nossr50.skills.repair.RepairManager; import com.gmail.nossr50.skills.repair.RepairManager;
import com.gmail.nossr50.skills.salvage.SalvageManager; import com.gmail.nossr50.skills.salvage.SalvageManager;
import com.gmail.nossr50.skills.smelting.SmeltingManager; import com.gmail.nossr50.skills.smelting.SmeltingManager;
import com.gmail.nossr50.skills.spears.SpearsManager;
import com.gmail.nossr50.skills.swords.SwordsManager; import com.gmail.nossr50.skills.swords.SwordsManager;
import com.gmail.nossr50.skills.taming.TamingManager; import com.gmail.nossr50.skills.taming.TamingManager;
import com.gmail.nossr50.skills.tridents.TridentsManager; import com.gmail.nossr50.skills.tridents.TridentsManager;
@@ -63,6 +64,7 @@ import com.gmail.nossr50.util.sounds.SoundType;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.logging.Level;
import net.kyori.adventure.identity.Identified; import net.kyori.adventure.identity.Identified;
import net.kyori.adventure.identity.Identity; import net.kyori.adventure.identity.Identity;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -171,73 +173,41 @@ public class McMMOPlayer implements Identified {
try { try {
initManager(primarySkillType); initManager(primarySkillType);
} catch (InvalidSkillException e) { } catch (InvalidSkillException e) {
e.printStackTrace(); mcMMO.p.getLogger().log(Level.SEVERE,
"Invalid skill while initializing skill managers for player "
+ player.getName()
+ ". Contact the plugin developers.", e);
} }
} }
} }
//TODO: Add test
private void initManager(PrimarySkillType primarySkillType) throws InvalidSkillException { private void initManager(PrimarySkillType primarySkillType) throws InvalidSkillException {
switch (primarySkillType) { final SkillManager manager = switch (primarySkillType) {
case ACROBATICS: case ACROBATICS -> new AcrobaticsManager(this);
skillManagers.put(primarySkillType, new AcrobaticsManager(this)); case ALCHEMY -> new AlchemyManager(this);
break; case ARCHERY -> new ArcheryManager(this);
case ALCHEMY: case AXES -> new AxesManager(this);
skillManagers.put(primarySkillType, new AlchemyManager(this)); case CROSSBOWS -> new CrossbowsManager(this);
break; case EXCAVATION -> new ExcavationManager(this);
case ARCHERY: case FISHING -> new FishingManager(this);
skillManagers.put(primarySkillType, new ArcheryManager(this)); case HERBALISM -> new HerbalismManager(this);
break; case MINING -> new MiningManager(this);
case AXES: case REPAIR -> new RepairManager(this);
skillManagers.put(primarySkillType, new AxesManager(this)); case SALVAGE -> new SalvageManager(this);
break; case SMELTING -> new SmeltingManager(this);
case CROSSBOWS: case SWORDS -> new SwordsManager(this);
skillManagers.put(primarySkillType, new CrossbowsManager(this)); case TAMING -> new TamingManager(this);
break; case TRIDENTS -> new TridentsManager(this);
case EXCAVATION: case UNARMED -> new UnarmedManager(this);
skillManagers.put(primarySkillType, new ExcavationManager(this)); case WOODCUTTING -> new WoodcuttingManager(this);
break; case MACES -> new MacesManager(this);
case FISHING: case SPEARS -> mcMMO.getMinecraftGameVersion().isAtLeast(1, 21, 11)
skillManagers.put(primarySkillType, new FishingManager(this)); ? new SpearsManager(this)
break; : null;
case HERBALISM: };
skillManagers.put(primarySkillType, new HerbalismManager(this));
break; if (manager != null) {
case MINING: skillManagers.put(primarySkillType, manager);
skillManagers.put(primarySkillType, new MiningManager(this));
break;
case REPAIR:
skillManagers.put(primarySkillType, new RepairManager(this));
break;
case SALVAGE:
skillManagers.put(primarySkillType, new SalvageManager(this));
break;
case SMELTING:
skillManagers.put(primarySkillType, new SmeltingManager(this));
break;
case SWORDS:
skillManagers.put(primarySkillType, new SwordsManager(this));
break;
case TAMING:
skillManagers.put(primarySkillType, new TamingManager(this));
break;
case TRIDENTS:
skillManagers.put(primarySkillType, new TridentsManager(this));
break;
case UNARMED:
skillManagers.put(primarySkillType, new UnarmedManager(this));
break;
case WOODCUTTING:
skillManagers.put(primarySkillType, new WoodcuttingManager(this));
break;
case MACES:
if (mcMMO.getCompatibilityManager().getMinecraftGameVersion().isAtLeast(1, 21, 0)) {
skillManagers.put(primarySkillType, new MacesManager(this));
}
break;
default:
throw new InvalidSkillException(
"The skill named has no manager! Contact the devs!");
} }
} }
@@ -369,6 +339,10 @@ public class McMMOPlayer implements Identified {
return (SmeltingManager) skillManagers.get(PrimarySkillType.SMELTING); return (SmeltingManager) skillManagers.get(PrimarySkillType.SMELTING);
} }
public SpearsManager getSpearsManager() {
return (SpearsManager) skillManagers.get(PrimarySkillType.SPEARS);
}
public SwordsManager getSwordsManager() { public SwordsManager getSwordsManager() {
return (SwordsManager) skillManagers.get(PrimarySkillType.SWORDS); return (SwordsManager) skillManagers.get(PrimarySkillType.SWORDS);
} }
@@ -1304,4 +1278,5 @@ public class McMMOPlayer implements Identified {
public void setChatMode(@NotNull ChatChannel chatChannel) { public void setChatMode(@NotNull ChatChannel chatChannel) {
this.chatChannel = chatChannel; this.chatChannel = chatChannel;
} }
} }

View File

@@ -87,7 +87,7 @@ public class PlayerProfile {
this.loaded = isLoaded; this.loaded = isLoaded;
} }
public PlayerProfile(@NotNull String playerName, UUID uuid, boolean isLoaded, int startingLvl) { public PlayerProfile(@NotNull String playerName, @Nullable UUID uuid, boolean isLoaded, int startingLvl) {
this(playerName, uuid, startingLvl); this(playerName, uuid, startingLvl);
this.loaded = isLoaded; this.loaded = isLoaded;
} }

View File

@@ -24,6 +24,7 @@ public enum PrimarySkillType {
REPAIR, REPAIR,
SALVAGE, SALVAGE,
SMELTING, SMELTING,
SPEARS,
SWORDS, SWORDS,
TAMING, TAMING,
TRIDENTS, TRIDENTS,

View File

@@ -83,6 +83,11 @@ public enum SubSkillType {
SMELTING_SECOND_SMELT, SMELTING_SECOND_SMELT,
SMELTING_UNDERSTANDING_THE_ART(8), SMELTING_UNDERSTANDING_THE_ART(8),
/* Spears */
SPEARS_SPEARS_LIMIT_BREAK(10),
SPEARS_MOMENTUM(10),
SPEARS_SPEAR_MASTERY(8),
/* Swords */ /* Swords */
SWORDS_COUNTER_ATTACK(1), SWORDS_COUNTER_ATTACK(1),
SWORDS_RUPTURE(4), SWORDS_RUPTURE(4),

View File

@@ -93,6 +93,13 @@ public enum SuperAbilityType {
"Placeholder", "Placeholder",
"Placeholder", "Placeholder",
"Placeholder"), "Placeholder"),
SPEARS_SUPER_ABILITY(
"Placeholder",
"Placeholder",
"Placeholder",
"Placeholder",
"Placeholder",
"Placeholder"),
/** /**
* Has cooldown - but has to share a skill with Super Breaker, so needs special treatment * Has cooldown - but has to share a skill with Super Breaker, so needs special treatment
@@ -216,8 +223,8 @@ public enum SuperAbilityType {
case SUPER_BREAKER -> Permissions.superBreaker(player); case SUPER_BREAKER -> Permissions.superBreaker(player);
case TREE_FELLER -> Permissions.treeFeller(player); case TREE_FELLER -> Permissions.treeFeller(player);
// TODO: once implemented, return permissions for the following abilities // TODO: once implemented, return permissions for the following abilities
case EXPLOSIVE_SHOT, TRIDENTS_SUPER_ABILITY, SUPER_SHOTGUN, MACES_SUPER_ABILITY -> case EXPLOSIVE_SHOT, TRIDENTS_SUPER_ABILITY, SUPER_SHOTGUN, MACES_SUPER_ABILITY,
false; SPEARS_SUPER_ABILITY -> false;
}; };
} }

View File

@@ -1,10 +1,8 @@
package com.gmail.nossr50.datatypes.skills.alchemy; package com.gmail.nossr50.datatypes.skills.alchemy;
import static com.gmail.nossr50.util.PotionUtil.samePotionType;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.PotionUtil;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects; import java.util.Objects;
@@ -80,12 +78,14 @@ public class AlchemyPotion {
return false; return false;
} }
if (!samePotionType(getAlchemyPotionMeta(), otherPotionMeta)) { @NotNull PotionMeta potionMeta = getAlchemyPotionMeta();
if (!(potionMeta.getBasePotionType() == otherPotionMeta.getBasePotionType())) {
return false; return false;
} }
// Legacy only comparison, compare PotionData // Legacy only comparison, compare PotionData
if (!PotionUtil.isPotionDataEqual(getAlchemyPotionMeta(), otherPotionMeta)) { @NotNull PotionMeta potionMeta1 = getAlchemyPotionMeta();
if (!(potionMeta1.getBasePotionType() == otherPotionMeta.getBasePotionType())) {
return false; return false;
} }

View File

@@ -147,12 +147,14 @@ public class BlockListener implements Listener {
} }
int amountToAddFromBonus = bonusDropMeta.asInt(); int amountToAddFromBonus = bonusDropMeta.asInt();
final McMMOModifyBlockDropItemEvent modifyBlockDropItemEvent final McMMOModifyBlockDropItemEvent modifyDropEvent
= new McMMOModifyBlockDropItemEvent(event, item, amountToAddFromBonus); = new McMMOModifyBlockDropItemEvent(event, item, amountToAddFromBonus);
plugin.getServer().getPluginManager().callEvent(modifyBlockDropItemEvent); plugin.getServer().getPluginManager().callEvent(modifyDropEvent);
if (!modifyBlockDropItemEvent.isCancelled() if (!modifyDropEvent.isCancelled()
&& modifyBlockDropItemEvent.getModifiedItemStackQuantity() > originalAmount) { && modifyDropEvent.getModifiedItemStackQuantity() > originalAmount) {
eventItemStack.setAmount(modifyBlockDropItemEvent.getModifiedItemStackQuantity()); eventItemStack.setAmount(
Math.min(modifyDropEvent.getModifiedItemStackQuantity(),
item.getItemStack().getMaxStackSize()));
} }
} }
} }
@@ -240,11 +242,16 @@ public class BlockListener implements Listener {
return; return;
} }
BlockState blockState = event.getNewState(); final BlockState newState = event.getNewState();
if (!newState.isPlaced()) {
// not backed by a real block
return;
}
if (ExperienceConfig.getInstance().isSnowExploitPrevented() && BlockUtils.shouldBeWatched( if (ExperienceConfig.getInstance().isSnowExploitPrevented() && BlockUtils.shouldBeWatched(
blockState)) { newState)) {
Block block = blockState.getBlock(); final Block block = newState.getBlock();
if (BlockUtils.isWithinWorldBounds(block)) { if (BlockUtils.isWithinWorldBounds(block)) {
BlockUtils.setUnnaturalBlock(block); BlockUtils.setUnnaturalBlock(block);
@@ -257,7 +264,7 @@ public class BlockListener implements Listener {
*/ */
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockFormEvent(BlockFormEvent event) { public void onBlockFormEvent(BlockFormEvent event) {
World world = event.getBlock().getWorld(); final World world = event.getBlock().getWorld();
/* WORLD BLACKLIST CHECK */ /* WORLD BLACKLIST CHECK */
if (WorldBlacklist.isWorldBlacklisted(world)) { if (WorldBlacklist.isWorldBlacklisted(world)) {
@@ -265,12 +272,16 @@ public class BlockListener implements Listener {
} }
if (ExperienceConfig.getInstance().preventStoneLavaFarming()) { if (ExperienceConfig.getInstance().preventStoneLavaFarming()) {
BlockState newState = event.getNewState(); final BlockState newState = event.getNewState();
if (!newState.isPlaced()) {
// not backed by a real block
return;
}
if (newState.getType() != Material.OBSIDIAN if (newState.getType() != Material.OBSIDIAN
&& ExperienceConfig.getInstance().doesBlockGiveSkillXP( && ExperienceConfig.getInstance().doesBlockGiveSkillXP(
PrimarySkillType.MINING, newState.getType())) { PrimarySkillType.MINING, newState.getType())) {
Block block = newState.getBlock(); final Block block = newState.getBlock();
if (BlockUtils.isWithinWorldBounds(block)) { if (BlockUtils.isWithinWorldBounds(block)) {
BlockUtils.setUnnaturalBlock(block); BlockUtils.setUnnaturalBlock(block);
} }

View File

@@ -168,6 +168,7 @@ public class PlayerListener implements Listener {
if (WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) { if (WorldBlacklist.isWorldBlacklisted(event.getEntity().getWorld())) {
return; return;
} }
// world guard main flag check // world guard main flag check
if (WorldGuardUtils.isWorldGuardLoaded() && !WorldGuardManager.getInstance() if (WorldGuardUtils.isWorldGuardLoaded() && !WorldGuardManager.getInstance()
.hasMainFlag((Player) event.getEntity())) { .hasMainFlag((Player) event.getEntity())) {
@@ -342,8 +343,8 @@ public class PlayerListener implements Listener {
FishingManager fishingManager = UserManager.getPlayer(player).getFishingManager(); FishingManager fishingManager = UserManager.getPlayer(player).getFishingManager();
switch (event.getState()) { switch (event.getState()) {
// CAUGHT_FISH happens for any item caught (including junk and treasure)
case CAUGHT_FISH: case CAUGHT_FISH:
//TODO Update to new API once available! Waiting for case CAUGHT_TREASURE
if (event.getCaught() != null) { if (event.getCaught() != null) {
Item fishingCatch = (Item) event.getCaught(); Item fishingCatch = (Item) event.getCaught();
@@ -488,8 +489,7 @@ public class PlayerListener implements Listener {
fishingManager.processExploiting(event.getHook().getLocation().toVector()); fishingManager.processExploiting(event.getHook().getLocation().toVector());
if (fishingManager.isExploitingFishing( if (fishingManager.isExploitingFishing()) {
event.getHook().getLocation().toVector())) {
player.sendMessage(LocaleLoader.getString("Fishing.ScarcityTip", player.sendMessage(LocaleLoader.getString("Fishing.ScarcityTip",
ExperienceConfig.getInstance() ExperienceConfig.getInstance()
.getFishingExploitingOptionMoveRange())); .getFishingExploitingOptionMoveRange()));
@@ -675,6 +675,10 @@ public class PlayerListener implements Listener {
*/ */
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerInteractLowest(PlayerInteractEvent event) { public void onPlayerInteractLowest(PlayerInteractEvent event) {
if (event.getAction() == Action.PHYSICAL) {
return;
}
/* WORLD BLACKLIST CHECK */ /* WORLD BLACKLIST CHECK */
if (WorldBlacklist.isWorldBlacklisted(event.getPlayer().getWorld())) { if (WorldBlacklist.isWorldBlacklisted(event.getPlayer().getWorld())) {
return; return;
@@ -817,6 +821,10 @@ public class PlayerListener implements Listener {
*/ */
@EventHandler(priority = EventPriority.MONITOR) @EventHandler(priority = EventPriority.MONITOR)
public void onPlayerInteractMonitor(PlayerInteractEvent event) { public void onPlayerInteractMonitor(PlayerInteractEvent event) {
if (event.getAction() == Action.PHYSICAL) {
return;
}
/* WORLD BLACKLIST CHECK */ /* WORLD BLACKLIST CHECK */
if (WorldBlacklist.isWorldBlacklisted(event.getPlayer().getWorld())) { if (WorldBlacklist.isWorldBlacklisted(event.getPlayer().getWorld())) {
return; return;
@@ -900,7 +908,6 @@ public class PlayerListener implements Listener {
HerbalismManager herbalismManager = mmoPlayer.getHerbalismManager(); HerbalismManager herbalismManager = mmoPlayer.getHerbalismManager();
// FakePlayerAnimationEvent fakeSwing = new FakePlayerAnimationEvent(event.getPlayer(), PlayerAnimationType.ARM_SWING); //PlayerAnimationEvent compat
if (!event.isCancelled() || event.useInteractedBlock() != Event.Result.DENY) { if (!event.isCancelled() || event.useInteractedBlock() != Event.Result.DENY) {
//TODO: Is this code to set false from bone meal even needed? I'll have to double check later. //TODO: Is this code to set false from bone meal even needed? I'll have to double check later.
if (heldItem.getType() == Material.BONE_MEAL) { if (heldItem.getType() == Material.BONE_MEAL) {

View File

@@ -56,14 +56,13 @@ import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.TransientEntityTracker; import com.gmail.nossr50.util.TransientEntityTracker;
import com.gmail.nossr50.util.TransientMetadataTools; import com.gmail.nossr50.util.TransientMetadataTools;
import com.gmail.nossr50.util.MinecraftGameVersionFactory;
import com.gmail.nossr50.util.blockmeta.ChunkManager; import com.gmail.nossr50.util.blockmeta.ChunkManager;
import com.gmail.nossr50.util.blockmeta.ChunkManagerFactory; import com.gmail.nossr50.util.blockmeta.ChunkManagerFactory;
import com.gmail.nossr50.util.blockmeta.UserBlockTracker; import com.gmail.nossr50.util.blockmeta.UserBlockTracker;
import com.gmail.nossr50.util.commands.CommandRegistrationManager; import com.gmail.nossr50.util.commands.CommandRegistrationManager;
import com.gmail.nossr50.util.compat.CompatibilityManager;
import com.gmail.nossr50.util.experience.FormulaManager; import com.gmail.nossr50.util.experience.FormulaManager;
import com.gmail.nossr50.util.platform.PlatformManager; import com.gmail.nossr50.util.platform.MinecraftGameVersion;
import com.gmail.nossr50.util.platform.ServerSoftwareType;
import com.gmail.nossr50.util.player.PlayerLevelUtils; import com.gmail.nossr50.util.player.PlayerLevelUtils;
import com.gmail.nossr50.util.player.UserManager; import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.scoreboards.ScoreboardManager; import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
@@ -94,7 +93,6 @@ import org.jetbrains.annotations.Nullable;
public class mcMMO extends JavaPlugin { public class mcMMO extends JavaPlugin {
/* Managers & Services */ /* Managers & Services */
private static PlatformManager platformManager;
private static ChunkManager chunkManager; private static ChunkManager chunkManager;
private static RepairableManager repairableManager; private static RepairableManager repairableManager;
private static SalvageableManager salvageableManager; private static SalvageableManager salvageableManager;
@@ -107,6 +105,7 @@ public class mcMMO extends JavaPlugin {
private static ChatManager chatManager; private static ChatManager chatManager;
private static CommandManager commandManager; //ACF private static CommandManager commandManager; //ACF
private static TransientEntityTracker transientEntityTracker; private static TransientEntityTracker transientEntityTracker;
private static MinecraftGameVersion minecraftGameVersion;
private SkillTools skillTools; private SkillTools skillTools;
@@ -170,8 +169,19 @@ public class mcMMO extends JavaPlugin {
//Filter out any debug messages (if debug/verbose logging is not enabled) //Filter out any debug messages (if debug/verbose logging is not enabled)
getLogger().setFilter(new LogFilter(this)); getLogger().setFilter(new LogFilter(this));
//Platform Manager // Determine game version before moving forward
platformManager = new PlatformManager(); final String versionStr = Bukkit.getVersion();
try {
minecraftGameVersion = MinecraftGameVersionFactory.calculateGameVersion(versionStr);
} catch (Exception e) {
// if anything goes wrong with our calculations, assume they are running the minimum
// supported version and log the error
getLogger().warning("Could not determine Minecraft version from"
+ " server software version string: " + versionStr +
", Please report this bug to the devs!");
e.printStackTrace();
minecraftGameVersion = new MinecraftGameVersion(1, 20, 5);
}
//Folia lib plugin instance //Folia lib plugin instance
foliaLib = new FoliaLib(this); foliaLib = new FoliaLib(this);
@@ -245,15 +255,14 @@ public class mcMMO extends JavaPlugin {
if (serverAPIOutdated) { if (serverAPIOutdated) {
foliaLib.getScheduler().runTimer( foliaLib.getScheduler().runTimer(
() -> getLogger().severe( () -> getLogger().severe(
"You are running an outdated version of " "You are potentially running an outdated version of your server software"
+ platformManager.getServerSoftware()
+ ", mcMMO will not work unless you update to a newer version!"), + ", mcMMO will not work unless you update to a newer version!"),
20, 20 * 60 * 30); 20, 20 * 60 * 30);
if (!minecraftGameVersion.isAtLeast(1, 20, 4)) {
if (platformManager.getServerSoftware() == ServerSoftwareType.CRAFT_BUKKIT) {
foliaLib.getScheduler().runTimer( foliaLib.getScheduler().runTimer(
() -> getLogger().severe( () -> getLogger().severe(
"We have detected you are using incompatible server software, our best guess is that you are using CraftBukkit. mcMMO requires Spigot or Paper, if you are not using CraftBukkit, you will still need to update your custom server software before mcMMO will work."), "This version of mcMMO requires at least Minecraft 1.20.4 to"
+ " function properly, please update your software or use an older version of mcMMO!"),
20, 20 * 60 * 30); 20, 20 * 60 * 30);
} }
} else { } else {
@@ -351,15 +360,15 @@ public class mcMMO extends JavaPlugin {
private void checkForOutdatedAPI() { private void checkForOutdatedAPI() {
try { try {
Class<?> checkForClass = Class.forName("org.bukkit.event.block.BlockDropItemEvent"); Class<?> blockDropItemEvent = Class.forName("org.bukkit.event.block.BlockDropItemEvent");
checkForClass.getMethod("getItems"); blockDropItemEvent.getMethod("getItems");
Class.forName("net.md_5.bungee.api.chat.BaseComponent"); Class.forName("net.md_5.bungee.api.chat.BaseComponent");
// 1.20.4 checks
Class<?> entityDamageEvent = Class.forName("org.bukkit.event.entity.EntityDamageEvent");
entityDamageEvent.getMethod("getDamageSource");
} catch (ClassNotFoundException | NoSuchMethodException e) { } catch (ClassNotFoundException | NoSuchMethodException e) {
serverAPIOutdated = true; serverAPIOutdated = true;
String software = platformManager.getServerSoftwareStr(); getLogger().severe("Your server software is missing APIs that mcMMO requires to function properly, please update your server software!");
getLogger().severe(
"You are running an older version of " + software
+ " that is not compatible with mcMMO, update your server software!");
} }
} }
@@ -502,10 +511,6 @@ public class mcMMO extends JavaPlugin {
return upgradeManager; return upgradeManager;
} }
public static @Nullable CompatibilityManager getCompatibilityManager() {
return platformManager.getCompatibilityManager();
}
@Deprecated @Deprecated
public static void setDatabaseManager(DatabaseManager databaseManager) { public static void setDatabaseManager(DatabaseManager databaseManager) {
mcMMO.databaseManager = databaseManager; mcMMO.databaseManager = databaseManager;
@@ -751,10 +756,6 @@ public class mcMMO extends JavaPlugin {
return worldBlacklist; return worldBlacklist;
} }
public static PlatformManager getPlatformManager() {
return platformManager;
}
public static BukkitAudiences getAudiences() { public static BukkitAudiences getAudiences() {
return audiences; return audiences;
} }
@@ -835,4 +836,13 @@ public class mcMMO extends JavaPlugin {
public @NotNull FoliaLib getFoliaLib() { public @NotNull FoliaLib getFoliaLib() {
return foliaLib; return foliaLib;
} }
/**
* Get the {@link MinecraftGameVersion}
*
* @return the {@link MinecraftGameVersion}
*/
public static MinecraftGameVersion getMinecraftGameVersion() {
return minecraftGameVersion;
}
} }

View File

@@ -49,12 +49,6 @@ public class McRankCommandDisplayTask extends CancellableRunnable {
sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Player", playerName)); sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Player", playerName));
for (PrimarySkillType skill : SkillTools.NON_CHILD_SKILLS) { for (PrimarySkillType skill : SkillTools.NON_CHILD_SKILLS) {
// Check if the command is for Maces but the MC version is not correct
if (skill == PrimarySkillType.MACES
&& !mcMMO.getCompatibilityManager().getMinecraftGameVersion()
.isAtLeast(1, 21, 0)) {
continue;
}
rank = skills.get(skill); rank = skills.get(skill);
sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill", sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill",
mcMMO.p.getSkillTools().getLocalizedSkillName(skill), mcMMO.p.getSkillTools().getLocalizedSkillName(skill),

View File

@@ -6,9 +6,10 @@ import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.ItemUtils; import com.gmail.nossr50.util.ItemUtils;
import com.gmail.nossr50.util.skills.RankUtils; import com.gmail.nossr50.util.skills.RankUtils;
import java.util.ArrayList; import java.util.Map;
import java.util.Iterator; import java.util.UUID;
import java.util.List; import java.util.concurrent.ConcurrentHashMap;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -16,7 +17,7 @@ import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class Archery { public class Archery {
private static final List<TrackedEntity> trackedEntities = new ArrayList<>(); private static final Map<UUID, TrackedEntity> trackedEntities = new ConcurrentHashMap<>();
public static double skillShotMaxBonusDamage = mcMMO.p.getAdvancedConfig() public static double skillShotMaxBonusDamage = mcMMO.p.getAdvancedConfig()
.getSkillShotDamageMax(); .getSkillShotDamageMax();
@@ -27,25 +28,12 @@ public class Archery {
.getArcheryDistanceMultiplier(); .getArcheryDistanceMultiplier();
protected static void incrementTrackerValue(LivingEntity livingEntity) { protected static void incrementTrackerValue(LivingEntity livingEntity) {
for (TrackedEntity trackedEntity : trackedEntities) { final TrackedEntity trackedEntity = trackedEntities.computeIfAbsent(livingEntity.getUniqueId(), k -> new TrackedEntity(livingEntity));
if (trackedEntity.getLivingEntity().getEntityId() == livingEntity.getEntityId()) {
trackedEntity.incrementArrowCount();
return;
}
}
addToTracker(livingEntity); // If the entity isn't tracked yet
}
protected static void addToTracker(LivingEntity livingEntity) {
TrackedEntity trackedEntity = new TrackedEntity(livingEntity);
trackedEntity.incrementArrowCount(); trackedEntity.incrementArrowCount();
trackedEntities.add(trackedEntity);
} }
protected static void removeFromTracker(TrackedEntity trackedEntity) { protected static void removeFromTracker(TrackedEntity trackedEntity) {
trackedEntities.remove(trackedEntity); trackedEntities.remove(trackedEntity.getID());
} }
/** /**
@@ -54,17 +42,11 @@ public class Archery {
* @param livingEntity The entity hit by the arrows * @param livingEntity The entity hit by the arrows
*/ */
public static void arrowRetrievalCheck(@NotNull LivingEntity livingEntity) { public static void arrowRetrievalCheck(@NotNull LivingEntity livingEntity) {
for (Iterator<TrackedEntity> entityIterator = trackedEntities.iterator(); final TrackedEntity trackedEntity = trackedEntities.remove(livingEntity.getUniqueId());
entityIterator.hasNext(); ) { if (trackedEntity != null) {
TrackedEntity trackedEntity = entityIterator.next(); ItemUtils.spawnItems(null, livingEntity.getLocation(),
new ItemStack(Material.ARROW), trackedEntity.getArrowCount(),
if (trackedEntity.getID() == livingEntity.getUniqueId()) { ItemSpawnReason.ARROW_RETRIEVAL_ACTIVATED);
ItemUtils.spawnItems(null, livingEntity.getLocation(),
new ItemStack(Material.ARROW), trackedEntity.getArrowCount(),
ItemSpawnReason.ARROW_RETRIEVAL_ACTIVATED);
entityIterator.remove();
return;
}
} }
} }

View File

@@ -28,7 +28,6 @@ import com.gmail.nossr50.util.MetadataConstants;
import com.gmail.nossr50.util.Misc; import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.adapter.BiomeAdapter; import com.gmail.nossr50.util.adapter.BiomeAdapter;
import com.gmail.nossr50.util.compat.layers.skills.MasterAnglerCompatibilityLayer;
import com.gmail.nossr50.util.player.NotificationManager; import com.gmail.nossr50.util.player.NotificationManager;
import com.gmail.nossr50.util.random.ProbabilityUtil; import com.gmail.nossr50.util.random.ProbabilityUtil;
import com.gmail.nossr50.util.skills.CombatUtils; import com.gmail.nossr50.util.skills.CombatUtils;
@@ -40,7 +39,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.block.BlockFace; import org.bukkit.block.BlockFace;
@@ -55,31 +53,24 @@ import org.bukkit.entity.Sheep;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.PlayerInventory;
import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.inventory.meta.SkullMeta;
import org.bukkit.metadata.Metadatable;
import org.bukkit.util.BoundingBox; import org.bukkit.util.BoundingBox;
import org.bukkit.util.Vector; import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class FishingManager extends SkillManager { public class FishingManager extends SkillManager {
public static final int FISHING_ROD_CAST_CD_MILLISECONDS = 100;
private final long FISHING_COOLDOWN_SECONDS = 1000L;
private long fishingRodCastTimestamp = 0L;
private long fishHookSpawnTimestamp = 0L; private long fishHookSpawnTimestamp = 0L;
private long lastWarned = 0L; private long lastWarned = 0L;
private final long lastWarnedExhaust = 0L;
private FishHook fishHookReference;
private BoundingBox lastFishingBoundingBox; private BoundingBox lastFishingBoundingBox;
private boolean sameTarget; private boolean sameTarget;
private Item fishingCatch;
private Location hookLocation;
private int fishCaughtCounter = 1; private int fishCaughtCounter = 1;
private final int masterAnglerMinWaitLowerBound; private final int masterAnglerMinWaitLowerBound;
private final int masterAnglerMaxWaitLowerBound; private final int masterAnglerMaxWaitLowerBound;
public FishingManager(McMMOPlayer mmoPlayer) { public FishingManager(McMMOPlayer mmoPlayer) {
super(mmoPlayer, PrimarySkillType.FISHING); super(mmoPlayer, PrimarySkillType.FISHING);
//Ticks for minWait and maxWait never go below this value // Ticks for minWait and maxWait never go below this value
int bonusCapMin = mcMMO.p.getAdvancedConfig().getFishingReductionMinWaitCap(); int bonusCapMin = mcMMO.p.getAdvancedConfig().getFishingReductionMinWaitCap();
int bonusCapMax = mcMMO.p.getAdvancedConfig().getFishingReductionMaxWaitCap(); int bonusCapMax = mcMMO.p.getAdvancedConfig().getFishingReductionMaxWaitCap();
@@ -95,53 +86,18 @@ public class FishingManager extends SkillManager {
} }
public boolean canMasterAngler() { public boolean canMasterAngler() {
return mcMMO.getCompatibilityManager().getMasterAnglerCompatibilityLayer() != null return getSkillLevel() >= RankUtils.getUnlockLevel(SubSkillType.FISHING_MASTER_ANGLER)
&& getSkillLevel() >= RankUtils.getUnlockLevel(SubSkillType.FISHING_MASTER_ANGLER)
&& Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.FISHING_MASTER_ANGLER); && Permissions.isSubSkillEnabled(getPlayer(), SubSkillType.FISHING_MASTER_ANGLER);
} }
// public void setFishingRodCastTimestamp() public void setFishHookReference(Metadatable fishHook) {
// { if (!fishHook.getMetadata(MetadataConstants.METADATA_KEY_FISH_HOOK_REF).isEmpty()) {
// long currentTime = System.currentTimeMillis();
// //Only track spam casting if the fishing hook is fresh
// if (currentTime > fishHookSpawnTimestamp + 1000)
// return;
//
// if (currentTime < fishingRodCastTimestamp + FISHING_ROD_CAST_CD_MILLISECONDS)
// {
// ItemStack fishingRod = getPlayer().getInventory().getItemInMainHand();
//
// //Ensure correct hand item is damaged
// if (fishingRod.getType() != Material.FISHING_ROD) {
// fishingRod = getPlayer().getInventory().getItemInOffHand();
// }
//
// getPlayer().setFoodLevel(Math.max(getPlayer().getFoodLevel() - 1, 0));
// fishingRod.setDurability((short) (fishingRod.getDurability() + 5));
// getPlayer().updateInventory();
//
// if (lastWarnedExhaust + (1000) < currentTime)
// {
// getPlayer().sendMessage(LocaleLoader.getString("Fishing.Exhausting"));
// lastWarnedExhaust = currentTime;
// SoundManager.sendSound(getPlayer(), getPlayer().getLocation(), SoundType.TIRED);
// }
// }
//
// fishingRodCastTimestamp = System.currentTimeMillis();
// }
public void setFishHookReference(FishHook fishHook) {
if (fishHook.getMetadata(MetadataConstants.METADATA_KEY_FISH_HOOK_REF).size() > 0) {
return; return;
} }
fishHook.setMetadata(MetadataConstants.METADATA_KEY_FISH_HOOK_REF, fishHook.setMetadata(MetadataConstants.METADATA_KEY_FISH_HOOK_REF,
MetadataConstants.MCMMO_METADATA_VALUE); MetadataConstants.MCMMO_METADATA_VALUE);
this.fishHookReference = fishHook;
fishHookSpawnTimestamp = System.currentTimeMillis(); fishHookSpawnTimestamp = System.currentTimeMillis();
fishingRodCastTimestamp = System.currentTimeMillis();
} }
public boolean isFishingTooOften() { public boolean isFishingTooOften() {
@@ -180,21 +136,41 @@ public class FishingManager extends SkillManager {
} }
} }
/**
* Determines if the player is exploiting fishing by checking if they have caught
* more fish than the configured limit without moving their fishing spot.
* This method relies on internal state to determine if the player is exploiting fishing,
* and the centerOfCastVector parameter is no longer used and will be removed in
* a future version.
*
* @param centerOfCastVector unused
* @deprecated since 2.2.050, the parameter is no longer used and will be removed in
* a future version. The method now relies on internal state to determine if the player
* is exploiting fishing.
* @return true if the player is exploiting fishing, false otherwise
*/
@Deprecated(forRemoval = true, since = "2.2.050")
public boolean isExploitingFishing(Vector centerOfCastVector) { public boolean isExploitingFishing(Vector centerOfCastVector) {
return this.sameTarget && fishCaughtCounter >= ExperienceConfig.getInstance()
.getFishingExploitingOptionOverFishLimit();
}
/*Block targetBlock = getPlayer().getTargetBlock(BlockUtils.getTransparentBlocks(), 100); /**
* Determines if the player is exploiting fishing by checking if they have caught
if (!targetBlock.isLiquid()) { * more fish than the configured limit without moving their fishing spot.
return false; *
}*/ * @return true if the player is exploiting fishing, false otherwise
*/
public boolean isExploitingFishing() {
return this.sameTarget && fishCaughtCounter >= ExperienceConfig.getInstance() return this.sameTarget && fishCaughtCounter >= ExperienceConfig.getInstance()
.getFishingExploitingOptionOverFishLimit(); .getFishingExploitingOptionOverFishLimit();
} }
public static BoundingBox makeBoundingBox(Vector centerOfCastVector) { public static BoundingBox makeBoundingBox(Vector centerOfCastVector) {
int exploitingRange = ExperienceConfig.getInstance().getFishingExploitingOptionMoveRange(); int exploitingRange = ExperienceConfig.getInstance().getFishingExploitingOptionMoveRange();
return BoundingBox.of(centerOfCastVector, exploitingRange / 2, 1, exploitingRange / 2); return BoundingBox.of(centerOfCastVector,
(double) exploitingRange / 2, 1,
(double) exploitingRange / 2);
} }
public void setFishingTarget() { public void setFishingTarget() {
@@ -243,15 +219,6 @@ public class FishingManager extends SkillManager {
return mcMMO.p.getAdvancedConfig().getFishingVanillaXPModifier(getLootTier()); return mcMMO.p.getAdvancedConfig().getFishingVanillaXPModifier(getLootTier());
} }
/**
* Gets the Shake Mob probability
*
* @return Shake Mob probability
*/
public double getShakeProbability() {
return getShakeChance();
}
/** /**
* Handle the Fisherman's Diet ability * Handle the Fisherman's Diet ability
* *
@@ -293,102 +260,95 @@ public class FishingManager extends SkillManager {
* @param fishHook target fish hook * @param fishHook target fish hook
*/ */
public void processMasterAngler(@NotNull FishHook fishHook, int lureLevel) { public void processMasterAngler(@NotNull FishHook fishHook, int lureLevel) {
MasterAnglerCompatibilityLayer masterAnglerCompatibilityLayer = (MasterAnglerCompatibilityLayer) mcMMO.getCompatibilityManager() int maxWaitTicks = fishHook.getMaxWaitTime();
.getMasterAnglerCompatibilityLayer(); int minWaitTicks = fishHook.getMinWaitTime();
if (masterAnglerCompatibilityLayer != null) { int masterAnglerRank = RankUtils.getRank(mmoPlayer, SubSkillType.FISHING_MASTER_ANGLER);
int maxWaitTicks = masterAnglerCompatibilityLayer.getMaxWaitTime(fishHook); int convertedLureBonus = 0;
int minWaitTicks = masterAnglerCompatibilityLayer.getMinWaitTime(fishHook);
int masterAnglerRank = RankUtils.getRank(mmoPlayer, SubSkillType.FISHING_MASTER_ANGLER); //This avoids a Minecraft bug where lure levels above 3 break fishing
int convertedLureBonus = 0; if (lureLevel > 0) {
fishHook.setApplyLure(false);
//This avoids a Minecraft bug where lure levels above 3 break fishing convertedLureBonus = lureLevel * 100;
if (lureLevel > 0) {
masterAnglerCompatibilityLayer.setApplyLure(fishHook, false);
convertedLureBonus = lureLevel * 100;
}
boolean boatBonus = isInBoat();
int minWaitReduction = getMasterAnglerTickMinWaitReduction(masterAnglerRank, boatBonus);
int maxWaitReduction = getMasterAnglerTickMaxWaitReduction(masterAnglerRank, boatBonus,
convertedLureBonus);
int reducedMinWaitTime = getReducedTicks(minWaitTicks, minWaitReduction,
masterAnglerMinWaitLowerBound);
int reducedMaxWaitTime = getReducedTicks(maxWaitTicks, maxWaitReduction,
masterAnglerMaxWaitLowerBound);
boolean badValuesFix = false;
//If we find bad values correct it
if (reducedMaxWaitTime < reducedMinWaitTime) {
reducedMaxWaitTime = reducedMinWaitTime + 100;
badValuesFix = true;
}
final McMMOPlayerMasterAnglerEvent event =
new McMMOPlayerMasterAnglerEvent(mmoPlayer, reducedMinWaitTime,
reducedMaxWaitTime, this);
mcMMO.p.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
reducedMaxWaitTime = event.getReducedMaxWaitTime();
reducedMinWaitTime = event.getReducedMinWaitTime();
if (mmoPlayer.isDebugMode()) {
mmoPlayer.getPlayer().sendMessage(ChatColor.GOLD + "Master Angler Debug");
if (badValuesFix) {
mmoPlayer.getPlayer()
.sendMessage(ChatColor.RED + "Bad values were applied and corrected," +
" check your configs, minWaitLowerBound wait should never be lower than min wait.");
}
mmoPlayer.getPlayer().sendMessage(
"ALLOW STACK WITH LURE: " + masterAnglerCompatibilityLayer.getApplyLure(
fishHook));
mmoPlayer.getPlayer().sendMessage("MIN TICK REDUCTION: " + minWaitReduction);
mmoPlayer.getPlayer().sendMessage("MAX TICK REDUCTION: " + maxWaitReduction);
mmoPlayer.getPlayer().sendMessage("BOAT BONUS: " + boatBonus);
if (boatBonus) {
mmoPlayer.getPlayer()
.sendMessage("BOAT MAX TICK REDUCTION: " + maxWaitReduction);
mmoPlayer.getPlayer()
.sendMessage("BOAT MIN TICK REDUCTION: " + maxWaitReduction);
}
mmoPlayer.getPlayer().sendMessage("");
mmoPlayer.getPlayer()
.sendMessage(ChatColor.DARK_AQUA + "BEFORE MASTER ANGLER WAS APPLIED");
mmoPlayer.getPlayer().sendMessage("Original Max Wait Ticks: " + maxWaitTicks);
mmoPlayer.getPlayer().sendMessage("Original Min Wait Ticks: " + minWaitTicks);
mmoPlayer.getPlayer().sendMessage("");
mmoPlayer.getPlayer()
.sendMessage(ChatColor.DARK_AQUA + "AFTER MASTER ANGLER WAS APPLIED");
mmoPlayer.getPlayer().sendMessage("Current Max Wait Ticks: " + reducedMaxWaitTime);
mmoPlayer.getPlayer().sendMessage("Current Min Wait Ticks: " + reducedMinWaitTime);
mmoPlayer.getPlayer().sendMessage("");
mmoPlayer.getPlayer()
.sendMessage(ChatColor.DARK_AQUA + "Caps / Limits (edit in advanced.yml)");
mmoPlayer.getPlayer().sendMessage("Lowest possible minWaitLowerBound wait ticks "
+ masterAnglerMinWaitLowerBound);
mmoPlayer.getPlayer().sendMessage(
"Lowest possible min wait ticks " + masterAnglerMaxWaitLowerBound);
}
masterAnglerCompatibilityLayer.setMaxWaitTime(fishHook, reducedMaxWaitTime);
masterAnglerCompatibilityLayer.setMinWaitTime(fishHook, reducedMinWaitTime);
} }
boolean boatBonus = isInBoat();
int minWaitReduction = getMasterAnglerTickMinWaitReduction(masterAnglerRank, boatBonus);
int maxWaitReduction = getMasterAnglerTickMaxWaitReduction(masterAnglerRank, boatBonus,
convertedLureBonus);
int reducedMinWaitTime = getReducedTicks(minWaitTicks, minWaitReduction,
masterAnglerMinWaitLowerBound);
int reducedMaxWaitTime = getReducedTicks(maxWaitTicks, maxWaitReduction,
masterAnglerMaxWaitLowerBound);
boolean badValuesFix = false;
//If we find bad values correct it
if (reducedMaxWaitTime < reducedMinWaitTime) {
reducedMaxWaitTime = reducedMinWaitTime + 100;
badValuesFix = true;
}
final McMMOPlayerMasterAnglerEvent event =
new McMMOPlayerMasterAnglerEvent(mmoPlayer, reducedMinWaitTime,
reducedMaxWaitTime, this);
mcMMO.p.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return;
}
reducedMaxWaitTime = event.getReducedMaxWaitTime();
reducedMinWaitTime = event.getReducedMinWaitTime();
if (mmoPlayer.isDebugMode()) {
mmoPlayer.getPlayer().sendMessage(ChatColor.GOLD + "Master Angler Debug");
if (badValuesFix) {
mmoPlayer.getPlayer()
.sendMessage(ChatColor.RED + "Bad values were applied and corrected," +
" check your configs, minWaitLowerBound wait should never be lower than min wait.");
}
mmoPlayer.getPlayer().sendMessage(
"ALLOW STACK WITH LURE: " + fishHook.getApplyLure());
mmoPlayer.getPlayer().sendMessage("MIN TICK REDUCTION: " + minWaitReduction);
mmoPlayer.getPlayer().sendMessage("MAX TICK REDUCTION: " + maxWaitReduction);
mmoPlayer.getPlayer().sendMessage("BOAT BONUS: " + boatBonus);
if (boatBonus) {
mmoPlayer.getPlayer()
.sendMessage("BOAT MAX TICK REDUCTION: " + maxWaitReduction);
mmoPlayer.getPlayer()
.sendMessage("BOAT MIN TICK REDUCTION: " + maxWaitReduction);
}
mmoPlayer.getPlayer().sendMessage("");
mmoPlayer.getPlayer()
.sendMessage(ChatColor.DARK_AQUA + "BEFORE MASTER ANGLER WAS APPLIED");
mmoPlayer.getPlayer().sendMessage("Original Max Wait Ticks: " + maxWaitTicks);
mmoPlayer.getPlayer().sendMessage("Original Min Wait Ticks: " + minWaitTicks);
mmoPlayer.getPlayer().sendMessage("");
mmoPlayer.getPlayer()
.sendMessage(ChatColor.DARK_AQUA + "AFTER MASTER ANGLER WAS APPLIED");
mmoPlayer.getPlayer().sendMessage("Current Max Wait Ticks: " + reducedMaxWaitTime);
mmoPlayer.getPlayer().sendMessage("Current Min Wait Ticks: " + reducedMinWaitTime);
mmoPlayer.getPlayer().sendMessage("");
mmoPlayer.getPlayer()
.sendMessage(ChatColor.DARK_AQUA + "Caps / Limits (edit in advanced.yml)");
mmoPlayer.getPlayer().sendMessage("Lowest possible minWaitLowerBound wait ticks "
+ masterAnglerMinWaitLowerBound);
mmoPlayer.getPlayer().sendMessage(
"Lowest possible min wait ticks " + masterAnglerMaxWaitLowerBound);
}
fishHook.setMaxWaitTime(reducedMaxWaitTime);
fishHook.setMinWaitTime(reducedMinWaitTime);
} }
public int getReducedTicks(int ticks, int totalBonus, int tickBounds) { public int getReducedTicks(int ticks, int totalBonus, int tickBounds) {
@@ -446,7 +406,6 @@ public class FishingManager extends SkillManager {
* @param fishingCatch The {@link Item} initially caught * @param fishingCatch The {@link Item} initially caught
*/ */
public void processFishing(@NotNull Item fishingCatch) { public void processFishing(@NotNull Item fishingCatch) {
this.fishingCatch = fishingCatch;
int fishXp = ExperienceConfig.getInstance() int fishXp = ExperienceConfig.getInstance()
.getXp(PrimarySkillType.FISHING, fishingCatch.getItemStack().getType()); .getXp(PrimarySkillType.FISHING, fishingCatch.getItemStack().getType());
int treasureXp = 0; int treasureXp = 0;
@@ -458,7 +417,6 @@ public class FishingManager extends SkillManager {
if (mcMMO.p.getGeneralConfig().getFishingDropsEnabled() && Permissions.isSubSkillEnabled( if (mcMMO.p.getGeneralConfig().getFishingDropsEnabled() && Permissions.isSubSkillEnabled(
player, SubSkillType.FISHING_TREASURE_HUNTER)) { player, SubSkillType.FISHING_TREASURE_HUNTER)) {
treasure = getFishingTreasure(); treasure = getFishingTreasure();
this.fishingCatch = null;
} }
if (treasure != null) { if (treasure != null) {
@@ -541,10 +499,6 @@ public class FishingManager extends SkillManager {
return experience * getVanillaXpMultiplier(); return experience * getVanillaXpMultiplier();
} }
public Location getHookLocation() {
return hookLocation;
}
/** /**
* Handle the Shake ability * Handle the Shake ability
* *
@@ -692,11 +646,6 @@ public class FishingManager extends SkillManager {
treasureDrop.setDurability((short) (Misc.getRandom().nextInt(maxDurability))); treasureDrop.setDurability((short) (Misc.getRandom().nextInt(maxDurability)));
} }
//TODO: Add option to randomize the amount rewarded
/*if (treasureDrop.getAmount() > 1) {
treasureDrop.setAmount(Misc.getRandom().nextInt(treasureDrop.getAmount()) + 1);
}*/
treasure.setDrop(treasureDrop); treasure.setDrop(treasureDrop);
return treasure; return treasure;

View File

@@ -535,6 +535,10 @@ public class HerbalismManager extends SkillManager {
} }
public boolean isAgeableMature(Ageable ageable) { public boolean isAgeableMature(Ageable ageable) {
// Sweet berry bush is harvestable at age 2 and 3 (max is 3)
if (ageable.getMaterial() == Material.SWEET_BERRY_BUSH) {
return ageable.getAge() >= 2;
}
return ageable.getAge() == ageable.getMaximumAge() return ageable.getAge() == ageable.getMaximumAge()
&& ageable.getAge() != 0; && ageable.getAge() != 0;
} }
@@ -868,6 +872,7 @@ public class HerbalismManager extends SkillManager {
case "beetroots" -> replantMaterial = Material.matchMaterial("BEETROOT_SEEDS"); case "beetroots" -> replantMaterial = Material.matchMaterial("BEETROOT_SEEDS");
case "cocoa" -> replantMaterial = Material.matchMaterial("COCOA_BEANS"); case "cocoa" -> replantMaterial = Material.matchMaterial("COCOA_BEANS");
case "torchflower" -> replantMaterial = Material.matchMaterial("TORCHFLOWER_SEEDS"); case "torchflower" -> replantMaterial = Material.matchMaterial("TORCHFLOWER_SEEDS");
case "sweet_berry_bush" -> replantMaterial = Material.matchMaterial("SWEET_BERRIES");
default -> { default -> {
return false; return false;
} }
@@ -956,6 +961,17 @@ public class HerbalismManager extends SkillManager {
} }
break; break;
case "sweet_berry_bush":
// Sweet berry bush has ages 0-3, where 2+ has berries
// Cap at age 1 to prevent instant re-harvest exploit with enough herbalism levels
if (greenTerra || greenThumbStage >= 2) {
finalAge = 1;
} else {
finalAge = 0;
}
break;
default: default:
return false; return false;
} }

View File

@@ -0,0 +1,121 @@
package com.gmail.nossr50.skills.spears;
import static com.gmail.nossr50.util.random.ProbabilityUtil.isStaticSkillRNGSuccessful;
import static com.gmail.nossr50.util.skills.RankUtils.getRank;
import com.gmail.nossr50.datatypes.interactions.NotificationType;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.skills.SkillManager;
import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.player.NotificationManager;
import java.util.Locale;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpearsManager extends SkillManager {
private static @Nullable PotionEffectType swiftnessEffectType;
public SpearsManager(McMMOPlayer mmoPlayer) {
super(mmoPlayer, PrimarySkillType.SPEARS);
}
private static @Nullable PotionEffectType mockSpigotMatch(@NotNull String input) {
// Replicates match() behaviour for older versions lacking this API
final String filtered = input.toLowerCase(Locale.ROOT).replaceAll("\\s+", "_");
final NamespacedKey namespacedKey = NamespacedKey.fromString(filtered);
return (namespacedKey != null) ? Registry.EFFECT.get(namespacedKey) : null;
}
/**
* Process Momentum activation.
*/
public void potentiallyApplyMomentum() {
// Lazy initialized to avoid some backwards compatibility issues
if (swiftnessEffectType == null) {
if (mockSpigotMatch("speed") == null) {
mcMMO.p.getLogger().severe("Unable to find the Speed PotionEffectType, " +
"mcMMO will not function properly.");
throw new IllegalStateException("Unable to find the Speed PotionEffectType!");
} else {
swiftnessEffectType = mockSpigotMatch("speed");
}
}
if (!canMomentumBeApplied()) {
return;
}
int momentumRank = getRank(getPlayer(), SubSkillType.SPEARS_MOMENTUM);
// Chance to activate on hit is influence by the CD
double momentumOdds = (mcMMO.p.getAdvancedConfig().getMomentumChanceToApplyOnHit(momentumRank)
* Math.min(mmoPlayer.getAttackStrength(), 1.0D));
if (isStaticSkillRNGSuccessful(PrimarySkillType.SPEARS, mmoPlayer, momentumOdds)) {
if (mmoPlayer.useChatNotifications()) {
NotificationManager.sendPlayerInformation(mmoPlayer.getPlayer(),
NotificationType.SUBSKILL_MESSAGE, "Spears.SubSkill.Momentum.Activated");
}
// Momentum is success, Momentum the target
getPlayer().addPotionEffect(swiftnessEffectType.createEffect(
getMomentumTickDuration(momentumRank),
getMomentumStrength()));
// TODO: Consider adding an effect here
// ParticleEffectUtils.playMomentumEffect(target);
}
}
public static int getMomentumTickDuration(int momentumRank) {
return 20 * (momentumRank * 2);
}
public static int getMomentumStrength() {
return 2;
}
private boolean canMomentumBeApplied() {
// TODO: Potentially it should overwrite the effect if we are providing a stronger one
if (swiftnessEffectType == null) {
return false;
}
final PotionEffect currentlyAppliedPotion = getPlayer()
.getPotionEffect(swiftnessEffectType);
if (currentlyAppliedPotion != null) {
if (isCurrentPotionEffectStronger(currentlyAppliedPotion)) {
return false;
}
}
if (!Permissions.canUseSubSkill(mmoPlayer.getPlayer(), SubSkillType.SPEARS_MOMENTUM)) {
return false;
}
return true;
}
private boolean isCurrentPotionEffectStronger(@NotNull PotionEffect potionEffect) {
if (potionEffect.getAmplifier() > getMomentumStrength()) {
return true;
}
if (potionEffect.getDuration() > getMomentumTickDuration(getRank(getPlayer(),
SubSkillType.SPEARS_MOMENTUM))) {
return true;
}
return false;
}
public double getSpearMasteryBonusDamage() {
return mcMMO.p.getAdvancedConfig().getSpearMasteryRankDamageMultiplier()
* getRank(getPlayer(), SubSkillType.SPEARS_SPEAR_MASTERY);
}
}

View File

@@ -304,7 +304,7 @@ public class TamingManager extends SkillManager {
Player owner = getPlayer(); Player owner = getPlayer();
wolf.teleport(owner); mcMMO.p.getFoliaLib().getScheduler().teleportAsync(wolf, owner.getLocation());
NotificationManager.sendPlayerInformation(owner, NotificationType.SUBSKILL_MESSAGE, NotificationManager.sendPlayerInformation(owner, NotificationType.SUBSKILL_MESSAGE,
"Taming.Listener.Wolf"); "Taming.Listener.Wolf");
} }

View File

@@ -166,20 +166,6 @@ public final class ItemUtils {
} }
} }
// TODO: Unit tests
public static boolean isCrossbow(@NotNull ItemStack item) {
return mcMMO.getMaterialMapStore().isCrossbow(item.getType().getKey().getKey());
}
// TODO: Unit tests
public static boolean isTrident(@NotNull ItemStack item) {
return mcMMO.getMaterialMapStore().isTrident(item.getType().getKey().getKey());
}
public static boolean isMace(@NotNull ItemStack item) {
return mcMMO.getMaterialMapStore().isMace(item.getType().getKey().getKey());
}
public static boolean hasItemInEitherHand(@NotNull Player player, Material material) { public static boolean hasItemInEitherHand(@NotNull Player player, Material material) {
return player.getInventory().getItemInMainHand().getType() == material return player.getInventory().getItemInMainHand().getType() == material
|| player.getInventory().getItemInOffHand().getType() == material; || player.getInventory().getItemInOffHand().getType() == material;
@@ -276,6 +262,46 @@ public final class ItemUtils {
return null; return null;
} }
/**
* Checks if the item is a crossbow.
*
* @param item Item to check
* @return true if the item is a crossbow, false otherwise
*/
public static boolean isCrossbow(@NotNull ItemStack item) {
return mcMMO.getMaterialMapStore().isCrossbow(item.getType().getKey().getKey());
}
/**
* Checks if the item is a trident.
*
* @param item Item to check
* @return true if the item is a trident, false otherwise
*/
public static boolean isTrident(@NotNull ItemStack item) {
return mcMMO.getMaterialMapStore().isTrident(item.getType().getKey().getKey());
}
/**
* Checks if the item is a mace.
*
* @param item Item to check
* @return true if the item is a mace, false otherwise
*/
public static boolean isMace(@NotNull ItemStack item) {
return mcMMO.getMaterialMapStore().isMace(item.getType().getKey().getKey());
}
/**
* Checks if the item is a spear.
* @param item Item to check
*
* @return true if the item is a spear, false otherwise
*/
public static boolean isSpear(@NotNull ItemStack item) {
return mcMMO.getMaterialMapStore().isSpear(item.getType().getKey().getKey());
}
/** /**
* Checks if the item is a sword. * Checks if the item is a sword.
* *

View File

@@ -8,6 +8,7 @@ public class LogUtils {
public static final String DEBUG_STR = "[D] "; public static final String DEBUG_STR = "[D] ";
public static void debug(@NotNull Logger logger, @NotNull String message) { public static void debug(@NotNull Logger logger, @NotNull String message) {
// Messages here will get filtered based on config settings via LogFilter
logger.info(DEBUG_STR + message); logger.info(DEBUG_STR + message);
} }
} }

View File

@@ -51,9 +51,10 @@ public class MaterialMapStore {
private final @NotNull HashSet<String> tridents; private final @NotNull HashSet<String> tridents;
private final @NotNull HashSet<String> bows; private final @NotNull HashSet<String> bows;
private final @NotNull HashSet<String> crossbows; private final @NotNull HashSet<String> crossbows;
private final @NotNull HashSet<String> tools;
private final @NotNull HashSet<String> enchantables;
private final @NotNull HashSet<String> maces; private final @NotNull HashSet<String> maces;
private final @NotNull HashSet<String> spears;
private final @NotNull HashSet<String> enchantables;
private final @NotNull HashSet<String> tools;
private final @NotNull HashSet<String> ores; private final @NotNull HashSet<String> ores;
private final @NotNull HashSet<String> intendedToolPickAxe; private final @NotNull HashSet<String> intendedToolPickAxe;
@@ -95,15 +96,15 @@ public class MaterialMapStore {
crossbows = new HashSet<>(); crossbows = new HashSet<>();
stringTools = new HashSet<>(); stringTools = new HashSet<>();
prismarineTools = new HashSet<>(); prismarineTools = new HashSet<>();
tools = new HashSet<>();
swords = new HashSet<>(); swords = new HashSet<>();
axes = new HashSet<>(); axes = new HashSet<>();
pickAxes = new HashSet<>(); pickAxes = new HashSet<>();
shovels = new HashSet<>(); shovels = new HashSet<>();
hoes = new HashSet<>(); hoes = new HashSet<>();
tridents = new HashSet<>(); tridents = new HashSet<>();
spears = new HashSet<>();
maces = new HashSet<>(); maces = new HashSet<>();
tools = new HashSet<>();
enchantables = new HashSet<>(); enchantables = new HashSet<>();
@@ -459,6 +460,7 @@ public class MaterialMapStore {
enchantables.addAll(bows); enchantables.addAll(bows);
enchantables.addAll(crossbows); enchantables.addAll(crossbows);
enchantables.addAll(maces); enchantables.addAll(maces);
enchantables.addAll(spears);
enchantables.add("shears"); enchantables.add("shears");
enchantables.add("fishing_rod"); enchantables.add("fishing_rod");
@@ -484,6 +486,7 @@ public class MaterialMapStore {
fillShovels(); fillShovels();
fillTridents(); fillTridents();
fillMaces(); fillMaces();
fillSpears();
fillStringTools(); fillStringTools();
fillPrismarineTools(); fillPrismarineTools();
fillBows(); fillBows();
@@ -502,6 +505,7 @@ public class MaterialMapStore {
tools.addAll(bows); tools.addAll(bows);
tools.addAll(crossbows); tools.addAll(crossbows);
tools.addAll(maces); tools.addAll(maces);
tools.addAll(spears);
} }
private void fillBows() { private void fillBows() {
@@ -527,6 +531,16 @@ public class MaterialMapStore {
maces.add("mace"); maces.add("mace");
} }
private void fillSpears() {
spears.add("wooden_spear");
spears.add("stone_spear");
spears.add("copper_spear");
spears.add("iron_spear");
spears.add("golden_spear");
spears.add("diamond_spear");
spears.add("netherite_spear");
}
private void fillTridents() { private void fillTridents() {
tridents.add("trident"); tridents.add("trident");
} }
@@ -659,6 +673,7 @@ public class MaterialMapStore {
woodTools.add("wooden_hoe"); woodTools.add("wooden_hoe");
woodTools.add("wooden_pickaxe"); woodTools.add("wooden_pickaxe");
woodTools.add("wooden_shovel"); woodTools.add("wooden_shovel");
woodTools.add("wooden_spear");
} }
private void fillStoneToolsWhiteList() { private void fillStoneToolsWhiteList() {
@@ -667,6 +682,7 @@ public class MaterialMapStore {
stoneTools.add("stone_hoe"); stoneTools.add("stone_hoe");
stoneTools.add("stone_pickaxe"); stoneTools.add("stone_pickaxe");
stoneTools.add("stone_shovel"); stoneTools.add("stone_shovel");
stoneTools.add("stone_spear");
} }
private void fillCopperToolsWhiteList() { private void fillCopperToolsWhiteList() {
@@ -675,6 +691,7 @@ public class MaterialMapStore {
copperTools.add("copper_hoe"); copperTools.add("copper_hoe");
copperTools.add("copper_pickaxe"); copperTools.add("copper_pickaxe");
copperTools.add("copper_shovel"); copperTools.add("copper_shovel");
copperTools.add("copper_spear");
} }
private void fillIronToolsWhiteList() { private void fillIronToolsWhiteList() {
@@ -683,6 +700,7 @@ public class MaterialMapStore {
ironTools.add("iron_hoe"); ironTools.add("iron_hoe");
ironTools.add("iron_pickaxe"); ironTools.add("iron_pickaxe");
ironTools.add("iron_shovel"); ironTools.add("iron_shovel");
ironTools.add("iron_spear");
//Used for repair, remove in 2.2 //Used for repair, remove in 2.2
//TODO: Remove in config update //TODO: Remove in config update
@@ -704,6 +722,7 @@ public class MaterialMapStore {
goldTools.add("golden_hoe"); goldTools.add("golden_hoe");
goldTools.add("golden_pickaxe"); goldTools.add("golden_pickaxe");
goldTools.add("golden_shovel"); goldTools.add("golden_shovel");
goldTools.add("golden_spear");
} }
private void fillDiamondToolsWhiteList() { private void fillDiamondToolsWhiteList() {
@@ -712,6 +731,7 @@ public class MaterialMapStore {
diamondTools.add("diamond_hoe"); diamondTools.add("diamond_hoe");
diamondTools.add("diamond_pickaxe"); diamondTools.add("diamond_pickaxe");
diamondTools.add("diamond_shovel"); diamondTools.add("diamond_shovel");
diamondTools.add("diamond_spear");
} }
private void fillNetheriteToolsWhiteList() { private void fillNetheriteToolsWhiteList() {
@@ -720,6 +740,7 @@ public class MaterialMapStore {
netheriteTools.add("netherite_hoe"); netheriteTools.add("netherite_hoe");
netheriteTools.add("netherite_pickaxe"); netheriteTools.add("netherite_pickaxe");
netheriteTools.add("netherite_shovel"); netheriteTools.add("netherite_shovel");
netheriteTools.add("netherite_spear");
} }
private void fillGlassBlockWhiteList() { private void fillGlassBlockWhiteList() {
@@ -874,6 +895,14 @@ public class MaterialMapStore {
return maces.contains(id); return maces.contains(id);
} }
public boolean isSpear(@NotNull Material material) {
return isSpear(material.getKey().getKey());
}
public boolean isSpear(@NotNull String id) {
return spears.contains(id);
}
public boolean isLeatherArmor(@NotNull Material material) { public boolean isLeatherArmor(@NotNull Material material) {
return isLeatherArmor(material.getKey().getKey()); return isLeatherArmor(material.getKey().getKey());
} }

View File

@@ -0,0 +1,36 @@
package com.gmail.nossr50.util;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.platform.MinecraftGameVersion;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jetbrains.annotations.NotNull;
public class MinecraftGameVersionFactory {
public static @NotNull MinecraftGameVersion calculateGameVersion(
@NotNull String platformVersionString) {
int major = 0, minor = 0, patch = 0;
LogUtils.debug(mcMMO.p.getLogger(), "Platform String: " + platformVersionString);
// Gets two numbers separated by . and optional third number after next dot. Must end with - or _
Matcher versionMatch = Pattern.compile("(\\d+)\\.(\\d+)(?:\\.(\\d+))?[-_].*")
.matcher(platformVersionString);
if (versionMatch.find()) {
major = Integer.parseInt(versionMatch.group(1));
minor = Integer.parseInt(versionMatch.group(2));
if (versionMatch.group(3) != null) {
patch = Integer.parseInt(versionMatch.group(3));
}
}
LogUtils.debug(mcMMO.p.getLogger(), "Minecraft version determined to be - "
+ major + "."
+ minor + "."
+ patch);
return new MinecraftGameVersion(major, minor, patch);
}
}

View File

@@ -1,6 +0,0 @@
package com.gmail.nossr50.util;
public enum PotionCompatibilityType {
PRE_1_20_5,
MODERN
}

View File

@@ -1,8 +1,5 @@
package com.gmail.nossr50.util; package com.gmail.nossr50.util;
import com.gmail.nossr50.mcMMO;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -10,34 +7,19 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.bukkit.NamespacedKey; import org.bukkit.NamespacedKey;
import org.bukkit.inventory.meta.PotionMeta; import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionType; import org.bukkit.potion.PotionType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class PotionUtil { public final class PotionUtil {
// Some of the old potion types got renamed, our configs can still contain these old names // Some of the old potion types got renamed, our configs can still contain these old names
private static final Map<String, String> legacyPotionTypes = new HashMap<>(); private static final Map<String, String> legacyPotionTypes = new HashMap<>();
private static final Method methodPotionTypeGetKey;
private static final Method methodPotionTypeGetEffectType;
private static final Method methodPotionTypeGetPotionEffects;
private static final Method methodPotionDataIsUpgraded;
private static final Method methodPotionDataIsExtended;
private static final Method methodPotionDataGetType;
private static final Method methodPotionMetaGetBasePotionData;
private static final Method methodPotionMetaSetBasePotionData;
private static final Method methodPotionMetaGetBasePotionType;
private static final Method methodPotionMetaSetBasePotionType;
private static final Class<?> potionDataClass;
public static final String STRONG = "STRONG"; public static final String STRONG = "STRONG";
public static final String LONG = "LONG"; public static final String LONG = "LONG";
public static final String WATER_POTION_TYPE_STR = "WATER";
private static final PotionCompatibilityType COMPATIBILITY_MODE;
static { static {
potionDataClass = getPotionDataClass();
// Uncraftable doesn't exist in modern versions // Uncraftable doesn't exist in modern versions
// It served as a potion that didn't craft into anything else so it didn't conflict with vanilla systems // It served as a potion that didn't craft into anything else so it didn't conflict with vanilla systems
// Instead we will use Mundane, which doesn't make anything in vanilla systems // Instead we will use Mundane, which doesn't make anything in vanilla systems
@@ -47,199 +29,48 @@ public class PotionUtil {
legacyPotionTypes.put("INSTANT_HEAL", "HEALING"); legacyPotionTypes.put("INSTANT_HEAL", "HEALING");
legacyPotionTypes.put("INSTANT_DAMAGE", "HARMING"); legacyPotionTypes.put("INSTANT_DAMAGE", "HARMING");
legacyPotionTypes.put("REGEN", "REGENERATION"); legacyPotionTypes.put("REGEN", "REGENERATION");
methodPotionTypeGetKey = getKeyMethod();
methodPotionDataIsUpgraded = getPotionDataIsUpgraded();
methodPotionDataIsExtended = getPotionDataIsExtended();
methodPotionMetaGetBasePotionData = getGetBasePotionDataMethod();
methodPotionMetaGetBasePotionType = getGetBasePotionTypeMethod();
methodPotionMetaSetBasePotionType = getMethodPotionMetaSetBasePotionType();
methodPotionDataGetType = getPotionDataGetTypeMethod();
methodPotionTypeGetEffectType = getPotionTypeEffectTypeMethod();
methodPotionTypeGetPotionEffects = getPotionTypeGetPotionEffectsMethod();
methodPotionMetaSetBasePotionData = setBasePotionData();
if (potionDataClass != null
&& !mcMMO.getCompatibilityManager().getMinecraftGameVersion().isAtLeast(1, 20, 5)) {
COMPATIBILITY_MODE = PotionCompatibilityType.PRE_1_20_5;
} else {
COMPATIBILITY_MODE = PotionCompatibilityType.MODERN;
}
} }
private PotionUtil() {}
/** /**
* Derive a potion from a partial name, and whether it should be upgraded or extended. * Derive a potion from a partial name, and whether it should be upgraded or extended.
* *
* @param partialName potion type as a string, can be a substring of the potion type but must * <p>In 1.20.5+, "upgraded" and "extended" are represented by distinct {@link PotionType}
* match exactly * constants (e.g. STRONG_HEALING, LONG_SWIFTNESS), not flags.
* @return The potion type
*/
public static PotionType matchPotionType(String partialName, boolean isUpgraded,
boolean isExtended) {
if (COMPATIBILITY_MODE == PotionCompatibilityType.PRE_1_20_5) {
return matchLegacyPotionType(partialName);
} else {
final String updatedName = convertLegacyNames(partialName).toUpperCase(Locale.ENGLISH);
return Arrays.stream(PotionType.values())
.filter(potionType -> getKeyGetKey(potionType)
.toUpperCase(Locale.ENGLISH).contains(updatedName))
.filter(potionType -> isUpgraded == potionType.name()
.toUpperCase(Locale.ENGLISH).startsWith(STRONG + "_"))
.filter(potionType -> isExtended == potionType.name()
.toUpperCase(Locale.ENGLISH).startsWith(LONG + "_"))
.findAny().orElse(null);
}
}
/**
* Legacy matching for {@link PotionType}
* *
* @param name The partial name of the potion * @param partialName potion type as a string, can be a substring of the potion type but must match
* @return The potion type * against the potion's key or enum name
* @return The potion type, or null if no match
*/ */
private static PotionType matchLegacyPotionType(String name) { public static @Nullable PotionType matchPotionType(String partialName, boolean isUpgraded, boolean isExtended) {
if (partialName == null || partialName.isEmpty()) {
return null;
}
final String updatedName = convertLegacyNames(partialName).toUpperCase(Locale.ENGLISH);
return Arrays.stream(PotionType.values()) return Arrays.stream(PotionType.values())
.filter(potionType -> getKeyGetKey(potionType).equalsIgnoreCase(name) // Allow matching by namespace key ("swiftness", "long_swiftness") or enum name
|| getKeyGetKey(potionType).equalsIgnoreCase(convertLegacyNames(name)) .filter(potionType -> {
|| potionType.name().equalsIgnoreCase(name) final NamespacedKey key = potionType.getKey();
|| potionType.name().equalsIgnoreCase(convertLegacyNames(name))) final String keyStr = key != null ? key.getKey() : "";
.findAny().orElse(null); return keyStr.toUpperCase(Locale.ENGLISH).contains(updatedName)
} || potionType.name().toUpperCase(Locale.ENGLISH).contains(updatedName);
})
private static String convertUpgradedOrExtended(String potionType, boolean isUpgraded, // Enforce strong/long selection by the enum name prefix convention
boolean isExtended) { .filter(potionType -> isUpgraded == potionType.name().startsWith(STRONG + "_"))
if (isUpgraded) { .filter(potionType -> isExtended == potionType.name().startsWith(LONG + "_"))
potionType = STRONG + "_" + potionType; .findAny()
} .orElse(null);
if (isExtended) {
potionType = LONG + "_" + potionType;
}
return potionType;
}
public static String getKeyGetKey(PotionType potionType) {
try {
if (getKeyMethod() != null) {
NamespacedKey key = (NamespacedKey) methodPotionTypeGetKey.invoke(potionType);
return key.getKey();
} else {
return potionType.name();
}
} catch (InvocationTargetException | IllegalAccessException e) {
mcMMO.p.getLogger().warning("Failed to get potion key for " + potionType.name());
return potionType.name();
}
}
private static Class<?> getPotionDataClass() {
try {
return Class.forName("org.bukkit.potion.PotionData");
} catch (ClassNotFoundException e) {
return null;
}
} }
/** /**
* Older versions of Spigot do not have getKey() in PotionType We need to check for the * Returns the NamespacedKey key string portion for this potion type (e.g. "swiftness").
* existence of this method before calling it
*
* @return The getKey method
*/ */
private static @Nullable Method getKeyMethod() { public static @NotNull String getKeyGetKey(@NotNull PotionType potionType) {
try { final NamespacedKey key = potionType.getKey();
return PotionType.class.getMethod("getKey"); return key != null ? key.getKey() : potionType.name();
} catch (NoSuchMethodException e) {
return null;
}
}
private static @Nullable Method setBasePotionData() {
try {
return PotionMeta.class.getMethod("setBasePotionData", potionDataClass);
} catch (NoSuchMethodException e) {
return null;
}
}
private static Method getMethodPotionMetaSetBasePotionType() {
try {
return PotionMeta.class.getMethod("setBasePotionType", PotionType.class);
} catch (NoSuchMethodException e) {
return null;
}
}
private static Method getSetBasePotionData() {
try {
return PotionMeta.class.getMethod("setBasePotionData", potionDataClass);
} catch (NoSuchMethodException e) {
return null;
}
}
private static @Nullable Method getPotionDataIsUpgraded() {
try {
// TODO: <?> Needed?
final Class<?> clazz = Class.forName("org.bukkit.potion.PotionData");
return clazz.getMethod("isUpgraded");
} catch (NoSuchMethodException | ClassNotFoundException e) {
return null;
}
}
private static @Nullable Method getPotionDataIsExtended() {
try {
// TODO: <?> Needed?
final Class<?> clazz = Class.forName("org.bukkit.potion.PotionData");
return clazz.getMethod("isExtended");
} catch (NoSuchMethodException | ClassNotFoundException e) {
return null;
}
}
/**
* Newer versions of Spigot do not have getBasePotionData() in PotionMeta
*
* @return the getBasePotionData method, or null if it does not exist
*/
private static @Nullable Method getGetBasePotionDataMethod() {
try {
return PotionMeta.class.getMethod("getBasePotionData");
} catch (NoSuchMethodException e) {
return null;
}
}
private static Method getGetBasePotionTypeMethod() {
try {
return PotionMeta.class.getMethod("getBasePotionType");
} catch (NoSuchMethodException e) {
return null;
}
}
private static Method getPotionDataGetTypeMethod() {
try {
final Class<?> clazz = Class.forName("org.bukkit.potion.PotionData");
return clazz.getMethod("getType");
} catch (NoSuchMethodException | ClassNotFoundException e) {
return null;
}
}
private static Method getPotionTypeEffectTypeMethod() {
try {
return PotionType.class.getMethod("getEffectType");
} catch (NoSuchMethodException e) {
return null;
}
}
private static Method getPotionTypeGetPotionEffectsMethod() {
try {
return PotionType.class.getMethod("getPotionEffects");
} catch (NoSuchMethodException e) {
return null;
}
} }
public static String convertPotionConfigName(String legacyName) { public static String convertPotionConfigName(String legacyName) {
@@ -252,318 +83,91 @@ public class PotionUtil {
if (replacementName.contains("_II")) { if (replacementName.contains("_II")) {
replacementName = replacementName.replace("_II", ""); replacementName = replacementName.replace("_II", "");
replacementName = "STRONG_" + replacementName; replacementName = STRONG + "_" + replacementName;
} else if (replacementName.contains("_EXTENDED")) { } else if (replacementName.contains("_EXTENDED")) {
replacementName = replacementName.replace("_EXTENDED", ""); replacementName = replacementName.replace("_EXTENDED", "");
replacementName = "LONG_" + replacementName; replacementName = LONG + "_" + replacementName;
} }
return replacementName; return replacementName;
} }
public static String convertLegacyNames(String legacyPotionType) { public static String convertLegacyNames(String legacyPotionType) {
String modernized = legacyPotionType; String modernized = legacyPotionType;
// check for legacy names
for (var key : legacyPotionTypes.keySet()) { for (var key : legacyPotionTypes.keySet()) {
if (modernized.contains(key)) { if (modernized.contains(key)) {
// Replace the legacy name with the new name
modernized = modernized.replace(key, legacyPotionTypes.get(key)); modernized = modernized.replace(key, legacyPotionTypes.get(key));
break; break;
} }
} }
return modernized; return modernized;
} }
public static boolean hasLegacyName(String potionType) { public static boolean isStrong(@NotNull PotionMeta potionMeta) {
for (var key : legacyPotionTypes.keySet()) { final PotionType base = potionMeta.getBasePotionType();
if (potionType.contains(key)) { return base != null && base.name().startsWith(STRONG + "_");
return true;
}
}
return false;
} }
public static boolean isStrong(PotionMeta potionMeta) { public static boolean isLong(@NotNull PotionMeta potionMeta) {
if (methodPotionMetaGetBasePotionData == null) { final PotionType base = potionMeta.getBasePotionType();
return isStrongModern(potionMeta); return base != null && base.name().startsWith(LONG + "_");
} else {
return isStrongLegacy(potionMeta);
}
}
public static boolean isLong(PotionMeta potionMeta) {
if (methodPotionMetaGetBasePotionData == null) {
return isLongModern(potionMeta);
} else {
return isLongLegacy(potionMeta);
}
}
private static boolean isLongLegacy(PotionMeta potionMeta) {
try {
Object potionData = methodPotionMetaGetBasePotionData.invoke(potionMeta);
return (boolean) methodPotionDataIsExtended.invoke(potionData);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static boolean isLongModern(PotionMeta potionMeta) {
try {
return getModernPotionTypeKey(potionMeta).getKey().startsWith(LONG);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static boolean isStrongLegacy(PotionMeta potionMeta) {
try {
Object potionData = methodPotionMetaGetBasePotionData.invoke(potionMeta);
return (boolean) methodPotionDataIsUpgraded.invoke(potionData);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static boolean isStrongModern(PotionMeta potionMeta) {
try {
return getModernPotionTypeKey(potionMeta).getKey().startsWith(STRONG);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static NamespacedKey getModernPotionTypeKey(PotionMeta potionMeta)
throws IllegalAccessException, InvocationTargetException {
PotionType potionType = (PotionType) methodPotionMetaGetBasePotionType.invoke(potionMeta);
return (NamespacedKey) methodPotionTypeGetKey.invoke(potionType);
}
public static boolean isPotionJustWater(PotionMeta potionMeta) {
return isPotionTypeWater(potionMeta)
&& !hasBasePotionEffects(potionMeta)
&& potionMeta.getCustomEffects().isEmpty();
} }
public static boolean isPotionTypeWater(@NotNull PotionMeta potionMeta) { public static boolean isPotionTypeWater(@NotNull PotionMeta potionMeta) {
if (COMPATIBILITY_MODE == PotionCompatibilityType.PRE_1_20_5) { return potionMeta.getBasePotionType() == PotionType.WATER;
return isPotionTypeWaterLegacy(potionMeta); }
} else {
return isPotionTypeWaterModern(potionMeta); public static boolean hasBasePotionEffects(@NotNull PotionMeta potionMeta) {
final PotionType base = potionMeta.getBasePotionType();
if (base == null) {
return false;
} }
}
public static boolean isPotionType(@NotNull PotionMeta potionMeta, String potionType) { final List<PotionEffect> effects = base.getPotionEffects();
if (COMPATIBILITY_MODE == PotionCompatibilityType.PRE_1_20_5) { return effects != null && !effects.isEmpty();
return isPotionTypeLegacy(potionMeta, potionType);
} else {
return isPotionTypeModern(potionMeta, potionType);
}
}
public static boolean isPotionTypeWithoutEffects(@NotNull PotionMeta potionMeta,
String potionType) {
return isPotionType(potionMeta, potionType)
&& !hasBasePotionEffects(potionMeta)
&& potionMeta.getCustomEffects().isEmpty();
}
private static boolean isPotionTypeModern(@NotNull PotionMeta potionMeta, String potionType) {
try {
return getModernPotionTypeKey(potionMeta).getKey().equalsIgnoreCase(potionType);
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
private static boolean isPotionTypeLegacy(@NotNull PotionMeta potionMeta, String potionType) {
try {
Object potionData = methodPotionMetaGetBasePotionData.invoke(potionMeta);
PotionType potionTypeObj = (PotionType) methodPotionDataGetType.invoke(potionData);
return potionTypeObj.name().equalsIgnoreCase(potionType);
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
private static boolean isPotionTypeWaterLegacy(@NotNull PotionMeta potionMeta) {
try {
Object potionData = methodPotionMetaGetBasePotionData.invoke(potionMeta);
PotionType potionType = (PotionType) methodPotionDataGetType.invoke(potionData);
return potionType.name().equalsIgnoreCase(WATER_POTION_TYPE_STR)
|| PotionType.valueOf(WATER_POTION_TYPE_STR) == potionType;
} catch (InvocationTargetException | IllegalAccessException ex) {
throw new RuntimeException(ex);
}
}
private static boolean isPotionTypeWaterModern(@NotNull PotionMeta potionMeta) {
try {
return getModernPotionTypeKey(potionMeta).getKey()
.equalsIgnoreCase(WATER_POTION_TYPE_STR);
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
public static boolean samePotionType(PotionMeta potionMeta, PotionMeta otherPotionMeta) {
if (COMPATIBILITY_MODE == PotionCompatibilityType.PRE_1_20_5) {
return samePotionTypeLegacy(potionMeta, otherPotionMeta);
} else {
return samePotionTypeModern(potionMeta, otherPotionMeta);
}
}
private static boolean samePotionTypeLegacy(PotionMeta potionMeta, PotionMeta otherPotionMeta) {
try {
Object potionData = methodPotionMetaGetBasePotionData.invoke(potionMeta);
Object otherPotionData = methodPotionMetaGetBasePotionData.invoke(otherPotionMeta);
PotionType potionType = (PotionType) methodPotionDataGetType.invoke(potionData);
PotionType otherPotionType = (PotionType) methodPotionDataGetType.invoke(
otherPotionData);
return potionType == otherPotionType;
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
private static boolean samePotionTypeModern(PotionMeta potionMeta, PotionMeta otherPotionMeta) {
try {
PotionType potionType = (PotionType) methodPotionMetaGetBasePotionType.invoke(
potionMeta);
PotionType otherPotionType = (PotionType) methodPotionMetaGetBasePotionType.invoke(
otherPotionMeta);
return potionType == otherPotionType;
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public static boolean samePotionEffects(PotionMeta potionMeta, PotionMeta otherPotionMeta) {
if (COMPATIBILITY_MODE == PotionCompatibilityType.PRE_1_20_5) {
return true;
} else {
return samePotionEffectsModern(potionMeta, otherPotionMeta);
}
}
private static boolean samePotionEffectsModern(PotionMeta potionMeta,
PotionMeta otherPotionMeta) {
return potionMeta.getCustomEffects().equals(otherPotionMeta.getCustomEffects());
}
public static boolean hasBasePotionEffects(PotionMeta potionMeta) {
if (COMPATIBILITY_MODE == PotionCompatibilityType.PRE_1_20_5) {
return hasBasePotionEffectsLegacy(potionMeta);
} else {
return hasBasePotionEffectsModern(potionMeta);
}
}
private static boolean hasBasePotionEffectsLegacy(PotionMeta potionMeta) {
try {
Object potionData = methodPotionMetaGetBasePotionData.invoke(potionMeta);
PotionType potionType = (PotionType) methodPotionDataGetType.invoke(potionData);
return methodPotionTypeGetEffectType.invoke(potionType) != null;
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
private static boolean hasBasePotionEffectsModern(PotionMeta potionMeta) {
try {
PotionType potionType = (PotionType) methodPotionMetaGetBasePotionType.invoke(
potionMeta);
List<PotionEffectType> potionEffectTypeList = (List<PotionEffectType>) methodPotionTypeGetPotionEffects.invoke(
potionType);
return potionEffectTypeList != null && !potionEffectTypeList.isEmpty();
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
} }
/** /**
* Set the base potion type of a potion meta. Note that extended/upgraded are ignored in 1.20.5 * Set the base potion type of a potion meta. Note that extended/upgraded are ignored in 1.20.5
* and later. * and later.
* *
* @param potionMeta the potion meta * <p>In 1.20.5+, "extended/upgraded" are encoded into {@link PotionType} variants. This method
* @param extended true if the potion is extended * attempts to select the appropriate STRONG_/LONG_ variant when requested. If no such variant
* @param upgraded true if the potion is upgraded * exists, it falls back to the provided base type.</p>
*/ */
public static void setBasePotionType(PotionMeta potionMeta, PotionType potionType, public static void setBasePotionType(@NotNull PotionMeta potionMeta,
boolean extended, boolean upgraded) { @NotNull PotionType potionType, boolean extended, boolean upgraded) {
if (methodPotionMetaSetBasePotionType == null) { final PotionType resolved = resolveVariant(potionType, upgraded, extended);
setBasePotionTypeLegacy(potionMeta, potionType, extended, upgraded); potionMeta.setBasePotionType(resolved);
} else {
setBasePotionTypeModern(potionMeta, potionType);
}
} }
public static void setUpgradedAndExtendedProperties(PotionType potionType, private static @NotNull PotionType resolveVariant(@NotNull PotionType base, boolean upgraded,
PotionMeta potionMeta, boolean extended) {
boolean isUpgraded, boolean isExtended) { // Apply the same prefix scheme your code already expects
if (potionDataClass == null || mcMMO.getCompatibilityManager().getMinecraftGameVersion() String name = base.name();
.isAtLeast(1, 20, 5)) {
return; // Avoid double-prefixing if caller already passed LONG_/STRONG_ variants
if (name.startsWith(STRONG + "_")) {
upgraded = false;
}
if (name.startsWith(LONG + "_")) {
extended = false;
}
if (upgraded) {
name = STRONG + "_" + name;
}
if (extended) {
name = LONG + "_" + name;
} }
try { try {
final Object potionData = potionDataClass.getConstructor(PotionType.class, return PotionType.valueOf(name);
boolean.class, boolean.class) } catch (IllegalArgumentException ignored) {
.newInstance(potionType, isExtended, isUpgraded); // Not all potion types have strong/long variants; just use the provided base.
methodPotionMetaSetBasePotionData.invoke(potionMeta, potionData); return base;
} catch (IllegalAccessException | InvocationTargetException | InstantiationException
| NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
private static void setBasePotionTypeLegacy(PotionMeta potionMeta, PotionType potionType,
boolean extended,
boolean upgraded) {
try {
Object potionData = potionDataClass.getConstructor(PotionType.class, boolean.class,
boolean.class)
.newInstance(potionType, extended, upgraded);
methodPotionMetaSetBasePotionData.invoke(potionMeta, potionData);
} catch (IllegalAccessException | InvocationTargetException | InstantiationException |
NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
private static void setBasePotionTypeModern(PotionMeta potionMeta, PotionType potionType) {
try {
methodPotionMetaSetBasePotionType.invoke(potionMeta, potionType);
} catch (IllegalAccessException | InvocationTargetException ex) {
throw new RuntimeException(ex);
}
}
public static boolean isPotionDataEqual(PotionMeta potionMeta, PotionMeta otherPotionMeta) {
if (COMPATIBILITY_MODE == PotionCompatibilityType.MODERN) {
return true; // we don't compare data on newer versions
} else {
try {
final Object potionData = methodPotionMetaGetBasePotionData.invoke(potionMeta);
final Object otherPotionData = methodPotionMetaGetBasePotionData.invoke(
otherPotionMeta);
final PotionType potionType = (PotionType) methodPotionDataGetType.invoke(
potionData);
final PotionType otherPotionType = (PotionType) methodPotionDataGetType.invoke(
otherPotionData);
if (potionType != otherPotionType) {
return false;
}
if (methodPotionDataIsExtended.invoke(potionData)
!= methodPotionDataIsExtended.invoke(otherPotionData)) {
return false;
}
return methodPotionDataIsUpgraded.invoke(potionData)
== methodPotionDataIsUpgraded.invoke(otherPotionData);
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
} }
} }
} }

View File

@@ -8,7 +8,6 @@ import com.gmail.nossr50.commands.McnotifyCommand;
import com.gmail.nossr50.commands.McrefreshCommand; import com.gmail.nossr50.commands.McrefreshCommand;
import com.gmail.nossr50.commands.McscoreboardCommand; import com.gmail.nossr50.commands.McscoreboardCommand;
import com.gmail.nossr50.commands.XprateCommand; import com.gmail.nossr50.commands.XprateCommand;
import com.gmail.nossr50.commands.admin.CompatibilityCommand;
import com.gmail.nossr50.commands.admin.McmmoReloadLocaleCommand; import com.gmail.nossr50.commands.admin.McmmoReloadLocaleCommand;
import com.gmail.nossr50.commands.admin.PlayerDebugCommand; import com.gmail.nossr50.commands.admin.PlayerDebugCommand;
import com.gmail.nossr50.commands.chat.McChatSpy; import com.gmail.nossr50.commands.chat.McChatSpy;
@@ -41,6 +40,7 @@ import com.gmail.nossr50.commands.skills.MmoInfoCommand;
import com.gmail.nossr50.commands.skills.RepairCommand; import com.gmail.nossr50.commands.skills.RepairCommand;
import com.gmail.nossr50.commands.skills.SalvageCommand; import com.gmail.nossr50.commands.skills.SalvageCommand;
import com.gmail.nossr50.commands.skills.SmeltingCommand; import com.gmail.nossr50.commands.skills.SmeltingCommand;
import com.gmail.nossr50.commands.skills.SpearsCommand;
import com.gmail.nossr50.commands.skills.SwordsCommand; import com.gmail.nossr50.commands.skills.SwordsCommand;
import com.gmail.nossr50.commands.skills.TamingCommand; import com.gmail.nossr50.commands.skills.TamingCommand;
import com.gmail.nossr50.commands.skills.TridentsCommand; import com.gmail.nossr50.commands.skills.TridentsCommand;
@@ -63,9 +63,8 @@ public final class CommandRegistrationManager {
private static void registerSkillCommands() { private static void registerSkillCommands() {
for (PrimarySkillType primarySkillType : PrimarySkillType.values()) { for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
if (primarySkillType == PrimarySkillType.MACES if (primarySkillType == PrimarySkillType.SPEARS
&& !mcMMO.getCompatibilityManager().getMinecraftGameVersion() && !mcMMO.getMinecraftGameVersion().isAtLeast(1, 21, 11)) {
.isAtLeast(1, 21, 0)) {
continue; continue;
} }
@@ -101,6 +100,7 @@ public final class CommandRegistrationManager {
case REPAIR -> command.setExecutor(new RepairCommand()); case REPAIR -> command.setExecutor(new RepairCommand());
case SALVAGE -> command.setExecutor(new SalvageCommand()); case SALVAGE -> command.setExecutor(new SalvageCommand());
case SMELTING -> command.setExecutor(new SmeltingCommand()); case SMELTING -> command.setExecutor(new SmeltingCommand());
case SPEARS -> command.setExecutor(new SpearsCommand());
case SWORDS -> command.setExecutor(new SwordsCommand()); case SWORDS -> command.setExecutor(new SwordsCommand());
case TAMING -> command.setExecutor(new TamingCommand()); case TAMING -> command.setExecutor(new TamingCommand());
case TRIDENTS -> command.setExecutor(new TridentsCommand()); case TRIDENTS -> command.setExecutor(new TridentsCommand());
@@ -403,13 +403,6 @@ public final class CommandRegistrationManager {
command.setExecutor(new McmmoReloadLocaleCommand()); command.setExecutor(new McmmoReloadLocaleCommand());
} }
private static void registerCompatibilityCommand() {
PluginCommand command = mcMMO.p.getCommand("mmocompat"); //TODO: Localize
command.setDescription(LocaleLoader.getString("Commands.Description.mmocompat"));
command.setUsage(LocaleLoader.getString("Commands.Usage.0", "mmocompat"));
command.setExecutor(new CompatibilityCommand());
}
private static void registerXPBarCommand() { private static void registerXPBarCommand() {
PluginCommand command = mcMMO.p.getCommand("mmoxpbar"); //TODO: Localize PluginCommand command = mcMMO.p.getCommand("mmoxpbar"); //TODO: Localize
command.setDescription(LocaleLoader.getString("Commands.Description.mmoxpbar")); command.setDescription(LocaleLoader.getString("Commands.Description.mmoxpbar"));
@@ -465,8 +458,5 @@ public final class CommandRegistrationManager {
// Admin commands // Admin commands
registerReloadLocaleCommand(); registerReloadLocaleCommand();
// Misc
registerCompatibilityCommand();
} }
} }

View File

@@ -1,16 +0,0 @@
package com.gmail.nossr50.util.compat;
/**
* Compatibility Layers should be named after the functionality they serve
*/
public interface CompatibilityLayer {
/**
* Whether this CompatibilityLayer successfully initialized and in theory should be functional
*
* @return true if this CompatibilityLayer is functional
*/
default boolean noErrorsOnInitialize() {
return true;
}
}

View File

@@ -1,163 +0,0 @@
package com.gmail.nossr50.util.compat;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.LogUtils;
import com.gmail.nossr50.util.compat.layers.bungee.AbstractBungeeSerializerCompatibilityLayer;
import com.gmail.nossr50.util.compat.layers.bungee.BungeeLegacySerializerCompatibilityLayer;
import com.gmail.nossr50.util.compat.layers.bungee.BungeeModernSerializerCompatibilityLayer;
import com.gmail.nossr50.util.compat.layers.skills.AbstractMasterAnglerCompatibility;
import com.gmail.nossr50.util.compat.layers.skills.MasterAnglerCompatibilityLayer;
import com.gmail.nossr50.util.nms.NMSVersion;
import com.gmail.nossr50.util.platform.MinecraftGameVersion;
import com.gmail.nossr50.util.text.StringUtils;
import java.util.HashMap;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* These classes are a band-aid solution for adding NMS support into 2.1.XXX In 2.2 we are switching
* to modules and that will clean things up significantly
*/
public class CompatibilityManager {
private @NotNull HashMap<CompatibilityType, Boolean> supportedLayers;
private boolean isFullyCompatibleServerSoftware = true; //true if all compatibility layers load successfully
private final @NotNull MinecraftGameVersion minecraftGameVersion;
private final @NotNull NMSVersion nmsVersion;
/* Compatibility Layers */
private AbstractBungeeSerializerCompatibilityLayer bungeeSerializerCompatibilityLayer;
private AbstractMasterAnglerCompatibility masterAnglerCompatibility;
public CompatibilityManager(@NotNull MinecraftGameVersion minecraftGameVersion) {
LogUtils.debug(mcMMO.p.getLogger(), "Loading compatibility layers...");
this.minecraftGameVersion = minecraftGameVersion;
this.nmsVersion = determineNMSVersion();
init();
LogUtils.debug(mcMMO.p.getLogger(), "Finished loading compatibility layers.");
}
private void init() {
initSupportedLayersMap();
initCompatibilityLayers();
}
private void initSupportedLayersMap() {
supportedLayers = new HashMap<>(); //Init map
for (CompatibilityType compatibilityType : CompatibilityType.values()) {
supportedLayers.put(compatibilityType,
false); //All layers are set to false when initialized
}
}
/**
* Initialize all necessary compatibility layers For any unsupported layers, load a dummy layer
*/
private void initCompatibilityLayers() {
initBungeeSerializerLayer();
initMasterAnglerLayer();
isFullyCompatibleServerSoftware = true;
}
private void initMasterAnglerLayer() {
if (minecraftGameVersion.isAtLeast(1, 16, 3)) {
masterAnglerCompatibility = new MasterAnglerCompatibilityLayer();
} else {
masterAnglerCompatibility = null;
}
}
private void initBungeeSerializerLayer() {
if (minecraftGameVersion.isAtLeast(1, 16, 0)) {
bungeeSerializerCompatibilityLayer = new BungeeModernSerializerCompatibilityLayer();
} else {
bungeeSerializerCompatibilityLayer = new BungeeLegacySerializerCompatibilityLayer();
}
supportedLayers.put(CompatibilityType.BUNGEE_SERIALIZER, true);
}
//TODO: move to text manager
public void reportCompatibilityStatus(@NotNull CommandSender commandSender) {
if (isFullyCompatibleServerSoftware) {
commandSender.sendMessage(LocaleLoader.getString("mcMMO.Template.Prefix",
"mcMMO is fully compatible with the currently running server software."));
} else {
//TODO: Better messages for each incompatible layer
for (CompatibilityType compatibilityType : CompatibilityType.values()) {
if (!supportedLayers.get(compatibilityType)) {
commandSender.sendMessage(LocaleLoader.getString("mcMMO.Template.Prefix",
LocaleLoader.getString("Compatibility.Layer.Unsupported",
StringUtils.getCapitalized(compatibilityType.toString()))));
}
}
}
commandSender.sendMessage(LocaleLoader.getString("mcMMO.Template.Prefix",
"NMS Status - " + nmsVersion));
}
public boolean isCompatibilityLayerOperational(@NotNull CompatibilityType compatibilityType) {
return supportedLayers.get(compatibilityType);
}
public boolean isFullyCompatibleServerSoftware() {
return isFullyCompatibleServerSoftware;
}
public @NotNull NMSVersion getNmsVersion() {
return nmsVersion;
}
private @NotNull NMSVersion determineNMSVersion() {
//This bit here helps prevent mcMMO breaking if it isn't updated but the game continues to update
if (minecraftGameVersion.isAtLeast(1, 17, 0)) {
return NMSVersion.NMS_1_17;
}
//Messy but it works
if (minecraftGameVersion.getMajorVersion().asInt() == 1) {
switch (minecraftGameVersion.getMinorVersion().asInt()) {
case 12:
return NMSVersion.NMS_1_12_2;
case 13:
return NMSVersion.NMS_1_13_2;
case 14:
return NMSVersion.NMS_1_14_4;
case 15:
return NMSVersion.NMS_1_15_2;
case 16:
if (minecraftGameVersion.getPatchVersion().asInt() == 1) {
return NMSVersion.NMS_1_16_1;
} else if (minecraftGameVersion.getPatchVersion().asInt() == 2) {
return NMSVersion.NMS_1_16_2;
} else if (minecraftGameVersion.getPatchVersion().asInt() == 3) {
return NMSVersion.NMS_1_16_3;
} else if (minecraftGameVersion.getPatchVersion().asInt() == 4) {
return NMSVersion.NMS_1_16_4;
} else if (minecraftGameVersion.getPatchVersion().asInt() >= 5) {
return NMSVersion.NMS_1_16_5;
}
case 17:
return NMSVersion.NMS_1_17;
}
}
return NMSVersion.UNSUPPORTED;
}
public AbstractBungeeSerializerCompatibilityLayer getBungeeSerializerCompatibilityLayer() {
return bungeeSerializerCompatibilityLayer;
}
public @Nullable AbstractMasterAnglerCompatibility getMasterAnglerCompatibilityLayer() {
return masterAnglerCompatibility;
}
public @NotNull MinecraftGameVersion getMinecraftGameVersion() {
return minecraftGameVersion;
}
}

View File

@@ -1,7 +0,0 @@
package com.gmail.nossr50.util.compat;
public enum CompatibilityType {
PERSISTENT_DATA,
BUNGEE_SERIALIZER,
MASTER_ANGLER,
}

View File

@@ -1,24 +0,0 @@
package com.gmail.nossr50.util.compat.layers;
import com.gmail.nossr50.util.compat.CompatibilityLayer;
/**
* These classes are a band-aid solution for adding NMS support into 2.1.XXX In 2.2 we are switching
* to modules and that will clean things up significantly
*/
public abstract class AbstractCompatibilityLayer implements CompatibilityLayer {
protected boolean noErrorsOnInitialize = true;
/**
* Initialize the CompatibilityLayer
*
* @return true if the CompatibilityLayer initialized and should be functional
*/
public abstract boolean initializeLayer();
@Override
public boolean noErrorsOnInitialize() {
return noErrorsOnInitialize;
}
}

View File

@@ -1,29 +0,0 @@
package com.gmail.nossr50.util.compat.layers;
import com.gmail.nossr50.util.nms.NMSVersion;
import org.jetbrains.annotations.NotNull;
/**
* These classes are a band-aid solution for adding NMS support into 2.1.XXX In 2.2 we are switching
* to modules and that will clean things up significantly
*/
public abstract class AbstractNMSCompatibilityLayer extends AbstractCompatibilityLayer {
protected final @NotNull NMSVersion nmsVersion;
public AbstractNMSCompatibilityLayer(@NotNull NMSVersion nmsVersion) {
this.nmsVersion = nmsVersion;
}
/**
* Initialize the CompatibilityLayer
*
* @return true if the CompatibilityLayer initialized and should be functional
*/
public abstract boolean initializeLayer();
@Override
public boolean noErrorsOnInitialize() {
return noErrorsOnInitialize;
}
}

View File

@@ -1,42 +0,0 @@
//package com.gmail.nossr50.util.compat.layers.attackcooldown;
//
//import com.gmail.nossr50.util.nms.NMSVersion;
//import org.bukkit.entity.Player;
//import org.jetbrains.annotations.NotNull;
//
//import java.lang.reflect.InvocationTargetException;
//
//public class DummyPlayerAttackCooldownToolLayer extends PlayerAttackCooldownToolLayer {
// public DummyPlayerAttackCooldownToolLayer() {
// super(NMSVersion.UNSUPPORTED);
// }
//
// @Override
// public boolean initializeLayer() {
// return noErrorsOnInitialize;
// }
//
// @Override
// public float getAttackStrength(@NotNull Player player) throws InvocationTargetException, IllegalAccessException {
// return 1.0F; //Always full strength
// }
//
// @Override
// public float getCooldownValue(@NotNull Player player) throws InvocationTargetException, IllegalAccessException {
// return 0F;
// }
//
// @Override
// public void resetAttackStrength(@NotNull Player player) throws InvocationTargetException, IllegalAccessException {
// //Do nothing
// }
//
// @Override
// public int getCooldownFieldValue(@NotNull Player player) throws InvocationTargetException, IllegalAccessException {
// return 0;
// }
//
// @Override
// public void setCooldownFieldValue(@NotNull Player player, int fieldValue) throws InvocationTargetException, IllegalAccessException {
// }
//}

View File

@@ -1,30 +0,0 @@
package com.gmail.nossr50.util.compat.layers.attackcooldown;
import java.lang.reflect.InvocationTargetException;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public interface PlayerAttackCooldownMethods {
/**
* Grabs the attack strength for a player Should be noted that as of today there is no way to
* capture a players current attack strength in spigot when they attack an entity outside of
* network packet listening
*
* @param player target player
* @return the float value of the player's attack strength
*/
float getAttackStrength(@NotNull Player player)
throws InvocationTargetException, IllegalAccessException;
float getCooldownValue(@NotNull Player player)
throws InvocationTargetException, IllegalAccessException;
void resetAttackStrength(@NotNull Player player)
throws InvocationTargetException, IllegalAccessException;
int getCooldownFieldValue(@NotNull Player player)
throws InvocationTargetException, IllegalAccessException;
void setCooldownFieldValue(@NotNull Player player, int fieldValue)
throws InvocationTargetException, IllegalAccessException;
}

View File

@@ -1,238 +0,0 @@
//package com.gmail.nossr50.util.compat.layers.attackcooldown;
//
//import com.gmail.nossr50.mcMMO;
//import com.gmail.nossr50.util.compat.layers.AbstractNMSCompatibilityLayer;
//import com.gmail.nossr50.util.nms.NMSConstants;
//import com.gmail.nossr50.util.nms.NMSVersion;
//import org.bukkit.entity.Player;
//import org.jetbrains.annotations.NotNull;
//import org.jetbrains.annotations.Nullable;
//
//import java.lang.reflect.Field;
//import java.lang.reflect.InvocationTargetException;
//import java.lang.reflect.Method;
//
/// **
// *
// * These classes are a band-aid solution for adding NMS support into 2.1.XXX
// * In 2.2 we are switching to modules and that will clean things up significantly
// *
// */
//public class PlayerAttackCooldownToolLayer extends AbstractNMSCompatibilityLayer implements PlayerAttackCooldownMethods {
//
// private final String cbNMSVersionPath;
//
// protected Class<?> craftPlayerClass;
// protected Class<?> entityHumanClass;
// protected Class<?> entityLivingClass;
//
// protected Method playerAttackCooldownMethod;
// protected Method playerAttackStrengthMethod;
// protected Method resetPlayerAttackCooldownMethod;
// protected Method setPlayerAttackStrengthMethod;
// protected Method getHandleMethod;
// protected Field attackCooldownField;
// protected String attackStrengthFieldName;
//
// public PlayerAttackCooldownToolLayer(@NotNull NMSVersion nmsVersion) {
// super(nmsVersion);
// mcMMO.p.getLogger().info("Loading Compatibility Layer... (Player Attack Cooldown Exploit Prevention)");
// if (!isCompatibleWithMinecraftVersion(nmsVersion)) {
// mcMMO.p.getLogger().severe("this version of mcMMO does not support NMS for this version of Minecraft, try updating mcMMO or updating Minecraft. Not all versions of Minecraft will have NMS support built into mcMMO.");
// cbNMSVersionPath = "";
// } else {
// if (NMSConstants.getCraftBukkitVersionPath(nmsVersion) != null) {
// cbNMSVersionPath = NMSConstants.getCraftBukkitVersionPath(nmsVersion);
// noErrorsOnInitialize = initializeLayer();
//
// if (noErrorsOnInitialize) {
// mcMMO.p.getLogger().info("Successfully Loaded Compatibility Layer! (Player Attack Cooldown Exploit Prevention)");
// }
// } else {
// mcMMO.p.getLogger().info("Failed to load - CL (Player Attack Cooldown Exploit Prevention) Could not find CB NMS path for CL");
// flagErrorsDuringStartup();
// mcMMO.p.getLogger().warning("Could not wire NMS package path for CraftBukkit!");
// cbNMSVersionPath = "";
// }
// }
// }
//
// public static boolean isCompatibleWithMinecraftVersion(@NotNull NMSVersion nmsVersion) {
// switch(nmsVersion) {
// case NMS_1_13_2:
// case NMS_1_14_4:
// case NMS_1_15_2:
// case NMS_1_16_4:
// case NMS_1_16_5:
// return true;
// default:
// return false;
// }
// }
//
// /**
// * Cache all reflection methods/types/classes needed for the NMS of this CompatibilityLayer
// * @param cooldownMethodName the cooldown method name
// * @param attackStrengthMethodName the attack strength method name
// * @param resetAttackCooldownMethodName the reset attack cooldown method name
// * @param getHandleMethodName the get handle method name
// * @return true if NMS was successfully wired
// */
// public boolean wireNMS(@NotNull String cooldownMethodName, @NotNull String attackStrengthMethodName, @NotNull String resetAttackCooldownMethodName, @NotNull String getHandleMethodName, @NotNull String attackStrengthFieldName) {
// entityHumanClass = initEntityHumanClass();
//
// if (entityHumanClass != null) {
// entityLivingClass = entityHumanClass.getSuperclass();
// }
//
// craftPlayerClass = initCraftPlayerClass();
// this.attackStrengthFieldName = attackStrengthFieldName;
//
// try {
// this.attackCooldownField = entityLivingClass.getDeclaredField(attackStrengthFieldName);
// this.attackCooldownField.setAccessible(true);
// } catch (NoSuchFieldException e) {
// e.printStackTrace();
// }
//
// try {
// this.playerAttackCooldownMethod = entityHumanClass.getMethod(cooldownMethodName);
// this.playerAttackStrengthMethod = entityHumanClass.getMethod(attackStrengthMethodName, float.class);
// this.resetPlayerAttackCooldownMethod = entityHumanClass.getMethod(resetAttackCooldownMethodName);
//
// if (craftPlayerClass != null) {
// this.getHandleMethod = craftPlayerClass.getMethod(getHandleMethodName);
// } else {
// return false;
// }
// return true;
// } catch (NoSuchMethodException e) {
// flagErrorsDuringStartup();
// e.printStackTrace();
// return false;
// }
// }
//
// /**
// * Get the cached player attack cooldown method
// * @return the cached player attack cooldown method
// */
// private @Nullable Method getPlayerAttackCooldownMethod() {
// return playerAttackCooldownMethod;
// }
//
// /**
// * Get the cached player attack strength method
// * @return the cached player attack strength method
// */
// private @Nullable Method getPlayerAttackStrengthMethod() {
// return playerAttackStrengthMethod;
// }
//
// /**
// * Get the cached player attack cooldown reset method
// * @return the cached player attack cooldown reset method
// */
// private @Nullable Method getResetPlayerAttackCooldownMethod() {
// return resetPlayerAttackCooldownMethod;
// }
//
// /**
// * Grab the CraftPlayer class type from NMS
// * @return the CraftPlayer class type from NMS
// */
// private @Nullable Class<?> initCraftPlayerClass() {
// try {
// return Class.forName(NMSConstants.getCraftPlayerClassPath(cbNMSVersionPath));
// } catch (ClassNotFoundException e) {
// flagErrorsDuringStartup();
// e.printStackTrace();
// return null;
// }
// }
//
// /**
// * Grab the EntityHuman class type from NMS
// * @return the EntityHuman class type from NMS
// */
// private @Nullable Class<?> initEntityHumanClass() {
// try {
// return Class.forName(NMSConstants.getEntityHumanClassPath(cbNMSVersionPath));
// } catch (ClassNotFoundException e) {
// flagErrorsDuringStartup();
// e.printStackTrace();
// return null;
// }
// }
//
// private void flagErrorsDuringStartup() {
// noErrorsOnInitialize = false;
// }
//
// /**
// * Grabs the attack strength for a player
// * Should be noted that as of today there is no way to capture a players current attack strength in spigot when they attack an entity outside of network packet listening
// * @param player target player
// * @return the float value of the player's attack strength
// */
// @Override
// public float getAttackStrength(@NotNull Player player) throws InvocationTargetException, IllegalAccessException {
// Object craftPlayer = craftPlayerClass.cast(player);
// Object entityHuman = entityHumanClass.cast(getHandleMethod.invoke(craftPlayer));
//
// return (float) playerAttackStrengthMethod.invoke(entityHuman, 0F); //Add no adjustment ticks
// }
//
// @Override
// public float getCooldownValue(@NotNull Player player) throws InvocationTargetException, IllegalAccessException {
// Object craftPlayer = craftPlayerClass.cast(player);
// Object entityHuman = entityHumanClass.cast(getHandleMethod.invoke(craftPlayer));
//
// return (float) playerAttackCooldownMethod.invoke(entityHuman); //Add no adjustment ticks
// }
//
// @Override
// public void resetAttackStrength(@NotNull Player player) throws InvocationTargetException, IllegalAccessException {
// Object craftPlayer = craftPlayerClass.cast(player);
// Object entityHuman = entityHumanClass.cast(getHandleMethod.invoke(craftPlayer));
// Object entityLiving = entityLivingClass.cast(entityHuman);
//
// resetPlayerAttackCooldownMethod.invoke(entityLiving);
// }
//
// @Override
// public int getCooldownFieldValue(@NotNull Player player) throws InvocationTargetException, IllegalAccessException {
// Object craftPlayer = craftPlayerClass.cast(player);
// Object entityHuman = entityHumanClass.cast(getHandleMethod.invoke(craftPlayer));
// Object entityLiving = entityLivingClass.cast(entityHuman);
//
// return attackCooldownField.getInt(entityLiving);
// }
//
// @Override
// public void setCooldownFieldValue(@NotNull Player player, int fieldValue) throws InvocationTargetException, IllegalAccessException {
// Object craftPlayer = craftPlayerClass.cast(player);
// Object entityHuman = entityHumanClass.cast(getHandleMethod.invoke(craftPlayer));
//
// attackCooldownField.setInt(entityHuman, fieldValue);
// }
//
// @Override
// public boolean initializeLayer() {
// switch(nmsVersion) {
// case NMS_1_12_2:
// return wireNMS("dr", "n", "ds", "getHandle", "at");
// case NMS_1_13_2:
// return wireNMS("dG", "r", "dH", "getHandle", "at");
// case NMS_1_14_4:
// return wireNMS("dY", "s", "dZ", "getHandle", "at");
// case NMS_1_15_2:
// return wireNMS("ex", "s", "ey", "getHandle", "at");
// case NMS_1_16_4:
// return wireNMS("eR", "getAttackCooldown", "resetAttackCooldown", "getHandle", "at");
// default:
// throw new RuntimeException("Unexpected NMS version support in PlayerAttackCooldown compatibility layer initialization!");
// }
// }
//}
//

View File

@@ -1,11 +0,0 @@
package com.gmail.nossr50.util.compat.layers.bungee;
import net.kyori.adventure.text.Component;
import net.md_5.bungee.api.chat.BaseComponent;
import org.checkerframework.checker.nullness.qual.NonNull;
public abstract class AbstractBungeeSerializerCompatibilityLayer {
public abstract @NonNull Component deserialize(final @NonNull BaseComponent @NonNull [] input);
}

View File

@@ -1,14 +0,0 @@
package com.gmail.nossr50.util.compat.layers.bungee;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
import org.checkerframework.checker.nullness.qual.NonNull;
public class BungeeLegacySerializerCompatibilityLayer extends
AbstractBungeeSerializerCompatibilityLayer {
@Override
public @NonNull Component deserialize(@NonNull BaseComponent @NonNull [] input) {
return BungeeComponentSerializer.legacy().deserialize(input);
}
}

View File

@@ -1,14 +0,0 @@
package com.gmail.nossr50.util.compat.layers.bungee;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
import org.checkerframework.checker.nullness.qual.NonNull;
public class BungeeModernSerializerCompatibilityLayer extends
AbstractBungeeSerializerCompatibilityLayer {
@Override
public @NonNull Component deserialize(@NonNull BaseComponent @NonNull [] input) {
return BungeeComponentSerializer.get().deserialize(input);
}
}

View File

@@ -1,6 +0,0 @@
package com.gmail.nossr50.util.compat.layers.skills;
import com.gmail.nossr50.util.compat.layers.AbstractCompatibilityLayer;
public abstract class AbstractMasterAnglerCompatibility extends AbstractCompatibilityLayer {
}

View File

@@ -1,79 +0,0 @@
package com.gmail.nossr50.util.compat.layers.skills;
import org.bukkit.entity.FishHook;
import org.jetbrains.annotations.NotNull;
public class MasterAnglerCompatibilityLayer extends AbstractMasterAnglerCompatibility {
@Override
public boolean initializeLayer() {
return true;
}
/**
* Get the minimum number of ticks one has to wait for a fish biting.
* <p>
* The default is 100 ticks (5 seconds).<br> Note that this is before applying lure.
*
* @return Minimum number of ticks one has to wait for a fish biting
*/
public int getMinWaitTime(@NotNull FishHook fishHook) {
return fishHook.getMinWaitTime();
}
/**
* Set the minimum number of ticks one has to wait for a fish biting.
* <p>
* The default is 100 ticks (5 seconds).<br> Note that this is before applying lure.
*
* @param minWaitTime Minimum number of ticks one has to wait for a fish biting
*/
public void setMinWaitTime(@NotNull FishHook fishHook, int minWaitTime) {
fishHook.setMinWaitTime(minWaitTime);
}
/**
* Get the maximum number of ticks one has to wait for a fish biting.
* <p>
* The default is 600 ticks (30 seconds).<br> Note that this is before applying lure.
*
* @return Maximum number of ticks one has to wait for a fish biting
*/
public int getMaxWaitTime(@NotNull FishHook fishHook) {
return fishHook.getMaxWaitTime();
}
/**
* Set the maximum number of ticks one has to wait for a fish biting.
* <p>
* The default is 600 ticks (30 seconds).<br> Note that this is before applying lure.
*
* @param maxWaitTime Maximum number of ticks one has to wait for a fish biting
*/
public void setMaxWaitTime(@NotNull FishHook fishHook, int maxWaitTime) {
fishHook.setMaxWaitTime(maxWaitTime);
}
/**
* Get whether the lure enchantment should be applied to reduce the wait time.
* <p>
* The default is true.<br> Lure reduces the wait time by 100 ticks (5 seconds) for each level
* of the enchantment.
*
* @return Whether the lure enchantment should be applied to reduce the wait time
*/
public boolean getApplyLure(@NotNull FishHook fishHook) {
return fishHook.getApplyLure();
}
/**
* Set whether the lure enchantment should be applied to reduce the wait time.
* <p>
* The default is true.<br> Lure reduces the wait time by 100 ticks (5 seconds) for each level
* of the enchantment.
*
* @param applyLure Whether the lure enchantment should be applied to reduce the wait time
*/
public void setApplyLure(@NotNull FishHook fishHook, boolean applyLure) {
fishHook.setApplyLure(applyLure);
}
}

View File

@@ -1,22 +0,0 @@
package com.gmail.nossr50.util.platform;
import com.gmail.nossr50.util.compat.CompatibilityManager;
/**
* These classes are a band-aid solution for adding NMS support into 2.1.XXX In 2.2 we are switching
* to modules and that will clean things up significantly
*/
public abstract class AbstractPlatform implements Platform {
protected final CompatibilityManager compatibilityManager;
protected final MinecraftGameVersion minecraftGameVersion;
protected final ServerSoftwareType serverSoftwareType;
public AbstractPlatform(MinecraftGameVersion minecraftGameVersion,
ServerSoftwareType serverSoftwareType, CompatibilityManager compatibilityManager) {
this.minecraftGameVersion = minecraftGameVersion;
this.serverSoftwareType = serverSoftwareType;
this.compatibilityManager = compatibilityManager;
}
}

View File

@@ -1,31 +0,0 @@
package com.gmail.nossr50.util.platform;
import com.gmail.nossr50.util.compat.CompatibilityManager;
import org.jetbrains.annotations.NotNull;
/**
* These classes are a band-aid solution for adding NMS support into 2.1.XXX In 2.2 we are switching
* to modules and that will clean things up significantly
*/
public class BukkitPlatform extends AbstractPlatform {
public BukkitPlatform(MinecraftGameVersion minecraftGameVersion) {
super(minecraftGameVersion, ServerSoftwareType.CRAFT_BUKKIT,
new CompatibilityManager(minecraftGameVersion));
}
@Override
public @NotNull ServerSoftwareType getServerSoftwareType() {
return super.serverSoftwareType;
}
@Override
public @NotNull CompatibilityManager getCompatibilityManager() {
return compatibilityManager;
}
@Override
public @NotNull MinecraftGameVersion getGameVersion() {
return super.minecraftGameVersion;
}
}

View File

@@ -1,33 +0,0 @@
package com.gmail.nossr50.util.platform;
import com.gmail.nossr50.util.compat.CompatibilityManager;
import org.jetbrains.annotations.NotNull;
/**
* These classes are a band-aid solution for adding NMS support into 2.1.XXX In 2.2 we are switching
* to modules and that will clean things up significantly
*/
public interface Platform {
/**
* Target {@link ServerSoftwareType} for this {@link Platform}
*
* @return the {@link ServerSoftwareType} for this {@link Platform}
*/
@NotNull ServerSoftwareType getServerSoftwareType();
/**
* Get the {@link CompatibilityManager} for this {@link Platform}
*
* @return the {@link CompatibilityManager} for this platform
*/
@NotNull CompatibilityManager getCompatibilityManager();
/**
* The target game version of this {@link Platform}
*
* @return the target {@link MinecraftGameVersion} of this {@link Platform}
*/
@NotNull MinecraftGameVersion getGameVersion();
}

View File

@@ -1,39 +0,0 @@
package com.gmail.nossr50.util.platform;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* These classes are a band-aid solution for adding NMS support into 2.1.XXX In 2.2 we are switching
* to modules and that will clean things up significantly
*/
public class PlatformBuilder {
private MinecraftGameVersion minecraftGameVersion;
private ServerSoftwareType serverSoftwareType;
public PlatformBuilder() {
}
public PlatformBuilder setMinecraftGameVersion(
@NotNull MinecraftGameVersion minecraftGameVersion) {
this.minecraftGameVersion = minecraftGameVersion;
return this;
}
public PlatformBuilder setSoftwareType(@NotNull ServerSoftwareType softwareType) {
this.serverSoftwareType = softwareType;
return this;
}
public @Nullable Platform build() {
return switch (serverSoftwareType) {
case PAPER, SPIGOT, CRAFT_BUKKIT -> createBukkitPlatform();
default -> null;
};
}
private BukkitPlatform createBukkitPlatform() {
return new BukkitPlatform(minecraftGameVersion);
}
}

View File

@@ -1,97 +0,0 @@
package com.gmail.nossr50.util.platform;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.LogUtils;
import com.gmail.nossr50.util.compat.CompatibilityManager;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* These classes are a band-aid solution for adding NMS support into 2.1.XXX In 2.2 we are switching
* to modules and that will clean things up significantly
*/
public class PlatformManager {
protected Platform platform; //current platform
public PlatformManager() {
init();
}
private void init() {
platform = loadPlatform();
}
public Platform getPlatform() {
return platform;
}
private @Nullable Platform loadPlatform() {
ServerSoftwareType serverSoftwareType = determinePlatformType();
PlatformBuilder platformBuilder = new PlatformBuilder();
MinecraftGameVersion gameVersion = determineGameVersion(Bukkit.getBukkitVersion());
return platformBuilder
.setMinecraftGameVersion(gameVersion)
.setSoftwareType(serverSoftwareType)
.build();
}
//TODO: make this work on things other than bukkit
@Deprecated //Only good for determining bukkit game versions
private @NotNull MinecraftGameVersion determineGameVersion(String platformVersionString) {
int major = 0, minor = 0, patch = 0;
LogUtils.debug(mcMMO.p.getLogger(), "Platform String: " + platformVersionString);
// Gets two numbers separated by . and optional third number after next dot. Must end with - or _
Matcher versionMatch = Pattern.compile("(\\d+)\\.(\\d+)(?:\\.(\\d+))?[-_].*")
.matcher(platformVersionString);
if (versionMatch.find()) {
major = Integer.parseInt(versionMatch.group(1));
minor = Integer.parseInt(versionMatch.group(2));
if (versionMatch.group(3) != null) {
patch = Integer.parseInt(versionMatch.group(3));
}
}
LogUtils.debug(mcMMO.p.getLogger(), "Minecraft version determined to be - "
+ major + "."
+ minor + "."
+ patch);
return new MinecraftGameVersion(major, minor, patch);
}
//TODO: Rewrite this properly once we actually support a not-bukkit platform
private @NotNull ServerSoftwareType determinePlatformType() {
if (Bukkit.getVersion().toLowerCase(Locale.ENGLISH).contains("paper")) {
return ServerSoftwareType.PAPER;
} else if (Bukkit.getVersion().toLowerCase(Locale.ENGLISH).contains("spigot")) {
return ServerSoftwareType.SPIGOT;
} else {
return ServerSoftwareType.CRAFT_BUKKIT;
}
}
public ServerSoftwareType getServerSoftware() {
return platform.getServerSoftwareType();
}
public String getServerSoftwareStr() {
return switch (getServerSoftware()) {
case PAPER -> "Paper";
case SPIGOT -> "Spigot";
default -> "CraftBukkit";
};
}
public @Nullable CompatibilityManager getCompatibilityManager() {
return platform.getCompatibilityManager();
}
}

View File

@@ -3,6 +3,7 @@ package com.gmail.nossr50.util.skills;
import static com.gmail.nossr50.datatypes.experience.XPGainReason.PVP; import static com.gmail.nossr50.datatypes.experience.XPGainReason.PVP;
import static com.gmail.nossr50.util.AttributeMapper.MAPPED_MOVEMENT_SPEED; import static com.gmail.nossr50.util.AttributeMapper.MAPPED_MOVEMENT_SPEED;
import static com.gmail.nossr50.util.MobMetadataUtils.hasMobFlag; import static com.gmail.nossr50.util.MobMetadataUtils.hasMobFlag;
import static com.gmail.nossr50.util.Permissions.canUseSubSkill;
import static com.gmail.nossr50.util.skills.ProjectileUtils.isCrossbowProjectile; import static com.gmail.nossr50.util.skills.ProjectileUtils.isCrossbowProjectile;
import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.config.experience.ExperienceConfig;
@@ -19,6 +20,7 @@ import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager;
import com.gmail.nossr50.skills.archery.ArcheryManager; import com.gmail.nossr50.skills.archery.ArcheryManager;
import com.gmail.nossr50.skills.axes.AxesManager; import com.gmail.nossr50.skills.axes.AxesManager;
import com.gmail.nossr50.skills.maces.MacesManager; import com.gmail.nossr50.skills.maces.MacesManager;
import com.gmail.nossr50.skills.spears.SpearsManager;
import com.gmail.nossr50.skills.swords.SwordsManager; import com.gmail.nossr50.skills.swords.SwordsManager;
import com.gmail.nossr50.skills.taming.TamingManager; import com.gmail.nossr50.skills.taming.TamingManager;
import com.gmail.nossr50.skills.tridents.TridentsManager; import com.gmail.nossr50.skills.tridents.TridentsManager;
@@ -331,6 +333,46 @@ public final class CombatUtils {
printFinalDamageDebug(player, event, mmoPlayer); printFinalDamageDebug(player, event, mmoPlayer);
} }
private static void processSpearsCombat(@NotNull LivingEntity target,
@NotNull Player player,
@NotNull EntityDamageByEntityEvent event) {
if (event.getCause() == DamageCause.THORNS) {
return;
}
double boostedDamage = event.getDamage();
final McMMOPlayer mmoPlayer = UserManager.getPlayer(player);
//Make sure the profiles been loaded
if (mmoPlayer == null) {
return;
}
final SpearsManager spearsManager = mmoPlayer.getSpearsManager();
if (canUseSubSkill(player, SubSkillType.SPEARS_SPEAR_MASTERY)) {
boostedDamage += spearsManager.getSpearMasteryBonusDamage()
* mmoPlayer.getAttackStrength();
}
// Apply Limit Break DMG
if (canUseLimitBreak(player, target, SubSkillType.SPEARS_SPEARS_LIMIT_BREAK)) {
boostedDamage += (getLimitBreakDamage(
player, target, SubSkillType.SPEARS_SPEARS_LIMIT_BREAK)
* mmoPlayer.getAttackStrength());
}
event.setDamage(boostedDamage);
// Apply any non-damage effects here
spearsManager.potentiallyApplyMomentum();
processCombatXP(mmoPlayer, target, PrimarySkillType.SPEARS);
printFinalDamageDebug(player, event, mmoPlayer);
}
private static void processAxeCombat(@NotNull LivingEntity target, @NotNull Player player, private static void processAxeCombat(@NotNull LivingEntity target, @NotNull Player player,
@NotNull EntityDamageByEntityEvent event) { @NotNull EntityDamageByEntityEvent event) {
if (event.getCause() == DamageCause.THORNS) { if (event.getCause() == DamageCause.THORNS) {
@@ -530,8 +572,12 @@ public final class CombatUtils {
public static void processCombatAttack(@NotNull EntityDamageByEntityEvent event, public static void processCombatAttack(@NotNull EntityDamageByEntityEvent event,
@NotNull Entity painSourceRoot, @NotNull Entity painSourceRoot,
@NotNull LivingEntity target) { @NotNull LivingEntity target) {
Entity painSource = event.getDamager(); final Entity painSource = event.getDamager();
EntityType entityType = painSource.getType(); final EntityType entityType = painSource.getType();
final String damageType = event.getDamageSource().getDamageType().getKey().getKey();
boolean isDamageTypeSpear =
damageType.equalsIgnoreCase("SPEAR");
if (target instanceof ArmorStand) { if (target instanceof ArmorStand) {
return; return;
@@ -574,7 +620,7 @@ public final class CombatUtils {
return; return;
} }
ItemStack heldItem = player.getInventory().getItemInMainHand(); final ItemStack heldItem = player.getInventory().getItemInMainHand();
if (target instanceof Tameable) { if (target instanceof Tameable) {
if (heldItem.getType() == Material.BONE) { if (heldItem.getType() == Material.BONE) {
@@ -592,7 +638,16 @@ public final class CombatUtils {
} }
} }
if (ItemUtils.isSword(heldItem)) { if (isDamageTypeSpear) {
if (!mcMMO.p.getSkillTools()
.canCombatSkillsTrigger(PrimarySkillType.SPEARS, target)) {
return;
}
if (mcMMO.p.getSkillTools()
.doesPlayerHaveSkillPermission(player, PrimarySkillType.SPEARS)) {
processSpearsCombat(target, player, event);
}
} if (ItemUtils.isSword(heldItem)) {
if (!mcMMO.p.getSkillTools() if (!mcMMO.p.getSkillTools()
.canCombatSkillsTrigger(PrimarySkillType.SWORDS, target)) { .canCombatSkillsTrigger(PrimarySkillType.SWORDS, target)) {
return; return;
@@ -872,8 +927,6 @@ public final class CombatUtils {
continue; continue;
} }
//EventUtils.callFakeArmSwingEvent(attacker);
switch (type) { switch (type) {
case SWORDS: case SWORDS:
if (entity instanceof Player) { if (entity instanceof Player) {

View File

@@ -1,6 +1,5 @@
package com.gmail.nossr50.util.skills; package com.gmail.nossr50.util.skills;
import com.gmail.nossr50.api.exceptions.InvalidSkillException;
import com.gmail.nossr50.config.experience.ExperienceConfig; import com.gmail.nossr50.config.experience.ExperienceConfig;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType; import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType; import com.gmail.nossr50.datatypes.skills.SubSkillType;
@@ -18,6 +17,7 @@ import java.util.Collections;
import java.util.EnumMap; import java.util.EnumMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -27,15 +27,16 @@ import org.jetbrains.annotations.VisibleForTesting;
public class SkillTools { public class SkillTools {
private final mcMMO pluginRef; private final mcMMO pluginRef;
// TODO: Java has immutable types now, switch to those // TODO: Java has immutable types now, switch to those
// TODO: Figure out which ones we don't need, this was copy pasted from a diff branch // TODO: Figure out which ones we don't need, this was copy pasted from a diff branch
public final @NotNull ImmutableList<String> LOCALIZED_SKILL_NAMES; public final @NotNull ImmutableList<String> LOCALIZED_SKILL_NAMES;
public final @NotNull ImmutableList<String> FORMATTED_SUBSKILL_NAMES; public final @NotNull ImmutableList<String> FORMATTED_SUBSKILL_NAMES;
public final @NotNull ImmutableSet<String> EXACT_SUBSKILL_NAMES; public final @NotNull ImmutableSet<String> EXACT_SUBSKILL_NAMES;
public final @NotNull ImmutableList<PrimarySkillType> CHILD_SKILLS; public final @NotNull ImmutableList<PrimarySkillType> CHILD_SKILLS;
public final static @NotNull ImmutableList<PrimarySkillType> NON_CHILD_SKILLS; public static final @NotNull ImmutableList<PrimarySkillType> NON_CHILD_SKILLS;
public final static @NotNull ImmutableList<PrimarySkillType> SALVAGE_PARENTS; public static final @NotNull ImmutableList<PrimarySkillType> SALVAGE_PARENTS;
public final static @NotNull ImmutableList<PrimarySkillType> SMELTING_PARENTS; public static final @NotNull ImmutableList<PrimarySkillType> SMELTING_PARENTS;
public final @NotNull ImmutableList<PrimarySkillType> COMBAT_SKILLS; public final @NotNull ImmutableList<PrimarySkillType> COMBAT_SKILLS;
public final @NotNull ImmutableList<PrimarySkillType> GATHERING_SKILLS; public final @NotNull ImmutableList<PrimarySkillType> GATHERING_SKILLS;
public final @NotNull ImmutableList<PrimarySkillType> MISC_SKILLS; public final @NotNull ImmutableList<PrimarySkillType> MISC_SKILLS;
@@ -44,73 +45,141 @@ public class SkillTools {
private final @NotNull ImmutableMap<SuperAbilityType, PrimarySkillType> superAbilityParentRelationshipMap; private final @NotNull ImmutableMap<SuperAbilityType, PrimarySkillType> superAbilityParentRelationshipMap;
private final @NotNull ImmutableMap<PrimarySkillType, Set<SubSkillType>> primarySkillChildrenMap; private final @NotNull ImmutableMap<PrimarySkillType, Set<SubSkillType>> primarySkillChildrenMap;
// The map below is for the super abilities which require readying a tool, its everything except blast mining
private final ImmutableMap<PrimarySkillType, SuperAbilityType> mainActivatedAbilityChildMap; private final ImmutableMap<PrimarySkillType, SuperAbilityType> mainActivatedAbilityChildMap;
private final ImmutableMap<PrimarySkillType, ToolType> primarySkillToolMap; private final ImmutableMap<PrimarySkillType, ToolType> primarySkillToolMap;
static { static {
// Build NON_CHILD_SKILLS once from the enum values
ArrayList<PrimarySkillType> tempNonChildSkills = new ArrayList<>(); ArrayList<PrimarySkillType> tempNonChildSkills = new ArrayList<>();
for (PrimarySkillType primarySkillType : PrimarySkillType.values()) { for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
if (primarySkillType != PrimarySkillType.SALVAGE if (!isChildSkill(primarySkillType)) {
&& primarySkillType != PrimarySkillType.SMELTING) {
tempNonChildSkills.add(primarySkillType); tempNonChildSkills.add(primarySkillType);
} }
} }
NON_CHILD_SKILLS = ImmutableList.copyOf(tempNonChildSkills); NON_CHILD_SKILLS = ImmutableList.copyOf(tempNonChildSkills);
SALVAGE_PARENTS = ImmutableList.of(PrimarySkillType.REPAIR, PrimarySkillType.FISHING);
SMELTING_PARENTS = ImmutableList.of(PrimarySkillType.MINING, PrimarySkillType.REPAIR); SALVAGE_PARENTS = ImmutableList.of(
PrimarySkillType.REPAIR,
PrimarySkillType.FISHING
);
SMELTING_PARENTS = ImmutableList.of(
PrimarySkillType.MINING,
PrimarySkillType.REPAIR
);
} }
public SkillTools(@NotNull mcMMO pluginRef) throws InvalidSkillException { public SkillTools(@NotNull mcMMO pluginRef) {
this.pluginRef = pluginRef; this.pluginRef = pluginRef;
/* /*
* Setup subskill -> parent relationship map * Setup subskill -> parent relationship map
*/ */
EnumMap<SubSkillType, PrimarySkillType> tempSubParentMap = new EnumMap<>( this.subSkillParentRelationshipMap = buildSubSkillParentMap();
SubSkillType.class);
//Super hacky and disgusting
for (PrimarySkillType primarySkillType1 : PrimarySkillType.values()) {
for (SubSkillType subSkillType : SubSkillType.values()) {
String[] splitSubSkillName = subSkillType.toString().split("_");
if (primarySkillType1.toString().equalsIgnoreCase(splitSubSkillName[0])) {
//Parent Skill Found
tempSubParentMap.put(subSkillType, primarySkillType1);
}
}
}
subSkillParentRelationshipMap = ImmutableMap.copyOf(tempSubParentMap);
/* /*
* Setup primary -> (collection) subskill map * Setup primary -> (collection) subskill map
*/ */
this.primarySkillChildrenMap = buildPrimarySkillChildrenMap(subSkillParentRelationshipMap);
EnumMap<PrimarySkillType, Set<SubSkillType>> tempPrimaryChildMap = new EnumMap<>(
PrimarySkillType.class);
//Init the empty Hash Sets
for (PrimarySkillType primarySkillType1 : PrimarySkillType.values()) {
tempPrimaryChildMap.put(primarySkillType1, new HashSet<>());
}
//Fill in the hash sets
for (SubSkillType subSkillType : SubSkillType.values()) {
PrimarySkillType parentSkill = subSkillParentRelationshipMap.get(subSkillType);
//Add this subskill as a child
tempPrimaryChildMap.get(parentSkill).add(subSkillType);
}
primarySkillChildrenMap = ImmutableMap.copyOf(tempPrimaryChildMap);
/* /*
* Setup primary -> tooltype map * Setup primary -> tooltype map
*/ */
EnumMap<PrimarySkillType, ToolType> tempToolMap = new EnumMap<>(PrimarySkillType.class); this.primarySkillToolMap = buildPrimarySkillToolMap();
/*
* Setup ability -> primary map
* Setup primary -> ability map
*/
var abilityMaps = buildSuperAbilityMaps();
this.superAbilityParentRelationshipMap = abilityMaps.superAbilityParentRelationshipMap();
this.mainActivatedAbilityChildMap = abilityMaps.mainActivatedAbilityChildMap();
/*
* Build child skill list
*/
this.CHILD_SKILLS = buildChildSkills();
/*
* Build categorized skill lists
*/
this.COMBAT_SKILLS = buildCombatSkills();
this.GATHERING_SKILLS = ImmutableList.of(
PrimarySkillType.EXCAVATION,
PrimarySkillType.FISHING,
PrimarySkillType.HERBALISM,
PrimarySkillType.MINING,
PrimarySkillType.WOODCUTTING
);
this.MISC_SKILLS = ImmutableList.of(
PrimarySkillType.ACROBATICS,
PrimarySkillType.ALCHEMY,
PrimarySkillType.REPAIR,
PrimarySkillType.SALVAGE,
PrimarySkillType.SMELTING
);
/*
* Build formatted/localized/etc string lists
*/
this.LOCALIZED_SKILL_NAMES = ImmutableList.copyOf(buildLocalizedPrimarySkillNames());
this.FORMATTED_SUBSKILL_NAMES = ImmutableList.copyOf(buildFormattedSubSkillNameList());
this.EXACT_SUBSKILL_NAMES = ImmutableSet.copyOf(buildExactSubSkillNameList());
}
@VisibleForTesting
@NotNull
ImmutableMap<SubSkillType, PrimarySkillType> buildSubSkillParentMap() {
EnumMap<SubSkillType, PrimarySkillType> tempSubParentMap =
new EnumMap<>(SubSkillType.class);
// SubSkillType names use a convention: <PRIMARY>_SOMETHING
for (SubSkillType subSkillType : SubSkillType.values()) {
String enumName = subSkillType.name();
int underscoreIndex = enumName.indexOf('_');
String parentPrefix = underscoreIndex == -1
? enumName
: enumName.substring(0, underscoreIndex);
for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
if (primarySkillType.name().equalsIgnoreCase(parentPrefix)) {
tempSubParentMap.put(subSkillType, primarySkillType);
break;
}
}
}
return ImmutableMap.copyOf(tempSubParentMap);
}
@VisibleForTesting
@NotNull
ImmutableMap<PrimarySkillType, Set<SubSkillType>> buildPrimarySkillChildrenMap(
ImmutableMap<SubSkillType, PrimarySkillType> subParentMap) {
EnumMap<PrimarySkillType, Set<SubSkillType>> tempPrimaryChildMap =
new EnumMap<>(PrimarySkillType.class);
// Initialize empty sets
for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
tempPrimaryChildMap.put(primarySkillType, new HashSet<>());
}
// Fill sets
for (SubSkillType subSkillType : SubSkillType.values()) {
PrimarySkillType parentSkill = subParentMap.get(subSkillType);
if (parentSkill != null) {
tempPrimaryChildMap.get(parentSkill).add(subSkillType);
}
}
return ImmutableMap.copyOf(tempPrimaryChildMap);
}
@VisibleForTesting
@NotNull
ImmutableMap<PrimarySkillType, ToolType> buildPrimarySkillToolMap() {
EnumMap<PrimarySkillType, ToolType> tempToolMap =
new EnumMap<>(PrimarySkillType.class);
tempToolMap.put(PrimarySkillType.AXES, ToolType.AXE); tempToolMap.put(PrimarySkillType.AXES, ToolType.AXE);
tempToolMap.put(PrimarySkillType.WOODCUTTING, ToolType.AXE); tempToolMap.put(PrimarySkillType.WOODCUTTING, ToolType.AXE);
@@ -120,56 +189,74 @@ public class SkillTools {
tempToolMap.put(PrimarySkillType.HERBALISM, ToolType.HOE); tempToolMap.put(PrimarySkillType.HERBALISM, ToolType.HOE);
tempToolMap.put(PrimarySkillType.MINING, ToolType.PICKAXE); tempToolMap.put(PrimarySkillType.MINING, ToolType.PICKAXE);
primarySkillToolMap = ImmutableMap.copyOf(tempToolMap); return ImmutableMap.copyOf(tempToolMap);
}
/* /**
* Setup ability -> primary map * Holder for the two super ability maps, so we can build them in one pass.
* Setup primary -> ability map */
*/ @VisibleForTesting
record SuperAbilityMaps(
@NotNull ImmutableMap<SuperAbilityType, PrimarySkillType> superAbilityParentRelationshipMap,
@NotNull ImmutableMap<PrimarySkillType, SuperAbilityType> mainActivatedAbilityChildMap) {
}
EnumMap<SuperAbilityType, PrimarySkillType> tempAbilityParentRelationshipMap = new EnumMap<>( @VisibleForTesting
SuperAbilityType.class); @NotNull
EnumMap<PrimarySkillType, SuperAbilityType> tempMainActivatedAbilityChildMap = new EnumMap<>( SuperAbilityMaps buildSuperAbilityMaps() {
PrimarySkillType.class); final Map<SuperAbilityType, PrimarySkillType> tempAbilityParentRelationshipMap =
new EnumMap<>(SuperAbilityType.class);
final Map<PrimarySkillType, SuperAbilityType> tempMainActivatedAbilityChildMap =
new EnumMap<>(PrimarySkillType.class);
for (SuperAbilityType superAbilityType : SuperAbilityType.values()) { for (SuperAbilityType superAbilityType : SuperAbilityType.values()) {
try { final PrimarySkillType parent = getSuperAbilityParent(superAbilityType);
PrimarySkillType parent = getSuperAbilityParent(superAbilityType); tempAbilityParentRelationshipMap.put(superAbilityType, parent);
tempAbilityParentRelationshipMap.put(superAbilityType, parent);
if (superAbilityType != SuperAbilityType.BLAST_MINING) { // This map is used only for abilities that have a tool readying phase,
//This map is used only for abilities that have a tool readying phase, so blast mining is ignored // so Blast Mining is ignored.
tempMainActivatedAbilityChildMap.put(parent, superAbilityType); if (superAbilityType != SuperAbilityType.BLAST_MINING) {
} tempMainActivatedAbilityChildMap.put(parent, superAbilityType);
} catch (InvalidSkillException e) {
e.printStackTrace();
} }
} }
superAbilityParentRelationshipMap = ImmutableMap.copyOf(tempAbilityParentRelationshipMap); return new SuperAbilityMaps(
mainActivatedAbilityChildMap = ImmutableMap.copyOf(tempMainActivatedAbilityChildMap); ImmutableMap.copyOf(tempAbilityParentRelationshipMap),
ImmutableMap.copyOf(tempMainActivatedAbilityChildMap)
/* );
* Build child skill and nonchild skill lists }
*/
@VisibleForTesting
@NotNull
ImmutableList<PrimarySkillType> buildChildSkills() {
List<PrimarySkillType> childSkills = new ArrayList<>(); List<PrimarySkillType> childSkills = new ArrayList<>();
for (PrimarySkillType primarySkillType : PrimarySkillType.values()) { for (PrimarySkillType primarySkillType : PrimarySkillType.values()) {
if (isChildSkill(primarySkillType)) { if (isChildSkill(primarySkillType)) {
childSkills.add(primarySkillType); childSkills.add(primarySkillType);
} }
} }
return ImmutableList.copyOf(childSkills);
}
CHILD_SKILLS = ImmutableList.copyOf(childSkills); @VisibleForTesting
@NotNull
/* ImmutableList<PrimarySkillType> buildCombatSkills() {
* Build categorized skill lists if (mcMMO.getMinecraftGameVersion().isAtLeast(1, 21, 11)) {
*/ // We are in a game version with Spears and Maces
return ImmutableList.of(
// We are in a game version with Maces PrimarySkillType.ARCHERY,
if (mcMMO.getCompatibilityManager().getMinecraftGameVersion().isAtLeast(1, 21, 0)) { PrimarySkillType.AXES,
COMBAT_SKILLS = ImmutableList.of( PrimarySkillType.CROSSBOWS,
PrimarySkillType.MACES,
PrimarySkillType.SWORDS,
PrimarySkillType.SPEARS,
PrimarySkillType.TAMING,
PrimarySkillType.TRIDENTS,
PrimarySkillType.UNARMED
);
} else if (mcMMO.getMinecraftGameVersion().isAtLeast(1, 21, 0)) {
// We are in a game version with Maces
return ImmutableList.of(
PrimarySkillType.ARCHERY, PrimarySkillType.ARCHERY,
PrimarySkillType.AXES, PrimarySkillType.AXES,
PrimarySkillType.CROSSBOWS, PrimarySkillType.CROSSBOWS,
@@ -177,42 +264,23 @@ public class SkillTools {
PrimarySkillType.SWORDS, PrimarySkillType.SWORDS,
PrimarySkillType.TAMING, PrimarySkillType.TAMING,
PrimarySkillType.TRIDENTS, PrimarySkillType.TRIDENTS,
PrimarySkillType.UNARMED); PrimarySkillType.UNARMED
);
} else { } else {
// No Maces in this version // No Maces in this version
COMBAT_SKILLS = ImmutableList.of( return ImmutableList.of(
PrimarySkillType.ARCHERY, PrimarySkillType.ARCHERY,
PrimarySkillType.AXES, PrimarySkillType.AXES,
PrimarySkillType.CROSSBOWS, PrimarySkillType.CROSSBOWS,
PrimarySkillType.SWORDS, PrimarySkillType.SWORDS,
PrimarySkillType.TAMING, PrimarySkillType.TAMING,
PrimarySkillType.TRIDENTS, PrimarySkillType.TRIDENTS,
PrimarySkillType.UNARMED); PrimarySkillType.UNARMED
);
} }
GATHERING_SKILLS = ImmutableList.of(
PrimarySkillType.EXCAVATION,
PrimarySkillType.FISHING,
PrimarySkillType.HERBALISM,
PrimarySkillType.MINING,
PrimarySkillType.WOODCUTTING);
MISC_SKILLS = ImmutableList.of(
PrimarySkillType.ACROBATICS,
PrimarySkillType.ALCHEMY,
PrimarySkillType.REPAIR,
PrimarySkillType.SALVAGE,
PrimarySkillType.SMELTING);
/*
* Build formatted/localized/etc string lists
*/
LOCALIZED_SKILL_NAMES = ImmutableList.copyOf(buildLocalizedPrimarySkillNames());
FORMATTED_SUBSKILL_NAMES = ImmutableList.copyOf(buildFormattedSubSkillNameList());
EXACT_SUBSKILL_NAMES = ImmutableSet.copyOf(buildExactSubSkillNameList());
} }
private @NotNull PrimarySkillType getSuperAbilityParent(SuperAbilityType superAbilityType) private @NotNull PrimarySkillType getSuperAbilityParent(SuperAbilityType superAbilityType) {
throws InvalidSkillException {
return switch (superAbilityType) { return switch (superAbilityType) {
case BERSERK -> PrimarySkillType.UNARMED; case BERSERK -> PrimarySkillType.UNARMED;
case GREEN_TERRA -> PrimarySkillType.HERBALISM; case GREEN_TERRA -> PrimarySkillType.HERBALISM;
@@ -225,11 +293,12 @@ public class SkillTools {
case TRIDENTS_SUPER_ABILITY -> PrimarySkillType.TRIDENTS; case TRIDENTS_SUPER_ABILITY -> PrimarySkillType.TRIDENTS;
case EXPLOSIVE_SHOT -> PrimarySkillType.ARCHERY; case EXPLOSIVE_SHOT -> PrimarySkillType.ARCHERY;
case MACES_SUPER_ABILITY -> PrimarySkillType.MACES; case MACES_SUPER_ABILITY -> PrimarySkillType.MACES;
case SPEARS_SUPER_ABILITY -> PrimarySkillType.SPEARS;
}; };
} }
/** /**
* Makes a list of the "nice" version of sub skill names Used in tab completion mostly * Makes a list of the "nice" version of sub skill names. Used in tab completion mostly.
* *
* @return a list of formatted sub skill names * @return a list of formatted sub skill names
*/ */
@@ -272,9 +341,12 @@ public class SkillTools {
} }
/** /**
* Matches a string of a skill to a skill This is NOT case sensitive First it checks the locale * Matches a string of a skill to a skill.
* file and tries to match by the localized name of the skill Then if nothing is found it checks * This is NOT case-sensitive.
* against the hard coded "name" of the skill, which is just its name in English * <p>
* First it checks the locale file and tries to match by the localized name of the skill.
* Then if nothing is found it checks against the hard coded "name" of the skill,
* which is just its name in English.
* *
* @param skillName target skill name * @param skillName target skill name
* @return the matching PrimarySkillType if one is found, otherwise null * @return the matching PrimarySkillType if one is found, otherwise null
@@ -282,8 +354,9 @@ public class SkillTools {
public PrimarySkillType matchSkill(String skillName) { public PrimarySkillType matchSkill(String skillName) {
if (!pluginRef.getGeneralConfig().getLocale().equalsIgnoreCase("en_US")) { if (!pluginRef.getGeneralConfig().getLocale().equalsIgnoreCase("en_US")) {
for (PrimarySkillType type : PrimarySkillType.values()) { for (PrimarySkillType type : PrimarySkillType.values()) {
if (skillName.equalsIgnoreCase(LocaleLoader.getString( String localized = LocaleLoader.getString(
StringUtils.getCapitalized(type.name()) + ".SkillName"))) { StringUtils.getCapitalized(type.name()) + ".SkillName");
if (skillName.equalsIgnoreCase(localized)) {
return type; return type;
} }
} }
@@ -297,15 +370,15 @@ public class SkillTools {
if (!skillName.equalsIgnoreCase("all")) { if (!skillName.equalsIgnoreCase("all")) {
pluginRef.getLogger() pluginRef.getLogger()
.warning("Invalid mcMMO skill (" + skillName + ")"); //TODO: Localize .warning("Invalid mcMMO skill (" + skillName + ")"); // TODO: Localize
} }
return null; return null;
} }
/** /**
* Gets the PrimarySkillStype to which a SubSkillType belongs Return null if it does not belong * Gets the PrimarySkillType to which a SubSkillType belongs.
* to one.. which should be impossible in most circumstances * Returns null if it does not belong to one (which should be impossible in most circumstances).
* *
* @param subSkillType target subskill * @param subSkillType target subskill
* @return the PrimarySkillType of this SubSkill, null if it doesn't exist * @return the PrimarySkillType of this SubSkill, null if it doesn't exist
@@ -315,8 +388,8 @@ public class SkillTools {
} }
/** /**
* Gets the PrimarySkillStype to which a SuperAbilityType belongs Return null if it does not * Gets the PrimarySkillType to which a SuperAbilityType belongs.
* belong to one.. which should be impossible in most circumstances * Returns null if it does not belong to one (which should be impossible in most circumstances).
* *
* @param superAbilityType target super ability * @param superAbilityType target super ability
* @return the PrimarySkillType of this SuperAbilityType, null if it doesn't exist * @return the PrimarySkillType of this SuperAbilityType, null if it doesn't exist
@@ -326,16 +399,15 @@ public class SkillTools {
} }
public SuperAbilityType getSuperAbility(PrimarySkillType primarySkillType) { public SuperAbilityType getSuperAbility(PrimarySkillType primarySkillType) {
if (mainActivatedAbilityChildMap.get(primarySkillType) == null) {
return null;
}
return mainActivatedAbilityChildMap.get(primarySkillType); return mainActivatedAbilityChildMap.get(primarySkillType);
} }
public boolean isSuperAbilityUnlocked(PrimarySkillType primarySkillType, Player player) { public boolean isSuperAbilityUnlocked(PrimarySkillType primarySkillType, Player player) {
SuperAbilityType superAbilityType = mcMMO.p.getSkillTools() SuperAbilityType superAbilityType = getSuperAbility(primarySkillType);
.getSuperAbility(primarySkillType); if (superAbilityType == null) {
return false;
}
SubSkillType subSkillType = superAbilityType.getSubSkillTypeDefinition(); SubSkillType subSkillType = superAbilityType.getSubSkillTypeDefinition();
return RankUtils.hasUnlockedSubskill(player, subSkillType); return RankUtils.hasUnlockedSubskill(player, subSkillType);
} }
@@ -368,7 +440,6 @@ public class SkillTools {
return ExperienceConfig.getInstance().getFormulaSkillModifier(primarySkillType); return ExperienceConfig.getInstance().getFormulaSkillModifier(primarySkillType);
} }
// TODO: This is a little "hacky", we probably need to add something to distinguish child skills in the enum, or to use another enum for them
public static boolean isChildSkill(PrimarySkillType primarySkillType) { public static boolean isChildSkill(PrimarySkillType primarySkillType) {
return switch (primarySkillType) { return switch (primarySkillType) {
case SALVAGE, SMELTING -> true; case SALVAGE, SMELTING -> true;
@@ -392,8 +463,10 @@ public class SkillTools {
} }
public boolean canCombatSkillsTrigger(PrimarySkillType primarySkillType, Entity target) { public boolean canCombatSkillsTrigger(PrimarySkillType primarySkillType, Entity target) {
return (target instanceof Player || (target instanceof Tameable boolean isPlayerOrTamed = (target instanceof Player)
&& ((Tameable) target).isTamed())) ? getPVPEnabled(primarySkillType) || (target instanceof Tameable && ((Tameable) target).isTamed());
return isPlayerOrTamed
? getPVPEnabled(primarySkillType)
: getPVEEnabled(primarySkillType); : getPVEEnabled(primarySkillType);
} }
@@ -410,7 +483,7 @@ public class SkillTools {
} }
public int getLevelCap(@NotNull PrimarySkillType primarySkillType) { public int getLevelCap(@NotNull PrimarySkillType primarySkillType) {
return mcMMO.p.getGeneralConfig().getLevelCap(primarySkillType); return pluginRef.getGeneralConfig().getLevelCap(primarySkillType);
} }
/** /**
@@ -445,17 +518,12 @@ public class SkillTools {
} }
public @NotNull ImmutableList<PrimarySkillType> getChildSkillParents( public @NotNull ImmutableList<PrimarySkillType> getChildSkillParents(
PrimarySkillType childSkill) PrimarySkillType childSkill) throws IllegalArgumentException {
throws IllegalArgumentException { return switch (childSkill) {
switch (childSkill) { case SALVAGE -> SALVAGE_PARENTS;
case SALVAGE -> { case SMELTING -> SMELTING_PARENTS;
return SALVAGE_PARENTS;
}
case SMELTING -> {
return SMELTING_PARENTS;
}
default -> throw new IllegalArgumentException( default -> throw new IllegalArgumentException(
"Skill " + childSkill + " is not a child skill"); "Skill " + childSkill + " is not a child skill");
} };
} }
} }

View File

@@ -29,6 +29,7 @@ import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.Recipe; import org.bukkit.inventory.Recipe;
import org.bukkit.inventory.RecipeChoice;
import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.ShapedRecipe;
import org.bukkit.inventory.ShapelessRecipe; import org.bukkit.inventory.ShapelessRecipe;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
@@ -360,6 +361,8 @@ public final class SkillUtils {
return 4; return 4;
} }
final ItemStack recipeItem = recipeMaterial != null ? new ItemStack(recipeMaterial) : null;
for (Iterator<? extends Recipe> recipeIterator = Bukkit.getServer().recipeIterator(); for (Iterator<? extends Recipe> recipeIterator = Bukkit.getServer().recipeIterator();
recipeIterator.hasNext(); ) { recipeIterator.hasNext(); ) {
Recipe bukkitRecipe = recipeIterator.next(); Recipe bukkitRecipe = recipeIterator.next();
@@ -368,21 +371,17 @@ public final class SkillUtils {
continue; continue;
} }
if (bukkitRecipe instanceof ShapelessRecipe) { if (bukkitRecipe instanceof ShapelessRecipe shapelessRecipe) {
for (ItemStack ingredient : ((ShapelessRecipe) bukkitRecipe).getIngredientList()) { for (RecipeChoice ingredient : shapelessRecipe.getChoiceList()) {
if (ingredient != null if (ingredient != null && recipeItem != null && ingredient.test(recipeItem)) {
&& (recipeMaterial == null || ingredient.getType() == recipeMaterial) quantity += 1;
&& (ingredient.getType() == recipeMaterial)) {
quantity += ingredient.getAmount();
} }
} }
} else if (bukkitRecipe instanceof ShapedRecipe) { } else if (bukkitRecipe instanceof ShapedRecipe shapedRecipe) {
for (ItemStack ingredient : ((ShapedRecipe) bukkitRecipe).getIngredientMap() for (RecipeChoice ingredient : shapedRecipe.getChoiceMap()
.values()) { .values()) {
if (ingredient != null if (ingredient != null && recipeItem != null && ingredient.test(recipeItem)) {
&& (recipeMaterial == null || ingredient.getType() == recipeMaterial) quantity += 1;
&& (ingredient.getType() == recipeMaterial)) {
quantity += ingredient.getAmount();
} }
} }
} }

View File

@@ -38,7 +38,7 @@ public class TextComponentFactory {
* *
* @param localeKey target locale string address * @param localeKey target locale string address
* @param values vars to be passed to the locale loader * @param values vars to be passed to the locale loader
* @return * @return a text component with the locale string and variables applied
*/ */
public static TextComponent getNotificationMultipleValues(String localeKey, String... values) { public static TextComponent getNotificationMultipleValues(String localeKey, String... values) {
String preColoredString = LocaleLoader.getString(localeKey, (Object[]) values); String preColoredString = LocaleLoader.getString(localeKey, (Object[]) values);
@@ -544,18 +544,28 @@ public class TextComponentFactory {
componentBuilder.append(Component.newline()); componentBuilder.append(Component.newline());
} }
/**
* @deprecated use appendSubSkillTextComponents(Player, List<Component>, PrimarySkillType)
* @param player target player
* @param textComponents list to append to
* @param parentSkill the parent skill
*/
@Deprecated(since = "2.2.046", forRemoval = true)
public static void getSubSkillTextComponents(Player player, List<Component> textComponents, public static void getSubSkillTextComponents(Player player, List<Component> textComponents,
PrimarySkillType parentSkill) {
appendSubSkillTextComponents(player, textComponents, parentSkill);
}
/**
* Appends sub-skill text components to a list for a given parent skill
* @param player target player
* @param textComponents list to append to
* @param parentSkill the parent skill
*/
public static void appendSubSkillTextComponents(Player player, List<Component> textComponents,
PrimarySkillType parentSkill) { PrimarySkillType parentSkill) {
for (SubSkillType subSkillType : SubSkillType.values()) { for (SubSkillType subSkillType : SubSkillType.values()) {
if (subSkillType.getParentSkill() == parentSkill) { if (subSkillType.getParentSkill() == parentSkill) {
//TODO: Hacky rewrite later
//Only some versions of MC have this skill
if (subSkillType == SubSkillType.FISHING_MASTER_ANGLER
&& mcMMO.getCompatibilityManager().getMasterAnglerCompatibilityLayer()
== null) {
continue;
}
if (Permissions.isSubSkillEnabled(player, subSkillType)) { if (Permissions.isSubSkillEnabled(player, subSkillType)) {
if (!InteractionManager.hasSubSkill(subSkillType)) { if (!InteractionManager.hasSubSkill(subSkillType)) {
textComponents.add(TextComponentFactory.getSubSkillTextComponent(player, textComponents.add(TextComponentFactory.getSubSkillTextComponent(player,

View File

@@ -1,6 +1,5 @@
package com.gmail.nossr50.util.text; package com.gmail.nossr50.util.text;
import com.gmail.nossr50.mcMMO;
import java.util.List; import java.util.List;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder; import net.kyori.adventure.text.ComponentBuilder;
@@ -10,7 +9,6 @@ import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.format.TextDecoration;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -106,20 +104,6 @@ public class TextUtils {
textComponent.hoverEvent(HoverEvent.showText(baseComponent)); textComponent.hoverEvent(HoverEvent.showText(baseComponent));
} }
public static BaseComponent[] convertToBungeeComponent(@NotNull String displayName) {
return net.md_5.bungee.api.chat.TextComponent.fromLegacyText(displayName);
}
public static @NotNull TextComponent ofBungeeComponents(@NotNull BaseComponent[] bungeeName) {
return Component.textOfChildren(
mcMMO.getCompatibilityManager().getBungeeSerializerCompatibilityLayer()
.deserialize(bungeeName));
}
public static @NotNull TextComponent ofBungeeRawStrings(@NotNull String bungeeRawString) {
return ofBungeeComponents(convertToBungeeComponent(bungeeRawString));
}
public static @NotNull TextComponent ofLegacyTextRaw(@NotNull String rawString) { public static @NotNull TextComponent ofLegacyTextRaw(@NotNull String rawString) {
return LegacyComponentSerializer.legacySection().deserialize(rawString); return LegacyComponentSerializer.legacySection().deserialize(rawString);
} }

View File

@@ -647,4 +647,19 @@ Skills:
Rank_1: 10 Rank_1: 10
Rank_2: 15 Rank_2: 15
Rank_3: 20 Rank_3: 20
Rank_4: 33 Rank_4: 33
Spears:
SpearMastery:
Rank_Damage_Multiplier: 0.4
Momentum:
Chance_To_Apply_On_Hit:
Rank_1: 5
Rank_2: 10
Rank_3: 15
Rank_4: 20
Rank_5: 25
Rank_6: 30
Rank_7: 35
Rank_8: 40
Rank_9: 45
Rank_10: 50

View File

@@ -229,6 +229,7 @@ Hardcore:
Tridents: false Tridents: false
Crossbows: false Crossbows: false
Maces: false Maces: false
Spears: false
Vampirism: Vampirism:
Leech_Percentage: 5.0 Leech_Percentage: 5.0
Level_Threshold: 0 Level_Threshold: 0
@@ -249,6 +250,7 @@ Hardcore:
Tridents: false Tridents: false
Crossbows: false Crossbows: false
Maces: false Maces: false
Spears: false
# #
# Settings for SMP Mods # Settings for SMP Mods
@@ -427,6 +429,10 @@ Skills:
Enabled_For_PVP: true Enabled_For_PVP: true
Enabled_For_PVE: true Enabled_For_PVE: true
Level_Cap: 0 Level_Cap: 0
Spears:
Enabled_For_PVP: true
Enabled_For_PVE: true
Level_Cap: 0
Taming: Taming:
Enabled_For_PVP: true Enabled_For_PVP: true
Enabled_For_PVE: true Enabled_For_PVE: true
@@ -478,6 +484,7 @@ Green_Thumb_Replanting_Crops:
Potatoes: true Potatoes: true
Beetroots: true Beetroots: true
Cocoa: true Cocoa: true
Sweet_Berry_Bush: true
# #
# Settings for Double Drops # Settings for Double Drops
### ###

View File

@@ -95,6 +95,10 @@ Experience_Bars:
Enable: true Enable: true
Color: BLUE Color: BLUE
BarStyle: SEGMENTED_6 BarStyle: SEGMENTED_6
Spears:
Enable: true
Color: BLUE
BarStyle: SEGMENTED_6
Repair: Repair:
Enable: true Enable: true
Color: PURPLE Color: PURPLE
@@ -167,6 +171,7 @@ Experience_Formula:
# Experience gained will get multiplied by these values. 1.0 by default, 0.5 means half XP gained. This happens right before multiplying the XP by the global multiplier. # Experience gained will get multiplied by these values. 1.0 by default, 0.5 means half XP gained. This happens right before multiplying the XP by the global multiplier.
Skill_Multiplier: Skill_Multiplier:
Spears: 1.0
Maces: 1.0 Maces: 1.0
Crossbows: 1.0 Crossbows: 1.0
Tridents: 1.0 Tridents: 1.0
@@ -218,6 +223,7 @@ Diminished_Returns:
Crossbows: 20000 Crossbows: 20000
Tridents: 20000 Tridents: 20000
Maces: 20000 Maces: 20000
Spears: 20000
Time_Interval: 10 Time_Interval: 10
@@ -546,6 +552,7 @@ Experience_Values:
Base: 1000.0 Base: 1000.0
Wood: 0.6 Wood: 0.6
Stone: 1.3 Stone: 1.3
Copper: 2.0
Iron: 2.5 Iron: 2.5
Gold: 0.3 Gold: 0.3
Diamond: 5.0 Diamond: 5.0
@@ -582,6 +589,7 @@ Experience_Values:
Taming: Taming:
Animal_Taming: Animal_Taming:
Camel: 1300 Camel: 1300
Camel_Husk: 1300
Sniffer: 1500 Sniffer: 1500
Snifflet: 900 Snifflet: 900
Llama: 1200 Llama: 1200
@@ -600,12 +608,15 @@ Experience_Values:
Goat: 250 Goat: 250
Axolotl: 600 Axolotl: 600
Frog: 900 Frog: 900
Nautilus: 1700
Zombie_Nautilus: 1700
Combat: Combat:
Multiplier: Multiplier:
Animals: 1.0 Animals: 1.0
Armadillo: 1.1 Armadillo: 1.1
Creeper: 4.0 Creeper: 4.0
Skeleton: 3.0 Skeleton: 3.0
Parched: 2.5
Spider: 2.0 Spider: 2.0
Giant: 4.0 Giant: 4.0
Zombie: 2.0 Zombie: 2.0
@@ -683,6 +694,7 @@ Experience_Values:
Sniffer: 1.1 Sniffer: 1.1
Snifflet: 1.1 Snifflet: 1.1
Camel: 1.2 Camel: 1.2
Camel_Husk: 1.25
Bogged: 2.0 Bogged: 2.0
Breeze: 4.0 Breeze: 4.0
Armor_Stand: 0.0 Armor_Stand: 0.0

View File

@@ -27,6 +27,7 @@ JSON.Salvage=Salvage
JSON.Swords=Swords JSON.Swords=Swords
JSON.Taming=Taming JSON.Taming=Taming
JSON.Tridents=Tridents JSON.Tridents=Tridents
JSON.Spears=Spears
JSON.Maces=Maces JSON.Maces=Maces
JSON.Unarmed=Unarmed JSON.Unarmed=Unarmed
JSON.Woodcutting=Woodcutting JSON.Woodcutting=Woodcutting
@@ -98,6 +99,7 @@ Overhaul.Name.Smelting=Smelting
Overhaul.Name.Swords=Swords Overhaul.Name.Swords=Swords
Overhaul.Name.Taming=Taming Overhaul.Name.Taming=Taming
Overhaul.Name.Tridents=Tridents Overhaul.Name.Tridents=Tridents
Overhaul.Name.Spears=Spears
Overhaul.Name.Maces=Maces Overhaul.Name.Maces=Maces
Overhaul.Name.Unarmed=Unarmed Overhaul.Name.Unarmed=Unarmed
Overhaul.Name.Woodcutting=Woodcutting Overhaul.Name.Woodcutting=Woodcutting
@@ -125,6 +127,7 @@ XPBar.Smelting=Smelting Lv.&6{0}
XPBar.Swords=Swords Lv.&6{0} XPBar.Swords=Swords Lv.&6{0}
XPBar.Taming=Taming Lv.&6{0} XPBar.Taming=Taming Lv.&6{0}
XPBar.Tridents=Tridents Lv.&6{0} XPBar.Tridents=Tridents Lv.&6{0}
XPBar.Spears=Spears Lv.&6{0}
XPBar.Maces=Maces Lv.&6{0} XPBar.Maces=Maces Lv.&6{0}
XPBar.Unarmed=Unarmed Lv.&6{0} XPBar.Unarmed=Unarmed Lv.&6{0}
XPBar.Woodcutting=Woodcutting Lv.&6{0} XPBar.Woodcutting=Woodcutting Lv.&6{0}
@@ -474,6 +477,24 @@ Maces.SubSkill.Cripple.Stat=Cripple Chance
Maces.SubSkill.Cripple.Stat.Extra=[[DARK_AQUA]]Cripple Duration: &e{0}s&a vs Players, &e{1}s&a vs Mobs. Maces.SubSkill.Cripple.Stat.Extra=[[DARK_AQUA]]Cripple Duration: &e{0}s&a vs Players, &e{1}s&a vs Mobs.
Maces.Listener=Maces: Maces.Listener=Maces:
#SPEARS
Spears.SkillName=SPEARS
Spears.Ability.Lower=&7You lower your spear.
Spears.Ability.Ready=&3You &6ready&3 your spear.
Spears.SubSkill.SpearsLimitBreak.Name=Spears Limit Break
Spears.SubSkill.SpearsLimitBreak.Description=Breaking your limits. Increased damage against tough opponents. Intended for PVP, up to server settings for whether it will boost damage in PVE.
Spears.SubSkill.SpearsLimitBreak.Stat=Limit Break Max DMG
Spears.SubSkill.SpearAbility.Name=WIP
Spears.SubSkill.Momentum.Name=Momentum
Spears.SubSkill.Momentum.Description=Adds a chance to increase movement speed for a short duration when attacking.
Spears.SubSkill.Momentum.Stat=Momentum Chance
Spears.SubSkill.Momentum.Stat.Extra=[[DARK_AQUA]]Momentum Duration: &e{0}s
Spears.SubSkill.Momentum.Activated=MOMENTUM ACTIVATED!
Spears.SubSkill.SpearMastery.Name=Spear Mastery
Spears.SubSkill.SpearMastery.Description=Adds bonus damage to your attacks.
Spears.SubSkill.SpearMastery.Stat=Spear Mastery Bonus DMG
Spears.Listener=Spears:
#SWORDS #SWORDS
Swords.Ability.Lower=&7You lower your sword. Swords.Ability.Lower=&7You lower your sword.
Swords.Ability.Ready=&3You &6ready&3 your Sword. Swords.Ability.Ready=&3You &6ready&3 your Sword.
@@ -913,6 +934,7 @@ Commands.XPGain.Repair=Repairing
Commands.XPGain.Swords=Attacking Monsters Commands.XPGain.Swords=Attacking Monsters
Commands.XPGain.Taming=Animal Taming, or combat w/ your wolves Commands.XPGain.Taming=Animal Taming, or combat w/ your wolves
Commands.XPGain.Tridents=Attacking Monsters Commands.XPGain.Tridents=Attacking Monsters
Commands.XPGain.Spears=Attacking Monsters
Commands.XPGain.Unarmed=Attacking Monsters Commands.XPGain.Unarmed=Attacking Monsters
Commands.XPGain.Woodcutting=Chopping down trees Commands.XPGain.Woodcutting=Chopping down trees
Commands.XPGain=&8XP GAIN: &f{0} Commands.XPGain=&8XP GAIN: &f{0}
@@ -1047,12 +1069,12 @@ Guides.Woodcutting.Section.1=&3How does Tree Feller work?\n&eTree Feller is an a
Guides.Woodcutting.Section.2=&3How does Leaf Blower work?\n&eLeaf Blower is a passive ability that will cause leaf\n&eblocks to break instantly when hit with an axe. By default,\n&ethis ability unlocks at level 100. Guides.Woodcutting.Section.2=&3How does Leaf Blower work?\n&eLeaf Blower is a passive ability that will cause leaf\n&eblocks to break instantly when hit with an axe. By default,\n&ethis ability unlocks at level 100.
Guides.Woodcutting.Section.3=&3How do Double Drops work?\n&eThis passive ability gives you a chance to obtain an extra\n&eblock for every log you chop. Guides.Woodcutting.Section.3=&3How do Double Drops work?\n&eThis passive ability gives you a chance to obtain an extra\n&eblock for every log you chop.
# Crossbows # Crossbows
Guides.Crossbows.Section.0=&3About Crossbows:\n&eCrossbows is all about shooting with your crossbow.\n\n&3XP GAIN:\n&eXP is gained whenever you shoot mobs with a crossbow.\nThis is a WIP skill and more information will be added soon. Guides.Crossbows.Section.0=&3About Crossbows:\n&eCrossbows is all about shooting with your crossbow.\n\n&3XP GAIN:\n&eXP is gained whenever you shoot mobs with a crossbow.
Guides.Crossbows.Section.1=&3How does Trickshot work?\n&eTrickshot is an passive ability, you shoot your bolts at a shallow angle with a crossbow to attempt a Trickshot. This will cause the arrow to ricochet off of blocks and potentially hit a target. The number of potential bounces from a ricochet depend on the rank of Trickshot. Guides.Crossbows.Section.1=&3How does Trickshot work?\n&eTrickshot is an passive ability, you shoot your bolts at a shallow angle with a crossbow to attempt a Trickshot. This will cause the arrow to ricochet off of blocks and potentially hit a target. The number of potential bounces from a ricochet depend on the rank of Trickshot.
# Tridents # Tridents
Guides.Tridents.Section.0=&3About Tridents:\n&eTridents skill involves impaling foes with your trident.\n\n&3XP GAIN:\n&eXP is gained whenever you hit mobs with a trident.\nThis is a WIP skill and more information will be added soon. Guides.Tridents.Section.0=&3About Tridents:\n&eTridents skill involves impaling foes with your trident.\n\n&3XP GAIN:\n&eXP is gained whenever you hit mobs with a trident.
Guides.Maces.Section.0=&3About Maces:\n&eMaces is all about smashing your foes with a mace.\n\n&3XP GAIN:\n&eXP is gained whenever you hit mobs with a mace.\nThis is a WIP skill and more information will be added soon. Guides.Maces.Section.0=&3About Maces:\n&eMaces is all about smashing your foes with a mace.\n\n&3XP GAIN:\n&eXP is gained whenever you hit mobs with a mace.
Guides.Spears.Section.0=&3About Spears:\n&eSpears is all about impaling your foes with a spear.\n\n&3XP GAIN:\n&eXP is gained whenever you hit mobs with a spear.
#INSPECT #INSPECT
Inspect.Offline= &cYou do not have permission to inspect offline players! Inspect.Offline= &cYou do not have permission to inspect offline players!
Inspect.OfflineStats=mcMMO Stats for Offline Player &e{0} Inspect.OfflineStats=mcMMO Stats for Offline Player &e{0}

View File

@@ -1,20 +1,20 @@
name: mcMMO name: mcMMO
version: ${project.version} version: ${project.version}
description: > description: >
The goal of mcMMO is to take core Minecraft game mechanics and expand them into The goal of mcMMO is to take core Minecraft game mechanics and expand them into
add an extensive and quality RPG experience. Everything in mcMMO has been carefully add an extensive and quality RPG experience. Everything in mcMMO has been carefully
thought out and is constantly being improved upon. Currently, mcMMO adds thirteen thought out and is constantly being improved upon. Currently, mcMMO adds thirteen
unique skills to train and level in. Each of these skills is highly customizable unique skills to train and level in. Each of these skills is highly customizable
through our configuration files, allowing server admins to tweak mcMMO to best suit through our configuration files, allowing server admins to tweak mcMMO to best suit
the needs of his or her server. Know that the mcMMO team is dedicated to providing the needs of his or her server. Know that the mcMMO team is dedicated to providing
an ever-evolving experience, and that we carefully read all feedback and bug reports an ever-evolving experience, and that we carefully read all feedback and bug reports
in order to evaluate and balance the mechanics of mcMMO in every update. in order to evaluate and balance the mechanics of mcMMO in every update.
author: nossr50 author: nossr50
authors: [GJ, NuclearW, bm01, Glitchfinder, TfT_02, t00thpick1, Riking, electronicboy, kashike] authors: [ GJ, NuclearW, bm01, Glitchfinder, TfT_02, t00thpick1, Riking, electronicboy, kashike ]
website: https://www.mcmmo.org website: https://www.mcmmo.org
main: com.gmail.nossr50.mcMMO main: com.gmail.nossr50.mcMMO
softdepend: [WorldGuard, CombatTag, HealthBar, PlaceholderAPI, ProtocolLib] softdepend: [ WorldGuard, CombatTag, HealthBar, PlaceholderAPI, ProtocolLib ]
load: POSTWORLD load: POSTWORLD
folia-supported: true folia-supported: true
api-version: 1.13 api-version: 1.13
@@ -26,14 +26,14 @@ commands:
mmocompat: mmocompat:
description: Information about the server and whether its considered fully compatible or running in compatibility mode description: Information about the server and whether its considered fully compatible or running in compatibility mode
mmodebug: mmodebug:
aliases: [mcmmodebugmode] aliases: [ mcmmodebugmode ]
description: Toggles a debug mode which will print useful information to chat description: Toggles a debug mode which will print useful information to chat
mmoinfo: mmoinfo:
aliases: [mcinfo] aliases: [ mcinfo ]
description: Info pages for mcMMO description: Info pages for mcMMO
permission: mcmmo.commands.mmoinfo permission: mcmmo.commands.mmoinfo
xprate: xprate:
aliases: [mcxprate] aliases: [ mcxprate ]
description: Modify the xp rate or start an event description: Modify the xp rate or start an event
permission: mcmmo.commands.xprate permission: mcmmo.commands.xprate
mcmmo: mcmmo:
@@ -59,7 +59,7 @@ commands:
permission: mcmmo.commands.mcrefresh permission: mcmmo.commands.mcrefresh
mccooldown: mccooldown:
description: Show the cooldowns on all your mcMMO abilities description: Show the cooldowns on all your mcMMO abilities
aliases: [mccooldowns] aliases: [ mccooldowns ]
permission: mcmmo.commands.mccooldown permission: mcmmo.commands.mccooldown
mcchatspy: mcchatspy:
description: Toggle mcMMO Party Chat spying on/off description: Toggle mcMMO Party Chat spying on/off
@@ -68,7 +68,7 @@ commands:
description: Toggle mcMMO god-mode on/off description: Toggle mcMMO god-mode on/off
permission: mcmmo.commands.mcgod permission: mcmmo.commands.mcgod
mcstats: mcstats:
aliases: [stats] aliases: [ stats ]
description: Shows your mcMMO stats and xp description: Shows your mcMMO stats and xp
permission: mcmmo.commands.mcstats permission: mcmmo.commands.mcstats
mcremove: mcremove:
@@ -84,7 +84,7 @@ commands:
description: Create/join a party description: Create/join a party
permission: mcmmo.commands.party permission: mcmmo.commands.party
inspect: inspect:
aliases: [mcinspect, mmoinspect] aliases: [ mcinspect, mmoinspect ]
description: View detailed mcMMO info on another player description: View detailed mcMMO info on another player
permission: mcmmo.commands.inspect permission: mcmmo.commands.inspect
mmoshowdb: mmoshowdb:
@@ -94,7 +94,7 @@ commands:
description: Convert between different database and formula types description: Convert between different database and formula types
permission: mcmmo.commands.mcconvert permission: mcmmo.commands.mcconvert
partychat: partychat:
aliases: [pc, p] aliases: [ pc, p ]
description: Toggle Party chat or send party chat messages description: Toggle Party chat or send party chat messages
permission: mcmmo.chat.partychat permission: mcmmo.chat.partychat
skillreset: skillreset:
@@ -145,6 +145,9 @@ commands:
smelting: smelting:
description: Detailed mcMMO skill info description: Detailed mcMMO skill info
permission: mcmmo.commands.smelting permission: mcmmo.commands.smelting
spears:
description: Detailed mcMMO skill info
permission: mcmmo.commands.spears
alchemy: alchemy:
description: Detailed mcMMO skill info description: Detailed mcMMO skill info
permission: mcmmo.commands.alchemy permission: mcmmo.commands.alchemy
@@ -157,24 +160,24 @@ commands:
mmopower: mmopower:
description: Shows skill mastery and power level info description: Shows skill mastery and power level info
permission: mcmmo.commands.mmopower permission: mcmmo.commands.mmopower
aliases: [mmopowerlevel, powerlevel] aliases: [ mmopowerlevel, powerlevel ]
adminchat: adminchat:
aliases: [ac, a] aliases: [ ac, a ]
description: Toggle Admin chat or send admin chat messages description: Toggle Admin chat or send admin chat messages
permission: mcmmo.chat.adminchat permission: mcmmo.chat.adminchat
mcpurge: mcpurge:
description: Purge users with 0 powerlevel and/or who haven't connected in several months from the server DB. description: Purge users with 0 powerlevel and/or who haven't connected in several months from the server DB.
permission: mcmmo.commands.mcpurge permission: mcmmo.commands.mcpurge
mcnotify: mcnotify:
aliases: [notify] aliases: [ notify ]
description: Toggle mcMMO abilities chat display notifications on/off description: Toggle mcMMO abilities chat display notifications on/off
permission: mcmmo.commands.mcnotify permission: mcmmo.commands.mcnotify
mcscoreboard: mcscoreboard:
aliases: [mcsb] aliases: [ mcsb ]
description: Manage your mcMMO Scoreboard description: Manage your mcMMO Scoreboard
permission: mcmmo.commands.mcscoreboard permission: mcmmo.commands.mcscoreboard
mcmmoreloadlocale: mcmmoreloadlocale:
aliases: [mcreloadlocale] aliases: [ mcreloadlocale ]
description: Reloads locale description: Reloads locale
permission: mcmmo.commands.reloadlocale permission: mcmmo.commands.reloadlocale
permissions: permissions:
@@ -237,6 +240,7 @@ permissions:
mcmmo.ability.repair.all: true mcmmo.ability.repair.all: true
mcmmo.ability.salvage.all: true mcmmo.ability.salvage.all: true
mcmmo.ability.smelting.all: true mcmmo.ability.smelting.all: true
mcmmo.ability.spears.all: true
mcmmo.ability.swords.all: true mcmmo.ability.swords.all: true
mcmmo.ability.taming.all: true mcmmo.ability.taming.all: true
mcmmo.ability.tridents.all: true mcmmo.ability.tridents.all: true
@@ -320,19 +324,19 @@ permissions:
mcmmo.ability.axes.skullsplitter: mcmmo.ability.axes.skullsplitter:
description: Allows access to the Skull Splitter ability description: Allows access to the Skull Splitter ability
mcmmo.ability.crossbows.*: mcmmo.ability.crossbows.*:
description: Allows access to all Crossbows abilities description: Allows access to all Crossbows abilities
children: children:
mcmmo.ability.crossbows.all: true mcmmo.ability.crossbows.all: true
mcmmo.ability.crossbows.all: mcmmo.ability.crossbows.all:
description: Allows access to all Crossbows abilities description: Allows access to all Crossbows abilities
children: children:
mcmmo.ability.crossbows.trickshot: true mcmmo.ability.crossbows.trickshot: true
mcmmo.ability.crossbows.poweredshot: true mcmmo.ability.crossbows.poweredshot: true
mcmmo.ability.crossbows.crossbowslimitbreak: true mcmmo.ability.crossbows.crossbowslimitbreak: true
mcmmo.ability.crossbows.crossbowslimitbreak: mcmmo.ability.crossbows.crossbowslimitbreak:
description: Adds damage to crossbows description: Adds damage to crossbows
mcmmo.ability.crossbows.trickshot: mcmmo.ability.crossbows.trickshot:
description: Allows access to the Trick Shot ability description: Allows access to the Trick Shot ability
mcmmo.ability.crossbows.poweredshot: mcmmo.ability.crossbows.poweredshot:
description: Allows access to the Powered Shot ability description: Allows access to the Powered Shot ability
mcmmo.ability.excavation.*: mcmmo.ability.excavation.*:
@@ -646,6 +650,23 @@ permissions:
description: Allows access to the Second Smelt ability description: Allows access to the Second Smelt ability
mcmmo.ability.smelting.vanillaxpboost: mcmmo.ability.smelting.vanillaxpboost:
description: Allows vanilla XP boost from Smelting description: Allows vanilla XP boost from Smelting
mcmmo.ability.spears.*:
default: false
description: Allows access to all Spear abilities
children:
mcmmo.ability.spears.all: true
mcmmo.ability.spears.all:
description: Allows access to all Spear abilities
children:
mcmmo.ability.spears.spearslimitbreak: true
mcmmo.ability.spears.momentum: true
mcmmo.ability.spears.spearmastery: true
mcmmo.ability.spears.spearslimitbreak:
description: Adds damage to spears
mcmmo.ability.spears.momentum:
description: Allows access to the Spear Momentum ability
mcmmo.ability.spears.spearmastery:
description: Allows access to the Spear Mastery ability
mcmmo.ability.swords.*: mcmmo.ability.swords.*:
default: false default: false
description: Allows access to all Swords abilities description: Allows access to all Swords abilities
@@ -885,6 +906,7 @@ permissions:
mcmmo.commands.repair: true mcmmo.commands.repair: true
mcmmo.commands.salvage: true mcmmo.commands.salvage: true
mcmmo.commands.smelting: true mcmmo.commands.smelting: true
mcmmo.commands.spears: true
mcmmo.commands.swords: true mcmmo.commands.swords: true
mcmmo.commands.taming: true mcmmo.commands.taming: true
mcmmo.commands.unarmed: true mcmmo.commands.unarmed: true
@@ -898,7 +920,7 @@ permissions:
mcmmo.commands.addxp: true mcmmo.commands.addxp: true
mcmmo.commands.addxp.others: true mcmmo.commands.addxp.others: true
mcmmo.commands.defaults: true mcmmo.commands.defaults: true
# mcmmo.commands.hardcore.all: true # mcmmo.commands.hardcore.all: true
mcmmo.commands.inspect.far: true mcmmo.commands.inspect.far: true
mcmmo.commands.inspect.hidden: true mcmmo.commands.inspect.hidden: true
mcmmo.commands.mcability.others: true mcmmo.commands.mcability.others: true
@@ -918,7 +940,7 @@ permissions:
mcmmo.commands.ptp.world.all: true mcmmo.commands.ptp.world.all: true
mcmmo.commands.reloadlocale: true mcmmo.commands.reloadlocale: true
mcmmo.commands.skillreset.all: true mcmmo.commands.skillreset.all: true
# mcmmo.commands.vampirism.all: true # mcmmo.commands.vampirism.all: true
mcmmo.commands.xprate.all: true mcmmo.commands.xprate.all: true
mcmmo.commands.acrobatics: mcmmo.commands.acrobatics:
description: Allows access to the acrobatics command description: Allows access to the acrobatics command
@@ -1058,6 +1080,7 @@ permissions:
mcmmo.commands.mctop.repair: true mcmmo.commands.mctop.repair: true
mcmmo.commands.mctop.salvage: true mcmmo.commands.mctop.salvage: true
mcmmo.commands.mctop.smelting: true mcmmo.commands.mctop.smelting: true
mcmmo.commands.mctop.spears: true
mcmmo.commands.mctop.swords: true mcmmo.commands.mctop.swords: true
mcmmo.commands.mctop.taming: true mcmmo.commands.mctop.taming: true
mcmmo.commands.mctop.tridents: true mcmmo.commands.mctop.tridents: true
@@ -1091,6 +1114,8 @@ permissions:
description: Allows access to the mctop command for salvage description: Allows access to the mctop command for salvage
mcmmo.commands.mctop.smelting: mcmmo.commands.mctop.smelting:
description: Allows access to the mctop command for smelting description: Allows access to the mctop command for smelting
mcmmo.commands.mctop.spears:
description: Allows access to the mctop command for spears
mcmmo.commands.mctop.swords: mcmmo.commands.mctop.swords:
description: Allows access to the mctop command for swords description: Allows access to the mctop command for swords
mcmmo.commands.mctop.taming: mcmmo.commands.mctop.taming:
@@ -1239,6 +1264,7 @@ permissions:
mcmmo.commands.skillreset.repair: true mcmmo.commands.skillreset.repair: true
mcmmo.commands.skillreset.salvage: true mcmmo.commands.skillreset.salvage: true
mcmmo.commands.skillreset.smelting: true mcmmo.commands.skillreset.smelting: true
mcmmo.commands.skillreset.spears: true
mcmmo.commands.skillreset.swords: true mcmmo.commands.skillreset.swords: true
mcmmo.commands.skillreset.taming: true mcmmo.commands.skillreset.taming: true
mcmmo.commands.skillreset.unarmed: true mcmmo.commands.skillreset.unarmed: true
@@ -1268,6 +1294,8 @@ permissions:
description: Allows access to the skillreset command for crossbows description: Allows access to the skillreset command for crossbows
mcmmo.commands.skillreset.tridents: mcmmo.commands.skillreset.tridents:
description: Allows access to the skillreset command for tridents description: Allows access to the skillreset command for tridents
mcmmo.commands.skillreset.spears:
description: Allows access to the skillreset command for spears
mcmmo.commands.skillreset.maces: mcmmo.commands.skillreset.maces:
description: Allows access to the skillreset command for maces description: Allows access to the skillreset command for maces
mcmmo.commands.skillreset.others.*: mcmmo.commands.skillreset.others.*:
@@ -1290,6 +1318,7 @@ permissions:
mcmmo.commands.skillreset.others.repair: true mcmmo.commands.skillreset.others.repair: true
mcmmo.commands.skillreset.others.salvage: true mcmmo.commands.skillreset.others.salvage: true
mcmmo.commands.skillreset.others.smelting: true mcmmo.commands.skillreset.others.smelting: true
mcmmo.commands.skillreset.others.spears: true
mcmmo.commands.skillreset.others.swords: true mcmmo.commands.skillreset.others.swords: true
mcmmo.commands.skillreset.others.taming: true mcmmo.commands.skillreset.others.taming: true
mcmmo.commands.skillreset.others.unarmed: true mcmmo.commands.skillreset.others.unarmed: true
@@ -1321,6 +1350,8 @@ permissions:
description: Allows access to the skillreset command for salvage for other players description: Allows access to the skillreset command for salvage for other players
mcmmo.commands.skillreset.others.smelting: mcmmo.commands.skillreset.others.smelting:
description: Allows access to the skillreset command for smelting for other players description: Allows access to the skillreset command for smelting for other players
mcmmo.commands.skillreset.others.spears:
description: Allows access to the skillreset command for spears for other players
mcmmo.commands.skillreset.others.swords: mcmmo.commands.skillreset.others.swords:
description: Allows access to the skillreset command for swords for other players description: Allows access to the skillreset command for swords for other players
mcmmo.commands.skillreset.others.taming: mcmmo.commands.skillreset.others.taming:
@@ -1406,7 +1437,7 @@ permissions:
default: false default: false
description: implies access to all mcmmo perks description: implies access to all mcmmo perks
children: children:
mcmmo.perks.all: true mcmmo.perks.all: true
mcmmo.perks.all: mcmmo.perks.all:
default: false default: false
description: implies access to all mcmmo perks description: implies access to all mcmmo perks
@@ -1497,6 +1528,7 @@ permissions:
mcmmo.perks.lucky.repair: true mcmmo.perks.lucky.repair: true
mcmmo.perks.lucky.salvage: true mcmmo.perks.lucky.salvage: true
mcmmo.perks.lucky.smelting: true mcmmo.perks.lucky.smelting: true
mcmmo.perks.lucky.spears: true
mcmmo.perks.lucky.swords: true mcmmo.perks.lucky.swords: true
mcmmo.perks.lucky.taming: true mcmmo.perks.lucky.taming: true
mcmmo.perks.lucky.unarmed: true mcmmo.perks.lucky.unarmed: true
@@ -1539,6 +1571,9 @@ permissions:
mcmmo.perks.lucky.salvage: mcmmo.perks.lucky.salvage:
default: false default: false
description: Gives Salvage abilities & skills a 33.3% better chance to activate. description: Gives Salvage abilities & skills a 33.3% better chance to activate.
mcmmo.perks.lucky.spears:
default: false
description: Gives Spears abilities & skills a 33.3% better chance to activate.
mcmmo.perks.lucky.smelting: mcmmo.perks.lucky.smelting:
default: false default: false
description: Gives Smelting abilities & skills a 33.3% better chance to activate. description: Gives Smelting abilities & skills a 33.3% better chance to activate.
@@ -1600,6 +1635,7 @@ permissions:
mcmmo.perks.xp.150percentboost.mining: true mcmmo.perks.xp.150percentboost.mining: true
mcmmo.perks.xp.150percentboost.repair: true mcmmo.perks.xp.150percentboost.repair: true
mcmmo.perks.xp.150percentboost.smelting: true mcmmo.perks.xp.150percentboost.smelting: true
mcmmo.perks.xp.150percentboost.spears: true
mcmmo.perks.xp.150percentboost.swords: true mcmmo.perks.xp.150percentboost.swords: true
mcmmo.perks.xp.150percentboost.taming: true mcmmo.perks.xp.150percentboost.taming: true
mcmmo.perks.xp.150percentboost.tridents: true mcmmo.perks.xp.150percentboost.tridents: true
@@ -1641,6 +1677,9 @@ permissions:
mcmmo.perks.xp.150percentboost.smelting: mcmmo.perks.xp.150percentboost.smelting:
default: false default: false
description: Multiplies incoming Smelting XP by 2.5 description: Multiplies incoming Smelting XP by 2.5
mcmmo.perks.xp.150percentboost.spears:
default: false
description: Multiplies incoming Spears XP by 2.5
mcmmo.perks.xp.150percentboost.swords: mcmmo.perks.xp.150percentboost.swords:
default: false default: false
description: Multiplies incoming Swords XP by 2.5 description: Multiplies incoming Swords XP by 2.5
@@ -1682,6 +1721,7 @@ permissions:
mcmmo.perks.xp.50percentboost.mining: true mcmmo.perks.xp.50percentboost.mining: true
mcmmo.perks.xp.50percentboost.repair: true mcmmo.perks.xp.50percentboost.repair: true
mcmmo.perks.xp.50percentboost.smelting: true mcmmo.perks.xp.50percentboost.smelting: true
mcmmo.perks.xp.50percentboost.spears: true
mcmmo.perks.xp.50percentboost.swords: true mcmmo.perks.xp.50percentboost.swords: true
mcmmo.perks.xp.50percentboost.taming: true mcmmo.perks.xp.50percentboost.taming: true
mcmmo.perks.xp.50percentboost.tridents: true mcmmo.perks.xp.50percentboost.tridents: true
@@ -1720,6 +1760,9 @@ permissions:
mcmmo.perks.xp.50percentboost.repair: mcmmo.perks.xp.50percentboost.repair:
default: false default: false
description: Multiplies incoming Repair XP by 1.5 description: Multiplies incoming Repair XP by 1.5
mcmmo.perks.xp.50percentboost.spears:
default: false
description: Multiplies incoming Spears XP by 1.5
mcmmo.perks.xp.50percentboost.smelting: mcmmo.perks.xp.50percentboost.smelting:
default: false default: false
description: Multiplies incoming Smelting XP by 1.5 description: Multiplies incoming Smelting XP by 1.5
@@ -1739,87 +1782,91 @@ permissions:
default: false default: false
description: Multiplies incoming Woodcutting XP by 1.5 description: Multiplies incoming Woodcutting XP by 1.5
mcmmo.perks.xp.25percentboost.*: mcmmo.perks.xp.25percentboost.*:
default: false
description: Multiplies incoming XP by 1.25
children:
mcmmo.perks.xp.25percentboost.all: true
mcmmo.perks.xp.25percentboost:
default: false default: false
description: Multiplies incoming XP by 1.25 description: Multiplies incoming XP by 1.25
children: children:
mcmmo.perks.xp.25percentboost.all: true mcmmo.perks.xp.25percentboost.all: true
mcmmo.perks.xp.25percentboost.all: mcmmo.perks.xp.25percentboost:
default: false default: false
description: Multiplies incoming XP by 1.25 description: Multiplies incoming XP by 1.25
children: children:
mcmmo.perks.xp.25percentboost.acrobatics: true mcmmo.perks.xp.25percentboost.all: true
mcmmo.perks.xp.25percentboost.alchemy: true mcmmo.perks.xp.25percentboost.all:
mcmmo.perks.xp.25percentboost.archery: true default: false
mcmmo.perks.xp.25percentboost.axes: true description: Multiplies incoming XP by 1.25
mcmmo.perks.xp.25percentboost.crossbows: true children:
mcmmo.perks.xp.25percentboost.excavation: true mcmmo.perks.xp.25percentboost.acrobatics: true
mcmmo.perks.xp.25percentboost.fishing: true mcmmo.perks.xp.25percentboost.alchemy: true
mcmmo.perks.xp.25percentboost.herbalism: true mcmmo.perks.xp.25percentboost.archery: true
mcmmo.perks.xp.25percentboost.maces: true mcmmo.perks.xp.25percentboost.axes: true
mcmmo.perks.xp.25percentboost.mining: true mcmmo.perks.xp.25percentboost.crossbows: true
mcmmo.perks.xp.25percentboost.repair: true mcmmo.perks.xp.25percentboost.excavation: true
mcmmo.perks.xp.25percentboost.smelting: true mcmmo.perks.xp.25percentboost.fishing: true
mcmmo.perks.xp.25percentboost.swords: true mcmmo.perks.xp.25percentboost.herbalism: true
mcmmo.perks.xp.25percentboost.taming: true mcmmo.perks.xp.25percentboost.maces: true
mcmmo.perks.xp.25percentboost.tridents: true mcmmo.perks.xp.25percentboost.mining: true
mcmmo.perks.xp.25percentboost.unarmed: true mcmmo.perks.xp.25percentboost.repair: true
mcmmo.perks.xp.25percentboost.woodcutting: true mcmmo.perks.xp.25percentboost.smelting: true
mcmmo.perks.xp.25percentboost.acrobatics: mcmmo.perks.xp.25percentboost.spears: true
default: false mcmmo.perks.xp.25percentboost.swords: true
description: Multiplies incoming Acrobatics XP by 1.25 mcmmo.perks.xp.25percentboost.taming: true
mcmmo.perks.xp.25percentboost.alchemy: mcmmo.perks.xp.25percentboost.tridents: true
default: false mcmmo.perks.xp.25percentboost.unarmed: true
description: Multiplies incoming Acrobatics XP by 1.25 mcmmo.perks.xp.25percentboost.woodcutting: true
mcmmo.perks.xp.25percentboost.archery: mcmmo.perks.xp.25percentboost.acrobatics:
default: false default: false
description: Multiplies incoming Archery XP by 1.25 description: Multiplies incoming Acrobatics XP by 1.25
mcmmo.perks.xp.25percentboost.axes: mcmmo.perks.xp.25percentboost.alchemy:
default: false default: false
description: Multiplies incoming Axes XP by 1.25 description: Multiplies incoming Acrobatics XP by 1.25
mcmmo.perks.xp.25percentboost.crossbows: mcmmo.perks.xp.25percentboost.archery:
default: false default: false
description: Multiplies incoming Crossbows XP by 1.25 description: Multiplies incoming Archery XP by 1.25
mcmmo.perks.xp.25percentboost.excavation: mcmmo.perks.xp.25percentboost.axes:
default: false default: false
description: Multiplies incoming Excavation XP by 1.25 description: Multiplies incoming Axes XP by 1.25
mcmmo.perks.xp.25percentboost.fishing: mcmmo.perks.xp.25percentboost.crossbows:
default: false default: false
description: Multiplies incoming Fishing XP by 1.25 description: Multiplies incoming Crossbows XP by 1.25
mcmmo.perks.xp.25percentboost.herbalism: mcmmo.perks.xp.25percentboost.excavation:
default: false default: false
description: Multiplies incoming Herbalism XP by 1.25 description: Multiplies incoming Excavation XP by 1.25
mcmmo.perks.xp.25percentboost.maces: mcmmo.perks.xp.25percentboost.fishing:
default: false default: false
description: Multiplies incoming Maces XP by 1.25 description: Multiplies incoming Fishing XP by 1.25
mcmmo.perks.xp.25percentboost.mining: mcmmo.perks.xp.25percentboost.herbalism:
default: false default: false
description: Multiplies incoming Mining XP by 1.25 description: Multiplies incoming Herbalism XP by 1.25
mcmmo.perks.xp.25percentboost.repair: mcmmo.perks.xp.25percentboost.maces:
default: false default: false
description: Multiplies incoming Repair XP by 1.25 description: Multiplies incoming Maces XP by 1.25
mcmmo.perks.xp.25percentboost.smelting: mcmmo.perks.xp.25percentboost.mining:
default: false default: false
description: Multiplies incoming Smelting XP by 1.25 description: Multiplies incoming Mining XP by 1.25
mcmmo.perks.xp.25percentboost.swords: mcmmo.perks.xp.25percentboost.repair:
default: false default: false
description: Multiplies incoming Swords XP by 1.25 description: Multiplies incoming Repair XP by 1.25
mcmmo.perks.xp.25percentboost.taming: mcmmo.perks.xp.25percentboost.smelting:
default: false default: false
description: Multiplies incoming Taming XP by 1.25 description: Multiplies incoming Smelting XP by 1.25
mcmmo.perks.xp.25percentboost.tridents: mcmmo.perks.xp.25percentboost.spears:
default: false default: false
description: Multiplies incoming Tridents XP by 1.25 description: Multiplies incoming Spears XP by 1.25
mcmmo.perks.xp.25percentboost.unarmed: mcmmo.perks.xp.25percentboost.swords:
default: false default: false
description: Multiplies incoming Unarmed XP by 1.5 description: Multiplies incoming Swords XP by 1.25
mcmmo.perks.xp.25percentboost.woodcutting: mcmmo.perks.xp.25percentboost.taming:
default: false default: false
description: Multiplies incoming Woodcutting XP by 1.25 description: Multiplies incoming Taming XP by 1.25
mcmmo.perks.xp.25percentboost.tridents:
default: false
description: Multiplies incoming Tridents XP by 1.25
mcmmo.perks.xp.25percentboost.unarmed:
default: false
description: Multiplies incoming Unarmed XP by 1.5
mcmmo.perks.xp.25percentboost.woodcutting:
default: false
description: Multiplies incoming Woodcutting XP by 1.25
mcmmo.perks.xp.10percentboost.*: mcmmo.perks.xp.10percentboost.*:
default: false default: false
description: Multiplies incoming XP by 1.1 description: Multiplies incoming XP by 1.1
@@ -1846,6 +1893,7 @@ permissions:
mcmmo.perks.xp.10percentboost.mining: true mcmmo.perks.xp.10percentboost.mining: true
mcmmo.perks.xp.10percentboost.repair: true mcmmo.perks.xp.10percentboost.repair: true
mcmmo.perks.xp.10percentboost.smelting: true mcmmo.perks.xp.10percentboost.smelting: true
mcmmo.perks.xp.10percentboost.spears: true
mcmmo.perks.xp.10percentboost.swords: true mcmmo.perks.xp.10percentboost.swords: true
mcmmo.perks.xp.10percentboost.taming: true mcmmo.perks.xp.10percentboost.taming: true
mcmmo.perks.xp.10percentboost.tridents: true mcmmo.perks.xp.10percentboost.tridents: true
@@ -1884,6 +1932,9 @@ permissions:
mcmmo.perks.xp.10percentboost.repair: mcmmo.perks.xp.10percentboost.repair:
default: false default: false
description: Multiplies incoming Repair XP by 1.1 description: Multiplies incoming Repair XP by 1.1
mcmmo.perks.xp.10percentboost.spears:
default: false
description: Multiplies incoming Spears XP by 1.1
mcmmo.perks.xp.10percentboost.smelting: mcmmo.perks.xp.10percentboost.smelting:
default: false default: false
description: Multiplies incoming Smelting XP by 1.1 description: Multiplies incoming Smelting XP by 1.1
@@ -1928,6 +1979,7 @@ permissions:
mcmmo.perks.xp.customboost.mining: true mcmmo.perks.xp.customboost.mining: true
mcmmo.perks.xp.customboost.repair: true mcmmo.perks.xp.customboost.repair: true
mcmmo.perks.xp.customboost.smelting: true mcmmo.perks.xp.customboost.smelting: true
mcmmo.perks.xp.customboost.spears: true
mcmmo.perks.xp.customboost.swords: true mcmmo.perks.xp.customboost.swords: true
mcmmo.perks.xp.customboost.taming: true mcmmo.perks.xp.customboost.taming: true
mcmmo.perks.xp.customboost.tridents: true mcmmo.perks.xp.customboost.tridents: true
@@ -1966,6 +2018,9 @@ permissions:
mcmmo.perks.xp.customboost.repair: mcmmo.perks.xp.customboost.repair:
default: false default: false
description: Multiplies incoming Repair XP by the boost amount defined in the experience config description: Multiplies incoming Repair XP by the boost amount defined in the experience config
mcmmo.perks.xp.customboost.spears:
default: false
description: Multiplies incoming Smelting XP by the boost amount defined in the experience config
mcmmo.perks.xp.customboost.smelting: mcmmo.perks.xp.customboost.smelting:
default: false default: false
description: Multiplies incoming Smelting XP by the boost amount defined in the experience config description: Multiplies incoming Smelting XP by the boost amount defined in the experience config
@@ -2010,6 +2065,7 @@ permissions:
mcmmo.perks.xp.double.mining: true mcmmo.perks.xp.double.mining: true
mcmmo.perks.xp.double.repair: true mcmmo.perks.xp.double.repair: true
mcmmo.perks.xp.double.smelting: true mcmmo.perks.xp.double.smelting: true
mcmmo.perks.xp.double.spears: true
mcmmo.perks.xp.double.swords: true mcmmo.perks.xp.double.swords: true
mcmmo.perks.xp.double.taming: true mcmmo.perks.xp.double.taming: true
mcmmo.perks.xp.double.tridents: true mcmmo.perks.xp.double.tridents: true
@@ -2048,6 +2104,9 @@ permissions:
mcmmo.perks.xp.double.repair: mcmmo.perks.xp.double.repair:
default: false default: false
description: Doubles incoming Repair XP description: Doubles incoming Repair XP
mcmmo.perks.xp.double.spears:
default: false
description: Doubles incoming Smelting XP
mcmmo.perks.xp.double.smelting: mcmmo.perks.xp.double.smelting:
default: false default: false
description: Doubles incoming Smelting XP description: Doubles incoming Smelting XP
@@ -2092,6 +2151,7 @@ permissions:
mcmmo.perks.xp.quadruple.mining: true mcmmo.perks.xp.quadruple.mining: true
mcmmo.perks.xp.quadruple.repair: true mcmmo.perks.xp.quadruple.repair: true
mcmmo.perks.xp.quadruple.smelting: true mcmmo.perks.xp.quadruple.smelting: true
mcmmo.perks.xp.quadruple.spears: true
mcmmo.perks.xp.quadruple.swords: true mcmmo.perks.xp.quadruple.swords: true
mcmmo.perks.xp.quadruple.taming: true mcmmo.perks.xp.quadruple.taming: true
mcmmo.perks.xp.quadruple.tridents: true mcmmo.perks.xp.quadruple.tridents: true
@@ -2133,6 +2193,9 @@ permissions:
mcmmo.perks.xp.quadruple.smelting: mcmmo.perks.xp.quadruple.smelting:
default: false default: false
description: Quadruples incoming Smelting XP description: Quadruples incoming Smelting XP
mcmmo.perks.xp.quadruple.spears:
default: false
description: Quadruples incoming Spears XP
mcmmo.perks.xp.quadruple.swords: mcmmo.perks.xp.quadruple.swords:
default: false default: false
description: Quadruples incoming Swords XP description: Quadruples incoming Swords XP
@@ -2174,6 +2237,7 @@ permissions:
mcmmo.perks.xp.triple.maces: true mcmmo.perks.xp.triple.maces: true
mcmmo.perks.xp.triple.repair: true mcmmo.perks.xp.triple.repair: true
mcmmo.perks.xp.triple.smelting: true mcmmo.perks.xp.triple.smelting: true
mcmmo.perks.xp.triple.spears: true
mcmmo.perks.xp.triple.swords: true mcmmo.perks.xp.triple.swords: true
mcmmo.perks.xp.triple.taming: true mcmmo.perks.xp.triple.taming: true
mcmmo.perks.xp.triple.tridents: true mcmmo.perks.xp.triple.tridents: true
@@ -2215,6 +2279,9 @@ permissions:
mcmmo.perks.xp.triple.smelting: mcmmo.perks.xp.triple.smelting:
default: false default: false
description: Triples incoming Smelting XP description: Triples incoming Smelting XP
mcmmo.perks.xp.triple.spears:
default: false
description: Triples incoming Spears XP
mcmmo.perks.xp.triple.swords: mcmmo.perks.xp.triple.swords:
default: false default: false
description: Triples incoming Swords XP description: Triples incoming Swords XP
@@ -2257,6 +2324,7 @@ permissions:
mcmmo.skills.salvage: true mcmmo.skills.salvage: true
mcmmo.skills.swords: true mcmmo.skills.swords: true
mcmmo.skills.smelting: true mcmmo.skills.smelting: true
mcmmo.skills.spears: true
mcmmo.skills.taming: true mcmmo.skills.taming: true
mcmmo.skills.unarmed: true mcmmo.skills.unarmed: true
mcmmo.skills.woodcutting: true mcmmo.skills.woodcutting: true
@@ -2322,6 +2390,11 @@ permissions:
children: children:
mcmmo.ability.smelting.all: true mcmmo.ability.smelting.all: true
mcmmo.commands.smelting: true mcmmo.commands.smelting: true
mcmmo.skills.spears:
description: Allows access to the Spears skill
children:
mcmmo.ability.spears.all: true
mcmmo.commands.spears: true
mcmmo.skills.swords: mcmmo.skills.swords:
description: Allows access to the Swords skill description: Allows access to the Swords skill
children: children:

View File

@@ -55,6 +55,9 @@ Repairables:
WOODEN_SWORD: WOODEN_SWORD:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .25 XpMultiplier: .25
WOODEN_SPEAR:
MinimumLevel: 0
XpMultiplier: .25
WOODEN_SHOVEL: WOODEN_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .16 XpMultiplier: .16
@@ -74,6 +77,9 @@ Repairables:
STONE_SWORD: STONE_SWORD:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .25 XpMultiplier: .25
STONE_SPEAR:
MinimumLevel: 0
XpMultiplier: .25
STONE_SHOVEL: STONE_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .16 XpMultiplier: .16
@@ -96,6 +102,12 @@ Repairables:
ItemType: TOOL ItemType: TOOL
ItemMaterialCategory: COPPER ItemMaterialCategory: COPPER
RepairMaterial: COPPER_INGOT RepairMaterial: COPPER_INGOT
COPPER_SPEAR:
MinimumLevel: 0
XpMultiplier: .3
ItemType: TOOL
ItemMaterialCategory: COPPER
RepairMaterial: COPPER_INGOT
COPPER_SHOVEL: COPPER_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .2 XpMultiplier: .2
@@ -152,6 +164,9 @@ Repairables:
IRON_SWORD: IRON_SWORD:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .5 XpMultiplier: .5
IRON_SPEAR:
MinimumLevel: 0
XpMultiplier: .5
IRON_SHOVEL: IRON_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .3 XpMultiplier: .3
@@ -190,6 +205,9 @@ Repairables:
GOLDEN_SWORD: GOLDEN_SWORD:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: 4 XpMultiplier: 4
GOLDEN_SPEAR:
MinimumLevel: 0
XpMultiplier: 4
GOLDEN_SHOVEL: GOLDEN_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: 2.6 XpMultiplier: 2.6
@@ -222,6 +240,9 @@ Repairables:
DIAMOND_SWORD: DIAMOND_SWORD:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .5 XpMultiplier: .5
DIAMOND_SPEAR:
MinimumLevel: 0
XpMultiplier: .5
DIAMOND_SHOVEL: DIAMOND_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .3 XpMultiplier: .3
@@ -255,6 +276,9 @@ Repairables:
NETHERITE_SWORD: NETHERITE_SWORD:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .6 XpMultiplier: .6
NETHERITE_SPEAR:
MinimumLevel: 0
XpMultiplier: .6
NETHERITE_SHOVEL: NETHERITE_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .4 XpMultiplier: .4

View File

@@ -50,6 +50,10 @@ Salvageables:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .25 XpMultiplier: .25
MaximumQuantity: 2 MaximumQuantity: 2
WOODEN_SPEAR:
MinimumLevel: 0
XpMultiplier: .16
MaximumQuantity: 1
WOODEN_SHOVEL: WOODEN_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .16 XpMultiplier: .16
@@ -74,6 +78,10 @@ Salvageables:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .25 XpMultiplier: .25
MaximumQuantity: 2 MaximumQuantity: 2
STONE_SPEAR:
MinimumLevel: 0
XpMultiplier: .16
MaximumQuantity: 1
STONE_SHOVEL: STONE_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .16 XpMultiplier: .16
@@ -98,6 +106,10 @@ Salvageables:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .4 XpMultiplier: .4
MaximumQuantity: 2 MaximumQuantity: 2
COPPER_SPEAR:
MinimumLevel: 0
XpMultiplier: .25
MaximumQuantity: 1
COPPER_SHOVEL: COPPER_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .25 XpMultiplier: .25
@@ -139,6 +151,10 @@ Salvageables:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .5 XpMultiplier: .5
MaximumQuantity: 2 MaximumQuantity: 2
IRON_SPEAR:
MinimumLevel: 0
XpMultiplier: .3
MaximumQuantity: 1
IRON_SHOVEL: IRON_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: .3 XpMultiplier: .3
@@ -186,6 +202,10 @@ Salvageables:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: 4 XpMultiplier: 4
MaximumQuantity: 2 MaximumQuantity: 2
GOLDEN_SPEAR:
MinimumLevel: 0
XpMultiplier: 4
MaximumQuantity: 1
GOLDEN_SHOVEL: GOLDEN_SHOVEL:
MinimumLevel: 0 MinimumLevel: 0
XpMultiplier: 2.6 XpMultiplier: 2.6
@@ -227,6 +247,10 @@ Salvageables:
MinimumLevel: 50 MinimumLevel: 50
XpMultiplier: .5 XpMultiplier: .5
MaximumQuantity: 2 MaximumQuantity: 2
DIAMOND_SPEAR:
MinimumLevel: 50
XpMultiplier: .5
MaximumQuantity: 1
DIAMOND_SHOVEL: DIAMOND_SHOVEL:
MinimumLevel: 50 MinimumLevel: 50
XpMultiplier: .3 XpMultiplier: .3
@@ -268,6 +292,10 @@ Salvageables:
MinimumLevel: 100 MinimumLevel: 100
XpMultiplier: .5 XpMultiplier: .5
MaximumQuantity: 4 MaximumQuantity: 4
NETHERITE_SPEAR:
MinimumLevel: 100
XpMultiplier: .3
MaximumQuantity: 4
NETHERITE_SHOVEL: NETHERITE_SHOVEL:
MinimumLevel: 100 MinimumLevel: 100
XpMultiplier: .3 XpMultiplier: .3

View File

@@ -404,6 +404,72 @@ Smelting:
Rank_6: 750 Rank_6: 750
Rank_7: 850 Rank_7: 850
Rank_8: 1000 Rank_8: 1000
Spears:
SpearsLimitBreak:
Standard:
Rank_1: 10
Rank_2: 20
Rank_3: 30
Rank_4: 40
Rank_5: 50
Rank_6: 60
Rank_7: 70
Rank_8: 80
Rank_9: 90
Rank_10: 100
RetroMode:
Rank_1: 100
Rank_2: 200
Rank_3: 300
Rank_4: 400
Rank_5: 500
Rank_6: 600
Rank_7: 700
Rank_8: 800
Rank_9: 900
Rank_10: 1000
SpearMastery:
Standard:
Rank_1: 5
Rank_2: 15
Rank_3: 30
Rank_4: 45
Rank_5: 60
Rank_6: 75
Rank_7: 90
Rank_8: 100
RetroMode:
Rank_1: 50
Rank_2: 150
Rank_3: 300
Rank_4: 450
Rank_5: 600
Rank_6: 750
Rank_7: 900
Rank_8: 1000
Momentum:
Standard:
Rank_1: 1
Rank_2: 10
Rank_3: 15
Rank_4: 20
Rank_5: 25
Rank_6: 40
Rank_7: 50
Rank_8: 60
Rank_9: 80
Rank_10: 95
RetroMode:
Rank_1: 1
Rank_2: 100
Rank_3: 150
Rank_4: 200
Rank_5: 250
Rank_6: 400
Rank_7: 500
Rank_8: 600
Rank_9: 800
Rank_10: 950
Salvage: Salvage:
ScrapCollector: ScrapCollector:
Standard: Standard:

View File

@@ -24,7 +24,6 @@ import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.Permissions;
import com.gmail.nossr50.util.TransientEntityTracker; import com.gmail.nossr50.util.TransientEntityTracker;
import com.gmail.nossr50.util.blockmeta.ChunkManager; import com.gmail.nossr50.util.blockmeta.ChunkManager;
import com.gmail.nossr50.util.compat.CompatibilityManager;
import com.gmail.nossr50.util.platform.MinecraftGameVersion; import com.gmail.nossr50.util.platform.MinecraftGameVersion;
import com.gmail.nossr50.util.player.NotificationManager; import com.gmail.nossr50.util.player.NotificationManager;
import com.gmail.nossr50.util.player.UserManager; import com.gmail.nossr50.util.player.UserManager;
@@ -85,19 +84,18 @@ public abstract class MMOTestEnvironment {
protected ChunkManager chunkManager; protected ChunkManager chunkManager;
protected MaterialMapStore materialMapStore; protected MaterialMapStore materialMapStore;
protected CompatibilityManager compatibilityManager; protected MinecraftGameVersion minecraftGameVersion;
protected void mockBaseEnvironment(Logger logger) throws InvalidSkillException { protected void mockBaseEnvironment(Logger logger) throws InvalidSkillException {
compatibilityManager = mock(CompatibilityManager.class);
final MinecraftGameVersion minecraftGameVersion = mock(MinecraftGameVersion.class);
when(compatibilityManager.getMinecraftGameVersion()).thenReturn(minecraftGameVersion);
// TODO: We should change minecraftGameVersion to be a passed in parameter instead of always returning true
when(minecraftGameVersion.isAtLeast(anyInt(), anyInt(), anyInt())).thenReturn(true);
mockedMcMMO = mockStatic(mcMMO.class); mockedMcMMO = mockStatic(mcMMO.class);
when(mcMMO.getCompatibilityManager()).thenReturn(compatibilityManager);
mcMMO.p = mock(mcMMO.class); mcMMO.p = mock(mcMMO.class);
when(mcMMO.p.getLogger()).thenReturn(logger); when(mcMMO.p.getLogger()).thenReturn(logger);
// Game version
minecraftGameVersion = mock(MinecraftGameVersion.class);
when(minecraftGameVersion.isAtLeast(anyInt(), anyInt(), anyInt())).thenReturn(true);
when(mcMMO.getMinecraftGameVersion()).thenReturn(minecraftGameVersion);
// place store // place store
chunkManager = mock(ChunkManager.class); chunkManager = mock(ChunkManager.class);
when(mcMMO.getUserBlockTracker()).thenReturn(chunkManager); when(mcMMO.getUserBlockTracker()).thenReturn(chunkManager);

View File

@@ -9,7 +9,6 @@ import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.compat.CompatibilityManager;
import com.gmail.nossr50.util.platform.MinecraftGameVersion; import com.gmail.nossr50.util.platform.MinecraftGameVersion;
import org.bukkit.potion.PotionEffectType; import org.bukkit.potion.PotionEffectType;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@@ -28,11 +27,9 @@ class PotionEffectUtilTest {
mockedStaticMcMMO = mockStatic(mcMMO.class); mockedStaticMcMMO = mockStatic(mcMMO.class);
mcMMO.p = mock(mcMMO.class); mcMMO.p = mock(mcMMO.class);
when(mcMMO.p.getLogger()).thenReturn(logger); when(mcMMO.p.getLogger()).thenReturn(logger);
CompatibilityManager compatibilityManager = mock(CompatibilityManager.class);
MinecraftGameVersion minecraftGameVersion = mock(MinecraftGameVersion.class); MinecraftGameVersion minecraftGameVersion = mock(MinecraftGameVersion.class);
when(compatibilityManager.getMinecraftGameVersion()).thenReturn(minecraftGameVersion);
when(minecraftGameVersion.isAtLeast(1, 20, 5)).thenReturn(false); when(minecraftGameVersion.isAtLeast(1, 20, 5)).thenReturn(false);
when(mcMMO.getCompatibilityManager()).thenReturn(compatibilityManager); when(mcMMO.getMinecraftGameVersion()).thenReturn(minecraftGameVersion);
} }
@AfterEach @AfterEach

View File

@@ -8,7 +8,6 @@ import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.compat.CompatibilityManager;
import com.gmail.nossr50.util.platform.MinecraftGameVersion; import com.gmail.nossr50.util.platform.MinecraftGameVersion;
import org.bukkit.potion.PotionType; import org.bukkit.potion.PotionType;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@@ -23,11 +22,9 @@ class PotionUtilTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
mockedStaticMcMMO = mockStatic(mcMMO.class); mockedStaticMcMMO = mockStatic(mcMMO.class);
CompatibilityManager compatibilityManager = mock(CompatibilityManager.class);
MinecraftGameVersion minecraftGameVersion = mock(MinecraftGameVersion.class); MinecraftGameVersion minecraftGameVersion = mock(MinecraftGameVersion.class);
when(compatibilityManager.getMinecraftGameVersion()).thenReturn(minecraftGameVersion);
when(minecraftGameVersion.isAtLeast(1, 20, 5)).thenReturn(true); when(minecraftGameVersion.isAtLeast(1, 20, 5)).thenReturn(true);
when(mcMMO.getCompatibilityManager()).thenReturn(compatibilityManager); when(mcMMO.getMinecraftGameVersion()).thenReturn(minecraftGameVersion);
} }
@AfterEach @AfterEach

View File

@@ -5,6 +5,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.MinecraftGameVersionFactory;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -96,9 +97,8 @@ class MinecraftGameVersionTest {
bukkit.when(Bukkit::getVersion).thenReturn(serverSoftwareVersion); bukkit.when(Bukkit::getVersion).thenReturn(serverSoftwareVersion);
bukkit.when(Bukkit::getBukkitVersion).thenReturn(gameVersion); bukkit.when(Bukkit::getBukkitVersion).thenReturn(gameVersion);
PlatformManager manager = new PlatformManager(); final MinecraftGameVersion minecraftVersion
Platform platform = manager.getPlatform(); = MinecraftGameVersionFactory.calculateGameVersion(gameVersion);
MinecraftGameVersion minecraftVersion = platform.getGameVersion();
assertEquals(major, minecraftVersion.getMajorVersion().asInt()); assertEquals(major, minecraftVersion.getMajorVersion().asInt());
assertEquals(minor, minecraftVersion.getMinorVersion().asInt()); assertEquals(minor, minecraftVersion.getMinorVersion().asInt());
@@ -125,7 +125,8 @@ class MinecraftGameVersionTest {
Arguments.of("1.14-pre5-SNAPSHOT", 1, 14, 0), Arguments.of("1.14-pre5-SNAPSHOT", 1, 14, 0),
Arguments.of("1.15-R0.1-SNAPSHOT", 1, 15, 0), Arguments.of("1.15-R0.1-SNAPSHOT", 1, 15, 0),
Arguments.of("1.16.5-R0.1-SNAPSHOT", 1, 16, 5), Arguments.of("1.16.5-R0.1-SNAPSHOT", 1, 16, 5),
Arguments.of("1.17-R0.1-SNAPSHOT", 1, 17, 0) Arguments.of("1.17-R0.1-SNAPSHOT", 1, 17, 0),
Arguments.of("1.21.11-106-0d768aa", 1, 21, 11)
); );
} }

View File

@@ -0,0 +1,350 @@
package com.gmail.nossr50.util.skills;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.gmail.nossr50.config.GeneralConfig;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
import com.gmail.nossr50.datatypes.skills.ToolType;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.platform.MinecraftGameVersion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
@TestInstance(Lifecycle.PER_CLASS)
class SkillToolsTest {
private static final @NotNull Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);
private static MockedStatic<mcMMO> mockedMcMMO;
private static MockedStatic<LocaleLoader> mockedLocaleLoader;
private GeneralConfig generalConfig;
@BeforeAll
void setUpAll() {
// Static mcMMO + LocaleLoader mocks
mockedMcMMO = Mockito.mockStatic(mcMMO.class);
mockedLocaleLoader = Mockito.mockStatic(LocaleLoader.class);
// Plugin instance
mcMMO.p = mock(mcMMO.class);
when(mcMMO.p.getLogger()).thenReturn(logger);
// General config
generalConfig = mock(GeneralConfig.class);
when(mcMMO.p.getGeneralConfig()).thenReturn(generalConfig);
when(generalConfig.getLocale()).thenReturn("en_US");
// LocaleLoader just echo key back to keep things simple/deterministic
mockedLocaleLoader.when(() -> LocaleLoader.getString(anyString()))
.thenAnswer(invocation -> invocation.getArgument(0));
}
@AfterAll
void tearDownAll() {
mockedLocaleLoader.close();
mockedMcMMO.close();
}
private SkillTools newSkillToolsForVersion(int major, int minor, int patch) throws Exception {
var mockGameVersion = mock(MinecraftGameVersion.class);
when(mockGameVersion.isAtLeast(major, minor, patch))
.thenReturn(true);
when(mcMMO.getMinecraftGameVersion()).thenReturn(mockGameVersion);
return new SkillTools(mcMMO.p);
}
// ------------------------------------------------------------------------
// NON_CHILD_SKILLS / isChildSkill / CHILD_SKILLS
// ------------------------------------------------------------------------
@Test
void nonChildSkillsShouldContainAllPrimarySkillsExceptSalvageAndSmelting() {
List<PrimarySkillType> expected = Arrays.stream(PrimarySkillType.values())
.filter(t -> t != PrimarySkillType.SALVAGE && t != PrimarySkillType.SMELTING)
.collect(Collectors.toList());
assertThat(SkillTools.NON_CHILD_SKILLS)
.containsExactlyInAnyOrderElementsOf(expected);
}
@Test
void isChildSkillShouldReturnTrueOnlyForSalvageAndSmelting() {
for (PrimarySkillType type : PrimarySkillType.values()) {
boolean isChild = SkillTools.isChildSkill(type);
if (type == PrimarySkillType.SALVAGE || type == PrimarySkillType.SMELTING) {
assertThat(isChild)
.as("%s should be considered a child skill", type)
.isTrue();
} else {
assertThat(isChild)
.as("%s should NOT be considered a child skill", type)
.isFalse();
}
}
}
@Test
void childSkillsListShouldMatchIsChildSkillClassification() throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 11);
List<PrimarySkillType> expectedChildren = Arrays.stream(PrimarySkillType.values())
.filter(SkillTools::isChildSkill)
.collect(Collectors.toList());
assertThat(skillTools.getChildSkills())
.containsExactlyInAnyOrderElementsOf(expectedChildren);
}
// ------------------------------------------------------------------------
// Child skill parents (SALVAGE_PARENTS / SMELTING_PARENTS / getChildSkillParents)
// ------------------------------------------------------------------------
@Test
void childSkillParentsShouldMatchStaticParentLists() throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 11);
assertThat(skillTools.getChildSkillParents(PrimarySkillType.SALVAGE))
.as("SALVAGE parents")
.containsExactlyElementsOf(SkillTools.SALVAGE_PARENTS);
assertThat(skillTools.getChildSkillParents(PrimarySkillType.SMELTING))
.as("SMELTING parents")
.containsExactlyElementsOf(SkillTools.SMELTING_PARENTS);
}
@Test
void getChildSkillParentsShouldThrowForNonChildSkill() throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 11);
assertThatThrownBy(() -> skillTools.getChildSkillParents(PrimarySkillType.MINING))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("is not a child skill");
}
// ------------------------------------------------------------------------
// Super ability ↔ primary skill relationships
// ------------------------------------------------------------------------
@Test
void superAbilityParentMappingShouldMatchDefinedSwitch() throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 11);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.BERSERK))
.isEqualTo(PrimarySkillType.UNARMED);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.GREEN_TERRA))
.isEqualTo(PrimarySkillType.HERBALISM);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.TREE_FELLER))
.isEqualTo(PrimarySkillType.WOODCUTTING);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.SUPER_BREAKER))
.isEqualTo(PrimarySkillType.MINING);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.BLAST_MINING))
.isEqualTo(PrimarySkillType.MINING);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.SKULL_SPLITTER))
.isEqualTo(PrimarySkillType.AXES);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.SERRATED_STRIKES))
.isEqualTo(PrimarySkillType.SWORDS);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.GIGA_DRILL_BREAKER))
.isEqualTo(PrimarySkillType.EXCAVATION);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.SUPER_SHOTGUN))
.isEqualTo(PrimarySkillType.CROSSBOWS);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.TRIDENTS_SUPER_ABILITY))
.isEqualTo(PrimarySkillType.TRIDENTS);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.EXPLOSIVE_SHOT))
.isEqualTo(PrimarySkillType.ARCHERY);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.MACES_SUPER_ABILITY))
.isEqualTo(PrimarySkillType.MACES);
assertThat(skillTools.getPrimarySkillBySuperAbility(SuperAbilityType.SPEARS_SUPER_ABILITY))
.isEqualTo(PrimarySkillType.SPEARS);
}
@Test
void mainActivatedAbilityChildMapShouldOmitBlastMiningAndMapOthersBackToAbility() throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 11);
// All super abilities EXCEPT BLAST_MINING should be discoverable via getSuperAbility()
assertThat(skillTools.getSuperAbility(PrimarySkillType.MINING))
.as("MINING should not expose BLAST_MINING as the 'main' tool-readied ability")
.isEqualTo(SuperAbilityType.SUPER_BREAKER);
assertThat(skillTools.getSuperAbility(PrimarySkillType.UNARMED))
.isEqualTo(SuperAbilityType.BERSERK);
assertThat(skillTools.getSuperAbility(PrimarySkillType.HERBALISM))
.isEqualTo(SuperAbilityType.GREEN_TERRA);
assertThat(skillTools.getSuperAbility(PrimarySkillType.WOODCUTTING))
.isEqualTo(SuperAbilityType.TREE_FELLER);
assertThat(skillTools.getSuperAbility(PrimarySkillType.AXES))
.isEqualTo(SuperAbilityType.SKULL_SPLITTER);
assertThat(skillTools.getSuperAbility(PrimarySkillType.SWORDS))
.isEqualTo(SuperAbilityType.SERRATED_STRIKES);
assertThat(skillTools.getSuperAbility(PrimarySkillType.EXCAVATION))
.isEqualTo(SuperAbilityType.GIGA_DRILL_BREAKER);
assertThat(skillTools.getSuperAbility(PrimarySkillType.CROSSBOWS))
.isEqualTo(SuperAbilityType.SUPER_SHOTGUN);
assertThat(skillTools.getSuperAbility(PrimarySkillType.TRIDENTS))
.isEqualTo(SuperAbilityType.TRIDENTS_SUPER_ABILITY);
assertThat(skillTools.getSuperAbility(PrimarySkillType.ARCHERY))
.isEqualTo(SuperAbilityType.EXPLOSIVE_SHOT);
assertThat(skillTools.getSuperAbility(PrimarySkillType.MACES))
.isEqualTo(SuperAbilityType.MACES_SUPER_ABILITY);
assertThat(skillTools.getSuperAbility(PrimarySkillType.SPEARS))
.isEqualTo(SuperAbilityType.SPEARS_SUPER_ABILITY);
// Skills without a main activated ability should return null
assertThat(skillTools.getSuperAbility(PrimarySkillType.REPAIR)).isNull();
assertThat(skillTools.getSuperAbility(PrimarySkillType.FISHING)).isNull();
}
// ------------------------------------------------------------------------
// Sub-skill → primary-skill mapping (name prefix convention)
// ------------------------------------------------------------------------
@Test
void primarySkillBySubSkillShouldFollowNamePrefixConvention() throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 11);
for (SubSkillType sub : SubSkillType.values()) {
PrimarySkillType parent = skillTools.getPrimarySkillBySubSkill(sub);
assertThat(parent)
.as("SubSkill %s should have a parent PrimarySkillType", sub)
.isNotNull();
String subName = sub.name().toUpperCase(Locale.ENGLISH);
String parentPrefix = parent.name().toUpperCase(Locale.ENGLISH);
assertThat(subName.startsWith(parentPrefix))
.as("SubSkill %s should start with its parent skill name %s", subName, parentPrefix)
.isTrue();
}
}
// ------------------------------------------------------------------------
// primarySkillToolMap
// ------------------------------------------------------------------------
@Test
void primarySkillToolTypeMappingShouldMatchDefinition() throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 11);
assertThat(skillTools.getPrimarySkillToolType(PrimarySkillType.AXES))
.isEqualTo(ToolType.AXE);
assertThat(skillTools.getPrimarySkillToolType(PrimarySkillType.WOODCUTTING))
.isEqualTo(ToolType.AXE);
assertThat(skillTools.getPrimarySkillToolType(PrimarySkillType.UNARMED))
.isEqualTo(ToolType.FISTS);
assertThat(skillTools.getPrimarySkillToolType(PrimarySkillType.SWORDS))
.isEqualTo(ToolType.SWORD);
assertThat(skillTools.getPrimarySkillToolType(PrimarySkillType.EXCAVATION))
.isEqualTo(ToolType.SHOVEL);
assertThat(skillTools.getPrimarySkillToolType(PrimarySkillType.HERBALISM))
.isEqualTo(ToolType.HOE);
assertThat(skillTools.getPrimarySkillToolType(PrimarySkillType.MINING))
.isEqualTo(ToolType.PICKAXE);
// And any skill not explicitly mapped should currently return null
assertThat(skillTools.getPrimarySkillToolType(PrimarySkillType.FISHING)).isNull();
assertThat(skillTools.getPrimarySkillToolType(PrimarySkillType.TAMING)).isNull();
}
// ------------------------------------------------------------------------
// Combat / Gathering / Misc groupings by Minecraft version
// ------------------------------------------------------------------------
@Test
void combatGatheringMiscGroupingsShouldMatchDefinitionForModernSpearsAndMacesVersion()
throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 11);
assertThat(skillTools.getCombatSkills())
.containsExactly(
PrimarySkillType.ARCHERY,
PrimarySkillType.AXES,
PrimarySkillType.CROSSBOWS,
PrimarySkillType.MACES,
PrimarySkillType.SWORDS,
PrimarySkillType.SPEARS,
PrimarySkillType.TAMING,
PrimarySkillType.TRIDENTS,
PrimarySkillType.UNARMED
);
assertThat(skillTools.getGatheringSkills())
.containsExactly(
PrimarySkillType.EXCAVATION,
PrimarySkillType.FISHING,
PrimarySkillType.HERBALISM,
PrimarySkillType.MINING,
PrimarySkillType.WOODCUTTING
);
assertThat(skillTools.getMiscSkills())
.containsExactly(
PrimarySkillType.ACROBATICS,
PrimarySkillType.ALCHEMY,
PrimarySkillType.REPAIR,
PrimarySkillType.SALVAGE,
PrimarySkillType.SMELTING
);
}
@Test
void combatSkillsShouldMatchDefinitionForVersionWithMacesButWithoutSpears() throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 0);
assertThat(skillTools.getCombatSkills())
.containsExactly(
PrimarySkillType.ARCHERY,
PrimarySkillType.AXES,
PrimarySkillType.CROSSBOWS,
PrimarySkillType.MACES,
PrimarySkillType.SWORDS,
PrimarySkillType.TAMING,
PrimarySkillType.TRIDENTS,
PrimarySkillType.UNARMED
);
}
// ------------------------------------------------------------------------
// LOCALIZED_SKILL_NAMES basic sanity (size + uniqueness, not content)
// ------------------------------------------------------------------------
@Test
void localizedSkillNamesShouldContainOneEntryPerPrimarySkillAndBeSorted() throws Exception {
SkillTools skillTools = newSkillToolsForVersion(1, 21, 11);
List<String> names = new ArrayList<>(skillTools.LOCALIZED_SKILL_NAMES);
// One per PrimarySkillType
assertThat(names).hasSize(PrimarySkillType.values().length);
// No duplicates
assertThat(new HashSet<>(names)).hasSize(names.size());
// Sorted ascending
List<String> sorted = new ArrayList<>(names);
Collections.sort(sorted);
assertThat(names).isEqualTo(sorted);
}
}

View File

@@ -1,3 +1,3 @@
nossr50:1:IGNORED:IGNORED:10:2:20:3:4:5:6:7:8:9:10:30:40:50:60:70:80:90:100:IGNORED:11:110:111:222:333:444:555:666:777:IGNORED:12:120:888:IGNORED:HEARTS:13:130:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1111:999:2020:140:14:150:15:1111:2222:3333:160:16:4444: nossr50:1:IGNORED:IGNORED:10:2:20:3:4:5:6:7:8:9:10:30:40:50:60:70:80:90:100:IGNORED:11:110:111:222:333:444:555:666:777:IGNORED:12:120:888:IGNORED:HEARTS:13:130:588fe472-1c82-4c4e-9aa1-7eefccb277e3:1111:999:2020:140:14:150:15:1111:2222:3333:160:16:4444:170:17:5555:
mrfloris:2420:::0:2452:0:1983:1937:1790:3042:1138:3102:2408:3411:0:0:0:0:0:0:0:0::642:0:1617583171:0:1617165043:0:1617583004:1617563189:1616785408::2184:0:0:1617852413:HEARTS:415:0:631e3896-da2a-4077-974b-d047859d76bc:5:1600906906:3030:0:0:0:0:0:0:0:0:0:0: mrfloris:2420:::0:2452:0:1983:1937:1790:3042:1138:3102:2408:3411:0:0:0:0:0:0:0:0::642:0:1617583171:0:1617165043:0:1617583004:1617563189:1616785408::2184:0:0:1617852413:HEARTS:415:0:631e3896-da2a-4077-974b-d047859d76bc:5:1600906906:3030:0:0:0:0:0:0:0:0:0:0:0:0:0:
powerless:0:::0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0::0:0:0:0:0:0:0:0:0::0:0:0:1337:HEARTS:0:0:e0d07db8-f7e8-43c7-9ded-864dfc6f3b7c:5:1600906906:4040:0:0:0:0:0:0:0:0:0:0: powerless:0:::0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0::0:0:0:0:0:0:0:0:0::0:0:0:1337:HEARTS:0:0:e0d07db8-f7e8-43c7-9ded-864dfc6f3b7c:5:1600906906:4040:0:0:0:0:0:0:0:0:0:0:0:0:0: