Skip to content

Commit 4ba8d32

Browse files
committed
add home command
1 parent a43db85 commit 4ba8d32

File tree

8 files changed

+380
-0
lines changed

8 files changed

+380
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.uravgcode.modernessentials.argument;
2+
3+
import com.mojang.brigadier.StringReader;
4+
import com.mojang.brigadier.arguments.ArgumentType;
5+
import com.mojang.brigadier.arguments.StringArgumentType;
6+
import com.mojang.brigadier.context.CommandContext;
7+
import com.mojang.brigadier.exceptions.CommandSyntaxException;
8+
import com.mojang.brigadier.suggestion.Suggestions;
9+
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
10+
import com.uravgcode.modernessentials.module.HomeModule;
11+
import io.papermc.paper.command.brigadier.CommandSourceStack;
12+
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
13+
import org.bukkit.entity.Player;
14+
import org.jspecify.annotations.NullMarked;
15+
16+
import java.util.concurrent.CompletableFuture;
17+
18+
@NullMarked
19+
public class HomeArgument implements CustomArgumentType<String, String> {
20+
21+
@Override
22+
public String parse(StringReader reader) {
23+
throw new UnsupportedOperationException();
24+
}
25+
26+
@Override
27+
public <S> String parse(StringReader reader, S source) throws CommandSyntaxException {
28+
return reader.readString();
29+
}
30+
31+
@Override
32+
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
33+
if (!(context.getSource() instanceof CommandSourceStack stack)) return Suggestions.empty();
34+
if (!(stack.getExecutor() instanceof Player player)) return Suggestions.empty();
35+
36+
final var remaining = builder.getRemainingLowerCase();
37+
for (final var home : HomeModule.getHomes(player.getUniqueId()).tailMap(remaining).keySet()) {
38+
if (!home.toLowerCase().startsWith(remaining)) break;
39+
builder.suggest(home);
40+
}
41+
return builder.buildFuture();
42+
}
43+
44+
@Override
45+
public ArgumentType<String> getNativeType() {
46+
return StringArgumentType.word();
47+
}
48+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.uravgcode.modernessentials.command.home;
2+
3+
import com.mojang.brigadier.Command;
4+
import com.mojang.brigadier.context.CommandContext;
5+
import com.mojang.brigadier.tree.LiteralCommandNode;
6+
import com.uravgcode.modernessentials.argument.HomeArgument;
7+
import com.uravgcode.modernessentials.command.CommandBuilder;
8+
import com.uravgcode.modernessentials.event.home.DelHomeEvent;
9+
import com.uravgcode.modernessentials.exception.RequiresPlayerException;
10+
import io.papermc.paper.command.brigadier.CommandSourceStack;
11+
import io.papermc.paper.command.brigadier.Commands;
12+
import org.jspecify.annotations.NullMarked;
13+
14+
@NullMarked
15+
@SuppressWarnings("unused")
16+
public final class DelHomeCommand implements CommandBuilder {
17+
18+
@Override
19+
public LiteralCommandNode<CommandSourceStack> build() {
20+
return Commands.literal("delhome")
21+
.requires(permission("essentials.delhome"))
22+
.then(Commands.argument("name", new HomeArgument())
23+
.executes(this::execute))
24+
.build();
25+
}
26+
27+
private int execute(CommandContext<CommandSourceStack> context) throws RequiresPlayerException {
28+
final var player = player(context);
29+
final var name = context.getArgument("name", String.class);
30+
player.getServer().getPluginManager().callEvent(new DelHomeEvent(player, name));
31+
return Command.SINGLE_SUCCESS;
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.uravgcode.modernessentials.command.home;
2+
3+
import com.mojang.brigadier.Command;
4+
import com.mojang.brigadier.context.CommandContext;
5+
import com.mojang.brigadier.tree.LiteralCommandNode;
6+
import com.uravgcode.modernessentials.argument.HomeArgument;
7+
import com.uravgcode.modernessentials.command.CommandBuilder;
8+
import com.uravgcode.modernessentials.event.home.HomeEvent;
9+
import com.uravgcode.modernessentials.exception.RequiresPlayerException;
10+
import io.papermc.paper.command.brigadier.CommandSourceStack;
11+
import io.papermc.paper.command.brigadier.Commands;
12+
import org.jspecify.annotations.NullMarked;
13+
14+
@NullMarked
15+
@SuppressWarnings("unused")
16+
public final class HomeCommand implements CommandBuilder {
17+
18+
@Override
19+
public LiteralCommandNode<CommandSourceStack> build() {
20+
return Commands.literal("home")
21+
.requires(permission("essentials.home"))
22+
.then(Commands.argument("name", new HomeArgument())
23+
.executes(this::execute))
24+
.build();
25+
}
26+
27+
private int execute(CommandContext<CommandSourceStack> context) throws RequiresPlayerException {
28+
final var player = player(context);
29+
final var name = context.getArgument("name", String.class);
30+
player.getServer().getPluginManager().callEvent(new HomeEvent(player, name));
31+
return Command.SINGLE_SUCCESS;
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.uravgcode.modernessentials.command.home;
2+
3+
import com.mojang.brigadier.Command;
4+
import com.mojang.brigadier.context.CommandContext;
5+
import com.mojang.brigadier.tree.LiteralCommandNode;
6+
import com.uravgcode.modernessentials.argument.HomeArgument;
7+
import com.uravgcode.modernessentials.command.CommandBuilder;
8+
import com.uravgcode.modernessentials.event.home.SetHomeEvent;
9+
import com.uravgcode.modernessentials.exception.RequiresPlayerException;
10+
import io.papermc.paper.command.brigadier.CommandSourceStack;
11+
import io.papermc.paper.command.brigadier.Commands;
12+
import org.jspecify.annotations.NullMarked;
13+
14+
@NullMarked
15+
@SuppressWarnings("unused")
16+
public final class SetHomeCommand implements CommandBuilder {
17+
18+
@Override
19+
public LiteralCommandNode<CommandSourceStack> build() {
20+
return Commands.literal("sethome")
21+
.requires(permission("essentials.sethome"))
22+
.then(Commands.argument("name", new HomeArgument())
23+
.executes(this::execute))
24+
.build();
25+
}
26+
27+
private int execute(CommandContext<CommandSourceStack> context) throws RequiresPlayerException {
28+
final var player = player(context);
29+
final var name = context.getArgument("name", String.class);
30+
player.getServer().getPluginManager().callEvent(new SetHomeEvent(player, name));
31+
return Command.SINGLE_SUCCESS;
32+
}
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.uravgcode.modernessentials.event.home;
2+
3+
import org.bukkit.entity.Player;
4+
import org.bukkit.event.HandlerList;
5+
import org.bukkit.event.player.PlayerEvent;
6+
import org.jspecify.annotations.NullMarked;
7+
8+
@NullMarked
9+
public final class DelHomeEvent extends PlayerEvent {
10+
private static final HandlerList handlers = new HandlerList();
11+
private final String name;
12+
13+
public DelHomeEvent(Player player, String name) {
14+
super(player);
15+
this.name = name;
16+
}
17+
18+
public String getName() {
19+
return name;
20+
}
21+
22+
@Override
23+
public HandlerList getHandlers() {
24+
return handlers;
25+
}
26+
27+
@SuppressWarnings("unused")
28+
public static HandlerList getHandlerList() {
29+
return handlers;
30+
}
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.uravgcode.modernessentials.event.home;
2+
3+
import org.bukkit.entity.Player;
4+
import org.bukkit.event.HandlerList;
5+
import org.bukkit.event.player.PlayerEvent;
6+
import org.jspecify.annotations.NullMarked;
7+
8+
@NullMarked
9+
public final class HomeEvent extends PlayerEvent {
10+
private static final HandlerList handlers = new HandlerList();
11+
private final String name;
12+
13+
public HomeEvent(Player player, String name) {
14+
super(player);
15+
this.name = name;
16+
}
17+
18+
public String getName() {
19+
return name;
20+
}
21+
22+
@Override
23+
public HandlerList getHandlers() {
24+
return handlers;
25+
}
26+
27+
@SuppressWarnings("unused")
28+
public static HandlerList getHandlerList() {
29+
return handlers;
30+
}
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.uravgcode.modernessentials.event.home;
2+
3+
import org.bukkit.entity.Player;
4+
import org.bukkit.event.HandlerList;
5+
import org.bukkit.event.player.PlayerEvent;
6+
import org.jspecify.annotations.NullMarked;
7+
8+
@NullMarked
9+
public final class SetHomeEvent extends PlayerEvent {
10+
private static final HandlerList handlers = new HandlerList();
11+
private final String name;
12+
13+
public SetHomeEvent( Player player, String name) {
14+
super(player);
15+
this.name = name;
16+
}
17+
18+
public String getName() {
19+
return name;
20+
}
21+
22+
@Override
23+
public HandlerList getHandlers() {
24+
return handlers;
25+
}
26+
27+
@SuppressWarnings("unused")
28+
public static HandlerList getHandlerList() {
29+
return handlers;
30+
}
31+
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package com.uravgcode.modernessentials.module;
2+
3+
import com.uravgcode.modernessentials.annotation.CommandModule;
4+
import com.uravgcode.modernessentials.event.home.DelHomeEvent;
5+
import com.uravgcode.modernessentials.event.home.HomeEvent;
6+
import com.uravgcode.modernessentials.event.home.SetHomeEvent;
7+
import net.kyori.adventure.text.Component;
8+
import net.kyori.adventure.text.format.NamedTextColor;
9+
import org.bukkit.Location;
10+
import org.bukkit.configuration.file.YamlConfiguration;
11+
import org.bukkit.event.EventHandler;
12+
import org.bukkit.event.EventPriority;
13+
import org.bukkit.plugin.java.JavaPlugin;
14+
import org.jspecify.annotations.NullMarked;
15+
16+
import java.io.File;
17+
import java.io.IOException;
18+
import java.util.Collections;
19+
import java.util.Map;
20+
import java.util.NavigableMap;
21+
import java.util.UUID;
22+
import java.util.concurrent.CompletableFuture;
23+
import java.util.concurrent.ConcurrentHashMap;
24+
import java.util.concurrent.ConcurrentSkipListMap;
25+
26+
@NullMarked
27+
@CommandModule(name = "home")
28+
public final class HomeModule extends PluginModule {
29+
private static final Map<UUID, NavigableMap<String, Location>> homes = new ConcurrentHashMap<>();
30+
private final File file;
31+
32+
public HomeModule(JavaPlugin plugin) {
33+
super(plugin);
34+
this.file = plugin.getDataPath().resolve("homes.yml").toFile();
35+
}
36+
37+
public static NavigableMap<String, Location> getHomes(UUID uuid) {
38+
final var playerHomes = homes.get(uuid);
39+
return playerHomes == null
40+
? Collections.emptyNavigableMap()
41+
: Collections.unmodifiableNavigableMap(playerHomes);
42+
}
43+
44+
@Override
45+
public void reload() {
46+
super.reload();
47+
48+
homes.clear();
49+
final var logger = plugin.getComponentLogger();
50+
final var config = YamlConfiguration.loadConfiguration(file);
51+
52+
for (final var uuidString : config.getKeys(false)) {
53+
try {
54+
final var uuid = UUID.fromString(uuidString);
55+
56+
final var section = config.getConfigurationSection(uuidString);
57+
if (section == null) continue;
58+
59+
final var playerHomes = new ConcurrentSkipListMap<String, Location>(String.CASE_INSENSITIVE_ORDER);
60+
for (final var name : section.getKeys(false)) {
61+
final var location = section.getLocation(name);
62+
if (location != null) {
63+
playerHomes.put(name, location);
64+
} else {
65+
logger.warn("Home '{}' for player {} does not contain a valid location", name, uuidString);
66+
}
67+
}
68+
69+
homes.put(uuid, playerHomes);
70+
} catch (IllegalArgumentException exception) {
71+
logger.warn("Invalid UUID in homes.yml: {}", uuidString);
72+
}
73+
}
74+
}
75+
76+
@EventHandler(priority = EventPriority.MONITOR)
77+
public void onHome(HomeEvent event) {
78+
final var player = event.getPlayer();
79+
final var name = event.getName();
80+
final var playerHomes = homes.get(player.getUniqueId());
81+
82+
if (playerHomes == null) {
83+
player.sendMessage(Component.text("Home not found", NamedTextColor.RED));
84+
return;
85+
}
86+
87+
final var location = playerHomes.get(name);
88+
if (location != null) {
89+
player.teleportAsync(location);
90+
} else {
91+
player.sendMessage(Component.text("Home not found", NamedTextColor.RED));
92+
}
93+
}
94+
95+
@EventHandler(priority = EventPriority.MONITOR)
96+
public void onSetHome(SetHomeEvent event) {
97+
final var player = event.getPlayer();
98+
final var name = event.getName();
99+
final var location = player.getLocation();
100+
101+
final var playerHomes = homes.computeIfAbsent(player.getUniqueId(), uuid -> new ConcurrentSkipListMap<>(String.CASE_INSENSITIVE_ORDER));
102+
if (playerHomes.put(name, location) == null) {
103+
player.sendMessage("Home created");
104+
} else {
105+
player.sendMessage("Home set");
106+
}
107+
108+
saveHomes();
109+
}
110+
111+
@EventHandler(priority = EventPriority.MONITOR)
112+
public void onDelHome(DelHomeEvent event) {
113+
final var player = event.getPlayer();
114+
final var name = event.getName();
115+
116+
final var playerHomes = homes.get(player.getUniqueId());
117+
if (playerHomes != null && playerHomes.remove(name) != null) {
118+
player.sendMessage("Home deleted");
119+
saveHomes();
120+
} else {
121+
player.sendMessage(Component.text("Home not found", NamedTextColor.RED));
122+
}
123+
}
124+
125+
private void saveHomes() {
126+
final var snapshot = Map.copyOf(homes);
127+
CompletableFuture.runAsync(() -> {
128+
try {
129+
final var config = new YamlConfiguration();
130+
snapshot.forEach((uuid, playerHomes) -> {
131+
final var playerSection = config.createSection(uuid.toString());
132+
playerHomes.forEach(playerSection::set);
133+
});
134+
config.save(file);
135+
} catch (IOException exception) {
136+
plugin.getComponentLogger().error("Could not save homes.yml", exception);
137+
}
138+
});
139+
}
140+
}

0 commit comments

Comments
 (0)