Data API - Add addtional Property to itemstack

Heyho,

i tried to use the Sponge data api to store addtional informations to an itemstack.
Unfortunately there is the following error i try to fix but nothing worked out…

I created the serializable class, the builder and the keys. I also registered the classes into the data manager. (Classes below)

Dataclass:

public class EquipmentProperty implements DataSerializable{
        public static final DataQuery EQP_ID = DataQuery.of("EQP_ID");
	public static final DataQuery EQP_NAME = DataQuery.of("EQP_NAME");
	
	
	private int id;
	private String name;
	
	
	public EquipmentProperty(int id, String name) {
		this.id = id;
		this.name = name;
	}
	
	@Override
	public int getContentVersion() {
		return EquipmentPropertyBuilder.CONENT_VERSION;
	}
	
	public String getName() {
		return name;
	}
	public int getId() {
		return id;
	}

	@Override
	public DataContainer toContainer() {
		return new MemoryDataContainer()
				.set(EQP_ID,this.id)
				.set(EQP_NAME, this.name)
				.set(Queries.CONTENT_VERSION, EquipmentPropertyBuilder.CONENT_VERSION);
	}

}

Builder-Class:

public class EquipmentPropertyBuilder extends AbstractDataBuilder<EquipmentProperty>{

	public static final int CONENT_VERSION = 1;
	
	public EquipmentPropertyBuilder() {
		super(EquipmentProperty.class, CONENT_VERSION);
	}

	@Override
	protected Optional<EquipmentProperty> buildContent(DataView container) throws InvalidDataException {
		if(!container.contains(EquipmentProperty.EQP_ID,EquipmentProperty.EQP_NAME))return Optional.empty();
		int id = container.getInt(EquipmentProperty.EQP_ID).get();
		String name = container.getString(EquipmentProperty.EQP_NAME).get();
		return Optional.of(new EquipmentProperty(id, name));
	}

}

Key:

public class Keys {
		
	public static final Key<Value<EquipmentProperty>> EQUIPMENTPROPERTIE = KeyFactory.makeSingleKey(
			 TypeToken.of(EquipmentProperty.class),
			 new TypeToken<Value<EquipmentProperty>>() {},
	        DataQuery.of("EQUIPMENTPROPERTIE"), "EQUIPMENTPROPERTIE:EQUIPMENTPROPERTIE", "EQUIPMENTPROPERTIE");	
	
}

Main:

@Listener
public void init(GameInitializationEvent event){
	
	DataManager dataManager = Sponge.getDataManager();
	dataManager.registerBuilder(EquipmentProperty.class,new EquipmentPropertyBuilder());		
}

Adding to Itemstack:

try {
	this.itemStack.tryOffer(Keys.EQUIPMENTPROPERTIE, new EquipmentProperty(1, "Test"));
 }catch(Exception e) {
	e.printStackTrace();
 }

Maybe you guys know what I am doing wrong :confused:

Thanks in advance. :slight_smile:

Greetings, Kyraja

I haven’t quite fully dived into the data API in its current form- but is there a particular rationale for using #tryOffer(...) over #offer(...)? What the try method does- is quite literally wrap the offer method in a try-catch for incompatability checks (which is the gist of what I read from the link below).

Here’s the source for #tryOffer(…).

Hey, thanks for the answer.

First i used the offer-Method but after i tried to select the value from the itemstack there were no value stored. So i used the tryOffer-Method to get the full stacktrace of the error.

But i don´t understand how i can fix the error or what´s missing ot get it work…

Greeting Kyraja

I am not sure you have actually read the documentation on how to implement the Data API.

You cannot arbitrarily attach random classes like EquipmentProperty to a DataHolder, whether they implement DataSerializable or not - your usage of DataManager is intended for mapping types from DataContainers.

You need three classes. A class implementing DataManipulator, a class implementing ImmutableDataManipulator, and a class implementing DataManipulatorBuilder. The first two should probably extend AbstractData and ImmutableAbstractData. You have the right idea with AbstractDataBuilder, but unlike the first two you still have to explicitly implement DataManipulatorBuilder on the builder class. Finally, register the whole thing in pre-init (not init) using a DataRegistration builder.

With that out of the way, some tips:

  • DataQuery naming convention is by and large PascalCase.
  • The id argument in KeyFactory methods should follow regular Minecraft ID naming conventions, i.e. plugin_id:key_id in all lowercase.
  • The name argument in KeyFactory methods is designed to be a user-friendly name, and should be written in plain English (or French or whatever, i.e. Equipment Propertie if that’s your key name)
  • You should have a different Key for each real value, i.e. a Key<Value<Integer>> EQUIPMENT_ID and a Key<Value<String>> EQUIPMENT_NAME, assuming the ID serves a separate purpose from the name. You should only use larger types for a key value if the type and key are representative of a concept, such as an Entity or Instant.
  • Change your buildContent implementation. This will get called even if no data has been saved, so if you return Optional.empty() when the DataView doesn’t contain your data, it just won’t add your data ever. You should return Optional.empty() from that method on incomplete or incorrectly formatted data, not on empty data.