Shopkeepers

Shopkeepers

2M Downloads

Unable to load shop data after update to 2.22.3: "The name of the profile contains invalid characters"

BigScary opened this issue ยท 1 comments

commented

Preliminaries:

  • Shopkeepers version: 2.22.3
  • Spigot version: This server is running Paper version 1.21-108-master@73a863b (2024-07-22T13:23:21Z) (Implementing API version 1.21-R0.1-SNAPSHOT)

  • I have checked that my issue/question does not get answered by:
  • I have checked all open and closed issues, but none seems to fit my issue/question. Yes

Reproduction on a fresh and up-to-date Spigot server:

This doesn't apply to my case because my repro is about specific shopkeepers data that can't be loaded. An entirely fresh server wouldn't repro because it wouldn't have the needed data state.

The issue:

Have been using Shopkeepers for several years on my Paper server. We were on 1.20.4 before we finally updated to a recommended build of Paper 1.21 last week. For a few days, we ran without Shopkeepers because there wasn't a Shopkeepers 1.21 update out yet. When I first tried booting my server with the latest Shopkeepers with 1.21 support, it failed to load with an error message about loading shop data, which I have pasted below in full. In brief: "Caused by: java.lang.IllegalArgumentException: The name of the profile contains invalid characters: Warden Head"

We had no data load issues when we last ran the plugin, and our server was at 1.20.4. I have not downgraded the version of the server or Shopkeepers.

[10:02:11] [Server thread/ERROR]: [Shopkeepers] Failed to load the save file! Note: Server downgrades or manually editing the save file are not supported!
com.nisovin.shopkeepers.util.data.persistence.InvalidDataFormatException: Failed to load data as Bukkit config!
at Shopkeepers.jar/com.nisovin.shopkeepers.util.data.persistence.bukkit.BukkitConfigDataStore.loadFromString(BukkitConfigDataStore.java:63) ~[Shopkeepers.jar:?]
at Shopkeepers.jar/com.nisovin.shopkeepers.util.data.persistence.DataStoreBase.load(DataStoreBase.java:57) ~[Shopkeepers.jar:?]
at Shopkeepers.jar/com.nisovin.shopkeepers.storage.SKShopkeeperStorage.doReload(SKShopkeeperStorage.java:456) ~[Shopkeepers.jar:?]
at Shopkeepers.jar/com.nisovin.shopkeepers.storage.SKShopkeeperStorage.reload(SKShopkeeperStorage.java:408) ~[Shopkeepers.jar:?]
at Shopkeepers.jar/com.nisovin.shopkeepers.SKShopkeepersPlugin.onEnable(SKShopkeepersPlugin.java:423) ~[Shopkeepers.jar:?]
at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:288) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at io.papermc.paper.plugin.manager.PaperPluginInstanceManager.enablePlugin(PaperPluginInstanceManager.java:202) ~[paper-1.21.jar:1.21-108-73a863b]
at io.papermc.paper.plugin.manager.PaperPluginManagerImpl.enablePlugin(PaperPluginManagerImpl.java:109) ~[paper-1.21.jar:1.21-108-73a863b]
at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:520) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.bukkit.craftbukkit.CraftServer.enablePlugin(CraftServer.java:640) ~[paper-1.21.jar:1.21-108-73a863b]
at org.bukkit.craftbukkit.CraftServer.enablePlugins(CraftServer.java:589) ~[paper-1.21.jar:1.21-108-73a863b]
at net.minecraft.server.MinecraftServer.loadWorld0(MinecraftServer.java:753) ~[paper-1.21.jar:1.21-108-73a863b]
at net.minecraft.server.MinecraftServer.loadLevel(MinecraftServer.java:515) ~[paper-1.21.jar:1.21-108-73a863b]
at net.minecraft.server.dedicated.DedicatedServer.initServer(DedicatedServer.java:329) ~[paper-1.21.jar:1.21-108-73a863b]
at net.minecraft.server.MinecraftServer.runServer(MinecraftServer.java:1214) ~[paper-1.21.jar:1.21-108-73a863b]
at net.minecraft.server.MinecraftServer.lambda$spin$0(MinecraftServer.java:330) ~[paper-1.21.jar:1.21-108-73a863b]
at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]
Caused by: org.bukkit.configuration.InvalidConfigurationException: Could not call method 'public static com.destroystokyo.paper.profile.CraftPlayerProfile com.destroystokyo.paper.profile.CraftPlayerProfile.deserialize(java.util.Map)' of class com.destroystokyo.paper.profile.CraftPlayerProfile for deserialization
at Shopkeepers.jar/com.nisovin.shopkeepers.util.bukkit.ConfigUtils.loadConfigSafely(ConfigUtils.java:246) ~[Shopkeepers.jar:?]
at Shopkeepers.jar/com.nisovin.shopkeepers.util.data.persistence.bukkit.BukkitConfigDataStore.loadFromString(BukkitConfigDataStore.java:61) ~[Shopkeepers.jar:?]
... 16 more
Caused by: java.lang.IllegalArgumentException: The name of the profile contains invalid characters: Warden Head
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:218) ~[guava-32.1.2-jre.jar:?]
at com.destroystokyo.paper.profile.CraftPlayerProfile.createAuthLibProfile(CraftPlayerProfile.java:274) ~[paper-1.21.jar:1.21-108-73a863b]
at com.destroystokyo.paper.profile.CraftPlayerProfile.(CraftPlayerProfile.java:41) ~[paper-1.21.jar:1.21-108-73a863b]
at com.destroystokyo.paper.profile.CraftPlayerProfile.deserialize(CraftPlayerProfile.java:333) ~[paper-1.21.jar:1.21-108-73a863b]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[?:?]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[?:?]
at org.bukkit.configuration.serialization.ConfigurationSerialization.deserializeViaMethod(ConfigurationSerialization.java:87) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.bukkit.configuration.serialization.ConfigurationSerialization.deserialize(ConfigurationSerialization.java:129) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.bukkit.configuration.serialization.ConfigurationSerialization.deserializeObject(ConfigurationSerialization.java:209) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.bukkit.configuration.file.YamlConstructor$ConstructCustomObject.construct(YamlConstructor.java:58) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:264) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:247) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:576) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:210) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:552) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:597) ~[snakeyaml-2.2.jar:?]
at org.bukkit.configuration.file.YamlConstructor$ConstructCustomObject.construct(YamlConstructor.java:49) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:264) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:247) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping2ndStep(BaseConstructor.java:576) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.SafeConstructor.constructMapping2ndStep(SafeConstructor.java:210) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructMapping(BaseConstructor.java:552) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.SafeConstructor$ConstructYamlMap.construct(SafeConstructor.java:597) ~[snakeyaml-2.2.jar:?]
at org.bukkit.configuration.file.YamlConstructor$ConstructCustomObject.construct(YamlConstructor.java:49) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObjectNoCheck(BaseConstructor.java:264) ~[snakeyaml-2.2.jar:?]
at org.yaml.snakeyaml.constructor.BaseConstructor.constructObject(BaseConstructor.java:247) ~[snakeyaml-2.2.jar:?]
at org.bukkit.configuration.file.YamlConstructor.construct(YamlConstructor.java:37) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.bukkit.configuration.file.YamlConfiguration.fromNodeTree(YamlConfiguration.java:163) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.bukkit.configuration.file.YamlConfiguration.fromNodeTree(YamlConfiguration.java:161) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.bukkit.configuration.file.YamlConfiguration.fromNodeTree(YamlConfiguration.java:161) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.bukkit.configuration.file.YamlConfiguration.fromNodeTree(YamlConfiguration.java:161) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at org.bukkit.configuration.file.YamlConfiguration.loadFromString(YamlConfiguration.java:120) ~[paper-api-1.21-R0.1-SNAPSHOT.jar:?]
at Shopkeepers.jar/com.nisovin.shopkeepers.util.bukkit.ConfigUtils.loadConfigSafely(ConfigUtils.java:239) ~[Shopkeepers.jar:?]
at Shopkeepers.jar/com.nisovin.shopkeepers.util.data.persistence.bukkit.BukkitConfigDataStore.loadFromString(BukkitConfigDataStore.java:61) ~[Shopkeepers.jar:?]
... 16 more
[10:02:11] [Server thread/ERROR]: [Shopkeepers] Detected an issue during the loading of the saved shopkeepers data! Disabling the plugin!
[10:02:11] [Server thread/INFO]: [Shopkeepers] Disabling Shopkeepers v2.22.3

commented

Minecraft and also the Paper server are more strict now regarding custom profile data on head items. The profile name has to adhere to the requirements of Minecraft usernames now (limited length and limited set of allowed characters). On Paper, the item fails to load, on Spigot, it currently loads but then crashes players when they open the shop.
The usual approach of loading the item and then passing it through Minecraft's data migrations doesn't seem to work, neither on Paper nor on Spigot currently. I still need to further investigate this.

There is no automatic migration path for this currently. The quickest solution would be to match the profile name property via a regex and replace all names, e.g. with an empty string. I think this is also how Minecraft itself has migrated previous custom head items. See also my comment here: https://www.spigotmc.org/threads/shopkeepers.447969/page-18#post-4759335
If you want to migrate these items to something nicer, e.g. preserve the name in some form, you would need to be more sophisticated.

Edit: I looked into this a bit more:

  • Minecraft itself simply cleared any invalid profile names of items inside player inventories, i.e. name: ''
  • Minecraft's check for what it considers an invalid name during the migration:
    private static boolean isValidPlayerName(String s) {
          return s.length() > 16 ? false : s.chars().filter((i) -> {
              return i <= 32 || i >= 127;
          }).findAny().isEmpty();
    }
    
  • My current guess as to why Shopkeeper's usual item migration (deserializing the item and then passing it through Minecraft's data migration) did not work in this case:
    • On Paper: The invalid profile name is already rejected during the deserialization.
    • On Spigot: The deserialized item already has the new "component" structure. But the Minecraft data fixers look for a pattern matching the old "tag" structure. So the data fixes does not match the data.