HyCodeYourTale

Scheduler & Tasks

Scheduler & Tasks

Dokumentace k plánování úloh a periodickým taskům.

Obsah

| Soubor | Popis |
|--------|-------|
| COMPLETABLE_FUTURE.md | CompletableFuture, CompletableFutureUtil, async patterns, error handling |
| SCHEDULED_TASKS.md | TaskRegistry, ScheduledExecutorService, TickingThread, periodické tasky |

---

Přehled

Hytale běží na více vláknech, takže je důležité správně plánovat tasky:

Scheduler Thread     - Async tasky, periodické úlohy
World Thread - Game logic, komponenty
Network Thread - Síťová komunikace

---

CompletableFuture (Doporučeno)

Jednorázový Async Task

CompletableFuture.runAsync(() -> {
// Těžká práce (I/O, databáze)
saveToDatabase();
});

S Callback na World Thread

CompletableFuture.runAsync(() -> {
// Async práce
PlayerData data = loadFromDatabase(uuid);
return data;
}).thenAccept(data -> {
// Callback - stále na async threadu!
// Pro komponenty potřebuješ world.execute()
world.execute(() -> {
applyData(player, data);
});
});

Řetězení

CompletableFuture
.supplyAsync(() -> {
// Krok 1: Načti data
return loadData();
})
.thenApply(data -> {
// Krok 2: Transformuj
return processData(data);
})
.thenAccept(result -> {
// Krok 3: Použij výsledek
world.execute(() -> {
applyResult(result);
});
})
.exceptionally(error -> {
// Error handling
getLogger().at(Level.SEVERE).withCause(error).log("Task failed:");
return null;
});

---

ScheduledExecutorService

Vytvoření Scheduleru

public class MyPlugin extends JavaPlugin {

private ScheduledExecutorService scheduler;

@Override
protected void setup() {
// Vytvoř scheduler s jedním vláknem
scheduler = Executors.newSingleThreadScheduledExecutor();
}

@Override
protected void shutdown() {
// DŮLEŽITÉ: Ukonči scheduler při shutdown
if (scheduler != null) {
scheduler.shutdown();
try {
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
scheduler.shutdownNow();
}
} catch (InterruptedException e) {
scheduler.shutdownNow();
}
}
}
}

Periodický Task

// Každých 5 minut
scheduler.scheduleAtFixedRate(
this::autoSave,
5, // Initial delay
5, // Period
TimeUnit.MINUTES
);

private void autoSave() {
getLogger().atInfo().log("Auto-saving...");
for (Player player : Universe.get().getPlayers()) {
savePlayerAsync(player.getUuid());
}
}

Delayed Task

// Jednou za 30 sekund
scheduler.schedule(
this::delayedTask,
30,
TimeUnit.SECONDS
);

private void delayedTask() {
getLogger().atInfo().log("Delayed task executed!");
}

S Fixed Delay (čekání mezi dokončením a startem)

// 10 sekund po dokončení předchozího
scheduler.scheduleWithFixedDelay(
this::cleanupTask,
0, // Initial delay
10, // Delay after completion
TimeUnit.SECONDS
);

---

Vzory pro Běžné Úlohy

Auto-Save

public class AutoSaveManager {

private final MyPlugin plugin;
private ScheduledExecutorService scheduler;
private final int saveIntervalMinutes = 5;

public AutoSaveManager(MyPlugin plugin) {
this.plugin = plugin;
}

public void start() {
scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(
this::performAutoSave,
saveIntervalMinutes,
saveIntervalMinutes,
TimeUnit.MINUTES
);
plugin.getLogger().atInfo().log("Auto-save started (every %d minutes)", saveIntervalMinutes);
}

public void stop() {
if (scheduler != null) {
scheduler.shutdown();
}
}

private void performAutoSave() {
plugin.getLogger().atInfo().log("Performing auto-save...");

for (Player player : Universe.get().getPlayers()) {
CompletableFuture.runAsync(() -> {
plugin.getPlayerDataManager().savePlayer(player.getUuid());
});
}
}
}

// Použití v pluginu
@Override
protected void setup() {
autoSaveManager = new AutoSaveManager(this);
}

@Override
protected void start() {
autoSaveManager.start();
}

@Override
protected void shutdown() {
autoSaveManager.stop();
}

Cleanup Task

public class CleanupTask {

private final MyPlugin plugin;
private ScheduledExecutorService scheduler;

public void start() {
scheduler = Executors.newSingleThreadScheduledExecutor();

// Cleanup každou hodinu
scheduler.scheduleAtFixedRate(
this::cleanup,
1,
1,
TimeUnit.HOURS
);
}

private void cleanup() {
plugin.getLogger().atInfo().log("Running cleanup...");

// Vyčisti staré záznamy
cleanOldRecords();

// Uvolni paměť
System.gc();

plugin.getLogger().atInfo().log("Cleanup complete");
}

public void stop() {
if (scheduler != null) {
scheduler.shutdown();
}
}
}

Delayed Player Action

public void delayedTeleport(Player player, Vector3d destination, int delaySeconds) {
player.sendMessage(Message.raw("Teleport za " + delaySeconds + " sekund..."));

scheduler.schedule(() -> {
// Zkontroluj že hráč je stále online
if (!player.isOnline()) return;

World world = player.getWorld();
world.execute(() -> {
// Teleportuj
teleportPlayer(player, destination);
player.sendMessage(Message.raw("Teleportován!"));
});
}, delaySeconds, TimeUnit.SECONDS);
}

Countdown

public void startCountdown(Player player, int seconds, Runnable onComplete) {
AtomicInteger remaining = new AtomicInteger(seconds);

ScheduledFuture task = scheduler.scheduleAtFixedRate(() -> {
int current = remaining.getAndDecrement();

if (current > 0) {
player.sendMessage(Message.raw("Zbývá: " + current + "s"));
} else {
// Zruší se automaticky při vrácení
}
}, 0, 1, TimeUnit.SECONDS);

// Po uplynutí času
scheduler.schedule(() -> {
task.cancel(false);
World world = player.getWorld();
world.execute(onComplete);
}, seconds, TimeUnit.SECONDS);
}

---

World.execute() Integration

Async → World Thread

scheduler.scheduleAtFixedRate(() -> {
// Na scheduler threadu

for (Player player : Universe.get().getPlayers()) {
World world = player.getWorld();

// Přepni na world thread pro komponenty
world.execute(() -> {
Ref ref = player.getRef();
Store store = ref.getStore();

// Bezpečný přístup ke komponentám
CustomComponent comp = store.getComponent(ref, CustomComponent.getComponentType());
if (comp != null) {
comp.incrementPlayTime();
}
});
}
}, 1, 1, TimeUnit.MINUTES);

---

Thread Pool pro Těžké Úlohy

public class HeavyTaskExecutor {

// Pool s více vlákny pro paralelní zpracování
private final ExecutorService heavyTaskPool =
Executors.newFixedThreadPool(4);

public void processAllPlayers(Collection players) {
List> futures = new ArrayList<>();

for (Player player : players) {
CompletableFuture future = CompletableFuture.runAsync(() -> {
processPlayer(player);
}, heavyTaskPool);

futures.add(future);
}

// Počkej na všechny
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenRun(() -> {
getLogger().atInfo().log("All players processed");
});
}

public void shutdown() {
heavyTaskPool.shutdown();
}
}

---

Časté Chyby

Chyba 1: Zapomenutý Shutdown

// ŠPATNĚ - Memory leak, scheduler běží po shutdown
@Override
protected void shutdown() {
// Zapomněl jsem scheduler.shutdown()
}

// SPRÁVNĚ
@Override
protected void shutdown() {
if (scheduler != null) {
scheduler.shutdown();
}
}

Chyba 2: Blokování World Threadu

// ŠPATNĚ - Blokuje world thread
world.execute(() -> {
Thread.sleep(5000); // NIKDY!
});

// SPRÁVNĚ - Async delay
scheduler.schedule(() -> {
world.execute(() -> {
// Akce po delay
});
}, 5, TimeUnit.SECONDS);

Chyba 3: Přístup ke Komponentám z Async

// ŠPATNĚ
scheduler.scheduleAtFixedRate(() -> {
// Na scheduler threadu!
store.getComponent(ref, Type.getComponentType()); // CRASH!
}, ...);

// SPRÁVNĚ
scheduler.scheduleAtFixedRate(() -> {
world.execute(() -> {
store.getComponent(ref, Type.getComponentType()); // OK
});
}, ...);

---

Shrnutí

| Metoda | Použití |
|--------|---------|
| CompletableFuture.runAsync() | Jednorázový async task |
| scheduler.schedule() | Delayed jednorázový task |
| scheduler.scheduleAtFixedRate() | Periodický task (fixní interval) |
| scheduler.scheduleWithFixedDelay() | Periodický task (delay po dokončení) |
| world.execute() | Přepnutí na world thread |

| TimeUnit | Použití |
|----------|---------|
| MILLISECONDS | Krátké delay |
| SECONDS | Většina tasků |
| MINUTES | Auto-save |
| HOURS | Cleanup, maintenance |

Last updated: 20. ledna 2026