Skip to content

Commit fdd9257

Browse files
authored
Merge pull request #3359 from Multiverse/fix/async-online-players
Implement ConcurrentPlayerWorldTracker to allow for async online player names and worlds lookup
2 parents 55d1347 + 29cc974 commit fdd9257

File tree

6 files changed

+118
-33
lines changed

6 files changed

+118
-33
lines changed

src/main/java/org/mvplugins/multiverse/core/command/MVCommandCompletions.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public class MVCommandCompletions extends PaperCommandCompletions {
100100
registerAsyncCompletion("mvworlds", this::suggestMVWorlds);
101101
registerAsyncCompletion("mvworldpropsname", this::suggestMVWorldPropsName);
102102
registerAsyncCompletion("mvworldpropsvalue", this::suggestMVWorldPropsValue);
103-
registerAsyncCompletion("playersarray", this::suggestPlayersArray);
103+
registerCompletion("playersarray", this::suggestPlayersArray); // getting online players cannot be async
104104
registerStaticCompletion("propsmodifyaction", suggestEnums(PropertyModifyAction.class));
105105
registerStaticCompletion("spawncategories", suggestEnums(SpawnCategory.class));
106106
registerAsyncCompletion("spawncategorypropsname", this::suggestSpawnCategoryPropsName);

src/main/java/org/mvplugins/multiverse/core/destination/core/BedDestination.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import co.aikar.locales.MessageKey;
88
import co.aikar.locales.MessageKeyProvider;
9-
import org.bukkit.Bukkit;
9+
import jakarta.inject.Inject;
1010
import org.bukkit.command.CommandSender;
1111
import org.bukkit.entity.Player;
1212
import org.jetbrains.annotations.NotNull;
@@ -20,8 +20,7 @@
2020
import org.mvplugins.multiverse.core.utils.PlayerFinder;
2121
import org.mvplugins.multiverse.core.utils.result.Attempt;
2222
import org.mvplugins.multiverse.core.utils.result.FailureReason;
23-
24-
import static org.mvplugins.multiverse.core.locale.message.MessageReplacement.replace;
23+
import org.mvplugins.multiverse.core.world.helpers.ConcurrentPlayerWorldTracker;
2524

2625
/**
2726
* {@link Destination} implementation for beds.
@@ -30,7 +29,11 @@
3029
public final class BedDestination implements Destination<BedDestination, BedDestinationInstance, BedDestination.InstanceFailureReason> {
3130
static final String OWN_BED_STRING = "playerbed";
3231

33-
BedDestination() {
32+
private final ConcurrentPlayerWorldTracker worldTracker;
33+
34+
@Inject
35+
BedDestination(@NotNull ConcurrentPlayerWorldTracker worldTracker) {
36+
this.worldTracker = worldTracker;
3437
}
3538

3639
/**
@@ -62,8 +65,8 @@ public final class BedDestination implements Destination<BedDestination, BedDest
6265
@Override
6366
public @NotNull Collection<DestinationSuggestionPacket> suggestDestinations(
6467
@NotNull CommandSender sender, @Nullable String destinationParams) {
65-
List<DestinationSuggestionPacket> collect = Bukkit.getOnlinePlayers().stream()
66-
.map(player -> new DestinationSuggestionPacket(this, player.getName(), player.getName()))
68+
List<DestinationSuggestionPacket> collect = worldTracker.getOnlinePlayers().stream()
69+
.map(player -> new DestinationSuggestionPacket(this, player, player))
6770
.collect(Collectors.toList());
6871
if (sender instanceof Player) {
6972
collect.add(new DestinationSuggestionPacket(this, OWN_BED_STRING, OWN_BED_STRING));

src/main/java/org/mvplugins/multiverse/core/destination/core/PlayerDestination.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import co.aikar.locales.MessageKey;
66
import co.aikar.locales.MessageKeyProvider;
7-
import org.bukkit.Bukkit;
7+
import jakarta.inject.Inject;
88
import org.bukkit.command.CommandSender;
99
import org.bukkit.entity.Player;
1010
import org.jetbrains.annotations.NotNull;
@@ -14,21 +14,26 @@
1414
import org.mvplugins.multiverse.core.destination.Destination;
1515
import org.mvplugins.multiverse.core.destination.DestinationSuggestionPacket;
1616
import org.mvplugins.multiverse.core.locale.MVCorei18n;
17-
import org.mvplugins.multiverse.core.locale.message.MessageReplacement;
1817
import org.mvplugins.multiverse.core.locale.message.MessageReplacement.Replace;
1918
import org.mvplugins.multiverse.core.utils.PlayerFinder;
2019
import org.mvplugins.multiverse.core.utils.result.Attempt;
2120
import org.mvplugins.multiverse.core.utils.result.FailureReason;
21+
import org.mvplugins.multiverse.core.world.helpers.ConcurrentPlayerWorldTracker;
2222

2323
/**
2424
* {@link Destination} implementation for players.s
2525
*/
2626
@Service
2727
public final class PlayerDestination implements Destination<PlayerDestination, PlayerDestinationInstance, PlayerDestination.InstanceFailureReason> {
28+
29+
private final ConcurrentPlayerWorldTracker playerWorldTracker;
30+
2831
/**
2932
* Creates a new instance of the PlayerDestination.
3033
*/
31-
PlayerDestination() {
34+
@Inject
35+
PlayerDestination(@NotNull ConcurrentPlayerWorldTracker playerWorldTracker) {
36+
this.playerWorldTracker = playerWorldTracker;
3237
}
3338

3439
/**
@@ -60,8 +65,8 @@ public final class PlayerDestination implements Destination<PlayerDestination, P
6065
@Override
6166
public @NotNull Collection<DestinationSuggestionPacket> suggestDestinations(
6267
@NotNull CommandSender sender, @Nullable String destinationParams) {
63-
return Bukkit.getOnlinePlayers().stream()
64-
.map(p -> new DestinationSuggestionPacket(this, p.getName(), p.getName()))
68+
return playerWorldTracker.getOnlinePlayers().stream()
69+
.map(player -> new DestinationSuggestionPacket(this, player, player))
6570
.toList();
6671
}
6772

src/main/java/org/mvplugins/multiverse/core/listeners/MVChatListener.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.mvplugins.multiverse.core.dynamiclistener.annotations.SkipIfEventExist;
1717
import org.mvplugins.multiverse.core.utils.text.ChatTextFormatter;
1818
import org.mvplugins.multiverse.core.world.WorldManager;
19+
import org.mvplugins.multiverse.core.world.helpers.ConcurrentPlayerWorldTracker;
1920

2021
/**
2122
* Multiverse's Listener for players.
@@ -24,16 +25,17 @@
2425
final class MVChatListener implements CoreListener {
2526
private final CoreConfig config;
2627
private final WorldManager worldManager;
27-
private final MVPlayerListener playerListener;
28+
private final ConcurrentPlayerWorldTracker playerWorldTracker;
2829

2930
@Inject
3031
MVChatListener(
3132
CoreConfig config,
3233
WorldManager worldManager,
33-
MVPlayerListener playerListener) {
34+
ConcurrentPlayerWorldTracker playerWorldTracker
35+
) {
3436
this.config = config;
3537
this.worldManager = worldManager;
36-
this.playerListener = playerListener;
38+
this.playerWorldTracker = playerWorldTracker;
3739
}
3840

3941
@EventClass("io.papermc.paper.event.player.AsyncChatEvent")
@@ -94,12 +96,9 @@ void asyncPlayerChat(AsyncPlayerChatEvent event) {
9496
}
9597

9698
private String getWorldName(Player player) {
97-
String world = playerListener.getPlayerWorld().get(player.getName());
98-
if (world == null) {
99-
world = player.getWorld().getName();
100-
playerListener.getPlayerWorld().put(player.getName(), world);
101-
}
102-
return this.worldManager.getLoadedWorld(world)
99+
String worldName = playerWorldTracker.getPlayerWorld(player.getName())
100+
.getOrElse(() -> player.getWorld().getName());
101+
return this.worldManager.getLoadedWorld(worldName)
103102
.map(mvworld -> mvworld.isHidden() ? "" : mvworld.getAliasOrName())
104103
.getOrElse("");
105104
}

src/main/java/org/mvplugins/multiverse/core/listeners/MVPlayerListener.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ final class MVPlayerListener implements CoreListener {
7474
private final CorePermissionsChecker corePermissionsChecker;
7575
private final AsyncSafetyTeleporter asyncSafetyTeleporter;
7676

77-
private final Map<String, String> playerWorld = new ConcurrentHashMap<>();
78-
7977
@Inject
8078
MVPlayerListener(
8179
MultiverseCore plugin,
@@ -120,15 +118,6 @@ private PluginLocales getLocales() {
120118
return getCommandManager().getLocales();
121119
}
122120

123-
/**
124-
* Gets the map of player and the world name they are in.
125-
*
126-
* @return the playerWorld-map
127-
*/
128-
Map<String, String> getPlayerWorld() {
129-
return playerWorld;
130-
}
131-
132121
/**
133122
* This method is called when a player respawns.
134123
*
@@ -272,7 +261,6 @@ private void teleportToDestinationOnJoin(Player player, DestinationInstance<?, ?
272261
void playerChangedWorld(PlayerChangedWorldEvent event) {
273262
// Permissions now determine whether or not to handle a gamemode.
274263
this.handleGameModeAndFlight(event.getPlayer(), event.getPlayer().getWorld());
275-
playerWorld.put(event.getPlayer().getName(), event.getPlayer().getWorld().getName());
276264
}
277265

278266
/**
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package org.mvplugins.multiverse.core.world.helpers;
2+
3+
import io.vavr.control.Option;
4+
import jakarta.inject.Inject;
5+
import org.bukkit.entity.Player;
6+
import org.bukkit.event.EventHandler;
7+
import org.bukkit.event.EventPriority;
8+
import org.bukkit.event.Listener;
9+
import org.bukkit.event.player.PlayerChangedWorldEvent;
10+
import org.bukkit.event.player.PlayerJoinEvent;
11+
import org.bukkit.event.player.PlayerQuitEvent;
12+
import org.jetbrains.annotations.ApiStatus;
13+
import org.jetbrains.annotations.NotNull;
14+
import org.jetbrains.annotations.UnmodifiableView;
15+
import org.jvnet.hk2.annotations.Service;
16+
import org.mvplugins.multiverse.core.MultiverseCore;
17+
18+
import java.util.Collection;
19+
import java.util.Collections;
20+
import java.util.Map;
21+
import java.util.concurrent.ConcurrentHashMap;
22+
23+
/**
24+
* Tracks which players are in which worlds, using a thread-safe map.
25+
* This allows async access to online players list and the world they are in.
26+
*
27+
* @since 5.4
28+
*/
29+
@ApiStatus.AvailableSince("5.4")
30+
@Service
31+
public final class ConcurrentPlayerWorldTracker implements Listener {
32+
33+
private final Map<String, String> playerWorldMap;
34+
35+
@Inject
36+
ConcurrentPlayerWorldTracker(@NotNull MultiverseCore plugin) {
37+
this.playerWorldMap = new ConcurrentHashMap<>();
38+
plugin.getServer().getPluginManager().registerEvents(this, plugin);
39+
}
40+
41+
/**
42+
* Get an unmodifiable collection of all online player names on the server.
43+
*
44+
* @return Unmodifiable collection of online player names.
45+
*
46+
* @since 5.4
47+
*/
48+
@ApiStatus.AvailableSince("5.4")
49+
@NotNull
50+
@UnmodifiableView
51+
public Collection<String> getOnlinePlayers() {
52+
return Collections.unmodifiableCollection(playerWorldMap.keySet());
53+
}
54+
55+
/**
56+
* Get the world name a player is currently in.
57+
*
58+
* @param playerName Name of the player.
59+
* @return World name the player is in, or null if the player is not online.
60+
*
61+
* @since 5.4
62+
*/
63+
@ApiStatus.AvailableSince("5.4")
64+
@NotNull
65+
public Option<String> getPlayerWorld(String playerName) {
66+
return Option.of(playerWorldMap.get(playerName));
67+
}
68+
69+
@EventHandler(priority = EventPriority.LOWEST)
70+
private void onPlayerJoin(PlayerJoinEvent event) {
71+
setPlayerWorld(event.getPlayer());
72+
}
73+
74+
@EventHandler(priority = EventPriority.LOWEST)
75+
private void onPlayerChangedWorld(@NotNull PlayerChangedWorldEvent event) {
76+
setPlayerWorld(event.getPlayer());
77+
}
78+
79+
private void setPlayerWorld(Player player) {
80+
String playerName = player.getName();
81+
String worldName = player.getWorld().getName();
82+
playerWorldMap.put(playerName, worldName);
83+
}
84+
85+
@EventHandler
86+
private void onPlayerQuit(@NotNull PlayerQuitEvent event) {
87+
String playerName = event.getPlayer().getName();
88+
playerWorldMap.remove(playerName);
89+
}
90+
}

0 commit comments

Comments
 (0)