Help with Custom DataManipulators

Hello everyone! Im completly new on Sponge (I came from others APIs) and Im trying to make a Sponge version of my plugin (with Kotlin). Ive been noticed that Sponge has a Data API, then I tried to use it.
Well, I have a class called “Mana”, who have a HashMap of an Enum type and Integers, and stores all the data I need to store on a Living DataHolder.
I read A LOT the documentation, and a friend helped me (hello Eufranio), but nothing is working…

When I try

Living#Offer(Key, Mana Object).isSuccessful()

it returns “false”. I think Im doing something wrong with serialization, can anyone help me? :smile:

Classes are here:
Mana class (that I need to save on DataHolder) and get it back using DataHolder#get(Mana.KEY)

package cf.nathanpb.mysticis.core.mana
 import com.google.common.reflect.TypeToken
 import org.spongepowered.api.data.DataContainer
 import org.spongepowered.api.data.DataHolder
 import org.spongepowered.api.data.DataQuery
 import org.spongepowered.api.data.DataSerializable
 import org.spongepowered.api.data.key.Key
 import org.spongepowered.api.data.manipulator.mutable.common.AbstractData
 import org.spongepowered.api.data.merge.MergeFunction
 import org.spongepowered.api.data.value.mutable.Value
 import java.util.*
 import java.util.function.Consumer
 import java.util.function.Supplier
 import javax.swing.text.html.Option
 import kotlin.collections.HashMap
 
 class Mana : AbstractData<Mana, ImmutableMana>(), DataSerializable{
 
     companion object {
         val KEY = Key.builder()
                 .type(Gambiarra())
                 .id("mana")
                 .name("Mysticis Mana")
                 .query(DataQuery.of("Mana"))
                 .build();
         val ELEMENTS = DataQuery.of("Elements")
     }
 
     var elements : HashMap<Type, Int> = HashMap();
 
     fun set(v : Int, vararg t : Type){
         val types = if(t.isEmpty()) Type.values() else t
         types.forEach { t2 -> elements.put(t2, elements.getOrDefault(t2, 0) + v) }
     }
 
     fun add(m : Mana?){
         if(m != null) {
             m.elements.forEach { t -> elements.put(t.key, get(t.key) + t.value); }
         }
     }
 
     fun remove(m : Mana?){
         if(m != null) {
             m.reverse();
             add(m);
         }
     }
 
     fun get(key: Type): Int {
         return elements.getOrDefault(key, 0);
     }
 
     fun getNegatives() : Array<Type>{
         return copy().elements.filter { e -> e.value < 0 } .keys.toTypedArray();
     }
 
     fun reverse(){
         elements.map { e -> -e.value }
     }
 
     override fun copy() : Mana {
         val m  = Mana();
         m.elements = HashMap(elements);
         return m;
     }
 
     override fun asImmutable(): ImmutableMana {
         return ImmutableMana(this);
     }
 
     override fun getContentVersion(): Int {
         return 1;
     }
 
     override fun from(container: DataContainer?): Optional<Mana> {
         if(container != null){
             val elements = container.get(Mana.ELEMENTS)
             if(elements.isPresent){
                 return Optional.of(ImmutableMana(elements.get() as Mana).asMutable())
             }
         }
         return Optional.of(Mana())
     }
 
     override fun fill(dataHolder: DataHolder?, overlap: MergeFunction?): Optional<Mana> {
         val mana = overlap?.merge(this, dataHolder?.get(Mana::class.java)?.orElse(null))
 
         if(mana?.elements != null){
             this.elements = mana?.elements
         }
         return Optional.of(this);
     }
 
     override fun registerGettersAndSetters() {
         registerFieldGetter(Mana.KEY, Supplier { this } );
         registerFieldSetter(Mana.KEY, Consumer { this });
     }
 
     override fun toContainer(): DataContainer {
         val container =  super.toContainer()
         container.set(Mana.ELEMENTS, elements);
         return container;
     }
 
     override fun toString(): String {
         var s = "[";
         Type.values().forEach { t -> s+=t.name+":"+elements.getOrDefault(t, 0)+", " }
         s = s.substring(0, s.length-1)
         s+="]";
         return s;
     }
 
 
     enum class Type{
         ICE, FIRE, WATER, AIR, NATURE, MAGIC;
     }
 
     private class Gambiarra : TypeToken<Value<Mana>>(){}
 }This text will be hidden

ImmutableMana and ManaBuilder class:
package cf.nathanpb.mysticis.core.mana

    import org.spongepowered.api.data.DataContainer
    import org.spongepowered.api.data.manipulator.immutable.common.AbstractImmutableData
    import java.util.function.Supplier

    class ImmutableMana(template: Mana) : AbstractImmutableData<ImmutableMana, Mana>(){
        val elements : HashMap<Mana.Type, Int> = HashMap(template.elements);

        override fun asMutable(): Mana {
            val  m = Mana()
            m.elements = HashMap(elements);
            return m;
        }

        override fun getContentVersion(): Int {
            return 1;
        }

        override fun registerGetters() {
            registerFieldGetter(Mana.KEY, Supplier { this });
        }

        override fun toContainer(): DataContainer {
            return asMutable().toContainer()
        }
    }

package cf.nathanpb.mysticis.core.mana

import org.spongepowered.api.data.DataHolder
import org.spongepowered.api.data.DataView
import org.spongepowered.api.data.manipulator.DataManipulatorBuilder
import java.util.*

class ManaBuilder : DataManipulatorBuilder<Mana, ImmutableMana>{
    override fun create(): Mana {
        return Mana()
    }

    override fun createFrom(dataHolder: DataHolder?): Optional<Mana> {
        return Mana().fill(dataHolder)
    }

    override fun build(container: DataView?): Optional<Mana> {
        if(container != null){
            return Optional.of(container.get(Mana.KEY.query).orElse(Mana()) as Mana)
        }
        return Optional.of(Mana())
    }
}

And Im registering the DataManipulators with this (on Docs it says to register during the Initialization, but it only works on Pre Initialization):

        @Listener
        fun onPreInitialization(e : GamePreInitializationEvent){
            Mysticis.instance.logger.info("Plugin is Starting!")
            //instanciar
            Mana()
            DataRegistration.builder()
                    .dataClass(Mana::class.java)
                    .immutableClass(ImmutableMana::class.java)
                    .dataName(Mana.KEY.name)
                    .builder(ManaBuilder())
                    .manipulatorId(Mana.KEY.id)
                    .buildAndRegister(Sponge.getPluginManager().getPlugin("mysticis").get())
        }

And then, Im doing this:
> @Listener

fun onClick(e : InteractBlockEvent, @First p : Player){
    val m = Mana()
    m.set(100, Mana.Type.AIR, Mana.Type.FIRE)
    p.sendMessage(Text.of("Successful: "+p.offer(Mana.KEY, m).isSuccessful))
    p.sendMessage(Text.of("Has Data: "+p.get(Mana.KEY).isPresent))
    p.sendMessage(Text.of(p.get(Mana.KEY).orElse(Mana()).toString()))
}

But I got this result:

Im sorry if Im doing something stupid (like posting the wrong area)
Thanks, bye
Holy i cant format this topic

I would like to help you since i recently managed to somehow understand whats going on with datamanipulators and that shit around but i cannot read those alien kotlin runes.

1 Like

I think you need to offer the DataManipulator to the player before you can manipulate the keys.

p.offer(Mana());
p.offer(Mana.KEY, m);

Dont works :confused:
But I got some warns on console:

[23:47:15 WARN] [Sponge]: mysticis: It is no longer required to include the plugin id when specifying a Key id through Key.Builder#id. This is deprecated and may be removed later. The plugin id will be retrieved from the current PluginContainer in the cause stack.
[23:47:33 WARN]: Failed to save player data for NathanPB
[23:48:18 WARN]: Failed to save player data for NathanPB
[23:49:03 WARN]: Failed to save player data for NathanPB
[23:49:48 WARN]: Failed to save player data for NathanPB
[23:50:33 WARN]: Failed to save player data for NathanPB
[23:51:18 WARN]: Failed to save player data for NathanPB

Any idea?

You want to have a look at my code(also Kotlin):

In other plugins I have just used the constructor like you. It should work.

I tried to change some things like your code and got a new error:

java.lang.IllegalStateException: A DataManipulatorBuilder is not registered for the manipulator class: cf.nathanpb.mysticis.core.mana.Mana

Is it registered wrong?

        DataRegistration.builder()
                .dataClass(Mana::class.java)
                .immutableClass(ImmutableMana::class.java)
                .dataName(Mana.KEY.name)
                .builder(ManaBuilder())
                .manipulatorId(Mana.KEY.id)
                .buildAndRegister(Sponge.getPluginManager().getPlugin("mysticis").get())

Did you try with a new fresh world? Is that code even run?

Make sure this registration is completed in pre-initialization.