You cannot offer a Key unless a DataManipulator using that key is offered first.
Without a DataManipulator, the values cannot be serialized. So you need to offer a new WarpData first so that Sponge knows which keys it may accept for that DataHolder.
[13:12:23] [Server thread/INFO] [STDOUT]: [com.blocklaunch.blwarps.eventhandlers.ChangeSignEventHandler:signChange:44]: home
[13:12:23] [Server thread/ERROR] [Sponge]: Could not pass ChangeSignEvent$Impl to Plugin{id=blwarps, name=BLWarps, version=1.2.6}
java.lang.NullPointerException
at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:213) ~[minecraft_server.1.8.jar:?]
at org.spongepowered.api.data.manipulator.mutable.common.AbstractData.getValues(AbstractData.java:182) ~[AbstractData.class:1.8-1577-3.1.0-BETA-1025]
at net.minecraft.tileentity.TileEntity.offerCustom(TileEntity.java:69) ~[bcm.class:?]
at net.minecraft.tileentity.TileEntity.offer(TileEntity.java:110) ~[bcm.class:?]
at net.minecraft.tileentity.TileEntity.offer(TileEntity.java:50) ~[bcm.class:?]
at org.spongepowered.api.data.value.mutable.CompositeValueStore.offer(CompositeValueStore.java:209) ~[CompositeValueStore.class:1.8-1577-3.1.0-BETA-1025]
at com.blocklaunch.blwarps.eventhandlers.ChangeSignEventHandler.signChange(ChangeSignEventHandler.java:45) ~[ChangeSignEventHandler.class:?]
at org.spongepowered.common.event.listener.ChangeSignEventListener_ChangeSignEventHandler_signChange5.handle(Unknown Source) ~[?:?]
at org.spongepowered.common.event.RegisteredListener.handle(RegisteredListener.java:86) ~[RegisteredListener.class:1.8-1577-3.1.0-BETA-1025]
at org.spongepowered.mod.event.SpongeModEventManager.post(SpongeModEventManager.java:233) [SpongeModEventManager.class:1.8-1577-3.1.0-BETA-1025]
at org.spongepowered.mod.event.SpongeModEventManager.post(SpongeModEventManager.java:277) [SpongeModEventManager.class:1.8-1577-3.1.0-BETA-1025]
at org.spongepowered.mod.event.SpongeModEventManager.post(SpongeModEventManager.java:245) [SpongeModEventManager.class:1.8-1577-3.1.0-BETA-1025]
at org.spongepowered.common.SpongeImpl.postEvent(SpongeImpl.java:117) [SpongeImpl.class:1.8-1577-3.1.0-BETA-1025]
at net.minecraft.network.NetHandlerPlayServer.callSignChangeEvent(NetHandlerPlayServer.java:199) [rj.class:?]
at net.minecraft.network.NetHandlerPlayServer.func_147343_a(NetHandlerPlayServer.java:1113) [rj.class:?]
at net.minecraft.network.play.client.C12PacketUpdateSign.func_148833_a(C12PacketUpdateSign.java:51) [mu.class:?]
at net.minecraft.network.play.client.C12PacketUpdateSign.func_148833_a(C12PacketUpdateSign.java:66) [mu.class:?]
at net.minecraft.network.PacketThreadUtil$1.onProcessPacket(SourceFile:110) [ih.class:?]
at net.minecraft.network.PacketThreadUtil$1.run(SourceFile:13) [ih.class:?]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_45-internal]
at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_45-internal]
at net.minecraftforge.fml.common.FMLCommonHandler.callFuture(FMLCommonHandler.java:714) [FMLCommonHandler.class:?]
at net.minecraft.server.MinecraftServer.func_71190_q(MinecraftServer.java:656) [MinecraftServer.class:?]
at net.minecraft.server.dedicated.DedicatedServer.func_71190_q(DedicatedServer.java:364) [po.class:?]
at net.minecraft.server.MinecraftServer.func_71217_p(MinecraftServer.java:598) [MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:478) [MinecraftServer.class:?]
at java.lang.Thread.run(Thread.java:745) [?:1.8.0_45-internal]
Just to make sure my Warp wasnāt null, I printed out its name (first line in the console output), and it correctly output its name (āhomeā).
Just saying, make absolutely sure that youāre registering everything. If Warp is supposed to be DataSerializable, then make it DataSerializable, otherwise the custom data doesnāt make sense to be stored to entities/tileentities/itemstacks. Having that said, itās very well possible that thereās some TileEntity bug somewhere that I need to track, but as @gravityfox said earlier, ItemStacks should be working perfectly now.
@gabizou
I was reading this thread, which is basically the same issue as mine. Why would Warp need to implement DataSerializable? If I print out my WarpData#toContainer, I get:
which seems to be working fine. Granted, I do get this exception:
[17:07:28] [Server thread/ERROR] [FML]: A TileEntity type net.minecraft.tileentity.TileEntitySign has throw an exception trying to write state. It will not persist. Report this to the mod author
java.lang.IllegalArgumentException: Unable to translate object to NBTBase!
at org.spongepowered.common.util.persistence.NbtTranslator.getBaseFromObject(NbtTranslator.java:166) ~[NbtTranslator.class:1.8-1577-3.1.0-BETA-1025]
at org.spongepowered.common.util.persistence.NbtTranslator.getBaseFromObject(NbtTranslator.java:152) ~[NbtTranslator.class:1.8-1577-3.1.0-BETA-1025]
at org.spongepowered.common.util.persistence.NbtTranslator.containerToCompound(NbtTranslator.java:91) ~[NbtTranslator.class:1.8-1577-3.1.0-BETA-1025]
at org.spongepowered.common.util.persistence.NbtTranslator.containerToCompound(NbtTranslator.java:72) ~[NbtTranslator.class:1.8-1577-3.1.0-BETA-1025]
at org.spongepowered.common.util.persistence.NbtTranslator.translateData(NbtTranslator.java:285) ~[NbtTranslator.class:1.8-1577-3.1.0-BETA-1025]
at net.minecraft.tileentity.TileEntity.writeToNbt(TileEntity.java:234) ~[bcm.class:?]
at net.minecraft.tileentity.TileEntity.onWriteToNBT(TileEntity.java:178) ~[bcm.class:?]
at net.minecraft.tileentity.TileEntity.func_145841_b(TileEntity.java) ~[bcm.class:?]
at net.minecraft.tileentity.TileEntitySign.func_145841_b(TileEntitySign.java:35) ~[bdj.class:?]
at net.minecraft.world.chunk.storage.AnvilChunkLoader.func_75820_a(AnvilChunkLoader.java:382) [bfy.class:?]
at net.minecraft.world.chunk.storage.AnvilChunkLoader.func_75816_a(AnvilChunkLoader.java:183) [bfy.class:?]
at net.minecraft.world.gen.ChunkProviderServer.func_73242_b(ChunkProviderServer.java:246) [qs.class:?]
at net.minecraft.world.gen.ChunkProviderServer.func_73151_a(ChunkProviderServer.java:305) [qs.class:?]
at net.minecraft.world.WorldServer.func_73044_a(WorldServer.java:938) [qt.class:?]
at net.minecraft.server.MinecraftServer.func_71267_a(MinecraftServer.java:363) [MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer.func_71217_p(MinecraftServer.java:621) [MinecraftServer.class:?]
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:478) [MinecraftServer.class:?]
at java.lang.Thread.run(Thread.java:745) [?:1.8.0_45-internal]
If I do have Warp implement DataSerializable, how do I construct a DataContainer? Do I need a Key for each property of the Warp (name, x,y,z coords, etc.)?
When you add an object to a DataContainer, it tries its best to convert it into raw primitive and collection types. It cannot do this with custom data objects without being told how to, so it saves them directly to the tree. While you can see the object because of its toString method, the serializer (in this case for NBT) has no clue how to turn your Warp object into a tree of values.
By making Warp implement DataSerializable, the DataContainer can instead add your object as a tree of values, just like it did for your DataManipulator. This allows for it to be serialized to NBT, JSON, or whatever else.
Iām not certain if theyāll fix the not present, however a couple things I noticed:
You havenāt made a DataBuilder for Warp - It wasnāt mentioned in the other post, but this does the deserialization of the object, make sure you register this with DataManager.
WarpData warpData = Preconditions.checkNotNull(mergeFn).merge(copy(), from(dataHolder.toContainer()).orElse(null));
This will not work as expected. The DataHolder#toContainer() will add your data into a list of all custom datas in the resulting DataContainer, so from() will fail to work. You should get the WarpData object just like you would normally with holder.get(WarpData.class) - that way you have a default object (first param), and an original to use instead if it exists (second object).
return Optional.of(set(Keys.WARP, (Warp) container.get(Keys.WARP.getQuery()).orElse(null))); is still incorrect. You need to specifically tell the DataContainer youāre wanting to get a serializable object, with container.getSerializable(Key.WARP.getQuery(), Warp.class).
Iām confused by DataBuilder: thereās a method to build from an existing DataView, but thereās no build method to build from the member variables of the class implementing DataBuilderā¦ Should I be building from the member variables previously set from DataBuilder#from if the given DataView is null?
DataBuilder is for deserialization. Sponge has just read a tree from a config/NBT and knows that itās a serializable object, but doesnāt know how to go from a tree to that object.
So your DataBuilder should take in a DataContainer, read in all the values it needs and create the object.
Make sure that you check if all the values are there that you need - return Optional.empty() otherwise. Throw an error if something doesnāt match up (like a Map where a String should be).
In regards to from(T) - you donāt need to implement this as far as I know. This is so that it fits the interface of ResettableBuilder.