Skip to content

Changelog

Лента релизов из GitHub API с фильтрами по модулям и типам изменений.

NextLib v1.0.6 STABLE

1.0.6-stabledatabasefeature

# API квестов   Новый API позволяет описывать квесты кодом, отслеживать прогресс (убийства мобов/игроков, перемещение, крафт) и сохранять его в базе данных через `DatabaseClient`.   ## Базовые понятия - **Quest** — описание квеста с идентификатором, названием и списком целей. - **QuestObjective** — отдельная цель квеста. Встроены четыре вида: - `KillObjective` — убийство сущности или игрока. - `TravelObjective` — пройти/пролететь нужное расстояние в метрах. - `CraftObjective` — скрафтить определённый предмет. - `PlaytimeObjective` — проведи на сервере нужное количество минут. - **QuestProgress** — состояние квеста для конкретного игрока. - **QuestManager** — регистрирует квесты, активирует их для игроков и принимает события (убийство, перемещение, крафт). - **QuestStore** — слой сохранения. Реализация `DatabaseQuestStore` сохраняет прогресс в JDBC-базу.   ## Быстрый старт ```java DatabaseManager databaseManager = new DatabaseManager(); DatabaseClient client = databaseManager.register( "quests", new DatabaseConfig(DatabaseType.SQLITE, "plugins/quests.db", "", "") );   QuestStore store = new DatabaseQuestStore(client); QuestManager quests = new QuestManager(store);   Quest winterQuest = quests.registerQuest(new Quest( "winter-2024", "Зимние приключения", "Согрей снеговика и докажи, что готов к празднику!", List.of( new KillObjective("snow-golem", "Убей 15 снеговиков", EntityType.SNOW_GOLEM, 15, false, true), new TravelObjective("long-walk", "Пройди 500 метров", 500), new CraftObjective("make-cookies", "Скрафти 24 печенья", Material.COOKIE, 24), new PlaytimeObjective("stay-awhile", "Поиграй час", 60) ) ));   // Активируем квест для игрока при входе QuestProgress progress = quests.activateQuest(player.getUniqueId(), winterQuest.getId()); ```   ## Пример интеграции с ивентами ```java @EventHandler public void onEntityDeath(EntityDeathEvent event) { if (event.getEntity().getKiller() == null) return; UUID playerId = event.getEntity().getKiller().getUniqueId(); EntityType killed = event.getEntityType(); boolean playerKill = killed == EntityType.PLAYER; quests.recordKill(playerId, killed, playerKill); }   @EventHandler public void onPlayerMove(PlayerMoveEvent event) { double distance = event.getTo().distance(event.getFrom()); if (distance > 0.01) { quests.recordTravel(event.getPlayer().getUniqueId(), distance); } }   @EventHandler public void onItemCraft(CraftItemEvent event) { if (!(event.getWhoClicked() instanceof Player player)) return; int amount = event.getRecipe().getResult().getAmount(); quests.recordCraft(player.getUniqueId(), event.getRecipe().getResult().getType(), amount); } ```   ### Пример: квест «Проведи час на сервере» Квест на время использует `PlaytimeObjective` и периодический таск, который каждую минуту прибавляет прогресс всем онлайн-игрокам.   ```java Quest stayLonger = quests.registerQuest(new Quest( "stay-long", "Тёплый вечер", "Проведи час на сервере, общаясь с игроками!", List.of(new PlaytimeObjective("one-hour", "Поиграй 60 минут", 60)) ));   // Планируем повторяющуюся задачу в onEnable() new BukkitRunnable() { @Override public void run() { for (Player player : Bukkit.getOnlinePlayers()) { quests.recordPlaytime(player.getUniqueId(), 1); // +1 минута } }.runTaskTimer(this, 20L * 60, 20L * 60); // раз в минуту   // Активируйте квест при входе или вручную: @EventHandler public void onJoin(PlayerJoinEvent event) { quests.activateQuest(event.getPlayer().getUniqueId(), stayLonger.getId()); } ```   ## Работа с сохранениями - `QuestManager.activateQuest` загружает прогресс игрока из хранилища или создаёт новый. - `QuestManager.restorePlayer` подтягивает все сохранённые квесты игрока (можно вызывать при заходе на сервер). - `DatabaseQuestStore` создаёт таблицу `nextlib_quest_progress` автоматически и хранит прогресс для каждой цели.   ## Настройка таблицы По умолчанию используется таблица `nextlib_quest_progress`. Если нужен другой префикс/название, передайте его во второй аргумент `new DatabaseQuestStore(client, "custom_table")`. <!--EndFragment--> <!--StartFragment--> # Полный пример: зимний ивент с меню и квестами --   Ниже — цельный класс плагина, который:   - создаёт GUI для зимних заданий, - регистрирует квест с несколькими целями, - запускает трекинг прогресса (убийства, перемещение, крафт, время на сервере), - автоматически активирует квест игрокам при входе.   ```java package com.example.winter;   import io.github.chi2l3s.nextlib.api.database.DatabaseClient; import io.github.chi2l3s.nextlib.api.database.DatabaseConfig; import io.github.chi2l3s.nextlib.api.database.DatabaseManager; import io.github.chi2l3s.nextlib.api.database.DatabaseType; import io.github.chi2l3s.nextlib.api.gui.Actions; import io.github.chi2l3s.nextlib.api.gui.Gui; import io.github.chi2l3s.nextlib.api.gui.GuiItem; import io.github.chi2l3s.nextlib.api.gui.GuiManager; import io.github.chi2l3s.nextlib.api.quest.CraftObjective; import io.github.chi2l3s.nextlib.api.quest.DatabaseQuestStore; import io.github.chi2l3s.nextlib.api.quest.KillObjective; import io.github.chi2l3s.nextlib.api.quest.PlaytimeObjective; import io.github.chi2l3s.nextlib.api.quest.Quest; import io.github.chi2l3s.nextlib.api.quest.QuestManager; import io.github.chi2l3s.nextlib.api.quest.QuestProgress; import io.github.chi2l3s.nextlib.api.quest.QuestStore; import io.github.chi2l3s.nextlib.api.quest.TravelObjective; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Material; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.inventory.CraftItemEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable;   import java.util.List; import java.util.UUID;   public final class WinterEventPlugin extends JavaPlugin implements Listener { private DatabaseManager databaseManager; private QuestManager quests; private GuiManager guiManager; private Quest winterQuest;   @Override public void onEnable() { // 1. Инициализируем базу, менеджер квестов и GUI. databaseManager = new DatabaseManager(); DatabaseClient client = databaseManager.register( "winter-quests", new DatabaseConfig(DatabaseType.SQLITE, "plugins/winter-quests.db", "", "") ); QuestStore store = new DatabaseQuestStore(client); quests = new QuestManager(store); guiManager = new GuiManager(this);   // 2. Регистрируем квест и меню. registerQuest(); registerMenu();   // 3. Вешаем обработчики и автосохранение прогресса. Bukkit.getPluginManager().registerEvents(this, this); startPlaytimeTask();   // 4. Команда для открытия меню. getCommand("winter").setExecutor((sender, command, label, args) -> { if (sender instanceof Player player) { guiManager.openGui(player, "winter_hub"); } else { sender.sendMessage("Только игроки могут открывать меню зимних квестов."); } return true; }); }   @Override public void onDisable() { if (databaseManager != null) { databaseManager.close(); } }   private void registerQuest() { winterQuest = quests.registerQuest(new Quest( "winter-2024", "Зимние приключения", "Согрей снеговика и докажи, что готов к празднику!", List.of( new KillObjective("snow-golems", "Убей 15 снеговиков", EntityType.SNOW_GOLEM, 15, false, true), new TravelObjective("long-walk", "Пройди 500 метров", 500), new CraftObjective("bake-cookies", "Скрафти 24 печенья", Material.COOKIE, 24), new PlaytimeObjective("stay-awhile", "Проведи 60 минут на сервере", 60) ) )); }   private void registerMenu() { Gui winterHub = guiManager.createGui("winter_hub", "&bНовогодние задания", 27);   // Кнопка старта/реактивации квеста. GuiItem startQuest = new GuiItem( item(Material.COMPASS, ChatColor.AQUA + "Начать цепочку", List.of( "§7ЛКМ — активировать квест", "§7ПКМ — обновить прогресс и подсветку")), 11 ); startQuest.addLeftClickAction(player -> activateWinterQuest(player.getUniqueId())); startQuest.addRightClickAction(Actions.update(guiManager));   // Кнопка получения награды с примером команды. GuiItem reward = new GuiItem( item(Material.EMERALD, ChatColor.GOLD + "Получить награду", List.of( "§7Доступно после выполнения всех целей")), 15 ); reward.addLeftClickAction(Actions.console("give %player% diamond 3")); reward.addEnchantmentCondition(player -> quests.getActiveProgress(player.getUniqueId(), winterQuest.getId()) .map(QuestProgress::isComplete) .orElse(false) );   // Кнопка закрытия. GuiItem closeButton = new GuiItem( item(Material.BARRIER, ChatColor.RED + "Закрыть", List.of("§7Вернуться в игру")), 26 ); closeButton.addLeftClickAction(Actions.close());   winterHub.addItem(startQuest); winterHub.addItem(reward); winterHub.addItem(closeButton); }   private ItemStack item(Material material, String name, List<String> lore) { ItemStack stack = new ItemStack(material); ItemMeta meta = stack.getItemMeta(); if (meta != null) { meta.setDisplayName(name); meta.setLore(lore); stack.setItemMeta(meta); } return stack; }   private void activateWinterQuest(UUID playerId) { quests.activateQuest(playerId, winterQuest.getId()); }   private void startPlaytimeTask() { new BukkitRunnable() { @Override public void run() { for (Player player : Bukkit.getOnlinePlayers()) { quests.recordPlaytime(player.getUniqueId(), 1); // +1 минута } } }.runTaskTimer(this, 20L * 60, 20L * 60); }   // ==== Ивенты для обновления прогресса квестов ====   @EventHandler public void onJoin(PlayerJoinEvent event) { activateWinterQuest(event.getPlayer().getUniqueId()); }   @EventHandler public void onEntityDeath(EntityDeathEvent event) { if (event.getEntity().getKiller() == null) return; quests.recordKill(event.getEntity().getKiller().getUniqueId(), event.getEntityType(), event.getEntityType() == EntityType.PLAYER); }   @EventHandler public void onPlayerMove(PlayerMoveEvent event) { double distance = event.getTo().distance(event.getFrom()); if (distance > 0.01) { quests.recordTravel(event.getPlayer().getUniqueId(), distance); } }   @EventHandler public void onCraft(CraftItemEvent event) { if (!(event.getWhoClicked() instanceof Player player)) return; int amount = event.getRecipe().getResult().getAmount(); quests.recordCraft(player.getUniqueId(), event.getRecipe().getResult().getType(), amount); } } ```   ## Как использовать пример 1. Скопируйте класс в свой плагин, замените пакет и путь к файлу базы при необходимости. 2. Зарегистрируйте команду `/winter` в `plugin.yml`. 3. Если хотите добавить/убрать цели, замените список в `registerQuest()`. 4. Подсветку и награды можно подстроить через условия и действия слотов. <!--EndFragment-->

GitHub release

NextLib v1.0.5.1

1.0.5.1corechore

**Full Changelog**: https://github.com/chi2l3s/next-lib/compare/1.0.3.6...1.5.1

GitHub release

NextLib v1.0.5

1.0.5corechore

**Full Changelog**: https://github.com/chi2l3s/next-lib/compare/1.0.3.6...1.0.5

GitHub release

NextLib v1.0.4

1.0.4databasefeature

# Dynamic database usage guide This document shows how to use the runtime database registration API that ships with NextLib. The feature lets you model your data as plain Java classes and then have NextLib create tables and helper queries on the fly when your plugin boots. ## 1. Describe your data as Java classes Create an immutable class that lists every column you need. The field names are used for the SQL column names, so prefer concise, lowercase identifiers. By default the first declared field becomes the primary key, but you can mark a different one explicitly with `@PrimaryKey`. ```java package cloud.nextgentech.nexttraps.types; import io.github.chi2l3s.nextlib.api.database.dynamic.PrimaryKey; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.ToString; import java.util.UUID; @AllArgsConstructor @Getter @ToString public class Player { @PrimaryKey private final UUID playerId; private final String nickname; private final String trapSkinId; } ``` ### Constructor requirements The dynamic mapper invokes the constructor that matches all declared fields, in declaration order. Lombok's `@AllArgsConstructor` (as in the example) or a manually written constructor satisfies this requirement. All non-static fields are persisted. Supported field types right now are: * `String` * `UUID` * primitive numeric types and their boxed counterparts (`int`, `long`, `double`, etc.) * `boolean` / `Boolean` * `Instant` ## 2. Bootstrap the dynamic database You can attach the dynamic layer to any `DatabaseClient`. The most common path inside a plugin is to plug in the `DatabaseManager` that your platform exposes: ```java import io.github.chi2l3s.nextlib.api.database.DatabaseManager; import io.github.chi2l3s.nextlib.api.database.dynamic.DynamicDatabase; import io.github.chi2l3s.nextlib.api.database.dynamic.DynamicTable; public final class NextTrapsPlugin extends JavaPlugin { private DynamicTable<Player> players; @Override public void onEnable() { DatabaseManager manager = getService(DatabaseManager.class); // however you obtain it DynamicDatabase database = DynamicDatabase.using(manager); // Table name defaults to the simple class name converted to snake_case and pluralised ("Player" -> "players") players = database.register(Player.class); } } ``` If you prefer a custom table name, call `database.register("player_profiles", Player.class)` instead. The first time you register a class the library will create the backing table automatically (using `CREATE TABLE IF NOT EXISTS`). ## 3. Run queries with the fluent helpers Every registered table exposes helper builders for the basic CRUD operations. ### Insert rows ```java players.create(new Player(playerId, nickname, "default")); ``` ### Fetch a single row ```java Optional<Player> found = players.findFirst() .where("nickname", "chi2l3s") .execute(); ``` The `where` clauses reference the Java field names, not SQL column names. All comparisons are equality checks; passing `null` produces `IS NULL` in SQL. ### Fetch multiple rows ```java List<Player> neonUsers = players.findMany() .where("trapSkinId", "neon") .execute(); ``` You can chain multiple `where` calls to AND the predicates together. ### Update existing rows ```java int updated = players.update() .set("trapSkinId", "midnight") .where("playerId", playerId) .execute(); ``` Every `set` and `where` argument uses the entity field names. Fields that you do not mention remain untouched. ## 4. Putting it together Below is a minimal end-to-end example that registers the `Player` entity when your plugin starts and then performs a few operations in response to commands or events. ```java public final class NextTrapsPlugin extends JavaPlugin { private DynamicTable<Player> players; @Override public void onEnable() { DatabaseManager manager = getService(DatabaseManager.class); DynamicDatabase database = DynamicDatabase.using(manager); players = database.register(Player.class); // seed one record on first boot players.create(new Player(UUID.randomUUID(), "chi2l3s", "default")); } public Optional<Player> lookupByNickname(String nickname) { return players.findFirst() .where("nickname", nickname) .execute(); } public List<Player> listWithSkin(String skinId) { return players.findMany() .where("trapSkinId", skinId) .execute(); } public int changeSkin(UUID playerId, String newSkin) { return players.update() .set("trapSkinId", newSkin) .where("playerId", playerId) .execute(); } } ``` This pattern scales to any number of entity classes. Register each class once during startup and reuse the returned `DynamicTable` instance wherever you need to interact with the database.

GitHub release

1.0.3.6

1.0.3.6corechore

**Full Changelog**: https://github.com/chi2l3s/next-lib/compare/1.0.3.5...1.0.3.6

GitHub release

1.0.3.5

1.0.3.5corechore

**Full Changelog**: https://github.com/chi2l3s/next-lib/compare/1.0.3.3...1.0.3.5

GitHub release

1.0.3.4

1.0.3.4corechore

**Full Changelog**: https://github.com/chi2l3s/next-lib/compare/1.0.3.2...1.0.3.4

GitHub release

1.0.3.3

1.0.3.3corechore

**Full Changelog**: https://github.com/chi2l3s/next-lib/compare/1.0.3.2...1.0.3.3

GitHub release

NextLib v1.0.3.2

1.0.3.2corechore

**Full Changelog**: https://github.com/chi2l3s/next-lib/compare/1.0.3.1...1.0.3.2

GitHub release

NextLib v1.0.3 Fix

1.0.3.1corechore

No release notes body.

GitHub release

NextLib v1.0.3

1.0.3databasedocs

# NextLib 1.0.3 — Database Toolkit NextLib 1.0.3 delivers a first-class database toolkit that lets plugin authors configure JDBC data sources, run queries safely, and generate type-safe repositories from simple YAML schemas. ## ✨ Highlights - **Unified database manager** with builders for MySQL, PostgreSQL, and SQLite connections, connection pooling defaults, and lifecycle-aware shutdown hooks. - **Fluent client helpers** (`DatabaseClient`) exposing convenience methods for transactional work, query execution, and ergonomic resource management. - **Schema-driven code generator** that turns YAML table definitions into Java records and repositories with CRUD SQL, mirroring Prisma-style workflows. ## 🚀 Getting started 1. Add your database connections during plugin bootstrap via `DatabaseManager` and `DatabaseConfig`. 2. Author a `database.schema.yml` describing tables, columns, and primary keys. 3. Run `SchemaGenerator#generate` (e.g., from a Gradle task) to emit strongly typed models into a generated sources directory. 4. Import the generated repositories and call the ready-made CRUD helpers in your plugin logic. ## 🔧 Configuration tips - Every generated repository exposes a static `using(DatabaseManager)` method to bind it to a configured data source. - Field types support common JDBC primitives (`INT`, `BIGINT`, `VARCHAR`, `BOOLEAN`, `TIMESTAMP`) with nullability and default value hints. - Customize output packages and source roots when invoking the generator to match your project layout. ## 🧭 Migration notes - Projects upgrading from 1.0.x can keep existing GUI action registrations unchanged; the new database APIs are additive. - Ensure Java 17 is used during compilation so the generated records compile without additional flags. - Regenerate code whenever you evolve the YAML schema to keep repositories in sync with your database model. ## 📚 Further reading - Review the bundled [schema example](../database-schema-example.yml) and README usage guide for end-to-end snippets. - Check the Javadocs on `DatabaseManager`, `DatabaseClient`, and `SchemaGenerator` for advanced options and extension points.

GitHub release

NextLib v1.0.2

1.0.2guichore

# NextLib 1.0.2 ## Краткое описание NextLib 1.0.2 добавляет расширяемый реестр действий GUI, который позволяет сторонним плагинам регистрировать собственные обработчики кликов через API. Обновление включает потокобезопасную регистрацию и улучшенную диагностику при загрузке YAML-меню. ## Основные изменения - Добавлен потокобезопасный реестр действий `Actions.registerAction`, позволяющий сопоставлять идентификаторы и аргументы из YAML с фабриками `GuiAction`. - Представлен функциональный интерфейс `GuiActionFactory`, упрощающий создание действий, которым передаётся `GuiManager` и строка аргументов. - `GuiLoader` теперь строит действия через реестр и журналирует неизвестные идентификаторы, что облегчает отладку конфигураций. ## Как использовать ```java import io.github.chi2l3s.nextlib.api.gui.Actions; import io.github.chi2l3s.nextlib.api.gui.GuiManager; import org.bukkit.Location; import org.bukkit.plugin.java.JavaPlugin; public final class CustomActionsPlugin extends JavaPlugin { @Override public void onLoad() { // Регистрация может происходить в onLoad или onEnable до загрузки YAML-меню. } @Override public void onEnable() { Actions.registerAction("teleport", (manager, args) -> { String[] parts = args.split(" "); if (parts.length < 3) { throw new IllegalArgumentException("Usage: teleport <x> <y> <z>"); } double x = Double.parseDouble(parts[0]); double y = Double.parseDouble(parts[1]); double z = Double.parseDouble(parts[2]); return player -> player.teleport(new Location(player.getWorld(), x, y, z)); }); } } ``` После регистрации действия его можно вызвать в YAML-меню: ```yaml items: tp-button: material: ENDER_PEARL slot: 13 name: "&bТелепорт" on_left_click: - "teleport 0 80 0" ``` При попытке использовать несуществующее действие в меню в лог плагина будет отправлено предупреждение с указанием проблемного элемента и типа клика.

GitHub release

NextLib v1.0.1

1.0.1corechore

**Full Changelog**: https://github.com/chi2l3s/next-lib/compare/1.0.0...1.0.1

GitHub release

1.0.0

1.0.0corechore

Initial commit **Full Changelog**: https://github.com/chi2l3s/next-lib/commits/1.0.0

GitHub release