not done yet

This commit is contained in:
mlus 2025-06-06 21:17:22 +08:00
parent 79e0e2cbe4
commit 3b442fbee1
7 changed files with 139 additions and 177 deletions

View File

@ -1,7 +1,7 @@
plugins {
id 'java-library'
id 'maven-publish'
id 'net.neoforged.moddev' version '2.0.86'
id 'net.neoforged.gradle.userdev' version '7.0.170'
id 'idea'
}
@ -46,99 +46,50 @@ base {
archivesName = mod_id
}
// Mojang ships Java 17 to end users from 1.18 to 1.20.4, so your mod should target Java 17.
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
java.toolchain.languageVersion = JavaLanguageVersion.of(21)
neoForge {
// Specify the version of NeoForge to use.
version = project.neo_version
//parchment {
// mappingsVersion = project.parchment_mappings_version
// minecraftVersion = project.parchment_minecraft_version
//}
parchment {
mappingsVersion = project.parchment_mappings_version
minecraftVersion = project.parchment_minecraft_version
// accessTransformers = project.files('src/main/resources/META-INF/accesstransformer.cfg')
runs {
configureEach {
systemProperty 'forge.logging.markers', 'REGISTRIES'
systemProperty 'forge.logging.console.level', 'debug'
modSource project.sourceSets.main
}
// This line is optional. Access Transformers are automatically detected
// accessTransformers = project.files('src/main/resources/META-INF/accesstransformer.cfg')
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
runs {
client {
client()
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
server {
server()
programArgument '--nogui'
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
// This run config launches GameTestServer and runs all registered gametests, then exits.
// By default, the server will crash when no gametests are provided.
// The gametest system is also enabled by default for other run configs under the /test command.
gameTestServer {
type = "gameTestServer"
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
data {
data()
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
// gameDirectory = project.file('run-data')
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
}
// applies to all the run configs above
configureEach {
// Recommended logging data for a userdev environment
// The markers can be added/remove as needed separated by commas.
// "SCAN": For mods scan.
// "REGISTRIES": For firing of registry events.
// "REGISTRYDUMP": For getting the contents of all registries.
systemProperty 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
// You can set various levels here.
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
logLevel = org.slf4j.event.Level.DEBUG
}
client {
client()
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
mods {
// define mod <-> source bindings
// these are used to tell the game which sources are for which mod
// multi mod projects should define one per mod
"${mod_id}" {
sourceSet(sourceSets.main)
}
server {
server()
programArgument '--nogui'
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
gameTestServer {
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
data {
programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
}
}
// Include resources generated by data generators.
sourceSets.main.resources { srcDir 'src/generated/resources' }
// Sets up a dependency configuration called 'localRuntime'.
// This configuration should be used instead of 'runtimeOnly' to declare
// a dependency that will be present for runtime testing but that is
// "optional", meaning it will not be pulled by dependents of this mod.
configurations {
runtimeClasspath.extendsFrom localRuntime
}
dependencies {
// Example optional mod dependency with JEI
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
// compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}"
// compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}"
// We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it
// localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}"
implementation "net.neoforged:neoforge:${neo_version}"
compileOnly "maven.modrinth:curios:7.4.3+1.20.4"
compileOnly "curse.maven:sophisticated-backpacks-422301:5297718"
compileOnly "curse.maven:sophisticated-core-618298:5296142"
@ -157,7 +108,7 @@ dependencies {
// embedd the JDBC driver in the mod using jarJar
runtimeOnly "com.mysql:mysql-connector-j:${jdbc_version}"
jarJar "com.mysql:mysql-connector-j:${jdbc_version}"
additionalRuntimeClasspath "com.mysql:mysql-connector-j:${jdbc_version}"
// runtimeClasspath "com.mysql:mysql-connector-j:${jdbc_version}"
// For more info:
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
@ -185,11 +136,6 @@ var generateModMetadata = tasks.register("generateModMetadata", ProcessResources
from "src/main/templates"
into "build/generated/sources/modMetadata"
}
// Include the output of "generateModMetadata" as an input directory for the build
// this works with both building through Gradle and the IDE.
sourceSets.main.resources.srcDir generateModMetadata
// To avoid having to run "generateModMetadata" manually, make it run on every project reload
neoForge.ideSyncTask generateModMetadata
// Example configuration to allow publishing using the maven-publish plugin
publishing {

View File

@ -7,23 +7,19 @@ org.gradle.configuration-cache=true
#read more on this at https://github.com/neoforged/ModDevGradle?tab=readme-ov-file#better-minecraft-parameter-names--javadoc-parchment
# you can also find the latest versions at: https://parchmentmc.org/docs/getting-started
parchment_minecraft_version=1.20.4
parchment_mappings_version=2024.04.14
parchment_minecraft_version=1.21.1
parchment_mappings_version=2024.11.17
# Environment Properties
# You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge
# The Minecraft version must agree with the Neo version to get a valid artifact
minecraft_version=1.20.4
minecraft_version=1.21.1
# The Minecraft version range can use any release version of Minecraft as bounds.
# Snapshots, pre-releases, and release candidates are not guaranteed to sort properly
# as they do not follow standard versioning conventions.
minecraft_version_range=[1.20.4]
# The Neo version must agree with the Minecraft version to get a valid artifact
neo_version=20.4.248
# The Neo version range can use any version of Neo as bounds
neo_version_range=[20.4.248,)
# The loader version range can only use the major version of FML as bounds
loader_version_range=[1,)
minecraft_version_range=[1.21,1.21.3)
neo_version=21.1.137
neo_version_range=[21.1.128,)
loader_version_range=[4,)
## Mod Properties
# The unique mod identifier for the mod. Must be lowercase in English locale. Must fit the regex [a-z][a-z0-9_]{1,63}

View File

@ -4,11 +4,11 @@ import com.mojang.brigadier.CommandDispatcher;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.event.RegisterCommandsEvent;
import vip.fubuki.playersync.sync.chat.ChatSyncClient;
@Mod.EventBusSubscriber()
@EventBusSubscriber()
public class CommandInit {
@SubscribeEvent

View File

@ -0,0 +1,18 @@
package vip.fubuki.playersync;
import com.mojang.serialization.Codec;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.codec.ByteBufCodecs;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
public class DataComponentTypes {
public static final DeferredRegister<DataComponentType<?>> REGISTRY = DeferredRegister.create(Registries.DATA_COMPONENT_TYPE, PlayerSync.MODID);
public static final DeferredHolder<DataComponentType<?>, DataComponentType<String>> ORIGINAL_ITEM_NBT =
REGISTRY.register("original_item_nbt", () -> DataComponentType.<String>builder().persistent(Codec.STRING).networkSynchronized(ByteBufCodecs.STRING_UTF8).build());
public static final DeferredHolder<DataComponentType<?>, DataComponentType<String>> ORIGINAL_ITEM_ID =
REGISTRY.register("original_item_id", () -> DataComponentType.<String>builder().persistent(Codec.STRING).networkSynchronized(ByteBufCodecs.STRING_UTF8).build());
public static final DeferredHolder<DataComponentType<?>, DataComponentType<String>> UNIQUE_ID =
REGISTRY.register("unique_id", () -> DataComponentType.<String>builder().persistent(Codec.STRING).networkSynchronized(ByteBufCodecs.STRING_UTF8).build());
}

View File

@ -1,6 +1,15 @@
package vip.fubuki.playersync;
import com.mojang.logging.LogUtils;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.ModList;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.config.ModConfig;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.server.ServerStartingEvent;
import org.slf4j.Logger;
import vip.fubuki.playersync.config.JdbcConfig;
import vip.fubuki.playersync.sync.ChatSync;
@ -11,27 +20,17 @@ import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.ModList;
import net.neoforged.fml.ModLoadingContext;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.config.ModConfig;
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent;
import net.neoforged.fml.javafmlmod.FMLJavaModLoadingContext;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.server.ServerStartingEvent;
import net.neoforged.bus.api.IEventBus;
@Mod(PlayerSync.MODID)
public class PlayerSync {
public static final String MODID = "playersync";
public static final Logger LOGGER = LogUtils.getLogger();
public PlayerSync(IEventBus modEventBus) {
public PlayerSync(IEventBus modEventBus, ModContainer modContainer) {
DataComponentTypes.REGISTRY.register(modEventBus);
modEventBus.addListener(this::commonSetup);
NeoForge.EVENT_BUS.register(this);
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, JdbcConfig.COMMON_CONFIG);
modContainer.registerConfig(ModConfig.Type.COMMON, JdbcConfig.COMMON_CONFIG);
}
private void commonSetup(final FMLCommonSetupEvent event) {

View File

@ -6,6 +6,11 @@ import net.minecraft.nbt.NbtUtils;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.neoforged.fml.ModList;
import net.neoforged.neoforge.server.ServerLifecycleHooks;
import top.theillusivec4.curios.api.CuriosApi;
import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler;
import top.theillusivec4.curios.api.type.inventory.ICurioStacksHandler;
import top.theillusivec4.curios.api.type.inventory.IDynamicStackHandler;
import vip.fubuki.playersync.PlayerSync;
import vip.fubuki.playersync.util.JDBCsetUp;
import vip.fubuki.playersync.util.LocalJsonUtil;
@ -14,11 +19,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import top.theillusivec4.curios.api.CuriosApi;
import top.theillusivec4.curios.api.type.capability.ICuriosItemHandler;
import top.theillusivec4.curios.api.type.inventory.ICurioStacksHandler;
import top.theillusivec4.curios.api.type.inventory.IDynamicStackHandler;
import java.util.Optional;
import java.util.UUID;
@ -75,7 +75,7 @@ public class ModsSupport {
try {
String nbtString = VanillaSync.deserializeString(serialized);
CompoundTag tag = NbtUtils.snbtToStructure(nbtString);
ItemStack stack = ItemStack.of(tag);
ItemStack stack = ItemStack.parse(ServerLifecycleHooks.getCurrentServer().registryAccess(),tag).get();
if (handler.getCurios().containsKey(slotType)) {
ICurioStacksHandler stacksHandler = handler.getCurios().get(slotType);
IDynamicStackHandler dynStacks = stacksHandler.getStacks();

View File

@ -2,8 +2,13 @@ package vip.fubuki.playersync.sync;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.ChatFormatting;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.*;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.Style;
import net.minecraft.resources.ResourceLocation;
@ -17,15 +22,18 @@ import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.ItemLore;
import net.minecraft.world.level.storage.WorldData;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.ModList;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.event.OnDatapackSyncEvent;
import net.neoforged.neoforge.event.TickEvent;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
import net.neoforged.neoforge.event.server.ServerStoppedEvent;
import net.neoforged.neoforge.event.tick.LevelTickEvent;
import net.neoforged.neoforge.event.tick.ServerTickEvent;
import net.neoforged.neoforge.server.ServerLifecycleHooks;
import vip.fubuki.playersync.DataComponentTypes;
import vip.fubuki.playersync.PlayerSync;
import vip.fubuki.playersync.config.JdbcConfig;
import vip.fubuki.playersync.util.JDBCsetUp;
@ -42,7 +50,7 @@ import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Mod.EventBusSubscriber
@EventBusSubscriber
public class VanillaSync {
public static void register() {}
@ -74,7 +82,7 @@ public class VanillaSync {
}
// Restore Advancements
File gameDir = Objects.requireNonNull(serverPlayer.getServer()).getServerDirectory();
File gameDir = Objects.requireNonNull(serverPlayer.getServer()).getServerDirectory().toFile();
final MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server != null && server.isDedicatedServer()) {
@ -269,7 +277,7 @@ public class VanillaSync {
if (BuiltInRegistries.ITEM.containsKey(registryName)) {
// Item exists (could be vanilla or a loaded mod item), restore normally
try {
ItemStack restoredItem = ItemStack.of(compoundTag);
ItemStack restoredItem = ItemStack.parse(ServerLifecycleHooks.getCurrentServer().registryAccess(),compoundTag).get();
// Only return the restored item if the ItemStack.of did not unexpectedly
// returned an empty item
// Either the item is not empty, or it is empty and the original tag was also
@ -293,29 +301,24 @@ public class VanillaSync {
PlayerSync.LOGGER.debug("Item {} not found in registry. Creating placeholder.", registryName);
ItemStack placeholder = new ItemStack(Items.PAPER);
CompoundTag placeholderNbt = placeholder.getOrCreateTag();
// Store the original serialized NBT string, not the parsed CompoundTag string
placeholderNbt.putString("playersync:original_item_nbt", serializedNbt);
placeholderNbt.putString("playersync:original_item_id", registryName.toString());
placeholder.set(DataComponentTypes.ORIGINAL_ITEM_NBT.get(), serializedNbt);
placeholder.set(DataComponentTypes.ORIGINAL_ITEM_ID.get(), registryName.toString());
// Add a unique UUID to ensure the item is unstackable
// Stacked placerholders would be converted into a single item when restoring item
placeholderNbt.putUUID("playersync:unique_id", UUID.randomUUID());
placeholder.set(DataComponentTypes.UNIQUE_ID.get(), UUID.randomUUID().toString());
// Add display name and lore
CompoundTag displayTag = placeholderNbt.getCompound("display");
if (!placeholderNbt.contains("display"))
placeholderNbt.put("display", displayTag);
String placeholderItemTitleOverride = JdbcConfig.ITEM_PLACEHOLDER_TITLE_OVERRIDE.get();
displayTag.putString("Name", Component.Serializer.toJson(
placeholder.set(DataComponents.ITEM_NAME,
Component
.literal(placeholderItemTitleOverride != null && !placeholderItemTitleOverride.isBlank()
? placeholderItemTitleOverride
: Component.translatable("playersync.item_placeholder_title").getString())
.setStyle(Style.EMPTY.withColor(ChatFormatting.RED).withItalic(true))));
.setStyle(Style.EMPTY.withColor(ChatFormatting.RED).withItalic(true)));
ListTag loreList = new ListTag();
List<Component> loreList = new ArrayList<>();
String placeholderItemDetails = registryName.toString();
// add a stack size if it is available
@ -325,11 +328,11 @@ public class VanillaSync {
placeholderItemDetails = placeholderItemAmount + "x " + placeholderItemDetails;
}
loreList.add(StringTag.valueOf(Component.Serializer.toJson(
loreList.add(
Component.literal(placeholderItemDetails)
.setStyle(Style.EMPTY.withColor(ChatFormatting.GRAY).withItalic(false)))));
.setStyle(Style.EMPTY.withColor(ChatFormatting.GRAY).withItalic(false)));
// add newline
loreList.add(StringTag.valueOf(Component.Serializer.toJson(Component.literal(""))));
loreList.add(Component.literal(""));
String placeholderItemDescriptionOverride = JdbcConfig.ITEM_PLACEHOLDER_DESCRIPTION_OVERRIDE.get();
String placeholderItemDescriptionLines = placeholderItemDescriptionOverride != null && ! placeholderItemDescriptionOverride.isBlank()
@ -337,11 +340,12 @@ public class VanillaSync {
: Component.translatable("playersync.item_placeholder_description").getString();
for (String descriptionLine : placeholderItemDescriptionLines.split("\n")) {
loreList.add(StringTag.valueOf(Component.Serializer.toJson(
loreList.add(
Component.literal(descriptionLine)
.setStyle(Style.EMPTY.withColor(ChatFormatting.DARK_GRAY)))));
.setStyle(Style.EMPTY.withColor(ChatFormatting.DARK_GRAY)));
}
displayTag.put("Lore", loreList);
placeholder.set(DataComponents.LORE,new ItemLore(loreList));
return placeholder;
}
@ -437,9 +441,9 @@ public class VanillaSync {
// Helper function to get the NBT string to be saved
// If item is a placeholder, get original NBT; otherwise, get current NBT
private static String getNbtForStorage(ItemStack itemStack) {
if (itemStack.is(Items.PAPER) && itemStack.hasTag() && itemStack.getTag().contains("playersync:original_item_nbt", Tag.TAG_STRING)) {
if (itemStack.is(Items.PAPER) && itemStack.getComponents().has(DataComponentTypes.ORIGINAL_ITEM_NBT.get())) {
// It's our placeholder, retrieve the original NBT string
return itemStack.getTag().getString("playersync:original_item_nbt");
return itemStack.getComponents().get(DataComponentTypes.ORIGINAL_ITEM_NBT.get());
} else {
// It's a normal item or empty, serialize its current NBT
return serialize(serializeNBT(itemStack).toString());
@ -451,8 +455,9 @@ public class VanillaSync {
return new CompoundTag();
}
// Serialize the ItemStack to NBT
HolderLookup.Provider provider = ServerLifecycleHooks.getCurrentServer().registryAccess();
CompoundTag compoundTag = new CompoundTag();
itemStack.save(compoundTag);
itemStack.save(provider);
return compoundTag;
}
@ -494,18 +499,18 @@ public class VanillaSync {
}
// Effects
Map<MobEffect, MobEffectInstance> effects = player.getActiveEffectsMap();
Map<Holder<MobEffect>, MobEffectInstance> effects = player.getActiveEffectsMap();
Map<Integer, String> effectMap = new HashMap<>();
for (Map.Entry<MobEffect, MobEffectInstance> entry : effects.entrySet()) {
CompoundTag effectTag = entry.getValue().save(new CompoundTag());
effectMap.put(BuiltInRegistries.MOB_EFFECT.getId(entry.getKey()), serialize(effectTag.toString()));
for (Map.Entry<Holder<MobEffect>, MobEffectInstance> entry : effects.entrySet()) {
Tag effectTag = entry.getValue().save();
effectMap.put(BuiltInRegistries.MOB_EFFECT.getId(entry.getKey().value()), serialize(effectTag.toString()));
}
// Advancements
File advancements = null;
byte[] advancementBytes = new byte[0];
if (JdbcConfig.SYNC_ADVANCEMENTS.get()) {
File gameDir = Objects.requireNonNull(player.getServer()).getServerDirectory();
File gameDir = Objects.requireNonNull(player.getServer()).getServerDirectory().toFile();
final MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server != null && server.isDedicatedServer() ) {
PlayerSync.LOGGER.trace("Reading dedicated server advancements");
@ -576,7 +581,7 @@ public class VanillaSync {
static int tick = 0;
@SubscribeEvent
public static void onUpdate(TickEvent.LevelTickEvent event) throws SQLException {
public static void onUpdate(LevelTickEvent event) throws SQLException {
tick++;
if (tick == 1800) {
tick = 0;
@ -592,37 +597,35 @@ public class VanillaSync {
//AutoSave
@SubscribeEvent
public static void onServerTick(TickEvent.ServerTickEvent event) {
public static void onServerTick(ServerTickEvent.Post event) {
// Run at the end phase to avoid interfering with game logic
if (event.phase == TickEvent.Phase.END) {
autoSaveTickCounter++;
if (autoSaveTickCounter >= AUTO_SAVE_INTERVAL_TICKS) {
autoSaveTickCounter = 0;
// Retrieve the current server instance
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server != null) {
// Iterate through all online players
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
executorService.submit(() -> {
try {
// Call the same store method used in logout and file save events.
store(player, false);
} catch (Exception e) {
PlayerSync.LOGGER.error("Error auto-saving player " + player.getUUID(), e);
}
});
executorService.submit(() -> {
try {
new ModsSupport().StoreCurios(player, false);
} catch (SQLException e) {
PlayerSync.LOGGER.error("Error auto-saving Curios data for player " + player.getUUID(), e);
}
});
autoSaveTickCounter++;
if (autoSaveTickCounter >= AUTO_SAVE_INTERVAL_TICKS) {
autoSaveTickCounter = 0;
// Retrieve the current server instance
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server != null) {
// Iterate through all online players
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
executorService.submit(() -> {
try {
// Call the same store method used in logout and file save events.
store(player, false);
} catch (Exception e) {
PlayerSync.LOGGER.error("Error auto-saving player " + player.getUUID(), e);
}
});
executorService.submit(() -> {
try {
new ModsSupport().StoreCurios(player, false);
} catch (SQLException e) {
PlayerSync.LOGGER.error("Error auto-saving Curios data for player " + player.getUUID(), e);
}
});
}
}
}
}
}
private static void setXpForPlayer(ServerPlayer serverPlayer, int databaseXp) {