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;

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

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

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

Uyarı

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.

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.

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:

  • 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.

Uyarı

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.

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;

PluginContainer plugin = ...;

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;

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

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.

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
}