Skip to content
Merged
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
19 changes: 1 addition & 18 deletions ec-core/src/main/java/dev/jpcode/eccore/util/TextUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,11 @@
import com.google.gson.JsonParser;
import eu.pb4.placeholders.api.ParserContext;
import eu.pb4.placeholders.api.parsers.TagParser;
import org.apache.logging.log4j.Level;

import com.mojang.serialization.JsonOps;

import net.minecraft.text.*;

import dev.jpcode.eccore.ECCore;

public final class TextUtil {
private TextUtil() {}

Expand Down Expand Up @@ -198,21 +195,7 @@ public static void registerTextParser(StringToTextParser parser) {

static {
registerTextParser(str -> TextCodecs.CODEC.parse(JsonOps.INSTANCE, JsonParser.parseString(str)).getOrThrow());
int javaVersion = Util.getJavaVersion();
if (javaVersion >= 16) {
ECCore.LOGGER.log(Level.INFO, String.format(
"Detected Java version %d. Enabling Java %d features.",
javaVersion,
16
));
registerTextParser(str -> TagParser.DEFAULT.parseText(str, ParserContext.of()));
} else {
ECCore.LOGGER.log(Level.WARN, String.format(
"Detected Java version %d. Some features require Java %d. Some text formatting features will be disabled.",
javaVersion,
16
));
}
registerTextParser(str -> TagParser.DEFAULT.parseText(str, ParserContext.of()));
}

public static Text parseText(String textStr) {
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/com/fibermc/essentialcommands/WorldData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.fibermc.essentialcommands;

import com.fibermc.essentialcommands.codec.Codecs;
import com.fibermc.essentialcommands.types.MinecraftLocation;
import com.fibermc.essentialcommands.types.WarpStorage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;

import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtOps;

public class WorldData {
Comment thread
John-Paul-R marked this conversation as resolved.
private @Nullable MinecraftLocation spawnLocation;
private final @NotNull WarpStorage warps;

WorldData() {
this.spawnLocation = null;
this.warps = new WarpStorage();
}

WorldData(@Nullable MinecraftLocation spawnLocation, @NotNull WarpStorage warps) {
this.spawnLocation = spawnLocation;
this.warps = warps;
}

public WarpStorage warps() {
return this.warps;
}

public MinecraftLocation getSpawn() {
return this.spawnLocation;
}

public void setSpawn(@Nullable MinecraftLocation spawn) {
this.spawnLocation = spawn;
}

public static WorldData fromNbt(NbtCompound nbt) {
return CODEC.parse(NbtOps.INSTANCE, nbt).getOrThrow();
}

public NbtCompound toNbt() {
return CODEC.encodeStart(NbtOps.INSTANCE, this).getOrThrow().asCompound().orElseThrow();
}

public static final Codec<WorldData> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Codecs.MINECRAFT_LOCATION.fieldOf("spawn").forGetter(WorldData::getSpawn),
Codecs.WARP_STORAGE.fieldOf("warps").forGetter(WorldData::warps)
).apply(instance, WorldData::new)
);
}
68 changes: 18 additions & 50 deletions src/main/java/com/fibermc/essentialcommands/WorldDataManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtSizeTracker;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.WorldSavePath;
Expand All @@ -32,17 +30,12 @@
import net.fabricmc.fabric.api.event.EventFactory;

public class WorldDataManager extends PersistentState {
private final WarpStorage warps;
private MinecraftLocation spawnLocation;
private Path saveDir;
private File worldDataFile;

private static final String SPAWN_KEY = "spawn";
private static final String WARPS_KEY = "warps";
private WorldData data;

public WorldDataManager() {
warps = new WarpStorage();
spawnLocation = null;
this.data = new WorldData();
}

public static WorldDataManager createForServer(MinecraftServer server)
Expand All @@ -68,10 +61,11 @@ public void onServerStart(MinecraftServer server) {
// if files was not JUST created, read data from it.
var tag = NbtIo.readCompressed(worldDataFile.toPath(), NbtSizeTracker.ofUnlimitedBytes());
// `data` was the main obj key in old mc PersistentState schema
this.fromNbt(tag.getCompound("data").orElse(tag));
this.data = WorldData.fromNbt(tag.getCompound("data").orElse(tag));
warpsLoadEvent.invoker().accept(this.data.warps());
} else {
this.markDirty();
this.save(server.getRegistryManager());
this.save();
}
} catch (IOException e) {
EssentialCommands.log(Level.ERROR, String.format("An unexpected error occoured while loading the Essential Commands World Data file (Path: '%s')", worldDataFile.getPath()));
Expand All @@ -83,18 +77,6 @@ private File getDataFile() {
return worldDataFile;
}

public void fromNbt(NbtCompound tag) {
this.spawnLocation = tag.getCompound(SPAWN_KEY)
.flatMap(spawnTag -> spawnTag.isEmpty() ? Optional.empty() : Optional.of(spawnTag))
.map(MinecraftLocation::fromNbt)
.orElse(null);

tag.getCompound(WARPS_KEY)
.ifPresent(warps::loadNbt);

warpsLoadEvent.invoker().accept(warps);
}

public final Event<Consumer<WarpStorage>> warpsLoadEvent = EventFactory.createArrayBacked(
Consumer.class,
(listeners) -> (warps) -> {
Expand All @@ -103,9 +85,9 @@ public void fromNbt(NbtCompound tag) {
}
});

public void save(RegistryWrapper.WrapperLookup wrapperLookup) {
public void save() {
EssentialCommands.log(Level.INFO, "Saving world_data.dat (Spawn/Warps)...");
NbtCompound data = this.writeNbt(new NbtCompound(), wrapperLookup);
NbtCompound data = this.data.toNbt();
try {
NbtIo.writeCompressed(data, this.worldDataFile.toPath());
} catch (IOException e) {
Expand All @@ -114,65 +96,51 @@ public void save(RegistryWrapper.WrapperLookup wrapperLookup) {
EssentialCommands.log(Level.INFO, "world_data.dat saved.");
}

public NbtCompound writeNbt(NbtCompound tag, RegistryWrapper.WrapperLookup wrapperLookup) {
// Spawn to NBT
if (spawnLocation != null) {
tag.put(SPAWN_KEY, spawnLocation.asNbt());
}

// Warps to NBT
NbtCompound warpsNbt = new NbtCompound();
warps.writeNbt(warpsNbt);
tag.put(WARPS_KEY, warpsNbt);

return tag;
}

// Command Actions
public void setWarp(String warpName, MinecraftLocation location, boolean requiresPermission) throws CommandSyntaxException {
warps.putCommand(warpName, new WarpLocation(
this.data.warps().putCommand(warpName, new WarpLocation(
location,
requiresPermission ? warpName : null,
warpName
));
this.markDirty();
this.save(DynamicRegistryManager.EMPTY);
this.save();
}

public boolean delWarp(String warpName) {
MinecraftLocation prevValue = warps.remove(warpName);
MinecraftLocation prevValue = this.data.warps().remove(warpName);
this.markDirty();
this.save(DynamicRegistryManager.EMPTY);
this.save();
return prevValue != null;
}

public WarpLocation getWarp(String warpName) {
return warps.get(warpName);
return this.data.warps().get(warpName);
}

public List<String> getWarpNames() {
return this.warps.keySet().stream().toList();
return this.data.warps().keySet().stream().toList();
}

public Stream<WarpLocation> getAccessibleWarps(ServerPlayerEntity player) {
var warpsStream = this.warps.values().stream();
var warpsStream = this.data.warps().values().stream();
return (EssentialCommands.CONFIG.USE_PERMISSIONS_API
? warpsStream.filter(loc -> loc.hasPermission(player))
: warpsStream);
}

public Set<Entry<String, WarpLocation>> getWarpEntries() {
return this.warps.entrySet();
return this.data.warps().entrySet();
}

public void setSpawn(MinecraftLocation location) {
spawnLocation = location;
this.data.setSpawn(location);
this.markDirty();
this.save(DynamicRegistryManager.EMPTY);
this.save();
}

public Optional<MinecraftLocation> getSpawn() {
return Optional.ofNullable(spawnLocation);
return Optional.ofNullable(this.data.getSpawn());
}

}
95 changes: 95 additions & 0 deletions src/main/java/com/fibermc/essentialcommands/codec/Codecs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.fibermc.essentialcommands.codec;

import java.util.HashMap;
import java.util.Optional;

import com.fibermc.essentialcommands.WorldData;
import com.fibermc.essentialcommands.types.*;

import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;

import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.world.World;

public final class Codecs {
private Codecs() {}

public static final Codec<RegistryKey<World>> WORLD_KEY = RegistryKey.createCodec(RegistryKeys.WORLD);

public static final Codec<MinecraftLocation> MINECRAFT_LOCATION = RecordCodecBuilder.create(instance ->
instance.group(
WORLD_KEY.fieldOf("WorldRegistryKey").forGetter(MinecraftLocation::dim),
Codec.DOUBLE.fieldOf("x").forGetter(MinecraftLocation::x),
Codec.DOUBLE.fieldOf("y").forGetter(MinecraftLocation::y),
Codec.DOUBLE.fieldOf("z").forGetter(MinecraftLocation::z),
Codec.FLOAT.optionalFieldOf("headYaw", 0.0f).forGetter(MinecraftLocation::headYaw),
Codec.FLOAT.optionalFieldOf("pitch", 0.0f).forGetter(MinecraftLocation::pitch)
).apply(instance, MinecraftLocation::new)
);

public static final Codec<NamedMinecraftLocation> NAMED_MINECRAFT_LOCATION = RecordCodecBuilder.create(instance ->
instance.group(
// Inherit all fields from NamedMinecraftLocation
WORLD_KEY.fieldOf("WorldRegistryKey").forGetter(NamedMinecraftLocation::dim),
Codec.DOUBLE.fieldOf("x").forGetter(MinecraftLocation::x),
Codec.DOUBLE.fieldOf("y").forGetter(MinecraftLocation::y),
Codec.DOUBLE.fieldOf("z").forGetter(MinecraftLocation::z),
Codec.FLOAT.optionalFieldOf("headYaw", 0.0f).forGetter(NamedMinecraftLocation::headYaw),
Codec.FLOAT.optionalFieldOf("pitch", 0.0f).forGetter(NamedMinecraftLocation::pitch),
// loaded from the map
Codec.STRING.optionalFieldOf("name").forGetter(home -> Optional.of(home.getName()))

).apply(instance, NamedMinecraftLocation::new)
);

public static final Codec<WarpLocation> WARP_LOCATION = RecordCodecBuilder.create(instance ->
instance.group(
// Inherit all fields from NamedMinecraftLocation
WORLD_KEY.fieldOf("WorldRegistryKey").forGetter(WarpLocation::dim),
Codec.DOUBLE.fieldOf("x").forGetter(MinecraftLocation::x),
Codec.DOUBLE.fieldOf("y").forGetter(MinecraftLocation::y),
Codec.DOUBLE.fieldOf("z").forGetter(MinecraftLocation::z),
Codec.FLOAT.optionalFieldOf("headYaw", 0.0f).forGetter(WarpLocation::headYaw),
Codec.FLOAT.optionalFieldOf("pitch", 0.0f).forGetter(WarpLocation::pitch),
// loaded from the map
Codec.STRING.optionalFieldOf("name").forGetter(warp -> Optional.of(warp.getName())),

Codec.STRING.optionalFieldOf("permissionString").forGetter(warp -> Optional.ofNullable(warp.getPermissionString()))

).apply(instance, WarpLocation::new)
);

public static final Codec<NamedLocationStorage> NAMED_LOCATION_STORAGE =
Codec.unboundedMap(Codec.STRING, MINECRAFT_LOCATION)
.xmap(
// Convert Map to NamedLocationStorage
map -> {
NamedLocationStorage storage = new NamedLocationStorage();
map.forEach(
(key, value) -> storage.put(key, new NamedMinecraftLocation(value, key))
);
return storage;
},
// Convert NamedLocationStorage to Map for serialization
HashMap::new
);

public static final Codec<WarpStorage> WARP_STORAGE =
Codec.unboundedMap(Codec.STRING, WARP_LOCATION)
.xmap(
// Convert Map to WarpStorage
map -> {
WarpStorage storage = new WarpStorage();
map.forEach(
(key, value) -> storage.put(key, WarpLocation.setName(value, key))
);
return storage;
},
// Convert WarpStorage to Map for serialization
HashMap::new
);

public static final Codec<WorldData> WORLD_DATA = WorldData.CODEC;
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public int run(CommandContext<ServerCommandSource> context) throws CommandSyntax
playerData.removeHome(homeName);
playerData.addHome(homeName, new MinecraftLocation(senderPlayer));

playerData.save(context.getSource().getServer().getRegistryManager());
playerData.save();
//inform command sender that the home has been set
playerData.sendCommandFeedback("cmd.overwritehome.feedback", homeNameText);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private static int exec(CommandContext<ServerCommandSource> context, String home
Text homeNameText = ECText.access(senderPlayer).accent(homeName);
playerData.addHome(homeName, new MinecraftLocation(senderPlayer));

playerData.save(context.getSource().getServer().getRegistryManager());
playerData.save();
//inform command sender that the home has been set
playerData.sendCommandFeedback("cmd.home.set.feedback", homeNameText);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.fibermc.essentialcommands.mixin;

import java.util.Objects;

import com.fibermc.essentialcommands.access.ServerPlayerEntityAccess;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
Expand All @@ -16,7 +14,7 @@ public class WorldSaveHandlerMixin {

@Inject(method = "savePlayerData", at = @At("RETURN"))
public void onSavePlayerData(PlayerEntity player, CallbackInfo ci) {
((ServerPlayerEntityAccess) player).ec$getPlayerData().save(Objects.requireNonNull(player.getServer()).getRegistryManager());
((ServerPlayerEntityAccess) player).ec$getPlayerData().save();
// System.out.printf("Saved PlayerData for player: %s\n", player.getName().getString());
}

Expand Down
Loading