Skip to content

Commit a72c065

Browse files
committed
Add guild presets
1 parent 1202308 commit a72c065

12 files changed

Lines changed: 182 additions & 8 deletions

File tree

api/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
dependencies {
2-
api("xyz.acrylicstyle.java-util:serialization:1.2.0-SNAPSHOT")
2+
api("xyz.acrylicstyle.java-util:serialization:2.1.1")
33
api("io.netty:netty-buffer:4.1.114.Final")
44
implementation("redis.clients:jedis:5.2.0")
55
implementation("org.yaml:snakeyaml:2.3")

api/src/main/resources/messages_en.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ command.guild.delete.success: "Deleted the guild."
5151
command.guild.delete.error: "Failed to delete the guild."
5252
command.guild.select.success: "Selected the guild."
5353
command.guild.select.error: "Failed to select the guild."
54+
command.guild.preset.not_set: "You must set guild preset %s using the /%s %s <guild name> command."
55+
command.guild.preset.set.success: "Set guild preset %s to %s."
56+
command.guild.preset.set.error: "Failed to set the guild preset."
5457
command.guild.role.not_owner: "You do not have permission to change the role of this member."
5558
command.guild.role.success: "Changed the role of %s to %s."
5659
command.guild.role.error: "Failed to change the role of this member."

api/src/main/resources/messages_ja.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ command.guild.delete.success: "ギルドを削除しました。"
5151
command.guild.delete.error: "ギルドの削除に失敗しました。"
5252
command.guild.select.success: "ギルドを選択しました。"
5353
command.guild.select.error: "ギルドの選択に失敗しました。"
54+
command.guild.preset.not_set: "ギルドプリセット%sを設定するには/%s %s <ギルド名>を実行してください。"
55+
command.guild.preset.set.success: "ギルドプリセット%sを%sに設定しました。"
56+
command.guild.preset.set.error: "ギルドプリセットの設定に失敗しました。"
5457
command.guild.role.not_owner: "このメンバーのロールを変更する権限がありません。"
5558
command.guild.role.success: "%sのロールを%sに変更しました。"
5659
command.guild.role.error: "メンバーのロールの変更に失敗しました。"

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ allprojects {
1616
}
1717

1818
group = "net.azisaba.interchat"
19-
version = "2.10.1"
19+
version = "2.11.0"
2020

2121
java {
2222
toolchain.languageVersion.set(JavaLanguageVersion.of(8))

velocity/src/main/java/net/azisaba/interchat/velocity/VelocityPlugin.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ public void onProxyInitialization(ProxyInitializeEvent e) {
108108
registerCommand(new GuildCommand(this).createCommand());
109109
registerCommand(new GuildAdminCommand().createCommand());
110110
registerCommand(new GShortCommand(this).createCommand());
111+
for (int i = 1; i <= 9; i++) {
112+
registerCommand(new GPresetCommand(this, i).createCommand());
113+
}
111114
registerCommand(new GSShortCommand(this).createCommand());
112115
registerCommand(new GuildTestCommand().createCommand());
113116
registerCommand(new GTellCommand().createCommand());

velocity/src/main/java/net/azisaba/interchat/velocity/command/ChatSuggestionGuildProvider.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
import com.velocitypowered.api.command.CommandSource;
66
import net.azisaba.interchat.api.guild.Guild;
77
import org.jetbrains.annotations.NotNull;
8+
import org.jetbrains.annotations.Nullable;
89

910
import java.util.UUID;
1011

1112
public interface ChatSuggestionGuildProvider {
12-
@NotNull Guild apply(@NotNull CommandContext<CommandSource> ctx, @NotNull UUID uuid) throws CommandSyntaxException;
13+
@Nullable Guild apply(@NotNull CommandContext<CommandSource> ctx, @NotNull UUID uuid) throws CommandSyntaxException;
1314
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package net.azisaba.interchat.velocity.command;
2+
3+
import com.mojang.brigadier.arguments.StringArgumentType;
4+
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
5+
import com.velocitypowered.api.command.CommandSource;
6+
import com.velocitypowered.api.proxy.Player;
7+
import net.azisaba.interchat.api.guild.Guild;
8+
import net.azisaba.interchat.velocity.VelocityPlugin;
9+
import net.azisaba.interchat.velocity.text.VMessages;
10+
import net.kyori.adventure.text.format.NamedTextColor;
11+
import org.jetbrains.annotations.NotNull;
12+
import org.jetbrains.annotations.Nullable;
13+
14+
public final class GPresetCommand extends AbstractCommand {
15+
private final VelocityPlugin plugin;
16+
private final int presetNumber;
17+
18+
public GPresetCommand(@NotNull VelocityPlugin plugin, int presetNumber) {
19+
this.plugin = plugin;
20+
this.presetNumber = presetNumber;
21+
}
22+
23+
@Override
24+
protected @NotNull LiteralArgumentBuilder<CommandSource> createBuilder() {
25+
String commandName = "g" + presetNumber;
26+
return literal(commandName)
27+
.requires(source -> source instanceof Player && source.hasPermission("interchat.guild") && source.hasPermission("interchat.guild.chat"))
28+
.executes(ctx -> executeFocus((Player) ctx.getSource(), GuildCommand.getPresetGuildForSuggestion(((Player) ctx.getSource()).getUniqueId(), presetNumber)))
29+
.then(argument("message", StringArgumentType.greedyString())
30+
.suggests(GuildCommand.getChatSuggestionProvider(plugin.getJedisBox(), (ctx, uuid) ->
31+
GuildCommand.getPresetGuildForSuggestion(uuid, presetNumber)))
32+
.executes(ctx -> GuildCommand.executePresetChat(
33+
(Player) ctx.getSource(),
34+
presetNumber,
35+
StringArgumentType.getString(ctx, "message")
36+
))
37+
);
38+
}
39+
40+
private int executeFocus(@NotNull Player player, @Nullable Guild guild) {
41+
if (guild == null) {
42+
player.sendMessage(VMessages.formatComponent(player, "command.guild.preset.not_set", presetNumber, GuildCommand.COMMAND_NAME, presetNumber).color(NamedTextColor.RED));
43+
return 0;
44+
}
45+
player.sendMessage(VMessages.formatComponent(player, "command.guild.focus.focused", guild.name()).color(NamedTextColor.GREEN));
46+
return 1;
47+
}
48+
}

velocity/src/main/java/net/azisaba/interchat/velocity/command/GuildCommand.java

Lines changed: 111 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,16 @@ public class GuildCommand extends AbstractCommand {
6767
"create", "format", "chat", "delete", "select", "role", "invite", "kick", "ban", "ban-public", "unban", "pardon",
6868
"leave", "dontinviteme", "toggleinvites", "accept", "reject", "info", "log", "jp-on", "jp-off",
6969
"linkdiscord", "unlinkdiscord", "nick", "force-nick", "open", "join", "hideall", "hide-guild", "hide-player",
70+
"1", "2", "3", "4", "5", "6", "7", "8", "9",
7071
// reserved names
7172
"permission", "permissions"
7273
);
7374
private static final String DEFAULT_FORMAT = "&b[&a%gname&7@&6%server&b] &r%username&a: &r%msg &7%prereplace-b";
7475
private static final Pattern GUILD_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_\\-.+]{2,32}$");
7576
public static final String COMMAND_NAME = "guild";
7677
private static final Map<UUID, Long> LAST_GUILD_INVITE = new ConcurrentHashMap<>();
78+
private static final long GUILD_PRESET_CACHE_TTL = TimeUnit.MINUTES.toMillis(10);
79+
private static final Map<UUID, Map<Integer, Map.Entry<Long, Long>>> GUILD_PRESET_CACHE = new ConcurrentHashMap<>();
7780
private static final Function<UUID, User> ACTUAL_USER = Functions.memoize(1000 * 10, uuid ->
7881
InterChatProvider.get().getUserManager().fetchUser(uuid).join()
7982
);
@@ -96,6 +99,9 @@ public class GuildCommand extends AbstractCommand {
9699
// preview and suggest
97100
User user = ACTUAL_USER.apply(((Player) context.getSource()).getUniqueId());
98101
Guild guild = chatSuggestionGuildProvider.apply(context, user.id());
102+
if (guild == null) {
103+
return builder.buildFuture();
104+
}
99105
GuildMember member = GUILD_MEMBER.apply(new AbstractMap.SimpleImmutableEntry<>(guild.id(), user.id()));
100106
String server = ((Player) context.getSource())
101107
.getCurrentServer()
@@ -142,6 +148,61 @@ public class GuildCommand extends AbstractCommand {
142148
};
143149
}
144150

151+
static @Nullable Guild getPresetGuildForSuggestion(@NotNull UUID uuid, int presetNumber) {
152+
long guildId = getPresetGuildId(uuid, presetNumber);
153+
if (guildId == -1) {
154+
return null;
155+
}
156+
try {
157+
return InterChatProvider.get().getGuildManager().fetchGuildById(guildId).join();
158+
} catch (CompletionException e) {
159+
return null;
160+
}
161+
}
162+
163+
public static void clearPresetCache(@NotNull UUID uuid) {
164+
GUILD_PRESET_CACHE.remove(uuid);
165+
}
166+
167+
public static void removePresetCacheWithGuildId(long guildId) {
168+
GUILD_PRESET_CACHE.forEach((uuid, presets) -> {
169+
presets.entrySet().removeIf(entry -> entry.getValue().getValue() == guildId);
170+
if (presets.isEmpty()) {
171+
GUILD_PRESET_CACHE.remove(uuid);
172+
}
173+
});
174+
}
175+
176+
private static void setPresetCache(@NotNull UUID uuid, int presetNumber, long guildId) {
177+
Map<Integer, Map.Entry<Long, Long>> presets = GUILD_PRESET_CACHE.computeIfAbsent(uuid, k -> new ConcurrentHashMap<>());
178+
presets.put(presetNumber, new AbstractMap.SimpleImmutableEntry<>(System.currentTimeMillis() + GUILD_PRESET_CACHE_TTL, guildId));
179+
}
180+
181+
private static long getPresetGuildId(@NotNull UUID uuid, int presetNumber) {
182+
Map<Integer, Map.Entry<Long, Long>> presets = GUILD_PRESET_CACHE.computeIfAbsent(uuid, k -> new ConcurrentHashMap<>());
183+
Map.Entry<Long, Long> entry = presets.get(presetNumber);
184+
if (entry != null && entry.getKey() > System.currentTimeMillis()) {
185+
return entry.getValue();
186+
}
187+
long guildId = -1L;
188+
try {
189+
guildId = DatabaseManager.get().getPrepareStatement("SELECT `guild_id` FROM `guild_presets` WHERE `uuid` = ? AND `number` = ?", stmt -> {
190+
stmt.setString(1, uuid.toString());
191+
stmt.setInt(2, presetNumber);
192+
try (ResultSet rs = stmt.executeQuery()) {
193+
if (rs.next()) {
194+
return rs.getLong("guild_id");
195+
}
196+
}
197+
return -1L;
198+
});
199+
} catch (SQLException e) {
200+
Logger.getCurrentLogger().error("Failed to load guild preset {} for {}", presetNumber, uuid, e);
201+
}
202+
presets.put(presetNumber, new AbstractMap.SimpleImmutableEntry<>(System.currentTimeMillis() + GUILD_PRESET_CACHE_TTL, guildId));
203+
return guildId;
204+
}
205+
145206
private final VelocityPlugin plugin;
146207

147208
public GuildCommand(@NotNull VelocityPlugin plugin) {
@@ -150,7 +211,7 @@ public GuildCommand(@NotNull VelocityPlugin plugin) {
150211

151212
@Override
152213
public @NotNull LiteralArgumentBuilder<CommandSource> createBuilder() {
153-
return literal(COMMAND_NAME)
214+
LiteralArgumentBuilder<CommandSource> builder = literal(COMMAND_NAME)
154215
.requires(source -> source instanceof Player && source.hasPermission("interchat.guild"))
155216
// everyone
156217
.then(literal("create")
@@ -163,10 +224,10 @@ public GuildCommand(@NotNull VelocityPlugin plugin) {
163224
.then(literal("format")
164225
.requires(source -> source.hasPermission("interchat.guild.format"))
165226
.then(argument("format", StringArgumentType.greedyString())
166-
.suggests((context, builder) -> {
227+
.suggests((context, b) -> {
167228
// suggest variables
168229
String last = context.getLastChild().getInput().substring(context.getLastChild().getInput().lastIndexOf(' ') + 1);
169-
FORMAT_VARIABLES.stream().filter(s -> s.startsWith(last)).forEach(builder::suggest);
230+
FORMAT_VARIABLES.stream().filter(s -> s.startsWith(last)).forEach(b::suggest);
170231

171232
// format preview
172233
UUID uuid = ((Player) context.getSource()).getUniqueId();
@@ -186,7 +247,7 @@ public GuildCommand(@NotNull VelocityPlugin plugin) {
186247
"テスト",
187248
VelocityPlugin.getPlugin().getServerAlias()
188249
);
189-
return builder.suggest(formatted.replace('&', '§')).buildFuture();
250+
return b.suggest(formatted.replace('&', '§')).buildFuture();
190251
})
191252
.executes(ctx -> executeFormat((Player) ctx.getSource(), StringArgumentType.getString(ctx, "format")))
192253
)
@@ -219,7 +280,7 @@ public GuildCommand(@NotNull VelocityPlugin plugin) {
219280
.then(argument("member", GuildMemberArgumentType.guildMember())
220281
.suggests(suggestMembersOfGuild(GuildRole.OWNER))
221282
.then(argument("role", GuildRoleArgumentType.guildRole())
222-
.suggests((ctx, builder) -> suggest(Arrays.stream(GuildRole.values()).map(Enum::name).map(String::toLowerCase), builder))
283+
.suggests((ctx, b) -> suggest(Arrays.stream(GuildRole.values()).map(Enum::name).map(String::toLowerCase), b))
223284
.executes(ctx ->
224285
executeRole(
225286
(Player) ctx.getSource(),
@@ -446,6 +507,19 @@ public GuildCommand(@NotNull VelocityPlugin plugin) {
446507
)
447508
)
448509
);
510+
for (int i = 1; i <= 9; i++) {
511+
builder.then(buildPresetSetLiteral(i));
512+
}
513+
return builder;
514+
}
515+
516+
private static @NotNull LiteralArgumentBuilder<CommandSource> buildPresetSetLiteral(int presetNumber) {
517+
return literal(String.valueOf(presetNumber))
518+
.requires(source -> source.hasPermission("interchat.guild.select"))
519+
.then(argument("guild", GuildArgumentType.guild())
520+
.suggests(suggestGuildsOfMember(false))
521+
.executes(ctx -> executePresetSet((Player) ctx.getSource(), presetNumber, GuildArgumentType.get(ctx, "guild", false)))
522+
);
449523
}
450524

451525
/**
@@ -648,6 +722,38 @@ public static int executeSelect(@NotNull Player player, @NotNull Guild guild) {
648722
return 0;
649723
}
650724

725+
private static int executePresetSet(@NotNull Player player, int presetNumber, @NotNull Guild guild) {
726+
try {
727+
guild.getMember(player.getUniqueId()).join();
728+
} catch (CompletionException e) {
729+
player.sendMessage(VMessages.formatComponent(player, "command.error.unknown_guild", guild.name()).color(NamedTextColor.RED));
730+
return 0;
731+
}
732+
try {
733+
DatabaseManager.get().query("INSERT INTO `guild_presets` (`uuid`, `number`, `guild_id`) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE `guild_id` = VALUES(`guild_id`)", stmt -> {
734+
stmt.setString(1, player.getUniqueId().toString());
735+
stmt.setInt(2, presetNumber);
736+
stmt.setLong(3, guild.id());
737+
stmt.executeUpdate();
738+
});
739+
setPresetCache(player.getUniqueId(), presetNumber, guild.id());
740+
player.sendMessage(VMessages.formatComponent(player, "command.guild.preset.set.success", presetNumber, guild.name()).color(NamedTextColor.GREEN));
741+
} catch (SQLException e) {
742+
Logger.getCurrentLogger().error("Failed to set guild preset {} for {}", presetNumber, player.getUniqueId(), e);
743+
player.sendMessage(VMessages.formatComponent(player, "command.guild.preset.set.error").color(NamedTextColor.RED));
744+
}
745+
return 0;
746+
}
747+
748+
static int executePresetChat(@NotNull Player player, int presetNumber, @NotNull String message) {
749+
long presetGuildId = getPresetGuildId(player.getUniqueId(), presetNumber);
750+
if (presetGuildId == -1) {
751+
player.sendMessage(VMessages.formatComponent(player, "command.guild.preset.not_set", presetNumber, COMMAND_NAME, presetNumber).color(NamedTextColor.RED));
752+
return 0;
753+
}
754+
return executeChat(player, message, presetGuildId);
755+
}
756+
651757
private static int executeRole(@NotNull Player player, @NotNull GuildMember member, @NotNull GuildRole role) {
652758
long selectedGuild = ensureSelected(player);
653759
if (selectedGuild == -1) return 0;

velocity/src/main/java/net/azisaba/interchat/velocity/database/DatabaseManager.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ private void createTables() throws SQLException {
8686
" `blocked_uuid` VARCHAR(36) NOT NULL," +
8787
" PRIMARY KEY (`id`, `blocked_uuid`)" +
8888
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci");
89+
statement.executeUpdate("CREATE TABLE IF NOT EXISTS `guild_presets` (" +
90+
" `uuid` VARCHAR(36) NOT NULL," +
91+
" `number` INT NOT NULL," +
92+
" `guild_id` BIGINT NOT NULL," +
93+
" PRIMARY KEY (`uuid`, `number`)" +
94+
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci");
8995
});
9096
}
9197

velocity/src/main/java/net/azisaba/interchat/velocity/guild/VelocityGuildManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import net.azisaba.interchat.api.network.protocol.GuildSoftDeletePacket;
1010
import net.azisaba.interchat.velocity.VelocityPlugin;
1111
import net.azisaba.interchat.velocity.database.DatabaseManager;
12+
import net.azisaba.interchat.velocity.command.GuildCommand;
1213
import net.azisaba.interchat.velocity.listener.ChatListener;
1314
import org.jetbrains.annotations.NotNull;
1415

@@ -36,6 +37,7 @@ public static void markDeleted(@NotNull CommandSource source, long guildId) {
3637
stmt.executeUpdate();
3738
});
3839
ChatListener.removeCacheWithGuildId(guildId);
40+
GuildCommand.removePresetCacheWithGuildId(guildId);
3941
// notify others
4042
UUID uuid;
4143
if (source instanceof Player) {

0 commit comments

Comments
 (0)