Skip to content
Draft
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
16 changes: 16 additions & 0 deletions api/src/main/java/net/thenextlvl/perworlds/WorldGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,23 @@ default boolean isLoadingData(Player player) {
*
* @param player the player whose data is to be persisted and modified
* @param data a {@link Consumer} that manipulates the {@link PlayerData} object
* @see #persistPlayerData(OfflinePlayer, Consumer)
*/
@Contract(mutates = "io")
void persistPlayerData(Player player, Consumer<PlayerData> data);

/**
* Persists and modifies the data of the specified offline player using the provided consumer.
* The method allows manipulation through the consumer and ensures the updated data is saved to persistent storage.
* <p>
* Opposed to {@link #persistPlayerData(Player, Consumer)},
* this method does not try to resolve the player's data even if they are online.
*
* @param player the offline player whose data is to be persisted and modified
* @param data a {@link Consumer} that manipulates the {@link PlayerData} object
* @see #persistPlayerData(Player, Consumer)
* @since 1.1.0
*/
@Contract(mutates = "io")
void persistPlayerData(OfflinePlayer player, Consumer<PlayerData> data);
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ public interface PlayerData {
UUID uuid();

@Nullable
@Contract(pure = true)
@Contract(value = " -> new", pure = true)
ItemStack[] enderChest();

@Contract(mutates = "this")
PlayerData enderChest(@Nullable ItemStack[] contents);

@Nullable
@Contract(pure = true)
@Contract(value = " -> new", pure = true)
ItemStack[] inventory();

@Contract(mutates = "this")
Expand Down
15 changes: 13 additions & 2 deletions src/main/java/net/thenextlvl/perworlds/PerWorldsPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,19 @@ private void registerServices() {
}

private void scheduleDelayedInitTask() {
if (groupsExist && config().migrateToGroup != null) return;
getServer().getGlobalRegionScheduler().execute(this, this::setupNotice);
var pluginsFolder = getServer().getPluginsFolder().toPath();
if (!groupsExist || config().migrateToGroup == null)
getServer().getGlobalRegionScheduler().execute(this, this::setupNotice);
if (Files.isDirectory(pluginsFolder.resolve("Multiverse-Inventories")))
getServer().getGlobalRegionScheduler().execute(this, () -> importNotice("Multiverse-Inventories"));
}

private void importNotice(String pluginName) {
var separator = "-".repeat(86);
getComponentLogger().info(separator);
getComponentLogger().info("It appears you have been using {} before!", pluginName);
getComponentLogger().info("To migrate your data to PerWorlds, run '/world group import {}'", pluginName);
getComponentLogger().info(separator);
}

private void setupNotice() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,13 @@ public void persistPlayerData(Player player, Consumer<PlayerData> data) {
writePlayerData(player, playerData);
}

@Override
public void persistPlayerData(OfflinePlayer player, Consumer<PlayerData> data) {
var playerData = new PaperPlayerData(player.getUniqueId(), this);
data.accept(playerData);
writePlayerData(player, playerData);
}

private Optional<PlayerData> readPlayerData(OfflinePlayer player, Path file) throws IOException {
return readFile(file, file.resolveSibling(file.getFileName() + "_old"), PaperPlayerData.class)
.map(paperPlayerData -> paperPlayerData.finalize(player, this));
Expand Down
79 changes: 79 additions & 0 deletions src/main/java/net/thenextlvl/perworlds/importer/Importer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package net.thenextlvl.perworlds.importer;

import net.thenextlvl.perworlds.PerWorldsPlugin;
import net.thenextlvl.perworlds.WorldGroup;
import net.thenextlvl.perworlds.data.PlayerData;
import org.jspecify.annotations.NullMarked;

import java.io.IOException;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

@NullMarked
public abstract class Importer {
protected final PerWorldsPlugin plugin;

private final Path dataPath;
private final String name;

protected Importer(PerWorldsPlugin plugin, String name) {
this.dataPath = Path.of("plugins", name);
this.name = name;
this.plugin = plugin;
}

public Path getDataPath() {
return dataPath;
}

public String getName() {
return name;
}

public void load() {
try {
var groups = loadGroups();
loadPlayers(groups);
} catch (IOException e) {
plugin.getComponentLogger().error("Failed to import {}", name, e);
}
}

public Set<WorldGroup> loadGroups() throws IOException {
var read = readGroups();
var groups = new HashSet<WorldGroup>(read.size());
read.forEach((group, worlds) -> {
var worldGroup = plugin.groupProvider().getGroup(group).orElseGet(() ->
plugin.groupProvider().createGroup(group));
worlds.stream().map(plugin.getServer()::getWorld)
.filter(Objects::nonNull)
.forEach(worldGroup::addWorld);
groups.add(worldGroup);
});
return groups;
}

public void loadPlayers(Set<WorldGroup> groups) throws IOException {
readPlayers().forEach((uuid, name) -> groups.forEach(group -> {
var offlinePlayer = plugin.getServer().getOfflinePlayer(uuid);
group.persistPlayerData(offlinePlayer, playerData -> {
try {
readPlayer(uuid, name, group, playerData);
} catch (IOException e) {
plugin.getComponentLogger().error("Failed to import player data for {} ({}) in group {}",
name, uuid, group.getName(), e);
}
});
}));
}

public abstract Map<String, Set<String>> readGroups() throws IOException;

public abstract Map<UUID, String> readPlayers() throws IOException;

public abstract void readPlayer(UUID uuid, String name, WorldGroup group, PlayerData data) throws IOException;
}
Loading