Menerapkan datamanipulator

Ini merupakan petunjuk bagi para kontributor yang ingin membantu pelaksanaan Data API dengan membuat DataManipulators. Daftar DataManipulators yang telah terupdate yang akan dijalankan bisa di temukan di 'SpongeCommon Issue #8 <https://github.com/SpongePowered/SpongeCommon/issues/8>'_.

Untuk menerapkan ' ' datamanipulator ' ' langkah-langkah ini harus diikuti:

  1. Terapkan ' ' datamanipulator ' ' sendiri

  2. Terapkan ' ' datamanipulator ' '

Bila langkah-langkah ini telah selesai, berikut juga harus dilakukan:

  1. Daftarkan kunci di dalam KunciDaftar

  2. Terapkan ' ' datamanipulator ' '

  3. Jalankan NilaiProcessor untuk tiap nilai yang telah digambarkan oleh DataManipulator

  4. Daftarkan semuanya kedalam DaftarSerialSpone

Jika data berlaku untuk blok, beberapa metode juga harus dicampur ke blok.

Catatan

Pastikan anda mengukiti ../petunjuk kami.

1. Terapkan Datamanipulator

Konvensi penamaan untuk DataManipulator implementasi adalah nama interface yang diawali dengan "Sponge". Jadi, untuk melaksanakan HealthData antarmuka, kita membuat sebuah class bernama SpongeHealthData dalam paket yang sesuai. Untuk melaksanakan DataManipulator pertama memilikinya memperpanjang sesuai kelas abstrak dari org.spongepowered.umum.data.manipulator.bisa berubah.umum paket. Yang paling umum ada AbstractData tapi ada juga abstraksi yang mengurangi kode boilerplate bahkan lebih untuk beberapa kasus khusus seperti DataManipulators hanya dapat berisi satu nilai.

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

Ada dua jenis argumen untuk AbstractData kelas. Yang pertama adalah interface yang diimplementasikan oleh kelas ini, yang kedua adalah antarmuka yang dilaksanakan oleh yang bersangkutan, ImmutableDataManipulator.

Pembuatnya

Dalam kebanyakan kasus saat menerapkan abstrak ' ' Datamanipulator ' ' kamu harus memiliki dua konstruktor:

  • Satu tanpa argumen (tidak-args) yang akan memanggil konstruktor kedua dengan "gagal" nilai

  • Konstruktor kedua yang mengambil semua nilai yang didukungnya.

Konstruktor kedua harus

  • lakukan panggilan ke AbstrakData konstruktor, melewati tahapan rujukan untuk pelaksanaan antar muka.

  • pastikan nilai yang dilewatkan benar

  • hubungi registerGettersAndSetters() metode

import static com.google.common.base.Preconditions.checkArgument;

public class SpongeHealthData {

    public SpongeHealthData() {
        this(DataConstants.DEFAULT_HEALTH, DataConstants.DEFAULT_HEALTH);
    }

    public SpongeHealthData(double currentHealth, double maxHealth) {
        super(HealthData.class);
        checkArgument(currentHealth >= DataConstants.MINIMUM_HEALTH && currentHealth <= (double) Float.MAX_VALUE);
        checkArgument(maxHealth >= DataConstants.MINIMUM_HEALTH && maxHealth <= (double) Float.MAX_VALUE);
        this.currentHealth = currentHealth;
        this.maximumHealth = maxHealth;
        this.registerGettersAndSetters();
    }

    ...

}

Karena kita tahu bahwa saat ini kesehatan dan kesehatan yang maksimal yang dibatasi nilai-nilai, kita perlu memastikan tidak ada nilai di luar batas-batas ini dapat dilewatkan. Untuk mencapai hal ini kita menggunakan jambu biji Prasyarat yang kita impor diperlukan metode.

Catatan

Tidak pernah menggunakan apa yang disebut sihir nilai-nilai (sewenang-wenang angka, boolean dll) dalam kode anda. Sebaliknya, cari org.spongepowered.umum.data.util.DataConstants kelas dan menggunakan pas konstan atau membuat satu, jika diperlukan.

Para pengakses ditentukan oleh Antarmuka

Antarmuka kami menerapkan menentukan beberapa metode untuk mengakses Nilai benda-benda. Untuk HealthData, mereka adalah kesehatan() dan maxHealth(). Setiap panggilan untuk orang-orang harus menghasilkan sebuah Nilai.

public MutableBoundedValue<Double> health() {
    return SpongeValueFactory.boundedBuilder(Keys.HEALTH)
        .minimum(DataConstants.MINIMUM_HEALTH)
        .maximum(this.maximumHealth)
        .defaultValue(this.maximumHealth)
        .actualValue(this.currentHealth)
        .build();
}

Tip

Karena Double adalah Comparable, kita tidak perlu secara eksplisit menentukan komparator.

Jika tidak ada nilai yang telah di tentukan, panggil get() pada Value mengembalikan nilai default.

Penyalinan dan serialisasi

Dua metode copy() dan asImmutable() tidak banyak pekerjaan yang harus melaksanakan. Untuk investasi anda hanya perlu untuk kembali bisa berubah atau berubah data manipulator masing-masing, yang berisi data yang sama sebagai contoh saat ini.

Metode toContainer() digunakan untuk serialisasi tujuan. Menggunakan MemoryDataContainer sebagai hasil dan menerapkan nilai-nilai yang disimpan dalam contoh ini. DataContainer pada dasarnya adalah sebuah peta pemetaan DataQuerys untuk nilai-nilai. Sejak a Kunci selalu berisi sesuai DataQuery, hanya digunakan oleh orang-orang yang lewat Kunci ini diatas.

public DataContainer toContainer() {
    return new MemoryDataContainer()
        .set(Keys.HEALTH, this.currentHealth)
        .set(Keys.MAX_HEALTH, this.maximumHealth);
}

registerGettersAndSetters()

Metode toContainer() digunakan untuk serialisasi tujuan. Menggunakan MemoryDataContainer sebagai hasil dan menerapkan nilai-nilai yang disimpan dalam contoh ini. DataContainer pada dasarnya adalah sebuah peta pemetaan DataQuerys untuk nilai-nilai. Sejak a Kunci selalu berisi sesuai DataQuery, hanya digunakan oleh orang-orang yang lewat Kunci ini diatas:

  • daftarkan Supplier untuk langsung mendapatkan nilai

  • daftarkan Consumer untuk langsung mengeset nilai

  • daftarkan Supplier<Value> untuk mendapatkan Nilai yang berubah-ubah

Supplier dan Consumer merupakan penghubung fungsional, sehingga Java 8 Lambdas dapat digunakan.

private void setCurrentHealthIfValid(double value) {
    if (value >= DataConstants.MINIMUM_HEALTH && value <= (double) Float.MAX_VALUE) {
        this.currentHealth = value;
    } else {
        throw new IllegalArgumentException("Invalid value for current health");
    }
}

private void setMaximumHealthIfValid(double value) {
    if (value >= DataConstants.MINIMUM_HEALTH && value <= (double) Float.MAX_VALUE) {
        this.maximumHealth = value;
    } else {
        throw new IllegalArgumentException("Invalid value for maximum health");
    }

}

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

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

Konsumen terdaftar sebagai bidang setter harus melakukan yang memadai pemeriksaan untuk memastikan diberikan nilai yang berlaku. Hal ini berlaku terutama untuk DataHolder`s yang tidak bisa menerima nilai-nilai negatif. Jika nilai yang tidak valid, `IllegalArgumentException harus dibuang.

Tip

Validitas kriteria bagi mereka setter adalah sama seperti untuk masing-masing Nilai objek, sehingga anda bisa mendelegasikan validitas cek ke call of ini.kesehatan().set() dan set ini.currentHealth = nilai jika baris pertama tidak memiliki pengecualian dilemparkan belum.

Itu dia, ' ' Datamanipulator ' ' harus dilakukan sekarang.

2. Terapkan ImmutableDataManipulator

Menjalankan ImmutableDataManipulator sama dengan menjalankan data yang bisa berubah.

Satu-satunya perbedaan adalah:

  • Nama kelas dibentuk oleh awalan nama DataManipulators yang bisa dapat diubah dengan ImmutableSponge

  • Mewarisi dari ImmutableAbstractData sebagai gantinya

  • Ganti dari registerGettersAndSetters(), metode ini disebut registerGetters()

Ketika menciptakan ImmutableDataHolders atau ImmutableValues, memeriksa apakah itu masuk akal untuk menggunakan ImmutableDataCachingUtil. Misalnya jika anda memiliki WetData yang berisi tidak lebih dari sebuah boolean, itu lebih layak untuk mempertahankan hanya dua cache contoh ImmutableWetData - satu untuk setiap kemungkinan nilai. Untuk manipulator dan nilai-nilai dengan banyak nilai yang mungkin (seperti SignData) namun, caching ini terbukti terlalu mahal.

Tip

Anda harus mendeklarasikan bagian dari ImmutableDataManipulator sebagai final untuk mencegah perubahan yang tidak disengaja.

3. Daftarkan Key di KeyRegistry

Langkah berikutnya adalah untuk mendaftar anda Kuncis KeyRegistry. Untuk melakukannya, cari org.spongepowered.umum.data.kunci.KeyRegistry kelas dan menemukan statis generateKeyMap() fungsi. Ada tambahkan baris untuk mendaftar (dan membuat) digunakan tombol.

keyMap.put("health"), makeSingleKey(Double.class, MutableBoundedValue.class, of("Health")));
keyMap.put("max_health", makeSingleKey(Double.class, MutableBoundedValue.class, of("MaxHealth")));

Peta peta string untuk Kuncis. String yang digunakan harus sesuai nama konstan dari Kunci utilitas kelas dalam huruf kecil. Kunci itu sendiri dibuat oleh salah satu metode statis yang diberikan oleh KeyFactory, dalam kebanyakan kasus makeSingleKey. makeSingleKey membutuhkan pertama kelas acuan bagi data yang mendasar, yang dalam kasus kita adalah "Ganda", maka kelas referensi untuk Value type yang digunakan. Argumen ketiga adalah DataQuery digunakan untuk serialisasi. Hal ini dibuat dari..... diimpor DataQuery.() metode menerima string. String ini juga harus konstan nama, dilucuti dari garis bawah dan kapitalisasi berubah ke atas unta kasus.

4. Terapkan DataProcessors

Berikutnya adalah DataProcessor. DataProcessor berfungsi sebagai jembatan antara kita DataManipulator dan Minecraft benda-benda. Setiap kali ada data yang diminta atau ditawarkan untuk DataHolders yang ada di Minecraft Vanilla, panggilan-panggilan itu berakhir menjadi didelegasikan ke DataProcessor atau ValueProcessor.

Untuk nama anda, anda harus menggunakan nama DataManipulator penghubung dan menambahkan Processor. Untuk HealthData kami membuat HealthDataProcessor.

Dalam rangka untuk mengurangi kode boilerplate, DataProcessor harus mewarisi dari yang sesuai kelas abstrak dalam org.spongepowered.umum.data.prosesor.umum paket. Karena kesehatan hanya dapat hadir pada badan-badan tertentu, kita dapat menggunakan AbstractEntityDataProcessor yang khusus ditujukan pada Entitas yang didasarkan pada net.minecraft.entitas.Entitas. AbstractEntitySingleDataProcessor akan memerlukan waktu kurang pelaksanaan pekerjaan, tetapi tidak dapat digunakan sebagai HealthData berisi lebih dari satu nilai.

public class HealthDataProcessor extends AbstractEntityDataProcessor<EntityLivingBase, HealthData, ImmutableHealthData> {
    public HealthDataProcessor() {
        super(EntityLivingBase.class);
    }
    [...]
}

Tergantung pada abstraksi yang anda gunakan, metode yang anda harus menerapkan mungkin sangat berbeda, tergantung pada seberapa jauh pelaksanaan pekerjaan sudah bisa dilakukan di kelas abstrak. Umumnya, metode-metode yang dapat dikategorikan.

Tip

Hal ini dimungkinkan untuk membuat beberapa DataProcessors untuk data yang sama. Jika sangat berbeda DataHolders harus didukung (misalnya kedua TileEntity dan pencocokan ItemStack), mungkin akan bermanfaat untuk membuat satu prosesor untuk setiap jenis DataHolder dalam rangka untuk membuat penuh penggunaan abstraksi yang disediakan. Pastikan anda mengikuti paket struktur untuk barang-barang, tileentities dan badan.

Metode validasi

Selalu kembalikan nilai boolean. Jika metode tersebut disebut supports() metode tersebut seharusnya melakukan pengecekan umum jika target yang diberikan biasanya mendukung jenis data yang ditangani oleh DataProcessor.

Untuk HealthDataProcessor kami Support() dijalankan oleh AbstractEntityDataProcessor. Per default, hal tersebut akan kembali benar jika argumen yang diberikan merupakan turunan dari kelas yang telah ditentukan saat memanggil super() konstruktor.

Sebagai gantinya, kita diminta untuk menyediakan metode doesDataExist(). Karena abstraksi tidak tahu bagaimana mendapatkan data, hal tersebut membiarkan fungsi ini dijalankan. Seperti nama nya, metode tersebut harus memeriksa apakah data sudah ada pada target yang didukung. Untuk HealthDataProcessor, hal ini selalu kembali benar, karena setiap entitas yang hidup selalu memiliki kesehatan.

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

Metode Pengaturan

Metode pengaturan menerima DataHolder dari beberapa jenis dan beberapa data yang harus dijalankan padanya, jika memungkinkan.

Antarmuka DataProcessor mendefinisikan sebuah set() metode menerima``DataHolder`` dan DataManipulator yang mengembalikan DataTransactionResult. Bergantung pada kelas abstraksi yang digunakan, beberapa fungsi yang diperlukan mungkin sudah diterapkan.

Pada kasus ini, AbstractEntitydataProcessor mengurus sebagian besar proses dan hanya membutuhkan sebuah metode untuk mengeset sebagian nilai agar kembali true jika metode tersebut sukses dijalankan dan false jika tidak sukses. Semua pengecekan jika DataHolder mendukung Data akan ditangani, kelas abstrak hanya akan memberikan peta tiap Key dari DataManipulator ke nilainya dan kemudian menyusun DataTransactionResult tergantung apakah operasi berhasi dijalankan atau tidak.

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

Tip

Untuk memahami DataTransactionResults, periksa :doc: sesuai halaman dokumen <../../plugin/data/transactions>` dan kembali ke DataTransactionResult.Builder docs untuk membuatnya.

Peringatan

Terutama saat bekerja dengan ItemStacks kemungkinan anda harus berurusam dengan NBTTagCompounds secara langsung. Banyak kunci NBT sudah didefinisikan sebagai ketetapan di kelas org.spongepowered.common.data.util.NbtDataUtil. Jika kunci yang anda butuhkan tidak ada disana, anda harus menambahkannya untuk menghindari 'magic values' didalam kodenya.

Metode penghapusan

Metode remove() mencoba untuk menghapus data dari DataHolder dan mengembalikan DataTransactionResult.

Penghapusan tidak diabstraksikan dalam abstrak DataProcessor karena abstraksi tidak memiliki cara untuk mengetahui apakah data selalu ada pada DataHolder yang kompatibel (seperti WetData atau HealthData) atau jika mungkin atau mungkin tidak hadir (seperti LoreData). Jika data selalu ada, hapus() harus selalu gagal. Jika mungkin atau mungkin tidak ada, hapus() harus menghapusnya. Dalam kasus tersebut, metode doesDataExist() harus diganti.

Karena entitas hidup selalu memiliki kesehatan, HealthData selalu hadir dan karena itu tidak didukung. Oleh karena itu kami hanya mengembalikan failNoData() dan tidak mengganti metode doesDataExist().

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

Metode Getter

Metode getter mendapatkan data dari DataHolder dan mengembalikan``DataManipulator`` opsional. Antarmuka DataProcessor menentukan metode dari() dan createFrom(), perbedaannya adalah bahwa dari() akan kembali Opsional.empty() jika dudukan data kompatibel, namun saat ini tidak berisi data, sementara createFrom() akan memberikan DataManipulator memegang nilai bawaan dalam kasus itu.

Sekali lagi, AbstractEntityDataProcessor akan menyediakan sebagian besar pengerjaan untuk prosess ini dan hanya membutuhkan satu metode untuk mendapatkan nilai sebenarnya yang tersedia di DataHolder. Metode ini hanya di butuhkan setelah supports() dan doesDataExist() keduanya telah kembali benar, artinya metode tersebut bekerja dengan anggapan bahwa data tersedia.

Peringatan

Jika data mungkin tidak selalu ada pada target DataHolder, seperti jika fungsi remove() mungkin berhasil dijalankan (lihat diatas), hal ini menjadi sangat penting bahwa anda mengganti metode doesDataExist() sehingga datanya kembali benar ketika data disajikan dan salah jika tidak.

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

Metode penyaringan

Metode pengisian berbeda dari metode getter ketika menerima DataManipulator untuk diisi dengan nilai-nilai. Nilai-nilai tersebut bisa datang dari DataHolder atau harus diserialkan kembali dari DataContainer. Metode ini mengembalikan Optional.empty() jika DataHolder tidak sesuai.

AbstractEntityDataProcessor telah menangani pengisian dari DataHolders dengan menciptakan sebuah DataManipulator dari pemegang dan kemudian menyatukannya dengan manipulator yang tersedia, tetapi serialisasi DataContainer tidak dapat disediakan.

public Optional<HealthData> fill(DataContainer container, HealthData healthData) {
    final Optional<Double> health = container.getDouble(Keys.HEALTH.getQuery());
    final Optional<Double> maxHealth = container.getDouble(Keys.MAX_HEALTH.getQuery());
    if (health.isPresent() && maxHealth.isPresent()) {
        healthData.set(Keys.HEALTH, health.get());
        healthData.set(Keys.MAX_HEALTH, maxHealth.get());
        return Optional.of(healthData);
    }
    return Optional.empty();
}

Metode fill() adalah untuk mengembalikan sebuah Optional dari healthData yang diubah, jika dan hanya jika semua data yang dibutuhkan dapat diambil dari DataContainer.

Metode lain

Tergantung pada abstract superclass yang digunakan, beberapa metode lain mungkin akan di butuhkan. Sebagai contoh, AbstractentityDataProcessor perlu membuat DataManipulator secara instan di beberapa titik. Hal ini tidak bisa di lakukan karena metode tersebut tidak mengetahui kelompok pelaksana juga tidak mengetahui konstruktor yang akan di gunakan. Oleh sebab itu metode ini akan menggunakan fungsi abstrak yang harus disediakan oleh pelaksana akhir. Hal ini hanya membuat DataManipulator dengan data bawaan.

Jika anda menjalankan DataManipulator anda sesuai dengan yang di rekomendasikan, anda hanya perlu menggunakan no-args konstruktor.

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

5. Terapkan ValueProcessors

Tidak hanya DataManipulator yang mungkin di tawarkan kepada DataHolder, tetapi juga kelengkapan Value dengan sendiri nya. Untuk itu, anda harus menyediakan minimal satu ValueProcessor untuk setiap Key yang ada di DataManipulator anda. Sebuah ValueProcessor dinamai mengikuti nama tetap setiap Key di dalam kelompok Keys dengan bentuk yang sama dengan DataQuery. Nama tetap tersebut dihilangkan garis bawah, digunakan dalam bentuk huruf besar dan di gabungkan dengan ValueProcessor.

ValueProcessor harus selalu di ambil dari AbstractSpongeValueProcessor, yang akan memproses sebagian pengecekan Support() sesuai dengan jenis DataHolder. Untuk Key.HEALTH, kita akan membuat dan mengkonstruk HealthValueProcessor sebagai berikut.

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

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

    [...]
}

Sekarang AbstractSpongeValueProcessor akan menggantikan kita dari keharusan mengecek apakah nilai telah didukung. Hal ini akan di asumsikan telah didukung jika target ValueContainer adalah jenis EntityLivingBase.

Tip

Untuk lebih memudahkan pengendalian terhadap objek EntityLivingBase yang telah didukung, metode support(EntityLivingBase) bisa diganti.

Sekali lagi, kebanyakan proses telah dilakukan oleh bagian abstraksi. Kita hanya perlu menjalankan dua metode pembantu untuk membuat value dan mitra tetapnya dan tiga metode untuk mendapatkan, mengatur dan menghapus data.

protected MutableBoundedValue<Double> constructValue(Double value) {
    return SpongeValueFactory.boundedBuilder(Keys.HEALTH)
        .minimum(DataConstants.MINIMUM_HEALTH)
        .maximum((double) Float.MAX_VALUE)
        .defaultValue(DataConstants.DEFAULT_HEALTH)
        .actualValue(value)
        .build();
}

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

Dikarenakan tidak mungkin sebuah EntityLivingBase tidak memiliki kesehatan, metode ini tidak akan pernah kembali ke Optional.empty().

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

Metode set() akan mengembalikan nilai boolean yang akan mengindikasikan apakah nilai dapat berhasil diatur. Melakukan proses ini akan menolak nilai di luar batas-batas yang telah digunakan didalam metode konstruksi nilai kami di atas.

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

Karena data dijamin selalu hadirkan, usaha untuk menghapusnya selalu gagal.

6. Daftar prosesor

Agar Sponge dapat menggunakan manipulator dan prosesor kami, kita perlu mendaftarkan nya. Proses ini di lakukan di kelas org.spongepowered.common.data.SpongeSerializationRegistry. Dalam metode setupSerialization ada dua blok besar dari tempat pendaftaran dimana kami menambahkan prosesor kami.

Dataprosesor

DataProcessor terdaftar bersama kelas penghubung dan pelaksana dari DataManipulator yang ditanganinya. Untuk setiap DataManipulators yang tidak kekal/kekal minimal satu DataProcessor harus terdaftar.

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

Nilaiprosesor

Nilai prosesor terdaftar dibagian bawah dari fungsi yang sama. Untuk setiap Key beberapa prosesor bisa didaftarkan dengan beberapa panggilan dari metode registeryValueProcessor().

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

Melakukan data blok

Blok data berbeda-beda dari satu jenis dengan yang lain dimana hal tersebut di jalankan dengan mencampurkan blok tersebut dengan blok itu sendiri. Ada beberapa metode di org.spongepowered.mixin.core.block.MixinBlock yang harus diganti untuk menjalankan data untuk setiap blok.

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

    [...]
}

supports() harus kembali benar jika baik penghubung ImmutableDataManipulator bisa dialihkan dari Class yang dilalui sebagai argumen, atau superkelas mendukungnya.

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

getStateWithData() harus kembali baru BlockState dengan data dari ImmutableDataManipulator yang dijalankan untuk nya. Jika manipolator tidak didukung secara langsung, metode harus didelegasikan kepada superclass.

@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);
}

getStateWithValue() sama dengan get StateWithData(), tetapi bekerja dengan satu Keys.

@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);
}

Pada akhirnya, getManipulators() harus mengembalikan dafter semua immutableDataManipulators blok yang didukung, bersamaan dengan nial sekarang untuk disajikan IBlockState. Data tersebut harus mencakup semua ImmutableDataManipulators yang berasal dari superclass.

@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();
}

Informasi lebih lanjut

Dengan Data yang merupakan konsep yang sedikit abstrak didalam Sponge, hal tersebut mempersulit untuk memberikan petunjuk umum tentang cara memperoleh data yang dibutuhkan dari kelas-kelas Minecraft itu sendiri. Mungkin akan membantu dengan memperhatikan prosesor yang serupa dengan yang anda kerjakan yang telah dijalankan untuk mendapatkan pemahaman yang lebih baik tentang bagaimana prosesor tersebut mestinya bekerja.

Jika anda terjebak atau tidak yakin tentang aspek-aspek tertentu, silahkan kunjungi #sponeedev salura IRC, form-forum, atau angkat masalah tersebut di GitHub. Pastikan anda memeriksa ``Data Processor Implementation Checklist<https://github.com/SpongePowered/SpongeCommon/issues/8>`_ untuk persyaratan kontribusi umum.