Compare commits

...

67 Commits

Author SHA1 Message Date
45c4fbe0ea Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-09-05 01:06:37 +00:00
1615ab416c Merge pull request #343 from AtlasMediaGroup/port/1.20.1
1.20.1 port
2023-07-31 21:26:12 -05:00
ed7f8b4b8e Add new developers to the dev list 2023-07-31 06:03:43 +00:00
9149b279a7 Bump dependency versions 2023-07-31 05:06:47 +00:00
243cba689f Bump API version to 1.20 2023-07-31 05:04:36 +00:00
1364b551d8 Merge pull request #340 from AtlasMediaGroup/merge/main/development
Main -> Development Pro Max
2023-07-25 14:29:10 -05:00
8fee6c7f14 Merge pull request #339 from AtlasMediaGroup/merge/development/main
Merge development into main
2023-07-25 13:38:29 -05:00
0559e99fb1 Drop ptero_id column from admin table
Fixes a SQLException thrown when adding admins to an older table schema version.
2023-07-25 18:56:06 +01:00
1251276451 Fix formatting in AdminList 2023-07-25 18:32:59 +01:00
9e584069e6 Ignore interact events from Spectators 2023-07-25 17:43:15 +01:00
11c24cff68 Ignore cancelled interact events 2023-07-25 17:42:32 +01:00
476c87ceb0 Improve Discord bridge sanitization 2023-07-25 17:32:28 +01:00
9bef4581aa Use Bukkit.getScheduler().runTaskAsynchronously to execute the report 2023-07-25 17:08:05 +01:00
794a25ba16 Remove getCommandMap() method in CommandBlocker 2023-07-25 16:40:35 +01:00
b4dd35c4df Stop pinning minor & patch revisions of workflows 2023-07-25 15:13:17 +01:00
ec19b6d398 Make the Discord command parsing more forgiving 2023-07-25 04:13:40 +01:00
b2c636f919 Fix invalid Discord command handling 2023-07-25 03:57:24 +01:00
8876076a9d Fix JDA deprecation issue 2023-07-25 03:47:33 +01:00
80f19f0349 Disable mentions in archival message 2023-07-25 03:47:05 +01:00
011535cf16 Make Discord report filing system asynchronous 2023-07-25 03:45:22 +01:00
eb99dcd3a3 Optimize EssentialsBridge imports 2023-07-25 03:05:19 +01:00
597a464623 Add run/ to .gitignore 2023-07-25 03:05:16 +01:00
08c8393abc Fix ArrayIndexOutOfBounds when a newly added admin leaves the game 2023-07-25 03:05:12 +01:00
ac704614c0 Fix updated plugin versions 2023-07-25 03:05:09 +01:00
04029eb144 Don't try to vanish/unvanish offline players...
(cherry picked from commit a03ecfd6f3248a6b4588db19549d4daf76952742)
2023-07-25 03:05:05 +01:00
00e5403491 Remove SK89Q maven repositories 2023-07-25 03:05:02 +01:00
08115470b0 Fix reactions showing up in Discord bridge & colour codes showing up in bridge 2023-07-25 03:04:56 +01:00
6e140ace7d Fix /ci tab completion throwing exceptions 2023-07-25 00:26:47 +01:00
9ce4dc6f3a Fix CommandSpy colours 2023-07-25 00:21:41 +01:00
f67db2286d Update GitHub actions dependencies 2023-07-25 00:18:26 +01:00
07b3553748 Update Essentials & WorldGuard 2023-07-25 00:16:26 +01:00
bbbccc2b10 Update some dependencies 2023-07-25 00:14:13 +01:00
d471490a94 Merge pull request #338 from AtlasMediaGroup/merge/main/development
Main -> Development
2023-07-24 17:56:29 -05:00
8f6d276bb6 Merge branch 'main' of github.com:AtlasMediaGroup/TotalFreedomMod into merge/main/development 2023-07-24 23:39:09 +01:00
b5178e6761 Merge pull request #336 from AtlasMediaGroup/feat/configurable-item-drops
Add configuration value for player item drops (main)
2023-07-24 17:35:30 -05:00
e11d72b54b Merge branch 'main' into merge/main/development 2023-07-24 23:23:01 +01:00
ee594e7c63 Add configuration value for player item drops 2023-07-23 00:18:23 +01:00
41cf62c8c0 Merge pull request #333 from AtlasMediaGroup/Paldiu-patch-1
Update plugin.yml
2023-07-11 11:49:44 -05:00
268b71f8a3 Update plugin.yml 2023-07-11 11:38:45 -05:00
a84a47980a Merge pull request #331 from AtlasMediaGroup/manual-patch-conflict-1
Update pom.xml
2023-07-11 11:33:47 -05:00
3a502713ea Merge branch 'main' into manual-patch-conflict-1 2023-07-11 09:18:06 -05:00
5dc5e5dcc5 Merge pull request #332 from AtlasMediaGroup/manual-patch-conflict-2
Update plugin.yml
2023-07-11 09:17:54 -05:00
171daf25a4 Update plugin.yml 2023-07-11 09:06:11 -05:00
2fa7b6855b Update pom.xml 2023-07-11 09:05:04 -05:00
7faf719555 Remove UUID from vanished list on removal (#298)
Co-authored-by: Ryan <Wild1145@users.noreply.github.com>
Co-authored-by: Paldiu <pawereus@gmail.com>
2023-07-05 16:10:07 +01:00
3bcf0f5082 Merge pull request #325 from AtlasMediaGroup/dependabot/maven/development/com.sk89q.worldedit-worldedit-bukkit-7.2.15
Bump worldedit-bukkit from 7.2.12 to 7.2.15
2023-07-04 18:49:01 -05:00
c5f24b46d5 Merge pull request #327 from AtlasMediaGroup/ver/1.19.3-main
Update to 1.19.4
2023-07-04 17:37:37 -05:00
3016c57e3e Merge branch 'main' into ver/1.19.3-main 2023-07-04 17:25:10 -05:00
00351f1163 Merge pull request #326 from AtlasMediaGroup/fix/movement-validator
Miscellaneous improvements to the movement validator
2023-07-04 17:24:55 -05:00
40d22fa2e3 Bump worldedit-bukkit from 7.2.12 to 7.2.15
Bumps worldedit-bukkit from 7.2.12 to 7.2.15.

---
updated-dependencies:
- dependency-name: com.sk89q.worldedit:worldedit-bukkit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-01 22:28:55 +00:00
41923b29d7 Merge pull request #301 from AtlasMediaGroup/ver/1.19.3
Update to 1.19.4
2023-07-01 16:28:08 -06:00
0e7a2d9bce Update to 1.19.4 2023-06-20 04:32:45 +01:00
dcebf7bbe7 Use Adventure for kick message 2023-06-17 20:23:54 +01:00
d30e335f57 Disallow custom spawns that are out of bounds 2023-06-17 20:23:24 +01:00
cca95dc3f1 Add dedicated method for checking if a position is out of bounds 2023-06-17 20:23:18 +01:00
42b68011ea Improve distance check 2023-06-17 20:09:22 +01:00
87d7ba19de Remove inventory checks in MovementValidator 2023-06-17 20:06:56 +01:00
16c00e3ed6 Update Scissors API artifact version to 1.19.4 2023-06-11 21:27:27 +01:00
79e7f6904b Merge branch 'development' into ver/1.19.3 2023-04-02 10:21:14 +01:00
1c096b97e3 Merge pull request #314 from AtlasMediaGroup/merge-main-to-dev
Merge main to dev
2023-03-31 21:29:35 -05:00
cc48f93556 Merge branch 'main' into development 2023-03-31 21:15:34 -05:00
dd373fc9aa Merge pull request #307 from AtlasMediaGroup/RELEASE-2022.06.1
Release 2022.06.1
2023-03-31 18:25:42 -05:00
41331e719d Updates version to 2022.06.1 2023-03-08 19:58:14 -07:00
72c83ba84a Merge pull request #306 from AtlasMediaGroup/critical-exploit-fix
Patches critical exploit in the command blocker
2023-03-08 19:53:51 -07:00
3deaaafb88 Patches critical exploit 2023-03-08 19:52:30 -07:00
520bd97176 Merge branch 'development' into ver/1.19.3 2023-03-04 05:14:30 +00:00
654f5900ba Update to 1.19.3 2023-02-20 17:44:57 +00:00
29 changed files with 319 additions and 350 deletions

View File

@ -21,11 +21,11 @@ jobs:
steps: steps:
# Checkout the repository to the GitHub Actions runner # Checkout the repository to the GitHub Actions runner
- name: Checkout code - name: Checkout code
uses: actions/checkout@v3 uses: actions/checkout@v4
# Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis
- name: Run Codacy Analysis CLI - name: Run Codacy Analysis CLI
uses: codacy/codacy-analysis-cli-action@v4.2.0 uses: codacy/codacy-analysis-cli-action@v4
with: with:
# Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository # Check https://github.com/codacy/codacy-analysis-cli#project-token to get your project token from your Codacy repository
# You can also omit the token and run the tools that support default configurations # You can also omit the token and run the tools that support default configurations

View File

@ -35,10 +35,10 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Setup Java JDK - name: Setup Java JDK
uses: actions/setup-java@v3.9.0 uses: actions/setup-java@v3
with: with:
# The Java version to make available on the path. Takes a whole or semver Java version, or 1.x syntax (e.g. 1.8 => Java 8.x). Early access versions can be specified in the form of e.g. 14-ea, 14.0.0-ea, or 14.0.0-ea.28 # The Java version to make available on the path. Takes a whole or semver Java version, or 1.x syntax (e.g. 1.8 => Java 8.x). Early access versions can be specified in the form of e.g. 14-ea, 14.0.0-ea, or 14.0.0-ea.28
java-version: 17 java-version: 17

View File

@ -9,11 +9,11 @@ jobs:
steps: steps:
# Checkout the code # Checkout the code
- uses: actions/checkout@v3 - uses: actions/checkout@v4
# Java 16 Builds # Java 16 Builds
- name: Set up JDK 17 - name: Set up JDK 17
uses: actions/setup-java@v3.9.0 uses: actions/setup-java@v3
with: with:
java-version: 17 java-version: 17
distribution: 'adopt' distribution: 'adopt'

3
.gitignore vendored
View File

@ -39,3 +39,6 @@ manifest.mf
ehthumbs.db ehthumbs.db
Thumbs.db Thumbs.db
.idea/inspectionProfiles/Project_Default.xml .idea/inspectionProfiles/Project_Default.xml
# Common working directory
run/

46
pom.xml
View File

@ -5,7 +5,7 @@
<groupId>me.totalfreedom</groupId> <groupId>me.totalfreedom</groupId>
<artifactId>TotalFreedomMod</artifactId> <artifactId>TotalFreedomMod</artifactId>
<version>2022.06</version> <version>2022.06.1</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
@ -65,11 +65,6 @@
<url>https://maven.elmakers.com/repository/</url> <url>https://maven.elmakers.com/repository/</url>
</repository> </repository>
<repository>
<id>sk89q-snapshots</id>
<url>https://maven.sk89q.com/artifactory/repo</url>
</repository>
<repository> <repository>
<id>dv8tion</id> <id>dv8tion</id>
<name>m2-dv8tion</name> <name>m2-dv8tion</name>
@ -91,11 +86,6 @@
<url>https://repo.dmulloy2.net/nexus/repository/public/</url> <url>https://repo.dmulloy2.net/nexus/repository/public/</url>
</repository> </repository>
<repository>
<id>sk89q-repo</id>
<url>https://maven.sk89q.com/repo/</url>
</repository>
<repository> <repository>
<id>esentialsx-repo</id> <id>esentialsx-repo</id>
<url>https://repo.essentialsx.net/releases/</url> <url>https://repo.essentialsx.net/releases/</url>
@ -108,28 +98,28 @@
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
<version>2.11.0</version> <version>2.13.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
<version>3.12.0</version> <version>3.13.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.bstats</groupId> <groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId> <artifactId>bstats-bukkit</artifactId>
<version>3.0.0</version> <version>3.0.2</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>me.totalfreedom.scissors</groupId> <groupId>me.totalfreedom.scissors</groupId>
<artifactId>scissors-api</artifactId> <artifactId>Scissors-API</artifactId>
<version>1.17.1-R0.1-SNAPSHOT</version> <version>1.19.4-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -150,35 +140,35 @@
<dependency> <dependency>
<groupId>com.sk89q.worldedit</groupId> <groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-bukkit</artifactId> <artifactId>worldedit-bukkit</artifactId>
<version>7.2.12</version> <version>7.2.15</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.dv8tion</groupId> <groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId> <artifactId>JDA</artifactId>
<version>4.4.0_352</version> <version>4.4.1_353</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.coreprotect</groupId> <groupId>net.coreprotect</groupId>
<artifactId>coreprotect</artifactId> <artifactId>coreprotect</artifactId>
<version>21.2</version> <version>21.3</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.sk89q.worldguard</groupId> <groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-bukkit</artifactId> <artifactId>worldguard-bukkit</artifactId>
<version>7.0.7</version> <version>7.0.8</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.papermc</groupId> <groupId>io.papermc</groupId>
<artifactId>paperlib</artifactId> <artifactId>paperlib</artifactId>
<version>1.0.7</version> <version>1.0.8</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
@ -192,7 +182,7 @@
<dependency> <dependency>
<groupId>net.essentialsx</groupId> <groupId>net.essentialsx</groupId>
<artifactId>EssentialsX</artifactId> <artifactId>EssentialsX</artifactId>
<version>2.19.6</version> <version>2.20.0</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -213,21 +203,21 @@
<dependency> <dependency>
<groupId>org.jetbrains</groupId> <groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId> <artifactId>annotations</artifactId>
<version>23.0.0</version> <version>24.0.1</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId> <artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.2</version> <version>3.3.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>
<version>5.9.0</version> <version>5.10.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
@ -259,7 +249,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version> <version>3.11.0</version>
<configuration> <configuration>
<outputFileName>TotalFreedomMod.jar</outputFileName> <outputFileName>TotalFreedomMod.jar</outputFileName>
<compilerVersion>17</compilerVersion> <compilerVersion>17</compilerVersion>
@ -363,7 +353,7 @@
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId> <artifactId>buildnumber-maven-plugin</artifactId>
<version>3.0.0</version> <version>3.2.0</version>
<executions> <executions>
<execution> <execution>
<phase>generate-resources</phase> <phase>generate-resources</phase>
@ -388,7 +378,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version> <version>3.4.1</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>

View File

@ -46,8 +46,13 @@ public class ChatManager extends FreedomService
private void handleChatEvent(AsyncPlayerChatEvent event) private void handleChatEvent(AsyncPlayerChatEvent event)
{ {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
String message = event.getMessage().trim(); String originalMessage = event.getMessage();
if (plugin.mu.onPlayerChat(player) || plugin.sh.handlePlayerChat(player, originalMessage))
{
event.setCancelled(true);
return;
}
String message = originalMessage.trim();
// Format colors and strip &k // Format colors and strip &k
message = FUtil.colorize(message); message = FUtil.colorize(message);
message = message.replaceAll(ChatColor.MAGIC.toString(), "&k"); message = message.replaceAll(ChatColor.MAGIC.toString(), "&k");
@ -82,6 +87,8 @@ public class ChatManager extends FreedomService
return; return;
} }
plugin.dc.onPlayerChat(player, ChatColor.stripColor(message));
// Check for 4chan trigger // Check for 4chan trigger
if (ConfigEntry.FOURCHAN_ENABLED.getBoolean()) if (ConfigEntry.FOURCHAN_ENABLED.getBoolean())
{ {

View File

@ -1,7 +1,7 @@
package me.totalfreedom.totalfreedommod; package me.totalfreedom.totalfreedommod;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent;
@ -23,7 +23,7 @@ public class CommandSpy extends FreedomService
{ {
server.getOnlinePlayers().stream().filter(player -> plugin.al.isAdmin(player) server.getOnlinePlayers().stream().filter(player -> plugin.al.isAdmin(player)
&& plugin.al.getAdmin(player).getCommandSpy() && player != event.getPlayer()).forEach(player -> && plugin.al.getAdmin(player).getCommandSpy() && player != event.getPlayer()).forEach(player ->
player.sendMessage(Component.text(event.getPlayer().getName()).append(Component.text(": ")) player.sendMessage(Component.text(event.getPlayer().getName(), NamedTextColor.GRAY).append(Component.text(": ", NamedTextColor.GRAY))
.append(Component.text(event.getMessage())))); .append(Component.text(event.getMessage(), NamedTextColor.GRAY))));
} }
} }

View File

@ -1,25 +1,17 @@
package me.totalfreedom.totalfreedommod; package me.totalfreedom.totalfreedommod;
import com.google.common.collect.Multimap;
import io.papermc.lib.PaperLib; import io.papermc.lib.PaperLib;
import java.util.Collection; import net.kyori.adventure.text.Component;
import java.util.Map; import net.kyori.adventure.text.format.NamedTextColor;
import java.util.Objects;
import org.bukkit.ChatColor;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeModifier;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerItemHeldEvent;
import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.ItemStack; import org.spigotmc.event.player.PlayerSpawnLocationEvent;
import org.bukkit.inventory.meta.ItemMeta;
public class MovementValidator extends FreedomService public class MovementValidator extends FreedomService
{ {
@ -41,67 +33,36 @@ public class MovementValidator extends FreedomService
public void onPlayerTeleport(PlayerTeleportEvent event) public void onPlayerTeleport(PlayerTeleportEvent event)
{ {
// Check absolute value to account for negatives // Check absolute value to account for negatives
if (Math.abs(Objects.requireNonNull(event.getTo()).getX()) >= MAX_XYZ_COORD || Math.abs(event.getTo().getZ()) >= MAX_XYZ_COORD || Math.abs(event.getTo().getY()) >= MAX_XYZ_COORD) if (isOutOfBounds(event.getTo()))
{ {
event.setCancelled(true); // illegal position, cancel it event.setCancelled(true); // illegal position, cancel it
} }
} }
private boolean isOutOfBounds(final Location position)
{
return Math.abs(position.getX()) >= MAX_XYZ_COORD || Math.abs(position.getY()) >= MAX_XYZ_COORD || Math.abs(position.getZ()) >= MAX_XYZ_COORD;
}
@EventHandler(priority = EventPriority.HIGH) @EventHandler(priority = EventPriority.HIGH)
public void onPlayerMove(PlayerMoveEvent event) public void onPlayerMove(PlayerMoveEvent event)
{ {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
Location from = event.getFrom(); Location from = event.getFrom();
Location to = event.getTo(); Location to = event.getTo();
assert to != null; double distance = from.distanceSquared(to);
if (to.getX() >= from.getX() + MAX_DISTANCE_TRAVELED || to.getY() >= from.getY() + MAX_DISTANCE_TRAVELED || to.getZ() >= from.getZ() + MAX_DISTANCE_TRAVELED)
if (distance >= MAX_DISTANCE_TRAVELED)
{ {
event.setCancelled(true); event.setCancelled(true);
player.kickPlayer(ChatColor.RED + "You were moving too quickly!"); player.kick(Component.text("You were moving too quickly!", NamedTextColor.RED));
} }
// Check absolute value to account for negatives // Check absolute value to account for negatives
if (Math.abs(event.getTo().getX()) >= MAX_XYZ_COORD || Math.abs(event.getTo().getZ()) >= MAX_XYZ_COORD || Math.abs(event.getTo().getY()) >= MAX_XYZ_COORD) if (isOutOfBounds(event.getTo()))
{ {
event.setCancelled(true); event.setCancelled(true);
PaperLib.teleportAsync(player, player.getWorld().getSpawnLocation()); PaperLib.teleportAsync(player, player.getWorld().getSpawnLocation());
} }
if (exploitItem(event.getPlayer().getInventory().getHelmet()))
{
event.getPlayer().getInventory().setHelmet(new ItemStack(Material.AIR));
event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your helmet slot.");
event.setCancelled(true);
}
if (exploitItem(event.getPlayer().getInventory().getBoots()))
{
event.getPlayer().getInventory().setBoots(new ItemStack(Material.AIR));
event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your boots slot.");
event.setCancelled(true);
}
if (exploitItem(event.getPlayer().getInventory().getLeggings()))
{
event.getPlayer().getInventory().setLeggings(new ItemStack(Material.AIR));
event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your leggings slot.");
event.setCancelled(true);
}
if (exploitItem(event.getPlayer().getInventory().getChestplate()))
{
event.getPlayer().getInventory().setChestplate(new ItemStack(Material.AIR));
event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your chestplate slot.");
event.setCancelled(true);
}
if (exploitItem(event.getPlayer().getInventory().getItemInMainHand()))
{
event.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.AIR));
event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your hand.");
event.setCancelled(true);
}
if (exploitItem(event.getPlayer().getInventory().getItemInOffHand()))
{
event.getPlayer().getInventory().setItemInOffHand(new ItemStack(Material.AIR));
event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your offhand.");
event.setCancelled(true);
}
} }
@EventHandler(priority = EventPriority.HIGH) @EventHandler(priority = EventPriority.HIGH)
@ -110,72 +71,28 @@ public class MovementValidator extends FreedomService
final Player player = event.getPlayer(); final Player player = event.getPlayer();
// Validate position // Validate position
if (Math.abs(player.getLocation().getX()) >= MAX_XYZ_COORD || Math.abs(player.getLocation().getZ()) >= MAX_XYZ_COORD || Math.abs(player.getLocation().getY()) >= MAX_XYZ_COORD) if (isOutOfBounds(player.getLocation()))
{ {
PaperLib.teleportAsync(player, player.getWorld().getSpawnLocation()); // Illegal position, teleport to spawn PaperLib.teleportAsync(player, player.getWorld().getSpawnLocation()); // Illegal position, teleport to spawn
} }
} }
@EventHandler @EventHandler(priority = EventPriority.HIGH)
public void onPlayerHoldItem(PlayerItemHeldEvent event) public void onPlayerSpawn(PlayerSpawnLocationEvent event)
{ {
if (exploitItem(event.getPlayer().getInventory().getItemInMainHand())) final Location playerSpawn = event.getSpawnLocation();
{ final Location worldSpawn = event.getPlayer().getWorld().getSpawnLocation();
event.getPlayer().getInventory().setItemInMainHand(new ItemStack(Material.AIR));
event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your hand.");
}
if (exploitItem(event.getPlayer().getInventory().getItemInOffHand()))
{
event.getPlayer().getInventory().setItemInOffHand(new ItemStack(Material.AIR));
event.getPlayer().sendMessage(ChatColor.RED + "An item with both negative infinity and positive infinity attributes was cleared from your offhand.");
}
}
private Boolean exploitItem(ItemStack item) // If the player's spawn is equal to the world's spawn, there is no need to check.
{ // This will also prevent any possible feedback loops pertaining to setting an out of bounds world spawn to the same world spawn.
if (item == null) if (playerSpawn == worldSpawn)
{ {
return false; return;
} }
ItemMeta meta = item.getItemMeta(); if (isOutOfBounds(worldSpawn))
if (meta != null)
{ {
Multimap<Attribute, AttributeModifier> attributes = meta.getAttributeModifiers(); event.setSpawnLocation(worldSpawn);
if (attributes != null)
{
Map<Attribute, Collection<AttributeModifier>> attrMap = attributes.asMap();
// For every attribute...
for (Attribute attr : attributes.keySet())
{
// Default values
boolean posInf = false;
boolean negInf = false;
// For every AttributeModifier...
for (AttributeModifier modifier : attrMap.get(attr))
{
// Are they ∞ or -∞?
if (modifier.getAmount() == Double.POSITIVE_INFINITY)
{
posInf = true;
}
else if (modifier.getAmount() == Double.NEGATIVE_INFINITY)
{
negInf = true;
}
}
// Are both values set as true?
if (posInf && negInf)
{
return true;
}
}
}
} }
return false;
} }
} }

View File

@ -3,7 +3,6 @@ package me.totalfreedom.totalfreedommod;
import me.totalfreedom.totalfreedommod.config.ConfigEntry; import me.totalfreedom.totalfreedommod.config.ConfigEntry;
import me.totalfreedom.totalfreedommod.player.FPlayer; import me.totalfreedom.totalfreedommod.player.FPlayer;
import me.totalfreedom.totalfreedommod.util.FLog; import me.totalfreedom.totalfreedommod.util.FLog;
import me.totalfreedom.totalfreedommod.util.FSync;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -11,7 +10,6 @@ import org.bukkit.command.Command;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent;
public class Muter extends FreedomService public class Muter extends FreedomService
@ -26,26 +24,23 @@ public class Muter extends FreedomService
{ {
} }
@EventHandler(priority = EventPriority.HIGHEST) public boolean onPlayerChat(Player player)
public void onAsyncPlayerChatEvent(AsyncPlayerChatEvent event)
{ {
Player player = event.getPlayer();
FPlayer fPlayer = plugin.pl.getPlayerSync(player); FPlayer fPlayer = plugin.pl.getPlayerSync(player);
if (!fPlayer.isMuted()) if (!fPlayer.isMuted())
{ {
return; return false;
} }
if (plugin.al.isAdminSync(player)) if (plugin.al.isAdminSync(player))
{ {
fPlayer.setMuted(false); fPlayer.setMuted(false);
return; return false;
} }
player.sendMessage(Component.text("You are muted.", NamedTextColor.RED)); player.sendMessage(Component.text("You are muted.", NamedTextColor.RED));
event.setCancelled(true); return true;
} }
@EventHandler(priority = EventPriority.LOW) @EventHandler(priority = EventPriority.LOW)

View File

@ -66,8 +66,13 @@ public class ActivityLogEntry implements IConfig
public void addLogin() public void addLogin()
{ {
Date currentTime = Date.from(Instant.now()); this.addLogin(System.currentTimeMillis());
timestamps.add("Login: " + FUtil.dateToString(currentTime)); }
public void addLogin(final long timestamp)
{
Date time = Date.from(Instant.ofEpochMilli(timestamp));
timestamps.add("Login: " + FUtil.dateToString(time));
} }
public void addLogout() public void addLogout()

View File

@ -156,7 +156,7 @@ public class Admin
} }
// Ensure admins don't have admin functionality when removed (FS-222) // Ensure admins don't have admin functionality when removed (FS-222)
AdminList.vanished.remove(getName()); AdminList.vanished.remove(getUuid());
if (plugin.esb != null) if (plugin.esb != null)
{ {

View File

@ -194,6 +194,15 @@ public class AdminList extends FreedomService
// Save admin // Save admin
plugin.sql.addAdmin(admin); plugin.sql.addAdmin(admin);
// Add login time
UUID uuid = admin.getUuid();
Player player = Bukkit.getPlayer(uuid);
if (player != null)
{
plugin.acl.getActivityLog(player).addLogin(player.getLastLogin());
}
return true; return true;
} }
@ -265,7 +274,8 @@ public class AdminList extends FreedomService
ResultSet currentSave = plugin.sql.getAdminByUuid(admin.getUuid()); ResultSet currentSave = plugin.sql.getAdminByUuid(admin.getUuid());
for (Map.Entry<String, Object> entry : admin.toSQLStorable().entrySet()) for (Map.Entry<String, Object> entry : admin.toSQLStorable().entrySet())
{ {
Object storedValue = plugin.sql.getValue(currentSave, entry.getKey(), entry.getValue()); if (storedValue != null && !storedValue.equals(entry.getValue()) || storedValue == null && entry.getValue() != null || entry.getValue() == null) Object storedValue = plugin.sql.getValue(currentSave, entry.getKey(), entry.getValue());
if (storedValue != null && !storedValue.equals(entry.getValue()) || storedValue == null && entry.getValue() != null || entry.getValue() == null)
{ {
plugin.sql.setAdminValue(admin, entry.getKey(), entry.getValue()); plugin.sql.setAdminValue(admin, entry.getKey(), entry.getValue());
} }

View File

@ -4,8 +4,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
/* TODO This will have to be changed from com.github.atlasmediagroup.scissors to me.totalfreedom.scissors when we migrate to 1.19 */ import me.totalfreedom.scissors.event.block.MasterBlockFireEvent;
import com.github.atlasmediagroup.scissors.event.block.MasterBlockFireEvent;
import io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent; import io.papermc.paper.event.player.PlayerSignCommandPreprocessEvent;
import me.totalfreedom.totalfreedommod.FreedomService; import me.totalfreedom.totalfreedommod.FreedomService;
import me.totalfreedom.totalfreedommod.config.ConfigEntry; import me.totalfreedom.totalfreedommod.config.ConfigEntry;
@ -173,7 +172,7 @@ public class EventBlocker extends FreedomService
@EventHandler(priority = EventPriority.NORMAL) @EventHandler(priority = EventPriority.NORMAL)
public void onPlayerDropItem(PlayerDropItemEvent event) public void onPlayerDropItem(PlayerDropItemEvent event)
{ {
if (!plugin.al.isAdmin(event.getPlayer())) if (!plugin.al.isAdmin(event.getPlayer()) && !ConfigEntry.ALLOW_ITEM_DROPS.getBoolean())
{ {
event.setCancelled(true); event.setCancelled(true);
} }

View File

@ -4,6 +4,7 @@ import me.totalfreedom.totalfreedommod.FreedomService;
import me.totalfreedom.totalfreedommod.config.ConfigEntry; import me.totalfreedom.totalfreedommod.config.ConfigEntry;
import me.totalfreedom.totalfreedommod.util.Groups; import me.totalfreedom.totalfreedommod.util.Groups;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
@ -26,7 +27,7 @@ public class InteractBlocker extends FreedomService
{ {
} }
@EventHandler(priority = EventPriority.HIGH) @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEvent event) public void onPlayerInteract(PlayerInteractEvent event)
{ {
switch (event.getAction()) switch (event.getAction())
@ -69,6 +70,10 @@ public class InteractBlocker extends FreedomService
private void handleRightClick(PlayerInteractEvent event) private void handleRightClick(PlayerInteractEvent event)
{ {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
if (player.getGameMode().equals(GameMode.SPECTATOR)) {
return;
}
final Block clickedBlock = event.getClickedBlock(); final Block clickedBlock = event.getClickedBlock();
if (clickedBlock != null && clickedBlock.getType() == Material.RESPAWN_ANCHOR && !ConfigEntry.ALLOW_RESPAWN_ANCHORS.getBoolean()) if (clickedBlock != null && clickedBlock.getType() == Material.RESPAWN_ANCHOR && !ConfigEntry.ALLOW_RESPAWN_ANCHORS.getBoolean())

View File

@ -24,30 +24,12 @@ import org.bukkit.plugin.SimplePluginManager;
public class CommandBlocker extends FreedomService public class CommandBlocker extends FreedomService
{ {
private final Pattern whitespacePattern = Pattern.compile("^/?( +)(.*)?");
private final Pattern flagPattern = Pattern.compile("(:([0-9]){5,})"); private final Pattern flagPattern = Pattern.compile("(:([0-9]){5,})");
// //
private final Map<String, CommandBlockerEntry> entryList = Maps.newHashMap(); private final Map<String, CommandBlockerEntry> entryList = Maps.newHashMap();
private final List<String> unknownCommands = Lists.newArrayList(); private final List<String> unknownCommands = Lists.newArrayList();
public static CommandMap getCommandMap()
{
try
{
SimplePluginManager simplePluginManager = (SimplePluginManager)Bukkit.getServer().getPluginManager();
Field commandMapField = SimplePluginManager.class.getDeclaredField("commandMap");
commandMapField.setAccessible(true);
return (SimpleCommandMap)commandMapField.get(simplePluginManager);
}
catch (NoSuchFieldException | IllegalArgumentException | IllegalAccessException e)
{
FLog.severe("Failed to get command map field (" + e.getMessage() + ")");
}
return null;
}
@Override @Override
public void onStart() public void onStart()
{ {
@ -65,7 +47,7 @@ public class CommandBlocker extends FreedomService
entryList.clear(); entryList.clear();
unknownCommands.clear(); unknownCommands.clear();
final CommandMap commandMap = getCommandMap(); final CommandMap commandMap = Bukkit.getCommandMap();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<String> blockedCommands = (List<String>)ConfigEntry.BLOCKED_COMMANDS.getList(); List<String> blockedCommands = (List<String>)ConfigEntry.BLOCKED_COMMANDS.getList();
@ -156,6 +138,14 @@ public class CommandBlocker extends FreedomService
// Format // Format
command = command.toLowerCase().trim(); command = command.toLowerCase().trim();
// Whitespaces
Matcher whitespaceMatcher = whitespacePattern.matcher(command);
if (whitespaceMatcher.matches() && whitespaceMatcher.groupCount() == 2)
{
command = whitespaceMatcher.group(2);
}
command = command.startsWith("/") ? command.substring(1) : command; command = command.startsWith("/") ? command.substring(1) : command;
// Check for plugin specific commands // Check for plugin specific commands

View File

@ -6,7 +6,6 @@ import me.totalfreedom.totalfreedommod.FreedomService;
import me.totalfreedom.totalfreedommod.player.FPlayer; import me.totalfreedom.totalfreedommod.player.FPlayer;
import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.rank.Rank;
import me.totalfreedom.totalfreedommod.util.FLog; import me.totalfreedom.totalfreedommod.util.FLog;
import me.totalfreedom.totalfreedommod.util.FUtil;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -14,7 +13,6 @@ import org.bukkit.event.EventPriority;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.InventoryHolder;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
@ -129,7 +127,7 @@ public class EssentialsBridge extends FreedomService
try try
{ {
User user = getEssentialsUser(username); User user = getEssentialsUser(username);
if (user != null) if (user != null && user.getBase().isOnline())
{ {
user.setVanished(vanished); user.setVanished(vanished);
} }
@ -210,4 +208,4 @@ public class EssentialsBridge extends FreedomService
return ess != null && ess.isEnabled(); return ess != null && ess.isEnabled();
} }
} }

View File

@ -1,6 +1,8 @@
package me.totalfreedom.totalfreedommod.command; package me.totalfreedom.totalfreedommod.command;
import me.totalfreedom.totalfreedommod.rank.Rank; import me.totalfreedom.totalfreedommod.rank.Rank;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.bukkit.ChatColor; import org.bukkit.ChatColor;
@ -8,11 +10,28 @@ import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
@CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME, blockHostConsole = true) @CommandPermissions(level = Rank.OP, source = SourceType.ONLY_IN_GAME, blockHostConsole = true)
@CommandParameters(description = "Report a player for all admins to see.", usage = "/<command> <player> <reason>") @CommandParameters(description = "Report a player for all admins to see.", usage = "/<command> <player> <reason>")
public class Command_report extends FreedomCommand public class Command_report extends FreedomCommand
{ {
private void handleLog(final @Nullable Boolean value, final @Nullable Throwable ex, final CommandSender sender)
{
if (ex != null)
{
sender.sendMessage(Component.text("An error occurred while attempting to log your previously filed report to a Discord channel.", NamedTextColor.RED));
ex.printStackTrace();
return;
}
if (Boolean.FALSE.equals(value))
{
return;
}
sender.sendMessage(Component.text("The report you previously filed has been successfully logged to a Discord channel. Please note that spamming reports is not allowed, and you will be sanctioned if you are found to be doing it.", NamedTextColor.GRAY));
}
@Override @Override
public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole)
@ -50,18 +69,16 @@ public class Command_report extends FreedomCommand
} }
String report = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " "); String report = StringUtils.join(ArrayUtils.subarray(args, 1, args.length), " ");
plugin.cm.reportAction(playerSender, (player == null) ? offlinePlayer.getName() : player.getName(), report); String reportedUsername = (player == null) ? offlinePlayer.getName() : player.getName();
plugin.cm.reportAction(playerSender, reportedUsername, report);
boolean logged = false; msg(ChatColor.GREEN + "Thank you, your report is being processed.");
if (plugin.dc.enabled) if (plugin.dc.enabled)
{ {
logged = (player == null) ? plugin.dc.sendReportOffline(playerSender, offlinePlayer, report) : plugin.dc.sendReport(playerSender, player, report); plugin.dc.sendReport(playerSender.getName(), reportedUsername, report).whenCompleteAsync((logged, ex) -> handleLog(logged, ex, sender));
} }
msg(ChatColor.GREEN + "Thank you, your report has been successfully logged."
+ (logged ? ChatColor.RED + "\nNote: This report has been logged to a discord channel, as with any report system, spamming reports can lead to you getting banned." : ""));
return true; return true;
} }
} }

View File

@ -19,9 +19,9 @@ public class Command_toggle extends FreedomCommand
private final List<String> toggles = Arrays.asList( private final List<String> toggles = Arrays.asList(
"waterplace", "fireplace", "lavaplace", "fluidspread", "lavadmg", "firespread", "frostwalk", "waterplace", "fireplace", "lavaplace", "fluidspread", "lavadmg", "firespread", "frostwalk",
"firework", "prelog", "lockdown", "petprotect", "entitywipe", "nonuke [range] [count]", "firework", "prelog", "lockdown", "petprotect", "entitywipe", "nonuke [range] [count]",
"explosives [radius]", "unsafeenchs", "bells", "armorstands", "masterblocks", "books", "grindstones", "explosives [radius]", "unsafeenchs", "bells", "armorstands", "masterblocks", "item_drops",
"jukeboxes", "spawners", "4chan", "beehives", "respawnanchors", "autotp", "autoclear", "minecarts", "mp44", "grindstones", "jukeboxes", "spawners", "4chan", "beehives", "respawnanchors", "autotp", "autoclear", "minecarts",
"landmines", "tossmob", "gravity"); "mp44", "landmines", "tossmob", "gravity");
@Override @Override
public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole) public boolean run(CommandSender sender, Player playerSender, Command cmd, String commandLabel, String[] args, boolean senderIsConsole)
@ -205,6 +205,12 @@ public class Command_toggle extends FreedomCommand
break; break;
} }
case "item_drops":
{
toggle("Item drops are", ConfigEntry.ALLOW_ITEM_DROPS);
break;
}
case "grindstones": case "grindstones":
{ {
toggle("Grindstones are", ConfigEntry.ALLOW_GRINDSTONES); toggle("Grindstones are", ConfigEntry.ALLOW_GRINDSTONES);
@ -319,4 +325,4 @@ public class Command_toggle extends FreedomCommand
} }
return Collections.emptyList(); return Collections.emptyList();
} }
} }

View File

@ -35,6 +35,7 @@ public enum ConfigEntry
ALLOW_GRAVITY(Boolean.class, "allow.gravity"), ALLOW_GRAVITY(Boolean.class, "allow.gravity"),
ALLOW_MASTERBLOCKS(Boolean.class, "allow.masterblocks"), ALLOW_MASTERBLOCKS(Boolean.class, "allow.masterblocks"),
ALLOW_BOOKS(Boolean.class, "allow.books"), ALLOW_BOOKS(Boolean.class, "allow.books"),
ALLOW_ITEM_DROPS(Boolean.class, "allow.item_drops"),
// //
BLOCKED_CHATCODES(String.class, "blocked_chatcodes"), BLOCKED_CHATCODES(String.class, "blocked_chatcodes"),
// //

View File

@ -3,10 +3,7 @@ package me.totalfreedom.totalfreedommod.discord;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import java.time.Instant; import java.time.Instant;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import javax.security.auth.login.LoginException; import javax.security.auth.login.LoginException;
@ -26,11 +23,9 @@ import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.PrivateChannel;
import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.SelfUser; import net.dv8tion.jda.api.entities.SelfUser;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.ReadyEvent; import net.dv8tion.jda.api.events.ReadyEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.requests.GatewayIntent;
@ -42,16 +37,18 @@ import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ChatColor;
import org.apache.commons.lang.WordUtils; import org.apache.commons.lang.WordUtils;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import org.bukkit.Bukkit;
import org.bukkit.GameRule; import org.bukkit.GameRule;
import org.bukkit.Location;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerQuitEvent;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class Discord extends FreedomService public class Discord extends FreedomService
{ {
@ -282,12 +279,6 @@ public class Discord extends FreedomService
{ {
String newMessage = message; String newMessage = message;
if (message.contains("@"))
{
// \u200B is Zero Width Space, invisible on Discord
newMessage = message.replaceAll("@", "@\u200B");
}
if (message.toLowerCase().contains("discord.gg")) // discord.gg/invite works as an invite if (message.toLowerCase().contains("discord.gg")) // discord.gg/invite works as an invite
{ {
return ""; return "";
@ -303,10 +294,10 @@ public class Discord extends FreedomService
if (message.contains("§")) if (message.contains("§"))
{ {
newMessage = message.replaceAll("§", ""); newMessage = message.replace("§", "");
} }
return deformat(newMessage); return newMessage;
} }
public void messageChatChannel(String message) public void messageChatChannel(String message)
@ -324,7 +315,7 @@ public class Discord extends FreedomService
if (enabled && !chat_channel_id.isEmpty()) if (enabled && !chat_channel_id.isEmpty())
{ {
CompletableFuture<Message> sentMessage = Objects.requireNonNull(bot.getTextChannelById(chat_channel_id)).sendMessage(sanitizedMessage).submit(true); CompletableFuture<Message> sentMessage = Objects.requireNonNull(bot.getTextChannelById(chat_channel_id)).sendMessage(sanitizedMessage).allowedMentions(Collections.emptyList()).submit(true);
sentMessages.add(sentMessage); sentMessages.add(sentMessage);
} }
} }
@ -344,7 +335,7 @@ public class Discord extends FreedomService
if (enabled && !chat_channel_id.isEmpty()) if (enabled && !chat_channel_id.isEmpty())
{ {
CompletableFuture<Message> sentMessage = Objects.requireNonNull(bot.getTextChannelById(chat_channel_id)).sendMessage(sanitizedMessage).submit(true); CompletableFuture<Message> sentMessage = Objects.requireNonNull(bot.getTextChannelById(chat_channel_id)).sendMessage(sanitizedMessage).allowedMentions(Collections.emptyList()).submit(true);
sentMessages.add(sentMessage); sentMessages.add(sentMessage);
} }
} }
@ -402,107 +393,86 @@ public class Discord extends FreedomService
return true; return true;
} }
public boolean sendReportOffline(Player reporter, OfflinePlayer reported, String reason) public CompletableFuture<Boolean> sendReport(String reporterName, String reportedName, String reason)
{ {
if (!shouldISendReport()) return CompletableFuture.supplyAsync(() ->
{ {
return false; if (!shouldISendReport())
}
final Guild server = bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString());
if (server == null)
{
FLog.severe("The guild ID specified in the config is invalid.");
return false;
}
final TextChannel channel = server.getTextChannelById(ConfigEntry.DISCORD_REPORT_CHANNEL_ID.getString());
if (channel == null)
{
FLog.severe("The report channel ID specified in the config is invalid.");
return false;
}
final EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setTitle("Report for " + reported.getName() + " (offline)");
embedBuilder.setDescription(reason);
embedBuilder.setFooter("Reported by " + reporter.getName(), "https://minotar.net/helm/" + reporter.getName() + ".png");
embedBuilder.setTimestamp(Instant.from(ZonedDateTime.now()));
if (plugin.esb.isEnabled())
{
com.earth2me.essentials.User user = plugin.esb.getEssentialsUser(reported.getName());
String location = "World: " + Objects.requireNonNull(user.getLastLocation().getWorld()).getName() + ", X: " + user.getLastLocation().getBlockX() + ", Y: " + user.getLastLocation().getBlockY() + ", Z: " + user.getLastLocation().getBlockZ();
embedBuilder.addField("Location", location, true);
embedBuilder.addField("God Mode", WordUtils.capitalizeFully(String.valueOf(user.isGodModeEnabled())), true);
if (user.getNickname() != null)
{ {
embedBuilder.addField("Nickname", user.getNickname(), true); return false;
} }
}
MessageEmbed embed = embedBuilder.build();
Message message = channel.sendMessageEmbeds(embed).complete();
if (!ConfigEntry.DISCORD_REPORT_ARCHIVE_CHANNEL_ID.getString().isEmpty()) final Guild server = bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString());
{
message.addReaction("\uD83D\uDCCB").complete();
}
return true; if (server == null)
}
public boolean sendReport(Player reporter, Player reported, String reason)
{
if (!shouldISendReport())
{
return false;
}
final Guild server = bot.getGuildById(ConfigEntry.DISCORD_SERVER_ID.getString());
if (server == null)
{
FLog.severe("The guild ID specified in the config is invalid.");
return false;
}
final TextChannel channel = server.getTextChannelById(ConfigEntry.DISCORD_REPORT_CHANNEL_ID.getString());
if (channel == null)
{
FLog.severe("The report channel ID specified in the config is invalid.");
return false;
}
final EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setTitle("Report for " + reported.getName());
embedBuilder.setDescription(reason);
embedBuilder.setFooter("Reported by " + reporter.getName(), "https://minotar.net/helm/" + reporter.getName() + ".png");
embedBuilder.setTimestamp(Instant.from(ZonedDateTime.now()));
String location = "World: " + Objects.requireNonNull(reported.getLocation().getWorld()).getName() + ", X: " + reported.getLocation().getBlockX() + ", Y: " + reported.getLocation().getBlockY() + ", Z: " + reported.getLocation().getBlockZ();
embedBuilder.addField("Location", location, true);
embedBuilder.addField("Game Mode", WordUtils.capitalizeFully(reported.getGameMode().name()), true);
if (plugin.esb.isEnabled())
{
com.earth2me.essentials.User user = plugin.esb.getEssentialsUser(reported.getName());
embedBuilder.addField("God Mode", WordUtils.capitalizeFully(String.valueOf(user.isGodModeEnabled())), true);
if (user.getNickname() != null)
{ {
embedBuilder.addField("Nickname", user.getNickname(), true); FLog.severe("The guild ID specified in the config is invalid.");
return false;
} }
}
MessageEmbed embed = embedBuilder.build(); final TextChannel channel = server.getTextChannelById(ConfigEntry.DISCORD_REPORT_CHANNEL_ID.getString());
Message message = channel.sendMessageEmbeds(embed).complete();
if (!ConfigEntry.DISCORD_REPORT_ARCHIVE_CHANNEL_ID.getString().isEmpty()) if (channel == null)
{ {
message.addReaction("\uD83D\uDCCB").complete(); FLog.severe("The report channel ID specified in the config is invalid.");
} return false;
}
return true; Player onlinePlayer = Bukkit.getPlayer(reportedName);
boolean online = onlinePlayer != null;
final EmbedBuilder embedBuilder = new EmbedBuilder();
embedBuilder.setTitle("Report for " + reportedName + (online ? "" : " (offline)"));
embedBuilder.setDescription(reason);
embedBuilder.setFooter("Reported by " + reporterName, "https://minotar.net/helm/" + reporterName + ".png");
embedBuilder.setTimestamp(Instant.from(ZonedDateTime.now()));
Location location = null;
Boolean godMode = null;
String nickName = null;
if (plugin.esb.isEnabled())
{
com.earth2me.essentials.User user = plugin.esb.getEssentialsUser(reportedName);
if (!online)
{
location = user.getLastLocation();
}
godMode = user.isGodModeEnabled();
nickName = user.getNickname();
}
if (location == null && online)
{
location = onlinePlayer.getLocation();
}
if (location != null)
{
embedBuilder.addField("Location", "World: " + location.getWorld().getName() + ", X: " + location.getBlockX() + ", Y: " + location.getBlockY() + ", Z: " + location.getBlockZ(), true);
}
if (godMode != null)
{
embedBuilder.addField("God Mode", WordUtils.capitalizeFully(godMode.toString()), true);
}
if (nickName != null)
{
embedBuilder.addField("Nickname", nickName, true);
}
MessageEmbed embed = embedBuilder.build();
Message message = channel.sendMessageEmbeds(embed).complete();
if (!ConfigEntry.DISCORD_REPORT_ARCHIVE_CHANNEL_ID.getString().isEmpty())
{
message.addReaction("\uD83D\uDCCB").complete();
}
return true;
}, t -> Bukkit.getScheduler().runTaskAsynchronously(plugin, t));
} }
// Do no ask why this is here. I spent two hours trying to make a simple thing work // Do no ask why this is here. I spent two hours trying to make a simple thing work
@ -514,16 +484,28 @@ public class Discord extends FreedomService
} }
} }
@EventHandler(ignoreCancelled = true) public void onPlayerChat(Player player, String message)
public void onAsyncPlayerChat(AsyncPlayerChatEvent event)
{ {
Player player = event.getPlayer(); if (ConfigEntry.ADMIN_ONLY_MODE.getBoolean())
String message = event.getMessage();
if (!ConfigEntry.ADMIN_ONLY_MODE.getBoolean() && !server.hasWhitelist()
&& !plugin.pl.getPlayer(player).isMuted() && bot != null)
{ {
messageChatChannel(player.getName() + " \u00BB " + ChatColor.stripColor(message)); return;
} }
if (server.hasWhitelist())
{
return;
}
if (plugin.pl.getPlayer(player).isMuted())
{
return;
}
if (bot == null)
{
return;
}
messageChatChannel(deformat(player.getName()) + " \u00BB " + ChatColor.stripColor(message));
} }
} }

View File

@ -58,9 +58,8 @@ public class DiscordToMinecraftListener extends ListenerAdapter
final Message msg = event.getMessage(); final Message msg = event.getMessage();
final String content = msg.getContentStripped(); final String content = msg.getContentStripped();
if (content.startsWith(ConfigEntry.DISCORD_PREFIX.getString())) if (content.toLowerCase().startsWith(ConfigEntry.DISCORD_PREFIX.getString().toLowerCase()) && Discord.DISCORD_COMMAND_MANAGER.parse(content, member, textChannel))
{ {
Discord.DISCORD_COMMAND_MANAGER.parse(content, member, textChannel);
return; return;
} }

View File

@ -10,6 +10,8 @@ import net.dv8tion.jda.api.entities.TextChannel;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent; import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import java.util.Collections;
public class MessageReactionListener extends ListenerAdapter public class MessageReactionListener extends ListenerAdapter
{ {
public void onMessageReactionAdd(MessageReactionAddEvent messageReactionAddEvent) public void onMessageReactionAdd(MessageReactionAddEvent messageReactionAddEvent)
@ -59,7 +61,8 @@ public class MessageReactionListener extends ListenerAdapter
final MessageEmbed embed = message.getEmbeds().get(0); final MessageEmbed embed = message.getEmbeds().get(0);
final MessageBuilder archiveMessageBuilder = new MessageBuilder(); final MessageBuilder archiveMessageBuilder = new MessageBuilder();
archiveMessageBuilder.setContent("Report completed by " + completer.getUser().getAsMention() + " (" + Discord.deformat(completer.getUser().getAsTag() + ")")); archiveMessageBuilder.setContent("Report completed by " + completer.getUser().getAsMention() + " (" + Discord.deformat(completer.getUser().getAsTag() + ")"));
archiveMessageBuilder.setEmbed(embed); archiveMessageBuilder.setAllowedMentions(Collections.emptyList());
archiveMessageBuilder.setEmbeds(embed);
final Message archiveMessage = archiveMessageBuilder.build(); final Message archiveMessage = archiveMessageBuilder.build();
archiveChannel.sendMessage(archiveMessage).complete(); archiveChannel.sendMessage(archiveMessage).complete();

View File

@ -46,11 +46,21 @@ public class DiscordCommandManager
FLog.info("Loaded " + commands.size() + " Discord commands."); FLog.info("Loaded " + commands.size() + " Discord commands.");
} }
public void parse(String content, Member member, TextChannel channel) public boolean parse(String content, Member member, TextChannel channel)
{ {
List<String> args = new ArrayList<>(Arrays.asList(content.split(" "))); final String actualContent = content.substring(PREFIX.length()).trim();
List<String> args = new ArrayList<>(Arrays.asList(actualContent.split(" ")));
if (args.isEmpty())
{
return false;
}
final String alias = args.remove(0).split(PREFIX)[1]; // The joys of command parsing final String alias = args.get(0);
if (alias.isEmpty())
{
return false;
}
for (DiscordCommand command : commands) for (DiscordCommand command : commands)
{ {
@ -63,6 +73,7 @@ public class DiscordCommandManager
final CompletableFuture<Message> futureMessage = channel.sendMessage(message).submit(true); final CompletableFuture<Message> futureMessage = channel.sendMessage(message).submit(true);
this.discord.sentMessages.add(futureMessage); this.discord.sentMessages.add(futureMessage);
return true;
} }
else else
{ {
@ -71,14 +82,17 @@ public class DiscordCommandManager
embedBuilder.setTitle("Command error"); embedBuilder.setTitle("Command error");
embedBuilder.setColor(Color.RED); embedBuilder.setColor(Color.RED);
embedBuilder.setDescription("You don't have permission to execute this command."); embedBuilder.setDescription("You don't have permission to execute this command.");
messageBuilder.setEmbed(embedBuilder.build()); messageBuilder.setEmbeds(embedBuilder.build());
final Message message = messageBuilder.build(); final Message message = messageBuilder.build();
final CompletableFuture<Message> futureMessage = channel.sendMessage(message).submit(true); final CompletableFuture<Message> futureMessage = channel.sendMessage(message).submit(true);
this.discord.sentMessages.add(futureMessage); this.discord.sentMessages.add(futureMessage);
return true;
} }
} }
} }
return false;
} }
} }

View File

@ -13,13 +13,7 @@ import me.totalfreedom.totalfreedommod.player.FPlayer;
import me.totalfreedom.totalfreedommod.player.PlayerData; import me.totalfreedom.totalfreedommod.player.PlayerData;
import me.totalfreedom.totalfreedommod.shop.ShopItem; import me.totalfreedom.totalfreedommod.shop.ShopItem;
import me.totalfreedom.totalfreedommod.util.FUtil; import me.totalfreedom.totalfreedommod.util.FUtil;
import org.bukkit.ChatColor; import org.bukkit.*;
import org.bukkit.Color;
import org.bukkit.FireworkEffect;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.SoundCategory;
import org.bukkit.block.Block; import org.bukkit.block.Block;
import org.bukkit.entity.Arrow; import org.bukkit.entity.Arrow;
import org.bukkit.entity.EnderPearl; import org.bukkit.entity.EnderPearl;
@ -95,11 +89,16 @@ public class ItemFun extends FreedomService
{ {
} }
@EventHandler @EventHandler(ignoreCancelled = true)
public void onPlayerEntityInteract(PlayerInteractEntityEvent event) public void onPlayerEntityInteract(PlayerInteractEntityEvent event)
{ {
Player player = event.getPlayer(); Player player = event.getPlayer();
if (player.getGameMode().equals(GameMode.SPECTATOR))
{
return;
}
Entity entity = event.getRightClicked(); Entity entity = event.getRightClicked();
FPlayer fPlayer = plugin.pl.getPlayer(player); FPlayer fPlayer = plugin.pl.getPlayer(player);

View File

@ -21,7 +21,6 @@ import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority; import org.bukkit.event.EventPriority;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory; import org.bukkit.inventory.PlayerInventory;
@ -412,23 +411,35 @@ public class Shop extends FreedomService
} }
@EventHandler public boolean handlePlayerChat(Player player, String message)
public void onPlayerChat(AsyncPlayerChatEvent event)
{ {
String message = event.getMessage(); if (!ConfigEntry.SHOP_ENABLED.getBoolean())
Player player = event.getPlayer();
if (ConfigEntry.SHOP_ENABLED.getBoolean() && ConfigEntry.SHOP_REACTIONS_ENABLED.getBoolean()
&& !plugin.sh.reactionString.isEmpty() && message.equals(plugin.sh.reactionString))
{ {
event.setCancelled(true); return false;
PlayerData data = plugin.pl.getData(player);
data.setCoins(data.getCoins() + plugin.sh.coinsPerReactionWin);
plugin.pl.save(data);
plugin.sh.endReaction(player.getName());
player.sendMessage(ChatColor.GREEN + "You have been given " + ChatColor.GOLD
+ plugin.sh.coinsPerReactionWin + ChatColor.GREEN + " coins!");
} }
if (!ConfigEntry.SHOP_REACTIONS_ENABLED.getBoolean())
{
return false;
}
if (plugin.sh.reactionString.isEmpty())
{
return false;
}
if (!message.equals(plugin.sh.reactionString))
{
return false;
}
PlayerData data = plugin.pl.getData(player);
data.setCoins(data.getCoins() + plugin.sh.coinsPerReactionWin);
plugin.pl.save(data);
plugin.sh.endReaction(player.getName());
player.sendMessage(ChatColor.GREEN + "You have been given " + ChatColor.GOLD
+ plugin.sh.coinsPerReactionWin + ChatColor.GREEN + " coins!");
return true;
} }
public ShopItem getShopItem(int slot) public ShopItem getShopItem(int slot)

View File

@ -90,6 +90,15 @@ public class SQLite extends FreedomService
{ {
FLog.severe("Failed to create the admins table: " + e.getMessage()); FLog.severe("Failed to create the admins table: " + e.getMessage());
} }
} else
{
try
{
connection.createStatement().execute("ALTER TABLE `admins` DROP COLUMN `ptero_id`");
} catch (SQLException e)
{
// Ignore the error. If someone else wants to add WORKING AND TESTED CODE to check if the `ptero_id` column exists, they can, but I couldn't find a good way.
}
} }
if (tableExists(meta, "players")) if (tableExists(meta, "players"))
{ {

View File

@ -21,6 +21,7 @@ import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static org.bukkit.Bukkit.getServer; import static org.bukkit.Bukkit.getServer;
@ -49,7 +50,10 @@ public class FUtil
"78408086-1991-4c33-a571-d8fa325465b2", // Telesphoreo "78408086-1991-4c33-a571-d8fa325465b2", // Telesphoreo
"f5cd54c4-3a24-4213-9a56-c06c49594dff", // Taahh "f5cd54c4-3a24-4213-9a56-c06c49594dff", // Taahh
"a52f1f08-a398-400a-bca4-2b74b81feae6", // G6_ "a52f1f08-a398-400a-bca4-2b74b81feae6", // G6_
"ca83b658-c03b-4106-9edc-72f70a80656d" // ayunami2000 "ca83b658-c03b-4106-9edc-72f70a80656d", // ayunami2000
"4d708f0c-cb2a-4a1e-928f-214daccc9d18", // evakc
"38e38fbd-c027-4cac-8e37-4158710a287d", // EnZaXD
"7076dcf5-b940-4e86-9a6d-a3b1d16b3649" // Gommeh
); );
public static final List<String> DEVELOPER_NAMES = Arrays.asList( public static final List<String> DEVELOPER_NAMES = Arrays.asList(
"Madgeek1450", "Madgeek1450",
@ -70,7 +74,10 @@ public class FUtil
"videogamesm12", "videogamesm12",
"Taahh", "Taahh",
"G6_", "G6_",
"ayunami2000"); "ayunami2000",
"evakc",
"EnZaXD",
"Gommeh");
public static final Map<String, ChatColor> CHAT_COLOR_NAMES = new HashMap<>(); public static final Map<String, ChatColor> CHAT_COLOR_NAMES = new HashMap<>();
public static final List<ChatColor> CHAT_COLOR_POOL = Arrays.asList( public static final List<ChatColor> CHAT_COLOR_POOL = Arrays.asList(
ChatColor.DARK_RED, ChatColor.DARK_RED,
@ -144,10 +151,11 @@ public class FUtil
return (count == 1 ? "" : "s"); return (count == 1 ? "" : "s");
} }
@SuppressWarnings("java:S6204")
public static List<String> getPlayerList() public static List<String> getPlayerList()
{ {
return getServer().getOnlinePlayers().stream().filter(player -> return getServer().getOnlinePlayers().stream().filter(player ->
!TotalFreedomMod.getPlugin().al.isVanished(player.getUniqueId())).map(HumanEntity::getName).toList(); !TotalFreedomMod.getPlugin().al.isVanished(player.getUniqueId())).map(HumanEntity::getName).collect(Collectors.toList());
} }
public static String listToString(List<String> list) public static String listToString(List<String> list)

View File

@ -221,6 +221,7 @@ allow:
gravity: false gravity: false
masterblocks: false masterblocks: false
books: true books: true
item_drops: false
blocked_commands: blocked_commands:
# #

View File

@ -13,9 +13,9 @@ softdepend:
- JDA - JDA
- Votifier - Votifier
authors: [Madgeek1450, Prozza] authors: [Madgeek1450, Prozza]
api-version: "1.17" api-version: "1.20"
libraries: libraries:
- org.apache.commons:commons-lang3:3.12.0 - org.apache.commons:commons-lang3:3.13.0
- commons-io:commons-io:2.11.0 - commons-io:commons-io:2.13.0
- org.jetbrains:annotations:23.0.0 - org.jetbrains:annotations:23.0.0
- org.javassist:javassist:3.29.1-GA - org.javassist:javassist:3.29.1-GA