Skip to content

Commit 8095efc

Browse files
committed
Implement player name mapper
1 parent 02819b3 commit 8095efc

File tree

4 files changed

+154
-14
lines changed

4 files changed

+154
-14
lines changed

src/main/java/org/mvplugins/multiverse/inventories/MultiverseInventories.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
import com.dumptruckman.minecraft.util.Logging;
44
import org.bukkit.entity.Player;
55
import org.bukkit.plugin.PluginManager;
6-
import org.mvplugins.multiverse.core.MultiverseCoreApi;
76
import org.mvplugins.multiverse.core.config.CoreConfig;
87
import org.mvplugins.multiverse.core.destination.DestinationsProvider;
9-
import org.mvplugins.multiverse.core.inject.PluginServiceLocatorFactory;
108
import org.mvplugins.multiverse.core.module.MultiverseModule;
119
import org.mvplugins.multiverse.core.utils.StringFormatter;
1210
import org.mvplugins.multiverse.inventories.command.MVInvCommandConditions;
@@ -21,6 +19,7 @@
2119
import org.mvplugins.multiverse.inventories.handleshare.SingleShareWriter;
2220
import org.mvplugins.multiverse.inventories.handleshare.SpawnChangeListener;
2321
import org.mvplugins.multiverse.inventories.handleshare.WriteOnlyShareHandler;
22+
import org.mvplugins.multiverse.inventories.profile.PlayerNamesMapper;
2423
import org.mvplugins.multiverse.inventories.profile.ProfileCacheManager;
2524
import org.mvplugins.multiverse.inventories.profile.ProfileDataSource;
2625
import org.mvplugins.multiverse.inventories.profile.key.GlobalProfileKey;
@@ -31,8 +30,6 @@
3130
import org.mvplugins.multiverse.inventories.util.ItemStackConverter;
3231
import org.mvplugins.multiverse.inventories.util.Perm;
3332
import org.bukkit.Bukkit;
34-
import org.mvplugins.multiverse.core.command.MVCommandManager;
35-
import org.mvplugins.multiverse.core.inject.PluginServiceLocator;
3633
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
3734
import org.mvplugins.multiverse.external.jakarta.inject.Provider;
3835
import org.jvnet.hk2.annotations.Service;
@@ -61,6 +58,8 @@ public class MultiverseInventories extends MultiverseModule {
6158
@Inject
6259
private Provider<WorldGroupManager> worldGroupManager;
6360
@Inject
61+
private Provider<PlayerNamesMapper> playerNamesMapperProvider;
62+
@Inject
6463
private Provider<ProfileDataSource> profileDataSource;
6564
@Inject
6665
private Provider<ProfileCacheManager> profileCacheManager;
@@ -119,6 +118,7 @@ public final void onEnable() {
119118
// Init other extensions
120119
this.hookLuckPerms();
121120
this.dupingPatch = InventoriesDupingPatch.enableDupingPatch(this);
121+
this.playerNamesMapperProvider.get().loadMap();
122122

123123
Logging.config("Version %s (API v%s) Enabled - By %s",
124124
this.getDescription().getVersion(), getVersionAsNumber(), StringFormatter.joinAnd(this.getDescription().getAuthors()));

src/main/java/org/mvplugins/multiverse/inventories/profile/FlatFileProfileDataSource.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,19 @@ final class FlatFileProfileDataSource implements ProfileDataSource {
3434
private final AsyncFileIO asyncFileIO;
3535
private final ProfileFilesLocator profileFilesLocator;
3636
private final ProfileCacheManager profileCacheManager;
37+
private final PlayerNamesMapper playerNamesMapper;
3738

3839
@Inject
3940
FlatFileProfileDataSource(
4041
@NotNull AsyncFileIO asyncFileIO,
4142
@NotNull ProfileFilesLocator profileFilesLocator,
42-
@NotNull ProfileCacheManager profileCacheManager
43+
@NotNull ProfileCacheManager profileCacheManager,
44+
@NotNull PlayerNamesMapper playerNamesMapper
4345
) {
4446
this.asyncFileIO = asyncFileIO;
4547
this.profileFilesLocator = profileFilesLocator;
4648
this.profileCacheManager = profileCacheManager;
49+
this.playerNamesMapper = playerNamesMapper;
4750
}
4851

4952
private FileConfiguration loadFileToJsonConfiguration(File file) {
@@ -263,7 +266,7 @@ public CompletableFuture<GlobalProfile> getGlobalProfile(GlobalProfileKey key) {
263266
globalProfile.setLastKnownName(key.getPlayerName());
264267
return CompletableFuture.completedFuture(globalProfile);
265268
}
266-
return asyncFileIO.queueFileCallable(globalFile, () -> getGlobalProfileFromDisk(key.getPlayerUUID(), key.getPlayerName(), globalFile));
269+
return asyncFileIO.queueFileCallable(globalFile, () -> getGlobalProfileFromDisk(key.getPlayerUUID(), globalFile));
267270
});
268271
}
269272

@@ -289,10 +292,8 @@ private void migrateGlobalProfileToUUID(UUID playerUUID, String playerName) {
289292
}
290293
}
291294

292-
private GlobalProfile getGlobalProfileFromDisk(UUID playerUUID, String playerName, File globalFile) {
293-
GlobalProfile globalProfile = new GlobalProfile(playerUUID, globalFile.toPath());
294-
globalProfile.setLastKnownName(playerName);
295-
return globalProfile;
295+
private GlobalProfile getGlobalProfileFromDisk(UUID playerUUID, File globalFile) {
296+
return new GlobalProfile(playerUUID, globalFile.toPath());
296297
}
297298

298299
/**
@@ -314,7 +315,10 @@ private CompletableFuture<Void> modifyGlobalProfile(GlobalProfile globalProfile,
314315
@Override
315316
public CompletableFuture<Void> updateGlobalProfile(GlobalProfile globalProfile) {
316317
File globalFile = profileFilesLocator.getGlobalFile(globalProfile.getPlayerUUID().toString());
317-
return asyncFileIO.queueFileAction(globalFile, () -> processGlobalProfileWrite(globalProfile));
318+
return asyncFileIO.queueFileAction(globalFile, () -> processGlobalProfileWrite(globalProfile))
319+
.thenCompose(ignore -> playerNamesMapper.setPlayerName(globalProfile.getPlayerUUID(), globalProfile.getLastKnownName())
320+
? asyncFileIO.queueFileAction(playerNamesMapper.getFile(), playerNamesMapper::savePlayerNames)
321+
: CompletableFuture.completedFuture(null));
318322
}
319323

320324
private void processGlobalProfileWrite(GlobalProfile globalProfile) {
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package org.mvplugins.multiverse.inventories.profile;
2+
3+
import com.dumptruckman.minecraft.util.Logging;
4+
import com.google.common.base.Strings;
5+
import net.minidev.json.JSONObject;
6+
import net.minidev.json.JSONValue;
7+
import net.minidev.json.parser.JSONParser;
8+
import org.jvnet.hk2.annotations.Service;
9+
import org.mvplugins.multiverse.external.jakarta.inject.Inject;
10+
import org.mvplugins.multiverse.external.jakarta.inject.Provider;
11+
import org.mvplugins.multiverse.external.jetbrains.annotations.NotNull;
12+
import org.mvplugins.multiverse.external.vavr.control.Option;
13+
import org.mvplugins.multiverse.inventories.MultiverseInventories;
14+
import org.mvplugins.multiverse.inventories.profile.key.GlobalProfileKey;
15+
16+
import java.io.File;
17+
import java.io.FileReader;
18+
import java.io.FileWriter;
19+
import java.util.List;
20+
import java.util.Map;
21+
import java.util.UUID;
22+
import java.util.concurrent.CompletableFuture;
23+
import java.util.concurrent.ConcurrentHashMap;
24+
25+
@Service
26+
public final class PlayerNamesMapper {
27+
28+
private static final String FILENAME = "playernames.json";
29+
30+
private final MultiverseInventories inventories;
31+
private final Provider<ProfileDataSource> profileDataSourceProvider;
32+
private final Provider<ProfileCacheManager> profileCacheManagerProvider;
33+
34+
private final File playerNamesFile;
35+
private final Map<String, GlobalProfileKey> playerNamesMap;
36+
private final Map<UUID, GlobalProfileKey> playerUUIDMap;
37+
38+
private Map<String, Object> playerNamesJson;
39+
40+
@Inject
41+
PlayerNamesMapper(
42+
@NotNull MultiverseInventories inventories,
43+
@NotNull Provider<ProfileDataSource> profileDataSourceProvider,
44+
@NotNull Provider<ProfileCacheManager> profileCacheManagerProvider
45+
) {
46+
this.inventories = inventories;
47+
this.profileDataSourceProvider = profileDataSourceProvider;
48+
this.profileCacheManagerProvider = profileCacheManagerProvider;
49+
50+
this.playerNamesFile = new File(inventories.getDataFolder(), FILENAME);
51+
this.playerNamesMap = new ConcurrentHashMap<>();
52+
this.playerUUIDMap = new ConcurrentHashMap<>();
53+
}
54+
55+
public void loadMap() {
56+
Logging.config("Loading player names map...");
57+
playerNamesMap.clear();
58+
playerUUIDMap.clear();
59+
if (playerNamesFile.exists()) {
60+
loadFromPlayerNamesFile();
61+
} else {
62+
buildPlayerNamesMap();
63+
}
64+
}
65+
66+
private void loadFromPlayerNamesFile() {
67+
try (FileReader fileReader = new FileReader(playerNamesFile)) {
68+
playerNamesJson = new ConcurrentHashMap<>((JSONObject) new JSONParser(JSONParser.DEFAULT_PERMISSIVE_MODE).parse(fileReader));
69+
if (playerNamesJson.isEmpty()) {
70+
buildPlayerNamesMap();
71+
return;
72+
}
73+
playerNamesJson.forEach((String uuid, Object name) -> {
74+
UUID playerUUID = UUID.fromString(uuid);
75+
String playerName = String.valueOf(name);
76+
GlobalProfileKey globalProfileKey = GlobalProfileKey.create(playerUUID, playerName);
77+
playerNamesMap.put(playerName, globalProfileKey);
78+
playerUUIDMap.put(playerUUID, globalProfileKey);
79+
});
80+
} catch (Exception e) {
81+
e.printStackTrace();
82+
}
83+
}
84+
85+
private void buildPlayerNamesMap() {
86+
Logging.info("Generating player names map... This may take a while.");
87+
playerNamesJson = new ConcurrentHashMap<>();
88+
89+
ProfileDataSource profileDataSource = profileDataSourceProvider.get();
90+
CompletableFuture[] futures = profileDataSource.listGlobalProfileUUIDs().stream()
91+
.map(uuid -> profileDataSource.getGlobalProfile(GlobalProfileKey.create(uuid))
92+
.thenAccept(globalProfile -> setPlayerName(uuid, globalProfile.getLastKnownName())))
93+
.toArray(CompletableFuture[]::new);
94+
CompletableFuture.allOf(futures).thenRun(this::savePlayerNames).join();
95+
Logging.info("Generated player names map.");
96+
}
97+
98+
boolean setPlayerName(UUID uuid, String name) {
99+
Logging.finer("Setting player name mapping for %s to %s", uuid, name);
100+
if (Strings.isNullOrEmpty(name)) {
101+
return false;
102+
}
103+
if (getKey(name).filter(g -> g.getPlayerUUID().equals(uuid)).isDefined()) {
104+
return false;
105+
}
106+
GlobalProfileKey globalProfileKey = GlobalProfileKey.create(uuid, name);
107+
playerNamesJson.put(uuid.toString(), name);
108+
playerNamesMap.put(name, globalProfileKey);
109+
playerUUIDMap.put(uuid, globalProfileKey);
110+
return true;
111+
}
112+
113+
void savePlayerNames() {
114+
try (FileWriter fileWriter = new FileWriter(playerNamesFile)) {
115+
fileWriter.write(JSONValue.toJSONString(playerNamesJson));
116+
} catch (Exception e) {
117+
e.printStackTrace();
118+
}
119+
}
120+
121+
File getFile() {
122+
return playerNamesFile;
123+
}
124+
125+
public Option<GlobalProfileKey> getKey(String playerName) {
126+
return Option.of(playerNamesMap.get(playerName));
127+
}
128+
129+
public Option<GlobalProfileKey> getKey(UUID playerUUID) {
130+
return Option.of(playerUUIDMap.get(playerUUID));
131+
}
132+
133+
public List<GlobalProfileKey> getKeys() {
134+
return playerNamesMap.values().stream().toList();
135+
}
136+
}

src/test/java/org/mvplugins/multiverse/inventories/profile/FilePerformanceTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ class FilePerformanceTest : TestWithMockBukkit() {
4848
}
4949

5050
@Test
51-
fun `Test 10K global profiles`() {
51+
fun `Test 1K global profiles`() {
5252
val startTime = System.nanoTime()
53-
val futures = ArrayList<Future<Void>>(10000)
54-
for (i in 0..9999) {
53+
val futures = ArrayList<Future<Void>>(1000)
54+
for (i in 0..1000) {
5555
futures.add(profileDataSource.modifyGlobalProfile(GlobalProfileKey.create(UUID.randomUUID()), { globalProfile ->
5656
globalProfile.setLoadOnLogin(true)
5757
}))

0 commit comments

Comments
 (0)