Update gradle to 8.14.2
This commit is contained in:
1 parent
1dbc26bb7a
commit
5484897d18
37 files changed
+170
-1663
No files matched your search
Submodule
+1
Submodule Essentials-OG added at 1dbc26bb7a.
@@ -1,65 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("essentials.shadow-module")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly('com.github.milkbowl:VaultAPI:1.7') {
|
|
||||||
exclude group: "org.bukkit", module: "bukkit"
|
|
||||||
}
|
|
||||||
compileOnly 'net.luckperms:api:5.0'
|
|
||||||
|
|
||||||
api 'io.papermc:paperlib:1.0.6'
|
|
||||||
|
|
||||||
api 'org.bstats:bstats-bukkit:2.2.1'
|
|
||||||
|
|
||||||
implementation 'org.spongepowered:configurate-yaml:4.1.2'
|
|
||||||
implementation 'org.checkerframework:checker-qual:3.14.0'
|
|
||||||
|
|
||||||
// Providers
|
|
||||||
api project(':providers:BaseProviders')
|
|
||||||
api project(':providers:PaperProvider')
|
|
||||||
api(project(':providers:NMSReflectionProvider')) {
|
|
||||||
exclude group: "org.bukkit", module: "bukkit"
|
|
||||||
}
|
|
||||||
api(project(':providers:1_8Provider')) {
|
|
||||||
exclude group: "org.spigotmc", module: "spigot"
|
|
||||||
}
|
|
||||||
api(project(':providers:1_12Provider')) {
|
|
||||||
exclude group: "org.bukkit", module: "bukkit"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test {
|
|
||||||
testLogging.showStandardStreams = true
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
archiveFileName = "Essentials-OG-${rootProject.ext.FULL_VERSION}.jar"
|
|
||||||
destinationDirectory = file("${rootProject.buildDir}/libs")
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
include (dependency('io.papermc:paperlib'))
|
|
||||||
include (dependency('org.bstats:bstats-bukkit'))
|
|
||||||
include (dependency('org.bstats:bstats-base'))
|
|
||||||
include (dependency('org.spongepowered:configurate-yaml'))
|
|
||||||
include (dependency('org.spongepowered:configurate-core'))
|
|
||||||
include (dependency('org.yaml:snakeyaml'))
|
|
||||||
include (dependency('io.leangen.geantyref:geantyref'))
|
|
||||||
include (dependency('org.checkerframework:checker-qual'))
|
|
||||||
include (project(':providers:BaseProviders'))
|
|
||||||
include (project(':providers:PaperProvider'))
|
|
||||||
include (project(':providers:NMSReflectionProvider'))
|
|
||||||
include (project(':providers:1_8Provider'))
|
|
||||||
include (project(':providers:1_12Provider'))
|
|
||||||
}
|
|
||||||
relocate 'io.papermc.lib', 'com.earth2me.essentials.paperlib'
|
|
||||||
relocate 'org.bstats', 'com.earth2me.essentials.libs.bstats'
|
|
||||||
relocate 'org.spongepowered.configurate', 'com.earth2me.essentials.libs.configurate'
|
|
||||||
relocate 'org.yaml.snakeyaml', 'com.earth2me.essentials.libs.snakeyaml'
|
|
||||||
relocate 'io.leangen.geantyref', 'com.earth2me.essentials.libs.geantyref'
|
|
||||||
relocate 'org.checkerframework', 'com.earth2me.essentials.libs.checkerframework'
|
|
||||||
|
|
||||||
minimize {
|
|
||||||
include(dependency('org.checkerframework:checker-qual'))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("essentials.shadow-module")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly("com.github.milkbowl:VaultAPI:1.7") {
|
||||||
|
exclude(group = "org.bukkit", module = "bukkit")
|
||||||
|
}
|
||||||
|
compileOnly("net.luckperms:api:5.0")
|
||||||
|
|
||||||
|
api("io.papermc:paperlib:1.0.6")
|
||||||
|
api("org.bstats:bstats-bukkit:2.2.1")
|
||||||
|
|
||||||
|
implementation("org.spongepowered:configurate-yaml:4.1.2")
|
||||||
|
implementation("org.checkerframework:checker-qual:3.14.0")
|
||||||
|
|
||||||
|
api(project(":providers:BaseProviders"))
|
||||||
|
api(project(":providers:PaperProvider"))
|
||||||
|
api(project(":providers:NMSReflectionProvider")) {
|
||||||
|
exclude(group = "org.bukkit", module = "bukkit")
|
||||||
|
}
|
||||||
|
api(project(":providers:1_8Provider")) {
|
||||||
|
exclude(group = "org.spigotmc", module = "spigot")
|
||||||
|
}
|
||||||
|
api(project(":providers:1_12Provider")) {
|
||||||
|
exclude(group = "org.bukkit", module = "bukkit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
testLogging.showStandardStreams = true
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named<ShadowJar>("shadowJar") {
|
||||||
|
archiveFileName.set("Essentials-OG-${rootProject.extra["FULL_VERSION"]}.jar")
|
||||||
|
destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs"))
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
include(dependency("io.papermc:paperlib"))
|
||||||
|
include(dependency("org.bstats:bstats-bukkit"))
|
||||||
|
include(dependency("org.bstats:bstats-base"))
|
||||||
|
include(dependency("org.spongepowered:configurate-yaml"))
|
||||||
|
include(dependency("org.spongepowered:configurate-core"))
|
||||||
|
include(dependency("org.yaml:snakeyaml"))
|
||||||
|
include(dependency("io.leangen.geantyref:geantyref"))
|
||||||
|
include(dependency("org.checkerframework:checker-qual"))
|
||||||
|
include(project(":providers:BaseProviders"))
|
||||||
|
include(project(":providers:PaperProvider"))
|
||||||
|
include(project(":providers:NMSReflectionProvider"))
|
||||||
|
include(project(":providers:1_8Provider"))
|
||||||
|
include(project(":providers:1_12Provider"))
|
||||||
|
}
|
||||||
|
|
||||||
|
relocate("io.papermc.lib", "com.earth2me.essentials.paperlib")
|
||||||
|
relocate("org.bstats", "com.earth2me.essentials.libs.bstats")
|
||||||
|
relocate("org.spongepowered.configurate","com.earth2me.essentials.libs.configurate")
|
||||||
|
relocate("org.yaml.snakeyaml", "com.earth2me.essentials.libs.snakeyaml")
|
||||||
|
relocate("io.leangen.geantyref", "com.earth2me.essentials.libs.geantyref")
|
||||||
|
relocate("org.checkerframework", "com.earth2me.essentials.libs.checkerframework")
|
||||||
|
|
||||||
|
minimize { include(dependency("org.checkerframework:checker-qual")) }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
# EssentialsX Discord Link
|
|
||||||
|
|
||||||
EssentialsX Discord Link is an addon for EssentialsX Discord which provides numerous features related to
|
|
||||||
group/role synchronization.
|
|
||||||
|
|
||||||
EssentialsX Discord Link offers features such as:
|
|
||||||
* Vault Group -> Discord Role Synchronization
|
|
||||||
* Discord Role -> Vault Group Synchronization
|
|
||||||
* Prevent unlinked players from joining
|
|
||||||
* Prevent unlinked players from moving/chatting
|
|
||||||
* & more...
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Table of Contents
|
|
||||||
> * [Setting Up Role Sync](#setting-up-role-sync)
|
|
||||||
> * [Linking an Account](#linking-an-account)
|
|
||||||
> * [Developer API](#developer-api)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Setting Up Role Sync
|
|
||||||
|
|
||||||
In EssentialsX Discord Link, you can define a synchronizations for both Vault groups -> Discord roles and for
|
|
||||||
Discord roles -> Vault groups.
|
|
||||||
|
|
||||||
The following tutorial (as an example) will show how to give players with the Discord role `Patreon` the `donator`
|
|
||||||
Vault group and how to give players with the `vip` Vault group the `VIP` Discord role.
|
|
||||||
|
|
||||||
0. First, head to your server's role page in order to get their IDs.
|
|
||||||
|
|
||||||
1. For both the `Patreon` and `VIP` role, right click them and click on "Copy ID".
|
|
||||||
> 
|
|
||||||
> Right Click on Role(s) -> `Copy ID` -> Paste into Notepad for later step
|
|
||||||
|
|
||||||
2. Now that you have the IDs you need from Discord, you can begin configuring the plugin. First place the
|
|
||||||
EssentialsX Discord Link jar (you can download it [here](https://essentialsx.net/downloads.html) if you do not
|
|
||||||
already have it) in your plugins folder and then start your server.
|
|
||||||
> 
|
|
||||||
> Drag EssentialsXDiscordLink jar into plugins folder -> Start Server
|
|
||||||
|
|
||||||
3. Once the server started, open the config for EssentialsX Discord Link at
|
|
||||||
`plugins/EssentialsDiscordLink/config.yml`. Once opened, put `group-name: role-id` in the `groups` section
|
|
||||||
to create a Vault group -> Discord role synchronization (`vip: 882835722640433242` for this example); Then put
|
|
||||||
`role-id: group-name` in the `roles` section to create a Discord role -> Vault group synchronization
|
|
||||||
(`882835662280224818: donator` for this example). When done, save the file.
|
|
||||||
> 
|
|
||||||
> Paste Vault->Discord syncs in the group section & Discord->Vault syncs in the roles section
|
|
||||||
|
|
||||||
5. Finally, once the file is saved, run `ess reload` from your console and then linked accounts should now have
|
|
||||||
their groups/roles linked between Minecraft/Discord! Now that you completed the basics of group/role syncing,
|
|
||||||
go back up to the [Table of Contents](#table-of-contents) to see what else you can do!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Linking an Account
|
|
||||||
|
|
||||||
0. This assumes the server has started and you have joined the server.
|
|
||||||
|
|
||||||
1. Once on the server, run `/link` in Minecraft and take note of the code if gives you.
|
|
||||||
> 
|
|
||||||
> Run `/link` in Minecraft
|
|
||||||
|
|
||||||
2. Next, all you have to do is run the `/link` command in discord with the code provided.
|
|
||||||
> 
|
|
||||||
> Run `/link` with the code in Discord
|
|
||||||
|
|
||||||
3. That's it! Now that you've learned how to link an account, go back up to the
|
|
||||||
[Table of Contents](#table-of-contents) to see what else you can do!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Developer API
|
|
||||||
|
|
||||||
EssentialsX Discord Link has a simple API to provide very simple methods to check if players are linked,
|
|
||||||
link players, unlink players, and to get linked player data.
|
|
||||||
|
|
||||||
Outside the specific examples below, you can also view javadocs for EssentialsX Discord Link
|
|
||||||
[here](https://jd-v2.essentialsx.net/EssentialsDiscordLink).
|
|
||||||
|
|
||||||
### Get a linked player's Discord tag
|
|
||||||
|
|
||||||
The following example shows how to get a linked player's Discord tag (in `Name#0000` format) or null if the player
|
|
||||||
isn't linked.
|
|
||||||
|
|
||||||
```java
|
|
||||||
public String getDiscordTag(final Player player) {
|
|
||||||
// Gets the API service for EssentialsX Discord Link
|
|
||||||
final DiscordLinkService linkApi = Bukkit.getServicesManager().load(DiscordLinkService.class);
|
|
||||||
|
|
||||||
final String discordId = linkApi.getDiscordId(player.getUniqueId());
|
|
||||||
if (discordId == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the API service for EssentialsX Discord which we will use to get the actual user
|
|
||||||
final DiscordService discordApi = Bukkit.getServicesManager().load(DiscordService.class);
|
|
||||||
|
|
||||||
final InteractionMember member = discordApi.getMemberById(discordId).join();
|
|
||||||
return member == null ? null : member.getTag();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("essentials.module-conventions")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly project(':EssentialsX')
|
|
||||||
compileOnly project(':EssentialsXDiscord')
|
|
||||||
}
|
|
||||||
-109
@@ -1,109 +0,0 @@
|
|||||||
package net.essentialsx.api.v2.events.discordlink;
|
|
||||||
|
|
||||||
import net.ess3.api.IUser;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionMember;
|
|
||||||
import org.bukkit.event.Event;
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fired when a User's link status has changed.
|
|
||||||
*/
|
|
||||||
public class DiscordLinkStatusChangeEvent extends Event {
|
|
||||||
private static final HandlerList handlers = new HandlerList();
|
|
||||||
|
|
||||||
private final IUser user;
|
|
||||||
private final InteractionMember member;
|
|
||||||
private final String memberId;
|
|
||||||
private final boolean state;
|
|
||||||
private final Cause cause;
|
|
||||||
|
|
||||||
public DiscordLinkStatusChangeEvent(IUser user, InteractionMember member, String memberId, boolean state, Cause cause) {
|
|
||||||
this.user = user;
|
|
||||||
this.member = member;
|
|
||||||
this.memberId = memberId;
|
|
||||||
this.state = state;
|
|
||||||
this.cause = cause;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the Essentials {@link IUser user} whose link status has been changed in this event.
|
|
||||||
* @return the user.
|
|
||||||
*/
|
|
||||||
public IUser getUser() {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the Discord {@link InteractionMember member} whose link status has been changed in this event.
|
|
||||||
* <p>
|
|
||||||
* This will return {@code null} if {@link #getCause()} returns {@link Cause#UNSYNC_LEAVE}.
|
|
||||||
* @see #getCause()
|
|
||||||
* @see #getMemberId()
|
|
||||||
* @return the member or null.
|
|
||||||
*/
|
|
||||||
public InteractionMember getMember() {
|
|
||||||
return member;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the ID of the Discord member whose link status has been changed in this event.
|
|
||||||
* <p>
|
|
||||||
* Unlink {@link #getMember()}, this method will never return null.
|
|
||||||
* @return the member's id.
|
|
||||||
*/
|
|
||||||
public String getMemberId() {
|
|
||||||
return memberId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the new link status of this {@link #getUser() user} after this event.
|
|
||||||
* @return true if the user is linked to a discord account.
|
|
||||||
*/
|
|
||||||
public boolean isLinked() {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The cause which triggered this event.
|
|
||||||
* @see Cause
|
|
||||||
* @return the cause.
|
|
||||||
*/
|
|
||||||
public Cause getCause() {
|
|
||||||
return cause;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HandlerList getHandlers() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HandlerList getHandlerList() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The cause of the link status change.
|
|
||||||
*/
|
|
||||||
public enum Cause {
|
|
||||||
/**
|
|
||||||
* Used when a player successfully completes an account link with the /link account in Minecraft.
|
|
||||||
*/
|
|
||||||
SYNC_PLAYER,
|
|
||||||
/**
|
|
||||||
* Used when a player is linked via an external plugin using API.
|
|
||||||
*/
|
|
||||||
SYNC_API,
|
|
||||||
/**
|
|
||||||
* Used when a player unlinks their account via the /unlink Discord or Minecraft command.
|
|
||||||
*/
|
|
||||||
UNSYNC_PLAYER,
|
|
||||||
/**
|
|
||||||
* Used when a player is unlinked via an external plugin using API.
|
|
||||||
*/
|
|
||||||
UNSYNC_API,
|
|
||||||
/**
|
|
||||||
* Used when a player is unlinked due to them leaving the Discord server.
|
|
||||||
*/
|
|
||||||
UNSYNC_LEAVE,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-85
@@ -1,85 +0,0 @@
|
|||||||
package net.essentialsx.api.v2.services.discordlink;
|
|
||||||
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionMember;
|
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class which provides numerous methods to interact with the link module for EssentialsX Discord.
|
|
||||||
*/
|
|
||||||
public interface DiscordLinkService {
|
|
||||||
/**
|
|
||||||
* Gets the Discord ID linked to the given {@link UUID} or {@code null} if none is present.
|
|
||||||
* @param uuid the {@link UUID} of the player to lookup.
|
|
||||||
* @return the Discord ID or {@code null}.
|
|
||||||
*/
|
|
||||||
String getDiscordId(final UUID uuid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if there is a Discord account linked to the given {@link UUID}.
|
|
||||||
* @param uuid the {@link UUID} to check.
|
|
||||||
* @return true if there is a Discord account linked to the given {@link UUID}.
|
|
||||||
*/
|
|
||||||
default boolean isLinked(final UUID uuid) {
|
|
||||||
return getDiscordId(uuid) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the {@link UUID} linked to the given Discord ID or {@code null} if none is present.
|
|
||||||
* @param discordId The Discord ID to lookup.
|
|
||||||
* @return the {@link UUID} or {@code null}.
|
|
||||||
*/
|
|
||||||
UUID getUUID(final String discordId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if there is a Minecraft account linked to the given Discord ID.
|
|
||||||
* @param discordId the Discord ID to check.
|
|
||||||
* @return true if there is a Minecraft account linked to the given Discord ID.
|
|
||||||
*/
|
|
||||||
default boolean isLinked(final String discordId) {
|
|
||||||
return getUUID(discordId) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Links the given {@link UUID} to the given {@link InteractionMember}.
|
|
||||||
* <p>
|
|
||||||
* This will automatically trigger role sync (if configured) for the given
|
|
||||||
* player if this method returns {@code true}.
|
|
||||||
* <p>
|
|
||||||
* This method will return true if the accounts are successfully linked, or
|
|
||||||
* false if either the provided {@link UUID} or {@link InteractionMember} are
|
|
||||||
* already linked to another account.
|
|
||||||
* @param uuid The {@link UUID} of the target player.
|
|
||||||
* @param member The {@link InteractionMember} to link to the target player.
|
|
||||||
* @see net.essentialsx.api.v2.services.discord.DiscordService#getMemberById(String) to get an
|
|
||||||
* {@link InteractionMember} by their ID.
|
|
||||||
* @see #isLinked(UUID) to ensure the given {@link UUID} isn't already linked to an account.
|
|
||||||
* @see #isLinked(String) to ensure the given {@link InteractionMember} isn't already linked to an account.
|
|
||||||
* @throws IllegalArgumentException if either of the {@link UUID} or {@link InteractionMember} are null.
|
|
||||||
* @return true if the accounts were linked successfully, otherwise false.
|
|
||||||
*/
|
|
||||||
boolean linkAccount(final UUID uuid, final InteractionMember member);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlinks the given {@link UUID} with its associated Discord account (if present).
|
|
||||||
* <p>
|
|
||||||
* This will automatically trigger role unsync (if configured) for the given player if this method
|
|
||||||
* returns {@code true}.
|
|
||||||
* @param uuid The {@link UUID} of the player to unlink.
|
|
||||||
* @throws IllegalArgumentException if the provided {@link UUID} is null.
|
|
||||||
* @return true if there was an account associated with the given {@link UUID}, otherwise false.
|
|
||||||
*/
|
|
||||||
boolean unlinkAccount(final UUID uuid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlinks the given {@link InteractionMember} with its associated Minecraft account (if present).
|
|
||||||
* <p>
|
|
||||||
* This will automatically trigger role unsync (if configured) for the given {@link InteractionMember}
|
|
||||||
* if this method returns {@code true}.
|
|
||||||
* @param member The {@link InteractionMember} to unlink.
|
|
||||||
* @throws IllegalArgumentException if the provided {@link InteractionMember} is null.
|
|
||||||
* @return true if there was a linked Minecraft account associated with the given
|
|
||||||
* {@link InteractionMember}, otherwise false.
|
|
||||||
*/
|
|
||||||
boolean unlinkAccount(final InteractionMember member);
|
|
||||||
}
|
|
||||||
-165
@@ -1,165 +0,0 @@
|
|||||||
package net.essentialsx.discordlink;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.IEssentialsModule;
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import net.ess3.api.IUser;
|
|
||||||
import net.essentialsx.api.v2.events.discordlink.DiscordLinkStatusChangeEvent;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionMember;
|
|
||||||
import net.essentialsx.api.v2.services.discordlink.DiscordLinkService;
|
|
||||||
import net.essentialsx.discordlink.rolesync.RoleSyncManager;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
public class AccountLinkManager implements IEssentialsModule, DiscordLinkService {
|
|
||||||
private static final char[] CODE_CHARACTERS = "abcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
|
|
||||||
|
|
||||||
private final EssentialsDiscordLink ess;
|
|
||||||
private final AccountStorage storage;
|
|
||||||
private final RoleSyncManager roleSyncManager;
|
|
||||||
|
|
||||||
private final Map<String, UUID> codeToUuidMap = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
public AccountLinkManager(EssentialsDiscordLink ess, AccountStorage storage, RoleSyncManager roleSyncManager) {
|
|
||||||
this.ess = ess;
|
|
||||||
this.storage = storage;
|
|
||||||
this.roleSyncManager = roleSyncManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String createCode(final UUID uuid) throws IllegalArgumentException {
|
|
||||||
final Optional<Map.Entry<String, UUID>> prevCode = codeToUuidMap.entrySet().stream().filter(stringUUIDEntry -> stringUUIDEntry.getValue().equals(uuid)).findFirst();
|
|
||||||
if (prevCode.isPresent()) {
|
|
||||||
throw new IllegalArgumentException(prevCode.get().getKey());
|
|
||||||
}
|
|
||||||
|
|
||||||
final String code = generateCode();
|
|
||||||
|
|
||||||
codeToUuidMap.put(code, uuid);
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getPendingUUID(final String code) {
|
|
||||||
return codeToUuidMap.remove(code);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDiscordId(final UUID uuid) {
|
|
||||||
return storage.getDiscordId(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IUser getUser(final String discordId) {
|
|
||||||
final UUID uuid = getUUID(discordId);
|
|
||||||
if (uuid == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return ess.getEss().getUser(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public UUID getUUID(final String discordId) {
|
|
||||||
return storage.getUUID(discordId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean unlinkAccount(InteractionMember member) {
|
|
||||||
Preconditions.checkNotNull(member, "member cannot be null");
|
|
||||||
|
|
||||||
return isLinked(member.getId()) && removeAccount(member, DiscordLinkStatusChangeEvent.Cause.UNSYNC_API);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeAccount(final InteractionMember member, final DiscordLinkStatusChangeEvent.Cause cause) {
|
|
||||||
final UUID uuid = getUUID(member.getId());
|
|
||||||
if (storage.remove(member.getId())) {
|
|
||||||
ensureAsync(() -> {
|
|
||||||
final IUser user = ess.getEss().getUser(uuid);
|
|
||||||
ensureSync(() -> ess.getServer().getPluginManager().callEvent(new DiscordLinkStatusChangeEvent(user, member, member.getId(), false, cause)));
|
|
||||||
|
|
||||||
roleSyncManager.unSync(uuid, member.getId());
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean unlinkAccount(UUID uuid) {
|
|
||||||
Preconditions.checkNotNull(uuid, "uuid cannot be null");
|
|
||||||
|
|
||||||
if (!isLinked(uuid)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureAsync(() -> removeAccount(ess.getEss().getUser(uuid), DiscordLinkStatusChangeEvent.Cause.UNSYNC_API));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean removeAccount(final IUser user, final DiscordLinkStatusChangeEvent.Cause cause) {
|
|
||||||
final String id = getDiscordId(user.getBase().getUniqueId());
|
|
||||||
if (storage.remove(user.getBase().getUniqueId())) {
|
|
||||||
ess.getApi().getMemberById(id).thenAccept(member -> ensureSync(() ->
|
|
||||||
ess.getServer().getPluginManager().callEvent(new DiscordLinkStatusChangeEvent(user, member, id, false, cause))));
|
|
||||||
|
|
||||||
ensureAsync(() -> roleSyncManager.unSync(user.getBase().getUniqueId(), id));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean linkAccount(UUID uuid, InteractionMember member) {
|
|
||||||
Preconditions.checkNotNull(uuid, "uuid cannot be null");
|
|
||||||
Preconditions.checkNotNull(member, "member cannot be null");
|
|
||||||
|
|
||||||
if (isLinked(uuid) || isLinked(member.getId())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
registerAccount(uuid, member, DiscordLinkStatusChangeEvent.Cause.SYNC_API);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerAccount(final UUID uuid, final InteractionMember member, final DiscordLinkStatusChangeEvent.Cause cause) {
|
|
||||||
storage.add(uuid, member.getId());
|
|
||||||
ensureAsync(() -> roleSyncManager.sync(uuid, member.getId()));
|
|
||||||
ensureAsync(() -> {
|
|
||||||
final IUser user = ess.getEss().getUser(uuid);
|
|
||||||
ensureSync(() -> ess.getServer().getPluginManager().callEvent(new DiscordLinkStatusChangeEvent(user, member, member.getId(), true, cause)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureSync(final Runnable runnable) {
|
|
||||||
if (ess.getServer().isPrimaryThread()) {
|
|
||||||
runnable.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ess.getEss().scheduleSyncDelayedTask(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureAsync(final Runnable runnable) {
|
|
||||||
if (!ess.getServer().isPrimaryThread()) {
|
|
||||||
runnable.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ess.getEss().runTaskAsynchronously(runnable);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String generateCode() {
|
|
||||||
final char[] code = new char[8];
|
|
||||||
final Random random = ThreadLocalRandom.current();
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
|
||||||
code[i] = CODE_CHARACTERS[random.nextInt(CODE_CHARACTERS.length)];
|
|
||||||
}
|
|
||||||
final String result = new String(code);
|
|
||||||
|
|
||||||
if (codeToUuidMap.containsKey(result)) {
|
|
||||||
// If this happens, buy a lottery ticket.
|
|
||||||
return generateCode();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
package net.essentialsx.discordlink;
|
|
||||||
|
|
||||||
import com.google.common.collect.BiMap;
|
|
||||||
import com.google.common.collect.HashBiMap;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.reflect.TypeToken;
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.Reader;
|
|
||||||
import java.io.Writer;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
public class AccountStorage {
|
|
||||||
private final Gson gson = new Gson();
|
|
||||||
private final EssentialsDiscordLink plugin;
|
|
||||||
private final File accountFile;
|
|
||||||
private final BiMap<String, String> uuidToDiscordIdMap;
|
|
||||||
private final AtomicBoolean mapDirty = new AtomicBoolean(false);
|
|
||||||
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
|
||||||
|
|
||||||
public AccountStorage(final EssentialsDiscordLink plugin) throws IOException {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.accountFile = new File(plugin.getDataFolder(), "accounts.json");
|
|
||||||
if (!plugin.getDataFolder().exists() && !plugin.getDataFolder().mkdirs()) {
|
|
||||||
throw new IOException("Unable to create account file!");
|
|
||||||
}
|
|
||||||
if (!accountFile.exists() && !accountFile.createNewFile()) {
|
|
||||||
throw new IOException("Unable to create account file!");
|
|
||||||
}
|
|
||||||
try (final Reader reader = new FileReader(accountFile)) {
|
|
||||||
final Map<String, String> map = gson.fromJson(reader, new TypeToken<Map<String, String>>() {}.getType());
|
|
||||||
uuidToDiscordIdMap = map == null ? Maps.synchronizedBiMap(HashBiMap.create()) : Maps.synchronizedBiMap(HashBiMap.create(map));
|
|
||||||
}
|
|
||||||
|
|
||||||
executorService.scheduleWithFixedDelay(() -> {
|
|
||||||
if (!mapDirty.compareAndSet(true, false)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (plugin.getEss().getSettings().isDebug()) {
|
|
||||||
plugin.getLogger().log(Level.INFO, "Saving linked discord accounts to disk...");
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, String> clone;
|
|
||||||
clone = new HashMap<>(uuidToDiscordIdMap);
|
|
||||||
try (final Writer writer = new FileWriter(accountFile)) {
|
|
||||||
gson.toJson(clone, writer);
|
|
||||||
} catch (IOException e) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to save link accounts!", e);
|
|
||||||
mapDirty.set(true); // mark the map as dirty and pray it fixes itself :D
|
|
||||||
}
|
|
||||||
}, 10, 10, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BiMap<String, String> getRawStorageMap() {
|
|
||||||
return HashBiMap.create(uuidToDiscordIdMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(final UUID uuid, final String discordId) {
|
|
||||||
uuidToDiscordIdMap.forcePut(uuid.toString(), discordId);
|
|
||||||
queueSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean remove(final UUID uuid) {
|
|
||||||
if (uuidToDiscordIdMap.remove(uuid.toString()) != null) {
|
|
||||||
queueSave();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean remove(final String discordId) {
|
|
||||||
if (uuidToDiscordIdMap.values().removeIf(discordId::equals)) {
|
|
||||||
queueSave();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID getUUID(final String discordId) {
|
|
||||||
final String uuid = uuidToDiscordIdMap.inverse().get(discordId);
|
|
||||||
return uuid == null ? null : UUID.fromString(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDiscordId(final UUID uuid) {
|
|
||||||
return uuidToDiscordIdMap.get(uuid.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void queueSave() {
|
|
||||||
mapDirty.set(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutdown() {
|
|
||||||
try {
|
|
||||||
executorService.shutdown();
|
|
||||||
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "Timed out while saving!");
|
|
||||||
executorService.shutdownNow();
|
|
||||||
}
|
|
||||||
if (mapDirty.get()) {
|
|
||||||
try (final Writer writer = new FileWriter(accountFile)) {
|
|
||||||
gson.toJson(uuidToDiscordIdMap, writer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException | IOException e) {
|
|
||||||
plugin.getLogger().log(Level.SEVERE, "Failed to shutdown link accounts save!", e);
|
|
||||||
executorService.shutdownNow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-96
@@ -1,96 +0,0 @@
|
|||||||
package net.essentialsx.discordlink;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.IConf;
|
|
||||||
import com.earth2me.essentials.config.EssentialsConfiguration;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class DiscordLinkSettings implements IConf {
|
|
||||||
private final EssentialsDiscordLink plugin;
|
|
||||||
private final EssentialsConfiguration config;
|
|
||||||
|
|
||||||
private LinkPolicy linkPolicy;
|
|
||||||
private Map<String, String> roleSyncGroups;
|
|
||||||
private Map<String, String> roleSyncRoles;
|
|
||||||
|
|
||||||
public DiscordLinkSettings(EssentialsDiscordLink plugin) {
|
|
||||||
this.plugin = plugin;
|
|
||||||
this.config = new EssentialsConfiguration(new File(plugin.getDataFolder(), "config.yml"), "/config.yml", EssentialsDiscordLink.class);
|
|
||||||
reloadConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
public LinkPolicy getLinkPolicy() {
|
|
||||||
return linkPolicy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBlockUnlinkedChat() {
|
|
||||||
return config.getBoolean("block-unlinked-chat", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUnlinkOnLeave() {
|
|
||||||
return config.getBoolean("unlink-on-leave", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRelayMail() {
|
|
||||||
return config.getBoolean("relay-mail", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRoleSyncRemoveRoles() {
|
|
||||||
return config.getBoolean("role-sync.remove-roles", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRoleSyncRemoveGroups() {
|
|
||||||
return config.getBoolean("role-sync.remove-groups", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRoleSyncResyncDelay() {
|
|
||||||
return config.getInt("role-sync.resync-delay", 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRoleSyncPrimaryGroupOnly() {
|
|
||||||
return config.getBoolean("role-sync.primary-group-only", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getRoleSyncGroups() {
|
|
||||||
return roleSyncGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, String> _getRoleSyncGroups() {
|
|
||||||
return config.getStringMap("role-sync.groups");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getRoleSyncRoles() {
|
|
||||||
return roleSyncRoles;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, String> _getRoleSyncRoles() {
|
|
||||||
return config.getStringMap("role-sync.roles");
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum LinkPolicy {
|
|
||||||
KICK,
|
|
||||||
FREEZE,
|
|
||||||
NONE;
|
|
||||||
|
|
||||||
static LinkPolicy fromName(final String name) {
|
|
||||||
for (LinkPolicy policy : values()) {
|
|
||||||
if (policy.name().equalsIgnoreCase(name)) {
|
|
||||||
return policy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return LinkPolicy.NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reloadConfig() {
|
|
||||||
config.load();
|
|
||||||
|
|
||||||
linkPolicy = LinkPolicy.fromName(config.getString("link-policy", "none"));
|
|
||||||
roleSyncGroups = _getRoleSyncGroups();
|
|
||||||
roleSyncRoles = _getRoleSyncRoles();
|
|
||||||
|
|
||||||
plugin.onReload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-135
@@ -1,135 +0,0 @@
|
|||||||
package net.essentialsx.discordlink;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.EssentialsLogger;
|
|
||||||
import com.earth2me.essentials.IEssentials;
|
|
||||||
import com.earth2me.essentials.metrics.MetricsWrapper;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
import net.essentialsx.api.v2.services.discord.DiscordService;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionException;
|
|
||||||
import net.essentialsx.api.v2.services.discordlink.DiscordLinkService;
|
|
||||||
import net.essentialsx.discord.EssentialsDiscord;
|
|
||||||
import net.essentialsx.discordlink.commands.discord.AccountInteractionCommand;
|
|
||||||
import net.essentialsx.discordlink.commands.discord.LinkInteractionCommand;
|
|
||||||
import net.essentialsx.discordlink.commands.discord.UnlinkInteractionCommand;
|
|
||||||
import net.essentialsx.discordlink.listeners.LinkBukkitListener;
|
|
||||||
import net.essentialsx.discordlink.rolesync.RoleSyncManager;
|
|
||||||
import org.bukkit.command.Command;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.plugin.ServicePriority;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
|
||||||
|
|
||||||
public class EssentialsDiscordLink extends JavaPlugin {
|
|
||||||
private transient IEssentials ess;
|
|
||||||
private transient MetricsWrapper metrics = null;
|
|
||||||
|
|
||||||
private DiscordService api;
|
|
||||||
private DiscordLinkSettings settings;
|
|
||||||
private AccountStorage accounts;
|
|
||||||
private AccountLinkManager linkManager;
|
|
||||||
private RoleSyncManager roleSyncManager;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnable() {
|
|
||||||
ess = (IEssentials) getServer().getPluginManager().getPlugin("Essentials");
|
|
||||||
final EssentialsDiscord essDiscord = (EssentialsDiscord) getServer().getPluginManager().getPlugin("EssentialsDiscord");
|
|
||||||
if (ess == null || !ess.isEnabled() || essDiscord == null || !essDiscord.isEnabled()) {
|
|
||||||
setEnabled(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!getDescription().getVersion().equals(ess.getDescription().getVersion())) {
|
|
||||||
getLogger().log(Level.WARNING, tl("versionMismatchAll"));
|
|
||||||
}
|
|
||||||
|
|
||||||
api = getServer().getServicesManager().load(DiscordService.class);
|
|
||||||
|
|
||||||
settings = new DiscordLinkSettings(this);
|
|
||||||
ess.addReloadListener(settings);
|
|
||||||
try {
|
|
||||||
accounts = new AccountStorage(this);
|
|
||||||
} catch (IOException e) {
|
|
||||||
getLogger().log(Level.SEVERE, "Unable to create link accounts file", e);
|
|
||||||
setEnabled(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
roleSyncManager = new RoleSyncManager(this);
|
|
||||||
linkManager = new AccountLinkManager(this, accounts, roleSyncManager);
|
|
||||||
|
|
||||||
getServer().getPluginManager().registerEvents(new LinkBukkitListener(this), this);
|
|
||||||
getServer().getServicesManager().register(DiscordLinkService.class, linkManager, this, ServicePriority.Normal);
|
|
||||||
|
|
||||||
if (!(api.getInteractionController().getCommand("link") instanceof LinkInteractionCommand)) {
|
|
||||||
try {
|
|
||||||
api.getInteractionController().registerCommand(new AccountInteractionCommand(linkManager));
|
|
||||||
api.getInteractionController().registerCommand(new LinkInteractionCommand(linkManager));
|
|
||||||
api.getInteractionController().registerCommand(new UnlinkInteractionCommand(linkManager));
|
|
||||||
} catch (InteractionException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
setEnabled(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ess.getPermissionsHandler().registerContext("essentials:linked", user ->
|
|
||||||
Collections.singleton(String.valueOf(linkManager.isLinked(user.getUUID()))), () -> ImmutableSet.of("true", "false"));
|
|
||||||
|
|
||||||
if (metrics == null) {
|
|
||||||
metrics = new MetricsWrapper(this, 11462, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisable() {
|
|
||||||
if (accounts != null) {
|
|
||||||
accounts.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onReload() {
|
|
||||||
if (roleSyncManager != null) {
|
|
||||||
roleSyncManager.onReload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEssentials getEss() {
|
|
||||||
return ess;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscordService getApi() {
|
|
||||||
return api;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscordLinkSettings getSettings() {
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AccountStorage getAccountStorage() {
|
|
||||||
return accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AccountLinkManager getLinkManager() {
|
|
||||||
return linkManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
|
||||||
return ess.onCommandEssentials(sender, command, label, args, EssentialsDiscordLink.class.getClassLoader(), "net.essentialsx.discordlink.commands.bukkit.Command", "essentials.", linkManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Logger getLogger() {
|
|
||||||
try {
|
|
||||||
return EssentialsLogger.getLoggerProvider(this);
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
// In case Essentials isn't installed/loaded
|
|
||||||
return super.getLogger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-30
@@ -1,30 +0,0 @@
|
|||||||
package net.essentialsx.discordlink.commands.bukkit;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.User;
|
|
||||||
import com.earth2me.essentials.commands.EssentialsCommand;
|
|
||||||
import net.essentialsx.discordlink.AccountLinkManager;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
|
||||||
|
|
||||||
public class Commandlink extends EssentialsCommand {
|
|
||||||
public Commandlink() {
|
|
||||||
super("link");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run(Server server, User user, String commandLabel, String[] args) {
|
|
||||||
final AccountLinkManager manager = (AccountLinkManager) module;
|
|
||||||
if (manager.isLinked(user.getUUID())) {
|
|
||||||
user.sendMessage(tl("discordLinkLinkedAlready"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
final String code = manager.createCode(user.getBase().getUniqueId());
|
|
||||||
user.sendMessage(tl("discordLinkLinked", "/link " + code));
|
|
||||||
} catch (final IllegalArgumentException e) {
|
|
||||||
user.sendMessage(tl("discordLinkPending", "/link " + e.getMessage()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-26
@@ -1,26 +0,0 @@
|
|||||||
package net.essentialsx.discordlink.commands.bukkit;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.User;
|
|
||||||
import com.earth2me.essentials.commands.EssentialsCommand;
|
|
||||||
import net.essentialsx.api.v2.events.discordlink.DiscordLinkStatusChangeEvent;
|
|
||||||
import net.essentialsx.discordlink.AccountLinkManager;
|
|
||||||
import org.bukkit.Server;
|
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
|
||||||
|
|
||||||
public class Commandunlink extends EssentialsCommand {
|
|
||||||
public Commandunlink() {
|
|
||||||
super("unlink");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run(Server server, User user, String commandLabel, String[] args) {
|
|
||||||
final AccountLinkManager manager = (AccountLinkManager) module;
|
|
||||||
if (!manager.removeAccount(user, DiscordLinkStatusChangeEvent.Cause.UNSYNC_PLAYER)) {
|
|
||||||
user.sendMessage(tl("discordLinkNoAccount"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
user.sendMessage(tl("discordLinkUnlinked"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-66
@@ -1,66 +0,0 @@
|
|||||||
package net.essentialsx.discordlink.commands.discord;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import net.ess3.api.IUser;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionCommand;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionCommandArgument;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionCommandArgumentType;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionEvent;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionMember;
|
|
||||||
import net.essentialsx.discordlink.AccountLinkManager;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
|
||||||
|
|
||||||
public class AccountInteractionCommand implements InteractionCommand {
|
|
||||||
private final List<InteractionCommandArgument> arguments;
|
|
||||||
private final AccountLinkManager accounts;
|
|
||||||
|
|
||||||
public AccountInteractionCommand(AccountLinkManager accounts) {
|
|
||||||
this.arguments = ImmutableList.of(new InteractionCommandArgument("user", tl("discordCommandAccountArgumentUser"), InteractionCommandArgumentType.USER, false));
|
|
||||||
this.accounts = accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDisabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEphemeral() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "account";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return tl("discordCommandAccountDescription");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<InteractionCommandArgument> getArguments() {
|
|
||||||
return arguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCommand(InteractionEvent event) {
|
|
||||||
final InteractionMember userArg = event.getUserArgument("user");
|
|
||||||
final InteractionMember effectiveUser = userArg == null ? event.getMember() : userArg;
|
|
||||||
final IUser user = accounts.getUser(effectiveUser.getId());
|
|
||||||
if (user == null) {
|
|
||||||
event.reply(tl(event.getMember().getId().equals(effectiveUser.getId()) ? "discordCommandAccountResponseNotLinked" : "discordCommandAccountResponseNotLinkedOther", effectiveUser.getAsMention()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.getMember().getId().equals(effectiveUser.getId())) {
|
|
||||||
event.reply(tl("discordCommandAccountResponseLinked", user.getName()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.reply(tl("discordCommandAccountResponseLinkedOther", effectiveUser.getAsMention(), user.getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
package net.essentialsx.discordlink.commands.discord;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import net.essentialsx.api.v2.events.discordlink.DiscordLinkStatusChangeEvent;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionCommand;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionCommandArgument;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionCommandArgumentType;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionEvent;
|
|
||||||
import net.essentialsx.discordlink.AccountLinkManager;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
|
||||||
|
|
||||||
public class LinkInteractionCommand implements InteractionCommand {
|
|
||||||
private final List<InteractionCommandArgument> arguments;
|
|
||||||
private final AccountLinkManager accounts;
|
|
||||||
|
|
||||||
public LinkInteractionCommand(final AccountLinkManager accounts) {
|
|
||||||
this.arguments = ImmutableList.of(new InteractionCommandArgument("code", tl("discordCommandLinkArgumentCode"), InteractionCommandArgumentType.STRING, true));
|
|
||||||
this.accounts = accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCommand(InteractionEvent event) {
|
|
||||||
if (accounts.isLinked(event.getMember().getId())) {
|
|
||||||
event.reply(tl("discordCommandLinkHasAccount"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final UUID uuid = accounts.getPendingUUID(event.getStringArgument("code"));
|
|
||||||
if (uuid == null) {
|
|
||||||
event.reply(tl("discordCommandLinkInvalidCode"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
accounts.registerAccount(uuid, event.getMember(), DiscordLinkStatusChangeEvent.Cause.SYNC_PLAYER);
|
|
||||||
event.reply(tl("discordCommandLinkLinked"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDisabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEphemeral() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "link";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return tl("discordCommandLinkDescription");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<InteractionCommandArgument> getArguments() {
|
|
||||||
return arguments;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
package net.essentialsx.discordlink.commands.discord;
|
|
||||||
|
|
||||||
import net.essentialsx.api.v2.events.discordlink.DiscordLinkStatusChangeEvent;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionCommand;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionCommandArgument;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionEvent;
|
|
||||||
import net.essentialsx.discordlink.AccountLinkManager;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
|
||||||
|
|
||||||
public class UnlinkInteractionCommand implements InteractionCommand {
|
|
||||||
private final AccountLinkManager accounts;
|
|
||||||
|
|
||||||
public UnlinkInteractionCommand(final AccountLinkManager accounts) {
|
|
||||||
this.accounts = accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCommand(InteractionEvent event) {
|
|
||||||
if (!accounts.removeAccount(event.getMember(), DiscordLinkStatusChangeEvent.Cause.UNSYNC_PLAYER)) {
|
|
||||||
event.reply(tl("discordCommandUnlinkInvalidCode"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
event.reply(tl("discordCommandUnlinkUnlinked"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDisabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEphemeral() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "unlink";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDescription() {
|
|
||||||
return tl("discordCommandUnlinkDescription");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<InteractionCommandArgument> getArguments() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-174
@@ -1,174 +0,0 @@
|
|||||||
package net.essentialsx.discordlink.listeners;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.utils.FormatUtil;
|
|
||||||
import net.essentialsx.api.v2.events.AsyncUserDataLoadEvent;
|
|
||||||
import net.essentialsx.api.v2.events.UserMailEvent;
|
|
||||||
import net.essentialsx.api.v2.events.discord.DiscordMessageEvent;
|
|
||||||
import net.essentialsx.api.v2.events.discordlink.DiscordLinkStatusChangeEvent;
|
|
||||||
import net.essentialsx.api.v2.services.discord.MessageType;
|
|
||||||
import net.essentialsx.discord.util.MessageUtil;
|
|
||||||
import net.essentialsx.discordlink.DiscordLinkSettings;
|
|
||||||
import net.essentialsx.discordlink.EssentialsDiscordLink;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.AsyncPlayerChatEvent;
|
|
||||||
import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
|
|
||||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
|
||||||
import org.bukkit.event.player.PlayerInteractEvent;
|
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
|
||||||
|
|
||||||
public class LinkBukkitListener implements Listener {
|
|
||||||
private final EssentialsDiscordLink ess;
|
|
||||||
|
|
||||||
public LinkBukkitListener(EssentialsDiscordLink ess) {
|
|
||||||
this.ess = ess;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
|
||||||
public void onMail(final UserMailEvent event) {
|
|
||||||
if (!ess.getSettings().isRelayMail()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String discordId = ess.getLinkManager().getDiscordId(event.getRecipient().getBase().getUniqueId());
|
|
||||||
if (discordId == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String sanitizedName = MessageUtil.sanitizeDiscordMarkdown(event.getMessage().getSenderUsername());
|
|
||||||
final String sanitizedMessage = MessageUtil.sanitizeDiscordMarkdown(FormatUtil.stripFormat(event.getMessage().getMessage()));
|
|
||||||
|
|
||||||
ess.getApi().getMemberById(discordId).thenAccept(member -> {
|
|
||||||
member.sendPrivateMessage(tl("discordMailLine", sanitizedName, sanitizedMessage));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGH)
|
|
||||||
public void onConnect(final AsyncPlayerPreLoginEvent event) {
|
|
||||||
if (ess.getSettings().getLinkPolicy() != DiscordLinkSettings.LinkPolicy.KICK) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ess.getLinkManager().isLinked(event.getUniqueId())) {
|
|
||||||
String code;
|
|
||||||
try {
|
|
||||||
code = ess.getLinkManager().createCode(event.getUniqueId());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
code = e.getMessage();
|
|
||||||
}
|
|
||||||
event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, tl("discordLinkLoginKick", "/link " + code, ess.getApi().getInviteUrl()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW)
|
|
||||||
public void onInteract(final PlayerInteractEvent event) {
|
|
||||||
if (ess.getSettings().getLinkPolicy() != DiscordLinkSettings.LinkPolicy.FREEZE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ess.getLinkManager().isLinked(event.getPlayer().getUniqueId())) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW)
|
|
||||||
public void onCommand(final PlayerCommandPreprocessEvent event) {
|
|
||||||
if (ess.getSettings().getLinkPolicy() != DiscordLinkSettings.LinkPolicy.FREEZE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo maybe allowed commands
|
|
||||||
if (!ess.getLinkManager().isLinked(event.getPlayer().getUniqueId())) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
String code;
|
|
||||||
try {
|
|
||||||
code = ess.getLinkManager().createCode(event.getPlayer().getUniqueId());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
code = e.getMessage();
|
|
||||||
}
|
|
||||||
event.getPlayer().sendMessage(tl("discordLinkLoginPrompt", "/link " + code, ess.getApi().getInviteUrl()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.LOW)
|
|
||||||
public void onChat(final AsyncPlayerChatEvent event) {
|
|
||||||
if (ess.getSettings().getLinkPolicy() != DiscordLinkSettings.LinkPolicy.FREEZE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ess.getLinkManager().isLinked(event.getPlayer().getUniqueId())) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
String code;
|
|
||||||
try {
|
|
||||||
code = ess.getLinkManager().createCode(event.getPlayer().getUniqueId());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
code = e.getMessage();
|
|
||||||
}
|
|
||||||
event.getPlayer().sendMessage(tl("discordLinkLoginPrompt", "/link " + code, ess.getApi().getInviteUrl()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR)
|
|
||||||
public void onUserDataLoad(final AsyncUserDataLoadEvent event) {
|
|
||||||
if (ess.getSettings().getLinkPolicy() != DiscordLinkSettings.LinkPolicy.FREEZE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ess.getLinkManager().isLinked(event.getUser().getBase().getUniqueId())) {
|
|
||||||
event.getUser().setFreeze(true);
|
|
||||||
String code;
|
|
||||||
try {
|
|
||||||
code = ess.getLinkManager().createCode(event.getUser().getBase().getUniqueId());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
code = e.getMessage();
|
|
||||||
}
|
|
||||||
event.getUser().sendMessage(tl("discordLinkLoginPrompt", "/link " + code, ess.getApi().getInviteUrl()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGH)
|
|
||||||
public void onDiscordMessage(final DiscordMessageEvent event) {
|
|
||||||
if (ess.getSettings().isBlockUnlinkedChat() && event.getType() == MessageType.DefaultTypes.CHAT && !ess.getLinkManager().isLinked(event.getUUID())) {
|
|
||||||
event.setCancelled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onUserLinkStatusChange(final DiscordLinkStatusChangeEvent event) {
|
|
||||||
if (event.isLinked() || ess.getSettings().getLinkPolicy() == DiscordLinkSettings.LinkPolicy.NONE) {
|
|
||||||
event.getUser().setFreeze(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String code;
|
|
||||||
try {
|
|
||||||
code = ess.getLinkManager().createCode(event.getUser().getBase().getUniqueId());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
code = e.getMessage();
|
|
||||||
}
|
|
||||||
final String finalCode = code;
|
|
||||||
|
|
||||||
switch (ess.getSettings().getLinkPolicy()) {
|
|
||||||
case KICK: {
|
|
||||||
final Runnable kickTask = () -> event.getUser().getBase().kickPlayer(tl("discordLinkLoginKick", "/link " + finalCode, ess.getApi().getInviteUrl()));
|
|
||||||
if (Bukkit.isPrimaryThread()) {
|
|
||||||
kickTask.run();
|
|
||||||
} else {
|
|
||||||
ess.getEss().scheduleSyncDelayedTask(kickTask);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FREEZE: {
|
|
||||||
event.getUser().sendMessage(tl("discordLinkLoginPrompt", "/link " + code, ess.getApi().getInviteUrl()));
|
|
||||||
event.getUser().setFreeze(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-192
@@ -1,192 +0,0 @@
|
|||||||
package net.essentialsx.discordlink.rolesync;
|
|
||||||
|
|
||||||
import com.earth2me.essentials.UUIDPlayer;
|
|
||||||
import com.google.common.collect.BiMap;
|
|
||||||
import net.essentialsx.api.v2.events.discordlink.DiscordLinkStatusChangeEvent;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionMember;
|
|
||||||
import net.essentialsx.api.v2.services.discord.InteractionRole;
|
|
||||||
import net.essentialsx.discordlink.EssentialsDiscordLink;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import static com.earth2me.essentials.I18n.tl;
|
|
||||||
|
|
||||||
public class RoleSyncManager implements Listener {
|
|
||||||
private final EssentialsDiscordLink ess;
|
|
||||||
private final Map<String, InteractionRole> groupToRoleMap = new HashMap<>();
|
|
||||||
private final Map<String, String> roleIdToGroupMap = new HashMap<>();
|
|
||||||
|
|
||||||
public RoleSyncManager(final EssentialsDiscordLink ess) {
|
|
||||||
this.ess = ess;
|
|
||||||
Bukkit.getPluginManager().registerEvents(this, ess);
|
|
||||||
onReload();
|
|
||||||
this.ess.getEss().runTaskTimerAsynchronously(() -> {
|
|
||||||
if (groupToRoleMap.isEmpty() && roleIdToGroupMap.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final BiMap<String, String> uuidToDiscordCopy = ess.getAccountStorage().getRawStorageMap();
|
|
||||||
final Map<String, InteractionRole> groupToRoleMapCopy = new HashMap<>(groupToRoleMap);
|
|
||||||
final Map<String, String> roleIdToGroupMapCopy = new HashMap<>(roleIdToGroupMap);
|
|
||||||
final boolean primaryOnly = ess.getSettings().isRoleSyncPrimaryGroupOnly();
|
|
||||||
final boolean removeGroups = ess.getSettings().isRoleSyncRemoveGroups();
|
|
||||||
final boolean removeRoles = ess.getSettings().isRoleSyncRemoveRoles();
|
|
||||||
for (final Map.Entry<String, String> entry : uuidToDiscordCopy.entrySet()) {
|
|
||||||
sync(new UUIDPlayer(UUID.fromString(entry.getKey())), entry.getValue(), groupToRoleMapCopy, roleIdToGroupMapCopy, primaryOnly, removeGroups, removeRoles);
|
|
||||||
}
|
|
||||||
}, 0, ess.getSettings().getRoleSyncResyncDelay() * 1200L);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sync(final UUID uuid, final String discordId) {
|
|
||||||
final Map<String, InteractionRole> groupToRoleMapCopy = new HashMap<>(groupToRoleMap);
|
|
||||||
final Map<String, String> roleIdToGroupMapCopy = new HashMap<>(roleIdToGroupMap);
|
|
||||||
final boolean primaryOnly = ess.getSettings().isRoleSyncPrimaryGroupOnly();
|
|
||||||
final boolean removeGroups = ess.getSettings().isRoleSyncRemoveGroups();
|
|
||||||
final boolean removeRoles = ess.getSettings().isRoleSyncRemoveRoles();
|
|
||||||
sync(new UUIDPlayer(uuid), discordId, groupToRoleMapCopy, roleIdToGroupMapCopy, primaryOnly, removeGroups, removeRoles);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sync(final Player player, final String discordId, final Map<String, InteractionRole> groupToRoleMap, final Map<String, String> roleIdToGroupMap,
|
|
||||||
final boolean primaryOnly, final boolean removeGroups, final boolean removeRoles) {
|
|
||||||
final List<String> groups = primaryOnly ?
|
|
||||||
Collections.singletonList(ess.getEss().getPermissionsHandler().getGroup(player)) : ess.getEss().getPermissionsHandler().getGroups(player);
|
|
||||||
final InteractionMember member = ess.getApi().getMemberById(discordId).join();
|
|
||||||
|
|
||||||
if (member == null) {
|
|
||||||
if (ess.getSettings().isUnlinkOnLeave()) {
|
|
||||||
ess.getLinkManager().removeAccount(ess.getEss().getUser(player), DiscordLinkStatusChangeEvent.Cause.UNSYNC_LEAVE);
|
|
||||||
} else {
|
|
||||||
unSync(player.getUniqueId(), discordId);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final List<InteractionRole> toAdd = new ArrayList<>();
|
|
||||||
final List<InteractionRole> toRemove = new ArrayList<>();
|
|
||||||
|
|
||||||
for (final Map.Entry<String, InteractionRole> entry : groupToRoleMap.entrySet()) {
|
|
||||||
if (groups.contains(entry.getKey()) && !member.hasRole(entry.getValue())) {
|
|
||||||
toAdd.add(entry.getValue());
|
|
||||||
} else if (removeRoles && !groups.contains(entry.getKey()) && member.hasRole(entry.getValue())) {
|
|
||||||
toRemove.add(entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Map.Entry<String, String> entry : roleIdToGroupMap.entrySet()) {
|
|
||||||
if (member.hasRole(entry.getKey()) && !groups.contains(entry.getValue())) {
|
|
||||||
ess.getEss().getPermissionsHandler().addToGroup(player, entry.getValue());
|
|
||||||
} else if (removeGroups && !member.hasRole(entry.getKey()) && groups.contains(entry.getValue())) {
|
|
||||||
ess.getEss().getPermissionsHandler().removeFromGroup(player, entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toAdd.isEmpty() && toRemove.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ess.getApi().modifyMemberRoles(member, toAdd, toRemove);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unSync(final UUID uuid, final String discordId) {
|
|
||||||
final boolean removeGroups = ess.getSettings().isRoleSyncRemoveGroups();
|
|
||||||
final boolean removeRoles = ess.getSettings().isRoleSyncRemoveRoles();
|
|
||||||
if (!removeGroups && !removeRoles) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, InteractionRole> groupToRoleMapCopy = new HashMap<>(groupToRoleMap);
|
|
||||||
final Map<String, String> roleIdToGroupMapCopy = new HashMap<>(roleIdToGroupMap);
|
|
||||||
|
|
||||||
final Player player = new UUIDPlayer(uuid);
|
|
||||||
final InteractionMember member = ess.getApi().getMemberById(discordId).join();
|
|
||||||
|
|
||||||
if (removeGroups) {
|
|
||||||
for (final String group : roleIdToGroupMapCopy.values()) {
|
|
||||||
ess.getEss().getPermissionsHandler().removeFromGroup(player, group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the member is no longer in the guild (null), they don't have any roles anyway.
|
|
||||||
if (removeRoles && member != null) {
|
|
||||||
ess.getApi().modifyMemberRoles(member, null, groupToRoleMapCopy.values());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onJoin(PlayerJoinEvent event) {
|
|
||||||
ess.getEss().runTaskAsynchronously(() -> {
|
|
||||||
if (ess.getLinkManager().isLinked(event.getPlayer().getUniqueId())) {
|
|
||||||
sync(event.getPlayer().getUniqueId(), ess.getLinkManager().getDiscordId(event.getPlayer().getUniqueId()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onReload() {
|
|
||||||
groupToRoleMap.clear();
|
|
||||||
roleIdToGroupMap.clear();
|
|
||||||
|
|
||||||
final List<String> groups = ess.getEss().getPermissionsHandler().getGroups();
|
|
||||||
|
|
||||||
for (final Map.Entry<String, String> entry : ess.getSettings().getRoleSyncGroups().entrySet()) {
|
|
||||||
if (isExampleRole(entry.getValue())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String group = entry.getKey();
|
|
||||||
final InteractionRole role = ess.getApi().getRole(entry.getValue());
|
|
||||||
if (!groups.contains(group)) {
|
|
||||||
ess.getLogger().warning(tl("discordLinkInvalidGroup", group, entry.getValue(), groups));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (role == null) {
|
|
||||||
ess.getLogger().warning(tl("discordLinkInvalidRole", entry.getValue(), group));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (role.isManaged() || role.isPublicRole()) {
|
|
||||||
ess.getLogger().warning(tl("discordLinkInvalidRoleManaged", role.getName(), role.getId()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!role.canInteract()) {
|
|
||||||
ess.getLogger().warning(tl("discordLinkInvalidRoleInteract", role.getName(), role.getId()));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
groupToRoleMap.put(group, role);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (final Map.Entry<String, String> entry : ess.getSettings().getRoleSyncRoles().entrySet()) {
|
|
||||||
if (isExampleRole(entry.getKey())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final InteractionRole role = ess.getApi().getRole(entry.getKey());
|
|
||||||
final String group = entry.getValue();
|
|
||||||
if (role == null) {
|
|
||||||
ess.getLogger().warning(tl("discordLinkInvalidRole", entry.getKey(), group));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!groups.contains(group)) {
|
|
||||||
ess.getLogger().warning(tl("discordLinkInvalidGroup", group, entry.getKey(), groups));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
roleIdToGroupMap.put(role.getId(), group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isExampleRole(final String role) {
|
|
||||||
return role.equals("0") || role.equals("11111111111111111") || role.equals("22222222222222222") || role.equals("33333333333333333");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
#############################################################
|
|
||||||
# +-------------------------------------------------------+ #
|
|
||||||
# | EssentialsX DiscordLink | #
|
|
||||||
# +-------------------------------------------------------+ #
|
|
||||||
#############################################################
|
|
||||||
|
|
||||||
# This is the config file for EssentialsXDiscordLink.
|
|
||||||
# This config was generated for version ${full.version}.
|
|
||||||
|
|
||||||
# The desired behavior when a player hasn't linked their Minecraft account to Discord.
|
|
||||||
# Accepts the following values:
|
|
||||||
# - kick: Kicks the player with a link code and requires they link their discord account before they can join.
|
|
||||||
# - freeze: Prevents player from moving/interacting/doing commands when they join until they link their discord account.
|
|
||||||
# - none: Places no restrictions on players for unlinked accounts.
|
|
||||||
link-policy: none
|
|
||||||
|
|
||||||
# Whether to ignore Discord messages from unlinked members and hide them from Minecraft chat.
|
|
||||||
block-unlinked-chat: false
|
|
||||||
|
|
||||||
# Whether someone's Minecraft account should be unlinked when they leave the Discord server.
|
|
||||||
unlink-on-leave: true
|
|
||||||
|
|
||||||
# Whether linked player's incoming mail should be DM'd to them on Discord.
|
|
||||||
relay-mail: true
|
|
||||||
|
|
||||||
# MC group to Discord role sync settings
|
|
||||||
# Allows for the ability to give players discord roles based on their Minecraft groups and/or give players Minecraft
|
|
||||||
# groups based on their Discord roles.
|
|
||||||
role-sync:
|
|
||||||
# Whether EssentialsX DiscordLink should remove synced Discord roles from players who unlink their Minecraft account,
|
|
||||||
# leave the Discord server, or who no longer have the groups that awarded them the role in the first place.
|
|
||||||
remove-roles: true
|
|
||||||
# Whether EssentialsX DiscordLink should remove synced Minecraft groups from players who unlink their Discord account,
|
|
||||||
# or who no longer have the Discord roles that awarded them the group in the first place.
|
|
||||||
remove-groups: true
|
|
||||||
# The amount of time (in minutes) between which EssentialsX DiscordLink should audit player groups/roles.
|
|
||||||
# Requires a restart after changing.
|
|
||||||
resync-delay: 5
|
|
||||||
# Whether EssentialsX DiscordLink should only consider the primary group of Minecraft users
|
|
||||||
primary-group-only: false
|
|
||||||
# Minecraft group to Discord role ID synchronization.
|
|
||||||
# Players in the following groups listed here will receive the corresponding role ID on discord when they link
|
|
||||||
# their Minecraft account to their Discord account.
|
|
||||||
groups:
|
|
||||||
default: 000000000000000000
|
|
||||||
admin: 11111111111111111
|
|
||||||
# Discord role ID to Minecraft group synchronization.
|
|
||||||
# Users with the following roles listed here will receive the corresponding group in Minecraft when they link
|
|
||||||
# their Discord account to their Minecraft account.
|
|
||||||
roles:
|
|
||||||
22222222222222222: vip
|
|
||||||
33333333333333333: booster
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
name: EssentialsDiscordLink
|
|
||||||
main: net.essentialsx.discordlink.EssentialsDiscordLink
|
|
||||||
# Note to developers: This next line cannot change, or the automatic versioning system will break.
|
|
||||||
version: ${full.version}
|
|
||||||
website: https://essentialsx.net/
|
|
||||||
description: EssentialsX Discord addon which allows you link your Minecraft and Discord accounts together.
|
|
||||||
authors: [JRoy]
|
|
||||||
depend: [EssentialsDiscord]
|
|
||||||
api-version: 1.13
|
|
||||||
commands:
|
|
||||||
link:
|
|
||||||
description: Generates a code to link your Minecraft account to Discord.
|
|
||||||
usage: /<command>
|
|
||||||
aliases: [elink, discordlink, ediscordlink]
|
|
||||||
unlink:
|
|
||||||
description: Unlinks your Minecraft account from any associated Discord account.
|
|
||||||
usage: /<command>
|
|
||||||
aliases: [eunlink, discordunlink, ediscordunlink]
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("essentials.module-conventions")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly project(':EssentialsX')
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
archiveFileName = "EssentialsXSpawn-OG-${rootProject.ext.FULL_VERSION}.jar"
|
|
||||||
destinationDirectory = file("${rootProject.buildDir}/libs")
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
plugins {
|
||||||
|
id("essentials.module-conventions")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(project(":EssentialsX"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.jar {
|
||||||
|
archiveFileName.set("EssentialsXSpawn-OG-${rootProject.extra["FULL_VERSION"]}.jar")
|
||||||
|
destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs"))
|
||||||
|
}
|
||||||
|
|
||||||
@@ -7,7 +7,11 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("net.kyori", "indra-common", "3.1.1")
|
implementation("net.kyori:indra-common:3.1.1")
|
||||||
implementation("com.github.johnrengelman", "shadow", "8.1.1")
|
implementation("xyz.jpenilla:run-task:2.1.0")
|
||||||
implementation("xyz.jpenilla", "run-task", "2.1.0")
|
|
||||||
|
implementation(
|
||||||
|
"com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:8.1.1"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("essentials.module-conventions")
|
id("essentials.module-conventions")
|
||||||
id("com.github.johnrengelman.shadow")
|
id("com.github.johnrengelman.shadow")
|
||||||
@@ -8,10 +10,11 @@ tasks {
|
|||||||
archiveClassifier.set("unshaded")
|
archiveClassifier.set("unshaded")
|
||||||
}
|
}
|
||||||
shadowJar {
|
shadowJar {
|
||||||
archiveClassifier.set(null)
|
archiveClassifier.set("") // Empty String, NOT null.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extensions.configure<EssentialsModuleExtension> {
|
extensions.configure<EssentialsModuleExtension> {
|
||||||
archiveFile.set(tasks.shadowJar.flatMap { it.archiveFile })
|
archiveFile.set(tasks.named<ShadowJar>("shadowJar").flatMap { it.archiveFile })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("eclipse")
|
|
||||||
id("essentials.parent-build-logic")
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "net.essentialsx"
|
|
||||||
version = "2.21.0-SNAPSHOT"
|
|
||||||
|
|
||||||
ext {
|
|
||||||
GIT_COMMIT = !indraGit.isPresent() ? "unknown" : indraGit.commit().abbreviate(7).name()
|
|
||||||
GIT_DEPTH = GitUtil.commitsSinceLastTag(project)
|
|
||||||
GIT_BRANCH = GitUtil.headBranchName(project)
|
|
||||||
|
|
||||||
FULL_VERSION = "${version}".replace("-SNAPSHOT", "-dev+${GIT_DEPTH}-${GIT_COMMIT}")
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
plugins {
|
||||||
|
eclipse
|
||||||
|
id("essentials.parent-build-logic")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "net.essentialsx"
|
||||||
|
version = "2.21.0-SNAPSHOT"
|
||||||
|
|
||||||
|
val gitCommit: String = indraGit.commit()?.abbreviate(7)?.name() ?: "unknown"
|
||||||
|
val gitDepth: Int = GitUtil.commitsSinceLastTag(project) ?: 0
|
||||||
|
val gitBranch: String = GitUtil.headBranchName(project) ?: "detached"
|
||||||
|
|
||||||
|
extra.apply {
|
||||||
|
this["GIT_COMMIT"] = gitCommit
|
||||||
|
this["GIT_DEPTH"] = gitDepth
|
||||||
|
this["GIT_BRANCH"] = gitBranch
|
||||||
|
this["FULL_VERSION"] =
|
||||||
|
version.toString().replace("-SNAPSHOT", "-dev+${gitDepth}-${gitCommit}")
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("essentials.base-conventions")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
api project(':providers:NMSReflectionProvider')
|
|
||||||
}
|
|
||||||
|
|
||||||
essentials {
|
|
||||||
injectBukkitApi.set(false)
|
|
||||||
injectBstats.set(false)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
plugins { id("essentials.base-conventions") }
|
||||||
|
|
||||||
|
dependencies { api(project(":providers:NMSReflectionProvider")) }
|
||||||
|
|
||||||
|
essentials {
|
||||||
|
injectBukkitApi.set(false)
|
||||||
|
injectBstats.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("essentials.base-conventions")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(':providers:BaseProviders')) {
|
|
||||||
exclude group: "org.spigotmc", module: "spigot-api"
|
|
||||||
}
|
|
||||||
implementation 'org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT'
|
|
||||||
}
|
|
||||||
|
|
||||||
essentials {
|
|
||||||
injectBukkitApi.set(false)
|
|
||||||
injectBstats.set(false)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
plugins { id("essentials.base-conventions") }
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":providers:BaseProviders")) {
|
||||||
|
exclude(group = "org.spigotmc", module = "spigot-api")
|
||||||
|
}
|
||||||
|
implementation("org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT")
|
||||||
|
}
|
||||||
|
|
||||||
|
essentials {
|
||||||
|
injectBukkitApi.set(false)
|
||||||
|
injectBstats.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("essentials.base-conventions")
|
|
||||||
}
|
|
||||||
|
|
||||||
essentials {
|
|
||||||
injectBstats.set(false)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
plugins { id("essentials.base-conventions") }
|
||||||
|
|
||||||
|
essentials { injectBstats.set(false) }
|
||||||
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("essentials.base-conventions")
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(':providers:BaseProviders')
|
|
||||||
api 'org.bukkit:bukkit:1.12.2-R0.1-SNAPSHOT'
|
|
||||||
}
|
|
||||||
|
|
||||||
essentials {
|
|
||||||
injectBukkitApi.set(false)
|
|
||||||
injectBstats.set(false)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
plugins { id("essentials.base-conventions") }
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":providers:BaseProviders"))
|
||||||
|
api("org.bukkit:bukkit:1.12.2-R0.1-SNAPSHOT")
|
||||||
|
}
|
||||||
|
|
||||||
|
essentials {
|
||||||
|
injectBukkitApi.set(false)
|
||||||
|
injectBstats.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id("essentials.base-conventions")
|
|
||||||
}
|
|
||||||
|
|
||||||
java {
|
|
||||||
disableAutoTargetJvm()
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(project(':providers:BaseProviders')) {
|
|
||||||
exclude(module: 'spigot-api')
|
|
||||||
}
|
|
||||||
compileOnly 'io.papermc.paper:paper-api:1.18.2-R0.1-SNAPSHOT'
|
|
||||||
compileOnly 'io.papermc.paper:paper-mojangapi:1.18.2-R0.1-SNAPSHOT'
|
|
||||||
}
|
|
||||||
|
|
||||||
essentials {
|
|
||||||
injectBukkitApi.set(false)
|
|
||||||
injectBstats.set(false)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
plugins { id("essentials.base-conventions") }
|
||||||
|
|
||||||
|
java { disableAutoTargetJvm() }
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(project(":providers:BaseProviders")) {
|
||||||
|
exclude(module = "spigot-api")
|
||||||
|
}
|
||||||
|
compileOnly("org.purpurmc.purpur:purpur-api:1.19.4-R0.1-SNAPSHOT")
|
||||||
|
compileOnly("io.papermc.paper:paper-mojangapi:1.19.4-R0.1-SNAPSHOT")
|
||||||
|
}
|
||||||
|
|
||||||
|
essentials {
|
||||||
|
injectBukkitApi.set(false)
|
||||||
|
injectBstats.set(false)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
dependencyResolutionManagement {
|
dependencyResolutionManagement {
|
||||||
repositories {
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven("https://repo.purpurmc.org/snapshots/")
|
||||||
maven("https://hub.spigotmc.org/nexus/content/groups/public/")
|
maven("https://hub.spigotmc.org/nexus/content/groups/public/")
|
||||||
maven("https://repo.papermc.io/repository/maven-public/")
|
maven("https://repo.papermc.io/repository/maven-public/")
|
||||||
maven("https://jitpack.io") {
|
maven("https://jitpack.io") {
|
||||||
|
|||||||
Reference in new issue
Block a user