Zamanlayıcı

Sponge, gelecekte yürütülecek görevleri belirlemenize izin vermek için Scheduler öğesini açığa çıkarır. Scheduler, gecikme, aralık, isim, eşzamanlılık(eşzamansızlık) ve Runnable (bkz: Görev Özellikleri) gibi görev özelliklerini belirtebileceğiniz bir Task.Builder sağlar.

Görev Oluşturucu

Önce bir Task.Builder örneği edinin:

import org.spongepowered.api.scheduler.Task;

Task.Builder taskBuilder = Task.builder();

Gerekli olan tek şey, Task.Builder#execute(Runnable) kullanarak belirtebileceğiniz Runnable özelliğidir:

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

ya da Task.Builder#execute(Runnable runnable) ile Java 8 sözdizimi kullanabilirsiniz:

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

ya da Task.Builder#execute(Consumer<Task> task) ile Java 8 söz dizimi kullanabilirsiniz:

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

Görev Özellikleri

Task.Builder kullanarak, aşağıda tanımlandığı gibi başka, opsiyonel özellikler belirtebilirsiniz.

Özellik

Kullanılan yöntem

Açıklama

Gecikme

delayTicks(uzun gecikme)

delay(uzun gecikme,

TimeUnit birimi)

Görevden önce geçirilecek isteğe bağlı süre.

Zaman delayTicks() yöntemi ile birkaç işaret olarak belirtilir veya delay() yöntemiyle bir TimeUnit belirterek daha uygun bir zaman birimi olarak sağlanabilir.

Her iki yöntem de, fakat aynı anda olmamak değil, görev başına belirtilebilir.

Aralık

intervalTicks(

uzun aralık)

interval(uzun aralık,

TimeUnit birimi)

Görevin tekrarı arasındaki zaman miktarı. Bir aralık belirtilmemişse, görev tekrar edilmeyecektir.

Zaman, intervalTicks() yöntemiyle birkaç işaret olarak belirtilir veya interval() yöntemiyle bir TimeUnit belirterek daha uygun bir zaman birimi olarak sağlanabilir.

Her iki yöntem de, fakat aynı anda olmamak değil, görev başına belirtilebilir.

Eşitleme

async()

Eşzamanlı bir görev, oyun döngüsüyle seri ana oyun halkasında yürütülür. Eğer Task.Builder#async kullanılırsa, görev zaman uyumsuz olarak çalıştırılacaktır. Bu nedenle, işaret döngüsünden bağımsız olarak kendi mesaj dizisini yürütecektir ve oyun durumunu güvenle kullanamayabilir. (Bkz. Asynchronous Tasks.)

İsim

İsim (dize ismi)

Görevin adı. Varsayılan olarak görevin adı PLUGIN_ID “-” ( “A-” | “S-” ) SERIAL_ID olacaktır. Aynı senkronizasyon türü için iki aktif görev aynı iki seri ID’ye sahip olmamalıdır. Eğer görev adı belirlenmişse bunlar tanımlayıcı ve eklentinizin hata ayıklamasında kullanıcılara yardımcı olmalıdır.

Son olarak, Task.Builder#submit(Object) kullanarak zamanlayıcıya görevi gönderin.

Ve bu kadar! Özetlemek gerekirse, 100 milisaniyelik bir başlangıç ​​gecikmesinden sonra her 5 dakikada bir asenkron çalışacak tam işlevli bir zamanlanmış görev oluşturulabilir ve şu kodu kullanarak gönderilebilir:

import java.util.concurrent.TimeUnit;

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

Bir görevi iptal etmek için, sadece Task#cancel() motodunu çağırmanız yeterli:

task.cancel();

Görevi çalıştırılabilirin kendisinden iptal etmeniz gerekiyorsa, görevi yerine getirmek için bir Consumer<Task>` i seçebilirsiniz. Aşağıdaki örnek, 60’dan aşağı sayacak ve 0’a ulaştığında kendini iptal edecek bir görevi zamanlayacaktır.

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

Asenkron Görevler

Asenkron görevler, öncelikle yürütme süresi uzun olan, yani başka bir sunucuya veya veritabanına talepte bulunan alan kodlar için kullanılmalıdır. Ana mesaj dizisinde yapılırsa, bir sonraki onay işareti, istek tamamlanıncaya kadar tetiklenemez, çünkü başka bir sunucuya yapılan bir istek oyunun performansını büyük ölçüde etkiler.

Minecraft genellikle tek iplikli olduğundan eş zamanlı olmayan bir iplikte yapabileceğiniz çok bir şey yoktur. Eğer eş zamanlı olmayarak bir iplik çalıştırmanız gerekiyorsa SpongeAPI/affect Minecraft kullanmayan bütün kodları çalıştırmalı, sonra da API’ye ihtiyacı olan kodu çözmek için diğer bir synchronous görevi kaydetmelisiniz.Aşağıdakileri de içeren asynchronously ile birlikte çalışabileceğiniz bir çok Minecraft bölümü vardır:

  • Sohbet

  • Sponge’un yapısal İzinler yönetimi

  • Sponge’un programcısı

Buna ek olarak, asenkron olarak yapmak için güvenli olan birkaç operasyon daha vardır:

  • Bağımsız ağ istekleri

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

Uyarı

Ana nesnenin dışındaki oyun nesnelerine erişmek çökmelere, tutarsızlıklara ve diğer çeşitli sorunlara neden olabilir ve kaçınılmalıdır. Eğer bu yanlış yapılırsa, en iyi durumda bir sunucu çöküşü olsun olmasın, en kötü ihtimalle bozulmuş bir oyuncu / dünya / sunucu olsun veya olmasın bir `` ConcurrentModificationException`` alabilirsiniz.

Diğer kütüphanelerle uyumluluk

Eklentiniz fiziksel olarak büyüdüğü için Java ve JVM için kullanılabilir olan bir çok aynı anda kullanılır kütüphaneden birini kullanmaya başlamak isteyebilirsiniz. Bu kütüphaneler görevin hangi iplikte çalıştırıldığına yöneltme yoluyla Java’nın ExecutorService desteklemeye eğilimlidir.

Bu kitaplıkların Sponge’un Scheduler ile çalışmasına izin vermek için aşağıdaki yöntemler kullanılabilir:

Göz önünde bulundurulması gereken bir nokta, senkronize etkileşimlerin ‘Asynchronous Tasks’_ bölümünde listelenen etkileşimler dışında etkileşime giren görevlerin, iş parçacıklarına göre güvenli olması için``Scheduler#createSyncExecutor(Object)`` ile oluşturulmuş ExecutorService üzerinde yürütülmesi gerekir.

import org.spongepowered.api.scheduler.SpongeExecutorService;

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

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

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

Neredeyse tüm kütüphanelerde, doğal görevleri zamanlamak için ExecutorService``ı uyarlamanın bazı yolları vardır. Örnek olarak aşağıdaki paragraflar, bir dizi kütüphanede ``ExecutorService işlevinin nasıl kullanıldığını açıklayacaktır.

CompletableFuture (Java 8)

Java 8 ile CompletableFuture nesnesi standart kütüphaneye eklendi. Future nesnesine kıyasla bu, geliştiriciye gelecek sonunda tamamlanıncaya kadar ipliği engellemek yerine geleceği tamamladığında çağırılan bir geri çağırma sağlar.

CompletableFuture akıcı bir arayüz olup, her fonksiyonu için genellikle aşağıdaki üç değişkene sahiptir:

  • CompletableFuture#<function>Async(…, Executor ex)`` bu fonksiyonu ex yoluyla çalıştırır

  • CompletableFuture#<function>Async(...) bu fonksiyonu ForkJoinPool.commonPool() yoluyla çalıştırır

  • CompletableFuture#<function>(...) bu fonksiyonu bir önceki CompletableFuture hangi iplik üzerinde tamamlandıysa orada çalıştırır.

import java.util.concurrent.CompletableFuture;

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

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

RxJava

RxJava, JVM için`Reactive Extensions <http://reactivex.io/>`_ konseptinin bir uygulamasıdır.

Rx’deki çoklu kullanım çeşitli Schedulers ‘ler yoluyla yönetilir. Schedulers#from(Executor executor) kullanmak Scheduler a dönüşebilen Sponge tarafından sağlanan Executor işlevini yerine getirir.

CompletableFuture gibi varsayılan eylemler, zincirin önceki bölümünü tamamlayan aynı iş parçacığında yürütülür. İş parçacıkları arasında gezinmek için Observable#observeOn(Scheduler scheduler) kullanın.

Unutulmaması gereken önemli bir nokta da, Observable#subscribe() da çağrılmış herhangi bir iş parçacığı üzerinde Observable kök çağrılmış olmasıdır. Kök gözlemci Sponge ile etkileşime giriyorsa, Observable#subscribeOn(Scheduler scheduler) kullanarak eş zamanlı olarak çalıştırılmaya zorlanmalıdır.

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

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, çok sayıda skala çerçevesinin dizayn edildiği dahili bir Future nesnesi ile birlikte gelir. Geleceğin çoğu yöntemi hangi operasyonun nerede yürütüldüğünü `ExecutionContext <https://www.scala-lang.org/api/current/index.html#scala.concurrent.ExecutionContext;crwdn:ht:1:ht:crwdn;gt;`_ kabul etmektedir. Bu, önceki işlemin sona erdiği aynı iş parçacığında yürütülmeyi varsayılan olarak ayarlayan CompletableFuture veya RxJava’dan farklıdır.

Tüm bu işlemlerin dolaylı olarak bir ExecutionContext bulmaya çalışması, varsayılan ExecutionContext.global işlevini kolaylıkla kullanabileceğiniz ve Sponge sunucu iş parçacıklarında iş parçacıklığa karşı korunması gereken parçaların çalıştırıldığı anlamına gelir.

Yanlışlıkla Sponge `` ExecutorContext`` yoluyla çalışmayı önlemek için, başka bir bağlam örtük olarak tanımlanmalıdır, böylece varsayılan seçim işlevi görür. Mesaj dizisi güvenliğini sağlamak için, yalnızca Sponge ile gerçekte etkileşime giren işlevlerin Sponge çalıştırıcısının belirtilmesi gerekecektir.

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
}