Penjadwalan

Spons memperlihatkan :javadoc:`Penjadwal` untuk memungkinkan anda untuk menunjuk tugas-tugas yang akan dieksekusi di masa depan. Yang Penjadwal menyediakan sebuah :javadoc:`Tugas.Pembangun` dengan mana anda dapat menentukan properti tugas seperti penundaan, selang, nama, (a)sinkronisitas, dan Runnable (lihat tugas-properti).

Penyusun Halaman

Pertama, mendapatkan contoh dari Tugas.Pembangun:

import org.spongepowered.api.scheduler.Task;

Task.Builder taskBuilder = Task.builder();

Satu-satunya diharuskan properti ini adalah Runnable, yang mana dapat anda tentukan menggunakan :javadoc:`Tugas.Pembangun#menjalankan(Runnable)`:

taskBuilder.execute(new Runnable() {
    public void run() {
        logger.info("Yay! Schedulers!");
    }
});

atau menggunakan Java 8 sintaks dengan Tugas.Pembangun#menjalankan(Runnable runnable)

taskBuilder.execute(
    () -> {
        logger.info("Yay! Schedulers!");
    }
);

atau menggunakan java 8 sintaks dengan Tugas.Pembangun#menjalankan(Konsumen<Task> tugas)

taskBuilder.execute(
    task -> {
        logger.info("Yay! Schedulers! :" + task.getName());
    }
);

Blok properti

Dengan menggunakan Tugas.Pembangun, anda dapat menentukan lain, properti opsional, seperti yang dijelaskan di bawah ini.

Properti

Motede Dipakai

Deskripsi

tundaan

delayTicks(long delay)

delay(long delay,

TimeUnit unit)

Yang opsional jumlah waktu untuk lulus sebelum tugas adalah yang harus menjalankan.

Waktu ditentukan sebagai jumlah kutu dengan delayTicks() metode, atau dapat diberikan sebagai jumlah waktu yang lebih nyaman unit dengan menentukan TimeUnit dengan delay() metode.

Salah satu metode, tetapi tidak keduanya, dapat ditentukan per tugas.

selang

intervalTicks(

rentang blok

interval(long interval,

TimeUnit unit)

Jumlah waktu antara pengulangan tugas. Jika selang waktu tidak ditentukan, tugas tidak akan terulang.

Waktu ditentukan sebagai jumlah kutu dengan intervalTicks() metode, atau dapat diberikan sebagai jumlah waktu yang lebih nyaman unit dengan menentukan TimeUnit dengan interval() metode.

Salah satu metode, tetapi tidak keduanya, dapat ditentukan per tugas.

sinkronisasi

async()

Sinkron tugas dijalankan dalam permainan ini loop utama di seri dengan centang siklus. Jika Task.Builder#async digunakan, tugas akan berjalan asynchronously. Oleh karena itu, itu akan berjalan di thread sendiri, secara independen dari kutu siklus, dan mungkin tidak aman menggunakan permainan negara. (Lihat Asynchronous Tasks.)

nama

name(String name)

Nama tugas. Secara default, nama tugas akan PLUGIN_ID " - " ( "- "|"S-" ) SERIAL_ID. Misalnya, default nama tugas bisa terlihat seperti "fooplugin-A-12". Ada dua tugas aktif akan memiliki yang sama serial ID yang sama jenis sinkronisasi. Jika nama tugas yang ditentukan, harus deskriptif dan membantu pengguna dalam debugging plugin anda.

Terakhir, menyerahkan tugas scheduler menggunakan Task.Builder#submit(Object).

Dan hanya itu! Untuk meringkas, sepenuhnya fungsional tugas terjadwal yang akan berjalan asynchronously setiap 5 menit setelah awal keterlambatan dari 100 milidetik bisa dibangun dan disampaikan dengan menggunakan kode berikut:

import java.util.concurrent.TimeUnit;

PluginContainer plugin = ...;

Task task = Task.builder().execute(() -> logger.info("Yay! Schedulers!"))
    .async().delay(100, TimeUnit.MILLISECONDS).interval(5, TimeUnit.MINUTES)
    .name("ExamplePlugin - Fetch Stats from Database").submit(plugin);

Untuk membatalkan tugas, hanya menyebut :javadoc:`Tugas#batal()` metode:

task.cancel();

If you need to cancel the task from within the runnable itself, you can instead opt to use a Consumer<Task> in order to access the task. The below example will schedule a task that will count down from 60 and cancel itself upon reaching 0.

@Listener
public void onGameInit(GameInitializationEvent event) {
    Task task = Task.builder().execute(new CancellingTimerTask())
        .interval(1, TimeUnit.SECONDS)
        .name("Self-Cancelling Timer Task").submit(plugin);
}

private class CancellingTimerTask implements Consumer<Task> {
    private int seconds = 60;
    @Override
    public void accept(Task task) {
        seconds--;
        Sponge.getServer()
            .getBroadcastChannel()
            .send(Text.of("Remaining Time: "+seconds+"s"));
        if (seconds < 1) {
            task.cancel();
        }
    }
}

Peringatan

Any scheduled tasks should either watch the current state of the Game or should be unregistered if they are no longer needed (e.g. during a GameStoppingServerEvent). This is of particular importance for the client because it can start and stop the server multiple times.

Asynchronous Tugas

Asynchronous tugas yang harus digunakan terutama untuk kode yang dapat mengambil jangka waktu yang signifikan untuk mengeksekusi, yaitu permintaan ke server lain atau database. Jika dilakukan di thread utama, permintaan ke server lain bisa sangat mempengaruhi kinerja dari permainan, karena berikutnya centang tidak bisa dipecat sampai request selesai.

Since Minecraft is largely single-threaded, there is little you can do in an asynchronous thread. If you must run a thread asynchronously, you should execute all of the code that does not use SpongeAPI/affect Minecraft, then register another synchronous task to handle the code that needs the API. There are a few parts of Minecraft that you can work with asynchronously, including:

  • Obrolan

  • Sponge's built-in Permissions penanganan

  • Sponge's scheduler

Selain itu, ada beberapa operasi lain yang aman untuk melakukan asynchronous:

  • Jaringan independen permintaan

  • Filesystem I/O (excluding files used by Sponge)

Peringatan

Mengakses permainan benda-benda di luar thread utama dapat menyebabkan crash, inkonsistensi dan berbagai masalah lainnya dan harus dihindari. Jika hal ini dilakukan salah, anda bisa mendapatkan ConcurrentModificationException dengan atau tanpa kecelakaan server terbaik dan rusak player/dunia/server yang paling buruk.

Peringatan

Any scheduled tasks should either watch the current state of the Game or should be unregistered if they are no longer needed (e.g. during a GameStoppingServerEvent). This is of particular importance for the client because it can start and stop the server multiple times.

Kompatibilitas dengan aturan belanja lainnya

Sebagai plugin anda tumbuh dalam ukuran dan ruang lingkup anda mungkin ingin mulai menggunakan salah satu dari banyak concurrency perpustakaan, tersedia untuk Java dan JVM. Perpustakaan ini memang cenderung mendukung Java ExecutorService sebagai sarana untuk mengarahkan di mana benang tugas dijalankan.

Untuk memungkinkan perpustakaan ini untuk bekerja dengan Spons Scheduler metode berikut dapat digunakan:

Satu hal yang perlu diingat adalah bahwa tugas-tugas yang berinteraksi dengan Sponge di luar interaksi yang terdaftar di Asynchronous Tugas butuh untuk dieksekusi pada ExecutorService dibuat dengan Scheduler#createSyncExecutor(Objek) untuk menjadi thread-safe.

import org.spongepowered.api.scheduler.SpongeExecutorService;

PluginContainer plugin = ...;

SpongeExecutorService minecraftExecutor = Sponge.getScheduler().createSyncExecutor(plugin);

minecraftExecutor.submit(() -> { ... });

minecraftExecutor.schedule(() -> { ... }, 10, TimeUnit.SECONDS);

Hampir semua perpustakaan memiliki beberapa cara beradaptasi ExecutorService untuk native jadwal tugas. Sebagai contoh paragraf berikut akan menjelaskan bagaimana ExecutorService digunakan di sejumlah perpustakaan.

CompletableFuture (Java 8)

Dengan Java 8 CompletableFuture objek ditambahkan ke perpustakaan standar. Dibandingkan dengan ` masa Depan` objek, hal ini memungkinkan bagi pengembang untuk menyediakan callback yang disebut ketika selesai masa depan daripada memblokir benang sampai depan akhirnya selesai.

CompletableFuture adalah menguasai antarmuka yang biasanya memiliki tiga variasi untuk masing-masing fungsinya:

  • CompletableFuture#<function>Async(..., Executor ex) Executes this function through ex

  • CompletableFuture#<function>Async(...) Executes this function through ForkJoinPool.commonPool()

  • `` CompletableFuture # <function> (...) `` Jalankan fungsi ini pada thread apa pun yang sebelumnya `` CompletableFuture`` telah selesai.

import java.util.concurrent.CompletableFuture;

PluginContainer plugin = ...;

SpongeExecutorService minecraftExecutor = Sponge.getScheduler().createSyncExecutor(plugin);

CompletableFuture.supplyAsync(() -> {
    // ASYNC: ForkJoinPool.commonPool()
    return 42;
}).thenAcceptAsync((awesomeValue) -> {
    // SYNC: minecraftExecutor
}, minecraftExecutor).thenRun(() -> {
    // SYNC: minecraftExecutor
});

RxJava

`RxJava <https://github.com/ReactiveX/RxJava> ` _ adalah implementasi dari konsep `Reactive Extensions <http://reactivex.io/> ` _ untuk JVM.

Multithreading di Rx dikelola melalui berbagai Schedulers <http://reactivex.io/documentation/scheduler.html> ` _. Menggunakan ` Penjadwal # dari (Pelaksana Pelaksana) `` fungsi `` Pelaksana` yang disediakan oleh Sponge dapat diubah menjadi Penjadwal `` `.

Seperti CompletableFuture secara default tindakan yang dilaksanakan pada benang yang sama yang melengkapi verifikasi identitas sebelumnya bagian dari rantai. Gunakan yang dapat Diamati#observeOn(Scheduler scheduler) untuk bergerak di antara benang.

Salah satu hal yang penting untuk diingat adalah bahwa akar Observable akan dipanggil pada apa pun benang Observable#subscribe()``dipanggil. Jika akar dapat diamati berinteraksi dengan sponge itu harus dipaksa untuk berjalan serempak menggunakan ``Observable#subscribeOn(Scheduler scheduler).

import rx.Observable;
import rx.Scheduler;
import rx.schedulers.Schedulers;

PluginContainer plugin = ...;

SpongeExecutorService executor = Sponge.getScheduler().createSyncExecutor(plugin);
Scheduler minecraftScheduler = Schedulers.from(executor);

Observable.defer(() -> Observable.from(Sponge.getServer().getOnlinePlayers()))
          .subscribeOn(minecraftScheduler) // defer -> SYNC: minecraftScheduler
          .observeOn(Schedulers.io()) // -> ASYNC: Schedulers.io()
          .filter(player -> {
              // ASYNC: Schedulers.io()
              return "Flards".equals(player.getName());
          })
          .observeOn(minecraftScheduler) // -> SYNC: minecraftScheduler
          .subscribe(player -> {
              // SYNC: minecraftScheduler
              player.kick(Text.of("Computer says no"));
          });

Scala

Scala comes with a built-in Future object which a lot of scala framework mirror in design. Most methods of the Future accept an ExecutionContext which determined where that part of the operation is executed. This is different from the CompletableFuture or RxJava since they default to executing on the same thread on which the previous operation ended.

The fact that all these operations try to implicitly find an ExecutionContext means that you can easily use the default ExecutionContext.global and specifically run the parts that need to be thread-safe on the Sponge server thread.

Untuk menghindari sengaja penjadwalan pekerjaan melalui Sponsge ExecutorContext konteks lain harus secara implisit didefinisikan sehingga bertindak sebagai pilihan default. Untuk menjaga keselamatan thread hanya fungsi yang benar-benar berinteraksi dengan Spons akan perlu untuk memiliki Spons pelaksana yang ditentukan.

import scala.concurrent.ExecutionContext

val executor = Sponge.getScheduler().createSyncExecutor(plugin)

import ExecutionContext.Implicits.global
val ec = ExecutionContext.fromExecutorService(executor)

val future = Future {
    // ASYNC: ExecutionContext.Implicits.global
}

future foreach {
    case value => // SYNC: ec
}(ec)

future map {
    case value => 42 // SYNC: ec
}(ec).foreach {
    case value => println(value) // ASYNC: ExecutionContext.Implicits.global
}