This commit is contained in:
skidoodle 2024-03-13 01:05:21 +01:00
commit 3f8830dce7
65 changed files with 4561 additions and 0 deletions

30
.gitignore vendored Normal file
View file

@ -0,0 +1,30 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# Virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*
# IntelliJ files
/YourSiteeCore.iml
/target/
/.idea/
/dependency-reduced-pom.xml

2
README.md Normal file
View file

@ -0,0 +1,2 @@
# ysmp-core
A core plugin for the YourSitee SMP Minecraft server.

83
pom.xml Normal file
View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dev.inventex.yoursitee.core</groupId>
<artifactId>YourSiteeCore</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<!-- SpigotMC repository -->
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
</repositories>
<dependencies>
<!--This adds the Spigot API artifact to the build -->
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jetbrains/annotations -->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>24.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.12.14</version>
</dependency>
<dependency>
<groupId>dev.inventex.octa</groupId>
<artifactId>Octa</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>dev.inventex.discord</groupId>
<artifactId>DiscordClient</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>ysmp-core</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,15 @@
package ee.yoursit.core;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin {
@Override
public void onEnable() {
YsmpCore.INSTANCE.enable(this);
}
@Override
public void onDisable() {
YsmpCore.INSTANCE.disable();
}
}

View file

@ -0,0 +1,119 @@
package ee.yoursit.core;
import dev.inventex.discord.DiscordClient;
import dev.inventex.discord.DiscordClientBuilder;
import ee.yoursit.core.config.Config;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.DataManager;
import ee.yoursit.core.data.PlayerData;
import ee.yoursit.core.database.Database;
import ee.yoursit.core.database.service.*;
import ee.yoursit.core.module.*;
import lombok.Getter;
import net.dv8tion.jda.api.requests.GatewayIntent;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.plugin.java.JavaPlugin;
import javax.security.auth.login.LoginException;
@Getter
public enum YsmpCore {
INSTANCE;
private JavaPlugin plugin;
private Config config, messages;
private DataManager dataManager;
private Database database;
private Location spawnLoc, verifyLoc;
private UserService userService;
private GroupService groupService;
private InviteService inviteService;
private BanService banService;
private VerificationService verificationService;
private CommandRegistry bukkitCommands, discordCommands;
private DiscordClient client;
public void enable(JavaPlugin plugin) {
this.plugin = plugin;
loadConfig();
loadDatabase();
createDiscordClient();
loadCommands();
Bukkit
.getPluginManager()
.registerEvents(new BukkitListener(), plugin);
}
private void loadConfig() {
plugin.getDataFolder().mkdir();
config = new Config("config");
messages = new Config("messages");
config.load();
messages.load();
Messages.init(messages);
Bukkit
.getScheduler()
.runTask(plugin, () -> {
spawnLoc = config.getLocation("spawn");
verifyLoc = config.getLocation("verify");
});
}
private void loadDatabase() {
dataManager = new DataManager();
database = new Database("127.0.0.1", 27017);
database.connect();
userService = new UserService();
groupService = new GroupService();
groupService.loadGroups();
inviteService = new InviteService();
banService = new BanService();
verificationService = new VerificationService();
}
private void loadCommands() {
bukkitCommands = new BukkitCommandRegistry();
bukkitCommands.load();
discordCommands = new DiscordCommandRegistry();
}
private void createDiscordClient() {
try {
String token = config.getString("bot-token");
client = new DiscordClientBuilder(token)
.useMembers()
.useRoles()
.enableIntents(GatewayIntent.MESSAGE_CONTENT)
.build();
client.registerEvents(new DiscordListener());
} catch (LoginException e) {
System.err.println("Unable to create discord client");
e.printStackTrace();
}
}
public void disable() {
database.disconnect();
bukkitCommands.unload();
discordCommands.unload();
client.getJda().shutdown();
}
}

View file

@ -0,0 +1,145 @@
package ee.yoursit.core.command.bukkit.api;
import lombok.Getter;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
@Getter
public class Argument {
@NotNull
private final String name;
@NotNull
private final String[] aliases;
private final boolean consoleAllowed;
@NotNull
private final List<Argument> subArguments = new ArrayList<>();
public Argument() {
CommandInfo info = getClass().getAnnotation(CommandInfo.class);
if (info == null)
throw new IllegalStateException(getClass().getName() + " is not annotated with "
+ CommandInfo.class);
name = info.name();
aliases = info.aliases();
consoleAllowed = info.consoleAllowed();
create();
}
public void create() {
}
public void onCommand(@NotNull CommandContext context) throws Exception {
}
public Completion onTabComplete(@NotNull CommandContext context) throws Exception {
return Completion.EMPTY;
}
public void onCommandError(@NotNull CommandContext context, @NotNull Throwable error) {
throw new CommandException("Error whilst handling command execution", error);
}
public Completion onTabCompleteError(@NotNull CommandContext context, @NotNull Throwable error) {
throw new CommandException("Error whilst handling command tab completion", error);
}
private void executeCommand(@NotNull CommandContext context) {
try {
onCommand(context);
} catch (Exception e) {
onCommandError(context, e);
}
}
private Completion executeTabCompletion(@NotNull CommandContext context) {
try {
return onTabComplete(context);
} catch (Exception e) {
return onTabCompleteError(context, e);
}
}
public final void addArgument(@NotNull Argument argument) {
subArguments.add(argument);
}
public final void execute(@NotNull CommandSender sender, @NotNull String[] args) {
if (!(sender instanceof Player) && !consoleAllowed) {
sender.sendMessage(ChatColor.RED + "Console is not allowed to execute this command.");
return;
}
if (args.length == 0 || subArguments.isEmpty()) {
executeCommand(new CommandContext(sender, args));
return;
}
String name = args[0];
for (Argument argument : subArguments) {
if (!argument.test(name))
continue;
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
argument.execute(sender, subArgs);
return;
}
executeCommand(new CommandContext(sender, args));
}
public final Completion complete(@NotNull CommandSender sender, @NotNull String[] args) {
if (subArguments.isEmpty())
return executeTabCompletion(new CommandContext(sender, args));
if (args.length == 0) {
Stream<String> stream = subArguments
.stream()
.map(Argument::getName);
return Completion.of(stream);
}
else if (args.length == 1) {
String prefix = args[0].toLowerCase();
Stream<String> stream = subArguments
.stream()
.map(Argument::getName)
.filter(name -> name.startsWith(prefix));
return Completion.of(stream);
}
else {
String name = args[0].toLowerCase();
for (Argument argument : subArguments) {
if (!argument.test(name))
continue;
String[] subArgs = Arrays.copyOfRange(args, 1, args.length);
return argument.complete(sender, subArgs);
}
}
return executeTabCompletion(new CommandContext(sender, args));
}
public final boolean test(@NotNull String name) {
if (name.equals(this.name))
return true;
for (String alias : aliases) {
if (name.equals(alias))
return true;
}
return false;
}
}

View file

@ -0,0 +1,30 @@
package ee.yoursit.core.command.bukkit.api;
import ee.yoursit.core.YsmpCore;
import org.bukkit.command.PluginCommand;
import java.util.Arrays;
public class Command extends Argument {
public final void register() {
PluginCommand command = YsmpCore.INSTANCE.getPlugin().getCommand(getName());
if (command == null)
throw new IllegalStateException("Command '" + getName() + "' is not registered in the plugin.yml");
command.setExecutor((sender, cmd, label, args) -> {
execute(sender, args);
return true;
});
command.setTabCompleter((sender, cmd, label, args) -> complete(sender, args).getData());
command.setAliases(Arrays.asList(getAliases()));
}
public final void unregister() {
PluginCommand command = YsmpCore.INSTANCE.getPlugin().getCommand(getName());
if (command == null)
throw new IllegalStateException("Command '" + getName() + "' is not registered in the plugin.yml");
command.setExecutor(null);
command.setTabCompleter(null);
}
}

View file

@ -0,0 +1,147 @@
package ee.yoursit.core.command.bukkit.api;
import dev.inventex.octa.data.convertible.Convertible;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@AllArgsConstructor
public class CommandContext {
@NotNull
@Getter
private final CommandSender sender;
@NotNull
private final String[] args;
public boolean isPlayer() {
return sender instanceof Player;
}
@NotNull
public Player getPlayer() {
if (!isPlayer())
throw new IllegalStateException("CommandSender is not a player");
return (Player) sender;
}
public boolean isEmpty() {
return args.length == 0;
}
public int size() {
return args.length;
}
@NotNull
public String[] getData() {
return args;
}
@Nullable
public String getStringRaw(int index) {
return index >= 0 && index < args.length
? args[index]
: null;
}
@NotNull
public Convertible<String, String> getString(int index) {
return index >= 0 && index < args.length
? Convertible.completed(args[index])
: Convertible.empty();
}
@NotNull
public Convertible<String, Integer> getInt(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], Integer::parseInt)
: Convertible.empty();
}
@NotNull
public Convertible<String, Float> getFloat(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], Float::parseFloat)
: Convertible.empty();
}
@NotNull
public Convertible<String, Double> getDouble(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], Double::parseDouble)
: Convertible.empty();
}
@NotNull
public Convertible<String, Boolean> getBoolean(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], value -> switch (value.toLowerCase()) {
case "enable", "allow", "true", "yes", "on", "y" -> true;
default -> false;
})
: Convertible.empty();
}
@NotNull
public Convertible<String, Player> getPlayer(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], Bukkit::getPlayer)
: Convertible.empty();
}
@NotNull
public Convertible<String, Player> getPlayerExact(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], Bukkit::getPlayerExact)
: Convertible.empty();
}
@NotNull
public Convertible<String, World> getWorld(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], Bukkit::getWorld)
: Convertible.empty();
}
public Convertible<String, Material> getMaterial(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], s -> Material.getMaterial(s.toUpperCase()))
: Convertible.empty();
}
public Convertible<String, PotionEffectType> getPotionEffectByName(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], s -> PotionEffectType.getByName(s.toUpperCase()))
: Convertible.empty();
}
public Convertible<String, PotionEffectType> getPotionEffectById(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], s -> PotionEffectType.getById(Integer.parseInt(s)))
: Convertible.empty();
}
public Convertible<String, PotionEffectType> getPotionEffect(int index) {
return index >= 0 && index < args.length
? Convertible.of(args[index], s -> {
PotionEffectType type = PotionEffectType.getByName(s.toUpperCase());
if (type != null)
return type;
return PotionEffectType.getById(Integer.parseInt(s));
})
: Convertible.empty();
}
public void reply(String message) {
sender.sendMessage(message);
}
}

View file

@ -0,0 +1,22 @@
package ee.yoursit.core.command.bukkit.api;
public class CommandException extends RuntimeException {
public CommandException() {
}
public CommandException(String message) {
super(message);
}
public CommandException(String message, Throwable cause) {
super(message, cause);
}
public CommandException(Throwable cause) {
super(cause);
}
public CommandException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View file

@ -0,0 +1,16 @@
package ee.yoursit.core.command.bukkit.api;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CommandInfo {
String name();
String[] aliases() default {};
boolean consoleAllowed() default true;
}

View file

@ -0,0 +1,46 @@
package ee.yoursit.core.command.bukkit.api;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@AllArgsConstructor
public class Completion {
public final static Completion EMPTY = of(Collections.emptyList());
public final static Completion PLAYERS = of((List<String>) null);
@Nullable
@Getter
private final List<String> data;
public Completion push(@NotNull String element) {
assert data != null : "Cannot push element to PLAYER_LIST completion";
data.add(element);
return this;
}
public static Completion of(List<String> data) {
return new Completion(data != null ? new ArrayList<>(data) : null);
}
public static Completion of(String... data) {
return new Completion(new ArrayList<>(Arrays.asList(data)));
}
public static Completion of(Stream<String> data) {
return new Completion(data != null ? data.collect(Collectors.toList()) : null);
}
public static Completion empty() {
return new Completion(new ArrayList<>());
}
}

View file

@ -0,0 +1,322 @@
package ee.yoursit.core.command.bukkit.impl.admin;
import dev.inventex.octa.concurrent.future.Future;
import dev.inventex.octa.data.convertible.Convertible;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.command.bukkit.api.Completion;
import ee.yoursit.core.command.bukkit.impl.admin.group.CreateArgument;
import ee.yoursit.core.command.bukkit.impl.admin.group.DeleteArgument;
import ee.yoursit.core.command.bukkit.impl.admin.group.ListArgument;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.database.entity.impl.Group;
import ee.yoursit.core.database.entity.impl.User;
import ee.yoursit.core.util.Message;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@CommandInfo(name = "group")
public class GroupCommand extends Command {
@Override
public void create() {
addArgument(new CreateArgument());
addArgument(new DeleteArgument());
addArgument(new ListArgument());
}
// TODO reword command system, so I don't need to do these manually >:(
@Override
public void onCommand(@NotNull CommandContext context) throws Exception {
if (!(context.getSender().hasPermission("ysmp.admin"))) {
context.reply(Messages.NO_PERMISSION);
return;
}
Convertible<String, String> groupArg = context.getString(0);
if (groupArg.isEmpty()) {
context.reply(Messages.GROUP_USAGE);
return;
}
String name = groupArg.get();
YsmpCore.INSTANCE
.getGroupService()
.getGroup(name)
.tryThen(group -> handleGroup(context, group))
.except(err ->
context.reply(Messages.GROUP_NOT_EXISTS
.replace("{group}", name)));
}
private void handleGroup(CommandContext context, Group group) throws Exception {
Convertible<String, String> commandArg = context.getString(1);
if (commandArg.isEmpty()) {
context.reply(Messages.GROUP_USAGE);
return;
}
switch (commandArg.get()) {
case "meta" -> handleGroupMeta(context, group);
case "permission" -> handleGroupPermission(context, group);
case "grant" -> handleGroupGrant(context, group);
default -> context.reply(Messages.GROUP_USAGE);
}
}
private void handleGroupMeta(CommandContext context, Group group) throws Exception {
Convertible<String, String> metaTypeArg = context.getString(2);
if (metaTypeArg.isEmpty()) {
context.reply(Messages.GROUP_META_PREFIX_USAGE);
return;
}
switch (metaTypeArg.get()) {
case "prefix" -> handleMetaPrefix(context, group);
case "suffix" -> handleMetaSuffix(context, group);
case "tabprefix" -> handleMetaTabPrefix(context, group);
case "tabsuffix" -> handleMetaTabSuffix(context, group);
case "info" -> handleMetaInfo(context, group);
default -> context.reply(Messages.GROUP_META_USAGE);
}
}
private void handleMetaPrefix(CommandContext context, Group group) throws Exception {
if (context.getString(3).isEmpty()) {
context.reply(Messages.GROUP_META_PREFIX_USAGE);
return;
}
String prefix = Arrays
.stream(context.getData())
.skip(3)
.collect(Collectors.joining(" "));
group.getPrefix().set(prefix);
prefix = Message.translate(prefix);
context.reply(Messages.GROUP_META_PREFIX_SET
.replace("{group}", group.getName().get())
.replace("{prefix}", prefix));
}
private void handleMetaSuffix(CommandContext context, Group group) throws Exception {
if (context.getString(3).isEmpty()) {
context.reply(Messages.GROUP_META_SUFFIX_USAGE);
return;
}
String suffix = Arrays
.stream(context.getData())
.skip(3)
.collect(Collectors.joining(" "));
group.getSuffix().set(suffix);
suffix = Message.translate(suffix);
context.reply(Messages.GROUP_META_SUFFIX_SET
.replace("{group}", group.getName().get())
.replace("{suffix}", suffix));
}
private void handleMetaTabPrefix(CommandContext context, Group group) throws Exception {
if (context.getString(3).isEmpty()) {
context.reply(Messages.GROUP_META_TAB_PREFIX_USAGE);
return;
}
String tabPrefix = Arrays
.stream(context.getData())
.skip(3)
.collect(Collectors.joining(" "));
group.getTabPrefix().set(tabPrefix);
tabPrefix = Message.translate(tabPrefix);
context.reply(Messages.GROUP_META_TAB_PREFIX_SET
.replace("{group}", group.getName().get())
.replace("{tabprefix}", tabPrefix));
}
private void handleMetaTabSuffix(CommandContext context, Group group) throws Exception {
if (context.getString(3).isEmpty()) {
context.reply(Messages.GROUP_META_TAB_SUFFIX_USAGE);
return;
}
String tabSuffix = Arrays
.stream(context.getData())
.skip(3)
.collect(Collectors.joining(" "));
group.getTabSuffix().set(tabSuffix);
tabSuffix = Message.translate(tabSuffix);
context.reply(Messages.GROUP_META_TAB_SUFFIX_SET
.replace("{group}", group.getName().get())
.replace("{tabsuffix}", tabSuffix));
}
private void handleMetaInfo(CommandContext context, Group group) {
String name = group.getName().get();
String prefix = group.getPrefix().get();
String suffix = group.getSuffix().get();
String tabPrefix = group.getTabPrefix().get();
String tabSuffix = group.getTabSuffix().get();
String message = Messages.GROUP_META_INFO
.replace("{group}", Message.translate(name))
.replace("{prefix}", Message.translate(prefix != null ? prefix : "not set"))
.replace("{suffix}", Message.translate(suffix != null ? suffix : "not set"))
.replace("{tabprefix}", Message.translate(tabPrefix != null ? tabPrefix : "not set"))
.replace("{tabsuffix}", Message.translate(tabSuffix != null ? tabSuffix : "not set"));
context.reply(message);
}
private void handleGroupPermission(CommandContext context, Group group) throws Exception {
Convertible<String, String> actionArg = context.getString(4);
if (actionArg.isEmpty()) {
context.reply(Messages.GROUP_PERMISSION_USAGE);
return;
}
switch (actionArg.get()) {
case "set" -> handleGroupPermissionSet(context, group);
case "unset" -> handleGroupPermissionUnset(context, group);
default -> context.reply(Messages.GROUP_PERMISSION_USAGE);
}
}
private void handleGroupPermissionSet(CommandContext context, Group group) throws Exception {
Convertible<String, String> permissionArg = context.getString(5);
if (permissionArg.isEmpty()) {
context.reply(Messages.GROUP_PERMISSION_SET_USAGE);
return;
}
Convertible<String, Boolean> stateArg = context.getBoolean(6);
if (stateArg.isEmpty()) {
context.reply(Messages.GROUP_PERMISSION_SET_USAGE);
return;
}
String permission = permissionArg.get();
Boolean state = stateArg.get();
group.getPermissions().set(permission, state);
context.reply(Messages.GROUP_PERMISSION_SET_UPDATED
.replace("{group}", group.getName().get())
.replace("{permission}", permission)
.replace("{state}", String.valueOf(state)));
}
private void handleGroupPermissionUnset(CommandContext context, Group group) throws Exception {
Convertible<String, String> permissionArg = context.getString(5);
if (permissionArg.isEmpty()) {
context.reply(Messages.GROUP_PERMISSION_UNSET_USAGE);
return;
}
String permission = permissionArg.get();
group.getPermissions().unset(permission);
context.reply(Messages.GROUP_PERMISSION_UNSET_UPDATED
.replace("{group}", group.getName().get())
.replace("{permission}", permission));
}
private void handleGroupGrant(CommandContext context, Group group) throws Exception {
Convertible<String, String> playerArg = context.getString(2);
if (playerArg.isEmpty()) {
context.reply(Messages.GROUP_GRANT_USAGE);
return;
}
String player = playerArg.get();
Future<User> future;
Player onlinePlayer = Bukkit.getPlayerExact(player);
if (onlinePlayer != null) {
future = YsmpCore.INSTANCE
.getDataManager()
.get(onlinePlayer)
.getUser();
} else {
future = YsmpCore.INSTANCE
.getUserService()
.getUser(player);
}
future
.then(user -> {
user.getGroup().set(group.getName().get());
context.reply(Messages.GROUP_GRANT_GRANTED
.replace("{group}", group.getName().get())
.replace("{player}", player));
})
.except(err -> context.reply(Messages.NO_SUCH_USER
.replace("{name}", player)));
}
@Override
public Completion onTabComplete(@NotNull CommandContext context) throws Exception {
if (!(context.getSender().hasPermission("ysmp.admin")))
return Completion.EMPTY;
// this command system is a piece of shit, so I have to do it manually
if (context.size() == 0) {
Completion completion = Completion.of("create", "delete");
List<String> groups = YsmpCore.INSTANCE
.getGroupService()
.getGroupCache()
.values()
.stream()
.map(group -> group.getName().get())
.toList();
completion.getData().addAll(groups);
return completion;
}
else if (context.size() == 1) {
String arg = context.getString(0).get();
if (arg.equals("create") || arg.equals("delete"))
return Completion.EMPTY;
return Completion.of("meta", "permission", "grant");
}
else if (context.size() == 2) {
String action = context.getString(1).get();
if (action.equals(""))
return Completion.of("meta", "permission", "grant");
if (action.startsWith("m"))
return Completion.of("meta");
else if (action.startsWith("p"))
return Completion.of("permission");
else if (action.startsWith("g"))
return Completion.of("grant");
}
else if (context.size() == 3) {
String action = context.getString(1).get();
return switch (action) {
case "meta" -> Completion.of("info", "prefix", "suffix", "tabprefix", "tabsuffix");
case "permission" -> Completion.of("set", "unset");
case "grant" -> Completion.PLAYERS;
default -> Completion.EMPTY;
};
}
return Completion.EMPTY;
}
}

View file

@ -0,0 +1,54 @@
package ee.yoursit.core.command.bukkit.impl.admin.group;
import dev.inventex.octa.data.convertible.Convertible;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Argument;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.Group;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
@CommandInfo(name = "create")
public class CreateArgument extends Argument {
@Override
public void onCommand(@NotNull CommandContext context) throws Exception {
if (!(context.getSender().hasPermission("ysmp.admin"))) {
context.reply(Messages.NO_PERMISSION);
return;
}
Convertible<String, String> nameArg = context.getString(0);
if (nameArg.isEmpty()) {
context.reply(Messages.GROUP_CREATE_USAGE);
return;
}
String name = nameArg.get();
YsmpCore.INSTANCE
.getGroupService()
.getGroup(name)
.then(group -> context.reply(Messages.GROUP_CREATE_ALREADY_EXISTS
.replace("{group}", name)))
.tryExcept(err -> createGroup(context, name));
}
private void createGroup(CommandContext context, String name) throws Exception {
Group group = EntitySerializer.init(Group.class);
group.getName().init(name);
group.getPermissions().init(new HashMap<>());
YsmpCore.INSTANCE
.getGroupService()
.addGroup(group)
.get();
context.reply(Messages.GROUP_CREATE_CREATED
.replace("{group}", name));
}
}

View file

@ -0,0 +1,52 @@
package ee.yoursit.core.command.bukkit.impl.admin.group;
import dev.inventex.octa.data.convertible.Convertible;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.config.Messages;
import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "delete")
public class DeleteArgument extends Command {
@Override
public void onCommand(@NotNull CommandContext context) throws Exception {
if (!(context.getSender().hasPermission("ysmp.admin"))) {
context.reply(Messages.NO_PERMISSION);
return;
}
Convertible<String, String> nameArg = context.getString(0);
if (nameArg.isEmpty()) {
context.reply(Messages.GROUP_CREATE_USAGE);
return;
}
String name = nameArg.get();
YsmpCore.INSTANCE
.getGroupService()
.getGroup(name)
.tryThen(group -> {
if (group == null) {
context.reply(Messages.GROUP_DELETE_NOT_EXISTS
.replace("{group}", name));
return;
}
deleteGroup(context, name);
})
.except(System.err::println);
}
private void deleteGroup(CommandContext context, String name) throws Exception {
YsmpCore.INSTANCE
.getGroupService()
.removeGroup(name)
.get();
context.reply(Messages.GROUP_DELETE_DELETED
.replace("{group}", name));
}
}

View file

@ -0,0 +1,41 @@
package ee.yoursit.core.command.bukkit.impl.admin.group;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Argument;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.database.entity.impl.Group;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.stream.Collectors;
@CommandInfo(name = "list")
public class ListArgument extends Argument {
@Override
public void onCommand(@NotNull CommandContext context) throws Exception {
if (!(context.getSender().hasPermission("ysmp.admin"))) {
context.reply(Messages.NO_PERMISSION);
return;
}
Collection<Group> groups = YsmpCore.INSTANCE
.getGroupService()
.getGroupCache()
.values();
if (groups.isEmpty()) {
context.reply(Messages.GROUP_NO_GROUPS);
return;
}
String list = groups
.stream()
.map(group -> group.getName().get())
.collect(Collectors.joining(", "));
context.reply(Messages.GROUP_LIST
.replace("{list}", list));
}
}

View file

@ -0,0 +1,35 @@
package ee.yoursit.core.command.bukkit.impl.home;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.PlayerData;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "delhome")
public class DelHomeCommand extends Command {
@Override
public void onCommand(@NotNull CommandContext context) {
Player player = context.getPlayer();
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(player);
data
.getUser()
.then(user -> {
if (user.getHome().get() == null) {
context.reply(Messages.HOME_NOT_SET);
return;
}
user.getHome().set(null);
context.reply(Messages.HOME_DELETED);
})
.except(e -> context.reply(Messages.INTERNAL_ERROR));
}
}

View file

@ -0,0 +1,42 @@
package ee.yoursit.core.command.bukkit.impl.home;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.command.bukkit.api.Completion;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.PlayerData;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "home")
public class HomeCommand extends Command {
@Override
public void onCommand(@NotNull CommandContext context) {
Player player = context.getPlayer();
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(player);
data
.getUser()
.then(user -> {
Location home = user.getHome().get();
if (home == null) {
context.reply(Messages.HOME_NOT_SET);
return;
}
player.teleport(home);
context.reply(Messages.HOME_TELEPORTING);
})
.except(e -> context.reply(Messages.INTERNAL_ERROR));
}
@Override
public Completion onTabComplete(@NotNull CommandContext context) {
return Completion.EMPTY;
}
}

View file

@ -0,0 +1,35 @@
package ee.yoursit.core.command.bukkit.impl.home;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.command.bukkit.api.Completion;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.PlayerData;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "sethome")
public class SetHomeCommand extends Command {
@Override
public void onCommand(@NotNull CommandContext context) {
Player player = context.getPlayer();
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(player);
data
.getUser()
.then(user -> {
user.getHome().set(player.getLocation());
context.reply(Messages.HOME_UPDATED);
})
.except(e -> context.reply(Messages.INTERNAL_ERROR));
}
@Override
public Completion onTabComplete(@NotNull CommandContext context) {
return Completion.EMPTY;
}
}

View file

@ -0,0 +1,100 @@
package ee.yoursit.core.command.bukkit.impl.invite;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.PlayerData;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.User;
import ee.yoursit.core.database.entity.impl.Verification;
import ee.yoursit.core.util.Constants;
import lombok.SneakyThrows;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "verify")
public class VerifyCommand extends Command {
@Override
public void onCommand(@NotNull CommandContext context) throws Exception {
Player player = context.getPlayer();
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(player);
if (!data.isVerifying()) {
context.reply(Messages.INVITE_ALREADY_VERIFIED);
return;
}
String code = context.getString(0).get();
YsmpCore.INSTANCE
.getVerificationService()
.getVerification(code)
.then(verification -> verifyUser(player, data, verification))
.except(e -> data.kick(Messages.INVITE_INVALID_CODE));
}
@SneakyThrows
private void verifyUser(Player player, PlayerData data, Verification verification) {
JDA jda = YsmpCore.INSTANCE
.getClient()
.getJda();
Guild guild = jda.getGuildById(Constants.YSMP_SERVER);
assert guild != null;
Member member = guild.getMemberById(verification.getDiscordId().get());
if (member == null) {
player.sendMessage(Messages.INVITE_NOT_JOINED_GUILD);
return;
}
User user = createUser(player, verification);
YsmpCore.INSTANCE
.getUserService()
.addUser(user)
.get();
YsmpCore.INSTANCE
.getVerificationService()
.removeVerification(verification)
.get();
data.setVerifying(false);
Role role = jda.getRoleById(Constants.PLAYER);
assert role != null;
guild
.addRoleToMember(member, role)
.queue();
player.sendMessage(Messages.INVITE_VERIFIED);
}
@SneakyThrows
private User createUser(Player player, Verification verification) {
User user = EntitySerializer.init(User.class);
user.getUsername().init(player.getName());
user.getUuid().init(player.getUniqueId());
user.getDiscordId().init(verification.getDiscordId().get());
user.getRegistered().init(System.currentTimeMillis());
user.getLastOnline().init(System.currentTimeMillis());
user.getPlayTime().init(0L);
user.getInvite().set(verification.getCode().get());
user.getGroup().init("Invited");
return user;
}
}

View file

@ -0,0 +1,56 @@
package ee.yoursit.core.command.bukkit.impl.message;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.command.bukkit.api.Completion;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.DataManager;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.stream.Collectors;
@CommandInfo(name = "message", aliases = {"msg", "m", "pm", "dm"})
public class MessageCommand extends Command {
@Override
public void onCommand(@NotNull CommandContext context) throws Exception {
if (context.size() < 2) {
context.reply(Messages.PRIVATE_MESSAGE_USAGE);
return;
}
Player player = context.getPlayer();
Player target = context.getPlayer(0).get();
if (target == null) {
context.reply(Messages
.PLAYER_NOT_ONLINE
.replace("{player}", context.getStringRaw(0)));
return;
}
String message = Arrays.stream(context.getData())
.skip(1)
.collect(Collectors.joining(" "));
player.sendMessage(Messages.PRIVATE_MESSAGE_SEND
.replace("{player}", target.getName())
.replace("{message}", message));
target.sendMessage(Messages.PRIVATE_MESSAGE_RECEIVE
.replace("{player}", player.getName())
.replace("{message}", message));
DataManager dataManager = YsmpCore.INSTANCE.getDataManager();
dataManager.get(player).setLastMessenger(target.getUniqueId());
dataManager.get(target).setLastMessenger(player.getUniqueId());
}
@Override
public Completion onTabComplete(@NotNull CommandContext context) {
return Completion.PLAYERS;
}
}

View file

@ -0,0 +1,51 @@
package ee.yoursit.core.command.bukkit.impl.message;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.command.bukkit.api.Completion;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.PlayerData;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.stream.Collectors;
@CommandInfo(name = "reply", aliases = {"r", "repl"})
public class ReplyCommand extends Command {
@Override
public void onCommand(@NotNull CommandContext context) throws Exception {
Player player = context.getPlayer();
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(player);
Player messenger = data.getLastMessenger();
if (messenger == null) {
context.reply(Messages.PRIVATE_MESSAGE_NO_OPEN_CHANNELS);
return;
}
String message = String.join(" ", context.getData());
player.sendMessage(Messages.PRIVATE_MESSAGE_SEND
.replace("{player}", messenger.getName())
.replace("{message}", message));
messenger.sendMessage(Messages.PRIVATE_MESSAGE_RECEIVE
.replace("{player}", player.getName())
.replace("{message}", message));
YsmpCore.INSTANCE
.getDataManager()
.get(messenger)
.setLastMessenger(player.getUniqueId());
}
@Override
public Completion onTabComplete(@NotNull CommandContext context) throws Exception {
return Completion.PLAYERS;
}
}

View file

@ -0,0 +1,84 @@
package ee.yoursit.core.command.bukkit.impl.teleport;
import dev.inventex.octa.data.convertible.Convertible;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.command.bukkit.api.Completion;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.PlayerData;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
@CommandInfo(name = "tpaccept")
public class TeleportAccept extends Command {
@Override
public void onCommand(@NotNull CommandContext context) throws Exception {
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(context.getPlayer());
Map<UUID, Long> teleportRequests = data.getTeleportRequests();
if (teleportRequests.isEmpty()) {
context.reply(Messages.TELEPORT_NO_REQUESTS);
return;
}
Convertible<String, Player> targetParam = context.getPlayer(0);
Player player = context.getPlayer();
Player target;
if (!targetParam.isEmpty())
target = targetParam.get();
else {
UUID next = teleportRequests
.keySet()
.iterator()
.next();
target = Bukkit.getPlayer(next);
}
if (target == null) {
if (targetParam.isEmpty())
context.reply(Messages.TELEPORT_NO_REQUESTS);
else
context.reply(Messages
.TELEPORT_NO_REQUEST_FROM
.replace("{player}", context.getStringRaw(0)));
return;
}
target.teleport(player.getLocation());
target.sendMessage(Messages
.TELEPORT_ACCEPT_SEND
.replace("{player}", player.getName()));
player.sendMessage(Messages
.TELEPORT_ACCEPT_RECEIVE
.replace("{player}", target.getName()));
data.removeTeleportRequest(target);
}
@Override
public Completion onTabComplete(@NotNull CommandContext context) {
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(context.getPlayer());
return Completion.of(data
.getTeleportRequests()
.keySet()
.stream()
.map(Bukkit::getPlayer)
.filter(Objects::nonNull)
.map(Player::getName));
}
}

View file

@ -0,0 +1,82 @@
package ee.yoursit.core.command.bukkit.impl.teleport;
import dev.inventex.octa.data.convertible.Convertible;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.command.bukkit.api.Completion;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.PlayerData;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
@CommandInfo(name = "tpdeny", aliases = {"tpdecline"})
public class TeleportDeny extends Command {
@Override
public void onCommand(@NotNull CommandContext context) throws Exception {
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(context.getPlayer());
Map<UUID, Long> teleportRequests = data.getTeleportRequests();
if (teleportRequests.isEmpty()) {
context.reply(Messages.TELEPORT_NO_REQUESTS);
return;
}
Convertible<String, Player> targetParam = context.getPlayer(0);
Player player = context.getPlayer();
Player target;
if (!targetParam.isEmpty())
target = targetParam.get();
else {
UUID next = teleportRequests
.keySet()
.iterator()
.next();
target = Bukkit.getPlayer(next);
}
if (target == null) {
if (targetParam.isEmpty())
context.reply(Messages.TELEPORT_NO_REQUESTS);
else
context.reply(Messages
.TELEPORT_NO_REQUEST_FROM
.replace("{player}", context.getStringRaw(0)));
return;
}
data.removeTeleportRequest(target);
target.sendMessage(Messages
.TELEPORT_DECLINE_SEND
.replace("{player}", player.getName()));
player.sendMessage(Messages
.TELEPORT_DECLINE_RECEIVE
.replace("{player}", target.getName()));
}
@Override
public Completion onTabComplete(@NotNull CommandContext context) {
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(context.getPlayer());
return Completion.of(data
.getTeleportRequests()
.keySet()
.stream()
.map(Bukkit::getPlayer)
.filter(Objects::nonNull)
.map(Player::getName));
}
}

View file

@ -0,0 +1,55 @@
package ee.yoursit.core.command.bukkit.impl.teleport;
import dev.inventex.octa.data.convertible.Convertible;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.api.CommandContext;
import ee.yoursit.core.command.bukkit.api.CommandInfo;
import ee.yoursit.core.command.bukkit.api.Completion;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.PlayerData;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@CommandInfo(name = "tpa")
public class TeleportRequest extends Command {
@Override
public void onCommand(@NotNull CommandContext context) {
Convertible<String, Player> playerArg = context.getPlayer(0);
if (playerArg.isEmpty()) {
context.reply(Messages.TELEPORT_REQUEST_USAGE);
return;
}
Player player = context.getPlayer();
Player target = playerArg.getOrDefault(null);
if (target == null) {
context.reply(Messages.PLAYER_NOT_ONLINE);
return;
}
PlayerData targetData = YsmpCore.INSTANCE
.getDataManager()
.get(target);
if (targetData.getTeleportRequest(player) != null) {
context.reply(Messages
.TELEPORT_REQUEST_ALREADY
.replace("{player}", target.getName()));
return;
}
targetData.addTeleportRequest(player);
context.reply(Messages.TELEPORT_REQUEST_SEND
.replace("{player}", target.getName()));
target.sendMessage(Messages.TELEPORT_REQUEST_RECEIVE
.replace("{player}", target.getName()));
}
@Override
public Completion onTabComplete(@NotNull CommandContext context) {
return Completion.PLAYERS;
}
}

View file

@ -0,0 +1,119 @@
package ee.yoursit.core.command.discord;
import dev.inventex.discord.command.slash.CommandContext;
import dev.inventex.discord.command.slash.SlashCommand;
import dev.inventex.discord.command.slash.SlashCommandInfo;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.Ban;
import ee.yoursit.core.database.entity.impl.User;
import ee.yoursit.core.util.Constants;
import lombok.SneakyThrows;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.UUID;
@SlashCommandInfo(name = "ban", description = "Remove a player from the server.")
public class BanCommand extends SlashCommand {
@Override
protected void initialize() {
addOption(
OptionType.USER,
"target",
"Target user to be removed",
true, false
);
addOption(
OptionType.STRING,
"reason",
"The reason for the removal",
true, false
);
}
@Override
public void execute(CommandContext context) {
String reason = context.getString("reason");
Guild guild = YsmpCore.INSTANCE
.getClient()
.getJda()
.getGuildById(Constants.YSMP_SERVER);
assert guild != null : "Unable to fetch YSMP guild";
Role role = guild.getRoleById(Constants.BANNED);
assert role != null : "Unable to fetch Banned role";
Member member = guild.getMember(context.getUser("target"));
assert member != null : "Unable to fetch target member";
guild
.addRoleToMember(member, role)
.queue();
String nickname = member.getNickname();
if (nickname == null)
return;
Player player = Bukkit.getPlayer(nickname);
if (player != null) {
YsmpCore.INSTANCE
.getDataManager()
.get(player)
.kick(Messages.BANNED_FROM_SERVER);
}
YsmpCore.INSTANCE
.getUserService()
.getUser(nickname)
.then(user -> {
Member self = context.getInteraction().getMember();
assert self != null;
banUser(context, user, self.getNickname(), reason);
})
.except(Throwable::printStackTrace);
}
@SneakyThrows
private void banUser(CommandContext context, User user, String nickname, String reason) {
user.getBanned().set(true);
User moderator = YsmpCore.INSTANCE
.getUserService()
.getUser(nickname)
.get();
Ban ban = EntitySerializer.init(Ban.class);
ban.getBanId().init(UUID.randomUUID());
ban.getModerator().init(moderator.getUuid().get());
ban.getTarget().init(user.getUuid().get());
ban.getReason().init(reason);
ban.getTimestamp().init(System.currentTimeMillis());
YsmpCore.INSTANCE
.getBanService()
.createBan(ban);
MessageEmbed embed = new EmbedBuilder()
.setTitle("User Banned")
.setColor(Constants.TRANSPARENT)
.addField("Target", user.getUsername().get(), true)
.addField("Moderator", moderator.getUsername().get(), true)
.addField("Reason", reason, true)
.build();
context
.getInteraction()
.replyEmbeds(embed)
.queue();
}
}

View file

@ -0,0 +1,138 @@
package ee.yoursit.core.command.discord;
import dev.inventex.discord.command.slash.CommandContext;
import dev.inventex.discord.command.slash.SlashCommand;
import dev.inventex.discord.command.slash.SlashCommandInfo;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.Invite;
import ee.yoursit.core.database.entity.impl.User;
import ee.yoursit.core.util.Constants;
import ee.yoursit.core.util.InviteGenerator;
import lombok.SneakyThrows;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.Role;
import java.awt.*;
@SlashCommandInfo(name = "invite", description = "Create an invite code for the YSMP server")
public class InviteCommand extends SlashCommand {
private final InviteGenerator generator = new InviteGenerator(5, 4);
@Override
public void execute(CommandContext context) {
Member member = context
.getInteraction()
.getMember();
assert member != null;
YsmpCore.INSTANCE
.getUserService()
.getUser(member.getNickname())
.then(user -> createInvite(context, user))
.except(e -> handleNotUser(context));
}
private void createInvite(CommandContext context, User user) {
Guild guild = context
.getInteraction()
.getGuild();
Member member = context
.getInteraction()
.getMember();
assert member != null;
assert guild != null;
int invites = user.getInvites().size();
int maxInvites = getMaxInvites(member, guild);
if (invites >= maxInvites) {
exceededInvites(context);
return;
}
Invite invite = newInvite(user);
YsmpCore.INSTANCE
.getInviteService()
.createInvite(invite);
MessageEmbed embed = new EmbedBuilder()
.setTitle("Invite Created")
.setDescription("```" + invite.getCode().get() + "```")
.setColor(Color.GREEN)
.build();
context
.getInteraction()
.deferReply(true)
.addEmbeds(embed)
.queue();
}
private int getMaxInvites(Member member, Guild guild) {
Role premium = guild.getRoleById(Constants.PREMIUM);
Role business = guild.getRoleById(Constants.BUSINESS);
assert premium != null;
assert business != null;
boolean hasPremiumOrHigher = member
.getRoles()
.stream()
.anyMatch(role -> role.getPosition() >= business.getPosition());
if (hasPremiumOrHigher)
return 2;
boolean hasBusiness = member
.getRoles()
.stream()
.anyMatch(role -> role.getPosition() == premium.getPosition());
return hasBusiness ? 1 : 0;
}
private void exceededInvites(CommandContext context) {
MessageEmbed embed = new EmbedBuilder()
.setTitle("Error")
.setDescription(Messages.INVITE_LIMIT_EXCEEDED)
.setColor(Color.RED)
.build();
context
.getInteraction()
.replyEmbeds(embed)
.queue();
}
@SneakyThrows
private Invite newInvite(User user) {
Invite invite = EntitySerializer.init(Invite.class);
invite.getCode().init(generator.generate());
invite.getUsed().init(false);
invite.getCreator().init(user.getUuid().get().toString());
return invite;
}
private void handleNotUser(CommandContext context) {
MessageEmbed embed = new EmbedBuilder()
.setTitle("Error")
.setDescription(Messages.NOT_A_USER)
.setColor(Color.RED)
.build();
context
.getInteraction()
.deferReply(true)
.addEmbeds(embed)
.queue();
}
}

View file

@ -0,0 +1,40 @@
package ee.yoursit.core.command.discord;
import dev.inventex.discord.command.slash.CommandContext;
import dev.inventex.discord.command.slash.SlashCommand;
import dev.inventex.discord.command.slash.SlashCommandInfo;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.util.Constants;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.MessageEmbed;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.Collection;
import java.util.stream.Collectors;
@SlashCommandInfo(name = "players", description = "List the online players on the server.")
public class PlayersCommand extends SlashCommand {
@Override
public void execute(CommandContext context) {
Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers();
String players = onlinePlayers
.stream()
.map(Player::getName)
.collect(Collectors.joining(", "));
if (onlinePlayers.isEmpty())
players = Messages.NO_PLAYERS_ONLINE;
MessageEmbed embed = new EmbedBuilder()
.setTitle("Online Players")
.setDescription(players)
.setColor(Constants.TRANSPARENT)
.build();
context
.getInteraction()
.replyEmbeds(embed)
.queue();
}
}

View file

@ -0,0 +1,140 @@
package ee.yoursit.core.command.discord;
import dev.inventex.discord.command.slash.CommandContext;
import dev.inventex.discord.command.slash.SlashCommand;
import dev.inventex.discord.command.slash.SlashCommandInfo;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.Invite;
import ee.yoursit.core.database.entity.impl.Verification;
import ee.yoursit.core.util.Constants;
import ee.yoursit.core.util.InviteGenerator;
import lombok.SneakyThrows;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import java.awt.*;
@SlashCommandInfo(name = "smp", description = "Invite yourself to the YourSitee SMP server.")
public class SmpCommand extends SlashCommand {
private final InviteGenerator generator = new InviteGenerator(5, 4);
@Override
protected void initialize() {
addOption(OptionType.STRING,
"username",
"The in-game name of your Minecraft account",
true, false
);
}
@Override
@SneakyThrows
public void execute(CommandContext context) {
Member member = context
.getInteraction()
.getMember();
assert member != null;
Guild guild = YsmpCore.INSTANCE
.getClient()
.getJda()
.getGuildById(Constants.YSMP_SERVER);
assert guild != null;
Member smpMember = guild.getMember(member.getUser());
if (smpMember != null
&& smpMember
.getRoles()
.stream()
.anyMatch(role -> role.getIdLong() == Constants.PLAYER)
) {
MessageEmbed embed = new EmbedBuilder()
.setTitle("Error")
.setDescription(Messages.ALREADY_USER)
.setColor(Color.RED)
.build();
context
.getInteraction()
.deferReply(true)
.addEmbeds(embed)
.queue();
return;
}
Verification existingVerification = YsmpCore.INSTANCE
.getVerificationService()
.getVerification(member.getIdLong())
.getOrDefault(null);
if (existingVerification != null) {
MessageEmbed embed = new EmbedBuilder()
.setTitle("Error")
.setDescription(Messages.INVITE_ALREADY_VERIFYING_SELF)
.setColor(Color.RED)
.build();
context
.getInteraction()
.deferReply(true)
.addEmbeds(embed)
.queue();
return;
}
String code = generator.generate();
Invite invite = EntitySerializer.init(Invite.class);
invite.getCode().init(code);
invite.getCreator().init("Server");
invite.getUsed().init(false);
YsmpCore.INSTANCE
.getInviteService()
.createInvite(invite)
.then(e -> invite(context, member, code))
.except(System.err::println);
}
@SneakyThrows
private void invite(CommandContext context, Member member, String code) {
String username = context.getString("username");
Verification verification = EntitySerializer.init(Verification.class);
verification.getDiscordId().init(member.getIdLong());
verification.getUsername().init(username);
verification.getCode().init(code);
verification.getCreation().init(System.currentTimeMillis());
YsmpCore.INSTANCE
.getVerificationService()
.addVerification(verification)
.get();
MessageEmbed embed = new EmbedBuilder()
.setTitle("You have been invited")
.setDescription(Messages.INVITE_VERIFICATION
.replace("{code}", code))
.setColor(Constants.TRANSPARENT)
.build();
String discordLink = YsmpCore.INSTANCE
.getConfig()
.getString("ysmp-invite");
context
.getInteraction()
.deferReply(true)
.addEmbeds(embed)
.addContent(discordLink)
.queue();
}
}

View file

@ -0,0 +1,68 @@
package ee.yoursit.core.command.discord;
import dev.inventex.discord.command.slash.CommandContext;
import dev.inventex.discord.command.slash.SlashCommand;
import dev.inventex.discord.command.slash.SlashCommandInfo;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.database.entity.impl.User;
import ee.yoursit.core.util.Constants;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.interactions.commands.OptionType;
@SlashCommandInfo(name = "unban", description = "Revoke the ban of a player.")
public class UnbanCommand extends SlashCommand {
@Override
protected void initialize() {
addOption(
OptionType.USER,
"target",
"Target user to be removed",
true, false
);
}
@Override
public void execute(CommandContext context) {
Guild guild = YsmpCore.INSTANCE
.getClient()
.getJda()
.getGuildById(Constants.YSMP_SERVER);
assert guild != null : "Unable to fetch YSMP guild";
Role role = guild.getRoleById(Constants.BANNED);
assert role != null : "Unable to fetch Banned role";
Member member = guild.getMember(context.getUser("target"));
assert member != null : "Unable to fetch target member";
guild
.removeRoleFromMember(member, role)
.queue();
YsmpCore.INSTANCE
.getUserService()
.getUser(member.getNickname())
.then(user -> unbanUser(context, user))
.except(Throwable::printStackTrace);
}
private void unbanUser(CommandContext context, User user) {
user.getBanned().set(false);
MessageEmbed embed = new EmbedBuilder()
.setTitle("User Unbanned")
.setColor(Constants.TRANSPARENT)
.addField("Target", user.getUsername().get(), true)
.addField("Moderator", context.getInteraction().getUser().getName(), true)
.build();
context
.getInteraction()
.replyEmbeds(embed)
.queue();
}
}

View file

@ -0,0 +1,118 @@
package ee.yoursit.core.command.discord;
import dev.inventex.discord.command.slash.CommandContext;
import dev.inventex.discord.command.slash.SlashCommand;
import dev.inventex.discord.command.slash.SlashCommandInfo;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.Invite;
import ee.yoursit.core.database.entity.impl.Verification;
import ee.yoursit.core.util.Constants;
import lombok.SneakyThrows;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import java.awt.*;
@SlashCommandInfo(name = "verify", description = "Accept an invitation from an SMP player.")
public class VerifyCommand extends SlashCommand {
@Override
protected void initialize() {
addOption(
OptionType.STRING,
"code",
"The invite code generated by a user",
true, false
);
addOption(OptionType.STRING,
"username",
"The in-game name of your Minecraft account",
true, false
);
}
@Override
public void execute(CommandContext context) {
Guild guild = context.getInteraction().getGuild();
if (guild == null || guild.getIdLong() != Constants.YSMP_SERVER) {
handleError(context, Messages.INVITE_NOT_ON_GUILD);
return;
}
String code = context.getString("code");
YsmpCore.INSTANCE
.getInviteService()
.getInvite(code)
.then(invite -> acceptInvite(context, invite))
.except(e -> handleError(context, Messages.INVITE_NOT_FOUND));
}
@SneakyThrows
private synchronized void acceptInvite(CommandContext context, Invite invite) {
if (invite.getUsed().get()) {
handleError(context, Messages.INVITE_NOT_FOUND);
return;
}
Verification existingVerification = YsmpCore.INSTANCE
.getVerificationService()
.getVerification(invite.getCode().get())
.get();
if (existingVerification != null) {
handleError(context, Messages.INVITE_ALREADY_VERIFYING);
return;
}
Member member = context
.getInteraction()
.getMember();
assert member != null;
String username = context.getString("username");
String code = context.getString("code");
Verification verification = EntitySerializer.init(Verification.class);
verification.getDiscordId().init(member.getIdLong());
verification.getUsername().init(username);
verification.getCode().init(code);
verification.getCreation().init(System.currentTimeMillis());
YsmpCore.INSTANCE
.getVerificationService()
.addVerification(verification)
.get();
MessageEmbed embed = new EmbedBuilder()
.setTitle("Verification")
.setDescription(Messages.INVITE_VERIFICATION
.replace("{code}", code))
.setColor(Constants.TRANSPARENT)
.build();
context
.getInteraction()
.deferReply(true)
.addEmbeds(embed)
.queue();
}
private void handleError(CommandContext context, String message) {
MessageEmbed embed = new EmbedBuilder()
.setTitle("Error")
.setDescription(message)
.setColor(Color.RED)
.build();
context
.getInteraction()
.deferReply(true)
.addEmbeds(embed)
.queue();
}
}

View file

@ -0,0 +1,329 @@
package ee.yoursit.core.config;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.util.Message;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class Config {
private final Executor executor = Executors.newSingleThreadExecutor();
@Getter
private final String name;
private final File file;
@Getter
private FileConfiguration config;
public Config(String name) {
this.name = name;
file = new File(YsmpCore.INSTANCE.getPlugin().getDataFolder(), name + ".yml");
reload();
}
public void reload() {
if (!file.exists()) {
try {
createConfig();
} catch (IOException e) {
Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Unable to reload config file '" + file.getName() + "'");
throw new RuntimeException(e);
}
}
load();
}
public void reloadAsync() {
if (!file.exists())
createConfigAsync(null);
loadAsync();
}
public void reloadAsync(Runnable action) {
if (!file.exists())
createConfigAsync(action);
loadAsync();
}
public void save() {
try {
config.save(file);
} catch (IOException e) {
Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Unable to save config file '" + file.getName() + "'");
e.printStackTrace();
}
}
public void saveAsync() {
executor.execute(this::save);
}
public void load() {
try {
config = new YamlConfiguration();
FileInputStream stream = new FileInputStream(file);
config.load(new InputStreamReader(stream, StandardCharsets.UTF_8));
} catch (Exception e) {
throw new IllegalStateException("Unable to load config file", e);
}
}
public void loadAsync() {
executor.execute(this::load);
}
private void createConfig() throws IOException {
if (!file.createNewFile())
throw new RuntimeException("Unable to create config.yml");
InputStream stream = YsmpCore.class.getClassLoader().getResourceAsStream(name + ".yml");
if (stream == null)
throw new RuntimeException("File config.yml is missing from class path.");
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
StringBuilder content = new StringBuilder();
String line;
while ((line = reader.readLine()) != null)
content.append(line).append('\n');
try (OutputStreamWriter writer = new OutputStreamWriter(Files.newOutputStream(file.toPath()), StandardCharsets.UTF_8)) {
writer.write(content.toString());
}
}
private void createConfigAsync(Runnable action) {
executor.execute(() -> {
try {
createConfig();
action.run();
} catch (Exception e) {
Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Error whilst creating config file '" + file.getName() + "'");
e.printStackTrace();
}
});
}
public boolean getOrDefault(String path, boolean defaultValue) {
if (config.isSet(path))
return config.getBoolean(path);
else {
config.set(path, defaultValue);
return defaultValue;
}
}
public int getOrDefault(String path, int defaultValue) {
if (config.isSet(path))
return config.getInt(path);
else {
config.set(path, defaultValue);
return defaultValue;
}
}
public int getOrDefaultNoSet(String path, int defaultValue) {
if (config.isSet(path))
return config.getInt(path);
else
return defaultValue;
}
public long getOrDefault(String path, long defaultValue) {
if (config.isSet(path))
return config.getInt(path);
else {
config.set(path, defaultValue);
return defaultValue;
}
}
public double getOrDefault(String path, double defaultValue) {
if (config.isSet(path))
return config.getDouble(path);
else {
config.set(path, defaultValue);
return defaultValue;
}
}
public float getOrDefault(String path, float defaultValue) {
if (config.isSet(path))
return (float) config.getDouble(path);
else {
config.set(path, defaultValue);
return defaultValue;
}
}
public String getOrDefault(String path, String defaultValue) {
if (config.isSet(path))
return config.getString(path);
else {
if (defaultValue != null)
config.set(path, defaultValue);
return defaultValue;
}
}
public String getString(String path) {
return config.getString(path);
}
public long getLong(String path) {
return config.getLong(path);
}
public String getOrDefaultNoSet(String path, String defaultValue) {
if (config.isSet(path))
return config.getString(path);
else
return defaultValue;
}
public String getF(String path) {
String string = config.getString(path);
if (string == null)
throw new IllegalStateException("No such config option: " + path);
return Message.translate(string);
}
public String getOrDefaultF(String path, String defaultValue) {
String result;
if (config.isSet(path))
result = config.getString(path);
else {
result = defaultValue;
if (defaultValue == null)
return null;
config.set(path, result);
}
return Message.translate(result);
}
public String getOrDefaultFLS(String path, String... defaultValue) {
String result;
if (config.isSet(path)) {
StringBuilder builder = new StringBuilder();
Iterator<String> iterator = config.getStringList(path).iterator();
while (iterator.hasNext()) {
builder.append(iterator.next());
if (iterator.hasNext())
builder.append('\n');
}
result = builder.toString();
} else {
result = String.join("\n", defaultValue);
config.set(path, Arrays.asList(defaultValue));
}
return Message.translate(result);
}
public Material getOrDefault(String path, Material defaultValue) {
if (config.isSet(path))
return Material.getMaterial(config.getString(path));
else
return defaultValue;
}
public List<String> getOrDefaultFL(String path, String... defaultValue) {
if (config.isSet(path))
return config
.getStringList(path)
.stream()
.map(s -> Message.translate(s))
.toList();
else if (defaultValue.length > 0) {
List<String> result = Arrays
.stream(defaultValue)
.map(s -> Message.translate(s))
.toList();
config.set(path, result);
return result;
}
return null;
}
public List<String> getOrDefault(String path, List<String> defaultValue) {
if (config.isSet(path))
return config.getStringList(path);
else {
config.set(path, defaultValue);
return defaultValue;
}
}
public Location getOrDefault(String path, Location defaultValue) {
if (config.isSet(path)) {
String world = config.getString(path + ".world");
if (world == null)
return null;
return new Location(
Bukkit.getWorld(world),
getOrDefault(path + ".posX", 0D),
getOrDefault(path + ".posY", 0D),
getOrDefault(path + ".posZ", 0D),
getOrDefault(path + ".yaw", 0F),
getOrDefault(path + ".pitch", 0F)
);
} else {
setLocation(path, defaultValue);
return defaultValue;
}
}
public Location getLocation(String path) {
return config.getLocation(path);
}
public void setLocation(String path, Location location) {
if (location == null) {
config.set(path, new HashMap<>());
return;
}
config.set(path + ".world", location.getWorld().getName());
config.set(path + ".posX", location.getX());
config.set(path + ".posY", location.getY());
config.set(path + ".posZ", location.getZ());
config.set(path + ".yaw", location.getYaw());
config.set(path + ".pitch", location.getPitch());
}
public Set<String> getKeys(String path) {
ConfigurationSection section = config.getConfigurationSection(path);
if (section == null)
return new HashSet<>();
return section.getKeys(false);
}
public void set(String path, Object value) {
config.set(path, value);
}
public void unset(String path) {
config.set(path, null);
}
public boolean isSet(String path) {
return config.isSet(path);
}
}

View file

@ -0,0 +1,148 @@
package ee.yoursit.core.config;
public class Messages {
public static String INTERNAL_ERROR;
public static String PLAYER_JOIN, PLAYER_LEAVE;
public static String PLAYER_NOT_ONLINE, NO_PLAYERS_ONLINE, NOT_A_USER, ALREADY_USER;
public static String TELEPORT_REQUEST_SEND, TELEPORT_REQUEST_RECEIVE, TELEPORT_REQUEST_USAGE,
TELEPORT_REQUEST_ALREADY;
public static String TELEPORT_ACCEPT_SEND, TELEPORT_ACCEPT_RECEIVE;
public static String TELEPORT_DECLINE_SEND, TELEPORT_DECLINE_RECEIVE;
public static String TELEPORT_NO_REQUESTS, TELEPORT_NO_REQUEST_FROM;
public static String PRIVATE_MESSAGE_SEND, PRIVATE_MESSAGE_RECEIVE, PRIVATE_MESSAGE_USAGE,
PRIVATE_MESSAGE_NO_OPEN_CHANNELS;
public static String HOME_UPDATED, HOME_TELEPORTING, HOME_NOT_SET, HOME_DELETED;
public static String AUTO_SLEEP;
public static String BANNED_FROM_SERVER, NOT_WHITELISTED, UNABLE_TO_FETCH;
public static String ACTIVITY_PLAYERS, ACTIVITY_DISCORD, ACTIVITY_ADDRESS;
public static String NO_SUCH_USER, NO_PERMISSION;
public static String INVITE_LIMIT_EXCEEDED, INVITE_NOT_FOUND, INVITE_NOT_ON_GUILD,
INVITE_VERIFIED, INVITE_VERIFICATION, INVITE_ALREADY_VERIFYING,
INVITE_ALREADY_VERIFIED, INVITE_USE_VERIFY, INVITE_VERIFY_TIMEOUT,
INVITE_INVALID_CODE, INVITE_NOT_JOINED_GUILD, INVITE_ALREADY_VERIFYING_SELF;
public static String GROUP_USAGE, GROUP_NOT_EXISTS,
GROUP_CREATE_USAGE, GROUP_CREATE_ALREADY_EXISTS, GROUP_CREATE_CREATED,
GROUP_DELETE_USAGE, GROUP_DELETE_NOT_EXISTS, GROUP_DELETE_DELETED,
GROUP_LIST, GROUP_NO_GROUPS,
GROUP_META_USAGE, GROUP_META_INFO,
GROUP_META_PREFIX_USAGE, GROUP_META_PREFIX_SET,
GROUP_META_SUFFIX_USAGE, GROUP_META_SUFFIX_SET,
GROUP_META_TAB_PREFIX_USAGE, GROUP_META_TAB_PREFIX_SET,
GROUP_META_TAB_SUFFIX_USAGE, GROUP_META_TAB_SUFFIX_SET,
GROUP_PERMISSION_USAGE,
GROUP_PERMISSION_SET_USAGE, GROUP_PERMISSION_SET_UPDATED,
GROUP_PERMISSION_UNSET_USAGE, GROUP_PERMISSION_UNSET_UPDATED,
GROUP_GRANT_USAGE, GROUP_GRANT_GRANTED;
public static void init(Config config) {
INTERNAL_ERROR = config.getF("internal-error");
PLAYER_JOIN = config.getF("player-join");
PLAYER_LEAVE = config.getF("player-leave");
PLAYER_NOT_ONLINE = config.getF("player-not-online");
NO_PLAYERS_ONLINE = config.getF("no-players-online");
NOT_A_USER = config.getF("not-a-user");
ALREADY_USER = config.getF("already-user");
TELEPORT_REQUEST_SEND = config.getF("teleport-request.send");
TELEPORT_REQUEST_RECEIVE = config.getF("teleport-request.receive");
TELEPORT_REQUEST_USAGE = config.getF("teleport-request.usage");
TELEPORT_REQUEST_ALREADY = config.getF("teleport-request.already");
TELEPORT_NO_REQUESTS = config.getF("teleport.no-requests");
TELEPORT_NO_REQUEST_FROM = config.getF("teleport.no-request-from");
TELEPORT_ACCEPT_SEND = config.getF("teleport-accept.send");
TELEPORT_ACCEPT_RECEIVE = config.getF("teleport-accept.receive");
TELEPORT_DECLINE_SEND = config.getF("teleport-decline.send");
TELEPORT_DECLINE_RECEIVE = config.getF("teleport-decline.receive");
PRIVATE_MESSAGE_SEND = config.getF("private-message.send");
PRIVATE_MESSAGE_RECEIVE = config.getF("private-message.receive");
PRIVATE_MESSAGE_USAGE = config.getF("private-message.usage");
PRIVATE_MESSAGE_NO_OPEN_CHANNELS = config.getF("private-message.no-open-channels");
HOME_UPDATED = config.getF("home.updated");
HOME_TELEPORTING = config.getF("home.teleporting");
HOME_NOT_SET = config.getF("home.not-set");
HOME_DELETED = config.getF("home.deleted");
AUTO_SLEEP = config.getF("auto-sleep");
BANNED_FROM_SERVER = config.getF("banned-from-server");
NOT_WHITELISTED = config.getF("not-whitelisted");
UNABLE_TO_FETCH = config.getF("unable-to-fetch");
ACTIVITY_PLAYERS = config.getF("activity.players");
ACTIVITY_DISCORD = config.getF("activity.discord");
ACTIVITY_ADDRESS = config.getF("activity.address");
NO_SUCH_USER = config.getF("no-such-user");
NO_PERMISSION = config.getF("no-permission");
INVITE_LIMIT_EXCEEDED = config.getF("invite.limit-exceeded");
INVITE_NOT_FOUND = config.getF("invite.not-found");
INVITE_NOT_ON_GUILD = config.getF("invite.not-on-guild");
INVITE_VERIFIED = config.getF("invite.verified");
INVITE_VERIFICATION = config.getF("invite.verification");
INVITE_ALREADY_VERIFYING = config.getF("invite.already-verifying");
INVITE_ALREADY_VERIFIED = config.getF("invite.already-verified");
INVITE_USE_VERIFY = config.getF("invite.use-verify");
INVITE_VERIFY_TIMEOUT = config.getF("invite.verify-timeout");
INVITE_INVALID_CODE = config.getF("invite.invalid-code");
INVITE_NOT_JOINED_GUILD = config.getF("invite.not-joined-guild");
INVITE_ALREADY_VERIFYING_SELF = config.getF("invite.already-verifying-self");
GROUP_USAGE = config.getF("group.usage");
GROUP_NOT_EXISTS = config.getF("group.not-exists");
GROUP_CREATE_USAGE = config.getF("group.create.usage");
GROUP_CREATE_ALREADY_EXISTS = config.getF("group.create.already-exists");
GROUP_CREATE_CREATED = config.getF("group.create.created");
GROUP_DELETE_USAGE = config.getF("group.delete.usage");
GROUP_DELETE_NOT_EXISTS = config.getF("group.delete.not-exists");
GROUP_DELETE_DELETED = config.getF("group.delete.deleted");
GROUP_LIST = config.getF("group.list");
GROUP_NO_GROUPS = config.getF("group.no-groups");
GROUP_META_USAGE = config.getF("group.meta.usage");
GROUP_META_INFO = config.getF("group.meta.info");
GROUP_META_PREFIX_USAGE = config.getF("group.meta.prefix.usage");
GROUP_META_PREFIX_SET = config.getF("group.meta.prefix.set");
GROUP_META_TAB_PREFIX_USAGE = config.getF("group.meta.tab-prefix.usage");
GROUP_META_TAB_PREFIX_SET = config.getF("group.meta.tab-prefix.set");
GROUP_META_TAB_SUFFIX_USAGE = config.getF("group.meta.tab-suffix.usage");
GROUP_META_TAB_SUFFIX_SET = config.getF("group.meta.tab-suffix.set");
GROUP_PERMISSION_USAGE = config.getF("group.permission.usage");
GROUP_PERMISSION_SET_USAGE = config.getF("group.permission.set.usage");
GROUP_PERMISSION_SET_UPDATED = config.getF("group.permission.set.updated");
GROUP_PERMISSION_UNSET_USAGE = config.getF("group.permission.unset.usage");
GROUP_PERMISSION_UNSET_UPDATED = config.getF("group.permission.unset.updated");
GROUP_GRANT_USAGE = config.getF("group.grant.usage");
GROUP_GRANT_GRANTED = config.getF("group.grant.granted");
}
}

View file

@ -0,0 +1,49 @@
package ee.yoursit.core.data;
import com.google.common.collect.Maps;
import ee.yoursit.core.YsmpCore;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.*;
@Getter
public class DataManager {
private final Map<UUID, PlayerData> playerData = Maps.newConcurrentMap();
public DataManager() {
Bukkit
.getOnlinePlayers()
.forEach(this::add);
Bukkit
.getScheduler()
.scheduleSyncRepeatingTask(YsmpCore.INSTANCE.getPlugin(),
this::updateOnline, 20 * 60, 20 * 60);
}
private void updateOnline() {
playerData
.values()
.forEach(PlayerData::updateLastOnline);
}
public PlayerData add(Player player) {
PlayerData data = new PlayerData(player);
playerData.put(player.getUniqueId(), data);
return data;
}
public PlayerData remove(Player player) {
return playerData.remove(player.getUniqueId());
}
public PlayerData get(Player player) {
return playerData.get(player.getUniqueId());
}
public PlayerData get(UUID uuid) {
return playerData.get(uuid);
}
}

View file

@ -0,0 +1,167 @@
package ee.yoursit.core.data;
import dev.inventex.octa.concurrent.future.Future;
import dev.inventex.octa.concurrent.future.FutureTimeoutException;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.database.entity.impl.Group;
import ee.yoursit.core.database.entity.impl.User;
import ee.yoursit.core.database.entity.impl.Verification;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Getter
@Setter
public class PlayerData {
public static final int TELEPORT_REQUEST_LIFETIME = 1000 * 30;
private final Map<UUID, Long> teleportRequests = new HashMap<>();
private final Reference<Player> player;
private boolean online;
private long joinTime;
@Nullable
private UUID lastMessenger;
private Future<User> user;
@Setter
private boolean verifying;
private Verification verification;
public PlayerData(Player player) {
this.player = new WeakReference<>(player);
Bukkit.getScheduler().scheduleSyncRepeatingTask(YsmpCore.INSTANCE.getPlugin(),
this::cleanUpExpiredRequests, 20L, 20L);
user = YsmpCore.INSTANCE
.getUserService()
.getUser(player.getUniqueId())
.timeout(3, TimeUnit.SECONDS)
.except(this::handleUserNotFound);
}
private void handleUserNotFound(Throwable err) {
Player player = this.player.get();
if (player == null)
return;
if (err instanceof FutureTimeoutException) {
kick(Messages.UNABLE_TO_FETCH);
return;
}
try {
Verification verification = YsmpCore.INSTANCE
.getVerificationService()
.getVerification(player)
.getOrDefault(null);
if (verification != null) {
handleVerification(player, verification);
return;
}
} catch (Exception ignored) {
}
kick(Messages.NOT_WHITELISTED);
}
private void handleVerification(Player player, Verification verification) {
try {
teleport(YsmpCore.INSTANCE.getVerifyLoc());
player.sendMessage(Messages.INVITE_USE_VERIFY);
verifying = true;
this.verification = verification;
long timeout = YsmpCore.INSTANCE
.getConfig()
.getLong("verify-timeout");
Bukkit
.getScheduler()
.scheduleSyncDelayedTask(YsmpCore.INSTANCE.getPlugin(), () -> {
if (verifying)
kick(Messages.INVITE_VERIFY_TIMEOUT);
}, timeout * 20);
} catch (Exception e) {
e.printStackTrace();
}
}
public void updateLastOnline() {
user.then(u -> u
.getLastOnline()
.set(System.currentTimeMillis()));
}
public Future<Group> getGroup() {
return getUser().tryTransform(user ->
YsmpCore.INSTANCE
.getGroupService()
.getGroup(user.getGroup().get())
.get());
}
public void addTeleportRequest(Player requester) {
teleportRequests.put(requester.getUniqueId(), System.currentTimeMillis());
}
public void removeTeleportRequest(Player requester) {
teleportRequests.remove(requester.getUniqueId());
}
@Nullable
public Long getTeleportRequest(Player requester) {
return teleportRequests.get(requester.getUniqueId());
}
private void cleanUpExpiredRequests() {
long now = System.currentTimeMillis();
teleportRequests
.entrySet()
.removeIf(entry -> now - entry.getValue() > TELEPORT_REQUEST_LIFETIME);
}
public Player getLastMessenger() {
if (lastMessenger == null)
return null;
return Bukkit.getPlayer(lastMessenger);
}
public void kick(String reason) {
sync(() -> {
Player player = this.player.get();
if (player != null)
player.kickPlayer(reason);
});
}
public void teleport(Location location) {
sync(() -> {
Player player = this.player.get();
if (player != null)
player.teleport(location);
});
}
private void sync(Runnable task) {
Bukkit
.getScheduler()
.runTask(YsmpCore.INSTANCE.getPlugin(), task);
}
}

View file

@ -0,0 +1,39 @@
package ee.yoursit.core.database;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import ee.yoursit.core.database.entity.impl.*;
import lombok.Getter;
import org.bson.Document;
@Getter
public class Database {
private final String host;
private final int port;
private MongoClient client;
private MongoCollection<Document> users, groups, invites, bans, verifications;
public Database(String host, int port) {
this.host = host;
this.port = port;
}
public void connect() {
client = new MongoClient(host, port);
MongoDatabase database = client.getDatabase("ysmp");
User.setCollection(users = database.getCollection("users"));
Group.setCollection(groups = database.getCollection("groups"));
Invite.setCollection(invites = database.getCollection("invites"));
Ban.setCollection(bans = database.getCollection("bans"));
Verification.setCollection(verifications = database.getCollection("verifications"));
}
public void disconnect() {
client.close();
}
}

View file

@ -0,0 +1,11 @@
package ee.yoursit.core.database.entity.api;
import com.mongodb.client.MongoCollection;
import org.bson.Document;
import org.bson.conversions.Bson;
public interface Entity {
Bson query();
MongoCollection<Document> collection();
}

View file

@ -0,0 +1,77 @@
package ee.yoursit.core.database.entity.api;
import com.mongodb.client.model.Updates;
import dev.inventex.octa.concurrent.future.Future;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.bson.conversions.Bson;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RequiredArgsConstructor
public class EntityProperty<T> {
private static final Map<Class<?>, Object> FALLBACK = new HashMap<>();
static {
FALLBACK.put(Byte.class, (byte) 0);
FALLBACK.put(Short.class, (short) 0);
FALLBACK.put(Integer.class, 0);
FALLBACK.put(Float.class, 0F);
FALLBACK.put(Double.class, 0D);
FALLBACK.put(Long.class, 0L);
FALLBACK.put(Boolean.class, false);
FALLBACK.put(Character.class, (char) 0);
FALLBACK.put(Number.class, 0);
FALLBACK.put(List.class, new ArrayList<>());
FALLBACK.put(Map.class, new HashMap<>());
}
private final Entity entity;
private final String name;
@Getter
private final Class<T> type;
public T value;
private boolean init = false;
public EntityProperty(Entity entity, String name, Class<T> type, T value) {
this(entity, name, type);
init(value);
}
@SuppressWarnings("unchecked")
public final T get() {
if (!init && FALLBACK.containsKey(type))
return (T) FALLBACK.get(type);
return value;
}
@MutateEntity
public final Future<Void> set(T value) {
this.value = value;
init = true;
return mutate();
}
public final void init(T value) {
this.value = value;
init = true;
}
@MutateEntity
public final Future<Void> mutate() {
return Future.completeAsync(() -> {
try {
Object serialize = EntitySerializer.serialize(type, value);
entity
.collection()
.updateOne(entity.query(), Updates.set(name, serialize));
} catch (Exception e) {
e.printStackTrace();
}
});
}
}

View file

@ -0,0 +1,142 @@
package ee.yoursit.core.database.entity.api;
import org.bson.Document;
import org.bukkit.Location;
import java.lang.reflect.*;
import java.util.*;
public class EntitySerializer {
private static final Set<Class<?>> NUMBERS = new HashSet<>();
static {
NUMBERS.add(byte.class);
NUMBERS.add(short.class);
NUMBERS.add(int.class);
NUMBERS.add(long.class);
NUMBERS.add(float.class);
NUMBERS.add(double.class);
NUMBERS.add(Byte.class);
NUMBERS.add(Short.class);
NUMBERS.add(Integer.class);
NUMBERS.add(Long.class);
NUMBERS.add(Float.class);
NUMBERS.add(Double.class);
NUMBERS.add(Number.class);
}
public static <T extends Entity> Document serialize(T entity) {
Document document = new Document();
for (Field field : entity.getClass().getDeclaredFields()) {
if (Modifier.isTransient(field.getModifiers()))
continue;
field.setAccessible(true);
try {
@SuppressWarnings("rawtypes")
EntityProperty property = (EntityProperty) field.get(entity);
@SuppressWarnings("unchecked")
Object serialize = serialize(property.getType(), property.get());
document.append(field.getName(), serialize);
} catch (Exception ignored) {
}
}
return document;
}
public static <T> Object serialize(Class<T> type, Object object) {
if (object == null)
return null;
else if (NUMBERS.contains(type))
return type.isInstance(object)
? object
: Double.parseDouble(String.valueOf(object));
else if (object instanceof UUID)
return object.toString();
else if (object instanceof Location location)
return location.serialize();
return object;
}
public static <T extends Entity> T deserialize(Document document, Class<T> type) throws Exception {
if (document == null)
throw new IllegalStateException("Trying to deserialize null document.");
T entity = type.getConstructor().newInstance();
for (Field field : type.getDeclaredFields()) {
if (Modifier.isTransient(field.getModifiers()))
continue;
field.setAccessible(true);
try {
Object value = document.get(field.getName());
ParameterizedType genericType = (ParameterizedType) field.getGenericType();
Class<?> propertyType = (Class<?>) genericType.getActualTypeArguments()[0];
Object deserialize = deserialize(propertyType, value);
@SuppressWarnings({"rawtypes", "unchecked"})
EntityProperty property = new EntityProperty(entity, field.getName(), propertyType, deserialize);
field.set(entity, property);
} catch (Exception ignored) {
}
}
return entity;
}
@SuppressWarnings("unchecked")
public static <T> Object deserialize(Class<T> type, Object object) {
if (object == null)
return null;
if (NUMBERS.contains(type))
return type.isInstance(object)
? object
: Double.parseDouble(String.valueOf(object));
else if (UUID.class.isAssignableFrom(type))
return UUID.fromString(String.valueOf(object));
else if (Location.class.isAssignableFrom(type))
return Location.deserialize((Map<String, Object>) object);
return object;
}
public static <T extends Entity> T init(Class<T> type) throws Exception {
T entity = type.getConstructor().newInstance();
int fieldIndex = 0;
for (Field field : type.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers()))
continue;
Class<?> fieldType = field.getType();
if (!EntityProperty.class.isAssignableFrom(fieldType))
continue;
ParameterizedType genericType = (ParameterizedType) field.getGenericType();
Class<?> propertyType = (Class<?>) genericType.getActualTypeArguments()[0];
Constructor<?> constructor = fieldType.getConstructor(Entity.class, String.class, Class.class);
@SuppressWarnings({"rawtypes", "unchecked"})
Object property = constructor.newInstance(entity, field.getName(), propertyType);
field.setAccessible(true);
field.set(entity, property);
}
return entity;
}
}

View file

@ -0,0 +1,55 @@
package ee.yoursit.core.database.entity.api;
import dev.inventex.octa.concurrent.future.Future;
import java.util.ArrayList;
import java.util.List;
public class ListEntityProperty<T> extends EntityProperty<List<T>> {
public ListEntityProperty(Entity entity, String name, Class<List<T>> type, List<T> value) {
super(entity, name, type, value);
}
public ListEntityProperty(Entity entity, String name, Class<List<T>> type) {
super(entity, name, type);
}
@MutateEntity
public Future<Boolean> push(T value) {
List<T> list = get();
if (list == null)
init(list = new ArrayList<>());
boolean add = list.add(value);
return mutate().transform(e -> add);
}
@MutateEntity
public Future<Boolean> pull(T value) {
List<T> list = get();
if (list == null)
return Future.completed(false);
boolean removed = list.remove(value);
return mutate().transform(e -> removed);
}
@MutateEntity
public Future<T> pop(int index) {
List<T> list = get();
if (list == null)
return Future.completed((T) null);
T removed = list.remove(index);
return mutate().transform(e -> removed);
}
public T get(int index) {
List<T> list = get();
if (list == null)
return null;
return list.get(index);
}
public int size() {
List<T> list = get();
return list != null ? list.size() : 0;
}
}

View file

@ -0,0 +1,41 @@
package ee.yoursit.core.database.entity.api;
import dev.inventex.octa.concurrent.future.Future;
import java.util.HashMap;
import java.util.Map;
public class MapEntityProperty<K, V> extends EntityProperty<Map<K, V>> {
public MapEntityProperty(Entity entity, String name, Class<Map<K, V>> type, Map<K, V> value) {
super(entity, name, type, value);
}
public MapEntityProperty(Entity entity, String name, Class<Map<K, V>> type) {
super(entity, name, type);
}
@MutateEntity
public Future<V> set(K key, V value) {
Map<K, V> map = get();
if (map == null)
init(map = new HashMap<>());
V oldValue = map.put(key, value);
return mutate().transform(e -> oldValue);
}
@MutateEntity
public Future<V> unset(K key) {
Map<K, V> map = get();
if (map == null)
return null;
V oldValue = map.remove(key);
return mutate().transform(e -> oldValue);
}
public V get(K key) {
Map<K, V> map = get();
if (map == null)
return null;
return map.get(key);
}
}

View file

@ -0,0 +1,4 @@
package ee.yoursit.core.database.entity.api;
public @interface MutateEntity {
}

View file

@ -0,0 +1,42 @@
package ee.yoursit.core.database.entity.impl;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import ee.yoursit.core.database.entity.api.Entity;
import ee.yoursit.core.database.entity.api.EntityProperty;
import lombok.Getter;
import lombok.Setter;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.UUID;
@Getter
public class Ban implements Entity {
@Setter
private static MongoCollection<Document> collection;
private EntityProperty<UUID> banId, moderator, target;
private EntityProperty<String> reason;
private EntityProperty<Long> timestamp;
@Override
public Bson query() {
return Filters.eq("banId", banId.get().toString());
}
public static Bson queryId(UUID banId) {
return Filters.eq("banId", banId.toString());
}
public static Bson queryTarget(UUID target) {
return Filters.eq("target", target.toString());
}
@Override
public MongoCollection<Document> collection() {
return collection;
}
}

View file

@ -0,0 +1,42 @@
package ee.yoursit.core.database.entity.impl;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import ee.yoursit.core.database.entity.api.Entity;
import ee.yoursit.core.database.entity.api.EntityProperty;
import ee.yoursit.core.database.entity.api.MapEntityProperty;
import lombok.Getter;
import lombok.Setter;
import org.bson.Document;
import org.bson.conversions.Bson;
import java.util.Map;
import static com.mongodb.client.model.Filters.eq;
@Getter
public class Group implements Entity {
@Setter
private static MongoCollection<Document> collection;
private EntityProperty<String> name;
private EntityProperty<String> prefix, suffix;
private EntityProperty<String> tabPrefix, tabSuffix;
private MapEntityProperty<String, Boolean> permissions;
public static Bson query(String name) {
return eq("name", name);
}
@Override
public Bson query() {
return Filters.eq("name", name.get());
}
@Override
public MongoCollection<Document> collection() {
return collection;
}
}

View file

@ -0,0 +1,34 @@
package ee.yoursit.core.database.entity.impl;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import ee.yoursit.core.database.entity.api.Entity;
import ee.yoursit.core.database.entity.api.EntityProperty;
import lombok.Getter;
import lombok.Setter;
import org.bson.Document;
import org.bson.conversions.Bson;
@Getter
public class Invite implements Entity {
@Setter
static MongoCollection<Document> collection;
private EntityProperty<String> code;
private EntityProperty<Boolean> used;
private EntityProperty<String> creator;
@Override
public Bson query() {
return Filters.eq("code", code.get());
}
public static Bson query(String code) {
return Filters.eq("code", code);
}
@Override
public MongoCollection<Document> collection() {
return collection;
}
}

View file

@ -0,0 +1,55 @@
package ee.yoursit.core.database.entity.impl;
import com.mongodb.client.MongoCollection;
import ee.yoursit.core.database.entity.api.Entity;
import ee.yoursit.core.database.entity.api.EntityProperty;
import ee.yoursit.core.database.entity.api.ListEntityProperty;
import lombok.Getter;
import lombok.Setter;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bukkit.Location;
import java.util.UUID;
import static com.mongodb.client.model.Filters.*;
@Getter
public class User implements Entity {
@Setter
private static MongoCollection<Document> collection;
private EntityProperty<String> username;
private EntityProperty<UUID> uuid;
private EntityProperty<Long> discordId;
private EntityProperty<Long> registered;
private EntityProperty<Long> lastOnline;
private EntityProperty<Long> playTime;
private EntityProperty<String> group;
private EntityProperty<Location> home;
private EntityProperty<Boolean> banned;
private EntityProperty<String> invite;
private ListEntityProperty<String> invites;
public static Bson query(UUID uuid) {
return eq("uuid", uuid.toString());
}
public static Bson query(String username) {
return eq("username", username);
}
@Override
public Bson query() {
return eq("uuid", uuid.get().toString());
}
@Override
public MongoCollection<Document> collection() {
return collection;
}
}

View file

@ -0,0 +1,43 @@
package ee.yoursit.core.database.entity.impl;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.model.Filters;
import ee.yoursit.core.database.entity.api.Entity;
import ee.yoursit.core.database.entity.api.EntityProperty;
import lombok.Getter;
import lombok.Setter;
import org.bson.Document;
import org.bson.conversions.Bson;
@Getter
public class Verification implements Entity {
@Setter
private static MongoCollection<Document> collection;
private EntityProperty<Long> discordId;
private EntityProperty<String> username;
private EntityProperty<String> code;
private EntityProperty<Long> creation;
@Override
public Bson query() {
return Filters.eq("code", code.get());
}
public static Bson queryCode(String code) {
return Filters.eq("code", code);
}
public static Bson queryName(String name) {
return Filters.eq("username", name);
}
public static Bson queryId(long discordId) {
return Filters.eq("discordId", discordId);
}
@Override
public MongoCollection<Document> collection() {
return collection;
}
}

View file

@ -0,0 +1,45 @@
package ee.yoursit.core.database.service;
import dev.inventex.octa.concurrent.future.Future;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.Ban;
import org.bson.Document;
import java.util.UUID;
public class BanService {
public Future<Ban> getBanById(UUID banId) {
return Future.tryCompleteAsync(() -> {
Document document = YsmpCore.INSTANCE
.getDatabase()
.getInvites()
.find(Ban.queryId(banId))
.first();
return EntitySerializer.deserialize(document, Ban.class);
});
}
public Future<Ban> getBanByTarget(UUID target) {
return Future.tryCompleteAsync(() -> {
Document document = YsmpCore.INSTANCE
.getDatabase()
.getInvites()
.find(Ban.queryTarget(target))
.first();
return EntitySerializer.deserialize(document, Ban.class);
});
}
public Future<Void> createBan(Ban ban) {
return Future.tryCompleteAsync(() -> {
Document document = EntitySerializer.serialize(ban);
YsmpCore.INSTANCE
.getDatabase()
.getInvites()
.insertOne(document);
});
}
}

View file

@ -0,0 +1,82 @@
package ee.yoursit.core.database.service;
import com.google.common.collect.Maps;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCursor;
import dev.inventex.octa.concurrent.future.Future;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.Group;
import lombok.Getter;
import org.bson.Document;
import java.util.Map;
public class GroupService {
@Getter
private final Map<String, Group> groupCache = Maps.newConcurrentMap();
public void loadGroups() {
try (MongoCursor<Document> cursor = YsmpCore.INSTANCE
.getDatabase()
.getGroups()
.find()
.cursor()) {
while (cursor.hasNext()) {
loadGroup(cursor.next());
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void loadGroup(Document document) {
try {
Group group = EntitySerializer.deserialize(document, Group.class);
groupCache.put(group.getName().get(), group);
} catch (Exception e) {
System.err.println("Unable to load group " + document);
e.printStackTrace();
}
}
public Future<Group> getGroup(String name) {
Group group = groupCache.get(name);
if (group != null)
return Future.completed(group);
return Future.tryCompleteAsync(() -> {
Document document = YsmpCore.INSTANCE
.getDatabase()
.getGroups()
.find(Group.query(name))
.first();
return EntitySerializer.deserialize(document, Group.class);
});
}
public Future<Void> addGroup(Group group) {
if (groupCache.containsKey(group.getName().get()))
return Future.failed(new IllegalStateException("Group '" + group.getName() + "' already exists."));
return Future.tryCompleteAsync(() -> {
Document document = EntitySerializer.serialize(group);
YsmpCore.INSTANCE
.getDatabase()
.getGroups()
.insertOne(document);
});
}
public Future<Void> removeGroup(String name) {
groupCache.remove(name);
return Future.tryCompleteAsync(() -> {
YsmpCore.INSTANCE
.getDatabase()
.getGroups()
.deleteOne(Group.query(name));
});
}
}

View file

@ -0,0 +1,31 @@
package ee.yoursit.core.database.service;
import dev.inventex.octa.concurrent.future.Future;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.Invite;
import org.bson.Document;
public class InviteService {
public Future<Invite> getInvite(String code) {
return Future.tryCompleteAsync(() -> {
Document document = YsmpCore.INSTANCE
.getDatabase()
.getInvites()
.find(Invite.query(code))
.first();
return EntitySerializer.deserialize(document, Invite.class);
});
}
public Future<Void> createInvite(Invite invite) {
return Future.completeAsync(() -> {
Document document = EntitySerializer.serialize(invite);
YsmpCore.INSTANCE
.getDatabase()
.getInvites()
.insertOne(document);
});
}
}

View file

@ -0,0 +1,45 @@
package ee.yoursit.core.database.service;
import dev.inventex.octa.concurrent.future.Future;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.User;
import org.bson.Document;
import java.util.UUID;
public class UserService {
public Future<User> getUser(UUID uuid) {
return Future.tryCompleteAsync(() -> {
Document document = YsmpCore.INSTANCE
.getDatabase()
.getUsers()
.find(User.query(uuid))
.first();
return EntitySerializer.deserialize(document, User.class);
});
}
public Future<User> getUser(String name) {
return Future.tryCompleteAsync(() -> {
Document document = YsmpCore.INSTANCE
.getDatabase()
.getUsers()
.find(User.query(name))
.first();
return EntitySerializer.deserialize(document, User.class);
});
}
public Future<Void> addUser(User user) {
return Future.completeAsync(() -> {
Document document = EntitySerializer.serialize(user);
YsmpCore.INSTANCE
.getDatabase()
.getUsers()
.insertOne(document);
});
}
}

View file

@ -0,0 +1,68 @@
package ee.yoursit.core.database.service;
import dev.inventex.octa.concurrent.future.Future;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.database.entity.api.EntitySerializer;
import ee.yoursit.core.database.entity.impl.User;
import ee.yoursit.core.database.entity.impl.Verification;
import org.bson.Document;
import org.bukkit.entity.Player;
import java.util.UUID;
public class VerificationService {
public Future<Verification> getVerification(String code) {
return Future.tryCompleteAsync(() -> {
Document document = YsmpCore.INSTANCE
.getDatabase()
.getVerifications()
.find(Verification.queryCode(code))
.first();
return EntitySerializer.deserialize(document, Verification.class);
});
}
public Future<Verification> getVerification(long discordId) {
return Future.tryCompleteAsync(() -> {
Document document = YsmpCore.INSTANCE
.getDatabase()
.getVerifications()
.find(Verification.queryId(discordId))
.first();
return EntitySerializer.deserialize(document, Verification.class);
});
}
public Future<Verification> getVerification(Player player) {
return Future.tryCompleteAsync(() -> {
Document document = YsmpCore.INSTANCE
.getDatabase()
.getVerifications()
.find(Verification.queryName(player.getName()))
.first();
return EntitySerializer.deserialize(document, Verification.class);
});
}
public Future<Void> addVerification(Verification verification) {
return Future.completeAsync(() -> {
Document document = EntitySerializer.serialize(verification);
YsmpCore.INSTANCE
.getDatabase()
.getVerifications()
.insertOne(document);
});
}
public Future<Void> removeVerification(Verification verification) {
return Future.completeAsync(() -> {
YsmpCore.INSTANCE
.getDatabase()
.getVerifications()
.deleteOne(verification.query());
});
}
}

View file

@ -0,0 +1,72 @@
package ee.yoursit.core.module;
import ee.yoursit.core.command.bukkit.api.Command;
import ee.yoursit.core.command.bukkit.impl.admin.GroupCommand;
import ee.yoursit.core.command.bukkit.impl.home.DelHomeCommand;
import ee.yoursit.core.command.bukkit.impl.home.HomeCommand;
import ee.yoursit.core.command.bukkit.impl.home.SetHomeCommand;
import ee.yoursit.core.command.bukkit.impl.invite.VerifyCommand;
import ee.yoursit.core.command.bukkit.impl.message.MessageCommand;
import ee.yoursit.core.command.bukkit.impl.message.ReplyCommand;
import ee.yoursit.core.command.bukkit.impl.teleport.TeleportAccept;
import ee.yoursit.core.command.bukkit.impl.teleport.TeleportDeny;
import ee.yoursit.core.command.bukkit.impl.teleport.TeleportRequest;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
public class BukkitCommandRegistry implements CommandRegistry {
private final Class<?>[] classes = {
TeleportRequest.class,
TeleportAccept.class,
TeleportDeny.class,
MessageCommand.class,
ReplyCommand.class,
SetHomeCommand.class,
DelHomeCommand.class,
HomeCommand.class,
VerifyCommand.class,
GroupCommand.class
};
private final Constructor<?>[] constructors = new Constructor[classes.length];
private final List<Command> commands = new ArrayList<>(classes.length);
public BukkitCommandRegistry() {
for (int i = 0; i < classes.length; i++) {
Class<?> command = classes[i];
try {
constructors[i] = command.getDeclaredConstructor();
} catch (NoSuchMethodException e) {
System.err.println("Unable to load command " + command.getSimpleName() + ":");
e.printStackTrace();
}
}
}
@Override
public void load() {
for (Constructor<?> constructor : constructors) {
try {
Command command = (Command) constructor.newInstance();
commands.add(command);
command.register();
} catch (Exception e) {
System.err.println("Unable to instantiate check " + constructor.getDeclaringClass().getSimpleName());
e.printStackTrace();
}
}
}
@Override
public void unload() {
for (Command command : commands)
command.unregister();
}
}

View file

@ -0,0 +1,170 @@
package ee.yoursit.core.module;
import dev.inventex.octa.concurrent.future.Future;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.config.Messages;
import ee.yoursit.core.data.PlayerData;
import ee.yoursit.core.database.entity.impl.Group;
import ee.yoursit.core.database.entity.impl.User;
import ee.yoursit.core.util.Message;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.*;
import org.bukkit.event.server.ServerListPingEvent;
import org.bukkit.util.Vector;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class BukkitListener implements Listener {
private final Set<UUID> sleeping = new HashSet<>();
@EventHandler
public void onLogin(PlayerLoginEvent event) {
YsmpCore.INSTANCE
.getDataManager()
.add(event.getPlayer());
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(player);
data.setOnline(true);
data.setJoinTime(System.currentTimeMillis());
data.updateLastOnline();
String message = Messages
.PLAYER_JOIN
.replace("{player}", player.getName());
event.setJoinMessage(message);
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
YsmpCore.INSTANCE
.getDataManager()
.get(player)
.updateLastOnline();
YsmpCore.INSTANCE
.getDataManager()
.remove(player);
String message = Messages
.PLAYER_LEAVE
.replace("{player}", player.getName());
event.setQuitMessage(message);
}
@EventHandler
public void onBedEnter(PlayerBedEnterEvent event) {
Collection<? extends Player> onlinePlayers = Bukkit.getOnlinePlayers();
long sleeping = onlinePlayers
.stream()
.filter(Player::isSleeping)
.count();
long total = onlinePlayers.size();
float ratio = (float) sleeping / (float) total;
if (ratio >= 2F / 3F) {
event
.getPlayer()
.getWorld()
.setTime(0);
onlinePlayers.forEach(player -> player.sendMessage(Messages.AUTO_SLEEP));
}
}
@EventHandler
public void onPing(ServerListPingEvent event) {
String motd = Message.translate(
"""
&b&lYourSitee SMP &f&l 1.20.1
&f Visit yoursit.ee/smp for more info
"""
);
event.setMotd(motd);
}
@EventHandler
public void onMove(PlayerMoveEvent event) {
Vector from = event.getFrom().toVector();
Vector to = event.getTo().toVector();
double distance = from.distance(to);
if (distance < .01)
return;
cancelVerifying(event.getPlayer(), event);
}
@EventHandler
public void onCommand(PlayerCommandPreprocessEvent event) {
String command = event
.getMessage()
.split(" ")[0]
.substring(1);
if (command.equals("verify"))
return;
cancelVerifying(event.getPlayer(), event);
}
@EventHandler
public void onChat(AsyncPlayerChatEvent event) {
Player player = event.getPlayer();
cancelVerifying(player, event);
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(player);
Group group = null;
try {
User user = data
.getUser()
.get();
group = YsmpCore.INSTANCE
.getGroupService()
.getGroup(user.getGroup().get())
.get();
} catch (Exception e) {
e.printStackTrace();
}
String prefix = null, suffix = null;
if (group != null) {
prefix = group.getPrefix().get();
suffix = group.getSuffix().get();
}
if (prefix == null) prefix = "";
if (suffix == null) suffix = "";
String format = "&r" + prefix + player.getName() + "&r" + suffix + "&7: &r" + event.getMessage();
event.setFormat(Message.translate(format));
}
private void cancelVerifying(Player player, Cancellable cancellable) {
PlayerData data = YsmpCore.INSTANCE
.getDataManager()
.get(player);
if (data.isVerifying())
cancellable.setCancelled(true);
}
}

View file

@ -0,0 +1,7 @@
package ee.yoursit.core.module;
public interface CommandRegistry {
void load();
void unload();
}

View file

@ -0,0 +1,91 @@
package ee.yoursit.core.module;
import dev.inventex.discord.command.CommandManager;
import dev.inventex.discord.command.slash.SlashCommand;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.command.discord.*;
import ee.yoursit.core.util.Constants;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.interactions.commands.Command;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.List;
public class DiscordCommandRegistry implements CommandRegistry {
private final Class<?>[] classes = {
PlayersCommand.class,
BanCommand.class,
UnbanCommand.class,
InviteCommand.class,
VerifyCommand.class,
SmpCommand.class
};
private final Constructor<?>[] constructors = new Constructor[classes.length];
private final List<SlashCommand> commands = new ArrayList<>(classes.length);
public DiscordCommandRegistry() {
for (int i = 0; i < classes.length; i++) {
Class<?> command = classes[i];
try {
constructors[i] = command.getDeclaredConstructor();
}
catch (NoSuchMethodException e) {
System.err.println("Unable to load command " + command.getSimpleName() + ":");
e.printStackTrace();
}
}
}
@Override
public void load() {
JDA jda = YsmpCore.INSTANCE
.getClient()
.getJda();
jda
.retrieveCommands()
.queue(list -> {
for (Command command : list) {
command
.delete()
.queue();
}
});
Guild guild = jda.getGuildById(Constants.YSMP_SERVER);
assert guild != null;
CommandManager commandManager = YsmpCore.INSTANCE
.getClient()
.getCommandManager();
for (Constructor<?> constructor : constructors) {
try {
SlashCommand command = (SlashCommand) constructor.newInstance();
commands.add(command);
if (command instanceof SmpCommand) {
Guild main = jda.getGuildById(Constants.MAIN_SERVER);
assert main != null;
commandManager.registerToGuild(command, main);
continue;
}
commandManager.registerToGuild(command, guild);
}
catch (Exception e) {
System.err.println("Unable to instantiate check " + constructor.getDeclaringClass().getSimpleName());
e.printStackTrace();
}
}
}
@Override
public void unload() {
}
}

View file

@ -0,0 +1,64 @@
package ee.yoursit.core.module;
import ee.yoursit.core.YsmpCore;
import ee.yoursit.core.config.Messages;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.SelfUser;
import net.dv8tion.jda.api.events.session.ReadyEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.bukkit.Bukkit;
import java.util.Timer;
import java.util.TimerTask;
public class DiscordListener extends ListenerAdapter {
private static final int ACTIVITY_UPDATE = 1000 * 5;
private int actionIndex = 0;
public void onReady(ReadyEvent event) {
SelfUser user = event.getJDA().getSelfUser();
System.out.println("Logged in as " + user.getName() + "#" + user.getDiscriminator());
TimerTask task = new TimerTask() {
@Override
public void run() {
updateActivity();
}
};
Timer timer = new Timer();
timer.scheduleAtFixedRate(task, ACTIVITY_UPDATE, ACTIVITY_UPDATE);
YsmpCore.INSTANCE
.getDiscordCommands()
.load();
}
private void updateActivity() {
ActivityState[] values = ActivityState.values();
ActivityState state = values[actionIndex++ % values.length];
Activity activity = switch (state) {
case LIST_PLAYERS -> {
String count = String.valueOf(Bukkit.getOnlinePlayers().size());
yield Activity.watching(Messages.ACTIVITY_PLAYERS
.replace("{count}", count));
}
case SHOW_DISCORD_LINK -> Activity.playing(Messages.ACTIVITY_DISCORD);
case SHOW_SERVER_ADDRESS -> Activity.playing(Messages.ACTIVITY_ADDRESS);
};
YsmpCore.INSTANCE
.getClient()
.getJda()
.getPresence()
.setActivity(activity);
}
public enum ActivityState {
LIST_PLAYERS,
SHOW_DISCORD_LINK,
SHOW_SERVER_ADDRESS
}
}

View file

@ -0,0 +1,9 @@
package ee.yoursit.core.util;
public enum ClickAction {
OPEN_URL,
OPEN_FILE,
RUN_COMMAND,
SUGGEST_COMMAND,
CHANGE_PAGE
}

View file

@ -0,0 +1,16 @@
package ee.yoursit.core.util;
public class Constants {
public static final long YSMP_SERVER = 598949514052894721L,
MAIN_SERVER = 886538767995895839L;
public static final int TRANSPARENT = 0x313338;
public static final long SERVER_LEADER = 1107079573763543152L,
PREMIUM = 1107244269112148109L,
BUSINESS = 1107244259163246603L,
INVITED = 1107080146051158046L,
PLAYER = 1107080049716375582L;
public static final long BANNED = 1124263377439555644L;
}

View file

@ -0,0 +1,8 @@
package ee.yoursit.core.util;
public enum HoverAction {
SHOW_TEXT,
SHOW_ACHIEVEMENT,
SHOW_ITEM,
SHOW_ENTITY
}

View file

@ -0,0 +1,32 @@
package ee.yoursit.core.util;
import lombok.RequiredArgsConstructor;
import java.util.Random;
@RequiredArgsConstructor
public class InviteGenerator {
private static final String LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final String DIGITS = "0123456789";
private static final char[] CHARS = (LETTERS + DIGITS).toCharArray();
private static final Random random = new Random();
private final int length;
private final int count;
public String generate() {
char[] buffer = new char[length * count + (count - 1)];
for (int index = 0, offset = 0; index < buffer.length; index++) {
if (offset == length) {
buffer[index] = '-';
offset = 0;
} else {
buffer[index] = CHARS[random.nextInt(CHARS.length)];
offset++;
}
}
return new String(buffer);
}
}

View file

@ -0,0 +1,79 @@
package ee.yoursit.core.util;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.*;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Message {
private final TextComponent component;
public Message(String message, Object... args) {
component = new TextComponent(translate(message, args));
}
public Message() {
component = new TextComponent();
}
public Message click(ClickAction action, String message, Object... args) {
ClickEvent.Action clickAction = ClickEvent.Action.valueOf(action.name());
component.setClickEvent(new ClickEvent(clickAction, translate(message, args)));
return this;
}
public Message click(String command, Object... args) {
return click(ClickAction.RUN_COMMAND, command, args);
}
public Message hover(HoverAction action, String message, Object... args) {
HoverEvent.Action hoverAction = HoverEvent.Action.valueOf(action.name());
BaseComponent[] data = new ComponentBuilder(translate(message, args)).create();
component.setHoverEvent(new HoverEvent(hoverAction, data));
return this;
}
public Message hover(String message, Object... args) {
return hover(HoverAction.SHOW_TEXT, message, args);
}
public Message add(String message, Object... args) {
component.addExtra(translate(message, args));
return this;
}
public Message add(Message message) {
component.addExtra(message.component);
return this;
}
public void send(Player player) {
player.spigot().sendMessage(component);
}
public void send(CommandSender sender) {
if (sender instanceof Player)
((Player) sender).spigot().sendMessage(component);
else
sender.sendMessage(component.toLegacyText());
}
public static String translate(String message, Object... args) {
message = String.format(message, args);
Pattern pattern = Pattern.compile("#[a-fA-F0-9]{6}");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
String color = message.substring(matcher.start(), matcher.end());
message = message.replace(color, String.valueOf(ChatColor.of(color)));
matcher = pattern.matcher(message);
}
return ChatColor.translateAlternateColorCodes('&', message);
}
}

View file

@ -0,0 +1,23 @@
bot-token: 'AAAA-AAAA-AAAA-AAAA'
spawn:
==: org.bukkit.Location
world: world
x: 100.0
y: 100.0
z: 100.0
yaw: 0.0
pitch: 0.0
verify:
==: org.bukkit.Location
world: verification
x: 0.0
y: 57.0
z: 0.0
yaw: 0.0
pitch: 0.0
verify-timeout: 60
ysmp-invite: "https://discord.gg/3sdUGhNNHp"

View file

@ -0,0 +1,107 @@
internal-error: "&c&lERROR &7An internal error occurred whilst trying to perform this command"
player-join: "&a● &f{player}"
player-leave: "&c● &f{player}"
player-not-online: "&c{player} is not online"
no-players-online: "There are no online players at the moment"
not-a-user: "You are not a registered YSMP user"
already-user: "You are already a member of the YourSitee SMP"
banned-from-server: "&c&lUnable to connect\n&7You have been banned from the server"
not-whitelisted: "&c&lERROR\n&7You are not whitelisted on this server"
unable-to-fetch: "&c&lERROR\n&7Unable to fetch player data\nTry again later"
invite:
limit-exceeded: "You have exceeded your invite creation limit"
not-found: "The specified invite does not exist or has been already used"
not-on-guild: "You can only accept invites in the YSMP discord server"
not-joined-guild: "&c&lERROR &7You need to join the YSMP discord server to be able to verify"
verification: "In order to verify your Minecraft username, you need to join `smp.yoursit.ee` and type in the following command: ```/verify {code}```"
use-verify: "&6&lVERIFY &7Type &f/verify <your code> &7in order to gain access for the server"
verify-timeout: "&c&lVERIFY\n&7You have exceeded the verification timeout"
verified: "&6&lYSMP &7You are now eligible to play on the YourSitee SMP server\n"
already-verified: "&c&lERROR &7You are already verified"
already-verifying: "This invite is already being verified by another user"
already-verifying-self: "You are already in the verification process"
invalid-code: "&c&lERROR\n&7The specified invite does not exist or has been already used"
teleport-request:
send: "&6&lTELEPORT &fYou have sent a teleport request to &e{player}"
receive: "&6&lTELEPORT &e{player} &fhas sent you a teleport request"
usage: "&c&lUSAGE &7/tpa <player>"
already: "&c&lERROR &7You have already sent a teleport request to &f{player}"
teleport-accept:
send: "&6&lTELEPORT &fyou have accepted &e{player}&f's teleport request"
receive: "&6&lTELEPORT &e{player} &faccepted your teleport request"
teleport-decline:
send: "&6&lTELEPORT &fyou have decliend &e{player}&f's teleport request"
receive: "&6&lTELEPORT &e{player} &fhas declined your teleport request"
teleport:
no-requests: "&c&lERROR &7You don't have any teleport requests"
no-request-from: "&c&lERROR &f{player} did not send you a teleport request"
private-message:
send: "&6&lFROM &e{player} &8► &f{message}"
receive: "&6&lTO &e{player} &8► &f{message}"
usage: "&c&lUSAGE &7/msg <player> <message>"
no-open-channels: "&c&lERROR &7You don't have any private message channels open right now"
home:
updated: "&6&lHOME &7You have successfully assigned a new home location"
teleporting: "&6&lHOME &7Teleporting to home"
not-set: "&c&lERROR &7You don't have a home location set"
deleted: "&6&lHOME &7You have successfully deleted your home location"
auto-sleep: "&6&lYSMP &7More than 2/3 of the players are sleeping. Switching to day..."
activity:
players: "{count} online players"
discord: "yoursit.ee/smp"
address: "smp.yoursit.ee"
no-such-user: "&c&lERROR &7No such user &7{name}"
no-permission: "&c&lERROR &7You don't have permission to use this command"
group:
not-exists: "&c&lERROR &7Group &f{group} &7does not exist"
usage: "&c&lUSAGE &7/group <create|delete|list|group> <meta|permission|grant> [...args]"
create:
usage: "&c&lUSAGE &7/group create <name>"
already-exists: "&c&lERROR &7Group &f{group} &7already exists"
created: "&6&lGROUP &7Successfully created group &f{group}"
delete:
usage: "&c&lUSAGE &7/group delete <name>"
not-exists: "&c&lERROR &7Group &f{group} &7does not exist"
deleted: "&6&lGROUP &7Group &f{group} &7has been deleted"
list: "&6&lGROUP &7Available groups: &f{list}"
no-groups: "&6&lGROUP &7There are no available groups yet"
meta:
usage: "&c&lUSAGE &7/group <group> meta <prefix|suffix|tabprefix|tabsuffix> [..args]"
info: "&6&lGROUP &7Group &f{group} &7meta information:\n &7prefix: &r{prefix}\n &7suffix: &r{suffix}\n &7tab prefix: &r{tabprefix}\n &7tab suffix: &r{tabsuffix}"
prefix:
usage: "&c&lUSAGE &7/group <group> meta prefix <prefix>"
set: "&6&lGROUP &7Updated group &f{group} &7prefix to &r{prefix}"
suffix:
usage: "&c&lUSAGE &7/group <group> meta suffix <suffix>"
set: "&6&lGROUP &7Updated group &f{group} &7suffix to &r{suffix}"
tab-prefix:
usage: "&c&lUSAGE &7/group <group> meta tabprefix <prefix>"
set: "&6&lGROUP &7Updated group &f{group} &7tab prefix to &r{tabprefix}"
tab-suffix:
usage: "&c&lUSAGE &7/group <group> meta tabsuffix <suffix>"
set: "&6&lGROUP &7Updated group &f{group} &7tab suffix to &r{tabsuffix}"
permission:
usage: "&c&lUSAGE &7/group <group> permission <set|unset> [..args]"
set:
usage: "&c&lUSAGE &7/group <group> permission set <permission> <state>"
updated: "&6&lGROUP &7Set group &f{group} &7permission &f{permission} &7to &f{state}"
unset:
usage: "&c&lUSAGE &7/group <group> permission unset <permission>"
updated: "&6&lGROUP &7Unset group &f{group} &7permission &f{permission}"
grant:
usage: "&c&lUSAGE &7/group <group> grant <user>"
granted: "&6&lGROUP &7Granted group &f{group} &7to player &f{player}"

View file

@ -0,0 +1,38 @@
name: ysmp-core
version: 0.1.1
main: ee.yoursit.core.Main
author: AdvancedAntiSkid
website: yoursit.ee
api-version: 1.20
softdepend:
- Multiverse-Core
commands:
tpa:
usage: Send a teleportation request to the specified player
tpaccept:
usage: Accept the teleportation request of a player
tpdeny:
usage: Decline the teleportation request of a player
message:
usage: Send a private message to a player
aliases:
- msg
- m
- pm
- dm
reply:
usage: Reply to the private message to a player
aliases:
- repl
- r
sethome:
usage: Update your home location
delhome:
usage: Delete your home location
home:
usage: Teleport to your home location
verify:
usage: Verify invitation code
group:
usage: Manage groups and permissions

View file

@ -0,0 +1,9 @@
import ee.yoursit.core.util.InviteGenerator;
public class GeneratorTest {
public static void main(String[] args) {
InviteGenerator generator = new InviteGenerator(5, 4);
String invite = generator.generate();
System.out.println(invite);
}
}