이해도 | 플러그인 |
---|---|
게임버전 (JE) | 버킷 |
안녕하세요.
가상창고 플러그인을 만들면서 가상창고의 데이터를 저장하고 불러오는 코드를 작성하고 있었습니다.
Inventory.ItemStack[]을 저장하고 불러오고 싶습니다.
new ObjectMapper(new YAMLFactory()).writeValue(new File(folder.getPath()+"/"+playerName+"."+string+"."+"yaml"),this.chests.get(playerName+"."+string).getInventory().getContents());
Jackson의 ObjectMapper를 이용해서 ItemStack[]을 저장했습니다.
--- - type: "DIAMOND_PICKAXE" amount: 1 data: data: 8 itemType: "DIAMOND_PICKAXE" itemTypeId: 278 durability: 8 maxItemUseDuration: 0 maxStackSize: 1 typeId: 278 enchantments: Enchantment[32, DIG_SPEED]: 5 Enchantment[34, DURABILITY]: 3 itemMeta: displayName: null lore: null repairCost: 3 unbreakable: false localizedName: null enchants: Enchantment[32, DIG_SPEED]: 5 Enchantment[34, DURABILITY]: 3 itemFlags: [] i18NDisplayName: "Diamond Pickaxe" lore: null itemFlags: [] - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null - null
위와 같이 데이터가 저장되었습니다.
이제 이걸 불러와야했습니다.
ItemStack[] itemStacks = new ObjectMapper(new YAMLFactory()).readValue(file,ItemStack[].class);
파일을 생성했던거와 같이 ObjectMapper를 사용해서 읽으려고 했습니다.
하지만 저 부분에서 에러가 뜨더군요.
[00:58:00 WARN]: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot find a (Map) Key deserializer for type [simple type, class org.bukkit.enchantments.Enchantment]
[00:58:00 WARN]: at [Source: (File); line: 2, column: 1]
[00:58:00 WARN]: at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1615)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.deser.DeserializerCache._handleUnknownKeyDeserializer(DeserializerCache.java:602)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.deser.DeserializerCache.findKeyDeserializer(DeserializerCache.java:168)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.DeserializationContext.findKeyDeserializer(DeserializationContext.java:514)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.deser.std.MapDeserializer.createContextual(MapDeserializer.java:248)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.DeserializationContext.handlePrimaryContextualization(DeserializationContext.java:665)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.resolve(BeanDeserializerBase.java:508)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:293)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.DeserializationContext.findContextualValueDeserializer(DeserializationContext.java:458)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.deser.std.ObjectArrayDeserializer.createContextual(ObjectArrayDeserializer.java:128)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.DeserializationContext.handleSecondaryContextualization(DeserializationContext.java:696)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:496)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.ObjectMapper._findRootDeserializer(ObjectMapper.java:4669)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4478)
[00:58:00 WARN]: at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3299)
[00:58:00 WARN]: at org.tikim.minecraft.plugin.sample.manager.ChestManager.load(ChestManager.java:91)
[00:58:00 WARN]: at org.tikim.minecraft.plugin.sample.plugin.onEnable(plugin.java:26)
[00:58:00 WARN]: at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:264)
[00:58:00 WARN]: at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:316)
[00:58:00 WARN]: at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:405)
[00:58:00 WARN]: at org.bukkit.craftbukkit.v1_12_R1.CraftServer.enablePlugin(CraftServer.java:395)
[00:58:00 WARN]: at org.bukkit.craftbukkit.v1_12_R1.CraftServer.enablePlugins(CraftServer.java:344)
[00:58:00 WARN]: at net.minecraft.server.v1_12_R1.MinecraftServer.t(MinecraftServer.java:442)
[00:58:00 WARN]: at net.minecraft.server.v1_12_R1.MinecraftServer.l(MinecraftServer.java:403)
[00:58:00 WARN]: at net.minecraft.server.v1_12_R1.MinecraftServer.a(MinecraftServer.java:341)
[00:58:00 WARN]: at net.minecraft.server.v1_12_R1.DedicatedServer.init(DedicatedServer.java:289)
[00:58:00 WARN]: at net.minecraft.server.v1_12_R1.MinecraftServer.run(MinecraftServer.java:616)
[00:58:00 WARN]: at java.lang.Thread.run(Unknown Source)
에러 로그를 보니 잘 읽어볼때 형태에 문제가 있는 것 처럼 보입니다.
ObjectMapper를 Gson으로 변경해서 처리도 해보고, Json으로 처리해보기도 했지만, 실패했습니다.
뭐가 잘못된 건지 감도 잡히지 않습니다.
문제점 파악이나 다른 방식으로 파일을 저장하고 형변환 시킬 수 있는 방법이 있을까요?
냥냐챠
2020.07.24한마포 댓글 기능이 몬가 이상해서 전체가 안올라가진다냥 나눠서 올릴께냥
--
그쪽이 JSON 으로 빼내려고 하는 그 ItemStack 이라는 클래스를 구성하는 데이터 중에 JSON 으로 쉽게 빼낼 수 없는 데이터가
종종 포함되어 있어서 나는 오류다냥. 인챈트 관련 정보, 현수막 정보, 포션 정보가 ItemStack 객체에 포함되어 있으면
저런 비슷한 오류가 발생한다냥.
이런 오해는 ItemStack 클래스가 일반적인 Serializable 인터페이스를 구현하지 않는데도 serialize 라는 메소드가 존재해서
그냥 되겠지 하는 것에서 비롯된다고 본다냥.
ItemStack 은 ConfigurationSerializable 라는 버킷이 정의한 특수한 직렬화 인터페이스를 구현한다냥.
이 인터페이스는 nms 단에 속해있는 각종 nbt 데이터들을 다루기 위해서 정의된 것으로 알고있다냥.
특히 그 내부 데이터가 일반적인 json 형식이 아닌 mojang json 이라는 변종의 json 형식으로 저장하기 때문인데냥, 이것은
시간날 때 알아서 찾아봐라냥.
냥냐챠
2020.07.24결론만 말해주겠다냥. ItemStack 객체 류의 데이터를 직렬화 하려면 json 이나 yaml 형식 보다는
경험상 base64 로 인코딩된 바이트 형식으로 저장하는게 제일 빠르고 쉽다냥. 물론, 이렇게 하면 그 데이터가 무엇인지
직관적으로 알 수 없기때문에 그 부분만은 단점냥. 아래는 그 인코딩된 형식으로 데이터를 뽑아내는 방법이다냥.
냥냐챠
2020.07.241. ItemStack 객체를 직렬화 냥, 결괏값은 Map<...>
2. 1의 반환값에서 "meta" 키를 찾아라냥, 있으면 ItemMeta 인터페이스로 캐스팅하고 캐스팅한 객체에서 다시 serialize 를 호출해라냥. 그 다음에는 같은 키로 결괏값을 1의 결과물에 덮어씌워라냥. 이렇게 까지 하면 사전 준비 끝 냥.
3. ByteArrayOutputStream 와 BukkitObjectOutputStream 를 준비해라냥. BukkitObjectOutputStream 은 생성자에서 OutputStream 을 받는데 ByteArrayOutputStream 객체와 연결해서 파이프를 구성해라냥.
4. BukkitObjectOutputStream 객체의 writeObject 로 2까지의 결과물에 해당하는 변수를 집어넣고 호출해라냥.
5. BukkitObjectOutputStream 객체의 flush 메소드로 입력이 끝났음을 표시해라냥.
6. 연결된 ByteArrayOutputStream 객체에서 toByteArray 를 호출해서 지금까지의 결과물을 바이트로 반환받아라냥.
(옵션). Base64 인코더에 6의 결과물을 집어넣어서 좀더 다루기 쉬운 형태로 만들어도 된다냥.
7. 6 내지 (옵션)의 결과물을 잘 다루는 포맷으로 모아서 저장해라냥.
아래에 위와 같은 구현과 작동이 검증된 lua 코드를 첨부하겠다냥.
의사 코드로만 보고 구현은 따라서 해보는게 좋을꺼다냥
즐거운 코딩 판치 해라냥 '^'b
추신: 한마포 에디터에서 몬가 이상한 유니코드가 붙어서 댓글 등록하면 그 유니코드 문자때문에 댓글등록 거절된다냥
해결좀 해달라냥
냥냐챠
2020.07.24꼬마개발자
2020.07.24정말 감사합니다. 갓냥이 찬양해요.
말씀해주신대로 ByteArray를 Base64로 인코딩하고 쓰는 방법으로 하니 잘 동작합니다.
중간에 시행착오도 있었지만 잘 적용되네요!
ItemStack 내부에 ItemMeta같은 경우 따로 Serialize 안해줘도 동작하는 것을 확인했습니다.
감사합니다.
도움주신 결과물을 나중에 업로드해보겠습니다.
감사합니다. 갓냥이 최고야
냥냐챠
2020.07.24`^`b
도움이 됐다니 다행냥
신나는 코딩 판치해라냥
wysohn
2020.07.24궂이 따로 구현하시는 이유가 있나요? ItemStack 자체가 이미 ConfigurationSerializable 이라 바로 버킷 api 통해서 저장하실 수 있습니다
꼬마개발자
2020.07.24처음에 JSON 혹은 Yaml로 저장하려고할때 버킷 api를 통해 Serialize했으나, 로드할때 오류가 떠서 올린 에러였습니다. ByteArray를 파일로 저장하고 불러오는 작업을 진행하니 잘 동작하네요.!!