Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage alts using in-game commands #15

Merged
merged 21 commits into from
May 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/main/java/pro/cloudnode/smp/smpcore/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,11 @@ public int apiPort() {
public int membersInactiveDays() {
return config.getInt("members.inactive-days");
}

/**
* Maximum number of alts you can have
*/
public int altsMax() {
return config.getInt("alts.max");
}
}
54 changes: 53 additions & 1 deletion src/main/java/pro/cloudnode/smp/smpcore/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
import java.sql.SQLException;
import java.util.Date;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.stream.Collectors;

public final class Member {
public final @NotNull UUID uuid;
Expand All @@ -30,6 +33,10 @@ private Member(final @NotNull UUID uuid, final @Nullable String nationID, final
this.added = added;
}

public Member(final @NotNull OfflinePlayer player, final @Nullable Member altOwner) {
this(player.getUniqueId(), null, false, altOwner == null ? null : altOwner.uuid, new Date());
}

private Member(final @NotNull ResultSet rs) throws @NotNull SQLException {
this(UUID.fromString(rs.getString("uuid")), rs.getString("nation"), rs.getBoolean("staff"), rs.getString("alt_owner") == null ? null : UUID.fromString(rs.getString("alt_owner")), rs.getTimestamp("added"));
}
Expand Down Expand Up @@ -58,6 +65,10 @@ public boolean isAlt() {
return Token.create(this);
}

public @NotNull HashSet<@NotNull Token> tokens() {
return Token.get(this);
}

public @NotNull HashSet<@NotNull Member> getAlts() {
final @NotNull HashSet<@NotNull Member> alts = new HashSet<>();
try (
Expand Down Expand Up @@ -96,7 +107,10 @@ public void save() {
}
}

private void delete() {
/**
* Removes only from database
*/
private void remove() {
try (
final @NotNull Connection conn = SMPCore.getInstance().db()
.getConnection(); final @NotNull PreparedStatement stmt = conn.prepareStatement("DELETE FROM `members` WHERE `uuid` = ?")
Expand All @@ -109,6 +123,36 @@ private void delete() {
}
}

/**
* Member deletion procedure, i.e. membership revocation
*
* <p>Will not be deleted if:</p>
* <ul>
* <li>has alts (delete alts first)</li>
* <li>is leader of a nation (change leader or delete nation)</li>
* </ul>
* <p>If vice leader of a nation, will set nation's leader to both leader and vice leader</p>
*
* @return whether the member was deleted
*/
public boolean delete() {
if (!getAlts().isEmpty()) return false;
final @NotNull OfflinePlayer player = player();
SMPCore.runMain(() -> player.setWhitelisted(false));
final @NotNull Optional<@NotNull Nation> nation = nation();
if (nation.isPresent()) {
if (nation.get().leaderUUID.equals(player.getUniqueId())) return false;
if (nation.get().viceLeaderUUID.equals(player.getUniqueId())) {
nation.get().viceLeaderUUID = nation.get().leaderUUID;
nation.get().save();
}
SMPCore.runMain(() -> nation.get().getTeam().removePlayer(player));
}
tokens().forEach(Token::delete);
remove();
return true;
}

public static @NotNull Member create(final @NotNull OfflinePlayer player, final @Nullable Member altOwner) {
final @NotNull Member member = new Member(player.getUniqueId(), null, false, altOwner == null ? null : altOwner.uuid, new Date());
member.save();
Expand Down Expand Up @@ -184,4 +228,12 @@ private void delete() {
}
return members;
}

public static @NotNull Set<@NotNull String> getNames() {
return get().stream().map(m -> m.player().getName()).filter(Objects::nonNull).collect(Collectors.toSet());
}

public static @NotNull Set<@NotNull String> getAltNames() {
return get().stream().filter(Member::isAlt).map(m -> m.player().getName()).filter(Objects::nonNull).collect(Collectors.toSet());
}
}
154 changes: 154 additions & 0 deletions src/main/java/pro/cloudnode/smp/smpcore/Messages.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package pro.cloudnode.smp.smpcore;

import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
Expand Down Expand Up @@ -61,6 +65,84 @@ public Messages() {
.toString())), Placeholder.unparsed("n-alts", String.valueOf(alts.size())));
}

// subcommands

public @NotNull Component subCommandHeader(final @NotNull String name, final @NotNull String usage) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("subcommands.header")), Placeholder.unparsed("name", name), Placeholder.unparsed("usage", usage));
}

public @NotNull Component subCommandEntry(final @NotNull String command, final @NotNull String label, final @NotNull SubCommandArgument @NotNull [] args) {
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("subcommands.entry"))
.replace("<command>", command), Placeholder.unparsed("label", label), Placeholder.component("args", SubCommandArgument.join(args)));
}

public @NotNull Component subCommandEntry(final @NotNull String command, final @NotNull String label) {
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("subcommands.entry"))
.replace("<command>", command), Placeholder.unparsed("label", label), Placeholder.component("args", SubCommandArgument.join(SubCommandArgument.of())));
}

public @NotNull Component subCommandEntry(final @NotNull String command, final @NotNull String label, final @NotNull SubCommandArgument @NotNull [] args, final @NotNull String description) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("subcommands.entry-with-description"))
.replace("<command>", command), Placeholder.unparsed("label", label), Placeholder.component("args", SubCommandArgument.join(args)), Placeholder.unparsed("description", description));
}

public @NotNull Component subCommandEntry(final @NotNull String command, final @NotNull String label, final @NotNull String description) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("subcommands.entry-with-description"))
.replace("<command>", command), Placeholder.unparsed("label", label), Placeholder.component("args", SubCommandArgument.join(SubCommandArgument.of())), Placeholder.unparsed("description", description));
}

public @NotNull Component subCommandArgumentRequired(final @NotNull String argument) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("subcommands.argument.required")), Placeholder.unparsed("arg", argument));
}

public @NotNull Component subCommandArgumentOptional(final @NotNull String argument) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("subcommands.argument.optional")), Placeholder.unparsed("arg", argument));
}

// end of subcommands

public @NotNull Component altsListHeader(final @NotNull Member member) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("alts.list.header")), Placeholder.unparsed("player", Optional
.ofNullable(member.player().getName()).orElse(member.player().getUniqueId().toString())));
}

public @NotNull Component altsListNone() {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("alts.list.none")));
}

public @NotNull Component altsListEntry(final @NotNull Member alt) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("alts.list.entry")), Placeholder.unparsed("alt", Optional
.ofNullable(alt.player().getName()).orElse(alt.player().getUniqueId().toString())));
}

public @NotNull Component altsConfirmAdd(final @NotNull OfflinePlayer alt, final @NotNull String confirmCommand) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("alts.confirm-add")).replace("<confirm-command>", confirmCommand), Placeholder.unparsed("alt", Optional
.ofNullable(alt.getName()).orElse(alt.getUniqueId().toString())));
}

public @NotNull Component altsCreated(final @NotNull Member alt) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("alts.created")), Placeholder.unparsed("alt", Optional
.ofNullable(alt.player().getName()).orElse(alt.player().getUniqueId().toString())));
}

public @NotNull Component altsDeleted(final @NotNull Member alt) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("alts.deleted")), Placeholder.unparsed("alt", Optional
.ofNullable(alt.player().getName()).orElse(alt.player().getUniqueId().toString())));
}

// errors

public @NotNull Component errorNoPermission() {
return MiniMessage.miniMessage().deserialize(Objects.requireNonNull(config.getString("error.no-permission")));
}
Expand All @@ -70,4 +152,76 @@ public Messages() {
.deserialize(Objects.requireNonNull(config.getString("error.player-not-banned")), Placeholder.unparsed("player", Optional
.ofNullable(player.getName()).orElse(player.getUniqueId().toString())));
}

public @NotNull Component errorNotMember(final @NotNull OfflinePlayer player) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("error.not-member")), Placeholder.unparsed("player", Optional
.ofNullable(player.getName()).orElse(player.getUniqueId().toString())));
}

public @NotNull Component errorNotMember() {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("error.not-member-you")));
}

public @NotNull Component errorAltAlreadyMember(final @NotNull Member player) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("error.alt-already-member")), Placeholder.unparsed("player", Optional
.ofNullable(player.player().getName()).orElse(player.player().getUniqueId().toString())));
}

public @NotNull Component errorDisallowedCharacters(final @NotNull HashSet<@NotNull Character> chars) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("error.disallowed-characters")), Placeholder.unparsed("chars", chars.stream().map(String::valueOf).collect(Collectors.joining())));
}

public @NotNull Component errorFailedDeleteMember(final @NotNull Member player) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("error.failed-delete-member")), Placeholder.unparsed("player", Optional
.ofNullable(player.player().getName()).orElse(player.player().getUniqueId().toString())));
}

public @NotNull Component errorAlreadyYourAlt(final @NotNull Member alt) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("error.already-your-alt")), Placeholder.unparsed("alt", Optional
.ofNullable(alt.player().getName()).orElse(alt.player().getUniqueId().toString())));
}

public @NotNull Component errorMaxAltsReached(final int max) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("error.max-alts-reached")), Placeholder.unparsed("max", String.valueOf(max)));
}

public @NotNull Component errorMemberNotAlt(final @NotNull Member player) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("error.member-not-alt")), Placeholder.unparsed("player", Optional
.ofNullable(player.player().getName()).orElse(player.player().getUniqueId().toString())));
}

public @NotNull Component errorRemoveJoinedAlt(final @NotNull Member player) {
return MiniMessage.miniMessage()
.deserialize(Objects.requireNonNull(config.getString("error.remove-joined-alt")), Placeholder.unparsed("player", Optional
.ofNullable(player.player().getName()).orElse(player.player().getUniqueId().toString())));
}

public record SubCommandArgument(@NotNull String name, boolean required) {
public @NotNull Component component() {
return required ? SMPCore.messages().subCommandArgumentRequired(name) : SMPCore.messages()
.subCommandArgumentOptional(name);
}

public static @NotNull Component join(final @NotNull SubCommandArgument @NotNull [] args) {
final @NotNull TextComponent.Builder builder = Component.text().append(Component.text(" "));

for (int i = 0; i < args.length; ++i) {
builder.append(args[i].component());
if (i < args.length - 1) builder.append(Component.text(" "));
}
return builder.build();
}

public static @NotNull SubCommandArgument @NotNull [] of(final @Nullable SubCommandArgument @NotNull ... args) {
return Arrays.stream(args).filter(Objects::nonNull).toArray(SubCommandArgument[]::new);
}
}
}
39 changes: 39 additions & 0 deletions src/main/java/pro/cloudnode/smp/smpcore/Permission.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,43 @@
public final class Permission {
public static @NotNull String RELOAD = "smpcore.reload";
public static @NotNull String BAN = "smpcore.ban";
/**
* See your own alts
*/
public static @NotNull String ALT = "smpcore.alt";

/**
* See someone else's alts
*/
public static @NotNull String ALT_OTHER = "smpcore.alt.other";

/**
* Add an alt
*/
public static @NotNull String ALT_ADD = "smpcore.alt.add";

/**
* Add an alt for someone else
*/
public static @NotNull String ALT_ADD_OTHER = "smpcore.alt.add.other";

/**
* Bypass the maximum alts limit
*/
public static @NotNull String ALT_MAX_BYPASS = "smpcore.alt.bypass.max";

/**
* Remove an alt
*/
public static @NotNull String ALT_REMOVE = "smpcore.alt.remove";

/**
* Remove someone else's alt
*/
public static @NotNull String ALT_REMOVE_OTHER = "smpcore.alt.remove.other";

/**
* Remove an alt that has joined the server
*/
public static @NotNull String ALT_REMOVE_JOINED = "smpcore.alt.remove.joined";
}
37 changes: 34 additions & 3 deletions src/main/java/pro/cloudnode/smp/smpcore/SMPCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import pro.cloudnode.smp.smpcore.command.AltsCommand;
import pro.cloudnode.smp.smpcore.command.BanCommand;
import pro.cloudnode.smp.smpcore.command.Command;
import pro.cloudnode.smp.smpcore.command.MainCommand;
import pro.cloudnode.smp.smpcore.command.UnbanCommand;
import pro.cloudnode.smp.smpcore.listener.NationTeamUpdaterListener;
Expand All @@ -15,8 +17,14 @@
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class SMPCore extends JavaPlugin {
public static @NotNull SMPCore getInstance() {
Expand Down Expand Up @@ -56,9 +64,14 @@ public void onEnable() {

getServer().getPluginManager().registerEvents(new NationTeamUpdaterListener(), this);

Objects.requireNonNull(getServer().getPluginCommand("smpcore")).setExecutor(new MainCommand());
Objects.requireNonNull(getServer().getPluginCommand("ban")).setExecutor(new BanCommand());
Objects.requireNonNull(getServer().getPluginCommand("unban")).setExecutor(new UnbanCommand());
final @NotNull HashMap<@NotNull String, @NotNull Command> commands = new HashMap<>() {{
put("smpcore", new MainCommand());
put("ban", new BanCommand());
put("unban", new UnbanCommand());
}};
commands.put("alts", new AltsCommand(commands.get("smpcore")));
for (final @NotNull Map.Entry<@NotNull String, @NotNull Command> entry : commands.entrySet())
Objects.requireNonNull(getServer().getPluginCommand(entry.getKey())).setExecutor(entry.getValue());
}

@Override
Expand Down Expand Up @@ -131,4 +144,22 @@ public static void runAsync(final @NotNull Runnable runnable) {
public static void runMain(final @NotNull Runnable runnable) {
getInstance().getServer().getScheduler().runTask(getInstance(), runnable);
}

public static @NotNull HashSet<@NotNull Character> getDisallowedCharacters(final @NotNull String source, final @NotNull Pattern pattern) {
final @NotNull Matcher matcher = pattern.matcher(source);
final @NotNull HashSet<@NotNull Character> chars = new HashSet<>();
while (matcher.find())
for (char c : matcher.group().toCharArray())
chars.add(c);
return chars;
}

public static boolean ifDisallowedCharacters(final @NotNull String source, final @NotNull Pattern pattern, final @NotNull Consumer<@NotNull HashSet<@NotNull Character>> consumer) {
final @NotNull HashSet<@NotNull Character> chars = getDisallowedCharacters(source, pattern);
if (!chars.isEmpty()) {
consumer.accept(chars);
return true;
}
return false;
}
}
Loading
Loading