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:
`` DataManipulator``nin kendisini uygula
Implement the ImmutableDataManipulator
Bu adımlar tamamlandığında, aşağıdakiler de yapılmalıdır:
Register the Key in the
KeyRegistryModule
“DataProcessor” ni Uygulama
Implement the
ValueProcessor
for each Value being represented by theDataManipulator
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
DataManipulator
s 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 ImmutableDataHolder
s or ImmutableValue
s, 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 Key
s 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 DataProcessor
s for the same data. If vastly different DataHolder
s
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
NBTTagCompound
s 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 DataManipulator
lar 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ı Key
s 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.