개인 자료란 (JE)

  서버 커뮤니티

Profile 꼬마개발자 대표칭호 없음
Profile

질문하기 Java

[spigot] ItemStack[] serialize deserialize 질문드립니다.

2020.07.24 조회 수 794 추천 수 0
이해도 플러그인 
게임버전 (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으로 처리해보기도 했지만, 실패했습니다.

뭐가 잘못된 건지 감도 잡히지 않습니다.

문제점 파악이나 다른 방식으로 파일을 저장하고 형변환 시킬 수 있는 방법이 있을까요?





8개의 댓글

냥냐챠
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.24
@냥냐챠

1. 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

추신: 한마포 에디터에서 몬가 이상한 유니코드가 붙어서 댓글 등록하면 그 유니코드 문자때문에 댓글등록 거절된다냥

해결좀 해달라냥

@냥냐챠

6121ab5cbf6f95dbe43e59ef5f952fc2.png


꼬마개발자
2020.07.24
@냥냐챠

정말 감사합니다. 갓냥이 찬양해요.

말씀해주신대로 ByteArray를 Base64로 인코딩하고 쓰는 방법으로 하니 잘 동작합니다.

중간에 시행착오도 있었지만 잘 적용되네요!

ItemStack 내부에 ItemMeta같은 경우 따로 Serialize 안해줘도 동작하는 것을 확인했습니다.

감사합니다.

도움주신 결과물을 나중에 업로드해보겠습니다.

감사합니다. 갓냥이 최고야

냥냐챠
2020.07.24
@꼬마개발자

`^`b

도움이 됐다니 다행냥

신나는 코딩 판치해라냥

wysohn
2020.07.24

궂이 따로 구현하시는 이유가 있나요? ItemStack 자체가 이미 ConfigurationSerializable 이라 바로 버킷 api 통해서 저장하실 수 있습니다

꼬마개발자
2020.07.24
@wysohn

처음에 JSON 혹은 Yaml로 저장하려고할때 버킷 api를 통해 Serialize했으나, 로드할때 오류가 떠서 올린 에러였습니다. ByteArray를 파일로 저장하고 불러오는 작업을 진행하니 잘 동작하네요.!!