DataManipulators Uygulanması

Bu, DataManipulators oluşturarak Data API uygulaması ile yardımcı olmak isteyenlere rehberlik eder. Uygulanacak DataManipulators’ın güncellenmiş bir listesi SpongeCommon Sayı # 8 <https://github.com/SpongePowered/SpongeCommon/issues/8> `_’de bulunabilir.

To fully implement a DataManipulator these steps must be followed:

  1. `` DataManipulator``nin kendisini uygula
  2. Implement the ImmutableDataManipulator

Bu adımlar tamamlandığında, aşağıdakiler de yapılmalıdır:

  1. Register the Key in the KeyRegistryModule
  2. “DataProcessor” ni Uygulama
  3. Implement the ValueProcessor for each Value being represented by the DataManipulator

Veriler bir blok için geçerliyse, bloğa birkaç yöntem karıştırılmalıdır.

Not

Bizim dokümanımızı takip ettiğinizden emin olun ../ guidelines.

The following snippet shows the imports/paths for some classes in SpongeCommon that you will need:

import org.spongepowered.common.data.DataProcessor;
import org.spongepowered.common.data.ValueProcessor;
import org.spongepowered.common.data.manipulator.immutable.entity.ImmutableSpongeHealthData;
import org.spongepowered.common.data.manipulator.mutable.common.AbstractData;
import org.spongepowered.common.data.manipulator.mutable.entity.SpongeHealthData;
import org.spongepowered.common.data.processor.common.AbstractEntityDataProcessor;
import org.spongepowered.common.util.Constants;
import org.spongepowered.common.data.util.NbtDataUtil;
import org.spongepowered.common.registry.type.data.KeyRegistryModule;

1. DataManipulator Uygulayın

The naming convention for DataManipulator implementations is the name of the interface prefixed with “Sponge”. So to implement the HealthData interface, we create a class named SpongeHealthData in the appropriate package. For implementing the DataManipulator first have it extend an appropriate abstract class from the org.spongepowered.common.data.manipulator.mutable.common package. The most generic there is AbstractData but there are also abstractions that reduce boilerplate code even more for some special cases like DataManipulators only containing a single value.

public class SpongeHealthData extends AbstractData<HealthData, ImmutableHealthData> implements HealthData {
    [...]
}

AbstractData sınıfında iki tür argüman vardır. İlki, bu sınıf tarafından uygulanan arayüz, ikincisi, karşılık gelen `` ImmutableDataManipulator`` tarafından uygulanan arayüz.

Kurucu

Çoğu durumda soyut bir `` DataManipulator`` uygularken iki kurucuya sahip olmalısınız:

  • İkinci yapıcıyı “varsayılan” değerlerle çağıran bağımsız değişkensiz (bağımsız değişken yok)
  • Desteklediği tüm değerleri alan ikinci kurucu.

İkinci kurucu gerekir

  • uygulanan arabirim için sınıf referansını ileterek, `` AbstractData`` yapıcısını çağırın.
  • geçen değerlerin geçerli olduğundan emin olun
  • `` registerGettersAndSetters () `` yöntemini çağır
import static com.google.common.base.Preconditions.checkArgument;

public class SpongeHealthData extends AbstractData<HealthData, ImmutableHealthData> implements HealthData {

    private double health;
    private double maxHealth;

    public SpongeHealthData() {
        this(20D, 20D);
    }

    public SpongeHealthData(double health, double maxHealth) {
        super(HealthData.class);
        checkArgument(maxHealth > 0);
        this.health = health;
        this.maxHealth = maxHealth;
        registerGettersAndSetters();
    }

    [...]

}

Since we know that both current health and maximum health are bounded values, we need to make sure no values outside of these bounds can be passed. To achieve this, we use guava’s Preconditions of which we import the required methods statically.

Not

Never use so-called magic values (arbitrary numbers, booleans etc.) in your code. Instead, locate the DataConstants class and use a fitting constant - or create one, if necessary.

Arabirim tarafından tanımlanan erişimciler

The interface we implement specifies some methods to access Value objects. For HealthData, those are HealthData#health() and HealthData#maxHealth(). Every call to those should yield a new Value.

public MutableBoundedValue<Double> health() {
    return SpongeValueFactory.boundedBuilder(Keys.HEALTH)
        .minimum(0)
        .maximum(this.maxHealth)
        .defaultValue(this.maxHealth)
        .actualValue(this.health)
        .build();
}

Tüyo

`` Double`` bir `` Comparable`` olduğundan, açıkça bir karşılaştırıcı belirtmemiz gerekmez.

If no current value is specified, calling BaseValue#get() on the Value returns the default value.

Kopyalama ve Seri hale getirme

The two methods DataManipulator#copy() and DataManipulator#asImmutable() are not much work to implement. For both you just need to return a mutable or an immutable data manipulator respectively, containing the same data as the current instance.

The method DataSerializable#toContainer() is used for serialization purposes. Use DataContainer#createNew() as the result and apply to it the values stored within this instance. A DataContainer is basically a map mapping DataQuerys to values. Since a Key always contains a corresponding DataQuery, just use those by passing the Key directly.

public DataContainer toContainer() {
    return super.toContainer()
        .set(Keys.HEALTH, this.health)
        .set(Keys.MAX_HEALTH, this.maxHealth);
}

almayıveayarlamayıkaydet()

`` DataManipulator`` ayrıca, tuşları kullanarak veri almak ve ayarlamak için yöntemler sağlar. Bunun için uygulanma, `` AbstractData`` tarafından gerçekleştirilir, ancak hangi veriye erişebileceğini ve nasıl erişebileceğini söylemeliyiz. Bu nedenle, `` registerGettersAndSetters () `` yönteminde her bir değer için aşağıdakileri yapmamız gerekir:

  • register a Supplier to directly get the value
  • register a Consumer to directly set the value
  • değiştirilebilir ‘değer’ i almak için bir ‘tedarikçi <değer>’ `i kaydettirin

“Tedarikçi” ve “Tüketici” fonksiyonel arabirimlerdir, bu nedenle Java 8 Lambda’lar kullanılabilir.

private SpongeHealthData setCurrentHealthIfValid(double value) {
    if (value >= 0 && value <= (double) Float.MAX_VALUE) {
        this.health = value;
    } else {
        throw new IllegalArgumentException("Invalid value for current health");
    }
    return this;
}

private SpongeHealthData setMaximumHealthIfValid(double value) {
    if (value >= 0 && value <= (double) Float.MAX_VALUE) {
        this.maxHealth = value;
    } else {
        throw new IllegalArgumentException("Invalid value for maximum health");
    }
    return this;
}

private void registerGettersAndSetters() {
    registerFieldGetter(Keys.HEALTH, () -> this.health);
    registerFieldSetter(Keys.HEALTH, this::setCurrentHealthIfValid);
    registerKeyValue(Keys.HEALTH, this::health);

    registerFieldGetter(Keys.MAX_HEALTH, () -> this.maxHealth);
    registerFieldSetter(Keys.MAX_HEALTH, this::setMaximumHealthIfValid);
    registerKeyValue(Keys.MAX_HEALTH, this::maxHealth);
}

The Consumer registered as field setter must perform the adequate checks to make sure the supplied value is valid. This applies especially for DataHolders which won’t accept negative values. If a value is invalid, an IllegalArgumentException should be thrown.

Tüyo

The validity criteria for those setters are the same as for the respective Value object, so you might delegate the validity check to a call of this.health().set() and just set this.health = value if the first line has not thrown an exception yet.

Bu kadar. `` DataManipulator`` şimdi yapılmalıdır.

2. ImmutableDataManipulator uygulamak

Implementing the ImmutableDataManipulator is similar to implementing the mutable one.

Tek fark:

  • Sınıf adı değiştirilebilir `` DataManipulator`` ismini `` ImmutableSponge`` ile önekleyerek oluşturulmuştur
  • Onun yerine `` ImmutableAbstractData`` dan miras bırakın
  • `` RegisterGettersAndSetters () `` yerine, yöntem `` registerGetters () ``

When creating ImmutableDataHolders or ImmutableValues, check if it makes sense to use the ImmutableDataCachingUtil. For example, if you have WetData which contains nothing more than a boolean, it is more feasible to retain only two cached instances of ImmutableWetData - one for each possible value. For manipulators and values with many possible values (like SignData) however, caching is proven to be too expensive.

Tüyo

Yanlışlıkla yapılan değişiklikleri önlemek için bir `` ImmutableDataManipulator`` alanlarını `` final`` olarak beyan etmelisiniz.

3. Register the Key in the KeyRegistryModule

The next step is to register your Keys to the Keys. To do so, locate the KeyRegistryModule class and find the registerDefaults() method. There add a line to register (and create) your used keys.

import static org.spongepowered.api.data.DataQuery.of;

this.register("health", Key.builder()
        .type(TypeTokens.BOUNDED_DOUBLE_VALUE_TOKEN)
        .id("health")
        .name("Health")
        .query(of("Health"))
        .build());
this.register("max_health", Key.builder()
        .type(TypeTokens.BOUNDED_DOUBLE_VALUE_TOKEN)
        .id("max_health")
        .name("Max Health")
        .query(of("MaxHealth"))
        .build());

The register(Key) method registers your Keys for later use. The string used for the id should be the corresponding constant name from the Keys utility class in lowercase. The Key itself is created by using the Key.Builder provided by the Key#builder() method. You have to set a TypeToken, an id, human readable name and a DataQuery. The DataQuery is used for serialization. It is created from the statically imported DataQuery.of() method accepting a string. This string should also be the constant name, stripped of underscores and capitalization changed to upper camel case.

4. Veri İşlemcilerini Uygulama

Sıradaki, “DataProcessor” dir. “DataProcessor”, “DataManipulator” ve Minecraft nesneleri arasında bir köprü görevi görür. Vanilla Minecraft’da bulunan “DataHolder’lar” dan talep edilen veya teklif edilen her çağrı, “DataProcessor” ya da “ValueProcessor” ‘a devredilir.

For your name, you should use the name of the DataManipulator interface and append Processor. Thus, for HealthData we create a HealthDataProcessor.

Hazır kaynak kodunu azaltmak için, “DataProcessor”, “org.spongepowered.common.data.processor.common” paketindeki uygun soyut sınıftan miras alınmalıdır. Sağlık yalnızca belirli varlıklar üzerinde mevcut olabileceği için, “net.minecraft.entity.Entity” ‘ye dayanarak “Entities” e özel olarak hedeflenen “AbstractEntityDataProcessor”’ u kullanabiliriz. `` AbstractEntitySingleDataProcessor`` daha az uygulama gerektirir ancak `` HealthData`` sadece bir değerden fazlasını içerdiği için kullanılamaz.

public class HealthDataProcessor
        extends AbstractEntityDataProcessor<EntityLivingBase, HealthData, ImmutableHealthData> {

    public HealthDataProcessor() {
        super(EntityLivingBase.class);
    }

    [...]

}

Hangi soyutlamayı kullandığınıza bağlı olarak, uygulamak zorunda olduğunuz yöntemler, soyut sınıfta ne kadar çok uygulama işi yapılabileceğine bağlı olarak büyük ölçüde farklılık gösterebilir. Genellikle, yöntemler kategorize edilebilir.

Tüyo

It is possible to create multiple DataProcessors for the same data. If vastly different DataHolders should be supported (for example both a TileEntity and a matching ItemStack), it may be beneficial to create one processor for each type of DataHolder in order to make full use of the provided abstractions. Make sure you follow the package structure for items, tileentities and entities.

Doğrulama Yöntemleri

Always return a boolean value. If any of the supports(target) methods is called it should perform a general check if the supplied target generally supports the kind of data handled by our DataProcessor. Based on your level of abstraction you might not have to implement it at all, if you have to just implement the most specific one, as the more generic ones usually delegate to them.

Hi everybody

Bunun yerine bir doesDataExist() yöntemi sağlamamız gerekiyor. Soyutlama, verilerin nasıl elde edileceğini bilmediği için bu işlevin uygulanmasına izin verir. İsmin de belirttiği gibi, yöntem desteklenen hedefle verilerin halihazırda olup olmadığını kontrol etmeli. HealthDataProcessor için bu her zaman doğru olur, çünkü her canlı varlığın sağlığı olduğu için.

@Override
protected boolean doesDataExist(EntityLivingBase entity) {
    return true;
}

Ayarlayıcı Yöntemleri

Bu ayarlayıcı yöntem bir tür DataHolder ve mümkünse ona uygulanması gereken bazı veriler alır.

‘’ DataProcessor’’ ara yüzü, bir ‘’DataHolder’’ alan bir ‘’ set() ‘’ yöntemi ve ‘’ DataTransactionResult’’ döndüren bir ‘’DataManipulator’’ tanımlar. Kullanılan soyutlama sınıfına bağlı olarak, gerekli işlevlerin bazıları zaten uygulanmış olabilir.

Bu durumda AbstractEntityDataProcessor çoğu şeyi halleder ve başarılı ise bazı değerleri true ya çevirmek için ve başarılı değilse false a çevirmek için sadece bir metoda ihtiyaç duyar. DataHolder``ın "Data"yı destekleyip desteklemediğine dair tüm kontroller halledilir, soyut sınıf her bir ``Key dosyasını DataManipulator``deki değeri ile eşleyerek sadece bir eşlemi geçecek ve sonra işlemin başarılı olup olmadığına bağlı olarak bir ``DataTransactionResult oluşturacak.

@Override
protected boolean set(EntityLivingBase entity, Map<Key<?>, Object> keyValues) {
    entity.getEntityAttribute(SharedMonsterAttributes.MAX_HEALTH)
            .setBaseValue(((Double) keyValues.get(Keys.MAX_HEALTH)).floatValue());
    float health = ((Double) keyValues.get(Keys.HEALTH)).floatValue();
    entity.setHealth(health);
    return true;
}

Tüyo

To understand DataTransactionResults, check the corresponding docs page and refer to the DataTransactionResult.Builder docs to create one.

Uyarı

Especially when working with ItemStacks it is likely that you will need to deal with NBTTagCompounds directly. Many NBT keys are already defined as constants in the NbtDataUtil class. If your required key is not there, you need to add it in order to avoid ‘magic values’ in the code.

Kaldırma yöntemi

‘’ Remove () ‘’ yöntemi, verileri ‘’ DataHolder ‘’ alanından kaldırmaya çalışır ve bir ‘’ DataTrans actionResult ‘’ döndürür.

Removal is not abstracted in any abstract DataProcessor as the abstractions have no way of knowing if the data is always present on a compatible DataHolder (like WetData or HealthData) or if it may or may not be present (like LoreData). If the data is always present, remove() must always fail. If it may or may not be present, remove() should remove it.

Since a living entity always has health, HealthData is always present and removal therefore not supported. Therefore we just return DataTransactionResult#failNoData().

@Override
public DataTransactionResult remove(DataHolder dataHolder) {
    return DataTransactionResult.failNoData();
}

Getter Yöntemleri

Getter yöntemleri, bir “DataHolder” den veri elde eder ve isteğe bağlı bir “DataManipulator” i döndürür.’’ DataProcessor ‘’ arabirimi, ‘’ from() ‘’ ve ‘’ createFrom() ‘’ yöntemlerini belirtir, fark ‘’ from() ‘’ ‘’ Optional.empty() ‘’ veri sahibi uyumlu ancak şu anki verileri içermiyor; buna karşın, ‘’ createFrom() ‘’ bu durumda varsayılan değerleri taşıyan bir “DataManipulator” sağlayacak.

Yine, ‘’ AbstractEntityDataProcessor ‘’ bunun için uygulamanın çoğunu sağlayacak ve yalnızca ‘’ DataHolder ‘’üzerinde fiili değerleri elde etmek için bir yöntem gerektiriyor. Bu yöntem yalnızca, ‘’ supports() ‘’ ve ‘’ doDataExist() ‘’ den sonra çağrılır, her ikisi de doğrudur, yani verilerin bulunduğu varsayımı altında çalışır.

Uyarı

If the data may not always exist on the target DataHolder, e.g. if the remove() function may be successful (see above), it is imperative that you implement the doesDataExist() method so that it returns true if the data is present and false if it is not.

@Override
protected Map<Key<?>, ?> getValues(EntityLivingBase entity) {
    final double health = entity.getHealth();
    final double maxHealth = entity.getMaxHealth();
    return ImmutableMap.of(Keys.HEALTH, health, Keys.MAX_HEALTH, maxHealth);
}

Dolgu Yöntemleri

Bir dolgu yöntemi, bir dolgu metodundan farklıdır; bu değerlerle doldurmak için bir “DataManipulator” alır. Bu değerler bir “DataHolder” dan gelebilir veya “DataContainer” den deserialize edilmelidir. Yöntem ‘’ DataHolder ‘’ uyumsuzsa ‘’ Optional.empty () ‘’ döner.

AbstractEntityDataProcessor already handles filling from DataHolders by creating a DataManipulator from the holder and then merging it with the supplied manipulator, but the DataContainer deserialization it cannot provide.

@Override
public Optional<HealthData> fill(DataContainer container, HealthData healthData) {
    if (!container.contains(Keys.MAX_HEALTH.getQuery()) || !container.contains(Keys.HEALTH.getQuery())) {
        return Optional.empty();
    }
    healthData.set(Keys.MAX_HEALTH, getData(container, Keys.MAX_HEALTH));
    healthData.set(Keys.HEALTH, getData(container, Keys.HEALTH));
    return Optional.of(healthData);
}

The fill() method is to return an Optional of the altered healthData, if and only if all required data could be obtained from the DataContainer.

Diğer yöntemler

Kullanılan soyut üst sınıfa bağlı olarak, bazı diğer yöntemler gerekebilir. Örneğin, `` AbstractEntityDataProcessor`` çeşitli noktalarda `` DataManipulator`` örnekleri oluşturmalıdır. Ne uygulama sınıfı ne de yapıcı kullanılacağını bildiği için bunu yapamazsınız. Bu nedenle, uygulama tarafından sağlanması gereken soyut bir işlevi kullanır. Bu, varsayılan veriyle bir `` DataManipulator`` yaratmaktan başka bir şey yapmaz.

``DataManipulator``ınızı önerilen olarak yerine getirirseniz, sadece no-args ustası kullanabilirsiniz.

@Override
protected HealthData createManipulator() {
    return new SpongeHealthData();
}

5. Değer İşlemlerini uygula

Yalnızca bir ‘’ DataManipulator ‘’, bir ‘’ DataHolder ‘’ ‘e değil, aynı zamanda anahtarlı ‘’ Value ‘’ ‘e ayrı ayrı teklif edilebilir. Dolayısıyla, ‘’ DataManipulator ‘’ de bulunan her ‘’ Key ‘’ için en az bir ‘’ ValueProcessor ‘’ vermelisiniz. ‘’ ValueProcessor ‘’, ‘’ Keys ‘’ sınıfındaki ‘’ Key ‘’ sabit isminden sonra ‘’ DataQuery ‘’ ye benzer şekilde isimlendirilir. Sabit ismin üstü deve kuşağında kullanılan alt çizgiden sıyrılır ve ardından “Değer İşlemci” ile sonlandırılır.

ValueProcessor daima DataHolder type’ına göre kontrol yapan supports()``un bir kısmını karşılamış olan ``AbstractSpongeValueProcessor ile inherit edilmelidir. Keys.HEALTH için HealthValueProcessor değerini aşağıdaki gibi oluşturmalı ve contruct etmeliyiz.

public class HealthValueProcessor
        extends AbstractSpongeValueProcessor<EntityLivingBase, Double, MutableBoundedValue<Double>> {

    public HealthValueProcessor() {
        super(EntityLivingBase.class, Keys.HEALTH);
    }

    [...]

}

Şimdi AbstractSpongeValueProcessor bizi değerin desteklenip desteklenmediğini kontrol etme gerekliliğinden kurtaracak. ValueContainer hedefi ``EntityLivingBase``in bir türü ise desteklendiğini gösterir.

Tüyo

Hangi EntityLivingBase öğelerinin desteklendiği ile ilgili daha etkili bir kontrol için, supports(EntityLivingBase) yöntemi iptal edilebilir.

Yine, çoğu işlem yardımcı uygulama tarafından tamamlandı. Bir Value ve onun değişmez kopyası verilerini almak, kaydetmek ve kaldırmak için bize iki yardımcı uygulama daha gerekiyor.

@Override
protected MutableBoundedValue<Double> constructValue(Double health) {
    return SpongeValueFactory.boundedBuilder(Keys.HEALTH)
        .minimum(0D)
        .maximum(((Float) Float.MAX_VALUE).doubleValue())
        .defaultValue(20D)
        .actualValue(health)
        .build();
}

@Override
protected ImmutableBoundedValue<Double> constructImmutableValue(Double value) {
    return constructValue(value).asImmutable();
}
@Override
protected Optional<Double> getVal(EntityLivingBase container) {
    return Optional.of((double) container.getHealth());
}

EntityLivingBase``in doğru çalışması mümkün olmadığı için, bu işlem asla geri dönmeyecektir ``Optional.empty().

@Override
protected boolean set(EntityLivingBase container, Double value) {
    if (value >= 0 && value <= (double) Float.MAX_VALUE) {
        container.setHealth(value.floatValue());
        return true;
    }
    return false;
}

set() yöntemi değerin başarıyla ayarlanıp ayarlanamayacağını belirten bir mantıksal değeri gösterecektir. Bu uygulama, yukarıda yaptığımız değerli işlem yöntemlerimiz de kullanılan sınırların dışındaki değerleri reddedecektir.

@Override
public DataTransactionResult removeFrom(ValueContainer<?> container) {
    return DataTransactionResult.failNoData();
}

Verilerin daima mevcut olması garanti edildiği için kaldırma işleminiz onaylanamaz.

6. Kayıt İşlemcileri

In order for Sponge to be able to use our manipulators and processors, we need to register them. This is done in the DataRegistrar class. In the setupSerialization() method there are two large blocks of registrations to which we add our processors.

Veri İşlemcileri

DataProcessor, DataManipulator kullanmakta olduğu arayüz ve uygulama sınıflarının yanısıra kaydedilmektedir. Her değiştirilebilen / değiştirilemeyen DataManipulatorlar için minimum bir DataProcessor kaydedilmelidir.

DataUtil.registerDataProcessorAndImpl(HealthData.class, SpongeHealthData.class,
        ImmutableHealthData.class, ImmutableSpongeHealthData.class,
        new HealthDataProcessor());

Değer İşlemcileri

Değer işlemcileri aynı işlevin en alt kısmında kayıt edilir. Her Key için birden çok işlemci registerValueProcessor() yönteminin sonraki çağrıları tarafından kaydedilir.

DataUtil.registerValueProcessor(Keys.HEALTH, new HealthValueProcessor());
DataUtil.registerValueProcessor(Keys.MAX_HEALTH, new MaxHealthValueProcessor());

Blok Veri Uygulaması

Blok verisi, bloğu kendi içine karıştırarak uygulanmasıyla diğer veri türlerinden biraz farklıdır. org.spongeprowered.mixin.core.block.MixinBlock da bloklara veri uygulamak için geçersiz sayılan birkaç yöntem bulunmaktadır.

@Mixin(BlockHorizontal.class)
public abstract class MixinBlockHorizontal extends MixinBlock {

    [...]

}

Eğer ImmutableDataManipulator değişken olarak bilinen Class dan yüklenebilir veya üst sınıfta destekleniyorsa support() true ya döndürülmelidir.

@Override
public boolean supports(Class<? extends ImmutableDataManipulator<?, ?>> immutable) {
    return super.supports(immutable) || ImmutableDirectionalData.class.isAssignableFrom(immutable);
}

‘’ getStateWithData() ‘’ kendisine uygulanan ‘’ ImmutableDataManipulator ‘’ verileriyle birlikte yeni bir ‘’ BlockState ‘’ döndürmelidir. Manipülör doğrudan desteklenmiyorsa, yöntem üst sınıfa devretmelidir.

@Override
public Optional<BlockState> getStateWithData(IBlockState blockState, ImmutableDataManipulator<?, ?> manipulator) {
    if (manipulator instanceof ImmutableDirectionalData) {
        final Direction direction = ((ImmutableDirectionalData) manipulator).direction().get();
        final EnumFacing facing = DirectionResolver.getFor(direction);
        return Optional.of((BlockState) blockState.withProperty(BlockHorizontal.FACING, facing));
    }
    return super.getStateWithData(blockState, manipulator);
}

getStateWhithValue() getStateWithData() ile aynı, yalnız tek farkı Keys ile çalışır.

@Override
public <E> Optional<BlockState> getStateWithValue(IBlockState blockState, Key<? extends BaseValue<E>> key, E value) {
    if (key.equals(Keys.DIRECTION)) {
        final Direction direction = (Direction) value;
        final EnumFacing facing = DirectionResolver.getFor(direction);
        return Optional.of((BlockState) blockState.withProperty(BlockHorizontal.FACING, facing));
    }
    return super.getStateWithValue(blockState, key, value);
}

Son olarak, ‘’ getManipulators () ‘’, sağlanan “IBlockState” için geçerli değerlerin yanı sıra, blok destekleyen tüm “ImmutableDataManipulator” ların bir listesini döndürmelidir. Süper sınıftaki tüm ‘’ ImmutableDataManipulator ‘’ leri içermelidir.

@Override
public List<ImmutableDataManipulator<?, ?>> getManipulators(IBlockState blockState) {
    return ImmutableList.<ImmutableDataManipulator<?, ?>>builder()
            .addAll(super.getManipulators(blockState))
            .add(new ImmutableSpongeDirectionalData(DirectionResolver.getFor(blockState.getValue(BlockHorizontal.FACING))))
            .build();
}

Daha fazla bilgi

Sponge’da soyut bir kavram olan Data ile Minecraft sınıflarından kendisine gerekli verilerin nasıl bulunacağı üzerine genel talimatlar vermek zordur. Nasıl çalışması gerektiğini daha iyi anlamak için üzerinde çalışmakta olduğunuz işlemcilerle benzer şekilde önceden hazırlanmış işlemcilere göz atmanız yararlı olacaktır.

If you are stuck or are unsure about certain aspects, go visit the #spongedev IRC channel, the #dev channel on Discord, the forums, or open up an Issue on GitHub. Be sure to check the Data Processor Implementation Checklist for general contribution requirements.